diff --git a/README.md b/README.md index d668ead..579a1f1 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ Use a Hardhat-supported Node runtime (`20.x` recommended). 5. `npm run deploy:mainnet` 6. `npm run deploy:entitlement:sepolia` 7. `npm run deploy:entitlement:mainnet` +8. `npm run update:membership:price:sepolia` +9. `npm run update:membership:price:mainnet` `make check` wraps build + tests. @@ -62,6 +64,12 @@ Copy `.env.example` values into your shell/session before deploy: 12. `OFFERS_JSON` (optional path to per-offer seed config JSON) 13. `OFFERS_INLINE_JSON` (optional inline JSON array alternative to `OFFERS_JSON`) +`update:membership:price:*` requires: + +1. `MEMBERSHIP_CONTRACT_ADDRESS` +2. `MINT_CURRENCY_ADDRESS` +3. `MINT_AMOUNT_ATOMIC` + If no offer override JSON is provided, deploy script seeds default offers at `OFFER_PRICE_ATOMIC`. Use `deploy/offers.template.json` to define per-offer prices and policy flags. diff --git a/package.json b/package.json index 0b9916c..3a3a529 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,8 @@ "check": "npm run build && npm run test", "deploy:sepolia": "hardhat run scripts/deploy-membership.cjs --network baseSepolia", "deploy:mainnet": "hardhat run scripts/deploy-membership.cjs --network base", + "update:membership:price:sepolia": "hardhat run scripts/update-membership-price.cjs --network baseSepolia", + "update:membership:price:mainnet": "hardhat run scripts/update-membership-price.cjs --network base", "deploy:entitlement:sepolia": "hardhat run scripts/deploy-entitlement.cjs --network baseSepolia", "deploy:entitlement:mainnet": "hardhat run scripts/deploy-entitlement.cjs --network base", "smoke:e2e:sepolia": "node scripts/e2e-membership-flow.cjs" diff --git a/scripts/update-membership-price.cjs b/scripts/update-membership-price.cjs new file mode 100644 index 0000000..de17c5e --- /dev/null +++ b/scripts/update-membership-price.cjs @@ -0,0 +1,82 @@ +const hre = require("hardhat"); + +function requiredEnv(name) { + const value = process.env[name]; + if (!value || !value.trim()) { + throw new Error(`Missing required env: ${name}`); + } + return value.trim(); +} + +function requireAddress(name, value, { allowZero = false } = {}) { + if (!hre.ethers.utils.isAddress(value)) { + throw new Error(`Invalid address for ${name}: ${value}`); + } + if (!allowZero && value === hre.ethers.constants.AddressZero) { + throw new Error(`${name} cannot be zero address`); + } + return value; +} + +function parseUint(name, value) { + try { + const parsed = hre.ethers.BigNumber.from(value); + if (parsed.lt(0)) { + throw new Error("must be >= 0"); + } + return parsed; + } catch (err) { + throw new Error(`Invalid uint for ${name}: ${value} (${err.message})`); + } +} + +async function main() { + const membershipContract = requireAddress( + "MEMBERSHIP_CONTRACT_ADDRESS", + requiredEnv("MEMBERSHIP_CONTRACT_ADDRESS"), + ); + const targetCurrency = requireAddress( + "MINT_CURRENCY_ADDRESS", + (process.env.MINT_CURRENCY_ADDRESS || hre.ethers.constants.AddressZero).trim(), + { allowZero: true }, + ); + const targetAmount = parseUint("MINT_AMOUNT_ATOMIC", requiredEnv("MINT_AMOUNT_ATOMIC")); + + const [deployer] = await hre.ethers.getSigners(); + console.log("deployer:", deployer.address); + console.log("network:", hre.network.name); + console.log("membership_contract:", membershipContract); + console.log("target_mint_currency:", targetCurrency); + console.log("target_mint_amount_atomic:", targetAmount.toString()); + + const abi = [ + "function currentMintPrice() view returns (address currency, uint256 amountAtomic)", + "function updateMintPrice(address currency, uint256 amountAtomic)", + ]; + const contract = new hre.ethers.Contract(membershipContract, abi, deployer); + + const [currentCurrency, currentAmount] = await contract.currentMintPrice(); + console.log("current_mint_currency:", currentCurrency); + console.log("current_mint_amount_atomic:", currentAmount.toString()); + + const sameCurrency = currentCurrency.toLowerCase() === targetCurrency.toLowerCase(); + const sameAmount = currentAmount.eq(targetAmount); + if (sameCurrency && sameAmount) { + console.log("status: no_change_required"); + return; + } + + const tx = await contract.updateMintPrice(targetCurrency, targetAmount); + console.log("submitted_tx:", tx.hash); + await tx.wait(1); + console.log("mined_tx:", tx.hash); + + const [nextCurrency, nextAmount] = await contract.currentMintPrice(); + console.log("updated_mint_currency:", nextCurrency); + console.log("updated_mint_amount_atomic:", nextAmount.toString()); +} + +main().catch((err) => { + console.error(err); + process.exitCode = 1; +});