Skip to content

Commit

Permalink
Merge pull request #168 from SweeprFi/agave-asset
Browse files Browse the repository at this point in the history
adds Agave asset for Gnosis chain
  • Loading branch information
maxcoto authored Mar 22, 2024
2 parents c937111 + a10342d commit 4fd086c
Show file tree
Hide file tree
Showing 6 changed files with 562 additions and 0 deletions.
114 changes: 114 additions & 0 deletions contracts/Assets/AgaveAsset.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

// ====================================================================
// ========================= AgaveAsset.sol ===========================
// ====================================================================

/**
* @title Agave Asset
* @dev Representation of an on-chain investment on a Agave pool
*/

import { IERC20Metadata } from "@openzeppelin/contracts/interfaces/IERC20Metadata.sol";
import { TransferHelper } from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
import { Stabilizer, OvnMath } from "../Stabilizer/Stabilizer.sol";
import { ILendingPool, IAgaveOracle } from "./Interfaces/Agave/IAgave.sol";

contract AgaveAsset is Stabilizer {
// Variables
IERC20Metadata public immutable aToken;
ILendingPool public immutable agavePool;

uint16 private constant DEADLINE_GAP = 15 minutes;

// Events
event Invested(uint256 indexed usdxAmount);
event Divested(uint256 indexed usdxAmount);

constructor(
string memory _name,
address _sweep,
address _usdx, // wxDAI
address _aToken, // agwxDAI
address _agavePool,
address _oracle,
address _borrower
) Stabilizer(_name, _sweep, _usdx, _oracle, _borrower) {
aToken = IERC20Metadata(_aToken);
agavePool = ILendingPool(_agavePool);
}

/* ========== Views ========== */

/**
* @notice Get Asset Value
* @return uint256 Asset Amount.
*/
function assetValue() public view override returns (uint256) {
uint256 aTokenBalance = aToken.balanceOf(address(this));

return _oracleUsdxToUsd(aTokenBalance);
}

/* ========== Actions ========== */

/**
* @notice Invest USDX
* @param usdxAmount USDX Amount to be invested.
*/
function invest(uint256 usdxAmount)
external onlyBorrower whenNotPaused nonReentrant validAmount(usdxAmount)
{
_invest(usdxAmount, 0, 0);
}

/**
* @notice Divests From Agave.
* @param usdxAmount Amount to be divested.
*/
function divest(uint256 usdxAmount)
external onlyBorrower nonReentrant validAmount(usdxAmount)
{
_divest(usdxAmount, 0);
}

function liquidate() external nonReentrant {
if(auctionAllowed) revert ActionNotAllowed();
_liquidate(_getToken(), getDebt());
}

/* ========== Internals ========== */

function _getToken() internal view override returns (address) {
return address(aToken);
}

/**
* @notice Invest
* @dev Deposits the amount into the Agave pool.
*/
function _invest(uint256 usdxAmount, uint256, uint256) internal override {
uint256 usdxBalance = usdx.balanceOf(address(this));
if (usdxBalance == 0) revert NotEnoughBalance();
if (usdxBalance < usdxAmount) usdxAmount = usdxBalance;

TransferHelper.safeApprove(address(usdx), address(agavePool), usdxAmount);
agavePool.deposit(address(usdx), usdxAmount, address(this), 0);

emit Invested(usdxAmount);
}

/**
* @notice Divest
* @dev Withdraws the amount from the Agave pool.
*/
function _divest(uint256 tokenAmount, uint256) internal override {
if (aToken.balanceOf(address(this)) < tokenAmount)
tokenAmount = type(uint256).max;

agavePool.withdraw(address(usdx), tokenAmount, address(this));

emit Divested(tokenAmount);
}
}
266 changes: 266 additions & 0 deletions contracts/Assets/Interfaces/Agave/IAgave.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

interface ILendingPool {
/**
* @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
* - E.g. User deposits 100 USDC and gets in return 100 aUSDC
* @param asset The address of the underlying asset to deposit
* @param amount The amount to be deposited
* @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
* wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
* is a different wallet
* @param referralCode Code used to register the integrator originating the operation, for potential rewards.
* 0 if the action is executed directly by the user, without any middle-man
**/
function deposit(
address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode
) external;

/**
* @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
* E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
* @param asset The address of the underlying asset to withdraw
* @param amount The underlying amount to be withdrawn
* - Send the value type(uint256).max in order to withdraw the whole aToken balance
* @param to Address that will receive the underlying, same as msg.sender if the user
* wants to receive it on his own wallet, or a different address if the beneficiary is a
* different wallet
* @return The final amount withdrawn
**/
function withdraw(
address asset,
uint256 amount,
address to
) external returns (uint256);

/**
* @dev Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower
* already deposited enough collateral, or he was given enough allowance by a credit delegator on the
* corresponding debt token (StableDebtToken or VariableDebtToken)
* - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet
* and 100 stable/variable debt tokens, depending on the `interestRateMode`
* @param asset The address of the underlying asset to borrow
* @param amount The amount to be borrowed
* @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable
* @param referralCode Code used to register the integrator originating the operation, for potential rewards.
* 0 if the action is executed directly by the user, without any middle-man
* @param onBehalfOf Address of the user who will receive the debt. Should be the address of the borrower itself
* calling the function if he wants to borrow against his own collateral, or the address of the credit delegator
* if he has been given credit delegation allowance
**/
function borrow(
address asset,
uint256 amount,
uint256 interestRateMode,
uint16 referralCode,
address onBehalfOf
) external;

/**
* @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned using the reserve token
* - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address
* @param asset The address of the borrowed underlying asset previously borrowed
* @param amount The amount to repay
* - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
* @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
* @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the
* user calling the function if he wants to reduce/remove his own debt, or the address of any other
* other borrower whose debt should be removed
* @return The final amount repaid
**/
function repay(
address asset,
uint256 amount,
uint256 rateMode,
address onBehalfOf
) external returns (uint256);

/**
* @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned using deposited balance of the same asset
* - E.g. User repays 100 agUSDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address
* @param asset The address of the borrowed underlying asset previously borrowed
* @param amount The amount to repay
* - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
* @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
* @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the
* user calling the function if he wants to reduce/remove his own debt, or the address of any other
* other borrower whose debt should be removed
* @return The final amount repaid
**/
function repayUsingAgToken(
address asset,
uint256 amount,
uint256 rateMode,
address onBehalfOf
) external returns (uint256);

/**
* @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa
* @param asset The address of the underlying asset borrowed
* @param rateMode The rate mode that the user wants to swap to
**/
function swapBorrowRateMode(address asset, uint256 rateMode) external;

/**
* @dev Rebalances the stable interest rate of a user to the current stable rate defined on the reserve.
* - Users can be rebalanced if the following conditions are satisfied:
* 1. Usage ratio is above 95%
* 2. the current deposit APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too much has been
* borrowed at a stable rate and depositors are not earning enough
* @param asset The address of the underlying asset borrowed
* @param user The address of the user to be rebalanced
**/
function rebalanceStableBorrowRate(address asset, address user) external;

/**
* @dev Allows depositors to enable/disable a specific deposited asset as collateral
* @param asset The address of the underlying asset deposited
* @param useAsCollateral `true` if the user wants to use the deposit as collateral, `false` otherwise
**/
function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external;

/**
* @dev Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1
* - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated using the reserve asset,
* and receives a proportionally amount of the `collateralAsset` plus a bonus to cover market risk
* @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
* @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
* @param user The address of the borrower getting liquidated
* @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
* @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants
* to receive the underlying collateral asset directly
**/
function liquidationCall(
address collateralAsset,
address debtAsset,
address user,
uint256 debtToCover,
bool receiveAToken
) external;

/**
* @dev Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1
* - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated using the corresponding agToken,
* and receives a proportionally amount of the `collateralAsset` plus a bonus to cover market risk
* @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
* @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
* @param user The address of the borrower getting liquidated
* @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
* @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants
* to receive the underlying collateral asset directly
**/
function liquidationCallUsingAgToken(
address collateralAsset,
address debtAsset,
address user,
uint256 debtToCover,
bool receiveAToken
) external;

/**
* @dev Allows smartcontracts to access the liquidity of the pool within one transaction,
* as long as the amount taken plus a fee is returned.
* IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration.
* For further details please visit https://developers.aave.com
* @param receiverAddress The address of the contract receiving the funds, implementing the IFlashLoanReceiver interface
* @param assets The addresses of the assets being flash-borrowed
* @param amounts The amounts amounts being flash-borrowed
* @param modes Types of the debt to open if the flash loan is not returned:
* 0 -> Don't open any debt, just revert if funds can't be transferred from the receiver
* 1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
* 2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
* @param onBehalfOf The address that will receive the debt in the case of using on `modes` 1 or 2
* @param params Variadic packed params to pass to the receiver as extra information
* @param referralCode Code used to register the integrator originating the operation, for potential rewards.
* 0 if the action is executed directly by the user, without any middle-man
**/
function flashLoan(
address receiverAddress,
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata modes,
address onBehalfOf,
bytes calldata params,
uint16 referralCode
) external;

/**
* @dev Returns the user account data across all the reserves
* @param user The address of the user
* @return totalCollateralETH the total collateral in ETH of the user
* @return totalDebtETH the total debt in ETH of the user
* @return availableBorrowsETH the borrowing power left of the user
* @return currentLiquidationThreshold the liquidation threshold of the user
* @return ltv the loan to value of the user
* @return healthFactor the current health factor of the user
**/
function getUserAccountData(address user)
external
view
returns (
uint256 totalCollateralETH,
uint256 totalDebtETH,
uint256 availableBorrowsETH,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
);

function initReserve(
address reserve,
address aTokenAddress,
address stableDebtAddress,
address variableDebtAddress,
address interestRateStrategyAddress
) external;

function setReserveInterestRateStrategyAddress(address reserve, address rateStrategyAddress)
external;

function setReserveLimits(
address asset,
uint256 depositLimit,
uint256 borrowLimit,
uint256 collateralUsageLimit
) external;

function setConfiguration(address reserve, uint256 configuration) external;

/**
* @dev Returns the normalized income normalized income of the reserve
* @param asset The address of the underlying asset of the reserve
* @return The reserve's normalized income
*/
function getReserveNormalizedIncome(address asset) external view returns (uint256);

/**
* @dev Returns the normalized variable debt per unit of asset
* @param asset The address of the underlying asset of the reserve
* @return The reserve normalized variable debt
*/
function getReserveNormalizedVariableDebt(address asset) external view returns (uint256);


function finalizeTransfer(
address asset,
address from,
address to,
uint256 amount,
uint256 balanceFromAfter,
uint256 balanceToBefore
) external;

function getReservesList() external view returns (address[] memory);

function setPause(bool val) external;

function paused() external view returns (bool);
}

interface IAgaveOracle {
function getAssetPrice(address asset) external view returns (uint256);
}
8 changes: 8 additions & 0 deletions hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ module.exports = {
browserURL: "https://avalanche.routescan.io"
}
},
{
network: "gnosis",
chainId: 100,
urls: {
apiURL: "https://api.gnosisscan.io/api",
browserURL: "https://gnosisscan.io"
}
},
]
},

Expand Down
Loading

0 comments on commit 4fd086c

Please sign in to comment.