Contract architecture, state management, access control, and function reference for CommodityCustody.
The Commodity Orderbook suite consists of a single deployable contract (CommodityCustody) with external dependencies on commodity tokens, USDC, a LiquidationRouter, and optionally a lending market. This page covers the contract’s architecture, state layout, and complete function reference.
CommodityCustody maintains an internal ledger of user balances using a keccak256(user, token) key scheme. Deposits move tokens from the user into the contract and credit the internal balance. Withdrawals debit the internal balance and transfer tokens out. Trade settlements move balances between users internally without any on-chain token transfer.All tokens - commodity tokens and USDC alike - are registered and treated uniformly via addSupportedToken. There is no compliance classification flag. Commodity tokens enforce their own compliance (KYC, whitelist/blacklist, frozen accounts, pause state) inside their transfer() and transferFrom() implementations. If the custody contract or user fails a compliance check, the commodity token reverts the transfer directly.
The IdentityRegistry is never called directly by CommodityCustody. It is referenced indirectly through the commodity token’s transfer validation when KYC is required on that token.
StockCustody performs pre-transfer compliance checks via ISecurityToken.canTransfer() and classifies tokens as compliance-checked or standard. CommodityCustody does not - commodity tokens enforce compliance inside their own transfer() and transferFrom() implementations. The custody contract has no complianceChecked flag and no checkTransferability function. If a compliance check fails, the commodity token reverts the transfer with its own error codes, not CommodityCustody errors.
function addSupportedToken(address token) external onlyAdminfunction removeSupportedToken(address token) external onlyAdmin
Both commodity tokens and settlement tokens (USDC) are registered the same way. There is no compliance classification - commodity tokens enforce their own compliance in transfer() and transferFrom().Removing a token from the supported list does not lock user funds. Users can still withdraw unsupported tokens. New deposits and trade settlements for the removed token are blocked.
function deposit(address token, uint256 amount) external nonReentrant tokenSupported(token)
Transfers tokens from the caller into custody via transferFrom. For commodity tokens, the token itself enforces KYC, whitelist/blacklist, frozen, and pause checks during the transfer. If any check fails, the commodity token reverts with its own error codes. The caller must have approved the custody contract for the deposit amount.
Copy
function withdraw(address token, uint256 amount) external nonReentrant
Withdraws available (unlocked) tokens from custody via transfer. The commodity token enforces compliance during the transfer. Blocked if the user is frozen in custody or withdrawals are globally paused. Allows withdrawal of unsupported tokens to prevent fund lockout after token removal.
There is no checkTransferability pre-check function on CommodityCustody. Commodity token compliance failures surface as reverts from the token contract itself during deposit or withdraw. Integrate with the commodity token’s compliance view functions directly (e.g., check whitelist status, KYC verification, frozen state) before submitting deposit or withdrawal transactions.
Atomically transfers locked base tokens from seller to buyer and locked quote tokens from buyer to seller. These are internal ledger movements only - no on-chain token transfers occur. Both parties must have sufficient locked balances. The settlementId must be unique - reuse reverts with SettlementAlreadyProcessed.
Settles multiple trades in one transaction. Skips already-processed settlement IDs. Includes a gas guard that stops processing if remaining gas drops below 80,000 per iteration. Emits BatchSettled(batchId, settledCount, totalSubmitted) so callers can detect partial completion.
The batchSettleTrades function takes 9 calldata array parameters, which exceeds the legacy compilation pipeline’s stack limit. The Solidity compiler must use viaIR: true. See Integration for compiler configuration.
Priority settlement for liquidated positions. The liquidated user’s tokens must be locked first. Use freezeUser() before locking to prevent withdrawal front-running between the lock and settlement steps. Emits both PrioritySettlement and TradeSettled.
Called by the liquidation router to deliver seized collateral into custody. Tokens are pulled from the router (which must have approved the custody contract) and credited to an internal liquidation pool keyed to address(this).
Settles after the operator sells tokens through an external venue. Commodity tokens transfer to tokenRecipient - the commodity token enforces its own compliance during this transfer. USDC is pulled from the operator and sent to the liquidation router. The lending market receives a receiveLiquidationProceeds callback. If the callback reverts, settlement still completes.
Matches liquidation tokens with a buyer who has USDC in custody. Tokens move from the liquidation pool to the buyer’s custody balance (internal ledger operation, no on-chain token transfer). USDC is deducted from the buyer’s internal balance and transferred on-chain to the router. Supports partial fills - the liquidation remains open until the full amount is matched. The lending market is notified via callback.
function freezeUser(address user) external onlyOperatorfunction unfreezeUser(address user) external onlyOperator
Per-user withdrawal freeze at the custody level. Used to prevent front-running during liquidation or for compliance holds. Does not affect the user’s ability to have their balances settled by the operator. Independent of the commodity token’s own freeze mechanism.
Copy
function pauseWithdrawals() external onlyAdminfunction unpauseWithdrawals() external onlyAdmin
Global withdrawal circuit breaker. Blocks all withdrawals for all users.
Commodity token compliance failures (KYC, whitelist, blacklist, frozen, paused) cause the token’s own transfer or transferFrom call to revert with the commodity token’s error codes, not CommodityCustody errors. Your integration layer must handle both error sources when processing deposits and withdrawals.
Callback interface that lending market contracts must implement to receive liquidation settlement notifications.
Copy
interface ILendingMarket { function receiveLiquidationProceeds(uint256 liquidationId, uint256 amount) external;}
Called by CommodityCustody when a liquidation is settled (both settleLiquidation and settleLiquidationWithBuyer). If the callback reverts, settlement still completes. Off-chain systems must reconcile via LiquidationSettled or LiquidationSoldToBuyer events.