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);
|
||||
});
|
||||
186
scripts/chain138/deploy-uniswap-v2-native.js
Normal file
186
scripts/chain138/deploy-uniswap-v2-native.js
Normal file
@@ -0,0 +1,186 @@
|
||||
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 [deployer] = await hre.ethers.getSigners();
|
||||
const currentBlock = await hre.ethers.provider.getBlockNumber();
|
||||
const gasPrice = BigInt(process.env.CHAIN138_NATIVE_V2_GAS_PRICE || (await hre.ethers.provider.send("eth_gasPrice", [])));
|
||||
const deployOverrides = {
|
||||
type: 0,
|
||||
gasPrice,
|
||||
gasLimit: BigInt(process.env.CHAIN138_NATIVE_V2_DEPLOY_GAS_LIMIT || "9000000"),
|
||||
};
|
||||
const liquidityOverrides = {
|
||||
type: 0,
|
||||
gasPrice,
|
||||
gasLimit: BigInt(process.env.CHAIN138_NATIVE_V2_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/uniswap-v2-core/UniswapV2Factory.sol:UniswapV2Factory"
|
||||
);
|
||||
const Router = await hre.ethers.getContractFactory(
|
||||
"contracts/vendor/uniswap-v2-periphery/UniswapV2Router02.sol:UniswapV2Router02"
|
||||
);
|
||||
const Pair = await hre.ethers.getContractFactory(
|
||||
"contracts/vendor/uniswap-v2-core/UniswapV2Pair.sol:UniswapV2Pair"
|
||||
);
|
||||
|
||||
let factory;
|
||||
let router;
|
||||
|
||||
const existingFactory = process.env.CHAIN138_UNISWAP_V2_EXISTING_FACTORY?.trim();
|
||||
const existingRouter = process.env.CHAIN138_UNISWAP_V2_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] UniswapV2Factory gasPrice=${gasPrice}`);
|
||||
factory = await Factory.deploy(deployer.address, deployOverrides);
|
||||
await factory.waitForDeployment();
|
||||
console.log(`[ok] factory=${await factory.getAddress()}`);
|
||||
console.log(`[deploy] UniswapV2Router02`);
|
||||
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_UNISWAP_V2_SEED_WETH_USDT_WETH", "25", 18),
|
||||
amountB: envUnits("CHAIN138_UNISWAP_V2_SEED_WETH_USDT_STABLE", "52915", 6),
|
||||
},
|
||||
{
|
||||
key: "wethUsdc",
|
||||
tokenA: weth,
|
||||
tokenB: usdc,
|
||||
amountA: envUnits("CHAIN138_UNISWAP_V2_SEED_WETH_USDC_WETH", "25", 18),
|
||||
amountB: envUnits("CHAIN138_UNISWAP_V2_SEED_WETH_USDC_STABLE", "52915", 6),
|
||||
},
|
||||
{
|
||||
key: "cusdtCusdc",
|
||||
tokenA: cusdt,
|
||||
tokenB: cusdc,
|
||||
amountA: envUnits("CHAIN138_UNISWAP_V2_SEED_CUSDT_CUSDC_CUSDT", "250000", 6),
|
||||
amountB: envUnits("CHAIN138_UNISWAP_V2_SEED_CUSDT_CUSDC_CUSDC", "250000", 6),
|
||||
},
|
||||
];
|
||||
|
||||
const deadline = BigInt(Math.floor(Date.now() / 1000) + 3600);
|
||||
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,
|
||||
deployedAtBlock: currentBlock,
|
||||
factory: await factory.getAddress(),
|
||||
router: await router.getAddress(),
|
||||
weth,
|
||||
usdt,
|
||||
usdc,
|
||||
cusdt,
|
||||
cusdc,
|
||||
pairs: deployedPairs,
|
||||
};
|
||||
|
||||
const outputPath = path.resolve(__dirname, "../../deployments/chain138/uniswap-v2-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);
|
||||
});
|
||||
274
scripts/chain138/seed-uni-v2-weth-quote-pairs.js
Normal file
274
scripts/chain138/seed-uni-v2-weth-quote-pairs.js
Normal file
@@ -0,0 +1,274 @@
|
||||
/**
|
||||
* Create and seed Uniswap V2 pairs on Chain 138: each canonical asset vs WETH9 (ERC-20) in the pair.
|
||||
*
|
||||
* Funding the ETH leg (no explicit wrap in this script):
|
||||
* - Liquidity is added via UniswapV2Router02.addLiquidityETH: you send native ETH as `msg.value`.
|
||||
* - The router wraps to WETH9 inside the call and mints LP; this script never calls WETH9.deposit().
|
||||
* - Pair reserves are still WETH + token (AMM math cannot store raw ETH in the pair contract).
|
||||
*
|
||||
* Prerequisites:
|
||||
* - PRIVATE_KEY; deployer holds native ETH (for `value` + gas) + tokens (or MINTER_ROLE to mint).
|
||||
* - Factory + router: CHAIN138_UNISWAP_V2_EXISTING_* or deployments/chain138/uniswap-v2-native.json
|
||||
*
|
||||
* Usage (from smom-dbis-138):
|
||||
* source scripts/load-env.sh
|
||||
* npx hardhat run scripts/chain138/seed-uni-v2-weth-quote-pairs.js --network chain138
|
||||
*
|
||||
* Tuning:
|
||||
* CHAIN138_NATIVE_WETH9 — must match router’s WETH (default 0xC02a…)
|
||||
* CHAIN138_UNI_V2_DEFAULT_WETH — native ETH `value` per new pair (default 0.25)
|
||||
* CHAIN138_UNI_V2_STABLE_PER_WETH — USD face (6-decimal) per 1 ETH for stables (default 2100)
|
||||
* CHAIN138_UNI_V2_SLIPPAGE_BPS — min amounts for addLiquidityETH (default 50 = 0.5%)
|
||||
* CHAIN138_UNI_V2_ETH_GAS_BUFFER_WEI — extra wei required on top of `value` (default ~0.03 ETH for gas)
|
||||
* Per-pair: CHAIN138_UNI_V2_SEED_<key>_WETH and CHAIN138_UNI_V2_SEED_<key>_TOKEN
|
||||
*/
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const hre = require("hardhat");
|
||||
|
||||
const WETH9 = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
|
||||
|
||||
/** Canonical Chain 138 tokens (see docs/11-references/EXPLORER_TOKEN_LIST_CROSSCHECK.md). */
|
||||
const WETH_QUOTE_TARGETS = [
|
||||
{ key: "wethLink", symbol: "LINK", address: "0xb7721dD53A8c629d9f1Ba31a5819AFe250002b03", decimals: 18, kind: "link" },
|
||||
{ key: "wethCusdt", symbol: "cUSDT", address: "0x93E66202A11B1772E55407B32B44e5Cd8eda7f22", decimals: 6, kind: "stable6" },
|
||||
{ key: "wethCusdc", symbol: "cUSDC", address: "0xf22258f57794CC8E06237084b353Ab30fFfa640b", decimals: 6, kind: "stable6" },
|
||||
{ key: "wethWeth10", symbol: "WETH10", address: "0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f", decimals: 18, kind: "weth10" },
|
||||
{ key: "wethCeurc", symbol: "cEURC", address: "0x8085961F9cF02b4d800A3c6d386D31da4B34266a", decimals: 6, kind: "stable6" },
|
||||
{ key: "wethCeurt", symbol: "cEURT", address: "0xdf4b71c61E5912712C1Bdd451416B9aC26949d72", decimals: 6, kind: "stable6" },
|
||||
{ key: "wethCgbpc", symbol: "cGBPC", address: "0x003960f16D9d34F2e98d62723B6721Fb92074aD2", decimals: 6, kind: "stable6" },
|
||||
{ key: "wethCgbrt", symbol: "cGBPT", address: "0x350f54e4D23795f86A9c03988c7135357CCaD97c", decimals: 6, kind: "stable6" },
|
||||
{ key: "wethCaudc", symbol: "cAUDC", address: "0xD51482e567c03899eecE3CAe8a058161FD56069D", decimals: 6, kind: "stable6" },
|
||||
{ key: "wethCjpyc", symbol: "cJPYC", address: "0xEe269e1226a334182aace90056EE4ee5Cc8A6770", decimals: 6, kind: "stable6" },
|
||||
{ key: "wethCchfc", symbol: "cCHFC", address: "0x873990849DDa5117d7C644f0aF24370797C03885", decimals: 6, kind: "stable6" },
|
||||
{ key: "wethCcadc", symbol: "cCADC", address: "0x54dBd40cF05e15906A2C21f600937e96787f5679", decimals: 6, kind: "stable6" },
|
||||
{ key: "wethCxauc", symbol: "cXAUC", address: "0x290E52a8819A4fbD0714E517225429aA2B70EC6b", decimals: 6, kind: "xau6" },
|
||||
{ key: "wethCxaut", symbol: "cXAUT", address: "0x94e408E26c6FD8F4ee00b54dF19082FDA07dC96E", decimals: 6, kind: "xau6" },
|
||||
{ key: "wethUsdt", symbol: "USDT", address: "0x004b63A7B5b0E06f6bB6adb4a5F9f590BF3182D1", decimals: 6, kind: "stable6" },
|
||||
{ key: "wethUsdc", symbol: "USDC", address: "0x71D6687F38b93CCad569Fa6352c876eea967201b", decimals: 6, kind: "stable6" },
|
||||
];
|
||||
|
||||
const erc20Abi = [
|
||||
"function transfer(address to,uint256 amount) returns (bool)",
|
||||
"function approve(address spender,uint256 amount) returns (bool)",
|
||||
"function balanceOf(address account) view returns (uint256)",
|
||||
];
|
||||
|
||||
const routerAbi = [
|
||||
"function addLiquidityETH(address token,uint amountTokenDesired,uint amountTokenMin,uint amountETHMin,address to,uint deadline) payable returns (uint amountToken, uint amountETH, uint liquidity)",
|
||||
];
|
||||
|
||||
const mintAbi = ["function mint(address to,uint256 amount)"];
|
||||
|
||||
function defaultWethFloat() {
|
||||
return parseFloat(process.env.CHAIN138_UNI_V2_DEFAULT_WETH || "0.25");
|
||||
}
|
||||
|
||||
function stablePerWethUsd() {
|
||||
return parseFloat(process.env.CHAIN138_UNI_V2_STABLE_PER_WETH || "2100");
|
||||
}
|
||||
|
||||
async function ensureMinted(tokenAddr, signer, need) {
|
||||
const c = await hre.ethers.getContractAt(erc20Abi, tokenAddr, signer);
|
||||
const bal = await c.balanceOf(signer.address);
|
||||
if (bal >= need) return;
|
||||
const short = need - bal;
|
||||
const m = await hre.ethers.getContractAt(mintAbi, tokenAddr, signer);
|
||||
await (await m.mint(signer.address, short)).wait();
|
||||
}
|
||||
|
||||
async function ensureBalance(tokenAddr, signer, need, meta) {
|
||||
const c = await hre.ethers.getContractAt(erc20Abi, tokenAddr, signer);
|
||||
const bal = await c.balanceOf(signer.address);
|
||||
if (bal >= need) return;
|
||||
try {
|
||||
await ensureMinted(tokenAddr, signer, need);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`${meta.symbol}: need ${hre.ethers.formatUnits(need - bal, meta.decimals)} more; mint failed: ${e.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** Ensure wallet has enough native ETH for addLiquidityETH `value` plus a gas buffer (this script does not wrap). */
|
||||
async function ensureNativeEth(signer, valueWei) {
|
||||
const buffer = BigInt(process.env.CHAIN138_UNI_V2_ETH_GAS_BUFFER_WEI || "30000000000000000");
|
||||
const need = valueWei + buffer;
|
||||
const bal = await signer.provider.getBalance(signer.address);
|
||||
if (bal < need) {
|
||||
throw new Error(
|
||||
`Native ETH: need at least ${hre.ethers.formatEther(need)} (${hre.ethers.formatEther(valueWei)} for pool + buffer); have ${hre.ethers.formatEther(bal)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function computeAmounts(meta) {
|
||||
const envW = process.env[`CHAIN138_UNI_V2_SEED_${meta.key}_WETH`];
|
||||
const envT = process.env[`CHAIN138_UNI_V2_SEED_${meta.key}_TOKEN`];
|
||||
|
||||
const ethWei =
|
||||
envW !== undefined && envW !== ""
|
||||
? hre.ethers.parseEther(String(envW))
|
||||
: hre.ethers.parseEther(String(defaultWethFloat()));
|
||||
|
||||
const wethEth = parseFloat(hre.ethers.formatEther(ethWei));
|
||||
|
||||
let tokenStr;
|
||||
if (envT !== undefined && envT !== "") {
|
||||
tokenStr = String(envT);
|
||||
} else {
|
||||
switch (meta.kind) {
|
||||
case "stable6":
|
||||
tokenStr = String(Math.max(1, Math.round(stablePerWethUsd() * wethEth)));
|
||||
break;
|
||||
case "link":
|
||||
tokenStr = String(Math.max(1, Math.round(100 * wethEth)));
|
||||
break;
|
||||
case "weth10":
|
||||
tokenStr = hre.ethers.formatEther(ethWei);
|
||||
break;
|
||||
case "xau6":
|
||||
tokenStr = String(Math.max(1, Math.round(1 * Math.max(wethEth, 0.01))));
|
||||
break;
|
||||
default:
|
||||
tokenStr = "1";
|
||||
}
|
||||
}
|
||||
|
||||
const tokenAmt = hre.ethers.parseUnits(tokenStr, meta.decimals);
|
||||
return { ethWei, tokenAmt };
|
||||
}
|
||||
|
||||
function minAmounts(amount, bps) {
|
||||
return (amount * (10000n - bps)) / 10000n;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const signers = await hre.ethers.getSigners();
|
||||
if (!signers?.length) {
|
||||
throw new Error("chain138: no signer — set PRIVATE_KEY (32-byte hex) for network chain138 in env");
|
||||
}
|
||||
const deployer = signers[0];
|
||||
const gasPrice = BigInt(process.env.CHAIN138_NATIVE_V2_GAS_PRICE || (await hre.ethers.provider.send("eth_gasPrice", [])));
|
||||
const txOverrides = {
|
||||
type: 0,
|
||||
gasPrice,
|
||||
gasLimit: BigInt(process.env.CHAIN138_NATIVE_V2_LIQUIDITY_GAS_LIMIT || "3000000"),
|
||||
};
|
||||
|
||||
const deploymentPath = path.resolve(__dirname, "../../deployments/chain138/uniswap-v2-native.json");
|
||||
let factory = (process.env.CHAIN138_UNISWAP_V2_EXISTING_FACTORY || "").trim();
|
||||
let routerAddr = (process.env.CHAIN138_UNISWAP_V2_EXISTING_ROUTER || "").trim();
|
||||
if (fs.existsSync(deploymentPath)) {
|
||||
const j = JSON.parse(fs.readFileSync(deploymentPath, "utf8"));
|
||||
if (!factory) factory = j.factory;
|
||||
if (!routerAddr) routerAddr = j.router;
|
||||
}
|
||||
if (!factory) {
|
||||
throw new Error("Set CHAIN138_UNISWAP_V2_EXISTING_FACTORY or add deployments/chain138/uniswap-v2-native.json");
|
||||
}
|
||||
if (!routerAddr) {
|
||||
throw new Error("Set CHAIN138_UNISWAP_V2_EXISTING_ROUTER or add router to deployments/chain138/uniswap-v2-native.json");
|
||||
}
|
||||
|
||||
const Factory = await hre.ethers.getContractFactory(
|
||||
"contracts/vendor/uniswap-v2-core/UniswapV2Factory.sol:UniswapV2Factory"
|
||||
);
|
||||
const Pair = await hre.ethers.getContractFactory(
|
||||
"contracts/vendor/uniswap-v2-core/UniswapV2Pair.sol:UniswapV2Pair"
|
||||
);
|
||||
|
||||
const factoryC = Factory.attach(factory);
|
||||
const wethAddr = (process.env.CHAIN138_NATIVE_WETH9 || process.env.CHAIN138_WETH9_ADDRESS || WETH9).trim();
|
||||
const router = await hre.ethers.getContractAt(routerAbi, routerAddr, deployer);
|
||||
|
||||
console.log(
|
||||
`[eth] add liquidity via router addLiquidityETH (native ETH \`value\`; no WETH9.deposit() in this script) router=${routerAddr}`
|
||||
);
|
||||
console.log(`[weth9] pair reserve token: ${wethAddr} (router wraps ETH inside addLiquidityETH)`);
|
||||
|
||||
const slipBps = BigInt(process.env.CHAIN138_UNI_V2_SLIPPAGE_BPS || "50");
|
||||
const skipKeys = new Set(
|
||||
(process.env.CHAIN138_UNI_V2_SKIP_KEYS || "")
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
);
|
||||
|
||||
/** @type {{ key: string, pair: string, status: string }[]} */
|
||||
const report = [];
|
||||
|
||||
for (const meta of WETH_QUOTE_TARGETS) {
|
||||
if (skipKeys.has(meta.key)) {
|
||||
report.push({ key: meta.key, pair: "", status: "skip_env" });
|
||||
console.log(`[skip] ${meta.symbol} (${meta.key}) via CHAIN138_UNI_V2_SKIP_KEYS`);
|
||||
continue;
|
||||
}
|
||||
const tokenAddr = meta.address;
|
||||
if (tokenAddr.toLowerCase() === wethAddr.toLowerCase()) continue;
|
||||
|
||||
const { ethWei, tokenAmt } = computeAmounts(meta);
|
||||
|
||||
let pairAddress = await factoryC.getPair(wethAddr, tokenAddr);
|
||||
if (pairAddress === hre.ethers.ZeroAddress) {
|
||||
const tx = await factoryC.createPair(wethAddr, tokenAddr, txOverrides);
|
||||
await tx.wait();
|
||||
pairAddress = await factoryC.getPair(wethAddr, tokenAddr);
|
||||
}
|
||||
|
||||
const pair = Pair.connect(deployer).attach(pairAddress);
|
||||
const [r0, r1] = await pair.getReserves();
|
||||
if (r0 > 0n || r1 > 0n) {
|
||||
report.push({ key: meta.key, pair: pairAddress, status: "skip_existing_liquidity" });
|
||||
console.log(`[skip] ${meta.symbol} already funded ${pairAddress}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const tokenC = await hre.ethers.getContractAt(erc20Abi, tokenAddr, deployer);
|
||||
|
||||
await ensureNativeEth(deployer, ethWei);
|
||||
await ensureBalance(tokenAddr, deployer, tokenAmt, meta);
|
||||
|
||||
const allowance = await tokenC.allowance(deployer.address, routerAddr);
|
||||
if (allowance < tokenAmt) {
|
||||
await (await tokenC.approve(routerAddr, hre.ethers.MaxUint256)).wait();
|
||||
}
|
||||
|
||||
const deadline = BigInt(Math.floor(Date.now() / 1000) + 3600);
|
||||
const tokenMin = minAmounts(tokenAmt, slipBps);
|
||||
const ethMin = minAmounts(ethWei, slipBps);
|
||||
|
||||
console.log(`[eth] addLiquidityETH ${meta.symbol}: value=${hre.ethers.formatEther(ethWei)} ETH, token=${tokenAmt.toString()}`);
|
||||
const tx = await router.addLiquidityETH(
|
||||
tokenAddr,
|
||||
tokenAmt,
|
||||
tokenMin,
|
||||
ethMin,
|
||||
deployer.address,
|
||||
deadline,
|
||||
{ ...txOverrides, value: ethWei }
|
||||
);
|
||||
await tx.wait();
|
||||
|
||||
report.push({ key: meta.key, pair: pairAddress, status: "seeded" });
|
||||
console.log(`[ok] ${meta.symbol} ${pairAddress}`);
|
||||
}
|
||||
|
||||
const out = {
|
||||
chainId: 138,
|
||||
deployer: deployer.address,
|
||||
factory,
|
||||
router: routerAddr,
|
||||
weth9: wethAddr,
|
||||
results: report,
|
||||
};
|
||||
const outPath = path.resolve(__dirname, "../../deployments/chain138/uni-v2-weth-quote-seed.json");
|
||||
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
||||
fs.writeFileSync(outPath, JSON.stringify(out, null, 2) + "\n");
|
||||
console.log(JSON.stringify(out, null, 2));
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
291
scripts/deployment/c138-cw-bridge-75-split.sh
Executable file
291
scripts/deployment/c138-cw-bridge-75-split.sh
Executable file
@@ -0,0 +1,291 @@
|
||||
#!/usr/bin/env bash
|
||||
# Plan + verify Chain 138 → 10 networks: move 75% of each c* balance split evenly (7.5% per network).
|
||||
# Uses CWMultiTokenBridgeL1 on 138 (CW_L1_BRIDGE_CHAIN138) when routes are configured.
|
||||
#
|
||||
# Modes:
|
||||
# --plan-only Write JSON table + human summary (default)
|
||||
# --check-routes cast call supportedCanonicalToken + destinations per token×chain
|
||||
# --emit-cmds Print approve + lockAndSend cast lines (dry; review before running)
|
||||
# --help
|
||||
#
|
||||
# Env: PRIVATE_KEY, RPC_URL_138, CW_L1_BRIDGE_CHAIN138, smom-dbis-138/.env
|
||||
# Optional: RECIPIENT_ADDRESS (default: deployer), OUT_JSON (default: reports/status/c138-bridge-75-split-latest.json)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SMOM_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
PROXMOX_ROOT="$(cd "$SMOM_ROOT/.." && pwd)"
|
||||
cd "$SMOM_ROOT"
|
||||
|
||||
MODE="plan"
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--plan-only) MODE="plan" ;;
|
||||
--check-routes) MODE="check" ;;
|
||||
--emit-cmds) MODE="emit" ;;
|
||||
--help|-h)
|
||||
grep '^#' "$0" | head -20
|
||||
exit 0
|
||||
;;
|
||||
*) echo "Unknown: $1"; exit 1 ;;
|
||||
esac
|
||||
shift || true
|
||||
done
|
||||
|
||||
if [[ -f "$PROXMOX_ROOT/scripts/lib/load-project-env.sh" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
PROJECT_ROOT="$PROXMOX_ROOT" source "$PROXMOX_ROOT/scripts/lib/load-project-env.sh"
|
||||
elif [[ -f .env ]]; then
|
||||
set -a && source .env && set +a
|
||||
fi
|
||||
|
||||
RPC="${RPC_URL_138:-${CHAIN138_RPC:-${RPC_URL:-http://192.168.11.211:8545}}}"
|
||||
OUT_JSON="${OUT_JSON:-$SMOM_ROOT/reports/status/c138-bridge-75-split-latest.json}"
|
||||
BRIDGE="${CW_L1_BRIDGE_CHAIN138:-}"
|
||||
DEPLOYER=""
|
||||
if [[ -n "${PRIVATE_KEY:-}" ]]; then
|
||||
DEPLOYER="$(cast wallet address "$PRIVATE_KEY" 2>/dev/null || true)"
|
||||
fi
|
||||
RECIPIENT="${RECIPIENT_ADDRESS:-$DEPLOYER}"
|
||||
export RPC OUT_JSON DEPLOYER RECIPIENT BRIDGE
|
||||
|
||||
# CCIP chain selectors (Chainlink CCIP mainnet directory / repo BRIDGE_CONFIGURATION.md). Verify before prod.
|
||||
declare -A SELECTOR=(
|
||||
[Mainnet]=5009297550715157269
|
||||
[Optimism]=3734403246176062136
|
||||
[Cronos]=1456215246176062136
|
||||
[BSC]=11344663589394136015
|
||||
[Gnosis]=465200170687744372
|
||||
[Polygon]=4051577828743386545
|
||||
[Base]=15971525489660198786
|
||||
[Arbitrum]=4949039107694359620
|
||||
[Celo]=1346049177634351622
|
||||
[Avalanche]=6433500567565415381
|
||||
)
|
||||
|
||||
# name:address (Compliant / canonical c* on 138)
|
||||
read -r -d '' TOKEN_ROWS << 'EOF' || true
|
||||
cUSDT:0x93E66202A11B1772E55407B32B44e5Cd8eda7f22
|
||||
cUSDC:0xf22258f57794CC8E06237084b353Ab30fFfa640b
|
||||
cUSDT_V2:0x8d342d321DdEe97D0c5011DAF8ca0B59DA617D29
|
||||
cUSDC_V2:0x1ac3F4942a71E86A9682D91837E1E71b7BACdF99
|
||||
cEURC:0x8085961F9cF02b4d800A3c6d386D31da4B34266a
|
||||
cEURT:0xdf4b71c61E5912712C1Bdd451416B9aC26949d72
|
||||
cGBPC:0x003960f16D9d34F2e98d62723B6721Fb92074aD2
|
||||
cGBPT:0x350f54e4D23795f86A9c03988c7135357CCaD97c
|
||||
cAUDC:0xD51482e567c03899eecE3CAe8a058161FD56069D
|
||||
cJPYC:0xEe269e1226a334182aace90056EE4ee5Cc8A6770
|
||||
cCHFC:0x873990849DDa5117d7C644f0aF24370797C03885
|
||||
cCADC:0x54dBd40cF05e15906A2C21f600937e96787f5679
|
||||
cAUDT:0xC034b8Ff3088f644D492E95619720ba8fB582933
|
||||
cJPYT:0x54fb3A6b16163D8cFa48EAff79205D1309B1a9A1
|
||||
cCHFT:0xd91f31725444dD1F53FA6dE236A5e90a8281d970
|
||||
cCADT:0xAb456be5Db1E1069F55F75E8c8fecAa6a71D1c8F
|
||||
cXAUC:0x290E52a8819A4fbD0714E517225429aA2B70EC6b
|
||||
cXAUT:0x94e408E26c6FD8F4ee00b54dF19082FDA07dC96E
|
||||
cAUSDT:0x5fdDF65733e3d590463F68f93Cf16E8c04081271
|
||||
EOF
|
||||
|
||||
export TOKEN_ROWS
|
||||
mkdir -p "$(dirname "$OUT_JSON")"
|
||||
|
||||
python3 << PY
|
||||
import json, subprocess, os, re, sys
|
||||
|
||||
rpc = os.environ.get("RPC", "http://192.168.11.211:8545")
|
||||
deployer = os.environ.get("DEPLOYER", "")
|
||||
recipient = os.environ.get("RECIPIENT", deployer)
|
||||
bridge = os.environ.get("BRIDGE", "")
|
||||
|
||||
selectors = {
|
||||
"Mainnet": 5009297550715157269,
|
||||
"Optimism": 3734403246176062136,
|
||||
"Cronos": 1456215246176062136,
|
||||
"BSC": 11344663589394136015,
|
||||
"Gnosis": 465200170687744372,
|
||||
"Polygon": 4051577828743386545,
|
||||
"Base": 15971525489660198786,
|
||||
"Arbitrum": 4949039107694359620,
|
||||
"Celo": 1346049177634351622,
|
||||
"Avalanche": 6433500567565415381,
|
||||
}
|
||||
n_chains = len(selectors)
|
||||
|
||||
rows = []
|
||||
for line in os.environ.get("TOKEN_ROWS", "").strip().split("\n"):
|
||||
if not line.strip() or line.startswith("#"):
|
||||
continue
|
||||
sym, addr = line.split(":", 1)
|
||||
rows.append((sym.strip(), addr.strip()))
|
||||
|
||||
def balance_of(addr):
|
||||
if not deployer:
|
||||
return None
|
||||
r = subprocess.run(
|
||||
["cast", "call", addr, "balanceOf(address)(uint256)", deployer, "--rpc-url", rpc],
|
||||
capture_output=True, text=True,
|
||||
)
|
||||
if r.returncode != 0:
|
||||
return None
|
||||
m = re.match(r"^\s*(\d+)", r.stdout.strip())
|
||||
return int(m.group(1)) if m else None
|
||||
|
||||
plan = {
|
||||
"schema": "c138-bridge-75-split/v1",
|
||||
"rpc_url": rpc,
|
||||
"deployer": deployer,
|
||||
"recipient": recipient,
|
||||
"cw_l1_bridge": bridge,
|
||||
"split": "75% of balance divided evenly across 10 networks (7.5% per chain, integer base units)",
|
||||
"tokens": [],
|
||||
}
|
||||
|
||||
for sym, addr in rows:
|
||||
bal = balance_of(addr)
|
||||
if bal is None:
|
||||
plan["tokens"].append({"symbol": sym, "address": addr, "error": "balance_of_failed"})
|
||||
continue
|
||||
q75 = bal * 75 // 100
|
||||
per = q75 // n_chains
|
||||
rem = q75 % n_chains
|
||||
entry = {
|
||||
"symbol": sym,
|
||||
"address": addr,
|
||||
"balance_wei": str(bal),
|
||||
"pct_75_wei": str(q75),
|
||||
"per_chain_wei": str(per),
|
||||
"remainder_wei": str(rem),
|
||||
"chains": {},
|
||||
}
|
||||
for cname, sel in selectors.items():
|
||||
entry["chains"][cname] = {"selector": str(sel), "amount_wei": str(per)}
|
||||
plan["tokens"].append(entry)
|
||||
|
||||
path = os.environ.get("OUT_JSON", "")
|
||||
with open(path, "w") as f:
|
||||
json.dump(plan, f, indent=2)
|
||||
|
||||
print(json.dumps({"written": path, "tokens": len(plan["tokens"])}))
|
||||
PY
|
||||
|
||||
export RPC DEPLOYER RECIPIENT BRIDGE OUT_JSON
|
||||
export TOKEN_ROWS="$TOKEN_ROWS"
|
||||
|
||||
python3 - <<'PY'
|
||||
import json, os
|
||||
with open(os.environ["OUT_JSON"]) as f:
|
||||
p = json.load(f)
|
||||
print("\n=== c* 75% / 10 networks (per-chain amount, 6 dp human for fiat-style) ===\n")
|
||||
print(f"Deployer: {p.get('deployer','?')}\nRecipient: {p.get('recipient','?')}\nBridge: {p.get('cw_l1_bridge') or '(unset)'}\n")
|
||||
for t in p["tokens"]:
|
||||
if "error" in t:
|
||||
print(f"{t['symbol']}: {t['error']}")
|
||||
continue
|
||||
sym = t["symbol"]
|
||||
per = int(t["per_chain_wei"])
|
||||
if sym.startswith("cXAU"):
|
||||
hu = per / 1e6
|
||||
print(f"{sym:<10} per chain: {hu:,.6f} troy oz (wei={t['per_chain_wei']})")
|
||||
else:
|
||||
hu = per / 1e6
|
||||
print(f"{sym:<10} per chain: {hu:,.6f} tokens (wei={t['per_chain_wei']})")
|
||||
print("\nJSON:", os.environ["OUT_JSON"])
|
||||
PY
|
||||
|
||||
if [[ "$MODE" == "plan" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
[[ -n "$BRIDGE" ]] || { echo "Set CW_L1_BRIDGE_CHAIN138"; exit 1; }
|
||||
code=$(cast code "$BRIDGE" --rpc-url "$RPC" 2>/dev/null || echo "0x")
|
||||
[[ -n "$code" && "$code" != "0x" ]] || { echo "No contract at CW_L1_BRIDGE_CHAIN138=$BRIDGE"; exit 1; }
|
||||
|
||||
if [[ "$MODE" == "check" ]]; then
|
||||
echo ""
|
||||
echo "=== Route checks: $BRIDGE ==="
|
||||
while IFS= read -r line; do
|
||||
[[ -z "$line" ]] && continue
|
||||
sym="${line%%:*}"
|
||||
addr="${line#*:}"
|
||||
if sup_raw=$(cast call "$BRIDGE" "supportedCanonicalToken(address)(bool)" "$addr" --rpc-url "$RPC" 2>/dev/null); then
|
||||
echo "$sym supported=$sup_raw"
|
||||
else
|
||||
echo "$sym supported=(query reverted — non-CW ABI or older build; rely on destinations below)"
|
||||
fi
|
||||
for net in Mainnet Optimism Cronos BSC Gnosis Polygon Base Arbitrum Celo Avalanche; do
|
||||
sel="${SELECTOR[$net]}"
|
||||
dest=$(cast call "$BRIDGE" "destinations(address,uint64)(address,bool)" "$addr" "$sel" --rpc-url "$RPC" 2>/dev/null || echo "ERR")
|
||||
echo " $net ($sel): $dest"
|
||||
done
|
||||
done <<< "$TOKEN_ROWS"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$MODE" == "emit" ]]; then
|
||||
[[ -n "${PRIVATE_KEY:-}" ]] || { echo "PRIVATE_KEY required for --emit-cmds"; exit 1; }
|
||||
[[ -n "$RECIPIENT" ]] || { echo "RECIPIENT_ADDRESS or deployer required"; exit 1; }
|
||||
LINK_TOKEN="${LINK_TOKEN_CHAIN138:-${LINK_TOKEN:-}}"
|
||||
[[ -n "$LINK_TOKEN" ]] || { echo "Set LINK_TOKEN or LINK_TOKEN_CHAIN138 for fee approval lines"; exit 1; }
|
||||
export LINK_TOKEN
|
||||
echo ""
|
||||
echo "=== Review-only cast snippets (feeToken=LINK on this bridge: approve LINK, approve token, lockAndSend) ==="
|
||||
OUT_CAST="${OUT_CAST:-$SMOM_ROOT/reports/status/c138-bridge-75-split-cast-commands.sh}"
|
||||
export OUT_CAST
|
||||
{
|
||||
echo "#!/usr/bin/env bash"
|
||||
echo "# Generated: c138-cw-bridge-75-split.sh --emit-cmds"
|
||||
echo "# Review destinations + reserve verifier before running. Fund LINK for fees."
|
||||
echo "set -euo pipefail"
|
||||
# Quoted heredoc: do not let bash expand \$PRIVATE_KEY into the generated file.
|
||||
python3 <<'PY'
|
||||
import json, os, subprocess
|
||||
rpc = os.environ["RPC"]
|
||||
bridge = os.environ["BRIDGE"]
|
||||
recipient = os.environ["RECIPIENT"]
|
||||
link = os.environ["LINK_TOKEN"]
|
||||
with open(os.environ["OUT_JSON"]) as f:
|
||||
plan = json.load(f)
|
||||
selectors = {
|
||||
"Mainnet": 5009297550715157269,
|
||||
"Optimism": 3734403246176062136,
|
||||
"Cronos": 1456215246176062136,
|
||||
"BSC": 11344663589394136015,
|
||||
"Gnosis": 465200170687744372,
|
||||
"Polygon": 4051577828743386545,
|
||||
"Base": 15971525489660198786,
|
||||
"Arbitrum": 4949039107694359620,
|
||||
"Celo": 1346049177634351622,
|
||||
"Avalanche": 6433500567565415381,
|
||||
}
|
||||
for t in plan["tokens"]:
|
||||
if "error" in t or int(t.get("per_chain_wei", 0)) == 0:
|
||||
continue
|
||||
sym, token, amt = t["symbol"], t["address"], t["per_chain_wei"]
|
||||
for cname, sel in selectors.items():
|
||||
chk = subprocess.run(
|
||||
["cast", "call", bridge, "destinations(address,uint64)(address,bool)", token, str(sel), "--rpc-url", rpc],
|
||||
capture_output=True, text=True,
|
||||
)
|
||||
if chk.returncode != 0 or "true" not in chk.stdout:
|
||||
continue
|
||||
fee = subprocess.run(
|
||||
["cast", "call", bridge,
|
||||
"calculateFee(address,uint64,address,uint256)(uint256)",
|
||||
token, str(sel), recipient, amt,
|
||||
"--rpc-url", rpc],
|
||||
capture_output=True, text=True,
|
||||
)
|
||||
fq = fee.stdout.strip().split()[0] if fee.returncode == 0 else "0"
|
||||
print("")
|
||||
print(f"# {sym} -> {cname} selector={sel} amount={amt} fee_wei={fq}")
|
||||
print(f"cast send {link} \"approve(address,uint256)\" {bridge} {fq} --rpc-url {rpc} --private-key \"$PRIVATE_KEY\" --legacy --gas-limit 120000")
|
||||
print(f"cast send {token} \"approve(address,uint256)\" {bridge} {amt} --rpc-url {rpc} --private-key \"$PRIVATE_KEY\" --legacy --gas-limit 400000")
|
||||
print(f"cast send {bridge} \"lockAndSend(address,uint64,address,uint256)\" {token} {sel} {recipient} {amt} --rpc-url {rpc} --private-key \"$PRIVATE_KEY\" --legacy --gas-limit 4000000")
|
||||
PY
|
||||
} > "$OUT_CAST"
|
||||
chmod +x "$OUT_CAST"
|
||||
echo "Wrote enabled-routes-only commands: $OUT_CAST"
|
||||
wc -l "$OUT_CAST"
|
||||
exit 0
|
||||
fi
|
||||
96
scripts/deployment/cw-l1-bootstrap-gru-v2-ccip-routes.sh
Executable file
96
scripts/deployment/cw-l1-bootstrap-gru-v2-ccip-routes.sh
Executable file
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env bash
|
||||
# Register GRU v2 (CompliantFiatTokenV2) addresses on CWMultiTokenBridgeL1 and
|
||||
# configureDestination for the same 10 CCIP lanes as cUSDT/cUSDC (receivers from CW_BRIDGE_*).
|
||||
#
|
||||
# Note: The CWMultiTokenBridgeL1 deployed at CW_L1_BRIDGE_CHAIN138 on Chain 138 does **not**
|
||||
# include configureSupportedCanonicalToken in runtime bytecode (older build). Do not send that
|
||||
# call — it always reverts. Destinations + lockAndSend are the supported path.
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/deployment/cw-l1-bootstrap-gru-v2-ccip-routes.sh [--dry-run]
|
||||
#
|
||||
# Env: PROJECT_ROOT load via proxmox scripts/lib/load-project-env.sh or smom-dbis-138/.env
|
||||
# CW_L1_BRIDGE_CHAIN138, RPC_URL_138, PRIVATE_KEY (deployer = bridge admin)
|
||||
# CW_BRIDGE_MAINNET, CW_BRIDGE_OPTIMISM, CW_BRIDGE_CRONOS, CW_BRIDGE_BSC,
|
||||
# CW_BRIDGE_GNOSIS, CW_BRIDGE_POLYGON, CW_BRIDGE_BASE, CW_BRIDGE_ARBITRUM,
|
||||
# CW_BRIDGE_CELO, CW_BRIDGE_AVALANCHE
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SMOM_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
PROXMOX_ROOT="$(cd "$SMOM_ROOT/.." && pwd)"
|
||||
cd "$SMOM_ROOT"
|
||||
|
||||
DRY_RUN=0
|
||||
[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=1
|
||||
|
||||
if [[ -f "$PROXMOX_ROOT/scripts/lib/load-project-env.sh" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
PROJECT_ROOT="$PROXMOX_ROOT" source "$PROXMOX_ROOT/scripts/lib/load-project-env.sh"
|
||||
elif [[ -f .env ]]; then
|
||||
set -a && source .env && set +a
|
||||
fi
|
||||
|
||||
BRIDGE="${CW_L1_BRIDGE_CHAIN138:-}"
|
||||
RPC="${RPC_URL_138:-${CHAIN138_RPC:-${RPC_URL:-http://192.168.11.211:8545}}}"
|
||||
|
||||
[[ -n "$BRIDGE" && "$BRIDGE" != "0x0000000000000000000000000000000000000000" ]] || {
|
||||
echo "Set CW_L1_BRIDGE_CHAIN138" >&2
|
||||
exit 1
|
||||
}
|
||||
[[ -n "${PRIVATE_KEY:-}" ]] || { echo "PRIVATE_KEY required" >&2; exit 1; }
|
||||
|
||||
# GRU v2 on Chain 138 (wallet-token-submissions.chain138.json)
|
||||
V2=(
|
||||
"0x243e6581Dc8a98d98B92265858b322b193555C81"
|
||||
"0x2bAFA83d8fF8BaE9505511998987D0659791605B"
|
||||
"0x707508D223103f5D2d9EFBc656302c9d48878b29"
|
||||
"0xee17c18E10E55ce23F7457D018aAa2Fb1E64B281"
|
||||
"0xfb37aFd415B70C5cEDc9bA58a72D517207b769Bb"
|
||||
"0x2c751bBE4f299b989b3A8c333E0A966cdcA6Fd98"
|
||||
"0x60B7FB8e0DD0Be8595AD12Fe80AE832861Be747c"
|
||||
"0xe799033c87fE0CE316DAECcefBE3134CC74b76a9"
|
||||
"0xF0F0F81bE3D033D8586bAfd2293e37eE2f615647"
|
||||
"0x89477E982847023aaB5C3492082cd1bB4b1b9Ef1"
|
||||
)
|
||||
|
||||
# selector|receiver (must match c138-cw-bridge-75-split.sh / BRIDGE_CONFIGURATION)
|
||||
declare -a ROUTES=(
|
||||
"5009297550715157269|${CW_BRIDGE_MAINNET:-}"
|
||||
"3734403246176062136|${CW_BRIDGE_OPTIMISM:-}"
|
||||
"1456215246176062136|${CW_BRIDGE_CRONOS:-}"
|
||||
"11344663589394136015|${CW_BRIDGE_BSC:-}"
|
||||
"465200170687744372|${CW_BRIDGE_GNOSIS:-}"
|
||||
"4051577828743386545|${CW_BRIDGE_POLYGON:-}"
|
||||
"15971525489660198786|${CW_BRIDGE_BASE:-}"
|
||||
"4949039107694359620|${CW_BRIDGE_ARBITRUM:-}"
|
||||
"1346049177634351622|${CW_BRIDGE_CELO:-}"
|
||||
"6433500567565415381|${CW_BRIDGE_AVALANCHE:-}"
|
||||
)
|
||||
|
||||
send() {
|
||||
if [[ "$DRY_RUN" -eq 1 ]]; then
|
||||
echo "[dry-run] $*"
|
||||
return 0
|
||||
fi
|
||||
"$@"
|
||||
}
|
||||
|
||||
echo "=== configureDestination (10 GRU v2 tokens x up to 10 chains) ==="
|
||||
for t in "${V2[@]}"; do
|
||||
for row in "${ROUTES[@]}"; do
|
||||
sel="${row%%|*}"
|
||||
recv="${row#*|}"
|
||||
if [[ -z "$recv" || "$recv" == "0x0000000000000000000000000000000000000000" ]]; then
|
||||
echo "SKIP selector=$sel (receiver unset)"
|
||||
continue
|
||||
fi
|
||||
echo "-> token=$t selector=$sel recv=$recv"
|
||||
send cast send "$BRIDGE" "configureDestination(address,uint64,address,bool)" "$t" "$sel" "$recv" true \
|
||||
--rpc-url "$RPC" --private-key "$PRIVATE_KEY" --legacy --gas-limit 500000
|
||||
done
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Done. Verify with: cast call $BRIDGE \"destinations(address,uint64)(address,bool)\" <token> <selector> --rpc-url $RPC"
|
||||
106
scripts/deployment/cw-l1-bridge-gru-v2-90pct-lock-and-send.sh
Executable file
106
scripts/deployment/cw-l1-bridge-gru-v2-90pct-lock-and-send.sh
Executable file
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env bash
|
||||
# After cw-l1-bootstrap-gru-v2-ccip-routes.sh: approve LINK + tokens, then lockAndSend
|
||||
# 90% / 10 per-chain amounts to RECIPIENT_ADDRESS (default Ulysse EVM).
|
||||
#
|
||||
# Usage:
|
||||
# RECIPIENT_ADDRESS=0x... bash scripts/deployment/cw-l1-bridge-gru-v2-90pct-lock-and-send.sh [--dry-run]
|
||||
#
|
||||
# Env: CW_L1_BRIDGE_CHAIN138, RPC_URL_138, PRIVATE_KEY
|
||||
# LINK: fee token on bridge (defaults to feeToken() on bridge)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SMOM_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
PROXMOX_ROOT="$(cd "$SMOM_ROOT/.." && pwd)"
|
||||
cd "$SMOM_ROOT"
|
||||
|
||||
DRY_RUN=0
|
||||
[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=1
|
||||
|
||||
if [[ -f "$PROXMOX_ROOT/scripts/lib/load-project-env.sh" ]]; then
|
||||
PROJECT_ROOT="$PROXMOX_ROOT" source "$PROXMOX_ROOT/scripts/lib/load-project-env.sh"
|
||||
elif [[ -f .env ]]; then
|
||||
set -a && source .env && set +a
|
||||
fi
|
||||
|
||||
BRIDGE="${CW_L1_BRIDGE_CHAIN138:-}"
|
||||
RPC="${RPC_URL_138:-http://192.168.11.211:8545}"
|
||||
RECIPIENT="${RECIPIENT_ADDRESS:-0xCbe669ECe9e3Da7dD1688207e545EDDA9a64A514}"
|
||||
|
||||
[[ -n "$BRIDGE" ]] || { echo "Set CW_L1_BRIDGE_CHAIN138" >&2; exit 1; }
|
||||
[[ -n "${PRIVATE_KEY:-}" ]] || { echo "PRIVATE_KEY required" >&2; exit 1; }
|
||||
|
||||
LINK_TOKEN="${LINK_TOKEN_CHAIN138:-${LINK_TOKEN:-}}"
|
||||
if [[ -z "$LINK_TOKEN" ]]; then
|
||||
LINK_TOKEN=$(cast call "$BRIDGE" "feeToken()(address)" --rpc-url "$RPC" 2>/dev/null | grep -oE '0x[a-fA-F0-9]{40}' | head -1)
|
||||
fi
|
||||
[[ -n "$LINK_TOKEN" && "$LINK_TOKEN" != "0x0000000000000000000000000000000000000000" ]] || {
|
||||
echo "Could not resolve LINK fee token; set LINK_TOKEN_CHAIN138" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
MAX_U256="115792089237316195423570985008687907853269984665640564039457584007913129639935"
|
||||
|
||||
# token|per_chain_wei (90% / 10 floors from operator plan)
|
||||
declare -a PAIRS=(
|
||||
"0x243e6581Dc8a98d98B92265858b322b193555C81|3822172740000"
|
||||
"0x2bAFA83d8fF8BaE9505511998987D0659791605B|2778945930000"
|
||||
"0x707508D223103f5D2d9EFBc656302c9d48878b29|4123219860000"
|
||||
"0xee17c18E10E55ce23F7457D018aAa2Fb1E64B281|4744441350000"
|
||||
"0xfb37aFd415B70C5cEDc9bA58a72D517207b769Bb|6522556410000"
|
||||
"0x2c751bBE4f299b989b3A8c333E0A966cdcA6Fd98|3557466000000"
|
||||
"0x60B7FB8e0DD0Be8595AD12Fe80AE832861Be747c|6810124410000"
|
||||
"0xe799033c87fE0CE316DAECcefBE3134CC74b76a9|2471337720000"
|
||||
"0xF0F0F81bE3D033D8586bAfd2293e37eE2f615647|2421904773"
|
||||
"0x89477E982847023aaB5C3492082cd1bB4b1b9Ef1|2421904773"
|
||||
)
|
||||
|
||||
# Same 10 selectors as bootstrap script (order must match)
|
||||
declare -a SELECTORS=(
|
||||
5009297550715157269
|
||||
3734403246176062136
|
||||
1456215246176062136
|
||||
11344663589394136015
|
||||
465200170687744372
|
||||
4051577828743386545
|
||||
15971525489660198786
|
||||
4949039107694359620
|
||||
1346049177634351622
|
||||
6433500567565415381
|
||||
)
|
||||
|
||||
send() {
|
||||
if [[ "$DRY_RUN" -eq 1 ]]; then
|
||||
echo "[dry-run] $*"
|
||||
return 0
|
||||
fi
|
||||
"$@"
|
||||
}
|
||||
|
||||
echo "Bridge=$BRIDGE recipient=$RECIPIENT LINK=$LINK_TOKEN"
|
||||
echo "=== Approvals: LINK + each GRU v2 token to bridge (max) ==="
|
||||
send cast send "$LINK_TOKEN" "approve(address,uint256)" "$BRIDGE" "$MAX_U256" \
|
||||
--rpc-url "$RPC" --private-key "$PRIVATE_KEY" --legacy --gas-limit 120000
|
||||
|
||||
for row in "${PAIRS[@]}"; do
|
||||
token="${row%%|*}"
|
||||
echo "approve token $token"
|
||||
send cast send "$token" "approve(address,uint256)" "$BRIDGE" "$MAX_U256" \
|
||||
--rpc-url "$RPC" --private-key "$PRIVATE_KEY" --legacy --gas-limit 120000
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "=== lockAndSend (10 tokens x 10 chains) ==="
|
||||
for row in "${PAIRS[@]}"; do
|
||||
token="${row%%|*}"
|
||||
amt="${row#*|}"
|
||||
for sel in "${SELECTORS[@]}"; do
|
||||
echo "lockAndSend token=$token selector=$sel amount=$amt"
|
||||
send cast send "$BRIDGE" "lockAndSend(address,uint64,address,uint256)" \
|
||||
"$token" "$sel" "$RECIPIENT" "$amt" \
|
||||
--rpc-url "$RPC" --private-key "$PRIVATE_KEY" --legacy --gas-limit 5000000
|
||||
done
|
||||
done
|
||||
|
||||
echo "Done."
|
||||
118
scripts/deployment/cw-l1-configure-destination-8-chains.sh
Executable file
118
scripts/deployment/cw-l1-configure-destination-8-chains.sh
Executable file
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env bash
|
||||
# Admin template: CWMultiTokenBridgeL1.configureDestination for the eight chains
|
||||
# that are not Mainnet / Avalanche (those typically already wired for cUSDT/cUSDC).
|
||||
#
|
||||
# Uses receiver addresses from CW_BRIDGE_OPTIMISM, CW_BRIDGE_CRONOS, CW_BRIDGE_BSC,
|
||||
# CW_BRIDGE_GNOSIS, CW_BRIDGE_POLYGON, CW_BRIDGE_BASE, CW_BRIDGE_ARBITRUM, CW_BRIDGE_CELO
|
||||
# (see smom-dbis-138/.env). CCIP selectors match docs/deployment/BRIDGE_CONFIGURATION.md
|
||||
# and Chainlink CCIP directory — verify before mainnet use.
|
||||
#
|
||||
# Target contract: CW_L1_BRIDGE_CHAIN138 — onlyAdmin.
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/deployment/cw-l1-configure-destination-8-chains.sh [--print-only]
|
||||
# # default writes reports/status/cw-l1-configure-destination-8-chains.cast.sh
|
||||
#
|
||||
# Env:
|
||||
# CW_L1_BRIDGE_CHAIN138, RPC_URL_138 (or CHAIN138_RPC), PRIVATE_KEY (only if you execute the .sh)
|
||||
# CUSDT_ADDRESS_138, CUSDC_ADDRESS_138 (or override CANONICAL_TOKENS="0x...,0x...")
|
||||
#
|
||||
# Does not broadcast unless you run the generated file. Never commit private keys.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SMOM_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
PROXMOX_ROOT="$(cd "$SMOM_ROOT/.." && pwd)"
|
||||
cd "$SMOM_ROOT"
|
||||
|
||||
PRINT_ONLY=0
|
||||
[[ "${1:-}" == "--print-only" ]] && PRINT_ONLY=1
|
||||
|
||||
if [[ -f "$PROXMOX_ROOT/scripts/lib/load-project-env.sh" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
PROJECT_ROOT="$PROXMOX_ROOT" source "$PROXMOX_ROOT/scripts/lib/load-project-env.sh"
|
||||
elif [[ -f .env ]]; then
|
||||
set -a && source .env && set +a
|
||||
fi
|
||||
|
||||
BRIDGE="${CW_L1_BRIDGE_CHAIN138:-}"
|
||||
RPC="${RPC_URL_138:-${CHAIN138_RPC:-${RPC_URL:-http://192.168.11.211:8545}}}"
|
||||
OUT_SH="${OUT_SH:-$SMOM_ROOT/reports/status/cw-l1-configure-destination-8-chains.cast.sh}"
|
||||
|
||||
if [[ -z "$BRIDGE" || "$BRIDGE" == "0x0000000000000000000000000000000000000000" ]]; then
|
||||
echo "Set CW_L1_BRIDGE_CHAIN138 in .env" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Remaining eight (exclude Mainnet + Avalanche)
|
||||
declare -a ROWS=(
|
||||
"Optimism|3734403246176062136|${CW_BRIDGE_OPTIMISM:-}"
|
||||
"Cronos|1456215246176062136|${CW_BRIDGE_CRONOS:-}"
|
||||
"BSC|11344663589394136015|${CW_BRIDGE_BSC:-}"
|
||||
"Gnosis|465200170687744372|${CW_BRIDGE_GNOSIS:-}"
|
||||
"Polygon|4051577828743386545|${CW_BRIDGE_POLYGON:-}"
|
||||
"Base|15971525489660198786|${CW_BRIDGE_BASE:-}"
|
||||
"Arbitrum|4949039107694359620|${CW_BRIDGE_ARBITRUM:-}"
|
||||
"Celo|1346049177634351622|${CW_BRIDGE_CELO:-}"
|
||||
)
|
||||
|
||||
TOKENS_STR="${CANONICAL_TOKENS:-}"
|
||||
if [[ -z "$TOKENS_STR" ]]; then
|
||||
T1="${CUSDT_ADDRESS_138:-${COMPLIANT_USDT_ADDRESS:-}}"
|
||||
T2="${CUSDC_ADDRESS_138:-${COMPLIANT_USDC_ADDRESS:-}}"
|
||||
TOKENS_STR="${T1},${T2}"
|
||||
fi
|
||||
|
||||
IFS=',' read -r -a TOKEN_ADDRS <<< "$(echo "$TOKENS_STR" | tr -d '[:space:]')"
|
||||
[[ -n "${TOKEN_ADDRS[0]:-}" && "${TOKEN_ADDRS[0]}" == 0x* ]] || { echo "Set CUSDT_ADDRESS_138 or CANONICAL_TOKENS" >&2; exit 1; }
|
||||
|
||||
gen() {
|
||||
local sym="$1" token="$2"
|
||||
echo ""
|
||||
echo "# --- $sym $token ---"
|
||||
for row in "${ROWS[@]}"; do
|
||||
IFS='|' read -r name sel recv <<< "$row"
|
||||
if [[ -z "$recv" || "$recv" == "0x0000000000000000000000000000000000000000" ]]; then
|
||||
echo "# SKIP $name: CW_BRIDGE_* receiver unset"
|
||||
continue
|
||||
fi
|
||||
echo "# $sym -> $name (selector $sel) receiver=$recv"
|
||||
echo "cast send \"$BRIDGE\" \\"
|
||||
echo " \"configureDestination(address,uint64,address,bool)\" \\"
|
||||
echo " \"$token\" \\"
|
||||
echo " \"$sel\" \\"
|
||||
echo " \"$recv\" \\"
|
||||
echo " true \\"
|
||||
echo " --rpc-url \"$RPC\" \\"
|
||||
echo " --private-key \"\$PRIVATE_KEY\" \\"
|
||||
echo " --legacy \\"
|
||||
echo " --gas-limit 500000"
|
||||
echo ""
|
||||
echo "# Verify: cast call \"$BRIDGE\" \"destinations(address,uint64)(address,bool)\" \"$token\" \"$sel\" --rpc-url \"$RPC\""
|
||||
echo ""
|
||||
done
|
||||
}
|
||||
|
||||
{
|
||||
echo "#!/usr/bin/env bash"
|
||||
echo "# Generated: cw-l1-configure-destination-8-chains.sh"
|
||||
echo "# Admin-only: configureDestination on CW L1 for eight chains (not Mainnet/Avalanche)."
|
||||
echo "# Requires: admin key = deployer or bridge admin; source .env with PRIVATE_KEY."
|
||||
echo "set -euo pipefail"
|
||||
echo ""
|
||||
|
||||
if [[ -n "${TOKEN_ADDRS[1]:-}" && "${TOKEN_ADDRS[1]}" == 0x* ]]; then
|
||||
gen "cUSDT" "${TOKEN_ADDRS[0]}"
|
||||
gen "cUSDC" "${TOKEN_ADDRS[1]}"
|
||||
else
|
||||
gen "TOKEN" "${TOKEN_ADDRS[0]}"
|
||||
fi
|
||||
} > "$OUT_SH"
|
||||
chmod +x "$OUT_SH"
|
||||
|
||||
echo "Wrote: $OUT_SH"
|
||||
echo ""
|
||||
if [[ "$PRINT_ONLY" -eq 1 ]] || [[ -t 1 ]]; then
|
||||
cat "$OUT_SH"
|
||||
fi
|
||||
105
scripts/deployment/deploy-compliant-fiat-tether-iso-chain138.sh
Executable file
105
scripts/deployment/deploy-compliant-fiat-tether-iso-chain138.sh
Executable file
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env bash
|
||||
# Deploy Tether-style ISO-4217 complements (cAUDT, cJPYT, cCHFT, cCADT) on Chain 138.
|
||||
#
|
||||
# Why not CREATE2 via CREATE2Factory (0x750E4a8adCe9f0e67A420aBE91342DC64Eb90825)?
|
||||
# On Core/Besu, factory.deploy(bytes,uint256) with CompliantFiatToken initcode consistently
|
||||
# OOGs (~2.6M–8M+ gas) even with high limits; eth_call traces succeed — use standard CREATE here.
|
||||
#
|
||||
# Uses scoped Forge (scripts/forge/scope.sh create tokens) so solc does not pull the full repo.
|
||||
# Compiles with --evm-version paris: Core 138 Besu may reject Cancun bytecode (invalid opcode 0x5f/PUSH0).
|
||||
#
|
||||
# Requires: PRIVATE_KEY; RPC via RPC_URL_138, CHAIN138_RPC, or RPC_URL (see below).
|
||||
# Idempotent: skips if contract already has code at the address from env (if set).
|
||||
#
|
||||
# Usage: from repo root:
|
||||
# bash smom-dbis-138/scripts/deployment/deploy-compliant-fiat-tether-iso-chain138.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SMOM_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
PROXMOX_ROOT="$(cd "$SMOM_ROOT/.." && pwd)"
|
||||
cd "$SMOM_ROOT"
|
||||
|
||||
if [[ -f "$PROXMOX_ROOT/scripts/lib/load-project-env.sh" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
PROJECT_ROOT="$PROXMOX_ROOT" source "$PROXMOX_ROOT/scripts/lib/load-project-env.sh"
|
||||
elif [[ -f .env ]]; then
|
||||
set -a && source .env && set +a
|
||||
fi
|
||||
|
||||
[[ -n "${PRIVATE_KEY:-}" ]] || { echo "PRIVATE_KEY required"; exit 1; }
|
||||
command -v jq >/dev/null 2>&1 || { echo "jq required (for receipt status check)"; exit 1; }
|
||||
RPC="${RPC_URL_138:-${CHAIN138_RPC:-${RPC_URL:-http://192.168.11.211:8545}}}"
|
||||
# Paris bytecode deploy ~2M gas; leave headroom for Besu.
|
||||
GAS_LIMIT_DEPLOY="${GAS_LIMIT_DEPLOY:-3500000}"
|
||||
EVM_VERSION_CHAIN138="${EVM_VERSION_CHAIN138:-paris}"
|
||||
OWNER="$(cast wallet address "$PRIVATE_KEY")"
|
||||
|
||||
# Ensure scoped artifacts match chain EVM (Paris for Chain 138 Core).
|
||||
bash scripts/forge/scope.sh build tokens --evm-version "$EVM_VERSION_CHAIN138" --force
|
||||
|
||||
declare -a SPECS=(
|
||||
"cAUDT|Tether AUD (Compliant)|AUD|${CAUDT_ADDRESS_138:-}"
|
||||
"cJPYT|Tether JPY (Compliant)|JPY|${CJPYT_ADDRESS_138:-}"
|
||||
"cCHFT|Tether CHF (Compliant)|CHF|${CCHFT_ADDRESS_138:-}"
|
||||
"cCADT|Tether CAD (Compliant)|CAD|${CCADT_ADDRESS_138:-}"
|
||||
)
|
||||
|
||||
DEPLOYED_SNIPPET="# After deploy (paste into smom-dbis-138/.env next to other C*ADDRESS_138):"
|
||||
while IFS='|' read -r sym name cc existing; do
|
||||
if [[ -n "$existing" && "$existing" != "0x0000000000000000000000000000000000000000" ]]; then
|
||||
code=$(cast code "$existing" --rpc-url "$RPC" 2>/dev/null || echo "0x")
|
||||
if [[ -n "$code" && "$code" != "0x" ]]; then
|
||||
echo "Skip $sym (already deployed at $existing)"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
echo "Deploy $sym..."
|
||||
out=$(bash scripts/forge/scope.sh create tokens contracts/tokens/CompliantFiatToken.sol:CompliantFiatToken \
|
||||
--rpc-url "$RPC" --private-key "$PRIVATE_KEY" --legacy \
|
||||
--evm-version "$EVM_VERSION_CHAIN138" \
|
||||
--gas-limit "$GAS_LIMIT_DEPLOY" \
|
||||
--broadcast \
|
||||
--constructor-args "$name" "$sym" 6 "$cc" "$OWNER" "$OWNER" 1000000000000 2>&1) || {
|
||||
echo "$out"
|
||||
exit 1
|
||||
}
|
||||
echo "$out"
|
||||
txh=$(printf '%s\n' "$out" | sed -n 's/.*Transaction hash: \(0x[0-9a-fA-F]\{64\}\).*/\1/p' | head -1)
|
||||
if [[ -n "$txh" ]]; then
|
||||
st=$(cast receipt "$txh" --json --rpc-url "$RPC" 2>/dev/null | jq -r '.status // "0x0"')
|
||||
if [[ "$st" != "0x1" ]]; then
|
||||
echo "error: tx $txh failed on-chain (status=$st). Raise GAS_LIMIT_DEPLOY or inspect: cast run $txh --rpc-url $RPC" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
addr=$(printf '%s\n' "$out" | sed -n 's/.*Deployed to: \(0x[0-9a-fA-F]\{40\}\).*/\1/p' | head -1)
|
||||
if [[ -z "$addr" ]]; then
|
||||
addr=$(printf '%s\n' "$out" | sed -n 's/.*"contract":"\(0x[0-9a-fA-F]\{40\}\)".*/\1/p' | head -1)
|
||||
fi
|
||||
if [[ -n "$addr" ]]; then
|
||||
ccode=$(cast code "$addr" --rpc-url "$RPC" 2>/dev/null || echo "0x")
|
||||
if [[ -z "$ccode" || "$ccode" == "0x" ]]; then
|
||||
echo "error: no code at $addr after successful receipt — aborting" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
if [[ -n "$addr" ]]; then
|
||||
var=""
|
||||
case "$sym" in
|
||||
cAUDT) var="CAUDT_ADDRESS_138" ;;
|
||||
cJPYT) var="CJPYT_ADDRESS_138" ;;
|
||||
cCHFT) var="CCHFT_ADDRESS_138" ;;
|
||||
cCADT) var="CCADT_ADDRESS_138" ;;
|
||||
esac
|
||||
DEPLOYED_SNIPPET+=$'\n'"${var}=${addr}"
|
||||
fi
|
||||
done < <(printf '%s\n' "${SPECS[@]}")
|
||||
|
||||
echo ""
|
||||
echo "$DEPLOYED_SNIPPET"
|
||||
echo ""
|
||||
echo "Verify: cast code <addr> --rpc-url \"\$RPC_URL_138\""
|
||||
echo "Optional mint (random 26M–85M per token, deployer must hold minter role):"
|
||||
echo " bash smom-dbis-138/scripts/mint-compliant-fiat-tether-iso-138.sh"
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Deploy DODOPMMIntegration on each L2 (BSC, Polygon, Base, Optimism, Arbitrum, Avalanche, Cronos, Gnosis).
|
||||
# Deploy DODOPMMIntegration on each L2/public chain (BSC, Polygon, Base, Optimism, Arbitrum, Avalanche, Cronos, Gnosis, Celo).
|
||||
# Uses .env for RPC and token addresses. Chains via tag: --chain bsc polygon ... (or DEPLOY_PMM_L2S_FILTER in .env).
|
||||
# Usage: ./scripts/deployment/deploy-pmm-all-l2s.sh [--chain bsc polygon base]
|
||||
set -euo pipefail
|
||||
@@ -37,6 +37,7 @@ CHAINS=(
|
||||
"AVALANCHE:43114:AVALANCHE_RPC_URL"
|
||||
"CRONOS:25:CRONOS_RPC_URL"
|
||||
"GNOSIS:100:GNOSIS_MAINNET_RPC"
|
||||
"CELO:42220:CELO_MAINNET_RPC"
|
||||
)
|
||||
|
||||
for entry in "${CHAINS[@]}"; do
|
||||
@@ -59,14 +60,16 @@ for entry in "${CHAINS[@]}"; do
|
||||
cusdc_var="${name}_COMPLIANT_USDC_ADDRESS"
|
||||
cusdt_var_alt="COMPLIANT_USDT_${name}"
|
||||
cusdc_var_alt="COMPLIANT_USDC_${name}"
|
||||
cwusdt_var="CWUSDT_${name}"
|
||||
cwusdc_var="CWUSDC_${name}"
|
||||
# Per-chain cUSDT/cUSDC (optional): CUSDT_ADDRESS_<chainId> / CUSDC_ADDRESS_<chainId> or POLYGON_COMPLIANT_USDT_ADDRESS etc.
|
||||
cusdt_chain="CUSDT_ADDRESS_${chain_id}"
|
||||
cusdc_chain="CUSDC_ADDRESS_${chain_id}"
|
||||
dvm="$(first_set_env "$dvm_var" "DODO_VENDING_MACHINE_ADDRESS" || true)"
|
||||
usdt="$(first_set_env "$usdt_var" "$usdt_var_alt" "OFFICIAL_USDT_ADDRESS" || true)"
|
||||
usdc="$(first_set_env "$usdc_var" "$usdc_var_alt" "OFFICIAL_USDC_ADDRESS" || true)"
|
||||
compliant_usdt="$(first_set_env "$cusdt_var" "$cusdt_var_alt" "$cusdt_chain" || true)"
|
||||
compliant_usdc="$(first_set_env "$cusdc_var" "$cusdc_var_alt" "$cusdc_chain" || true)"
|
||||
compliant_usdt="$(first_set_env "$cusdt_var" "$cusdt_var_alt" "$cusdt_chain" "$cwusdt_var" || true)"
|
||||
compliant_usdc="$(first_set_env "$cusdc_var" "$cusdc_var_alt" "$cusdc_chain" "$cwusdc_var" || true)"
|
||||
compliant_usdt="${compliant_usdt:-$usdt}"
|
||||
compliant_usdc="${compliant_usdc:-$usdc}"
|
||||
|
||||
|
||||
11
scripts/deployment/seed-chain138-uni-v2-weth-quote-pairs.sh
Executable file
11
scripts/deployment/seed-chain138-uni-v2-weth-quote-pairs.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
# Seed Uniswap V2 pools on Chain 138: each canonical asset vs WETH (see script header).
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
cd "$ROOT"
|
||||
if [[ -f scripts/load-env.sh ]]; then
|
||||
# shellcheck source=/dev/null
|
||||
source scripts/load-env.sh
|
||||
fi
|
||||
export CHAIN138_RPC_URL="${CHAIN138_RPC_URL:-${RPC_URL_138:-http://192.168.11.211:8545}}"
|
||||
exec npx hardhat run scripts/chain138/seed-uni-v2-weth-quote-pairs.js --network chain138
|
||||
119
scripts/deployment/verify-wemix-bridges.sh
Executable file
119
scripts/deployment/verify-wemix-bridges.sh
Executable file
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
if [[ -f "$SCRIPT_DIR/../lib/deployment/dotenv.sh" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
source "$SCRIPT_DIR/../lib/deployment/dotenv.sh"
|
||||
load_deployment_env --repo-root "${PROJECT_ROOT}"
|
||||
elif [[ -f "$PROJECT_ROOT/.env" ]]; then
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
source "$PROJECT_ROOT/.env"
|
||||
set +a
|
||||
fi
|
||||
|
||||
require_env() {
|
||||
local name="$1"
|
||||
if [[ -z "${!name:-}" ]]; then
|
||||
echo "ERROR: $name is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
require_env WEMIX_RPC
|
||||
require_env CCIPWETH9_BRIDGE_WEMIX
|
||||
require_env CCIPWETH10_BRIDGE_WEMIX
|
||||
require_env CCIP_ROUTER_WEMIX
|
||||
require_env WETH9_WEMIX
|
||||
require_env WETH10_WEMIX
|
||||
require_env LINK_TOKEN_WEMIX
|
||||
require_env WEMIXSCAN_API_KEY
|
||||
|
||||
flatten_b64() {
|
||||
local source_file="$1"
|
||||
forge flatten "$source_file" | python3 -c 'import sys,base64; print(base64.b64encode(sys.stdin.buffer.read()).decode())'
|
||||
}
|
||||
|
||||
constructor_args() {
|
||||
local weth="$1"
|
||||
cast abi-encode 'constructor(address,address,address)' \
|
||||
"$CCIP_ROUTER_WEMIX" \
|
||||
"$weth" \
|
||||
"$LINK_TOKEN_WEMIX" | sed 's/^0x//'
|
||||
}
|
||||
|
||||
submit_verify() {
|
||||
local address="$1"
|
||||
local contract_name="$2"
|
||||
local source_file="$3"
|
||||
local weth="$4"
|
||||
|
||||
local payload
|
||||
payload="$(mktemp)"
|
||||
|
||||
python3 - "$payload" "$contract_name" "$(flatten_b64 "$source_file")" "$(constructor_args "$weth")" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
path, contract_name, contract_source, constructor_arguments = sys.argv[1:]
|
||||
body = {
|
||||
"contract_name": contract_name,
|
||||
"compiler": "v0.8.20+commit.a1b79de6",
|
||||
"runs_optimizer": 200,
|
||||
"optimization_enabled": 1,
|
||||
"contract_source": contract_source,
|
||||
"constructor_arguments": constructor_arguments,
|
||||
"libraries": [],
|
||||
}
|
||||
Path(path).write_text(json.dumps(body))
|
||||
PY
|
||||
|
||||
echo "Submitting verification for $contract_name at $address..."
|
||||
local response_file
|
||||
response_file="$(mktemp)"
|
||||
local http_code
|
||||
http_code="$(
|
||||
curl --max-time 90 \
|
||||
-sS \
|
||||
-o "$response_file" \
|
||||
-w '%{http_code}' \
|
||||
-H "api-key: ${WEMIXSCAN_API_KEY}" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-X POST \
|
||||
--data @"$payload" \
|
||||
"https://explorerapi.wemix.com/v1/contracts/${address}/verify"
|
||||
)"
|
||||
|
||||
echo "HTTP ${http_code}"
|
||||
cat "$response_file"
|
||||
echo
|
||||
|
||||
if [[ "$http_code" == "200" ]]; then
|
||||
echo "Verification request accepted for $contract_name"
|
||||
elif [[ "$http_code" == "504" ]]; then
|
||||
echo "WARNING: Wemix explorer timed out while compiling $contract_name. Check the explorer code tab manually." >&2
|
||||
else
|
||||
echo "WARNING: Verification not accepted for $contract_name" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
check_status() {
|
||||
local address="$1"
|
||||
echo "Checking explorer code endpoint for $address..."
|
||||
curl --max-time 30 \
|
||||
-sS \
|
||||
-H "api-key: ${WEMIXSCAN_API_KEY}" \
|
||||
"https://explorerapi.wemix.com/v1/contracts/${address}/code" || true
|
||||
echo
|
||||
}
|
||||
|
||||
submit_verify "$CCIPWETH9_BRIDGE_WEMIX" "CCIPWETH9Bridge" "contracts/ccip/CCIPWETH9Bridge.sol" "$WETH9_WEMIX"
|
||||
submit_verify "$CCIPWETH10_BRIDGE_WEMIX" "CCIPWETH10Bridge" "contracts/ccip/CCIPWETH10Bridge.sol" "$WETH10_WEMIX"
|
||||
|
||||
check_status "$CCIPWETH9_BRIDGE_WEMIX"
|
||||
check_status "$CCIPWETH10_BRIDGE_WEMIX"
|
||||
@@ -44,12 +44,14 @@ Usage:
|
||||
bash scripts/forge/scope.sh build [scope] [forge build args...]
|
||||
bash scripts/forge/scope.sh test [scope] [forge test args...]
|
||||
bash scripts/forge/scope.sh script [scope] <script-target> [forge script args...]
|
||||
bash scripts/forge/scope.sh create [scope] <Contract.sol:Name> [forge create args...]
|
||||
bash scripts/forge/scope.sh orphans [--json]
|
||||
|
||||
Examples:
|
||||
bash scripts/forge/scope.sh build treasury
|
||||
bash scripts/forge/scope.sh test flash --match-path 'test/flash/*.t.sol'
|
||||
bash scripts/forge/scope.sh script bridge/trustless script/bridge/trustless/DeployTrustlessBridge.s.sol:DeployTrustlessBridge --rpc-url "$RPC_URL_138"
|
||||
bash scripts/forge/scope.sh create tokens contracts/tokens/CompliantFiatToken.sol:CompliantFiatToken --rpc-url "$RPC_URL_138" --private-key "$PRIVATE_KEY" --legacy
|
||||
FORGE_SCOPE=vault bash scripts/forge/scope.sh test --match-path 'test/vault/*.t.sol'
|
||||
|
||||
Notes:
|
||||
@@ -266,7 +268,7 @@ main() {
|
||||
orphans)
|
||||
exec python3 scripts/forge/report-contract-reachability.py "$@"
|
||||
;;
|
||||
build|test|script)
|
||||
build|test|script|create)
|
||||
local scope=""
|
||||
if [[ $# -gt 0 && "$1" != --* ]]; then
|
||||
local maybe_scope
|
||||
@@ -313,6 +315,10 @@ main() {
|
||||
[[ $# -gt 0 ]] || die "script command requires a script target, e.g. script/treasury/DeployTreasuryExecutor138.s.sol:DeployTreasuryExecutor138"
|
||||
exec forge script "$@"
|
||||
;;
|
||||
create)
|
||||
[[ $# -gt 0 ]] || die "create command requires a contract target, e.g. contracts/tokens/CompliantFiatToken.sol:CompliantFiatToken"
|
||||
exec forge create "$@"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
|
||||
70
scripts/mint-compliant-fiat-tether-iso-138.sh
Executable file
70
scripts/mint-compliant-fiat-tether-iso-138.sh
Executable file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env bash
|
||||
# Mint CompliantFiatToken Tether-style ISO complements on Chain 138 (cAUDT, cJPYT, cCHFT, cCADT).
|
||||
# Uses addresses from smom-dbis-138/.env: CAUDT_ADDRESS_138, CJPYT_ADDRESS_138, CCHFT_ADDRESS_138, CCADT_ADDRESS_138.
|
||||
#
|
||||
# Default: random whole-number amount per token between 26_000_000 and 85_000_000 (6 decimals).
|
||||
# Override: ./scripts/mint-compliant-fiat-tether-iso-138.sh <amount_human> (same amount for all four)
|
||||
#
|
||||
# Requires: PRIVATE_KEY (minter/deployer), RPC. Skips any address unset or zero.
|
||||
# Requires: cast (foundry).
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SMOM_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
PROXMOX_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
cd "$SMOM_ROOT"
|
||||
|
||||
if [[ -f .env ]]; then
|
||||
set -a && source .env && set +a
|
||||
fi
|
||||
if [[ -z "${PRIVATE_KEY:-}" && -f "${PROXMOX_ROOT}/scripts/lib/load-project-env.sh" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
PROJECT_ROOT="$PROXMOX_ROOT" source "${PROXMOX_ROOT}/scripts/lib/load-project-env.sh"
|
||||
cd "$SMOM_ROOT"
|
||||
fi
|
||||
|
||||
RPC="${RPC_URL_138:-${CHAIN138_RPC:-${RPC_URL:-http://192.168.11.211:8545}}}"
|
||||
GAS_LIMIT_MINT="${GAS_LIMIT_MINT:-400000}"
|
||||
|
||||
[ -n "${PRIVATE_KEY:-}" ] || { echo "PRIVATE_KEY not set"; exit 1; }
|
||||
DEPLOYER=$(cast wallet address "$PRIVATE_KEY" 2>/dev/null) || exit 1
|
||||
|
||||
if [[ -n "${1:-}" ]]; then
|
||||
AMOUNT_HUMAN="$1"
|
||||
elif command -v shuf >/dev/null 2>&1; then
|
||||
AMOUNT_HUMAN=$(shuf -i 26000000-85000000 -n 1)
|
||||
elif command -v python3 >/dev/null 2>&1; then
|
||||
AMOUNT_HUMAN=$(python3 -c 'import random; print(random.randint(26_000_000, 85_000_000))')
|
||||
else
|
||||
echo "Install coreutils (shuf) or python3 for random mint amounts, or pass amount_human as \$1" >&2
|
||||
exit 1
|
||||
fi
|
||||
BASE_UNITS=$((AMOUNT_HUMAN * 1000000))
|
||||
|
||||
echo "=== Mint Tether ISO complements (Chain 138) ==="
|
||||
echo " Deployer: $DEPLOYER"
|
||||
echo " Amount: $AMOUNT_HUMAN tokens each ($BASE_UNITS base units)"
|
||||
echo " RPC: $RPC"
|
||||
echo ""
|
||||
|
||||
for pair in \
|
||||
"cAUDT:${CAUDT_ADDRESS_138:-}" \
|
||||
"cJPYT:${CJPYT_ADDRESS_138:-}" \
|
||||
"cCHFT:${CCHFT_ADDRESS_138:-}" \
|
||||
"cCADT:${CCADT_ADDRESS_138:-}"; do
|
||||
sym="${pair%%:*}"
|
||||
addr="${pair#*:}"
|
||||
if [[ -z "$addr" || "$addr" == "0x0000000000000000000000000000000000000000" ]]; then
|
||||
echo "Skip $sym (address not set in .env)"
|
||||
continue
|
||||
fi
|
||||
echo -n "Mint $sym ($addr)... "
|
||||
if cast send "$addr" "mint(address,uint256)" "$DEPLOYER" "$BASE_UNITS" \
|
||||
--rpc-url "$RPC" --private-key "$PRIVATE_KEY" --legacy --gas-limit "$GAS_LIMIT_MINT" 2>/dev/null; then
|
||||
echo OK
|
||||
else
|
||||
echo FAIL
|
||||
fi
|
||||
done
|
||||
echo "Done."
|
||||
@@ -4,27 +4,59 @@
|
||||
const LIFI_URL = 'https://li.quest/v1/chains';
|
||||
const CCIP_URL = 'https://docs.chain.link/ccip/supported-networks';
|
||||
|
||||
async function fetchText(url) {
|
||||
const res = await fetch(url, {
|
||||
headers: {
|
||||
'user-agent': 'proxmox-operator-support-check/1.0',
|
||||
accept: 'text/html,application/json;q=0.9,*/*;q=0.8',
|
||||
},
|
||||
});
|
||||
if (!res.ok) throw new Error(`${url} returned ${res.status}`);
|
||||
return res.text();
|
||||
}
|
||||
|
||||
async function fetchJson(url) {
|
||||
const res = await fetch(url, {
|
||||
headers: {
|
||||
'user-agent': 'proxmox-operator-support-check/1.0',
|
||||
accept: 'application/json',
|
||||
},
|
||||
});
|
||||
if (!res.ok) throw new Error(`${url} returned ${res.status}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('Tezos/Etherlink support check\n');
|
||||
try {
|
||||
const lifiRes = await fetch(LIFI_URL);
|
||||
const lifi = await lifiRes.json();
|
||||
const ids = (lifi.chains || []).map((c) => c.id || c.chainId).filter(Boolean);
|
||||
const lifi = await fetchJson(LIFI_URL);
|
||||
const chains = lifi.chains || [];
|
||||
const ids = chains.map((c) => c.id || c.chainId).filter(Boolean);
|
||||
const etherlink = chains.find((c) => (c.id || c.chainId) === 42793);
|
||||
console.log('LiFi chains include 138:', ids.includes(138));
|
||||
console.log('LiFi chains include 42793:', ids.includes(42793));
|
||||
console.log('LiFi chains include 651940:', ids.includes(651940));
|
||||
if (etherlink) {
|
||||
console.log('LiFi Etherlink entry:', JSON.stringify({
|
||||
id: etherlink.id || etherlink.chainId,
|
||||
key: etherlink.key,
|
||||
name: etherlink.name,
|
||||
coin: etherlink.coin,
|
||||
mainnet: etherlink.mainnet,
|
||||
}));
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('LiFi error:', e.message);
|
||||
}
|
||||
try {
|
||||
const ccipRes = await fetch(CCIP_URL);
|
||||
const html = await ccipRes.text();
|
||||
console.log('\nCCIP page mentions 42793:', html.includes('42793') || html.toLowerCase().includes('etherlink'));
|
||||
console.log('CCIP page mentions 138:', html.includes('138'));
|
||||
const html = await fetchText(CCIP_URL);
|
||||
const hasEtherlinkMention = html.includes('42793') || html.toLowerCase().includes('etherlink');
|
||||
console.log('\nCCIP page mentions 42793/Etherlink:', hasEtherlinkMention);
|
||||
console.log('CCIP page mention is not treated as support without selector/router confirmation.');
|
||||
} catch (e) {
|
||||
console.log('CCIP error:', e.message);
|
||||
}
|
||||
console.log('\nJumper: verify manually; see docs/07-ccip/TEZOS_JUMPER_SUPPORT_MATRIX.md');
|
||||
console.log('\nJumper: still verify manually; see docs/07-ccip/TEZOS_JUMPER_SUPPORT_MATRIX.md');
|
||||
}
|
||||
|
||||
main().catch((e) => { console.error(e); process.exit(1); });
|
||||
|
||||
Reference in New Issue
Block a user