Skip to content

Conversation

@salaheldinsoliman
Copy link
Contributor

This PR brings support to user defined structs in Soroban's Solidity.

Here are some points to note:

  • In-memory structs were naturally supported by integrating a sorobanVm-friendly bump allocator that was introduced in Soroban: support dynamic memory arrays #1838

  • For storing structs in storage, we do as follows:
    We encode struct fields as Soroban Vals, and store each one separately in storage where the key of each element is SorobanVec[storage_slot, field_index].

When we retrieve a whole struct, i.e StructDef memory my_struct = my_structs_map[my_struct], we simply loop over all fields, fetch them from storage, and create an in memory instance.

That is why we strongly advise to access a single struct field at a time instead of fetching it all from storage at once. This a complete example:

contract timelock {
    
    mapping(address => TimeLock) timelocks;
    
    struct TimeLock {
        uint64 release_time;
        address beneficiary;
        uint64 amount;
    }

   

    function create_timelock(
        uint64 release_time,
        address beneficiary,
        uint64 amount
    ) public returns (uint64) {

        TimeLock memory tl = TimeLock({
            release_time: release_time,
            beneficiary: beneficiary,
            amount: amount
        });

        timelocks[beneficiary] = tl;

        return tl.amount;
    }

    function get_timelock_amount_cheap_op(address beneficiary) public view returns (uint64) {
      return timelocks[beneficiary].amount;
    }

    function get_timelock_amount_expensive_op(address beneficiary) public view returns (uint64) {
      TimeLock memory tl = timelocks[beneficiary];
      return tl.amount;
    }
}

Here, get_timelock_amount_expensive_op not only retrieves all fields from storage, but puts in extra instructions construct the struct in-memory.

get_timelock_amount_cheap_op on the other hand simply calls the soroban host function get_contract_data and doesn't need extra work.

salaheldinsoliman and others added 6 commits October 5, 2025 15:24
Signed-off-by: salaheldinsoliman <[email protected]>
Signed-off-by: salaheldinsoliman <[email protected]>
Signed-off-by: salaheldinsoliman <[email protected]>
@salaheldinsoliman salaheldinsoliman merged commit 9f01585 into hyperledger-solang:main Nov 2, 2025
14 of 17 checks passed
guptapratykshh pushed a commit to guptapratykshh/pg-solang that referenced this pull request Nov 16, 2025
This PR brings support to user defined structs in Soroban's Solidity.

Here are some points to note:

- In-memory structs were naturally supported by integrating a
sorobanVm-friendly bump allocator that was introduced in hyperledger-solang#1838

- For storing structs in storage, we do as follows:
We encode struct fields as Soroban Vals, and store each one separately
in storage where the key of each element is SorobanVec[storage_slot,
field_index].

When we retrieve a whole struct, i.e `StructDef memory my_struct =
my_structs_map[my_struct]`, we simply loop over all fields, fetch them
from storage, and create an in memory instance.

That is why we strongly advise to access a single struct field at a time
instead of fetching it all from storage at once. This a complete
example:

```
contract timelock {

    mapping(address => TimeLock) timelocks;

    struct TimeLock {
        uint64 release_time;
        address beneficiary;
        uint64 amount;
    }

    function create_timelock(
        uint64 release_time,
        address beneficiary,
        uint64 amount
    ) public returns (uint64) {

        TimeLock memory tl = TimeLock({
            release_time: release_time,
            beneficiary: beneficiary,
            amount: amount
        });

        timelocks[beneficiary] = tl;

        return tl.amount;
    }

    function get_timelock_amount_cheap_op(address beneficiary) public view returns (uint64) {
      return timelocks[beneficiary].amount;
    }

    function get_timelock_amount_expensive_op(address beneficiary) public view returns (uint64) {
      TimeLock memory tl = timelocks[beneficiary];
      return tl.amount;
    }
}
```

Here, `get_timelock_amount_expensive_op` not only retrieves all fields
from storage, but puts in extra instructions construct the struct
in-memory.

`get_timelock_amount_cheap_op` on the other hand simply calls the
soroban host function `get_contract_data` and doesn't need extra work.

---------

Signed-off-by: salaheldinsoliman <[email protected]>
Signed-off-by: Pratyksh Gupta <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant