kabu_execution_multicaller/pool_opcodes_encoder/
curve.rs

1use alloy_primitives::{Address, U256};
2use eyre::{eyre, Result};
3use lazy_static::lazy_static;
4use std::collections::HashMap;
5use tracing::trace;
6
7use crate::opcodes_helpers::OpcodesHelpers;
8use crate::pool_abi_encoder::ProtocolAbiSwapEncoderTrait;
9use crate::pool_opcodes_encoder::swap_opcodes_encoders::MulticallerOpcodesPayload;
10use crate::pool_opcodes_encoder::SwapOpcodesEncoderTrait;
11use kabu_defi_abi::AbiEncoderHelper;
12use kabu_defi_address_book::TokenAddressEth;
13use kabu_types_blockchain::{MulticallerCall, MulticallerCalls};
14use kabu_types_entities::Pool;
15use kabu_types_entities::{PreswapRequirement, SwapAmountType};
16
17pub struct CurveSwapOpcodesEncoder;
18
19lazy_static! {
20    static ref NEED_BALANCE_MAP : HashMap<Address, bool> = {
21        let mut hm = HashMap::new();
22        hm.insert("0xD51a44d3FaE010294C616388b506AcdA1bfAAE46".parse::<Address>().unwrap(), true);
23        hm.insert("0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7".parse::<Address>().unwrap(), true);
24        hm.insert("0xA5407eAE9Ba41422680e2e00537571bcC53efBfD".parse::<Address>().unwrap(), true); // sUSD
25        hm
26    };
27}
28
29impl CurveSwapOpcodesEncoder {
30    fn need_balance(address: Address) -> bool {
31        *NEED_BALANCE_MAP.get(&address).unwrap_or(&false)
32    }
33}
34
35impl SwapOpcodesEncoderTrait for CurveSwapOpcodesEncoder {
36    #[allow(clippy::too_many_arguments)]
37    fn encode_swap_in_amount_provided(
38        &self,
39        swap_opcodes: &mut MulticallerCalls,
40        abi_encoder: &dyn ProtocolAbiSwapEncoderTrait,
41        token_from_address: Address,
42        token_to_address: Address,
43        amount_in: SwapAmountType,
44        cur_pool: &dyn Pool,
45        next_pool: Option<&dyn Pool>,
46        payload: MulticallerOpcodesPayload,
47        multicaller: Address,
48    ) -> Result<()> {
49        //let pool_encoder = abi_encoder.cur_pool.get_encoder().ok_or_eyre("NO_POOL_ENCODER")?;
50        let pool_id = cur_pool.get_address();
51        let pool_address = match pool_id {
52            kabu_types_entities::PoolId::Address(addr) => addr,
53            kabu_types_entities::PoolId::B256(_) => {
54                return Err(eyre!("B256 pool ID variant is not supported for Curve pools"));
55            }
56        };
57
58        let in_native = if cur_pool.is_native() { TokenAddressEth::is_weth(&token_from_address) } else { false };
59        let out_native = if cur_pool.is_native() { TokenAddressEth::is_weth(&token_to_address) } else { false };
60
61        trace!(
62            "curve swap for pool={:?} native={} amount={:?} from {} to {}",
63            cur_pool.get_address(),
64            cur_pool.is_native(),
65            amount_in,
66            token_from_address,
67            token_to_address
68        );
69
70        let mut opcodes: Vec<(MulticallerCall, u32, usize)> = Vec::new();
71
72        if in_native {
73            // Swap opcode
74            let mut swap_opcode = MulticallerCall::new_call_with_value(
75                pool_address,
76                &abi_encoder.encode_swap_in_amount_provided(
77                    cur_pool,
78                    token_from_address,
79                    token_to_address,
80                    amount_in.unwrap_or_default(),
81                    multicaller,
82                    payload.encode()?,
83                )?,
84                amount_in.unwrap_or_default(),
85            );
86
87            if !Self::need_balance(pool_address) {
88                swap_opcode.set_return_stack(true, 0, 0x0, 0x20);
89            }
90
91            // Withdraw WETH
92            opcodes.push((
93                MulticallerCall::new_call(token_from_address, &AbiEncoderHelper::encode_weth_withdraw(amount_in.unwrap_or_default())),
94                0x4,
95                0x20,
96            ));
97            opcodes.push((swap_opcode, abi_encoder.swap_in_amount_offset(cur_pool, token_from_address, token_to_address).unwrap(), 0x20));
98        } else {
99            //Approve
100            opcodes.push((
101                MulticallerCall::new_call(
102                    token_from_address,
103                    &AbiEncoderHelper::encode_erc20_approve(pool_address, amount_in.unwrap_or_default()),
104                ),
105                0x24,
106                0x20,
107            ));
108
109            // SWAP
110            let mut swap_opcode = MulticallerCall::new_call(
111                pool_address,
112                &abi_encoder.encode_swap_in_amount_provided(
113                    cur_pool,
114                    token_from_address,
115                    token_to_address,
116                    amount_in.unwrap_or_default(),
117                    multicaller,
118                    payload.encode()?,
119                )?,
120            );
121
122            if !Self::need_balance(pool_address) {
123                swap_opcode.set_return_stack(true, 0, 0x0, 0x20);
124            }
125            opcodes.push((swap_opcode, abi_encoder.swap_in_amount_offset(cur_pool, token_from_address, token_to_address).unwrap(), 0x20));
126        }
127
128        swap_opcodes.merge(OpcodesHelpers::build_multiple_stack(amount_in, opcodes, Some(token_from_address))?);
129
130        if out_native {
131            let mut weth_deposit_opcode =
132                MulticallerCall::new_call_with_value(token_to_address, &AbiEncoderHelper::encode_weth_deposit(), U256::ZERO);
133            weth_deposit_opcode.set_call_stack(true, 0, 0x0, 0x0);
134            swap_opcodes.add(weth_deposit_opcode);
135        }
136
137        if let Some(next_pool) = next_pool {
138            if Self::need_balance(pool_address) {
139                let mut balance_opcode =
140                    MulticallerCall::new_static_call(token_to_address, &AbiEncoderHelper::encode_erc20_balance_of(multicaller));
141                balance_opcode.set_return_stack(true, 0, 0x0, 0x20);
142                swap_opcodes.add(balance_opcode);
143            }
144
145            if let PreswapRequirement::Transfer(addr) = next_pool.preswap_requirement() {
146                trace!("transfer token={:?}, to={:?}, amount=stack_rel_0", token_to_address, addr);
147
148                let mut transfer_opcode =
149                    MulticallerCall::new_call(token_to_address, &AbiEncoderHelper::encode_erc20_transfer(addr, U256::ZERO));
150                transfer_opcode.set_call_stack(true, 0, 0x24, 0x20);
151                swap_opcodes.add(transfer_opcode);
152            }
153        }
154        Ok(())
155    }
156
157    fn encode_swap_out_amount_provided(
158        &self,
159        _swap_opcodes: &mut MulticallerCalls,
160        _abi_encoder: &dyn ProtocolAbiSwapEncoderTrait,
161        _token_from_address: Address,
162        _token_to_address: Address,
163        _amount_out: SwapAmountType,
164        _cur_pool: &dyn Pool,
165        _next_pool: Option<&dyn Pool>,
166        _payload: MulticallerOpcodesPayload,
167        _multicaller_address: Address,
168    ) -> Result<()> {
169        Err(eyre!("NOT_IMPLEMENTED"))
170    }
171}