# Tracked sub-accounts

> How tracked sub-accounts hold only registered assets, price each one with an oracle, and check every swap, loan and liquidity move against oracle prices.

Source: https://docs.tango.fun/vaults/tracked-sub-accounts

A tracked sub-account is the part of a vault that holds tokens the vault can price onchain. It may hold only assets on an approved list, it prices each one with an onchain price feed, and it acts only through a few built-in tools. Because every asset it can hold has a price, its value can be worked out entirely onchain. That value counts toward the vault's NAV (net asset value), the total value of everything the vault holds.

> **Key points**
>
> - A tracked sub-account can only hold assets listed in its vault's asset registry. Anything else is never recorded and never counted.
> - An oracle adapter prices every registered asset. If a price goes stale, the vault's NAV goes stale and settlement pauses.
> - The sub-account acts only through modules. After each action, it measures what it actually spent and received, prices both with the oracle, and reverts if the loss is above a fixed ceiling.

## The asset registry

Each vault has one asset registry: for every tracked sub-account, it lists the assets that sub-account may hold. USDC is always registered.

An asset is in one of three states, and it only ever moves forward through them:

| State | Can be acquired | Can be spent | Counted in NAV |
| --- | --- | --- | --- |
| Unregistered | No | No | No |
| Registered | Yes | Yes | Yes |
| Deregistering | No, except when unwinding a position into it | Yes | Yes |

To register an asset, its balance must be readable, and its oracle must price one whole unit at a fresh price above zero. Registering is a Critical-tier action, the longest timelock, so on Tango's production factories it waits 14 days.

Deregistering an asset takes two steps. Starting it is immediate, and from then on the sub-account may only reduce that asset. Finishing it requires a tracked balance of exactly zero. NAV does not change at either step, so nobody can use deregistering to reprice redemptions that are waiting to settle.

Each sub-account has a maximum number of registered assets, set when the vault is deployed.

## Only registered assets

This rule is what makes the valuation complete. Every asset the sub-account can hold has an oracle price, and nothing else can enter NAV. A tracked sub-account cannot hold anything outside its registry in any way that matters:

- It cannot acquire one, because every output of every action must be a registered asset or the action reverts (fails and is undone).
- It cannot spend one, because every input must be a registered asset, within the quantity the sub-account has tracked.
- It does not count one, because NAV is calculated from the tracked quantities of registered assets, not from token balances.
- Tokens sent straight to the sub-account without being asked are not tracked and sit outside NAV. They can only leave through the admin's rescue function, which is limited to the untracked amount.

## Oracles

An oracle is an onchain price feed. The vault's price oracle maps each registered asset to an adapter, a small contract that knows how to price that kind of asset, along with its settings. Binding (connecting) an adapter is Critical tier. Removing one is immediate, except for USDC's.

| Adapter | Prices |
| --- | --- |
| Chainlink | An asset with a USD price feed |
| Chainlink two-feed | An asset priced by multiplying two feeds, such as WBTC/BTC times BTC/USD |
| Tokenized equity | A tokenized stock with a Chainlink feed. It also checks the issuer's registry for pauses and corporate-action multipliers. |
| ERC-4626 | Shares in another vault, at what they redeem for, priced through the underlying asset's feed |
| Concentrated liquidity | A liquidity position, at the token amounts implied by oracle prices plus its fees, never at the pool's own price |

### When a price is stale

A price is stale when it is too old or too broken to trust. A Chainlink reading is stale when:

- the answer is zero or negative;
- it was carried over from an older round (an earlier update);
- its timestamp is zero or in the future;
- it is older than the feed's heartbeat (its promised update interval) plus 90 seconds.

On Base and other layer 2 networks, every price is also stale while the network's sequencer is down, and for one hour after it comes back. The sequencer is the service that orders the network's transactions.

A stale price for an asset the sub-account holds makes the whole vault NAV stale, which pauses settlement (see [When NAV is stale](https://docs.tango.fun/vaults/sub-accounts.md#when-nav-is-stale)). An asset with a zero balance is skipped, so a dead feed for something the vault does not hold does not stop it.

## Modules

A module is a built-in tool for one kind of action, such as swapping. A tracked sub-account acts only through the modules installed on it. Installing a module is Critical tier; removing one is immediate.

| Module | Does | Bounded by |
| --- | --- | --- |
| Swap | Swaps one registered asset for another through a router | An oracle check at the swap policy's slippage ceiling, plus per-asset rate limits in and out |
| ERC-4626 lend | Supplies to and withdraws from ERC-4626 vaults, such as Morpho vaults | An oracle check at the lend policy's ceiling, plus a supply rate limit for each venue |
| Concentrated liquidity | Adds and removes Uniswap V3 and V4 liquidity positions | An oracle check on the whole position's value, plus its own ceiling and rate limits |

A slippage ceiling is the most any single action may lose against oracle prices. A rate limit caps how much can flow in or out over time, like a bucket that refills.

Modules hold nothing between calls. They cannot borrow or loop (borrow against a position to build a bigger one), because the debt would be invisible to a NAV built from token quantities.

### How an action is checked

For each action, the manager names a tolerance: the most value this action may lose against oracle prices. It can be no higher than the policy's slippage ceiling. The sub-account then checks the result itself, after the action runs.

```text
Manager -> Sub-account: execute a module with inputs, expected outputs and a tolerance
Policy check: the tolerance must be at or below the policy's fixed slippage ceiling
Sub-account -> Module: hand over the inputs
Module -> Router or venue: perform the swap, supply or liquidity change
Module -> Sub-account: send back every asset; the module must end holding nothing
Sub-account: measure what was spent and received from its own balance changes, not from what the module reports
Oracle check: value received must be at least value spent times (1 - tolerance), at oracle prices read after the action
Rate limits: spent and received amounts are charged to the outbound and inbound buckets
Any stale price, excess loss or empty bucket reverts the whole action
```

The check happens after the action, using the sub-account's own balances. It works like a minimum output priced by the oracle. A route that loses more than the tolerance against oracle prices reverts, whatever the router claims.

Each policy's slippage ceiling is fixed when the policy is created. Changing it means binding a new policy, which is Critical tier.

### Limits that remain

- The ceiling applies to each action on its own. Repeated actions can each lose up to the tolerance. The rate limits cap how much value can pass through in a window, but there is no total loss budget.
- Lending positions are valued at the venue's own `convertToAssets` figure. A venue that misreports its share price would misstate NAV. That is why lending venues are reviewed before they are admitted.
- A lending venue is admitted only by giving it a supply bucket above zero. It must also be a registered asset with an ERC-4626 adapter.
