Release gate: v2-dev → v2 (waits for cross-repo E2E) - #188
Conversation
* Release v1.1.7 [skip ci] * ci: scope down release/sync-docs to dedicated GitHub Apps (#168) Replace the over-privileged shared CI bot with two dedicated, minimally scoped GitHub Apps gated behind protected environments. Mirrors the same change in genlayer-testing-suite#78 and genlayer-cli#297. - publish.yml: rename environment npm -> Publish, switch from tibdex/github-app-token@v1 (archived) to actions/create-github-app-token@v3 with vars.PUBLISH_CI_APP_CLIENT_ID + secrets.PUBLISH_CI_APP_KEY. - sync-docs.yml: bump create-github-app-token to @V3, switch to client-id, gate behind the Sync-docs environment with vars.DOCS_SYNC_APP_CLIENT_ID + secrets.DOCS_SYNC_APP_KEY. Each App should be installed only on the repos it needs (Publish: this repo only; Sync-docs: this repo + genlayer-docs) with Contents: read & write as the only permission. * fix(ci): add trigger comment to publish workflow (#170) No-op patch release to verify the publish pipeline end-to-end after the env scope-down (#168) and npm trusted publisher update. * Release v1.1.8 [skip ci] * feat: branch-per-major release model (#172) Aligns this repo with genlayer-node + genvm: branches per major (v1 today; v2 / v2-dev when fees lands), no main, deliberate releases via script + skill. What changes: - publish.yml fires on `push: tags v*` instead of `push: main`. The workflow no longer bumps versions — it just sanity-checks that the tag matches package.json, builds, publishes to npm, and creates the GitHub Release. No commits back to the branch. - scripts/release.sh is the new release entry point. Pre-flight checks: on a v<major> branch, clean tree, in sync with origin, latest CI green. Refuses major bumps without --allow-major (those need a new branch in this model). Then runs release-it with an explicit version to bump package.json, prepend CHANGELOG.md, commit, tag, push — but with npm/github plugins disabled so the dev machine doesn't need npm auth and the GH release is cut by CI from the tag. - .claude/skills/release/SKILL.md documents the flow for Claude: when to use it, what to confirm with the user, what to refuse, and the roll-back path (deprecate, never unpublish blind). - CONTRIBUTING.md explains the branch model + points at the skill. - npm run release → ./scripts/release.sh so the convenience script routes through pre-flight checks (the previous `release-it --ci` invocation bypassed every guard). - .github/e2e-track on this branch points at runner track v0.5 (current stable). v2-dev (when it exists) will point at v0.6-dev. Why kill auto-bump: - Twice landed accidental majors when a BREAKING CHANGE footer in a PR body triggered the conventional-commit bump algorithm — once on this repo (0.28.7 → 1.0.0), once on genlayer-testing-suite (yanked v1/v2 tags). The whatBump cap mitigated future jumps but didn't address the underlying issue: shipping a release shouldn't be a side-effect of merging a PR. - The new flow puts a human checkpoint between "code lands" and "users get it" without losing the bump-and-tag automation. Follow-up (separate PRs): - After this lands: switch default branch on github.com from main to v1, then delete origin/main. - Same shape applied to genlayer-py, genlayer-cli, genlayer-testing-suite, genlayer-explorer (per their existing major). - Runner matrix updates: matrix/v0.5.yaml tooling refs switch from tag pins to branch pins (genlayer-js: v1). * fix: run CI on v1 branch (#173) * fix: run CI on v1 branch * fix: sync testnet consensus abi * ci(workflows): sync e2e.yml from genlayer-e2e (#174) Co-authored-by: ci-core-e2e-runner[bot] <263344042+ci-core-e2e-runner[bot]@users.noreply.github.com> * ci(workflows): sync e2e-housekeeper.yml from genlayer-e2e (#175) Co-authored-by: ci-core-e2e-runner[bot] <263344042+ci-core-e2e-runner[bot]@users.noreply.github.com> * ci(workflows): sync e2e.yml from genlayer-e2e (#176) Co-authored-by: ci-core-e2e-runner[bot] <263344042+ci-core-e2e-runner[bot]@users.noreply.github.com> * ci(workflows): sync e2e-housekeeper.yml from genlayer-e2e (#177) Co-authored-by: ci-core-e2e-runner[bot] <263344042+ci-core-e2e-runner[bot]@users.noreply.github.com> * feat: add v0.6 fee-aware transactions * fix: handle studio raw transaction hashes * fix: align studio fee floor fallback * fix: satisfy fee estimate typecheck --------- Co-authored-by: github-actions[bot] <github-actions[bot]@genlayer.com> Co-authored-by: Rafał Rabenda <99408377+rrabenda@users.noreply.github.com> Co-authored-by: ci-core-e2e-runner[bot] <263344042+ci-core-e2e-runner[bot]@users.noreply.github.com>
|
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: Team 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 |
… enum completeness (#187) * feat(fees)!: estimation correctness, wait-for-decided semantics, v0.6 enum completeness Estimation (designer rulings): - receipt gas price: max(quoteGasPrice(), eth_gasPrice) — quoteGasPrice() reads tx.gasprice which is ~0 under plain eth_call on chain-derived networks; a zero effective price on an enabled policy now throws instead of producing a zero cap (consensus rejects those with FeeValueMustBeNonZero(6)) - executionBudgetFloor: max(on-chain view, local recompute at effective price) — the on-chain view multiplies by the same ~0 quoteGasPrice; local formula pins estimateProposeReceiptGas(MIN_RECEIPT_BYTES=512) = 306,192 gas - simulation-derived budgets no longer clobbered by the blanket default: recommended = max(floor, observed × headroom); DEFAULT_TRANSACTION_EXECUTION_GAS (100M) applies only to the no-simulation fallback, exported with provenance + TODO(data) Wait semantics (designer: 'wait for decided', legacy naming was wrong): - waitUntil: 'decided' | 'finalized' replaces status; legacy status param maps ACCEPTED→decided / FINALIZED→finalized with one-time deprecation warning - new isSuccessful(tx): status ∈ {ACCEPTED, FINALIZED} && FINISHED_WITH_RETURN v0.6 ABI completeness: - status 14 LEADER_REVEALING; VoteType 3 TIMEOUT / 4 NONDET_DISAGREE; ResultType remapped to v0.6 (dead v0.5 6/7 entries removed) - revert selectors: BudgetTooLow, RollupBudgetBelowFloor, FeeValueMustBeNonZero - DEPLOY_CALL_KEY = bytes32(1) (deploy callKey sentinel, pending GenVM emit); fullTransaction/waitUntil added to ITransactionActions * ci: restore unit-test and chains-drift CI for the v2 line; add prepare script - test.yml and chains-drift.yml only triggered on v1 — PRs to v2-dev ran nothing but the smoke workflow, so the entire fee rework would have merged with zero unit-test CI (lost in the branching-strategy transition) - prepare script (npm run build) makes git-ref installs usable: dependents (genlayer-cli CI) can pin github:genlayerlabs/genlayer-js#<branch> until the v2 npm release exists
|
/run-e2e all |
…y key = bytes32(0) (#190) The wildcard sentinel previously shared bytes32(0) with GenVM's natural empty-method-name key (deploy, emit_transfer), making deploy-specific Mode-2 allocations impossible. Per cross-team decision, the wildcard moves to the untagged keccak256 of empty bytes (0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470), which is provably outside the derived-key space. DEPLOY_CALL_KEY drops the bytes32(1) sentinel (GenVM ask withdrawn) and now equals CALL_KEY_UNNAMED = bytes32(0), matching what GenVM actually emits. Must land together with the consensus CALL_KEY_WILDCARD constant and the node Mode-2 tree decode change in v0.6.
|
/run-e2e all v0.6 |
6 similar comments
|
/run-e2e all v0.6 |
|
/run-e2e all v0.6 |
|
/run-e2e all v0.6 |
|
/run-e2e all v0.6 |
|
/run-e2e all v0.6 |
|
/run-e2e all v0.6 |
|
/run-e2e v0.6-dev all force |
|
/run-e2e v0.6 all force |
* feat(sdk): developer-NFT read/claim actions Expose the on-chain developer-NFT reward flow: getDeveloperNft, getClaimableRewardsFromFees, getClaimableRewardsFromInflation, claimNft rewards/epochs. NFTMinter ABI + AddressManager-based address resolution. Mirrors existing action patterns; unit tests added. (Codex-generated; build + 90 tests green. Base is fix/callkey-wildcard-sentinel — rebase onto v2-dev before PR.) * fix(nonce): coerce eth_getTransactionCount hex string to number getCurrentNonce returned the RPC's hex quantity string while the declared return type (and callers) expect a number. On the local-account signing path the raw string flows into viem's transaction serializer, which encodes the ASCII characters as the nonce bytes — producing an unmineable transaction and an infinite receipt wait. Affects any local-account consensus call (appeals, top-ups) on network backends.
* feat: add vesting delegator actions * feat(vesting): validator-leg actions (join/deposit/exit/claim, operator transfer, identity, wallet reads) Implements the CON-607 Vesting.sol validator surface (consensus branch con-607-vesting-lock-then-validate): vestingValidatorJoin/Deposit/Exit/ Claim, operator-transfer initiate/complete/cancel, setIdentity, and getValidatorWallets/validatorWalletCount/validatorDeposited/ isValidatorWallet reads. Replaces the #1154 stub in vesting/validator.ts.
…ke) (#194) * feat: getCurrentEpoch + isValidatorBelowMinStake epoch-zero helpers Convenience reads for epoch-0-aware tooling: getCurrentEpoch() (bootstrap epoch where min stake is not enforced) and isValidatorBelowMinStake() so callers can present a below-min validator as bootstrapping vs faulted while epoch is 0. Additive; existing signatures unchanged. * fix(types): add epoch-zero fields to ValidatorInfo getValidatorInfo returns currentEpoch, validatorMinStake, validatorMinStakeRaw, and belowMin, but the ValidatorInfo interface was never extended to include them, breaking the dts build (TS2353) and failing the drift, test, and smoke checks.
This file is a temporary artifact emitted by `vitest --typecheck` and deleted/regenerated on each run, so committing it produced spurious working-tree changes on every test run. It was accidentally committed in #145 (34de39f). Remove it from tracking and ignore `tsconfig.vitest-temp*.json` so the tree stays clean.
#200) B1: executeWrite in staking + vesting now branches on account.type === "local". The local-key path (getTransactionCount -> prepareTransactionRequest -> sign -> sendRawTransaction) is unchanged and stays the regression gate. Address-only / injected-provider accounts route through client.request eth_sendTransaction (wallet manages nonce + signing), mirroring the proven IC provider lane in src/contracts/actions.ts. Shared preflight simulate + gas estimate and the post-mine receipt wait / revert diagnostics are unchanged around the branch. B2: new accountActions.transfer({to, value}) for native GEN transfers, local-key only, mirroring the executeWrite local lane. accountActions now takes publicClient. Unblocks CLI browser-wallet writes routing through the SDK.
…ng) (#192) The v0.6 genvm-manager renamed the method-call calldata map key from "method" to "" (empty string). The empty key sorts first in canonical calldata map encoding (keys ordered by unicode-codepoint array; the empty array precedes any non-empty key), so the method name becomes a prefix of the encoded binary calldata. makeCalldataObject now writes the method name under ret[""]. The decoder is unchanged: it builds a generic Map from raw key bytes and already reads whatever key is present. No back-compat toggle — there is no calldata versioning mechanism in this repo, so this is a hard cutover. The genvm-manager still auto-remaps "method" -> "" but logs an error each time; this migration stops that error. BREAKING CHANGE: method-call calldata now uses the "" key instead of "method". Encoded bytes for every write/read/view/schema call change. Peers must run the v0.6 genvm-manager (or a node/studio build that expects the "" key).
* fix: support proof-bearing vesting validator joins * fix: require operator proofs for validator joins
…mpanions) (#208) * Reapply "fix: support proof-bearing validator joins (#206)" (#207) This reverts commit 7666f0b. * feat(staking): support the two-step operator rotation CON-715 removed ValidatorWalletBlueprint.setOperator in favour of initiateOperatorTransfer + completeOperatorTransfer. The SDK only spoke the single-call surface, so rotating an operator against a consensus deployment that carries the change fails: the selector no longer exists, so viem reverts with no reason and the Python SDK reports ABIFunctionNotFound. Adds both calls plus cancelOperatorTransfer and the getPendingOperator view, and keeps setOperator for deployments that still expose it. The proof binding is the subtle part. validatorJoin's possession proof is verified by the ValidatorWalletFactory and is bound to it; rotation is verified by the wallet, so the registrar is the wallet's own address (PubKeyUtils.validateWithPossession(pubKey, address(this), owner(), proof)). createOperatorRegistration already takes the registrar as a parameter, so no change was needed there — but a join proof silently fails to verify for a rotation, which is the easy mistake to make. getOperatorTransferContext builds the wallet-bound context and reads owner() from the wallet rather than assuming the caller is the owner, so a mismatch surfaces as a clear local error instead of an onlyOwner revert. A test pins that a join-bound proof does not verify for rotation while a wallet-bound one does. Callers still need updating; this only adds the surface they need: genlayer-e2e's driver interface, genlayer-cli's staking set-operator, and genlayer-py's staking_set_operator all remain single-call. Verified: npx tsc --noEmit, npm test -- --run (123/123, no type errors), npm run build. * fix(staking): resolve the Claim/Commit layout from the chain CON-715 widened both staking structs — Claim gained `offset`, Commit gained `outstanding`/`priced`/`fragmented` and narrowed several members — while keeping the same function names and arguments. Static tuples decode positionally, so reading a post-CON-715 chain with the shape in STAKING_ABI does not fail: `commit.input` picks up `claim.commit`. Pending deposits came back as small indices instead of amounts, which is why the e2e delegator scenarios asserted 0.1 GEN and got "0" or "2". Both layouts are deployed — consensus v0.6-dev has the old one, #1290 the new — so neither shape can simply replace the other. STAKING_COMMIT_VIEWS_CURRENT_ABI adds the post-CON-715 shape for the four struct-returning views, and readCommitView resolves which one the chain speaks, once, then caches it for the client: getStakeInfo loops over every pending entry and must not re-probe per entry. The probe only works in one direction, and the tests pin that: decoding a legacy response with the current shape throws (the response is short), while decoding a current response with the legacy shape succeeds and lies. So the current shape is always attempted first and a decode *failure* identifies a legacy chain. If that asymmetry ever breaks, the probe would quietly start reporting wrong balances again — hence the test asserting the exact misread value rather than merely "not equal". Also drops duplicate ValidatorWalletBlueprint entries: the ABI already carried initiateOperatorTransfer/completeOperatorTransfer/cancelOperatorTransfer/ getPendingOperator, with initiateOperatorTransfer declared as taking an address rather than the pubkey and possession proof it actually takes. Verified: npx tsc --noEmit, npm test -- --run (126/126, no type errors), npm run build. --------- Co-authored-by: kirilaa <kirilantevski@gmail.com>
* fix: read the consensus surfaces the resolution-kernel train exposes
Four entrypoints the SDK calls were removed or reshaped by the train. The
first one is what fails in E2E today, on the dev-env stack, before any
scenario completes:
getTransactionData(txId, timestamp) -> not found on ABI
That read answered with a projection evaluated at a caller-supplied clock.
The train splits the two apart: the stored record is
getStoredTransactionData(txId) and the projection lives behind
getTransactionLifecycle. Chains upgrade independently, so the transaction
read now picks whichever the chain's OWN abi offers rather than assuming --
the dev-env harness builds its chain from the live deployment, so its abi is
the train's while a testnet's is not.
The stored record renames three fields. The decoder already normalised two of
them (txData/txCalldata, numOfInitialValidators/initialRotations); it now
also accepts observedAt for currentTimestamp, which it reads unconditionally,
so an unrecognised name was a crash rather than a missing field.
activeValidators() is gone because committee capacity is 1,543 and an
address[] that long overruns the return-size limit. The joined registry is
paged instead: read validatorsJoinedCount() first so a registry that grows
underneath the walk cannot spin the loop, then getValidatorsJoined 64 at a
time -- the size the paged reads are written around, and the one
genlayer-node uses for the same walk. activeValidatorsCount() went with it,
which took all of getEpochInfo() down with a single read.
validatorView lost its registry tree pointers (left, right, parent), so the
struct is resynced to the nine fields the train returns; both call sites read
it by name, so nothing else moves. validatorsRoot() is removed from the abi
as well -- nothing here calls it, and leaving a declaration for a function
that no longer exists only invites a revert.
The status map is renumbered with them. ReadyToFinalize stopped being a
stored status -- readiness is the resolution kernel's verdict now -- and
removing it at ordinal 11 shifts the three above it down. Left alone, a
ValidatorsTimeout transaction decoded as "READY_TO_FINALIZE": no error, just
the wrong answer. The enum member stays, because the node still reports that
state; what changed is that no chain value decodes to it, so the name->number
map is now partial and says so in its type.
The repo's own tests pinned the old numbering and are re-pointed to the
train's.
* fix: resync validatorViewPrimed and validatorViewPrePrimed
The earlier commit resynced validatorView and missed its two siblings, which
return the same struct and drifted the same way: the train dropped the
registry tree pointers (left, right, parent), so a twelve-field decode runs
off the end of a nine-field return.
It surfaces as a bounds error rather than a wrong value --
"Position 319 is out of bounds (0 < position < 288)" from
validatorViewPrimed -- because the reader walks past the encoded tail.
* fix: align SDK consumers with train contracts
* fix: route studio chains around the train lifecycle reads
The train adaptation made the appeal and finalization actions read
getTransactionLifecycle from ConsensusData unconditionally. Studio chains
(localnet, studionet) run the studio-embedded consensus, which predates the
train: that ConsensusData exposes no getTransactionLifecycle and no
estimateLatestAppealCharge, so the read decodes a short tuple and viem raises
"Position 63 is out of bounds (0 < position < 32)".
This failed the studio suites of e2e run 33112369501, including
051_fee_lifecycle.feature ("submitAppeal records an appeal bond without
expanding the primary fee budget").
Guard each affected action on client.chain.isStudio before any train read,
mirroring transactionActions.getTransaction, and reinstate the pre-train call
shape recovered from the v2-dev base:
- appealTransaction encodes submitAppeal(bytes32) against the chain's own
ConsensusMain ABI, with the caller's value defaulting to zero. The base
resolved the value through FeeManager/RoundsStorage, both of which carry no
address on studio, so it already degraded to zero there.
- topUpAndSubmitAppeal encodes topUpAndSubmitAppeal(bytes32, FeesDistribution)
without a decision id.
- finalizeTransaction encodes finalizeTransaction(bytes32).
- getAppealCharge and getMinAppealBond report the missing quote surface, which
is the error the base already raised on studio.
- resolveTransactions and finalizeDecisions are train-only additions with no
base equivalent, so they report the gap and point at finalizeTransaction.
canAppeal and the rounds-storage reads already returned their base error on
studio: their missing-contract guards run before any train read, so they are
left alone. finalizeIdlenessTxs keeps its migration error; studio's
ConsensusMain ABI never carried that entrypoint, so the base did not support it
there either.
The train path is unchanged for non-studio chains.
* feat(transactions)!: layer consumer and protocol lifecycles
Keep the default transaction model on materialized state with a small discriminated lifecycle and stored-state wait helpers. Move projection, resolution source/action, and active decision identity behind the advanced namespace using one normalized schema on both Studio and contract networks.\n\nRemove ReadyToFinalize and duplicate canFinalize exposure. Finalize remains a protocol action.\n\nValidation: npm test -- --run; npm run lint; npm run build; npm run docs; git diff --check.
* fix(staking): restore complete public action surface
* fix(types): stop merging unrelated transaction results
* fix(appeals): bind Studio calls to decisions
* fix(appeals): use train ABI on Studio networks
* fix(lifecycle): fall back to the Studio status when the RPC is absent
The train surface routes Studio chains through the node RPC
gen_getTransactionLifecycle: advanced.getTransactionLifecycle reads it
directly, and _readLifecycleIdentity reads it for every appeal and
finalization. The genlayer-studio backend deployed from studio main does
not implement that method, so viem raises MethodNotFoundRpcError and the
whole flow dies before it reaches anything the transaction can answer.
Keep the node-RPC-first design and degrade only when the endpoint says it
does not implement the method (viem's typed error, JSON-RPC -32601, or a
message that reports the gap in text). The fallback synthesizes the
lifecycle from the Studio consumer surface the SDK already uses,
client.getTransaction, which proves the exact stored status and nothing
else: the projection repeats the stored status, the resolution is
NoOp/Unspecified, and the decision identity stays inactive. When the
status cannot be read, the original RPC error is re-raised rather than
reported as a lifecycle.
_readLifecycleIdentity returns the same conservative identity, so a
Studio chain without the RPC reports no decision instead of a fabricated
one. A fabricated decision id would be signed into submitAppeal or
finalizeTransaction as expectedDecisionId, and every consumer of that
identity refuses to act on an inactive decision, which is the honest
outcome for a decision the SDK cannot read.
This unblocks the Studio flows that never touch a decision. It does not
make the four appeal scenarios of 051_fee_lifecycle pass: those need a
real active decision id on Studio, which only the backend can supply.
* fix(studio): preserve the current consensus surface
* fix(fees): fund default consensus rotations in estimates
---------
Co-authored-by: Edgars <edgars@genlayerlabs.com>
Co-authored-by: kirilaa <kiril@yeager.ai>
* fix(fees): mirror consensus deposit quote * fix(studio): bind decision lifecycle actions Use Studio's lifecycle and appeal-charge RPCs to bind appeal, combined top-up/appeal, and finalization to the active decision. Keep Studio ABI exports aligned with the v0.6 consensus surface and cover quoting and guarded selectors. * fix(fees): encode internal message price caps * fix(studio): surface mined envelope reverts * fix(fees): encode schedule-free topups * fix(transactions): discover message payment events * test(appeals): lock configured contract routing Verify that canAppeal reads the active decision from the separately configured Appeals contract at the lifecycle snapshot. This catches local-chain interface drift before cross-stack E2E. * fix(appeals): admit unfunded rounds safely Route the public appeal action through topUpAndSubmitAppeal, whose consensus-derived schedule extension is valid for both pre-funded and unfunded rounds. Keep the direct submitAppeal selector out of the high-level path so a full bond-plus-funding quote cannot still fail AppealRoundNotPermitted.\n\nCover quoted and caller-provided values on deployed Consensus and Studio, and refresh generated API docs.
Add the dedicated Studio dev chain preset and make prerelease npm publication use a non-latest channel.
Remove the CI-suppression marker from release commits and directly test the real release configuration against GitHub skip directives.
Standing release-gate PR:
v2-dev→v2This is a long-lived gate PR, not a normal feature PR. Do not merge ad-hoc.
Purpose
v2-devis the active integration branch for the v0.6 fee work. Individual PRs merge intov2-devand only need to pass normal repo CI — they are not blocked on the full cross-repo release train being green.v2-devis promoted tov2. It waits for cross-repo E2E (the release train) to go green before it is merged.v2-dev.How to use
v2".(Mirrors genlayerlabs/genlayer-consensus#1070.)