World Chain Specs

About World Chain

World Chain is a blockchain designed for humans. Prioritizing scalability and accessibility for real users, World Chain provides the rails for a frictionless onchain UX.

Navigate this site using the sidebar on the left, the search icon found at the top of this page, or the left/right navigation buttons found to the sides of each page.

Priority Blockspace for Humans

Priority Blockspace for Humans introduces a new transaction ordering policy on World Chain that grants verified World ID holders top-of-block priority, reducing friction and making transactions fairer for real users.

Where bots create congestion, PBH is a highway for humans.

PBH Architecture

World Chain is an OP Stack chain that enables Priority Blockspace for Humans (PBH) through the World Chain Builder. World Chain leverages rollup-boost to support external block production, allowing the builder to propose PBH blocks to the sequencer while remaining fully compatible with the OP Stack.

Block Production on the OP Stack

The Engine API defines the communication protocol between the Consensus Layer (CL) and the Execution Layer (EL) and is responsible for orchestrating block production on the OP Stack. Periodically, the sequencer's consensus client will send a fork choice update (FCU) to its execution client, signaling for a new block to be built. After a series of API calls between the CL and EL, the EL will return a new ExecutionPayload containing a newly constructed block. The CL will then advance the unsafe head of the chain and peer the new block to other nodes in the network.

sequenceDiagram
    box OP Stack Sequencer
        participant sequencer-cl as Sequencer CL
        participant sequencer-el as Sequencer EL
    end
    box Network
        participant peers-cl as Peers
    end

    Note over sequencer-cl: FCU with Attributes
    sequencer-cl->>sequencer-el: engine_forkChoiceUpdatedV3(ForkChoiceState, Attrs)
    sequencer-el-->>sequencer-cl: {payloadStatus: {status: VALID, ...}, payloadId: PayloadId}
    sequencer-el->>sequencer-el: Build execution payload
    sequencer-cl->>sequencer-el: engine_getPayloadV3(PayloadId)
    sequencer-el-->>sequencer-cl: {executionPayload, blockValue}
    sequencer-cl->>peers-cl: Propagate new block


For a detailed look at how block production works on the OP Stack, see the OP Stack specs.

Rollup Boost

rollup-boost is a block building sidecar for OP Stack chains, enabling external block production while remaining fully compatible with the OP Stack. rollup-boost acts as an intermediary between the sequencer's consensus and execution client. When sequencer-cl sends a new FCU to rollup-boost, the request will be multiplexed to both the sequencer's execution client and external block builders signaling that a new block should be built.

When the sequencer is ready to propose a new block, op-node will send an engine_getPayload request to rollup-boost which is forwarded to the default execution client and external block builders.

Once rollup-boost receives the built block from external builder, it will then validate the block by sending it to the sequencer's execution client via engine_newPayload. If the external block is valid, it is returned to the sequencer's op-node, otherwise rollup-boost will return the fallback block. Note that rollup-boost will always fallback to the default execution client's block in the case that the external builder does not respond in time or returns an invalid block.

sequenceDiagram
    box Sequencer
        participant sequencer-cl as Sequencer CL
        participant rollup-boost
        participant sequencer-el as Sequencer EL
    end
    box Builder
        participant builder-el as Builder EL
    end

    Note over sequencer-cl: FCU with Attributes
    sequencer-cl->>rollup-boost: engine_forkChoiceUpdatedV3(..., Attrs)

    Note over rollup-boost: Forward FCU
    rollup-boost->>builder-el: engine_forkChoiceUpdatedV3(..., Attrs)

    rollup-boost->>sequencer-el: engine_forkChoiceUpdatedV3(..., Attrs)
    sequencer-el-->>rollup-boost: {payloadId: PayloadId}
    rollup-boost-->>sequencer-cl: {payloadId: PayloadId}


    Note over sequencer-cl: Get Payload
    sequencer-cl->>rollup-boost: engine_getPayloadV3(PayloadId)
    Note over rollup-boost: Forward Get Payload
    rollup-boost->>sequencer-el: engine_getPayloadV3(PayloadId)
    rollup-boost->>builder-el: engine_getPayloadV3(PayloadId)
    builder-el-->>rollup-boost: {executionPayload, blockValue}
    sequencer-el-->>rollup-boost: {executionPayload, blockValue}



    Note over rollup-boost, sequencer-el: Validate builder block
    rollup-boost->>sequencer-el: engine_newPayloadV3(ExecutionPayload)
    sequencer-el->>rollup-boost: {status: VALID, ...}

    Note over rollup-boost: Propose exectuion payload
    rollup-boost->>sequencer-cl: {executionPayload, blockValue}
    
    Note over sequencer-cl: Propagate new block

In addition to Engine API requests, rollup-boost will proxy all RPC calls from the sequencer op-node to its local execution client. The following RPC calls will also be forwarded to external builders:

  • miner_*
    • The Miner API is used to notify execution clients of changes in effective gas price, extra data, and DA throttling requests from the batcher.
  • eth_sendRawTransaction*
    • Forwards transactions the sequencer receives to the builder for block building.

Block Production on World Chain

World Chain leverages rollup-boost to enable external block production and integrates the World Chain Builder as a block builder in the network. The World Chain Builder implements a custom block ordering policy (ie. PBH) to provide priority inclusion for transactions with a valid World ID proof. Note that the custom ordering policy adheres to the OP Stack spec.

Each block has a "PBH blockspace capacity", which determines how many PBH transactions will be included in the block. Blocks on World Chain will always reserve a percentage of blockspace for non-PBH transactions to ensure inclusion for automated systems and non-verified users. If there are not enough pending PBH transactions to fill the entirety of PBH blockspace, standard transactions will be used to fill the remainder of the block.


Default Block
Tx Hash Fee
0xaaaa$0.04
0xbbbb$0.04
0xcccc$0.03
0xdddd$0.03
0xeeee$0.03
0x2222$0.02
0x3333$0.02
0x4444$0.02
0x5555$0.01
0x6666$0.01
PBH Block
Tx Hash Fee
0x3333$0.02
0x4444$0.02
0x5555$0.01
0x6666$0.01
0xaaaa$0.04
0xbbbb$0.04
0xcccc$0.03
0xdddd$0.03
0xeeee$0.03
0x2222$0.02

If the amount of pending PBH transactions exceed the PBH blockspace capacity, the remaining PBH transactions will carry over to the next block. PBH transactions aim to provide verified users with faster, cheaper transaction inclusion, especially during network congestion. Note that transactions within PBH blockspace are ordered by priority fee.

In the event that the block builder is offline, rollup-boost will fallback to the block built by the default execution client with standard OP Stack ordering rules.

PBH Transactions

The World Chain Builder introduces the concept of PBH transactions, which are standard OP transactions that target the PBHEntryPoint and includes a PBHPayload encoded in the tx calldata.

PBH 4337 UserOps

The PBHEntryPoint contract also provides priority inclusion for 4337 UserOps through PBH bundles. A PBH bundle is a standard 4337 bundle where the aggregated signature field is consists of an array of PBHPayload. A valid PBH bundle should include a n PBHPayloads, with each item corresponding to a UserOp in the bundle.

When creating a PBH UserOp, users will append the PBHPayload to the signature field and specify the PBHSignatureAggregator as the sigAuthorizer. The UserOp can then be sent to a 4337 bundler that supports PBH and maintains an alt-mempool for PBH UserOps.

The bundler will validate the PBHPayload, strip the payload from the userOp.signature field and add it to the aggregated signature.

    /**
     * Aggregate multiple signatures into a single value.
     * This method is called off-chain to calculate the signature to pass with handleOps()
     * @param userOps              - Array of UserOperations to collect the signatures from.
     * @return aggregatedSignature - The aggregated signature.
     */
    function aggregateSignatures(PackedUserOperation[] calldata userOps)
        external
        view
        returns (bytes memory aggregatedSignature)
    {
        IPBHEntryPoint.PBHPayload[] memory pbhPayloads = new IPBHEntryPoint.PBHPayload[](userOps.length);
        for (uint256 i = 0; i < userOps.length; ++i) {
            (, bytes memory proofData) = SafeModuleSignatures.extractProof(
                userOps[i].signature, ISafe(payable(userOps[i].sender)).getThreshold()
            );
            pbhPayloads[i] = abi.decode(proofData, (IPBHEntryPoint.PBHPayload));
        }
        aggregatedSignature = abi.encode(pbhPayloads);
    }

Upon submitting a PBH bundle to the network, the World Chain builder will ensure that all PBH bundles have valid proofs and mark the bundle for priority inclusion.

Visit the validation section of the docs to see how to encode the signalHash for a PBH UserOps work, check out the handleAggregatedOps() function and PBH4337Module.

PBH Payload

A PBHPayload consists of the following RLP encoded fields:

FieldTypeDescription
external_nullifierbytesA unique identifier derived from the proof version, date marker, and nonce.
nullifier_hashFieldA cryptographic nullifier ensuring uniqueness and preventing double-signaling.
rootFieldThe Merkle root proving inclusion in the World ID set.
proofProofSemaphore proof verifying membership in the World ID set.

External Nullifier

The external_nullifier is a unique identifier used when verifying PBH transactions to prevent double-signaling and enforce PBH transaction rate limiting. For a given external_nullifier, the resulting nullifier_hash will be the same, meaning it can be used a unique marker for a given World ID user while maintaining all privacy preserving guarantees provided by World ID. Note that since the external_nullifier is different for every PBH transaction a user sends, no third party can know if two PBH transactions are from the same World ID.

The external_nullifier is used to enforce that a World ID user can only submit n PBH transactions per month, with the Builder ensuring that all proofs and external_nullifiers are valid before a transaction is included.

The external_nullifier is encoded as a 32-bit packed unsigned integer, with the following structure:

  • Version (uint8): Defines the schema version.
  • Year (uint16): The current year expressed as yyyy.
  • Month (uint8): The current month expressed as (1-12).
  • PBH Nonce (uint8): The PBH nonce, where 0 <= n < pbhNonceLimit.

Below is an example of how to encode and decode the external_nullifier for a given PBH transaction.

uint8 public constant V1 = 1;

/// @notice Encodes a PBH external nullifier using the provided year, month, and nonce.
/// @param version An 8-bit version number (0-255) used to identify the encoding format.
/// @param pbhNonce An 8-bit nonce value (0-255) used to uniquely identify the nullifier within a month.
/// @param month An 8-bit 1-indexed value representing the month (1-12).
/// @param year A 16-bit value representing the year (e.g., 2024).
/// @return The encoded PBHExternalNullifier.
function encode(uint8 version, uint8 pbhNonce, uint8 month, uint16 year) internal pure returns (uint256) {
    require(month > 0 && month < 13, InvalidExternalNullifierMonth());
    return (uint256(year) << 24) | (uint256(month) << 16) | (uint256(pbhNonce) << 8) | uint256(version);
}

/// @notice Decodes an encoded PBHExternalNullifier into its constituent components.
/// @param externalNullifier The encoded external nullifier to decode.
/// @return version The 8-bit version extracted from the external nullifier.
/// @return pbhNonce The 8-bit nonce extracted from the external nullifier.
/// @return month The 8-bit month extracted from the external nullifier.
/// @return year The 16-bit year extracted from the external nullifier.
function decode(uint256 externalNullifier)
    internal
    pure
    returns (uint8 version, uint8 pbhNonce, uint8 month, uint16 year)
{
    year = uint16(externalNullifier >> 24);
    month = uint8((externalNullifier >> 16) & 0xFF);
    pbhNonce = uint8((externalNullifier >> 8) & 0xFF);
    version = uint8(externalNullifier & 0xFF);
}

The World Chain Builder enforces:

  • The external_nullifier is correctly formatted and specifies the current month, year and a valid nonce.
  • The proof is valid and specifies the external_nullifier as a public input to the proof.
  • The external_nullifier has not been used before, ensuring that the pbh_nonce is unique for the given month and year.

Signal Hash

One of the required inputs when generating a World ID proof is the signal_hash. As the name suggests, the signal_hash is the hash of the signal which is an arbitrary message provided by the prover, allowing the verifier to hash the signal when verifying the proof, ensuring that the proof was generated with the expected inputs.

Within the context of PBH, the signal_hash is used to ensure the proof was created for the transaction being submitted. Depending on the type of the PBH transaction, this value could be a hash of the tx calldata, a 4337 UserOp hash or the tx hash itself.

PBH Validation

Upon receiving new transactions, the World Chain Builder will first ensure that the payload is a valid OP Stack tranasaction. In addition to the default checks, the builder will also evaluate transactions for PBH conditions.

Any transaction that calls the pbhMulticall() or handleAggregatedOps() function on the PBHEntyrPoint will be considered a PBH transaction and must clear PBH Validation. PBH transactions must contain a valid PBHPayload or PBHPayload[] in the case of PBH 4337 bundles.

    struct PBHPayload {
        uint256 root;
        uint256 pbhExternalNullifier;
        uint256 nullifierHash;
        uint256[8] proof;
    }

Signal Hash

Transactions that target the pbhMulticall() function must provide a valid PBHPayload where included proof is generated with a signalHash specified as:

uint256 signalHash = abi.encode(msg.sender, calls).hashToField();

Transactions that target the handleAggregatedOps()function (ie. PBH 4337 Bundles) must contain an aggregated signature consisting of an array of PBHPayload where there is a PBHPayload for each UserOp in the bundle. The included proof must be generated with a signalHash specified as:

uint256 signalHash = abi.encodePacked(sender, userOp.nonce, userOp.callData).hashToField();

External Nullifier

PBH transactions must contain a valid external nullifier where:

  • The month is the current month
  • The year is the current year (specified as yyyy)
  • The pbhNonce is < pbhNonceLimit. PBH nonces are 0 indexed, meaning if the pbhNonce limit is 29, a user is allotted 30 PBH transactions per month.

Root

The root provided must be a valid World ID Root with a timestamp less than 7 days old.

Proof

The proof must be a valid semaphore proof, proving inclusion in the World ID set associated with the specified root.