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/// Use an efficient WASM allocator.
6#[global_allocator]
7static ALLOC: mini_alloc::MiniAlloc = mini_alloc::MiniAlloc::INIT;
8
9
10/// Import items from the SDK. The prelude contains common traits and macros.
11use stylus_sdk::{alloy_primitives::{U256, Address, FixedBytes}, abi::Bytes, prelude::*};
12use alloc::string::String;
13// 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
14use alloy_sol_types::{sol_data::{Address as SOLAddress, String as SOLString, Bytes as SOLBytes, *}, SolType};
15use sha3::{Digest, Keccak256};
16
17// Define some persistent storage using the Solidity ABI.
18// `Encoder` will be the entrypoint.
19#[solidity_storage]
20#[entrypoint]
21pub struct Encoder;
22
23impl Encoder {
24 fn keccak256(&self, data: Bytes) -> FixedBytes<32> {
25 // prepare hasher
26 let mut hasher = Keccak256::new();
27 // populate the data
28 hasher.update(data);
29 // hashing with keccack256
30 let result = hasher.finalize();
31 // convert the result hash to FixedBytes<32>
32 let result_vec = result.to_vec();
33 FixedBytes::<32>::from_slice(&result_vec)
34 }
35}
36
37/// Declare that `Encoder` is a contract with the following external methods.
38#[external]
39impl Encoder {
40
41 // Encode the data and hash it
42 pub fn encode(
43 &self,
44 target: Address,
45 value: U256,
46 func: String,
47 data: Bytes,
48 timestamp: U256
49 ) -> Vec<u8> {
50 // define sol types tuple
51 type TxIdHashType = (SOLAddress, Uint<256>, SOLString, SOLBytes, Uint<256>);
52 // set the tuple
53 let tx_hash_data = (target, value, func, data, timestamp);
54 // encode the tuple
55 let tx_hash_data_encode = TxIdHashType::abi_encode_sequence(&tx_hash_data);
56 tx_hash_data_encode
57 }
58
59 // Packed encode the data and hash it, the same result with the following one
60 pub fn packed_encode(
61 &self,
62 target: Address,
63 value: U256,
64 func: String,
65 data: Bytes,
66 timestamp: U256
67 )-> Vec<u8> {
68 // define sol types tuple
69 type TxIdHashType = (SOLAddress, Uint<256>, SOLString, SOLBytes, Uint<256>);
70 // set the tuple
71 let tx_hash_data = (target, value, func, data, timestamp);
72 // encode the tuple
73 let tx_hash_data_encode_packed = TxIdHashType::abi_encode_packed(&tx_hash_data);
74 tx_hash_data_encode_packed
75 }
76
77 // Packed encode the data and hash it, the same result with the above one
78 pub fn packed_encode_2(
79 &self,
80 target: Address,
81 value: U256,
82 func: String,
83 data: Bytes,
84 timestamp: U256
85 )-> Vec<u8> {
86 // set the data to arrary and concat it directly
87 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();
88 tx_hash_data_encode_packed
89 }
90
91
92 // The func example: "transfer(address,uint256)"
93 pub fn encode_with_signature(
94 &self,
95 func: String,
96 address: Address,
97 amount: U256
98 ) -> Vec<u8> {
99 type TransferType = (SOLAddress, Uint<256>);
100 let tx_data = (address, amount);
101 let data = TransferType::abi_encode_sequence(&tx_data);
102 // Get function selector
103 let hashed_function_selector = self.keccak256(func.as_bytes().to_vec().into());
104 // Combine function selector and input data (use abi_packed way)
105 let calldata = [&hashed_function_selector[..4], &data].concat();
106 calldata
107 }
108
109}
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/// Use an efficient WASM allocator.
6#[global_allocator]
7static ALLOC: mini_alloc::MiniAlloc = mini_alloc::MiniAlloc::INIT;
8
9
10/// Import items from the SDK. The prelude contains common traits and macros.
11use stylus_sdk::{alloy_primitives::{U256, Address, FixedBytes}, abi::Bytes, prelude::*};
12use alloc::string::String;
13// 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
14use alloy_sol_types::{sol_data::{Address as SOLAddress, String as SOLString, Bytes as SOLBytes, *}, SolType};
15use sha3::{Digest, Keccak256};
16
17// Define some persistent storage using the Solidity ABI.
18// `Encoder` will be the entrypoint.
19#[solidity_storage]
20#[entrypoint]
21pub struct Encoder;
22
23impl Encoder {
24 fn keccak256(&self, data: Bytes) -> FixedBytes<32> {
25 // prepare hasher
26 let mut hasher = Keccak256::new();
27 // populate the data
28 hasher.update(data);
29 // hashing with keccack256
30 let result = hasher.finalize();
31 // convert the result hash to FixedBytes<32>
32 let result_vec = result.to_vec();
33 FixedBytes::<32>::from_slice(&result_vec)
34 }
35}
36
37/// Declare that `Encoder` is a contract with the following external methods.
38#[external]
39impl Encoder {
40
41 // Encode the data and hash it
42 pub fn encode(
43 &self,
44 target: Address,
45 value: U256,
46 func: String,
47 data: Bytes,
48 timestamp: U256
49 ) -> Vec<u8> {
50 // define sol types tuple
51 type TxIdHashType = (SOLAddress, Uint<256>, SOLString, SOLBytes, Uint<256>);
52 // set the tuple
53 let tx_hash_data = (target, value, func, data, timestamp);
54 // encode the tuple
55 let tx_hash_data_encode = TxIdHashType::abi_encode_sequence(&tx_hash_data);
56 tx_hash_data_encode
57 }
58
59 // Packed encode the data and hash it, the same result with the following one
60 pub fn packed_encode(
61 &self,
62 target: Address,
63 value: U256,
64 func: String,
65 data: Bytes,
66 timestamp: U256
67 )-> Vec<u8> {
68 // define sol types tuple
69 type TxIdHashType = (SOLAddress, Uint<256>, SOLString, SOLBytes, Uint<256>);
70 // set the tuple
71 let tx_hash_data = (target, value, func, data, timestamp);
72 // encode the tuple
73 let tx_hash_data_encode_packed = TxIdHashType::abi_encode_packed(&tx_hash_data);
74 tx_hash_data_encode_packed
75 }
76
77 // Packed encode the data and hash it, the same result with the above one
78 pub fn packed_encode_2(
79 &self,
80 target: Address,
81 value: U256,
82 func: String,
83 data: Bytes,
84 timestamp: U256
85 )-> Vec<u8> {
86 // set the data to arrary and concat it directly
87 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();
88 tx_hash_data_encode_packed
89 }
90
91
92 // The func example: "transfer(address,uint256)"
93 pub fn encode_with_signature(
94 &self,
95 func: String,
96 address: Address,
97 amount: U256
98 ) -> Vec<u8> {
99 type TransferType = (SOLAddress, Uint<256>);
100 let tx_data = (address, amount);
101 let data = TransferType::abi_encode_sequence(&tx_data);
102 // Get function selector
103 let hashed_function_selector = self.keccak256(func.as_bytes().to_vec().into());
104 // Combine function selector and input data (use abi_packed way)
105 let calldata = [&hashed_function_selector[..4], &data].concat();
106 calldata
107 }
108
109}
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"
11sha3 = "0.10.8"
12
13[features]
14export-abi = ["stylus-sdk/export-abi"]
15debug = ["stylus-sdk/debug"]
16
17[lib]
18crate-type = ["lib", "cdylib"]
19
20[profile.release]
21codegen-units = 1
22strip = true
23lto = true
24panic = "abort"
25opt-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"
11sha3 = "0.10.8"
12
13[features]
14export-abi = ["stylus-sdk/export-abi"]
15debug = ["stylus-sdk/debug"]
16
17[lib]
18crate-type = ["lib", "cdylib"]
19
20[profile.release]
21codegen-units = 1
22strip = true
23lto = true
24panic = "abort"
25opt-level = "s"