|
| 1 | +import { ethers, run } from "hardhat"; |
| 2 | +import { web3 } from "hardhat"; |
| 3 | + |
| 4 | +import { UniswapV3WrapperInstance } from "../../typechain-types"; |
| 5 | +import { getAssetManagerFXRP } from "../utils/getters"; |
| 6 | +import { ERC20Instance } from "../../typechain-types/@openzeppelin/contracts/token/ERC20/ERC20"; |
| 7 | + |
| 8 | +// yarn hardhat run scripts/fassets/uniswapV3Wrapper.ts --network flare |
| 9 | + |
| 10 | +// 1. Contract artifacts |
| 11 | +const IAssetManager = artifacts.require("IAssetManager"); |
| 12 | +const UniswapV3Wrapper = artifacts.require("UniswapV3Wrapper"); |
| 13 | +const ERC20 = artifacts.require("ERC20"); |
| 14 | + |
| 15 | +// 2. Flare Uniswap V3 addresses (SparkDEX) |
| 16 | +// https://docs.sparkdex.ai/additional-information/smart-contract-overview/v2-v3.1-dex |
| 17 | +const SWAP_ROUTER = "0x8a1E35F5c98C4E85B36B7B253222eE17773b2781"; |
| 18 | + |
| 19 | +// 3. USDT0 token addresses on Flare |
| 20 | +// https://docs.usdt0.to/technical-documentation/developer#flare-eid-30295 |
| 21 | +const USDT0 = "0xe7cd86e13AC4309349F30B3435a9d337750fC82D"; |
| 22 | + |
| 23 | +// 4. Pool fee tier |
| 24 | +const FEE = 500; // 0.05% |
| 25 | + |
| 26 | +// 5. Swap parameters |
| 27 | +const AMOUNT_IN = ethers.parseUnits("1.0", 6); // 1 USDT0 (6 decimals) |
| 28 | +const AMOUNT_OUT_MIN = ethers.parseUnits("0.3", 6); // 0.3 FXRP minimum expected (6 decimals) |
| 29 | + |
| 30 | +// 6. Function to deploy and verify the smart contract |
| 31 | +async function deployAndVerifyContract() { |
| 32 | + const args = [SWAP_ROUTER]; |
| 33 | + const uniswapV3Wrapper: UniswapV3WrapperInstance = await UniswapV3Wrapper.new( |
| 34 | + ...args, |
| 35 | + ); |
| 36 | + |
| 37 | + const uniswapV3WrapperAddress = await uniswapV3Wrapper.address; |
| 38 | + |
| 39 | + try { |
| 40 | + await run("verify:verify", { |
| 41 | + address: uniswapV3WrapperAddress, |
| 42 | + constructorArguments: args, |
| 43 | + }); |
| 44 | + } catch (e: any) { |
| 45 | + console.log(e); |
| 46 | + } |
| 47 | + |
| 48 | + console.log("UniswapV3Wrapper deployed to:", uniswapV3WrapperAddress); |
| 49 | + |
| 50 | + return uniswapV3Wrapper; |
| 51 | +} |
| 52 | + |
| 53 | +// 7. The main function to execute the swap |
| 54 | +async function main() { |
| 55 | + // 8. Deploy and verify the UniswapV3 wrapper smart contract and get its address |
| 56 | + const uniswapV3Wrapper: UniswapV3WrapperInstance = |
| 57 | + await deployAndVerifyContract(); |
| 58 | + const uniswapV3WrapperAddress = await uniswapV3Wrapper.address; |
| 59 | + |
| 60 | + // 9. Get the deployer account |
| 61 | + const accounts = await web3.eth.getAccounts(); |
| 62 | + const deployer = accounts[0]; |
| 63 | + |
| 64 | + console.log("Deployer:", deployer); |
| 65 | + console.log("Total accounts available:", accounts.length); |
| 66 | + |
| 67 | + // 10. Get the FXRP address on Flare (from the Asset Manager) |
| 68 | + const assetManager = await getAssetManagerFXRP(); |
| 69 | + const FXRP = await assetManager.fAsset(); |
| 70 | + |
| 71 | + console.log("USDT0:", USDT0); |
| 72 | + console.log("FXRP:", FXRP); |
| 73 | + console.log("Fee:", FEE); |
| 74 | + console.log("Amount In:", AMOUNT_IN.toString()); |
| 75 | + console.log("Amount Out Min:", AMOUNT_OUT_MIN.toString()); |
| 76 | + console.log(""); |
| 77 | + |
| 78 | + // 11. Define the USDT0 and FXRP token addresses |
| 79 | + const usdt0: ERC20Instance = await ERC20.at(USDT0); |
| 80 | + const fxrp: ERC20Instance = await ERC20.at(FXRP); |
| 81 | + |
| 82 | + // 12. Check initial balances |
| 83 | + const initialUsdt0Balance = BigInt( |
| 84 | + (await usdt0.balanceOf(deployer)).toString(), |
| 85 | + ); |
| 86 | + const initialFxrpBalance = BigInt( |
| 87 | + (await fxrp.balanceOf(deployer)).toString(), |
| 88 | + ); |
| 89 | + |
| 90 | + console.log("Initial USDT0 balance:", initialUsdt0Balance.toString()); |
| 91 | + console.log("Initial FXRP balance:", initialFxrpBalance.toString()); |
| 92 | + |
| 93 | + // 13. Check if there is enough USDT0 to perform the swap |
| 94 | + const amountInBN = AMOUNT_IN; |
| 95 | + if (initialUsdt0Balance < amountInBN) { |
| 96 | + console.log( |
| 97 | + "❌ Insufficient USDT0 balance. Need:", |
| 98 | + AMOUNT_IN.toString(), |
| 99 | + "Have:", |
| 100 | + initialUsdt0Balance.toString(), |
| 101 | + ); |
| 102 | + console.log( |
| 103 | + "Please ensure you have sufficient USDT0 tokens to perform the swap.", |
| 104 | + ); |
| 105 | + return; |
| 106 | + } |
| 107 | + |
| 108 | + // 14 Check Uniswap V3 pool using wrapper if it exists and has liquidity |
| 109 | + console.log("\n=== Step 1: Pool Verification ==="); |
| 110 | + const poolInfo = await uniswapV3Wrapper.checkPool(USDT0, FXRP, FEE); |
| 111 | + console.log("Pool info:", poolInfo); |
| 112 | + const poolAddress = poolInfo.poolAddress; |
| 113 | + const hasLiquidity = poolInfo.hasLiquidity; |
| 114 | + const liquidity = poolInfo.liquidity; |
| 115 | + |
| 116 | + console.log("Pool Address:", poolAddress); |
| 117 | + console.log("Has Liquidity:", hasLiquidity); |
| 118 | + console.log("Liquidity:", liquidity.toString()); |
| 119 | + |
| 120 | + if (poolAddress === "0x0000000000000000000000000000000000000000") { |
| 121 | + console.log("❌ Pool does not exist for this token pair and fee tier"); |
| 122 | + console.log("Please check if the USDT0/FXRP pool exists on SparkDEX"); |
| 123 | + return; |
| 124 | + } |
| 125 | + |
| 126 | + if (!hasLiquidity) { |
| 127 | + console.log("❌ Pool exists but has no liquidity"); |
| 128 | + return; |
| 129 | + } |
| 130 | + |
| 131 | + console.log("✅ Pool verification successful!"); |
| 132 | + |
| 133 | + // 15. Approve USDT0 to wrapper contract to spend the tokens |
| 134 | + console.log("\n=== Step 2: Token Approval ==="); |
| 135 | + const approveTx = await usdt0.approve( |
| 136 | + uniswapV3WrapperAddress, |
| 137 | + AMOUNT_IN.toString(), |
| 138 | + ); |
| 139 | + console.log("✅ USDT0 approved to wrapper contract", approveTx); |
| 140 | + |
| 141 | + // 16. Execute swap using the Uniswap V3 wrapper contract |
| 142 | + console.log("\n=== Step 3: Execute SparkDEX Swap ==="); |
| 143 | + const deadline = Math.floor(Date.now() / 1000) + 20 * 60; // 20 minutes |
| 144 | + console.log("Deadline:", deadline); |
| 145 | + |
| 146 | + console.log("Executing SparkDEX swap using wrapper..."); |
| 147 | + const swapTx = await uniswapV3Wrapper.swapExactInputSingle( |
| 148 | + USDT0, |
| 149 | + FXRP, |
| 150 | + FEE, |
| 151 | + AMOUNT_IN.toString(), |
| 152 | + AMOUNT_OUT_MIN.toString(), |
| 153 | + deadline, |
| 154 | + 0, // sqrtPriceLimitX96 = 0 (no limit) |
| 155 | + ); |
| 156 | + |
| 157 | + console.log("Transaction submitted:", swapTx); |
| 158 | + |
| 159 | + const swapReceipt = await swapTx.receipt; |
| 160 | + console.log("✅ SparkDEX swap executed successfully!"); |
| 161 | + |
| 162 | + // 17. Extract amount out from events or calculate from balance change |
| 163 | + const finalFxrpBalance = BigInt((await fxrp.balanceOf(deployer)).toString()); |
| 164 | + const amountOut = finalFxrpBalance - initialFxrpBalance; |
| 165 | + console.log("Amount out:", amountOut.toString()); |
| 166 | + |
| 167 | + // 18. Check final balances to verify the swap |
| 168 | + console.log("\n=== Step 4: Final Balances ==="); |
| 169 | + const finalUsdt0Balance = BigInt( |
| 170 | + (await usdt0.balanceOf(deployer)).toString(), |
| 171 | + ); |
| 172 | + const finalFxrpBalanceAfter = BigInt( |
| 173 | + (await fxrp.balanceOf(deployer)).toString(), |
| 174 | + ); |
| 175 | + |
| 176 | + console.log("Final USDT0 balance:", finalUsdt0Balance.toString()); |
| 177 | + console.log("Final FXRP balance:", finalFxrpBalanceAfter.toString()); |
| 178 | + console.log( |
| 179 | + "USDT0 spent:", |
| 180 | + (initialUsdt0Balance - finalUsdt0Balance).toString(), |
| 181 | + ); |
| 182 | + console.log( |
| 183 | + "FXRP received:", |
| 184 | + (finalFxrpBalanceAfter - initialFxrpBalance).toString(), |
| 185 | + ); |
| 186 | +} |
| 187 | + |
| 188 | +main().catch((error) => { |
| 189 | + console.error(error); |
| 190 | + process.exitCode = 1; |
| 191 | +}); |
0 commit comments