Uniswap / v2-core

🦄 🦄 Core smart contracts of Uniswap V2
https://uniswap.org/docs
GNU General Public License v3.0
2.95k stars 3.16k forks source link

Store dynamic bytes to call swap function with solidity assembly #188

Open Neronjust2017 opened 1 year ago

Neronjust2017 commented 1 year ago

Hi, I want to save gas with solidity inline assembly when calling UniswapV2 pair's swap function. Here is my code:

pragma solidity >=0.8.0;

import "./interface/IERC20.sol";
import "./lib/SafeTransfer.sol";

contract Mycontract {

    // transfer(address,uint256)
    bytes4 internal constant TRANSFER = 0xa9059cbb;
    // swap(uint256,uint256,address,bytes)
    bytes4 internal constant PAIR_SWAP = 0x022c0d9f;

    // Contructor sets the only user
    receive() external payable {}

    fallback() external payable {
        assembly {

            let pair := shr(96, calldataload(0x00))

            let tokenAmountOut := calldataload(0x14)

            mstore(0x00, PAIR_SWAP)
            mstore(0x04, tokenAmountOut)
            mstore(0x24, 0)
            mstore(0x44, address())
            mstore(0x64, ???)  // I want the length of this is zer0, what value shoud it be?

            let s := call(sub(gas(), 5000), pair, 0, 0x00, ???, 0, 0) // what the length should be?

        }       
    }        
}

The parameters of swap function are:

function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data)

I can store uint, and address variables in memory, but I want calldata_data be zero-length bytes, so what value should be stored from 0x64? Thanks!

rupak21 commented 1 year ago

In order to pass a zero-length bytes value to the swap function of the UniswapV2 pair, you can simply store a single zero byte at memory location 0x64. This can be done using the mstore8 opcode, which stores a single byte at the specified memory location.

Here's the modified assembly code with the mstore8 instruction to set the length of the data parameter to zero:

fallback() external payable { assembly { let pair := shr(96, calldataload(0)) let tokenAmountOut := calldataload(4) mstore(0, PAIR_SWAP) mstore(4, tokenAmountOut) mstore(0x24, 0) mstore(0x44, address()) mstore8(0x64, 0) let result := call(gas(), pair, callvalue(), 0, 0x84, 0, 0) returndatacopy(0, 0, returndatasize()) switch result case 0 {revert(0, returndatasize())} default {return (0, returndatasize())} } } Note that in addition to setting the length of the data parameter to zero, this code also includes some additional changes to handle the result of the call to the swap function, and to copy the return data back to the caller.

rupak21 commented 1 year ago

fallback() external payable { assembly { let pair := shr(96, calldataload(0)) let tokenAmountOut := calldataload(4) mstore(0, PAIR_SWAP) mstore(4, tokenAmountOut) mstore(0x24, 0) mstore(0x44, address()) mstore8(0x64, 0) let result := call(gas(), pair, callvalue(), 0, 0x84, 0, 0) returndatacopy(0, 0, returndatasize()) switch result case 0 {revert(0, returndatasize())} default {return (0, returndatasize())} } }

Neronjust2017 commented 1 year ago

much appreciated, this works!