use std::sync::Arc;
use ethers::{
abi::{Token, parse_abi},
types::{Bytes, U256, Address, TransactionRequest, H256, H256 as Bytes32},
signers::Signer,
providers::{Middleware, Provider, Http},
contract::Contract,
};
use serde::{Deserialize, Serialize};
use crate::{
client::Client,
error::{Result, InvalidInputsError, Transaction},
utils::encode_with_signature,
};
use ethers_middleware::SignerMiddleware;
#[derive(Debug)]
pub struct CalldataQueue {
pub client: Arc<Client>,
pub manager_address: Address,
pub chain_id: u64,
pub root: String,
pub calls: Vec<Call>,
}
#[derive(Debug, Serialize, Deserialize)]
struct Call {
target_address: Address,
data: Bytes,
value: U256,
args: Vec<Token>, function_signature: String,
proof_data: Vec<Bytes>,
decoder_and_sanitizer: Address,
}
impl CalldataQueue {
pub async fn new(
chain_id: u64,
strategist_address: String,
rpc_url: String,
symbol: String,
client: Arc<Client>,
) -> Result<Self> {
let network_string = chain_id.to_string();
let manager_address = client
.address_book
.get(&network_string)
.and_then(|network| network.get("nucleus"))
.and_then(|nucleus| nucleus.get(&symbol))
.and_then(|symbol_data| symbol_data.get("manager"))
.and_then(|addr| addr.as_str())
.ok_or_else(|| {
InvalidInputsError(format!(
"Could not find manager address for network '{}' and symbol '{}'. Please check the network and symbol are valid.",
network_string, symbol
))
})?;
let manager_address = manager_address.parse()
.map_err(|_| InvalidInputsError("Invalid manager address format".to_string()))?;
let provider = Arc::new(
Provider::<Http>::try_from(&rpc_url)
.map_err(|_| InvalidInputsError(format!("Invalid RPC URL: {}", rpc_url)))?
);
provider.get_block_number().await
.map_err(|_| InvalidInputsError(format!("Could not connect to RPC URL '{}'. Please check the RPC URL is valid and accessible.", rpc_url)))?;
let strategist = strategist_address.parse::<Address>()
.map_err(|_| InvalidInputsError("Invalid strategist address format".to_string()))?;
let abi = parse_abi(&[
"function manageRoot(address strategist) view returns (bytes32)"
]).expect("Failed to parse ABI");
let contract = Contract::new(
manager_address,
abi,
Arc::clone(&provider),
);
let root: Bytes32 = contract
.method::<_, Bytes32>("manageRoot", strategist)
.map_err(|e| InvalidInputsError(format!("Failed to create manageRoot method call: {}", e)))?
.call()
.await
.map_err(|e| InvalidInputsError(format!("Failed to call manageRoot: {}", e)))?;
let root = format!("{:?}", root);
if root == "0x0000000000000000000000000000000000000000000000000000000000000000" {
return Err(InvalidInputsError(format!(
"Could not find root for strategist '{}'. Please check the strategist address is valid.",
strategist_address
)).into());
}
Ok(Self {
client,
manager_address,
chain_id,
root,
calls: Vec::new(),
})
}
pub fn add_call(
&mut self,
target_address: Address,
function_signature: String,
args: Vec<Token>,
value: U256,
) -> Result<()> {
let data = encode_with_signature(&function_signature, &args)
.map_err(|e| InvalidInputsError(format!("Failed to encode function data: {}", e)))?;
self.calls.push(Call {
target_address,
data: data.into(),
value,
args,
function_signature,
proof_data: vec![],
decoder_and_sanitizer: Address::zero(),
});
Ok(())
}
pub async fn get_calldata(&mut self) -> Result<Bytes> {
let proof_requests: Vec<ProofRequest> = self.calls.iter()
.filter(|call| call.proof_data.is_empty())
.map(|call| ProofRequest {
target: format!("{:?}", call.target_address),
calldata: format!("0x{}", hex::encode(&call.data)),
value: call.value.as_u64(),
})
.collect();
if !proof_requests.is_empty() {
let batch_response = self._get_batch_proofs_and_decoders(proof_requests).await?;
let mut proof_index = 0;
for call in &mut self.calls {
if call.proof_data.is_empty() {
call.proof_data = batch_response.proofs[proof_index].clone();
call.decoder_and_sanitizer = batch_response.decoder_and_sanitizer_addresses[proof_index];
proof_index += 1;
}
}
}
let mut proofs = Vec::new();
let mut decoders = Vec::new();
let mut targets = Vec::new();
let mut data = Vec::new();
let mut values = Vec::new();
for call in &self.calls {
proofs.push(Token::Array(
call.proof_data.iter()
.map(|p| Token::FixedBytes(p.to_vec()))
.collect()
));
decoders.push(Token::Address(call.decoder_and_sanitizer));
targets.push(Token::Address(call.target_address));
data.push(Token::Bytes(call.data.to_vec()));
values.push(Token::Uint(call.value));
}
let function = "manageVaultWithMerkleVerification(bytes32[][],address[],address[],bytes[],uint256[])";
let args = vec![
Token::Array(proofs),
Token::Array(decoders),
Token::Array(targets),
Token::Array(data),
Token::Array(values),
];
let encoded = encode_with_signature(function, &args)
.map_err(|e| InvalidInputsError(format!("Failed to encode manager function: {}", e)))?;
Ok(encoded.into())
}
pub async fn execute<M: Middleware, S: Signer>(
&mut self,
signer: &SignerMiddleware<M, S>
) -> Result<H256> {
if self.calls.is_empty() {
return Err(InvalidInputsError("No calls to execute".to_string()).into());
}
let calldata = self.get_calldata().await?;
let from_address = signer.address();
let tx = TransactionRequest {
from: Some(from_address),
to: Some(self.manager_address.into()),
data: Some(calldata),
value: Some(U256::zero()),
..Default::default()
};
let pending_tx = signer.send_transaction(tx, None)
.await
.map_err(|e| Transaction(format!("Transaction failed: {}", e)))?;
Ok(pending_tx.tx_hash())
}
async fn _get_batch_proofs_and_decoders(
&self,
leaves: Vec<ProofRequest>
) -> Result<BatchProofResponse> {
let request = BatchProofRequest {
chain: self.chain_id,
calls: leaves,
};
let json_value = serde_json::to_value(&request)
.map_err(|e| InvalidInputsError(format!("Failed to serialize batch proof request: {}", e)))?;
let response: BatchProofResponse = self.client
.post(&format!("multiproofs/{}", self.root), Some(&json_value))
.await?;
if response.proofs.len() != response.decoder_and_sanitizer_addresses.len() {
return Err(InvalidInputsError(
"Mismatched lengths in proof response arrays".to_string()
).into());
}
Ok(response)
}
}
#[derive(Serialize)]
struct BatchProofRequest {
chain: u64, calls: Vec<ProofRequest>,
}
#[derive(Deserialize, Serialize)]
struct BatchProofResponse {
proofs: Vec<Vec<Bytes>>,
#[serde(rename = "decoderAndSanitizerAddress")]
decoder_and_sanitizer_addresses: Vec<Address>,
}
#[derive(Serialize)]
struct ProofRequest {
target: String,
calldata: String,
value: u64,
}
#[derive(Deserialize)]
struct ProofResponse {
proof: Vec<Bytes>,
decoder_and_sanitizer: Address,
}