Skip to content

SwapHelper

The SwapHelper is a Venus periphery contract for backend-authorized token operations. It provides an atomic multicall interface with EIP-712 signature verification, allowing the configured backend signer to authorize a specific batch of approvals, arbitrary external calls, and token sweeps.

Version scope: This API reference follows the standalone SwapHelper in venus-periphery v1.2.0. It is not a proxy. Its current owner, pending owner, backend signer, EIP-712 domain/address, and consuming manager/router versions are live trust inputs that must be checked before use.

Token swaps in leverage operations require interaction with external DEX protocols. The SwapHelper provides a controlled execution environment where:

  1. Backend Authorization: All swap operations must be signed by the authorized backend signer
  2. Atomic Execution: Multiple operations execute atomically via multicall
  3. Replay Protection: Each signed payload is single-use via salt tracking
  4. Time-Bounded: Signatures include a deadline to prevent stale quote execution

The contract is designed to be called by the LeverageStrategiesManager during flash loan callbacks, though it can also be used independently for authorized token operations.

┌─────────────────────────┐
│ Venus Swap API │
│ (Backend Service) │
└───────────┬─────────────┘
│ Signs multicall payload
▼
┌─────────────────────────┐
│ Frontend / User │
│ │
└───────────┬─────────────┘
│ Passes signed data to LSM
▼
┌─────────────────────────┐
│ LeverageStrategiesManager│
│ (Flash Loan Callback) │
└───────────┬─────────────┘
│ Calls multicall
▼
┌─────────────────────────┐
│ SwapHelper │
│ - Verifies signature │
│ - Executes calls │
└───────────┬─────────────┘
│ genericCall
▼
┌─────────────────────────┐
│ DEX Router │
│ (Uniswap, 1inch, etc.) │
└─────────────────────────┘
  • EIP712 - EIP-712 typed data hashing for signature verification
  • Ownable2Step - Two-step ownership transfer and owner access for admin functions
  • ReentrancyGuard - Reentrancy protection
VariableTypeDescription
backendSigneraddressAddress authorized to sign multicall operations
usedSaltsmapping(bytes32 => bool)Tracks used salts for replay protection
bytes32 internal constant MULTICALL_TYPEHASH =
keccak256("Multicall(address caller,bytes[] calls,uint256 deadline,bytes32 salt)");

Executes multiple calls atomically with backend signature verification.

function multicall(
bytes[] calldata calls,
uint256 deadline,
bytes32 salt,
bytes calldata signature
) external nonReentrant
NameTypeDescription
callsbytes[]Array of encoded function calls to execute on this contract
deadlineuint256Unix timestamp after which the transaction will revert
saltbytes32Unique value ensuring this multicall can only be executed once
signaturebytesEIP-712 signature from the backend signer
  1. Validate calls array is not empty
  2. Check block.timestamp <= deadline
  3. Verify signature is provided
  4. Check salt has not been used
  5. Mark salt as used
  6. Recover signer from EIP-712 digest (including caller address) and verify against backendSigner
  7. Execute each call atomically
  8. Emit MulticallExecuted event

A multicall for a swap operation typically contains:

calls = [
abi.encodeCall(SwapHelper.approveMax, (tokenIn, dexRouter)),
abi.encodeCall(SwapHelper.genericCall, (dexRouter, swapCalldata)),
abi.encodeCall(SwapHelper.sweep, (tokenOut, recipient))
]
  • MulticallExecuted(caller, callsCount, deadline, salt) on success
  • NoCallsProvided if calls array is empty
  • DeadlineReached if block.timestamp > deadline
  • MissingSignature if signature length is zero
  • SaltAlreadyUsed if salt has been used before
  • Unauthorized if recovered signer does not match backendSigner

Executes an arbitrary call to an external contract. Only callable via multicall or by the owner.

function genericCall(address target, bytes calldata data) external onlyOwnerOrSelf
NameTypeDescription
targetaddressAddress of the contract to call
databytesEncoded function call data
  • GenericCallExecuted(target, data) on success
  • Only callable by owner or contract itself (via multicall)
  • CallerNotAuthorized if caller is not owner or contract itself

Transfers the entire balance of an ERC-20 token to a specified recipient.

function sweep(IERC20Upgradeable token, address to) external onlyOwnerOrSelf
NameTypeDescription
tokenIERC20UpgradeableERC-20 token contract to sweep
toaddressRecipient address for the swept tokens
  • Swept(token, to, amount) on execution (emits even if amount is 0)
  • Only callable by owner or contract itself (via multicall)
  • CallerNotAuthorized if caller is not authorized

Grants maximum approval of an ERC-20 token to a spender.

function approveMax(IERC20Upgradeable token, address spender) external onlyOwnerOrSelf
NameTypeDescription
tokenIERC20UpgradeableERC-20 token contract to approve
spenderaddressAddress to grant approval to
  • ApprovedMax(token, spender) on success
  • Only callable by owner or contract itself (via multicall)
  • CallerNotAuthorized if caller is not authorized

Updates the authorized backend signer address.

function setBackendSigner(address newSigner) external onlyOwner
NameTypeDescription
newSigneraddressNew backend signer address
  • BackendSignerUpdated(oldSigner, newSigner) on success
  • Only callable by contract owner
  • ZeroAddress if newSigner is address(0)
EventParametersDescription
BackendSignerUpdatedoldSigner, newSignerBackend signer address changed
MulticallExecutedcaller, callsCount, deadline, saltMulticall successfully executed
Swepttoken, to, amountTokens transferred out of contract
ApprovedMaxtoken, spenderMaximum approval granted
GenericCallExecutedtarget, dataExternal call executed
ErrorDescription
DeadlineReachedTransaction deadline has passed
UnauthorizedSignature verification failed
ZeroAddressZero address provided as parameter
SaltAlreadyUsedSalt has already been used (replay protection)
CallerNotAuthorizedCaller is not owner or contract itself
NoCallsProvidedEmpty calls array in multicall
MissingSignatureSignature is required but empty

All multicall operations require a valid EIP-712 signature from the backendSigner. The signature covers:

  • The caller address (prevents cross-address replay)
  • The encoded calls array
  • The deadline timestamp
  • A unique salt

Assuming the backend signer and owner remain trusted, these checks prevent:

  • Unauthorized swap execution
  • Cross-address signature replay attacks
  • Replay attacks (via salt tracking)
  • Stale quote execution (via deadline)

They do not constrain what a valid signed batch may call. The signer can authorize approveMax, genericCall, and sweep, and the owner can call those functions directly. Integrations must validate the complete encoded call list and output conditions, not only the signature and deadline.

Functions that interact with external contracts (genericCall, sweep, approveMax) are protected by onlyOwnerOrSelf:

  • Direct calls require owner privileges
  • Calls within multicall are authorized via backend signature

approveMax grants an unlimited allowance and sweep transfers the helper’s full balance of the selected token. End users should not approve SwapHelper or leave tokens in it; callers such as LeverageStrategiesManager should transfer only the amount needed for an atomic operation and validate the returned output.

The multicall function is protected by OpenZeppelin’s nonReentrant modifier, preventing reentrant calls during execution.

The contract initializes with:

  • Name: "VenusSwap"
  • Version: "1"

This domain separation ensures signatures are specific to this contract and version.

SwapHelper is currently published for BNB Chain Mainnet and testnet. See Deployed Contracts for current addresses. Source portability does not establish a deployment on another network.

The SwapHelper is the designated swap executor for the LeverageStrategiesManager. During leverage operations:

  1. Frontend obtains signed swapData from Venus Swap API
  2. User calls LeverageStrategiesManager entry function with swapData
  3. LSM transfers tokens to SwapHelper during flash loan callback
  4. LSM calls swapHelper.call(swapData) which invokes multicall
  5. SwapHelper executes the swap and sweeps output tokens back to LSM
  6. LSM validates received amount against slippage protection

SwapHelper exposes a clear interface for integration:

interface ISwapHelper {
function multicall(
bytes[] calldata calls,
uint256 deadline,
bytes32 salt,
bytes calldata signature
) external;
function genericCall(address target, bytes calldata data) external;
function sweep(IERC20Upgradeable token, address to) external;
function approveMax(IERC20Upgradeable token, address spender) external;
function setBackendSigner(address newSigner) external;
}

When generating signatures for the multicall function, the backend must include the caller address in the EIP-712 digest:

bytes32 digest = keccak256(abi.encode(
MULTICALL_TYPEHASH,
callerAddress, // Address that will call multicall
keccak256(abi.encodePacked(callHashes)),
deadline,
salt
));

This ensures signatures are specific to the address calling multicall and cannot be replayed from a different address.

When integrating with the Venus Swap API, include:

ParameterDescription
tokenInSource token address
tokenOutDestination token address
amountInExact input amount
recipientLeverageStrategiesManager address
deadlineUnix timestamp for expiry
callerAddress that will call multicall

The API returns encoded multicall parameters with the backend signature that includes the caller address.

Published SwapHelper audit reports are indexed in the venus-periphery v1.2.0 release. Check each report’s scope and reviewed commit against the deployed standalone bytecode.