# Contracts

Divigent's protocol layer is intentionally small. Four contracts handle every on-chain action.

## DivigentVaultRouter

`DivigentVaultRouter` is the protocol hub.

It is the contract wallets interact with for deposits and withdrawals. It holds the protocol's pooled Aave aTokens and Morpho vault shares, tracks each wallet's USDC cost basis, mints and burns dvUSDC through the receipt-token contract, asks the oracle for routing guidance, and delegates fee collection to the fee collector.

The router enforces:

* wallet registration and operator permissions;
* a 10 USDC minimum deposit size;
* phased TVL caps;
* deposit pause state;
* oracle freshness checks;
* amount-aware route capacity checks;
* withdrawal-capacity planning;
* slippage bounds through `minSharesOut` and `minUsdcOut`.

The router holds no idle USDC between normal transactions. During deposit, USDC moves from the wallet to the router and then into Aave or Morpho in the same transaction. During withdrawal, USDC moves from the underlying venues to the router and then to the wallet after fee calculation.

For the deposit and withdrawal sequences, see [Deposit Flow](/divigent-docs/protocol/deposit.md) and [Withdrawal Flow](/divigent-docs/protocol/withdrawal.md).

## DivigentYieldOracle

`DivigentYieldOracle` is the routing signal.

It reads rate data from Aave V3 and Morpho, records observations, computes a 4-hour time-weighted average rate, and returns the preferred route for new deposits.

The oracle uses:

* a 4-hour TWAR window;
* a 5-minute minimum interval between observations;
* a 48-slot circular checkpoint buffer;
* a 2-hour freshness limit;
* safety checks for both supported venues;
* a default minimum routing differential of 20 bps, adjustable between 10 bps and 100 bps through a 24-hour timelocked admin path.

The router treats the oracle as necessary but not sufficient. Even if the oracle recommends a venue, the router still checks whether that venue can accept the specific deposit amount.

For the full TWAR computation and routing rule, see [Oracle and Routing](/divigent-docs/protocol/oracle-and-routing.md).

## dvUSDC

`dvUSDC` is the receipt token.

When a wallet deposits USDC, the router mints dvUSDC to that wallet. When the wallet withdraws, the router burns dvUSDC from that wallet. The token uses 6 decimals, matching USDC.

dvUSDC is intentionally non-transferable. Peer-to-peer transfers revert at the token level. Only minting from `address(0)` and burning to `address(0)` are allowed, and only the router can mint or burn.

This is an accounting choice. The router tracks `costBasisUSDC[wallet]` per wallet. If dvUSDC could move freely, a receipt token could become separated from the principal record used to calculate fees. Non-transferability keeps the receipt balance and principal basis attached to the same wallet.

### Share Accounting

`totalVaultAssets()` reads the current value of the router's underlying venue positions:

* Aave value is the router's aToken balance.
* Morpho value is the router's vault shares converted to assets.

`pricePerShare()` is derived from total vault assets and total dvUSDC supply.

Minting and redemption use virtual-offset math:

```
shares = assets * (totalSupply + virtualOffset) / (totalAssets + virtualOffset)
assets = shares * (totalAssets + virtualOffset) / (totalSupply + virtualOffset)
```

The virtual offset is 1 USDC at 6-decimal precision. It preserves clean first-deposit behaviour and makes first-depositor inflation attacks economically impractical.

### Cost Basis

For fee accounting, the router tracks `costBasisUSDC[wallet]`. This value is the wallet's deposited principal minus principal already attributed to prior withdrawals. It is not the same thing as current position value.

`getPosition(wallet)` returns:

* deposited principal;
* current value of the wallet's dvUSDC;
* accrued yield, floored at zero.

## DivigentFeeCollector

`DivigentFeeCollector` handles the protocol fee.

The fee is 10% of realised yield, calculated at withdrawal time. If realised yield is zero, the fee is zero. If the underlying venues lose value and the withdrawal returns less than principal, the fee is still zero.

The fee collector does not custody USDC. When a fee exists, it pulls the fee amount directly from the router to the treasury using `safeTransferFrom`. Treasury rotation is possible through a 7-day timelock and 14-day grace window, gated through the emergency multisig path.

See [Fees](/divigent-docs/protocol/fees.md) for the full fee calculation.

## Wallets and Operators

A wallet must be authorised before using the router.

There are two registration paths:

1. `initialize()` registers `msg.sender` directly.
2. `initializeFor(wallet, deadline, sig)` allows gasless registration using an EIP-712 signature from the wallet.

After registration, a wallet can call `setOperator(operator, approved)` to let another address deposit or withdraw on its behalf. Operators do not receive dvUSDC and do not own the wallet's position. They only receive permission to call router functions for that wallet until revoked.


---

# 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://divigent.gitbook.io/divigent-docs/architecture/contracts.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.
