Skip to content

Commit

Permalink
feat: add aave 4626 wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
0xChin committed Aug 4, 2024
1 parent 9c306ac commit 8d41a3e
Show file tree
Hide file tree
Showing 11 changed files with 114 additions and 47 deletions.
2 changes: 1 addition & 1 deletion .solhint.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"rules": {
"compiler-version": ["off"],
"constructor-syntax": "warn",
"quotes": ["error", "single"],
"quotes": ["error", "double"],
"func-visibility": ["warn", { "ignoreConstructors": true }],
"not-rely-on-time": "off",
"no-inline-assembly": "off",
Expand Down
2 changes: 1 addition & 1 deletion .solhint.tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"rules": {
"compiler-version": ["off"],
"constructor-syntax": "warn",
"quotes": ["error", "single"],
"quotes": ["error", "double"],
"func-visibility": ["warn", { "ignoreConstructors": true }],
"not-rely-on-time": "off",
"style-guide-casing": "off",
Expand Down
4 changes: 2 additions & 2 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ line_length = 120
tab_width = 2
bracket_spacing = false
int_types = 'long'
quote_style = 'single'
quote_style = 'double'
number_underscore = 'thousands'
multiline_func_header = 'params_first'
sort_imports = true

[profile.default]
solc_version = '0.8.23'
solc_version = '0.8.26'
libs = ['node_modules', 'lib']
optimizer_runs = 10_000

Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@
"package.json": "sort-package-json"
},
"dependencies": {
"@aave/core-v3": "^1.19.3"
"solmate": "^6.2.0"
},
"devDependencies": {
"@aave/core-v3": "^1.19.3",
"@commitlint/cli": "19.3.0",
"@commitlint/config-conventional": "19.2.2",
"@defi-wonderland/natspec-smells": "1.1.1",
Expand All @@ -47,6 +48,7 @@
"husky": ">=8",
"lint-staged": ">=10",
"solhint-community": "4.0.0",
"sort-package-json": "2.10.0"
"sort-package-json": "2.10.0",
"yield-daddy": "timeless-fi/yield-daddy"
}
}
2 changes: 2 additions & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ forge-std/=node_modules/forge-std/src
halmos-cheatcodes=node_modules/halmos-cheatcodes
openzeppelin-contracts/=node_modules/@openzeppelin/contracts
aave/core-v3/=node_modules/@aave/core-v3/contracts
yield-daddy/=node_modules/yield-daddy/src
solmate/=node_modules/solmate/src

contracts/=src/contracts
interfaces/=src/interfaces
10 changes: 5 additions & 5 deletions script/Deploy.sol
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
pragma solidity 0.8.26;

import {IPool} from '@aave/core-v3/contracts/interfaces/IPool.sol';
import {Grateful} from 'contracts/Grateful.sol';
import {Script} from 'forge-std/Script.sol';
import {IERC20} from 'forge-std/interfaces/IERC20.sol';
import {Grateful} from "contracts/Grateful.sol";
import {Script} from "forge-std/Script.sol";
import {IERC20} from "forge-std/interfaces/IERC20.sol";

Check warning on line 6 in script/Deploy.sol

View workflow job for this annotation

GitHub Actions / Lint Commit Messages

Variable "IERC20" is unused
import {IPool} from "yield-daddy/aave-v3/AaveV3ERC4626.sol";

contract Deploy is Script {
struct DeploymentParams {
Expand Down
42 changes: 26 additions & 16 deletions src/contracts/Grateful.sol
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
pragma solidity 0.8.26;

import {IPool} from '@aave/core-v3/contracts/interfaces/IPool.sol';
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
import {Ownable2Step} from '@openzeppelin/contracts/access/Ownable2Step.sol';
import {IERC20} from 'forge-std/interfaces/IERC20.sol';
import {IGrateful} from 'interfaces/IGrateful.sol';
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {IERC20} from "forge-std/interfaces/IERC20.sol";
import {IGrateful} from "interfaces/IGrateful.sol";
import {AaveV3ERC4626, IPool, IRewardsController} from "yield-daddy/aave-v3/AaveV3ERC4626.sol";

Check warning on line 8 in src/contracts/Grateful.sol

View workflow job for this annotation

GitHub Actions / Lint Commit Messages

Variable "IRewardsController" is unused

contract Grateful is IGrateful, Ownable2Step {
// @inheritdoc IGrateful
IPool public aavePool;

// inheritdoc IGrateful
mapping(address => bool) public tokensWhitelisted;

// @inheritdoc IGrateful
mapping(address => bool) public yieldingFunds;
mapping(address => AaveV3ERC4626) public vaults;
mapping(address => mapping(address => uint256)) public shares;

modifier onlyWhenTokenWhitelisted(address _token) {
if (!tokensWhitelisted[_token]) {
revert Grateful_TokenNotWhitelisted();
}
_;
}

constructor(address[] memory _tokens, IPool _aavePool) Ownable(msg.sender) {
aavePool = _aavePool;
Expand All @@ -26,14 +30,20 @@ contract Grateful is IGrateful, Ownable2Step {
}

// @inheritdoc IGrateful
function pay(address _merchant, address _token, uint256 _amount) external {
if (!tokensWhitelisted[_token]) {
revert Grateful_TokenNotWhitelisted();
}
function addVault(address _token, address _vault) external onlyWhenTokenWhitelisted(_token) onlyOwner {
vaults[_token] = AaveV3ERC4626(_vault);
}

// @inheritdoc IGrateful
function pay(address _merchant, address _token, uint256 _amount) external onlyWhenTokenWhitelisted(_token) {
if (yieldingFunds[_merchant]) {
AaveV3ERC4626 vault = vaults[_token];
if (address(vault) == address(0)) {
revert Grateful_VaultNotSet();
}
IERC20(_token).transferFrom(msg.sender, address(this), _amount);
aavePool.supply(_token, _amount, address(this), 0);
uint256 _shares = vault.deposit(_amount, address(this));
shares[_merchant][_token] += _shares;
} else {
if (!IERC20(_token).transferFrom(msg.sender, _merchant, _amount)) {
revert Grateful_TransferFailed();
Expand Down
48 changes: 41 additions & 7 deletions src/interfaces/IGrateful.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
pragma solidity 0.8.26;

import {IPool} from '@aave/core-v3/contracts/interfaces/IPool.sol';
import {IERC20} from 'forge-std/interfaces/IERC20.sol';
import {IERC20} from "forge-std/interfaces/IERC20.sol";

Check warning on line 4 in src/interfaces/IGrateful.sol

View workflow job for this annotation

GitHub Actions / Lint Commit Messages

Variable "IERC20" is unused
import {AaveV3ERC4626, IPool} from "yield-daddy/aave-v3/AaveV3ERC4626.sol";

/**
* @title Grateful Contract
* @author Chin
* @notice Contract for allowing payments in whitelisted tokens. Payments can be done using Uniswap's Permit2. Merchants can choose to yield their payments in AAVE and withdraw them at any time. Recurring payments are enabled by using Chainlink Keepers
* @title Grateful Contract Interface
* @notice Interface for the Grateful contract that allows payments in whitelisted tokens with optional yield via AAVE.
*/
interface IGrateful {
/*///////////////////////////////////////////////////////////////
Expand All @@ -17,6 +16,7 @@ interface IGrateful {
/*///////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/

/**
* @notice Throws if the token is not whitelisted
*/
Expand All @@ -27,9 +27,20 @@ interface IGrateful {
*/
error Grateful_TransferFailed();

/**
* @notice Throws if the vault for a token is not set
*/
error Grateful_VaultNotSet();

/**
* @notice Throws if the token is not whitelisted when adding a vault
*/
error Grateful_VaultTokenNotWhitelisted();

/*///////////////////////////////////////////////////////////////
VARIABLES
//////////////////////////////////////////////////////////////*/

/**
* @notice Aave pool for yielding merchants funds
* @return _aavePool Aave pool
Expand All @@ -48,16 +59,39 @@ interface IGrateful {
*/
function yieldingFunds(address _merchant) external view returns (bool _isYieldingFunds);

/**
* @notice Returns the vault associated with a token
* @return _vault Address of the vault contract
*/
function vaults(address _token) external view returns (AaveV3ERC4626 _vault);

/**
* @notice Returns the amount of shares for a merchant
* @return _shares Amount of shares
*/
function shares(address _merchant, address _token) external view returns (uint256 _shares);

/*///////////////////////////////////////////////////////////////
LOGIC
//////////////////////////////////////////////////////////////*/

/**
* @notice Makes a payment to merchant
* @notice Makes a payment to a merchant
* @param _merchant Address of the merchant receiving payment
* @param _token Address of the token being used for payment
* @param _amount Amount of the token to be paid
*/
function pay(address _merchant, address _token, uint256 _amount) external;

Check warning on line 84 in src/interfaces/IGrateful.sol

View workflow job for this annotation

GitHub Actions / Lint Commit Messages

Function order is incorrect, external function can not go after external view function (line 72)

/**
* @notice Switch the preference of the merchant to yield funds or not
*/
function switchYieldingFunds() external;

/**
* @notice Adds a vault for a specific token
* @param _token Address of the token for which the vault is being set
* @param _vault Address of the vault contract
*/
function addVault(address _token, address _vault) external;
}
6 changes: 3 additions & 3 deletions test/integration/Grateful.t.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
pragma solidity 0.8.26;

import {console} from 'forge-std/console.sol';
import {IntegrationBase} from 'test/integration/IntegrationBase.sol';
import {console} from "forge-std/console.sol";

Check warning on line 4 in test/integration/Grateful.t.sol

View workflow job for this annotation

GitHub Actions / Lint Commit Messages

Variable "console" is unused
import {IntegrationBase} from "test/integration/IntegrationBase.sol";

contract IntegrationGreeter is IntegrationBase {
function test_Payment() public {
Expand Down
30 changes: 20 additions & 10 deletions test/integration/IntegrationBase.sol
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
pragma solidity 0.8.26;

import {IPool} from '@aave/core-v3/contracts/interfaces/IPool.sol';
import {Grateful, IGrateful} from 'contracts/Grateful.sol';
import {Test} from 'forge-std/Test.sol';
import {IERC20} from 'forge-std/interfaces/IERC20.sol';
import {Grateful, IGrateful} from "contracts/Grateful.sol";
import {Test} from "forge-std/Test.sol";

import {IERC20} from "forge-std/interfaces/IERC20.sol";
import {ERC20} from "solmate/tokens/ERC20.sol";
import {AaveV3ERC4626, IPool, IRewardsController} from "yield-daddy/aave-v3/AaveV3ERC4626.sol";

contract IntegrationBase is Test {
uint256 internal constant _FORK_BLOCK = 18_920_905;

string internal _initialGreeting = 'hola';
address internal _user = makeAddr('user');
address internal _merchant = makeAddr('merchant');
address internal _owner = makeAddr('owner');
string internal _initialGreeting = "hola";
address internal _user = makeAddr("user");
address internal _merchant = makeAddr("merchant");
address internal _owner = makeAddr("owner");
address internal _daiWhale = 0xbf702ea18BB1AB2A710394993a576eC61476cCf3;
address[] internal _tokens;
IERC20 internal _dai = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);
address _aDai = 0x018008bfb33d285247A21d44E50697654f754e63;

Check warning on line 21 in test/integration/IntegrationBase.sol

View workflow job for this annotation

GitHub Actions / Lint Commit Messages

Explicitly mark visibility of state
IPool internal _aavePool = IPool(0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2);
IGrateful internal _grateful;
AaveV3ERC4626 internal _vault = new AaveV3ERC4626(
ERC20(address(_dai)),
ERC20(_aDai),
_aavePool,
address(0),
IRewardsController(address(_dai)) // TODO: put real rewards controller
);

function setUp() public {
vm.createSelectFork(vm.rpcUrl('mainnet'), _FORK_BLOCK);
vm.createSelectFork(vm.rpcUrl("mainnet"), _FORK_BLOCK);
vm.prank(_owner);
_tokens = new address[](1);
_tokens[0] = address(_dai);
Expand Down
9 changes: 9 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1711,6 +1711,11 @@ [email protected]:
optionalDependencies:
prettier "^2.8.3"

solmate@^6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/solmate/-/solmate-6.2.0.tgz#edd29b5f3d6faafafdcf65fe4d1d959b4841cfa8"
integrity sha512-AM38ioQ2P8zRsA42zenb9or6OybRjOLXIu3lhIT8rhddUuduCt76pUEuLxOIg9GByGojGz+EbpFdCB6B+QZVVA==

sort-object-keys@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/sort-object-keys/-/sort-object-keys-1.1.3.tgz#bff833fe85cab147b34742e45863453c1e190b45"
Expand Down Expand Up @@ -1990,6 +1995,10 @@ [email protected], yargs@^17.0.0:
y18n "^5.0.5"
yargs-parser "^21.1.1"

yield-daddy@timeless-fi/yield-daddy:
version "0.0.0"
resolved "https://codeload.github.com/timeless-fi/yield-daddy/tar.gz/88753dfb04a0951df679c4199a865fe29646d285"

yocto-queue@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251"
Expand Down

0 comments on commit 8d41a3e

Please sign in to comment.