-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
388 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,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); | ||
} |
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,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
198
contracts/peer-to-peer/oracles/custom/MysoArbitrumUsdOracle.sol
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,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 | ||
); | ||
} | ||
} |
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,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); | ||
} | ||
} |