rustsol
Description
rustsol
is a tool designed to generate Rust storage bindings for Ethereum smart contracts.
These bindings can be used to retrieve storage slots, offsets, and sizes of all stored variables within a contract, including objects stored in mappings and arrays.
When initialized with a slots_getter
, the generated structures can be used to conveniently access the values of these storage variables.
Bindings Example Usage
Get storage position of a contract variable:
let contract = new;
let = contract.observations.at.tickCumulative.position;
println!;
// Output:
// slot=50, offset=4, size_bytes=7
Get storage value of a contract variable using provided slots getter:
let contract = new;
contract.set_slots_getter;
let tick_value = contract.ticks.at.get_value.unwrap;
println!;
// Output (prettified):
// TickInfoValue {
// liquidityGross: 398290794261,
// liquidityNet: 398290794261,
// feeGrowthOutside0X128: 0x0000000000000000000000000000000000000b73d798604f1b0cd4f1d544c646_U256,
// feeGrowthOutside1X128: 0x000000000000000000000000000000f2a960acbe8891e526c025b819077f15ae_U256,
// tickCumulativeOutside: 622572156443,
// secondsPerLiquidityOutsideX128: 0x0000000000000000000000000000000000000001e576ee66a9d9f002e36fad4c_U256,
// secondsOutside: 1623419923,
// initialized: true
// }
Generated Structs
The generated structs from the bindings will look similar to the following:
Installation
Setup and Usage
1. Get Contract Storage Layout
To generate storage bindings for a contract, you first need the contract's storage layout in JSON format.
For Solidity contracts, this can be generated using the solc
compiler.
rustsol
provides a simple Python script to assist with this process.
See the original repository for instructions.
2. Generate Bindings
To generate bindings, use the following command:
To understand what contract_path
and contract_name
mean, let's look at an example storage layout JSON:
Here, UniswapV3Pool.sol
is the contract path, and UniswapV3Pool
is the contract name.
For the provided example, it would be:
After running the above command, the generated structs will be in generated_contract.rs
file.
See the original repository for an example.
Types Mapping
Solidity -> Rust Mapping
Solidity variable types are mapped to rustsol
binding types.
This mapping is shown in the table below.
Here, value_type
corresponds to a (possibly nested) rustsol
binding type, such as Primitive
or Mapping
.
The native_type
is a type to which the actual slot values are converted, such as u64
, bool
and U256
.
Note that the keys of Mappings
are also native types.
Solidity Type | Generated Rust Type |
---|---|
all integer types, bool, enum, address, contract, small bytes1..32 | Primitive<byte_size, native_type> |
string, bytes | Bytes<String> , Bytes<Vec<u8>> |
static arrays | StaticArray<byte_size, value_type> |
dynamic arrays | DynamicArray<value_type> |
mapping | Mapping<key_native_type, value_type> |
struct | CustomNamedStructWithCorrespondingFields |
Note that all enums are mapped to Primitive<byte_size, U256>
.
It would be better to use native Rust enums as the native_type
instead of the generic U256
,
but this requires deeper Solidity contract analysis,
as information about enum field names is not available from the storage layout.
Value Types Mapping
Variable values obtained with get_value()
are converted to native types according to the table below.
rustsol Type |
Generated Rust Type |
---|---|
Primitive<_, native_type> |
native_type |
Bytes<String> |
String |
Bytes<Vec<u8>> |
Vec<u8> |
StaticArray<value_type> |
Vec<recursive native type of value_type> |
DynamicArray |
Vec<recursive native type of value_type> |
Mapping |
Mapping<key_native_type, value_type> getter |
SomeStruct |
SomeStructValue |
Note that Mapping
types are actually mapped to the same Mapping
.
This is because there is no way to get all elements of a Solidity mapping.
Therefore, we return a value getter instead of a concrete value.
License
This project is licensed under the MIT License.