Skip to main content

RateManagerV1 Contract

RateManagerV1 is a canonical delegated rate manager that allows rate managers to set conversion rates on behalf of depositors while giving depositors the ability to configure floor protections. This enables dynamic rate management with depositor safety controls.

Overview

  • Contract: RateManagerV1.sol
  • Inherits: Ownable, IRateManager
  • Purpose: Delegated rate management with manager-owned rates and depositor-owned per-deposit floors

Key Concepts

Delegation Model

Depositors can delegate rate management to a professional rate manager who:
  • Sets conversion rates for payment method/currency pairs
  • Collects optional fees on fulfilled intents
  • Manages rates across multiple deposits
Depositors retain control through:
  • Fixed floor rates that manager rates cannot go below
  • Oracle-based floor rates with spread protection
  • Ability to enable/disable specific currency pairs
  • Ability to opt-out at any time

State Variables

escrowRegistry
IEscrowRegistry
Registry of whitelisted escrow contracts
nextRateManagerId
uint256
Counter for generating unique rate manager IDs

Core Functions

Rate Manager Creation

createRateManager

Creates a new rate manager configuration.
_config
RateManagerConfig
Rate manager configuration:
  • manager: Manager address that can set rates
  • feeRecipient: Address to receive manager fees
  • maxFee: Maximum fee this manager can charge (immutable)
  • fee: Current fee (must be less than or equal to maxFee)
  • minLiquidity: Minimum deposit liquidity required for opt-in
  • name: Display name for the manager
  • uri: Metadata URI for additional info
function createRateManager(RateManagerConfig calldata _config) 
    external 
    returns (bytes32 rateManagerId)
rateManagerId
bytes32
Newly created manager ID
Events Emitted:
  • RateManagerCreated(rateManagerId, manager, feeRecipient, maxFee, fee, name, uri)
  • MinLiquidityUpdated(rateManagerId, minLiquidity) (if minLiquidity > 0)
Example:
RateManagerV1.RateManagerConfig memory config = RateManagerV1.RateManagerConfig({
    manager: managerAddress,
    feeRecipient: feeRecipient,
    maxFee: 3e16, // 3% max
    fee: 1e16,    // 1% current fee
    minLiquidity: 1000e6, // 1000 USDC minimum
    name: "Premium Rate Manager",
    uri: "https://example.com/manager.json"
});

bytes32 managerId = rateManager.createRateManager(config);

Manager Configuration

setRateManagerConfig

Updates mutable manager config fields.
_rateManagerId
bytes32
Manager ID
_manager
address
New manager address
_feeRecipient
address
New fee recipient
_name
string
New display name
_uri
string
New metadata URI
function setRateManagerConfig(
    bytes32 _rateManagerId,
    address _manager,
    address _feeRecipient,
    string calldata _name,
    string calldata _uri
) external onlyManager(_rateManagerId)

setFee

Updates manager fee. Must be less than or equal to maxFee.
function setFee(bytes32 _rateManagerId, uint256 _fee) 
    external onlyManager(_rateManagerId)
Events Emitted:
  • RateManagerFeeUpdated(rateManagerId, fee)

setMinLiquidity

Updates minimum liquidity requirement for deposit opt-in.
function setMinLiquidity(bytes32 _rateManagerId, uint256 _minLiquidity) 
    external onlyManager(_rateManagerId)
Events Emitted:
  • MinLiquidityUpdated(rateManagerId, minLiquidity)

Rate Management

setRate

Sets manager-side rate for one payment/currency tuple.
_rateManagerId
bytes32
Manager ID
_paymentMethod
bytes32
Payment method key
_currencyCode
bytes32
Currency key
_rate
uint256
New rate in precise units (1e18 = 1:1)
function setRate(
    bytes32 _rateManagerId,
    bytes32 _paymentMethod,
    bytes32 _currencyCode,
    uint256 _rate
) external onlyManager(_rateManagerId)
Events Emitted:
  • RateManagerRateUpdated(rateManagerId, paymentMethod, currencyCode, rate)
Example:
// Set USD rate for Venmo to 0.99 USDC per USD
rateManager.setRate(
    managerId,
    venmoHash,
    usdHash,
    0.99e18
);

setRateBatch

Batch sets manager-side rates for multiple tuples.
_rateManagerId
bytes32
Manager ID
_paymentMethods
bytes32[]
Payment method keys
_currencyCodes
bytes32[][]
Currency keys grouped by payment method index
_rates
uint256[][]
Rates grouped by payment method index
function setRateBatch(
    bytes32 _rateManagerId,
    bytes32[] calldata _paymentMethods,
    bytes32[][] calldata _currencyCodes,
    uint256[][] calldata _rates
) external onlyManager(_rateManagerId)
Events Emitted:
  • RateManagerRateUpdated(...) for each rate
  • RateManagerRatesBatchUpdated(rateManagerId, totalUpdated)
Example:
bytes32[] memory paymentMethods = new bytes32[](2);
paymentMethods[0] = venmoHash;
paymentMethods[1] = cashappHash;

bytes32[][] memory currencies = new bytes32[][](2);
currencies[0] = new bytes32[](1);
currencies[0][0] = usdHash;
currencies[1] = new bytes32[](1);
currencies[1][0] = usdHash;

uint256[][] memory rates = new uint256[][](2);
rates[0] = new uint256[](1);
rates[0][0] = 0.99e18; // Venmo USD
rates[1] = new uint256[](1);
rates[1][0] = 0.98e18; // CashApp USD

rateManager.setRateBatch(managerId, paymentMethods, currencies, rates);

Depositor Floor Configuration

setDepositorFloor

Sets depositor floor config for one delegated tuple. Only callable by deposit owner.
_rateManagerId
bytes32
Manager ID
_escrow
address
Escrow address
_depositId
uint256
Deposit ID
_paymentMethod
bytes32
Payment method key
_currencyCode
bytes32
Currency key
_config
DepositorFloorConfig
Floor configuration:
  • enabled: Whether this tuple is enabled
  • floorFixed: Fixed floor rate
  • floorSpreadBps: Oracle spread in basis points (0-10000)
  • oracleAdapter: Oracle adapter address (address(0) for no oracle)
  • adapterConfig: Oracle adapter configuration bytes
  • maxStaleness: Max oracle staleness in seconds
function setDepositorFloor(
    bytes32 _rateManagerId,
    address _escrow,
    uint256 _depositId,
    bytes32 _paymentMethod,
    bytes32 _currencyCode,
    DepositorFloorConfig calldata _config
) external
Events Emitted:
  • DepositorFloorSet(rateManagerId, escrow, depositId, paymentMethod, currencyCode, ...)
Example: Fixed Floor
RateManagerV1.DepositorFloorConfig memory config = RateManagerV1.DepositorFloorConfig({
    enabled: true,
    floorFixed: 0.95e18, // Never accept rates below 0.95 USDC per USD
    floorSpreadBps: 0,
    oracleAdapter: address(0),
    adapterConfig: "",
    maxStaleness: 0
});

rateManager.setDepositorFloor(
    managerId,
    escrowAddress,
    depositId,
    venmoHash,
    usdHash,
    config
);
Example: Oracle Floor with Spread
RateManagerV1.DepositorFloorConfig memory config = RateManagerV1.DepositorFloorConfig({
    enabled: true,
    floorFixed: 0.90e18,        // Fallback fixed floor
    floorSpreadBps: 200,        // 2% above oracle rate
    oracleAdapter: oracleAddr,  // Chainlink or other oracle
    adapterConfig: adapterConfig, // Oracle-specific config
    maxStaleness: 3600          // 1 hour max staleness
});

rateManager.setDepositorFloor(managerId, escrowAddress, depositId, venmoHash, usdHash, config);

setDepositorFloorBatch

Batch sets depositor floors for one deposit.
function setDepositorFloorBatch(
    bytes32 _rateManagerId,
    address _escrow,
    uint256 _depositId,
    bytes32[] calldata _paymentMethods,
    bytes32[][] calldata _currencyCodes,
    DepositorFloorConfig[][] calldata _configs
) external

setDepositorCurrencyEnabled

Sets depositor-side currency enabled state. Only callable by deposit owner.
function setDepositorCurrencyEnabled(
    bytes32 _rateManagerId,
    address _escrow,
    uint256 _depositId,
    bytes32 _paymentMethod,
    bytes32 _currencyCode,
    bool _enabled
) external
Events Emitted:
  • DepositorCurrencyEnabledSet(rateManagerId, escrow, depositId, paymentMethod, currencyCode, enabled)

Callbacks

onDepositOptIn

Callback invoked by EscrowV2 when a deposit opts into this manager. Validates minimum liquidity requirement.
function onDepositOptIn(
    uint256 _depositId,
    bytes32 _rateManagerId
) external view override

View Functions

getRate

Returns manager-adjusted rate for a delegated deposit tuple.
function getRate(
    bytes32 _rateManagerId,
    address _escrow,
    uint256 _depositId,
    bytes32 _paymentMethod,
    bytes32 _currencyCode
) external view override returns (uint256 rate)
rate
uint256
Effective delegated rate (max of manager rate and depositor floor). Returns 0 if disabled or no manager rate set.
Rate Calculation Logic:
  1. Check if depositor has enabled this tuple (return 0 if disabled)
  2. Get manager rate (return 0 if not set)
  3. Compute depositor’s effective floor:
    • If oracle configured: max(floorFixed, oracleRate * (1 + floorSpreadBps/10000))
    • Otherwise: floorFixed
  4. Return max(managerRate, effectiveFloor)
  5. If oracle configured but returns 0 and no fixed floor, return 0 (safety protection)

getFee

Returns fee recipient and fee for a manager ID.
function getFee(bytes32 _rateManagerId) 
    external view override 
    returns (address recipient, uint256 fee)

isRateManager

Returns whether a manager ID exists.
function isRateManager(bytes32 _rateManagerId) 
    public view override 
    returns (bool exists)

getRateManager

Returns full manager config for a manager ID.
function getRateManager(bytes32 _rateManagerId) 
    external view 
    returns (RateManagerConfig memory config)

getManagerRate

Returns manager-set rate for one tuple (without floor logic).
function getManagerRate(
    bytes32 _rateManagerId,
    bytes32 _paymentMethod,
    bytes32 _currencyCode
) external view returns (uint256 rate)

getDepositorFloor

Returns depositor floor config for a delegated tuple.
function getDepositorFloor(
    bytes32 _rateManagerId,
    address _escrow,
    uint256 _depositId,
    bytes32 _paymentMethod,
    bytes32 _currencyCode
) external view returns (DepositorFloorConfig memory config)

isDepositorCurrencyEnabled

Returns whether a tuple is enabled for the deposit.
function isDepositorCurrencyEnabled(
    bytes32 _rateManagerId,
    address _escrow,
    uint256 _depositId,
    bytes32 _paymentMethod,
    bytes32 _currencyCode
) external view returns (bool enabled)

Constants

GLOBAL_MAX_MANAGER_FEE
uint256
5e16 - Global maximum manager fee (5%)
BPS
uint256
10_000 - Basis points for percentage calculations
MAX_ADAPTER_CONFIG_BYTES
uint256
256 - Maximum oracle adapter config size

Events

RateManagerCreated

event RateManagerCreated(
    bytes32 indexed rateManagerId,
    address indexed manager,
    address indexed feeRecipient,
    uint256 maxFee,
    uint256 fee,
    string name,
    string uri
)

RateManagerRateUpdated

event RateManagerRateUpdated(
    bytes32 indexed rateManagerId, 
    bytes32 indexed paymentMethod, 
    bytes32 indexed currencyCode, 
    uint256 rate
)

DepositorFloorSet

event DepositorFloorSet(
    bytes32 indexed rateManagerId,
    address indexed escrow,
    uint256 indexed depositId,
    bytes32 paymentMethod,
    bytes32 currencyCode,
    uint256 floorFixed,
    uint16 floorSpreadBps,
    address oracleAdapter,
    bytes adapterConfig,
    uint32 maxStaleness,
    bool enabled
)

Use Cases

Professional Rate Management

Rate managers can:
  • Manage conversion rates across multiple depositors
  • Adjust rates based on market conditions
  • Collect fees for their service
  • Build reputation and attract more depositors

Depositor Protection

Depositors can:
  • Set minimum acceptable rates (floors)
  • Use oracle-based dynamic floors with spread protection
  • Enable/disable specific currency pairs
  • Opt-out at any time by clearing the rate manager

Example: Complete Integration

// 1. Manager creates rate manager
bytes32 managerId = rateManager.createRateManager(config);

// 2. Manager sets rates
rateManager.setRate(managerId, venmoHash, usdHash, 0.99e18);

// 3. Depositor opts into rate manager on EscrowV2
escrow.setRateManager(depositId, address(rateManager), managerId);

// 4. Depositor sets floor protection
rateManager.setDepositorFloor(
    managerId,
    address(escrow),
    depositId,
    venmoHash,
    usdHash,
    floorConfig
);

// 5. Depositor enables the currency pair
rateManager.setDepositorCurrencyEnabled(
    managerId,
    address(escrow),
    depositId,
    venmoHash,
    usdHash,
    true
);

// 6. Intent takers see the effective rate
uint256 effectiveRate = escrow.getEffectiveRate(
    depositId,
    venmoHash,
    usdHash
); // Returns max(managerRate, depositorFloor)

Build docs developers (and LLMs) love