diff --git a/Cargo.lock b/Cargo.lock index ccf00d13fb..61c1f901d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4129,6 +4129,7 @@ dependencies = [ "alloy-eips", "alloy-primitives", "alloy-rpc-types", + "chain", "derive_more 1.0.0", "num", "number", diff --git a/crates/driver/src/domain/competition/solution/encoding.rs b/crates/driver/src/domain/competition/solution/encoding.rs index ea55b103af..7d4fe975c7 100644 --- a/crates/driver/src/domain/competition/solution/encoding.rs +++ b/crates/driver/src/domain/competition/solution/encoding.rs @@ -43,6 +43,7 @@ pub fn tx( approvals: impl Iterator, internalization: settlement::Internalization, solver_native_token: ManageNativeToken, + chain: chain::Chain, ) -> Result { let mut tokens = Vec::with_capacity(solution.prices.len() + (solution.trades().len() * 2)); let mut clearing_prices = @@ -170,7 +171,10 @@ pub fn tx( // Encode allowances for approval in approvals { - interactions.push(approve(&approval.0)) + if approval.requires_reset(chain) { + interactions.push(approve(&approval.revoke().0)); + } + interactions.push(approve(&approval.0)); } // Encode interactions diff --git a/crates/driver/src/domain/competition/solution/settlement.rs b/crates/driver/src/domain/competition/solution/settlement.rs index 907713046b..a469cfe9f8 100644 --- a/crates/driver/src/domain/competition/solution/settlement.rs +++ b/crates/driver/src/domain/competition/solution/settlement.rs @@ -114,6 +114,7 @@ impl Settlement { solution.approvals(eth, Internalization::Enable).await?, Internalization::Enable, solver_native_token, + eth.chain(), )?, uninternalized: encoding::tx( auction, @@ -122,6 +123,7 @@ impl Settlement { solution.approvals(eth, Internalization::Disable).await?, Internalization::Disable, solver_native_token, + eth.chain(), )?, may_revert: solution.revertable(), }; diff --git a/crates/eth-domain-types/Cargo.toml b/crates/eth-domain-types/Cargo.toml index 9b5e27952d..466837e34e 100644 --- a/crates/eth-domain-types/Cargo.toml +++ b/crates/eth-domain-types/Cargo.toml @@ -9,6 +9,7 @@ license = "MIT OR Apache-2.0" alloy-eips = { workspace = true } alloy-primitives = { workspace = true } alloy-rpc-types = { workspace = true } +chain = { workspace = true } derive_more = { workspace = true } num = { workspace = true } number = { workspace = true } diff --git a/crates/eth-domain-types/src/allowance.rs b/crates/eth-domain-types/src/allowance.rs index 91222566df..17f1881c31 100644 --- a/crates/eth-domain-types/src/allowance.rs +++ b/crates/eth-domain-types/src/allowance.rs @@ -1,6 +1,7 @@ use { super::{Address, TokenAddress}, - alloy_primitives::U256, + alloy_primitives::{U256, address}, + chain::Chain, derive_more::{From, Into}, }; @@ -60,4 +61,25 @@ impl Approval { ..self.0 }) } + + /// Some tokens (e.g. USDT) revert when approving a non-zero value if the + /// current allowance is also non-zero. For these tokens the allowance must + /// be reset to 0 before setting a new value. + pub fn requires_reset(&self, chain: Chain) -> bool { + let tokens: &[Address] = match chain { + Chain::Mainnet => &[address!("dAC17F958D2ee523a2206206994597C13D831ec7")], // https://etherscan.io/token/0xdAC17F958D2ee523a2206206994597C13D831ec7 + + // Other implementations of USDT don't require you to reset the allowance first: + // - Bnb: https://bscscan.com/token/0x55d398326f99059ff775485246999027b3197955 + // - Base: https://basescan.org/token/0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2 + // - Arbitrum: https://arbiscan.io/token/0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9 + // - Polygon: https://polygonscan.com/token/0xc2132d05d31c914a87c6611c10748aeb04b58e8f + // - Avalanche: https://snowscan.xyz/token/0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7 + // - Linea: https://lineascan.build/token/0xa219439258ca9da29e9cc4ce5596924745e12b93 + // - Gnosis: https://gnosisscan.io/token/0x4ECaBa5870353805a9F068101A40E0f32ed605C6 + _ => &[], + }; + + tokens.contains(&self.0.token.0) + } }