script (⏱,💰)

script (⏱,💰)

NG#6 - Storage Layout

This task is about storing contract state variables. It requires writing specific values directly to the specified locations in the storage of the smart contract, similar to the sstore function in Solidity.

After downloading the task, you can see that you need to use the storage_write_syscall function to write felt252 at the specified slot position, with both parameters being of felt252 type.

fn write(ref self: ContractState, slot_index: felt252, value: felt252) {
  let storageAddress = slot_index.try_into().unwrap();
  storage_write_syscall(0, storageAddress, value);
}

The StarkNet official documentation and the Cairo book provide explanations related to this. It is not specified which function is equivalent to sn_keccak, but there is guidance in the task's workthrough.

In Cairo, the slot address is calculated based on the variable name, unlike in Solidity where it is continuous. This makes it easier to upgrade the contract logic, as long as the attribute names remain the same, the slot addresses will be the same.

The approach I took was to write local tests. After passing the tests, I printed out the slot address and value, and used starkli invoke write [slot_index] [value] to answer. This way, calling the contract function directly is simpler, so I didn't write a separate hack contract.

For testing logic, I used starknet-foundry, and with the following code, I could simulate contract deployment, which was very convenient.

use src::contracts::catacombs::{ICatacombsDispatcher, ICatacombsDispatcherTrait};
use snforge_std::{declare, ContractClassTrait};

#[test]
fn test_1() {
    let contract = declare('Catacombs');
    let contract_address = contract.deploy(@ArrayTrait::new()).unwrap();
    let dispatcher = ICatacombsDispatcher { contract_address };
...

felt252#

The first entry_code is a felt252 represented as a string, which can be directly written in the write function. The only issue is that it needs to be printed and displayed as hex to be passed as an argument in the starkli command.

u256#

The second subtask is to write a value of 10^40 as a u256. This involves how to write exponentiation and how to store u256.

I didn't see any relevant operators in the official core library, but the third-party library alexandria has an implementation. However, the simplest way is to directly write 10000000000000000000000000000000000000000_u256 for 10^40.

u256 is stored in two parts, low and high, as one struct. According to the official documentation, the first element in the struct is stored in sn_keccak, and then sn_keccak+1. So, after calculating the two positions, submit 2 transactions to write u256.low and u256.high respectively.

map#

The third task is to write LegacyMap::<u256, bool>, similar to Solidity's mapping. The documentation provides detailed explanations. If it is a Map, the position calculation for the slot is h(...h(h(sn_keccak(variable_name),k_1),k_2),...,k_n).

Since the key is u256 and occupies 2 positions, it needs to be hashed twice.

The hash function used is core::pedersen::pedersen.

The corresponding felt252 values for the bool values are 0 and 1. Find 3 index positions and write 1 to them.

struct#

#[derive(Copy, Drop, Serde, starknet::Store)]
struct Chest {
    is_open: bool,
    owner: starknet::ContractAddress
}

The fourth task is to write the Chest structure. Actually, we already know how to calculate the position from the second task. The only issue here is how to convert a StarkNet contract address to felt252. I found the corresponding method in the source code.

Summary#

The main knowledge point of this task is the storage layout in StarkNet, which is not difficult. Understanding the documentation makes it easy to complete.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.