Skip to content

Instantly share code, notes, and snippets.

@BlockmanCodes
Created July 23, 2023 20:04
Show Gist options
  • Select an option

  • Save BlockmanCodes/52ede9384fd774ed18c5dea5912fbe3d to your computer and use it in GitHub Desktop.

Select an option

Save BlockmanCodes/52ede9384fd774ed18c5dea5912fbe3d to your computer and use it in GitHub Desktop.

Revisions

  1. BlockmanCodes created this gist Jul 23, 2023.
    157 changes: 157 additions & 0 deletions 01_simpleSwap.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,157 @@
    const { SwapRouter } = require('@uniswap/universal-router-sdk')
    const { TradeType, Ether, Token, CurrencyAmount, Percent } = require('@uniswap/sdk-core')
    const { Trade: V2Trade } = require('@uniswap/v2-sdk')
    const { Pool, nearestUsableTick, TickMath, TICK_SPACINGS, FeeAmount, Trade: V3Trade, Route: RouteV3 } = require('@uniswap/v3-sdk')
    const { MixedRouteTrade, Trade: RouterTrade } = require('@uniswap/router-sdk')
    const IUniswapV3Pool = require('@uniswap/v3-core/artifacts/contracts/UniswapV3Pool.sol/UniswapV3Pool.json')
    const JSBI = require('jsbi')
    const erc20Abi = require('../abis/erc20.json')


    const hardhat = require("hardhat");
    const provider = hardhat.ethers.provider;


    const ETHER = Ether.onChain(1)
    const WETH = new Token(1, '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', 18, 'WETH', 'Wrapped Ether')
    const USDC = new Token(1, '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', 6, 'USDC', 'USD Coin')


    const wethContract = new hardhat.ethers.Contract(WETH.address, erc20Abi, provider)
    const usdcContract = new hardhat.ethers.Contract(USDC.address, erc20Abi, provider)


    async function getPool(tokenA, tokenB, feeAmount) {
    const [token0, token1] = tokenA.sortsBefore(tokenB) ? [tokenA, tokenB] : [tokenB, tokenA]

    const poolAddress = Pool.getAddress(token0, token1, feeAmount)

    const contract = new hardhat.ethers.Contract(poolAddress, IUniswapV3Pool.abi, provider)

    let liquidity = await contract.liquidity()

    let { sqrtPriceX96, tick } = await contract.slot0()

    liquidity = JSBI.BigInt(liquidity.toString())
    sqrtPriceX96 = JSBI.BigInt(sqrtPriceX96.toString())

    return new Pool(token0, token1, feeAmount, sqrtPriceX96, liquidity, tick, [
    {
    index: nearestUsableTick(TickMath.MIN_TICK, TICK_SPACINGS[feeAmount]),
    liquidityNet: liquidity,
    liquidityGross: liquidity,
    },
    {
    index: nearestUsableTick(TickMath.MAX_TICK, TICK_SPACINGS[feeAmount]),
    liquidityNet: JSBI.multiply(liquidity, JSBI.BigInt('-1')),
    liquidityGross: liquidity,
    },
    ])
    }


    function swapOptions(options) {
    return Object.assign(
    {
    slippageTolerance: new Percent(5, 100),
    recipient: RECIPIENT,
    },
    options
    )
    }


    function buildTrade(trades) {
    return new RouterTrade({
    v2Routes: trades
    .filter((trade) => trade instanceof V2Trade)
    .map((trade) => ({
    routev2: trade.route,
    inputAmount: trade.inputAmount,
    outputAmount: trade.outputAmount,
    })),
    v3Routes: trades
    .filter((trade) => trade instanceof V3Trade)
    .map((trade) => ({
    routev3: trade.route,
    inputAmount: trade.inputAmount,
    outputAmount: trade.outputAmount,
    })),
    mixedRoutes: trades
    .filter((trade) => trade instanceof MixedRouteTrade)
    .map((trade) => ({
    mixedRoute: trade.route,
    inputAmount: trade.inputAmount,
    outputAmount: trade.outputAmount,
    })),
    tradeType: trades[0].tradeType,
    })
    }


    const RECIPIENT = '0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B'


    async function main() {
    const signer = await hardhat.ethers.getImpersonatedSigner(RECIPIENT);

    const WETH_USDC_V3 = await getPool(WETH, USDC, FeeAmount.MEDIUM)

    const inputEther = hardhat.ethers.utils.parseEther('1').toString()

    const trade = await V3Trade.fromRoute(
    new RouteV3([WETH_USDC_V3], ETHER, USDC),
    CurrencyAmount.fromRawAmount(ETHER, inputEther),
    TradeType.EXACT_INPUT
    )

    const routerTrade = buildTrade([trade])

    const opts = swapOptions({})

    const params = SwapRouter.swapERC20CallParameters(routerTrade, opts)

    let ethBalance
    let wethBalance
    let usdcBalance
    ethBalance = await provider.getBalance(RECIPIENT)
    wethBalance = await wethContract.balanceOf(RECIPIENT)
    usdcBalance = await usdcContract.balanceOf(RECIPIENT)
    console.log('---------------------------- BEFORE')
    console.log('ethBalance', hardhat.ethers.utils.formatUnits(ethBalance, 18))
    console.log('wethBalance', hardhat.ethers.utils.formatUnits(wethBalance, 18))
    console.log('usdcBalance', hardhat.ethers.utils.formatUnits(usdcBalance, 6))

    const tx = await signer.sendTransaction({
    data: params.calldata,
    to: '0xEf1c6E67703c7BD7107eed8303Fbe6EC2554BF6B',
    value: params.value,
    from: RECIPIENT,
    })

    const receipt = await tx.wait()
    console.log('---------------------------- SUCCESS?')
    console.log('status', receipt.status)

    ethBalance = await provider.getBalance(RECIPIENT)
    wethBalance = await wethContract.balanceOf(RECIPIENT)
    usdcBalance = await usdcContract.balanceOf(RECIPIENT)
    console.log('---------------------------- AFTER')
    console.log('ethBalance', hardhat.ethers.utils.formatUnits(ethBalance, 18))
    console.log('wethBalance', hardhat.ethers.utils.formatUnits(wethBalance, 18))
    console.log('usdcBalance', hardhat.ethers.utils.formatUnits(usdcBalance, 6))
    }


    /*
    node scripts/01_simpleSwap.js
    */



    main()
    .then(() => process.exit(0))
    .catch((error) => {
    console.error(error);
    process.exit(1);
    });
    277 changes: 277 additions & 0 deletions abis__erc20.json
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,277 @@
    [
    {
    "inputs": [],
    "stateMutability": "nonpayable",
    "type": "constructor"
    },
    {
    "anonymous": false,
    "inputs": [
    {
    "indexed": true,
    "internalType": "address",
    "name": "owner",
    "type": "address"
    },
    {
    "indexed": true,
    "internalType": "address",
    "name": "spender",
    "type": "address"
    },
    {
    "indexed": false,
    "internalType": "uint256",
    "name": "value",
    "type": "uint256"
    }
    ],
    "name": "Approval",
    "type": "event"
    },
    {
    "anonymous": false,
    "inputs": [
    {
    "indexed": true,
    "internalType": "address",
    "name": "from",
    "type": "address"
    },
    {
    "indexed": true,
    "internalType": "address",
    "name": "to",
    "type": "address"
    },
    {
    "indexed": false,
    "internalType": "uint256",
    "name": "value",
    "type": "uint256"
    }
    ],
    "name": "Transfer",
    "type": "event"
    },
    {
    "inputs": [
    {
    "internalType": "address",
    "name": "owner",
    "type": "address"
    },
    {
    "internalType": "address",
    "name": "spender",
    "type": "address"
    }
    ],
    "name": "allowance",
    "outputs": [
    {
    "internalType": "uint256",
    "name": "",
    "type": "uint256"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    },
    {
    "inputs": [
    {
    "internalType": "address",
    "name": "spender",
    "type": "address"
    },
    {
    "internalType": "uint256",
    "name": "amount",
    "type": "uint256"
    }
    ],
    "name": "approve",
    "outputs": [
    {
    "internalType": "bool",
    "name": "",
    "type": "bool"
    }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
    },
    {
    "inputs": [
    {
    "internalType": "address",
    "name": "account",
    "type": "address"
    }
    ],
    "name": "balanceOf",
    "outputs": [
    {
    "internalType": "uint256",
    "name": "",
    "type": "uint256"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    },
    {
    "inputs": [],
    "name": "decimals",
    "outputs": [
    {
    "internalType": "uint8",
    "name": "",
    "type": "uint8"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    },
    {
    "inputs": [
    {
    "internalType": "address",
    "name": "spender",
    "type": "address"
    },
    {
    "internalType": "uint256",
    "name": "subtractedValue",
    "type": "uint256"
    }
    ],
    "name": "decreaseAllowance",
    "outputs": [
    {
    "internalType": "bool",
    "name": "",
    "type": "bool"
    }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
    },
    {
    "inputs": [
    {
    "internalType": "address",
    "name": "spender",
    "type": "address"
    },
    {
    "internalType": "uint256",
    "name": "addedValue",
    "type": "uint256"
    }
    ],
    "name": "increaseAllowance",
    "outputs": [
    {
    "internalType": "bool",
    "name": "",
    "type": "bool"
    }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
    },
    {
    "inputs": [],
    "name": "name",
    "outputs": [
    {
    "internalType": "string",
    "name": "",
    "type": "string"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    },
    {
    "inputs": [],
    "name": "symbol",
    "outputs": [
    {
    "internalType": "string",
    "name": "",
    "type": "string"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    },
    {
    "inputs": [],
    "name": "totalSupply",
    "outputs": [
    {
    "internalType": "uint256",
    "name": "",
    "type": "uint256"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    },
    {
    "inputs": [
    {
    "internalType": "address",
    "name": "to",
    "type": "address"
    },
    {
    "internalType": "uint256",
    "name": "amount",
    "type": "uint256"
    }
    ],
    "name": "transfer",
    "outputs": [
    {
    "internalType": "bool",
    "name": "",
    "type": "bool"
    }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
    },
    {
    "inputs": [
    {
    "internalType": "address",
    "name": "from",
    "type": "address"
    },
    {
    "internalType": "address",
    "name": "to",
    "type": "address"
    },
    {
    "internalType": "uint256",
    "name": "amount",
    "type": "uint256"
    }
    ],
    "name": "transferFrom",
    "outputs": [
    {
    "internalType": "bool",
    "name": "",
    "type": "bool"
    }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
    }
    ]
    13 changes: 13 additions & 0 deletions hardhat.config.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,13 @@
    require("@nomiclabs/hardhat-waffle");

    /** @type import('hardhat/config').HardhatUserConfig */
    module.exports = {
    solidity: "0.8.18",
    networks: {
    hardhat: {
    forking: {
    url: "https://mainnet.infura.io/v3/<YOUR_KEY>"
    }
    }
    }
    };
    26 changes: 26 additions & 0 deletions package.json
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,26 @@
    {
    "name": "tutorials",
    "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.3",
    "@nomiclabs/hardhat-waffle": "^2.0.6",
    "@types/sinon-chai": "^3.2.9",
    "@uniswap/sdk": "^3.0.3",
    "@uniswap/sdk-core": "^4.0.2",
    "@uniswap/smart-order-router": "^3.13.2-hotfix2",
    "@uniswap/v3-core": "^1.0.1",
    "@uniswap/v3-periphery": "^1.4.3",
    "@uniswap/v3-sdk": "^3.9.0",
    "ethereum-waffle": "^4.0.10",
    "ethers": "^5.7.2",
    "hardhat": "^2.14.0"
    }
    }