CenturionDEX
Launch App

Arbiters

Last modified:

When a sponsor creates a compact, they assign an arbiter per chain. That arbiter is the sole account that can trigger a claim on the respective chain and determines which addresses receive the locked balances and in what amounts.

Responsibilities

  1. Claim verification — validate that claim conditions are met before submission.
  2. Claim submission — submit claims to The Compact on behalf of claimants.
  3. Recipient allocation — specify which accounts receive funds and their amounts.
  4. Witness-data validation — verify any witness data included in compacts.
  5. Cross-chain coordination — for multichain compacts, coordinate claims across chains.

Arbiter selection

Single and batch compacts

struct Compact {
    address arbiter;    // The designated arbiter for this compact
    address sponsor;
    uint256 nonce;
    uint256 expires;
    // ...
}

Multichain compacts

Each element can designate a different arbiter per chain:

struct Element {
    address arbiter;    // Can be different per chain
    uint256 chainId;
    Lock[] commitments;
    Mandate mandate;    // Required for multichain
}

Specifying claimants

Arbiters specify recipients through a Component struct that packs both the destination and the amount:

struct Component {
    uint256 claimant;  // Encodes both lockTag and recipient address
    uint256 amount;    // The amount of tokens to claim
}

Claimant encoding

The claimant field packs two values:

Destination options

The lockTag determines how claimed tokens are processed:

Lock tagBehaviour
Matches original lock's tagDirect transfer — ERC6909 tokens are transferred to the recipient, preserving the same lock properties (allocator, reset period, scope).
Non-zero, different from originalConvert to new lock — tokens are moved into a new resource lock with different properties (allocator, reset period, or scope).
bytes12(0)Withdraw — ERC6909 tokens are burned and the underlying tokens (native or ERC-20) are sent to the recipient.

:::important To prevent griefing via malicious receive hooks or intentional gas under-payment, The Compact implements a withdrawal fallback:

  1. Attempt the withdrawal with half the available gas.
  2. If that fails (and sufficient gas remains above a benchmarked stipend), fall back to a direct ERC6909 transfer to the recipient.

This ensures claims cannot be blocked through receive-hook manipulation while preserving intended withdrawal behaviour under normal conditions. Required stipends are determined by benchmarking cold-account access, typical ERC-20 transfer costs, and native-token transfer costs. The benchmark can be re-run at any time via __benchmark. :::

Example

// Example: Arbiter specifying three different destinations
Component[] memory claimants = new Component[](3);
 
// Direct transfer - keep same lock properties
claimants[0] = Component({
    claimant: uint256(uint160(alice)) | (uint256(originalLockTag) << 160),
    amount: 100e18
});
 
// Convert to new lock with different allocator
claimants[1] = Component({
    claimant: uint256(uint160(bob)) | (uint256(newLockTag) << 160),
    amount: 50e18
});
 
// Withdraw to underlying token
claimants[2] = Component({
    claimant: uint256(uint160(charlie)), // lockTag is 0
    amount: 25e18
});

Claim functions

Six claim functions cover the matrix of single-vs-batch and single-chain-vs-multichain:

Single-chain

// Single resource lock
function claim(Claim calldata claimPayload) external returns (bytes32 claimHash)
 
// Multiple resource locks
function batchClaim(BatchClaim calldata claimPayload) external returns (bytes32 claimHash)

Multichain — notarized chain

Used when the claim is on the chain whose domain matches the EIP-712 signature:

function multichainClaim(
    MultichainClaim calldata claimPayload
) external returns (bytes32 claimHash)
 
function batchMultichainClaim(
    BatchMultichainClaim calldata claimPayload
) external returns (bytes32 claimHash)

Multichain — exogenous chain

Used when the claim is on a chain other than the notarized chain:

function exogenousClaim(
    ExogenousMultichainClaim calldata claimPayload
) external returns (bytes32 claimHash)
 
function exogenousBatchClaim(
    ExogenousBatchMultichainClaim calldata claimPayload
) external returns (bytes32 claimHash)

Authorization flow

When an arbiter submits a claim, The Compact verifies authorization in order:

  1. Arbiter is callermsg.sender must match the arbiter in the compact.
  2. Claim validity — not expired, correct nonce, etc.
  3. Sponsor signature — the sponsor's signature must authorize the compact (unless the compact is registered on-chain).
  4. Allocator authorization — the allocator must approve the claim.

Trust model

Sponsors trust arbiters to submit only valid claims, not collude with claimants, and properly verify witness data.

Claimants trust arbiters to submit claims promptly when conditions are met, not censor valid claims, and distribute resources according to compact terms.

Common patterns

Automated arbiters

Smart contracts can serve as trustless arbiters — oracle-based arbiters that verify external conditions, time-locked arbiters for scheduled releases, or multi-signature arbiters requiring multiple approvals.

Arbiter networks

Multiple arbiters can be coordinated through consensus mechanisms, reputation-based selection, or stake-based security models.

Events

event Claim(
    address indexed sponsor,
    address indexed allocator,
    address indexed arbiter,
    bytes32 claimHash,
    uint256 nonce
)

Errors

ErrorMeaning
InvalidArbiter()Caller is not the designated arbiter.
ClaimExpired()Claim has passed its expiration time.
InvalidClaimSignature()Sponsor's signature is invalid.
UnauthorizedClaim()Arbiter is not authorized for this claim.

Security considerations

Key compromise — a compromised arbiter key can submit unauthorized claims. Sponsors should monitor for suspicious activity and consider time delays or multi-sig requirements for high-value compacts.

Censorship — arbiters can refuse to submit valid claims. Sponsors can mitigate this by using multiple arbiters, implementing replacement mechanisms, or setting appropriate expiration times.

Front-running — arbiters should consider private mempools or commit-reveal schemes for sensitive claims.