Router Integration Guide
Interacting with Boros contracts directly is not recommended for most use cases. The REST API and SDK handle authentication, calldata encoding, and transaction submission for you. Direct contract interaction is error-prone and may break if contract addresses or ABIs change. The information below is provided as a reference for advanced integrators only.
The Router contract serves as the main entry point for all user interactions with Boros.
Router follows a modular architecture:
- AuthModule: agent authentication and delegated trading (irrelevant for external parties)
- AMMModule: AMM-specific operations (add/remove liquidity, swaps)
- TradeModule: core trading operations (enter/exit markets, place/cancel orders, cash transfer)
- ConditionalModule: conditional order execution (used by stop-order system)
- DepositModule: deposit box operations (managed by Pendle backend)
- MiscModule: utility functions (simulation, batch operations)
Account Authorization
Currently, you can only operate on the main account (subaccount 0). Support for other subaccounts may be added in future versions.
Most Router functions require two key parameters:
bool cross: Specifies margin mode (true = cross-margin, false = isolated-margin)MarketId marketId: The market identifier
These parameters construct the MarketAcc (Market Account) identifier:
// Cross-margin account (can trade multiple markets)
bool cross = true;
MarketId marketId = MarketIdLib.CROSS; // Special cross-margin value
// Isolated-margin account (single market)
bool cross = false;
MarketId marketId = MarketId.wrap(12345); // Specific market ID
Vault Operations
Vault operations manage the underlying collateral tokens.
Deposit cash
router.vaultDeposit(
0, // accountId (0 = main account)
tokenId, // TokenId of collateral token
marketId, // marketId (2^24-1 for cross account)
1 ether // Raw token amount
);
Withdraw cash
Withdrawals follow a two-step process: request → cooldown period → finalize.
Checking Withdrawal Status: You can monitor your withdrawal request status and cooldown period through MarketHub's getUserWithdrawalStatus function. This returns detailed information including the pending amount and exact timestamp when withdrawal becomes available.
// Step 1: Request withdrawal
router.requestVaultWithdrawal(tokenId, 1 ether);
// Step 2: After cooldown period, finalize withdrawal on MarketHub
IMarketHub(marketHub).finalizeVaultWithdrawal(
address(this), // root address
tokenId // TokenId of collateral token
);
// Can cancel pending withdrawal
router.cancelVaultWithdrawal(tokenId);
Cash Transfer
Transfer cash between cross-margin and isolated-margin accounts using the same collateral token.
// Transfer from cross-margin to isolated-margin account
CashTransferReq memory req = CashTransferReq({
marketId: marketId, // Target isolated market
signedAmount: 1000e18 // Positive = from cross to isolated
});
router.cashTransfer(req);
// Transfer from isolated-margin back to cross-margin account
req.signedAmount = -500e18; // Negative = from isolated to cross
router.cashTransfer(req);
Subaccount Transfer
Transfer cash between subaccounts under the same root. This is distinct from cross/isolated cash transfer — it moves funds between different subaccount IDs (e.g., from subaccount 0 to subaccount 1).
router.subaccountTransfer(
1, // target accountId
tokenId, // TokenId of collateral token
marketId, // marketId (CROSS for cross-margin)
500e18, // amount to transfer
true // isDeposit: true = transfer TO target, false = transfer FROM target
);
Currently only subaccount 0 is available for external users. Subaccount transfers are primarily used internally.
Market Entry and Exit
Enter Markets
Users must enter markets before trading.
MarketId[] memory marketIds = new MarketId[](2);
marketIds[0] = marketId0;
marketIds[1] = marketId1;
// Enter multiple markets
EnterExitMarketsReq memory req = EnterExitMarketsReq({
cross: true,
isEnter: true,
marketIds: marketIds
});
router.enterExitMarkets(req);
Market Entry Limits: Each market account (marketAcc) can enter a maximum of 10 markets simultaneously. This limit is per combination of (address, accountId, tokenId) — i.e., per collateral type. Exceeding this limit will cause transactions to revert. You must exit expired or matured markets to free up slots before entering new ones.
Market Entrance Fees: A one-time entrance fee is charged when you first interact with any market. See Fees — Market Entrance Fees for amounts.
Minimum Cash Requirements: You must maintain minimum cash balances in your account to participate in markets. Check the specific requirements for cross vs isolated margin modes.
Exit Markets
Exit Requirements: To exit a market, you must:
- Close all positions (zero position size)
- Cancel all open orders
- Have no pending settlements
Market Maturity: When markets reach maturity, you should exit them to free up market slots.
MarketId[] memory marketIds = new MarketId[](2);
marketIds[0] = marketId0;
marketIds[1] = marketId1;
// Exit multiple markets
EnterExitMarketsReq memory req = EnterExitMarketsReq({
cross: true,
isEnter: false,
marketIds: marketIds
});
router.enterExitMarkets(req);
Order Book Trading
Place Single Order
Compound Function: placeSingleOrder is a compound function that can:
- Automatically enter markets if
enterMarketis true - Cancel existing orders via
idToStrictCancel - Place the new order
- Transfer cash for isolated margin positions
- Automatically exit markets if
exitMarketis true
This reduces the number of transactions needed for complex operations.
// Basic limit order
SingleOrderReq memory req = SingleOrderReq({
order: OrderReq({
cross: true, // Cross-margin
marketId: marketId,
ammId: AMMIdLib.ZERO, // Order book only (not AMM)
side: Side.LONG, // Buy interest rate swap
tif: TimeInForce.GTC, // Good till cancelled
size: 1000e18, // Position size
tick: 125 // Price tick (≈5% rate)
}),
enterMarket: false, // Don't auto-enter market
idToStrictCancel: OrderIdLib.ZERO, // No order to cancel
exitMarket: false, // Don't auto-exit
isolated_cashIn: 0, // For isolated margin only
isolated_cashTransferAll: false,
desiredMatchRate: 0 // Accept any match rate
});
router.placeSingleOrder(req);