Storing Arrays
En Starknet, los valores complejos (por ejemplo, tuplas o structs), se almacenan en un segmento continuo a partir de la dirección de la variable de almacenamiento. Hay una limitación de 256 elementos de campo para el tamaño máximo de un valor de almacenamiento complejo, lo que significa que para almacenar matrices de más de 255 elementos en el almacenamiento, necesitaríamos dividirlo en segmentos de tamaño n <= 255
y almacenar estos segmentos en múltiples direcciones de almacenamiento. Actualmente no hay soporte nativo para almacenar arrays en Cairo, por lo que necesitarás escribir tu propia implementación del rasgo Store
para el tipo de array que desees almacenar.
Note: While storing arrays in storage is possible, it is not always recommended, as the read and write operations can get very costly. For example, reading an array of size
n
requiresn
storage reads, and writing to an array of sizen
requiresn
storage writes. If you only need to access a single element of the array at a time, it is recommended to use aLegacyMap
and store the length in another variable instead.
El siguiente ejemplo muestra cómo escribir una implementación sencilla del rasgo StorageAccess
para el tipo Array<felt252>
, permitiéndonos almacenar arrays de hasta 255 elementos felt252
.
impl StoreFelt252Array of Store<Array<felt252>> {
fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult<Array<felt252>> {
StoreFelt252Array::read_at_offset(address_domain, base, 0)
}
fn write(
address_domain: u32, base: StorageBaseAddress, value: Array<felt252>
) -> SyscallResult<()> {
StoreFelt252Array::write_at_offset(address_domain, base, 0, value)
}
fn read_at_offset(
address_domain: u32, base: StorageBaseAddress, mut offset: u8
) -> SyscallResult<Array<felt252>> {
let mut arr: Array<felt252> = ArrayTrait::new();
// Read the stored array's length. If the length is superior to 255, the read will fail.
let len: u8 = Store::<u8>::read_at_offset(address_domain, base, offset)
.expect('Storage Span too large');
offset += 1;
// Sequentially read all stored elements and append them to the array.
let exit = len + offset;
loop {
if offset >= exit {
break;
}
let value = Store::<felt252>::read_at_offset(address_domain, base, offset).unwrap();
arr.append(value);
offset += Store::<felt252>::size();
};
// Return the array.
Result::Ok(arr)
}
fn write_at_offset(
address_domain: u32, base: StorageBaseAddress, mut offset: u8, mut value: Array<felt252>
) -> SyscallResult<()> {
// // Store the length of the array in the first storage slot.
let len: u8 = value.len().try_into().expect('Storage - Span too large');
Store::<u8>::write_at_offset(address_domain, base, offset, len);
offset += 1;
// Store the array elements sequentially
loop {
match value.pop_front() {
Option::Some(element) => {
Store::<felt252>::write_at_offset(address_domain, base, offset, element);
offset += Store::<felt252>::size();
},
Option::None(_) => { break Result::Ok(()); }
};
}
}
fn size() -> u8 {
255 * Store::<felt252>::size()
}
}
A continuación, puede importar esta implementación en su contrato y utilizarla para almacenar arrays en el almacenamiento:
#[starknet::interface]
trait IStoreArrayContract<TContractState> {
fn store_array(ref self: TContractState, arr: Array<felt252>);
fn read_array(self: @TContractState) -> Array<felt252>;
}
#[starknet::contract]
mod StoreArrayContract {
use super::StoreFelt252Array;
#[storage]
struct Storage {
arr: Array<felt252>
}
#[abi(embed_v0)]
impl StoreArrayImpl of super::IStoreArrayContract<ContractState> {
fn store_array(ref self: ContractState, arr: Array<felt252>) {
self.arr.write(arr);
}
fn read_array(self: @ContractState) -> Array<felt252> {
self.arr.read()
}
}
}