├── .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 | --------------------------------------------------------------------------------