1use ccnext_abi_encoding::common::{compute_v, compute_y_parity};
2use alloy::consensus::Transaction as _;
3use alloy::rpc::types::eth::transaction::Transaction;
4use alloy::eips::eip2930::AccessList;
5use alloy::{
6 primitives::{Address, B256, U256},
7 providers::{Provider, ProviderBuilder},
8 rpc::types::TransactionReceipt,
9};
10use async_trait::async_trait;
11use std::str::FromStr;
12
13use crate::abi::models::QueryBuilderError;
14use crate::abi::query_builder::AbiProvider;
15
16pub enum ResultField {
17 TxType(u8),
18 TxChainId(u64),
19 TxNonce(u64),
20 TxGasPrice(u128),
21 TxMaxPriorityFeePerGas(u128),
22 TxMaxFeePerGas(u128),
23 TxGasLimit(u64),
24 EthAddress(Address),
25 TxValue(U256),
26 TxAccessList(AccessList),
27 TxV(U256),
28 TxYParity(u8),
29 TxR([u8; 32]),
30 TxS([u8; 32]),
31 RxStatus(u8),
32 RxGasUsed(u64),
33 RxLogBlooms(Vec<u8>),
34 EventTopic([u8; 32]),
35 EventDataField([u8; 32]),
36 FunctionSignifier([u8; 4]),
37 CallDataField([u8; 32]),
38}
39
40pub async fn get_transaction_and_receipt(tx_hash_str: &str) -> (Transaction, TransactionReceipt) {
41 let rpc_url = "https://sepolia-proxy-rpc.creditcoin.network";
43 let provider = ProviderBuilder::new().on_http(rpc_url.parse().unwrap());
44
45 let tx_hash = B256::from_str(tx_hash_str).unwrap();
47
48 let tx = provider
50 .get_transaction_by_hash(tx_hash)
51 .await
52 .unwrap()
53 .unwrap();
54 let rx = provider
55 .get_transaction_receipt(tx_hash)
56 .await
57 .unwrap()
58 .unwrap();
59 (tx, rx)
60}
61
62pub fn check_results(
63 expected_results: Vec<ResultField>,
64 result_segments: Vec<(usize, usize)>,
65 abi: Vec<u8>,
66) {
67 assert_eq!(expected_results.len(), result_segments.len(), "Number of expected results doesn't match segment count. Expected results: {}, Result segments: {}", expected_results.len(), result_segments.len());
68
69 for (field_number, (expected, (offset, size))) in
70 expected_results.iter().zip(result_segments).enumerate()
71 {
72 let expected_padded: Vec<u8> = match expected {
74 ResultField::TxType(value) |
76 ResultField::TxYParity(value) |
77 ResultField::RxStatus(value) => {
78 let mut value_padded: Vec<u8> = vec![0; 31];
79 value_padded.push(*value);
80 value_padded
81 },
82 ResultField::RxGasUsed(value) |
84 ResultField::TxNonce(value) |
85 ResultField::TxChainId(value) |
86 ResultField::TxGasLimit(value) => {
87 let mut value_padded: Vec<u8> = vec![0; 24];
88 value_padded.append(&mut Vec::from(value.to_be_bytes()));
89 value_padded
90 }
91 ResultField::TxGasPrice(value) |
93 ResultField::TxMaxPriorityFeePerGas(value) |
94 ResultField::TxMaxFeePerGas(value) => {
95 let mut value_padded: Vec<u8> = vec![0; 16];
96 value_padded.append(&mut Vec::from(value.to_be_bytes()));
97 value_padded
98 }
99 ResultField::EthAddress(address) => {
100 let mut address_padded: Vec<u8> = vec![0; 12];
101 address_padded.append(&mut Vec::from(address.0 .0));
102 address_padded
103 }
104 ResultField::TxValue(value) => value.to_be_bytes_vec(),
105 ResultField::TxAccessList(_list) => {
106 vec![]
108 }
109 ResultField::TxV(v) => v.to_be_bytes_vec(),
110 ResultField::TxR(r) => Vec::from(r),
111 ResultField::TxS(s) => Vec::from(s),
112 ResultField::RxLogBlooms(blooms) => blooms.clone(),
113 ResultField::EventTopic(topic) => Vec::from(topic),
114 ResultField::EventDataField(field) => Vec::from(field),
115 ResultField::FunctionSignifier(signifier) => Vec::from(signifier),
116 ResultField::CallDataField(field) => Vec::from(field),
117 };
118 let segment_bytes = &abi[offset..offset + size];
120 assert_eq!(
123 &expected_padded, segment_bytes,
124 "Expected and actual didn't match. Field num: {}, Expected: {:?}, Actual: {:?}",
125 field_number, expected_padded, segment_bytes
126 );
127 }
128}
129
130pub fn get_vrs(tx: &Transaction) -> (U256, [u8; 32], [u8; 32]) {
131 let signed_tx = tx
132 .inner
133 .as_legacy()
134 .expect("Already checked that tx is legacy");
135 let signature = signed_tx.signature();
136 let chain_id = tx.chain_id();
137 let v = compute_v(signature, chain_id);
138 let r: [u8; 32] = signature.r().to_be_bytes::<32>()[0..32].try_into().unwrap();
139 let s: [u8; 32] = signature.s().to_be_bytes::<32>()[0..32].try_into().unwrap();
140 (v, r, s)
141}
142
143pub fn get_y_parity(tx: &Transaction) -> u8 {
144 let signature = tx
145 .inner
146 .signature();
147 compute_y_parity(signature)
148}
149
150pub struct TestAbiProvider();
151
152#[async_trait]
153impl AbiProvider for TestAbiProvider {
154 async fn get_abi(&self, _contract_address: String) -> Result<String, QueryBuilderError> {
155 let json_str = r#"[{"constant":false,"inputs":[{"name":"tokenHolders","type":"address[]"},{"name":"amounts","type":"uint256[]"}],"name":"recordSales730Days","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"VestingStartDate","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"value","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"tokenHolder","type":"address"}],"name":"vestedBalanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"amount","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"tokenHolder","type":"address"}],"name":"purchasedBalanceOf365Days","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"value","type":"uint256"},{"name":"sighash","type":"string"}],"name":"exchange","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"tokenHolder","type":"address"}],"name":"vestedBalanceOf183Days","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"tokenHolders","type":"address[]"},{"name":"amounts","type":"uint256[]"}],"name":"recordSales1095Days","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"tokenHolder","type":"address"}],"name":"vestedBalanceOf365Days","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"value","type":"uint256"}],"name":"burn","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"tokenHolder","type":"address"}],"name":"purchasedBalanceOf2190Days","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"tokenHolders","type":"address[]"},{"name":"amounts","type":"uint256[]"}],"name":"recordSales183Days","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"tokenHolder","type":"address"},{"name":"numCoins","type":"uint256"}],"name":"recordSale365Days","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"tokenHolder","type":"address"}],"name":"vestedBalanceOf730Days","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"tokenHolder","type":"address"}],"name":"purchasedBalanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"finalizeSales","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"from","type":"address"},{"name":"value","type":"uint256"}],"name":"burnFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"tokenHolder","type":"address"}],"name":"vestedBalanceOf2190Days","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"tokenHolder","type":"address"}],"name":"purchasedBalanceOf730Days","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"tokenHolder","type":"address"},{"name":"numCoins","type":"uint256"}],"name":"recordSale183Days","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"creditcoinSalesLimit","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"tokenHolder","type":"address"}],"name":"vestedBalanceOf1095Days","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"creditcoinLimitInFrac","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"tokenHolder","type":"address"},{"name":"numCoins","type":"uint256"}],"name":"recordSale2190Days","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"tokenHolder","type":"address"},{"name":"numCoins","type":"uint256"}],"name":"recordSale730Days","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"tokenHolder","type":"address"}],"name":"purchasedBalanceOf1095Days","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"startVesting","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"tokenHolders","type":"address[]"},{"name":"amounts","type":"uint256[]"}],"name":"recordSales2190Days","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"tokenHolder","type":"address"}],"name":"purchasedBalanceOf183Days","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"IsSalesFinalized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"tokenHolders","type":"address[]"},{"name":"amounts","type":"uint256[]"}],"name":"recordSales365Days","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"tokenHolder","type":"address"},{"name":"numCoins","type":"uint256"}],"name":"recordSale1095Days","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"creditcoinFoundation","type":"address"},{"name":"devCost","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":true,"name":"sighash","type":"string"}],"name":"Exchange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Burnt","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"}]"#;
157
158 Ok(json_str.into())
159 }
160}