189 lines
7.0 KiB
JavaScript
189 lines
7.0 KiB
JavaScript
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);
|
|
});
|