From af4f7796e7bf052277ccd80771b08d24d7d45200 Mon Sep 17 00:00:00 2001 From: Joshua Date: Thu, 19 Feb 2026 14:03:21 -0800 Subject: [PATCH] Add Sepolia funding threshold helper for smoke runs --- README.md | 12 +++ package.json | 3 +- scripts/e2e-control-plane-flow.cjs | 2 +- scripts/report-smoke-funding-threshold.cjs | 98 ++++++++++++++++++++++ 4 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 scripts/report-smoke-funding-threshold.cjs diff --git a/README.md b/README.md index 3027df0..07a2c5e 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ Use a Hardhat-supported Node runtime (`20.x` recommended). 8. `npm run deploy:entitlement:mainnet` 9. `npm run update:membership:price:sepolia` 10. `npm run update:membership:price:mainnet` +11. `npm run smoke:funding:sepolia` `make check` wraps build + tests. @@ -91,6 +92,9 @@ Smoke flow optional vars: 9. `E2E_DEVICE_ID` 10. `E2E_PLATFORM` 11. `E2E_LAUNCHER_VERSION` +12. `E2E_GAS_LIMIT` (optional fixed gas limit, default `300000`) +13. `E2E_GAS_PRICE_WEI` (optional fixed gas price) +14. `SMOKE_MIN_GAS_PRICE_WEI` (optional threshold floor for funding estimator, default `1000000000`) Example (Sepolia): @@ -117,6 +121,14 @@ export ENTITLEMENT_CONTRACT_ADDRESS="0x..." npm run verify:offers:sepolia ``` +Sepolia smoke funding threshold from live fee data: + +```bash +cd /Users/vsg/Documents/VSG\ Codex/contracts +export BASE_SEPOLIA_RPC_URL="https://base-sepolia.g.alchemy.com/v2/" +npm run smoke:funding:sepolia +``` + ## Boundary Contracts are settlement primitives. Runtime execution remains off-chain and fail-closed by entitlement state. diff --git a/package.json b/package.json index cf29bc0..c85fbf1 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "deploy:entitlement:mainnet": "hardhat run scripts/deploy-entitlement.cjs --network base", "smoke:e2e:sepolia": "node scripts/e2e-membership-flow.cjs", "smoke:e2e:controlplane:sepolia": "node scripts/e2e-control-plane-flow.cjs", - "verify:offers:sepolia": "node scripts/verify-offer-config-readback.cjs" + "verify:offers:sepolia": "node scripts/verify-offer-config-readback.cjs", + "smoke:funding:sepolia": "node scripts/report-smoke-funding-threshold.cjs" }, "devDependencies": { "@nomiclabs/hardhat-ethers": "^2.2.3", diff --git a/scripts/e2e-control-plane-flow.cjs b/scripts/e2e-control-plane-flow.cjs index eff251d..d64c479 100644 --- a/scripts/e2e-control-plane-flow.cjs +++ b/scripts/e2e-control-plane-flow.cjs @@ -7,7 +7,7 @@ const CHAIN_ID = Number((process.env.SECRET_API_CHAIN_ID || "84532").trim()); const PRIVATE_KEY = (process.env.DEPLOYER_PRIVATE_KEY || "").trim(); const RPC_URL = (process.env.BASE_SEPOLIA_RPC_URL || "").trim(); const GAS_PRICE_WEI = (process.env.E2E_GAS_PRICE_WEI || "").trim(); -const GAS_LIMIT = (process.env.E2E_GAS_LIMIT || "500000").trim(); +const GAS_LIMIT = (process.env.E2E_GAS_LIMIT || "300000").trim(); const OFFER_ID = (process.env.E2E_OFFER_ID || "edut.workspace.core").trim(); const ORG_ROOT_ID = (process.env.E2E_ORG_ROOT_ID || "org.e2e.workspace").trim(); diff --git a/scripts/report-smoke-funding-threshold.cjs b/scripts/report-smoke-funding-threshold.cjs new file mode 100644 index 0000000..7391e98 --- /dev/null +++ b/scripts/report-smoke-funding-threshold.cjs @@ -0,0 +1,98 @@ +const { ethers } = require("ethers"); + +const RPC_URL = (process.env.BASE_SEPOLIA_RPC_URL || "").trim(); +const MEMBERSHIP_GAS = Number.parseInt((process.env.SMOKE_MEMBERSHIP_GAS || "160000").trim(), 10); +const CHECKOUT_GAS = Number.parseInt((process.env.SMOKE_CHECKOUT_GAS || "220000").trim(), 10); +const SAFETY_BPS = Number.parseInt((process.env.SMOKE_SAFETY_BPS || "15000").trim(), 10); // 1.5x +const MIN_GAS_PRICE_WEI = (process.env.SMOKE_MIN_GAS_PRICE_WEI || "1000000000").trim(); // 1 gwei floor + +function required(name, value) { + if (!value) { + throw new Error(`Missing required env: ${name}`); + } + return value; +} + +function validatePositiveInt(name, value) { + if (!Number.isFinite(value) || value <= 0) { + throw new Error(`${name} must be a positive integer`); + } + return value; +} + +function maxBigInt(values) { + let out = values[0]; + for (let i = 1; i < values.length; i += 1) { + if (values[i].gt(out)) { + out = values[i]; + } + } + return out; +} + +function applySafety(valueWei) { + return valueWei.mul(SAFETY_BPS).add(9999).div(10000); +} + +async function main() { + required("BASE_SEPOLIA_RPC_URL", RPC_URL); + validatePositiveInt("SMOKE_MEMBERSHIP_GAS", MEMBERSHIP_GAS); + validatePositiveInt("SMOKE_CHECKOUT_GAS", CHECKOUT_GAS); + validatePositiveInt("SMOKE_SAFETY_BPS", SAFETY_BPS); + const minGasPrice = ethers.BigNumber.from(required("SMOKE_MIN_GAS_PRICE_WEI", MIN_GAS_PRICE_WEI)); + if (minGasPrice.lte(0)) { + throw new Error("SMOKE_MIN_GAS_PRICE_WEI must be > 0"); + } + + const provider = new ethers.providers.JsonRpcProvider(RPC_URL); + const feeData = await provider.getFeeData(); + const feeCandidates = [feeData.gasPrice, feeData.maxFeePerGas, minGasPrice].filter(Boolean); + if (feeCandidates.length === 0) { + throw new Error("Unable to resolve gas price from provider"); + } + const gasPrice = maxBigInt(feeCandidates); + + const membershipWei = gasPrice.mul(MEMBERSHIP_GAS); + const checkoutWei = gasPrice.mul(CHECKOUT_GAS); + const freshWalletBaseWei = membershipWei.add(checkoutWei); + const activeWalletBaseWei = checkoutWei; + const freshWalletRecommendedWei = applySafety(freshWalletBaseWei); + const activeWalletRecommendedWei = applySafety(activeWalletBaseWei); + + const report = { + chain: "base-sepolia", + gas_price_sources: { + provider_gas_price_wei: feeData.gasPrice ? feeData.gasPrice.toString() : null, + provider_max_fee_per_gas_wei: feeData.maxFeePerGas ? feeData.maxFeePerGas.toString() : null, + floor_gas_price_wei: minGasPrice.toString(), + }, + gas_price_wei: gasPrice.toString(), + gas_price_gwei: ethers.utils.formatUnits(gasPrice, "gwei"), + gas_budgets: { + membership: MEMBERSHIP_GAS, + checkout: CHECKOUT_GAS, + }, + safety_multiplier: `${(SAFETY_BPS / 10000).toFixed(2)}x`, + thresholds: { + fresh_wallet_membership_plus_checkout: { + base_wei: freshWalletBaseWei.toString(), + base_eth: ethers.utils.formatEther(freshWalletBaseWei), + recommended_wei: freshWalletRecommendedWei.toString(), + recommended_eth: ethers.utils.formatEther(freshWalletRecommendedWei), + }, + active_wallet_checkout_only: { + base_wei: activeWalletBaseWei.toString(), + base_eth: ethers.utils.formatEther(activeWalletBaseWei), + recommended_wei: activeWalletRecommendedWei.toString(), + recommended_eth: ethers.utils.formatEther(activeWalletRecommendedWei), + }, + }, + }; + + console.log(JSON.stringify(report, null, 2)); +} + +main().catch((err) => { + console.error(err.message || err); + process.exit(1); +});