Skip to content

Commit

Permalink
started adding in arbitrum oracle
Browse files Browse the repository at this point in the history
  • Loading branch information
jpick713 committed Apr 22, 2024
1 parent 3f5ad21 commit c1d245a
Show file tree
Hide file tree
Showing 4 changed files with 388 additions and 0 deletions.
11 changes: 11 additions & 0 deletions contracts/peer-to-peer/interfaces/oracles/IGLPManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface IGLPManager {
/**
* @notice gets price of GLP in USD with 30 decimals
* @param _maximize will pass true
* @return price of GLP in USD
*/
function getPrice(bool _maximize) external view returns (uint256);
}
10 changes: 10 additions & 0 deletions contracts/peer-to-peer/interfaces/oracles/IGTOKEN.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface IGTOKEN {
/**
* @notice gets amount of underlying token for a given amount of gToken
* @return amount of underlying token
*/
function shareToAssetsPrice() external view returns (uint256);
}
198 changes: 198 additions & 0 deletions contracts/peer-to-peer/oracles/custom/MysoArbitrumUsdOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.19;

//import {ChainlinkBase} from "../chainlink/ChainlinkBase.sol";
import {ChainlinkArbitrumSequencerUSD} from "../chainlink/ChainlinkArbitrumSequencerUSD.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IMysoTokenManager} from "../../interfaces/oracles/IMysoTokenManager.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {LogExpMath} from "./utils/LogExpMath.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

/**
* @dev supports oracles which are compatible with v2v3 or v3 interfaces
*/
contract MysoOracle is ChainlinkArbitrumSequencerUSD, Ownable {
struct PriceParams {
// maxPrice is in 8 decimals for chainlink consistency
uint96 maxPrice;
// k is in 18 decimals
// e.g. 8e17 is 0.8 in decimal
uint96 k;
// a and b are in terms of 1000
// e.g. 1770 is 1.77 in decimal
uint32 a;
uint32 b;
}
// solhint-disable var-name-mixedcase
//address internal constant MYSO = 0x00000000000000000000000000000000DeaDBeef; // TODO: put in real myso address
address internal constant MYSO = 0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1;
address internal constant GDAI = 0xd85E038593d7A098614721EaE955EC2022B9B91B;
address internal constant GUSDC =
0xd3443ee1e91aF28e5FB858Fbd0D72A63bA8046E0;
address internal constant GETH = 0x5977A9682D7AF81D347CFc338c61692163a2784C;
address internal constant GLP = 0x4277f8F2c384827B5273592FF7CeBd9f2C1ac258;
address internal constant ETH_USD_CHAINLINK =
0x639Fe6ab55C921f74e7fac1ee960C0B6293ba612;
address internal constant DAI_ETH_CHAINLINK =
0xc5C8E77B397E531B8EC06BFb0048328B30E9eCfB;
address internal constant USDC_USD_CHAINLINK =
0x50834F3163758fcC1Df9973b6e91f0F0F0434aD3;
address internal constant GLP_MANAGER =
0x3963FfC9dff443c2A94f21b129D429891E32ec18;

uint256 internal constant MYSO_PRICE_TIME_LOCK = 5 minutes;

address public mysoTokenManager;

PriceParams public mysoPriceParams;

event MysoTokenManagerUpdated(address newMysoTokenManager);

error NoMyso();

/**
* @dev constructor for MysoOracle
* @param _tokenAddrs array of token addresses
* @param _oracleAddrs array of oracle addresses
* @param _owner owner of the contract
* @param _mysoTokenManager address of myso token manager contract
* @param _maxPrice max price in 8 decimals
* @param _k k in 18 decimals
* @param _a a in terms of 1000
* @param _b b in terms of 1000
*/
constructor(
address[] memory _tokenAddrs,
address[] memory _oracleAddrs,
address _owner,
address _mysoTokenManager,
uint96 _maxPrice,
uint96 _k,
uint32 _a,
uint32 _b
) ChainlinkArbitrumSequencerUSD(_tokenAddrs, _oracleAddrs) Ownable() {
mysoTokenManager = _mysoTokenManager;
mysoPriceParams = PriceParams(_maxPrice, _k, _a, _b);
_transferOwnership(_owner);
}

/**
* @dev updates myso token manager contract address
* @param _newMysoTokenManager new myso token manager contract address
*/

function setMysoTokenManager(
address _newMysoTokenManager
) external onlyOwner {
mysoTokenManager = _newMysoTokenManager;
emit MysoTokenManagerUpdated(_newMysoTokenManager);
}

/**
* @dev updates myso price params
* @param _maxPrice max price in 8 decimals
* @param _k k in 18 decimals
* @param _a a in terms of 1000
* @param _b b in terms of 1000
*/
function setMysoPriceParams(
uint96 _maxPrice,
uint96 _k,
uint32 _a,
uint32 _b
) external onlyOwner {
mysoPriceParams = PriceParams(_maxPrice, _k, _a, _b);
}

function getPrice(
address collToken,
address loanToken
) external view override returns (uint256 collTokenPriceInLoanToken) {
(uint256 priceOfCollToken, uint256 priceOfLoanToken) = getRawPrices(
collToken,
loanToken
);
uint256 loanTokenDecimals = (loanToken == MYSO)
? 18
: IERC20Metadata(loanToken).decimals();
collTokenPriceInLoanToken =
(priceOfCollToken * 10 ** loanTokenDecimals) /
priceOfLoanToken;
}

function getRawPrices(
address collToken,
address loanToken
)
public
view
override
returns (uint256 collTokenPriceRaw, uint256 loanTokenPriceRaw)
{
// must have at least one token is MYSO to use this oracle
if (collToken != MYSO && loanToken != MYSO) {
revert NoMyso();
}
(collTokenPriceRaw, loanTokenPriceRaw) = (
_getPriceOfToken(collToken),
_getPriceOfToken(loanToken)
);
}

function _getPriceOfToken(
address token
) internal view virtual override returns (uint256 tokenPriceRaw) {
if (token == MYSO) {
tokenPriceRaw = _getMysoPriceInUsd();
} else if (token == GDAI) {
tokenPriceRaw = _getGTOKENPriceInUsd(GDAI, DAI_ETH_CHAINLINK);
} else if (token == GUSDC) {
tokenPriceRaw = _getGTOKENPriceInUsd(GUSDC, USDC_USD_CHAINLINK);
} else if (token == GETH) {
tokenPriceRaw = _getGTOKENPriceInUsd(GETH, ETH_USD_CHAINLINK);
} else if (token == GLP) {
tokenPriceRaw = IGLPManager(GLP_MANAGER).getPrice(true) / 1e22;
} else {
tokenPriceRaw = super._getPriceOfToken(token);
}
}

function _getMysoPriceInUsd()
internal
view
returns (uint256 mysoPriceInUsd)
{
uint256 _totalMysoLoanAmount = IMysoTokenManager(mysoTokenManager)
.totalMysoLoanAmount();
PriceParams memory params = mysoPriceParams;
uint256 maxPrice = uint256(params.maxPrice);
uint256 k = uint256(params.k);
uint256 a = uint256(params.a);
uint256 b = uint256(params.b);
uint256 numerator = k * b;
uint256 denominator = uint256(
LogExpMath.exp(
int256(Math.mulDiv(_totalMysoLoanAmount, a, 1000000000))
)
) + (2 * b - 1000) * 1e15;
mysoPriceInUsd = maxPrice - Math.mulDiv(numerator, 1e5, denominator);
}

function _getGTOKENPriceInUsd(
address token,
address chainlinkOracle
) internal view returns (uint256 gTokenPriceRaw) {
uint256 assetsPerGtoken = IGTOKEN(token).shareToAssetsPrice();
uint256 assetPriceInUsd = _checkAndReturnLatestRoundData(
chainlinkOracle
);
uint256 tokenPriceInUsd = _getPriceOfToken(token);
gTokenPriceRaw = Math.mulDiv(
tokenPriceInUsd * assetsPerGtoken,
1e8,
assetPriceInUsd * 1e18
);
}
}
169 changes: 169 additions & 0 deletions contracts/test/TestnetTokenManagerArbitrumOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.19;

import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {DataTypesPeerToPeer} from "../peer-to-peer/DataTypesPeerToPeer.sol";
import {DataTypesPeerToPool} from "../peer-to-pool/DataTypesPeerToPool.sol";
import {Errors} from "../Errors.sol";
import {IMysoTokenManager} from "../interfaces/IMysoTokenManager.sol";
import {ILenderVaultImpl} from "../peer-to-peer/interfaces/ILenderVaultImpl.sol";

contract TestnetTokenManagerArbitrumOracle is Ownable2Step, IMysoTokenManager {
using SafeERC20 for IERC20;
struct RewardInfo {
uint128 collThreshold;
uint128 mysoTokenMultiplier;
}
address internal constant MYSO_TOKEN =
0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1; // Dai testnet stand-in arbitrum test
mapping(address => RewardInfo) public rewardInfos;
bool public mysoRewardsActive;
uint256 public totalMysoLoanAmount;
uint256 public minMysoStakingRequirement;
address public mysoIOOVault;

event RewardInfoSet(
address indexed collToken,
uint128 collThreshold,
uint128 mysoTokenMultiplier
);
event MysoRewardsToggled(bool active);
event MinMysoStakingRequirementSet(uint256 minStakingRequirement);
event IOOVaultSet(address indexed mysoIOOVault);

// TODO: mapping oracleAddr -> vaultAddr -> tokenAddr -> loanAmount + flag for being turned tracked
// This will allow for other IOOs to use a custom oracle with auto-updating price for loan amount if desired

constructor() {
minMysoStakingRequirement = 10_000 * 1e18;
_transferOwnership(msg.sender);
}

function processP2PBorrow(
uint128[2] memory currProtocolFeeParams,
DataTypesPeerToPeer.BorrowTransferInstructions
calldata /*borrowInstructions*/,
DataTypesPeerToPeer.Loan calldata loan,
address lenderVault
) external returns (uint128[2] memory applicableProtocolFeeParams) {
applicableProtocolFeeParams = currProtocolFeeParams;
if (loan.loanToken == MYSO_TOKEN && lenderVault == mysoIOOVault) {
totalMysoLoanAmount += loan.initLoanAmount;
}
}

// solhint-disable no-empty-blocks
function processP2PCreateVault(
uint256 /*numRegisteredVaults*/,
address /*vaultCreator*/,
address /*newLenderVaultAddr*/
) external {}

// solhint-disable no-empty-blocks
function processP2PCreateWrappedTokenForERC721s(
address /*tokenCreator*/,
DataTypesPeerToPeer.WrappedERC721TokenInfo[]
calldata /*tokensToBeWrapped*/,
bytes calldata /*mysoTokenManagerData*/
) external {}

// solhint-disable no-empty-blocks
function processP2PCreateWrappedTokenForERC20s(
address /*tokenCreator*/,
DataTypesPeerToPeer.WrappedERC20TokenInfo[]
calldata /*tokensToBeWrapped*/,
bytes calldata /*mysoTokenManagerData*/
) external {}

// solhint-disable no-empty-blocks
function processP2PoolDeposit(
address /*fundingPool*/,
address /*depositor*/,
uint256 /*depositAmount*/,
uint256 /*depositLockupDuration*/,
uint256 /*transferFee*/
) external {}

// solhint-disable no-empty-blocks
function processP2PoolSubscribe(
address /*fundingPool*/,
address /*subscriber*/,
address /*loanProposal*/,
uint256 /*subscriptionAmount*/,
uint256 /*subscriptionLockupDuration*/,
uint256 /*totalSubscriptions*/,
DataTypesPeerToPool.LoanTerms calldata /*loanTerms*/
) external {}

// solhint-disable no-empty-blocks
function processP2PoolLoanFinalization(
address /*loanProposal*/,
address /*fundingPool*/,
address /*arranger*/,
address /*borrower*/,
uint256 /*grossLoanAmount*/,
bytes calldata /*mysoTokenManagerData*/
) external {}

// solhint-disable no-empty-blocks
function processP2PoolCreateLoanProposal(
address /*fundingPool*/,
address /*proposalCreator*/,
address /*collToken*/,
uint256 /*arrangerFee*/,
uint256 /*numLoanProposals*/
) external {}

function withdraw(address token, address to, uint256 amount) external {
_checkOwner();
SafeERC20.safeTransfer(IERC20(token), to, amount);
}

function setRewardInfo(
address collToken,
uint128 collThreshold,
uint128 mysoTokenMultiplier
) external {
_checkOwner();
RewardInfo storage rewardInfo = rewardInfos[collToken];
rewardInfo.collThreshold = collThreshold;
rewardInfo.mysoTokenMultiplier = mysoTokenMultiplier;
emit RewardInfoSet(collToken, collThreshold, mysoTokenMultiplier);
}

function toggleMysoRewards() external {
_checkOwner();
mysoRewardsActive = !mysoRewardsActive;
emit MysoRewardsToggled(mysoRewardsActive);
}

function setMinMysoStakingRequirement(
uint256 _minMysoStakingRequirement
) external {
_checkOwner();
minMysoStakingRequirement = _minMysoStakingRequirement;
emit MinMysoStakingRequirementSet(_minMysoStakingRequirement);
}

function setIOOVault(address _mysoIOOVault) external {
_checkOwner();
mysoIOOVault = _mysoIOOVault;
emit IOOVaultSet(_mysoIOOVault);
}

function transferOwnership(address _newOwnerProposal) public override {
_checkOwner();
if (
_newOwnerProposal == address(0) ||
_newOwnerProposal == address(this) ||
_newOwnerProposal == pendingOwner() ||
_newOwnerProposal == owner()
) {
revert Errors.InvalidNewOwnerProposal();
}
super._transferOwnership(_newOwnerProposal);
}
}

0 comments on commit c1d245a

Please sign in to comment.