nucleus_sdk/
calldata_queue.rs

1use std::sync::Arc;
2use ethers::{
3    abi::{Token, parse_abi},
4    types::{Bytes, U256, Address, TransactionRequest, H256, H256 as Bytes32},
5    signers::Signer,
6    providers::{Middleware, Provider, Http},
7    contract::Contract,
8};
9use serde::{Deserialize, Serialize};
10use crate::{
11    client::Client,
12    error::{Result, InvalidInputsError, Transaction},
13    utils::encode_with_signature,
14};
15use ethers_middleware::SignerMiddleware;
16
17#[derive(Debug)]
18pub struct CalldataQueue {
19    pub client: Arc<Client>,
20    pub manager_address: Address,
21    pub chain_id: u64,
22    pub root: String,
23    pub calls: Vec<Call>,
24}
25
26#[derive(Debug, Serialize, Deserialize)]
27struct Call {
28    target_address: Address,
29    data: Bytes,
30    value: U256,
31    args: Vec<Token>,  // Using ethers Token for type-safe args
32    function_signature: String,
33    proof_data: Vec<Bytes>,
34    decoder_and_sanitizer: Address,
35}
36
37impl CalldataQueue {
38    pub async fn new(
39        chain_id: u64,
40        strategist_address: String,
41        rpc_url: String,
42        symbol: String,
43        client: Arc<Client>,
44    ) -> Result<Self> {
45        // Convert chain_id to string for address book lookup
46        let network_string = chain_id.to_string();
47
48        // get the manager address from the address book using chain_id
49        let manager_address = client
50            .address_book
51            .get(&network_string)
52            .and_then(|network| network.get("nucleus"))
53            .and_then(|nucleus| nucleus.get(&symbol))
54            .and_then(|symbol_data| symbol_data.get("manager"))
55            .and_then(|addr| addr.as_str())
56            .ok_or_else(|| {
57                InvalidInputsError(format!(
58                    "Could not find manager address for network '{}' and symbol '{}'. Please check the network and symbol are valid.",
59                    network_string, symbol
60                ))
61            })?;
62
63        // parse the manager address
64        let manager_address = manager_address.parse()
65            .map_err(|_| InvalidInputsError("Invalid manager address format".to_string()))?;
66
67        // Create a provider to interact with the blockchain
68        let provider = Arc::new(
69            Provider::<Http>::try_from(&rpc_url)
70                .map_err(|_| InvalidInputsError(format!("Invalid RPC URL: {}", rpc_url)))?
71        );
72
73        // Test the connection
74        provider.get_block_number().await
75            .map_err(|_| InvalidInputsError(format!("Could not connect to RPC URL '{}'. Please check the RPC URL is valid and accessible.", rpc_url)))?;
76
77        // Parse the strategist address
78        let strategist = strategist_address.parse::<Address>()
79            .map_err(|_| InvalidInputsError("Invalid strategist address format".to_string()))?;
80
81        // Create contract instance
82        let abi = parse_abi(&[
83            "function manageRoot(address strategist) view returns (bytes32)"
84        ]).expect("Failed to parse ABI");
85
86        let contract = Contract::new(
87            manager_address,
88            abi,
89            Arc::clone(&provider),
90        );
91
92        // Call manageRoot function
93        let root: Bytes32 = contract
94            .method::<_, Bytes32>("manageRoot", strategist)
95            .map_err(|e| InvalidInputsError(format!("Failed to create manageRoot method call: {}", e)))?
96            .call()
97            .await
98            .map_err(|e| InvalidInputsError(format!("Failed to call manageRoot: {}", e)))?;
99
100        // Convert root to hex string
101        let root = format!("{:?}", root);
102
103        // Check if root is zero
104        if root == "0x0000000000000000000000000000000000000000000000000000000000000000" {
105            return Err(InvalidInputsError(format!(
106                "Could not find root for strategist '{}'. Please check the strategist address is valid.",
107                strategist_address
108            )).into());
109        }
110
111
112        Ok(Self {
113            client,
114            manager_address,
115            chain_id,
116            root,
117            calls: Vec::new(),
118        })
119    }
120
121    // add a call to the manager call
122    pub fn add_call(
123        &mut self,
124        target_address: Address,
125        function_signature: String,
126        args: Vec<Token>,
127        value: U256,
128    ) -> Result<()> {
129        // encode the function call
130        let data = encode_with_signature(&function_signature, &args)
131            .map_err(|e| InvalidInputsError(format!("Failed to encode function data: {}", e)))?;
132
133        // push the call to the calls vector
134        self.calls.push(Call {
135            target_address,
136            data: data.into(),
137            value,
138            args,
139            function_signature,
140            proof_data: vec![],
141            decoder_and_sanitizer: Address::zero(),
142        });
143
144        Ok(())
145    }
146
147    pub async fn get_calldata(&mut self) -> Result<Bytes> {
148        // Collect all calls that need proof data
149        let proof_requests: Vec<ProofRequest> = self.calls.iter()
150            .filter(|call| call.proof_data.is_empty())
151            .map(|call| ProofRequest {
152                target: format!("{:?}", call.target_address),
153                calldata: format!("0x{}", hex::encode(&call.data)),
154                value: call.value.as_u64(),
155            })
156            .collect();
157
158        // If we have any calls needing proofs, get them in batch
159        if !proof_requests.is_empty() {
160            let batch_response = self._get_batch_proofs_and_decoders(proof_requests).await?;
161            
162            // Update the calls with their proof data
163            let mut proof_index = 0;
164            for call in &mut self.calls {
165                if call.proof_data.is_empty() {
166                    call.proof_data = batch_response.proofs[proof_index].clone();
167                    call.decoder_and_sanitizer = batch_response.decoder_and_sanitizer_addresses[proof_index];
168                    proof_index += 1;
169                }
170            }
171        }
172
173        // Then build the vectors for the final call
174        let mut proofs = Vec::new();
175        let mut decoders = Vec::new();
176        let mut targets = Vec::new();
177        let mut data = Vec::new();
178        let mut values = Vec::new();
179
180        for call in &self.calls {
181            proofs.push(Token::Array(
182                call.proof_data.iter()
183                    .map(|p| Token::FixedBytes(p.to_vec()))
184                    .collect()
185            ));
186            decoders.push(Token::Address(call.decoder_and_sanitizer));
187            targets.push(Token::Address(call.target_address));
188            data.push(Token::Bytes(call.data.to_vec()));
189            values.push(Token::Uint(call.value));
190        }
191
192        // Create the manager function
193        let function = "manageVaultWithMerkleVerification(bytes32[][],address[],address[],bytes[],uint256[])";
194
195        let args = vec![
196            Token::Array(proofs),
197            Token::Array(decoders),
198            Token::Array(targets),
199            Token::Array(data),
200            Token::Array(values),
201        ];
202
203        let encoded = encode_with_signature(function, &args)
204            .map_err(|e| InvalidInputsError(format!("Failed to encode manager function: {}", e)))?;
205
206        Ok(encoded.into())
207    }
208
209    /// Execute the queued calls using the provided signer.
210    ///
211    /// The signer contains both the provider connection and the account information.
212    pub async fn execute<M: Middleware, S: Signer>(
213        &mut self, 
214        signer: &SignerMiddleware<M, S>
215    ) -> Result<H256> {
216        if self.calls.is_empty() {
217            return Err(InvalidInputsError("No calls to execute".to_string()).into());
218        }
219
220        let calldata = self.get_calldata().await?;
221        
222        // Get the sender's address from the signer
223        let from_address = signer.address();
224
225        let tx = TransactionRequest {
226            from: Some(from_address),
227            to: Some(self.manager_address.into()),
228            data: Some(calldata),
229            value: Some(U256::zero()),
230            ..Default::default()
231        };
232
233        // Send the transaction using the signer and map the error
234        let pending_tx = signer.send_transaction(tx, None)
235            .await
236            .map_err(|e| Transaction(format!("Transaction failed: {}", e)))?;
237        
238        // Return the transaction hash immediately (like the Python version)
239        Ok(pending_tx.tx_hash())
240    }
241
242    async fn _get_batch_proofs_and_decoders(
243        &self,
244        leaves: Vec<ProofRequest>
245    ) -> Result<BatchProofResponse> {
246        // Create the batch request with chain_id at the top level
247        let request = BatchProofRequest {
248            chain: self.chain_id,
249            calls: leaves,
250        };
251
252        // Convert request to JSON value
253        let json_value = serde_json::to_value(&request)
254            .map_err(|e| InvalidInputsError(format!("Failed to serialize batch proof request: {}", e)))?;
255        
256        // Make the API call
257        let response: BatchProofResponse = self.client
258            .post(&format!("multiproofs/{}", self.root), Some(&json_value))
259            .await?;
260
261        // Verify the response arrays are the same length
262        if response.proofs.len() != response.decoder_and_sanitizer_addresses.len() {
263            return Err(InvalidInputsError(
264                "Mismatched lengths in proof response arrays".to_string()
265            ).into());
266        }
267
268        Ok(response)
269    }
270}
271
272#[derive(Serialize)]
273struct BatchProofRequest {
274    chain: u64,  // Chain ID at the top level
275    calls: Vec<ProofRequest>,
276}
277
278#[derive(Deserialize, Serialize)]
279struct BatchProofResponse {
280    proofs: Vec<Vec<Bytes>>,
281    #[serde(rename = "decoderAndSanitizerAddress")]
282    decoder_and_sanitizer_addresses: Vec<Address>,
283}
284
285#[derive(Serialize)]
286struct ProofRequest {
287    target: String,
288    calldata: String,
289    value: u64,
290}
291
292#[derive(Deserialize)]
293struct ProofResponse {
294    proof: Vec<Bytes>,
295    decoder_and_sanitizer: Address,
296}