kabu_execution_multicaller/pool_opcodes_encoder/
curve.rs

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