Configuration
The addresses your target must be wired to, and how to verify them.
GasKillerSDK holds its configuration in an
ERC-7201 namespace, so it never
collides with your contract's own storage. Two values are required; the rest have
working defaults.
| Value | Setter | Required | Default |
|---|---|---|---|
avsAddress | _setAvsAddress | Yes | none |
schnorrRegistry | _setSchnorrRegistry | Yes | none |
blockStaleMeasure | _setBlockStaleMeasure | No | 300 blocks |
All three setters are internal. The SDK deliberately ships no access control,
you decide who may call them and when, by exposing them or not.
The two required addresses
schnorrRegistry is the SchnorrStakeRegistry holding the operator set's
aggregate key and stake weights. It is the contract your target trusts to verify
that a quorum of operators signed the state update, and it must be the one the
router is signing for. avsAddress is the AVS service manager; it records which
deployment the target belongs to and does not participate in signature
verification.
For the public testnet (Sepolia, chain 11155111):
| Value | Address |
|---|---|
| Chain ID | 11155111 |
| avsAddress | 0x0eF3c25243004C0F81Dc678c3411E619D61577ef |
| schnorrStakeRegistry | 0x8A86301675ac9617895117afFE9577f5154555F9 |
| registryCoordinator | 0x57981e930B19C52E95Fd727Ee51923119acCeeC0 |
| demoTarget | 0xCAcD6fd494cde74A84e7e5F6816ca7C87aa88aaD |
Read from GET /avs-metadata on the router itself, so this table follows the deployment rather than being maintained by hand. Cached for up to an hour.
While Gas Killer is in early development on Sepolia, these addresses can change. Every target holding an old one has to be updated, so resolve them at deploy time rather than copying them into your source, and make sure your contract can be rewired afterwards.
Read them yourself the same way, in a script or a deploy pipeline:
curl -s https://testnet.gaskiller.xyz/avs-metadata | jq .contractsThe two values a target's constructor takes, resolved at the moment you deploy rather than copied from a page:
CONTRACTS=$(curl -s https://testnet.gaskiller.xyz/avs-metadata | jq .contracts)
AVS_ADDRESS=$(jq -r .avsAddress <<<"$CONTRACTS")
SCHNORR_REGISTRY_ADDRESS=$(jq -r .schnorrStakeRegistry <<<"$CONTRACTS")The full response shape is in the API reference.
Verify your own wiring
Once your target is deployed, confirm it agrees with the published set:
# Must equal schnorrStakeRegistry in the table above.
cast call "$TARGET" "schnorrRegistry()(address)" --rpc-url "$RPC_URL"
# Non-zero: the registry actually holds an operator set.
cast call "$SCHNORR_REGISTRY_ADDRESS" "totalWeight()(uint256)" --rpc-url "$RPC_URL"If your target points at a different registry, its address is stale. Its tasks
fail with InvalidQuorumSignature (0x68477238), because the aggregate key it
checks against is not the one the operators signed with, or with
NotRegistered (0xbfc6c337) when a non-signer the router lists is unknown to
it. See the error table.
Stay reconfigurable
Gas Killer is not production-ready. It is unaudited and under active
development, so treat this section as required rather than optional. Because
the addresses above can change, a contract you
can reconfigure follows a migration, while one you cannot has to be redeployed
with its state moved across. Guard the setter like the upgrade key it is, with
a multisig or a timelock: whoever can change schnorrRegistry can point your
contract at a registry they control.
Because the setters are internal, a contract that only calls them in its
constructor can never be rewired, whether an address changed or you simply
got one wrong.
Expose a guarded setter:
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {GasKillerSDK} from "gas-killer-sdk/GasKillerSDK.sol";
contract Counter is GasKillerSDK, Ownable {
constructor(address avs, address schnorrRegistry)
Ownable(msg.sender)
{
_setAvsAddress(avs);
_setSchnorrRegistry(schnorrRegistry);
}
/// Lets the deployment follow an operator-set migration without redeploying.
function setGasKillerConfig(address avs, address schnorrRegistry) external onlyOwner {
_setAvsAddress(avs);
_setSchnorrRegistry(schnorrRegistry);
}
}blockStaleMeasure
The maximum age, in blocks, of the referenceBlockNumber a payload may carry.
verifyAndUpdate requires:
referenceBlockNumber < block.number
referenceBlockNumber + blockStaleMeasure >= block.numberUnset means 300 blocks, roughly an hour on Sepolia. Below that floor a payload
expires before a client can reasonably submit it; well above it, a payload stays
applicable long after the state it was computed against.
Lowering it tightens the window in which a signed transition can land. Raising it
widens the period during which an operator-set change can invalidate an
already-assembled signature. The registry schedules operator changes behind a
notice window (600 blocks on the public testnet) sized to exceed this value
plus a signing round, so raising it past that window loses the guarantee that a
payload settles under the operator set that signed it. 300 is a sensible
default; change it only with a reason.
constructor(address avs, address schnorrRegistry) {
_setAvsAddress(avs);
_setSchnorrRegistry(schnorrRegistry);
_setBlockStaleMeasure(150);
}Reading configuration back
All public, all view:
| Getter | Returns |
|---|---|
avsAddress() | Configured AVS service manager |
schnorrRegistry() | Configured stake registry |
blockStaleMeasure() | Effective value, including the 300 default |
stateTransitionCount() | Transitions applied so far |
inTransition() | True while a transition is being applied |
supportsInterface(0x82b35a01) | Whether the router will accept this target |
