Skip to main content
Approve the asset token, then call createOffer to escrow collateral and register the offer atomically.

Prerequisites

Before calling createOffer, you must grant the OTCVenue contract an ERC-20 allowance for at least assetAmount of the token you intend to sell. Without this approval the contract’s transferFrom call will revert.
For fee-on-transfer tokens, approve the nominal amount you want to deposit. The venue measures the actual balance change after the transfer and escrows only what it receives. The assetAmount you pass sets the ceiling, not the guaranteed escrow balance.

Function Signature

Parameters

address
required
The ERC-20 token you are selling. This token must be approved for transfer to the venue contract before calling createOffer. Cannot be address(0) and cannot be the same as quote.
address
required
The ERC-20 token you will accept as payment. Buyers must hold and approve this token to fill your offer. Cannot be address(0).
uint256
required
The total amount of collateral to deposit, in the asset token’s native decimals. Must be greater than zero. For standard ERC-20 tokens this equals the amount escrowed. For fee-on-transfer tokens, the actual escrowed amount may be less because the contract records the net balance received.
uint256
required
The quote-token cost per one whole unit of the asset token. One “whole unit” equals 10^assetDecimals base units. Must be greater than zero.For example, to sell a token with 18 decimals at a price of 0.43 USDC (6 decimals) per token:
  • One whole unit = 10^18 base units of the asset
  • pricePerUnit = 430000 (0.43 × 10^6 USDC base units)
The quote amount charged for any fill is calculated as:
uint64
required
Unix timestamp after which the offer can no longer be filled. Must be in the future (expiry > block.timestamp). For deferred offers, expiry must also be less than or equal to unlockTime.
uint64
required
Controls the settlement mode of the offer:
  • 0 — Spot offer. Assets transfer directly to the buyer at fill time.
  • Non-zero Unix timestamp — 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 the unlock timestamp. Must be in the future and must satisfy expiry <= unlockTime.
address
required
Controls who can fill the offer:
  • address(0) — Public offer; any address may fill.
  • Specific address — Private bilateral offer; only that address may fill.

Return Value

uint256
A sequential identifier for the newly created offer, assigned from the venue’s nextOfferId counter. Store this value — you will need it to fill, query, or cancel the offer.

Events

The venue emits the following event upon successful offer creation:

Validation Rules

createOffer will revert with InvalidParams() if any of the following are true:
  • asset is address(0)
  • quote is address(0)
  • asset == quote
  • assetAmount == 0
  • pricePerUnit == 0
  • expiry <= block.timestamp (expired or current block)
  • unlockTime != 0 and unlockTime <= block.timestamp (deferred but already past)
  • unlockTime != 0 and expiry > unlockTime (fills accepted after unlock)
  • Asset token’s decimals() > 77
Additionally, the call reverts with ZeroReceived() if the actual amount received by the venue after the transfer is zero.

Example: Creating a Public Spot Offer

The following TypeScript snippet uses ethers.js v6 to approve collateral and create a public spot offer selling 500,000 TECH tokens at 0.43 USDC each.
For private bilateral offers, set allowedBuyer to the buyer’s wallet address instead of address(0). Only that address will be able to call fill on your offer — all other callers will revert with NotAllowedBuyer().
Once createOffer succeeds, the collateral is escrowed in the venue contract and is no longer in your wallet. You can only recover it by calling cancel, which returns the unfilled portion of the escrow. Any portion that has already been filled is settled and cannot be reversed.
The fee rate and fee recipient are snapshotted into the offer at creation time. Even if the admin changes the venue’s fee rate later, your offer’s fills will always use the rate that was active when you created the offer.