Developer Reference
This page documents the on-chain interface of DinoSecondary, the secondary-market module of the DINO stack.
Each DinoSecondary instance is bound to one T-REX (ERC-3643) token and exposes a simple offer book where users can post and take bilateral offers between that T-REX token and any ERC-20 payment token. The contract supports:
Freeze-based handling for the ERC-3643 leg (tokens locked in the investor’s wallet)
Escrow-based handling for the non-ERC-3643 leg (tokens held by the contract)
Fixed and volume-based fees
Ecosystem-level fees (handled by the T-REX fee collector)
This page is aimed at dapp and integration developers. Admin-only concerns (roles, pausing, fee configuration) are covered in the Admin & Management page.
Contract Surface
At a high level, the public interface exposed to integrators is:
interface IDinoSecondary {
struct Fee {
address feeToken;
uint256 feeAmount;
address feeCollector;
}
enum OfferStatus {
Active,
Filled,
Cancelled,
Expired
}
struct Offer {
address offerCreator;
address tokenOut;
uint256 amountOut;
uint256 filledAmountOut;
address tokenIn;
uint256 amountIn;
uint256 filledAmountIn;
uint256 expiry;
OfferStatus status;
uint256 createdAt;
}
function createOffer(
address tokenOut,
uint256 amountOut,
address tokenIn,
uint256 amountIn,
uint256 expiry
) external returns (uint256 offerID);
function takeOffer(uint256 offerID, uint256 amount) external;
function cancelOffer(uint256 offerID) external;
function pruneOffer(uint256 offerID) external;
function setFixedFee(address feeToken, address feeRecipient, uint256 amount) external;
function setVolumeBasedFee(address feeRecipient, uint256 basisPoints) external;
function TREX() external view returns (address);
function fixedFee() external view returns (Fee memory);
function volumeFee() external view returns (Fee memory);
function getOfferExpiry(uint256 offerID) external view returns (uint256);
function getCurrentOfferID() external view returns (uint256);
function getOfferDetails(uint256 offerID) external view returns (Offer memory);
}Core Data Structures
Fee
Represents a fee configuration used by DinoSecondary:
feeToken
address
Token in which the fee is paid (ERC-20).
feeAmount
uint256
Meaning depends on context: fixed amount (for fixed fee) or basis points (for volume fee).
feeCollector
address
Address receiving the fee.
For fixed fees,
feeAmountis the exact amount offeeTokencharged pertakeOffer.For volume-based fees,
feeAmountrepresents the basis points (bps) applied on the non-ERC-3643 leg, capped at 100 bps (1%).
OfferStatus
Offer lifecycle states:
Active– Offer is live and can be taken.Filled– Offer is fully executed.Cancelled– Cancelled by creator.Expired– Expired based onexpirytimestamp and/or pruned.
Offer
Represents a single order in the book:
offerCreator
address
Maker address.
tokenOut
address
Token being sold (outgoing from maker).
amountOut
uint256
Total amount offered for sale.
filledAmountOut
uint256
Cumulative portion of amountOut that has been taken.
tokenIn
address
Token expected in exchange.
amountIn
uint256
Total amount expected in the counter-token.
filledAmountIn
uint256
Cumulative portion of amountIn that has been received.
expiry
uint256
UNIX timestamp after which offer is no longer tradable.
status
OfferStatus
Current status (Active/Filled/Cancelled/Expired).
createdAt
uint256
Creation timestamp.
Amounts always keep the original price ratio: partial fills are proportional and update the filledAmount* fields accordingly.
Trading Functions
createOffer
function createOffer(
address tokenOut,
uint256 amountOut,
address tokenIn,
uint256 amountIn,
uint256 expiry
) external returns (uint256 offerID);Purpose
Create a new offer to sell tokenOut for tokenIn at a fixed ratio amountIn / amountOut.
High-level flow
Caller checks prerequisites and approves tokens for DinoSecondary (for the ERC-20 leg).
Contract validates:
amountOutandamountInare non-zero.expiryis in the future.At least one leg is the ERC-3643 token bound to this DinoSecondary instance.
Token handling:
If the ERC-3643 token is being sold: tokens are frozen in the maker’s wallet (not transferred).
If the ERC-20 token is being sold: tokens are moved into escrow (held by the contract).
Offer struct is stored and a unique
offerIDis assigned (monotonically increasing).Ecosystem fee is charged via the T-REX fee collector.
OfferCreatedevent is emitted.
Common errors
ZeroAmount()– if one of the amounts is 0.InvalidExpiry(expiry)– if expiry is in the past or otherwise rejected.InvalidToken(token)– if either token address is not allowed.
takeOffer
function takeOffer(uint256 offerID, uint256 amount) external;Purpose
Accept an existing offer (partially or fully) and execute the trade.
High-level flow
Validates that:
offerIDrefers to an existing offer.Offer is Active and not expired.
amountis > 0.
Computes the proportional
amountInbased on original price ratio.Token transfers:
ERC-3643 leg:
If maker is selling ERC-3643, previously frozen tokens are unfrozen and transferred to the taker.
If taker is selling ERC-3643, tokens move via standard
transferFromlogic to the maker.
ERC-20 leg: tokens move via standard
transferFrom/escrow logic.
Fees:
Volume-based fee applied on the non-ERC-3643 leg in basis points (
volumeFee().feeAmount).Fixed fee charged to the taker in
fixedFee().feeTokenand sent tofixedFee().feeCollector.Ecosystem fee collected via the fee collector.
Offer state updated:
filledAmountOut/filledAmountInupdated.Status set to
Filledif fully executed; otherwise remainsActive.
Events:
OfferPartiallyFilledif there is remaining liquidity.OfferFilledif fully executed.
Common errors
InvalidOfferId(offerID)– offer does not exist.ZeroAmount()– taker tries to take 0.InvalidTokenTransfer(token)– failure in underlying token transfer.VolumeFeeExceedsAmount(volumeFee, amount)– volume-based fee would be larger than the traded amount.
cancelOffer
function cancelOffer(uint256 offerID) external;Purpose
Allow the offer creator to cancel an active offer.
Behavior
Only the original
offerCreatorcan cancel their own offers.Any frozen ERC-3643 tokens are released.
Any ERC-20 tokens held in escrow are returned to the maker.
Status is set to
Cancelled.OfferCancelledevent is emitted.
Common errors
InvalidOfferId(offerID)– no such offer.OfferNotCreatedBySender(offerID, sender)– caller is not the original maker.
pruneOffer
function pruneOffer(uint256 offerID) external;Purpose
Clean up offers that are no longer valid, for example:
Expired offers.
Offers that became non-compliant.
Offers where the ERC-3643 leg is no longer funded (maker removed allowance or an agent moved away the frozen balance reserved for the transfer).
Pruning is open to any caller; it updates status to Expired and emits OfferPruned. If tokens had been locked/frozen, they are freed accordingly.
Common errors
InvalidOfferId(offerID)– no such offer.
Fee Configuration & Getters
setFixedFee
function setFixedFee(address feeToken, address feeRecipient, uint256 amount) external;Admin-only function to configure the per-transaction fixed fee:
feeToken: ERC-20 token used to pay the fee.feeRecipient: address that receives fixed fees.amount: exact amount offeeTokencharged on eachtakeOffer.
Reverts with:
VolumeBasedFeeTooHighdoes not apply here.May revert with
ZeroAddress(common error) if token or recipient is zero.
setVolumeBasedFee
function setVolumeBasedFee(address feeRecipient, uint256 basisPoints) external;Admin-only function to configure the percentage-based fee:
basisPoints: rate in bps (100 = 1%) applied to the non-ERC-3643 leg of each trade.basisPointsis capped at 100; higher values revert withVolumeBasedFeeTooHigh.
Fee getters
function fixedFee() external view returns (Fee memory);
function volumeFee() external view returns (Fee memory);fixedFee()feeToken: token used for the fixed fee.feeAmount: fixed fee amount.feeCollector: address receiving fixed fees.
volumeFee()feeToken: not used for calculations (bps is applied on the trade amount).feeAmount: basis points value (0–100).feeCollector: address receiving the volume-based fee.
The implementation also provides convenience view functions for the raw recipients (fixedFeeRecipient, volumeFeeRecipient) as documented in the DinoSecondary docs.
Read-Only Helpers
TREX
function TREX() external view returns (address);Returns the ERC-3643 token address this DinoSecondary instance is bound to. Every offer must involve this token in at least one leg.
getOfferExpiry
function getOfferExpiry(uint256 offerID) external view returns (uint256);Shortcut to read only the expiry field for a given offer.
getCurrentOfferID
function getCurrentOfferID() external view returns (uint256);Returns the next offer ID that will be assigned on the next createOffer call (i.e. a counter, not the last valid ID). To iterate over all offers, index from 0 (or 1, depending on deployment conventions) up to getCurrentOfferID() - 1, combined with getOfferDetails and event history.
getOfferDetails
function getOfferDetails(uint256 offerID) external view returns (Offer memory);Returns the full Offer struct for the given offerID. Use this for:
Front-end display of the order book.
Reconciling with your indexer state.
Checking status, amounts and timestamps on-chain.
Events
DinoSecondary uses a dedicated events library for consistent logging.
Trading events
OfferCreated(uint256 offerID, address offerCreator, address tokenOut, uint256 amountOut, address tokenIn, uint256 amountIn, uint256 creationTime, uint256 expiry)
A new offer is created
offerID, maker, tokens and amounts, timestamps
OfferPartiallyFilled(uint256 offerID, uint256 amount, uint256 remainingAmount, address taker)
An offer is filled but liquidity remains
amount filled in this trade, remaining, taker
OfferFilled(uint256 offerID, address taker)
Offer is fully filled
Final taker
OfferCancelled(uint256 offerID)
Maker cancels the offer
offerID
OfferPruned(uint256 offerID)
Offer is administratively pruned
offerID
Fee events
FixedFeeSet(address feeToken, address feeRecipient, uint256 amount)
Fixed fee config updated.
VolumeBasedFeeSet(address feeRecipient, uint256 basisPoints)
Volume-based fee config updated.
FixedFeePaid(address taker, address collector, uint256 amount)
Fixed fee paid on a takeOffer.
VolumeFeePaid(address feePayer, address collector, uint256 amount)
Volume-based fee paid on a trade.
Pausable & AccessManager events
From inherited contracts:
Paused(address account)/Unpaused(address account)fromPausable.AuthorityUpdated(address authority)fromAccessManagedwhen the AccessManager is changed.
These are particularly useful for indexers and monitoring dashboards.
Error Reference
InvalidExpiry(uint256 expiry)
Expiry not acceptable (e.g. in the past).
InvalidOfferId(uint256 offerID)
Referenced offer does not exist.
InvalidToken(address token)
Token not accepted for trading.
InvalidTokenTransfer(address token)
Transfer or freeze/unfreeze failed for this token.
LimitsArraySizeExceeded(uint256 arraySize, uint256 maxSize)
(Used for batched operations, if exposed.)
OfferNotCreatedBySender(uint256 offerID, address sender)
Someone other than the maker tried to cancel.
VolumeBasedFeeTooHigh(uint256 basisPoints, uint16 maxBasisPoints)
Attempt to set volume fee above 100 bps.
VolumeFeeExceedsAmount(uint256 volumeFee, uint256 amount)
Computed volume fee greater than traded amount.
ZeroAmount()
One of the amounts is zero.
ZeroAddress()
Critical address parameter is zero.
You should surface these errors in your dapp for clearer UX (e.g. mapping them to human-readable messages).
Multicall Support
The contract supports OpenZeppelin Multicall, allowing you to batch several calls into a single transaction (for example, multiple takeOffer operations or a takeOffer + off-chain accounting update).
This is especially useful for:
Wallets wanting to group multiple fills.
Protocols executing rebalancing strategies that span several offers.
Integration Tips
Use
TREX()to ensure you are interacting with the right DinoSecondary instance for a given asset. IDinoSecondaryIndex events (
OfferCreated,OfferPartiallyFilled,OfferFilled,OfferCancelled,OfferPruned) as your primary source of truth, and reconcile withgetOfferDetailsfor on-chain verification.Never assume offers are contiguous or permanent: always be ready for cancellation, pruning and expiry.
For UX, guide users through approvals (ERC-20) and highlight when the freeze path vs escrow path applies.
Last updated