kabu_execution_multicaller/pool_opcodes_encoder/
uniswap2.rs

1use crate::opcodes_helpers::OpcodesHelpers;
2use crate::pool_abi_encoder::ProtocolAbiSwapEncoderTrait;
3use crate::pool_opcodes_encoder::SwapOpcodesEncoderTrait;
4use crate::pool_opcodes_encoder::swap_opcodes_encoders::MulticallerOpcodesPayload;
5use alloy_primitives::{Address, Bytes, U256};
6use eyre::eyre;
7use kabu_defi_abi::AbiEncoderHelper;
8use kabu_types_blockchain::{MulticallerCall, MulticallerCalls};
9use kabu_types_market::{Pool, PoolId, PreswapRequirement};
10use kabu_types_swap::SwapAmountType;
11use tracing::{trace, warn};
12
13pub struct UniswapV2SwapOpcodesEncoder;
14
15impl SwapOpcodesEncoderTrait for UniswapV2SwapOpcodesEncoder {
16    fn encode_swap_in_amount_provided(
17        &self,
18        swap_opcodes: &mut MulticallerCalls,
19        abi_encoder: &dyn ProtocolAbiSwapEncoderTrait,
20        token_from_address: Address,
21        token_to_address: Address,
22        amount_in: SwapAmountType,
23        cur_pool: &dyn Pool,
24        next_pool: Option<&dyn Pool>,
25        _payload: MulticallerOpcodesPayload,
26        multicaller_address: Address,
27    ) -> eyre::Result<()> {
28        // Getting destination address
29        let swap_to = next_pool.and_then(|next_pool| next_pool.preswap_requirement().address()).unwrap_or(multicaller_address);
30
31        trace!(
32            "uniswap v2 get out amount for pool={:?}, amount={:?} from {} to {}",
33            cur_pool.get_address(),
34            amount_in,
35            token_from_address,
36            token_to_address
37        );
38
39        // calculating out amount for in amount provided
40        let pool_address = match cur_pool.get_address() {
41            PoolId::Address(addr) => addr,
42            PoolId::B256(_) => return Err(eyre!("B256 pool ID not supported for UniswapV2")),
43        };
44        let get_out_amount_opcode = MulticallerCall::new_internal_call(&AbiEncoderHelper::encode_multicaller_uni2_get_out_amount(
45            token_from_address,
46            token_to_address,
47            pool_address,
48            amount_in.unwrap_or_default(),
49            cur_pool.get_fee(),
50        ));
51
52        // setting argument from stack if it is required
53        swap_opcodes.merge(OpcodesHelpers::build_call_stack(amount_in, get_out_amount_opcode, 0x24, 0x20, Some(token_from_address))?);
54
55        // abi encode and add uniswap swap opcode
56        let mut swap_opcode = MulticallerCall::new_call(
57            pool_address,
58            &abi_encoder.encode_swap_out_amount_provided(
59                cur_pool,
60                token_from_address,
61                token_to_address,
62                U256::from(1),
63                swap_to,
64                Bytes::new(),
65            )?,
66        );
67
68        // setting stack swap argument based on calculated out amount
69        swap_opcode.set_call_stack(
70            true,
71            0,
72            abi_encoder.swap_out_amount_offset(cur_pool, token_from_address, token_to_address).unwrap(),
73            0x20,
74        );
75
76        swap_opcodes.add(swap_opcode);
77
78        Ok(())
79    }
80
81    fn encode_swap_out_amount_provided(
82        &self,
83        _swap_opcodes: &mut MulticallerCalls,
84        _abi_encoder: &dyn ProtocolAbiSwapEncoderTrait,
85        _token_from_address: Address,
86        _token_to_address: Address,
87        _amount_out: SwapAmountType,
88        _cur_pool: &dyn Pool,
89        _next_pool: Option<&dyn Pool>,
90        _payload: MulticallerOpcodesPayload,
91        _multicaller_address: Address,
92    ) -> eyre::Result<()> {
93        Err(eyre!("NOT_IMPLEMENTED"))
94    }
95
96    fn encode_flash_swap_in_amount_provided(
97        &self,
98        swap_opcodes: &mut MulticallerCalls,
99        abi_encoder: &dyn ProtocolAbiSwapEncoderTrait,
100        token_from_address: Address,
101        token_to_address: Address,
102        amount_in: SwapAmountType,
103        flash_pool: &dyn Pool,
104        prev_pool: Option<&dyn Pool>,
105        payload: MulticallerOpcodesPayload,
106        multicaller_address: Address,
107    ) -> eyre::Result<()> {
108        // Extract flash pool address early
109        let flash_pool_address = match flash_pool.get_address() {
110            PoolId::Address(addr) => addr,
111            PoolId::B256(_) => return Err(eyre!("B256 pool ID not supported for UniswapV2")),
112        };
113
114        let payload = if let MulticallerOpcodesPayload::Opcodes(inside_opcodes) = &payload {
115            let mut inside_opcodes = inside_opcodes.clone();
116
117            match amount_in {
118                SwapAmountType::Set(amount) => {
119                    // first swap, need to return token_from to this pool, otherwise tokens should be already on the contract
120                    trace!("uniswap v2 transfer token={:?}, to={:?}, amount={}", token_from_address, flash_pool.get_address(), amount);
121
122                    inside_opcodes.add(MulticallerCall::new_call(
123                        token_from_address,
124                        &AbiEncoderHelper::encode_erc20_transfer(flash_pool_address, amount),
125                    ));
126                }
127                _ => {
128                    warn!("InAmountTypeNotSet");
129                }
130            }
131
132            // if there is a prev_pool transfer funds in case it is uniswap2.
133            if let Some(prev_pool) = prev_pool
134                && let PreswapRequirement::Transfer(swap_to) = prev_pool.preswap_requirement()
135            {
136                trace!("uniswap v2 transfer token_to_address={:?}, funds_to={:?} amount=stack_norel_0", token_to_address, swap_to);
137                let mut transfer_opcode =
138                    MulticallerCall::new_call(token_to_address, &AbiEncoderHelper::encode_erc20_transfer(swap_to, U256::ZERO));
139                transfer_opcode.set_call_stack(false, 0, 0x24, 0x20);
140                inside_opcodes.insert(transfer_opcode);
141            }
142            MulticallerOpcodesPayload::Opcodes(inside_opcodes)
143        } else {
144            payload
145        };
146
147        let inside_call_bytes = payload.encode()?;
148
149        // getting out amount for in amount provided
150
151        trace!("uniswap v2 get out amount for pool={:?}, amount={:?}", flash_pool.get_address(), amount_in);
152        let mut get_out_amount_opcode = MulticallerCall::new_internal_call(&AbiEncoderHelper::encode_multicaller_uni2_get_out_amount(
153            token_from_address,
154            token_to_address,
155            flash_pool_address,
156            amount_in.unwrap_or_default(),
157            flash_pool.get_fee(),
158        ));
159
160        // setting up stack, in amount is out amount for previous swap and is located in stack0
161        if amount_in.is_not_set() {
162            get_out_amount_opcode.set_call_stack(false, 0, 0x24, 0x20);
163        }
164
165        // abi encode uniswap2 out amount provided swap.
166        let mut swap_opcode = MulticallerCall::new_call(
167            flash_pool_address,
168            &abi_encoder.encode_swap_out_amount_provided(
169                flash_pool,
170                token_from_address,
171                token_to_address,
172                U256::ZERO,
173                multicaller_address,
174                inside_call_bytes,
175            )?,
176        );
177
178        // setting call stack to calculated out amount.
179        swap_opcode.set_call_stack(
180            true,
181            0,
182            abi_encoder.swap_out_amount_offset(flash_pool, token_from_address, token_to_address).unwrap(),
183            0x20,
184        );
185
186        swap_opcodes.add(get_out_amount_opcode).add(swap_opcode);
187
188        Ok(())
189    }
190
191    fn encode_flash_swap_out_amount_provided(
192        &self,
193        swap_opcodes: &mut MulticallerCalls,
194        abi_encoder: &dyn ProtocolAbiSwapEncoderTrait,
195        token_from_address: Address,
196        token_to_address: Address,
197        amount_out: SwapAmountType,
198        flash_pool: &dyn Pool,
199        next_pool: Option<&dyn Pool>,
200        payload: MulticallerOpcodesPayload,
201        multicaller_address: Address,
202    ) -> eyre::Result<()> {
203        // Extract flash pool address early
204        let flash_pool_address = match flash_pool.get_address() {
205            PoolId::Address(addr) => addr,
206            PoolId::B256(_) => return Err(eyre!("B256 pool ID not supported for UniswapV2")),
207        };
208
209        // getting address for token_to
210        let swap_to = next_pool.and_then(|next_pool| next_pool.preswap_requirement().address()).unwrap_or(multicaller_address);
211
212        // add get_in amount to keep amount we should return in stack.
213        let payload = if let MulticallerOpcodesPayload::Opcodes(inside_opcodes) = &payload {
214            let mut inside_opcodes = inside_opcodes.clone();
215
216            let mut get_in_amount_opcode = MulticallerCall::new_internal_call(&AbiEncoderHelper::encode_multicaller_uni2_get_in_amount(
217                token_from_address,
218                token_to_address,
219                flash_pool_address,
220                amount_out.unwrap_or_default(),
221                flash_pool.get_fee(),
222            ));
223
224            // is not set, will use stack0
225            if amount_out.is_not_set() {
226                get_in_amount_opcode.set_call_stack(false, 0, 0x24, 0x20);
227            }
228
229            inside_opcodes.insert(get_in_amount_opcode);
230
231            // if we need funds somewhere else, we transfer it because cannot set destination in uniswap2 with parameters
232            if swap_to != multicaller_address {
233                trace!("retflash transfer token={:?}, to={:?}, amount=stack_norel_0", token_to_address, swap_to);
234
235                let mut transfer_opcode =
236                    MulticallerCall::new_call(token_to_address, &AbiEncoderHelper::encode_erc20_transfer(swap_to, U256::ZERO));
237                transfer_opcode.set_call_stack(false, 0, 0x24, 0x20);
238                inside_opcodes.add(transfer_opcode);
239            }
240
241            MulticallerOpcodesPayload::Opcodes(inside_opcodes)
242        } else {
243            payload
244        };
245
246        // encode inside bytes
247        let inside_call_bytes = payload.encode()?;
248
249        // flash swap without amount
250        trace!(
251            "uniswap v2 swap out amount provided for pool={:?}, from={} to={} amount_out={:?} receiver={} inside_opcodes_len={}",
252            flash_pool.get_address(),
253            token_from_address,
254            token_to_address,
255            amount_out,
256            swap_to,
257            inside_call_bytes.len()
258        );
259        let mut swap_opcode = MulticallerCall::new_call(
260            flash_pool_address,
261            &abi_encoder.encode_swap_out_amount_provided(
262                flash_pool,
263                token_from_address,
264                token_to_address,
265                amount_out.unwrap_or_default(),
266                multicaller_address,
267                inside_call_bytes,
268            )?,
269        );
270
271        // set rel_stack(0) is amount is not set
272        if amount_out.is_not_set() {
273            trace!("uniswap v2 amount not set");
274            swap_opcode.set_call_stack(
275                true,
276                0,
277                abi_encoder.swap_out_amount_offset(flash_pool, token_from_address, token_to_address).unwrap(),
278                0x20,
279            );
280        }
281
282        swap_opcodes.add(swap_opcode);
283        Ok(())
284    }
285}