RAAC NFT Vault Adapter V2¶
Overview¶
RAACNFTVaultAdapterV2
is an ERC‑721 vault adapter optimized for scalable NAV accounting. Instead of recomputing NFT portfolio value via loops, it maintains a running sum of all deposited NFTs’ prices in USD and syncs in real time with the on‑chain house price oracle.
- Real‑time NAV tracking via running sum in USD
- Syncs on each price update through the House Prices “syncer” interface
- Converts USD → crvUSD on demand when the vault queries
totalValue()
- Prevents deposits/withdrawals while a price update for a specific NFT is pending
Why a running sum?
Iterating over all deposited NFTs to compute NAV is not scalable. By keeping nftsValue
as a running USD total and updating it on deposit/withdraw and on oracle price updates, the adapter supports O(1) NAV reads.
Architecture¶
flowchart TD
H[RAACHousePrices] -->|notify tokenId, old, new| A[RAACNFTVaultAdapterV2]
A -->|nftsValue += Δ| S[nftsValue USD]
A -->|totalValue| V[RWAVault]
H -->|crvUSDToUSDPrice| A
nftsValue
stores the running USD sum for deposited NFTsnotify
adjustsnftsValue
when an NFT price changes and the NFT is currently depositedtotalValue
converts USD → crvUSD usingcrvUSDToUSDPrice()
fromRAACHousePrices
Mechanics¶
How it stays in sync (at a glance)¶
sequenceDiagram
participant User
participant Vault
participant Adapter as RAACNFTVaultAdapterV2
participant Prices as RAACHousePrices
User->>Vault: deposit NFT
Vault->>Adapter: deposit(data, from)
Adapter->>Prices: isPendingRequest(tokenId)?
Prices-->>Adapter: false
Adapter->>Adapter: nftsValue += usdPrice(tokenId)
Adapter-->>Vault: value (crvUSD)
Prices-->>Adapter: notify(tokenId, oldUSD, newUSD)
Adapter->>Adapter: nftsValue = nftsValue - oldUSD + newUSD
Vault->>Adapter: totalValue()
Adapter->>Prices: crvUSDToUSDPrice()
Adapter-->>Vault: nftsValue (USD) -> crvUSD
Trigger → Effect reference¶
Trigger | Guard/Check | State Update | Notes |
---|---|---|---|
deposit(data, from) |
Reject if isPendingRequest(tokenId) |
nftsValue += usdPrice(tokenId) |
Base ERC721 deposit, value added in USD |
withdraw(data, to) |
Reject if isPendingRequest(tokenId) |
nftsValue = max(0, nftsValue - usdPrice(tokenId)) |
Base ERC721 withdraw, value removed in USD |
notify(tokenId, old, new) |
Only from RAACHousePrices ; ignore if NFT not deposited |
nftsValue = nftsValue - old + new |
Keeps running sum aligned with oracle |
totalValue() |
– | Returns (nftsValue * 1e18) / crvUSDToUSDPrice() |
Converts USD running sum to crvUSD on read |
Details¶
- Deposits/withdrawals are blocked for a specific NFT while its price update is pending to avoid inconsistent NAV snapshots.
- The USD running sum (
nftsValue
) removes the need to iterate over all NFTs, enabling O(1) NAV reads. - Conversion to
crvUSD
happens on demand using the FX rate surfaced byRAACHousePrices
.
Key Functions¶
deposit¶
Item | Details |
---|---|
Purpose | Deposit an ERC‑721 into the vault via adapter and update NAV running sum (USD) |
Guards | Reverts if isPendingRequest(tokenId) is true |
State | nftsValue += usdPrice(tokenId) ; base ERC721 deposit logic executes |
Returns | value – deposit value in crvUSD as per base adapter |
Source code
function deposit(bytes calldata data, address from)
public
virtual
override
onlyVault
returns (uint256 value)
{
uint256 tokenId = abi.decode(data, (uint256));
if (IRAACHousePrices(address(priceOracle)).isPendingRequest(tokenId)) revert HousePricePending();
value = super.deposit(data, from);
(uint256 usdPrice,) = IRAACHousePrices(address(priceOracle)).getRawPrice(tokenId);
nftsValue += usdPrice;
return value;
}
withdraw¶
Item | Details |
---|---|
Purpose | Withdraw an ERC‑721 from the vault and decrement NAV running sum (USD) |
Guards | Reverts if isPendingRequest(tokenId) is true |
State | nftsValue = max(0, nftsValue - usdPrice(tokenId)) ; base ERC721 withdraw executes |
Returns | value – withdrawal value in crvUSD as per base adapter |
Source code
function withdraw(bytes calldata data, address to)
public
virtual
override
onlyVault
returns (uint256 value)
{
uint256 tokenId = abi.decode(data, (uint256));
if (IRAACHousePrices(address(priceOracle)).isPendingRequest(tokenId)) revert HousePricePending();
(uint256 usdValue,) = IRAACHousePrices(address(priceOracle)).getRawPrice(tokenId);
if (nftsValue < usdValue) {
nftsValue = 0;
} else {
nftsValue -= usdValue;
}
return super.withdraw(data, to);
}
notify (Price Syncer)¶
Item | Details |
---|---|
Purpose | Keep NAV running sum aligned with latest house prices |
Caller | RAACHousePrices only |
State | If NFT is deposited: nftsValue = nftsValue - previousPrice + newPrice |
Source code
totalValue¶
Item | Details |
---|---|
Purpose | Report adapter NAV in crvUSD for the vault |
Calc | (nftsValue (USD) * 1e18) / crvUSDToUSDPrice() |
Notes | nftsValue kept in USD; FX conversion is on-demand |
Source code
Interfaces & Compatibility¶
- Inherits
ERC721VaultAdapter
- Implements
IRAACHousePriceSyncer
and supportsERC165
- Works with
RWAVault
via standard adapter interface (deposit
,withdraw
,totalValue
)
Operational Notes¶
- Pending price updates block deposit/withdraw for the affected NFT to avoid NAV inconsistencies
nftsValue
is kept in USD for precision and portability; conversion to crvUSD happens at read time- As a registered syncer, this adapter should be added via
RAACHousePrices.addSyncer(adapter)
so it receivesnotify
callbacks
Syncer registration
Ensure the adapter is registered as a syncer on RAACHousePrices
. Without notify
callbacks, nftsValue
will not reflect live price changes.