@gatedpath/preflight 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -2
- package/dist/checks/compiler.js +9 -0
- package/dist/checks/deployer.js +2 -2
- package/dist/config.d.ts +2 -0
- package/dist/config.js +3 -1
- package/dist/gas.d.ts +17 -0
- package/dist/gas.js +18 -0
- package/dist/ship-cli.d.ts +1 -1
- package/dist/ship-cli.js +15 -4
- package/dist/ship.d.ts +4 -2
- package/dist/ship.js +7 -4
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -93,7 +93,8 @@ and `reports/gas-151-2026-09-14.md`. On both, 21,000 gas came to 1.000 US cents
|
|
|
93
93
|
## redbelly ship
|
|
94
94
|
|
|
95
95
|
The mainnet gate that produces the document you wanted anyway (PLAN.md 18.3). `redbelly ship --chain
|
|
96
|
-
<151|153> --account <keystore-name> [--admin <safe>] [--gas <n>]` (
|
|
96
|
+
<151|153> --account <keystore-name> [--admin <safe>] [--gas <n>]` (or `--address <0x…>` in place of
|
|
97
|
+
`--account`, or `DEPLOYER` in `.env`; also `npm run ship` in a scaffold
|
|
97
98
|
and the MCP server's `ship_report` tool) runs the gas report first (its deployment row is the gas the
|
|
98
99
|
balance check prices), then pre-flight's seven checks with the keystore's address, checks the verifier
|
|
99
100
|
(`--verifier` or `VERIFIER` in `.env`; required on 151, where a missing one fails and on 153 warns that
|
|
@@ -104,7 +105,16 @@ the deployer and admin readings, the contracts from `deployments/<chain>-*.json`
|
|
|
104
105
|
constructor arguments and the exact `forge verify-contract` command, the gas table, the tests, the
|
|
105
106
|
Slither line, the compiler pins, and the list of what the report does not prove. It refuses to write
|
|
106
107
|
when any check fails and exits 1 with the failing lines. Run it again after the deploy to fill in the
|
|
107
|
-
contract section; the file is dated in UTC.
|
|
108
|
+
contract section; the file is dated in UTC. On 153 the report also says how many faucet claims the
|
|
109
|
+
deployment needs at today's base fee (the deployment rows plus the 25% margin, 500 test RBNT a claim,
|
|
110
|
+
one a day); `redbelly gas` prints the same line.
|
|
111
|
+
|
|
112
|
+
Two things learnt on the first real deploy (17 October 2026). `cast wallet address --account` asks for
|
|
113
|
+
the keystore password on a terminal, so run `redbelly ship` in one, not from an editor's run button;
|
|
114
|
+
without a terminal pass `--password-file`, or `--address` and no cast call is made. And the deploy
|
|
115
|
+
command itself needs `--sender <your address>` beside `--account`: without it forge rehearses as its
|
|
116
|
+
default sender, which fails `isAllowed`, and the scaffold's deploy script now says so and refuses to
|
|
117
|
+
broadcast.
|
|
108
118
|
|
|
109
119
|
The scaffold's `RedbellyDeployScript.sol` refuses to broadcast on 151 without `deployments/ship-151-<today>.md`
|
|
110
120
|
(a dry run says so and continues; 153 warns), and the Hardhat deploy script does the same. Tested on
|
package/dist/checks/compiler.js
CHANGED
|
@@ -6,6 +6,15 @@ export function checkCompilerPins(config) {
|
|
|
6
6
|
const where = `${config.file}${config.profile ? ` (${config.kind === 'foundry' ? 'profile' : 'network'} ${config.profile})` : ''}`;
|
|
7
7
|
const data = { solc: config.solc, evmVersion: config.evmVersion, file: config.file, profile: config.profile };
|
|
8
8
|
const problems = [];
|
|
9
|
+
// A project that vendors a suite with its own compiler (ERC-3643's reference contracts pin 0.8.17, which
|
|
10
|
+
// knows neither prague nor cancun) can only build with solc detected per file. The pin then lives in each
|
|
11
|
+
// file's pragma, which this check does not read, so it warns instead of failing (card 15, 17 October 2026).
|
|
12
|
+
if (config.kind === 'foundry' && config.solc === null && config.autoDetectSolc) {
|
|
13
|
+
const evmOk = config.evmVersion !== null && config.evmVersion.toLowerCase() === EVM_PIN;
|
|
14
|
+
return { id: 'compiler-pins', status: evmOk ? 'warn' : 'fail', reason: evmOk
|
|
15
|
+
? `${where} detects solc per file (auto_detect_solc = true) and sets EVM ${EVM_PIN}. The pin is not in the config: your own files must say pragma solidity ${SOLC_PIN}; a vendored dependency may compile with its own solc for the newest EVM it knows.`
|
|
16
|
+
: `${where} detects solc per file but ${config.evmVersion === null ? 'sets no evm_version' : `sets evm version ${config.evmVersion}, not ${EVM_PIN}`}; Redbelly is Prague.`, data };
|
|
17
|
+
}
|
|
9
18
|
if (config.solc === null)
|
|
10
19
|
problems.push(`no ${config.kind === 'foundry' ? 'solc_version' : 'solidity version'} is pinned`);
|
|
11
20
|
else if (config.solc !== SOLC_PIN)
|
package/dist/checks/deployer.js
CHANGED
|
@@ -16,12 +16,12 @@ export async function resolveDeployer(src) {
|
|
|
16
16
|
const args = ['wallet', 'address', '--account', src.account];
|
|
17
17
|
if (src.passwordFile)
|
|
18
18
|
args.push('--password-file', src.passwordFile);
|
|
19
|
-
const r = await run('cast', args, { cwd: src.cwd, env: src.env, timeoutMs:
|
|
19
|
+
const r = await run('cast', args, { cwd: src.cwd, env: src.env, timeoutMs: 180_000, stdin: '' });
|
|
20
20
|
if (r.spawnError)
|
|
21
21
|
return { address: null, how: '--account', error: `cast is not installed or not on PATH (${r.spawnError}); install Foundry or pass --address for a read-only run.` };
|
|
22
22
|
const m = /0x[0-9a-fA-F]{40}/.exec(r.stdout);
|
|
23
23
|
if (r.code !== 0 || !m)
|
|
24
|
-
return { address: null, how: '--account', error: `cast wallet address --account ${src.account} failed: ${(r.stderr || r.stdout).trim().split('\n').pop() ?? 'no output'}.` };
|
|
24
|
+
return { address: null, how: '--account', error: `cast wallet address --account ${src.account} failed: ${(r.stderr || r.stdout).trim().split('\n').pop() ?? 'no output'}. cast asks for the keystore password on a terminal; without one pass --password-file or --address.` };
|
|
25
25
|
return { address: m[0], how: `cast wallet address --account ${src.account}` };
|
|
26
26
|
}
|
|
27
27
|
return { address: null, how: 'none' };
|
package/dist/config.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export interface ProjectConfig {
|
|
|
3
3
|
file: string | null;
|
|
4
4
|
profile: string | null;
|
|
5
5
|
solc: string | null;
|
|
6
|
+
/** foundry `auto_detect_solc = true`: solc chosen per file from its pragma; the pin cannot be read from the config alone. */
|
|
7
|
+
autoDetectSolc: boolean;
|
|
6
8
|
evmVersion: string | null;
|
|
7
9
|
optimizerRuns: number | null;
|
|
8
10
|
/** foundry: `src`; hardhat: `contracts`. */
|
package/dist/config.js
CHANGED
|
@@ -16,7 +16,7 @@ export function readProjectConfig(project, profile, env = process.env) {
|
|
|
16
16
|
if (existsSync(file))
|
|
17
17
|
return readHardhatConfig(file, profile ?? null);
|
|
18
18
|
}
|
|
19
|
-
return { kind: 'none', file: null, profile: null, solc: null, evmVersion: null, optimizerRuns: null, sourceDir: 'src', chainId: null, rpcEndpoints: {}, rpcUrl: null };
|
|
19
|
+
return { kind: 'none', file: null, profile: null, solc: null, autoDetectSolc: false, evmVersion: null, optimizerRuns: null, sourceDir: 'src', chainId: null, rpcEndpoints: {}, rpcUrl: null };
|
|
20
20
|
}
|
|
21
21
|
/** Enough TOML for foundry.toml: sections, dotted headers, scalars, one-line arrays, comments. */
|
|
22
22
|
export function parseToml(text) {
|
|
@@ -125,6 +125,7 @@ export function readFoundryConfig(file, profile) {
|
|
|
125
125
|
file,
|
|
126
126
|
profile,
|
|
127
127
|
solc: str('solc') ?? str('solc_version'),
|
|
128
|
+
autoDetectSolc: get('auto_detect_solc') === true,
|
|
128
129
|
evmVersion: str('evm_version'),
|
|
129
130
|
optimizerRuns: num('optimizer_runs'),
|
|
130
131
|
sourceDir: str('src') ?? 'src',
|
|
@@ -166,6 +167,7 @@ export function readHardhatConfig(file, network) {
|
|
|
166
167
|
file,
|
|
167
168
|
profile: chosen,
|
|
168
169
|
solc,
|
|
170
|
+
autoDetectSolc: false,
|
|
169
171
|
evmVersion: evm ? evm[1] : null,
|
|
170
172
|
optimizerRuns: runs ? Number(runs[1]) : null,
|
|
171
173
|
sourceDir: contractsDir ? contractsDir[1].replace(/^\.\//, '') : 'contracts',
|
package/dist/gas.d.ts
CHANGED
|
@@ -62,7 +62,24 @@ export interface GasReport {
|
|
|
62
62
|
readonly added: readonly string[];
|
|
63
63
|
readonly removed: readonly string[];
|
|
64
64
|
} | null;
|
|
65
|
+
/** Testnet only: what the deployment rows cost in faucet claims, with pre-flight's 25% margin, at this quote. */
|
|
66
|
+
readonly faucet: FaucetDays | null;
|
|
65
67
|
}
|
|
68
|
+
export interface FaucetDays {
|
|
69
|
+
/** What one FAUCETME claim pays, in test RBNT, and how often (read 15 September 2026). */
|
|
70
|
+
readonly perClaimRbnt: number;
|
|
71
|
+
readonly claimsPerDay: number;
|
|
72
|
+
/** Deployment gas summed over the rows, plus the 25% pre-flight's balance check adds. */
|
|
73
|
+
readonly gasWithMargin: bigint;
|
|
74
|
+
readonly rbnt: number;
|
|
75
|
+
readonly claims: number;
|
|
76
|
+
readonly days: number;
|
|
77
|
+
readonly pricedAt: string;
|
|
78
|
+
}
|
|
79
|
+
export declare const FAUCET_PER_CLAIM_RBNT = 500;
|
|
80
|
+
export declare const FAUCET_CLAIMS_PER_DAY = 1;
|
|
81
|
+
/** Faucet claims a project needs before its deploy: the deployment rows, 25% on top, at the quote's base fee and price. 153 only. */
|
|
82
|
+
export declare function faucetDays(rows: readonly PricedRow[], quote: GasQuote): FaucetDays | null;
|
|
66
83
|
export interface GasOptions {
|
|
67
84
|
/** A Foundry project, or a scaffold root (its contracts/ is used). Default: cwd. */
|
|
68
85
|
project?: string;
|
package/dist/gas.js
CHANGED
|
@@ -11,6 +11,21 @@ import { PRICE_FEED_DECIMALS, chainById, formatUnits, getLatestPrice, toCaller }
|
|
|
11
11
|
import { run } from './proc.js';
|
|
12
12
|
const require = createRequire(import.meta.url);
|
|
13
13
|
export const GAS_VERSION = require('../package.json').version;
|
|
14
|
+
export const FAUCET_PER_CLAIM_RBNT = 500;
|
|
15
|
+
export const FAUCET_CLAIMS_PER_DAY = 1;
|
|
16
|
+
/** Faucet claims a project needs before its deploy: the deployment rows, 25% on top, at the quote's base fee and price. 153 only. */
|
|
17
|
+
export function faucetDays(rows, quote) {
|
|
18
|
+
if (quote.chain !== 153)
|
|
19
|
+
return null;
|
|
20
|
+
const deploys = rows.filter((r) => r.item === 'deployment');
|
|
21
|
+
if (deploys.length === 0)
|
|
22
|
+
return null;
|
|
23
|
+
const gas = deploys.reduce((a, r) => a + r.gas, 0n);
|
|
24
|
+
const gasWithMargin = gas + gas / 4n;
|
|
25
|
+
const rbnt = Number(gasWithMargin * quote.baseFeeWei) / 1e18;
|
|
26
|
+
const claims = Math.max(1, Math.ceil(rbnt / FAUCET_PER_CLAIM_RBNT));
|
|
27
|
+
return { perClaimRbnt: FAUCET_PER_CLAIM_RBNT, claimsPerDay: FAUCET_CLAIMS_PER_DAY, gasWithMargin, rbnt, claims, days: Math.ceil(claims / FAUCET_CLAIMS_PER_DAY), pricedAt: quote.priceTimestamp };
|
|
28
|
+
}
|
|
14
29
|
// ---- parsing, pure ----
|
|
15
30
|
const SNAPSHOT_LINE = /^([A-Za-z0-9_$]+):(\S.*?)\s+\((.*)\)\s*$/;
|
|
16
31
|
/** `.gas-snapshot`: `Contract:test() (gas: N)`, fuzz lines by their mean (μ), invariant lines (no gas) skipped. */
|
|
@@ -175,6 +190,7 @@ export async function runGas(opts = {}) {
|
|
|
175
190
|
quote,
|
|
176
191
|
rows: priced.rows,
|
|
177
192
|
diff: diffFile ? { file: diffFile, rows: before.length, added: priced.added, removed: priced.removed } : null,
|
|
193
|
+
faucet: faucetDays(priced.rows, quote),
|
|
178
194
|
};
|
|
179
195
|
}
|
|
180
196
|
// ---- text ----
|
|
@@ -212,6 +228,8 @@ export function renderGasText(report) {
|
|
|
212
228
|
lines.push('');
|
|
213
229
|
if (deploys.length)
|
|
214
230
|
lines.push(`deployment: ${deploys.map((d) => `${d.contract} ${num(d.gas)} gas = ${rbnt6(d.rbnt)} RBNT = US$${fixed(d.usd, 4)}`).join('; ')}. Pre-flight's balance check adds 25% on top; pass its --gas the figure here.`);
|
|
231
|
+
if (report.faucet)
|
|
232
|
+
lines.push(`faucet: ${num(report.faucet.gasWithMargin)} gas with the 25% margin = ${fixed(report.faucet.rbnt, 1)} test RBNT at this base fee = ${report.faucet.claims} claim${report.faucet.claims === 1 ? '' : 's'} of ${report.faucet.perClaimRbnt} (${report.faucet.days} day${report.faucet.days === 1 ? '' : 's'}, one claim a day) before you can deploy. Priced at ${report.faucet.pricedAt}; the base fee moves, so re-run on the day.`);
|
|
215
233
|
lines.push(`Gas is priced in US dollars and converted to RBNT at execution from the feed, so the cents column is the stable one; the RBNT column moves with the price. Base fee only: the chain charges no priority fee.`);
|
|
216
234
|
return lines.join('\n') + '\n';
|
|
217
235
|
}
|
package/dist/ship-cli.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ShipOptions } from './ship.js';
|
|
2
|
-
export declare const SHIP_HELP = "redbelly ship --chain <151|153> --account <keystore-name> [options]\n\nRuns pre-flight (seven checks) with the keystore's address, checks the Slither sidecar, runs\nforge test and the five-state suites, prices the gas report at the chain's base fee and feed\nprice, reads the deployer and the admin (isAllowed, Safe 1.4.1, threshold, owners), and writes\ndeployments/ship-<chain>-<date>.md with every check and its result, the addresses, the\nconstructor arguments and the verify command. Refuses to write when any check fails. The\ndeploy script refuses to broadcast on 151 without a report dated today; 153 warns.\n\n --chain <151|153> required\n --account <name>
|
|
2
|
+
export declare const SHIP_HELP = "redbelly ship --chain <151|153> --account <keystore-name> [options]\n (or --address <0x\u2026> in place of --account; DEPLOYER in .env also works)\n\nRuns pre-flight (seven checks) with the keystore's address, checks the Slither sidecar, runs\nforge test and the five-state suites, prices the gas report at the chain's base fee and feed\nprice, reads the deployer and the admin (isAllowed, Safe 1.4.1, threshold, owners), and writes\ndeployments/ship-<chain>-<date>.md with every check and its result, the addresses, the\nconstructor arguments and the verify command. Refuses to write when any check fails. The\ndeploy script refuses to broadcast on 151 without a report dated today; 153 warns.\n\n --chain <151|153> required\n --account <name> Foundry keystore name, resolved with cast wallet address. cast asks for the\n keystore password on the terminal, so run this in one (not from an editor's\n run button); without a terminal pass --password-file, or use --address\n --address <0x\u2026> the deployer's address, no cast call; the report says it came this way\n --password-file <path> passed through to cast (or set ETH_PASSWORD)\n --admin <0x\u2026> the Safe that receives every role (or ADMIN_SAFE in .env); required on 151\n --gas <units> deployment gas for the balance check (default: the gas report's deployment row)\n --verifier <0x\u2026> the dApp's verifier (or VERIFIER in .env); required on 151\n --project <dir> Foundry project or scaffold root (default: current directory)\n --rpc <url> JSON-RPC endpoint (default: the governors RPC for --chain; a fork works)\n --out <path> write the report here instead\n --json print the report as JSON (the file is still written when checks pass)\n --version, --help\n";
|
|
3
3
|
export declare function parseShipArgs(argv: readonly string[]): (Partial<ShipOptions> & {
|
|
4
4
|
json: boolean;
|
|
5
5
|
help: boolean;
|
package/dist/ship-cli.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// arguments. --json for agents. Never takes a key: --account names a keystore that cast reads.
|
|
4
4
|
import { SHIP_VERSION, renderShipText, runShip, shipReportJson, ShipUsageError } from './ship.js';
|
|
5
5
|
export const SHIP_HELP = `redbelly ship --chain <151|153> --account <keystore-name> [options]
|
|
6
|
+
(or --address <0x…> in place of --account; DEPLOYER in .env also works)
|
|
6
7
|
|
|
7
8
|
Runs pre-flight (seven checks) with the keystore's address, checks the Slither sidecar, runs
|
|
8
9
|
forge test and the five-state suites, prices the gas report at the chain's base fee and feed
|
|
@@ -12,7 +13,10 @@ constructor arguments and the verify command. Refuses to write when any check fa
|
|
|
12
13
|
deploy script refuses to broadcast on 151 without a report dated today; 153 warns.
|
|
13
14
|
|
|
14
15
|
--chain <151|153> required
|
|
15
|
-
--account <name>
|
|
16
|
+
--account <name> Foundry keystore name, resolved with cast wallet address. cast asks for the
|
|
17
|
+
keystore password on the terminal, so run this in one (not from an editor's
|
|
18
|
+
run button); without a terminal pass --password-file, or use --address
|
|
19
|
+
--address <0x…> the deployer's address, no cast call; the report says it came this way
|
|
16
20
|
--password-file <path> passed through to cast (or set ETH_PASSWORD)
|
|
17
21
|
--admin <0x…> the Safe that receives every role (or ADMIN_SAFE in .env); required on 151
|
|
18
22
|
--gas <units> deployment gas for the balance check (default: the gas report's deployment row)
|
|
@@ -50,6 +54,9 @@ export function parseShipArgs(argv) {
|
|
|
50
54
|
case '--account':
|
|
51
55
|
o.account = next();
|
|
52
56
|
break;
|
|
57
|
+
case '--address':
|
|
58
|
+
o.address = next();
|
|
59
|
+
break;
|
|
53
60
|
case '--password-file':
|
|
54
61
|
o.passwordFile = next();
|
|
55
62
|
break;
|
|
@@ -91,9 +98,13 @@ export function parseShipArgs(argv) {
|
|
|
91
98
|
if (!o.help && !o.version) {
|
|
92
99
|
if (!o.chain)
|
|
93
100
|
throw new Error(`--chain 151 or 153 is required\n\n${SHIP_HELP}`);
|
|
94
|
-
if (!o.account)
|
|
95
|
-
throw new Error(`--account <keystore-name> is required: the report carries the deployer's address and its isAllowed reading\n\n${SHIP_HELP}`);
|
|
96
|
-
if (
|
|
101
|
+
if (!o.account && !o.address && !process.env['DEPLOYER'])
|
|
102
|
+
throw new Error(`--account <keystore-name> is required (or --address <0x…>, or DEPLOYER in the environment): the report carries the deployer's address and its isAllowed reading\n\n${SHIP_HELP}`);
|
|
103
|
+
if (o.account && o.address)
|
|
104
|
+
throw new Error('give --account or --address, not both');
|
|
105
|
+
if (o.address && !/^0x[0-9a-fA-F]{40}$/.test(o.address))
|
|
106
|
+
throw new Error('--address must be a 20-byte hex address');
|
|
107
|
+
if (o.account && !/^[A-Za-z0-9._-]{1,64}$/.test(o.account))
|
|
97
108
|
throw new Error('--account: keystore names use letters, digits, dot, dash and underscore only');
|
|
98
109
|
}
|
|
99
110
|
return o;
|
package/dist/ship.d.ts
CHANGED
|
@@ -7,8 +7,10 @@ export interface ShipOptions {
|
|
|
7
7
|
/** Foundry project or scaffold root (its contracts/ is used). Default: cwd. */
|
|
8
8
|
project?: string;
|
|
9
9
|
chain: 151 | 153;
|
|
10
|
-
/** Foundry keystore name; cast resolves the address, nothing else is read from it. */
|
|
11
|
-
account
|
|
10
|
+
/** Foundry keystore name; cast resolves the address, nothing else is read from it. One of account, address or DEPLOYER in .env. */
|
|
11
|
+
account?: string;
|
|
12
|
+
/** The deployer's address given directly: no cast call, no password prompt. */
|
|
13
|
+
address?: string;
|
|
12
14
|
passwordFile?: string;
|
|
13
15
|
/** The Safe that receives every role. Required on 151; ADMIN_SAFE in .env fills it in. */
|
|
14
16
|
admin?: string;
|
package/dist/ship.js
CHANGED
|
@@ -13,7 +13,7 @@ import { chainById, decodeWords, ethCall, isAllowed, toCaller } from '@gatedpath
|
|
|
13
13
|
import { readProjectConfig } from './config.js';
|
|
14
14
|
import { readDotEnv } from './dotenv.js';
|
|
15
15
|
import { readAdmin } from './checks/admin.js';
|
|
16
|
-
import { parseGasReportJson, priceRows, quoteGas } from './gas.js';
|
|
16
|
+
import { faucetDays, parseGasReportJson, priceRows, quoteGas } from './gas.js';
|
|
17
17
|
import { run } from './proc.js';
|
|
18
18
|
import { DEFAULT_GAS, runPreflight } from './run.js';
|
|
19
19
|
import { findGatedSuites, summariseTests } from './suites.js';
|
|
@@ -151,7 +151,7 @@ export async function runShip(opts) {
|
|
|
151
151
|
// 2. pre-flight, seven checks, with the keystore's address and the measured deployment gas
|
|
152
152
|
const gasForBalance = opts.gas !== undefined ? BigInt(opts.gas) : (deploymentGas ?? DEFAULT_GAS);
|
|
153
153
|
const admin = opts.admin ?? rootEnv['ADMIN_SAFE'];
|
|
154
|
-
const preflight = await runPreflight({ project: dir, chain, rpc, account: opts.account, ...(opts.passwordFile ? { passwordFile: opts.passwordFile } : {}), ...(admin ? { admin } : {}), gas: gasForBalance, env });
|
|
154
|
+
const preflight = await runPreflight({ project: dir, chain, rpc, ...(opts.account ? { account: opts.account } : {}), ...(opts.address ? { address: opts.address } : {}), ...(opts.passwordFile ? { passwordFile: opts.passwordFile } : {}), ...(admin ? { admin } : {}), gas: gasForBalance, env });
|
|
155
155
|
for (const c of preflight.checks)
|
|
156
156
|
checks.push({ id: c.id, status: c.status, reason: c.reason, ...(c.data ? { data: c.data } : {}) });
|
|
157
157
|
if (chain === 151 && !admin)
|
|
@@ -214,7 +214,7 @@ export async function runShip(opts) {
|
|
|
214
214
|
const deployerCheck = preflight.checks.find((c) => c.id === 'deployer-verified');
|
|
215
215
|
const deployer = {
|
|
216
216
|
address: preflight.deployer,
|
|
217
|
-
how: deployerCheck?.data?.['how'] ?? (opts.account ? `cast wallet address --account ${opts.account}` : 'none'),
|
|
217
|
+
how: deployerCheck?.data?.['how'] ?? (opts.account ? `cast wallet address --account ${opts.account}` : opts.address ? '--address' : 'none'),
|
|
218
218
|
isAllowed: typeof deployerCheck?.data?.['allowed'] === 'boolean' ? deployerCheck.data['allowed'] : null,
|
|
219
219
|
wellKnownDevAccount: deployerCheck?.data?.['wellKnownDevAccount'] === true,
|
|
220
220
|
};
|
|
@@ -316,7 +316,7 @@ export function renderShipMarkdown(r) {
|
|
|
316
316
|
L.push('## Contracts');
|
|
317
317
|
L.push('');
|
|
318
318
|
if (r.contracts.length === 0) {
|
|
319
|
-
L.push(`No deployment record for chain ${r.chain} under \`deployments/\` yet. Deploy with \`forge script script/Deploy.s.sol --rpc-url ${r.network === 'mainnet' ? 'redbelly_mainnet' : 'redbelly_testnet'} --account <keystore-name> --broadcast\`, then run \`redbelly ship\` again today: this section fills in with the address, the constructor arguments and the exact verify command.`);
|
|
319
|
+
L.push(`No deployment record for chain ${r.chain} under \`deployments/\` yet. Deploy with \`forge script script/Deploy.s.sol --rpc-url ${r.network === 'mainnet' ? 'redbelly_mainnet' : 'redbelly_testnet'} --account <keystore-name> --sender ${r.deployer.address ?? '<your-address>'} --broadcast\`, then run \`redbelly ship\` again today: this section fills in with the address, the constructor arguments and the exact verify command.`);
|
|
320
320
|
}
|
|
321
321
|
else {
|
|
322
322
|
for (const c of r.contracts) {
|
|
@@ -343,6 +343,9 @@ export function renderShipMarkdown(r) {
|
|
|
343
343
|
const q = r.gas.quote;
|
|
344
344
|
L.push(`Priced at block ${fmt(q.block)}: base fee ${(Number(q.baseFeeWei) / 1e9).toLocaleString('en-US', { maximumFractionDigits: 2 })} gwei, RBNT US$${q.usdPerRbnt} on the feed at ${q.priceFeed} (${q.priceTimestamp}). Gas is priced in US dollars on this chain and converted to RBNT at execution, so the cents column is the stable one.`);
|
|
345
345
|
L.push('');
|
|
346
|
+
const faucet = faucetDays(r.gas.rows, r.gas.quote);
|
|
347
|
+
if (faucet)
|
|
348
|
+
L.push(`Faucet: the deployment rows with pre-flight's 25% margin come to ${fmt(faucet.gasWithMargin)} gas = ${faucet.rbnt.toFixed(1)} test RBNT at this base fee, ${faucet.claims} claim${faucet.claims === 1 ? '' : 's'} of ${faucet.perClaimRbnt} from the faucet (${faucet.days} day${faucet.days === 1 ? '' : 's'}, one claim a day). The base fee moves; re-run on the day.`, '');
|
|
346
349
|
if (r.gas.deployment)
|
|
347
350
|
L.push(`Deployment of ${r.gas.deployment.contract}: ${fmt(r.gas.deployment.gas)} gas = ${short(r.gas.deployment.rbnt)} RBNT = ${r.gas.deployment.cents.toFixed(2)} US cents. Pre-flight's balance check used ${fmt(BigInt(r.preflight.checks.find((c) => c.id === 'balance')?.data?.['gas'] ?? '0'))} gas with 25% on top.`);
|
|
348
351
|
L.push('');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gatedpath/preflight",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Pre-flight checks before deploying to Redbelly Network (151 mainnet, 153 testnet): chain ID, deployer verification, Safe admin, compiler pins, secrets in git history, RBNT balance, Slither report.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"prepack": "npm run build"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@gatedpath/agent-rules": "0.2.
|
|
44
|
-
"@gatedpath/chains": "0.2.
|
|
43
|
+
"@gatedpath/agent-rules": "0.2.2",
|
|
44
|
+
"@gatedpath/chains": "0.2.2"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@types/node": "^22.20.2",
|