Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master-v2' into mUSD-migration
- Loading branch information
Showing
8 changed files
with
1,082 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,291 @@ | ||
pragma solidity 0.8.0; | ||
|
||
// External | ||
import { | ||
IAaveATokenV2, | ||
IAaveLendingPoolV2, | ||
ILendingPoolAddressesProviderV2 | ||
} from "./IAave.sol"; | ||
|
||
// Libs | ||
import { MassetHelpers } from "../../shared/MassetHelpers.sol"; | ||
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | ||
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
import { AbstractIntegration } from "./AbstractIntegration.sol"; | ||
|
||
/** | ||
* @title AaveV2Integration | ||
* @author Stability Labs Pty. Ltd. | ||
* @notice A simple connection to deposit and withdraw bAssets from Aave | ||
* @dev VERSION: 1.0 | ||
* DATE: 2020-16-11 | ||
*/ | ||
contract AaveV2Integration is AbstractIntegration { | ||
|
||
using SafeERC20 for IERC20; | ||
|
||
// Core address for the given platform */ | ||
address public immutable platformAddress; | ||
|
||
event RewardTokenApproved(address rewardToken, address account); | ||
|
||
/** | ||
* @param _nexus Address of the Nexus | ||
* @param _mAsset Address of mAsset | ||
* @param _platformAddress Generic platform address | ||
*/ | ||
constructor( | ||
address _nexus, | ||
address _mAsset, | ||
address _platformAddress | ||
) AbstractIntegration(_nexus, _mAsset) { | ||
require(_platformAddress != address(0), "Invalid platform address"); | ||
platformAddress = _platformAddress; | ||
} | ||
|
||
/*************************************** | ||
ADMIN | ||
****************************************/ | ||
|
||
/** | ||
* @dev Approves Liquidator to spend reward tokens | ||
*/ | ||
function approveRewardToken() | ||
external | ||
onlyGovernor | ||
{ | ||
address liquidator = nexus.getModule(keccak256("Liquidator")); | ||
require(liquidator != address(0), "Liquidator address cannot be zero"); | ||
|
||
// Official checksummed AAVE token address | ||
// https://ethplorer.io/address/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9 | ||
address aaveToken = address(0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9); | ||
|
||
MassetHelpers.safeInfiniteApprove(aaveToken, liquidator); | ||
|
||
emit RewardTokenApproved(address(aaveToken), liquidator); | ||
} | ||
|
||
|
||
/*************************************** | ||
CORE | ||
****************************************/ | ||
|
||
/** | ||
* @dev Deposit a quantity of bAsset into the platform. Credited aTokens | ||
* remain here in the vault. Can only be called by whitelisted addresses | ||
* (mAsset and corresponding BasketManager) | ||
* @param _bAsset Address for the bAsset | ||
* @param _amount Units of bAsset to deposit | ||
* @param _hasTxFee Is the bAsset known to have a tx fee? | ||
* @return quantityDeposited Quantity of bAsset that entered the platform | ||
*/ | ||
function deposit( | ||
address _bAsset, | ||
uint256 _amount, | ||
bool _hasTxFee | ||
) | ||
external | ||
override | ||
onlyMasset | ||
nonReentrant | ||
returns (uint256 quantityDeposited) | ||
{ | ||
require(_amount > 0, "Must deposit something"); | ||
|
||
IAaveATokenV2 aToken = _getATokenFor(_bAsset); | ||
|
||
quantityDeposited = _amount; | ||
|
||
if(_hasTxFee) { | ||
// If we charge a fee, account for it | ||
uint256 prevBal = _checkBalance(aToken); | ||
_getLendingPool().deposit(_bAsset, _amount, address(this), 36); | ||
uint256 newBal = _checkBalance(aToken); | ||
quantityDeposited = _min(quantityDeposited, newBal - prevBal); | ||
} else { | ||
_getLendingPool().deposit(_bAsset, _amount, address(this), 36); | ||
} | ||
|
||
emit Deposit(_bAsset, address(aToken), quantityDeposited); | ||
} | ||
|
||
/** | ||
* @dev Withdraw a quantity of bAsset from the platform | ||
* @param _receiver Address to which the bAsset should be sent | ||
* @param _bAsset Address of the bAsset | ||
* @param _amount Units of bAsset to withdraw | ||
* @param _hasTxFee Is the bAsset known to have a tx fee? | ||
*/ | ||
function withdraw( | ||
address _receiver, | ||
address _bAsset, | ||
uint256 _amount, | ||
bool _hasTxFee | ||
) | ||
external | ||
override | ||
onlyMasset | ||
nonReentrant | ||
{ | ||
_withdraw(_receiver, _bAsset, _amount, _amount, _hasTxFee); | ||
} | ||
|
||
/** | ||
* @dev Withdraw a quantity of bAsset from the platform | ||
* @param _receiver Address to which the bAsset should be sent | ||
* @param _bAsset Address of the bAsset | ||
* @param _amount Units of bAsset to send to recipient | ||
* @param _totalAmount Total units to pull from lending platform | ||
* @param _hasTxFee Is the bAsset known to have a tx fee? | ||
*/ | ||
function withdraw( | ||
address _receiver, | ||
address _bAsset, | ||
uint256 _amount, | ||
uint256 _totalAmount, | ||
bool _hasTxFee | ||
) | ||
external | ||
override | ||
onlyMasset | ||
nonReentrant | ||
{ | ||
_withdraw(_receiver, _bAsset, _amount, _totalAmount, _hasTxFee); | ||
} | ||
|
||
/** @dev Withdraws _totalAmount from the lending pool, sending _amount to user */ | ||
function _withdraw( | ||
address _receiver, | ||
address _bAsset, | ||
uint256 _amount, | ||
uint256 _totalAmount, | ||
bool _hasTxFee | ||
) | ||
internal | ||
{ | ||
require(_totalAmount > 0, "Must withdraw something"); | ||
|
||
IAaveATokenV2 aToken = _getATokenFor(_bAsset); | ||
|
||
if(_hasTxFee) { | ||
require(_amount == _totalAmount, "Cache inactive for assets with fee"); | ||
_getLendingPool().withdraw(_bAsset, _amount, _receiver); | ||
} else { | ||
_getLendingPool().withdraw(_bAsset, _totalAmount, address(this)); | ||
// Send redeemed bAsset to the receiver | ||
IERC20(_bAsset).safeTransfer(_receiver, _amount); | ||
} | ||
|
||
emit PlatformWithdrawal(_bAsset, address(aToken), _totalAmount, _amount); | ||
} | ||
|
||
/** | ||
* @dev Withdraw a quantity of bAsset from the cache. | ||
* @param _receiver Address to which the bAsset should be sent | ||
* @param _bAsset Address of the bAsset | ||
* @param _amount Units of bAsset to withdraw | ||
*/ | ||
function withdrawRaw( | ||
address _receiver, | ||
address _bAsset, | ||
uint256 _amount | ||
) | ||
external | ||
override | ||
onlyMasset | ||
nonReentrant | ||
{ | ||
require(_amount > 0, "Must withdraw something"); | ||
require(_receiver != address(0), "Must specify recipient"); | ||
|
||
IERC20(_bAsset).safeTransfer(_receiver, _amount); | ||
|
||
emit Withdrawal(_bAsset, address(0), _amount); | ||
} | ||
|
||
/** | ||
* @dev Get the total bAsset value held in the platform | ||
* This includes any interest that was generated since depositing | ||
* Aave gradually increases the balances of all aToken holders, as the interest grows | ||
* @param _bAsset Address of the bAsset | ||
* @return balance Total value of the bAsset in the platform | ||
*/ | ||
function checkBalance(address _bAsset) | ||
external | ||
override | ||
returns (uint256 balance) | ||
{ | ||
// balance is always with token aToken decimals | ||
IAaveATokenV2 aToken = _getATokenFor(_bAsset); | ||
return _checkBalance(aToken); | ||
} | ||
|
||
/*************************************** | ||
APPROVALS | ||
****************************************/ | ||
|
||
/** | ||
* @dev Internal method to respond to the addition of new bAsset / pTokens | ||
* We need to approve the Aave lending pool core conrtact and give it permission | ||
* to spend the bAsset | ||
* @param _bAsset Address of the bAsset to approve | ||
*/ | ||
function _abstractSetPToken(address _bAsset, address /*_pToken*/) | ||
internal | ||
override | ||
{ | ||
address lendingPool = address(_getLendingPool()); | ||
// approve the pool to spend the bAsset | ||
MassetHelpers.safeInfiniteApprove(_bAsset, lendingPool); | ||
} | ||
|
||
/*************************************** | ||
HELPERS | ||
****************************************/ | ||
|
||
/** | ||
* @dev Get the current address of the Aave lending pool, which is the gateway to | ||
* depositing. | ||
* @return Current lending pool implementation | ||
*/ | ||
function _getLendingPool() | ||
internal | ||
view | ||
returns (IAaveLendingPoolV2) | ||
{ | ||
address lendingPool = ILendingPoolAddressesProviderV2(platformAddress).getLendingPool(); | ||
require(lendingPool != address(0), "Lending pool does not exist"); | ||
return IAaveLendingPoolV2(lendingPool); | ||
} | ||
|
||
|
||
/** | ||
* @dev Get the pToken wrapped in the IAaveAToken interface for this bAsset, to use | ||
* for withdrawing or balance checking. Fails if the pToken doesn't exist in our mappings. | ||
* @param _bAsset Address of the bAsset | ||
* @return aToken Corresponding to this bAsset | ||
*/ | ||
function _getATokenFor(address _bAsset) | ||
internal | ||
view | ||
returns (IAaveATokenV2) | ||
{ | ||
address aToken = bAssetToPToken[_bAsset]; | ||
require(aToken != address(0), "aToken does not exist"); | ||
return IAaveATokenV2(aToken); | ||
} | ||
|
||
/** | ||
* @dev Get the total bAsset value held in the platform | ||
* @param _aToken aToken for which to check balance | ||
* @return balance Total value of the bAsset in the platform | ||
*/ | ||
function _checkBalance(IAaveATokenV2 _aToken) | ||
internal | ||
view | ||
returns (uint256 balance) | ||
{ | ||
return _aToken.balanceOf(address(this)); | ||
} | ||
} |
Oops, something went wrong.