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