# 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**](https://docs.t-rex.network/t-rex-network/t-rex-apps/dino/dino-primary/admin-and-management) 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**](https://docs.t-rex.network/t-rex-network/t-rex-apps/dino/dino-primary/admin-and-management) 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.
