Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
Neronjust2017 opened this issue Feb 26, 2023 · 4 comments
Open

Comments

@Neronjust2017
Copy link

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
Copy link

rupak21 commented Apr 4, 2023

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
Copy link

rupak21 commented Apr 4, 2023

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
Copy link
Author

much appreciated, this works!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants
@Neronjust2017 @rupak21 and others