Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/cowprotocol/flash-loan-router/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Borrower adapters are the bridge between the Flash-Loan Router and flash loan providers. Each adapter implements a standardized interface while handling provider-specific loan request and callback logic.

Adapter Architecture

The adapter system uses an abstract base contract that provides common functionality:
src/mixin/Borrower.sol
abstract contract Borrower is IBorrower {
    IFlashLoanRouter public immutable router;
    ICowSettlement public immutable settlementContract;
    
    constructor(IFlashLoanRouter _router) {
        router = _router;
        settlementContract = _router.settlementContract();
    }
}

Key Components

Router Reference

Immutable reference to the Flash-Loan Router that coordinates execution

Settlement Reference

Direct reference to CoW Settlement contract for authorization

Standard Interface

Common IBorrower interface for router interaction

Access Control

Modifiers ensuring only authorized callers

Core Functions

Router Interaction

The router calls adapters through the standard interface:
src/mixin/Borrower.sol
function flashLoanAndCallBack(
    address lender,
    IERC20 token,
    uint256 amount,
    bytes calldata callBackData
) external onlyRouter {
    triggerFlashLoan(lender, token, amount, callBackData);
}
lender
address
required
The flash loan provider contract address
token
IERC20
required
The ERC-20 token to borrow
amount
uint256
required
The amount of tokens to request
callBackData
bytes
required
Data to pass back to the router unchanged

Fund Management

Settlements can approve token transfers from borrowers:
src/mixin/Borrower.sol
function approve(
    IERC20 token,
    address target,
    uint256 amount
) external onlySettlementContract {
    token.forceApprove(target, amount);
}
Security: Only the settlement contract can set approvals. This prevents unauthorized access to borrowed funds.

Implementing an Adapter

To support a new flash loan provider, create a concrete adapter by:
  1. Inheriting the abstract Borrower contract
  2. Implementing triggerFlashLoan for provider-specific requests
  3. Implementing the provider’s callback interface
  4. Forwarding callbacks to flashLoanCallBack

Required Implementation

abstract contract Borrower {
    /// Implement this to request loans from the specific provider
    function triggerFlashLoan(
        address lender,
        IERC20 token,
        uint256 amount,
        bytes calldata callBackData
    ) internal virtual;
    
    /// Call this from the provider's callback function
    function flashLoanCallBack(bytes calldata callBackData) internal {
        router.borrowerCallBack(callBackData);
    }
}

Example: Aave Adapter

The Aave adapter demonstrates the pattern:
contract AaveBorrower is Borrower, IAaveFlashLoanReceiver {
    constructor(IFlashLoanRouter _router) Borrower(_router) {}
    
    function triggerFlashLoan(
        address lender,
        IERC20 token,
        uint256 amount,
        bytes calldata callBackData
    ) internal override {
        // Prepare Aave-specific parameters
        address[] memory assets = new address[](1);
        assets[0] = address(token);
        uint256[] memory amounts = new uint256[](1);
        amounts[0] = amount;
        uint256[] memory interestRateModes = new uint256[](1);
        interestRateModes[0] = 0; // No debt position
        
        // Request flash loan from Aave
        IAavePool(lender).flashLoan(
            address(this),
            assets,
            amounts,
            interestRateModes,
            address(this),
            callBackData,
            0 // referralCode
        );
    }
    
    function executeOperation(
        address[] calldata,
        uint256[] calldata,
        uint256[] calldata,
        address,
        bytes calldata callBackData
    ) external returns (bool) {
        flashLoanCallBack(callBackData);
        return true;
    }
}
Parameter Mapping:
  • assets: Single-element array with token address
  • amounts: Single-element array with loan amount
  • interestRateModes: Set to [0] to avoid opening debt positions
  • params: Pass through callBackData unchanged
  • referralCode: Set to 0 (currently inactive)
Callback Contract:
  • Must implement IAaveFlashLoanReceiver.executeOperation
  • Receives loan details and custom parameters
  • Returns true to indicate success
  • Calls flashLoanCallBack to continue router execution

Example: ERC-3156 Adapter

The ERC-3156 adapter shows a simpler pattern:
contract ERC3156Borrower is Borrower, IERC3156FlashBorrower {
    bytes32 private constant ERC3156_ONFLASHLOAN_SUCCESS = 
        keccak256("ERC3156FlashBorrower.onFlashLoan");
    
    constructor(IFlashLoanRouter _router) Borrower(_router) {}
    
    function triggerFlashLoan(
        address lender,
        IERC20 token,
        uint256 amount,
        bytes calldata callBackData
    ) internal override {
        bool success = IERC3156FlashLender(lender).flashLoan(
            this,
            address(token),
            amount,
            callBackData
        );
        require(success, "Flash loan was unsuccessful");
    }
    
    function onFlashLoan(
        address,
        address,
        uint256,
        uint256,
        bytes calldata callBackData
    ) external returns (bytes32) {
        flashLoanCallBack(callBackData);
        return ERC3156_ONFLASHLOAN_SUCCESS;
    }
}
ERC-3156 provides a standardized interface, making adapters simpler and more uniform across providers.

Access Control

Adapters enforce strict access control:

Router-Only Access

src/mixin/Borrower.sol
modifier onlyRouter() {
    require(msg.sender == address(router), "Not the router");
    _;
}
Only the registered router can trigger flash loans through the adapter.

Settlement-Only Access

src/mixin/Borrower.sol
modifier onlySettlementContract() {
    require(
        msg.sender == address(settlementContract),
        "Only callable in a settlement"
    );
    _;
}
Only the settlement contract can approve token transfers from the adapter.
Security: These access controls ensure that:
  • Only the router can initiate loans
  • Only settlements can access borrowed funds
  • External callers cannot disrupt the execution flow

Fund Flow

The adapter manages fund flow between lenders, router, and settlement:

Adding New Providers

To add support for a new flash loan provider:
1

Research Provider API

Understand the provider’s flash loan request and callback mechanisms
2

Create Adapter Contract

contract NewProviderBorrower is Borrower, IProviderCallback {
    constructor(IFlashLoanRouter _router) Borrower(_router) {}
    // Implementation here
}
3

Implement triggerFlashLoan

Map standard parameters to provider-specific request format
4

Implement Provider Callback

Forward the provider’s callback to flashLoanCallBack(callBackData)
5

Deploy and Test

Deploy the adapter and test with the router on testnets
6

Update Documentation

Document the new provider and its adapter address

Best Practices

The callBackData parameter must be passed to the router exactly as received. Never modify, parse, or validate this data in the adapter.
// ✓ Correct
flashLoanCallBack(callBackData);

// ✗ Wrong - modifying data
flashLoanCallBack(abi.encode(parsed));
Each provider has unique requirements:
  • Aave requires arrays and specific return values
  • ERC-3156 requires returning a success constant
  • Some providers may charge fees
Ensure your adapter handles all provider-specific requirements correctly.
Check for success conditions specific to the provider:
bool success = IERC3156FlashLender(lender).flashLoan(...);
require(success, "Flash loan was unsuccessful");
Router and settlement references should be immutable for security:
IFlashLoanRouter public immutable router;
ICowSettlement public immutable settlementContract;

Next Steps

Flash Loans

Learn about flash loan fundamentals

Security Model

Understand security guarantees

Build docs developers (and LLMs) love