Skip to content

[#4076] Describe the gasleft() check before a randomness read - #60

Merged
isaacdoidge merged 1 commit into
feature/evm_automationfrom
issue-4076-gasleft-guidance
Sep 24, 2026
Merged

isaacdoidge merged 1 commit into
feature/evm_automationfrom
issue-4076-gasleft-guidance

Conversation

@isaacdoidge

@isaacdoidge isaacdoidge commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

Part of Entropy-Foundation/smr-moonshot#4076.

What changes

The consumer guidance on ISupraRandomness.next says the gas a contract spends after a read must not depend on the value in a way that makes an outcome the sender would reject the more expensive one. It offered two ways to meet that: equalise the branches, or record the outcome and settle later. This PR adds a third: check gasleft() immediately before the read against the gas of the most expensive outcome.

  • ISupraRandomness.next NatSpec: describes the check: once, immediately before the first read, against the gas from that read to the end of the reading function. It states that gasleft() is the reading frame's own gas. Because rule 2 is stated over the context address, the reading frame can be a sub-frame in the root context, such as a proxy's implementation, a DELEGATECALL library, a self-call, a callback or a batching Multicall, with a share of gas passed down to it. The check covers that frame's work only, so a reading function must not be reachable through a batching path that continues after it.
  • LibRandomness.valueWithGasLeft(uint256 minGasAfterRead): new internal helper, for the first read of a function. It reverts with the new InsufficientGasForRead(gasLeft, minGasAfterRead) before reading when less gas remains than required, and otherwise returns next(). The library's header points to it.
  • test/LibRandomness.t.sol: new. With the precompile mocked by vm.etch, the helper reads exactly once when enough gas remains. When gas is short it reverts with InsufficientGasForRead and never calls the precompile.

Compatibility

No deployed contract imports either file, and foundry.toml sets bytecode_hash = "none" and cbor_metadata = false, so the genesis bytecode and every CREATE2 address derived from it are unchanged. smr-moonshot does not need a new pin for this change.

Testing

  • forge test --match-path test/LibRandomness.t.sol: 2 passed. With the check in valueWithGasLeft removed, the short-gas test fails.
  • forge fmt --check on the three files: clean.

The matching smr-moonshot guide and RFC changes are in Entropy-Foundation/smr-moonshot#4077.

🤖 Generated with Claude Code

A contract whose gas after a read depends on the value can check
gasleft() immediately before the read against its most expensive
outcome, so that every outcome can complete. Describe the check on
ISupraRandomness.next, and add LibRandomness.valueWithGasLeft, which
performs the check and the read together, with Foundry tests.

Documentation and a library helper only: no deployed contract imports
either file, and the build embeds no metadata hash, so genesis bytecode
is unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@isaacdoidge
isaacdoidge merged commit e1e8d26 into feature/evm_automation Sep 24, 2026
19 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.

1 participant