Add Sepolia funding threshold helper for smoke runs
Some checks are pending
check / contracts (push) Waiting to run

This commit is contained in:
Joshua 2026-02-19 14:03:21 -08:00
parent 1dd74d57e6
commit af4f7796e7
4 changed files with 113 additions and 2 deletions

View File

@ -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/<key>"
npm run smoke:funding:sepolia
```
## Boundary
Contracts are settlement primitives. Runtime execution remains off-chain and fail-closed by entitlement state.

View File

@ -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",

View File

@ -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();

View File

@ -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);
});