ccnext_abi_encoding/solidity_pack/
mod.rs

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