Overview
Borrower adapters serve as intermediaries 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:
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
function flashLoanAndCallBack (
address lender ,
IERC20 token ,
uint256 amount ,
bytes calldata callBackData
) external onlyRouter {
triggerFlashLoan (lender, token, amount, callBackData);
}
Parameters:
lender: The flash loan provider contract address
token: The ERC-20 token to borrow
amount: The amount of tokens to request
callBackData: Data to pass back to the router unchanged
Fund Management
function approve (
IERC20 token ,
address target ,
uint256 amount
) external onlySettlementContract {
token. forceApprove (target, amount);
}
Only the settlement contract can set approvals, preventing unauthorized access to borrowed funds.
Implementing an Adapter
To support a new flash loan provider:
Inherit the abstract Borrower contract
Implement triggerFlashLoan for provider-specific requests
Implement the provider’s callback interface
Forward 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
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 ;
}
}
Key Points:
Inherit both Borrower and provider interface
Implement triggerFlashLoan with provider-specific logic
Implement provider’s callback and forward to base
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)
Example: ERC-3156 Adapter
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;
}
}
Key Points:
Simpler than Aave due to standardized interface
Must return specific success constant
Access Control
Router-Only Access
modifier onlyRouter () {
require ( msg.sender == address (router), "Not the router" );
_ ;
}
Only the registered router can trigger flash loans through the adapter.
Settlement-Only Access
modifier onlySettlementContract () {
require (
msg.sender == address (settlementContract),
"Only callable in a settlement"
);
_ ;
}
Only the settlement contract can approve token transfers from the adapter.
Security Assurances:
Only the router can initiate loans
Only settlements can access borrowed funds
External callers cannot disrupt execution flow
Fund Flow
Adding New Providers
Steps to follow:
Research Provider API : Understand the provider’s flash loan request and callback mechanisms
Create Adapter Contract :
contract NewProviderBorrower is Borrower , IProviderCallback {
constructor ( IFlashLoanRouter _router ) Borrower (_router) {}
// Implementation here
}
Implement triggerFlashLoan : Map standard parameters to provider-specific request format
Implement Provider Callback : Forward the provider’s callback to flashLoanCallBack(callBackData)
Deploy and Test : Deploy the adapter and test with the router on testnets
Update Documentation : Document the new provider and its adapter address
Best Practices
Always Pass Data Unchanged
The callBackData parameter must be passed to the router exactly as received:
// Correct
flashLoanCallBack (callBackData);
// Wrong - modifying data
flashLoanCallBack ( abi . encode (parsed));
Handle Provider-Specific Requirements
Each provider has unique requirements:
Aave requires arrays and specific return values
ERC-3156 requires returning a success constant
Some providers may charge fees
Validate Provider Responses
Check for success conditions specific to the provider:
bool success = IERC3156FlashLender (lender). flashLoan (...);
require (success, "Flash loan was unsuccessful" );
Use Immutable References
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