SwapRouter
Overview
Section titled “Overview”The SwapRouter is a Venus periphery contract that enables users to swap tokens and interact with Venus markets in a single transaction. It supports both ERC-20 and native tokens (e.g., BNB), allowing users to supply or repay Venus markets seamlessly after a swap. This contract is designed for composability, security, and gas efficiency.
Prerequisites
Section titled “Prerequisites”Before using the SwapRouter, users should:
- Approve the SwapRouter to spend their tokens (for ERC-20 operations).
- Ensure the target vToken market is listed in the Venus Comptroller.
- For native token operations, send the appropriate value with the transaction.
Inheritance
Section titled “Inheritance”Ownable2StepUpgradeable— Two-step ownership transfer patternReentrancyGuardUpgradeable— Reentrancy protection
Immutable State Variables
Section titled “Immutable State Variables”NATIVE_TOKEN_ADDR: Address representing the native token (e.g., BNB)COMPTROLLER: Venus Comptroller contract for market validationSWAP_HELPER: SwapHelper contract for executing swapsWRAPPED_NATIVE: Wrapped native token contract (e.g., WBNB)NATIVE_VTOKEN: vToken contract for the native token (e.g., vBNB)
Architecture
Section titled “Architecture”┌───────────────────────────────────────────────┐│ Venus Core Protocol ││ ┌───────────┐ ┌──────────┐ ││ │Comptroller│ │ vTokens │ ││ └─────┬─────┘ └────┬─────┘ │└────────┼─────────────┼───────────────────────┘ │ │ ▼ ▼┌───────────────────────────────────────────────┐│ SwapRouter ││ - Swaps tokens via SwapHelper ││ - Supplies or repays Venus markets ││ - Handles native and ERC-20 tokens │└───────────────┬──────────────────────────────┘ │ ▼┌───────────────────────────────────────────────┐│ SwapHelper ││ - Executes authorized swaps ││ - Interacts with DEX protocols │└───────────────────────────────────────────────┘Solidity API
Section titled “Solidity API”swapAndSupply
Section titled “swapAndSupply”Swaps an input token for the underlying asset of a Venus market and supplies it on behalf of the user.
function swapAndSupply( address vToken, address tokenIn, uint256 amountIn, uint256 minAmountOut, bytes calldata swapCallData) externalParameters:
vToken: The vToken market to supply totokenIn: The input token to swap fromamountIn: The amount of input tokens to swapminAmountOut: The minimum amount of output tokens expectedswapCallData: Encoded swap instructions for SwapHelper
Scenario Example: User wants to supply 1,000 USDC to a Venus market using DAI:
- User holds 1,000 DAI.
- Calls
swapAndSupplywith vUSDC, DAI, 1,000 DAI, minAmountOut = 995 USDC, and swapCallData for DAI→USDC swap. - SwapRouter swaps 1,000 DAI → ~998 USDC.
- Supplies ~998 USDC to vUSDC market.
- Final: User has ~998 USDC supplied, 0 DAI, receives vUSDC tokens.
Events:
SwapAndSupply
Errors:
ZeroAmount: Thrown ifamountInis zero.ZeroAddress: Thrown ifvTokenis zero address.MarketNotListed: Thrown ifvTokenis not listed in Comptroller.SwapFailed: Thrown if swap operation fails.InsufficientAmountOut: Thrown if received amount is less thanminAmountOut.NoTokensReceived: Thrown if no tokens are received from swap.SupplyFailed: Thrown if supply to Venus market fails (returns error code).
swapNativeAndSupply
Section titled “swapNativeAndSupply”Swaps native tokens (e.g., BNB) for the underlying asset and supplies it to a Venus market.
function swapNativeAndSupply( address vToken, uint256 minAmountOut, bytes calldata swapCallData) external payableParameters:
vToken: The vToken market to supply tominAmountOut: The minimum amount of output tokens expectedswapCallData: Encoded swap instructions for SwapHelper
Scenario Example:
User wants to supply 2 BNB to a Venus market using native BNB:
- User holds 2 BNB.
- Calls
swapNativeAndSupplywith vUSDT, minAmountOut = 600 USDT, and swapCallData for BNB→USDT swap, sending 2 BNB. - SwapRouter wraps 2 BNB to WBNB, swaps WBNB → ~610 USDT.
- Supplies ~610 USDT to vUSDT market.
- Final: User has ~610 USDT supplied, 0 BNB, receives vUSDT tokens.
Events:
SwapAndSupply
Errors:
ZeroAmount: Thrown ifmsg.valueis zero.ZeroAddress: Thrown ifvTokenis zero address.MarketNotListed: Thrown ifvTokenis not listed in Comptroller.SwapFailed: Thrown if swap operation fails.InsufficientAmountOut: Thrown if received amount is less thanminAmountOut.NoTokensReceived: Thrown if no tokens are received from swap.SupplyFailed: Thrown if supply to Venus market fails.
swapAndRepay
Section titled “swapAndRepay”Swaps an input token and repays the user’s debt in a Venus market.
function swapAndRepay( address vToken, address tokenIn, uint256 amountIn, uint256 minAmountOut, bytes calldata swapCallData) externalParameters:
vToken: The vToken market to repay debt totokenIn: The input token to swap fromamountIn: The amount of input tokens to swapminAmountOut: The minimum amount of output tokens expectedswapCallData: Encoded swap instructions for SwapHelper
Scenario Example: User has 500 USDT debt in a Venus market and wants to repay using USDC:
- User holds 510 USDC.
- Calls
swapAndRepaywith vUSDT, USDC, 510 USDC, minAmountOut = 500 USDT, and swapCallData for USDC→USDT swap. - SwapRouter swaps 510 USDC → ~505 USDT.
- Repays 500 USDT debt in vUSDT market.
- Final: User has 0 USDT debt, 5 USDT returned as excess, 0 USDC.
Events:
SwapAndRepay
Errors:
ZeroAmount: Thrown ifamountInor user debt is zero.ZeroAddress: Thrown ifvTokenis zero address.MarketNotListed: Thrown ifvTokenis not listed in Comptroller.SwapFailed: Thrown if swap operation fails.InsufficientAmountOut: Thrown if received amount is less thanminAmountOut.NoTokensReceived: Thrown if no tokens are received from swap.RepayFailed: Thrown if repay to Venus market fails (returns error code).
swapNativeAndRepay
Section titled “swapNativeAndRepay”Swaps native tokens and repays the user’s debt in a Venus market.
function swapNativeAndRepay( address vToken, uint256 minAmountOut, bytes calldata swapCallData) external payableParameters:
vToken: The vToken market to repay debt tominAmountOut: The minimum amount of output tokens expectedswapCallData: Encoded swap instructions for SwapHelper
Scenario Example:
User has 1,000 DAI debt in vDAI and wants to repay it using up to 0.5 BNB:
- User holds 0.5 BNB.
- Calls
swapNativeAndRepaywith vDAI, minAmountOut = 1,000 DAI, and swapCallData for BNB→DAI swap, sending 0.5 BNB. - SwapRouter wraps 0.5 BNB to WBNB and swaps WBNB → ~1,010 DAI.
- Repays 1,000 DAI debt in vDAI market.
- Final: User has 0 DAI debt, 10 DAI returned as excess output, and 0 BNB remaining from the amount sent.
Events:
SwapAndRepay
Errors:
ZeroAmount: Thrown ifmsg.valueor user debt is zero.ZeroAddress: Thrown ifvTokenis zero address.MarketNotListed: Thrown ifvTokenis not listed in Comptroller.SwapFailed: Thrown if swap operation fails.InsufficientAmountOut: Thrown if received amount is less thanminAmountOut.NoTokensReceived: Thrown if no tokens are received from swap.RepayFailed: Thrown if repay to Venus market fails.
swapAndRepayFull
Section titled “swapAndRepayFull”Swaps tokens to repay the user’s full debt in a Venus market.
function swapAndRepayFull( address vToken, address tokenIn, uint256 maxAmountIn, bytes calldata swapCallData) externalParameters:
vToken: The vToken market to repay full debt totokenIn: The input token to swap frommaxAmountIn: The ERC-20 amount that the Router requests from the caller and makes available to the signed SwapHelper execution. Treat it as the maximum total input you are willing to expose for the current debt amount.swapCallData: Backend-signed swap instructions for SwapHelper. For a cross-asset route, DEX limits and any unused-input sweep or refund must be encoded in these instructions; SwapRouter does not validate them separately. IftokenInis already the market underlying, SwapHelper is skipped and_repayreturns input above the current debt as excess underlying.
Scenario Example: User has approximately 1,200 USDC debt in a Venus market and wants to repay the full debt using at most 1,250 DAI:
- User holds 1,250 DAI.
- Requests a fresh signed DAI→USDC quote and verifies its input limit, price limits, and unused-input recipient.
- Calls
swapAndRepayFullwith vUSDC, DAI, 1,250 DAI, and the signedswapCallData. - SwapRouter reads the current debt and requires the swap to return at least that amount of USDC. If the swap returns 1,210 USDC and the current debt is 1,200 USDC, it repays the debt and returns 10 USDC as excess output.
- Because this is a cross-asset route, any unused DAI is handled only as encoded in the signed SwapHelper/DEX call sequence; SwapRouter does not itself refund that unused input.
swapAndRepayFull has no caller-selected minAmountOut. Its on-chain output floor is the current debt amount. The caller’s input-side protection comes from choosing maxAmountIn conservatively and verifying the price and amount limits in the signed swapCallData.
Events:
SwapAndRepay
Errors:
ZeroAmount: Thrown ifmaxAmountInor user debt is zero.ZeroAddress: Thrown ifvTokenis zero address.MarketNotListed: Thrown ifvTokenis not listed in Comptroller.SwapFailed: Thrown if swap operation fails.InsufficientAmountOut: Thrown if received amount is less than debt amount.NoTokensReceived: Thrown if no tokens are received from swap.RepayFailed: Thrown if repay to Venus market fails.
swapNativeAndRepayFull
Section titled “swapNativeAndRepayFull”Swaps native tokens to repay the user’s full debt in a Venus market.
function swapNativeAndRepayFull( address vToken, bytes calldata swapCallData) external payableParameters:
vToken: The vToken market to repay full debt toswapCallData: Backend-signed swap instructions for SwapHelper. For a cross-asset route, DEX limits and any unused-input sweep or refund must be encoded in these instructions; SwapRouter does not validate them separately. When repayingvBNB, the wrapped input and market underlying are both WBNB, so SwapHelper is skipped and_repayreturns WBNB above the current debt.
Scenario Example:
User has approximately 250 USDT debt in vUSDT and is willing to spend at most 0.85 BNB to repay it:
- User holds 0.85 BNB.
- Requests a fresh signed BNB→USDT quote and verifies its maximum-input and price limits.
- Calls
swapNativeAndRepayFullwith vUSDT and the signedswapCallData, sending 0.85 BNB. - SwapRouter wraps the full
msg.value, reads the current USDT debt, and requires the swap to return at least that debt amount. If the swap returns 260 USDT and the current debt is 250 USDT, it repays the debt and returns 10 USDT as excess output. - The excess is returned as USDT, not BNB. Any unused WBNB input is handled only as encoded in the signed SwapHelper/DEX call sequence.
swapNativeAndRepayFull has no caller-selected minAmountOut. Its output floor is the current debt amount, while msg.value is the total input cap made available to SwapHelper.
Events:
SwapAndRepay
Errors:
ZeroAmount: Thrown ifmsg.valueor user debt is zero.ZeroAddress: Thrown ifvTokenis zero address.MarketNotListed: Thrown ifvTokenis not listed in Comptroller.SwapFailed: Thrown if swap operation fails.InsufficientAmountOut: Thrown if received amount is less than debt amount.NoTokensReceived: Thrown if no tokens are received from swap.RepayFailed: Thrown if repay to Venus market fails.
sweepToken
Section titled “sweepToken”Allows the contract owner to recover leftover ERC-20 tokens from the contract.
function sweepToken(IERC20Upgradeable token) external onlyOwnerExample:
swapRouter.sweepToken(tokenAddress);sweepNative
Section titled “sweepNative”Allows the contract owner to recover leftover native tokens from the contract.
function sweepNative() external onlyOwnerExample:
swapRouter.sweepNative();Error Definitions
Section titled “Error Definitions”ZeroAddress(): Thrown when a zero address is provided for required parameters.ZeroAmount(): Thrown when a zero amount is provided for required parameters or user debt is zero.SupplyFailed(uint256 errorCode): Thrown when supply operation to Venus market fails. Returns the error code from the vToken.RepayFailed(uint256 errorCode): Thrown when repay operation to Venus market fails. Returns the error code from the vToken.SwapFailed(): Thrown when the swap operation fails (e.g., SwapHelper call reverts).NoTokensReceived(): Thrown when no tokens are received from the swap operation.NativeTransferFailed(): Thrown whensweepNativefails to send native currency.InsufficientBalance(): Declared in the ABI but not explicitly used by the mapped implementation’s entry paths. Insufficient balance or allowance normally causes the token transfer to revert instead.MarketNotListed(address vToken): Thrown when the vToken market is not listed in the Comptroller.InsufficientAmountOut(uint256 amountOut, uint256 minAmountOut): Thrown when the swap output is less than the minimum required amount (slippage protection).UnauthorizedNativeSender(address sender): Thrown when an unauthorized sender tries to send native tokens to the contract.
Security Considerations
Section titled “Security Considerations”Access Control
Section titled “Access Control”- Only the contract owner can sweep tokens or native currency.
- All swap/supply/repay functions are open to users, but require proper approvals and market listing.
Reentrancy Protection
Section titled “Reentrancy Protection”- All user swap, supply, and repay entry points are protected by the
nonReentrantmodifier. The initializer and owner-only sweep functions are not markednonReentrant.
Price and Slippage Protection
Section titled “Price and Slippage Protection”swapAndSupply,swapNativeAndSupply,swapAndRepay, andswapNativeAndRepayaccept a caller-selectedminAmountOutand revert when the received output is below it.swapAndRepayFullandswapNativeAndRepayFulldo not accept a caller-selectedminAmountOut. They read the caller’s current debt and require the swap output to be at least that debt amount.- For full-repay calls, choose
maxAmountInormsg.valueas the maximum input you are willing to expose and verify the DEX price and amount limits encoded in the backend-signedswapCallData. A quote can consume the full maximum at a poor price while still producing enough output to repay the debt.
Market Validation
Section titled “Market Validation”- The contract checks that the vToken market is listed in the Comptroller before proceeding.
Excess and Unused Tokens
Section titled “Excess and Unused Tokens”_repayreturns underlying tokens received above the current debt amount. On a cross-asset route, this is an excess-tokenOutrefund; unusedtokenInremains dependent on the backend-signed SwapHelper/DEX call sequence, including its recipients and sweep steps.- If
tokenInalready equals the market underlying, SwapHelper is skipped. The full input is treated as output, and_repayreturns the amount above the debt in that same underlying token. - For native input used to repay
vBNB, the Router first wraps BNB to WBNB. Because the market underlying is also WBNB,_repayreturns any excess as WBNB, not BNB.
Integration
Section titled “Integration”For Users
Section titled “For Users”- Approve only the verified, chain-specific SwapRouter proxy to spend your tokens (for ERC-20 operations). Do not approve the implementation or SwapHelper. Approve no more than the intended input cap and reduce or revoke any unused allowance after the transaction. Find the proxy on the Deployed Contracts page:
IERC20(tokenIn).approve(swapRouterAddress, amount);
- Call the desired function (
swapAndSupply,swapAndRepay, etc.) with the appropriate parameters and swapCallData. - For native token operations, send the required value with your transaction.
- Monitor events (
SwapAndSupply,SwapAndRepay) for transaction status.
For Developers
Section titled “For Developers”Swap API Integration
Section titled “Swap API Integration”Cross-asset operations require signed swap data from the Venus Swap API:
- Request a fresh swap quote from the API with source and destination tokens.
- Receive encoded
multicallparameters with a backend signature. - Verify the quote’s input cap, DEX price or amount limits, deadline, output and unused-input recipients, and sweep steps.
- Pass the signed
swapCallDatato the entry function. For full-repay calls, remember that SwapRouter enforces only the current debt as its output floor.
Audits
Section titled “Audits”Published SwapRouter audit reports are available in the venus-periphery repository. An audit report covers the code and version reviewed; verify that the live proxy implementation bytecode matches the audited version before treating the report as coverage for a deployment.
Deployment
Section titled “Deployment”See Deployed Contracts for current addresses.