-
-
Save owade/ea9d9c60c96ab2c34c84f5c9379782b3 to your computer and use it in GitHub Desktop.
Uniswap V3 Flashloans and Arbitrage
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { Contract, ContractFactory, utils, BigNumber } = require("ethers") | |
const WETH9 = require("../WETH9.json") | |
const artifacts = { | |
UniswapV3Factory: require("@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json"), | |
SwapRouter: require("@uniswap/v3-periphery/artifacts/contracts/SwapRouter.sol/SwapRouter.json"), | |
NFTDescriptor: require("@uniswap/v3-periphery/artifacts/contracts/libraries/NFTDescriptor.sol/NFTDescriptor.json"), | |
NonfungibleTokenPositionDescriptor: require("@uniswap/v3-periphery/artifacts/contracts/NonfungibleTokenPositionDescriptor.sol/NonfungibleTokenPositionDescriptor.json"), | |
NonfungiblePositionManager: require("@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json"), | |
WETH9, | |
}; | |
const linkLibraries = ({ bytecode, linkReferences }, libraries) => { | |
Object.keys(linkReferences).forEach((fileName) => { | |
Object.keys(linkReferences[fileName]).forEach((contractName) => { | |
if (!libraries.hasOwnProperty(contractName)) { | |
throw new Error(`Missing link library name ${contractName}`) | |
} | |
const address = utils | |
.getAddress(libraries[contractName]) | |
.toLowerCase() | |
.slice(2) | |
linkReferences[fileName][contractName].forEach( | |
({ start, length }) => { | |
const start2 = 2 + start * 2 | |
const length2 = length * 2 | |
bytecode = bytecode | |
.slice(0, start2) | |
.concat(address) | |
.concat(bytecode.slice(start2 + length2, bytecode.length)) | |
} | |
) | |
}) | |
}) | |
return bytecode | |
} | |
async function main() { | |
const [owner] = await ethers.getSigners(); | |
Weth = new ContractFactory(artifacts.WETH9.abi, artifacts.WETH9.bytecode, owner); | |
weth = await Weth.deploy(); | |
Factory = new ContractFactory(artifacts.UniswapV3Factory.abi, artifacts.UniswapV3Factory.bytecode, owner); | |
factory = await Factory.deploy(); | |
SwapRouter = new ContractFactory(artifacts.SwapRouter.abi, artifacts.SwapRouter.bytecode, owner); | |
swapRouter = await SwapRouter.deploy(factory.address, weth.address); | |
NFTDescriptor = new ContractFactory(artifacts.NFTDescriptor.abi, artifacts.NFTDescriptor.bytecode, owner); | |
nftDescriptor = await NFTDescriptor.deploy(); | |
const linkedBytecode = linkLibraries( | |
{ | |
bytecode: artifacts.NonfungibleTokenPositionDescriptor.bytecode, | |
linkReferences: { | |
"NFTDescriptor.sol": { | |
NFTDescriptor: [ | |
{ | |
length: 20, | |
start: 1261, | |
}, | |
], | |
}, | |
}, | |
}, | |
{ | |
NFTDescriptor: nftDescriptor.address, | |
} | |
); | |
NonfungibleTokenPositionDescriptor = new ContractFactory(artifacts.NonfungibleTokenPositionDescriptor.abi, linkedBytecode, owner); | |
nonfungibleTokenPositionDescriptor = await NonfungibleTokenPositionDescriptor.deploy(weth.address); | |
NonfungiblePositionManager = new ContractFactory(artifacts.NonfungiblePositionManager.abi, artifacts.NonfungiblePositionManager.bytecode, owner); | |
nonfungiblePositionManager = await NonfungiblePositionManager.deploy(factory.address, weth.address, nonfungibleTokenPositionDescriptor.address); | |
console.log('WETH_ADDRESS=', `'${weth.address}'`) | |
console.log('FACTORY_ADDRESS=', `'${factory.address}'`) | |
console.log('SWAP_ROUTER_ADDRESS=', `'${swapRouter.address}'`) | |
console.log('NFT_DESCRIPTOR_ADDRESS=', `'${nftDescriptor.address}'`) | |
console.log('POSITION_DESCRIPTOR_ADDRESS=', `'${nonfungibleTokenPositionDescriptor.address}'`) | |
console.log('POSITION_MANAGER_ADDRESS=', `'${nonfungiblePositionManager.address}'`) | |
} | |
/* | |
npx hardhat run --network localhost scripts/01_deployContracts.js | |
*/ | |
main() | |
.then(() => process.exit(0)) | |
.catch((error) => { | |
console.error(error); | |
process.exit(1); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
async function main() { | |
const [owner, signer2] = await ethers.getSigners(); | |
Tether = await ethers.getContractFactory('Tether', owner); | |
tether = await Tether.deploy(); | |
Usdc = await ethers.getContractFactory('UsdCoin', owner); | |
usdc = await Usdc.deploy(); | |
await tether.connect(owner).mint( | |
owner.address, | |
ethers.utils.parseEther('100000') | |
) | |
await usdc.connect(owner).mint( | |
owner.address, | |
ethers.utils.parseEther('100000') | |
) | |
console.log('TETHER_ADDRESS=', `'${tether.address}'`) | |
console.log('USDC_ADDRESS=', `'${usdc.address}'`) | |
} | |
/* | |
npx hardhat run --network localhost scripts/02_deployTokens.js | |
*/ | |
main() | |
.then(() => process.exit(0)) | |
.catch((error) => { | |
console.error(error); | |
process.exit(1); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Token addresses | |
TETHER_ADDRESS= '0x0165878A594ca255338adfa4d48449f69242Eb8F' | |
USDC_ADDRESS= '0xa513E6E4b8f2a923D98304ec87F64353C4D5C853' | |
// Uniswap contract address | |
WETH_ADDRESS= '0x5FbDB2315678afecb367f032d93F642f64180aa3' | |
FACTORY_ADDRESS= '0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512' | |
SWAP_ROUTER_ADDRESS= '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0' | |
NFT_DESCRIPTOR_ADDRESS= '0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9' | |
POSITION_DESCRIPTOR_ADDRESS= '0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9' | |
POSITION_MANAGER_ADDRESS= '0x5FC8d32690cc91D4c39d9d3abcBD16989F875707' | |
const artifacts = { | |
UniswapV3Factory: require("@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json"), | |
NonfungiblePositionManager: require("@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json"), | |
}; | |
const { Contract, BigNumber } = require("ethers") | |
const bn = require('bignumber.js') | |
bn.config({ EXPONENTIAL_AT: 999999, DECIMAL_PLACES: 40 }) | |
const provider = waffle.provider; | |
function encodePriceSqrt(reserve1, reserve0) { | |
return BigNumber.from( | |
new bn(reserve1.toString()) | |
.div(reserve0.toString()) | |
.sqrt() | |
.multipliedBy(new bn(2).pow(96)) | |
.integerValue(3) | |
.toString() | |
) | |
} | |
const nonfungiblePositionManager = new Contract( | |
POSITION_MANAGER_ADDRESS, | |
artifacts.NonfungiblePositionManager.abi, | |
provider | |
) | |
const factory = new Contract( | |
FACTORY_ADDRESS, | |
artifacts.UniswapV3Factory.abi, | |
provider | |
) | |
async function deployPool(token0, token1, fee, price) { | |
const [owner] = await ethers.getSigners(); | |
await nonfungiblePositionManager.connect(owner).createAndInitializePoolIfNecessary( | |
token0, | |
token1, | |
fee, | |
price, | |
{ gasLimit: 5000000 } | |
) | |
const poolAddress = await factory.connect(owner).getPool( | |
token0, | |
token1, | |
fee, | |
) | |
return poolAddress | |
} | |
async function main() { | |
const usdtUsdc500 = await deployPool(TETHER_ADDRESS, USDC_ADDRESS, 500, encodePriceSqrt(1, 1)) | |
const usdtUsdc3000 = await deployPool(TETHER_ADDRESS, USDC_ADDRESS, 3000, encodePriceSqrt(1, 2)) | |
const usdtUsdc10000 = await deployPool(TETHER_ADDRESS, USDC_ADDRESS, 10000, encodePriceSqrt(2, 1)) | |
console.log('USDT_USDC_500=', `'${usdtUsdc500}'`) | |
console.log('USDT_USDC_3000=', `'${usdtUsdc3000}'`) | |
console.log('USDT_USDC_10000=', `'${usdtUsdc10000}'`) | |
} | |
/* | |
npx hardhat run --network localhost scripts/03_deployPools.js | |
*/ | |
main() | |
.then(() => process.exit(0)) | |
.catch((error) => { | |
console.error(error); | |
process.exit(1); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Uniswap contract addresses | |
WETH_ADDRESS= '0x5FbDB2315678afecb367f032d93F642f64180aa3' | |
FACTORY_ADDRESS= '0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512' | |
SWAP_ROUTER_ADDRESS= '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0' | |
NFT_DESCRIPTOR_ADDRESS= '0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9' | |
POSITION_DESCRIPTOR_ADDRESS= '0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9' | |
POSITION_MANAGER_ADDRESS= '0x5FC8d32690cc91D4c39d9d3abcBD16989F875707' | |
// Pool addresses | |
USDT_USDC_500= '0x1FA8DDa81477A5b6FA1b2e149e93ed9C7928992F' | |
USDT_USDC_3000= '0x3B00F82071576B8489A6e3df223dcC0e937841d1' | |
USDT_USDC_10000= '0xb09EB46A30889ae3cE4AFa5d8ebD136B4f389B85' | |
// Token addresses | |
TETHER_ADDRESS= '0x0165878A594ca255338adfa4d48449f69242Eb8F' | |
USDC_ADDRESS= '0xa513E6E4b8f2a923D98304ec87F64353C4D5C853' | |
const artifacts = { | |
NonfungiblePositionManager: require("@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json"), | |
Usdt: require("../artifacts/contracts/Tether.sol/Tether.json"), | |
Usdc: require("../artifacts/contracts/UsdCoin.sol/UsdCoin.json"), | |
UniswapV3Pool: require("@uniswap/v3-core/artifacts/contracts/UniswapV3Pool.sol/UniswapV3Pool.json"), | |
}; | |
const { Contract } = require("ethers") | |
const { Token } = require('@uniswap/sdk-core') | |
const { Pool, Position, nearestUsableTick } = require('@uniswap/v3-sdk') | |
async function getPoolData(poolContract) { | |
const [tickSpacing, fee, liquidity, slot0] = await Promise.all([ | |
poolContract.tickSpacing(), | |
poolContract.fee(), | |
poolContract.liquidity(), | |
poolContract.slot0(), | |
]) | |
return { | |
tickSpacing: tickSpacing, | |
fee: fee, | |
liquidity: liquidity, | |
sqrtPriceX96: slot0[0], | |
tick: slot0[1], | |
} | |
} | |
LIQUIDITY = ethers.utils.parseEther('100') | |
DEADLINE = Math.floor(Date.now() / 1000) + (60 * 10) | |
POOL_ADDRESSES = [USDT_USDC_500, USDT_USDC_3000, USDT_USDC_10000] // , | |
async function main() { | |
const [owner, signer2] = await ethers.getSigners(); | |
const provider = waffle.provider; | |
const nonfungiblePositionManager = new Contract( | |
POSITION_MANAGER_ADDRESS, | |
artifacts.NonfungiblePositionManager.abi, | |
provider | |
) | |
const usdtContract = new Contract(TETHER_ADDRESS,artifacts.Usdt.abi,provider) | |
const usdcContract = new Contract(USDC_ADDRESS,artifacts.Usdc.abi,provider) | |
await usdtContract.connect(owner).approve(POSITION_MANAGER_ADDRESS, ethers.utils.parseEther('9999999')) | |
await usdcContract.connect(owner).approve(POSITION_MANAGER_ADDRESS, ethers.utils.parseEther('9999999')) | |
const UsdtToken = new Token(31337, TETHER_ADDRESS, 18, 'USDT', 'Tether') | |
const UsdcToken = new Token(31337, USDC_ADDRESS, 18, 'USDC', 'UsdCoin') | |
const poolContract1 = new Contract(USDT_USDC_500, artifacts.UniswapV3Pool.abi, provider) | |
const poolContract2 = new Contract(USDT_USDC_3000, artifacts.UniswapV3Pool.abi, provider) | |
const poolContract3 = new Contract(USDT_USDC_10000, artifacts.UniswapV3Pool.abi, provider) | |
const poolData = {} | |
poolData[USDT_USDC_500] = await getPoolData(poolContract1) | |
poolData[USDT_USDC_3000] = await getPoolData(poolContract2) | |
poolData[USDT_USDC_10000] = await getPoolData(poolContract3) | |
// appears I cannot interact with contracts in the async map | |
const mintParams = {} | |
POOL_ADDRESSES.map(async poolAddress => { | |
pd = poolData[poolAddress] | |
const poolObj = new Pool( | |
UsdtToken, | |
UsdcToken, | |
pd.fee, | |
pd.sqrtPriceX96.toString(), | |
pd.liquidity.toString(), | |
pd.tick | |
) | |
const tickLower = nearestUsableTick(pd.tick, pd.tickSpacing) - pd.tickSpacing * 100 | |
const tickUpper = nearestUsableTick(pd.tick, pd.tickSpacing) + pd.tickSpacing * 100 | |
const positionObj = new Position({ | |
pool: poolObj, | |
liquidity: LIQUIDITY, | |
tickLower: tickLower, | |
tickUpper: tickUpper, | |
}) | |
const { amount0: amount0Desired, amount1: amount1Desired} = positionObj.mintAmounts | |
const params = { | |
token0: TETHER_ADDRESS, | |
token1: USDC_ADDRESS, | |
fee: pd.fee, | |
tickLower: tickLower, | |
tickUpper: tickUpper, | |
amount0Desired: amount0Desired.toString(), | |
amount1Desired: amount1Desired.toString(), | |
amount0Min: 0, | |
amount1Min: 0, | |
recipient: signer2.address, | |
deadline: DEADLINE | |
} | |
mintParams[poolAddress] = params | |
}) | |
const tx1 = await nonfungiblePositionManager.connect(owner).mint(mintParams[USDT_USDC_500], { gasLimit: '1000000' }) | |
await tx1.wait() | |
const tx2 = await nonfungiblePositionManager.connect(owner).mint(mintParams[USDT_USDC_3000], { gasLimit: '1000000' }) | |
await tx2.wait() | |
const tx3 = await nonfungiblePositionManager.connect(owner).mint(mintParams[USDT_USDC_10000], { gasLimit: '1000000' }) | |
await tx3.wait() | |
console.log('done') | |
} | |
/* | |
npx hardhat run --network localhost scripts/04_addLiquidity.js | |
*/ | |
main() | |
.then(() => process.exit(0)) | |
.catch((error) => { | |
console.error(error); | |
process.exit(1); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// pool | |
USDT_USDC_500= '0x1FA8DDa81477A5b6FA1b2e149e93ed9C7928992F' | |
USDT_USDC_3000= '0x3B00F82071576B8489A6e3df223dcC0e937841d1' | |
USDT_USDC_10000= '0xb09EB46A30889ae3cE4AFa5d8ebD136B4f389B85' | |
const UniswapV3Pool = require("@uniswap/v3-core/artifacts/contracts/UniswapV3Pool.sol/UniswapV3Pool.json") | |
const { Contract, BigNumber } = require("ethers") | |
async function getPoolData(poolContract) { | |
const [tickSpacing, fee, liquidity, slot0] = await Promise.all([ | |
poolContract.tickSpacing(), | |
poolContract.fee(), | |
poolContract.liquidity(), | |
poolContract.slot0(), | |
]) | |
const sqrtPriceX96 = slot0[0] | |
const numerator = BigNumber.from(sqrtPriceX96).pow(2) | |
const denominator = BigNumber.from(2).pow(192) | |
const priceRatio = numerator/denominator | |
return { | |
tickSpacing: tickSpacing, | |
fee: fee, | |
liquidity: liquidity.toString(), | |
sqrtPriceX96: sqrtPriceX96.toString(), | |
priceRatio: priceRatio.toString(), | |
tick: slot0[1], | |
} | |
} | |
async function main() { | |
const provider = waffle.provider; | |
const poolContract500 = new Contract(USDT_USDC_500, UniswapV3Pool.abi, provider) | |
const poolData500 = await getPoolData(poolContract500) | |
console.log('poolData500', poolData500) | |
const poolContract3000 = new Contract(USDT_USDC_3000, UniswapV3Pool.abi, provider) | |
const poolData3000 = await getPoolData(poolContract3000) | |
console.log('poolData3000', poolData3000) | |
const poolContract10000 = new Contract(USDT_USDC_10000, UniswapV3Pool.abi, provider) | |
const poolData10000 = await getPoolData(poolContract10000) | |
console.log('poolData10000', poolData10000) | |
} | |
/* | |
npx hardhat run --network localhost scripts/05_checkLiquidity.js | |
*/ | |
main() | |
.then(() => process.exit(0)) | |
.catch((error) => { | |
console.error(error); | |
process.exit(1); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { Contract } = require("ethers") | |
// Uniswap contract address | |
WETH_ADDRESS= '0x5FbDB2315678afecb367f032d93F642f64180aa3' | |
FACTORY_ADDRESS= '0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512' | |
SWAP_ROUTER_ADDRESS= '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0' | |
NFT_DESCRIPTOR_ADDRESS= '0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9' | |
POSITION_DESCRIPTOR_ADDRESS= '0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9' | |
POSITION_MANAGER_ADDRESS= '0x5FC8d32690cc91D4c39d9d3abcBD16989F875707' | |
// Token addresses | |
TETHER_ADDRESS= '0x0165878A594ca255338adfa4d48449f69242Eb8F' | |
USDC_ADDRESS= '0xa513E6E4b8f2a923D98304ec87F64353C4D5C853' | |
const WETH9 = require("../WETH9.json") | |
const artifacts = { | |
UniswapV3Factory: require("@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json"), | |
SwapRouter: require("@uniswap/v3-periphery/artifacts/contracts/SwapRouter.sol/SwapRouter.json"), | |
NFTDescriptor: require("@uniswap/v3-periphery/artifacts/contracts/libraries/NFTDescriptor.sol/NFTDescriptor.json"), | |
NonfungibleTokenPositionDescriptor: require("@uniswap/v3-periphery/artifacts/contracts/NonfungibleTokenPositionDescriptor.sol/NonfungibleTokenPositionDescriptor.json"), | |
NonfungiblePositionManager: require("@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json"), | |
Usdt: require("../artifacts/contracts/Tether.sol/Tether.json"), | |
Usdc: require("../artifacts/contracts/UsdCoin.sol/UsdCoin.json"), | |
WETH9, | |
}; | |
const toEth = (wei) => ethers.utils.formatEther(wei) | |
async function main() { | |
const provider = waffle.provider; | |
const [owner, signer2] = await ethers.getSigners(); | |
Flash = await ethers.getContractFactory('PairFlash', signer2); | |
flash = await Flash.deploy(SWAP_ROUTER_ADDRESS, FACTORY_ADDRESS, WETH_ADDRESS); | |
console.log('flash', flash.address) | |
const usdtContract = new Contract(TETHER_ADDRESS,artifacts.Usdt.abi,provider) | |
const usdcContract = new Contract(USDC_ADDRESS,artifacts.Usdc.abi,provider) | |
let usdtBalance = await usdtContract.connect(provider).balanceOf(signer2.address) | |
let usdcBalance = await usdcContract.connect(provider).balanceOf(signer2.address) | |
console.log('-------------------- BEFORE') | |
console.log('usdtBalance', toEth(usdtBalance.toString())) | |
console.log('usdcBalance', toEth(usdcBalance.toString())) | |
console.log('--------------------') | |
const tx = await flash.connect(signer2).initFlash( | |
[ | |
TETHER_ADDRESS, | |
USDC_ADDRESS, | |
500, | |
ethers.utils.parseEther('1'), | |
ethers.utils.parseEther('1'), | |
3000, | |
10000 | |
], | |
{ gasLimit: ethers.utils.hexlify(1000000) } | |
); | |
await tx.wait() | |
usdtBalance = await usdtContract.connect(provider).balanceOf(signer2.address) | |
usdcBalance = await usdcContract.connect(provider).balanceOf(signer2.address) | |
console.log('-------------------- AFTER') | |
console.log('usdtBalance', toEth(usdtBalance.toString())) | |
console.log('usdcBalance', toEth(usdcBalance.toString())) | |
console.log('--------------------') | |
} | |
/* | |
npx hardhat run --network localhost scripts/06_flashSwap.js | |
*/ | |
main() | |
.then(() => process.exit(0)) | |
.catch((error) => { | |
console.error(error); | |
process.exit(1); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require("@nomiclabs/hardhat-waffle"); | |
module.exports = { | |
solidity: { | |
compilers: [ | |
{ version: "0.7.6" }, | |
], | |
settings: { | |
optimizer: { | |
enabled: true, | |
runs: 5000, | |
details: { yul: false }, | |
}, | |
} | |
}, | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "solidity-flashswap-1", | |
"version": "1.0.0", | |
"description": "", | |
"main": "index.js", | |
"scripts": { | |
"test": "echo \"Error: no test specified\" && exit 1" | |
}, | |
"keywords": [], | |
"author": "", | |
"license": "ISC", | |
"dependencies": { | |
"@nomiclabs/hardhat-ethers": "^2.2.1", | |
"@nomiclabs/hardhat-waffle": "^2.0.3", | |
"@openzeppelin/contracts": "^3.4.0", | |
"@uniswap/v3-periphery": "^1.0.1", | |
"@uniswap/v3-sdk": "^3.9.0", | |
"bignumber.js": "^9.1.0", | |
"ethereum-waffle": "^3.4.4", | |
"ethers": "^5.7.2", | |
"hardhat": "^2.12.2" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: GPL-2.0-or-later | |
pragma solidity =0.7.6; | |
pragma abicoder v2; | |
import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3FlashCallback.sol'; | |
import '@uniswap/v3-core/contracts/libraries/LowGasSafeMath.sol'; | |
import '@uniswap/v3-periphery/contracts/base/PeripheryPayments.sol'; | |
import '@uniswap/v3-periphery/contracts/base/PeripheryImmutableState.sol'; | |
import '@uniswap/v3-periphery/contracts/libraries/PoolAddress.sol'; | |
import '@uniswap/v3-periphery/contracts/libraries/CallbackValidation.sol'; | |
import '@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol'; | |
import '@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol'; | |
contract PairFlash is IUniswapV3FlashCallback, PeripheryImmutableState, PeripheryPayments { | |
using LowGasSafeMath for uint256; | |
using LowGasSafeMath for int256; | |
ISwapRouter public immutable swapRouter; | |
constructor( | |
ISwapRouter _swapRouter, | |
address _factory, | |
address _WETH9 | |
) PeripheryImmutableState(_factory, _WETH9) { | |
swapRouter = _swapRouter; | |
} | |
function uniswapV3FlashCallback( | |
uint256 fee0, | |
uint256 fee1, | |
bytes calldata data | |
) external override { | |
FlashCallbackData memory decoded = abi.decode(data, (FlashCallbackData)); | |
CallbackValidation.verifyCallback(factory, decoded.poolKey); | |
address token0 = decoded.poolKey.token0; | |
address token1 = decoded.poolKey.token1; | |
TransferHelper.safeApprove(token0, address(swapRouter), decoded.amount0); | |
TransferHelper.safeApprove(token1, address(swapRouter), decoded.amount1); | |
uint256 amount1Min = LowGasSafeMath.add(decoded.amount1, fee1); | |
uint256 amount0Min = LowGasSafeMath.add(decoded.amount0, fee0); | |
uint256 amountOut0 = | |
swapRouter.exactInputSingle( | |
ISwapRouter.ExactInputSingleParams({ | |
tokenIn: token1, | |
tokenOut: token0, | |
fee: decoded.poolFee2, | |
recipient: address(this), | |
deadline: block.timestamp, | |
amountIn: decoded.amount1, | |
amountOutMinimum: amount0Min, | |
sqrtPriceLimitX96: 0 | |
}) | |
); | |
uint256 amountOut1 = | |
swapRouter.exactInputSingle( | |
ISwapRouter.ExactInputSingleParams({ | |
tokenIn: token0, | |
tokenOut: token1, | |
fee: decoded.poolFee3, | |
recipient: address(this), | |
deadline: block.timestamp, | |
amountIn: decoded.amount0, | |
amountOutMinimum: amount1Min, | |
sqrtPriceLimitX96: 0 | |
}) | |
); | |
uint256 amount0Owed = LowGasSafeMath.add(decoded.amount0, fee0); | |
uint256 amount1Owed = LowGasSafeMath.add(decoded.amount1, fee1); | |
TransferHelper.safeApprove(token0, address(this), amount0Owed); | |
TransferHelper.safeApprove(token1, address(this), amount1Owed); | |
if (amount0Owed > 0) pay(token0, address(this), msg.sender, amount0Owed); | |
if (amount1Owed > 0) pay(token1, address(this), msg.sender, amount1Owed); | |
if (amountOut0 > amount0Owed) { | |
uint256 profit0 = LowGasSafeMath.sub(amountOut0, amount0Owed); | |
TransferHelper.safeApprove(token0, address(this), profit0); | |
pay(token0, address(this), decoded.payer, profit0); | |
} | |
if (amountOut1 > amount1Owed) { | |
uint256 profit1 = LowGasSafeMath.sub(amountOut1, amount1Owed); | |
TransferHelper.safeApprove(token0, address(this), profit1); | |
pay(token1, address(this), decoded.payer, profit1); | |
} | |
} | |
struct FlashParams { | |
address token0; | |
address token1; | |
uint24 fee1; | |
uint256 amount0; | |
uint256 amount1; | |
uint24 fee2; | |
uint24 fee3; | |
} | |
struct FlashCallbackData { | |
uint256 amount0; | |
uint256 amount1; | |
address payer; | |
PoolAddress.PoolKey poolKey; | |
uint24 poolFee2; | |
uint24 poolFee3; | |
} | |
function initFlash(FlashParams memory params) external { | |
// {token0: token0, token1: token1, fee: fee} | |
PoolAddress.PoolKey memory poolKey = PoolAddress.PoolKey({token0: params.token0, token1: params.token1, fee: params.fee1}); | |
IUniswapV3Pool pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey)); | |
pool.flash( | |
address(this), | |
params.amount0, | |
params.amount1, | |
abi.encode( | |
FlashCallbackData({ | |
amount0: params.amount0, | |
amount1: params.amount1, | |
payer: msg.sender, | |
poolKey: poolKey, | |
poolFee2: params.fee2, | |
poolFee3: params.fee3 | |
}) | |
) | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: GPL-2.0-or-later | |
pragma solidity =0.7.6; | |
import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; | |
import "@openzeppelin/contracts/access/Ownable.sol"; | |
contract Tether is ERC20, Ownable { | |
constructor() ERC20('Tether', 'USDT') {} | |
function mint(address to, uint256 amount) public onlyOwner { | |
_mint(to, amount); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: GPL-2.0-or-later | |
pragma solidity =0.7.6; | |
import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; | |
import "@openzeppelin/contracts/access/Ownable.sol"; | |
contract UsdCoin is ERC20, Ownable { | |
constructor() ERC20('UsdCoin', 'USDC') {} | |
function mint(address to, uint256 amount) public onlyOwner { | |
_mint(to, amount); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"bytecode": "60606040526040805190810160405280600d81526020017f57726170706564204574686572000000000000000000000000000000000000008152506000908051906020019061004f9291906100c8565b506040805190810160405280600481526020017f57455448000000000000000000000000000000000000000000000000000000008152506001908051906020019061009b9291906100c8565b506012600260006101000a81548160ff021916908360ff16021790555034156100c357600080fd5b61016d565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061010957805160ff1916838001178555610137565b82800160010185558215610137579182015b8281111561013657825182559160200191906001019061011b565b5b5090506101449190610148565b5090565b61016a91905b8082111561016657600081600090555060010161014e565b5090565b90565b610c348061017c6000396000f3006060604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b9578063095ea7b31461014757806318160ddd146101a157806323b872dd146101ca5780632e1a7d4d14610243578063313ce5671461026657806370a082311461029557806395d89b41146102e2578063a9059cbb14610370578063d0e30db0146103ca578063dd62ed3e146103d4575b6100b7610440565b005b34156100c457600080fd5b6100cc6104dd565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561010c5780820151818401526020810190506100f1565b50505050905090810190601f1680156101395780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561015257600080fd5b610187600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061057b565b604051808215151515815260200191505060405180910390f35b34156101ac57600080fd5b6101b461066d565b6040518082815260200191505060405180910390f35b34156101d557600080fd5b610229600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061068c565b604051808215151515815260200191505060405180910390f35b341561024e57600080fd5b61026460048080359060200190919050506109d9565b005b341561027157600080fd5b610279610b05565b604051808260ff1660ff16815260200191505060405180910390f35b34156102a057600080fd5b6102cc600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610b18565b6040518082815260200191505060405180910390f35b34156102ed57600080fd5b6102f5610b30565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561033557808201518184015260208101905061031a565b50505050905090810190601f1680156103625780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561037b57600080fd5b6103b0600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610bce565b604051808215151515815260200191505060405180910390f35b6103d2610440565b005b34156103df57600080fd5b61042a600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610be3565b6040518082815260200191505060405180910390f35b34600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055503373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a2565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105735780601f1061054857610100808354040283529160200191610573565b820191906000526020600020905b81548152906001019060200180831161055657829003601f168201915b505050505081565b600081600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60003073ffffffffffffffffffffffffffffffffffffffff1631905090565b600081600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101515156106dc57600080fd5b3373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16141580156107b457507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414155b156108cf5781600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015151561084457600080fd5b81600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b81600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610a2757600080fd5b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055503373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501515610ab457600080fd5b3373ffffffffffffffffffffffffffffffffffffffff167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65826040518082815260200191505060405180910390a250565b600260009054906101000a900460ff1681565b60036020528060005260406000206000915090505481565b60018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610bc65780601f10610b9b57610100808354040283529160200191610bc6565b820191906000526020600020905b815481529060010190602001808311610ba957829003601f168201915b505050505081565b6000610bdb33848461068c565b905092915050565b60046020528160005260406000206020528060005260406000206000915091505054815600a165627a7a72305820deb4c2ccab3c2fdca32ab3f46728389c2fe2c165d5fafa07661e4e004f6c344a0029", | |
"abi": [ | |
{ | |
"constant": true, | |
"inputs": [], | |
"name": "name", | |
"outputs": [{ "name": "", "type": "string" }], | |
"payable": false, | |
"stateMutability": "view", | |
"type": "function" | |
}, | |
{ | |
"constant": false, | |
"inputs": [ | |
{ "name": "guy", "type": "address" }, | |
{ "name": "wad", "type": "uint256" } | |
], | |
"name": "approve", | |
"outputs": [{ "name": "", "type": "bool" }], | |
"payable": false, | |
"stateMutability": "nonpayable", | |
"type": "function" | |
}, | |
{ | |
"constant": true, | |
"inputs": [], | |
"name": "totalSupply", | |
"outputs": [{ "name": "", "type": "uint256" }], | |
"payable": false, | |
"stateMutability": "view", | |
"type": "function" | |
}, | |
{ | |
"constant": false, | |
"inputs": [ | |
{ "name": "src", "type": "address" }, | |
{ "name": "dst", "type": "address" }, | |
{ "name": "wad", "type": "uint256" } | |
], | |
"name": "transferFrom", | |
"outputs": [{ "name": "", "type": "bool" }], | |
"payable": false, | |
"stateMutability": "nonpayable", | |
"type": "function" | |
}, | |
{ | |
"constant": false, | |
"inputs": [{ "name": "wad", "type": "uint256" }], | |
"name": "withdraw", | |
"outputs": [], | |
"payable": false, | |
"stateMutability": "nonpayable", | |
"type": "function" | |
}, | |
{ | |
"constant": true, | |
"inputs": [], | |
"name": "decimals", | |
"outputs": [{ "name": "", "type": "uint8" }], | |
"payable": false, | |
"stateMutability": "view", | |
"type": "function" | |
}, | |
{ | |
"constant": true, | |
"inputs": [{ "name": "", "type": "address" }], | |
"name": "balanceOf", | |
"outputs": [{ "name": "", "type": "uint256" }], | |
"payable": false, | |
"stateMutability": "view", | |
"type": "function" | |
}, | |
{ | |
"constant": true, | |
"inputs": [], | |
"name": "symbol", | |
"outputs": [{ "name": "", "type": "string" }], | |
"payable": false, | |
"stateMutability": "view", | |
"type": "function" | |
}, | |
{ | |
"constant": false, | |
"inputs": [ | |
{ "name": "dst", "type": "address" }, | |
{ "name": "wad", "type": "uint256" } | |
], | |
"name": "transfer", | |
"outputs": [{ "name": "", "type": "bool" }], | |
"payable": false, | |
"stateMutability": "nonpayable", | |
"type": "function" | |
}, | |
{ | |
"constant": false, | |
"inputs": [], | |
"name": "deposit", | |
"outputs": [], | |
"payable": true, | |
"stateMutability": "payable", | |
"type": "function" | |
}, | |
{ | |
"constant": true, | |
"inputs": [ | |
{ "name": "", "type": "address" }, | |
{ "name": "", "type": "address" } | |
], | |
"name": "allowance", | |
"outputs": [{ "name": "", "type": "uint256" }], | |
"payable": false, | |
"stateMutability": "view", | |
"type": "function" | |
}, | |
{ "payable": true, "stateMutability": "payable", "type": "fallback" }, | |
{ | |
"anonymous": false, | |
"inputs": [ | |
{ "indexed": true, "name": "src", "type": "address" }, | |
{ "indexed": true, "name": "guy", "type": "address" }, | |
{ "indexed": false, "name": "wad", "type": "uint256" } | |
], | |
"name": "Approval", | |
"type": "event" | |
}, | |
{ | |
"anonymous": false, | |
"inputs": [ | |
{ "indexed": true, "name": "src", "type": "address" }, | |
{ "indexed": true, "name": "dst", "type": "address" }, | |
{ "indexed": false, "name": "wad", "type": "uint256" } | |
], | |
"name": "Transfer", | |
"type": "event" | |
}, | |
{ | |
"anonymous": false, | |
"inputs": [ | |
{ "indexed": true, "name": "dst", "type": "address" }, | |
{ "indexed": false, "name": "wad", "type": "uint256" } | |
], | |
"name": "Deposit", | |
"type": "event" | |
}, | |
{ | |
"anonymous": false, | |
"inputs": [ | |
{ "indexed": true, "name": "src", "type": "address" }, | |
{ "indexed": false, "name": "wad", "type": "uint256" } | |
], | |
"name": "Withdrawal", | |
"type": "event" | |
} | |
] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment