nautilus_blockchain/contracts/
base.rs1use std::sync::Arc;
17
18use alloy::{primitives::Address, sol, sol_types::SolCall};
19use nautilus_core::hex;
20use nautilus_model::defi::validation::validate_address;
21
22use crate::rpc::{error::BlockchainRpcClientError, http::BlockchainHttpRpcClient};
23
24sol! {
25 #[sol(rpc)]
26 contract Multicall3 {
27 struct Call {
28 address target;
29 bytes callData;
30 }
31
32 struct Call3 {
33 address target;
34 bool allowFailure;
35 bytes callData;
36 }
37
38 struct Result {
39 bool success;
40 bytes returnData;
41 }
42
43 function aggregate3(Call3[] calldata calls) external payable returns (Result[] memory returnData);
44 function tryAggregate(bool requireSuccess, Call[] calldata calls) external payable returns (Result[] memory returnData);
45 }
46}
47
48pub const MULTICALL3_ADDRESS: &str = "0xcA11bde05977b3631167028862bE2a173976CA11";
50
51#[derive(Debug)]
56pub struct BaseContract {
57 client: Arc<BlockchainHttpRpcClient>,
59 multicall_address: Address,
61}
62
63#[derive(Debug)]
65pub struct ContractCall {
66 pub target: Address,
68 pub allow_failure: bool,
70 pub call_data: Vec<u8>,
72}
73
74impl BaseContract {
75 #[must_use]
81 pub fn new(client: Arc<BlockchainHttpRpcClient>) -> Self {
82 let multicall_address =
83 validate_address(MULTICALL3_ADDRESS).expect("Invalid multicall address");
84
85 Self {
86 client,
87 multicall_address,
88 }
89 }
90
91 #[must_use]
93 pub const fn client(&self) -> &Arc<BlockchainHttpRpcClient> {
94 &self.client
95 }
96
97 pub async fn execute_call(
103 &self,
104 contract_address: &Address,
105 call_data: &[u8],
106 block: Option<u64>,
107 ) -> Result<Vec<u8>, BlockchainRpcClientError> {
108 let rpc_request =
109 self.client
110 .construct_eth_call(&contract_address.to_string(), call_data, block);
111
112 let encoded_response = self
113 .client
114 .execute_rpc_call::<String>(rpc_request)
115 .await
116 .map_err(|e| BlockchainRpcClientError::ClientError(format!("RPC call failed: {e}")))?;
117
118 decode_hex_response(&encoded_response)
119 }
120
121 pub async fn execute_multicall(
127 &self,
128 calls: Vec<ContractCall>,
129 block: Option<u64>,
130 ) -> Result<Vec<Multicall3::Result>, BlockchainRpcClientError> {
131 let multicall_calls: Vec<Multicall3::Call> = calls
133 .into_iter()
134 .map(|call| Multicall3::Call {
135 target: call.target,
136 callData: call.call_data.into(),
137 })
138 .collect();
139
140 let multicall_data = Multicall3::tryAggregateCall {
141 requireSuccess: false,
142 calls: multicall_calls,
143 }
144 .abi_encode();
145 let rpc_request = self.client.construct_eth_call(
146 &self.multicall_address.to_string(),
147 multicall_data.as_slice(),
148 block,
149 );
150
151 let encoded_response = self
152 .client
153 .execute_rpc_call::<String>(rpc_request)
154 .await
155 .map_err(|e| BlockchainRpcClientError::ClientError(format!("Multicall failed: {e}")))?;
156
157 let bytes = decode_hex_response(&encoded_response)?;
158 let results = Multicall3::tryAggregateCall::abi_decode_returns(&bytes).map_err(|e| {
159 BlockchainRpcClientError::AbiDecodingError(format!(
160 "Failed to decode multicall results: {e}"
161 ))
162 })?;
163
164 Ok(results)
165 }
166}
167
168pub fn decode_hex_response(encoded_response: &str) -> Result<Vec<u8>, BlockchainRpcClientError> {
174 let encoded_str = encoded_response
176 .strip_prefix("0x")
177 .unwrap_or(encoded_response);
178 hex::decode(encoded_str).map_err(|e| {
179 BlockchainRpcClientError::AbiDecodingError(format!("Error decoding hex response: {e}"))
180 })
181}