I am developing a smart contract that needs to store Transaction structs in storage until they can be executed. The Transaction struct is defined as follows:
/// The transaction that is being proposed.
pub struct Transaction {
tx_id: TxId,
to: Identity,
tx_parameters: TransactionParameters,
}
pub type TxId = u64;
/// Determines the type of transaction parameters.
pub enum TransactionParameters {
Call: ContractCallParams,
Transfer: TransferParams,
}
/// Parameters for calling a contract.
pub struct ContractCallParams {
/// The calldata for the call.
calldata: Bytes,
/// The amount of gas to forward.
forwarded_gas: u64,
/// The function selector for the call.
function_selector: Bytes,
/// Whether the function being called takes a single value-type argument.
single_value_type_arg: bool,
/// Parameters for a transfer.
transfer_params: TransferParams,
}
/// Parameters for a transfer.
pub struct TransferParams {
/// The asset to transfer.
asset_id: ContractId,
/// The amount to transfer.
value: Option<u64>,
}
In my storage definition, I am getting the following error:
storage {
...
// Other fields
/// The transactions that are currently active.
txs: StorageMap<TxId, Transaction> = StorageMap {},
-> Error: `The type "pointer" is not allowed in storage.`
...
}
Transaction contains TransactionParams which contains ContractCallParams which contains Bytes which contains RawBytes which contains a raw_ptr, and that’s not allowed. Storage can’t contain heap types.
Using StorageBytes worked for making the compiler happy. But now when im actually retrieving the data, i cannot access it.
Recalling the fixed data types:
/// Determines the type of transaction parameters.
pub enum TransactionParameters {
Call: ContractCallParams,
Transfer: TransferParams,
}
/// Parameters for calling a contract.
pub struct ContractCallParams {
/// The calldata for the call.
calldata: StorageBytes,
/// The amount of gas to forward.
forwarded_gas: u64,
/// The function selector for the call.
function_selector: StorageBytes,
/// Whether the function being called takes a single value-type argument.
single_value_type_arg: bool,
/// Parameters for a transfer.
transfer_params: TransferParams,
}
When trying to access the calldata and function selector fields from the Transaction struct:
The approach shown below does not work because the type of contract_call_params.function_selector is StorageBytes, and it doesn’t have any methods to retrieve the content. I did some research and the only way it works is if the StorageBytes type is defined inside the storage by itself, because when accessing storage.function_selector its wrapped in a StorageKey.
let transaction:Transaction = storage.txs.get(tx_id).try_read().unwrap();
match transaction.tx_parameters {
TransactionParameters::Call(contract_call_params) => {
// I would expect something like this to work to retrieve the underlying Bytes
let function_selector = contract_call_params.function_selector.read_slice().unwrap();
}
How should i access the underlying bytes that were previously stored?
There are some limitations with the way the current storage interface works, but you should be able to store dynamic data as a key into another mapping.
Instead of a struct with StorageBytes you’d just have a u64 that’s a key into another storage slot’s StorageMap<u64, StorageBytes> or something to that effect.