Skip to content

feat: Read State - #300

Merged
0xNilesh merged 79 commits into
developfrom
feat/read-state
Aug 19, 2026
Merged

feat: Read State#300
0xNilesh merged 79 commits into
developfrom
feat/read-state

Conversation

@Aman035

@Aman035 Aman035 commented Aug 7, 2026

Copy link
Copy Markdown
Member

Summary

Brief description of changes.

References

  • Fixes #
  • Related issue/PR:

Changes

Testing

  • go test ./...

Checklist

  • Ready for review
  • Docs updated (if applicable)
  • Env vars updated (if applicable)

Aman035 and others added 30 commits July 28, 2026 16:59
- decode EvmQueryEnvelope (AccountBalance / ERC20Balance / ContractCall /
  StorageSlot) and SolanaQueryEnvelope (LamportBalance / SPLTokenAccount /
  RawAccountData) from ReadSpec.query
- ExecuteRead on each chain client: deterministic pinned-height queries,
  canonical result encoding for identical-bytes ballot voting
- new RPC methods: GetBalanceAt / GetStorageAt / GetHeaderByNumber (EVM),
  GetBalanceWithSlot / GetAccountInfoWithSlot (SVM)
- pushwatcher listener polls pending reads via gRPC and routes each
  READ_REQUEST event into the target chain's DB (Chains.GetStore)
- EventProcessor gains a READ_REQUEST branch: execute on the chain's own
  reader -> vote -> COMPLETED; corrupt/expired -> REVERTED; transient -> retry
- signer narrowed to consumer-side VoteSigner interface; evm/svm clients
  pass themselves as reader
- implementation plan in docs/read-from-chains-implementation-plan.md
Chain clients own writes to their DB: pushwatcher resolves the target
client via GetClient (same as tss consumers) and hands it the event,
instead of writing into the chain's store directly. Drops the
ExternalChainStoreResolver interface and the chainDBs bookkeeping in
the chains manager.
ChainReader moves next to its consumer (EventProcessor), the uint256
canonical encoder moves to types.go, and the near-no-op bytes32 encoder
is inlined at its single call site.
All query types plus failure modes: eth_call revert, invalid envelopes,
transient RPC failures, slot/height constraints, SPL account validation.
compat/orm-api/module matched the module-dir scan, so it was moved into
api/ and removed, breaking the go.mod replace and failing go mod tidy.
Generated with spawn, ORM scaffolding removed to match the other modules
which all use collections. Genesis moved to keeper/genesis.go.
UniversalRead, ReadRequest and ReadResult. ReadRequest mirrors the fields
universalClient expects; ReadResult carries no error text so ERROR ballots
converge. Aggregates reserved for v2 median support.
Aman035 and others added 28 commits August 7, 2026 13:41
Add a ucallback query client to pushcore and wire GetAllPendingReadRequests
to AllPendingReadRequests, unwrapping UniversalRead.Request from the response.
Covered by TestClient_GetAllPendingReadRequests.
Swap the temporary uread types for the generated ucallback types across
the read listener, parser, and event processor. Key the read event on the
on-chain requestId directly (a unique incrementing nonce) instead of
hashing it. Adds TestConvertReadRequestEvent.
Adopt the generated ucallback ReadRequest/ReadResult across the evm, svm,
and web2 read executors and the shared ReadRequestHandler interface, with a
common NewReadErrorResult helper for votable ERROR observations.
The read path now uses the generated x/ucallback types, so the uread
proto-mirror is no longer referenced.
Classify contract reverts instead of treating every failure as terminal. A revert
rolls the whole tx back, so the deposit stays escrowed — retiring the record there
dropped it from PendingByExpiry and left nothing able to release the funds.

Also switches the expiry retry budget to an explicit counter rather than len(PcTx),
which a failed fulfilment now inflates, and caps it at three.
callbackGasLimit sits between originalFunder and totalPaid, not last. Order is
part of the signature, so this was the wrong topic0 and ingest dropped every
real log. Pinned against the compiled artifact.
Adds the bank and feemarket keepers the settle path needs, grants Burner, and
lifts the module address out of the blocked set so it can hold the escrow it
burns.
protocol_fee, callback_budget, callback_gas_limit and revert_recipient come off
the event. observed_block_height/hash go the other way: the contract does not
take them and nothing consumed them, so they leave the ballot key.
DerivedEVMCall returns (nil, err) when the EVM reverts, discarding res.Ret, so
ClassifyCall could never see a revert reason and CallAlreadySettled was
unreachable. Call DerivedEVMCallWithData instead and drop the wrapper from the
interface so reaching for it is a compile error.
Gate on the budget covering the declared gas limit, then report the cost and
burn exactly that. Report before take: reportCallbackGas releases the refund
and decrements escrow first.
VoteOnBallot fires the terminal hook synchronously at quorum, and that hook
finds the request by ballot key. Assigning afterwards left the deciding vote
looking at the previous observation, so a read that reached quorum sat in
VOTING until the sweeper expired it. The old write also landed after the
hook's, clobbering FULFILLED.
app.EVMKeeper is a *evmkeeper.Keeper assigned further down, so the module was
handed a nil pointer that still satisfies the interface. Every fulfilment would
panic in EndBlock.
Real runtime bytecode with the module-address immutables pre-substituted, plus
a minimal IUniversalCore returning a zero read fee so a request can be created
without the vault.
Ingest of a contract-emitted event, fulfil/settle/burn, the expiry sweep and
EndBlock, vote to quorum through the ballot hook, the admin retry hatch and the
queries. Contract state is read back through the contract's own getters.
@0xNilesh
0xNilesh merged commit faf0173 into develop Aug 19, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants