Core Design Principles
Checks-Effects-Interactions (CEI)
Every state-modifying function in the OTCVenue updates internal state — balances, fill records, offer status — before making any external token transfer calls.
assetRemaining is decremented and assetFilled is incremented before any safeTransfer or safeTransferFrom. This strict ordering means that even if an external call triggers a reentrant callback, the contract’s internal state already reflects the outcome of the current operation.Reentrancy Protection
All state-modifying functions carry explicit reentrancy guards via OpenZeppelin’s
ReentrancyGuard. This dual-layer defense (CEI + guard) ensures safety even when interacting with ERC-777 tokens or other callback-capable token contracts whose hooks might attempt to call back into the venue mid-execution. The guard causes the transaction to revert immediately rather than proceeding with corrupted state.Full Collateralization
An offer is not visible or fillable until the seller’s assets are fully escrowed in the contract. Canopy does not support speculative or credit-based offers of any kind. Every fillable order represents real, on-chain collateral that has already been transferred and verified. Buyers face no counterparty risk on the asset leg — the tokens they expect to receive are already locked.
Cumulative Precision Arithmetic
Fill costs are computed using a cumulative
mulDiv approach: quoteCost = mulDiv(newFilledTotal, pricePerUnit, 10^assetDecimals) - previousQuotePaid. This means split fills produce exactly the same aggregate cost as a single fill of the same total size. An attacker cannot craft fill sequences that exploit rounding to extract more assets than they paid for.Measured Escrow Deposits
When a seller deposits collateral, the venue records the actual received balance — the difference in the contract’s token balance before and after the transfer — rather than the nominal amount the seller declared. This protects the protocol against fee-on-transfer tokens and other transfer mechanics that deliver less than the stated amount. The escrow record always reflects what is truly held.
Measured Outbound Transfers
Outbound transfers (to buyers at fill time, to sellers at cancellation, and to redeemers) are also measured. The venue verifies that its own balance decreased by exactly the stated amount and that the recipient’s balance increased. If these checks fail — indicating a token with unsupported transfer behavior — the transaction reverts with
UnsupportedTransferBehavior().Cumulative Fill Accounting
Partial fills are tracked cumulatively per offer (
assetFilled, quotePaid) rather than computed as running deltas. The contract validates that assetUnits <= assetRemaining on every fill. When assetRemaining reaches zero, the offer is deactivated. This prevents double-counting and makes it structurally impossible for aggregate fills to overdraw the escrow.Fee Snapshotting
Each offer captures
feeBps and feeRecipient at creation time. This means the fee terms for an offer are immutable once created — an admin changing the venue’s fee rate cannot retroactively affect existing offers.Permissionless and Unpausable Redemption
No single party — including the protocol owner, the original seller, or any other privileged address — can block or gate claim redemptions after the unlock time. The
redeem and redeemWithMinimum functions carry the nonReentrant modifier but not the whenEntryOpen modifier. Even when the entry pause is active, redemption remains fully operational.Series Isolation
Each series in Canopy maintains its own independentbacking counter. The backing and claim supply for one series are entirely separate from every other series. A bug, an accounting imbalance, or an unexpected token behavior in one series has no access to and no effect on the collateral held in any other series. This isolation is structural — there are no shared balance pools or cross-series accounting entries — so you can reason about each series as a self-contained unit.
Trust Model Summary
Understanding what Canopy does and does not hold in trust is essential for integrating or auditing the protocol.| Property | Detail |
|---|---|
| Quote token custody | The venue does not custody quote tokens. Quote payments flow directly from buyer to seller (and fee recipient) at fill time. |
| Fund flow | Assets move directly between counterparties at the moment of fill; there is no intermediate holding period for quote tokens. |
| Escrowed assets | Only the asset being sold is held in escrow, and only for the duration between offer creation and fill/cancel/redemption. |
| Administration | Owner permissions are intentionally limited to fee configuration and entry pause. See Admin Controls for a full breakdown. |
| Ownership model | The venue uses OpenZeppelin’s Ownable2Step, requiring a two-step ownership transfer for safety. |