Arbitrum Stylus logo

Stylus by Example

ABI Decode

The decode can not be used for encode_packed data because it ignores padding when encode. (For more information you can refer to ABI Encode)

So here we show an example for using decode on data encoded with abi_encode_sequence:

1// This should always return true
2pub fn encode_and_decode(
3    &self, 
4    target: Address,
5    value: U256,
6    func: String,
7    data: Bytes,
8    timestamp: U256
9) -> Result<bool, HasherError> {
10    // define sol types tuple
11    type TxIdHashType = (SOLAddress, Uint<256>, SOLString, SOLBytes, Uint<256>);
12    // because the abi_encode_sequence will return alloy_primitives::Bytes rather than stylus_sdk::bytes, so we need to make sure the input and return types are the same
13    let primative_data = alloy_primitives::Bytes::copy_from_slice(&data);
14    // set the tuple
15    let tx_hash_data = (target, value, func, primative_data, timestamp);
16    // encode the tuple
17    let tx_hash_data_encode = TxIdHashType::abi_encode_sequence(&tx_hash_data);
18
19    let validate = true;
20    
21    // Check the result
22    match TxIdHashType::abi_decode_sequence(&tx_hash_data_encode, validate) {
23        Ok(res) => Ok(res == tx_hash_data),
24        Err(_) => {
25            return Err(HasherError::DecodedFailed(DecodedFailed{}));
26        },
27    }   
28}
1// This should always return true
2pub fn encode_and_decode(
3    &self, 
4    target: Address,
5    value: U256,
6    func: String,
7    data: Bytes,
8    timestamp: U256
9) -> Result<bool, HasherError> {
10    // define sol types tuple
11    type TxIdHashType = (SOLAddress, Uint<256>, SOLString, SOLBytes, Uint<256>);
12    // because the abi_encode_sequence will return alloy_primitives::Bytes rather than stylus_sdk::bytes, so we need to make sure the input and return types are the same
13    let primative_data = alloy_primitives::Bytes::copy_from_slice(&data);
14    // set the tuple
15    let tx_hash_data = (target, value, func, primative_data, timestamp);
16    // encode the tuple
17    let tx_hash_data_encode = TxIdHashType::abi_encode_sequence(&tx_hash_data);
18
19    let validate = true;
20    
21    // Check the result
22    match TxIdHashType::abi_decode_sequence(&tx_hash_data_encode, validate) {
23        Ok(res) => Ok(res == tx_hash_data),
24        Err(_) => {
25            return Err(HasherError::DecodedFailed(DecodedFailed{}));
26        },
27    }   
28}

Full Example code:

src/main.rs

1#![no_main]
2#![no_std]
3extern crate alloc;
4/// Use an efficient WASM allocator.
5#[global_allocator]
6static ALLOC: mini_alloc::MiniAlloc = mini_alloc::MiniAlloc::INIT;
7
8/// Import items from the SDK. The prelude contains common traits and macros.
9use stylus_sdk::{alloy_primitives::{U256, Address}, prelude::*};
10// Becauce the naming of alloy_primitives and alloy_sol_types is the same, so we need to re-name the types in alloy_sol_types
11use alloy_sol_types::{sol_data::{Address as SOLAddress, *}, SolType};
12use alloy_sol_types::sol;
13
14// Define error
15sol! {
16    error DecodedFailed();
17}
18
19// Error types for the MultiSig contract
20#[derive(SolidityError)]
21pub enum DecoderError{
22    DecodedFailed(DecodedFailed)
23}
24
25// Define some persistent storage using the Solidity ABI.
26// `Decoder` will be the entrypoint.
27sol_storage! {
28    #[entrypoint]
29    pub struct Decoder {
30    }
31}
32
33
34/// Declare that `Decoder` is a contract with the following external methods.
35#[external]
36impl Decoder {
37    // This should always return true
38    pub fn encode_and_decode(
39        &self, 
40        address: Address, 
41        amount: U256
42    ) -> Result<bool, DecoderError> {
43        // define sol types tuple
44        type TxIdHashType = (SOLAddress, Uint<256>);
45        // set the tuple
46        let tx_hash_data = (address, amount);
47        // encode the tuple
48        let tx_hash_data_encode = TxIdHashType::abi_encode_sequence(&tx_hash_data);
49
50        let validate = true;
51        
52        // Check the result
53        match TxIdHashType::abi_decode_sequence(&tx_hash_data_encode, validate) {
54            Ok(res) => Ok(res == tx_hash_data),
55            Err(_) => {
56                return Err(DecoderError::DecodedFailed(DecodedFailed{}));
57            },
58        }   
59    }
60
61}
1#![no_main]
2#![no_std]
3extern crate alloc;
4/// Use an efficient WASM allocator.
5#[global_allocator]
6static ALLOC: mini_alloc::MiniAlloc = mini_alloc::MiniAlloc::INIT;
7
8/// Import items from the SDK. The prelude contains common traits and macros.
9use stylus_sdk::{alloy_primitives::{U256, Address}, prelude::*};
10// Becauce the naming of alloy_primitives and alloy_sol_types is the same, so we need to re-name the types in alloy_sol_types
11use alloy_sol_types::{sol_data::{Address as SOLAddress, *}, SolType};
12use alloy_sol_types::sol;
13
14// Define error
15sol! {
16    error DecodedFailed();
17}
18
19// Error types for the MultiSig contract
20#[derive(SolidityError)]
21pub enum DecoderError{
22    DecodedFailed(DecodedFailed)
23}
24
25// Define some persistent storage using the Solidity ABI.
26// `Decoder` will be the entrypoint.
27sol_storage! {
28    #[entrypoint]
29    pub struct Decoder {
30    }
31}
32
33
34/// Declare that `Decoder` is a contract with the following external methods.
35#[external]
36impl Decoder {
37    // This should always return true
38    pub fn encode_and_decode(
39        &self, 
40        address: Address, 
41        amount: U256
42    ) -> Result<bool, DecoderError> {
43        // define sol types tuple
44        type TxIdHashType = (SOLAddress, Uint<256>);
45        // set the tuple
46        let tx_hash_data = (address, amount);
47        // encode the tuple
48        let tx_hash_data_encode = TxIdHashType::abi_encode_sequence(&tx_hash_data);
49
50        let validate = true;
51        
52        // Check the result
53        match TxIdHashType::abi_decode_sequence(&tx_hash_data_encode, validate) {
54            Ok(res) => Ok(res == tx_hash_data),
55            Err(_) => {
56                return Err(DecoderError::DecodedFailed(DecodedFailed{}));
57            },
58        }   
59    }
60
61}

Cargo.toml

1[package]
2name = "stylus-encode-hashing"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
7alloy-primitives = "0.7.3"
8alloy-sol-types = "0.7.3"
9mini-alloc = "0.4.2"
10stylus-sdk = "0.5.1"
11
12[features]
13export-abi = ["stylus-sdk/export-abi"]
14debug = ["stylus-sdk/debug"]
15
16[lib]
17crate-type = ["lib", "cdylib"]
18
19[profile.release]
20codegen-units = 1
21strip = true
22lto = true
23panic = "abort"
24opt-level = "s"
1[package]
2name = "stylus-encode-hashing"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
7alloy-primitives = "0.7.3"
8alloy-sol-types = "0.7.3"
9mini-alloc = "0.4.2"
10stylus-sdk = "0.5.1"
11
12[features]
13export-abi = ["stylus-sdk/export-abi"]
14debug = ["stylus-sdk/debug"]
15
16[lib]
17crate-type = ["lib", "cdylib"]
18
19[profile.release]
20codegen-units = 1
21strip = true
22lto = true
23panic = "abort"
24opt-level = "s"