feat: expand non-evm relay and route planning support
This commit is contained in:
188
scripts/chain138/deploy-sushiswap-native.js
Normal file
188
scripts/chain138/deploy-sushiswap-native.js
Normal file
@@ -0,0 +1,188 @@
|
||||
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);
|
||||
});
|
||||
Reference in New Issue
Block a user