Flash Loan Router implements a modular architecture that separates concerns between routing logic, lender-specific adapters, and settlement execution. This design enables support for multiple flash loan providers while maintaining a secure and predictable execution flow.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.
Architecture overview
The system consists of four main components:Flash Loan Router
Core contract that orchestrates flash loans and settlement execution
Borrower adapters
Lender-specific contracts that handle protocol differences
Flash loan lenders
External protocols (Aave, Maker) that provide liquidity
CoW Settlement
Target settlement contract where trades are executed
Component interactions
The following diagram illustrates the call flow for a settlement using two flash loans (Aave and Maker):The call context never changes throughout execution. Each call increases the call depth, and all calls unwind after settlement completes. This ensures atomic execution.
Core contracts
FlashLoanRouter
The router is the entry point and orchestrator for all flash loan settlements. It manages the execution flow and ensures security properties are maintained. Key responsibilities:- Authenticate solver calls through CoW Protocol’s authentication contract
- Process flash loan requests sequentially in the specified order
- Coordinate with borrower adapters to obtain loans
- Execute the CoW Protocol settlement once all loans are received
- Maintain execution state to prevent reentrancy and unauthorized calls
FlashLoanRouter.sol
Solver control
Solver control
The solver has complete control over settlement data. The router guarantees that the exact settlement data provided in
flashLoanAndSettle() is used, even if tokens, lenders, or borrowers are malicious.Single settlement guarantee
Single settlement guarantee
Each call to
flashLoanAndSettle() executes exactly one call to settle(). Multiple settlements cannot be triggered, and settlement data cannot be modified during execution.Sequential loan processing
Sequential loan processing
Flash loans are requested in the order specified. Out-of-order execution is prevented through state validation and will cause transaction revert.
Atomic execution
Atomic execution
All operations succeed or fail together. If any lender cannot provide funds or the settlement fails, the entire transaction reverts.
Borrower adapters
Borrowers are adapter contracts that bridge the gap between the router’s generic interface and lender-specific protocols. Each flash loan provider requires its own borrower implementation. Abstract Borrower base: The repository provides an abstractBorrower contract that implements common functionality:
Borrower.sol
- AaveBorrower
- ERC3156Borrower
Implements Aave Protocol flash loan interface.
AaveBorrower.sol
- Settlement calls
borrower.approve(token, spender, amount) - Spender (e.g., settlement contract) calls
token.transferFrom(borrower, destination, amount)
For safe operations like approving the settlement contract, set unlimited approval once and reuse it across settlements to save gas.
Loan data structure
Each flash loan request is represented by theLoan.Data struct:
Loan.sol
Execution flow
The complete execution sequence involves multiple phases:Phase 1: Solver initiates settlement
- Verify caller is a registered solver
- Emit
Settlement(solver)event - Store loan and settlement data
- Begin loan acquisition process
Phase 2: Sequential loan acquisition
For each loan in the array:- Router calls
borrower.flashLoanAndCallBack(lender, token, amount, data) - Borrower calls lender-specific flash loan function
- Lender transfers assets to borrower
- Lender calls borrower’s callback function
- Borrower calls
router.borrowerCallBack(data) - Router validates callback came from expected borrower
- Router proceeds to next loan or settlement
Phase 3: Settlement execution
Once all loans are obtained:- Router sets internal state to
SETTLINGto prevent reentrancy - Router calls
settlementContract.settle(settlementData) - Settlement transfers borrowed funds where needed
- Settlement executes trades and user interactions
- Settlement repays borrowers and sets lender approvals
- Lenders pull repayment from borrowers
Phase 4: Loan repayment
Repayment mechanism depends on the lender: Aave repayment:- Settlement transfers borrowed amount + fee to borrower
- Settlement calls
borrower.approve(token, aavePool, repaymentAmount) - Aave pool calls
token.transferFrom(borrower, pool, repaymentAmount)
- Settlement transfers borrowed amount + fee to borrower
- Settlement calls
borrower.approve(token, lender, repaymentAmount) - Lender calls
token.transferFrom(borrower, lender, repaymentAmount)
If repayment is insufficient or fails, the lender reverts the transaction, ensuring the solver never incurs unpayable debt.
Security model
Router security guarantees
The router maintains critical security properties:Authentication
Only registered CoW Protocol solvers can call
flashLoanAndSettle(). The router cannot call itself.Single settlement
Exactly one call to
settle() executes per flashLoanAndSettle() call, using the exact data provided.Ordered execution
Flash loans are processed sequentially in the specified order. Out-of-order callbacks cause revert.
- Settlement data cannot be modified
- Additional settlements cannot be triggered
- Execution order cannot be changed
- Cause transaction revert
- Modify chain state before settlement
- Exploit slippage tolerance
Borrower security
Borrowers have no special privileges:- Cannot call CoW Protocol functions directly
- Cannot access router functions outside of callback flow
- Only respond to authenticated router requests
- Impair borrower’s adapter functionality
- Modify expected borrower behavior
- Extract funds (only approvals can authorize transfers)
Supported lenders
The architecture currently supports:Aave
Aave V3 flash loans through
AaveBorrowerAave Flash Loan DocsERC-3156 Compatible
Any lender implementing ERC-3156 standard through
ERC3156BorrowerExamples: Maker, custom implementationsAdding new lenders
To support additional flash loan providers:- Create new borrower contract inheriting from
Borrower - Implement
triggerFlashLoan()for lender-specific request format - Implement lender’s callback interface
- Call
flashLoanCallBack()from the lender’s callback - Deploy borrower contract with router address
- Solvers can now use the new lender
New borrower implementations require no changes to the router or existing borrowers. The architecture is fully modular and extensible.
Gas optimization
The architecture includes several gas optimizations: Transient storage: Using transient storage for execution state variables (pendingBorrower, pendingDataHash) saves gas by not persisting state after transaction completion.
Efficient encoding:
Loan data is tightly packed in memory to minimize allocation and copying costs.
Reusable approvals:
Set unlimited approval once for safe spenders (settlement contract) instead of approving each transaction.
Benchmark results:
Gas costs for different flash loan providers are tracked in the repository’s snapshots/ directory.
Design principles
The architecture follows these key principles:Modularity
Modularity
Router and borrowers are independent. New lenders can be added without modifying core contracts.
Security by design
Security by design
Multiple layers of validation ensure solvers maintain control and execution follows expected flow.
Deterministic deployment
Deterministic deployment
CREATE2 deployment ensures same addresses across networks, simplifying integration and verification.
Gas efficiency
Gas efficiency
Transient storage, tight encoding, and reusable approvals minimize gas costs.
Composability
Composability
Multiple loans from different providers can be combined in a single settlement.