├── .gitignore
├── README.md
├── contracts
├── Token.sol
├── uniswap-v2-core
│ ├── UniswapV2ERC20.sol
│ ├── UniswapV2Factory.sol
│ ├── UniswapV2Pair.sol
│ ├── interfaces
│ │ ├── IERC20.sol
│ │ ├── IUniswapV2Callee.sol
│ │ ├── IUniswapV2ERC20.sol
│ │ ├── IUniswapV2Factory.sol
│ │ └── IUniswapV2Pair.sol
│ ├── libraries
│ │ ├── Math.sol
│ │ ├── SafeMath.sol
│ │ └── UQ112x112.sol
│ └── test
│ │ └── ERC20.sol
└── uniswap-v2-periphery
│ ├── UniswapV2Migrator.sol
│ ├── UniswapV2Router01.sol
│ ├── UniswapV2Router02.sol
│ ├── examples
│ ├── ExampleComputeLiquidityValue.sol
│ ├── ExampleFlashSwap.sol
│ ├── ExampleOracleSimple.sol
│ ├── ExampleSlidingWindowOracle.sol
│ ├── ExampleSwapToPrice.sol
│ └── README.md
│ ├── interfaces
│ ├── IERC20.sol
│ ├── IUniswapV2Migrator.sol
│ ├── IUniswapV2Router01.sol
│ ├── IUniswapV2Router02.sol
│ ├── IWETH.sol
│ └── V1
│ │ ├── IUniswapV1Exchange.sol
│ │ └── IUniswapV1Factory.sol
│ ├── libraries
│ ├── SafeMath.sol
│ ├── UniswapV2Library.sol
│ ├── UniswapV2LiquidityMathLibrary.sol
│ └── UniswapV2OracleLibrary.sol
│ └── test
│ ├── DeflatingERC20.sol
│ ├── ERC20.sol
│ ├── RouterEventEmitter.sol
│ └── WETH9.sol
├── hardhat.config.ts
├── package-lock.json
├── package.json
├── scripts
├── deploy.ts
├── init.ts
├── mockTrader0.ts
└── mockTrader1.ts
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 |
3 | node_modules
4 | .env
5 | coverage
6 | coverage.json
7 | typechain
8 | typechain-types
9 |
10 | # Hardhat files
11 | cache
12 | artifacts
13 | abi
14 |
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # UniswapV2 本地部署流程
2 |
3 | ```shell
4 | # 1 初始阶段,只用执行一次,以后都不用执行(在项目根目录下打开命令行窗口)
5 | npm i
6 | npx hardhat compile
7 | npx hardhat run --network local scripts/init.ts
8 | # 2 部署 UniswapV2
9 | # (1)单独开一个命令行窗口
10 | npx hardhat node
11 | # (2)单独再开一个命令行窗口
12 | npx hardhat run --network local scripts/deploy.ts
13 | ```
14 |
15 | # Hardhat知识点框架总结
16 | ## 测试的三种方法
17 | ### 合约层测试
18 | 合约也是可以调试的,调试输出信息将展示在hardhat本地网络,使用语法如下:
19 | ```solidity
20 | import "hardhat/console.log";
21 | ...
22 | console.log(字符串1,字符串2...);
23 | ```
24 | ### 单元测试
25 | 合约开发完成之后,往往需要逐个函数编写测试脚本进行测试,这种逐个测试的过程叫做单元测试,hardhat提供了我们单元测试的文件夹`test/`,具体使用方法如下:
26 | ```ts
27 | // `describe`函数 的回调函数内有`it`|`before`|`each`|`describe`四个测试相关的函数,大致作用描述如下:
28 | - `it` 测试单元,一般用来模拟一次测试行为,测试单元之间相互独立,状态不传递,每次执行完即区块链恢复
29 | - `before` 在测试单元执行之前执行,一般用来初始化,状态维持到所处describe函数结束,比如: 读取部署账号,加载loadFixture函数
30 | - `beforeeach` 在每个单元测试执行前执行一次,状态随it同生共死,一般用来执行loadFixture函数
31 | - `aftereeach` 在每个单元测试执行后执行一次,状态随it同生共死,很少使用
32 | 说明: loadFixture函数是区块链网络闪存函数,在每次执行时直接基于闪存快速恢复区块链网络到闪存时刻,从而免去大量合约的部署过程,节约时间
33 | describe("Transfers", function () {
34 | it("Should transfer the funds to the owner", async function () {
35 | const { lock, unlockTime, lockedAmount, owner } = await loadFixture(
36 | deployOneYearLockFixture
37 | );
38 |
39 | await time.increaseTo(unlockTime);
40 |
41 | await expect(lock.withdraw()).to.changeEtherBalances(
42 | [owner, lock],
43 | [lockedAmount, -lockedAmount]
44 | );
45 | });
46 | });
47 | ```
48 | ### 项目测试
49 | 项目测试一般涉及多端联调,因此需要保证区块链网络状态时维持的,持久的,hardhat给我们提供了`scripts/`文件夹,我们可以在这个文件夹下编写部署脚本及持久状态的测试脚本
50 |
51 |
52 | # 补充
53 | 1. `hardhat-abi-exporter` 额外导出ABI接口,详情看配置文件
54 | 2. `控制挖块速度`,详情看配置文件
55 | 3. 脚本目录新增监听交易池的模拟脚本: mockTrader*.ts
56 | 4. 在`Jetbrains`编辑器一键运行`ts`的方法: (1) npm全局安装`ts-node`(2)`IDE`插件: `Run Configuration for TypeScrip`
57 | # 资料
58 | > 说明: 下文的资料是个人一年之前开发的笔记,近年来未曾有详尽的使用和即时的纠正,或有错误之处,请以官方文档为准
59 |
60 | ## ethers。js
61 | **2022/04/07 [警告]:`new ethers.Contract()`会堆积占用内存,不适合大规模实例化,暂时无法解决。**
62 |
63 | ```js
64 | // 1- 节点实例
65 | const provider = new ethers.providers.JsonRpcProvider(`https://mainnet.infura.io/v3/${INFURA_ID}`);
66 | let accounts = await provider.listAccounts();
67 | // 1.1 - 读取本币
68 | const address = '0x73BCEb1Cd57C711feaC4224D062b0F6ff338501e';
69 | const balance = await provider.getBalance(address);
70 | // 1.2 - 转移本币
71 | const wallet = new ethers.Wallet(privateKey, provider);
72 | const tx = await wallet.sendTransaction({
73 | to: account2,
74 | value: ethers.utils.parseEther("0.025")
75 | });
76 | const newBalance = await provider.getBalance(address);
77 | ```
78 |
79 | ```js
80 | // 2- 合约实例
81 | const ERC20_ABI = [
82 | "function name() view returns (string)",
83 | "function symbol() view returns (string)",
84 | "function totalSupply() view returns (uint256)",
85 | "function balanceOf(address) view returns (uint)",
86 | ];
87 | const contract = new ethers.Contract(address, ERC20_ABI, provider);
88 |
89 | // 2.1- 读取合约
90 | const contract = new ethers.Contract(address, ERC20_ABI, provider);
91 | const name = await contract.name()
92 | const symbol = await contract.symbol()
93 | const totalSupply = await contract.totalSupply()
94 | const balance = await contract.balanceOf(address);
95 | // 2.2- [私钥]写入合约
96 | const wallet = new ethers.Wallet(privateKey1, provider)
97 | const contract = new ethers.Contract(address, ERC20_ABI, provider)
98 | const contractWithWallet = contract.connect(wallet)
99 | const tx = await contractWithWallet.transfer(account2, balance)
100 | await tx.wait()
101 | // 2.3- [钱包]写入合约
102 | await window.etherum.request({method:"eht_requestAccounts"});
103 | provider = new ethers.providers.Web3Provider(window.ethereum);
104 | const signer = provider.getSigner();
105 | const contract = new ethers.Contract(address, ERC20_ABI, signer);
106 | ```
107 |
108 | ```js
109 | // 3- 事件监听
110 | const contract = new ethers.Contract(address, ERC20_ABI, provider)
111 | const block = await provider.getBlockNumber()
112 | const transferEvents = await contract.queryFilter('Transfer', block - 1, block)
113 |
114 | ```
115 |
116 | ```js
117 | // 4- 区块审查
118 | const block = await provider.getBlockNumber()
119 | const blockInfo = await provider.getBlock(block)
120 | const { transactions } = await provider.getBlockWithTransactions(block)
121 | ```
122 |
123 | ## web3.js
124 | > web3.js的笔记过于陈旧和杂乱,部分摘录了较为精华的部分(建议按照ethers.js的纲要自己整理一份,较为清晰),如下:
125 |
126 | ```js
127 | const Web3 = require("web3"); // import Web3 from 'web3'
128 |
129 | // 网络id
130 | // const chainId = await new web3.eth.getChainId();
131 | // 网络最新区块
132 | // web3.eth.getBlockNumber().then{}
133 | // 交易详情
134 | // web3.eth.getTransaction(交易哈希).then{}
135 |
136 | // 0- 节点实例:`infura.io`网站创建项目以获取接入ropsten区块链网络的API
137 | const web3 = new Web3("");
138 | // const web3 = new Web3(Web3.givenProvider || "http://localhost:8545");
139 |
140 | // 1- 账户余额(网络主代币余额)
141 | web3.eth.getBalance("0xcB7419533E5470fA00Edf05dC46Dee44586010d2")
142 | // + 调用某代币合约接口,查询合约内指定账户的ERC20余额
143 | let tokenContract = await new web3.eth.Contract(ERC20.abi, token.address);
144 | tokenContract.methods.balanceOf("0x123...3080").call();
145 |
146 | // 2- 新建账户
147 | let account1 = web3.eth.accounts.create(); // (1) 无密码
148 | console.log(account1)
149 | let account2 = web3.eth.accounts.create("123456"); // (2) 带密码
150 | console.log(account2)
151 | web3.eth.defaultAccount = web3.eth.accounts[0]; // 默认账户,部分函数省略from时使用默认账户,如:
152 | // web3.eth.sendTransaction()
153 | // web3.eth.call()
154 |
155 | // 3- 查询钱包中的账户列表(异步)
156 | web3.eth.getAccounts((err, accounts) => {
157 | if (typeof accounts == 'undefined') throw new Error("连接失败,请检查`web3`参数!")
158 | console.log(accounts)
159 | }).catch(err => {
160 | console.log(err)
161 | })
162 | web3.isAddress(eth.account[0]) // 检测地址有效性
163 |
164 | // 4- 通过私钥获取账户地址
165 | let account3 = web3.eth.accounts.privateKeyToAccount("<私钥>")
166 | console.log(account3)
167 |
168 | // 5- 内存钱包添加账户:0xcB7419533E5470fA00Edf05dC46Dee44586010d2
169 | web3.eth.accounts.wallet.add("<私钥>")
170 |
171 | // 6- 本币转账交易:web3.eth
172 | let options = {
173 | from: "..",
174 | to: "..",
175 | value: 1000000000000000000,
176 | gas: 21000,
177 | gasPrice: 4
178 | }
179 | web3.eth.sendTransaction(options)
180 |
181 | // 7- 合约实例
182 | // const contract1json = require('./build/contracts/Xxx.json')
183 | // 参数一为合约的abi 参数二为合约地址,获取已经部署的合约对象
184 | const contract1 = new web3.eth.Contract(contract1json.abi, "地址")
185 | // 合约函数:https://web3js.readthedocs.io/en/v1.5.2/web3-eth-contract.html#id28
186 | // 交易参数:https://web3js.readthedocs.io/en/v1.5.2/web3-eth-contract.html#id14
187 | contract1.methods.xxx(实参).call({交易参数}, 回调函数); // 调用view或pure函数
188 | contract1.methods.xxx(实参).send({交易参数(至少from)}, 回调函数); // 调用函数
189 | // 部署合约
190 | // contract1.deploy(options)
191 | ```
192 | #### 合约实例
193 |
194 | ```js
195 | // 格式:new web3.eth.Contract(jsonInterface[, address][, options])
196 | - jsonInterface:[必选]合约abi
197 | - address:[可选]合约地址
198 | - options: [可选]交易时的默认参数
199 | let myContract = new web3.eth.Contract([...], '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe', // 可选
200 | {
201 | from: '0x1234567890123456789012345678901234567891',
202 | gasPrice: '20000000000'
203 | }
204 | );
205 | ```
206 |
207 | #### 合约属性
208 |
209 | ##### myContract.options
210 |
211 | - **address- String:读取或修改合约地址(返回值为 null 则表示address参数缺省)**
212 | - **jsonInterface- Array:合同的 json 接口,请参见 options.jsonInterface**
213 | - **data- String:合同的字节代码,用于部署合约**
214 | - **from**- String:地址交易应该来自
215 | - **gasPrice**- String:用于交易的天然气价格
216 | - **gas**- Number:为交易提供的最大气体(气体限制)
217 |
218 | #### 合约方法
219 |
220 | - clone:合约实例克隆
221 | - deploy..send:合约部署(完成后自动解析设置address参数),返回交易对象
222 | - methods.myMethod.call:合约函数调用(不修改合约状态,不需要Gas费)
223 | - methods.myMethod.send:合约函数调用(改变合约状态,需要Gas费)
224 | - methods.myMethod.estimateGas:合约函数调用时的预计Gas费
225 | - methods.myMethod.encodeABI:合约函数的调用签名
226 |
227 | ```js
228 | 格式: myContract.methods.myMethod(123)
229 | 带参数的名称: myContract.methods['myMethod(uint256)'](123)
230 | 签名: myContract.methods['0x58cf5f10'](123)
231 | ```
232 |
233 | - once:单次指定事件监听
234 | - events.MyEvent:指定事件监听
235 | - events.allEvents:任意事件监听
236 | - getPastEvents:历史事件检索
--------------------------------------------------------------------------------
/contracts/Token.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.0;
4 |
5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
6 |
7 | contract Token is ERC20 {
8 |
9 | constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) {
10 | _mint(msg.sender, 1000000000000000000000000);
11 | _mint(0x0000000000000000000000000000000000001004, 1000000000000000000000000);
12 | }
13 | }
--------------------------------------------------------------------------------
/contracts/uniswap-v2-core/UniswapV2ERC20.sol:
--------------------------------------------------------------------------------
1 | pragma solidity =0.5.16;
2 |
3 | import './interfaces/IUniswapV2ERC20.sol';
4 | import './libraries/SafeMath.sol';
5 |
6 | contract UniswapV2ERC20 is IUniswapV2ERC20 {
7 | using SafeMath for uint;
8 |
9 | string public constant name = 'Uniswap V2';
10 | string public constant symbol = 'UNI-V2';
11 | uint8 public constant decimals = 18;
12 | uint public totalSupply;
13 | mapping(address => uint) public balanceOf;
14 | mapping(address => mapping(address => uint)) public allowance;
15 |
16 | bytes32 public DOMAIN_SEPARATOR;
17 | // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
18 | bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
19 | mapping(address => uint) public nonces;
20 |
21 | event Approval(address indexed owner, address indexed spender, uint value);
22 | event Transfer(address indexed from, address indexed to, uint value);
23 |
24 | constructor() public {
25 | uint chainId;
26 | assembly {
27 | chainId := chainid
28 | }
29 | DOMAIN_SEPARATOR = keccak256(
30 | abi.encode(
31 | keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
32 | keccak256(bytes(name)),
33 | keccak256(bytes('1')),
34 | chainId,
35 | address(this)
36 | )
37 | );
38 | }
39 |
40 | function _mint(address to, uint value) internal {
41 | totalSupply = totalSupply.add(value);
42 | balanceOf[to] = balanceOf[to].add(value);
43 | emit Transfer(address(0), to, value);
44 | }
45 |
46 | function _burn(address from, uint value) internal {
47 | balanceOf[from] = balanceOf[from].sub(value);
48 | totalSupply = totalSupply.sub(value);
49 | emit Transfer(from, address(0), value);
50 | }
51 |
52 | function _approve(address owner, address spender, uint value) private {
53 | allowance[owner][spender] = value;
54 | emit Approval(owner, spender, value);
55 | }
56 |
57 | function _transfer(address from, address to, uint value) private {
58 | balanceOf[from] = balanceOf[from].sub(value);
59 | balanceOf[to] = balanceOf[to].add(value);
60 | emit Transfer(from, to, value);
61 | }
62 |
63 | function approve(address spender, uint value) external returns (bool) {
64 | _approve(msg.sender, spender, value);
65 | return true;
66 | }
67 |
68 | function transfer(address to, uint value) external returns (bool) {
69 | _transfer(msg.sender, to, value);
70 | return true;
71 | }
72 |
73 | function transferFrom(address from, address to, uint value) external returns (bool) {
74 | if (allowance[from][msg.sender] != uint(-1)) {
75 | allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
76 | }
77 | _transfer(from, to, value);
78 | return true;
79 | }
80 |
81 | function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
82 | require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
83 | bytes32 digest = keccak256(
84 | abi.encodePacked(
85 | '\x19\x01',
86 | DOMAIN_SEPARATOR,
87 | keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
88 | )
89 | );
90 | address recoveredAddress = ecrecover(digest, v, r, s);
91 | require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE');
92 | _approve(owner, spender, value);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-core/UniswapV2Factory.sol:
--------------------------------------------------------------------------------
1 | pragma solidity =0.5.16;
2 |
3 | import './interfaces/IUniswapV2Factory.sol';
4 | import './UniswapV2Pair.sol';
5 |
6 | contract UniswapV2Factory is IUniswapV2Factory {
7 | address public feeTo;
8 | address public feeToSetter;
9 |
10 | mapping(address => mapping(address => address)) public getPair;
11 | address[] public allPairs;
12 |
13 | event PairCreated(address indexed token0, address indexed token1, address pair, uint);
14 |
15 | constructor(address _feeToSetter) public {
16 | feeToSetter = _feeToSetter;
17 | }
18 |
19 | function allPairsLength() external view returns (uint) {
20 | return allPairs.length;
21 | }
22 |
23 | function createPair(address tokenA, address tokenB) external returns (address pair) {
24 | require(tokenA != tokenB, 'UniswapV2: IDENTICAL_ADDRESSES');
25 | (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
26 | require(token0 != address(0), 'UniswapV2: ZERO_ADDRESS');
27 | require(getPair[token0][token1] == address(0), 'UniswapV2: PAIR_EXISTS'); // single check is sufficient
28 | bytes memory bytecode = type(UniswapV2Pair).creationCode;
29 | bytes32 salt = keccak256(abi.encodePacked(token0, token1));
30 | assembly {
31 | pair := create2(0, add(bytecode, 32), mload(bytecode), salt)
32 | }
33 | IUniswapV2Pair(pair).initialize(token0, token1);
34 | getPair[token0][token1] = pair;
35 | getPair[token1][token0] = pair; // populate mapping in the reverse direction
36 | allPairs.push(pair);
37 | emit PairCreated(token0, token1, pair, allPairs.length);
38 | }
39 |
40 | function setFeeTo(address _feeTo) external {
41 | require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN');
42 | feeTo = _feeTo;
43 | }
44 |
45 | function setFeeToSetter(address _feeToSetter) external {
46 | require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN');
47 | feeToSetter = _feeToSetter;
48 | }
49 |
50 | function pairCodeHash() external pure returns (bytes32) {
51 | return keccak256(type(UniswapV2Pair).creationCode);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-core/UniswapV2Pair.sol:
--------------------------------------------------------------------------------
1 | pragma solidity =0.5.16;
2 |
3 | import './interfaces/IUniswapV2Pair.sol';
4 | import './UniswapV2ERC20.sol';
5 | import './libraries/Math.sol';
6 | import './libraries/UQ112x112.sol';
7 | import './interfaces/IERC20.sol';
8 | import './interfaces/IUniswapV2Factory.sol';
9 | import './interfaces/IUniswapV2Callee.sol';
10 |
11 | contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
12 | using SafeMath for uint;
13 | using UQ112x112 for uint224;
14 |
15 | uint public constant MINIMUM_LIQUIDITY = 10**3;
16 | bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));
17 |
18 | address public factory;
19 | address public token0;
20 | address public token1;
21 |
22 | uint112 private reserve0; // uses single storage slot, accessible via getReserves
23 | uint112 private reserve1; // uses single storage slot, accessible via getReserves
24 | uint32 private blockTimestampLast; // uses single storage slot, accessible via getReserves
25 |
26 | uint public price0CumulativeLast;
27 | uint public price1CumulativeLast;
28 | uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event
29 |
30 | uint private unlocked = 1;
31 | modifier lock() {
32 | require(unlocked == 1, 'UniswapV2: LOCKED');
33 | unlocked = 0;
34 | _;
35 | unlocked = 1;
36 | }
37 |
38 | function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
39 | _reserve0 = reserve0;
40 | _reserve1 = reserve1;
41 | _blockTimestampLast = blockTimestampLast;
42 | }
43 |
44 | function _safeTransfer(address token, address to, uint value) private {
45 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
46 | require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED');
47 | }
48 |
49 | event Mint(address indexed sender, uint amount0, uint amount1);
50 | event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
51 | event Swap(
52 | address indexed sender,
53 | uint amount0In,
54 | uint amount1In,
55 | uint amount0Out,
56 | uint amount1Out,
57 | address indexed to
58 | );
59 | event Sync(uint112 reserve0, uint112 reserve1);
60 |
61 | constructor() public {
62 | factory = msg.sender;
63 | }
64 |
65 | // called once by the factory at time of deployment
66 | function initialize(address _token0, address _token1) external {
67 | require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
68 | token0 = _token0;
69 | token1 = _token1;
70 | }
71 |
72 | // update reserves and, on the first call per block, price accumulators
73 | function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
74 | require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW');
75 | uint32 blockTimestamp = uint32(block.timestamp % 2**32);
76 | uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
77 | if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
78 | // * never overflows, and + overflow is desired
79 | price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
80 | price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
81 | }
82 | reserve0 = uint112(balance0);
83 | reserve1 = uint112(balance1);
84 | blockTimestampLast = blockTimestamp;
85 | emit Sync(reserve0, reserve1);
86 | }
87 |
88 | // if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)
89 | function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
90 | address feeTo = IUniswapV2Factory(factory).feeTo();
91 | feeOn = feeTo != address(0);
92 | uint _kLast = kLast; // gas savings
93 | if (feeOn) {
94 | if (_kLast != 0) {
95 | uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));
96 | uint rootKLast = Math.sqrt(_kLast);
97 | if (rootK > rootKLast) {
98 | uint numerator = totalSupply.mul(rootK.sub(rootKLast));
99 | uint denominator = rootK.mul(5).add(rootKLast);
100 | uint liquidity = numerator / denominator;
101 | if (liquidity > 0) _mint(feeTo, liquidity);
102 | }
103 | }
104 | } else if (_kLast != 0) {
105 | kLast = 0;
106 | }
107 | }
108 |
109 | // this low-level function should be called from a contract which performs important safety checks
110 | function mint(address to) external lock returns (uint liquidity) {
111 | (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
112 | uint balance0 = IERC20(token0).balanceOf(address(this));
113 | uint balance1 = IERC20(token1).balanceOf(address(this));
114 | uint amount0 = balance0.sub(_reserve0);
115 | uint amount1 = balance1.sub(_reserve1);
116 |
117 | bool feeOn = _mintFee(_reserve0, _reserve1);
118 | uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
119 | if (_totalSupply == 0) {
120 | liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
121 | _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
122 | } else {
123 | liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
124 | }
125 | require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
126 | _mint(to, liquidity);
127 |
128 | _update(balance0, balance1, _reserve0, _reserve1);
129 | if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
130 | emit Mint(msg.sender, amount0, amount1);
131 | }
132 |
133 | // this low-level function should be called from a contract which performs important safety checks
134 | function burn(address to) external lock returns (uint amount0, uint amount1) {
135 | (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
136 | address _token0 = token0; // gas savings
137 | address _token1 = token1; // gas savings
138 | uint balance0 = IERC20(_token0).balanceOf(address(this));
139 | uint balance1 = IERC20(_token1).balanceOf(address(this));
140 | uint liquidity = balanceOf[address(this)];
141 |
142 | bool feeOn = _mintFee(_reserve0, _reserve1);
143 | uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
144 | amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
145 | amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution
146 | require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED');
147 | _burn(address(this), liquidity);
148 | _safeTransfer(_token0, to, amount0);
149 | _safeTransfer(_token1, to, amount1);
150 | balance0 = IERC20(_token0).balanceOf(address(this));
151 | balance1 = IERC20(_token1).balanceOf(address(this));
152 |
153 | _update(balance0, balance1, _reserve0, _reserve1);
154 | if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
155 | emit Burn(msg.sender, amount0, amount1, to);
156 | }
157 |
158 | // this low-level function should be called from a contract which performs important safety checks
159 | function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
160 | require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
161 | (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
162 | require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');
163 |
164 | uint balance0;
165 | uint balance1;
166 | { // scope for _token{0,1}, avoids stack too deep errors
167 | address _token0 = token0;
168 | address _token1 = token1;
169 | require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
170 | if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
171 | if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
172 | if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
173 | balance0 = IERC20(_token0).balanceOf(address(this));
174 | balance1 = IERC20(_token1).balanceOf(address(this));
175 | }
176 | uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
177 | uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
178 | require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
179 | { // scope for reserve{0,1}Adjusted, avoids stack too deep errors
180 | uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
181 | uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
182 | require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
183 | }
184 |
185 | _update(balance0, balance1, _reserve0, _reserve1);
186 | emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
187 | }
188 |
189 | // force balances to match reserves
190 | function skim(address to) external lock {
191 | address _token0 = token0; // gas savings
192 | address _token1 = token1; // gas savings
193 | _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
194 | _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
195 | }
196 |
197 | // force reserves to match balances
198 | function sync() external lock {
199 | _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-core/interfaces/IERC20.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.5.0;
2 |
3 | interface IERC20 {
4 | event Approval(address indexed owner, address indexed spender, uint value);
5 | event Transfer(address indexed from, address indexed to, uint value);
6 |
7 | function name() external view returns (string memory);
8 | function symbol() external view returns (string memory);
9 | function decimals() external view returns (uint8);
10 | function totalSupply() external view returns (uint);
11 | function balanceOf(address owner) external view returns (uint);
12 | function allowance(address owner, address spender) external view returns (uint);
13 |
14 | function approve(address spender, uint value) external returns (bool);
15 | function transfer(address to, uint value) external returns (bool);
16 | function transferFrom(address from, address to, uint value) external returns (bool);
17 | }
18 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-core/interfaces/IUniswapV2Callee.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.5.0;
2 |
3 | interface IUniswapV2Callee {
4 | function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external;
5 | }
6 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-core/interfaces/IUniswapV2ERC20.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.5.0;
2 |
3 | interface IUniswapV2ERC20 {
4 | event Approval(address indexed owner, address indexed spender, uint value);
5 | event Transfer(address indexed from, address indexed to, uint value);
6 |
7 | function name() external pure returns (string memory);
8 | function symbol() external pure returns (string memory);
9 | function decimals() external pure returns (uint8);
10 | function totalSupply() external view returns (uint);
11 | function balanceOf(address owner) external view returns (uint);
12 | function allowance(address owner, address spender) external view returns (uint);
13 |
14 | function approve(address spender, uint value) external returns (bool);
15 | function transfer(address to, uint value) external returns (bool);
16 | function transferFrom(address from, address to, uint value) external returns (bool);
17 |
18 | function DOMAIN_SEPARATOR() external view returns (bytes32);
19 | function PERMIT_TYPEHASH() external pure returns (bytes32);
20 | function nonces(address owner) external view returns (uint);
21 |
22 | function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
23 | }
24 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-core/interfaces/IUniswapV2Factory.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.5.0;
2 |
3 | interface IUniswapV2Factory {
4 | event PairCreated(address indexed token0, address indexed token1, address pair, uint);
5 |
6 | function feeTo() external view returns (address);
7 | function feeToSetter() external view returns (address);
8 |
9 | function getPair(address tokenA, address tokenB) external view returns (address pair);
10 | function allPairs(uint) external view returns (address pair);
11 | function allPairsLength() external view returns (uint);
12 |
13 | function createPair(address tokenA, address tokenB) external returns (address pair);
14 |
15 | function setFeeTo(address) external;
16 | function setFeeToSetter(address) external;
17 | }
18 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-core/interfaces/IUniswapV2Pair.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.5.0;
2 |
3 | interface IUniswapV2Pair {
4 | event Approval(address indexed owner, address indexed spender, uint value);
5 | event Transfer(address indexed from, address indexed to, uint value);
6 |
7 | function name() external pure returns (string memory);
8 | function symbol() external pure returns (string memory);
9 | function decimals() external pure returns (uint8);
10 | function totalSupply() external view returns (uint);
11 | function balanceOf(address owner) external view returns (uint);
12 | function allowance(address owner, address spender) external view returns (uint);
13 |
14 | function approve(address spender, uint value) external returns (bool);
15 | function transfer(address to, uint value) external returns (bool);
16 | function transferFrom(address from, address to, uint value) external returns (bool);
17 |
18 | function DOMAIN_SEPARATOR() external view returns (bytes32);
19 | function PERMIT_TYPEHASH() external pure returns (bytes32);
20 | function nonces(address owner) external view returns (uint);
21 |
22 | function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
23 |
24 | event Mint(address indexed sender, uint amount0, uint amount1);
25 | event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
26 | event Swap(
27 | address indexed sender,
28 | uint amount0In,
29 | uint amount1In,
30 | uint amount0Out,
31 | uint amount1Out,
32 | address indexed to
33 | );
34 | event Sync(uint112 reserve0, uint112 reserve1);
35 |
36 | function MINIMUM_LIQUIDITY() external pure returns (uint);
37 | function factory() external view returns (address);
38 | function token0() external view returns (address);
39 | function token1() external view returns (address);
40 | function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
41 | function price0CumulativeLast() external view returns (uint);
42 | function price1CumulativeLast() external view returns (uint);
43 | function kLast() external view returns (uint);
44 |
45 | function mint(address to) external returns (uint liquidity);
46 | function burn(address to) external returns (uint amount0, uint amount1);
47 | function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
48 | function skim(address to) external;
49 | function sync() external;
50 |
51 | function initialize(address, address) external;
52 | }
53 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-core/libraries/Math.sol:
--------------------------------------------------------------------------------
1 | pragma solidity =0.5.16;
2 |
3 | // a library for performing various math operations
4 |
5 | library Math {
6 | function min(uint x, uint y) internal pure returns (uint z) {
7 | z = x < y ? x : y;
8 | }
9 |
10 | // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
11 | function sqrt(uint y) internal pure returns (uint z) {
12 | if (y > 3) {
13 | z = y;
14 | uint x = y / 2 + 1;
15 | while (x < z) {
16 | z = x;
17 | x = (y / x + x) / 2;
18 | }
19 | } else if (y != 0) {
20 | z = 1;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-core/libraries/SafeMath.sol:
--------------------------------------------------------------------------------
1 | pragma solidity =0.5.16;
2 |
3 | // a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
4 |
5 | library SafeMath {
6 | function add(uint x, uint y) internal pure returns (uint z) {
7 | require((z = x + y) >= x, 'ds-math-add-overflow');
8 | }
9 |
10 | function sub(uint x, uint y) internal pure returns (uint z) {
11 | require((z = x - y) <= x, 'ds-math-sub-underflow');
12 | }
13 |
14 | function mul(uint x, uint y) internal pure returns (uint z) {
15 | require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-core/libraries/UQ112x112.sol:
--------------------------------------------------------------------------------
1 | pragma solidity =0.5.16;
2 |
3 | // a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
4 |
5 | // range: [0, 2**112 - 1]
6 | // resolution: 1 / 2**112
7 |
8 | library UQ112x112 {
9 | uint224 constant Q112 = 2**112;
10 |
11 | // encode a uint112 as a UQ112x112
12 | function encode(uint112 y) internal pure returns (uint224 z) {
13 | z = uint224(y) * Q112; // never overflows
14 | }
15 |
16 | // divide a UQ112x112 by a uint112, returning a UQ112x112
17 | function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
18 | z = x / uint224(y);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-core/test/ERC20.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity =0.5.16;
3 |
4 | import '../UniswapV2ERC20.sol';
5 |
6 | contract ERC20 is UniswapV2ERC20 {
7 | constructor(uint _totalSupply) public {
8 | _mint(msg.sender, _totalSupply);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/UniswapV2Migrator.sol:
--------------------------------------------------------------------------------
1 | pragma solidity =0.6.6;
2 |
3 | import '@uniswap/lib/contracts/libraries/TransferHelper.sol';
4 |
5 | import './interfaces/IUniswapV2Migrator.sol';
6 | import './interfaces/V1/IUniswapV1Factory.sol';
7 | import './interfaces/V1/IUniswapV1Exchange.sol';
8 | import './interfaces/IUniswapV2Router01.sol';
9 | import './interfaces/IERC20.sol';
10 |
11 | contract UniswapV2Migrator is IUniswapV2Migrator {
12 | IUniswapV1Factory immutable factoryV1;
13 | IUniswapV2Router01 immutable router;
14 |
15 | constructor(address _factoryV1, address _router) public {
16 | factoryV1 = IUniswapV1Factory(_factoryV1);
17 | router = IUniswapV2Router01(_router);
18 | }
19 |
20 | // needs to accept ETH from any v1 exchange and the router. ideally this could be enforced, as in the router,
21 | // but it's not possible because it requires a call to the v1 factory, which takes too much gas
22 | receive() external payable {}
23 |
24 | function migrate(address token, uint amountTokenMin, uint amountETHMin, address to, uint deadline)
25 | external
26 | override
27 | {
28 | IUniswapV1Exchange exchangeV1 = IUniswapV1Exchange(factoryV1.getExchange(token));
29 | uint liquidityV1 = exchangeV1.balanceOf(msg.sender);
30 | require(exchangeV1.transferFrom(msg.sender, address(this), liquidityV1), 'TRANSFER_FROM_FAILED');
31 | (uint amountETHV1, uint amountTokenV1) = exchangeV1.removeLiquidity(liquidityV1, 1, 1, uint(-1));
32 | TransferHelper.safeApprove(token, address(router), amountTokenV1);
33 | (uint amountTokenV2, uint amountETHV2,) = router.addLiquidityETH{value: amountETHV1}(
34 | token,
35 | amountTokenV1,
36 | amountTokenMin,
37 | amountETHMin,
38 | to,
39 | deadline
40 | );
41 | if (amountTokenV1 > amountTokenV2) {
42 | TransferHelper.safeApprove(token, address(router), 0); // be a good blockchain citizen, reset allowance to 0
43 | TransferHelper.safeTransfer(token, msg.sender, amountTokenV1 - amountTokenV2);
44 | } else if (amountETHV1 > amountETHV2) {
45 | // addLiquidityETH guarantees that all of amountETHV1 or amountTokenV1 will be used, hence this else is safe
46 | TransferHelper.safeTransferETH(msg.sender, amountETHV1 - amountETHV2);
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/UniswapV2Router01.sol:
--------------------------------------------------------------------------------
1 | pragma solidity =0.6.6;
2 |
3 | import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol';
4 | import '@uniswap/lib/contracts/libraries/TransferHelper.sol';
5 |
6 | import './libraries/UniswapV2Library.sol';
7 | import './interfaces/IUniswapV2Router01.sol';
8 | import './interfaces/IERC20.sol';
9 | import './interfaces/IWETH.sol';
10 |
11 | contract UniswapV2Router01 is IUniswapV2Router01 {
12 | address public immutable override factory;
13 | address public immutable override WETH;
14 |
15 | modifier ensure(uint deadline) {
16 | require(deadline >= block.timestamp, 'UniswapV2Router: EXPIRED');
17 | _;
18 | }
19 |
20 | constructor(address _factory, address _WETH) public {
21 | factory = _factory;
22 | WETH = _WETH;
23 | }
24 |
25 | receive() external payable {
26 | assert(msg.sender == WETH); // only accept ETH via fallback from the WETH contract
27 | }
28 |
29 | // **** ADD LIQUIDITY ****
30 | function _addLiquidity(
31 | address tokenA,
32 | address tokenB,
33 | uint amountADesired,
34 | uint amountBDesired,
35 | uint amountAMin,
36 | uint amountBMin
37 | ) private returns (uint amountA, uint amountB) {
38 | // create the pair if it doesn't exist yet
39 | if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) {
40 | IUniswapV2Factory(factory).createPair(tokenA, tokenB);
41 | }
42 | (uint reserveA, uint reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);
43 | if (reserveA == 0 && reserveB == 0) {
44 | (amountA, amountB) = (amountADesired, amountBDesired);
45 | } else {
46 | uint amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB);
47 | if (amountBOptimal <= amountBDesired) {
48 | require(amountBOptimal >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
49 | (amountA, amountB) = (amountADesired, amountBOptimal);
50 | } else {
51 | uint amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA);
52 | assert(amountAOptimal <= amountADesired);
53 | require(amountAOptimal >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
54 | (amountA, amountB) = (amountAOptimal, amountBDesired);
55 | }
56 | }
57 | }
58 | function addLiquidity(
59 | address tokenA,
60 | address tokenB,
61 | uint amountADesired,
62 | uint amountBDesired,
63 | uint amountAMin,
64 | uint amountBMin,
65 | address to,
66 | uint deadline
67 | ) external override ensure(deadline) returns (uint amountA, uint amountB, uint liquidity) {
68 | (amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin);
69 | address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
70 | TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA);
71 | TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB);
72 | liquidity = IUniswapV2Pair(pair).mint(to);
73 | }
74 | function addLiquidityETH(
75 | address token,
76 | uint amountTokenDesired,
77 | uint amountTokenMin,
78 | uint amountETHMin,
79 | address to,
80 | uint deadline
81 | ) external override payable ensure(deadline) returns (uint amountToken, uint amountETH, uint liquidity) {
82 | (amountToken, amountETH) = _addLiquidity(
83 | token,
84 | WETH,
85 | amountTokenDesired,
86 | msg.value,
87 | amountTokenMin,
88 | amountETHMin
89 | );
90 | address pair = UniswapV2Library.pairFor(factory, token, WETH);
91 | TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken);
92 | IWETH(WETH).deposit{value: amountETH}();
93 | assert(IWETH(WETH).transfer(pair, amountETH));
94 | liquidity = IUniswapV2Pair(pair).mint(to);
95 | if (msg.value > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH); // refund dust eth, if any
96 | }
97 |
98 | // **** REMOVE LIQUIDITY ****
99 | function removeLiquidity(
100 | address tokenA,
101 | address tokenB,
102 | uint liquidity,
103 | uint amountAMin,
104 | uint amountBMin,
105 | address to,
106 | uint deadline
107 | ) public override ensure(deadline) returns (uint amountA, uint amountB) {
108 | address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
109 | IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair
110 | (uint amount0, uint amount1) = IUniswapV2Pair(pair).burn(to);
111 | (address token0,) = UniswapV2Library.sortTokens(tokenA, tokenB);
112 | (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);
113 | require(amountA >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
114 | require(amountB >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
115 | }
116 | function removeLiquidityETH(
117 | address token,
118 | uint liquidity,
119 | uint amountTokenMin,
120 | uint amountETHMin,
121 | address to,
122 | uint deadline
123 | ) public override ensure(deadline) returns (uint amountToken, uint amountETH) {
124 | (amountToken, amountETH) = removeLiquidity(
125 | token,
126 | WETH,
127 | liquidity,
128 | amountTokenMin,
129 | amountETHMin,
130 | address(this),
131 | deadline
132 | );
133 | TransferHelper.safeTransfer(token, to, amountToken);
134 | IWETH(WETH).withdraw(amountETH);
135 | TransferHelper.safeTransferETH(to, amountETH);
136 | }
137 | function removeLiquidityWithPermit(
138 | address tokenA,
139 | address tokenB,
140 | uint liquidity,
141 | uint amountAMin,
142 | uint amountBMin,
143 | address to,
144 | uint deadline,
145 | bool approveMax, uint8 v, bytes32 r, bytes32 s
146 | ) external override returns (uint amountA, uint amountB) {
147 | address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
148 | uint value = approveMax ? uint(-1) : liquidity;
149 | IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
150 | (amountA, amountB) = removeLiquidity(tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline);
151 | }
152 | function removeLiquidityETHWithPermit(
153 | address token,
154 | uint liquidity,
155 | uint amountTokenMin,
156 | uint amountETHMin,
157 | address to,
158 | uint deadline,
159 | bool approveMax, uint8 v, bytes32 r, bytes32 s
160 | ) external override returns (uint amountToken, uint amountETH) {
161 | address pair = UniswapV2Library.pairFor(factory, token, WETH);
162 | uint value = approveMax ? uint(-1) : liquidity;
163 | IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
164 | (amountToken, amountETH) = removeLiquidityETH(token, liquidity, amountTokenMin, amountETHMin, to, deadline);
165 | }
166 |
167 | // **** SWAP ****
168 | // requires the initial amount to have already been sent to the first pair
169 | function _swap(uint[] memory amounts, address[] memory path, address _to) private {
170 | for (uint i; i < path.length - 1; i++) {
171 | (address input, address output) = (path[i], path[i + 1]);
172 | (address token0,) = UniswapV2Library.sortTokens(input, output);
173 | uint amountOut = amounts[i + 1];
174 | (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0));
175 | address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to;
176 | IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output)).swap(amount0Out, amount1Out, to, new bytes(0));
177 | }
178 | }
179 | function swapExactTokensForTokens(
180 | uint amountIn,
181 | uint amountOutMin,
182 | address[] calldata path,
183 | address to,
184 | uint deadline
185 | ) external override ensure(deadline) returns (uint[] memory amounts) {
186 | amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
187 | require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
188 | TransferHelper.safeTransferFrom(path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]);
189 | _swap(amounts, path, to);
190 | }
191 | function swapTokensForExactTokens(
192 | uint amountOut,
193 | uint amountInMax,
194 | address[] calldata path,
195 | address to,
196 | uint deadline
197 | ) external override ensure(deadline) returns (uint[] memory amounts) {
198 | amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
199 | require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
200 | TransferHelper.safeTransferFrom(path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]);
201 | _swap(amounts, path, to);
202 | }
203 | function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
204 | external
205 | override
206 | payable
207 | ensure(deadline)
208 | returns (uint[] memory amounts)
209 | {
210 | require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
211 | amounts = UniswapV2Library.getAmountsOut(factory, msg.value, path);
212 | require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
213 | IWETH(WETH).deposit{value: amounts[0]}();
214 | assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));
215 | _swap(amounts, path, to);
216 | }
217 | function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
218 | external
219 | override
220 | ensure(deadline)
221 | returns (uint[] memory amounts)
222 | {
223 | require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
224 | amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
225 | require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
226 | TransferHelper.safeTransferFrom(path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]);
227 | _swap(amounts, path, address(this));
228 | IWETH(WETH).withdraw(amounts[amounts.length - 1]);
229 | TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
230 | }
231 | function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
232 | external
233 | override
234 | ensure(deadline)
235 | returns (uint[] memory amounts)
236 | {
237 | require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
238 | amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
239 | require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
240 | TransferHelper.safeTransferFrom(path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]);
241 | _swap(amounts, path, address(this));
242 | IWETH(WETH).withdraw(amounts[amounts.length - 1]);
243 | TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
244 | }
245 | function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
246 | external
247 | override
248 | payable
249 | ensure(deadline)
250 | returns (uint[] memory amounts)
251 | {
252 | require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
253 | amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
254 | require(amounts[0] <= msg.value, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
255 | IWETH(WETH).deposit{value: amounts[0]}();
256 | assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));
257 | _swap(amounts, path, to);
258 | if (msg.value > amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]); // refund dust eth, if any
259 | }
260 |
261 | function quote(uint amountA, uint reserveA, uint reserveB) public pure override returns (uint amountB) {
262 | return UniswapV2Library.quote(amountA, reserveA, reserveB);
263 | }
264 |
265 | function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) public pure override returns (uint amountOut) {
266 | return UniswapV2Library.getAmountOut(amountIn, reserveIn, reserveOut);
267 | }
268 |
269 | function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) public pure override returns (uint amountIn) {
270 | return UniswapV2Library.getAmountOut(amountOut, reserveIn, reserveOut);
271 | }
272 |
273 | function getAmountsOut(uint amountIn, address[] memory path) public view override returns (uint[] memory amounts) {
274 | return UniswapV2Library.getAmountsOut(factory, amountIn, path);
275 | }
276 |
277 | function getAmountsIn(uint amountOut, address[] memory path) public view override returns (uint[] memory amounts) {
278 | return UniswapV2Library.getAmountsIn(factory, amountOut, path);
279 | }
280 | }
281 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/UniswapV2Router02.sol:
--------------------------------------------------------------------------------
1 | pragma solidity =0.6.6;
2 |
3 | import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol';
4 | import '@uniswap/lib/contracts/libraries/TransferHelper.sol';
5 |
6 | import './interfaces/IUniswapV2Router02.sol';
7 | import './libraries/UniswapV2Library.sol';
8 | import './libraries/SafeMath.sol';
9 | import './interfaces/IERC20.sol';
10 | import './interfaces/IWETH.sol';
11 |
12 | contract UniswapV2Router02 is IUniswapV2Router02 {
13 | using SafeMath for uint;
14 |
15 | address public immutable override factory;
16 | address public immutable override WETH;
17 |
18 | modifier ensure(uint deadline) {
19 | require(deadline >= block.timestamp, 'UniswapV2Router: EXPIRED');
20 | _;
21 | }
22 |
23 | constructor(address _factory, address _WETH) public {
24 | factory = _factory;
25 | WETH = _WETH;
26 | }
27 |
28 | receive() external payable {
29 | assert(msg.sender == WETH); // only accept ETH via fallback from the WETH contract
30 | }
31 |
32 | // **** ADD LIQUIDITY ****
33 | function _addLiquidity(
34 | address tokenA,
35 | address tokenB,
36 | uint amountADesired,
37 | uint amountBDesired,
38 | uint amountAMin,
39 | uint amountBMin
40 | ) internal virtual returns (uint amountA, uint amountB) {
41 | // create the pair if it doesn't exist yet
42 | if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) {
43 | IUniswapV2Factory(factory).createPair(tokenA, tokenB);
44 | }
45 | (uint reserveA, uint reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);
46 | if (reserveA == 0 && reserveB == 0) {
47 | (amountA, amountB) = (amountADesired, amountBDesired);
48 | } else {
49 | uint amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB);
50 | if (amountBOptimal <= amountBDesired) {
51 | require(amountBOptimal >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
52 | (amountA, amountB) = (amountADesired, amountBOptimal);
53 | } else {
54 | uint amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA);
55 | assert(amountAOptimal <= amountADesired);
56 | require(amountAOptimal >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
57 | (amountA, amountB) = (amountAOptimal, amountBDesired);
58 | }
59 | }
60 | }
61 | function addLiquidity(
62 | address tokenA,
63 | address tokenB,
64 | uint amountADesired,
65 | uint amountBDesired,
66 | uint amountAMin,
67 | uint amountBMin,
68 | address to,
69 | uint deadline
70 | ) external virtual override ensure(deadline) returns (uint amountA, uint amountB, uint liquidity) {
71 | (amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin);
72 | address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
73 | TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA);
74 | TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB);
75 | liquidity = IUniswapV2Pair(pair).mint(to);
76 | }
77 | function addLiquidityETH(
78 | address token,
79 | uint amountTokenDesired,
80 | uint amountTokenMin,
81 | uint amountETHMin,
82 | address to,
83 | uint deadline
84 | ) external virtual override payable ensure(deadline) returns (uint amountToken, uint amountETH, uint liquidity) {
85 | (amountToken, amountETH) = _addLiquidity(
86 | token,
87 | WETH,
88 | amountTokenDesired,
89 | msg.value,
90 | amountTokenMin,
91 | amountETHMin
92 | );
93 | address pair = UniswapV2Library.pairFor(factory, token, WETH);
94 | TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken);
95 | IWETH(WETH).deposit{value: amountETH}();
96 | assert(IWETH(WETH).transfer(pair, amountETH));
97 | liquidity = IUniswapV2Pair(pair).mint(to);
98 | // refund dust eth, if any
99 | if (msg.value > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH);
100 | }
101 |
102 | // **** REMOVE LIQUIDITY ****
103 | function removeLiquidity(
104 | address tokenA,
105 | address tokenB,
106 | uint liquidity,
107 | uint amountAMin,
108 | uint amountBMin,
109 | address to,
110 | uint deadline
111 | ) public virtual override ensure(deadline) returns (uint amountA, uint amountB) {
112 | address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
113 | IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair
114 | (uint amount0, uint amount1) = IUniswapV2Pair(pair).burn(to);
115 | (address token0,) = UniswapV2Library.sortTokens(tokenA, tokenB);
116 | (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);
117 | require(amountA >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
118 | require(amountB >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
119 | }
120 | function removeLiquidityETH(
121 | address token,
122 | uint liquidity,
123 | uint amountTokenMin,
124 | uint amountETHMin,
125 | address to,
126 | uint deadline
127 | ) public virtual override ensure(deadline) returns (uint amountToken, uint amountETH) {
128 | (amountToken, amountETH) = removeLiquidity(
129 | token,
130 | WETH,
131 | liquidity,
132 | amountTokenMin,
133 | amountETHMin,
134 | address(this),
135 | deadline
136 | );
137 | TransferHelper.safeTransfer(token, to, amountToken);
138 | IWETH(WETH).withdraw(amountETH);
139 | TransferHelper.safeTransferETH(to, amountETH);
140 | }
141 | function removeLiquidityWithPermit(
142 | address tokenA,
143 | address tokenB,
144 | uint liquidity,
145 | uint amountAMin,
146 | uint amountBMin,
147 | address to,
148 | uint deadline,
149 | bool approveMax, uint8 v, bytes32 r, bytes32 s
150 | ) external virtual override returns (uint amountA, uint amountB) {
151 | address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
152 | uint value = approveMax ? uint(-1) : liquidity;
153 | IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
154 | (amountA, amountB) = removeLiquidity(tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline);
155 | }
156 | function removeLiquidityETHWithPermit(
157 | address token,
158 | uint liquidity,
159 | uint amountTokenMin,
160 | uint amountETHMin,
161 | address to,
162 | uint deadline,
163 | bool approveMax, uint8 v, bytes32 r, bytes32 s
164 | ) external virtual override returns (uint amountToken, uint amountETH) {
165 | address pair = UniswapV2Library.pairFor(factory, token, WETH);
166 | uint value = approveMax ? uint(-1) : liquidity;
167 | IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
168 | (amountToken, amountETH) = removeLiquidityETH(token, liquidity, amountTokenMin, amountETHMin, to, deadline);
169 | }
170 |
171 | // **** REMOVE LIQUIDITY (supporting fee-on-transfer tokens) ****
172 | function removeLiquidityETHSupportingFeeOnTransferTokens(
173 | address token,
174 | uint liquidity,
175 | uint amountTokenMin,
176 | uint amountETHMin,
177 | address to,
178 | uint deadline
179 | ) public virtual override ensure(deadline) returns (uint amountETH) {
180 | (, amountETH) = removeLiquidity(
181 | token,
182 | WETH,
183 | liquidity,
184 | amountTokenMin,
185 | amountETHMin,
186 | address(this),
187 | deadline
188 | );
189 | TransferHelper.safeTransfer(token, to, IERC20(token).balanceOf(address(this)));
190 | IWETH(WETH).withdraw(amountETH);
191 | TransferHelper.safeTransferETH(to, amountETH);
192 | }
193 | function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
194 | address token,
195 | uint liquidity,
196 | uint amountTokenMin,
197 | uint amountETHMin,
198 | address to,
199 | uint deadline,
200 | bool approveMax, uint8 v, bytes32 r, bytes32 s
201 | ) external virtual override returns (uint amountETH) {
202 | address pair = UniswapV2Library.pairFor(factory, token, WETH);
203 | uint value = approveMax ? uint(-1) : liquidity;
204 | IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
205 | amountETH = removeLiquidityETHSupportingFeeOnTransferTokens(
206 | token, liquidity, amountTokenMin, amountETHMin, to, deadline
207 | );
208 | }
209 |
210 | // **** SWAP ****
211 | // requires the initial amount to have already been sent to the first pair
212 | function _swap(uint[] memory amounts, address[] memory path, address _to) internal virtual {
213 | for (uint i; i < path.length - 1; i++) {
214 | (address input, address output) = (path[i], path[i + 1]);
215 | (address token0,) = UniswapV2Library.sortTokens(input, output);
216 | uint amountOut = amounts[i + 1];
217 | (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0));
218 | address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to;
219 | IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output)).swap(
220 | amount0Out, amount1Out, to, new bytes(0)
221 | );
222 | }
223 | }
224 | function swapExactTokensForTokens(
225 | uint amountIn,
226 | uint amountOutMin,
227 | address[] calldata path,
228 | address to,
229 | uint deadline
230 | ) external virtual override ensure(deadline) returns (uint[] memory amounts) {
231 | amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
232 | require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
233 | TransferHelper.safeTransferFrom(
234 | path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
235 | );
236 | _swap(amounts, path, to);
237 | }
238 | function swapTokensForExactTokens(
239 | uint amountOut,
240 | uint amountInMax,
241 | address[] calldata path,
242 | address to,
243 | uint deadline
244 | ) external virtual override ensure(deadline) returns (uint[] memory amounts) {
245 | amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
246 | require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
247 | TransferHelper.safeTransferFrom(
248 | path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
249 | );
250 | _swap(amounts, path, to);
251 | }
252 | function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
253 | external
254 | virtual
255 | override
256 | payable
257 | ensure(deadline)
258 | returns (uint[] memory amounts)
259 | {
260 | require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
261 | amounts = UniswapV2Library.getAmountsOut(factory, msg.value, path);
262 | require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
263 | IWETH(WETH).deposit{value: amounts[0]}();
264 | assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));
265 | _swap(amounts, path, to);
266 | }
267 | function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
268 | external
269 | virtual
270 | override
271 | ensure(deadline)
272 | returns (uint[] memory amounts)
273 | {
274 | require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
275 | amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
276 | require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
277 | TransferHelper.safeTransferFrom(
278 | path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
279 | );
280 | _swap(amounts, path, address(this));
281 | IWETH(WETH).withdraw(amounts[amounts.length - 1]);
282 | TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
283 | }
284 | function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
285 | external
286 | virtual
287 | override
288 | ensure(deadline)
289 | returns (uint[] memory amounts)
290 | {
291 | require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
292 | amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
293 | require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
294 | TransferHelper.safeTransferFrom(
295 | path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
296 | );
297 | _swap(amounts, path, address(this));
298 | IWETH(WETH).withdraw(amounts[amounts.length - 1]);
299 | TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
300 | }
301 | function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
302 | external
303 | virtual
304 | override
305 | payable
306 | ensure(deadline)
307 | returns (uint[] memory amounts)
308 | {
309 | require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
310 | amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
311 | require(amounts[0] <= msg.value, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
312 | IWETH(WETH).deposit{value: amounts[0]}();
313 | assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));
314 | _swap(amounts, path, to);
315 | // refund dust eth, if any
316 | if (msg.value > amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]);
317 | }
318 |
319 | // **** SWAP (supporting fee-on-transfer tokens) ****
320 | // requires the initial amount to have already been sent to the first pair
321 | function _swapSupportingFeeOnTransferTokens(address[] memory path, address _to) internal virtual {
322 | for (uint i; i < path.length - 1; i++) {
323 | (address input, address output) = (path[i], path[i + 1]);
324 | (address token0,) = UniswapV2Library.sortTokens(input, output);
325 | IUniswapV2Pair pair = IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output));
326 | uint amountInput;
327 | uint amountOutput;
328 | { // scope to avoid stack too deep errors
329 | (uint reserve0, uint reserve1,) = pair.getReserves();
330 | (uint reserveInput, uint reserveOutput) = input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
331 | amountInput = IERC20(input).balanceOf(address(pair)).sub(reserveInput);
332 | amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput);
333 | }
334 | (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOutput) : (amountOutput, uint(0));
335 | address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to;
336 | pair.swap(amount0Out, amount1Out, to, new bytes(0));
337 | }
338 | }
339 | function swapExactTokensForTokensSupportingFeeOnTransferTokens(
340 | uint amountIn,
341 | uint amountOutMin,
342 | address[] calldata path,
343 | address to,
344 | uint deadline
345 | ) external virtual override ensure(deadline) {
346 | TransferHelper.safeTransferFrom(
347 | path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn
348 | );
349 | uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
350 | _swapSupportingFeeOnTransferTokens(path, to);
351 | require(
352 | IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin,
353 | 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'
354 | );
355 | }
356 | function swapExactETHForTokensSupportingFeeOnTransferTokens(
357 | uint amountOutMin,
358 | address[] calldata path,
359 | address to,
360 | uint deadline
361 | )
362 | external
363 | virtual
364 | override
365 | payable
366 | ensure(deadline)
367 | {
368 | require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
369 | uint amountIn = msg.value;
370 | IWETH(WETH).deposit{value: amountIn}();
371 | assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn));
372 | uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
373 | _swapSupportingFeeOnTransferTokens(path, to);
374 | require(
375 | IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin,
376 | 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'
377 | );
378 | }
379 | function swapExactTokensForETHSupportingFeeOnTransferTokens(
380 | uint amountIn,
381 | uint amountOutMin,
382 | address[] calldata path,
383 | address to,
384 | uint deadline
385 | )
386 | external
387 | virtual
388 | override
389 | ensure(deadline)
390 | {
391 | require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
392 | TransferHelper.safeTransferFrom(
393 | path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn
394 | );
395 | _swapSupportingFeeOnTransferTokens(path, address(this));
396 | uint amountOut = IERC20(WETH).balanceOf(address(this));
397 | require(amountOut >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
398 | IWETH(WETH).withdraw(amountOut);
399 | TransferHelper.safeTransferETH(to, amountOut);
400 | }
401 |
402 | // **** LIBRARY FUNCTIONS ****
403 | function quote(uint amountA, uint reserveA, uint reserveB) public pure virtual override returns (uint amountB) {
404 | return UniswapV2Library.quote(amountA, reserveA, reserveB);
405 | }
406 |
407 | function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut)
408 | public
409 | pure
410 | virtual
411 | override
412 | returns (uint amountOut)
413 | {
414 | return UniswapV2Library.getAmountOut(amountIn, reserveIn, reserveOut);
415 | }
416 |
417 | function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut)
418 | public
419 | pure
420 | virtual
421 | override
422 | returns (uint amountIn)
423 | {
424 | return UniswapV2Library.getAmountIn(amountOut, reserveIn, reserveOut);
425 | }
426 |
427 | function getAmountsOut(uint amountIn, address[] memory path)
428 | public
429 | view
430 | virtual
431 | override
432 | returns (uint[] memory amounts)
433 | {
434 | return UniswapV2Library.getAmountsOut(factory, amountIn, path);
435 | }
436 |
437 | function getAmountsIn(uint amountOut, address[] memory path)
438 | public
439 | view
440 | virtual
441 | override
442 | returns (uint[] memory amounts)
443 | {
444 | return UniswapV2Library.getAmountsIn(factory, amountOut, path);
445 | }
446 | }
447 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/examples/ExampleComputeLiquidityValue.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity =0.6.6;
3 |
4 | import '../libraries/UniswapV2LiquidityMathLibrary.sol';
5 |
6 | contract ExampleComputeLiquidityValue {
7 | using SafeMath for uint256;
8 |
9 | address public immutable factory;
10 |
11 | constructor(address factory_) public {
12 | factory = factory_;
13 | }
14 |
15 | // see UniswapV2LiquidityMathLibrary#getReservesAfterArbitrage
16 | function getReservesAfterArbitrage(
17 | address tokenA,
18 | address tokenB,
19 | uint256 truePriceTokenA,
20 | uint256 truePriceTokenB
21 | ) external view returns (uint256 reserveA, uint256 reserveB) {
22 | return UniswapV2LiquidityMathLibrary.getReservesAfterArbitrage(
23 | factory,
24 | tokenA,
25 | tokenB,
26 | truePriceTokenA,
27 | truePriceTokenB
28 | );
29 | }
30 |
31 | // see UniswapV2LiquidityMathLibrary#getLiquidityValue
32 | function getLiquidityValue(
33 | address tokenA,
34 | address tokenB,
35 | uint256 liquidityAmount
36 | ) external view returns (
37 | uint256 tokenAAmount,
38 | uint256 tokenBAmount
39 | ) {
40 | return UniswapV2LiquidityMathLibrary.getLiquidityValue(
41 | factory,
42 | tokenA,
43 | tokenB,
44 | liquidityAmount
45 | );
46 | }
47 |
48 | // see UniswapV2LiquidityMathLibrary#getLiquidityValueAfterArbitrageToPrice
49 | function getLiquidityValueAfterArbitrageToPrice(
50 | address tokenA,
51 | address tokenB,
52 | uint256 truePriceTokenA,
53 | uint256 truePriceTokenB,
54 | uint256 liquidityAmount
55 | ) external view returns (
56 | uint256 tokenAAmount,
57 | uint256 tokenBAmount
58 | ) {
59 | return UniswapV2LiquidityMathLibrary.getLiquidityValueAfterArbitrageToPrice(
60 | factory,
61 | tokenA,
62 | tokenB,
63 | truePriceTokenA,
64 | truePriceTokenB,
65 | liquidityAmount
66 | );
67 | }
68 |
69 | // test function to measure the gas cost of the above function
70 | function getGasCostOfGetLiquidityValueAfterArbitrageToPrice(
71 | address tokenA,
72 | address tokenB,
73 | uint256 truePriceTokenA,
74 | uint256 truePriceTokenB,
75 | uint256 liquidityAmount
76 | ) external view returns (
77 | uint256
78 | ) {
79 | uint gasBefore = gasleft();
80 | UniswapV2LiquidityMathLibrary.getLiquidityValueAfterArbitrageToPrice(
81 | factory,
82 | tokenA,
83 | tokenB,
84 | truePriceTokenA,
85 | truePriceTokenB,
86 | liquidityAmount
87 | );
88 | uint gasAfter = gasleft();
89 | return gasBefore - gasAfter;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/examples/ExampleFlashSwap.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity =0.6.6;
3 |
4 | import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Callee.sol';
5 |
6 | import '../libraries/UniswapV2Library.sol';
7 | import '../interfaces/V1/IUniswapV1Factory.sol';
8 | import '../interfaces/V1/IUniswapV1Exchange.sol';
9 | import '../interfaces/IUniswapV2Router01.sol';
10 | import '../interfaces/IERC20.sol';
11 | import '../interfaces/IWETH.sol';
12 |
13 | contract ExampleFlashSwap is IUniswapV2Callee {
14 | IUniswapV1Factory immutable factoryV1;
15 | address immutable factory;
16 | IWETH immutable WETH;
17 |
18 | constructor(address _factory, address _factoryV1, address router) public {
19 | factoryV1 = IUniswapV1Factory(_factoryV1);
20 | factory = _factory;
21 | WETH = IWETH(IUniswapV2Router01(router).WETH());
22 | }
23 |
24 | // needs to accept ETH from any V1 exchange and WETH. ideally this could be enforced, as in the router,
25 | // but it's not possible because it requires a call to the v1 factory, which takes too much gas
26 | receive() external payable {}
27 |
28 | // gets tokens/WETH via a V2 flash swap, swaps for the ETH/tokens on V1, repays V2, and keeps the rest!
29 | function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external override {
30 | address[] memory path = new address[](2);
31 | uint amountToken;
32 | uint amountETH;
33 | { // scope for token{0,1}, avoids stack too deep errors
34 | address token0 = IUniswapV2Pair(msg.sender).token0();
35 | address token1 = IUniswapV2Pair(msg.sender).token1();
36 | assert(msg.sender == UniswapV2Library.pairFor(factory, token0, token1)); // ensure that msg.sender is actually a V2 pair
37 | assert(amount0 == 0 || amount1 == 0); // this strategy is unidirectional
38 | path[0] = amount0 == 0 ? token0 : token1;
39 | path[1] = amount0 == 0 ? token1 : token0;
40 | amountToken = token0 == address(WETH) ? amount1 : amount0;
41 | amountETH = token0 == address(WETH) ? amount0 : amount1;
42 | }
43 |
44 | assert(path[0] == address(WETH) || path[1] == address(WETH)); // this strategy only works with a V2 WETH pair
45 | IERC20 token = IERC20(path[0] == address(WETH) ? path[1] : path[0]);
46 | IUniswapV1Exchange exchangeV1 = IUniswapV1Exchange(factoryV1.getExchange(address(token))); // get V1 exchange
47 |
48 | if (amountToken > 0) {
49 | (uint minETH) = abi.decode(data, (uint)); // slippage parameter for V1, passed in by caller
50 | token.approve(address(exchangeV1), amountToken);
51 | uint amountReceived = exchangeV1.tokenToEthSwapInput(amountToken, minETH, uint(-1));
52 | uint amountRequired = UniswapV2Library.getAmountsIn(factory, amountToken, path)[0];
53 | assert(amountReceived > amountRequired); // fail if we didn't get enough ETH back to repay our flash loan
54 | WETH.deposit{value: amountRequired}();
55 | assert(WETH.transfer(msg.sender, amountRequired)); // return WETH to V2 pair
56 | (bool success,) = sender.call{value: amountReceived - amountRequired}(new bytes(0)); // keep the rest! (ETH)
57 | assert(success);
58 | } else {
59 | (uint minTokens) = abi.decode(data, (uint)); // slippage parameter for V1, passed in by caller
60 | WETH.withdraw(amountETH);
61 | uint amountReceived = exchangeV1.ethToTokenSwapInput{value: amountETH}(minTokens, uint(-1));
62 | uint amountRequired = UniswapV2Library.getAmountsIn(factory, amountETH, path)[0];
63 | assert(amountReceived > amountRequired); // fail if we didn't get enough tokens back to repay our flash loan
64 | assert(token.transfer(msg.sender, amountRequired)); // return tokens to V2 pair
65 | assert(token.transfer(sender, amountReceived - amountRequired)); // keep the rest! (tokens)
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/examples/ExampleOracleSimple.sol:
--------------------------------------------------------------------------------
1 | pragma solidity =0.6.6;
2 |
3 | import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol';
4 | import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';
5 | import '@uniswap/lib/contracts/libraries/FixedPoint.sol';
6 |
7 | import '../libraries/UniswapV2OracleLibrary.sol';
8 | import '../libraries/UniswapV2Library.sol';
9 |
10 | // fixed window oracle that recomputes the average price for the entire period once every period
11 | // note that the price average is only guaranteed to be over at least 1 period, but may be over a longer period
12 | contract ExampleOracleSimple {
13 | using FixedPoint for *;
14 |
15 | uint public constant PERIOD = 24 hours;
16 |
17 | IUniswapV2Pair immutable pair;
18 | address public immutable token0;
19 | address public immutable token1;
20 |
21 | uint public price0CumulativeLast;
22 | uint public price1CumulativeLast;
23 | uint32 public blockTimestampLast;
24 | FixedPoint.uq112x112 public price0Average;
25 | FixedPoint.uq112x112 public price1Average;
26 |
27 | constructor(address factory, address tokenA, address tokenB) public {
28 | IUniswapV2Pair _pair = IUniswapV2Pair(UniswapV2Library.pairFor(factory, tokenA, tokenB));
29 | pair = _pair;
30 | token0 = _pair.token0();
31 | token1 = _pair.token1();
32 | price0CumulativeLast = _pair.price0CumulativeLast(); // fetch the current accumulated price value (1 / 0)
33 | price1CumulativeLast = _pair.price1CumulativeLast(); // fetch the current accumulated price value (0 / 1)
34 | uint112 reserve0;
35 | uint112 reserve1;
36 | (reserve0, reserve1, blockTimestampLast) = _pair.getReserves();
37 | require(reserve0 != 0 && reserve1 != 0, 'ExampleOracleSimple: NO_RESERVES'); // ensure that there's liquidity in the pair
38 | }
39 |
40 | function update() external {
41 | (uint price0Cumulative, uint price1Cumulative, uint32 blockTimestamp) =
42 | UniswapV2OracleLibrary.currentCumulativePrices(address(pair));
43 | uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
44 |
45 | // ensure that at least one full period has passed since the last update
46 | require(timeElapsed >= PERIOD, 'ExampleOracleSimple: PERIOD_NOT_ELAPSED');
47 |
48 | // overflow is desired, casting never truncates
49 | // cumulative price is in (uq112x112 price * seconds) units so we simply wrap it after division by time elapsed
50 | price0Average = FixedPoint.uq112x112(uint224((price0Cumulative - price0CumulativeLast) / timeElapsed));
51 | price1Average = FixedPoint.uq112x112(uint224((price1Cumulative - price1CumulativeLast) / timeElapsed));
52 |
53 | price0CumulativeLast = price0Cumulative;
54 | price1CumulativeLast = price1Cumulative;
55 | blockTimestampLast = blockTimestamp;
56 | }
57 |
58 | // note this will always return 0 before update has been called successfully for the first time.
59 | function consult(address token, uint amountIn) external view returns (uint amountOut) {
60 | if (token == token0) {
61 | amountOut = price0Average.mul(amountIn).decode144();
62 | } else {
63 | require(token == token1, 'ExampleOracleSimple: INVALID_TOKEN');
64 | amountOut = price1Average.mul(amountIn).decode144();
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/examples/ExampleSlidingWindowOracle.sol:
--------------------------------------------------------------------------------
1 | pragma solidity =0.6.6;
2 |
3 | import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol';
4 | import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';
5 | import '@uniswap/lib/contracts/libraries/FixedPoint.sol';
6 |
7 | import '../libraries/SafeMath.sol';
8 | import '../libraries/UniswapV2Library.sol';
9 | import '../libraries/UniswapV2OracleLibrary.sol';
10 |
11 | // sliding window oracle that uses observations collected over a window to provide moving price averages in the past
12 | // `windowSize` with a precision of `windowSize / granularity`
13 | // note this is a singleton oracle and only needs to be deployed once per desired parameters, which
14 | // differs from the simple oracle which must be deployed once per pair.
15 | contract ExampleSlidingWindowOracle {
16 | using FixedPoint for *;
17 | using SafeMath for uint;
18 |
19 | struct Observation {
20 | uint timestamp;
21 | uint price0Cumulative;
22 | uint price1Cumulative;
23 | }
24 |
25 | address public immutable factory;
26 | // the desired amount of time over which the moving average should be computed, e.g. 24 hours
27 | uint public immutable windowSize;
28 | // the number of observations stored for each pair, i.e. how many price observations are stored for the window.
29 | // as granularity increases from 1, more frequent updates are needed, but moving averages become more precise.
30 | // averages are computed over intervals with sizes in the range:
31 | // [windowSize - (windowSize / granularity) * 2, windowSize]
32 | // e.g. if the window size is 24 hours, and the granularity is 24, the oracle will return the average price for
33 | // the period:
34 | // [now - [22 hours, 24 hours], now]
35 | uint8 public immutable granularity;
36 | // this is redundant with granularity and windowSize, but stored for gas savings & informational purposes.
37 | uint public immutable periodSize;
38 |
39 | // mapping from pair address to a list of price observations of that pair
40 | mapping(address => Observation[]) public pairObservations;
41 |
42 | constructor(address factory_, uint windowSize_, uint8 granularity_) public {
43 | require(granularity_ > 1, 'SlidingWindowOracle: GRANULARITY');
44 | require(
45 | (periodSize = windowSize_ / granularity_) * granularity_ == windowSize_,
46 | 'SlidingWindowOracle: WINDOW_NOT_EVENLY_DIVISIBLE'
47 | );
48 | factory = factory_;
49 | windowSize = windowSize_;
50 | granularity = granularity_;
51 | }
52 |
53 | // returns the index of the observation corresponding to the given timestamp
54 | function observationIndexOf(uint timestamp) public view returns (uint8 index) {
55 | uint epochPeriod = timestamp / periodSize;
56 | return uint8(epochPeriod % granularity);
57 | }
58 |
59 | // returns the observation from the oldest epoch (at the beginning of the window) relative to the current time
60 | function getFirstObservationInWindow(address pair) private view returns (Observation storage firstObservation) {
61 | uint8 observationIndex = observationIndexOf(block.timestamp);
62 | // no overflow issue. if observationIndex + 1 overflows, result is still zero.
63 | uint8 firstObservationIndex = (observationIndex + 1) % granularity;
64 | firstObservation = pairObservations[pair][firstObservationIndex];
65 | }
66 |
67 | // update the cumulative price for the observation at the current timestamp. each observation is updated at most
68 | // once per epoch period.
69 | function update(address tokenA, address tokenB) external {
70 | address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
71 |
72 | // populate the array with empty observations (first call only)
73 | for (uint i = pairObservations[pair].length; i < granularity; i++) {
74 | pairObservations[pair].push();
75 | }
76 |
77 | // get the observation for the current period
78 | uint8 observationIndex = observationIndexOf(block.timestamp);
79 | Observation storage observation = pairObservations[pair][observationIndex];
80 |
81 | // we only want to commit updates once per period (i.e. windowSize / granularity)
82 | uint timeElapsed = block.timestamp - observation.timestamp;
83 | if (timeElapsed > periodSize) {
84 | (uint price0Cumulative, uint price1Cumulative,) = UniswapV2OracleLibrary.currentCumulativePrices(pair);
85 | observation.timestamp = block.timestamp;
86 | observation.price0Cumulative = price0Cumulative;
87 | observation.price1Cumulative = price1Cumulative;
88 | }
89 | }
90 |
91 | // given the cumulative prices of the start and end of a period, and the length of the period, compute the average
92 | // price in terms of how much amount out is received for the amount in
93 | function computeAmountOut(
94 | uint priceCumulativeStart, uint priceCumulativeEnd,
95 | uint timeElapsed, uint amountIn
96 | ) private pure returns (uint amountOut) {
97 | // overflow is desired.
98 | FixedPoint.uq112x112 memory priceAverage = FixedPoint.uq112x112(
99 | uint224((priceCumulativeEnd - priceCumulativeStart) / timeElapsed)
100 | );
101 | amountOut = priceAverage.mul(amountIn).decode144();
102 | }
103 |
104 | // returns the amount out corresponding to the amount in for a given token using the moving average over the time
105 | // range [now - [windowSize, windowSize - periodSize * 2], now]
106 | // update must have been called for the bucket corresponding to timestamp `now - windowSize`
107 | function consult(address tokenIn, uint amountIn, address tokenOut) external view returns (uint amountOut) {
108 | address pair = UniswapV2Library.pairFor(factory, tokenIn, tokenOut);
109 | Observation storage firstObservation = getFirstObservationInWindow(pair);
110 |
111 | uint timeElapsed = block.timestamp - firstObservation.timestamp;
112 | require(timeElapsed <= windowSize, 'SlidingWindowOracle: MISSING_HISTORICAL_OBSERVATION');
113 | // should never happen.
114 | require(timeElapsed >= windowSize - periodSize * 2, 'SlidingWindowOracle: UNEXPECTED_TIME_ELAPSED');
115 |
116 | (uint price0Cumulative, uint price1Cumulative,) = UniswapV2OracleLibrary.currentCumulativePrices(pair);
117 | (address token0,) = UniswapV2Library.sortTokens(tokenIn, tokenOut);
118 |
119 | if (token0 == tokenIn) {
120 | return computeAmountOut(firstObservation.price0Cumulative, price0Cumulative, timeElapsed, amountIn);
121 | } else {
122 | return computeAmountOut(firstObservation.price1Cumulative, price1Cumulative, timeElapsed, amountIn);
123 | }
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/examples/ExampleSwapToPrice.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity =0.6.6;
3 |
4 | import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';
5 | import '@uniswap/lib/contracts/libraries/Babylonian.sol';
6 | import '@uniswap/lib/contracts/libraries/TransferHelper.sol';
7 |
8 | import '../libraries/UniswapV2LiquidityMathLibrary.sol';
9 | import '../interfaces/IERC20.sol';
10 | import '../interfaces/IUniswapV2Router01.sol';
11 | import '../libraries/SafeMath.sol';
12 | import '../libraries/UniswapV2Library.sol';
13 |
14 | contract ExampleSwapToPrice {
15 | using SafeMath for uint256;
16 |
17 | IUniswapV2Router01 public immutable router;
18 | address public immutable factory;
19 |
20 | constructor(address factory_, IUniswapV2Router01 router_) public {
21 | factory = factory_;
22 | router = router_;
23 | }
24 |
25 | // swaps an amount of either token such that the trade is profit-maximizing, given an external true price
26 | // true price is expressed in the ratio of token A to token B
27 | // caller must approve this contract to spend whichever token is intended to be swapped
28 | function swapToPrice(
29 | address tokenA,
30 | address tokenB,
31 | uint256 truePriceTokenA,
32 | uint256 truePriceTokenB,
33 | uint256 maxSpendTokenA,
34 | uint256 maxSpendTokenB,
35 | address to,
36 | uint256 deadline
37 | ) public {
38 | // true price is expressed as a ratio, so both values must be non-zero
39 | require(truePriceTokenA != 0 && truePriceTokenB != 0, "ExampleSwapToPrice: ZERO_PRICE");
40 | // caller can specify 0 for either if they wish to swap in only one direction, but not both
41 | require(maxSpendTokenA != 0 || maxSpendTokenB != 0, "ExampleSwapToPrice: ZERO_SPEND");
42 |
43 | bool aToB;
44 | uint256 amountIn;
45 | {
46 | (uint256 reserveA, uint256 reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);
47 | (aToB, amountIn) = UniswapV2LiquidityMathLibrary.computeProfitMaximizingTrade(
48 | truePriceTokenA, truePriceTokenB,
49 | reserveA, reserveB
50 | );
51 | }
52 |
53 | require(amountIn > 0, 'ExampleSwapToPrice: ZERO_AMOUNT_IN');
54 |
55 | // spend up to the allowance of the token in
56 | uint256 maxSpend = aToB ? maxSpendTokenA : maxSpendTokenB;
57 | if (amountIn > maxSpend) {
58 | amountIn = maxSpend;
59 | }
60 |
61 | address tokenIn = aToB ? tokenA : tokenB;
62 | address tokenOut = aToB ? tokenB : tokenA;
63 | TransferHelper.safeTransferFrom(tokenIn, msg.sender, address(this), amountIn);
64 | TransferHelper.safeApprove(tokenIn, address(router), amountIn);
65 |
66 | address[] memory path = new address[](2);
67 | path[0] = tokenIn;
68 | path[1] = tokenOut;
69 |
70 | router.swapExactTokensForTokens(
71 | amountIn,
72 | 0, // amountOutMin: we can skip computing this number because the math is tested
73 | path,
74 | to,
75 | deadline
76 | );
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/examples/README.md:
--------------------------------------------------------------------------------
1 | # Examples
2 |
3 | ## Disclaimer
4 |
5 | These contracts are for demonstrative purposes only.
6 | While these contracts have unit tests, and we generally expect them to be
7 | correct, there are no guarantees about the correctness or security of
8 | these contracts. We hold these contracts to a different standard of
9 | correctness and security than other contracts in this repository.
10 | E.g., we have explicitly excluded these contracts from the
11 | [bug bounty](https://uniswap.org/bug-bounty/#scope).
12 |
13 | You must do your own due diligence if you wish to use code
14 | from these examples in your project.
15 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/interfaces/IERC20.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.5.0;
2 |
3 | interface IERC20 {
4 | event Approval(address indexed owner, address indexed spender, uint value);
5 | event Transfer(address indexed from, address indexed to, uint value);
6 |
7 | function name() external view returns (string memory);
8 | function symbol() external view returns (string memory);
9 | function decimals() external view returns (uint8);
10 | function totalSupply() external view returns (uint);
11 | function balanceOf(address owner) external view returns (uint);
12 | function allowance(address owner, address spender) external view returns (uint);
13 |
14 | function approve(address spender, uint value) external returns (bool);
15 | function transfer(address to, uint value) external returns (bool);
16 | function transferFrom(address from, address to, uint value) external returns (bool);
17 | }
18 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/interfaces/IUniswapV2Migrator.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity >=0.5.0;
3 |
4 | interface IUniswapV2Migrator {
5 | function migrate(address token, uint amountTokenMin, uint amountETHMin, address to, uint deadline) external;
6 | }
7 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/interfaces/IUniswapV2Router01.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.6.2;
2 |
3 | interface IUniswapV2Router01 {
4 | function factory() external pure returns (address);
5 | function WETH() external pure returns (address);
6 |
7 | function addLiquidity(
8 | address tokenA,
9 | address tokenB,
10 | uint amountADesired,
11 | uint amountBDesired,
12 | uint amountAMin,
13 | uint amountBMin,
14 | address to,
15 | uint deadline
16 | ) external returns (uint amountA, uint amountB, uint liquidity);
17 | function addLiquidityETH(
18 | address token,
19 | uint amountTokenDesired,
20 | uint amountTokenMin,
21 | uint amountETHMin,
22 | address to,
23 | uint deadline
24 | ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
25 | function removeLiquidity(
26 | address tokenA,
27 | address tokenB,
28 | uint liquidity,
29 | uint amountAMin,
30 | uint amountBMin,
31 | address to,
32 | uint deadline
33 | ) external returns (uint amountA, uint amountB);
34 | function removeLiquidityETH(
35 | address token,
36 | uint liquidity,
37 | uint amountTokenMin,
38 | uint amountETHMin,
39 | address to,
40 | uint deadline
41 | ) external returns (uint amountToken, uint amountETH);
42 | function removeLiquidityWithPermit(
43 | address tokenA,
44 | address tokenB,
45 | uint liquidity,
46 | uint amountAMin,
47 | uint amountBMin,
48 | address to,
49 | uint deadline,
50 | bool approveMax, uint8 v, bytes32 r, bytes32 s
51 | ) external returns (uint amountA, uint amountB);
52 | function removeLiquidityETHWithPermit(
53 | address token,
54 | uint liquidity,
55 | uint amountTokenMin,
56 | uint amountETHMin,
57 | address to,
58 | uint deadline,
59 | bool approveMax, uint8 v, bytes32 r, bytes32 s
60 | ) external returns (uint amountToken, uint amountETH);
61 | function swapExactTokensForTokens(
62 | uint amountIn,
63 | uint amountOutMin,
64 | address[] calldata path,
65 | address to,
66 | uint deadline
67 | ) external returns (uint[] memory amounts);
68 | function swapTokensForExactTokens(
69 | uint amountOut,
70 | uint amountInMax,
71 | address[] calldata path,
72 | address to,
73 | uint deadline
74 | ) external returns (uint[] memory amounts);
75 | function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
76 | external
77 | payable
78 | returns (uint[] memory amounts);
79 | function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
80 | external
81 | returns (uint[] memory amounts);
82 | function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
83 | external
84 | returns (uint[] memory amounts);
85 | function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
86 | external
87 | payable
88 | returns (uint[] memory amounts);
89 |
90 | function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
91 | function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
92 | function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
93 | function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
94 | function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
95 | }
96 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/interfaces/IUniswapV2Router02.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.6.2;
2 |
3 | import './IUniswapV2Router01.sol';
4 |
5 | interface IUniswapV2Router02 is IUniswapV2Router01 {
6 | function removeLiquidityETHSupportingFeeOnTransferTokens(
7 | address token,
8 | uint liquidity,
9 | uint amountTokenMin,
10 | uint amountETHMin,
11 | address to,
12 | uint deadline
13 | ) external returns (uint amountETH);
14 | function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
15 | address token,
16 | uint liquidity,
17 | uint amountTokenMin,
18 | uint amountETHMin,
19 | address to,
20 | uint deadline,
21 | bool approveMax, uint8 v, bytes32 r, bytes32 s
22 | ) external returns (uint amountETH);
23 |
24 | function swapExactTokensForTokensSupportingFeeOnTransferTokens(
25 | uint amountIn,
26 | uint amountOutMin,
27 | address[] calldata path,
28 | address to,
29 | uint deadline
30 | ) external;
31 | function swapExactETHForTokensSupportingFeeOnTransferTokens(
32 | uint amountOutMin,
33 | address[] calldata path,
34 | address to,
35 | uint deadline
36 | ) external payable;
37 | function swapExactTokensForETHSupportingFeeOnTransferTokens(
38 | uint amountIn,
39 | uint amountOutMin,
40 | address[] calldata path,
41 | address to,
42 | uint deadline
43 | ) external;
44 | }
45 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/interfaces/IWETH.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.5.0;
2 |
3 | interface IWETH {
4 | function deposit() external payable;
5 | function transfer(address to, uint value) external returns (bool);
6 | function withdraw(uint) external;
7 | }
8 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/interfaces/V1/IUniswapV1Exchange.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.5.0;
2 |
3 | interface IUniswapV1Exchange {
4 | function balanceOf(address owner) external view returns (uint);
5 | function transferFrom(address from, address to, uint value) external returns (bool);
6 | function removeLiquidity(uint, uint, uint, uint) external returns (uint, uint);
7 | function tokenToEthSwapInput(uint, uint, uint) external returns (uint);
8 | function ethToTokenSwapInput(uint, uint) external payable returns (uint);
9 | }
10 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/interfaces/V1/IUniswapV1Factory.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.5.0;
2 |
3 | interface IUniswapV1Factory {
4 | function getExchange(address) external view returns (address);
5 | }
6 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/libraries/SafeMath.sol:
--------------------------------------------------------------------------------
1 | pragma solidity =0.6.6;
2 |
3 | // a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
4 |
5 | library SafeMath {
6 | function add(uint x, uint y) internal pure returns (uint z) {
7 | require((z = x + y) >= x, 'ds-math-add-overflow');
8 | }
9 |
10 | function sub(uint x, uint y) internal pure returns (uint z) {
11 | require((z = x - y) <= x, 'ds-math-sub-underflow');
12 | }
13 |
14 | function mul(uint x, uint y) internal pure returns (uint z) {
15 | require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/libraries/UniswapV2Library.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.5.0;
2 |
3 | import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';
4 |
5 | import "./SafeMath.sol";
6 |
7 | library UniswapV2Library {
8 | using SafeMath for uint;
9 |
10 | // returns sorted token addresses, used to handle return values from pairs sorted in this order
11 | function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
12 | require(tokenA != tokenB, 'UniswapV2Library: IDENTICAL_ADDRESSES');
13 | (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
14 | require(token0 != address(0), 'UniswapV2Library: ZERO_ADDRESS');
15 | }
16 |
17 | // calculates the CREATE2 address for a pair without making any external calls
18 | function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
19 | (address token0, address token1) = sortTokens(tokenA, tokenB);
20 | pair = address(uint(keccak256(abi.encodePacked(
21 | hex'ff',
22 | factory,
23 | keccak256(abi.encodePacked(token0, token1)),
24 | hex'10703f189e744d7346631411acd7d5e40a023a5036d4fde2e541403216fe5586' // init code hash
25 | ))));
26 | }
27 |
28 | // fetches and sorts the reserves for a pair
29 | function getReserves(address factory, address tokenA, address tokenB) internal view returns (uint reserveA, uint reserveB) {
30 | (address token0,) = sortTokens(tokenA, tokenB);
31 | (uint reserve0, uint reserve1,) = IUniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves();
32 | (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
33 | }
34 |
35 | // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
36 | function quote(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) {
37 | require(amountA > 0, 'UniswapV2Library: INSUFFICIENT_AMOUNT');
38 | require(reserveA > 0 && reserveB > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
39 | amountB = amountA.mul(reserveB) / reserveA;
40 | }
41 |
42 | // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
43 | function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) {
44 | require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');
45 | require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
46 | uint amountInWithFee = amountIn.mul(997);
47 | uint numerator = amountInWithFee.mul(reserveOut);
48 | uint denominator = reserveIn.mul(1000).add(amountInWithFee);
49 | amountOut = numerator / denominator;
50 | }
51 |
52 | // given an output amount of an asset and pair reserves, returns a required input amount of the other asset
53 | function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn) {
54 | require(amountOut > 0, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT');
55 | require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
56 | uint numerator = reserveIn.mul(amountOut).mul(1000);
57 | uint denominator = reserveOut.sub(amountOut).mul(997);
58 | amountIn = (numerator / denominator).add(1);
59 | }
60 |
61 | // performs chained getAmountOut calculations on any number of pairs
62 | function getAmountsOut(address factory, uint amountIn, address[] memory path) internal view returns (uint[] memory amounts) {
63 | require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
64 | amounts = new uint[](path.length);
65 | amounts[0] = amountIn;
66 | for (uint i; i < path.length - 1; i++) {
67 | (uint reserveIn, uint reserveOut) = getReserves(factory, path[i], path[i + 1]);
68 | amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
69 | }
70 | }
71 |
72 | // performs chained getAmountIn calculations on any number of pairs
73 | function getAmountsIn(address factory, uint amountOut, address[] memory path) internal view returns (uint[] memory amounts) {
74 | require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
75 | amounts = new uint[](path.length);
76 | amounts[amounts.length - 1] = amountOut;
77 | for (uint i = path.length - 1; i > 0; i--) {
78 | (uint reserveIn, uint reserveOut) = getReserves(factory, path[i - 1], path[i]);
79 | amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/libraries/UniswapV2LiquidityMathLibrary.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.5.0;
2 |
3 | import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';
4 | import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol';
5 | import '@uniswap/lib/contracts/libraries/Babylonian.sol';
6 | import '@uniswap/lib/contracts/libraries/FullMath.sol';
7 |
8 | import './SafeMath.sol';
9 | import './UniswapV2Library.sol';
10 |
11 | // library containing some math for dealing with the liquidity shares of a pair, e.g. computing their exact value
12 | // in terms of the underlying tokens
13 | library UniswapV2LiquidityMathLibrary {
14 | using SafeMath for uint256;
15 |
16 | // computes the direction and magnitude of the profit-maximizing trade
17 | function computeProfitMaximizingTrade(
18 | uint256 truePriceTokenA,
19 | uint256 truePriceTokenB,
20 | uint256 reserveA,
21 | uint256 reserveB
22 | ) pure internal returns (bool aToB, uint256 amountIn) {
23 | aToB = FullMath.mulDiv(reserveA, truePriceTokenB, reserveB) < truePriceTokenA;
24 |
25 | uint256 invariant = reserveA.mul(reserveB);
26 |
27 | uint256 leftSide = Babylonian.sqrt(
28 | FullMath.mulDiv(
29 | invariant.mul(1000),
30 | aToB ? truePriceTokenA : truePriceTokenB,
31 | (aToB ? truePriceTokenB : truePriceTokenA).mul(997)
32 | )
33 | );
34 | uint256 rightSide = (aToB ? reserveA.mul(1000) : reserveB.mul(1000)) / 997;
35 |
36 | if (leftSide < rightSide) return (false, 0);
37 |
38 | // compute the amount that must be sent to move the price to the profit-maximizing price
39 | amountIn = leftSide.sub(rightSide);
40 | }
41 |
42 | // gets the reserves after an arbitrage moves the price to the profit-maximizing ratio given an externally observed true price
43 | function getReservesAfterArbitrage(
44 | address factory,
45 | address tokenA,
46 | address tokenB,
47 | uint256 truePriceTokenA,
48 | uint256 truePriceTokenB
49 | ) view internal returns (uint256 reserveA, uint256 reserveB) {
50 | // first get reserves before the swap
51 | (reserveA, reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);
52 |
53 | require(reserveA > 0 && reserveB > 0, 'UniswapV2ArbitrageLibrary: ZERO_PAIR_RESERVES');
54 |
55 | // then compute how much to swap to arb to the true price
56 | (bool aToB, uint256 amountIn) = computeProfitMaximizingTrade(truePriceTokenA, truePriceTokenB, reserveA, reserveB);
57 |
58 | if (amountIn == 0) {
59 | return (reserveA, reserveB);
60 | }
61 |
62 | // now affect the trade to the reserves
63 | if (aToB) {
64 | uint amountOut = UniswapV2Library.getAmountOut(amountIn, reserveA, reserveB);
65 | reserveA += amountIn;
66 | reserveB -= amountOut;
67 | } else {
68 | uint amountOut = UniswapV2Library.getAmountOut(amountIn, reserveB, reserveA);
69 | reserveB += amountIn;
70 | reserveA -= amountOut;
71 | }
72 | }
73 |
74 | // computes liquidity value given all the parameters of the pair
75 | function computeLiquidityValue(
76 | uint256 reservesA,
77 | uint256 reservesB,
78 | uint256 totalSupply,
79 | uint256 liquidityAmount,
80 | bool feeOn,
81 | uint kLast
82 | ) internal pure returns (uint256 tokenAAmount, uint256 tokenBAmount) {
83 | if (feeOn && kLast > 0) {
84 | uint rootK = Babylonian.sqrt(reservesA.mul(reservesB));
85 | uint rootKLast = Babylonian.sqrt(kLast);
86 | if (rootK > rootKLast) {
87 | uint numerator1 = totalSupply;
88 | uint numerator2 = rootK.sub(rootKLast);
89 | uint denominator = rootK.mul(5).add(rootKLast);
90 | uint feeLiquidity = FullMath.mulDiv(numerator1, numerator2, denominator);
91 | totalSupply = totalSupply.add(feeLiquidity);
92 | }
93 | }
94 | return (reservesA.mul(liquidityAmount) / totalSupply, reservesB.mul(liquidityAmount) / totalSupply);
95 | }
96 |
97 | // get all current parameters from the pair and compute value of a liquidity amount
98 | // **note this is subject to manipulation, e.g. sandwich attacks**. prefer passing a manipulation resistant price to
99 | // #getLiquidityValueAfterArbitrageToPrice
100 | function getLiquidityValue(
101 | address factory,
102 | address tokenA,
103 | address tokenB,
104 | uint256 liquidityAmount
105 | ) internal view returns (uint256 tokenAAmount, uint256 tokenBAmount) {
106 | (uint256 reservesA, uint256 reservesB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);
107 | IUniswapV2Pair pair = IUniswapV2Pair(UniswapV2Library.pairFor(factory, tokenA, tokenB));
108 | bool feeOn = IUniswapV2Factory(factory).feeTo() != address(0);
109 | uint kLast = feeOn ? pair.kLast() : 0;
110 | uint totalSupply = pair.totalSupply();
111 | return computeLiquidityValue(reservesA, reservesB, totalSupply, liquidityAmount, feeOn, kLast);
112 | }
113 |
114 | // given two tokens, tokenA and tokenB, and their "true price", i.e. the observed ratio of value of token A to token B,
115 | // and a liquidity amount, returns the value of the liquidity in terms of tokenA and tokenB
116 | function getLiquidityValueAfterArbitrageToPrice(
117 | address factory,
118 | address tokenA,
119 | address tokenB,
120 | uint256 truePriceTokenA,
121 | uint256 truePriceTokenB,
122 | uint256 liquidityAmount
123 | ) internal view returns (
124 | uint256 tokenAAmount,
125 | uint256 tokenBAmount
126 | ) {
127 | bool feeOn = IUniswapV2Factory(factory).feeTo() != address(0);
128 | IUniswapV2Pair pair = IUniswapV2Pair(UniswapV2Library.pairFor(factory, tokenA, tokenB));
129 | uint kLast = feeOn ? pair.kLast() : 0;
130 | uint totalSupply = pair.totalSupply();
131 |
132 | // this also checks that totalSupply > 0
133 | require(totalSupply >= liquidityAmount && liquidityAmount > 0, 'ComputeLiquidityValue: LIQUIDITY_AMOUNT');
134 |
135 | (uint reservesA, uint reservesB) = getReservesAfterArbitrage(factory, tokenA, tokenB, truePriceTokenA, truePriceTokenB);
136 |
137 | return computeLiquidityValue(reservesA, reservesB, totalSupply, liquidityAmount, feeOn, kLast);
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/libraries/UniswapV2OracleLibrary.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.5.0;
2 |
3 | import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';
4 | import '@uniswap/lib/contracts/libraries/FixedPoint.sol';
5 |
6 | // library with helper methods for oracles that are concerned with computing average prices
7 | library UniswapV2OracleLibrary {
8 | using FixedPoint for *;
9 |
10 | // helper function that returns the current block timestamp within the range of uint32, i.e. [0, 2**32 - 1]
11 | function currentBlockTimestamp() internal view returns (uint32) {
12 | return uint32(block.timestamp % 2 ** 32);
13 | }
14 |
15 | // produces the cumulative price using counterfactuals to save gas and avoid a call to sync.
16 | function currentCumulativePrices(
17 | address pair
18 | ) internal view returns (uint price0Cumulative, uint price1Cumulative, uint32 blockTimestamp) {
19 | blockTimestamp = currentBlockTimestamp();
20 | price0Cumulative = IUniswapV2Pair(pair).price0CumulativeLast();
21 | price1Cumulative = IUniswapV2Pair(pair).price1CumulativeLast();
22 |
23 | // if time has elapsed since the last update on the pair, mock the accumulated price values
24 | (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) = IUniswapV2Pair(pair).getReserves();
25 | if (blockTimestampLast != blockTimestamp) {
26 | // subtraction overflow is desired
27 | uint32 timeElapsed = blockTimestamp - blockTimestampLast;
28 | // addition overflow is desired
29 | // counterfactual
30 | price0Cumulative += uint(FixedPoint.fraction(reserve1, reserve0)._x) * timeElapsed;
31 | // counterfactual
32 | price1Cumulative += uint(FixedPoint.fraction(reserve0, reserve1)._x) * timeElapsed;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/test/DeflatingERC20.sol:
--------------------------------------------------------------------------------
1 | pragma solidity =0.6.6;
2 |
3 | import '../libraries/SafeMath.sol';
4 |
5 | contract DeflatingERC20 {
6 | using SafeMath for uint;
7 |
8 | string public constant name = 'Deflating Test Token';
9 | string public constant symbol = 'DTT';
10 | uint8 public constant decimals = 18;
11 | uint public totalSupply;
12 | mapping(address => uint) public balanceOf;
13 | mapping(address => mapping(address => uint)) public allowance;
14 |
15 | bytes32 public DOMAIN_SEPARATOR;
16 | // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
17 | bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
18 | mapping(address => uint) public nonces;
19 |
20 | event Approval(address indexed owner, address indexed spender, uint value);
21 | event Transfer(address indexed from, address indexed to, uint value);
22 |
23 | constructor(uint _totalSupply) public {
24 | uint chainId;
25 | assembly {
26 | chainId := chainid()
27 | }
28 | DOMAIN_SEPARATOR = keccak256(
29 | abi.encode(
30 | keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
31 | keccak256(bytes(name)),
32 | keccak256(bytes('1')),
33 | chainId,
34 | address(this)
35 | )
36 | );
37 | _mint(msg.sender, _totalSupply);
38 | }
39 |
40 | function _mint(address to, uint value) internal {
41 | totalSupply = totalSupply.add(value);
42 | balanceOf[to] = balanceOf[to].add(value);
43 | emit Transfer(address(0), to, value);
44 | }
45 |
46 | function _burn(address from, uint value) internal {
47 | balanceOf[from] = balanceOf[from].sub(value);
48 | totalSupply = totalSupply.sub(value);
49 | emit Transfer(from, address(0), value);
50 | }
51 |
52 | function _approve(address owner, address spender, uint value) private {
53 | allowance[owner][spender] = value;
54 | emit Approval(owner, spender, value);
55 | }
56 |
57 | function _transfer(address from, address to, uint value) private {
58 | uint burnAmount = value / 100;
59 | _burn(from, burnAmount);
60 | uint transferAmount = value.sub(burnAmount);
61 | balanceOf[from] = balanceOf[from].sub(transferAmount);
62 | balanceOf[to] = balanceOf[to].add(transferAmount);
63 | emit Transfer(from, to, transferAmount);
64 | }
65 |
66 | function approve(address spender, uint value) external returns (bool) {
67 | _approve(msg.sender, spender, value);
68 | return true;
69 | }
70 |
71 | function transfer(address to, uint value) external returns (bool) {
72 | _transfer(msg.sender, to, value);
73 | return true;
74 | }
75 |
76 | function transferFrom(address from, address to, uint value) external returns (bool) {
77 | if (allowance[from][msg.sender] != uint(-1)) {
78 | allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
79 | }
80 | _transfer(from, to, value);
81 | return true;
82 | }
83 |
84 | function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
85 | require(deadline >= block.timestamp, 'EXPIRED');
86 | bytes32 digest = keccak256(
87 | abi.encodePacked(
88 | '\x19\x01',
89 | DOMAIN_SEPARATOR,
90 | keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
91 | )
92 | );
93 | address recoveredAddress = ecrecover(digest, v, r, s);
94 | require(recoveredAddress != address(0) && recoveredAddress == owner, 'INVALID_SIGNATURE');
95 | _approve(owner, spender, value);
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/test/ERC20.sol:
--------------------------------------------------------------------------------
1 | pragma solidity =0.6.6;
2 |
3 | import '../libraries/SafeMath.sol';
4 |
5 | contract ERC20 {
6 | using SafeMath for uint;
7 |
8 | string public constant name = 'Test Token';
9 | string public constant symbol = 'TT';
10 | uint8 public constant decimals = 18;
11 | uint public totalSupply;
12 | mapping(address => uint) public balanceOf;
13 | mapping(address => mapping(address => uint)) public allowance;
14 |
15 | bytes32 public DOMAIN_SEPARATOR;
16 | // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
17 | bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
18 | mapping(address => uint) public nonces;
19 |
20 | event Approval(address indexed owner, address indexed spender, uint value);
21 | event Transfer(address indexed from, address indexed to, uint value);
22 |
23 | constructor(uint _totalSupply) public {
24 | uint chainId;
25 | assembly {
26 | chainId := chainid()
27 | }
28 | DOMAIN_SEPARATOR = keccak256(
29 | abi.encode(
30 | keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
31 | keccak256(bytes(name)),
32 | keccak256(bytes('1')),
33 | chainId,
34 | address(this)
35 | )
36 | );
37 | _mint(msg.sender, _totalSupply);
38 | }
39 |
40 | function _mint(address to, uint value) internal {
41 | totalSupply = totalSupply.add(value);
42 | balanceOf[to] = balanceOf[to].add(value);
43 | emit Transfer(address(0), to, value);
44 | }
45 |
46 | function _burn(address from, uint value) internal {
47 | balanceOf[from] = balanceOf[from].sub(value);
48 | totalSupply = totalSupply.sub(value);
49 | emit Transfer(from, address(0), value);
50 | }
51 |
52 | function _approve(address owner, address spender, uint value) private {
53 | allowance[owner][spender] = value;
54 | emit Approval(owner, spender, value);
55 | }
56 |
57 | function _transfer(address from, address to, uint value) private {
58 | balanceOf[from] = balanceOf[from].sub(value);
59 | balanceOf[to] = balanceOf[to].add(value);
60 | emit Transfer(from, to, value);
61 | }
62 |
63 | function approve(address spender, uint value) external returns (bool) {
64 | _approve(msg.sender, spender, value);
65 | return true;
66 | }
67 |
68 | function transfer(address to, uint value) external returns (bool) {
69 | _transfer(msg.sender, to, value);
70 | return true;
71 | }
72 |
73 | function transferFrom(address from, address to, uint value) external returns (bool) {
74 | if (allowance[from][msg.sender] != uint(-1)) {
75 | allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
76 | }
77 | _transfer(from, to, value);
78 | return true;
79 | }
80 |
81 | function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
82 | require(deadline >= block.timestamp, 'EXPIRED');
83 | bytes32 digest = keccak256(
84 | abi.encodePacked(
85 | '\x19\x01',
86 | DOMAIN_SEPARATOR,
87 | keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
88 | )
89 | );
90 | address recoveredAddress = ecrecover(digest, v, r, s);
91 | require(recoveredAddress != address(0) && recoveredAddress == owner, 'INVALID_SIGNATURE');
92 | _approve(owner, spender, value);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/test/RouterEventEmitter.sol:
--------------------------------------------------------------------------------
1 | pragma solidity =0.6.6;
2 |
3 | import '../interfaces/IUniswapV2Router01.sol';
4 |
5 | contract RouterEventEmitter {
6 | event Amounts(uint[] amounts);
7 |
8 | receive() external payable {}
9 |
10 | function swapExactTokensForTokens(
11 | address router,
12 | uint amountIn,
13 | uint amountOutMin,
14 | address[] calldata path,
15 | address to,
16 | uint deadline
17 | ) external {
18 | (bool success, bytes memory returnData) = router.delegatecall(abi.encodeWithSelector(
19 | IUniswapV2Router01(router).swapExactTokensForTokens.selector, amountIn, amountOutMin, path, to, deadline
20 | ));
21 | assert(success);
22 | emit Amounts(abi.decode(returnData, (uint[])));
23 | }
24 |
25 | function swapTokensForExactTokens(
26 | address router,
27 | uint amountOut,
28 | uint amountInMax,
29 | address[] calldata path,
30 | address to,
31 | uint deadline
32 | ) external {
33 | (bool success, bytes memory returnData) = router.delegatecall(abi.encodeWithSelector(
34 | IUniswapV2Router01(router).swapTokensForExactTokens.selector, amountOut, amountInMax, path, to, deadline
35 | ));
36 | assert(success);
37 | emit Amounts(abi.decode(returnData, (uint[])));
38 | }
39 |
40 | function swapExactETHForTokens(
41 | address router,
42 | uint amountOutMin,
43 | address[] calldata path,
44 | address to,
45 | uint deadline
46 | ) external payable {
47 | (bool success, bytes memory returnData) = router.delegatecall(abi.encodeWithSelector(
48 | IUniswapV2Router01(router).swapExactETHForTokens.selector, amountOutMin, path, to, deadline
49 | ));
50 | assert(success);
51 | emit Amounts(abi.decode(returnData, (uint[])));
52 | }
53 |
54 | function swapTokensForExactETH(
55 | address router,
56 | uint amountOut,
57 | uint amountInMax,
58 | address[] calldata path,
59 | address to,
60 | uint deadline
61 | ) external {
62 | (bool success, bytes memory returnData) = router.delegatecall(abi.encodeWithSelector(
63 | IUniswapV2Router01(router).swapTokensForExactETH.selector, amountOut, amountInMax, path, to, deadline
64 | ));
65 | assert(success);
66 | emit Amounts(abi.decode(returnData, (uint[])));
67 | }
68 |
69 | function swapExactTokensForETH(
70 | address router,
71 | uint amountIn,
72 | uint amountOutMin,
73 | address[] calldata path,
74 | address to,
75 | uint deadline
76 | ) external {
77 | (bool success, bytes memory returnData) = router.delegatecall(abi.encodeWithSelector(
78 | IUniswapV2Router01(router).swapExactTokensForETH.selector, amountIn, amountOutMin, path, to, deadline
79 | ));
80 | assert(success);
81 | emit Amounts(abi.decode(returnData, (uint[])));
82 | }
83 |
84 | function swapETHForExactTokens(
85 | address router,
86 | uint amountOut,
87 | address[] calldata path,
88 | address to,
89 | uint deadline
90 | ) external payable {
91 | (bool success, bytes memory returnData) = router.delegatecall(abi.encodeWithSelector(
92 | IUniswapV2Router01(router).swapETHForExactTokens.selector, amountOut, path, to, deadline
93 | ));
94 | assert(success);
95 | emit Amounts(abi.decode(returnData, (uint[])));
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/contracts/uniswap-v2-periphery/test/WETH9.sol:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2015, 2016, 2017 Dapphub
2 |
3 | // This program is free software: you can redistribute it and/or modify
4 | // it under the terms of the GNU General Public License as published by
5 | // the Free Software Foundation, either version 3 of the License, or
6 | // (at your option) any later version.
7 |
8 | // This program is distributed in the hope that it will be useful,
9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | // GNU General Public License for more details.
12 |
13 | // You should have received a copy of the GNU General Public License
14 | // along with this program. If not, see .
15 |
16 | pragma solidity =0.6.6;
17 |
18 | contract WETH9 {
19 | string public name = "Wrapped Ether";
20 | string public symbol = "WETH";
21 | uint8 public decimals = 18;
22 |
23 | event Approval(address indexed src, address indexed guy, uint wad);
24 | event Transfer(address indexed src, address indexed dst, uint wad);
25 | event Deposit(address indexed dst, uint wad);
26 | event Withdrawal(address indexed src, uint wad);
27 |
28 | mapping(address => uint) public balanceOf;
29 | mapping(address => mapping(address => uint)) public allowance;
30 |
31 | constructor() public {
32 | balanceOf[msg.sender] += 10000 ether;
33 | }
34 |
35 | function deposit() public payable {
36 | balanceOf[msg.sender] += msg.value;
37 | emit Deposit(msg.sender, msg.value);
38 | }
39 |
40 | function withdraw(uint wad) public {
41 | require(balanceOf[msg.sender] >= wad, "");
42 | balanceOf[msg.sender] -= wad;
43 | msg.sender.transfer(wad);
44 | emit Withdrawal(msg.sender, wad);
45 | }
46 |
47 | function totalSupply() public view returns (uint) {
48 | return address(this).balance;
49 | }
50 |
51 | function approve(address guy, uint wad) public returns (bool) {
52 | allowance[msg.sender][guy] = wad;
53 | emit Approval(msg.sender, guy, wad);
54 | return true;
55 | }
56 |
57 | function transfer(address dst, uint wad) public returns (bool) {
58 | return transferFrom(msg.sender, dst, wad);
59 | }
60 |
61 | function transferFrom(address src, address dst, uint wad)
62 | public
63 | returns (bool)
64 | {
65 | require(balanceOf[src] >= wad, "");
66 |
67 | if (src != msg.sender && allowance[src][msg.sender] != uint(- 1)) {
68 | require(allowance[src][msg.sender] >= wad, "");
69 | allowance[src][msg.sender] -= wad;
70 | }
71 |
72 | balanceOf[src] -= wad;
73 | balanceOf[dst] += wad;
74 |
75 | emit Transfer(src, dst, wad);
76 |
77 | return true;
78 | }
79 | }
80 |
81 |
82 | /*
83 | GNU GENERAL PUBLIC LICENSE
84 | Version 3, 29 June 2007
85 |
86 | Copyright (C) 2007 Free Software Foundation, Inc.
87 | Everyone is permitted to copy and distribute verbatim copies
88 | of this license document, but changing it is not allowed.
89 |
90 | Preamble
91 |
92 | The GNU General Public License is a free, copyleft license for
93 | software and other kinds of works.
94 |
95 | The licenses for most software and other practical works are designed
96 | to take away your freedom to share and change the works. By contrast,
97 | the GNU General Public License is intended to guarantee your freedom to
98 | share and change all versions of a program--to make sure it remains free
99 | software for all its users. We, the Free Software Foundation, use the
100 | GNU General Public License for most of our software; it applies also to
101 | any other work released this way by its authors. You can apply it to
102 | your programs, too.
103 |
104 | When we speak of free software, we are referring to freedom, not
105 | price. Our General Public Licenses are designed to make sure that you
106 | have the freedom to distribute copies of free software (and charge for
107 | them if you wish), that you receive source code or can get it if you
108 | want it, that you can change the software or use pieces of it in new
109 | free programs, and that you know you can do these things.
110 |
111 | To protect your rights, we need to prevent others from denying you
112 | these rights or asking you to surrender the rights. Therefore, you have
113 | certain responsibilities if you distribute copies of the software, or if
114 | you modify it: responsibilities to respect the freedom of others.
115 |
116 | For example, if you distribute copies of such a program, whether
117 | gratis or for a fee, you must pass on to the recipients the same
118 | freedoms that you received. You must make sure that they, too, receive
119 | or can get the source code. And you must show them these terms so they
120 | know their rights.
121 |
122 | Developers that use the GNU GPL protect your rights with two steps:
123 | (1) assert copyright on the software, and (2) offer you this License
124 | giving you legal permission to copy, distribute and/or modify it.
125 |
126 | For the developers' and authors' protection, the GPL clearly explains
127 | that there is no warranty for this free software. For both users' and
128 | authors' sake, the GPL requires that modified versions be marked as
129 | changed, so that their problems will not be attributed erroneously to
130 | authors of previous versions.
131 |
132 | Some devices are designed to deny users access to install or run
133 | modified versions of the software inside them, although the manufacturer
134 | can do so. This is fundamentally incompatible with the aim of
135 | protecting users' freedom to change the software. The systematic
136 | pattern of such abuse occurs in the area of products for individuals to
137 | use, which is precisely where it is most unacceptable. Therefore, we
138 | have designed this version of the GPL to prohibit the practice for those
139 | products. If such problems arise substantially in other domains, we
140 | stand ready to extend this provision to those domains in future versions
141 | of the GPL, as needed to protect the freedom of users.
142 |
143 | Finally, every program is threatened constantly by software patents.
144 | States should not allow patents to restrict development and use of
145 | software on general-purpose computers, but in those that do, we wish to
146 | avoid the special danger that patents applied to a free program could
147 | make it effectively proprietary. To prevent this, the GPL assures that
148 | patents cannot be used to render the program non-free.
149 |
150 | The precise terms and conditions for copying, distribution and
151 | modification follow.
152 |
153 | TERMS AND CONDITIONS
154 |
155 | 0. Definitions.
156 |
157 | "This License" refers to version 3 of the GNU General Public License.
158 |
159 | "Copyright" also means copyright-like laws that apply to other kinds of
160 | works, such as semiconductor masks.
161 |
162 | "The Program" refers to any copyrightable work licensed under this
163 | License. Each licensee is addressed as "you". "Licensees" and
164 | "recipients" may be individuals or organizations.
165 |
166 | To "modify" a work means to copy from or adapt all or part of the work
167 | in a fashion requiring copyright permission, other than the making of an
168 | exact copy. The resulting work is called a "modified version" of the
169 | earlier work or a work "based on" the earlier work.
170 |
171 | A "covered work" means either the unmodified Program or a work based
172 | on the Program.
173 |
174 | To "propagate" a work means to do anything with it that, without
175 | permission, would make you directly or secondarily liable for
176 | infringement under applicable copyright law, except executing it on a
177 | computer or modifying a private copy. Propagation includes copying,
178 | distribution (with or without modification), making available to the
179 | public, and in some countries other activities as well.
180 |
181 | To "convey" a work means any kind of propagation that enables other
182 | parties to make or receive copies. Mere interaction with a user through
183 | a computer network, with no transfer of a copy, is not conveying.
184 |
185 | An interactive user interface displays "Appropriate Legal Notices"
186 | to the extent that it includes a convenient and prominently visible
187 | feature that (1) displays an appropriate copyright notice, and (2)
188 | tells the user that there is no warranty for the work (except to the
189 | extent that warranties are provided), that licensees may convey the
190 | work under this License, and how to view a copy of this License. If
191 | the interface presents a list of user commands or options, such as a
192 | menu, a prominent item in the list meets this criterion.
193 |
194 | 1. Source Code.
195 |
196 | The "source code" for a work means the preferred form of the work
197 | for making modifications to it. "Object code" means any non-source
198 | form of a work.
199 |
200 | A "Standard Interface" means an interface that either is an official
201 | standard defined by a recognized standards body, or, in the case of
202 | interfaces specified for a particular programming language, one that
203 | is widely used among developers working in that language.
204 |
205 | The "System Libraries" of an executable work include anything, other
206 | than the work as a whole, that (a) is included in the normal form of
207 | packaging a Major Component, but which is not part of that Major
208 | Component, and (b) serves only to enable use of the work with that
209 | Major Component, or to implement a Standard Interface for which an
210 | implementation is available to the public in source code form. A
211 | "Major Component", in this context, means a major essential component
212 | (kernel, window system, and so on) of the specific operating system
213 | (if any) on which the executable work runs, or a compiler used to
214 | produce the work, or an object code interpreter used to run it.
215 |
216 | The "Corresponding Source" for a work in object code form means all
217 | the source code needed to generate, install, and (for an executable
218 | work) run the object code and to modify the work, including scripts to
219 | control those activities. However, it does not include the work's
220 | System Libraries, or general-purpose tools or generally available free
221 | programs which are used unmodified in performing those activities but
222 | which are not part of the work. For example, Corresponding Source
223 | includes interface definition files associated with source files for
224 | the work, and the source code for shared libraries and dynamically
225 | linked subprograms that the work is specifically designed to require,
226 | such as by intimate data communication or control flow between those
227 | subprograms and other parts of the work.
228 |
229 | The Corresponding Source need not include anything that users
230 | can regenerate automatically from other parts of the Corresponding
231 | Source.
232 |
233 | The Corresponding Source for a work in source code form is that
234 | same work.
235 |
236 | 2. Basic Permissions.
237 |
238 | All rights granted under this License are granted for the term of
239 | copyright on the Program, and are irrevocable provided the stated
240 | conditions are met. This License explicitly affirms your unlimited
241 | permission to run the unmodified Program. The output from running a
242 | covered work is covered by this License only if the output, given its
243 | content, constitutes a covered work. This License acknowledges your
244 | rights of fair use or other equivalent, as provided by copyright law.
245 |
246 | You may make, run and propagate covered works that you do not
247 | convey, without conditions so long as your license otherwise remains
248 | in force. You may convey covered works to others for the sole purpose
249 | of having them make modifications exclusively for you, or provide you
250 | with facilities for running those works, provided that you comply with
251 | the terms of this License in conveying all material for which you do
252 | not control copyright. Those thus making or running the covered works
253 | for you must do so exclusively on your behalf, under your direction
254 | and control, on terms that prohibit them from making any copies of
255 | your copyrighted material outside their relationship with you.
256 |
257 | Conveying under any other circumstances is permitted solely under
258 | the conditions stated below. Sublicensing is not allowed; section 10
259 | makes it unnecessary.
260 |
261 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
262 |
263 | No covered work shall be deemed part of an effective technological
264 | measure under any applicable law fulfilling obligations under article
265 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
266 | similar laws prohibiting or restricting circumvention of such
267 | measures.
268 |
269 | When you convey a covered work, you waive any legal power to forbid
270 | circumvention of technological measures to the extent such circumvention
271 | is effected by exercising rights under this License with respect to
272 | the covered work, and you disclaim any intention to limit operation or
273 | modification of the work as a means of enforcing, against the work's
274 | users, your or third parties' legal rights to forbid circumvention of
275 | technological measures.
276 |
277 | 4. Conveying Verbatim Copies.
278 |
279 | You may convey verbatim copies of the Program's source code as you
280 | receive it, in any medium, provided that you conspicuously and
281 | appropriately publish on each copy an appropriate copyright notice;
282 | keep intact all notices stating that this License and any
283 | non-permissive terms added in accord with section 7 apply to the code;
284 | keep intact all notices of the absence of any warranty; and give all
285 | recipients a copy of this License along with the Program.
286 |
287 | You may charge any price or no price for each copy that you convey,
288 | and you may offer support or warranty protection for a fee.
289 |
290 | 5. Conveying Modified Source Versions.
291 |
292 | You may convey a work based on the Program, or the modifications to
293 | produce it from the Program, in the form of source code under the
294 | terms of section 4, provided that you also meet all of these conditions:
295 |
296 | a) The work must carry prominent notices stating that you modified
297 | it, and giving a relevant date.
298 |
299 | b) The work must carry prominent notices stating that it is
300 | released under this License and any conditions added under section
301 | 7. This requirement modifies the requirement in section 4 to
302 | "keep intact all notices".
303 |
304 | c) You must license the entire work, as a whole, under this
305 | License to anyone who comes into possession of a copy. This
306 | License will therefore apply, along with any applicable section 7
307 | additional terms, to the whole of the work, and all its parts,
308 | regardless of how they are packaged. This License gives no
309 | permission to license the work in any other way, but it does not
310 | invalidate such permission if you have separately received it.
311 |
312 | d) If the work has interactive user interfaces, each must display
313 | Appropriate Legal Notices; however, if the Program has interactive
314 | interfaces that do not display Appropriate Legal Notices, your
315 | work need not make them do so.
316 |
317 | A compilation of a covered work with other separate and independent
318 | works, which are not by their nature extensions of the covered work,
319 | and which are not combined with it such as to form a larger program,
320 | in or on a volume of a storage or distribution medium, is called an
321 | "aggregate" if the compilation and its resulting copyright are not
322 | used to limit the access or legal rights of the compilation's users
323 | beyond what the individual works permit. Inclusion of a covered work
324 | in an aggregate does not cause this License to apply to the other
325 | parts of the aggregate.
326 |
327 | 6. Conveying Non-Source Forms.
328 |
329 | You may convey a covered work in object code form under the terms
330 | of sections 4 and 5, provided that you also convey the
331 | machine-readable Corresponding Source under the terms of this License,
332 | in one of these ways:
333 |
334 | a) Convey the object code in, or embodied in, a physical product
335 | (including a physical distribution medium), accompanied by the
336 | Corresponding Source fixed on a durable physical medium
337 | customarily used for software interchange.
338 |
339 | b) Convey the object code in, or embodied in, a physical product
340 | (including a physical distribution medium), accompanied by a
341 | written offer, valid for at least three years and valid for as
342 | long as you offer spare parts or customer support for that product
343 | model, to give anyone who possesses the object code either (1) a
344 | copy of the Corresponding Source for all the software in the
345 | product that is covered by this License, on a durable physical
346 | medium customarily used for software interchange, for a price no
347 | more than your reasonable cost of physically performing this
348 | conveying of source, or (2) access to copy the
349 | Corresponding Source from a network server at no charge.
350 |
351 | c) Convey individual copies of the object code with a copy of the
352 | written offer to provide the Corresponding Source. This
353 | alternative is allowed only occasionally and noncommercially, and
354 | only if you received the object code with such an offer, in accord
355 | with subsection 6b.
356 |
357 | d) Convey the object code by offering access from a designated
358 | place (gratis or for a charge), and offer equivalent access to the
359 | Corresponding Source in the same way through the same place at no
360 | further charge. You need not require recipients to copy the
361 | Corresponding Source along with the object code. If the place to
362 | copy the object code is a network server, the Corresponding Source
363 | may be on a different server (operated by you or a third party)
364 | that supports equivalent copying facilities, provided you maintain
365 | clear directions next to the object code saying where to find the
366 | Corresponding Source. Regardless of what server hosts the
367 | Corresponding Source, you remain obligated to ensure that it is
368 | available for as long as needed to satisfy these requirements.
369 |
370 | e) Convey the object code using peer-to-peer transmission, provided
371 | you inform other peers where the object code and Corresponding
372 | Source of the work are being offered to the general public at no
373 | charge under subsection 6d.
374 |
375 | A separable portion of the object code, whose source code is excluded
376 | from the Corresponding Source as a System Library, need not be
377 | included in conveying the object code work.
378 |
379 | A "User Product" is either (1) a "consumer product", which means any
380 | tangible personal property which is normally used for personal, family,
381 | or household purposes, or (2) anything designed or sold for incorporation
382 | into a dwelling. In determining whether a product is a consumer product,
383 | doubtful cases shall be resolved in favor of coverage. For a particular
384 | product received by a particular user, "normally used" refers to a
385 | typical or common use of that class of product, regardless of the status
386 | of the particular user or of the way in which the particular user
387 | actually uses, or expects or is expected to use, the product. A product
388 | is a consumer product regardless of whether the product has substantial
389 | commercial, industrial or non-consumer uses, unless such uses represent
390 | the only significant mode of use of the product.
391 |
392 | "Installation Information" for a User Product means any methods,
393 | procedures, authorization keys, or other information required to install
394 | and execute modified versions of a covered work in that User Product from
395 | a modified version of its Corresponding Source. The information must
396 | suffice to ensure that the continued functioning of the modified object
397 | code is in no case prevented or interfered with solely because
398 | modification has been made.
399 |
400 | If you convey an object code work under this section in, or with, or
401 | specifically for use in, a User Product, and the conveying occurs as
402 | part of a transaction in which the right of possession and use of the
403 | User Product is transferred to the recipient in perpetuity or for a
404 | fixed term (regardless of how the transaction is characterized), the
405 | Corresponding Source conveyed under this section must be accompanied
406 | by the Installation Information. But this requirement does not apply
407 | if neither you nor any third party retains the ability to install
408 | modified object code on the User Product (for example, the work has
409 | been installed in ROM).
410 |
411 | The requirement to provide Installation Information does not include a
412 | requirement to continue to provide support service, warranty, or updates
413 | for a work that has been modified or installed by the recipient, or for
414 | the User Product in which it has been modified or installed. Access to a
415 | network may be denied when the modification itself materially and
416 | adversely affects the operation of the network or violates the rules and
417 | protocols for communication across the network.
418 |
419 | Corresponding Source conveyed, and Installation Information provided,
420 | in accord with this section must be in a format that is publicly
421 | documented (and with an implementation available to the public in
422 | source code form), and must require no special password or key for
423 | unpacking, reading or copying.
424 |
425 | 7. Additional Terms.
426 |
427 | "Additional permissions" are terms that supplement the terms of this
428 | License by making exceptions from one or more of its conditions.
429 | Additional permissions that are applicable to the entire Program shall
430 | be treated as though they were included in this License, to the extent
431 | that they are valid under applicable law. If additional permissions
432 | apply only to part of the Program, that part may be used separately
433 | under those permissions, but the entire Program remains governed by
434 | this License without regard to the additional permissions.
435 |
436 | When you convey a copy of a covered work, you may at your option
437 | remove any additional permissions from that copy, or from any part of
438 | it. (Additional permissions may be written to require their own
439 | removal in certain cases when you modify the work.) You may place
440 | additional permissions on material, added by you to a covered work,
441 | for which you have or can give appropriate copyright permission.
442 |
443 | Notwithstanding any other provision of this License, for material you
444 | add to a covered work, you may (if authorized by the copyright holders of
445 | that material) supplement the terms of this License with terms:
446 |
447 | a) Disclaiming warranty or limiting liability differently from the
448 | terms of sections 15 and 16 of this License; or
449 |
450 | b) Requiring preservation of specified reasonable legal notices or
451 | author attributions in that material or in the Appropriate Legal
452 | Notices displayed by works containing it; or
453 |
454 | c) Prohibiting misrepresentation of the origin of that material, or
455 | requiring that modified versions of such material be marked in
456 | reasonable ways as different from the original version; or
457 |
458 | d) Limiting the use for publicity purposes of names of licensors or
459 | authors of the material; or
460 |
461 | e) Declining to grant rights under trademark law for use of some
462 | trade names, trademarks, or service marks; or
463 |
464 | f) Requiring indemnification of licensors and authors of that
465 | material by anyone who conveys the material (or modified versions of
466 | it) with contractual assumptions of liability to the recipient, for
467 | any liability that these contractual assumptions directly impose on
468 | those licensors and authors.
469 |
470 | All other non-permissive additional terms are considered "further
471 | restrictions" within the meaning of section 10. If the Program as you
472 | received it, or any part of it, contains a notice stating that it is
473 | governed by this License along with a term that is a further
474 | restriction, you may remove that term. If a license document contains
475 | a further restriction but permits relicensing or conveying under this
476 | License, you may add to a covered work material governed by the terms
477 | of that license document, provided that the further restriction does
478 | not survive such relicensing or conveying.
479 |
480 | If you add terms to a covered work in accord with this section, you
481 | must place, in the relevant source files, a statement of the
482 | additional terms that apply to those files, or a notice indicating
483 | where to find the applicable terms.
484 |
485 | Additional terms, permissive or non-permissive, may be stated in the
486 | form of a separately written license, or stated as exceptions;
487 | the above requirements apply either way.
488 |
489 | 8. Termination.
490 |
491 | You may not propagate or modify a covered work except as expressly
492 | provided under this License. Any attempt otherwise to propagate or
493 | modify it is void, and will automatically terminate your rights under
494 | this License (including any patent licenses granted under the third
495 | paragraph of section 11).
496 |
497 | However, if you cease all violation of this License, then your
498 | license from a particular copyright holder is reinstated (a)
499 | provisionally, unless and until the copyright holder explicitly and
500 | finally terminates your license, and (b) permanently, if the copyright
501 | holder fails to notify you of the violation by some reasonable means
502 | prior to 60 days after the cessation.
503 |
504 | Moreover, your license from a particular copyright holder is
505 | reinstated permanently if the copyright holder notifies you of the
506 | violation by some reasonable means, this is the first time you have
507 | received notice of violation of this License (for any work) from that
508 | copyright holder, and you cure the violation prior to 30 days after
509 | your receipt of the notice.
510 |
511 | Termination of your rights under this section does not terminate the
512 | licenses of parties who have received copies or rights from you under
513 | this License. If your rights have been terminated and not permanently
514 | reinstated, you do not qualify to receive new licenses for the same
515 | material under section 10.
516 |
517 | 9. Acceptance Not Required for Having Copies.
518 |
519 | You are not required to accept this License in order to receive or
520 | run a copy of the Program. Ancillary propagation of a covered work
521 | occurring solely as a consequence of using peer-to-peer transmission
522 | to receive a copy likewise does not require acceptance. However,
523 | nothing other than this License grants you permission to propagate or
524 | modify any covered work. These actions infringe copyright if you do
525 | not accept this License. Therefore, by modifying or propagating a
526 | covered work, you indicate your acceptance of this License to do so.
527 |
528 | 10. Automatic Licensing of Downstream Recipients.
529 |
530 | Each time you convey a covered work, the recipient automatically
531 | receives a license from the original licensors, to run, modify and
532 | propagate that work, subject to this License. You are not responsible
533 | for enforcing compliance by third parties with this License.
534 |
535 | An "entity transaction" is a transaction transferring control of an
536 | organization, or substantially all assets of one, or subdividing an
537 | organization, or merging organizations. If propagation of a covered
538 | work results from an entity transaction, each party to that
539 | transaction who receives a copy of the work also receives whatever
540 | licenses to the work the party's predecessor in interest had or could
541 | give under the previous paragraph, plus a right to possession of the
542 | Corresponding Source of the work from the predecessor in interest, if
543 | the predecessor has it or can get it with reasonable efforts.
544 |
545 | You may not impose any further restrictions on the exercise of the
546 | rights granted or affirmed under this License. For example, you may
547 | not impose a license fee, royalty, or other charge for exercise of
548 | rights granted under this License, and you may not initiate litigation
549 | (including a cross-claim or counterclaim in a lawsuit) alleging that
550 | any patent claim is infringed by making, using, selling, offering for
551 | sale, or importing the Program or any portion of it.
552 |
553 | 11. Patents.
554 |
555 | A "contributor" is a copyright holder who authorizes use under this
556 | License of the Program or a work on which the Program is based. The
557 | work thus licensed is called the contributor's "contributor version".
558 |
559 | A contributor's "essential patent claims" are all patent claims
560 | owned or controlled by the contributor, whether already acquired or
561 | hereafter acquired, that would be infringed by some manner, permitted
562 | by this License, of making, using, or selling its contributor version,
563 | but do not include claims that would be infringed only as a
564 | consequence of further modification of the contributor version. For
565 | purposes of this definition, "control" includes the right to grant
566 | patent sublicenses in a manner consistent with the requirements of
567 | this License.
568 |
569 | Each contributor grants you a non-exclusive, worldwide, royalty-free
570 | patent license under the contributor's essential patent claims, to
571 | make, use, sell, offer for sale, import and otherwise run, modify and
572 | propagate the contents of its contributor version.
573 |
574 | In the following three paragraphs, a "patent license" is any express
575 | agreement or commitment, however denominated, not to enforce a patent
576 | (such as an express permission to practice a patent or covenant not to
577 | sue for patent infringement). To "grant" such a patent license to a
578 | party means to make such an agreement or commitment not to enforce a
579 | patent against the party.
580 |
581 | If you convey a covered work, knowingly relying on a patent license,
582 | and the Corresponding Source of the work is not available for anyone
583 | to copy, free of charge and under the terms of this License, through a
584 | publicly available network server or other readily accessible means,
585 | then you must either (1) cause the Corresponding Source to be so
586 | available, or (2) arrange to deprive yourself of the benefit of the
587 | patent license for this particular work, or (3) arrange, in a manner
588 | consistent with the requirements of this License, to extend the patent
589 | license to downstream recipients. "Knowingly relying" means you have
590 | actual knowledge that, but for the patent license, your conveying the
591 | covered work in a country, or your recipient's use of the covered work
592 | in a country, would infringe one or more identifiable patents in that
593 | country that you have reason to believe are valid.
594 |
595 | If, pursuant to or in connection with a single transaction or
596 | arrangement, you convey, or propagate by procuring conveyance of, a
597 | covered work, and grant a patent license to some of the parties
598 | receiving the covered work authorizing them to use, propagate, modify
599 | or convey a specific copy of the covered work, then the patent license
600 | you grant is automatically extended to all recipients of the covered
601 | work and works based on it.
602 |
603 | A patent license is "discriminatory" if it does not include within
604 | the scope of its coverage, prohibits the exercise of, or is
605 | conditioned on the non-exercise of one or more of the rights that are
606 | specifically granted under this License. You may not convey a covered
607 | work if you are a party to an arrangement with a third party that is
608 | in the business of distributing software, under which you make payment
609 | to the third party based on the extent of your activity of conveying
610 | the work, and under which the third party grants, to any of the
611 | parties who would receive the covered work from you, a discriminatory
612 | patent license (a) in connection with copies of the covered work
613 | conveyed by you (or copies made from those copies), or (b) primarily
614 | for and in connection with specific products or compilations that
615 | contain the covered work, unless you entered into that arrangement,
616 | or that patent license was granted, prior to 28 March 2007.
617 |
618 | Nothing in this License shall be construed as excluding or limiting
619 | any implied license or other defenses to infringement that may
620 | otherwise be available to you under applicable patent law.
621 |
622 | 12. No Surrender of Others' Freedom.
623 |
624 | If conditions are imposed on you (whether by court order, agreement or
625 | otherwise) that contradict the conditions of this License, they do not
626 | excuse you from the conditions of this License. If you cannot convey a
627 | covered work so as to satisfy simultaneously your obligations under this
628 | License and any other pertinent obligations, then as a consequence you may
629 | not convey it at all. For example, if you agree to terms that obligate you
630 | to collect a royalty for further conveying from those to whom you convey
631 | the Program, the only way you could satisfy both those terms and this
632 | License would be to refrain entirely from conveying the Program.
633 |
634 | 13. Use with the GNU Affero General Public License.
635 |
636 | Notwithstanding any other provision of this License, you have
637 | permission to link or combine any covered work with a work licensed
638 | under version 3 of the GNU Affero General Public License into a single
639 | combined work, and to convey the resulting work. The terms of this
640 | License will continue to apply to the part which is the covered work,
641 | but the special requirements of the GNU Affero General Public License,
642 | section 13, concerning interaction through a network will apply to the
643 | combination as such.
644 |
645 | 14. Revised Versions of this License.
646 |
647 | The Free Software Foundation may publish revised and/or new versions of
648 | the GNU General Public License from time to time. Such new versions will
649 | be similar in spirit to the present version, but may differ in detail to
650 | address new problems or concerns.
651 |
652 | Each version is given a distinguishing version number. If the
653 | Program specifies that a certain numbered version of the GNU General
654 | Public License "or any later version" applies to it, you have the
655 | option of following the terms and conditions either of that numbered
656 | version or of any later version published by the Free Software
657 | Foundation. If the Program does not specify a version number of the
658 | GNU General Public License, you may choose any version ever published
659 | by the Free Software Foundation.
660 |
661 | If the Program specifies that a proxy can decide which future
662 | versions of the GNU General Public License can be used, that proxy's
663 | public statement of acceptance of a version permanently authorizes you
664 | to choose that version for the Program.
665 |
666 | Later license versions may give you additional or different
667 | permissions. However, no additional obligations are imposed on any
668 | author or copyright holder as a result of your choosing to follow a
669 | later version.
670 |
671 | 15. Disclaimer of Warranty.
672 |
673 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
674 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
675 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
676 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
677 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
678 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
679 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
680 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
681 |
682 | 16. Limitation of Liability.
683 |
684 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
685 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
686 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
687 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
688 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
689 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
690 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
691 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
692 | SUCH DAMAGES.
693 |
694 | 17. Interpretation of Sections 15 and 16.
695 |
696 | If the disclaimer of warranty and limitation of liability provided
697 | above cannot be given local legal effect according to their terms,
698 | reviewing courts shall apply local law that most closely approximates
699 | an absolute waiver of all civil liability in connection with the
700 | Program, unless a warranty or assumption of liability accompanies a
701 | copy of the Program in return for a fee.
702 |
703 | END OF TERMS AND CONDITIONS
704 |
705 | How to Apply These Terms to Your New Programs
706 |
707 | If you develop a new program, and you want it to be of the greatest
708 | possible use to the public, the best way to achieve this is to make it
709 | free software which everyone can redistribute and change under these terms.
710 |
711 | To do so, attach the following notices to the program. It is safest
712 | to attach them to the start of each source file to most effectively
713 | state the exclusion of warranty; and each file should have at least
714 | the "copyright" line and a pointer to where the full notice is found.
715 |
716 |
717 | Copyright (C)
718 |
719 | This program is free software: you can redistribute it and/or modify
720 | it under the terms of the GNU General Public License as published by
721 | the Free Software Foundation, either version 3 of the License, or
722 | (at your option) any later version.
723 |
724 | This program is distributed in the hope that it will be useful,
725 | but WITHOUT ANY WARRANTY; without even the implied warranty of
726 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
727 | GNU General Public License for more details.
728 |
729 | You should have received a copy of the GNU General Public License
730 | along with this program. If not, see .
731 |
732 | Also add information on how to contact you by electronic and paper mail.
733 |
734 | If the program does terminal interaction, make it output a short
735 | notice like this when it starts in an interactive mode:
736 |
737 | Copyright (C)
738 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
739 | This is free software, and you are welcome to redistribute it
740 | under certain conditions; type `show c' for details.
741 |
742 | The hypothetical commands `show w' and `show c' should show the appropriate
743 | parts of the General Public License. Of course, your program's commands
744 | might be different; for a GUI interface, you would use an "about box".
745 |
746 | You should also get your employer (if you work as a programmer) or school,
747 | if any, to sign a "copyright disclaimer" for the program, if necessary.
748 | For more information on this, and how to apply and follow the GNU GPL, see
749 | .
750 |
751 | The GNU General Public License does not permit incorporating your program
752 | into proprietary programs. If your program is a subroutine library, you
753 | may consider it more useful to permit linking proprietary applications with
754 | the library. If this is what you want to do, use the GNU Lesser General
755 | Public License instead of this License. But first, please read
756 | .
757 |
758 | */
--------------------------------------------------------------------------------
/hardhat.config.ts:
--------------------------------------------------------------------------------
1 | import {HardhatUserConfig} from "hardhat/config";
2 | import "@nomicfoundation/hardhat-toolbox";
3 | import "hardhat-abi-exporter";
4 |
5 | const settings = {optimizer: {enabled: true, runs: 200}};
6 | const config: HardhatUserConfig = {
7 | defaultNetwork: 'hardhat',
8 | abiExporter: [{ // 额外导出ABI接口: web3和ethers
9 | runOnCompile: true,
10 | clear: true,
11 | path: './abi/web3',
12 | format: "json"
13 | }, {
14 | runOnCompile: true,
15 | clear: true,
16 | path: './abi/ethers',
17 | format: "fullName"
18 | }],
19 | networks: {
20 | hardhat: { // 控制区块挖出速度,默认情况下有交易才挖块
21 | mining: {
22 | auto: false,
23 | interval: 1000
24 | }
25 | },
26 | local: {
27 | url: "http://127.0.0.1:8545/",
28 | }
29 | },
30 | solidity: {
31 | compilers: [{
32 | // For uniswap-v2-core
33 | version: '0.5.16',
34 | settings
35 | }, {
36 | // For uniswap-v2-periphery
37 | version: '0.6.6',
38 | settings
39 | }, {
40 | // For arbitrage
41 | version: '0.8.15',
42 | settings
43 | }],
44 | overrides: {
45 | "@uniswap/lib/contracts/libraries/FullMath.sol": {
46 | version: "0.6.6",
47 | settings
48 | },
49 | "@uniswap/lib/contracts/libraries/BitMath.sol": {
50 | version: "0.6.6",
51 | settings
52 | },
53 | "@uniswap/lib/contracts/libraries/FixedPoint.sol": {
54 | version: "0.6.6",
55 | settings
56 | },
57 | "contracts/uniswap-v2-periphery/libraries/UniswapV2OracleLibrary.sol": {
58 | version: "0.6.6",
59 | settings
60 | },
61 | }
62 | },
63 | paths: {
64 | sources: './contracts',
65 | cache: './cache',
66 | artifacts: './artifacts',
67 | },
68 | mocha: {
69 | timeout: 20000,
70 | },
71 | };
72 |
73 | export default config;
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "local-uniswap",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "devDependencies": {
13 | "@nomicfoundation/hardhat-toolbox": "^2.0.1",
14 | "hardhat": "^2.12.6"
15 | },
16 | "dependencies": {
17 | "@openzeppelin/contracts": "^4.8.2",
18 | "@uniswap/lib": "^4.0.1-alpha",
19 | "@uniswap/v2-core": "^1.0.1",
20 | "hardhat-abi-exporter": "^2.10.1"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/scripts/deploy.ts:
--------------------------------------------------------------------------------
1 | import {ethers} from "hardhat";
2 | import {Token, UniswapV2Factory, UniswapV2Router02, WETH9} from "../typechain-types";
3 |
4 | /*
5 | * 代码流程:
6 | * 1- 代币部署:[WETH9, USDT, TOKN]
7 | * 2- 交易所部署:Uniswap x 2
8 | * 3- 交易对部署:[WETH-TOKN, USDT-TOKN, WETH-USDT] x 2
9 | * */
10 | async function main() {
11 | const {weth, usdt, tokn} = await deployTokens();
12 | const {router: router1, factory: factory1} = await deployUniswap(weth);
13 | const {router: router2, factory: factory2} = await deployUniswap(weth);
14 | const {weth_usdt} = await createPairs(weth, usdt, tokn, router1, factory1);
15 | await createPairs(weth, usdt, tokn, router2, factory2);
16 | // TODO 部署自己的合约
17 | console.log(`\n${"-".repeat(32) + deployTokens.name + "-".repeat(32)}`);
18 | // 账号2 approve router1
19 | const [, a1] = await ethers.getSigners();
20 | await (await weth.transfer(a1.address, 1000n * 10n ** 18n)).wait();
21 | console.log("weth balance of " + a1.address, (await weth.balanceOf(a1.address)).toString())
22 | // 查询 账号2 approve router1的情况
23 | await (await weth.connect(a1).approve(router1.address, ethers.constants.MaxInt256)).wait()
24 | const allowance = await weth.allowance(a1.address,router1.address);
25 | console.log("weth allowance ", router1.address, allowance.toString())
26 | }
27 |
28 |
29 |
30 | async function deployTokens() {
31 | console.log(`\n${"-".repeat(32) + deployTokens.name + "-".repeat(32)}`);
32 | const Token = await ethers.getContractFactory("Token");
33 |
34 | const [weth, usdt, tokn] = await Promise.all([
35 | (await (await ethers.getContractFactory('WETH9')).deploy()).deployed(),
36 | (await Token.deploy(`USDT TOKEN`, `USDT`)).deployed(),
37 | (await Token.deploy(`TOKN TOKEN`, `TOKN`)).deployed()
38 | ])
39 | console.log(`${"WETH9 deployed to : ".padStart(28)}${weth.address}`);
40 | console.log(`${"USDT deployed to : ".padStart(28)}${usdt.address}`);
41 | console.log(`${"TOKN deployed to : ".padStart(28)}${tokn.address}`);
42 |
43 | return {weth, usdt, tokn};
44 | }
45 |
46 | async function deployUniswap(weth: WETH9) {
47 | console.log(`${"-".repeat(32) + deployUniswap.name + "-".repeat(32)}`);
48 |
49 | const factory = await (await (await ethers.getContractFactory('UniswapV2Factory')).deploy((await ethers.getSigners())[0].address)).deployed();
50 | const router = await (await (await ethers.getContractFactory('UniswapV2Router02')).deploy(factory.address, weth.address)).deployed();
51 | console.log(`${"Factory deployed to : ".padStart(28)}${factory.address}`);
52 | console.log(`${"Pair init code is : ".padStart(28)}${await factory.pairCodeHash()}`);
53 | console.log(`${"Router deployed to : ".padStart(28)}${router.address}`);
54 |
55 | return {factory, router};
56 | }
57 |
58 | async function createPairFactory(router: UniswapV2Router02, factory: UniswapV2Factory) {
59 | const deadline = Math.floor((new Date()).getTime() / 1000) + 20 * 60;
60 | const amountOf = (num: number) => (10n ** 18n * BigInt(num)).toString();
61 | const toAddress = (await ethers.getSigners())[0].address;
62 | return async (tokenA: string, tokenB: string, numA: number, numB: number) => {
63 | let tx = await router.addLiquidity(tokenA, tokenB, amountOf(numA), amountOf(numB), amountOf(numA), amountOf(numB), toAddress, deadline);
64 | let receipt = await tx.wait();
65 | let log = receipt.logs.filter((log) => log.address === factory.address)[0];
66 | return factory.interface.decodeFunctionResult("createPair", log.data).pair;
67 | }
68 | }
69 |
70 |
71 | async function createPairs(weth: WETH9, usdt: Token, tokn: Token, router: UniswapV2Router02, factory: UniswapV2Factory) {
72 | console.log(`${"-".repeat(11) + createPairs.name + `[${router.address}]` + "-".repeat(11)}`);
73 | // 1- Approve router
74 | const MAX = 2n ** 256n - 1n;
75 | await Promise.all([
76 | weth.approve(router.address, MAX),
77 | usdt.approve(router.address, MAX),
78 | tokn.approve(router.address, MAX)
79 | ]);
80 | // 2- Add Liquidity
81 | const createPair = await createPairFactory(router, factory);
82 | const [weth_tokn, usdt_tokn, weth_usdt] = await Promise.all([
83 | createPair(weth.address, tokn.address, 10, 100),
84 | createPair(usdt.address, tokn.address, 10, 100),
85 | createPair(weth.address, usdt.address, 100, 100)
86 | ]);
87 | console.log(`${"WETH-TOKN Liquidity : ".padStart(28)}${weth_tokn}`);
88 | console.log(`${"USDT-TOKN Liquidity : ".padStart(28)}${usdt_tokn}`);
89 | console.log(`${"WETH-USDT Liquidity : ".padStart(28)}${weth_usdt}`);
90 |
91 | return {weth_tokn, usdt_tokn, weth_usdt};
92 | }
93 |
94 | main().then(() => console.log(" ")).catch(console.error);
95 |
--------------------------------------------------------------------------------
/scripts/init.ts:
--------------------------------------------------------------------------------
1 | import {keccak256} from "@ethersproject/solidity";
2 | import UniswapV2PairMeta from "../artifacts/contracts/uniswap-v2-core/UniswapV2Pair.sol/UniswapV2Pair.json";
3 | import path from "path";
4 | import fs from "fs";
5 |
6 | init()
7 |
8 | function init() {
9 | const COMPUTED_INIT_CODE_HASH = keccak256(['bytes'], [UniswapV2PairMeta.bytecode])
10 | console.log("[CHECK] init code in UniswapV2Library.sol : " + COMPUTED_INIT_CODE_HASH)
11 | let libPath = path.resolve(__dirname, "../contracts/uniswap-v2-periphery/libraries/UniswapV2Library.sol")
12 | fs.writeFileSync(libPath, fs.readFileSync(libPath).toString().replace("10703f189e744d7346631411acd7d5e40a023a5036d4fde2e541403216fe5586", COMPUTED_INIT_CODE_HASH.substring(2)))
13 | }
--------------------------------------------------------------------------------
/scripts/mockTrader0.ts:
--------------------------------------------------------------------------------
1 | import {BigNumber, ethers} from "ethers";
2 | import UniswapV2Router02Abi
3 | from "../abi/ethers/contracts/uniswap-v2-periphery/UniswapV2Router02.sol/UniswapV2Router02.json";
4 | import TokenAbi from "../abi/ethers/contracts/Token.sol/Token.json";
5 | import {UniswapV2Router02, Token} from "../typechain-types"
6 |
7 | // 0号账户: 抢跑机器人 n倍GasPrice
8 | // Account #0: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000 ETH)
9 | // Private Key: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
10 | (async () => {
11 | console.log("0 account 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, listener...")
12 | const RouterAddr = "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9";
13 | const WETHAddr = "0x5FbDB2315678afecb367f032d93F642f64180aa3";
14 | const USDTAddr = "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512";
15 |
16 | const provider = new ethers.providers.JsonRpcProvider(" http://127.0.0.1:8545/");
17 | const wallet = new ethers.Wallet("0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", provider);
18 | const UniswapV2Router02 = new ethers.Contract(RouterAddr, UniswapV2Router02Abi, provider) as UniswapV2Router02;
19 | const WETH = new ethers.Contract(WETHAddr, TokenAbi, provider) as Token;
20 | const USDT = new ethers.Contract(USDTAddr, TokenAbi, provider) as Token;
21 | console.log("1- WETH balance of " + wallet.address, (await WETH.balanceOf(wallet.address)).toString())
22 | console.log("2- USDT balance of " + wallet.address, (await USDT.balanceOf(wallet.address)).toString())
23 |
24 | provider.on("pending", async (tx) => {
25 | if (tx.data.slice(2, 10) === "38ed1739" && tx.from !== "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266") {
26 | let receipt = await (await UniswapV2Router02.connect(wallet).swapExactTokensForTokens(10n ** 18n, 0, [WETHAddr, USDTAddr], wallet.address, new Date().getTime() + 30 * 60, {
27 | gasLimit: 30000000,
28 | gasPrice: (tx.gasPrice as BigNumber).mul(5)
29 | })).wait();
30 | console.log("3- WETH balance of " + wallet.address, (await WETH.balanceOf(wallet.address)).toString())
31 | console.log("4- USDT balance of " + wallet.address, (await USDT.balanceOf(wallet.address)).toString())
32 | console.log(receipt)
33 | }
34 | })
35 | })()
36 |
--------------------------------------------------------------------------------
/scripts/mockTrader1.ts:
--------------------------------------------------------------------------------
1 | import {ethers} from "ethers";
2 | import UniswapV2Router02Abi
3 | from "../abi/ethers/contracts/uniswap-v2-periphery/UniswapV2Router02.sol/UniswapV2Router02.json";
4 | import {UniswapV2Router02, Token} from "../typechain-types"
5 | import TokenAbi from "../abi/ethers/contracts/Token.sol/Token.json";
6 |
7 | // 1号账户: 普通用户
8 | // Account #1: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 (10000 ETH)
9 | // Private Key: 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
10 | (async () => {
11 | const RouterAddr = "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9";
12 | const WETHAddr = "0x5FbDB2315678afecb367f032d93F642f64180aa3";
13 | const USDTAddr = "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512";
14 |
15 | const provider = new ethers.providers.JsonRpcProvider(" http://127.0.0.1:8545/");
16 | const wallet = new ethers.Wallet("0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d", provider);
17 | const UniswapV2Router02 = new ethers.Contract(RouterAddr, UniswapV2Router02Abi, provider) as UniswapV2Router02;
18 | const WETH = new ethers.Contract(WETHAddr, TokenAbi, provider) as Token;
19 | const USDT = new ethers.Contract(USDTAddr, TokenAbi, provider) as Token;
20 | console.log("1- WETH balance of " + wallet.address, (await WETH.balanceOf(wallet.address)).toString())
21 | console.log("2- USDT balance of " + wallet.address, (await USDT.balanceOf(wallet.address)).toString())
22 |
23 | let receipt = await (await UniswapV2Router02.connect(wallet).swapExactTokensForTokens(10n ** 18n, 0, [WETHAddr, USDTAddr], wallet.address, new Date().getTime() + 30 * 60)).wait();
24 |
25 | console.log("3- WETH balance of " + wallet.address, (await WETH.balanceOf(wallet.address)).toString())
26 | console.log("4- USDT balance of " + wallet.address, (await USDT.balanceOf(wallet.address)).toString())
27 | console.log(receipt)
28 | })();
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2020",
4 | "module": "commonjs",
5 | "esModuleInterop": true,
6 | "forceConsistentCasingInFileNames": true,
7 | "strict": true,
8 | "resolveJsonModule": true,
9 | "skipLibCheck": true
10 | }
11 | }
12 |
--------------------------------------------------------------------------------