The ABI Encode
has 2 types which are encode
and encode_packed
.
encode
will concatenate all values and add padding to fit into 32 bytes for each values.encode_packed
will concatenate all values in the exact byte representations without padding. (For example, encode_packed("a", "bc") == encode_packed("ab", "c")
)Suppose we have a tuple of values: (target, value, func, data, timestamp)
to encode, and their alloy primitives type
are (Address, U256, String, Bytes, U256)
.
Firstly we need to import those types we need from alloy_primitives
, stylus_sdk::abi
and alloc::string
:
1// Import items from the SDK. The prelude contains common traits and macros.
2use stylus_sdk::{alloy_primitives::{U256, Address, FixedBytes}, abi::Bytes, prelude::*};
3// Import String from alloc
4use alloc::string::String;
1// Import items from the SDK. The prelude contains common traits and macros.
2use stylus_sdk::{alloy_primitives::{U256, Address, FixedBytes}, abi::Bytes, prelude::*};
3// Import String from alloc
4use alloc::string::String;
Secondly because we will use the method abi_encode_sequence
and abi_encode_packed
under alloy_sol_types
to encode data, we also need to import the types from alloy_sol_types
:
1// 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
2use alloy_sol_types::{sol_data::{Address as SOLAddress, String as SOLString, Bytes as SOLBytes, *}, SolType};
1// 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
2use alloy_sol_types::{sol_data::{Address as SOLAddress, String as SOLString, Bytes as SOLBytes, *}, SolType};
Then encode
them:
1// define sol types tuple
2type TxIdHashType = (SOLAddress, Uint<256>, SOLString, SOLBytes, Uint<256>);
3// set the tuple
4let tx_hash_data = (target, value, func, data, timestamp);
5// encode the tuple
6let tx_hash_bytes = TxIdHashType::abi_encode_sequence(&tx_hash_data);
1// define sol types tuple
2type TxIdHashType = (SOLAddress, Uint<256>, SOLString, SOLBytes, Uint<256>);
3// set the tuple
4let tx_hash_data = (target, value, func, data, timestamp);
5// encode the tuple
6let tx_hash_bytes = TxIdHashType::abi_encode_sequence(&tx_hash_data);
There are 2 methods to encode_packed
data:
encode_packed
them:1// define sol types tuple
2type TxIdHashType = (SOLAddress, Uint<256>, SOLString, SOLBytes, Uint<256>);
3// set the tuple
4let tx_hash_data = (target, value, func, data, timestamp);
5// encode the tuple
6let tx_hash_data_encode_packed = TxIdHashType::abi_encode_packed(&tx_hash_data);
1// define sol types tuple
2type TxIdHashType = (SOLAddress, Uint<256>, SOLString, SOLBytes, Uint<256>);
3// set the tuple
4let tx_hash_data = (target, value, func, data, timestamp);
5// encode the tuple
6let tx_hash_data_encode_packed = TxIdHashType::abi_encode_packed(&tx_hash_data);
encode_packed
them:1let tx_hash_data_encode_packed = [&target.to_vec(), &value.to_be_bytes_vec(), func.as_bytes(), &data.to_vec(), ×tamp.to_be_bytes_vec()].concat();
1let tx_hash_data_encode_packed = [&target.to_vec(), &value.to_be_bytes_vec(), func.as_bytes(), &data.to_vec(), ×tamp.to_be_bytes_vec()].concat();
1// Allow `cargo stylus export-abi` to generate a main function.
2#![cfg_attr(not(feature = "export-abi"), no_main)]
3extern crate alloc;
4
5
6/// Import items from the SDK. The prelude contains common traits and macros.
7use stylus_sdk::{alloy_primitives::{U256, Address, FixedBytes}, abi::Bytes, prelude::*};
8use alloc::string::String;
9// 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
10use alloy_sol_types::{sol_data::{Address as SOLAddress, String as SOLString, Bytes as SOLBytes, *}, SolType};
11use sha3::{Digest, Keccak256};
12
13// Define some persistent storage using the Solidity ABI.
14// `Encoder` will be the entrypoint.
15#[storage]
16#[entrypoint]
17pub struct Encoder;
18
19impl Encoder {
20 fn keccak256(&self, data: Bytes) -> FixedBytes<32> {
21 // prepare hasher
22 let mut hasher = Keccak256::new();
23 // populate the data
24 hasher.update(data);
25 // hashing with keccack256
26 let result = hasher.finalize();
27 // convert the result hash to FixedBytes<32>
28 let result_vec = result.to_vec();
29 FixedBytes::<32>::from_slice(&result_vec)
30 }
31}
32
33/// Declare that `Encoder` is a contract with the following external methods.
34#[public]
35impl Encoder {
36
37 // Encode the data and hash it
38 pub fn encode(
39 &self,
40 target: Address,
41 value: U256,
42 func: String,
43 data: Bytes,
44 timestamp: U256
45 ) -> Vec<u8> {
46 // define sol types tuple
47 type TxIdHashType = (SOLAddress, Uint<256>, SOLString, SOLBytes, Uint<256>);
48 // set the tuple
49 let tx_hash_data = (target, value, func, data, timestamp);
50 // encode the tuple
51 let tx_hash_data_encode = TxIdHashType::abi_encode_params(&tx_hash_data);
52 tx_hash_data_encode
53 }
54
55 // Packed encode the data and hash it, the same result with the following one
56 pub fn packed_encode(
57 &self,
58 target: Address,
59 value: U256,
60 func: String,
61 data: Bytes,
62 timestamp: U256
63 )-> Vec<u8> {
64 // define sol types tuple
65 type TxIdHashType = (SOLAddress, Uint<256>, SOLString, SOLBytes, Uint<256>);
66 // set the tuple
67 let tx_hash_data = (target, value, func, data, timestamp);
68 // encode the tuple
69 let tx_hash_data_encode_packed = TxIdHashType::abi_encode_packed(&tx_hash_data);
70 tx_hash_data_encode_packed
71 }
72
73 // Packed encode the data and hash it, the same result with the above one
74 pub fn packed_encode_2(
75 &self,
76 target: Address,
77 value: U256,
78 func: String,
79 data: Bytes,
80 timestamp: U256
81 )-> Vec<u8> {
82 // set the data to arrary and concat it directly
83 let tx_hash_data_encode_packed = [&target.to_vec(), &value.to_be_bytes_vec(), func.as_bytes(), &data.to_vec(), ×tamp.to_be_bytes_vec()].concat();
84 tx_hash_data_encode_packed
85 }
86
87
88 // The func example: "transfer(address,uint256)"
89 pub fn encode_with_signature(
90 &self,
91 func: String,
92 address: Address,
93 amount: U256
94 ) -> Vec<u8> {
95 type TransferType = (SOLAddress, Uint<256>);
96 let tx_data = (address, amount);
97 let data = TransferType::abi_encode_params(&tx_data);
98 // Get function selector
99 let hashed_function_selector = self.keccak256(func.as_bytes().to_vec().into());
100 // Combine function selector and input data (use abi_packed way)
101 let calldata = [&hashed_function_selector[..4], &data].concat();
102 calldata
103 }
104
105}
1// Allow `cargo stylus export-abi` to generate a main function.
2#![cfg_attr(not(feature = "export-abi"), no_main)]
3extern crate alloc;
4
5
6/// Import items from the SDK. The prelude contains common traits and macros.
7use stylus_sdk::{alloy_primitives::{U256, Address, FixedBytes}, abi::Bytes, prelude::*};
8use alloc::string::String;
9// 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
10use alloy_sol_types::{sol_data::{Address as SOLAddress, String as SOLString, Bytes as SOLBytes, *}, SolType};
11use sha3::{Digest, Keccak256};
12
13// Define some persistent storage using the Solidity ABI.
14// `Encoder` will be the entrypoint.
15#[storage]
16#[entrypoint]
17pub struct Encoder;
18
19impl Encoder {
20 fn keccak256(&self, data: Bytes) -> FixedBytes<32> {
21 // prepare hasher
22 let mut hasher = Keccak256::new();
23 // populate the data
24 hasher.update(data);
25 // hashing with keccack256
26 let result = hasher.finalize();
27 // convert the result hash to FixedBytes<32>
28 let result_vec = result.to_vec();
29 FixedBytes::<32>::from_slice(&result_vec)
30 }
31}
32
33/// Declare that `Encoder` is a contract with the following external methods.
34#[public]
35impl Encoder {
36
37 // Encode the data and hash it
38 pub fn encode(
39 &self,
40 target: Address,
41 value: U256,
42 func: String,
43 data: Bytes,
44 timestamp: U256
45 ) -> Vec<u8> {
46 // define sol types tuple
47 type TxIdHashType = (SOLAddress, Uint<256>, SOLString, SOLBytes, Uint<256>);
48 // set the tuple
49 let tx_hash_data = (target, value, func, data, timestamp);
50 // encode the tuple
51 let tx_hash_data_encode = TxIdHashType::abi_encode_params(&tx_hash_data);
52 tx_hash_data_encode
53 }
54
55 // Packed encode the data and hash it, the same result with the following one
56 pub fn packed_encode(
57 &self,
58 target: Address,
59 value: U256,
60 func: String,
61 data: Bytes,
62 timestamp: U256
63 )-> Vec<u8> {
64 // define sol types tuple
65 type TxIdHashType = (SOLAddress, Uint<256>, SOLString, SOLBytes, Uint<256>);
66 // set the tuple
67 let tx_hash_data = (target, value, func, data, timestamp);
68 // encode the tuple
69 let tx_hash_data_encode_packed = TxIdHashType::abi_encode_packed(&tx_hash_data);
70 tx_hash_data_encode_packed
71 }
72
73 // Packed encode the data and hash it, the same result with the above one
74 pub fn packed_encode_2(
75 &self,
76 target: Address,
77 value: U256,
78 func: String,
79 data: Bytes,
80 timestamp: U256
81 )-> Vec<u8> {
82 // set the data to arrary and concat it directly
83 let tx_hash_data_encode_packed = [&target.to_vec(), &value.to_be_bytes_vec(), func.as_bytes(), &data.to_vec(), ×tamp.to_be_bytes_vec()].concat();
84 tx_hash_data_encode_packed
85 }
86
87
88 // The func example: "transfer(address,uint256)"
89 pub fn encode_with_signature(
90 &self,
91 func: String,
92 address: Address,
93 amount: U256
94 ) -> Vec<u8> {
95 type TransferType = (SOLAddress, Uint<256>);
96 let tx_data = (address, amount);
97 let data = TransferType::abi_encode_params(&tx_data);
98 // Get function selector
99 let hashed_function_selector = self.keccak256(func.as_bytes().to_vec().into());
100 // Combine function selector and input data (use abi_packed way)
101 let calldata = [&hashed_function_selector[..4], &data].concat();
102 calldata
103 }
104
105}
1[package]
2name = "stylus-encode-hashing"
3version = "0.1.7"
4edition = "2021"
5license = "MIT OR Apache-2.0"
6keywords = ["arbitrum", "ethereum", "stylus", "alloy"]
7
8[dependencies]
9alloy-primitives = "=0.7.6"
10alloy-sol-types = "=0.7.6"
11mini-alloc = "0.4.2"
12stylus-sdk = "0.6.0"
13hex = "0.4.3"
14sha3 = "0.10"
15
16[dev-dependencies]
17tokio = { version = "1.12.0", features = ["full"] }
18ethers = "2.0"
19eyre = "0.6.8"
20
21[features]
22export-abi = ["stylus-sdk/export-abi"]
23
24[lib]
25crate-type = ["lib", "cdylib"]
26
27[profile.release]
28codegen-units = 1
29strip = true
30lto = true
31panic = "abort"
32opt-level = "s"
1[package]
2name = "stylus-encode-hashing"
3version = "0.1.7"
4edition = "2021"
5license = "MIT OR Apache-2.0"
6keywords = ["arbitrum", "ethereum", "stylus", "alloy"]
7
8[dependencies]
9alloy-primitives = "=0.7.6"
10alloy-sol-types = "=0.7.6"
11mini-alloc = "0.4.2"
12stylus-sdk = "0.6.0"
13hex = "0.4.3"
14sha3 = "0.10"
15
16[dev-dependencies]
17tokio = { version = "1.12.0", features = ["full"] }
18ethers = "2.0"
19eyre = "0.6.8"
20
21[features]
22export-abi = ["stylus-sdk/export-abi"]
23
24[lib]
25crate-type = ["lib", "cdylib"]
26
27[profile.release]
28codegen-units = 1
29strip = true
30lto = true
31panic = "abort"
32opt-level = "s"