> For the complete documentation index, see [llms.txt](https://docs.t-rex.network/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.t-rex.network/t-rex-network/t-rex-apps/dino/dino-primary/developer-reference.md).

# Developer Reference

This page documents the **public interface** of the `DinoPrimary` contract:

* Solidity interface (high-level)
* Function-by-function behavior (with access control & revert reasons)
* Events & custom errors

Admin concepts (roles, issuerSafe, preminted, rules, etc.) are described on the [**Admin & Management**](/t-rex-network/t-rex-apps/dino/dino-primary/admin-and-management.md) page; here we focus on how to **call** the contract.

### Contract & Imports

```solidity
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC3643 } from "@erc3643org/erc-3643/contracts/ERC-3643/IERC3643.sol";
import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
import { IDinoPrimary } from "./interfaces/IDinoPrimary.sol";
import { IFeeCollector } from "./interfaces/IFeeCollector.sol";

contract DinoPrimary is IDinoPrimary { /* ... */ }
```

Each asset has **its own instance** of `DinoPrimary`, typically deployed and initialized via **DinoFactory**.

### State Variables (Public Getters)

`DinoPrimary` exposes a few public variables:

```solidity
IERC3643 public erc3643;
address  public issuerSafe;
bool     public preminted;
IFeeCollector public ecosystemFeeCollector;

uint256 public nonce;
mapping(address => PaymentToken) public approvedPaymentTokens;
```

* `erc3643()` – underlying ERC-3643 token for this primary rail.
* `issuerSafe()` – address receiving subscription proceeds and paying redemptions.
* `preminted()` – `true` if supply is pre-minted in a treasury and transferred; `false` if `DinoPrimary` mints/burns.
* `ecosystemFeeCollector()` – protocol fee collector.
* `nonce()` – incremented per subscription / redemption; also emitted in events.
* `approvedPaymentTokens(token)` – returns price feed and stability info for a payment token.

{% hint style="info" %}
You won’t write to these directly – use the admin functions and rely on DinoFactory for initialization.
{% endhint %}

### Solidity Interface

```solidity
interface IDinoPrimary {
    // --- Lifecycle / Setup ---

    function initialize(
        address _erc3643,
        address _issuerSafe,
        bool _preminted,
        address _accessManager,
        address _ecosystemFeeCollector
    ) external;

    // --- Investor functions ---

    function subscribe(
        uint256 amount,
        address paymentToken
    ) external returns (uint256 nonce);

    function redeem(
        uint256 amount,
        address paymentToken
    ) external returns (uint256 nonce);

    // --- Preview helpers (read-only) ---

    function previewSubscribeWithExactErc3643(
        uint256 erc3643Amount,
        address paymentToken
    ) external view returns (uint256 paymentTokenAmount);

    function previewSubscribeWithExactToken(
        uint256 paymentTokenAmount,
        address paymentToken
    ) external view returns (uint256 erc3643Amount);

    function previewRedeemWithExactErc3643(
        uint256 erc3643Amount,
        address paymentToken
    ) external view returns (uint256 paymentTokenAmount);

    function previewRedeemWithExactToken(
        uint256 paymentTokenAmount,
        address paymentToken
    ) external view returns (uint256 erc3643Amount);

    // --- Payment token management (TOKEN_MANAGER_ROLE) ---

    function addPaymentToken(
        address token,
        address priceFeed,
        bool isStable
    ) external;

    function removePaymentToken(
        address token
    ) external;

    function updatePaymentToken(
        address token,
        address priceFeed,
        bool isStable
    ) external;

    function getPaymentTokens()
        external
        view
        returns (address[] memory);

    function getPaymentTokenCount()
        external
        view
        returns (uint256);

    // --- Rules (RULES_MANAGER_ROLE) ---

    function updateSubscriptionRules(
        uint256 dateOpened,
        uint256 dateClosed,
        uint256 minAmount,
        uint256 maxAmount
    ) external;

    function updateRedemptionRules(
        uint256 dateOpened,
        uint256 dateClosed,
        uint256 minAmount,
        uint256 maxAmount,
        uint16 redemptionMalus
    ) external;

    function subscriptionRules()
        external
        view
        returns (
            bool isOpen,
            uint256 dateOpened,
            uint256 dateClosed,
            uint256 minAmount,
            uint256 maxAmount
        );

    function redemptionRules()
        external
        view
        returns (
            bool isOpen,
            uint256 dateOpened,
            uint256 dateClosed,
            uint256 minAmount,
            uint256 maxAmount,
            uint16 malus
        );

    // --- Admin (ADMIN_ROLE) ---

    function updateIssuerSafe(address newIssuerSafe) external;

    function updatePreminted(bool _preminted) external;
}
```

Access control (roles) is enforced by `AccessManaged` via an [AccessManager](https://docs.openzeppelin.com/contracts/5.x/api/access#AccessManager) contract; see [**Admin & Management**](/t-rex-network/t-rex-apps/dino/dino-primary/admin-and-management.md) for role mapping.

### Investor Functions

#### `subscribe`

```solidity
function subscribe(
    uint256 amount,
    address paymentToken
) external nonReentrant returns (uint256 nonce);
```

**Description**

Subscribes `amount` of ERC-3643 tokens using `paymentToken` as consideration.

* For **preminted** assets:
  * Transfers `amount` of ERC-3643 from `issuerSafe` to `msg.sender`.
* For **non-preminted** assets:
  * Mints `amount` of ERC-3643 to `msg.sender`.

In both cases:

* Calculates total cost using a price feed implementing the [AggregatorV3Interface](https://docs.chain.link/data-feeds/api-reference#aggregatorv3interface).
* Transfers `paymentToken` from investor to `issuerSafe`.
* Collects protocol fee via `ecosystemFeeCollector`.
* Emits `Subscribed(nonce, investor, paymentToken, amount, totalCost)`.

**Reverts if:**

* Subscriptions are closed (`SubscriptionClosed`).
* `amount < minAmount` or `amount > maxAmount` (`AmountBelowMinimum` / `AmountAboveMaximum`).
* `paymentToken` is not approved (`PaymentTokenNotApproved`).
* ERC-3643 transfer / mint is not compliant (`TransferNotCompliant`, `InvestorNotQualified`).
* Price feed issues (`InvalidPriceFeed`).
* Fee collection fails (e.g. missing approval / balance on fee token).

#### `redeem`

```solidity
function redeem(
    uint256 amount,
    address paymentToken
) external nonReentrant returns (uint256 nonce);
```

**Description**

Redeems `amount` of ERC-3643 tokens back to the issuer, and pays the investor in `paymentToken`.

* For **preminted** assets:
  * Transfers ERC-3643 from `msg.sender` back to `issuerSafe`.
* For **non-preminted** assets:
  * Burns ERC-3643 from `msg.sender` (DinoPrimary is an authorized agent).

Then:

* Computes payment amount based on NAV / price feed.
* Applies **redemption malus** (haircut) if configured.
* Transfers `paymentToken` from `issuerSafe` to investor.
* Collects protocol fee.
* Emits `Redeemed(nonce, investor, paymentToken, amount, paymentAmount)`.

**Reverts if:**

* Redemptions are closed (`RedemptionClosed`).
* `amount < minAmount` or `amount > maxAmount` (`AmountBelowMinimum` / `AmountAboveMaximum`).
* `paymentToken` not approved.
* Investor or transfer not compliant.
* Price feed invalid.
* `issuerSafe` doesn’t have enough `paymentToken` balance / allowance.
* Fee collection fails (e.g. missing approval / balance on fee token).

### Preview Functions (Read-Only Quotes)

These functions **do not modify state** and allow UIs or off-chain systems to quote amounts without executing the operation.

#### `previewSubscribeWithExactErc3643`

```solidity
function previewSubscribeWithExactErc3643(
    uint256 erc3643Amount,
    address paymentToken
) external view returns (uint256 paymentTokenAmount);
```

Returns how much `paymentToken` is required to subscribe `erc3643Amount` of the asset.

#### `previewSubscribeWithExactToken`

```solidity
function previewSubscribeWithExactToken(
    uint256 paymentTokenAmount,
    address paymentToken
) external view returns (uint256 erc3643Amount);
```

Returns how many ERC-3643 tokens the investor would receive when paying `paymentTokenAmount`.

#### `previewRedeemWithExactErc3643`

```solidity
function previewRedeemWithExactErc3643(
    uint256 erc3643Amount,
    address paymentToken
) external view returns (uint256 paymentTokenAmount);
```

Returns **net** payment after applying redemption malus for redeeming `erc3643Amount`.

#### `previewRedeemWithExactToken`

```solidity
function previewRedeemWithExactToken(
    uint256 paymentTokenAmount,
    address paymentToken
) external view returns (uint256 erc3643Amount);
```

Returns how many ERC-3643 tokens the investor needs to redeem to receive `paymentTokenAmount` **after** malus.

{% hint style="success" %}
These helpers are ideal for front-ends: quote first, then call `subscribe` / `redeem` using the same parameters.
{% endhint %}

### Payment Token Management

{% hint style="warning" %}
Requires `TOKEN_MANAGER_ROLE` via AccessManager.
{% endhint %}

#### `addPaymentToken`

```solidity
function addPaymentToken(
    address token,
    address priceFeed,
    bool isStable
) external;
```

Registers a new payment token:

* `token` – ERC-20 used for payments.
* `priceFeed` – [AggregatorV3Interface](https://docs.chain.link/data-feeds/api-reference#aggregatorv3interface) for `token` vs pricing currency.
* `isStable` – `true` if the token is considered stable (e.g. fully backed stablecoin).

Reverts if:

* `token` is zero address (`ZeroAddress`).
* Token already added (`PaymentTokenAlreadyAdded`).

#### `removePaymentToken`

```solidity
function removePaymentToken(address token) external;
```

Removes a payment token. It will no longer be accepted for new subscriptions or redemptions.

Reverts if:

* Token is not in the approved set (`PaymentTokenNotApproved`).

#### `updatePaymentToken`

```solidity
function updatePaymentToken(
    address token,
    address priceFeed,
    bool isStable
) external;
```

Updates price feed and stability flag for an existing payment token.

Reverts if token is not approved.

#### `getPaymentTokens` / `getPaymentTokenCount`

```solidity
function getPaymentTokens()
    external
    view
    returns (address[] memory);

function getPaymentTokenCount()
    external
    view
    returns (uint256);
```

Allow enumerating all approved payment tokens, on top of the `approvedPaymentTokens(token)` getter.

### Rules Management

{% hint style="warning" %}
Requires `RULES_MANAGER_ROLE` via AccessManager.
{% endhint %}

#### `updateSubscriptionRules`

```solidity
function updateSubscriptionRules(
    uint256 dateOpened,
    uint256 dateClosed,
    uint256 minAmount,
    uint256 maxAmount
) external;
```

Sets subscription period and per-tx min/max.

* `dateOpened` – unix timestamp when subscriptions start.
* `dateClosed` – unix timestamp when subscriptions end (0 = no end).
* `minAmount` / `maxAmount` – per-call bounds for `amount`.

Reverts if `minAmount > maxAmount` (`InvalidAmounts`).

#### `updateRedemptionRules`

```solidity
function updateRedemptionRules(
    uint256 dateOpened,
    uint256 dateClosed,
    uint256 minAmount,
    uint256 maxAmount,
    uint16 redemptionMalus
) external;
```

Same as subscription rules, plus:

* `redemptionMalus` – in basis points (e.g. 100 = 1%, 500 = 5%) applied as haircut on redemptions.

#### `subscriptionRules`

```solidity
function subscriptionRules()
    external
    view
    returns (
        bool isOpen,
        uint256 dateOpened,
        uint256 dateClosed,
        uint256 minAmount,
        uint256 maxAmount
    );
```

Returns current subscription rule set and whether the window is open at `block.timestamp`.

#### `redemptionRules`

```solidity
function redemptionRules()
    external
    view
    returns (
        bool isOpen,
        uint256 dateOpened,
        uint256 dateClosed,
        uint256 minAmount,
        uint256 maxAmount,
        uint16 malus
    );
```

Same for redemptions, including the currently configured malus.

### Admin Functions

{% hint style="warning" %}
Require `ADMIN_ROLE` via AccessManager.
{% endhint %}

#### `updateIssuerSafe`

```solidity
function updateIssuerSafe(address newIssuerSafe) external;
```

Updates the address that:

* Receives subscription proceeds.
* Pays redemptions.

Reverts if `newIssuerSafe` is zero address.

#### `updatePreminted`

```solidity
function updatePreminted(bool _preminted) external;
```

Toggles mode:

* `true` – transfer from `issuerSafe` (treasury).
* `false` – mint/burn via ERC-3643 permissions.

Emits `PremintedUpdated`.

### Events

* `event IssuerSafeUpdated(address indexed oldIssuerSafe, address indexed newIssuerSafe)`
* `event PremintedUpdated(address indexed updatedBy, bool preminted)`
* `event PaymentTokenAdded(address indexed token, address priceFeed, bool isStable)`
* `event PaymentTokenRemoved(address indexed token)`
* `event PaymentTokenUpdated(address indexed token, address priceFeed, bool isStable)`
* `event SubscriptionRulesUpdated(uint256 dateOpened, uint256 dateClosed, uint256 minAmount, uint256 maxAmount, address indexed updatedBy)`
* `event RedemptionRulesUpdated(uint256 dateOpened, uint256 dateClosed, uint256 minAmount, uint256 maxAmount, uint16 redemptionMalus, address indexed updatedBy)`
* `event Subscribed(uint256 indexed nonce, address indexed investor, address indexed paymentToken, uint256 amount, uint256 totalCost)`
* `event Redeemed(uint256 indexed nonce, address indexed investor, address indexed paymentToken, uint256 amount, uint256 paymentAmount)`

Plus inherited:

* `AuthorityUpdated(address authority)` from `AccessManaged`.

### Custom Errors (from `ErrorsLib`)

`DinoPrimary` relies on a shared error library. Relevant errors include:

* `ZeroAddress()` – one of the addresses is zero.
* `SubscriptionClosed()` – subscription window not open.
* `RedemptionClosed()` – redemption window not open.
* `AmountBelowMinimum(uint256 amount, uint256 min)`
* `AmountAboveMaximum(uint256 amount, uint256 max)`
* `PaymentTokenAlreadyAdded(address token)`
* `PaymentTokenNotApproved(address token)`
* `InvalidAmounts()` – `minAmount > maxAmount` in rules updates.
* `InvalidPriceFeed()` – price feed returns non-positive value.
* `InvestorNotQualified(address investor)` – identity / eligibility issue.
* `TransferNotCompliant()` – ERC-3643 compliance prevents transfer/mint/burn.

These are meant to be **developer-friendly** and easily mapped to front-end error messages.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
