Skip to main content

Overview

RWA delegate contracts provide optional, asset-specific risk management and compliance controls for Real World Asset tokens. They are only deployed when custom risk controls beyond the token’s native functionality are required. Base Contract: src/RWADelegate.sol
RWA delegates are optional. If an RWA token already implements sufficient controls (e.g., built-in KYC, transfer restrictions), no delegate is needed.

When to Use RWA Delegates

Deploy an RWA delegate when you need:
  • Volume limits or time-based restrictions
  • Additional compliance metadata validation
  • Pause capabilities specific to the RWA
  • Risk management beyond token-native controls

Core Interface

interface IRWADelegate {
    // Risk validation before RWA leaves the system
    function checkRWAOut(
        address user,
        uint256 amount,
        bytes calldata metadata
    ) external returns (bool);

    // Risk validation before RWA enters the system
    function checkRWAIn(
        address user,
        uint256 amount,
        bytes calldata metadata
    ) external returns (bool);
}

Key Functions

checkRWAOut

Validates RWA token transfers out of the protocol (when user receives RWA).
function checkRWAOut(
    address user,
    uint256 amount,
    bytes calldata metadata
) external onlyRole(MULTILIQUID_SWAP_CONTRACT) returns (bool);
Called During:
  • Stablecoin → RWA swaps
  • Stablecoin → RWA (in RWA-to-RWA swaps)
Typical Validations:
  • Check daily volume limits
  • Verify compliance metadata
  • Validate user eligibility
  • Enforce time-based restrictions
Returns: true if validation passes, reverts otherwise

checkRWAIn

Validates RWA token transfers into the protocol (when user provides RWA).
function checkRWAIn(
    address user,
    uint256 amount,
    bytes calldata metadata
) external onlyRole(MULTILIQUID_SWAP_CONTRACT) returns (bool);
Called During:
  • RWA → Stablecoin swaps
  • RWA → RWA swaps (from source RWA)
Typical Validations:
  • Check incoming volume limits
  • Validate token source
  • Verify compliance requirements
Returns: true if validation passes, reverts otherwise

Pause Controls

pause() and unpause() are defined on the concrete RWADelegate implementation contract, not on the IRWADelegate interface.
function pause() external onlyRole(ISSUER_ADMIN_ROLE);
function unpause() external onlyRole(ISSUER_ADMIN_ROLE);
Effect: When paused, all swaps involving this RWA are blocked Access: RWA issuer admin only

Example: ULTRA Delegate

The ULTRA RWA delegate implements daily volume limits with timezone-specific resets.

Features

Daily Volume Limits:
  • Single global daily volume limit applied to all users
  • Per-user volume tracking compared against the global limit
  • Automatic reset at 2pm Singapore Time (UTC+8)
Implementation:
struct UserVolumeData {
    uint256 volume;       // Cumulative volume for the current day
    uint32 lastResetDay;  // Day number of last reset
    uint96 reserved;
}

uint256 public dailyVolumeLimit;  // Global limit applied to every user
mapping(address => UserVolumeData) public userVolumes;

uint256 public constant RESET_HOUR = 6;  // 2pm SGT = 6am UTC

function checkRWAOut(address user, uint256 amount, bytes calldata)
    external
    override
    returns (bool)
{
    UserVolumeData storage data = userVolumes[user];
    uint32 currentDay = _currentDay();

    // Reset if new day
    if (data.lastResetDay < currentDay) {
        data.volume = 0;
        data.lastResetDay = currentDay;
    }

    // Check against the global daily volume limit
    require(
        data.volume + amount <= dailyVolumeLimit,
        "Daily volume limit exceeded"
    );

    // Update user volume
    data.volume += amount;

    return true;
}

Administrative Functions

// Set the global daily volume limit (applies to all users)
function setDailyVolumeLimit(uint256 limit)
    external
    onlyRole(ISSUER_ADMIN_ROLE);

// Get current volume status for a specific user
function getVolumeStatus(address user)
    external
    view
    returns (uint256 used, uint256 limit, uint256 available);

Access Control

DEFAULT_ADMIN_ROLE: Can upgrade delegate, manage roles ISSUER_ADMIN_ROLE: RWA issuer’s administrative role
  • Configure the global daily volume limit
  • Set risk parameters
  • Pause/unpause delegate
  • Update compliance requirements
MULTILIQUID_SWAP_CONTRACT: Granted to MultiliquidSwap contract
  • Call checkRWAIn() and checkRWAOut()
  • Trigger risk validations
OPERATOR_ROLE: Operational management

Events

event VolumeRecorded(
    address indexed user,
    uint256 amount,
    uint256 newTotal,
    bool isOutbound
);

event DailyVolumeLimitUpdated(
    uint256 newLimit
);

event DelegatePaused(address indexed pauser);
event DelegateUnpaused(address indexed unpauser);

Integration

Without RWA Delegate

If no RWA delegate is needed:
// Register RWA without delegate
multiliquidSwap.setRWAAcceptance(
    rwaID,
    address(0),           // No delegate
    rwaTokenAddress,
    true                  // Accepted
);

// Set price adapter
multiliquidSwap.setRwaPriceAdapter(rwaID, priceAdapter);

With RWA Delegate

If custom risk controls are needed:
// Deploy delegate
RWADelegate delegate = new ULTRADelegate();
delegate.initialize(multiliquidAdmin, issuerAdmin, multiliquidSwapAddress, rwaID, rwaTokenAddress);

// Register RWA with delegate
multiliquidSwap.setRWAAcceptance(
    rwaID,
    address(delegate),    // Delegate address
    rwaTokenAddress,
    true                  // Accepted
);

// Set price adapter
multiliquidSwap.setRwaPriceAdapter(rwaID, priceAdapter);

Next: Price Adapters

Learn about the modular oracle system providing USD-denominated pricing