Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

System transactions reference

The StableSystem precompile exposes Stable protocol data and events through a fixed EVM contract interface. In v1.8.0, any client can query the guaranteed blockspace registry, while only protocol-generated transactions can emit system logs.

Contract

The precompile uses the same address on Mainnet and Testnet:

0x0000000000000000000000000000000000009999

MethodSelectorAccessGas limitPurpose
blockspaceLanes()0x2c0ac3beAnyone, read-only10,000Return the active Enterprise and transaction-type lane registry.
notifySystemTxLogs()0xe8e983b7System transaction sender only50,000Process up to 100 queued system log entries.

Use this Solidity interface for the v1.8.0 ABI:

// SAFE: This interface matches the Stable v1.8.0 StableSystem ABI.
struct EnterpriseLane {
    uint64 id;
    string name;
    uint32 weight;
}
 
struct TxTypeLane {
    uint64 id;
    string name;
    address[] toAddrs;
    bytes4[] methods;
    uint8[] txTypes;
    uint64[] nonceKeys;
    address[] senders;
    uint32 weight;
    bool noOverflow;
}
 
struct BlockspaceLanes {
    uint32 maxBlockspaceGasWeight;
    EnterpriseLane[] enterpriseLanes;
    TxTypeLane[] txTypeLanes;
}
 
interface IStableSystem {
    event UnbondingCompleted(
        address indexed delegator,
        address indexed validator,
        uint64 indexed originBlockHeight,
        uint256 amount
    );
 
    function notifySystemTxLogs() external;
 
    function blockspaceLanes()
        external
        view
        returns (BlockspaceLanes memory lanes);
}

blockspaceLanes()

blockspaceLanes() returns the governance-controlled lane registry used during block proposal and validation. It is a normal eth_call, not a custom JSON-RPC method, and it does not require an Enterprise gateway.

Both lane arrays are sorted by id in ascending order. Lower IDs have higher match priority.

Return value

FieldTypeDescription
maxBlockspaceGasWeightuint32Percentage from 0 to 100 of the block gas limit reserved across all configured lanes.
enterpriseLanesEnterpriseLane[]Enterprise lane definitions. Governance allows at most one Enterprise lane.
txTypeLanesTxTypeLane[]Lanes whose match rules inspect transaction fields.

Enterprise lane

Any CustomTx whose NonceKey has bit 63 set routes to the configured Enterprise lane, except MaxUint64. The lower 63 bits select an independent nonce channel, not a lane.

FieldTypeDescription
iduint64Drain-order priority and identifier.
namestringHuman-readable lane name.
weightuint32Percentage from 0 to 100 of the reserved blockspace pool assigned to the lane.

Transaction-type lanes

A transaction must match every populated matcher field. Entries within one field are alternatives, and an empty matcher is a wildcard.

FieldTypeDescription
iduint64Lane priority and identifier. Lower IDs match first.
namestringHuman-readable lane name.
toAddrsaddress[]Allowed destination addresses. Empty matches any destination.
methodsbytes4[]Allowed four-byte function selectors. Empty matches any selector.
txTypesuint8[]Allowed transaction formats. Empty or an entry of 0 matches any format.
nonceKeysuint64[]Allowed nonce keys. Empty or an entry of 0 matches any key.
sendersaddress[]Allowed sender addresses. Empty matches any sender.
weightuint32Percentage from 0 to 100 of the reserved blockspace pool assigned to the lane.
noOverflowboolWhen true, unused capacity does not cascade to later lanes.

txTypes uses these TxFormat values:

ValueTransaction formatEVM type
0WildcardAny
1Legacy0x00
2Access list0x01
3Dynamic fee0x02
4Set code0x04
5Two-dimensional nonce0x3F

Query the registry

Call the precompile with Foundry's cast command:

cast call \
  0x0000000000000000000000000000000000009999 \
  "blockspaceLanes()((uint32,(uint64,string,uint32)[],(uint64,string,address[],bytes4[],uint8[],uint64[],address[],uint32,bool)[]))" \
  --rpc-url https://rpc.stable.xyz

The v1.8.0 Mainnet activation seeded a 20% reserved pool and one Enterprise lane with 50% of that pool. Governance can change this output.

(20, [(1, "enterprise", 50)], [])

notifySystemTxLogs()

notifySystemTxLogs() reads queued protocol log entries from state and processes up to 100 per call. It takes no arguments and returns no value.

Only a system transaction with sender 0x0000000000000000000000000000000000000001 can call this method. Calls from users or contracts revert.

When an unbonding completes, the flow is:

  1. The staking module completes the operation and records its SDK event.
  2. The x/stable module queues the event data and its original block height.
  3. The next block proposer creates a system transaction that calls notifySystemTxLogs().
  4. The precompile emits EVM logs for up to 100 queued entries.
  5. Clients read the logs with eth_getLogs, a WebSocket subscription, or a block explorer.

Entries above the batch limit stay queued for a later block.

UnbondingCompleted event

ParameterTypeIndexedDescription
delegatoraddressYesAddress whose delegated tokens finished unbonding.
validatoraddressYesValidator address derived from its validator-operator address.
originBlockHeightuint64YesBlock height at which the SDK-layer unbonding originally completed.
amountuint256NoAmount that finished unbonding.

The EVM log appears in the block containing the system transaction. Use originBlockHeight when you need the height of the original SDK event.

Security model

  • The protocol creates system transactions during block proposal.
  • EVM state-transition rules reserve sender 0x0000000000000000000000000000000000000001 for system transactions.
  • User transactions cannot forge the sender or call notifySystemTxLogs() successfully.
  • blockspaceLanes() is intentionally public and cannot change state.

Where to go next