Flash Loans

conforming to EIP-3156

Overview

C.R.E.A.M. is bringing Flash Loans to our money markets.

Flash Loans allow developers access to undercollateralized loans, provided that the borrowed amount (and fee) is returned within one transaction block.

Flash Loans offer a wide range of use cases, including democratized liquidations, arbitrage, collateral swapping and interest rate swapping.

Our Flash Loans feature is very similar to AAVE Flash Loans V1, except C.R.E.A.M. Flash Loans are implemented on crToken.

There are 3 major differences between C.R.E.A.M. Flash Loans and AAVE v1 Flash Loans:

  1. Using C.R.E.A.M. Flash Loans devs interact with flashLoanLender contract , instead of the lending pool.

  2. C.R.E.A.M has deployed two flashLoanLenders that conform to EIP-3156; one for Lending and one for IronBank.

  3. Fee is cheaper. C.R.E.A.M. fee is 0.03%

Only tokens listed below are flash-loanable:

LON IBBTC PAXG PAX EURT BNT WOO FEI SWAP GNO COVER VVSP VSP MLN ARNXM ARMOR SFI RARI OCEAN PERP RAI RUNE FTM UST ALPHA FRAX AMP OGN AKRO PICKLE SUSD SNX WBTC OMG 1INCH ESD HEGIC DAI HUSD CRETH2 HFIL HBTC KP3R AAVE BOND BBTC DPI CEL WNXM SRM UNI FTT SUSHI MTA BUSD RENBTC CRV LINK COMP BAL YFI USDC USDT

flashloanLender

Step by step guide

1. Deploy your Flash Loan contract

Your contract that receives the flash loaned funds must conform to IERC3156FlashBorrowerInterface interface as specified in the standard

// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;
import "./ERC3156FlashLenderInterface.sol";
import "./ERC3156FlashBorrowerInterface.sol";
interface Comptroller {
function isMarketListed(address cTokenAddress) external view returns (bool);
}
interface ERC20 {
function approve(address spender, uint256 amount) external;
}
// FlashloanBorrower is a simple flashloan Borrower implementation for testing
contract FlashloanBorrower is ERC3156FlashBorrowerInterface {
/**
* @notice C.R.E.A.M. comptroller address
*/
address public comptroller;
constructor(address _comptroller) {
comptroller = _comptroller;
}
function doFlashloan(
address flashloanLender,
address borrowToken,
uint256 borrowAmount
) external {
bytes memory data = abi.encode(borrowToken, borrowAmount);
ERC3156FlashLenderInterface(flashloanLender).flashLoan(this, borrowToken, borrowAmount, data);
}
function onFlashLoan(
address initiator,
address token,
uint256 amount,
uint256 fee,
bytes calldata data
) override external returns (bytes32) {
require(Comptroller(comptroller).isMarketListed(msg.sender), "untrusted message sender");
require(initiator == address(this), "FlashBorrower: Untrusted loan initiator");
(address borrowToken, uint256 borrowAmount) = abi.decode(data, (address, uint256));
require(borrowToken == token, "encoded data (borrowToken) does not match");
require(borrowAmount == amount, "encoded data (borrowAmount) does not match");
ERC20(token).approve(msg.sender, amount + fee);
// your logic is written here...
return keccak256("ERC3156FlashBorrowerInterface.onFlashLoan");
}
}

2. Calling flashLoan()

To call flashLoan(ERC3156FlashBorrowerInterface receiver, address token, uint256 amount, bytes calldata data) on flashLoanLender (Lending, IronBank), 4 parameters are required.

  • receiver : The Flash Loan contract address you deployed.

  • token : The token you'd like to borrow

  • amount : Keep in mind that the decimal of amount is dependent on borrowToken's contract implementation.

  • data : encoded parameter for onFlashloan().

    • If no parameters are needed in your Flash Loan contract, use an empty value "".

    If you would like to pass parameters into your flash loan, you will need to encode it.

How to pass your parameters by encoding and decoding

Decoding

Before you pass your parameters, you will need to determine the type and layout of your params and decode them in executeOperation function by using the built-in abi.decode() function.

For example:

(address target) = abi.decode(params, (address));

Encoding

Encoding can be done off-chain by using a package like ethers.js.

const data = ethers.utils.defaultAbiCoder.encode(
["string", "address"],
["hello world!", "0x0000000000000000000000000000000000000000"]
);

Or else, can be done with solidity.

bytes memory data = abi.encode(address(this), 1234);

Like in functiondoFlashloan(address flashloanLender, address borrowToken, uint256 borrowAmount) of the above example.

Playground

To play around with our Flash Loans with mainnet forking environment, please check out this repo: CreamFi/flashloan-playground