Skip to content

Commit

Permalink
Merge pull request #169 from SweeprFi/apollo
Browse files Browse the repository at this point in the history
adds apollox asset
  • Loading branch information
maxcoto authored Apr 16, 2024
2 parents 4fd086c + a3eb058 commit 9d8b7c8
Show file tree
Hide file tree
Showing 6 changed files with 270 additions and 0 deletions.
123 changes: 123 additions & 0 deletions contracts/Assets/ApolloAsset.sol
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);
}

}
20 changes: 20 additions & 0 deletions contracts/Assets/Interfaces/ApolloX/IApolloX.sol
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);
}
1 change: 1 addition & 0 deletions hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const networks = {
url: alchemyLink,
// blockNumber: 20005467
},
chainId: network.id,
},
localhost: {
allowUnlimitedContractSize: true,
Expand Down
45 changes: 45 additions & 0 deletions scripts/deploy/assets/apollo.js
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();

73 changes: 73 additions & 0 deletions test/assets/apollo.js
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);
});
});
});
8 changes: 8 additions & 0 deletions utils/networks/bsc.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ module.exports = {
observationCardinality: 480,
},

protocols: {
apollo: {
apollo: "0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0",
apx: "0x78f5d389f5cdccfc41594abab4b0ed02f31398b3",
alp: "0x4E47057f45adF24ba41375a175dA0357cB3480E5",
}
},

deployments: {
balancer: '0xa884970F06Dda7BedD86829E14BeCa2c8fEd5220',
treasury: '0x7c9131d7E2bEdb29dA39503DD8Cf809739f047B3',
Expand Down

0 comments on commit 9d8b7c8

Please sign in to comment.