Skip to content

Commit

Permalink
feat: implement aave with new model
Browse files Browse the repository at this point in the history
  • Loading branch information
jakekidd committed Jul 1, 2024
1 parent fef29dd commit 570aef0
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 113 deletions.
127 changes: 42 additions & 85 deletions src/contracts/child/QWAaveV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ pragma solidity 0.8.23;
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {IQWChild} from 'interfaces/IQWChild.sol';
import {ILendingPool} from 'interfaces/aave-v2/ILendingPool.sol';
import {IAToken} from 'interfaces/aave-v2/IAToken.sol';

/**
* @title AaveV2 Integration for Quant Wealth
Expand All @@ -13,13 +12,15 @@ import {IAToken} from 'interfaces/aave-v2/IAToken.sol';
contract QWAaveV2 is IQWChild {
// Variables
address public immutable QW_MANAGER;
address public immutable LENDING_POOL;
address public immutable INVESTMENT_TOKEN;
address public immutable A_TOKEN;
address public immutable ASSET_TOKEN;
address public immutable LENDING_POOL;

// Custom errors
error InvalidCallData(); // Error for invalid call data
error UnauthorizedAccess(); // Error for unauthorized caller
error NoInvestmentTokensReceived();
error NoAssetTokensReceived();

modifier onlyQwManager() {
if (msg.sender != QW_MANAGER) {
Expand All @@ -33,125 +34,81 @@ contract QWAaveV2 is IQWChild {
* @param _qwManager The address of the Quant Wealth Manager contract.
* @param _lendingPool The address of the AaveV2 pool contract.
* @param _investmentToken The address of the investment token (e.g., USDT).
* @param _aInvestmentToken The address of the aToken (e.g., aUSDT).
* @param _assetToken The address of the corresponding aToken (e.g., aUSDT).
*/
constructor(
address _qwManager,
address _lendingPool,
address _investmentToken,
address _aInvestmentToken
address _assetToken
) {
QW_MANAGER = _qwManager;
LENDING_POOL = _lendingPool;
INVESTMENT_TOKEN = _investmentToken;
A_TOKEN = _aInvestmentToken;
ASSET_TOKEN = _assetToken;
}

// Functions
/**
* @notice Executes a transaction on AaveV2 pool to deposit tokens.
* @dev This function is called by the parent contract to deposit tokens into the AaveV2 pool.
* @param _callData Encoded function call data containing total shares.
* @param _tokenAmount Amount of tokens to be deposited.
* @param _amount Amount of tokens to be deposited.
* @return success boolean indicating the success of the transaction.
* @return shares Number of shares to be allocated to the user in return for investment created.
* @return assetAmountReceived Amount of asset tokens received.
*/
function create(
bytes memory _callData,
uint256 _tokenAmount
) external override onlyQwManager returns (bool success, uint256 shares) {
(uint256 _totalShares) = abi.decode(_callData, (uint256));

function open(uint256 _amount) external override onlyQwManager returns (bool success, uint256 assetAmountReceived) {
// Transfer tokens from QWManager to this contract.
IERC20 token = IERC20(INVESTMENT_TOKEN);
token.transferFrom(QW_MANAGER, address(this), _tokenAmount);
// IERC20 token = IERC20(INVESTMENT_TOKEN);
// token.transferFrom(QW_MANAGER, address(this), _amount);
// Check whether we have been transferred the tokens to spend.
if (IERC20(INVESTMENT_TOKEN).balanceOf(address(this)) == 0) {
revert NoInvestmentTokensReceived();
}

// Approve the Aave lending pool to spend the tokens.
token.approve(LENDING_POOL, _tokenAmount);

// Calculate price per share before new investment. This is the price that the investment is
// 'buying' shares of the pool at.
uint256 sharePrice = pricePerShare(_totalShares);
IERC20(INVESTMENT_TOKEN).approve(LENDING_POOL, _amount);

// Deposit tokens into Aave.
ILendingPool(LENDING_POOL).deposit(INVESTMENT_TOKEN, _tokenAmount, address(this), 0);
ILendingPool(LENDING_POOL).deposit(INVESTMENT_TOKEN, _amount, address(this), 0);

// Get the balance of aTokens, which will reflect the principle investment(s) + interest.
assetAmountReceived = IERC20(ASSET_TOKEN).balanceOf(address(this));

// Calculate shares to be issued for the new investment.
shares = _tokenAmount / sharePrice;
// Check to ensure we have received the target asset.
if (assetAmountReceived == 0) {
revert NoAssetTokensReceived();
}

// Transfer assets to QWManager.
IERC20(ASSET_TOKEN).transfer(QW_MANAGER, assetAmountReceived);

success = true;
}

/**
* @notice Executes a transaction on AaveV2 pool to withdraw tokens.
* @dev This function is called by the parent contract to withdraw tokens from the AaveV2 pool.
* @param _callData Encoded function call data containing total shares.
* @param _sharesAmount Amount of shares to be withdrawn.
* @param _amount Amount of holdings to be withdrawn.
* @return success boolean indicating the success of the transaction.
* @return tokens Number of tokens to be returned to the user in exchange for shares withdrawn.
* @return tokenAmountReceived Number of tokens to be returned to the user in exchange for the withdrawn ratio.
*/
function close(
bytes memory _callData,
uint256 _sharesAmount
) external override onlyQwManager returns (bool success, uint256 tokens) {
(uint256 _totalShares) = abi.decode(_callData, (uint256));

if (_sharesAmount > _totalShares) {
revert InvalidCallData();
function close(uint256 _amount) external override onlyQwManager returns (bool success, uint256 tokenAmountReceived) {
if (IERC20(ASSET_TOKEN).balanceOf(address(this)) == 0) {
revert NoAssetTokensReceived();
}

// Calculate the amount of tokens to withdraw based on the shares.
uint256 totalInvestmentValue = getInvestmentValue();
// If shares amount < total shares, then the token amount is share * price per share.
uint256 tokens = (_sharesAmount == _totalShares) ?
totalInvestmentValue
: (_sharesAmount * totalInvestmentValue) / _totalShares;
// Withdraw the tokens from Aave.
ILendingPool(LENDING_POOL).withdraw(INVESTMENT_TOKEN, _amount, address(this));

// Withdraw the tokens from Aave. The number of aTokens to withdraw is equal to underlying tokens received.
ILendingPool(LENDING_POOL).withdraw(INVESTMENT_TOKEN, tokens, QW_MANAGER);
// Check the balance of the investment token received.
tokenAmountReceived = IERC20(INVESTMENT_TOKEN).balanceOf(address(this));
if (tokenAmountReceived == 0) {
revert NoInvestmentTokensReceived();
}

// TODO: Send tokens back to QWManager.
// Transfer tokens to QWManager.
IERC20(INVESTMENT_TOKEN).transfer(QW_MANAGER, tokenAmountReceived);

success = true;
}

/**
* @notice Gets the price per share in terms of the specified token.
* @dev This function calculates the value of one share in terms of the specified token.
* @param _totalShares The total shares.
* @return pricePerShare uint256 representing the value of one share in the specified token.
*/
function pricePerShare(uint256 _totalShares) external view returns (uint256) {
return _totalShares == 0 ?
1 * 10 ** token.decimals()
: getInvestmentValue() / _totalShares;
}

/**
* @notice Gets the total investment value in terms of the specified token.
* @dev This function calculates the total value of the investment in terms of the specified token.
* @return investmentValue uint256 representing the total value of the investment in the specified token.
*/
function getInvestmentValue() public view returns (uint256 investmentValue) {
// Get the balance of aTokens, which will reflect the principle investment(s) + interest.
uint256 aTokenBalance = IAToken(A_TOKEN).balanceOf(address(this));
return aTokenBalance;
}

/**
* @notice Gets the address of the Quant Wealth Manager contract.
* @dev Returns the address of the Quant Wealth Manager contract.
*/
function QW_MANAGER() external view override returns (address) {
return qwManager;
}

/**
* @notice Gets the address of the investment token.
* @dev Returns the address of the token that is initially invested and received once the investment is withdrawn.
* @return The address of the investment token.
*/
function INVESTMENT_TOKEN() external view override returns (address) {
return investmentToken;
}
}
30 changes: 2 additions & 28 deletions src/interfaces/IQWChild.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,37 +20,11 @@ interface IQWChild {
/**
* @notice Closes a transaction on the child contract.
* @dev This function is called by the parent contract to close a transaction on the child contract.
* @param _ratio Percentage of holdings to be withdrawn, with 8 decimal places for precision.
* @param _amount Amount of holdings to be withdrawn.
* @return success boolean indicating whether the transaction was successfully closed.
* @return tokenAmountReceived Number of tokens to be returned to the user in exchange for the withdrawn ratio.
*/
function close(
uint256 _ratio
uint256 _amount
) external returns (bool success, uint256 tokenAmountReceived);

/**
* @notice Gets the total amount of the asset currently held.
* @return uint256 The total amount of the asset currently held.
*/
function holdings() external view returns (uint256);

/**
* @notice Calculates the amount of tokens to be withdrawn for a given ratio.
* @param _ratio Percentage of holdings for which to calculate a withdraw, with 8 decimal places for precision.
* @return uint256 The amount of tokens that would be received for the given ratio.
*/
function calc(uint256 _ratio) external view returns (uint256);

/**
* @notice Gets the address of the Quant Wealth Manager contract.
* @return address The address of the Quant Wealth Manager contract recorded in this child contract.
*/
function QW_MANAGER() external view returns (address);

/**
* @notice Gets the address of the target investment token.
* @dev Returns the address of the token that is initially invested and received on withdraw.
* @return address The address of the investment token.
*/
function INVESTMENT_TOKEN() external view returns (address);
}

0 comments on commit 570aef0

Please sign in to comment.