# 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*](/t-rex-network/t-rex-apps/dino/dino-secondary/admin-and-management.md) page.

### Contract Surface

At a high level, the public interface exposed to integrators is:

```solidity
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);
}
```

{% hint style="info" %}
**Note:** The implementation also integrates AccessManager roles, pausing and ecosystem fee collection. Those are configured via the factory and documented in the DinoFactory and Admin pages.
{% endhint %}

### Core Data Structures

#### Fee

Represents a fee configuration used by DinoSecondary:

| Field          | Type      | Description                                                                                |
| -------------- | --------- | ------------------------------------------------------------------------------------------ |
| `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**, `feeAmount` is the **exact amount** of `feeToken` charged per `takeOffer`.
* For **volume-based fees**, `feeAmount` represents 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 on `expiry` timestamp and/or pruned.

#### Offer

Represents a single order in the book:

| Field             | Type          | Description                                              |
| ----------------- | ------------- | -------------------------------------------------------- |
| `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

```solidity
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**

1. Caller checks prerequisites and approves tokens for DinoSecondary (for the ERC-20 leg).
2. Contract validates:
   * `amountOut` and `amountIn` are non-zero.
   * `expiry` is in the future.
   * At least one leg is the ERC-3643 token bound to this DinoSecondary instance.
3. 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).
4. Offer struct is stored and a unique `offerID` is assigned (monotonically increasing).
5. Ecosystem fee is charged via the T-REX fee collector.
6. `OfferCreated` event is emitted.

**Common errors**

* `ZeroAmount()` – if one of the amounts is 0.&#x20;
* `InvalidExpiry(expiry)` – if expiry is in the past or otherwise rejected.
* `InvalidToken(token)` – if either token address is not allowed.&#x20;

#### takeOffer

```solidity
function takeOffer(uint256 offerID, uint256 amount) external;
```

**Purpose**

Accept an existing offer (partially or fully) and execute the trade.

**High-level flow**

1. Validates that:
   * `offerID` refers to an existing offer.
   * Offer is Active and not expired.
   * `amount` is > 0.
2. Computes the proportional `amountIn` based on original price ratio.
3. 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 `transferFrom` logic to the maker.&#x20;
   * ERC-20 leg: tokens move via standard `transferFrom`/escrow logic.
4. Fees:
   * **Volume-based fee** applied on the non-ERC-3643 leg in basis points (`volumeFee().feeAmount`).
   * **Fixed fee** charged to the taker in `fixedFee().feeToken` and sent to `fixedFee().feeCollector`.
   * Ecosystem fee collected via the fee collector.
5. Offer state updated:
   * `filledAmountOut` / `filledAmountIn` updated.
   * Status set to `Filled` if fully executed; otherwise remains `Active`.
6. Events:
   * `OfferPartiallyFilled` if there is remaining liquidity.
   * `OfferFilled` if 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.&#x20;

#### cancelOffer

```solidity
function cancelOffer(uint256 offerID) external;
```

**Purpose**

Allow the **offer creator** to cancel an active offer.

**Behavior**

* Only the original `offerCreator` can 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`.
* `OfferCancelled` event is emitted.

**Common errors**

* `InvalidOfferId(offerID)` – no such offer.
* `OfferNotCreatedBySender(offerID, sender)` – caller is not the original maker.

#### pruneOffer

```solidity
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.&#x20;

### Fee Configuration & Getters

#### setFixedFee

```solidity
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 of `feeToken` charged on each `takeOffer`.

Reverts with:

* `VolumeBasedFeeTooHigh` does *not* apply here.
* May revert with `ZeroAddress` (common error) if token or recipient is zero.&#x20;

#### setVolumeBasedFee

```solidity
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.
* `basisPoints` is capped at 100; higher values revert with `VolumeBasedFeeTooHigh`.

#### Fee getters

```solidity
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.&#x20;

{% hint style="info" %}
For the **ecosystem fee**, see DinoFactory / Global Fees docs – it is collected separately by the T-REX Ecosystem Fee Collector.
{% endhint %}

### Read-Only Helpers

#### TREX

```solidity
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

```solidity
function getOfferExpiry(uint256 offerID) external view returns (uint256);
```

Shortcut to read only the `expiry` field for a given offer.

#### getCurrentOfferID

```solidity
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.&#x20;

#### getOfferDetails

```solidity
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.&#x20;

#### Trading events

<table><thead><tr><th width="417.32421875">Event</th><th>When it fires</th><th>Key fields</th></tr></thead><tbody><tr><td><code>OfferCreated(uint256 offerID, address offerCreator, address tokenOut, uint256 amountOut, address tokenIn, uint256 amountIn, uint256 creationTime, uint256 expiry)</code></td><td>A new offer is created</td><td><code>offerID</code>, maker, tokens and amounts, timestamps</td></tr><tr><td><code>OfferPartiallyFilled(uint256 offerID, uint256 amount, uint256 remainingAmount, address taker)</code></td><td>An offer is filled but liquidity remains</td><td><code>amount</code> filled in this trade, remaining, taker</td></tr><tr><td><code>OfferFilled(uint256 offerID, address taker)</code></td><td>Offer is fully filled</td><td>Final taker</td></tr><tr><td><code>OfferCancelled(uint256 offerID)</code></td><td>Maker cancels the offer</td><td><code>offerID</code></td></tr><tr><td><code>OfferPruned(uint256 offerID)</code></td><td>Offer is administratively pruned</td><td><code>offerID</code></td></tr></tbody></table>

#### Fee events

| Event                                                                 | Description                       |
| --------------------------------------------------------------------- | --------------------------------- |
| `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)` from `Pausable`.&#x20;
* `AuthorityUpdated(address authority)` from `AccessManaged` when the AccessManager is changed.

These are particularly useful for indexers and monitoring dashboards.

### Error Reference

| Error                                                               | Typical cause                                      |
| ------------------------------------------------------------------- | -------------------------------------------------- |
| `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. IDinoSecondary
* Index **events** (`OfferCreated`, `OfferPartiallyFilled`, `OfferFilled`, `OfferCancelled`, `OfferPruned`) as your primary source of truth, and reconcile with `getOfferDetails` for 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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.t-rex.network/t-rex-network/t-rex-apps/dino/dino-secondary/developer-reference.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
