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

WIP: Code tweaks #5

Merged
merged 13 commits into from
Aug 1, 2023
2 changes: 1 addition & 1 deletion FlashLender.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { console2 } from "forge-std/console2.sol";
import { StdCheats } from "forge-std/StdCheats.sol";

import { FlashLender } from "../src/FlashLender.sol";
import { FlashBorrower } from "../src/test/FlashBorrower.sol";
import { FlashBorrower } from "./FlashBorrower.sol";
import { ERC20Mock } from "../src/test/ERC20Mock.sol";
import { IERC20 } from "../src/interfaces/IERC20.sol";

Expand Down
6 changes: 1 addition & 5 deletions script/Deploy.s.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.19 <=0.9.0;

import { Foo } from "../src/Foo.sol";

import { BaseScript } from "./Base.s.sol";

/// @dev See the Solidity Scripting tutorial: https://book.getfoundry.sh/tutorials/solidity-scripting
contract Deploy is BaseScript {
function run() public broadcast returns (Foo foo) {
foo = new Foo();
}
function run() public broadcast { }

Check warning on line 8 in script/Deploy.s.sol

View workflow job for this annotation

GitHub Actions / lint

Code contains empty blocks
}
88 changes: 88 additions & 0 deletions src/BaseWrapper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// SPDX-License-Identifier: MIT
// 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 { TransferHelper } from "./utils/TransferHelper.sol";

abstract contract BaseWrapper is IERC3156PPFlashLender {
using TransferHelper for IERC20;

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

bytes internal _callbackResult;

/// @inheritdoc IERC3156PPFlashLender
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should @inheritdoc from the ERC7399 interface when possible, I think.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does that interface lives? I can't see it in the deps

function flashLoan(
address loanReceiver,
IERC20 asset,
uint256 amount,
bytes calldata initiatorData,
function(address, address, IERC20, uint256, uint256, bytes memory) external returns (bytes memory) callback
)
external
returns (bytes memory result)
{
Data memory data = Data({
loanReceiver: loanReceiver,
initiator: msg.sender,
callback: callback,
initiatorData: initiatorData
});

_flashLoan(asset, amount, abi.encode(data));

result = _callbackResult;
// Avoid storage write if not needed
if (result.length > 0) {
delete _callbackResult;
}
return result;
}

/// @dev Call the flashloan function in the child contract
function _flashLoan(IERC20 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 {
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);

_approveRepayment(asset, amount, fee);

if (result.length > 0) {
// if there's any result, it is kept in a storage variable to be retrieved later in this tx
_callbackResult = result;
}
}

/// @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 {
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 {
if (_repayTo() == address(this)) {
asset.approve(msg.sender, amount + fee);
}
}

/// @dev Where should the end client send the funds to repay the loan
/// Override it if the provider can receive the funds directly
function _repayTo() internal view virtual returns (address) {
return address(this);
}
}
8 changes: 0 additions & 8 deletions src/Foo.sol

This file was deleted.

100 changes: 17 additions & 83 deletions src/aave/AaveWrapper.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// 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,27 +8,17 @@
import { IPoolAddressesProvider } from "./interfaces/IPoolAddressesProvider.sol";
import { IFlashLoanSimpleReceiver } from "./interfaces/IFlashLoanSimpleReceiver.sol";

import { FunctionCodec } from "../utils/FunctionCodec.sol";
import { TransferHelper } from "../utils/TransferHelper.sol";

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

import { console2 } from "forge-std/console2.sol";
import { BaseWrapper } from "../BaseWrapper.sol";

contract AaveWrapper is IERC3156PPFlashLender, IFlashLoanSimpleReceiver {
using TransferHelper for IERC20;
using
FunctionCodec
for function(address, address, IERC20, uint256, uint256, bytes memory) external returns (bytes memory);
using FunctionCodec for bytes24;
contract AaveWrapper is BaseWrapper, IFlashLoanSimpleReceiver {
using FixedPointMathLib for uint256;
using ReserveConfiguration for DataTypes.ReserveConfigurationMap;

bytes internal _callbackResult;
IPoolAddressesProvider public immutable ADDRESSES_PROVIDER;

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
IPool public POOL;

Check warning on line 21 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;
Expand All @@ -39,6 +29,13 @@
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));
DataTypes.ReserveConfigurationMap memory configuration = reserve.configuration;
Expand All @@ -50,95 +47,32 @@
else fee = type(uint256).max;
}

/// @dev Use the aggregator to serve an ERC3156++ flash loan.
/// @dev Forward the callback to the callback receiver. The borrower only needs to trust the aggregator and its
/// governance, instead of the underlying lenders.
/// @param loanReceiver The address receiving the flash loan
/// @param asset The asset to be loaned
/// @param amount The amount to loaned
/// @param initiatorData The ABI encoded initiator data
/// @param callback The address and signature of the callback function
/// @return result ABI encoded result of the callback
function flashLoan(
address loanReceiver,
IERC20 asset,
uint256 amount,
bytes calldata initiatorData,
/// @dev callback.
/// This is a concatenation of (address, bytes4), where the address is the callback receiver, and the bytes4 is
/// the signature of callback function.
/// The arguments in the callback function are fixed.
/// If the callback receiver needs to know the loan receiver, it should be encoded by the initiator in `data`.
/// @param initiator The address that called this function
/// @param paymentReceiver The address that needs to receive the amount plus fee at the end of the callback
/// @param asset The asset to be loaned
/// @param amount The amount to loaned
/// @param fee The fee to be paid
/// @param data The ABI encoded data to be passed to the callback
/// @return result ABI encoded result of the callback
function(address, address, IERC20, uint256, uint256, bytes memory) external returns (bytes memory) callback
)
external
returns (bytes memory result)
{
bytes memory data = abi.encode(msg.sender, loanReceiver, callback.encodeFunction(), initiatorData);

function _flashLoan(IERC20 asset, uint256 amount, bytes memory data) internal override {
POOL.flashLoanSimple({
receiverAddress: address(this),
asset: address(asset),
amount: amount,
params: data,
referralCode: 0
});

result = _callbackResult;
// Avoid storage write if not needed
if (result.length > 0) {
delete _callbackResult;
}
return result;
}

/// @inheritdoc IFlashLoanSimpleReceiver
function executeOperation(
address asset,
uint256 amount,
uint256 fee,
address aaveInitiator,
bytes calldata data
address initiator,
bytes calldata params
)
external
override
returns (bool)
{
console2.log("executeOperation");
require(msg.sender == address(POOL), "not pool");
require(aaveInitiator == address(this), "AaveFlashLoanProvider: not initiator");

address initiator;
bytes memory initiatorData;
function(address, address, IERC20, uint256, uint256, bytes memory) external returns (bytes memory) callback;
{
address loanReceiver;
bytes24 encodedCallback;

// decode data
console2.log("abi decoding...");
(initiator, loanReceiver, encodedCallback, initiatorData) =
abi.decode(data, (address, address, bytes24, bytes));
console2.log("callback decoding...");
callback = encodedCallback.decodeFunction();

IERC20(asset).approve(address(POOL), amount + fee);
IERC20(asset).safeTransfer(loanReceiver, amount);
} // release loanReceiver and encodedCallback from the stack

// call the callback and tell the calback receiver to repay the loan to this contract
bytes memory result = callback(initiator, address(this), IERC20(asset), amount, fee, initiatorData);
require(msg.sender == address(POOL), "AaveFlashLoanProvider: not pool");
require(initiator == address(this), "AaveFlashLoanProvider: not initiator");

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

View workflow job for this annotation

GitHub Actions / lint

Error message for require is too long

if (result.length > 0) {
// if there's any result, it is kept in a storage variable to be retrieved later in this tx
_callbackResult = result;
}
bridgeToCallback(IERC20(asset), amount, fee, params);

return true;
}
Expand Down
Loading
Loading