Skip to main content

Overview

Price adapters provide USD-denominated pricing for all assets in the Multiliquid Protocol. Each asset RWA has an associated price adapter that the MultiliquidSwap contract queries during swap calculations. Interface: src/interface/IPriceAdapter.sol

Price Adapter Interface

All price adapters implement a simple, standardized interface:
interface IPriceAdapter {
    /**
     * @notice Returns the USD price of the asset
     * @return price The price in 18-decimal WAD format (1e18 = $1 USD)
     */
    function getPrice() external view returns (uint256 price);
}
Price Format: 18-decimal WAD (1e18 = $1.00 USD) Examples:
  • $1.00 = 1000000000000000000 (1e18)
  • $1.05 = 1050000000000000000 (1.05e18)
  • $0.99 = 990000000000000000 (0.99e18)

Adapter Types

ULTRAAdapter

Fetches ULTRA (Delta Wellington Ultra Short Treasury On-Chain Fund) price from ULTRA’s manager contract using the last set mint exchange rate. Location: src/PriceAdapters/ULTRAAdapter.sol Implementation:
interface IUltraManager {
    function lastSetMintExchangeRate() external view returns (uint256);
    function BPS_DENOMINATOR() external view returns (uint256);
}

contract ULTRAAdapter is IPriceAdapter {
    IUltraManager public ultraManager;

    error ZeroAddress();

    constructor(address _ultraManager) {
        if (_ultraManager == address(0)) revert ZeroAddress();
        ultraManager = IUltraManager(_ultraManager);
    }

    function getPrice() external view returns (uint256) {
        return ultraManager.lastSetMintExchangeRate() * 1e18
            / ultraManager.BPS_DENOMINATOR();
    }
}
How It Works:
  • Reads the mint exchange rate from ULTRA’s manager via lastSetMintExchangeRate()
  • The exchange rate is denominated in basis points, so the adapter divides by BPS_DENOMINATOR() to normalize
  • Multiplies by 1e18 first to produce a WAD-format USD price
Dependencies:
  • ULTRA Manager contract must be deployed and accessible
  • lastSetMintExchangeRate() must reflect the current exchange rate

JTRSYAdapter

Fetches JTRSY (Janus Henderson Anemoy Treasury Fund) price from JTRSY’s async vault contract. Location: src/PriceAdapters/JTRSYAdapter.sol Implementation:
interface IAsyncVault {
    function pricePerShare() external view returns (uint256);
}

contract JTRSYAdapter is IPriceAdapter {
    IAsyncVault public asyncVault;

    error ZeroAddress();

    constructor(address _asyncVault) {
        if (_asyncVault == address(0)) revert ZeroAddress();
        asyncVault = IAsyncVault(_asyncVault);
    }

    // JTRSY is a 6-decimal token, so scale to 18 decimals for WAD math
    function getPrice() external view returns (uint256) {
        return asyncVault.pricePerShare() * 1e12; // 1e12 = 1e(18-6)
    }
}
How It Works:
  • Reads the share price from JTRSY’s IAsyncVault via pricePerShare()
  • JTRSY uses 6-decimal precision, so the adapter scales by 1e12 to produce an 18-decimal WAD price
Dependencies:
  • JTRSY async vault contract must be deployed and accessible
  • pricePerShare() must return the current share price in 6-decimal format

USTBAdapter

Fetches USTB (Superstate Short Duration US Government Securities Fund) price from a Chainlink price feed. Location: src/PriceAdapters/USTBAdapter.sol Implementation:
contract USTBAdapter is IPriceAdapter {
    AggregatorV3Interface private immutable PRICE_FEED;

    error ZeroAddress();
    error InvalidPrice();

    constructor(address _priceFeed) {
        if (_priceFeed == address(0)) revert ZeroAddress();
        PRICE_FEED = AggregatorV3Interface(_priceFeed);
    }

    function getPrice() external view returns (uint256) {
        (, int256 answer,,,) = PRICE_FEED.latestRoundData();

        if (answer <= 0) revert InvalidPrice();

        return uint256(answer) * 1e12;
    }
}
How It Works:
  • Reads the latest price from a Chainlink AggregatorV3Interface price feed via latestRoundData()
  • Chainlink feeds for this asset return prices with 6 decimals, so the adapter scales by 1e12 to produce an 18-decimal WAD price
  • Reverts with InvalidPrice() if the feed returns a non-positive value, guarding against stale or corrupted data
Dependencies:
  • A Chainlink-compatible price feed must be deployed and accessible
  • The price feed must return a positive int256 answer in 6-decimal format

DollarPeggedAdapter

For RWAs with a NAV pegged to $1 USD (e.g., BENJI, WTGXX). Implementation:
contract DollarPeggedAdapter is IPriceAdapter {
    constructor() {}

    function getPrice() external pure returns (uint256) {
        return 1e18; // Always $1.00
    }
}
Use Cases:
  • BENJI (Franklin OnChain US Gov Money Fund)
  • WTGXX (WisdomTree US Dollar Digital Fund)
  • Other RWAs with $1.00 target NAV
Characteristics:
  • No external dependencies
  • Zero gas cost (pure function)
  • Deterministic pricing

ThirdPartySetterPriceAdapter

A generic, role-controlled price adapter for assets whose price is set off-chain by an authorized party. Used when no on-chain oracle or direct contract query is available for an asset’s NAV. Location: src/PriceAdapters/ThirdPartySetterPriceAdapter.sol Implementation:
contract ThirdPartySetterPriceAdapter is IPriceAdapter, AccessControlEnumerable {
    bytes32 public constant PRICE_SETTER_ROLE = keccak256("PRICE_SETTER_ROLE");

    uint256 public _price;

    error PriceNotSet();
    error AdminCannotBeZeroAddress();

    event PriceUpdated(uint256 newPrice, address indexed setter);

    constructor(address admin) {
        if (admin == address(0)) revert AdminCannotBeZeroAddress();
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
    }

    function getPrice() external view returns (uint256) {
        if (_price == 0) revert PriceNotSet();
        return _price;
    }

    function setPrice(uint256 newPrice) external onlyRole(PRICE_SETTER_ROLE) {
        _price = newPrice;
        emit PriceUpdated(newPrice, msg.sender);
    }
}
How It Works:
  • An admin grants PRICE_SETTER_ROLE to one or more authorized addresses
  • Authorized setters call setPrice() with a WAD-format USD price (18 decimals)
  • getPrice() returns the most recently set price, reverting with PriceNotSet() if no price has been configured
  • Emits a PriceUpdated event on every price change for off-chain monitoring
Access Control:
  • DEFAULT_ADMIN_ROLE: Manages role assignments (grant/revoke PRICE_SETTER_ROLE)
  • PRICE_SETTER_ROLE: Authorized to call setPrice()
Use Cases:
  • VBILL (VanEck Treasury Fund) — see below
  • Any future RWA whose pricing relies on off-chain NAV reporting

VBILLAdapter

VBILL (VanEck Treasury Fund) uses the ThirdPartySetterPriceAdapter described above. There is no dedicated VBILLAdapter contract; instead, a ThirdPartySetterPriceAdapter instance is deployed and configured with an authorized price setter that periodically updates the VBILL NAV on-chain. Characteristics:
  • NAV accrues to token (not pegged to $1.00)
  • Price is updated off-chain by an authorized PRICE_SETTER_ROLE address
  • Reverts if queried before the initial price is set

Next: Integration Guide

Learn how to integrate Multiliquid into your application