-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #169 from SweeprFi/apollo
adds apollox asset
- Loading branch information
Showing
6 changed files
with
270 additions
and
0 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,123 @@ | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
pragma solidity 0.8.19; | ||
|
||
// ==================================================================== | ||
// ========================== ApolloAsset.sol =========================== | ||
// ==================================================================== | ||
|
||
/** | ||
* @title Apollo Asset | ||
* @dev Representation of an on-chain investment on ApolloX finance. | ||
*/ | ||
|
||
import { Stabilizer } from "../Stabilizer/Stabilizer.sol"; | ||
import { IApolloX } from "./Interfaces/ApolloX/IApolloX.sol"; | ||
import { IERC20Metadata } from "@openzeppelin/contracts/interfaces/IERC20Metadata.sol"; | ||
|
||
contract ApolloAsset is Stabilizer { | ||
error UnexpectedAmount(); | ||
error CooldownError(); | ||
|
||
// Variables | ||
IApolloX private constant apollo = IApolloX(0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0); | ||
IERC20Metadata private constant alp = IERC20Metadata(0x4E47057f45adF24ba41375a175dA0357cB3480E5); | ||
uint256 public investedAt; | ||
|
||
// Events | ||
event Invested(uint256 indexed usdxAmount); | ||
event Divested(uint256 indexed usdxAmount); | ||
|
||
constructor( | ||
string memory _name, | ||
address _sweep, | ||
address _usdx, | ||
address _oracleUsdx, | ||
address _borrower | ||
) Stabilizer(_name, _sweep, _usdx, _oracleUsdx, _borrower) { | ||
alp.approve(address(apollo), type(uint256).max); | ||
usdx.approve(address(apollo), type(uint256).max); | ||
} | ||
|
||
/* ========== Views ========== */ | ||
|
||
/** | ||
* @notice Asset Value of investment. | ||
* @return the Returns the value of the investment in the USD coin | ||
*/ | ||
function assetValue() public view override returns (uint256) { | ||
return _oracleUsdxToUsd(getDepositAmount()); | ||
} | ||
|
||
function getDepositAmount() public view returns (uint256) { | ||
return apollo.stakeOf(address(this)) * apollo.alpPrice() / 1e8; | ||
} | ||
|
||
/* ========== Actions ========== */ | ||
|
||
/** | ||
* @notice Invest. | ||
* @param usdxAmount Amount of usdx to be swapped for token. | ||
* @param alpAmountOut Minimum amount out of ALP. | ||
*/ | ||
function invest(uint256 usdxAmount, uint256 alpAmountOut) | ||
external onlyBorrower whenNotPaused nonReentrant validAmount(usdxAmount) | ||
{ | ||
_invest(usdxAmount, 0, alpAmountOut); | ||
} | ||
|
||
/** | ||
* @notice Divest. | ||
* @param alpAmount Amount to be divested. | ||
* @param usdxMinOut Minimum amount out of usdx. | ||
*/ | ||
function divest(uint256 alpAmount, uint256 usdxMinOut) | ||
external onlyBorrower nonReentrant validAmount(alpAmount) | ||
{ | ||
_divest(alpAmount, usdxMinOut); | ||
} | ||
|
||
/** | ||
* @notice Liquidate | ||
*/ | ||
function liquidate() external nonReentrant { | ||
if(auctionAllowed) revert ActionNotAllowed(); | ||
apollo.unStake(apollo.stakeOf(address(this))); | ||
_liquidate(_getToken(), getDebt()); | ||
} | ||
|
||
function collect() external nonReentrant onlyBorrower { | ||
apollo.claimAllReward(); | ||
} | ||
|
||
/* ========== Internals ========== */ | ||
|
||
function _getToken() internal pure override returns (address) { | ||
return address(alp); | ||
} | ||
|
||
function _invest(uint256 usdxAmount, uint256, uint256 minAlpOut) | ||
internal override | ||
{ | ||
investedAt = block.timestamp; | ||
|
||
uint256 usdxBalance = usdx.balanceOf(address(this)); | ||
if (usdxBalance < usdxAmount) usdxAmount = usdxBalance; | ||
|
||
apollo.mintAlp(address(usdx), usdxAmount, minAlpOut, true); | ||
|
||
emit Invested(usdxAmount); | ||
} | ||
|
||
function _divest(uint256 alpAmount, uint256 usdxMinOut) internal override { | ||
if(block.timestamp - investedAt < apollo.coolingDuration()) revert CooldownError(); | ||
|
||
uint256 alpBalance = apollo.stakeOf(address(this)); | ||
if(alpAmount > alpBalance) alpAmount = alpBalance; | ||
|
||
apollo.unStake(alpAmount); | ||
apollo.burnAlp(address(usdx), alpAmount, usdxMinOut, address(this)); | ||
|
||
emit Divested(usdxMinOut); | ||
} | ||
|
||
} |
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,20 @@ | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
pragma solidity 0.8.19; | ||
|
||
// import "@openzeppelin/contracts/interfaces/IERC4626.sol"; | ||
|
||
interface IApolloX { // } is IERC4626 { | ||
|
||
function coolingDuration() external view returns (uint256); | ||
|
||
function mintAlp(address tokenIn, uint256 amountIn, uint256 minAlp, bool stake) external; | ||
function burnAlp(address tokenOut, uint256 alpAmount, uint256 minOut, address receiver) external; | ||
|
||
function unStake(uint256 _amount) external; | ||
function claimAllReward() external; | ||
|
||
function alpPrice() external view returns (uint256); | ||
function stakeOf(address _user) external view returns (uint256); | ||
|
||
function pendingApx(address _account) external view returns (uint256); | ||
} |
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
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,45 @@ | ||
const { ethers } = require("hardhat"); | ||
const { tokens, network, wallets, protocols, chainlink } = require("../../../utils/constants"); | ||
const { ask } = require("../../../utils/helper_functions"); | ||
|
||
async function main() { | ||
[deployer] = await ethers.getSigners(); | ||
|
||
const name = 'ApolloX Asset'; | ||
const sweep = tokens.sweep; | ||
const usdc = tokens.usdc; | ||
const oracleUsdc = chainlink.usdc_usd; | ||
const borrower = wallets.borrower; | ||
|
||
console.log("==========================================="); | ||
console.log("APOLLO ASSET DEPLOY"); | ||
console.log("==========================================="); | ||
console.log("Network:", network.name); | ||
console.log("Deployer:", deployer.address); | ||
console.log("==========================================="); | ||
console.log("Asset Name:", name); | ||
console.log("SWEEP:", sweep); | ||
console.log("USDC:", usdc); | ||
console.log("USDC/USD Chainlink Oracle:", oracleUsdc); | ||
console.log("Borrower:", borrower); | ||
console.log("==========================================="); | ||
const answer = (await ask("continue? y/n: ")); | ||
if(answer !== 'y'){ process.exit(); } | ||
console.log("Deploying..."); | ||
|
||
const Asset = await ethers.getContractFactory("ApolloAsset"); | ||
const asset = await Asset.deploy( | ||
name, | ||
sweep, | ||
usdc, | ||
oracleUsdc, | ||
borrower, | ||
); | ||
|
||
console.log("Apollo Asset deployed to:", asset.address); | ||
console.log(`\nnpx hardhat verify --network ${network.name} ${asset.address} "${name}" ${sweep} ${usdc} ${oracleUsdc} ${borrower}`) | ||
|
||
} | ||
|
||
main(); | ||
|
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,73 @@ | ||
const { expect } = require("chai"); | ||
const { ethers } = require("hardhat"); | ||
const { wallets, tokens, chainlink, protocols } = require("../../utils/constants"); | ||
const { impersonate, sendEth, toBN, increaseTime } = require("../../utils/helper_functions"); | ||
|
||
contract("Apollo Asset", async function () { | ||
before(async () => { | ||
[borrower] = await ethers.getSigners(); | ||
|
||
depositAmount = toBN("7000", 18); | ||
investAmount = toBN("7000", 18); | ||
divestAmount = toBN("7000", 18); | ||
|
||
Token = await ethers.getContractFactory("ERC20"); | ||
usdt = await Token.attach(tokens.usdt); | ||
|
||
apollo = await ethers.getContractAt("IApolloX", protocols.apollo.apollo); | ||
apx = await Token.attach(protocols.apollo.apx); | ||
|
||
ApolloAsset = await ethers.getContractFactory("ApolloAsset"); | ||
apollo_asset = await ApolloAsset.deploy( | ||
'Apollo Asset', | ||
tokens.sweep, | ||
tokens.usdt, | ||
chainlink.usdt_usd, | ||
borrower.address, | ||
); | ||
|
||
await sendEth(wallets.usdt_holder); | ||
const usdHolder = await impersonate(wallets.usdt_holder); | ||
await usdt.connect(usdHolder).transfer(apollo_asset.address, depositAmount); | ||
}); | ||
|
||
describe("apollo asset functions", async function () { | ||
it("invests into apollo correctly", async function () { | ||
expect(await apollo_asset.assetValue()).to.equal(0); | ||
expect(await usdt.balanceOf(apollo_asset.address)).to.equal(depositAmount); | ||
|
||
const minAlpOut = investAmount.mul(toBN("98",6)).div(await apollo.alpPrice()); | ||
await apollo_asset.invest(investAmount, minAlpOut); | ||
|
||
const assetValue = await apollo_asset.assetValue(); | ||
expect(assetValue).to.greaterThan(7000e6 * 0.98); | ||
|
||
expect(await usdt.balanceOf(apollo_asset.address)).to.equal(0); | ||
}); | ||
|
||
it("collects APX rewards", async function () { | ||
await increaseTime(259200); | ||
|
||
expect(await apx.balanceOf(apollo_asset.address)).to.equal(0); | ||
|
||
await apollo_asset.collect(); | ||
|
||
const apxBalance = await apx.balanceOf(apollo_asset.address); | ||
expect(apxBalance).to.be.greaterThan(0); | ||
}); | ||
|
||
it("divests by reedeming the staked tokens and exiting the pool", async function () { | ||
expect(await usdt.balanceOf(apollo_asset.address)).to.eq(0); | ||
expect(await apollo_asset.currentValue()).to.equal(await apollo_asset.assetValue()); | ||
|
||
const minUsdtOut = toBN("6800", 18); | ||
const alpAmount = toBN("99999", 18); // big int value | ||
await apollo_asset.divest(alpAmount, minUsdtOut); | ||
|
||
expect(await apollo_asset.currentValue()).to.be.greaterThan(await apollo_asset.assetValue()); | ||
|
||
const usdtValue = await usdt.balanceOf(apollo_asset.address); | ||
expect(usdtValue).to.greaterThan(minUsdtOut); | ||
}); | ||
}); | ||
}); |
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