For the complete documentation index, see llms.txt. This page is also available as Markdown.

Core Contracts Reference

Technical reference for the V2 core contracts: USDx (the token) and the market contract, USDxMarket + MarketConfig. Axis is a market protocol, not a stablecoin-minting protocol: USDxMarket is a market contract, not a minting contract, it exposes buy/sell (market-order) settlement of USDx, the set of supported assets, EIP-712 signature verification, and the per-block max-mint / max-redeem limits. Minting and redemption are simply the buy and sell sides of that market. For design rationale, see Architecture. For the yield-token vault and its ERC-7540 staking functions, see Staking Contracts. For the full role map, see Access Control. For at-launch addresses, see Contract Addresses. Terminology follows the canonical Protocol Taxonomy.

All three contracts are upgradeable behind TransparentUpgradeableProxy (transparent proxy + ProxyAdmin); each implementation disables its initializers in the constructor and exposes a one-shot initialize that runs once behind the proxy and grants DEFAULT_ADMIN_ROLE. Supply, settlement policy, and capacity accounting are split across the three contracts so that no single surface holds both execution and configuration authority.


USDx

Verified source: read on Etherscan Inherits: Initializable, IUSDx, ERC20PermitUpgradeable, AccessControlUpgradeable

The Axis Dollar: an upgradeable ERC-20 (with ERC20Permit) synthetic dollar with role-gated supply, bridge, pause, and restriction controls. Every balance-changing path, transfer, mint, burn, funnels through _update, the single enforcement point for pause and blacklist checks. Supply changes are approved primary market or bridge flows, never open user actions. Issuer: Coordinate Origin Limited. 18 decimals (ERC-20 default).

Roles

Role identifiers are defined once protocol-wide and exposed as public constants on the token.

Constant
Description

MARKET_ROLE

Primary market supply authority, the only role that can call mint. Held by the USDxMarket contract so all issuance flows through settled orders. The only live launch supply authority.

BRIDGE_ROLE

Cross-chain supply authority for bridgeMint / bridgeBurn. Defined in code but intentionally not granted at initialization, no live holder at V2 launch.

RESTRICTION_ROLE

Sets and clears the blacklist via blacklist / unBlacklist. Can restrict but never mint, burn, or seize.

PAUSER_ROLE

Emergency pause/unpause of all token balance changes via pause / unpause.

DEFAULT_ADMIN_ROLE

Admin over every other role (grant/revoke). Granted to the governance admin at initialize. See Access Control for the launch key model.

State Variables

Variable
Type
Description

_paused

bool

Global pause flag (read via paused()).

_blacklisted

mapping(address => bool)

Per-account restriction flag (read via isBlacklisted()).

Functions

initialize

Sets token metadata (name "USDx", symbol "USDx", ERC20Permit name "USDx") and grants DEFAULT_ADMIN_ROLE to admin. Runs exactly once behind the proxy.

Reverts: ZeroAddress(), admin == address(0).


mint

Mint amount USDx to to. Only callable by the MARKET_ROLE holder (USDxMarket). Routes through _update, so it reverts while paused or when to is blacklisted.

Reverts: ZeroAddress(), to == address(0); plus _update guards (OperationPaused, RestrictedAddress).

Events: ERC-20 Transfer(address(0), to, amount).


burn

Destroys amount USDx from the caller's own balance. Permissionless on the caller's balance, redemption burns are effected by the market spending an approval via burnFrom. Routes through _update.


burnFrom

Spends the caller's allowance over from, then burns amount USDx from from. Used by USDxMarket to settle redemptions. Rejects a blacklisted spender before spending the allowance.

Reverts: RestrictedAddress(spender), caller is blacklisted; ZeroAddress(), from == address(0); plus _update guards.


transferFrom

Standard ERC-20 transferFrom, additionally rejecting a blacklisted spender (msg.sender) before delegating to the base implementation.

Reverts: RestrictedAddress(spender), caller is blacklisted; plus _update guards.


bridgeMint

Cross-chain supply primitive: mints amount USDx to to on this chain. Moves supply between chains without changing global supply, each chain's totalSupply() is local only; global supply is reconciled offchain. BRIDGE_ROLE has no live holder at launch.

Reverts: ZeroAddress(), to == address(0).

Events: BridgeMint(msg.sender, to, amount).


bridgeBurn

Cross-chain supply primitive: burns amount USDx from the bridge caller.

Events: BridgeBurn(msg.sender, msg.sender, amount).


blacklist / unBlacklist

Set or clear account's restriction flag. Circle-compatible adapter, enforced centrally in _update: a blacklisted address is blocked from sending, receiving, minting, and burning, and cannot be a spender. Deliberately omits USDT-style seizure, funds are frozen, never taken.

Reverts: ZeroAddress(), account == address(0).

Events: Blacklisted(account) / UnBlacklisted(account).


pause / unpause

Halt or restore all USDx balance changes. While paused, _update, approve, and permit all revert.

Events: Paused(msg.sender) / Unpaused(msg.sender).


paused / isBlacklisted

Read the pause flag and per-account restriction flag.


approve / permit / nonces

Standard ERC20 / ERC20Permit surface. approve and permit additionally revert while the token is paused (_requireNotPaused). nonces returns the permit nonce for owner.


_update (internal)

The single balance-change guard for every transfer, mint, and burn. Reverts OperationPaused() while paused, and RestrictedAddress(from) / RestrictedAddress(to) if either party is blacklisted, before delegating to the base ERC-20 update.

Events

Event
Parameters

Blacklisted

address account

UnBlacklisted

address account

Paused

address account

Unpaused

address account

BridgeMint

address caller, address to, uint256 amount

BridgeBurn

address caller, address from, uint256 amount

(Plus standard ERC-20 Transfer / Approval.)

Errors

Error
Trigger

ZeroAddress()

Zero address where a non-zero one is required

RestrictedAddress(address)

A blacklisted party in a transfer, mint, burn, or spend

OperationPaused()

Any balance change, approve, or permit while paused


USDxMarket

Verified source: read on Etherscan Inherits: Initializable, IUSDxMarket, AccessControlUpgradeable

The market contract, not a minting contract: it validates signed EIP-712 market orders and settles them exactly once, executing the buy side (mint) or sell side (redeem) of USDx against approved routes, channels, and capacity limits. Order validation, nonce use, cap checks, and asset movement are sequenced so a failed settlement consumes neither a nonce nor capacity. USDxMarket holds MARKET_ROLE on USDx (to mint / burnFrom) and SETTLEMENT_MANAGER_ROLE on MarketConfig (to consume capacity); it reads all routing, whitelist, and capacity policy from MarketConfig.

Roles

Constant
Description

MINT_OPERATOR_ROLE

May settle orders whose side == MINT. Execution authority only.

REDEEM_OPERATOR_ROLE

May settle orders whose side == REDEEM. Execution authority only.

SETTLEMENT_MANAGER_ROLE

May cancelOrder (tombstone an order hash). Manage-vs-execute is deliberately split from the operator roles.

DEFAULT_ADMIN_ROLE

Admin over the operator/manager roles; granted at initialize.

Operators execute pre-authorized orders; they cannot change what an order means. Delegation and cancellation are separated from execution.

State Variables

Variable
Type
Description

usdx

IUSDx

The USDx token contract.

marketConfig

IPrimaryMarketConfig

Policy/capacity registry (MarketConfig).

_orders

mapping(bytes32 => OrderRecord)

Per-order-hash lifecycle: {bool settled; bool cancelled; bytes32 proofId}.

_nonceBitmaps

mapping(bytes32 => mapping(uint256 => uint256))

Nonce bitmap keyed per accountId (not per signer).

_delegatedSigners

mapping(address => mapping(address => SignerStatus))

signer => source => status.

Types

The EIP-712 type string is fixed in ORDER_TYPEHASH; the domain is EIP712Domain(name="USDxMarket", version="1", chainId, verifyingContract).

Units: capacity and block limits meter USDx notional (18-decimal wei), minOutputAmount on a MINT, inputAmount on a REDEEM. Collateral amounts use the collateral token's native decimals. settle pays out exactly minOutputAmount; there is no partial fill.

Functions: Settlement

settle

Executes a valid signed order exactly once. Re-runs the full precheck (_checkSettlement), enforces the side-appropriate operator role, marks the nonce used, records the settlement proof, consumes capacity, and moves assets. Emits OrderSettled plus the side event.

Authorization: requires MINT_OPERATOR_ROLE when side == MINT, else REDEEM_OPERATOR_ROLE. Reverts AccessControlUnauthorizedAccount otherwise.

Order of operations (all-or-nothing):

  1. _checkSettlement, order shape, status (not settled/cancelled), nonce unused, signature, whitelist, accountId match, token pause/blacklist, route/channel, block limits, and multi-scope capacity. Any failure reverts via _revertForReason before state changes.

  2. Operator role check.

  3. Mark order.nonce used in the order.accountId bitmap (irreversible on success).

  4. Record proof: proofId = keccak256(orderHash, address(this), chainId, block.number); set _orders[orderHash] = {settled: true, proofId}.

  5. marketConfig.consumeCapacity and consumeBlockCapacity.

  6. MINT: transfer inputAmount of inputAsset from signer to the channel destination, then usdx.mint(receiver, minOutputAmount). REDEEM: usdx.burnFrom(signer, inputAmount), then transfer minOutputAmount of outputAsset to receiver.

Events: OrderSettled(...); then Mint(msg.sender, signer, receiver, inputAsset, inputAmount, minOutputAmount) or Redeem(msg.sender, signer, receiver, outputAsset, minOutputAmount, inputAmount).

Signer authorization accepts a direct EOA / ERC-1271 signature from order.signer, an accepted delegated ECDSA signer, or an encoded smart-account delegate signature, checked in that order. Delegation is an onchain registry (REJECTED / PENDING / ACCEPTED), not just ecrecover.


mint / redeem

Thin side-guarded wrappers over settle: mint reverts InvalidOrder() unless side == MINT, redeem unless side == REDEEM, then delegate to settle (which enforces the operator role and all checks).


checkSettlement

Non-mutating preview of whether an order can settle under current policy, routing, nonce, signature, and capacity. Returns {allowed, reasonCode, orderHash, routeId, channelId, availableNotionalAmount, requiredNotionalAmount}. settlementData is ignored by the preview. Execution re-runs every check before state changes.


cancelOrder

Tombstones an unsettled order hash so it can never settle. Cancellation does not consume the nonce; the order is invalidated via _orders[orderHash].cancelled. Signature verification is not part of cancellation.

Reverts: InvalidNonce(), the order is already settled or cancelled.

Events: OrderCancelled(orderHash, order.accountId).

Functions: Delegation

Delegation is a two-step process: the source account initiates, and the delegate confirms. A delegate cannot sign until it has accepted.

Reverts: InvalidAddress(), zero signer; SignerNotInitiated(), confirming a relationship that is not PENDING.

Events: DelegatedSignerInitiated(signer, source), DelegatedSignerAdded(signer, source), DelegatedSignerRemoved(signer, source).

Functions: Verification (view)

Function
Returns / behavior

hashOrder(Order)

EIP-712 typed-data hash over DOMAIN_SEPARATOR() and the order struct hash.

verifyOrder(Order, Signature)

Order hash if currently valid; reverts via _revertForReason otherwise.

verifyNonce(bytes32 accountId, uint256 nonce)

(slot, bitmap, bit) if the nonce is non-zero and unused; reverts InvalidNonce() otherwise.

isNonceUsed(bytes32 accountId, uint256 nonce)

true if the nonce bit is set.

orderState(bytes32 orderHash)

(settled, cancelled, proofId).

proofOfOrder(bytes32 orderHash)

The linked proofId (the full receipt is reconstructed offchain from OrderSettled).

checkMint(Order, Signature) / checkRedeem(Order, Signature)

OrderCheck{valid, code, orderHash, signer, nonceUsed, availableBlockCapacity} for the matching side.

DOMAIN_SEPARATOR() / eip712Domain()

The EIP-712 domain over name USDxMarket, version 1, chain id, and this contract.

Nonce bitmap: each uint256 slot tracks 256 nonces, keyed per accountId. Slot index nonce >> 8, bit 1 << (nonce & 0xff). Nonces are non-sequential and single-use; the bit is set only on successful settlement, and the flip is irreversible.

Functions: Registry reads (proxied to MarketConfig)

route, channel, isRouteEnabled, isChannelEnabled, isSupportedAsset, getSupportedAssets, getCustodians, isCustodian, isWhitelisted, signerAccount, checkChannel, cap, capUsage, checkCapacity, maxCapacity, getCurrentBlockCapacity, getMintingState, getUserLimit, getUserBlockCapacity, all read policy from marketConfig. delegatedSigner(signer, source) reads the market's own delegation registry.

Events

Event
Parameters

OrderSettled

proofId, orderHash, accountId, routeId, channelId, destination, channelHash, settlementHash

Mint

caller, signer, receiver, inputAsset, inputAmount, outputAmount

Redeem

caller, signer, receiver, outputAsset, outputAmount, inputAmount

OrderCancelled

orderHash, accountId

DelegatedSignerInitiated / DelegatedSignerAdded / DelegatedSignerRemoved

signer, source

Errors

Error
Trigger

InvalidUSDxAddress()

Zero USDx address in initialize

InvalidAddress()

Zero config/admin/signer address

InvalidOrder()

Malformed order or mint/redeem side mismatch

InvalidSignature()

Signer is not the account, an accepted delegate, or a valid ERC-1271 signer

SignatureExpired()

block.timestamp > order.deadline

InvalidNonce()

Nonce used/zero, or order already settled/cancelled

NotWhitelisted()

Signer/receiver not whitelisted, accountId mismatch, or a restricted token party

InvalidRoute()

Route disabled, side/asset mismatch, or amount below route minimum

InvalidChannel()

Channel disabled, channel/route mismatch, or custodian not approved

CapacityExceeded(bytes32 code)

A global/asset/route/account/channel cap is exceeded

MaxMintPerBlockExceeded() / MaxRedeemPerBlockExceeded()

Same-block circuit breaker hit

SignerNotInitiated()

Confirming a delegation that is not PENDING


MarketConfig

Verified source: read on Etherscan Inherits: Initializable, IPrimaryMarketConfig, AccessControlUpgradeable

The policy and capacity registry USDxMarket reads during settlement. Admins configure routes, channels, caps, whitelists, and signer→account bindings; the market holds SETTLEMENT_MANAGER_ROLE and is the only expected capacity consumer. Configuration follows the risk-direction rule, risk-down changes (pause capacity, disable mint/redeem) may execute immediately via EMERGENCY_ROLE; risk-up changes (re-enable, raise caps, add routes/channels) require DEFAULT_ADMIN_ROLE.

Roles

Constant
Description

DEFAULT_ADMIN_ROLE

Full configuration authority: routes, channels, caps, whitelist, signer accounts, block limits, re-enabling flows.

SETTLEMENT_MANAGER_ROLE

May consumeCapacity and consumeBlockCapacity. Held by USDxMarket so external callers cannot move cap usage.

EMERGENCY_ROLE

Risk-down only: pauseCapacity and disabling mint/redeem via setMintRedeemEnabled. Cannot re-enable or raise limits.

State Variables

Variable
Type
Description

mintEnabled / redeemEnabled

bool

Global mint/redeem settlement switches.

maxMintPerBlock / maxRedeemPerBlock

uint256

Same-block USDx notional circuit breakers.

mintedPerBlock / redeemedPerBlock

mapping(uint256 => uint256)

Per-block-number usage counters.

Internal mappings hold routes, channels, caps, cap usage, supported assets, custodians, the whitelist, signer→account bindings, and per-cap pause flags.

Types

A route (RouteState) defines a side, input/output asset pair, SettlementMode, minimum amounts, and an enabled flag, what may trade. A channel (ChannelState) binds a route to a concrete custodian and destination with its own enabled flag and channelHash, where it settles. A cap (CapConfig) sets a notional cap, WindowType, and windowSize for a CapKey (scope + subject).

Functions: Initialization

Enables mint and redeem, sets the per-block limits, grants DEFAULT_ADMIN_ROLE to admin, and registers usdx as a supported asset.

Reverts: InvalidAddress(), zero usdx or admin.

Functions: Configuration (DEFAULT_ADMIN_ROLE)

Function
Effect

setRoute(RouteState)

Register/update a route; auto-registers its input/output assets. Emits RouteSet.

setChannel(ChannelState)

Register/update a channel on an existing route with an approved custodian/destination; auto-registers the custodian. Emits ChannelSet (and CustodianAdded).

setChannelStatus(bytes32 channelId, bool enabled)

Enable/disable a channel. Emits ChannelStatusSet.

setWhitelist(address account, bool allowed)

Allow/deny an account as signer or receiver. Emits WhitelistSet.

setSignerAccount(address signer, bytes32 accountId)

Bind/clear the accountId a signer may use. Emits SignerAccountSet.

setCap(CapKey, CapConfig)

Configure a global/asset/route/account/channel cap (fixed windows require non-zero windowSize). Emits CapSet.

unpauseCapacity(CapKey)

Restore a paused cap. Emits CapacityUnpaused.

setMaxMintPerBlock / setMaxRedeemPerBlock

Update the same-block circuit-breaker limits. Emits MaxMintPerBlockChanged / MaxRedeemPerBlockChanged.

Functions: Emergency (risk-down)

pauseCapacity disables a configured cap immediately (emits CapacityPaused). setMintRedeemEnabled lets EMERGENCY_ROLE turn flows off immediately, but re-enabling a disabled flow requires DEFAULT_ADMIN_ROLE, an emergency holder that tries to risk up reverts AccessControlUnauthorizedAccount.

Functions: Capacity accounting (SETTLEMENT_MANAGER_ROLE)

Called by USDxMarket during settle to record usage across every enabled cap scope and the same-block counter after validation passes. Restricted so no external caller can move cap usage.

Functions: Reads

route, channel, isRouteEnabled, isChannelEnabled, isSupportedAsset, getSupportedAssets, getCustodians, isCustodian, isWhitelisted, signerAccount, getCurrentBlockCapacity, cap, capUsage, checkChannel, checkCapacity, maxCapacity. checkCapacity walks the caps in scope order (global → asset → route → account → channel) and returns the first failing reason code with the tightest remaining availability.

getUserLimit and getUserBlockCapacity are present for interface compatibility and currently return zeroed structs, per-user limits are not applied at launch; throughput is governed by the block limits and the five cap scopes.

Events

RouteSet, ChannelSet, ChannelStatusSet, CustodianAdded, WhitelistSet, SignerAccountSet, CapSet, CapacityConsumed, CapacityPaused, CapacityUnpaused, MaxMintPerBlockChanged, MaxRedeemPerBlockChanged, AssetAdded, AssetRemoved.

Errors

Error
Trigger

InvalidAddress()

Zero address where non-zero required

InvalidRoute()

Malformed route (zero id or asset)

InvalidChannel()

Channel with no matching route, zero custodian, or zero destination

InvalidAssetAddress()

Zero asset in the supported-asset set

InvalidAmount()

Invalid cap key/window, or capacity consumed without passing checks

AccessControlUnauthorizedAccount(address, bytes32)

Caller lacks the required role (including emergency risk-up attempts)

Last updated