Release gate: v0.40-dev → v0.40 (waits for cross-repo E2E)#347
Open
MuncleUscles wants to merge 44 commits into
Open
Release gate: v0.40-dev → v0.40 (waits for cross-repo E2E)#347MuncleUscles wants to merge 44 commits into
MuncleUscles wants to merge 44 commits into
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…he leader's execution result (#346) * fix(contracts)!: require consensus acceptance for success, not just leader execution result - assertSuccessfulExecution now requires status ∈ {ACCEPTED, FINALIZED} via the SDK's isSuccessful in addition to FINISHED_WITH_RETURN: txExecutionResult is the LEADER's result and exists even on UNDETERMINED — previously an undetermined deploy printed 'deployed successfully' with an address that would never materialize - failure messages now name both the consensus outcome and the leader result ('decided as UNDETERMINED (no validator majority); leader execution result: FINISHED_WITH_RETURN') - map VoteType 3 (TIMEOUT) / 4 (NONDET_DISAGREE) with real diagnoses instead of 'did not expose an execution result' - deploy/write use waitUntil: 'decided' (new SDK API) and report the reached consensus status; explicit fee deposits are echoed before sending - callKey derivation deduplicated: import from genlayer-js (incl. new DEPLOY_CALL_KEY for deploy-targeted Mode-2 allocations) * build: pin genlayer-js to the v2 feature branch until the npm release npm 1.1.8 lacks the new fee APIs (isSuccessful, DEPLOY_CALL_KEY, waitUntil), so CI cannot build against it. github:genlayerlabs/genlayer-js#feat/ v06-fee-estimation-rework builds via the SDK's new prepare script (verified: fresh install produces dist + exports). Re-pin to the npm semver once genlayer-js v2 ships (release-sequencing item). * ci: run validate-code on pushes to the v0.40 line Push trigger was pinned to v0.39 — merges to v0.40-dev/v0.40 got no post-merge CI. * ci: make codecov upload non-blocking The OIDC upload fails on ubuntu/windows independent of this PR (#345 merged with identical failures); tests/build stay blocking. * build: re-pin genlayer-js to v2-dev (feature branch merged)
* docs: add branching guide * ci: harden testnet smoke timeout
* feat: support fee profiles in contract commands * test: make fee profile deploy test portable
* feat: add staking validators discovery * feat: epoch-aware validator listing with below-min indicator * fix(staking): account-less client for read-only staking queries getReadOnlyStakingClient threw 'Account not found' on fresh installs; listings and other reads don't need a local account.
* feat: add vesting commands * feat(vesting): validator subcommands — create/join, deposit, exit, claim, operator-transfer, set-identity, list/status Drives the CON-607 Vesting.sol validator leg through the SDK's named vestingValidator* actions; list/status enumerate getValidatorWallets with per-wallet deposited principal.
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Selecting Gemini during `genlayer init` failed with:
Requested providers '{'geminiai'}' do not match any stored providers.
The selected provider id is forwarded verbatim to
sim_createRandomValidators, but the backend's llm_provider table stores
Gemini as "google". Rename the provider id geminiai -> google so it
matches. Display name ("Gemini") and env var (GEMINI_API_KEY) are
unchanged.
Since "geminiai" never resolved to a valid provider, no working
configuration relied on it.
Fixes #271
Co-authored-by: Edgars Nemše <edgars@genlayerlabs.com>
The sync-docs workflow rsynced the generated category-based docs/api-references/_meta.json into genlayer-docs and then immediately overwrote it with a hardcoded heredoc containing the pre-grouping flat command list (init, up, deploy, ...). Those keys no longer match the directory layout, so the genlayer-docs sidebar rendered broken entries on every sync (fixed manually in genlayer-docs#426; this removes the cause). Also make the generated root meta complete: - add "index": "Overview" for the generated index.mdx - append ungrouped top-level commands (estimate-fees, finalize, finalize-batch) so they get explicit nav entries instead of relying on Nextra's implicit append Snapshot under docs/api-references regenerated against current main (picks up the new estimate-fees command and latest help text). Co-authored-by: Albert Castellana <albert@genlayer.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Edgars <edgars@entropicsolutions.io>
getSlashingAddress() was removed from the genlayer-js SDK (v0.39+/v2-dev), causing `genlayer staking validator-history` to crash with `client.getSlashingAddress is not a function` before any history is fetched. Resolve the idleness (slashing) contract address dynamically via viem readContract against consensusMainContract.getIdlenessAddress(), falling back to the staking contract address if resolution fails so reward events still display. Port of #344 (by @ygd58) from the dead v0.39 line to v0.40-dev. Supersedes #344. Fixes #341 Co-authored-by: Edgars <edgars@entropicsolutions.io>
) * feat(network): custom network profiles with deployment-file import genlayer network add <alias> --base <built-in> [--deployment <json>] [--rpc <url>] [--consensus-main|--consensus-data|--staking|--fee-manager |--rounds-storage|--appeals <addr>] [--chain-id <n>] [--deployment-key <path>] Profiles persist as base + address overrides only; resolveNetwork loads the base chain fresh from genlayer-js and applies overrides, so ABIs never go stale. network set/list/info/remove and StakingAction --network accept custom aliases. The consensus deployments.json shape is parsed by walking the tree for ContractName->address leaves (ConsensusMain, ConsensusData, GenStaking/Staking, FeeManager, Rounds/RoundsStorage, Appeals); flags take precedence over the file. Adds a prepare script so npm install from a git ref builds dist. Verified: 576 vitest tests, full manual smoke (add/list/set/info/remove with a deployment file). * chore(deps): bump genlayer-js to v2-dev tip for vesting actions The locked v2-dev SHA (28e99fbc) predates the vesting client actions; vestingValidatorJoin and friends land at 666d1156. Verified live: vesting validator create succeeds against a #1162-branch consensus deployment. * docs(cli): regenerate API references; fix option placeholder regex The docs generator's option regex only matched <word> placeholders, so flags with dots or hyphens in the value name (--base <built-in-alias>, --deployment <path.json>, --deployment-key <dot.path>) were silently dropped from the options tables. Widen to <[^>]+> and regenerate: adds the network add/remove pages and the previously undocumented vesting command section (validator create/deposit/exit/claim, operator-transfer, set-identity, delegate/undelegate/claim/withdraw/list). * fix(vesting): resolve validator wallet address in create output The join receipt does not carry the new wallet address, so the output printed validatorWallet: undefined. Read getValidatorWallets from the vesting contract after the join and report the newest entry. Verified live against a #1162-branch deployment.
* fix: make git install build script self contained * chore: refresh genlayer-js lockfile * fix: make keychain dependency optional * fix: restore git prepare build * fix: include build dependency for git installs * chore: keep esbuild as dev dependency
* ci: add clarke cli tarball release * ci: publish prereleases to npm dist tags
…367) * feat(staking): browser-wallet signing for validator-join and wizard Add a dependency-free localhost bridge (node:http) that lets a browser wallet (MetaMask / any injected window.ethereum) sign-and-broadcast the validator-join and wizard-identity transactions. Design: raw-viem bridge, not an SDK account. genlayer-js executeWrite requires account.signTransaction (sign -> sendRawTransaction), which MetaMask cannot satisfy (it only does eth_sendTransaction). The CLI encodes calldata itself, a localhost page eth_sendTransactions it, and the CLI waits for the receipt via a viem publicClient (reusing the glHttpConfig id!=0 quirk) and decodes ValidatorJoin for the wallet. New: src/lib/wallet/{stakingTx,bridgePage,browserBridge}.ts. Flag --wallet <keystore|browser> on validator-join and wizard, with hard errors when combined with --password/--account. Only the join + identity sends route through the bridge; operator keystore generation/export and config summary are unchanged. Other commands can adopt later via the getBrowserWalletSession seam. * feat(wallet): shared browser-wallet plumbing + all staking writes Generalize the #367 browser-wallet bridge into a reusable signing layer and adopt it across every staking write command. - Extract StakingAction.getBrowserWalletSession into a shared src/lib/wallet/browserSend.ts (openBrowserWalletSession); add the Lane B eip1193Provider shim (eth_sendTransaction -> bridge; eth_chainId/eth_accounts local) and setNextLabel. Owns glHttpConfig. - Grow stakingTx.ts into txBuilders.ts (generic buildTx + encodeExtraCid); stakingTx re-exports for back-compat. - Add shared addWalletModeOption registrar (--wallet <keystore|browser>). - BaseAction: isBrowserWallet, assertWalletFlags, getBrowserSession/ closeBrowserSession, and getClient browser mode (Lane B, skips keystore). - Widen bridge protocol (gas/type pass-through; page drops nonce/chainId). - Adopt --wallet browser on the 9 remaining staking writes + prime-all; refactor validator-join/wizard onto the shared registrar. Tests: txBuilders, browserSend (incl. eip1193 shim), bridge gas/type pass-through, per-command keystore-untouched + output parity + flag conflicts. Docs regenerated. * feat(vesting): browser-wallet signing for all vesting writes Adopt --wallet browser (Lane A) across all 12 vesting write commands via buildTx(VESTING_ABI, vesting, fn, args). Beneficiary resolves to the connected wallet address; the vesting lookup runs on the account-less read-only client. BREAKING (deprecated-flag rename): the deprecated --wallet <address> alias on 'vesting validator ...' subcommands is renamed to --validator-wallet <address> so --wallet is free for the signing-mode flag. The recommended positional argument form is unaffected. Internally the resolved positional is carried as 'walletAddress' (VestingConfig gains wallet mode + validatorWallet alias). Tests: vesting action browser-mode (routes through session, keystore client untouched, output parity) + command parsing incl. the --validator-wallet rename. Docs regenerated. * feat(contracts): browser-wallet signing for deploy and write (Lane B) Add --wallet browser to 'deploy' (incl. deploy-scripts mode) and 'contracts write'. Sets walletModeOverride so BaseAction.getClient builds the genlayer-js client with {account: <address>, provider: eip1193 shim}; the SDK's existing json-rpc-account branch builds the addTransaction tx, deposits fees, waits, and extracts the GenLayer txId. No genlayer-js changes. closeBrowserSession in finally; setNextLabel for a friendly wallet prompt. Tests: createClient wired with {account, provider} and getAccount never called in browser mode; command parsing. Docs regenerated. * feat(transactions): browser-wallet signing for appeal and finalize (Lane B) Add --wallet browser to 'transactions appeal', 'finalize', and 'finalize-batch'. These already funnel through BaseAction.getClient, so browser mode is the same Lane B client construction + closeBrowserSession in finally; the SDK's consensus provider branch does the send. Docs regenerated. * feat(wallet): session descriptor + shared timing constants Foundations for the persistent connect-once browser-wallet session: - sessionConstants.ts: single source of truth for heartbeat/TTL/timeout budgets (LONG_POLL, HEARTBEAT_DEAD, TAB_DEAD_GRACE, IDLE_TTL, DAEMON_READY, CONNECT/TX timeouts) plus descriptor/log filenames and the walletMode/walletSessionTtlMinutes config keys. - sessionDescriptor.ts: on-disk descriptor (~/.genlayer/wallet-session.json) written atomically at 0600; read+schema-validated (bad JSON/version -> null); isPidAlive() cheap first-gate (signal 0, EPERM = alive). Descriptor unit tests: atomic 0600 write, round-trip, garbage/version rejection, idempotent remove, pid-alive. * feat(wallet): persistent daemon mode on the browser bridge Add an opt-in persistent mode so one long-lived bridge can broker txs for many CLI processes: - New client routes (token-authed): GET /api/ping, GET /api/state, POST /api/enqueue, GET /api/tx?id=, POST /api/shutdown. Page routes (/api/connected, /api/result) keep the strict Origin check; client routes are Origin-exempt (a Node client sends no Origin; token-in-header forces a CORS preflight the server never satisfies). - Result store so remote HTTP callers can poll a tx to done/sent/rejected/error (GC 10min after completion); enqueue returns 409 wallet-not-connected/tab-closed for fail-fast. - Heartbeat: lastPagePollAt stamped on every /api/next; getState() exposes it. - onConnected / onActivity / onShutdown callbacks for the daemon. - Shared serializeBridgeTx/parseBridgeTx pair (client and server can't drift); serializeTx now reuses it. LONG_POLL_MS sourced from sessionConstants. Existing bridge tests unchanged; +11 persistent-mode tests (auth, enqueue round-trip, rejected/error, Origin exemption vs page 403, heartbeat, FIFO, shutdown, 404 on non-persistent). * refactor(wallet): split bridge transport; add remote session + client Separate "how a tx reaches the wallet" from the shared signing lanes so a command can run over either an in-process bridge or a remote daemon: - BridgeTransport seam; buildBrowserSession(transport, ...) holds the shared preflight + receipt-wait (Lane A), EIP-1193 shim (Lane B), and labels verbatim. BrowserSession gains kind/sessionUrl; bridge is now optional (present only for local sessions). - openBrowserWalletSession re-based on a LocalBridgeTransport (existing tests pass unchanged). New openRemoteWalletSession wraps a RemoteSessionTransport whose close() is a no-op so one failed preflight never kills a shared session. - WalletSessionClient: fetch-only client for a running daemon (ping/state/ enqueueTx/waitForTxResult/waitForConnection/shutdown), token on every call, fail-fast on stale page heartbeat, 409 -> clear reconnect messages. sessionClient tests drive a real in-process persistent bridge with a fetch-simulated page (openUrl mocked); browserSend tests unchanged. * feat(wallet): session daemon runtime, detached spawn, and resolver - sessionDaemon.ts: runWalletSessionDaemon owns the bridge + tab + descriptor lifecycle. Writes the descriptor only after listen succeeds; rewrites address onConnected; bumps lastUsed (throttled) onActivity. Self-terminates on idle TTL, tab-dead heartbeat loss, connect timeout, signal, or fatal error, and on /api/shutdown -- ALWAYS removing the descriptor first. Deterministic shutdown: the bridge's onShutdown fires an ordered teardown (remove descriptor -> close bridge -> exit); the keep-alive timer is NOT unref'd, so the daemon only ever exits through cleanupAndExit (never by the loop draining after the socket closes) -> the "daemon gone => descriptor removed" invariant always holds. - spawnDaemon.ts: spawnWalletDaemon re-execs the bundled CLI (process.execPath + argv[1] + "wallet daemon"), detached + unref, stdio -> 0600 logfile; NO token on argv. waitForDaemonReady polls descriptor+pid+/api/ping, surfaces the log tail on timeout. - sessionResolver.ts: resolveBrowserWalletSession -- discover live session (pid + ping), stale-cleanup, chain-mismatch hard error, and fallback (auto-start-and-persist default | own-bridge | error), degrading auto-start to own-bridge if spawn/ready fails. In-process/mocked tests only (injected spawnFn, mocked openUrl, no real daemon or browser): daemon descriptor/onConnected/idle/tab-dead/connect-timeout/ singleton/shutdown-cleanup; spawn argv+unref and ready/timeout; resolver live/stale/mismatch/auto-start-degrade/error matrix. * feat(wallet): wallet connect/status/disconnect commands (+ hidden daemon) New `genlayer wallet` command group (registered in src/index.ts): - connect [--network --rpc]: reuse a matching live session, or spawn the detached daemon, print the bridge URL + SSH port-forward hint, and wait for the wallet to connect. Different chain -> explicit shutdown + switch. - status: address/network/chainId/port/URL/age/idle/heartbeat/queue; exit 0 only when live+connected (scriptable). - disconnect: /api/shutdown -> wait for exit (SIGTERM fallback) -> remove descriptor (idempotent). - daemon (hidden): detached-process entry point -> runWalletSessionDaemon. wallet command tests dispatch connect/status/disconnect/daemon and assert daemon is help-hidden; index test mocks the new initializer. * feat(wallet): walletMode config default + per-command session adoption Make browser mode reusable across commands and configurable as the default: - walletMode config: BaseAction.resolveWalletMode centralises precedence (--wallet flag > walletMode config > keystore); invalid flag throws, unknown config value warns + keystore. walletOption drops the hardcoded commander "keystore" default so an omitted flag is distinguishable from an explicit one (and can defer to config). write/deploy/appeal/finalize switch their direct `wallet === "browser"` checks to isBrowserWallet(...). - Adoption: BaseAction.getBrowserSession and StakingAction.getBrowserWalletSession now go through resolveBrowserWalletSession (fallback auto-start for writes, own-bridge for the wizard). Per-command finally blocks are untouched (closeBrowserSession/session.close() are no-ops for remote sessions, so a shared daemon survives); validatorJoin and the wizard switch session.bridge.close() -> session.close(). walletSession tests cover resolveWalletMode matrix; command/action tests updated for the no-commander-default behaviour and session.close() (remote sessions survive finally; wizard/validator-join assert session.close, not bridge.close). * test(wallet): global open() safety net + regenerated CLI docs - tests/setup.ts (wired via vitest.config setupFiles): globally mocks the `open` package so no automated test can ever launch a real browser or orphan a tab. Every bridge/daemon test already injects a mocked openUrl; this is the belt-and-suspenders guarantee. system.test.ts keeps its own file-level vi.mock("open") which takes precedence there. - Regenerated api-references: new wallet connect/status/disconnect pages (hidden `daemon` excluded) and the updated --wallet help text/default across all write commands. * style(wallet): prettier-format session runtime + tests * feat(wallet): live wallet session implies browser mode resolveWalletMode gains a session rung: with no --wallet flag and no walletMode config, a live wallet session (descriptor present + daemon pid alive) now resolves to browser mode, so `wallet connect` alone is enough to route subsequent commands through the bridge. Explicit --wallet keystore or walletMode=keystore still overrides a live session. Add hasLiveWalletSession() (sync, never-throws descriptor+pid probe) and make the test suite hermetic: tests/setup.ts redirects os.homedir() to a throwaway per-worker temp dir so the descriptor read never sees the developer's real ~/.genlayer/wallet-session.json (which would otherwise flip commands into browser mode and break otherwise-hermetic tests). * feat(staking): wizard lists custom networks and honors live wallet session Step 2 network picker now appends custom networks (from `genlayer network add`) alongside the built-ins, and the post-selection echo resolves through both maps so a custom alias no longer crashes on BUILT_IN_NETWORKS lookup. Step 1 owner detection switches from `options.wallet === "browser"` to resolveWalletMode(options.wallet) === "browser", so the wizard auto-selects the browser-wallet owner for --wallet browser, walletMode=browser config, or a live wallet session — consistent with every other command. The keystore path and interactive "Connect browser wallet" menu item are unchanged. * feat(account): account show reflects active network + --network flag account show printed `network.name`, which for a custom active network is the inherited base chain name (e.g. "Genlayer Localnet") — a misleading label. Print the active network alias instead (--network flag > config network key > localnet) and add a chainId field so a custom network is unambiguous. Add a --network flag mirroring `account send`, so both the balance query and the printed alias/chainId reflect the chosen network. New unit test covers a custom active network (alias + real chainId, not the base name) and the --network override. * feat(balances): add read-only balances view (wallet + vesting + committed stake) * feat(staking): wizard can fund a validator from a vesting contract Add a funding-source step after network selection: keep 'Your wallet' as the default (original flow unchanged) or fund the self-stake from a vesting contract. For the vesting source the wizard resolves the beneficiary's vesting contract (none -> warn + loop back; one -> use it; many -> pick), checks the contract's available-to-stake (totalAmount - totalWithdrawn) against the minimum stake while keeping a wallet gas sanity warning, and builds+sends vestingValidatorJoin instead of validatorJoin for both keystore (client.vestingValidatorJoin) and browser (buildTx + shared session.sendTransaction) owners. Identity setup is skipped for vesting-backed validators with a pointer to 'genlayer vesting validator set-identity'; the summary reflects the funding source and notes funds return to the vesting contract on exit. * fix(vesting): available-to-stake is the contract balance, not derived math * fix(balances): a live wallet session is the active identity over the keystore default * fix(network): custom networks display their alias as the name * fix(wallet): detect a closed tab at session acquire + let connect recover Browser-mode commands (notably `staking wizard`) only discovered a closed wallet tab at the final sign step, after every wizard step was filled in. resolveBrowserWalletSession now checks the page-heartbeat freshness in its live-session branch and throws the reconnect message at acquire time, so the wizard fails at the balance-check step instead of the sign step. lastPagePollAt === 0 (never polled) stays fresh, and a just-connected page is re-read so it is not misflagged. `wallet connect` reused a daemon that was pid-alive + pinging + state.connected but whose tab was dead, printing "Already connected" and returning — leaving the user stuck. The reuse branch now tears the stale daemon down (reusing the chain-switch teardown) and starts a fresh session; the healthy path is unchanged. TAB_CLOSED_MESSAGE is centralized in sessionConstants and shared by the client and the resolver.
…, vesting wizard (#381)
…on anvil) (#380) Add a headless, deterministic Tier-2 e2e harness that drives the real bridge page (bridgePage.ts) in chromium with an injected mock window.ethereum. eth_sendTransaction is delegated to Node via page.exposeFunction(__glSign), where a viem local account (anvil dev key) actually signs+broadcasts to an ephemeral anvil, so the full loop runs for real: bridge + page JS + session daemon + chain, with zero human/extension. Lanes (all green on anvil): - S1 connect/status/disconnect (descriptor 0600, /api/ping, teardown) - S2 validator-join --wallet browser signs + mines against a recording StakingStub, asserted via real receipt + on-chain callCount - S4 session reuse: two sequential joins over one daemon/tab - S5 config default walletMode=browser; --wallet keystore overrides - S6 user-reject (4001) + tab-closed fail-fast (short env timeouts) Lane B (S3, IC deploy on Docker localnet) is deferred as a nightly follow-up: describe.skip guarded by GENLAYER_E2E_LOCALNET. Prod-safe support changes: GENLAYER_E2E_* env overrides for the timing constants (unset in prod) and a GENLAYER_E2E_NO_OPEN guard so the daemon does not auto-open a system browser under test. New e2e-wallet.yml CI job (informational, not a required gate initially). Playwright specs are kept out of the vitest glob so test:coverage is unaffected.
The `staking validator-deposit` and `staking validator-exit` keystore paths called viem's `walletClient.writeContract` directly. viem then negotiates its own fee/tx-type against the GenLayer consensus RPC, which has no EIP-1559 fee support, so the write fails. Every other staking command (and both `vesting validator` commands) instead go through the SDK's staking client, whose `executeWrite` pins `type: "legacy"` and does manual nonce/gas + sign + sendRawTransaction. Route both commands through `client.validatorDeposit` / `client.validatorExit`, which forward to the ValidatorWallet's own functions (preserving msg.sender == ValidatorWallet on re-entry into Staking) over the correct legacy-tx path. The genlayer-e2e cli-driver hard-skips these two with a "SDK bug" note; that was a misdiagnosis — the SDK actions exist and are correct, the CLI just wasn't using them. Add keystore-path tests asserting both commands call the SDK client and never touch getViemClients.
…token, 0700, url scrub, body cap) (#383) Targeted hardening of the browser-wallet signing bridge per security review; all existing behavior and tests preserved. - Verify signer: reject a wallet result whose from differs from the connected/expected signer, both in the bridge (handleResult) and in browserSend (assertResultSigner); plumb from through sessionClient.waitForTxResult. On accountsChanged the page re-POSTs /api/connected so the daemon stays coherent, and the misleading CLI-will-verify-the-sender copy now matches the enforced behavior. - Host-header validation: reject Host != 127.0.0.1:<port>/localhost:<port> with 403 before route dispatch (validated against the port captured at start() so long-polls flushed during teardown still pass). - Constant-time token compare via length-guarded timingSafeEqual. - Create ~/.genlayer and keystores dir with mode 0o700 + chmodSync. - Scrub the hash token from the URL via history.replaceState. - Cap request bodies at 64KB (413 PayloadTooLargeError). - Warn against ssh -g / GatewayPorts yes / public-interface binding. Existing no-CORS, origin-fail-closed, and token-on-every-route checks are unchanged.
Add a fully non-interactive path to `staking wizard` so every prompt can be supplied by a flag, making it scriptable and e2e-testable. New flags: --non-interactive/--yes, --funding-source, --vesting-contract, --operator, --create-operator, --operator-same, --operator-password, --operator-keystore-out, --amount, and identity flags (--moniker, --logo-uri, --website, --description, --email, --twitter, --telegram, --github). When --non-interactive (or --yes) is set the wizard runs end-to-end with zero prompts, erroring clearly and naming the missing flag when a required choice is absent. The interactive path is unchanged (additive branches only). The browser-wallet owner path works non-interactively too.
…ntity) (#385) Every read command now resolves its default identity through one shared BaseAction.resolveActiveIdentity() helper, so `wallet connect` alone is enough to make bare reads follow the connected wallet — matching how signing already resolves via resolveWalletMode. Precedence (no keystore ever unlocked): explicit address flag (--beneficiary/--validator/--delegator) > --account keystore selection > live browser session > active keystore default > last-resort live session. Routed onto the helper: balances, vesting list, vesting validator list, staking validator-info, staking delegation-info, staking validators ("mine" marking), staking validator-history. Write commands are untouched. account show stays keystore-scoped (it reports keystore-only fields: name, locked/unlocked, active). Also tighten the balances committed-delegation scan: union active + quarantined + banned validators (de-duped) instead of active-only, so committed principal — and thus available-to-stake — is not under-counted for validators that left the active set.
The wizard previously skipped the identity step for vesting-backed validators, only printing a pointer to `genlayer vesting validator set-identity`. It now runs the same guided identity prompts (and --non-interactive flags) on the vesting path and sets identity through the vesting contract's vestingValidatorSetIdentity — the SDK method the standalone command uses — for both keystore and browser owners. No raw-viem bypass. A revert is caught: the wizard warns and still reaches the summary. --skip-identity remains the opt-out; the wallet path is unchanged.
…hrough the SDK (#387) The keystore paths of `staking set-operator`, `staking validator-claim`, `staking set-identity` and `transactions trace` used raw viem `walletClient.writeContract` / `client.request`. viem negotiates its own EIP-1559 fee/tx-type against the GenLayer consensus RPC, which has no EIP-1559 fee support, so those writes fail. This is the same latently-broken class already fixed for validator-deposit/exit. Route each keystore path through the genlayer-js client instead: - set-operator -> client.setOperator({validator, operator}) - validator-claim-> client.validatorClaim({validator}) (surfaces claimedAmount) - set-identity -> client.setIdentity({validator, moniker, ...}); the SDK now owns extraCid encoding (drop the hand-rolled toHex/TextEncoder) - trace -> client.debugTraceTransaction({hash, round}) (typed action, same wire call as gen_dbg_traceTransaction) setOperator/setIdentity exist on the client at runtime (route through the SDK's legacy-tx executeWrite) but are missing from the installed genlayer-js StakingActions .d.ts; a narrow cast bridges that type gap. Browser-wallet paths are unchanged (the bridge signs, so they were already correct). With every caller gone, StakingAction.getViemClients is deleted along with its now-unused viem imports. Add keystore-path tests asserting each command calls the SDK client, and a trace test using debugTraceTransaction.
* refactor(wallet): route browser-wallet writes through the SDK; drop txBuilders Collapse the staking/vesting browser-wallet write lanes onto the same genlayer-js client calls as the keystore lane, now that the SDK's executeWrite handles an Address-only (provider) account by routing eth_sendTransaction through the injected EIP-1193 provider. Each browser branch builds a provider-bound client (getBrowserStakingClient / getBrowserVestingClient / the wizard's getWizardVestingBrowserClient) and calls client.<method>(...) exactly like the keystore lane, replacing the per-command buildTx(...) + session.sendTransaction(rawTx) reimplementation. The hand-rolled calldata layer (txBuilders.ts + the stakingTx.ts shim) is deleted; validatorJoin's SDK path already decodes the ValidatorJoin event and returns validatorWallet for both lanes. Per-tx bridge labels are preserved via session.setNextLabel(label) before each SDK call (consumed by the EIP-1193 shim for the next eth_sendTransaction). The SDK now preflight-simulates and throws before eth_sendTransaction, so the command's failSpinner + session.close() (no-op for a shared daemon) surfaces reverts cleanly and keeps the bridge usable; no doubled preflight. * chore(deps): bump genlayer-js to v2-dev provider-lane build (#200)
The balances command scans the global validator set (active + quarantined +
banned) to compute delegated committed principal per vesting. Those SDK reads
throw on networks with no staking contract ("Staking is not supported on
studio-based networks"), so `genlayer balances` failed outright on studio —
even though wallet balance and vesting totals need no staking data.
Gate the scan on staking availability, mirroring the SDK's own guard (missing
or zero staking address ⇒ unsupported): when absent, use an empty validator set
so delegated principal is 0 (correct — there is no delegation without staking)
and still render wallet + vesting holdings. Self-stake, vested/withdrawable and
available-to-stake all come from vesting/wallet reads and are unaffected.
Fixes genlayer-cli (studio) e2e 090_cli_network_and_balances #2.
…391) After #389 stopped the staking scan from crashing balances on studio, the next consensus-dependent read surfaced: the vesting-factory lookup resolves through ConsensusMain → AddressManager, and on a network without that infrastructure deployed (studio, or a custom profile on a bare RPC) it decodes garbage — `genlayer balances` died with 'Position 32 is out of bounds ... name()' (e2e 090 scenario 2 on the studio stack). Custom networks inherit the base chain's ConsensusMain address even without a --consensus-main override, so a studio profile carries a non-null (localnet) address that simply isn't deployed on the studio RPC — a static check can't tell them apart. Probe eth_getCode(consensusMain) once up front: if empty, the consensus infra isn't deployed, so skip the whole consensus-dependent section (vesting + staking) and render the wallet balance only, with a clear note. Handles the entire missing-infra class in one capability check rather than one revert at a time. dev-env (real deployed address) is unaffected. Verified against a live studio stack: e2e 090 (both scenarios) green. 772/772 unit tests pass.
* fix(account): clear error for password prompts with no TTY
Running e.g. `genlayer account export --password <pw>` without --source-password
in a non-interactive context (piped stdin, CI, automation) died with
`ExitPromptError: User force closed the prompt` — inquirer's message for a
force-closed prompt, which reads exactly like the user hit Ctrl-C when in fact
a required flag is missing.
Guard promptPassword with assertInteractive(): when stdin is not a TTY, throw an
actionable error naming the flag(s) that make the command non-interactive
(--source-password / --password) instead of letting inquirer force-close. export
and import pass contextual hints. confirmPrompt is intentionally left alone
(its callers gate on their own --yes/--overwrite flags).
Adds non-TTY coverage in baseAction.test.ts; existing prompt tests simulate a
TTY (vitest stdin is not one).
* fix(account): catch inquirer force-close instead of guarding on isTTY
The first cut guarded promptPassword with !process.stdin.isTTY, which was too
blunt: it rejected PIPED stdin outright. But piped stdin is exactly how the e2e
harness (and real automation) supply the keystore password — so `genlayer
deploy` and every keystore-unlock path started failing with 'Maximum password
attempts exceeded' in CI (isTTY is false for a pipe, so the guard threw before
inquirer could read the piped password).
Instead, let inquirer run and catch its ExitPromptError ('User force closed the
prompt') — which only fires on an ACTUAL force-close (no TTY AND no piped
input). Rewrite that into the actionable, flag-naming message. Piped stdin now
reaches inquirer normally; the clear error still appears when there is genuinely
no input.
Verified against a keystore: piped source-password exports OK; empty stdin
yields 'No interactive terminal available for a password prompt. Pass
--source-password ...'. 774/774 unit tests.
…392) The bridge page grabbed the single injected `window.ethereum`, which is ambiguous when several wallet extensions are installed. Adopt EIP-6963: on load dispatch `eip6963:requestProvider` and collect each `eip6963:announceProvider` ({info:{name,icon,rdns,uuid}, provider}) into a list; route every provider call (eth_requestAccounts / eth_chainId / wallet_switch|addEthereumChain / eth_sendTransaction / accountsChanged) through one selected active provider. - 1 wallet → auto-select; >=2 → hand-rolled picker (icon+name); 0 announced → fall back to window.ethereum (older wallets), else a clear no-wallet message. - Also subscribe chainChanged (page only heard accountsChanged) to warn on a mid-session network switch; ensureChain still corrects it before each tx. - Transport / session daemon / /api contract untouched. - Playwright mock now announces via eip6963:announceProvider (keeps window.ethereum too) so the Tier-2 lanes discover it. Verified: Tier-2 browser-signing suite 9 passed / 1 skipped (nightly deploy).
…ng the base explorer (#393) A custom network profile inherited its base chain's block explorer (e.g. a pre-clarke profile based on bradbury showed explorer-bradbury.genlayer.com in `network info`) — misleading, since that explorer never indexed the custom deployment's transactions. - Add `--explorer <url>` to `network add` (validated http(s); stored on the profile as overrides.explorer). - applyCustomNetworkProfile no longer inherits the base explorer for custom profiles: it sets blockExplorers ONLY from --explorer, otherwise blanks it. Built-in networks are unaffected (they don't go through this path). Also fixes two pre-existing tsc errors in the touched files (a narrowing cast in parseDeploymentObject; a spy typing in the getNetwork test). Tests: 778 passed (+3 explorer cases: stores+applies override, no-inherit default, invalid-URL rejection).
…view (#394) * feat(staking): warn/block validator self-stake below on-chain minimum Add a shared self-stake eligibility gate (BaseAction.assertOrWarnSelfStakeMinimum) used by the four validator write commands (staking validator-join/deposit, vesting validator-create/deposit). Eligibility counts self-stake only; the minimum is read from getEpochInfo().validatorMinStakeRaw (never hardcoded) and mirrors the wizard's epoch-0 carve-out. Below-min blocks by default and is overridable with --force; epoch-0 and eligible cases only inform. Add liquid/vesting mixing hard-guards (no override) to the two deposit commands: staking validator-deposit fails when the wallet is owned by a vesting contract; vesting validator-deposit fails when the wallet was not created by the vesting contract (isValidatorWallet). Both would revert on-chain otherwise. Add informational source notes on join/create, --force flags on the four write commands, and a best-effort primed-below-minimum note on validator-prime. Adjust existing action/command tests to mock getEpochInfo/getValidatorInfo for the new pre-submit checks (min driven from mocked epochInfo, not hardcoded) and make the test parseStakingAmount mock exact at the integer-GEN boundary. * test(staking): cover self-stake minimum gate, mixing guards, and clean validator-info view Add unit tests driving the minimum from mocked epochInfo (never hardcoded): below-min blocks without --force; --force proceeds with a warning; epoch-0 does not block; deposit counts still-pending self-stake toward the resulting stake; liquid-into-vesting and vesting-into-liquid mixing hard-blocks (not overridable); validator-info --json returns the raw object; the clean grouped view keeps load-bearing values (addresses, GEN amounts, moniker, 'Not banned', live boolean) as plain substrings and never relabels live as 'active'; display-only below-min warning never blocks; pending-deposit crossing shows the activation epoch. * fix(staking): min-stake preflight degrades if the chain can't report the minimum The self-stake below-minimum check called getEpochInfo() unconditionally in the validator-join/deposit + vesting create/deposit preflights. On a chain that can't report epoch/minStake (a minimal or stub staking contract, or a transient read failure) that read reverts and HARD-FAILS the write — e.g. the Tier-2 browser-signing lanes join against a StakingStub without epoch(), so validator-join died with 'The contract function "epoch" reverted'. Wrap the getEpochInfo() read: on failure, skip the (advisory) min check and proceed, rather than blocking the operation. The mixing guard (owner / isValidatorWallet) stays a hard, always-enforced check. Also pass --force on the Tier-2 lanes' deliberately-sub-minimum test joins. Verified: Tier-2 browser-signing suite 6/6 green; 786 unit tests pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Standing release-gate PR:
v0.40-dev→v0.40This is a long-lived gate PR, not a normal feature PR. Do not merge ad-hoc.
Purpose
v0.40-devis the active integration branch for the v0.6 fee work. Individual PRs merge intov0.40-devand only need to pass normal repo CI — they are not blocked on the full cross-repo release train being green.v0.40-devis promoted tov0.40. It waits for cross-repo E2E (the release train) to go green before it is merged.v0.40-dev.How to use
v0.40".(Mirrors genlayerlabs/genlayer-consensus#1070.)