|
| 1 | +# BreakerBox Rate Feed Trading Mode SDK Usage |
| 2 | + |
| 3 | +This document shows how to use the rate feed trading mode functionality from the Mento SDK. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +The SDK exposes two key functions for working with rate feed trading modes: |
| 8 | +- `getRateFeedTradingMode()` - Query the current trading mode for a rate feed |
| 9 | +- `toRateFeedId()` - Convert a rate feed identifier string to its address |
| 10 | + |
| 11 | +## Trading Modes |
| 12 | + |
| 13 | +The SDK exposes a `TradingMode` enum with three possible values: |
| 14 | + |
| 15 | +- `TradingMode.BIDIRECTIONAL` (0) - Bidirectional trading is enabled |
| 16 | +- `TradingMode.HALTED` (1) - Trading is temporarily halted (circuit breaker tripped) |
| 17 | +- `TradingMode.DISABLED` (2) - Trading is permanently disabled |
| 18 | + |
| 19 | +## Usage Examples |
| 20 | + |
| 21 | +### Example 1: Using toRateFeedId() to compute a rate feed address |
| 22 | + |
| 23 | +```typescript |
| 24 | +import { Mento, TradingMode, toRateFeedId } from '@mento-protocol/mento-sdk' |
| 25 | +import { ethers } from 'ethers' |
| 26 | + |
| 27 | +async function checkTradingModeByIdentifier() { |
| 28 | + const provider = new ethers.providers.JsonRpcProvider('https://forno.celo.org') |
| 29 | + const mento = await Mento.create(provider) |
| 30 | + |
| 31 | + // Compute the rate feed ID from a string identifier |
| 32 | + const eurUsdRateFeedId = toRateFeedId('EURUSD') |
| 33 | + console.log(`Rate Feed ID for EURUSD: ${eurUsdRateFeedId}`) |
| 34 | + // Output: 0x5d5a22116233bdb2a9c2977279cc348b8b8ce917 |
| 35 | + |
| 36 | + // Works with relayed feeds too |
| 37 | + const copUsdRateFeedId = toRateFeedId('relayed:COPUSD') |
| 38 | + console.log(`Rate Feed ID for relayed:COPUSD: ${copUsdRateFeedId}`) |
| 39 | + // Output: 0x0196d1f4fda21fa442e53eaf18bf31282f6139f1 |
| 40 | + |
| 41 | + // Get the current trading mode |
| 42 | + const tradingMode = await mento.getRateFeedTradingMode(eurUsdRateFeedId) |
| 43 | + |
| 44 | + // Check the mode |
| 45 | + switch (tradingMode) { |
| 46 | + case TradingMode.BIDIRECTIONAL: |
| 47 | + console.log('Trading is enabled') |
| 48 | + break |
| 49 | + case TradingMode.HALTED: |
| 50 | + console.log('Trading is halted - circuit breaker tripped') |
| 51 | + break |
| 52 | + case TradingMode.DISABLED: |
| 53 | + console.log('Trading is disabled') |
| 54 | + break |
| 55 | + } |
| 56 | +} |
| 57 | +``` |
| 58 | + |
| 59 | +### Example 2: Getting rate feed ID from an exchange |
| 60 | + |
| 61 | +```typescript |
| 62 | +import { Mento, TradingMode } from '@mento-protocol/mento-sdk' |
| 63 | +import { BiPoolManager__factory } from '@mento-protocol/mento-core-ts' |
| 64 | +import { ethers } from 'ethers' |
| 65 | + |
| 66 | +async function checkTradingModeFromExchange() { |
| 67 | + const provider = new ethers.providers.JsonRpcProvider('https://forno.celo.org') |
| 68 | + const mento = await Mento.create(provider) |
| 69 | + |
| 70 | + // Get a rate feed ID from an exchange |
| 71 | + const exchanges = await mento.getExchanges() |
| 72 | + const biPoolManager = BiPoolManager__factory.connect( |
| 73 | + exchanges[0].providerAddr, |
| 74 | + provider |
| 75 | + ) |
| 76 | + const exchangeConfig = await biPoolManager.getPoolExchange(exchanges[0].id) |
| 77 | + const rateFeedId = exchangeConfig.config.referenceRateFeedID |
| 78 | + |
| 79 | + // Get the current trading mode |
| 80 | + const tradingMode = await mento.getRateFeedTradingMode(rateFeedId) |
| 81 | + console.log(`Trading mode: ${tradingMode}`) |
| 82 | + |
| 83 | + // Alternative: use the existing convenience method for exchange-level check |
| 84 | + const isEnabled = await mento.isTradingEnabled(exchanges[0].id) |
| 85 | + console.log(`Is trading enabled: ${isEnabled}`) |
| 86 | +} |
| 87 | +``` |
| 88 | + |
| 89 | +## Implementation Details |
| 90 | + |
| 91 | +### toRateFeedId(rateFeed: string): Address |
| 92 | + |
| 93 | +This utility function computes the rate feed ID from a string identifier following the Solidity formula: |
| 94 | +```solidity |
| 95 | +address(uint160(uint256(keccak256(abi.encodePacked(rateFeed))))) |
| 96 | +``` |
| 97 | + |
| 98 | +Steps: |
| 99 | +1. Compute keccak256 hash of the UTF-8 encoded string |
| 100 | +2. Convert to BigInt (uint256) |
| 101 | +3. Mask to 160 bits (uint160) |
| 102 | +4. Convert to hex address string |
| 103 | + |
| 104 | +### getRateFeedTradingMode(rateFeedId: Address): Promise<TradingMode> |
| 105 | + |
| 106 | +This method: |
| 107 | +1. Gets the BreakerBox contract address for the current chain |
| 108 | +2. Connects to the BreakerBox contract |
| 109 | +3. Queries `getRateFeedTradingMode()` with the provided rate feed ID |
| 110 | +4. Returns the trading mode as a `TradingMode` enum value |
| 111 | + |
| 112 | +**Note:** Multiple exchanges can share the same rate feed, so the BreakerBox operates at the rate feed level rather than the exchange level. |
| 113 | + |
| 114 | +## Related Methods |
| 115 | + |
| 116 | +- `isPairTradable(tokenIn: Address, tokenOut: Address): Promise<boolean>` - Checks if a token pair is currently tradable (most convenient for UI use) |
| 117 | +- `isTradingEnabled(exchangeId: string): Promise<boolean>` - Returns true only if the exchange's rate feed mode is BIDIRECTIONAL (operates at exchange level) |
| 118 | +- `getTradingLimits(exchangeId: string): Promise<TradingLimit[]>` - Get trading limits for an exchange |
| 119 | + |
| 120 | +## Important Notes |
| 121 | + |
| 122 | +### Multi-Hop Routes |
| 123 | + |
| 124 | +The `isPairTradable()` method intelligently handles multi-hop routes. For example, if you want to swap CELO → USDT but there's no direct exchange, the route might be: |
| 125 | +- CELO → cUSD (hop 1) |
| 126 | +- cUSD → USDT (hop 2) |
| 127 | + |
| 128 | +The method will check that **ALL** rate feeds in the path are in BIDIRECTIONAL mode. If any hop is HALTED or DISABLED, the entire pair is considered not tradable. |
| 129 | + |
| 130 | +```typescript |
| 131 | +// Example: Multi-hop route check |
| 132 | +const isTradable = await mento.isPairTradable(celoAddress, usdtAddress) |
| 133 | +// Returns true ONLY if both CELO/cUSD AND cUSD/USDT rate feeds are BIDIRECTIONAL |
| 134 | +``` |
| 135 | + |
| 136 | +### Method Comparison |
| 137 | + |
| 138 | +- **`isPairTradable(tokenIn, tokenOut)`** - Operates at the token pair level. Checks all rate feeds in the routing path. **Use this for UI validation.** |
| 139 | +- **`getRateFeedTradingMode(rateFeedId)`** - Operates at the rate feed level. Returns the specific mode for a single rate feed. |
| 140 | +- **`isTradingEnabled(exchangeId)`** - Operates at the exchange level. Checks a single exchange by ID. |
| 141 | + |
| 142 | +## UI Integration Example |
| 143 | + |
| 144 | +Here's a practical example for a trading UI with token dropdowns: |
| 145 | + |
| 146 | +```typescript |
| 147 | +import { Mento, TradingMode } from '@mento-protocol/mento-sdk' |
| 148 | +import { ethers } from 'ethers' |
| 149 | + |
| 150 | +// Your UI state |
| 151 | +interface TokenDropdownState { |
| 152 | + tokenToSell: string // token address |
| 153 | + tokenToBuy: string // token address |
| 154 | +} |
| 155 | + |
| 156 | +async function checkIfPairIsTradable( |
| 157 | + state: TokenDropdownState, |
| 158 | + mento: Mento |
| 159 | +): Promise<boolean> { |
| 160 | + if (!state.tokenToSell || !state.tokenToBuy) { |
| 161 | + return false |
| 162 | + } |
| 163 | + |
| 164 | + try { |
| 165 | + // Simple check - returns true if tradable, false if not |
| 166 | + const isTradable = await mento.isPairTradable( |
| 167 | + state.tokenToSell, |
| 168 | + state.tokenToBuy |
| 169 | + ) |
| 170 | + return isTradable |
| 171 | + } catch (error) { |
| 172 | + // Pair doesn't exist or network error |
| 173 | + console.error('Error checking pair tradability:', error) |
| 174 | + return false |
| 175 | + } |
| 176 | +} |
| 177 | + |
| 178 | +// React example |
| 179 | +function TradingForm() { |
| 180 | + const [tokenToSell, setTokenToSell] = useState<string>('') |
| 181 | + const [tokenToBuy, setTokenToBuy] = useState<string>('') |
| 182 | + const [isTradable, setIsTradable] = useState<boolean>(false) |
| 183 | + const [isLoading, setIsLoading] = useState<boolean>(false) |
| 184 | + |
| 185 | + useEffect(() => { |
| 186 | + async function checkPair() { |
| 187 | + if (!tokenToSell || !tokenToBuy) { |
| 188 | + setIsTradable(false) |
| 189 | + return |
| 190 | + } |
| 191 | + |
| 192 | + setIsLoading(true) |
| 193 | + try { |
| 194 | + const tradable = await mento.isPairTradable(tokenToSell, tokenToBuy) |
| 195 | + setIsTradable(tradable) |
| 196 | + } catch (error) { |
| 197 | + setIsTradable(false) |
| 198 | + } finally { |
| 199 | + setIsLoading(false) |
| 200 | + } |
| 201 | + } |
| 202 | + |
| 203 | + checkPair() |
| 204 | + }, [tokenToSell, tokenToBuy]) |
| 205 | + |
| 206 | + return ( |
| 207 | + <div> |
| 208 | + <select onChange={(e) => setTokenToSell(e.target.value)}> |
| 209 | + {/* token options */} |
| 210 | + </select> |
| 211 | + <select onChange={(e) => setTokenToBuy(e.target.value)}> |
| 212 | + {/* token options */} |
| 213 | + </select> |
| 214 | + |
| 215 | + {isLoading && <p>Checking tradability...</p>} |
| 216 | + {!isLoading && !isTradable && tokenToSell && tokenToBuy && ( |
| 217 | + <p style={{ color: 'red' }}> |
| 218 | + ⚠️ Trading is currently disabled for this pair |
| 219 | + </p> |
| 220 | + )} |
| 221 | + |
| 222 | + <button disabled={!isTradable || isLoading}> |
| 223 | + Swap |
| 224 | + </button> |
| 225 | + </div> |
| 226 | + ) |
| 227 | +} |
| 228 | +``` |
| 229 | + |
0 commit comments