Arbitrum Stylus logo

Stylus by Example

Export the Solidity ABI interface of your contract

Stylus contracts are fully interoperable across all languages, including Solidity. The Stylus SDK provides tools for exporting a Solidity interface for your contract so that others can call it. This is usually done with the cargo stylus CLI tool.

The Stylus Rust SDK does this automatically for you via a feature flag called export-abi that causes the #[public] and #[entrypoint] macros to generate a main function that prints the Solidity ABI to the console.

Let's see an example:

These are the contents of a lib.rs file.

1sol_storage! {
2    #[entrypoint]
3    pub struct Counter {
4        uint256 number;
5    }
6}
7
8#[public]
9impl Counter {
10    pub fn number(&self) -> U256 {
11        self.number.get()
12    }
13    
14    pub fn set_number(&mut self, new_number: U256) {
15        self.number.set(new_number);
16    }
17    
18    pub fn increment(&mut self) {
19        let number = self.number.get();
20        self.set_number(number + U256::from(1));
21    }
22}
1sol_storage! {
2    #[entrypoint]
3    pub struct Counter {
4        uint256 number;
5    }
6}
7
8#[public]
9impl Counter {
10    pub fn number(&self) -> U256 {
11        self.number.get()
12    }
13    
14    pub fn set_number(&mut self, new_number: U256) {
15        self.number.set(new_number);
16    }
17    
18    pub fn increment(&mut self) {
19        let number = self.number.get();
20        self.set_number(number + U256::from(1));
21    }
22}

This is the main.rs file.

1#[cfg(feature = "export-abi")]
2fn main() {
3    counter::print_abi("MIT-OR-APACHE-2.0", "pragma solidity ^0.8.23;");
4}
1#[cfg(feature = "export-abi")]
2fn main() {
3    counter::print_abi("MIT-OR-APACHE-2.0", "pragma solidity ^0.8.23;");
4}

And the relevant parts of the Cargo.toml file, notice that it has the export-abi feature flag enabled.

1...
2[dependencies]
3stylus-sdk = "0.6.0"
4...
5
6[features]
7export-abi = ["stylus-sdk/export-abi"]
8...
1...
2[dependencies]
3stylus-sdk = "0.6.0"
4...
5
6[features]
7export-abi = ["stylus-sdk/export-abi"]
8...

With this code, you can run the export-abi command

1cargo stylus export-abi
1cargo stylus export-abi

and this is the result obtained:

1// SPDX-License-Identifier: MIT-OR-APACHE-2.0
2pragma solidity ^0.8.23;
3
4interface ICounter {
5    function number() external view returns (uint256);
6
7    function setNumber(uint256 new_number) external;
8
9    function increment() external;
10}
1// SPDX-License-Identifier: MIT-OR-APACHE-2.0
2pragma solidity ^0.8.23;
3
4interface ICounter {
5    function number() external view returns (uint256);
6
7    function setNumber(uint256 new_number) external;
8
9    function increment() external;
10}

Observe that the method names change from Rust's snake_case to Solidity's camelCase. For compatibility reasons, on-chain method selectors are always camelCase.

You can also obtain a JSON formatted ABI using the parameter --json. Running

1cargo stylus export-abi --json
1cargo stylus export-abi --json

will result in

1[{"inputs":[],"name":"increment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"number","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"new_number","type":"uint256"}],"name":"setNumber","outputs":[],"stateMutability":"nonpayable","type":"function"}]
1[{"inputs":[],"name":"increment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"number","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"new_number","type":"uint256"}],"name":"setNumber","outputs":[],"stateMutability":"nonpayable","type":"function"}]

Finally, note that when using argument names like address, the SDK will prepend an _ when generating the Solidity ABI interface. For example:

1interface Erc20 {
2    function name() external pure returns (string memory);
3    function balanceOf(address _address) external view returns (uint256);
4}
5
6interface Weth is Erc20 {
7    function mint() external payable;
8    function burn(uint256 amount) external;
9}
1interface Erc20 {
2    function name() external pure returns (string memory);
3    function balanceOf(address _address) external view returns (uint256);
4}
5
6interface Weth is Erc20 {
7    function mint() external payable;
8    function burn(uint256 amount) external;
9}