tycho_execution/encoding/evm/approvals/
protocol_approvals_manager.rs1use std::sync::Arc;
2
3use alloy::{
4 primitives::{Address, Bytes, TxKind, U256},
5 providers::Provider,
6 rpc::types::{TransactionInput, TransactionRequest},
7 sol_types::SolValue,
8};
9use tokio::{
10 runtime::{Handle, Runtime},
11 task::block_in_place,
12};
13
14use crate::encoding::{
15 errors::EncodingError,
16 evm::{
17 encoding_utils::encode_input,
18 utils::{get_client, get_runtime, EVMProvider},
19 },
20};
21
22pub struct ProtocolApprovalsManager {
24 client: EVMProvider,
25 runtime_handle: Handle,
26 #[allow(dead_code)]
27 runtime: Option<Arc<Runtime>>,
28}
29impl ProtocolApprovalsManager {
30 pub fn new() -> Result<Self, EncodingError> {
31 let (handle, runtime) = get_runtime()?;
32 let client = block_in_place(|| handle.block_on(get_client()))?;
33 Ok(Self { client, runtime_handle: handle, runtime })
34 }
35
36 pub fn approval_needed(
39 &self,
40 token: Address,
41 owner_address: Address,
42 spender_address: Address,
43 ) -> Result<bool, EncodingError> {
44 let args = (owner_address, spender_address);
45 let data = encode_input("allowance(address,address)", args.abi_encode());
46 let tx = TransactionRequest {
47 to: Some(TxKind::from(token)),
48 input: TransactionInput { input: Some(Bytes::from(data)), data: None },
49 ..Default::default()
50 };
51
52 let output = block_in_place(|| {
53 self.runtime_handle
54 .block_on(async { self.client.call(tx).await })
55 });
56 match output {
57 Ok(response) => {
58 let allowance: U256 = U256::abi_decode(&response).map_err(|_| {
59 EncodingError::FatalError("Failed to decode response for allowance".to_string())
60 })?;
61
62 Ok(allowance.is_zero())
63 }
64 Err(err) => Err(EncodingError::RecoverableError(format!(
65 "Allowance call failed with error: {err}"
66 ))),
67 }
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use std::str::FromStr;
74
75 use rstest::rstest;
76
77 use super::*;
78 #[rstest]
79 #[case::approval_not_needed(
80 "0xba12222222228d8ba445958a75a0704d566bf2c8",
81 "0x2c6a3cd97c6283b95ac8c5a4459ebb0d5fd404f4",
82 false
83 )]
84 #[case::approval_needed(
85 "0x2c6a3cd97c6283b95ac8c5a4459ebb0d5fd404f4",
86 "0xba12222222228d8ba445958a75a0704d566bf2c8",
87 true
88 )]
89 fn test_approval_needed(#[case] spender: &str, #[case] owner: &str, #[case] expected: bool) {
90 let manager = ProtocolApprovalsManager::new().unwrap();
91
92 let token = Address::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
93 let spender = Address::from_str(spender).unwrap();
94 let owner = Address::from_str(owner).unwrap();
95
96 let result = manager
97 .approval_needed(token, owner, spender)
98 .unwrap();
99 assert_eq!(result, expected);
100 }
101}