fix(evm): make evm.faucet usable — unwrap the result envelope, wait for the claim on-chain - #341
Conversation
🐼 Smoke eval —
|
| question | result | tokens | tools |
|---|---|---|---|
forky_node_coverage |
❌ | 0 | 0 |
tracoor_node_coverage |
❌ | 0 | 0 |
mainnet_block_arrival_p50 |
❌ | 0 | 0 |
list_datasources |
❌ | 0 | 0 |
block_count_24h |
❌ | 0 | 0 |
missed_slots_24h |
❌ | 0 | 0 |
chartkit_default_arrival_distribution |
❌ | 0 | 0 |
storage_upload_session_scoped |
❌ | 0 | 0 |
🔭 Langfuse traces (8 runs; ⚠️ = failed)
The report walks this branch's commits against the master baseline and the most recent release. A self-contained copy is in the run's eval-smoke-* artifact.
evm.faucet returned the raw operation envelope
({kind:"object", data:{...claim_hash...}}) and then looked for claim_hash on
the envelope itself, so every claim raised
ValueError: faucet claim did not return a tx hash
even though the claim had already succeeded server-side and the funds landed.
Use _runtime.invoke_data, which unwraps data and asserts kind == "object",
matching every other object-kind operation in the sandbox library.
evm.faucet returned as soon as the faucet reported claimStatus "confirmed", which only means the faucet broadcast the transaction. An immediate eth_getBalance after a claim could therefore read 0, so any automated check that funded an address and asserted its balance was racy. The operation now polls eth_getTransactionReceipt for the claim hash before responding, via the ethnode handler's "lb" sentinel instance (rpc.<network>.ethpandaops.io), and reports the block it landed in as confirmed/block_number on the result. The claim is already paid for by the time the wait runs, so a missing receipt is not fatal: a slow chain or unreachable execution endpoint returns the hash with confirmed=false, warned server-side and on the sandbox's stderr, rather than reporting a funded address as a failed claim. Only a reverted receipt is an error. The wait is capped at 30s because sandbox.timeout defaults to 60s and mining alone measures 19-34s — a longer wait would push a successful claim past the sandbox deadline, a worse failure than the race it fixes. pkg/faucet stays a pure faucet REST client; its comments claiming the flow waited for on-chain confirmation were wrong and are corrected.
e66c034 to
454361d
Compare
There was a problem hiding this comment.
Two fixes to evm.faucet: switch the sandbox client to invoke_data so the object envelope's data is unwrapped before the claim_hash lookup, and extend the server operation to poll eth_getTransactionReceipt (via the documented 'lb' load-balanced ethnode instance) so the op returns only once the claim is on-chain, surfacing confirmed/block_number and gracefully degrading to unconfirmed with a warning. I traced both fixes end-to-end (runtime invoke_data, ResultKindObject, ethNodeExecutionRPC, hex parse, proxy lb handling, the sole faucet.Result caller) and the code, tests and documented tradeoffs are consistent and sound.
Reviewed 5 changed file(s) @ 454361d9 — no blocking issues found.
A red panda's tail is nearly as long as its body — balance bar by day, blanket by night.
CI status — two failures, both pre-existing on
|
Two fixes to
evm.faucet, found by running the CLI faucet flow end-to-endagainst a proxy built from #277.
1.
evm.faucetalways failed from the sandboxThe server returns object operations in an envelope
(
{kind: "object", data: {…claim_hash…}}). The sandbox client used_runtime.invoke_json, which hands back the raw envelope, and then looked forclaim_hashon the envelope itself — so every claim raisedeven though the claim had already succeeded server-side and the funds had
landed. Users saw an error, and would naturally re-claim, burning their
30-sessions/hour budget for nothing.
Fix:
_runtime.invoke_data, which unwrapsdataand assertskind == "object", matching every other object-kind operation in the library.This is a regression-only bug in the shipped sandbox image —
modules/*/python/*.pyis baked in at image build time (
sandbox/Dockerfile:33), so it needs a newsandbox image, not just a server rollout.
2.
evm.faucetreturned before the claim was on-chainawaitClaimreturned as soon as the faucet reportedclaimStatus == "confirmed",which is PoWFaucet's own status and only means it has broadcast the
transaction. The docstring and the
pollInterval/pollAttemptscomment bothclaimed on-chain confirmation, which was not what the code did.
Observed: a claim returned a hash, and an immediate
eth_getBalanceread 0 ETH;the same read moments later returned 1 ETH. Any automated check that funds an
address and asserts its balance is therefore racy.
The operation now polls
eth_getTransactionReceiptfor the claim hash beforeresponding, via the ethnode handler's
lbsentinel instance(
rpc.<network>.ethpandaops.io), so it needs no individual node name.faucet.Resultgainedconfirmedandblock_number.Design notes
A missing receipt is not an error. The claim is already paid for by the time
the wait runs, so a slow chain or an unreachable execution endpoint returns the
hash with
confirmed: false— warned server-side and on the sandbox's stderr —rather than reporting a funded address as a failed claim. Only a receipt saying
the transaction reverted is an error.
The wait is capped at 30s.
sandbox.timeoutdefaults to 60s(
pkg/config/config.go:451) and mining alone measured 19–34s. A longer receiptwait would push a successful claim past the sandbox deadline — a worse failure
than the race it fixes. For a slow network, raise
sandbox.timeoutrather thanthe receipt timeout.
pkg/faucetstays a pure faucet REST client. Chain access lives in theoperation layer; the package's comments claiming it waited for on-chain
confirmation are corrected.
Verification
make lint0 issues;make testgreen (40 packages).Unit:
TestApplyFaucetReceiptcovers mined, pre-Byzantium (nostatusfield),reverted, missing block number, and unparseable block number.
Live, on rebuilt server + sandbox image, against
glamsterdam-devnet-7:0xf892c24a…bd65confirmed: true, block 2249340x185a904b…fbdd2confirmed: true, block 2249450xbc0790d8…86c7confirmed: trueAlso re-ran the surrounding matrix: unknown network → clean 404; a network whose
faucet ingress is down (
glamsterdam-devnet-8, 503) → clean 502 in 0.6s, nohang and no credential in the message.
Known caveat
Claim 2 above returned
confirmed: truewith a valid receipt, yet an immediateeth_getBalanceread 0.rpc.<network>.ethpandaops.iofans out across nodes, soa receipt served by one node does not guarantee the next request's node has
that state. This is a property of the endpoint, not of the wait — the receipt is
the real on-chain guarantee, and read-your-writes across an LB can't be fixed
client-side. Tests asserting a balance right after
evm.faucet()should stillpoll with a short retry.
Not covered
first needs an interactive device-flow re-login, the second burns 30 sessions
of the per-user hourly budget. Neither is touched by these changes.
still serving
proxy-0.38.7(its/faucet/*route 404s), and its Authentik isdown, so no token can be minted for it. Testing used a local proxy built from
this branch, which is credential-identical to staging for the faucet path (both
fall back to the
ethnodecredential viaToFaucetHandlerConfig).