ccnext_abi_encoding/abi/
mod.rs

1
2use super::common::{
3    compute_v, compute_y_parity, encode_blob_hashes,
4    AbiEncodeResult,
5};
6use alloy::{
7    consensus::{
8        Signed, Transaction as ConsensusTransaction, TxEip1559, TxEip2930,
9        TxEip4844Variant, TxEip7702, TxEnvelope, TxLegacy,
10    }, dyn_abi::DynSolValue, eips::eip7702::SignedAuthorization, primitives::{Address, B256, U256}, rpc::types::{AccessListItem, Transaction, TransactionReceipt}
11};
12use thiserror::Error;
13
14#[derive(Debug, Error)]
15pub enum EncodeError {
16    #[error("Custom error: {0}")]
17    Custom(String),
18}
19
20pub fn encode_transaction_type_0(tx: Transaction, signed_tx: Signed<TxLegacy>) -> Vec<DynSolValue> {
21
22    // Extract transaction fields
23    let signature = signed_tx.signature();
24    let chain_id = tx.chain_id();
25    let v = compute_v(signature, chain_id);
26    
27    let values: Vec<DynSolValue> = vec![
28        DynSolValue::Uint(U256::from(0), 8),                            // Transaction type 0
29        DynSolValue::Uint(U256::from(signed_tx.tx().nonce), 64),        // Nonce
30        DynSolValue::Uint(U256::from(signed_tx.tx().gas_price), 128),   // Gas price
31        DynSolValue::Uint(U256::from(signed_tx.tx().gas_limit), 64),    // Gas limit
32        DynSolValue::Address(tx.from),                                  // From address
33        DynSolValue::Address(tx.to().unwrap_or(Address::ZERO)),         // To address
34        DynSolValue::Uint(tx.value(), 256), // Value                           // Value
35        DynSolValue::Bytes(tx.input().to_vec()), // Input data (true = dynamic encoding)
36        DynSolValue::Uint(U256::from(v), 256),                          // v (legacy is uint8)
37        DynSolValue::FixedBytes(B256::from(signature.r()), 32),         // r
38        DynSolValue::FixedBytes(B256::from(signature.s()), 32)          // s
39    ];
40
41    values
42}
43
44pub fn encode_transaction_type_1(
45    tx: Transaction,
46    signed_tx: Signed<TxEip2930>,
47) -> Vec<DynSolValue> {
48    // Extract transaction fields
49    let signature = signed_tx.signature();
50    let y_parity: u8 = compute_y_parity(signature);
51    let access_list = encode_access_list(signed_tx.tx().access_list.0.clone());
52
53    let values: Vec<DynSolValue> = vec![
54        DynSolValue::Uint(U256::from(1), 8), // Transaction type 1
55        DynSolValue::Uint(U256::from(signed_tx.tx().chain_id), 64),
56        DynSolValue::Uint(U256::from(signed_tx.tx().nonce), 64), // Nonce
57        DynSolValue::Uint(U256::from(signed_tx.tx().gas_price), 128), // Gas price
58        DynSolValue::Uint(U256::from(signed_tx.tx().gas_limit), 64), // Gas limit
59        DynSolValue::Address(tx.from),       // From address
60        DynSolValue::Address(tx.to().unwrap_or(Address::ZERO)), // To address
61        DynSolValue::Uint(tx.value(), 256),  // Value
62        DynSolValue::Bytes(tx.input().to_vec()), // Input data (true = dynamic encoding)
63        access_list,
64        DynSolValue::Uint(U256::from(y_parity), 8),     // y parity
65        DynSolValue::FixedBytes(B256::from(signature.r()), 32), // r
66        DynSolValue::FixedBytes(B256::from(signature.s()), 32), // s
67    ];
68
69    values
70}
71
72pub fn encode_transaction_type_2(
73    tx: Transaction,
74    signed_tx: Signed<TxEip1559>,
75) -> Vec<DynSolValue> {
76    // Extract transaction fields
77    let signature = signed_tx.signature();
78    let y_parity = compute_y_parity(signature);
79    let access_list = encode_access_list(signed_tx.tx().access_list.0.clone());
80
81    let values: Vec<DynSolValue> = vec![
82        DynSolValue::Uint(U256::from(2), 8),
83        DynSolValue::Uint(U256::from(signed_tx.tx().chain_id), 64),
84        DynSolValue::Uint(U256::from(signed_tx.tx().nonce), 64),
85        DynSolValue::Uint(U256::from(tx.max_priority_fee_per_gas().unwrap_or(0)), 128),
86        DynSolValue::Uint(U256::from(tx.max_fee_per_gas()), 128),
87        DynSolValue::Uint(U256::from(signed_tx.tx().gas_limit), 64),
88        DynSolValue::Address(tx.from),
89        DynSolValue::Address(tx.to().unwrap_or(Address::ZERO)),
90        DynSolValue::Uint(tx.value(), 256),
91        DynSolValue::Bytes(tx.input().to_vec()),
92        access_list,
93        DynSolValue::Uint(U256::from(y_parity), 8),
94        DynSolValue::FixedBytes(B256::from(signature.r()), 32),
95        DynSolValue::FixedBytes(B256::from(signature.s()), 32),
96    ];
97
98    values
99}
100
101pub fn encode_transaction_type_3(
102    tx: Transaction,
103    signed_tx: Signed<TxEip4844Variant>,
104) -> Vec<DynSolValue> {
105    // Extract transaction fields
106    let signature = signed_tx.signature();
107    let y_parity = compute_y_parity(signature);
108
109    let access_list_item_vector = match tx.access_list() {
110        Some(access_list) => access_list.0.clone(),
111        None => Vec::new(),
112    };
113    let access_list = encode_access_list(access_list_item_vector);
114    let blob_version_hashes = tx.blob_versioned_hashes().unwrap_or_default();
115    let blob_hashes = encode_blob_hashes(blob_version_hashes.to_vec());
116
117    // it seems its possible that chain id isn't set for some reason?
118    // if its a side car blob transaction.
119    let chain_id = tx.chain_id().unwrap_or(0);
120
121    let values: Vec<DynSolValue> = vec![
122        DynSolValue::Uint(U256::from(3), 8),
123        DynSolValue::Uint(U256::from(chain_id), 64),
124        DynSolValue::Uint(U256::from(signed_tx.tx().nonce()), 64),
125        DynSolValue::Uint(U256::from(tx.max_priority_fee_per_gas().unwrap_or(0)), 128),
126        DynSolValue::Uint(U256::from(tx.max_fee_per_gas()), 128),
127        DynSolValue::Uint(U256::from(signed_tx.tx().gas_limit()), 64),
128        DynSolValue::Address(tx.from),
129        DynSolValue::Address(tx.to().unwrap_or(Address::ZERO)),
130        DynSolValue::Uint(tx.value(), 256),
131        DynSolValue::Bytes(tx.input().to_vec()),
132        access_list,
133        DynSolValue::Uint(
134            U256::from(signed_tx.tx().max_fee_per_blob_gas().unwrap_or(0u128)),
135            128,
136        ),
137        blob_hashes,
138        DynSolValue::Uint(U256::from(y_parity), 8),
139        DynSolValue::FixedBytes(B256::from(signature.r()), 32),
140        DynSolValue::FixedBytes(B256::from(signature.s()), 32),
141    ];
142
143    values
144}
145
146pub fn encode_transaction_type_4(
147    tx: Transaction,
148    signed_tx: Signed<TxEip7702>,
149) -> Vec<DynSolValue> {
150    // Extract transaction fields
151    let signature = signed_tx.signature();
152    let y_parity = compute_y_parity(signature);
153    let access_list = encode_access_list(signed_tx.tx().access_list.0.clone());
154    let authorization_list = encode_authorization_list(signed_tx.tx().authorization_list.clone());
155
156    // it seems its possible that chain id isn't set for some reason?
157    // if its a side car blob transaction.
158    let chain_id = tx.chain_id().unwrap_or(0);
159
160    let values: Vec<DynSolValue> = vec![
161        DynSolValue::Uint(U256::from(3), 8),
162        DynSolValue::Uint(U256::from(chain_id), 64),
163        DynSolValue::Uint(U256::from(signed_tx.tx().nonce()), 64),
164        DynSolValue::Uint(U256::from(tx.max_priority_fee_per_gas().unwrap_or(0)), 128),
165        DynSolValue::Uint(U256::from(tx.max_fee_per_gas()), 128),
166        DynSolValue::Uint(U256::from(signed_tx.tx().gas_limit()), 64),
167        DynSolValue::Address(tx.from),
168        DynSolValue::Address(tx.to().unwrap_or(Address::ZERO)),
169        DynSolValue::Uint(tx.value(), 256),
170        DynSolValue::Bytes(tx.input().to_vec()),
171        access_list,
172        authorization_list,
173        DynSolValue::Uint(U256::from(y_parity), 8),
174        DynSolValue::FixedBytes(B256::from(signature.r()), 32),
175        DynSolValue::FixedBytes(B256::from(signature.s()), 32),
176    ];
177
178    values
179}
180
181pub fn encode_transaction(tx: Transaction) -> Vec<DynSolValue> {
182    match tx.inner.clone() {
183        TxEnvelope::Legacy(signed_tx) => encode_transaction_type_0(tx, signed_tx),
184        TxEnvelope::Eip2930(signed_tx) => encode_transaction_type_1(tx, signed_tx),
185        TxEnvelope::Eip1559(signed_tx) => encode_transaction_type_2(tx, signed_tx),
186        TxEnvelope::Eip4844(signed_tx) => encode_transaction_type_3(tx, signed_tx),
187        TxEnvelope::Eip7702(signed_tx) => encode_transaction_type_4(tx, signed_tx)
188    }
189}
190
191fn encode_receipt(rx: TransactionReceipt) -> Vec<DynSolValue> {
192
193    let log_blooms = rx.inner.logs_bloom().0.to_vec();
194    let result = vec![
195        DynSolValue::Uint(U256::from(rx.status()), 8),
196        DynSolValue::Uint(U256::from(rx.gas_used), 64),
197        DynSolValue::Array(
198            rx.inner.logs().into_iter().map(|log| {
199
200                let topics = DynSolValue::Array(log.topics().into_iter().map(|topic| {
201                    DynSolValue::FixedBytes(topic.clone(), 32)
202                }).collect());
203
204                DynSolValue::Tuple(vec![
205                    DynSolValue::Address(log.address()),
206                    topics,
207                    DynSolValue::Bytes(log.data().data.to_vec())
208                ])
209            }).collect()
210        ),
211        DynSolValue::Bytes(log_blooms),
212    ];
213
214    result
215}
216
217pub fn encode_authorization_list(signed_authorizations: Vec<SignedAuthorization>) -> DynSolValue {
218    let mut result = Vec::new();
219    for signed_authorization in signed_authorizations {
220
221        let signed_authorization_tuple = DynSolValue::Tuple(vec![
222            DynSolValue::Uint(U256::from(signed_authorization.chain_id().clone()), 256),
223            DynSolValue::Address(signed_authorization.address().clone()),
224            DynSolValue::Uint(U256::from(signed_authorization.nonce()), 64),
225            DynSolValue::Uint(U256::from(signed_authorization.y_parity()), 8),
226            DynSolValue::Uint(U256::from(signed_authorization.r()), 256),
227            DynSolValue::Uint(U256::from(signed_authorization.s()), 256)
228        ]);
229        
230        //let pack_encoded = signed_authorization_tuple.abi_encode_packed();
231        result.push(signed_authorization_tuple);
232    }
233
234    DynSolValue::Array(result)
235}
236
237pub fn encode_access_list(access_list: Vec<AccessListItem>) -> DynSolValue {
238
239    let mut list = Vec::new();
240    for access_list_item in access_list {
241
242        let mut storage_keys = Vec::new();
243        for storage_item in access_list_item.storage_keys {
244            storage_keys.push(DynSolValue::FixedBytes(storage_item, 32));
245        }
246
247        // Create the `DynSolValue::Tuple` (address, storage_keys)
248        let access_list_tuple = DynSolValue::Tuple(vec![
249            DynSolValue::Address(access_list_item.address),  // Address
250            DynSolValue::Array(storage_keys)          
251        ]);
252
253        list.push(access_list_tuple);
254    }
255
256    // Wrap into `DynSolValue::Array`
257    DynSolValue::Array(list)
258}
259
260pub fn abi_encode(
261    tx: Transaction,
262    rx: TransactionReceipt,
263) -> Result<AbiEncodeResult, Box<dyn std::error::Error>> {
264
265    let transaction_fields = encode_transaction(tx);
266    let receipt_fields = encode_receipt(rx);
267    let mut all_fields = Vec::new();
268    all_fields.extend(transaction_fields);
269    all_fields.extend(receipt_fields);
270    let tuple = DynSolValue::Tuple(all_fields.clone());
271    let final_bytes = match tuple.abi_encode_sequence() {
272        Some(final_bytes) => {
273            final_bytes
274        },
275        None => {
276            return Err(Box::new(EncodeError::Custom("Failed to encode sequence".into())));
277        }
278    };
279
280    let field_types: Vec<String> = all_fields.into_iter().map(|field| {
281
282        match field.as_type() {
283            Some(sol_type) => {
284                sol_type.sol_type_name().into_owned()
285            },
286            None => "unknown".into()
287        }
288
289    }).collect();
290
291    Ok(AbiEncodeResult {
292        types: field_types,
293        abi: final_bytes,
294    })
295}