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);

    // Pause controls
    function pause() external;
    function unpause() external;
}

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

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:
  • Configurable per-wallet limits
  • Cumulative tracking over rolling 24-hour window
  • Automatic reset at 2pm Singapore Time (UTC+8)
Implementation:
mapping(address => uint256) public dailyVolume;
mapping(address => uint256) public lastResetTime;
mapping(address => uint256) public volumeLimit;

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

function checkRWAOut(address user, uint256 amount, bytes calldata)
    external
    override
    returns (bool)
{
    // Reset if new day
    if (shouldReset(user)) {
        dailyVolume[user] = 0;
        lastResetTime[user] = block.timestamp;
    }

    // Check limit
    require(
        dailyVolume[user] + amount <= volumeLimit[user],
        "Daily volume limit exceeded"
    );

    // Update volume
    dailyVolume[user] += amount;

    return true;
}

function shouldReset(address user) internal view returns (bool) {
    uint256 lastReset = lastResetTime[user];
    uint256 currentHour = (block.timestamp / 3600) % 24;
    uint256 lastResetHour = (lastReset / 3600) % 24;

    // Reset if crossed the reset hour
    return currentHour >= RESET_HOUR && lastResetHour < RESET_HOUR;
}

Administrative Functions

// Set volume limit for a user
function setVolumeLimit(address user, uint256 limit)
    external
    onlyRole(ISSUER_ADMIN_ROLE);

// Get current volume status
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 volume limits
  • 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 VolumeLimitUpdated(
    address indexed user,
    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