Skip to content

Commit

Permalink
Merge pull request #28 from pooltogether/pool-2332-fix-dust-problem-i…
Browse files Browse the repository at this point in the history
…n-aave-v2-yield-source

fix(contract): fix supplyTokenTo and redeemToken amounts
  • Loading branch information
PierrickGT authored Jun 30, 2022
2 parents a452362 + 3918484 commit 8928715
Show file tree
Hide file tree
Showing 8 changed files with 376 additions and 248 deletions.
23 changes: 23 additions & 0 deletions contracts/test/ATokenMintable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

import "./ERC20Mintable.sol";

contract ATokenMintable is ERC20Mintable {
address public immutable underlyingAssetAddress;

constructor(
address _underlyingAssetAddress,
string memory _name,
string memory _symbol,
uint8 decimals_
) ERC20Mintable(_name, _symbol, decimals_) {
underlyingAssetAddress = _underlyingAssetAddress;
}

/* solhint-disable func-name-mixedcase */
function UNDERLYING_ASSET_ADDRESS() external view returns (address) {
return underlyingAssetAddress;
}
}
10 changes: 5 additions & 5 deletions contracts/test/ATokenYieldSourceHarness.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ contract ATokenYieldSourceHarness is ATokenYieldSource {

}

function approveLendingPool(uint256 amount) external {
IERC20(aToken.UNDERLYING_ASSET_ADDRESS()).approve(address(_lendingPool()), amount);
}

function mint(address account, uint256 amount) public returns (bool) {
_mint(account, amount);
return true;
Expand All @@ -35,11 +39,7 @@ contract ATokenYieldSourceHarness is ATokenYieldSource {
}

function tokenAddress() external view returns (address) {
return _tokenAddress();
}

function lendingPoolProvider() external view returns (ILendingPoolAddressesProvider) {
return _lendingPoolProvider();
return aToken.UNDERLYING_ASSET_ADDRESS();
}

function lendingPool() external view returns (ILendingPool) {
Expand Down
41 changes: 41 additions & 0 deletions contracts/test/AaveLendingPool.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import { ATokenMintable } from "./ATokenMintable.sol";

contract AaveLendingPool {
IERC20 public immutable token;
ATokenMintable public immutable aToken;

constructor(
IERC20 _token,
ATokenMintable _aToken
) {
token = _token;
aToken = _aToken;
}

/* solhint-disable no-unused-vars */
function deposit(
address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode
) external {
IERC20(asset).transferFrom(msg.sender, address(this), amount);
aToken.mint(onBehalfOf, amount);
}

function withdraw(
address asset,
uint256 amount,
address to
) external returns (uint256) {
aToken.burn(msg.sender, amount);
IERC20(asset).transfer(to, amount);
return amount;
}
}
13 changes: 0 additions & 13 deletions contracts/test/ERC20Mintable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,6 @@ pragma solidity 0.8.6;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

/**
* @dev Extension of {ERC20} that adds a set of accounts with the {MinterRole},
* which have permission to mint (create) new tokens as they see fit.
*
* At construction, the deployer of the contract is the only minter.
*/
contract ERC20Mintable is ERC20 {
uint8 internal __decimals;

Expand All @@ -21,13 +15,6 @@ contract ERC20Mintable is ERC20 {
return __decimals;
}

/**
* @dev See {ERC20-_mint}.
*
* Requirements:
*
* - the caller must have the {MinterRole}.
*/
function mint(address account, uint256 amount) public returns (bool) {
_mint(account, amount);
return true;
Expand Down
92 changes: 48 additions & 44 deletions contracts/yield-source/ATokenYieldSource.sol
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,19 @@ contract ATokenYieldSource is ERC20, IProtocolYieldSource, Manageable, Reentranc
);

/// @notice Interface for the yield-bearing Aave aToken
ATokenInterface public aToken;
ATokenInterface public immutable aToken;

/// @notice Interface for Aave incentivesController
IAaveIncentivesController public incentivesController;
IAaveIncentivesController public immutable incentivesController;

/// @notice Interface for Aave lendingPoolAddressesProviderRegistry
ILendingPoolAddressesProviderRegistry public lendingPoolAddressesProviderRegistry;

uint8 internal __decimals;
/// @notice Underlying asset token address.
address private immutable _tokenAddress;

/// @notice ERC20 token decimals.
uint8 private immutable __decimals;

/// @dev Aave genesis market LendingPoolAddressesProvider's ID
/// @dev This variable could evolve in the future if we decide to support other markets
Expand All @@ -107,21 +111,21 @@ contract ATokenYieldSource is ERC20, IProtocolYieldSource, Manageable, Reentranc
) Ownable(_owner) ERC20(_name, _symbol) ReentrancyGuard()
{
require(address(_aToken) != address(0), "ATokenYieldSource/aToken-not-zero-address");
aToken = _aToken;

require(address(_incentivesController) != address(0), "ATokenYieldSource/incentivesController-not-zero-address");
incentivesController = _incentivesController;

require(address(_lendingPoolAddressesProviderRegistry) != address(0), "ATokenYieldSource/lendingPoolRegistry-not-zero-address");
lendingPoolAddressesProviderRegistry = _lendingPoolAddressesProviderRegistry;

require(_owner != address(0), "ATokenYieldSource/owner-not-zero-address");

require(_decimals > 0, "ATokenYieldSource/decimals-gt-zero");

aToken = _aToken;
incentivesController = _incentivesController;
lendingPoolAddressesProviderRegistry = _lendingPoolAddressesProviderRegistry;
__decimals = _decimals;

// approve once for max amount
IERC20(_tokenAddress()).safeApprove(address(_lendingPool()), type(uint256).max);
address tokenAddress = address(_aToken.UNDERLYING_ASSET_ADDRESS());
_tokenAddress = tokenAddress;

// Approve once for max amount
IERC20(tokenAddress).safeApprove(address(_lendingPool()), type(uint256).max);

emit ATokenYieldSourceInitialized (
_aToken,
Expand All @@ -144,29 +148,26 @@ contract ATokenYieldSource is ERC20, IProtocolYieldSource, Manageable, Reentranc
/// @return true if operation is successful
function approveMaxAmount() external onlyOwner returns (bool) {
address _lendingPoolAddress = address(_lendingPool());
IERC20 _underlyingAsset = IERC20(_tokenAddress());
uint256 _allowance = _underlyingAsset.allowance(address(this), _lendingPoolAddress);
IERC20 _underlyingAsset = IERC20(_tokenAddress);

_underlyingAsset.safeIncreaseAllowance(
_lendingPoolAddress,
type(uint256).max.sub(_underlyingAsset.allowance(address(this), _lendingPoolAddress))
);

_underlyingAsset.safeIncreaseAllowance(_lendingPoolAddress, type(uint256).max.sub(_allowance));
return true;
}

/// @notice Returns the ERC20 asset token used for deposits
/// @return The ERC20 asset token address
function depositToken() public view override returns (address) {
return _tokenAddress();
}

/// @notice Returns the underlying asset token address
/// @return Underlying asset token address
function _tokenAddress() internal view returns (address) {
return aToken.UNDERLYING_ASSET_ADDRESS();
return _tokenAddress;
}

/// @notice Returns user total balance (in asset tokens). This includes the deposits and interest.
/// @param addr User address
/// @return The underlying balance of asset tokens
function balanceOfToken(address addr) external override returns (uint256) {
function balanceOfToken(address addr) external override view returns (uint256) {
return _sharesToToken(balanceOf(addr));
}

Expand Down Expand Up @@ -206,15 +207,17 @@ contract ATokenYieldSource is ERC20, IProtocolYieldSource, Manageable, Reentranc
return _tokens;
}

/// @notice Checks that the amount of shares is greater than zero.
/// @param _shares Amount of shares to check
function _requireSharesGTZero(uint256 _shares) internal pure {
require(_shares > 0, "ATokenYieldSource/shares-gt-zero");
}

/// @notice Deposit asset tokens to Aave
/// @param mintAmount The amount of asset tokens to be deposited
function _depositToAave(uint256 mintAmount) internal {
address _underlyingAssetAddress = _tokenAddress();
ILendingPool __lendingPool = _lendingPool();
IERC20 _depositToken = IERC20(_underlyingAssetAddress);

_depositToken.safeTransferFrom(msg.sender, address(this), mintAmount);
__lendingPool.deposit(_underlyingAssetAddress, mintAmount, address(this), REFERRAL_CODE);
IERC20(_tokenAddress).safeTransferFrom(msg.sender, address(this), mintAmount);
_lendingPool().deposit(_tokenAddress, mintAmount, address(this), REFERRAL_CODE);
}

/// @notice Supplies asset tokens to the yield source
Expand All @@ -224,12 +227,13 @@ contract ATokenYieldSource is ERC20, IProtocolYieldSource, Manageable, Reentranc
/// @param to The user whose balance will receive the tokens
function supplyTokenTo(uint256 mintAmount, address to) external override nonReentrant {
uint256 shares = _tokenToShares(mintAmount);
_requireSharesGTZero(shares);

require(shares > 0, "ATokenYieldSource/shares-gt-zero");
_depositToAave(mintAmount);
uint256 tokenAmount = _sharesToToken(shares);
_depositToAave(tokenAmount);
_mint(to, shares);

emit SuppliedTokenTo(msg.sender, shares, mintAmount, to);
emit SuppliedTokenTo(msg.sender, shares, tokenAmount, to);
}

/// @notice Redeems asset tokens from the yield source
Expand All @@ -238,20 +242,22 @@ contract ATokenYieldSource is ERC20, IProtocolYieldSource, Manageable, Reentranc
/// @param redeemAmount The amount of asset tokens to be redeemed
/// @return The actual amount of asset tokens that were redeemed
function redeemToken(uint256 redeemAmount) external override nonReentrant returns (uint256) {
address _underlyingAssetAddress = _tokenAddress();
IERC20 _depositToken = IERC20(_underlyingAssetAddress);

uint256 shares = _tokenToShares(redeemAmount);
_requireSharesGTZero(shares);

uint256 tokenAmount = _sharesToToken(shares);

_burn(msg.sender, shares);

IERC20 _depositToken = IERC20(_tokenAddress);
uint256 beforeBalance = _depositToken.balanceOf(address(this));
_lendingPool().withdraw(_underlyingAssetAddress, redeemAmount, address(this));
_lendingPool().withdraw(_tokenAddress, tokenAmount, address(this));
uint256 afterBalance = _depositToken.balanceOf(address(this));

uint256 balanceDiff = afterBalance.sub(beforeBalance);
_depositToken.safeTransfer(msg.sender, balanceDiff);

emit RedeemedToken(msg.sender, shares, redeemAmount);
emit RedeemedToken(msg.sender, shares, tokenAmount);
return balanceDiff;
}

Expand Down Expand Up @@ -292,15 +298,13 @@ contract ATokenYieldSource is ERC20, IProtocolYieldSource, Manageable, Reentranc
return true;
}

/// @notice Retrieves Aave LendingPoolAddressesProvider address
/// @return A reference to LendingPoolAddressesProvider interface
function _lendingPoolProvider() internal view returns (ILendingPoolAddressesProvider) {
return ILendingPoolAddressesProvider(lendingPoolAddressesProviderRegistry.getAddressesProvidersList()[ADDRESSES_PROVIDER_ID]);
}

/// @notice Retrieves Aave LendingPool address
/// @return A reference to LendingPool interface
function _lendingPool() internal view returns (ILendingPool) {
return ILendingPool(_lendingPoolProvider().getLendingPool());
return ILendingPool(
ILendingPoolAddressesProvider(
lendingPoolAddressesProviderRegistry.getAddressesProvidersList()[ADDRESSES_PROVIDER_ID]
).getLendingPool()
);
}
}
8 changes: 4 additions & 4 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@ const config: HardhatUserConfig = {
runs: 200,
},
evmVersion: 'istanbul',
}
}
]
},
},
],
},
typechain: {
outDir: 'types',
target: 'ethers-v5',
}
},
};

forkTasks;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pooltogether/aave-yield-source",
"version": "1.1.0",
"version": "1.2.0",
"description": "PoolTogether Aave Yield Source",
"main": "index.js",
"license": "GPL-3.0",
Expand Down
Loading

0 comments on commit 8928715

Please sign in to comment.