neo_solidity/cli/bytecode/bytecode_helpers/storage/structs/
value.rs

1fn emit_load_struct_value_from_slot(
2    bytecode: &mut Vec<u8>,
3    fields: &[ir::StructField],
4    use_callt: bool,
5    token_patches: &mut Vec<MethodTokenPatch>,
6) {
7    // Stack before: [base_slot]
8    // Stack after:  [struct_array]
9    push_integer_bigint(bytecode, &BigInt::from(fields.len() as u64));
10    bytecode.push(0xC3); // NEWARRAY
11
12    // Stack: [base_slot, out_array]
13    for (field_index, field) in fields.iter().enumerate() {
14        // Derive the field slot from the base slot:
15        // field_slot = keccak256(field_key || base_slot)
16        bytecode.push(0x4B); // OVER (duplicate base_slot)
17        push_data(bytecode, &field.key);
18        bytecode.push(0x50); // SWAP -> [base_slot, out_array, field_key, base_slot]
19        bytecode.push(0x8B); // CAT
20        emit_native_contract_call(
21            bytecode,
22            ir::NativeContract::CryptoLib,
23            "keccak256",
24            1,
25            use_callt,
26            token_patches,
27        );
28
29        // Stack: [base_slot, out_array, field_slot]
30        match &field.ty {
31            ValueType::Struct { fields, .. } => {
32                emit_load_struct_value_from_slot(bytecode, fields, use_callt, token_patches);
33            }
34            _ => {
35                emit_syscall(bytecode, "System.Storage.GetContext");
36                emit_syscall(bytecode, "System.Storage.Get");
37                emit_coerce_storage_value(bytecode, &field.ty);
38            }
39        }
40
41        // Set `out_array[field_index] = field_value`, preserving the array reference.
42        bytecode.push(0x4B); // OVER (duplicate out_array)
43        bytecode.push(0x50); // SWAP -> [base_slot, out_array, out_array, field_value]
44        push_integer_bigint(bytecode, &BigInt::from(field_index as u64));
45        bytecode.push(0x50); // SWAP -> [base_slot, out_array, out_array, idx, value]
46        bytecode.push(0xD0); // SETITEM
47    }
48
49    // Drop base_slot, leaving the struct array.
50    bytecode.push(0x50); // SWAP -> [out_array, base_slot]
51    bytecode.push(0x45); // DROP
52}
53
54fn emit_store_struct_value_to_slot(
55    bytecode: &mut Vec<u8>,
56    fields: &[ir::StructField],
57    use_callt: bool,
58    token_patches: &mut Vec<MethodTokenPatch>,
59) {
60    // Stack before: [base_slot, struct_array]
61    // Stack after:  []
62    for (field_index, field) in fields.iter().enumerate() {
63        // Derive field_slot = keccak256(field_key || base_slot)
64        bytecode.push(0x4B); // OVER (duplicate base_slot)
65        push_data(bytecode, &field.key);
66        bytecode.push(0x50); // SWAP
67        bytecode.push(0x8B); // CAT
68        emit_native_contract_call(
69            bytecode,
70            ir::NativeContract::CryptoLib,
71            "keccak256",
72            1,
73            use_callt,
74            token_patches,
75        );
76
77        // Stack: [base_slot, struct_array, field_slot]
78        // Extract field_value = struct_array[field_index]
79        bytecode.push(0x4B); // OVER (duplicate struct_array)
80        push_integer_bigint(bytecode, &BigInt::from(field_index as u64));
81        bytecode.push(0xCE); // PICKITEM -> [base_slot, struct_array, field_slot, field_value]
82
83        match &field.ty {
84            ValueType::Struct { fields, .. } => {
85                emit_store_struct_value_to_slot(bytecode, fields, use_callt, token_patches);
86            }
87            _ => {
88                // Store expects [value, key, context], so swap to put value on top of the slot.
89                bytecode.push(0x50); // SWAP -> [base_slot, struct_array, field_value, field_slot]
90                emit_syscall(bytecode, "System.Storage.GetContext");
91                emit_syscall(bytecode, "System.Storage.Put");
92            }
93        }
94    }
95
96    // Drop `[base_slot, struct_array]`.
97    bytecode.push(0x45); // DROP (struct_array)
98    bytecode.push(0x45); // DROP (base_slot)
99}