Skip to content

fix(runtime): exact-slot eth_getStorageAt, eth_getCode fallback for create2, and receipt-poll retry - #31

Merged
luiz-lvj merged 1 commit into
mainfrom
runtime/storage-getcode-receipt-fixes
Jun 2, 2026
Merged

fix(runtime): exact-slot eth_getStorageAt, eth_getCode fallback for create2, and receipt-poll retry#31
luiz-lvj merged 1 commit into
mainfrom
runtime/storage-getcode-receipt-fixes

Conversation

@luiz-lvj

@luiz-lvj luiz-lvj commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

Three independent runtime/bridge fixes surfaced while running the OZ tron-contracts suite against TRE. Together they clear ~25 deterministic failures (proxy/upgradeable + create2) and stop a transient socket drop from cascading an entire test worker.

1. stubProvider.getStorage — read the EXACT slot via eth_getStorageAt

getStorage POSTed debug_storageRangeAt(0, 0, addr, slot, 1) to /tre and returned Object.values(storage)[0] — i.e. the FIRST slot the node yields, not the requested one (java-tron's storageRangeAt doesn't filter to the key). So ERC-1967 reads came back as slot 0: getAddressInSlot(proxy, ImplementationSlot) returned the implementation's value field (10/100/42) instead of the implementation address.

This cascaded: TransparentUpgradeableProxy's tests read the ProxyAdmin address from the admin slot (a deliberate TVM workaround), got garbage, then impersonated the wrong address — so every admin-gated upgradeToAndCall reverted with data=0x and proxyAdmin.owner() reverted as call reverted (data=0x).

Fix: read the exact slot via eth_getStorageAt(hexAddr, slotHex, 'latest') on /jsonrpc (the same endpoint maybeAutoRegisterContract uses for eth_getCode), plus the EVM-predicted → TVM-actual remap that getCode/getBalance already apply, and normalize the result with zeroPadValue(..., 32).

2. stubProvider.getCode — fall back to eth_getCode for CREATE2-opcode contracts

getContract reads ContractStore, which doesn't contain contracts deployed via the CREATE2 opcode (e.g. RelayedCall's assembly-create2 relayer, or OZ Clones) — those live in CodeStore. When getContract returns empty, fall back to eth_getCode on /jsonrpc, which reads CodeStore. Fixes provider.getCode(relayer) returning 0x.

3. wait.js _getInfoBigSafe — retry transient socket errors on the receipt poll

The receipt poll did a bare fetch + resp.text() with no retry, unlike rpcCall (which already retries via isTransientFetchError). A G1GC stop-the-world late in a long run drops the keep-alive socket → TypeError: fetch failed / "other side closed" → the test fails. Worse, since every later fixture's waitForReceipt (and refundSigners) hits the same node, one drop cascades into failing the rest of the worker. Wrap the fetch+body-read in the same single-retry-with-100ms-backoff and export isTransientFetchError from cheatcodes.js for reuse. (Resilience for transient drops; node-level memory pressure is a separate, host-side concern.)

Files

  • src/runtime/ethers-bridge.jsgetStorage (eth_getStorageAt), getCode (eth_getCode fallback)
  • src/runtime/wait.js_getInfoBigSafe transient retry
  • src/runtime/cheatcodes.js — export isTransientFetchError

@luiz-lvj
luiz-lvj requested a review from km631 June 2, 2026 18:22
@luiz-lvj
luiz-lvj merged commit 822664e into main Jun 2, 2026
5 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