const fs = require("fs"); const path = require("path"); const hre = require("hardhat"); const DEFAULTS = { weth: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", usdt: "0x004b63A7B5b0E06f6bB6adb4a5F9f590BF3182D1", usdc: "0x71D6687F38b93CCad569Fa6352c876eea967201b", cusdt: "0x93E66202A11B1772E55407B32B44e5Cd8eda7f22", cusdc: "0xf22258f57794CC8E06237084b353Ab30fFfa640b", }; function envAddress(name, fallback) { const value = String(process.env[name] || fallback || "").trim(); if (!value) { throw new Error(`Missing required address env ${name}`); } return value; } function envUnits(name, fallback, decimals) { return hre.ethers.parseUnits(String(process.env[name] || fallback), decimals); } async function ensureAllowance(token, owner, spender, amount) { const allowance = await token.allowance(owner, spender); if (allowance >= amount) return; const tx = await token.approve(spender, hre.ethers.MaxUint256); await tx.wait(); } async function transferIfNeeded(token, to, amount) { if (amount <= 0n) return; const tx = await token.transfer(to, amount); await tx.wait(); } async function main() { const signers = await hre.ethers.getSigners(); const deployer = signers[0]; const feeTo = signers[5] || deployer; const currentBlock = await hre.ethers.provider.getBlockNumber(); const gasPrice = BigInt(process.env.CHAIN138_SUSHISWAP_GAS_PRICE || (await hre.ethers.provider.send("eth_gasPrice", []))); const deployOverrides = { type: 0, gasPrice, gasLimit: BigInt(process.env.CHAIN138_SUSHISWAP_DEPLOY_GAS_LIMIT || "9000000"), }; const liquidityOverrides = { type: 0, gasPrice, gasLimit: BigInt(process.env.CHAIN138_SUSHISWAP_LIQUIDITY_GAS_LIMIT || "3000000"), }; const weth = envAddress("CHAIN138_NATIVE_WETH9", process.env.WETH9 || DEFAULTS.weth); const usdt = envAddress("CHAIN138_NATIVE_USDT", process.env.OFFICIAL_USDT_ADDRESS || DEFAULTS.usdt); const usdc = envAddress("CHAIN138_NATIVE_USDC", process.env.OFFICIAL_USDC_ADDRESS || DEFAULTS.usdc); const cusdt = envAddress("CHAIN138_COMPLIANT_USDT", process.env.COMPLIANT_USDT_ADDRESS || DEFAULTS.cusdt); const cusdc = envAddress("CHAIN138_COMPLIANT_USDC", process.env.COMPLIANT_USDC_ADDRESS || DEFAULTS.cusdc); const Factory = await hre.ethers.getContractFactory( "contracts/vendor/sushiswap-v2/UniswapV2Factory.sol:UniswapV2Factory" ); const Router = await hre.ethers.getContractFactory( "contracts/vendor/sushiswap-v2/UniswapV2Router02.sol:UniswapV2Router02" ); const Pair = await hre.ethers.getContractFactory( "contracts/vendor/sushiswap-v2/UniswapV2Pair.sol:UniswapV2Pair" ); let factory; let router; const existingFactory = process.env.CHAIN138_SUSHISWAP_EXISTING_FACTORY?.trim(); const existingRouter = process.env.CHAIN138_SUSHISWAP_EXISTING_ROUTER?.trim(); if (existingFactory && existingRouter) { factory = Factory.attach(existingFactory); router = Router.attach(existingRouter); console.log(`[reuse] factory=${existingFactory}`); console.log(`[reuse] router=${existingRouter}`); } else { console.log(`[deploy] SushiSwapFactory gasPrice=${gasPrice}`); factory = await Factory.deploy(feeTo.address, deployOverrides); await factory.waitForDeployment(); console.log(`[ok] factory=${await factory.getAddress()}`); console.log(`[deploy] SushiSwapRouter02`); router = await Router.deploy(await factory.getAddress(), weth, deployOverrides); await router.waitForDeployment(); console.log(`[ok] router=${await router.getAddress()}`); } const erc20Abi = [ "function transfer(address to,uint256 amount) returns (bool)", "function approve(address spender,uint256 amount) returns (bool)", "function allowance(address owner,address spender) view returns (uint256)", "function balanceOf(address account) view returns (uint256)", ]; const tokenByAddress = { [weth.toLowerCase()]: new hre.ethers.Contract(weth, erc20Abi, deployer), [usdt.toLowerCase()]: new hre.ethers.Contract(usdt, erc20Abi, deployer), [usdc.toLowerCase()]: new hre.ethers.Contract(usdc, erc20Abi, deployer), [cusdt.toLowerCase()]: new hre.ethers.Contract(cusdt, erc20Abi, deployer), [cusdc.toLowerCase()]: new hre.ethers.Contract(cusdc, erc20Abi, deployer), }; const seedSpecs = [ { key: "wethUsdt", tokenA: weth, tokenB: usdt, amountA: envUnits("CHAIN138_SUSHISWAP_SEED_WETH_USDT_WETH", "25", 18), amountB: envUnits("CHAIN138_SUSHISWAP_SEED_WETH_USDT_STABLE", "52915", 6), }, { key: "wethUsdc", tokenA: weth, tokenB: usdc, amountA: envUnits("CHAIN138_SUSHISWAP_SEED_WETH_USDC_WETH", "25", 18), amountB: envUnits("CHAIN138_SUSHISWAP_SEED_WETH_USDC_STABLE", "52915", 6), }, { key: "cusdtCusdc", tokenA: cusdt, tokenB: cusdc, amountA: envUnits("CHAIN138_SUSHISWAP_SEED_CUSDT_CUSDC_CUSDT", "250000", 6), amountB: envUnits("CHAIN138_SUSHISWAP_SEED_CUSDT_CUSDC_CUSDC", "250000", 6), }, ]; const deployedPairs = {}; for (const spec of seedSpecs) { console.log(`[seed] ${spec.key}`); let pairAddress = await factory.getPair(spec.tokenA, spec.tokenB); if (pairAddress === hre.ethers.ZeroAddress) { const tx = await factory.createPair(spec.tokenA, spec.tokenB, liquidityOverrides); await tx.wait(); pairAddress = await factory.getPair(spec.tokenA, spec.tokenB); } const pair = Pair.attach(pairAddress); const [reserve0, reserve1] = await pair.getReserves(); if (reserve0 > 0n || reserve1 > 0n) { deployedPairs[spec.key] = pairAddress; console.log(`[skip] ${spec.key} already seeded ${pairAddress}`); continue; } const currentABalance = await tokenByAddress[spec.tokenA.toLowerCase()].balanceOf(pairAddress); const currentBBalance = await tokenByAddress[spec.tokenB.toLowerCase()].balanceOf(pairAddress); const topUpA = spec.amountA > currentABalance ? spec.amountA - currentABalance : 0n; const topUpB = spec.amountB > currentBBalance ? spec.amountB - currentBBalance : 0n; await transferIfNeeded(tokenByAddress[spec.tokenA.toLowerCase()], pairAddress, topUpA); await transferIfNeeded(tokenByAddress[spec.tokenB.toLowerCase()], pairAddress, topUpB); const mintTx = await pair.mint(deployer.address, liquidityOverrides); await mintTx.wait(); deployedPairs[spec.key] = pairAddress; console.log(`[ok] ${spec.key}=${deployedPairs[spec.key]}`); } const output = { chainId: 138, deployer: deployer.address, feeToSetter: feeTo.address, deployedAtBlock: currentBlock, factory: await factory.getAddress(), router: await router.getAddress(), weth, usdt, usdc, cusdt, cusdc, pairs: deployedPairs, }; const outputPath = path.resolve(__dirname, "../../deployments/chain138/sushiswap-native.json"); fs.mkdirSync(path.dirname(outputPath), { recursive: true }); fs.writeFileSync(outputPath, JSON.stringify(output, null, 2) + "\n"); console.log(JSON.stringify(output, null, 2)); } main().catch((error) => { console.error(error); process.exit(1); });