-
-
Save BlockmanCodes/52ede9384fd774ed18c5dea5912fbe3d to your computer and use it in GitHub Desktop.
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); | |
}); |
[ | |
{ | |
"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" | |
} | |
] |
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>" | |
} | |
} | |
} | |
}; |
{ | |
"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" | |
} | |
} |
No it is not possible we need to wrap it to WETH first.
Invariant failed: WRAPPED . cannot run this code on bsc after modified params.
got this error when swapping on V2 pool
Error: Unexpected pool type in route when constructing trade object
Here is the code to calculate the trade instance:
let route = new RouteV2([pool], nativeEth, token1)
let trade = new V2Trade(route,CurrencyAmount.fromRawAmount(nativeEth, inputEther),TradeType.EXACT_INPUT )
let routerTrade = buildTrade([trade])
what kind of V2 pool format should I use in RouterTrade
? Thanks.
TypeError: SwapRouter.swapERC20CallParameters is not a function
TypeError: SwapRouter.swapERC20CallParameters is not a function
update dependences
"@uniswap/sdk": "^3.0.3",
"@uniswap/sdk-core": "^6.1.1",
"@uniswap/smart-order-router": "^4.9.2",
"@uniswap/universal-router-sdk": "^4.7.0",
"@uniswap/v3-core": "^1.0.1",
"@uniswap/v3-periphery": "^1.4.3",
"@uniswap/v3-sdk": "^3.9.0",
and use
const params = SwapRouter.swapCallParameters(routerTrade, opts)
is working for me
are you able to swap from a ERC20 to Eth with the universal router?