I have this struct in sway:
pub struct TestStructMetadataBytes {
val1: b256,
val2: b256,
// metadata_keys: Vec<(String, String)>,
metadata_keys: Vec<(raw_slice, raw_slice)>,
}
impl TestStructMetadataBytes {
pub fn new(val1: b256, val2: b256) -> Self {
Self {
val1,
val2,
metadata_keys: Vec::new(),
}
}
pub fn from_bytes(bytes: Bytes) -> Self {
let mut bytes_alloc = Bytes::with_capacity(size_of::<TestStructMetadataBytes>());
bytes.buf.ptr.copy_bytes_to(bytes_alloc.buf.ptr, bytes.len);
return bytes_alloc.buf.ptr.read::<TestStructMetadataBytes>();
}
pub fn to_bytes(self) -> Bytes {
let foo_len = size_of_val(self);
let foo_ptr = __addr_of(self);
let buf_len = foo_len / size_of::<u8>();
let foo_buf = raw_slice::from_parts::<u8>(foo_ptr, buf_len);
return Bytes::from(foo_buf);
}
pub fn has_metadata_key(self, key: String) -> bool {
let mut count: u64 = 0;
while count < self.metadata_keys.len() {
let (metadata_key, _) = self.metadata_keys.get(count).unwrap();
if String::from(metadata_key) == key {
return true;
}
count = count + 1;
}
return false;
}
pub fn add_metadata(ref mut self, metadata: MetadataValue) {
self.metadata_keys.push((metadata.key.as_raw_slice(), metadata.value.as_raw_slice()));
}
pub fn get_metadata(self, key: String) -> Option<MetadataValue> {
let mut count: u64 = 0;
while count < self.metadata_keys.len() {
let (metadata_key, metadata_value) = self.metadata_keys.get(count).unwrap();
if String::from(metadata_key) == key {
return Option::Some(MetadataValue {
key: String::from(metadata_key),
value: String::from(metadata_value),
});
}
count = count + 1;
}
return Option::None;
}
}
When I convert the struct to bytes and store it in storage, I can access the values of val1 and val2, but the metadata_keys comes with 1 item in the array and the Strings are empty.
abi MetadataStorage {
#[storage(read, write)]
fn save(name: String, metadata: MetadataValue);
#[storage(read)]
fn get(name: String, key_metadata: String) -> Option<MetadataValue>;
#[storage(read, write)]
fn save_handle(name: String);
}
storage {
handles: StorageMap<b256, StorageBytes> = StorageMap {},
}
impl MetadataStorage for Contract {
#[storage(read, write)]
fn save_handle(name: String) {
let key = sha256(name);
let mut domain = TestStructMetadataBytes::new(key, key);
storage.handles.insert(key, StorageBytes {});
storage.handles.get(key).write_slice(domain.to_bytes());
}
#[storage(read, write)]
fn save(name: String, metadata: MetadataValue) {
let key = sha256(name);
let domain_bytes = storage.handles.get(key).read_slice().unwrap();
let mut domain = TestStructMetadataBytes::from_bytes(domain_bytes);
domain.add_metadata(metadata);
storage.handles.get(key).write_slice(domain.to_bytes());
}
#[storage(read)]
fn get(name: String, key_metadata: String) -> Option<MetadataValue> {
let key = sha256(name);
let domain_bytes = storage.handles.get(key).read_slice().unwrap();
let mut domain = TestStructMetadataBytes::from_bytes(domain_bytes);
return domain.get_metadata(key_metadata);
}
}
In tests on sway works normally but executing the method get(name, key_metadata)
in fuels-ts don’t return the value because metadata_keys
has 1 item in Vec with empty Strings.
I tried change to Vec<Bytes, Bytes> and Vec<String, String) but not work.
Here the test in sway:
#[test]
fn test_contract() {
let metadata_storage = abi(MetadataStorage, CONTRACT_ID);
let name = String::from_ascii_str("github");
let github_metadata = MetadataValue {
key: String::from_ascii_str("github"),
value: String::from_ascii_str("name"),
};
let linkedin_metadata = MetadataValue {
key: String::from_ascii_str("linkedin"),
value: String::from_ascii_str("name"),
};
metadata_storage.save_handle(name);
metadata_storage.save(name, github_metadata);
metadata_storage.save(name, linkedin_metadata);
let github_metadata1 = metadata_storage.get(name, github_metadata.key);
let linkedin_metadata1 = metadata_storage.get(name, linkedin_metadata.key);
assert(github_metadata1.unwrap().value == github_metadata.value);
assert(linkedin_metadata1.unwrap().value == linkedin_metadata.value);
}
The same test In ts:
const provider = await Provider.create('http://localhost:4000/graphql');
const walletUnlocked = Wallet.fromPrivateKey('PRIVATE_KEY', provider);
const metadataStorageAbi = MetadataStorageAbi__factory.connect(metadataStorage, walletUnlocked);
const name = 'github';
const txParams = {gasPrice: 1, gasLimit: 1_000_000};
const github_metadata = {
key: 'github',
value: 'name',
};
await metadataStorageAbi.functions.save_handle(name).txParams(txParams).call();
await metadataStorageAbi.functions.save(name, github_metadata).txParams(txParams).call();
const result = await metadataStorageAbi.functions.get(name, github_metadata.key).txParams(txParams).call();
console.log(result.value) // undefined
Is there any way to work around this issue or any other solution?