Pre:
在学习闪电贷的过程中,先熟悉一下合约里调用其他swap合约的用法先试一下如何在自己的合约里调用uniswap
完整代码:
kovan测试网里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 pragma solidity 0.8 .0 ; import "https://github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/interfaces/IUniswapV2Router02.sol" ;import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol" ;contract UseSwap { IUniswapV 2Router02 private constant ROUTER = IUniswapV 2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D ); address internal owner; constructor ( ) { owner = msg.sender ; } modifier isOwner ( ){ require (msg.sender == owner, "Caller is not owner" ); _; } function swapTokenToEth (address token, uint amountOut, uint amountInMax ) public{ IERC20 (token).transferFrom (msg.sender , address (this ), amountInMax); _approveTokenIfNeeded (token); uint deadline = block.timestamp + 100 ; ROUTER .swapTokensForExactETH (amountOut, amountInMax, getPathForTokenToEth (token), address (this ), deadline); } function swapEthToToken (address token, uint TokenOutAmountMin ) public payable{ uint deadline = block.timestamp + 100 ; ROUTER .swapExactETHForTokens { value : msg.value }(TokenOutAmountMin , getPathForEthToToken (token), address (this ), deadline); } function _approveTokenIfNeeded (address token ) private { if (IERC20 (token).allowance (address (this ), address (ROUTER )) == 0 ) { IERC20 (token).approve (address (ROUTER ), 1000000 ether); } } function approveToken (address token ) public { IERC20 (token).approve (address (ROUTER ), 1000000 ether); } function getPathForTokenToEth (address token ) private view returns (address[] memory){ address[] memory path = new address[](2 ); path[0 ] = token; path[1 ] = ROUTER .WETH (); return path; } function getPathForEthToToken (address token ) private view returns (address[] memory){ address[] memory path = new address[](2 ); path[0 ] = ROUTER .WETH (); path[1 ] = token; return path; } function withdraw (address payable _address, uint withdrawAmount ) public payable isOwner{ _address.transfer (withdrawAmount); } receive () external payable {} }
实例化合约:
知道合约的interface和合约地址后,就可以实例化合约了。
Eth -> Token:
定义receive函数:
1 2 3 4 receive 接收以太函数 一个合约最多有一个 receive 函数, 声明函数为: receive() external payable { ... } 不需要 function 关键字,也没有参数和返回值并且必须是 external 可见性和 payable 修饰. 它可以是 virtual 的,可以被重载也可以有 修改器modifier 。
合约里要定义receive函数才能接受eth
swapExactETHForTokens:
要用到uniswapRouter
的swapExactETHForTokens
函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function swapExactETHForTokens (uint amountOutMin, address[] calldata path, address to, uint deadline ) external virtual override payable ensure (deadline) returns (uint[] memory amounts) { require (path[0 ] == WETH , 'UniswapV2Router: INVALID_PATH' ); amounts = UniswapV2Library .getAmountsOut (factory, msg.value , path); require (amounts[amounts.length - 1 ] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT' ); IWETH (WETH ).deposit {value : amounts[0 ]}(); assert (IWETH (WETH ).transfer (UniswapV2Library .pairFor (factory, path[0 ], path[1 ]), amounts[0 ])); _swap (amounts, path, to); }
该函数有payable
修饰,需要输入eth的数量通过msg.value
传入
通过在自己合约里调用swapExactETHForTokens
函数的流程是:
1 2 3 4 function swapEthToToken (address token, uint TokenOutAmountMin ) public payable{ uint deadline = block.timestamp + 100 ; ROUTER .swapExactETHForTokens { value : msg.value }(TokenOutAmountMin , getPathForEthToToken (token), address (this ), deadline); }
函数为payable
调用函数的同时转账msg.value
数量的eth
eth继续通过msg.value
传入,调用swapExactETHForTokens
Token -> Eth:
这里要用到uniswapRouter
的swapTokensForExactETH
函数,一开始遇到Fail with error 'TransferHelper: ETH_TRANSFER_FAILED'
报错看了一下函数的定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function swapTokensForExactETH (uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline ) external virtual override ensure (deadline) returns (uint[] memory amounts) { require (path[path.length - 1 ] == WETH , 'UniswapV2Router: INVALID_PATH' ); amounts = UniswapV2Library .getAmountsIn (factory, amountOut, path); require (amounts[0 ] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT' ); TransferHelper .safeTransferFrom ( path[0 ], msg.sender , UniswapV2Library .pairFor (factory, path[0 ], path[1 ]), amounts[0 ] ); _swap (amounts, path, address (this )); IWETH (WETH ).withdraw (amounts[amounts.length - 1 ]); TransferHelper .safeTransferETH (to, amounts[amounts.length - 1 ]); }
没有payable修饰,在兑换成ETH之前,需要把token转进去
通过在自己合约里调用swapTokensForExactETH
函数的流程是:
1 2 3 4 5 6 function swapTokenToEth (address token, uint amountOut, uint amountInMax ) public{ IERC20 (token).transferFrom (msg.sender , address (this ), amountInMax); _approveTokenIfNeeded (token); uint deadline = block.timestamp + 100 ; ROUTER .swapTokensForExactETH (amountOut, amountInMax, getPathForTokenToEth (token), address (this ), deadline); }
自己的钱包地址授权Dai
额度给自己的合约地址
合约从钱包里提取一定额度Dai
合约授权Dai
给uniswap router
uniswap router从合约里提取Dai
,兑换成eth,再发回来
Refs: