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)]
31 runtime: Option<Arc<Runtime>>,
32}
33impl ProtocolApprovalsManager {
34 pub fn new() -> Result<Self, EncodingError> {
35 let (handle, runtime) = get_runtime()?;
36 let client = block_in_place(|| handle.block_on(get_client()))?;
37 Ok(Self { client, runtime_handle: handle, runtime })
38 }
39
40 pub fn approval_needed(
43 &self,
44 token: Address,
45 owner_address: Address,
46 spender_address: Address,
47 ) -> Result<bool, EncodingError> {
48 let args = (owner_address, spender_address);
49 let data = encode_input("allowance(address,address)", args.abi_encode());
50 let tx = TransactionRequest {
51 to: Some(TxKind::from(token)),
52 input: TransactionInput { input: Some(Bytes::from(data)), data: None },
53 ..Default::default()
54 };
55
56 let output = block_in_place(|| {
57 self.runtime_handle
58 .block_on(async { self.client.call(tx).await })
59 });
60 match output {
61 Ok(response) => {
62 let allowance: U256 = U256::abi_decode(&response).map_err(|_| {
63 EncodingError::FatalError("Failed to decode response for allowance".to_string())
64 })?;
65
66 Ok(allowance.is_zero())
67 }
68 Err(err) => Err(EncodingError::RecoverableError(format!(
69 "Allowance call failed with error: {err}"
70 ))),
71 }
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use std::str::FromStr;
78
79 use rstest::rstest;
80
81 use super::*;
82 #[rstest]
83 #[case::approval_not_needed(
84 "0xba12222222228d8ba445958a75a0704d566bf2c8",
85 "0x2c6a3cd97c6283b95ac8c5a4459ebb0d5fd404f4",
86 false
87 )]
88 #[case::approval_needed(
89 "0x2c6a3cd97c6283b95ac8c5a4459ebb0d5fd404f4",
90 "0xba12222222228d8ba445958a75a0704d566bf2c8",
91 true
92 )]
93 fn test_approval_needed(#[case] spender: &str, #[case] owner: &str, #[case] expected: bool) {
94 let manager = ProtocolApprovalsManager::new().unwrap();
95
96 let token = Address::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
97 let spender = Address::from_str(spender).unwrap();
98 let owner = Address::from_str(owner).unwrap();
99
100 let result = manager
101 .approval_needed(token, owner, spender)
102 .unwrap();
103 assert_eq!(result, expected);
104 }
105}