Matrix logo

LayerX SettlementAnchor, IERC20, EIP712, SafeERC20, and Deployment Contracts

The repository documentation states that production src/ has zero external imports, and that safety primitives are reimplemented in-house under src/lib. That makes this section...

Overview

The repository documentation states that production src/ has zero external imports, and that safety primitives are reimplemented in-house under src/lib. That makes this section unusually self-contained: deployment, settlement anchoring, typed-data verification, token safety, and governance all live in the same audited surface.

Repository and Build Notes

FileWhat it establishes
layerx/contracts/README.mdDefines the contract suite’s purpose, the trust model, build and test commands, mainnet wiring notes, and frozen-spec open decisions.
layerx/contracts/foundry.tomlSets the Foundry layout and compiler profile for src, test, and script, including Solidity 0.8.27, Shanghai EVM settings, optimizer settings, and the paxeer RPC endpoint key.

The README also records the suite’s two primary contracts: LayerXVault for reserve custody and settlement, and SettlementAnchor for the immutable root log. It further states that forge-std is vendored under lib/ for tests only and is never imported by production src/.

1. Build contracts | Run `forge build`.
2. Run the test suite | Run `forge test`.

Deployment Flow

Deploy Script

layerx/contracts/script/Deploy.s.sol

Deploy.run is the deployment entrypoint for the Paxeer contract pair. It reads the required environment variables, starts a broadcast, deploys SettlementAnchor first, deploys LayerXVault second, and then conditionally wires the anchor writer to the vault when the broadcasting key matches the anchor governor.

Environment variableRole in run
LAYERX_GOVERNORProtocol root authority used for both deployments.
LAYERX_OPERATOR_ADDRSequencer EVM key that submits settlement batches and signs balance proofs.
LAYERX_GUARDIANEmergency pause authority.
LAYERX_USDL_ADDRUSDL reserve token address.
LAYERX_DEX_ROUTERPaxeer DEX router used by the vault’s swap path.
LAYERX_WRAPPED_NATIVEWrapped native asset used for native deposit handling.
LAYERX_EXIT_DELAYForce-exit challenge window in seconds.

Contract Wiring Overview

Contract and Library Reference

SettlementAnchor

Deploy.run creates SettlementAnchor with writer_ set to address(0). It only calls setWriter when anchorC.governor() == msg.sender, so a deployment broadcast from a non-governor key leaves the anchor unwired until the governor performs the follow-up transaction.

layerx/contracts/src/SettlementAnchor.sol

SettlementAnchor is the minimal, append-only registry for settled batch Merkle roots. It stores only anchoring metadata and never holds funds. The contract is deliberately small so the historical root log survives a future vault migration, while the governor can rotate the authorized writer.

Constructor dependencies

TypeDescription
address governor_Protocol root authority that can rotate the writer.
address writer_Initial writer address allowed to anchor roots.

Properties

PropertyTypeDescription
writeraddressAuthorized writer allowed to call record.
rootOfmapping(bytes32 => bytes32)Anchored Merkle root for each batch id.
anchoredmapping(bytes32 => bool)Idempotency flag keyed by batch id.

Methods

MethodDescription
setWriterUpdates the authorized writer after a governor check.
recordAnchors one batch root, rejects duplicates, and emits the anchoring event.

Error surface

  • NotWriter when a non-writer calls record.
  • AlreadyAnchored when the same batch id is anchored again.
  • ZeroRoot when a batch root is empty.

The record call writes anchored[batchId] = true, stores rootOf[batchId] = root, and emits SettlementAnchored with the batch metadata plus block.timestamp.

ISettlementAnchor

layerx/contracts/src/interfaces/ISettlementAnchor.sol

ISettlementAnchor is the on-chain contract boundary for the anchor log. It exposes the batch-root getter and the anchoring event used by the vault and by off-chain receipt verification.

Event

EventFields
SettlementAnchoredbatchId, root, totalSettled, count, windowEnd, anchoredAt

Methods

MethodDescription
recordRecords a settled batch root, callable only by the authorized writer.
rootOfReturns the anchored root for a batch id, or zero if no root exists.

IERC20

layerx/contracts/src/interfaces/IERC20.sol

IERC20 is the in-house ERC-20 interface used by the vault and the token safety helpers. It includes decimals because LayerX accounting is denominated in reserve-token native units.

Events

EventFields
Transferfrom, to, value
Approvalowner, spender, value

Methods

MethodDescription
totalSupplyReturns token supply.
balanceOfReturns the balance for one account.
transferTransfers tokens to one recipient.
allowanceReturns the remaining allowance.
approveSets allowance for a spender.
transferFromTransfers tokens using allowance.
decimalsReturns the token decimal precision.

EIP712

layerx/contracts/src/lib/EIP712.sol

EIP712 implements the typed-data domain hashing used by the vault’s balance-proof flow. The domain separator is cached at construction and re-derived if the chain id changes, which keeps signatures fork-safe.

Constructor dependencies

TypeDescription
string memory nameTyped-data domain name.
string memory versionTyped-data domain version.

Properties

PropertyTypeDescription
_TYPE_HASHbytes32EIP-712 domain type hash.
_hashedNamebytes32Keccak256 hash of the configured name.
_hashedVersionbytes32Keccak256 hash of the configured version.
_cachedDomainSeparatorbytes32Cached domain separator built at construction.
_cachedChainIduint256Chain id captured at construction.
_cachedThisaddressContract address captured at construction.

Methods

MethodDescription
domainSeparatorV4Returns the current domain separator, cached when safe and rebuilt after a fork.
_buildDomainSeparatorBuilds the typed-data domain separator from the cached name, version, chain id, and contract address.
_hashTypedDataV4Produces the typed-data digest for one struct hash.

ECDSA

layerx/contracts/src/lib/ECDSA.sol

ECDSA performs secp256k1 signer recovery for the vault’s operator co-signed balance proofs. It explicitly rejects high-s signatures, invalid v values, and zero-address recovery.

Properties

PropertyTypeDescription
_HALF_Nbytes32Upper-half secp256k1 bound used to reject malleable signatures.

Methods

MethodDescription
recoverRecovers a signer from a packed 65-byte signature.
recoverRecovers a signer from split v, r, and s values.

Error surface

  • InvalidSignature for invalid v values or zero-address recovery.
  • InvalidSignatureLength for signatures that are not 65 bytes.
  • InvalidSignatureS for malleable high-s signatures.

Governed

layerx/contracts/src/lib/Governed.sol

Governed implements the two-step governance handoff used by SettlementAnchor and LayerXVault. The current governor can start a transfer, but the new governor must accept it explicitly.

Constructor dependencies

TypeDescription
address governor_Initial protocol root authority.

Properties

PropertyTypeDescription
governoraddressCurrent governor.
pendingGovernoraddressGovernor address waiting to accept control.

Methods

MethodDescription
transferGovernanceStarts a governance transfer to a new governor.
acceptGovernanceCompletes the transfer when called by the pending governor.

Error surface

  • NotGovernor when a non-governor calls a governor-gated path.
  • NotPendingGovernor when a non-pending governor attempts acceptance.
  • ZeroAddress when a zero address is provided where a real address is required.

Pausable

layerx/contracts/src/lib/Pausable.sol

Pausable is the emergency-stop primitive used by the vault. It exposes the pause state and the internal toggles that the vault’s governor or guardian-controlled wrappers call.

Properties

PropertyTypeDescription
_pausedboolInternal paused flag.

Methods

MethodDescription
pausedReturns the current paused state.
_pauseSets the paused flag and emits Paused.
_unpauseClears the paused flag and emits Unpaused.

Error surface

  • EnforcedPause when a whenNotPaused path is called while paused.
  • ExpectedPause when a whenPaused path is called while not paused.

SafeERC20

layerx/contracts/src/lib/SafeERC20.sol

SafeERC20 is the token-call wrapper used everywhere value moves. It tolerates ERC-20 variants that return no boolean, rejects failures and false returns, and handles the allowance reset-to-zero path required by USDT-style tokens.

Methods

MethodDescription
safeTransferTransfers tokens and reverts on any failure shape.
safeTransferFromTransfers tokens from one account to another with failure checking.
forceApproveSets an allowance, retrying through zero when the token requires a reset.
_callOptionalReturnPerforms the low-level call and validates the optional boolean return.
_callOptionalReturnBoolPerforms the same call pattern but returns a success flag for forceApprove.

Error surface

  • SafeERC20FailedOperation when the ERC-20 call reverts, returns false, or targets a non-contract.

LayerXVault

layerx/contracts/src/LayerXVault.sol

LayerXVault is the on-chain custody and settlement contract that the deployment script wires behind SettlementAnchor. It holds the USDL reserve, mints USDX accounting against deposits, executes settlement payouts, and provides the unilateral force-exit escape hatch bounded by the operator’s last co-signed balance proof.

Constructor dependencies

TypeDescription
address usdl_Canonical reserve asset.
address governor_Protocol root authority.
address operator_Sequencer key that submits settlement batches and signs balance proofs.
address guardian_Emergency pause authority.
address anchor_Immutable settlement-root log.
address dexRouter_Paxeer DEX router for swap-based deposits.
address wrappedNative_Wrapped native asset used for native deposits.
uint64 exitDelay_Force-exit challenge window.

Properties

PropertyTypeDescription
MAX_PAYOUTSuint256Upper bound on settlement payouts per batch.
MIN_EXIT_DELAYuint64Minimum allowed exit delay.
MAX_EXIT_DELAYuint64Maximum allowed exit delay.
_BALANCE_PROOF_TYPEHASHbytes32EIP-712 type hash for balance proofs.
usdlIERC20Canonical reserve token.
operatoraddressSettlement operator and balance-proof signer.
guardianaddressEmergency pause authority.
dexRouterIPECORRouterRouter used by deposit swap paths.
anchorISettlementAnchorAnchor contract that records batch roots.
wrappedNativeIWrappedNativeWrapped native token used by native deposits.
exitDelayuint64Force-exit challenge window.
maxSettlementPerBatchuint256Settlement cap, with zero meaning uncapped.
totalDepositeduint256Cumulative USDL credited through deposits.
totalSettledOutuint256Cumulative USDL paid out through settlement.
totalExiteduint256Cumulative USDL paid out through force-exit.
swapAllowedmapping(address => bool)Allowlist for swap-deposit input tokens.
settledBatchmapping(bytes32 => bool)Batch idempotency flag.
pendingExitmapping(address => Exit)Pending exit per account.
claimedEpochmapping(address => uint64)Highest finalized epoch for each account.
exitedmapping(address => bool)Accounts barred from further settlement after force-exit.

Nested struct

PropertyTypeDescription
amountuint256Exit amount.
epochuint64Operator proof epoch.
claimableAtuint64Timestamp after which finalization is allowed.

Methods

MethodDescription
receiveAccepts native PAX only from the wrapped-native contract.
depositUSDLDeposits USDL directly and mints USDX 1:1.
depositSwapDeposits an allowlisted token, swaps it to USDL, and mints USDX from the realized output.
depositNativeWraps native PAX, swaps to USDL, and mints USDX from the realized output.
settleApplies one settlement batch, anchors the root, and pays the batch recipients.
initiateExitStarts a force-exit using the operator’s last co-signed balance proof.
challengeExitReplaces a pending exit with a strictly newer operator co-signed balance.
finalizeExitCompletes a matured exit and pays the account.
setOperatorRotates the operator.
setGuardianRotates the guardian.
setDexRouterRotates the DEX router.
setAnchorRotates the anchor contract reference.
setWrappedNativeRotates the wrapped-native reference.
setExitDelayUpdates the exit challenge window within the configured bounds.
setMaxSettlementPerBatchUpdates the per-batch settlement cap.
setSwapAllowedUpdates the token swap allowlist.
pausePauses deposit, settlement, and exit paths when called by the guardian or governor.
unpauseClears the paused state when called by the governor.
reserveBalanceReturns the live USDL reserve balance.
hashBalanceProofReturns the EIP-712 digest for a balance proof.

Error surface

LayerXVault defines a broad validation surface: NotOperator, NotGuardianOrGovernor, ZeroAmount, TokenNotAllowed, SameTokenSwap, SlippageExceeded, DeadlineExpired, RouterUnset, WrappedNativeUnset, BatchAlreadySettled, TooManyPayouts, SettlementCapExceeded, InsufficientReserve, AccountExited, InvalidProofSigner, NotProofAccount, ProofExpired, StaleEpoch, NoPendingExit, ExitNotMatured, ExitDelayOutOfRange, NativeValueMismatch, plus inherited ZeroAddress from Governed.

The main runtime patterns are:

  • deposit paths measure token balance deltas before and after transfer, so fee-on-transfer surprises do not inflate minted accounting;
  • swap deposits use forceApprove and then reset the router allowance back to zero;
  • settlement anchors the root before token transfers, while the whole call is protected by reentrancy control;
  • force-exit uses EIP-712 typed data and ECDSA.recover to validate the operator’s co-signature.

Test Coverage

SettlementAnchor behavior

layerx/contracts/test/SettlementAnchor.t.sol

This test file proves the anchor contract’s core invariants:

  • a writer can call record and the root becomes visible through rootOf;
  • non-writers are rejected;
  • the same batch id cannot be recorded twice;
  • zero roots are rejected;
  • the governor can rotate the writer and the new writer can anchor subsequent batches.

SafeERC20 behavior

layerx/contracts/test/SafeERC20.t.sol

This file exercises the token-safety wrapper with a harness that exposes the library functions:

  • standard ERC-20 transfers succeed;
  • USDT-style tokens that return no boolean are accepted;
  • transferFrom works with the same non-bool behavior;
  • forceApprove transparently handles the reset-to-zero path;
  • calling into an address with no code reverts instead of silently succeeding.

Vault wiring behavior

layerx/contracts/test/LayerXVault.t.sol

The vault test suite covers the deployment-relevant runtime paths:

  • direct USDL deposits mint 1:1 and grow the reserve balance;
  • swap deposits accept allowlisted input tokens and enforce slippage and deadline checks;
  • native deposits wrap and swap through the configured wrapped-native and router path;
  • settlement is idempotent, capped, reserve-bounded, and barred for exited accounts;
  • force-exit covers initiate, challenge, and finalize, including epoch checks and proof validation;
  • pause and unpause obey the governor and guardian access rules;
  • governance transfer remains two-step;
  • the reentrancy guard blocks a malicious deposit token from re-entering mid-flow;
  • exit-delay bounds are enforced at construction and on later updates.