存储优化
智能合约只有有限的存储槽位。每个槽位可以存储一个 felt252
值。
写入一个存储槽位会产生成本,因此我们希望尽可能少地使用存储槽位。
在 Cairo 中,每种类型都源自 felt252
类型,它使用 252 位来存储一个值。
这种设计相当简单,但它有一个缺点:它在存储效率方面并不高。例如,如果要存储一个 u8
值,我们需要使用整个槽位,尽管我们只需要 8 位。
打包
当存储多个值时,我们可以使用一种称为**打包(packing)**的技术。打包是一种允许我们在单个 felt 值中存储多个值的技术。这是通过使用 felt 值的位来存储多个值来实现的。
例如,如果我们想存储两个 u8
值,我们可以使用 felt 值的前 8 位来存储第一个 u8
值,而使用后 8 位来存储第二个 u8
值。这样,我们就可以在单个 felt 值中存储两个 u8
值。
Cairo 提供了一个内置的打包存储功能,您可以通过 StorePacking
特性来使用它。
trait StorePacking<T, PackedT> {
fn pack(value: T) -> PackedT;
fn unpack(value: PackedT) -> T;
}
这允许通过首先使用 pack
函数将类型 T
打包成 PackedT
类型,然后使用其 Store
实现来存储 PackedT
值。在读取值时,我们首先获取 PackedT
值,然后使用 unpack
函数将其解包为类型 T
。
以下是一个使用 StorePacking
特性存储包含两个 u8
值的 Time
结构体的示例:
#[starknet::interface]
trait ITime<TContractState> {
fn set(ref self: TContractState, value: TimeContract::Time);
fn get(self: @TContractState) -> TimeContract::Time;
}
#[starknet::contract]
mod TimeContract {
use starknet::storage_access::StorePacking;
use integer::{
U8IntoFelt252, Felt252TryIntoU16, U16DivRem, u16_as_non_zero, U16IntoFelt252,
Felt252TryIntoU8
};
use traits::{Into, TryInto, DivRem};
use option::OptionTrait;
use serde::Serde;
#[storage]
struct Storage {
time: Time
}
#[derive(Copy, Serde, Drop)]
struct Time {
hour: u8,
minute: u8
}
impl TimePackable of StorePacking<Time, felt252> {
fn pack(value: Time) -> felt252 {
let msb: felt252 = 256 * value.hour.into();
let lsb: felt252 = value.minute.into();
return msb + lsb;
}
fn unpack(value: felt252) -> Time {
let value: u16 = value.try_into().unwrap();
let (q, r) = U16DivRem::div_rem(value, u16_as_non_zero(256));
let hour: u8 = Into::<u16, felt252>::into(q).try_into().unwrap();
let minute: u8 = Into::<u16, felt252>::into(r).try_into().unwrap();
return Time { hour, minute };
}
}
#[abi(embed_v0)]
impl TimeContract of super::ITime<ContractState> {
fn set(ref self: ContractState, value: Time) {
// This will call the pack method of the TimePackable trait
// and store the resulting felt252
self.time.write(value);
}
fn get(self: @ContractState) -> Time {
// This will read the felt252 value from storage
// and return the result of the unpack method of the TimePackable trait
return self.time.read();
}
}
}
在 Remix 上测试这个合约.