part2. UniSwap V2 | Swap Token

访问 UniSwap

本章我们教大家如何解锁ETH主网上的巨鲸地址,并用该地址发起交易

1.编写合约接口

在/interfaces文件夹下创建IERC20.sol和Uniswap.sol文件

IERC20.sol(官方标准接口,建议保存)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.5.0;

interface IERC20 {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);

function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);

function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
}

Uniswap.sol(自写接口文件)

1
2
3
4
5
6
7
8
9
10
11
12
13
//SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.0;

interface IUniswapV2Router {
function swapExactTokenForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);

}

2.接口函数swapExactTokenForTokens参数详解

这是一个用已知数量的代币去交换未知数量代币的接口函数

举个例子:用100usdt的代币去交换btc,100是已知的,但能够交换到多少btc是未知的

  1. amountIn 用于交换的代币的数量,就是上例中的100;
  2. amountOutMin 即用户所期望的滑点;
  3. path 交易路径,用于确认交换代币的种类,上例中用usdt交换btc,那么交易路径就是[‘usdt合约地址’,’btc合约地址’];
  4. to 自己的钱包地址
  5. deadline 常说的ddl,如果这次交易到ddl还没有打包成功,那么该次交易将会撤回

3.编写测试合约

TestUniswap.sol

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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;

//导入接口
import "./interfaces/IERC20.sol";
import "./interfaces/Uniswap.sol";

//在本合约中我们模拟巨鲸账户在主网发起交易
//本实例中,使用WBTC交换DAI币
contract TestUniswap {
//定义UniSwap路由
address private constant UNISWAP_V2_ROUTER =
0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
address private constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
//定义巨鲸账户地址
address private constant WBTC_WHALE =
0x2FAF487A4414Fe77e2327F0bf4AE2a264a776AD2;
//交换函数
function swap(
address _tokenIn,
address _tokenOut,
uint256 _amountIn,
uint256 _amountOutMin,
address _to
) external {
//模拟巨鲸账户地址触发该币种交易
IERC20(_tokenIn).transferFrom(WBTC_WHALE, address(this), _amountIn);
//向uniswap路由授权该币种
IERC20(_tokenIn).approve(UNISWAP_V2_ROUTER, _amountIn);

address[] memory path;
//判断
//在uniswap中,WETH作为所有币种交换的中间币
//因此在这里需要判断是否是WETH作为交换币种
if (_tokenIn == WETH || _tokenOut == WETH) {
path = new address[](2);
path[0] = _tokenIn;
path[1] = _tokenOut;
} else {
path = new address[](3);
path[0] = _tokenIn;
path[1] = WETH;
path[2] = _tokenOut;
}

//调用接口函数
IUniswapV2Router(UNISWAP_V2_ROUTER).swapExactTokensForTokens(
_amountIn,
_amountOutMin,
path,
_to,
block.timestamp
);
}
}

编译合约

4.测试项目

在本教程中,我们选在hardhat的waffle写法来实现测试

使用ganache-cli来模拟网络环境

须知:

  • hardhat与ether.js&&waffle是原生搭配
  • truffle 与web3.js&&mocha是原生搭配
  • waffle和mocha都是独特的接口写法类型
  • 他们分别基于ether.js和web3.js实现

test/testUniswap.js

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
const {
ethers
} = require('hardhat');

const hre = require('hardhat');

const {
WETH,
DAI,
WETH_WHALE
} = require("./config");


const { pow } = require('./util');

//模块测试
describe("TestUniswap", () => {

//定义用于交换的代币数量
const AMOUNT_IN = 100000;
//最小
const AMOUNT_OUT_MIN = 1;
//以太坊
const TOKEN_IN = WETH;
//dai币
const TOKEN_OUT = DAI;


let TestUniswap;
let testUniswap;
let tokenIn;
let tokenOut;
let whale;


beforeEach(async () => {

//解锁巨鲸账户
await hre.network.provider.request({
method: "hardhat_impersonateAccount",
params: [WETH_WHALE],
})

//实例化巨鲸账户
whale = await ethers.getSigner(WETH_WHALE);

//whale.address
console.log('the whale address is: ', whale.address)

//实例化tokenin,实例化tokenout
tokenIn = await ethers.getContractAt("IERC20", TOKEN_IN);
tokenOut = await ethers.getContractAt("IERC20", TOKEN_OUT);

//实例化刚写的测试合约
TestUniswap = await ethers.getContractFactory('TestUniswap');
testUniswap = await TestUniswap.deploy();

console.log('tokenIn address is:', tokenIn.address, '\n', 'tokenOut address is:', tokenOut.address, '\n', 'testUniswap address is:', testUniswap.address);


console.log("the whale WETH balance is:", await tokenIn.balanceOf(whale.address))

await tokenIn.connect(whale).approve(testUniswap.address,AMOUNT_IN);

console.log('approve token success!')

});



it("should swap", async () => {


console.log('the whale dai balance is : ', await tokenOut.balanceOf(whale.address));


await testUniswap.connect(whale).swap(
tokenIn.address,
tokenOut.address,
AMOUNT_IN,
AMOUNT_OUT_MIN,
whale.address,
{
gasPrice: 10000000000,
gasLimit: 2000000
}

)

console.log(`swap tokenin is ${AMOUNT_IN}`);

console.log('after swap the whale dai balance is : ', await tokenOut.balanceOf(whale.address));


});
});

别忘了配置hardhat.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
require("@nomiclabs/hardhat-waffle");



const MAINNET_URL = "https://eth-mainnet.g.alchemy.com/v2/自己的apikey";

module.exports = {
solidity: "0.8.0",
networks: {
hardhat: {
forking: {
url: MAINNET_URL
}

}
}
};

运行测试

  1. 开启本地节点监听
1
npx hardhat node 
  1. 开始测试(需要指定在当前网络之下测试)
1
npx hardhat test --network localhost

part2. UniSwap V2 | Swap Token
http://nangbowan.github.io/2022/10/23/Part2. UniSwap V2 Swap Tokens/
作者
Science_Jun
发布于
2022年10月23日
许可协议