Skip to main content

Compacts & EIP-712

A compact is an agreement created by a sponsor that allows their locked resources to be claimed under specified conditions. The Compact protocol uses EIP-712 typed structured data for creating and verifying signatures for these agreements.

Compact Types​

1. Single Compact​

For single resource lock operations on a single chain.

struct Compact {
address arbiter; // The account tasked with verifying and submitting the claim
address sponsor; // The account to source the tokens from
uint256 nonce; // A parameter to enforce replay protection, scoped to allocator
uint256 expires; // The time at which the claim expires
bytes12 lockTag; // A tag representing the allocator, reset period, and scope
address token; // The locked token, or address(0) for native tokens
uint256 amount; // The amount of ERC6909 tokens to commit from the lock
// (Optional) Witness data may follow:
// Mandate mandate;
}

2. Batch Compact​

For allocating multiple resource locks on a single chain.

struct BatchCompact {
address arbiter; // The account tasked with verifying and submitting the claim
address sponsor; // The account to source the tokens from
uint256 nonce; // A parameter to enforce replay protection, scoped to allocator
uint256 expires; // The time at which the claim expires
Lock[] commitments; // The committed locks with lock tags, tokens, & amounts
// (Optional) Witness data may follow:
// Mandate mandate;
}

struct Lock {
bytes12 lockTag; // A tag representing the allocator, reset period, and scope
address token; // The locked token, or address(0) for native tokens
uint256 amount; // The maximum committed amount of tokens
}

3. Multichain Compact​

For allocating one or more resource locks across multiple chains.

struct MultichainCompact {
address sponsor; // The account to source the tokens from
uint256 nonce; // A parameter to enforce replay protection, scoped to allocator
uint256 expires; // The time at which the claim expires
Element[] elements; // Arbiter, chainId, commitments, and mandate for each chain
}

struct Element {
address arbiter; // The account tasked with verifying and submitting the claim
uint256 chainId; // The chainId where the tokens are located
Lock[] commitments; // The committed locks with lock tags, tokens, & amounts
// Witness data MUST follow (mandatory for multichain compacts):
Mandate mandate;
}

Witness Structure​

The witness mechanism (Mandate struct) allows extending compacts with additional data for specifying conditions or parameters for a claim.

Format​

The witness is always a Mandate struct appended to the compact:

Compact(..., Mandate mandate)Mandate(uint256 myArg, bytes32 otherArg)

Witness Typestring​

The witnessTypestring provided during a claim should be the arguments inside the Mandate struct (e.g., uint256 myArg,bytes32 otherArg), followed by any nested structs.

Nested Structs​

EIP-712 requires nested structs to be ordered alphanumerically after the top-level struct in the typestring. Prefix nested structs with "Mandate" (e.g., MandateCondition) to ensure correct ordering.

Example witness typestring:

MandateCondition condition,uint256 arg)MandateCondition(bool flag,uint256 val

⚠️ Important: Do not include the closing parenthesis in your witness typestring. It will be added by the protocol during dynamic typestring construction.

Permit2 Integration​

The Compact supports integration with Permit2 for gasless deposits:

CompactDeposit​

For basic Permit2 deposits:

CompactDeposit(bytes12 lockTag,address recipient)

Activation​

Combines deposits with single compact registration:

Activation(address activator,uint256 id,Compact compact)Compact(...)Mandate(...)

BatchActivation​

Combines deposits with batch compact registration:

BatchActivation(address activator,uint256[] ids,Compact compact)Compact(...)Mandate(...)

CompactCategory Enum​

Used to distinguish between different types of compacts when using Permit2 integration:

enum CompactCategory {
Compact, // 0
BatchCompact, // 1
MultichainCompact // 2
}

Registration​

As an alternative to signing EIP-712 payloads, compacts can be registered directly on The Compact contract:

Register Functions​

// Register a single compact
function register(bytes32 claimHash, bytes32 typehash) external;

// Register multiple compacts
function registerMultiple(bytes32[] calldata claimHashes, bytes32[] calldata typehashes) external;

// Register on behalf of sponsor
function registerFor(
address sponsor,
bytes32 claimHash,
bytes32 typehash,
bytes memory sponsorSignature
) external;

Registration Event​

When a compact is registered, the following event is emitted:

event CompactRegistered(
address indexed sponsor,
bytes32 claimHash,
bytes32 typehash
);

This event is emitted when a compact is registered via register, registerMultiple, or any of the combined deposit-and-register functions.

Check Registration Status​

function isRegistered(
address sponsor,
bytes32 claimHash,
bytes32 typehash
) external view returns (bool);

Signature Verification​

When a claim is submitted for a non-registered compact, The Compact verifies the sponsor's authorization in the following order:

  1. Caller is Sponsor: If msg.sender == sponsor, authorization is granted
  2. ECDSA Signature: Attempt standard ECDSA signature verification
  3. EIP-1271 isValidSignature: If ECDSA fails, call isValidSignature on the sponsor's address
  4. Emissary verifyClaim: If EIP-1271 fails, call the emissary's verifyClaim function