Gas Killer Docs
Solidity Reference

Reference

GasKillerSDK API surface, state update types, and revert selectors.

API surface of GasKillerSDK. Inheriting it gives your contract everything on this page.

abstract contract GasKillerSDK is
    StateTracker, TransitionGuard, ERC165, IGasKillerSDK, IGasKillerSDKBatch

Constants

ConstantValueMeaning
—300Default blockStaleMeasure, in blocks, when unset
ERC-165 id0x82b35a01type(IGasKillerSDK).interfaceId; the router requires it
ERC-165 id0x2ea5ee1dtype(IGasKillerSDKBatch).interfaceId; batching and the in-transition latch

The quorum threshold is not an SDK constant. It belongs to the SchnorrStakeRegistry the target is wired to, as thresholdNum / thresholdDen of the registry's total weight. The public testnet registry requires 2/3.

verifyAndUpdate

The settlement entrypoint. The router builds this call for you (you never encode it by hand), but its parameters explain every failure mode.

function verifyAndUpdate(
    bytes32 msgHash,
    uint32 referenceBlockNumber,
    bytes calldata storageUpdates,
    uint256 transitionIndex,
    bytes4 targetFunction,
    uint256 s,
    address Raddr,
    address[] calldata nonSigners
) external payable;
ParameterPurpose
msgHashThe signed digest; recomputed on-chain and compared
referenceBlockNumberBlock the operator set is evaluated at
storageUpdatesABI-encoded (StateUpdateType[], bytes[]) — the diff to apply
transitionIndexCounter value this payload was computed against
targetFunctionSelector of the tracked function that was simulated
sAggregate Schnorr response scalar
RaddrAggregate nonce, as address(R)
nonSignersOperators that did not sign, in strictly ascending order

It executes in this order, so an earlier check masks any later one:

  1. referenceBlockNumber < block.number → else FutureBlockNumber
  2. referenceBlockNumber + blockStaleMeasure >= block.number → else StaleBlockNumber
  3. transitionIndex + 1 == stateTransitionCount() → else InvalidTransitionIndex
  4. Digest matches msgHash → else InvalidSignature
  5. isValidSignature on the configured registry: subtract each non-signer's key and weight from the aggregate, check the threshold, verify the signature → else InvalidQuorumSignature, or a registry revert
  6. Apply the updates

Verification gas doesn't grow with the size of the operator set: one ecrecover against the registry's cached aggregate key, plus one point subtraction per non-signer.

The signed digest

sha256(abi.encode(transitionIndex, address(this), targetFunction, storageUpdates))

Available as a view for off-chain cross-checking:

function getMessageHash(
    uint256 transitionIndex,
    bytes4 targetFunction,
    bytes calldata storageUpdates
) external view returns (bytes32);

Binding address(this) means a payload cannot be replayed against a different contract, and binding transitionIndex means it cannot be replayed against a different state of the same one.

verifyAndUpdateBatch

Settles several independently signed transitions in one transaction. Every transition after the first verifies at warm-access prices.

struct TaskSubmission {
    bytes32 msgHash;
    uint32 referenceBlockNumber;
    bytes storageUpdates;
    uint256 transitionIndex;
    bytes4 targetFunction;
    uint256 s;
    address Raddr;
    address[] nonSigners;
}

function verifyAndUpdateBatch(TaskSubmission[] calldata submissions) external payable;

Each submission is checked exactly as a standalone verifyAndUpdate would check it, so the signed digest does not change when a transition is batched. Submissions apply in order of ascending transitionIndex, and the batch is atomic.

A submission whose index has already settled is skipped rather than reverting the batch. Settlement is permissionless, so without the skip anyone could lift one submission from the mempool, settle it on its own first, and void the rest. Only a gap, meaning an index above the next expected one, reverts with InvalidTransitionIndex.

State update types

storageUpdates decodes to a list of operations applied in order.

TypeEffect
STOREWrite a 32-byte value to a storage slot
CALLExternal call, optionally with ETH value
LOG0–LOG4Emit a log with 0–4 indexed topics
CREATEDeploy a contract, nonce-derived address
CREATE2Deploy a contract, salt-derived address

Most integrations only ever produce STORE and LOG*. See Tracked functions for the funding rules that apply to the value-bearing types.

Inherited members

From StateTracker:

MemberDescription
trackStateModifier; increments the transition counter before the body
stateTransitionCount()Transitions applied so far

From TransitionGuard:

MemberDescription
guardTransitionModifier; reverts re-entry, holds the in-transition latch
inTransition()True while a transition is applying

Both use fixed hashed slots, and TransitionGuard uses EIP-1153 transient storage, so a Cancun-or-later EVM is required.

Revert selectors

What a failed settlement means. Reverts from the SDK:

SelectorErrorCauseFix
0x252f8a0eFutureBlockNumberReference block is not yet minedRetry; usually a node lagging behind head
0x305c3e93StaleBlockNumberPayload older than blockStaleMeasureSubmit a new task — the payload expired
0x7376e0a2InvalidTransitionIndexContract state advanced since signingSubmit a new task; another transition landed first
0x8baa579fInvalidSignatureRecomputed digest ≠ msgHashPayload was altered, or the target's ABI differs from the router's
0x68477238InvalidQuorumSignatureThe registry rejected the signatureSee below
0xc2e5347dEmptyBatchverifyAndUpdateBatch called with no submissionsReport it — malformed batch
0x83a33c51BlockStaleMeasureOverflow_setBlockStaleMeasure given a value above uint96Fix the value your contract passes
0x287cdcedReentrantTransitionverifyAndUpdate re-entered mid-transitionA CALL update re-entered the contract; see inTransition()
0x493f09c4RevertingContextA CALL update revertedOften under-funded msg.value; carries the inner revert data
0x30116425DeploymentFailedA CREATE/CREATE2 update failedUsually under-funded msg.value
0x5f6f132cInvalidArgumentsUpdate arguments malformedReport it
0x25773e13MalformedLogPayloadLog update malformedReport it

Reverts that come from the SchnorrStakeRegistry, not the SDK:

SelectorErrorWhat it usually means
0xdc897c0cStaleSnapshotThe operator set changed after the reference block. Submit a new task
0x11ea130dFutureReferenceBlockReference block is not yet mined. Retry
0xbfc6c337NotRegistered(address)A listed non-signer is not in this registry. Your target may be wired to a different registry than the router signs for, see Configuration
0xedb3eb6cNonSignersNotSortedMalformed non-signer list. Report it

InvalidQuorumSignature

The registry returned false. There are two causes:

  • Too few signers. The operators who signed hold less than the threshold share of the registry's weight. This is transient: too many operators missed the round.
  • The aggregate doesn't match. The key the registry holds is not the one the operators signed with. If it happens on every task, your target is wired to the wrong registry. If it happens once, the operator set changed mid-round and a new task will settle.

Decode an unknown selector with cast 4byte <selector>, or recover a revert locally by replaying the payload at a block inside its validity window:

cast call "$TO" "$DATA" --from "$FROM" --block <n> --rpc-url "$RPC_URL"

A payload that reverts at every block in its window is not a timing problem: it is a configuration problem.

Source

On this page