Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Example for how to swap multiple tokens in one on Ropsten
// SPDX-License-Identifier: MIT
pragma solidity =0.7.6;
pragma abicoder v2;
import "https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/interfaces/ISwapRouter.sol";
import "https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/interfaces/IQuoter.sol";
import {IERC20, SafeERC20} from "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.4-solc-0.7/contracts/token/ERC20/SafeERC20.sol";
interface IUniswapRouter is ISwapRouter {
function refundETH() external payable;
}
interface IUniswapV2Router02 {
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapTokensForExactTokens(
uint amountOut,
uint amountInMax,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
external
payable
returns (uint[] memory amounts);
function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
external
returns (uint[] memory amounts);
function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
external
returns (uint[] memory amounts);
function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
external
payable
returns (uint[] memory amounts);
}
// https://docs.bancor.network/developer-quick-start/trading-with-bancor#trading-from-your-smart-contract
// https://app.bancor.network/eth/swap?from=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE&to=0xF35cCfbcE1228014F66809EDaFCDB836BFE388f5
// https://ropsten.etherscan.io/tx/0x21b95960b1a7c832c91e705390420edf3faa35b18469a8bc517056d88af9634e
interface IBancorNetwork {
function convertByPath(
address[] memory _path,
uint256 _amount,
uint256 _minReturn,
address _beneficiary,
address _affiliateAccount,
uint256 _affiliateFee
) external payable returns (uint256);
function rateByPath(
address[] memory _path,
uint256 _amount
) external view returns (uint256);
}
// sushi https://ropsten.etherscan.io/tx/0x727301c32fcdbb29e14203610b26c7ab7f44f5d940057c2c39ecc0ae9e919c0f
// https://app.sushi.com/swap?inputCurrency=0x9108Ab1bb7D054a3C1Cd62329668536f925397e5&outputCurrency=0xF35cCfbcE1228014F66809EDaFCDB836BFE388f5
// uni https://ropsten.etherscan.io/tx/0xc23e6efa4c95747cb1421b582b1d29ce1ae1a529f84c28a94f74536997358262
// https://app.uniswap.org/#/swap
contract MultiTrade {
using SafeERC20 for IERC20;
// Bancor
IBancorNetwork private constant bancorNetwork = IBancorNetwork(0xb3fa5DcF7506D146485856439eb5e401E0796B5D);
address private constant BANCOR_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
address private constant BANCOR_ETHBNT_POOL = 0x1aCE5DD13Ba14CA42695A905526f2ec366720b13;
address private constant BNT = 0xF35cCfbcE1228014F66809EDaFCDB836BFE388f5;
// SushiSwap
IUniswapV2Router02 private constant sushiRouter = IUniswapV2Router02(0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506);
address private constant INJ = 0x9108Ab1bb7D054a3C1Cd62329668536f925397e5;
// Uniswap
IUniswapRouter private constant uniswapRouter = IUniswapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564);
address private constant DAI = 0xaD6D458402F60fD3Bd25163575031ACDce07538D;
constructor() {
IERC20(BNT).safeApprove(address(sushiRouter), type(uint256).max);
IERC20(INJ).safeApprove(address(uniswapRouter), type(uint256).max);
}
function _tradeOnBancor(uint256 amountIn, uint256 amountOutMin) private {
bancorNetwork.convertByPath{value: msg.value}(_getPathForBancor(), amountIn, amountOutMin, address(0), address(0), 0);
}
function _getPathForBancor() private pure returns (address[] memory) {
address[] memory path = new address[](3);
path[0] = BANCOR_ETH_ADDRESS;
path[1] = BANCOR_ETHBNT_POOL;
path[2] = BNT;
return path;
}
function _tradeOnSushi(uint256 amountIn, uint256 amountOutMin, uint256 deadline) private {
address recipient = address(this);
sushiRouter.swapExactTokensForTokens(
amountIn,
amountOutMin,
_getPathForSushiSwap(),
recipient,
deadline
);
}
function _getPathForSushiSwap() private pure returns (address[] memory) {
address[] memory path = new address[](2);
path[0] = BNT;
path[1] = INJ;
return path;
}
function _tradeOnUniswap(uint256 amountIn, uint256 amountOutMin, uint256 deadline) private {
address tokenIn = INJ;
address tokenOut = DAI;
uint24 fee = 3000;
address recipient = msg.sender;
uint160 sqrtPriceLimitX96 = 0;
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams(
tokenIn,
tokenOut,
fee,
recipient,
deadline,
amountIn,
amountOutMin,
sqrtPriceLimitX96
);
uniswapRouter.exactInputSingle(params);
uniswapRouter.refundETH();
// refund leftover ETH to user
(bool success,) = msg.sender.call{ value: address(this).balance }("");
require(success, "refund failed");
}
// meant to be called as view function
function multiSwapPreview() external payable returns(uint256) {
uint256 daiBalanceUserBeforeTrade = IERC20(DAI).balanceOf(msg.sender);
uint256 deadline = block.timestamp + 300;
uint256 amountOutMinBancor = 1;
uint256 amountOutMinSushiSwap = 1;
uint256 amountOutMinUniswap = 1;
_tradeOnBancor(msg.value, amountOutMinBancor);
_tradeOnSushi(IERC20(BNT).balanceOf(address(this)), amountOutMinSushiSwap, deadline);
_tradeOnUniswap(IERC20(INJ).balanceOf(address(this)), amountOutMinUniswap, deadline);
uint256 daiBalanceUserAfterTrade = IERC20(DAI).balanceOf(msg.sender);
return daiBalanceUserAfterTrade - daiBalanceUserBeforeTrade;
}
function multiSwap(uint256 deadline, uint256 amountOutMinBancor, uint256 amountOutMinSushiSwap, uint256 amountOutMinUniswap) external payable {
_tradeOnBancor(msg.value, amountOutMinBancor);
_tradeOnSushi(IERC20(BNT).balanceOf(address(this)), amountOutMinSushiSwap, deadline);
_tradeOnUniswap(IERC20(INJ).balanceOf(address(this)), amountOutMinUniswap, deadline);
}
// important to receive ETH
receive() payable external {}
}
@cfengliu
Copy link

cfengliu commented Aug 26, 2021

Thanks for the tutorial on multiswap. Help me a lot! I also found that uint256 deadline = block.timestamp + 300; should be added to function multiSwap. Otherwise, Error: cannot estimate gas will occur when I execute this code. But I'm not sure if that's correct. You can correct me if I am wrong. Thx!

Loading

@gorgos
Copy link
Author

gorgos commented Aug 26, 2021

@cfengliu You need to pass a deadline that is in the future to multiSwap. This is a UNIX timestamp which you can get for example via https://www.unixtimestamp.com/.

It's important to pass this parameter yourself and not like it's done in the preview function, unless you really don't care about when the swap happens. But be aware that if you do it like the preview function and your transaction is not mined, it will still a valid transaction and might be mined months later by a miner when the prices are very different from now.

Loading

@cfengliu
Copy link

cfengliu commented Aug 27, 2021

@gorogs Get it. I used the wrong argument. Thx for the reply!

Loading

@franklee00
Copy link

franklee00 commented Sep 6, 2021

wow, that's what I need for long time! thx a lot!

Loading

@tc64
Copy link

tc64 commented Sep 14, 2021

Thanks for making this. After deploying I always see this message when using multiSwapPreview:

Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending?
execution reverted

Any idea why?

Loading

@tc64
Copy link

tc64 commented Sep 14, 2021

Now I see, the error is:

'{"value":{"code":-32000,"message":"exceeds block gas limit"}}'

Loading

@tc64
Copy link

tc64 commented Sep 14, 2021

Another try after changing gas limit and it fails with Transaction mined but execution failed.

tx hash: 0x0eeb5106378a28715d47f806b25b565d1f247313a59f2cf7490716724ce78fc6

Same if I try passing these parameters (and tried some others) to multiSwap: 1631645842,1,1,1

0x1af184c9ec168ad0825048bcc28e2388b3da248da9970b9f56f4f71573b9ccf0

I am using the Ropsten network via Remix with a metamask wallet Web3 injection.

Any idea what could be going wrong @gorgos ?

Loading

@gorgos
Copy link
Author

gorgos commented Sep 14, 2021

@tc64 Try to follow the instructions at https://soliditydeveloper.com/multiswap in particular doing the swaps individually yourself first. If they all work individually, then the combination should also work.

Loading

@tc64
Copy link

tc64 commented Sep 16, 2021

Thanks for replying @gorgos - I think the problem was actually that I wasn't putting enough wei into msg.value. Rookie mistake.

Loading

@dmtrbch
Copy link

dmtrbch commented Nov 13, 2021

Hello @gorgos I am trying to follow along this tutorial, I have set up a local development environment using Truffle and web3.js, connected to Ropsten network, and added all of the tokens in MetaMask. I have done the transaction manually, I've deployed the contract, bur for some reason I can not interact with it from my frontend.

To be honest I don't quite understand this statement const estimatedDAI = (await myContract.multiSwapPreview({ value: ethAmount }).call())[0]; Shouldn't it be const estimatedDAI = (await myContract.multiSwapPreview().call()); ?

Why are we sending { value: ethAmount } as a function argument, when inside the contract multiSwapPreview function does not have any parameters?

Thanks in advance

Loading

@gorgos
Copy link
Author

gorgos commented Nov 16, 2021

@dmtrbch { value: ethAmount } is not the function parameter, those are inside the rounded brackets. Rather in Solidity calls, you can pass transaction parameters in curly brackets: mySmartContractAddress{ value: ethAmount, gas: gasAmount }(myParameter1, myParameter2).

Loading

@dmtrbch
Copy link

dmtrbch commented Nov 20, 2021

@gorgos Thanks for the response. I have one more question and I want bother anymore. Are you familiar with other DEXs that support Ropsten Test Network? So far I haven't been able to find any...

Best Regards

Loading

@gorgos
Copy link
Author

gorgos commented Nov 20, 2021

@dmtrbch Haven't tried anything else yet, but just try a few here https://defirate.com/dex/. Go to their exchange websites and switch MetaMask to Ropsten. If you can trade, they have something deployed on Ropsten.

Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment