feat: add frame accounts, transaction preparation and account-aware signing - #24
Conversation
Align the type 0x06 envelope, RPC parsing, and gas accounting with the current EIP-8141 format. Add FrameAccount, EOA preparation and signing helpers, an ETH transfer example, tests, and updated documentation. BREAKING CHANGE: FrameTransaction now uses a scalar nonce. The EIP-8250 nonceKeys/nonceSeq and EIP-8272 recentRootReferences fields and related APIs have been removed.
feat!: support scalar-nonce frame transactions and EOA accounts
There was a problem hiding this comment.
thanks @nikhilkumar1612!
src/rpc.ts reads the frame field as stateLimit. That is what chain 8141 served when test/fixtures/chain/ was captured, but both live chains now serve stateGasLimit — so BigInt(undefined) throws, and getFrameTransaction (src/viem.ts:52, the first example in the README) is unusable against rpc1.frames.ethrex.xyz.
The hermetic suite can't see it: GOLDEN_RPC_JSON is hand-written and uses the same old name, so the fixture and the code agree with each other and disagree with the node.
Reproduce
import { parseRpcFrameTransaction } from './src/rpc.js'
const r = await fetch('https://rpc1.frames.ethrex.xyz', {
method: 'POST', headers: { 'content-type': 'application/json' },
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_getTransactionByHash',
params: ['0x7f1793de1f93d259ef353252da7f70a7e3cea5a2dc132c36092be1e14360f3f6'] }),
})
parseRpcFrameTransaction((await r.json()).result)
// TypeError: Invalid argument type in ToBigInt operation <- f.stateLimit is undefinedFix
src/rpc.ts is outside this PR's diff hunks, so GitHub won't let me leave an applyable suggestion there — patch inline:
@@ -22,7 +22,8 @@ export type RpcFrameTransaction = {
gasLimit: Hex
- stateLimit: Hex
+ /** The node calls this `stateGasLimit`, not `stateLimit`. */
+ stateGasLimit: Hex
value: Hex
@@ -112,7 +113,7 @@ export function parseRpcFrameTransaction(json: RpcFrameTransaction): FrameTransa
target: f.to === null ? null : getAddress(f.to),
- limits: { execution: BigInt(f.gasLimit), state: BigInt(f.stateLimit) },
+ limits: { execution: BigInt(f.gasLimit), state: BigInt(f.stateGasLimit) },
value: BigInt(f.value),GOLDEN_RPC_JSON has an applyable suggestion below.
Please leave test/fixtures/chain/*.json on stateLimit — they are records of what the old node actually served, and only their simulate block is read now.
Evidence that the rest of the envelope work is correct
With only that rename applied, feeding the node's JSON verbatim through this branch across 22 live type-0x6 transactions on chain 81410:
| check | result |
|---|---|
keccak256(encodeFrameTx(parseRpcFrameTransaction(json))) == node hash |
22 / 22 |
decodeFrameTx(encodeFrameTx(tx)) round-trip |
22 / 22 |
empty-msg secp256k1 signatures recovering to their resolved signer |
23 / 23 |
receipt.gasUsed == intrinsic + Sum(frame gasUsed) + Sum(frame stateGasUsed) |
5 / 5 exact |
So the seven-field envelope, the nested fees, the nested [execution, state] limits and the sig-hash elision are all right against real chain data — this one field name is what stands between the branch and working end to end.
Worth capturing a handful of these as fixtures to replace the retired test/fixtures/chain/ set: an eth_getLogs sweep from block 0 finds them in one call, and that oracle is exactly what would have caught this.
|
🎉 This PR is included in version 0.2.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
What this changes
Updates
frametx-kitto use the current seven-field EIP-8141 transaction envelope with a scalar nonce. It also introducesFrameAccount, EOA transaction preparation and account-aware signing APIs.Why
The previous implementation targeted the superseded composed envelope containing EIP-8250 keyed nonces and EIP-8272 recent-root references. The current Ethrex Frames devnet uses a scalar nonce and no recent-root envelope field.
This also adds a higher-level workflow for constructing EOA frame transactions without manually assembling validation frames, execution frames, nonce values, fees, and signatures.
How the new behaviour is pinned
Which oracle covers this change, and why it could actually fail:
frame_tx_wire_tests.rsmaxCost, or thegasUseddecomposition)The updated scalar-nonce envelope and signature hash are pinned against Ethrex's golden wire vector. The test fails if field ordering, scalar encoding, fee nesting, signature elision, or the resulting transaction bytes change.
The new account helpers are additionally covered by focused tests for nonce retrieval, frame construction, gas-limit mapping, fee preparation, sender matching, and signature recovery.
Load-bearing rules
Did this touch anything under "Rules that are not style preferences" in
CONTRIBUTING.md, such as a derived gas constant, a golden vector, a captured fixture, the module import order, orencodeFrameTxvalidating?This intentionally replaces the superseded nine-field composed envelope with the current seven-field Ethrex envelope: