CenturionDEX
Launch App

Resource Locks

Last modified:

Resource locks are the fundamental building blocks of The Compact protocol. They are created when a depositor places tokens — native or ERC-20 — into The Compact.

Structure

Each resource lock has four properties:

PropertyPurpose
Underlying tokenThe token held in the lock.
AllocatorThe entity that co-signs claims against the lock.
ScopeMultichain (spendable on any chain) or chain-specific.
Reset periodTimelock for forced withdrawals and emissary reassignment.

Every unique combination of these four properties maps to a fungible ERC6909 token ID. Holding these tokens makes the owner a potential sponsor who can create compacts.

Lock tag

A lock tag is a bytes12 value that encodes the allocator ID, scope, and reset period. Combined with the underlying token address, it produces the ERC6909 token ID:

lockTag = scope << 255 | resetPeriod << 252 | allocatorId << 160
lockId  = lockTag | tokenAddress

Allocator ID

The allocator ID is a 92-bit value derived from the allocator address. A 4-bit compact flag (0–15) counts leading zero nibbles in the address (with an offset), and the lowest 88 bits of the address form the remainder:

compactFlag = min(max(0, leadingZeroNibbles - 3), 15)
id = (compactFlag << 88) | (allocator & 0x00000000000000000000FFFFFFFFFFFFFFFFFFFF)

Reset period

One of eight predefined values:

IndexValueIndexValue
0OneSecond4OneHourAndFiveMinutes
1FifteenSeconds5OneDay
2OneMinute6SevenDaysAndOneHour
3TenMinutes7ThirtyDays

Creating resource locks

Resource locks are created by depositing tokens into The Compact.

Native token deposits

function depositNative(
    address recipient,
    bytes12 lockTag
) external payable returns (uint256 id)

ERC-20 deposits

function depositERC20(
    address token,
    address recipient,
    uint256 amount,
    bytes12 lockTag
) external returns (uint256 id)

Batch deposits

function batchDeposit(
    DepositDetails[] calldata depositDetails
) external payable returns (uint256[] memory ids)

Permit2 deposits

function depositERC20ViaPermit2(
    ISignatureTransfer.PermitBatchTransferFrom memory permit,
    bytes memory signature
) external returns (uint256 id)

Token handling

Native tokens

The Compact mints ERC6909 tokens 1:1 with msg.value. A deposit of 1e18 wei produces exactly 1e18 ERC6909 tokens. Withdrawals burn the ERC6909 tokens and transfer an equal native value out.

ERC-20 tokens

The Compact mints ERC6909 tokens equal to the actual balance change observed after the ERC-20 transfer — not the nominal amount parameter. This correctly handles fee-on-transfer tokens, where the actual and intended amounts differ.

Deposits and withdrawals both check the contract's balance before and after the token transfer, minting or burning based on the observed delta.

Fee-on-transfer tokens

Fully supported. The ERC6909 mint/burn amount tracks the real balance change, so fee-on-transfer tokens work without special handling by the depositor.

Integration notice

To support fee-on-transfer tokens, The Compact does not fully follow the Checks-Effects-Interactions (CEI) pattern during deposits and withdrawals.


If you integrate with The Compact — particularly as an allocator — be aware that ERC6909 balances may change within the same transaction if a deposit, withdrawal, or claim triggers a nested call back to your contract.


To confirm all balances are settled, verify that the reentrancy guard is not set:

require(
    TheCompact.exttload(0x0000000000000000000000000000000000000000000000929eee149b4bd21268) < 2,
    "Balance state may not be final on The Compact"
)

Use extsload instead of exttload on chains without tstore support.

Rebasing tokens

Rebasing tokens (e.g., stETH) are not supported. Any yield or balance change after deposit does not accrue to the depositor's ERC6909 tokens. Use the non-rebasing wrapped counterpart (e.g., wstETH) instead.

Forced withdrawals

If an allocator becomes unresponsive, the lock owner can bypass it:

  1. Enable — call enableForcedWithdrawal(uint256 id).
  2. Wait — the lock's resetPeriod must elapse.
  3. Withdraw — call forcedWithdrawal(uint256 id, address recipient, uint256 amount).

Cancel at any time with disableForcedWithdrawal(uint256 id).

View functions

function getLockDetails(uint256 id) external view returns (
    address token,
    address allocator,
    uint48 resetPeriod,
    Scope scope,
    bytes12 lockTag
)
 
function getForcedWithdrawalStatus(
    address account,
    uint256 id
) external view returns (ForcedWithdrawalStatus status)

Errors

ErrorMeaning
InvalidToken(address token)Invalid token address.
InvalidLockTag()Invalid lock tag.
InvalidDepositBalanceChange()Actual balance change does not match expectation.
PrematureWithdrawal(uint256 id)Withdrawal attempted before reset period elapsed.
ForcedWithdrawalFailed()Forced withdrawal operation failed.