kabu_execution_multicaller/pool_opcodes_encoder/
uniswap2.rs

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