Skip to content

feat(Stellar): Represent vectors in storage as VecObjects#1851

Merged
salaheldinsoliman merged 6 commits intohyperledger-solang:mainfrom
salaheldinsoliman:soroban/storage-vectors
Mar 15, 2026
Merged

feat(Stellar): Represent vectors in storage as VecObjects#1851
salaheldinsoliman merged 6 commits intohyperledger-solang:mainfrom
salaheldinsoliman:soroban/storage-vectors

Conversation

@salaheldinsoliman
Copy link
Contributor

In this PR, we improve the overall structure of how vectors are represented in memory and in storage.
We propose and implement the following:

Arrays representation in storage

Arrays of native types

Arrays of native types are represented in storage as a VecObject handle. When storage array operations are called, the VecObject handle is retrieved from storage, updated, and then put back to storage.

Arrays of custom types

Arrays of structs follow a sparse storage approach, where each element is stored independently in storage, with its own storage key. The key is a VecObject, where the first index is the Solidity storage slot, and the following indexes are the specific indexes the program wants to access.

For example:

contract storage_struct_vec {
    struct Pair {
        uint64 a; // slot 0
        uint64 b; // slot 1
    }

    Pair[] items; // slot 3, stores VecLength

    function push_pair_len() public returns (uint64) {
        Pair memory p1 = Pair({a: 1, b: 2});

        //set_contract_data(key: Vec(0,0), data: 1)
        //set_contract_data(key: Vec(0,1), data: 2)
        items.push(p1);

        //get_contract_data(key: 0)
        return items.length; // 2
    }

    function write_then_read() public returns (uint64) {
        items.push(); // append empty slot

        //set_contract_data(key: Vec(1,0), data: 9)
        //set_contract_data(key: Vec(1,1), data: 11)
        items[1] = Pair({a: 9, b: 11});

        // get_contract_data(key: Vec(1,0)) + get_contract_deploy(key: Vec(2,2))
        return items[1].a + items[2].b;
    }

    function load_all_struct() public returns (uint64) {
        //get_contract_data(key: Vec(0,0))
        //get_contract_data(key: Vec(0,1))
        Pair memory p1 = items[0];

        return p1.a;
    }
}

We claim that this approach makes the mutation of nested type more efficient. We will follow up with experiments that prove this claim

The downside is that passing a nested array, a nestedVecObject, is not currently possible.
One solution would be to encode/decode structs as a MapObject which will be done in following PRs.

Arrays representation in linear memory

Passing an array as a function argument

Soroban contracts accept array function arguments as VecObjects. We follow a lazy decoding/encoding approach, where the element is only decoded/encoded if it is accessed.

The array argument is first unpacked to the guest linear memory, where the type of the unpacked buffer is Array(SorobanHandle). This makes sense since the underlying elements are not decoded yet.

When a memory array element is accessed, this translates in Solang to a Load/Store memory instruction. If the type is an Array(SorobanHandle), the inner SorobanHandle is lazily decoded/encoded.

Allocating a memory array

When a memory array is allocated in linear memory, i.e:

function random_access(uint64 index) public returns (uint64) {
    uint64[] mylist;
    uint64 sum = 0;

    mylist.push(5);
    mylist.push(10);
    mylist.push(15);

    sum += mylist[index];
    sum += mylist[index + 1];

    return sum;
}

It is allocated in the guest linear memory with a bump allocator. If an array passed in as an argument needs to be copied to an array allocated inside the function, Array(SorobanHandle) -> Array(NativeType), the elements are decoded and copied in loop.

This operation is expensive, and we usually advise against it in practice.

Signed-off-by: salaheldinsoliman <salaheldin_sameh@aucegypt.edu>
Signed-off-by: salaheldinsoliman <salaheldin_sameh@aucegypt.edu>
Signed-off-by: salaheldinsoliman <salaheldin_sameh@aucegypt.edu>
Signed-off-by: salaheldinsoliman <salaheldin_sameh@aucegypt.edu>
Signed-off-by: salaheldinsoliman <salaheldin_sameh@aucegypt.edu>
Signed-off-by: salaheldinsoliman <salaheldin_sameh@aucegypt.edu>
@salaheldinsoliman salaheldinsoliman merged commit 901bfb9 into hyperledger-solang:main Mar 15, 2026
11 of 17 checks passed
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