Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use IERC7399 #10

Merged
merged 2 commits into from
Aug 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
[submodule "lib/erc3156"]
path = lib/erc3156
url = https://github.com/alcueca/erc3156
[submodule "lib/erc3156pp"]
path = lib/erc3156pp
url = https://github.com/alcueca/erc3156pp
[submodule "lib/erc7399"]
path = lib/erc7399
url = https://github.com/alcueca/erc7399
1 change: 0 additions & 1 deletion lib/erc3156pp
Submodule erc3156pp deleted from 56ba9d
1 change: 1 addition & 0 deletions lib/erc7399
Submodule erc7399 added at 69a194
2 changes: 2 additions & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
@prb/test/=lib/prb-test/src/
forge-std/=lib/forge-std/src/
erc7399/=lib/erc7399/src/
solmate/=lib/solmate/src/
31 changes: 15 additions & 16 deletions src/BaseWrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,29 @@
// Thanks to ultrasecr.eth
pragma solidity ^0.8.0;

import { IERC3156PPFlashLender } from "lib/erc3156pp/src/interfaces/IERC3156PPFlashLender.sol";
import { IERC20 } from "lib/erc3156pp/src/interfaces/IERC20.sol";
import "erc7399/IERC7399.sol";

Check warning on line 5 in src/BaseWrapper.sol

View workflow job for this annotation

GitHub Actions / lint

global import of path erc7399/IERC7399.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)

import { TransferHelper } from "./utils/TransferHelper.sol";
import { TransferHelper, ERC20 } from "./utils/TransferHelper.sol";

abstract contract BaseWrapper is IERC3156PPFlashLender {
using TransferHelper for IERC20;
abstract contract BaseWrapper is IERC7399 {
using TransferHelper for address;

struct Data {
address loanReceiver;
address initiator;
function(address, address, IERC20, uint256, uint256, bytes memory) external returns (bytes memory) callback;
function(address, address, address, uint256, uint256, bytes memory) external returns (bytes memory) callback;
bytes initiatorData;
}

bytes internal _callbackResult;

/// @inheritdoc IERC3156PPFlashLender
function flashLoan(
/// @inheritdoc IERC7399
function flash(
address loanReceiver,
IERC20 asset,
address asset,
uint256 amount,
bytes calldata initiatorData,
function(address, address, IERC20, uint256, uint256, bytes memory) external returns (bytes memory) callback
function(address, address, address, uint256, uint256, bytes memory) external returns (bytes memory) callback
)
external
returns (bytes memory result)
Expand All @@ -48,15 +47,15 @@
}

/// @dev Call the flashloan function in the child contract
function _flashLoan(IERC20 asset, uint256 amount, bytes memory params) internal virtual;
function _flashLoan(address asset, uint256 amount, bytes memory params) internal virtual;

/// @dev Handle the common parts of bridging the callback
function bridgeToCallback(IERC20 asset, uint256 amount, uint256 fee, bytes memory params) internal {
function bridgeToCallback(address asset, uint256 amount, uint256 fee, bytes memory params) internal {
Data memory data = abi.decode(params, (Data));
_transferAssets(asset, amount, data.loanReceiver);

// call the callback and tell the callback receiver to repay the loan to this contract
bytes memory result = data.callback(data.initiator, _repayTo(), IERC20(asset), amount, fee, data.initiatorData);
bytes memory result = data.callback(data.initiator, _repayTo(), address(asset), amount, fee, data.initiatorData);

_approveRepayment(asset, amount, fee);

Expand All @@ -68,15 +67,15 @@

/// @dev Transfer the assets to the loan receiver.
/// Override it if the provider can send the funds directly
function _transferAssets(IERC20 asset, uint256 amount, address loanReceiver) internal virtual {
function _transferAssets(address asset, uint256 amount, address loanReceiver) internal virtual {
asset.safeTransfer(loanReceiver, amount);
}

/// @dev Approve the repayment of the loan to the provider if needed.
/// Override it if the provider can receive the funds directly and you want to avoid the if condition
function _approveRepayment(IERC20 asset, uint256 amount, uint256 fee) internal virtual {
function _approveRepayment(address asset, uint256 amount, uint256 fee) internal virtual {
if (_repayTo() == address(this)) {
asset.approve(msg.sender, amount + fee);
ERC20(asset).approve(msg.sender, amount + fee);
}
}

Expand Down
40 changes: 16 additions & 24 deletions src/aave/AaveWrapper.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
// Thanks to ultrasecr.eth
pragma solidity ^0.8.0;

Check failure on line 3 in src/aave/AaveWrapper.sol

View workflow job for this annotation

GitHub Actions / lint

Compiler version ^0.8.0 does not satisfy the >=0.8.19 semver requirement

import { IPool } from "./interfaces/IPool.sol";
import { DataTypes } from "./interfaces/DataTypes.sol";
Expand All @@ -8,49 +8,41 @@
import { IPoolAddressesProvider } from "./interfaces/IPoolAddressesProvider.sol";
import { IFlashLoanSimpleReceiver } from "./interfaces/IFlashLoanSimpleReceiver.sol";

import { IERC20 } from "lib/erc3156pp/src/interfaces/IERC20.sol";
import { FixedPointMathLib } from "lib/solmate/src/utils/FixedPointMathLib.sol";

import { BaseWrapper } from "../BaseWrapper.sol";
import { BaseWrapper, IERC7399, ERC20 } from "../BaseWrapper.sol";

contract AaveWrapper is BaseWrapper, IFlashLoanSimpleReceiver {
using FixedPointMathLib for uint256;
using ReserveConfiguration for DataTypes.ReserveConfigurationMap;

IPoolAddressesProvider public immutable ADDRESSES_PROVIDER;

Check warning on line 19 in src/aave/AaveWrapper.sol

View workflow job for this annotation

GitHub Actions / lint

Variable name must be in mixedCase
IPool public POOL;
IPool public immutable POOL;

Check warning on line 20 in src/aave/AaveWrapper.sol

View workflow job for this annotation

GitHub Actions / lint

Variable name must be in mixedCase

constructor(IPoolAddressesProvider provider) {
ADDRESSES_PROVIDER = provider;
POOL = IPool(provider.getPool());
}

function updatePool() external {
POOL = IPool(ADDRESSES_PROVIDER.getPool());
}

/**
* @dev From ERC-3156. The fee to be charged for a given loan.
* @param asset The loan currency.
* @param amount The amount of assets lent.
* @return fee The amount of `asset` to be charged for the loan, on top of the returned principal.
* type(uint256).max if the loan is not possible.
*/
function flashFee(IERC20 asset, uint256 amount) external view returns (uint256 fee) {
DataTypes.ReserveData memory reserve = POOL.getReserveData(address(asset));
/// @inheritdoc IERC7399
function maxFlashLoan(address asset) external view returns (uint256 max) {
DataTypes.ReserveData memory reserve = POOL.getReserveData(asset);
DataTypes.ReserveConfigurationMap memory configuration = reserve.configuration;

if (
!configuration.getPaused() && configuration.getActive() && configuration.getFlashLoanEnabled()
&& amount < asset.balanceOf(reserve.aTokenAddress)
) fee = amount.mulWadUp(POOL.FLASHLOAN_PREMIUM_TOTAL() * 0.0001e18);
else fee = type(uint256).max;
max = !configuration.getPaused() && configuration.getActive() && configuration.getFlashLoanEnabled()
? ERC20(asset).balanceOf(reserve.aTokenAddress)
: 0;
}

/// @inheritdoc IERC7399
function flashFee(address, uint256 amount) external view returns (uint256) {
return amount.mulWadUp(POOL.FLASHLOAN_PREMIUM_TOTAL() * 0.0001e18);
}

function _flashLoan(IERC20 asset, uint256 amount, bytes memory data) internal override {
function _flashLoan(address asset, uint256 amount, bytes memory data) internal override {
POOL.flashLoanSimple({
receiverAddress: address(this),
asset: address(asset),
asset: asset,
amount: amount,
params: data,
referralCode: 0
Expand All @@ -70,9 +62,9 @@
returns (bool)
{
require(msg.sender == address(POOL), "AaveFlashLoanProvider: not pool");
require(initiator == address(this), "AaveFlashLoanProvider: not initiator");

Check warning on line 65 in src/aave/AaveWrapper.sol

View workflow job for this annotation

GitHub Actions / lint

Error message for require is too long

bridgeToCallback(IERC20(asset), amount, fee, params);
bridgeToCallback(asset, amount, fee, params);

return true;
}
Expand Down
27 changes: 12 additions & 15 deletions src/balancer/BalancerWrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
import { Arrays } from "../utils/Arrays.sol";

import { FixedPointMathLib } from "lib/solmate/src/utils/FixedPointMathLib.sol";
import { IERC20 } from "lib/erc3156pp/src/interfaces/IERC20.sol";

import { BaseWrapper } from "../BaseWrapper.sol";
import { BaseWrapper, IERC7399, ERC20 } from "../BaseWrapper.sol";

contract BalancerWrapper is BaseWrapper, IFlashLoanRecipient {
using Arrays for uint256;
Expand All @@ -25,21 +24,19 @@
balancer = _balancer;
}

/**
* @dev From ERC-3156. The fee to be charged for a given loan.
* @param asset The loan currency.
* @param amount The amount of assets lent.
* @return fee The amount of `asset` to be charged for the loan, on top of the returned principal.
* type(uint256).max if the loan is not possible.
*/
function flashFee(IERC20 asset, uint256 amount) external view returns (uint256 fee) {
if (amount >= asset.balanceOf(address(balancer))) fee = type(uint256).max;
else fee = amount.mulWadUp(balancer.getProtocolFeesCollector().getFlashLoanFeePercentage());
/// @inheritdoc IERC7399
function maxFlashLoan(address asset) external view returns (uint256) {
return ERC20(asset).balanceOf(address(balancer));
}

function _flashLoan(IERC20 asset, uint256 amount, bytes memory data) internal override {
/// @inheritdoc IERC7399
function flashFee(address, uint256 amount) external view returns (uint256) {
return amount.mulWadUp(balancer.getProtocolFeesCollector().getFlashLoanFeePercentage());
}

function _flashLoan(address asset, uint256 amount, bytes memory data) internal override {
flashLoanDataHash = keccak256(data);
balancer.flashLoan(this, address(asset).toArray(), amount.toArray(), data);
balancer.flashLoan(this, asset.toArray(), amount.toArray(), data);
}

/// @inheritdoc IFlashLoanRecipient
Expand All @@ -53,10 +50,10 @@
override
{
require(msg.sender == address(balancer), "BalancerWrapper: not balancer");
require(keccak256(params) == flashLoanDataHash, "BalancerWrapper: params hash mismatch");

Check warning on line 53 in src/balancer/BalancerWrapper.sol

View workflow job for this annotation

GitHub Actions / lint

Error message for require is too long
delete flashLoanDataHash;

bridgeToCallback(IERC20(assets[0]), amounts[0], fees[0], params);
bridgeToCallback(assets[0], amounts[0], fees[0], params);
}

function _repayTo() internal view override returns (address) {
Expand Down
34 changes: 16 additions & 18 deletions src/erc3156/ERC3156Wrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
import { IERC3156FlashLender } from "lib/erc3156/contracts/interfaces/IERC3156FlashLender.sol";
import { IERC3156FlashBorrower } from "lib/erc3156/contracts/interfaces/IERC3156FlashBorrower.sol";

import { IERC20 } from "lib/erc3156pp/src/interfaces/IERC20.sol";

import { BaseWrapper } from "../BaseWrapper.sol";
import { BaseWrapper, IERC7399 } from "../BaseWrapper.sol";

/**
* @author Alberto Cuesta Cañada
Expand All @@ -15,34 +13,34 @@
contract ERC3156Wrapper is BaseWrapper, IERC3156FlashBorrower {
bytes32 public constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan");

mapping(IERC20 => IERC3156FlashLender) public lenders;
mapping(address => IERC3156FlashLender) public lenders;

Check warning on line 16 in src/erc3156/ERC3156Wrapper.sol

View workflow job for this annotation

GitHub Actions / lint

Main key parameter in mapping lenders is not named

Check warning on line 16 in src/erc3156/ERC3156Wrapper.sol

View workflow job for this annotation

GitHub Actions / lint

Value parameter in mapping lenders is not named

/**
* @param assets_ Asset contracts supported for flash lending.
* @param lenders_ The flash lenders that will be used for each asset.
*/
constructor(IERC20[] memory assets_, IERC3156FlashLender[] memory lenders_) {
constructor(address[] memory assets_, IERC3156FlashLender[] memory lenders_) {
require(assets_.length == lenders_.length, "Arrays must be the same length");
for (uint256 i = 0; i < assets_.length; i++) {
lenders[assets_[i]] = IERC3156FlashLender(address(lenders_[i]));
}
}

/**
* @dev From ERC-3156. The fee to be charged for a given loan.
* @param asset The loan currency.
* @param amount The amount of assets lent.
* @return The amount of `asset` to be charged for the loan, on top of the returned principal.
* type(uint256).max if the loan is not possible.
*/
function flashFee(IERC20 asset, uint256 amount) external view returns (uint256) {
/// @inheritdoc IERC7399
function maxFlashLoan(address asset) external view returns (uint256) {
IERC3156FlashLender lender = lenders[asset];
require(address(lender) != address(0), "Unsupported currency");
return lender.maxFlashLoan(asset);
}

/// @inheritdoc IERC7399
function flashFee(address asset, uint256 amount) external view returns (uint256) {
IERC3156FlashLender lender = lenders[asset];
require(address(lender) != address(0), "Unsupported currency");
if (lender.maxFlashLoan(address(asset)) < amount) return type(uint256).max;
else return lender.flashFee(address(asset), amount);
return lender.flashFee(asset, amount);
}

function _flashLoan(IERC20 asset, uint256 amount, bytes memory data) internal override {
function _flashLoan(address asset, uint256 amount, bytes memory data) internal override {
IERC3156FlashLender lender = lenders[asset];
require(address(lender) != address(0), "Unsupported currency");

Expand All @@ -62,9 +60,9 @@
returns (bytes32)
{
require(erc3156initiator == address(this), "External loan initiator");
require(msg.sender == address(lenders[IERC20(asset)]), "Unknown lender");
require(msg.sender == address(lenders[asset]), "Unknown lender");

bridgeToCallback(IERC20(asset), amount, fee, params);
bridgeToCallback(asset, amount, fee, params);

return CALLBACK_SUCCESS;
}
Expand Down
Loading
Loading