Skip to main content

Offer Struct

Created when a seller calls createOffer. Fields evolve as the offer is filled or cancelled. Records persist after the offer is consumed for historical queries.
seller
address
The wallet address that created the offer and deposited the collateral. This is the address that receives quote token payments when the offer is filled and that is authorized to cancel the offer.
asset
address
The ERC-20 token contract address of the asset being sold. This is the token that the seller has deposited as collateral and that buyers (or claim token holders) ultimately receive.
quote
address
The ERC-20 token contract address accepted as payment. Quote tokens are never held by the venue — they are transferred directly to the seller and fee recipient at fill time.
allowedBuyer
address
The address permitted to fill this offer. When set to address(0), the offer is public and any address may fill it. When set to a specific address, the offer is private and only that address can execute a fill.
feeRecipient
address
The address that receives the protocol fee for fills against this offer. Snapshotted from the venue’s feeRecipient at offer creation time so that later changes to the venue’s fee recipient do not affect existing offers.
assetRemaining
uint256
The amount of escrowed asset collateral that has not yet been filled or cancelled. This value starts at the actual amount received by the venue (which may differ from the requested amount for fee-on-transfer tokens) and decreases as fills occur. When it reaches 0, the offer becomes inactive.
pricePerUnit
uint256
The quote-token cost per one whole unit of the asset (i.e., per 10^assetDecimals of the asset token). See the Price Computation section below for how fill costs are calculated.
expiry
uint64
A Unix timestamp after which the offer can no longer be filled. The offer reverts any fill attempt where block.timestamp >= expiry. Expiry must be in the future at creation time.
unlockTime
uint64
A Unix timestamp controlling the settlement mode:
  • 0 — Spot offer. Assets transfer directly to the buyer at fill time.
  • Non-zero — Deferred (forward) offer. Assets remain in escrow until the unlock time; buyers receive ERC-20 claim tokens at fill time and redeem them for the underlying collateral after block.timestamp >= unlockTime. Must satisfy unlockTime >= expiry.
assetDecimals
uint8
The decimal precision of the asset token, cached at offer creation. Used in the price calculation formula to convert pricePerUnit to the correct quote amount.
feeBps
uint16
The fee rate in basis points, snapshotted from the venue’s feeBps at offer creation time. This means the fee rate is locked for the lifetime of the offer, even if the admin changes the venue-level fee later.
assetFilled
uint256
The cumulative amount of asset that has been filled so far across all partial fills. Used in the cumulative price calculation to ensure consistent pricing regardless of fill sequence.
quotePaid
uint256
The cumulative quote tokens paid across all fills. Used with assetFilled in the cumulative mulDiv formula to ensure mathematically precise split-fill pricing.
active
bool
true while the offer is open for fills. Set to false when either the offer is fully filled (assetRemaining == 0) or the seller cancels.

Series Struct

A Series record is created automatically the first time a deferred offer is filled for a novel (asset, unlockTime) pair. All deferred offers that share these two parameters contribute to the same series and share the same claim token contract.
asset
address
The ERC-20 asset token address that defines this series.
unlockTime
uint64
The unlock timestamp shared by all offers in this series. Claim tokens for this series become redeemable once block.timestamp >= unlockTime.
claimToken
address
The address of the ERC-20 claim token contract deployed for this series by the venue on first fill. Buyers who fill deferred offers in this series receive tokens at this address, and redemption burns tokens from this contract.
backing
uint256
The total amount of asset collateral currently backing outstanding claim tokens for this series. This counter increases when fills mint claim tokens and decreases when holders redeem. The protocol enforces that claimToken.totalSupply() == series.backing as an invariant.

SeriesId Derivation

The seriesId is computed deterministically from the two fields that define a series:
bytes32 seriesId = keccak256(abi.encode(asset, unlockTime));
The series key uses abi.encode (not abi.encodePacked) and is derived from only the asset address and unlock time — not the quote token. This means all deferred fills for the same asset with the same unlock time share a single fungible claim token, regardless of which quote token was used for payment.
A convenience function is available on the contract:
function seriesIdFor(address asset, uint64 unlockTime) external pure returns (bytes32);

OfferId

Offer IDs are sequential unsigned integers (uint256), assigned from a nextOfferId counter that starts at 0 and increments by one for each new offer. This means:
  • The first offer created on the venue has offerId = 0, the second has offerId = 1, and so on.
  • You can enumerate all offers by iterating from 0 to nextOfferId - 1.
  • You can predict the next offer ID by reading the nextOfferId public variable.
uint256 public nextOfferId;

Price Computation

The pricePerUnit field represents the quote-token cost per one whole unit of the asset token (i.e., per 10^assetDecimals base units). When computing the quote cost for a fill, the contract uses a cumulative pricing approach with full-precision integer arithmetic:
uint256 newFilled = assetFilled + assetUnits;
uint256 cumulativeQuote = Math.mulDiv(newFilled, pricePerUnit, 10 ** assetDecimals);
uint256 quoteCost = cumulativeQuote - quotePaid;
This cumulative approach ensures that split fills produce exactly the same total quote cost as a single fill of the same total size — there is no rounding drift regardless of how many partial fills occur or in what order.
The venue provides a view function quoteCostFor(offerId, assetUnits) that computes the exact quote cost for a prospective fill without sending a transaction. Use this to preview costs before submitting fills.

Fee Calculation

The protocol fee is computed on the quote cost and paid by the buyer on top of the seller’s proceeds:
uint256 fee = Math.mulDiv(quoteCost, feeBps, 10_000);
The buyer’s total outlay is quoteCost + fee (plus any transfer fees if either token is fee-on-transfer). The fee is transferred directly to the offer’s snapshotted feeRecipient.