Credit Support Annex
CreditSupportAnnex.sol is the per-relationship collateral valuer: it decides which tokens may be posted and shaves each by a haircut, priced off the core's global oracle. A custom credit policy is a new CSA contract, named at agreement creation.
Code
CreditSupportAnnex.sol is the external collateral-pricing contract, chosen per Account Control Agreement at creation. It decides which tokens may be posted, shaves each by a haircut, and reads every token's USD price from the global oracle the core binds. The haircut is the only discount, the same on every unit of a token, however much of it a basket holds. The core never prices collateral itself: it asks the CSA. A custom credit policy is deployed as a new CSA contract and named at agreement creation, with no change to the core.
The price is global; only the haircut is per-relationship. The CSA reads the per-token price from the core's IOracleRegistry at valuation time. One feed serves every CSA and the valuation of the safe.
State
TokenConfig struct
Each eligible token carries a config. It is defined as follows:
struct TokenConfig {
bool eligible;
uint16 haircutBps; // shave: value *= (1 - haircutBps/BPS)
YieldMode yieldMode;
uint16 holderShareBps;
}config[token] returns the TokenConfig for a token. A token with eligible == false may not be posted.
registry
IOracleRegistry public immutable registry;The CRX core, which serves the global per-token price oracle. The CSA resolves a token's oracle through registry.collateralOracle(token) and reads its USD price (expo -8, 1e8 = $1) at valuation time.
Functions
setToken
function setToken(
address token,
bool eligible,
uint16 haircutBps,
YieldMode mode,
uint16 holderShareBps
) external;Configures a collateral token. Owner-only.
- Retirement is allowed. The owner may flip a live token
eligible → falseor raise its haircut at any time. A retired token does not brick an open agreement: the lenient and value-conserving paths (valueBasketLenient,fairValue) skip it instead of reverting, which lets a close-out still run, while the strictvalueBasketrevertsIneligibleto ensure a party can never withdraw against a token the CSA no longer accepts. - Reverts
HaircutTooHighifhaircutBps > 10_000. That is the only revert; the write is otherwise unconditional.
Parameters:
| Name | Type | Description |
|---|---|---|
token | address | The collateral token to configure. |
eligible | bool | Whether the token may be posted under this CSA. |
haircutBps | uint16 | The value shaved off, in bps (value *= 1 - haircutBps/10_000). |
mode | YieldMode | How the token's yield is routed (informational). |
holderShareBps | uint16 | The holder's share of the yield, in bps (informational). |
valueBasket
function valueBasket(address[] calldata tokens, uint256[] calldata amounts) external view returns (uint256 usd);The sufficiency test: values a basket at the global price after this CSA's haircut. The haircut is the only discount. Returns USD scaled to 1e18.
tokensmust be strictly ascending and distinct (the same set validation the core applies to position sets).- Reverts
Ineligibleon any token whoseeligibleis false.deallocatedepends on this. A party can never withdraw against a token the CSA no longer accepts. - Reverts
LengthMismatch,UnsortedOrDup,NoOracle,BadMarkScale, orStaleMarkon a malformed input or feed.
Parameters:
| Name | Type | Description |
|---|---|---|
tokens | address[] | The basket tokens, strictly ascending and distinct. |
amounts | uint256[] | The 18-dec amount of each token, index-aligned with tokens. |
Return Values:
| Name | Type | Description |
|---|---|---|
usd | uint256 | The basket's haircut-adjusted USD value (1e18). |
valueBasketLenient
function valueBasketLenient(address[] calldata tokens, uint256[] calldata amounts) external view returns (uint256 usd);As valueBasket (haircut only), but skip-not-revert on an ineligible token: it contributes zero instead of reverting the whole basket. Returns USD scaled to 1e18.
- The cascade's solvency gate reads this to ensure a token retired mid-life cannot brick a close-out.
valueBasketstays strict. tokensmust be strictly ascending and distinct.
Parameters:
| Name | Type | Description |
|---|---|---|
tokens | address[] | The basket tokens, strictly ascending and distinct. |
amounts | uint256[] | The 18-dec amount of each token, index-aligned with tokens. |
Return Values:
| Name | Type | Description |
|---|---|---|
usd | uint256 | The basket's haircut-adjusted USD value, ineligible legs at zero. |
fairValue
function fairValue(address[] calldata tokens, uint256[] calldata amounts) external view returns (uint256 usd);The value-conserving valuation: every token at its pure global oracle USD price, no haircut, no eligibility revert. Returns USD scaled to 1e18.
- This is the denomination for every money-movement leg in the cascade.
valueBasketis the sufficiency test; mixing the two manufactures a phantom shortfall. tokensmust be strictly ascending and distinct.
Parameters:
| Name | Type | Description |
|---|---|---|
tokens | address[] | The basket tokens, strictly ascending and distinct. |
amounts | uint256[] | The 18-dec amount of each token, index-aligned with tokens. |
Return Values:
| Name | Type | Description |
|---|---|---|
usd | uint256 | The basket's pure oracle USD value (1e18, no shave). |
isEligible
function isEligible(address token) external view returns (bool);Whether token may be posted as collateral under this CSA.
Parameters:
| Name | Type | Description |
|---|---|---|
token | address | The token to check. |
Return Values:
| Type | Description |
|---|---|
| bool | true if the token is eligible, false otherwise. |
yieldMode
function yieldMode(address token) external view returns (YieldMode mode, uint16 holderShareBps);How a token's yield is routed. Informational.
Parameters:
| Name | Type | Description |
|---|---|---|
token | address | The token. |
Return Values:
| Name | Type | Description |
|---|---|---|
mode | YieldMode | The routing mode (ToPoster/ToHolder/Shared). |
holderShareBps | uint16 | The holder's share, in bps. |
Events
| Event | Signature | Emitted when |
|---|---|---|
TokenConfigured | TokenConfigured(address indexed token, bool eligible, uint16 haircutBps) | a token is configured via setToken. |
Errors
| Error | Thrown when |
|---|---|
HaircutTooHigh | haircutBps exceeds 10_000. |
LengthMismatch | tokens.length != amounts.length. |
UnsortedOrDup | tokens is not strictly ascending and distinct. |
Ineligible | a strict valueBasket token is not eligible. |
NoOracle | the token has no collateral oracle bound in the registry. |
BadMarkScale | the oracle exponent is not -8. |
StaleMark | the oracle publish time is zero, or the mark is older than 10 minutes. |
The credit policy lives outside the core, swappable per agreement. See CRX (~10 min) and CRX Contracts (~3 min).