Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Usda setup2 #110

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions contracts/interfaces/external/morpho/IMorphoOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.5.0;

/// @title IMorphoOracle
/// @notice Interface for the oracle contracts used within Morpho
interface IMorphoOracle {
function price() external view returns (uint256);
}
83 changes: 42 additions & 41 deletions contracts/transmuter/Storage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ enum OracleReadType {
RETH,
SFRXETH,
PYTH,
MAX
MAX,
MORPHO_ORACLE
}

enum OracleQuoteType {
Expand All @@ -65,70 +66,70 @@ enum WhitelistType {
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/

struct Permit2Details {
address to; // Address that will receive the funds
uint256 nonce; // Nonce of the transaction
bytes signature; // Permit signature of the user
address to; // Address that will receive the funds
uint256 nonce; // Nonce of the transaction
bytes signature; // Permit signature of the user
}

struct FacetCut {
address facetAddress; // Facet contract address
FacetCutAction action; // Can be add, remove or replace
bytes4[] functionSelectors; // Ex. bytes4(keccak256("transfer(address,uint256)"))
address facetAddress; // Facet contract address
FacetCutAction action; // Can be add, remove or replace
bytes4[] functionSelectors; // Ex. bytes4(keccak256("transfer(address,uint256)"))
}

struct Facet {
address facetAddress; // Facet contract address
bytes4[] functionSelectors; // Ex. bytes4(keccak256("transfer(address,uint256)"))
address facetAddress; // Facet contract address
bytes4[] functionSelectors; // Ex. bytes4(keccak256("transfer(address,uint256)"))
}

struct FacetInfo {
address facetAddress; // Facet contract address
uint16 selectorPosition; // Position in the list of all selectors
address facetAddress; // Facet contract address
uint16 selectorPosition; // Position in the list of all selectors
}

struct DiamondStorage {
bytes4[] selectors; // List of all available selectors
mapping(bytes4 => FacetInfo) selectorInfo; // Selector to (address, position in list)
IAccessControlManager accessControlManager; // Contract handling access control
bytes4[] selectors; // List of all available selectors
mapping(bytes4 => FacetInfo) selectorInfo; // Selector to (address, position in list)
IAccessControlManager accessControlManager; // Contract handling access control
}

struct ImplementationStorage {
address implementation; // Dummy implementation address for Etherscan usability
address implementation; // Dummy implementation address for Etherscan usability
}

struct ManagerStorage {
IERC20[] subCollaterals; // Subtokens handled by the manager or strategies
bytes config; // Additional configuration data
IERC20[] subCollaterals; // Subtokens handled by the manager or strategies
bytes config; // Additional configuration data
}

struct Collateral {
uint8 isManaged; // If the collateral is managed through external strategies
uint8 isMintLive; // If minting from this asset is unpaused
uint8 isBurnLive; // If burning to this asset is unpaused
uint8 decimals; // IERC20Metadata(collateral).decimals()
uint8 onlyWhitelisted; // If only whitelisted addresses can burn or redeem for this token
uint216 normalizedStables; // Normalized amount of stablecoins issued from this collateral
uint64[] xFeeMint; // Increasing exposures in [0,BASE_9[
int64[] yFeeMint; // Mint fees at the exposures specified in `xFeeMint`
uint64[] xFeeBurn; // Decreasing exposures in ]0,BASE_9]
int64[] yFeeBurn; // Burn fees at the exposures specified in `xFeeBurn`
bytes oracleConfig; // Data about the oracle used for the collateral
bytes whitelistData; // For whitelisted collateral, data used to verify whitelists
ManagerStorage managerData; // For managed collateral, data used to handle the strategies
uint8 isManaged; // If the collateral is managed through external strategies
uint8 isMintLive; // If minting from this asset is unpaused
uint8 isBurnLive; // If burning to this asset is unpaused
uint8 decimals; // IERC20Metadata(collateral).decimals()
uint8 onlyWhitelisted; // If only whitelisted addresses can burn or redeem for this token
uint216 normalizedStables; // Normalized amount of stablecoins issued from this collateral
uint64[] xFeeMint; // Increasing exposures in [0,BASE_9[
int64[] yFeeMint; // Mint fees at the exposures specified in `xFeeMint`
uint64[] xFeeBurn; // Decreasing exposures in ]0,BASE_9]
int64[] yFeeBurn; // Burn fees at the exposures specified in `xFeeBurn`
bytes oracleConfig; // Data about the oracle used for the collateral
bytes whitelistData; // For whitelisted collateral, data used to verify whitelists
ManagerStorage managerData; // For managed collateral, data used to handle the strategies
}

struct TransmuterStorage {
IAgToken agToken; // agToken handled by the system
uint8 isRedemptionLive; // If redemption is unpaused
uint8 statusReentrant; // If call is reentrant or not
uint128 normalizedStables; // Normalized amount of stablecoins issued by the system
uint128 normalizer; // To reconcile `normalizedStables` values with the actual amount
address[] collateralList; // List of collateral assets supported by the system
uint64[] xRedemptionCurve; // Increasing collateral ratios > 0
int64[] yRedemptionCurve; // Value of the redemption fees at `xRedemptionCurve`
mapping(address => Collateral) collaterals; // Maps a collateral asset to its parameters
mapping(address => uint256) isTrusted; // If an address is trusted to update the normalizer value
IAgToken agToken; // agToken handled by the system
uint8 isRedemptionLive; // If redemption is unpaused
uint8 statusReentrant; // If call is reentrant or not
uint128 normalizedStables; // Normalized amount of stablecoins issued by the system
uint128 normalizer; // To reconcile `normalizedStables` values with the actual amount
address[] collateralList; // List of collateral assets supported by the system
uint64[] xRedemptionCurve; // Increasing collateral ratios > 0
int64[] yRedemptionCurve; // Value of the redemption fees at `xRedemptionCurve`
mapping(address => Collateral) collaterals; // Maps a collateral asset to its parameters
mapping(address => uint256) isTrusted; // If an address is trusted to update the normalizer value
mapping(address => uint256) isSellerTrusted; // If an address is trusted to sell accruing reward tokens
mapping(WhitelistType => mapping(address => uint256)) isWhitelistedForType;
// Whether an address is whitelisted for a specific whitelist type
// Whether an address is whitelisted for a specific whitelist type
}
1 change: 0 additions & 1 deletion contracts/transmuter/configs/FakeGnosis.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ contract FakeGnosis {
address[] memory _collateralAddresses,
address[] memory _oracleAddresses
) external {
uint256 BPS = 1e14;
// Fee structure

uint64[] memory xMintFee = new uint64[](4);
Expand Down
34 changes: 15 additions & 19 deletions contracts/transmuter/libraries/LibOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity ^0.8.19;

import { ITransmuterOracle } from "interfaces/ITransmuterOracle.sol";
import { AggregatorV3Interface } from "interfaces/external/chainlink/AggregatorV3Interface.sol";
import { IMorphoOracle } from "interfaces/external/morpho/IMorphoOracle.sol";
import { IPyth, PythStructs } from "interfaces/external/pyth/IPyth.sol";

import { LibStorage as s } from "./LibStorage.sol";
Expand Down Expand Up @@ -33,25 +34,14 @@ library LibOracle {
ITransmuterOracle externalOracle = abi.decode(oracleData, (ITransmuterOracle));
return externalOracle.readRedemption();
} else {
// We consider the actual oracle value and not the processed one
// as it doesn't impact directly the redemption process
(oracleValue, ) = readSpotAndTarget(oracleType, targetType, oracleData, targetData, 0);
// We don't consider the mint firewall as `readRedemption` is only used to compute the collateral ratio
// `getCollateralRatio` is only used in `_quoteRedemptionCurve` and `accrue` on the savingsVest
// `_quoteRedemptionCurve` use the collateral ratio to compute the penalty factor. Artificially increase the
// oracle rate will just allow you to navigate through the penalty factor curve and when
// the collateral ratio > 100% the penalty factor curve is decreasing such that there is no incentives
// for upward manipulation
// `accrue` would be impacted by an inflated oracle value, but only governors can call this function
// We don't consider the burn firewall is less relevant for redemptions
// as there is already a surplus buffer to circumvent small deviations
return oracleValue;
}
}

/// @notice Reads the oracle value used during mint operations for an asset with `oracleConfig`
/// @dev For assets which do not rely on external oracles, this value is the minimum between the asset oracle
/// value and its target price
/// @dev For assets which do not rely on external oracles, this value is the minimum between the processed oracle
/// value for the asset and its target price
function readMint(bytes memory oracleConfig) internal view returns (uint256 oracleValue) {
(
OracleReadType oracleType,
Expand All @@ -70,7 +60,7 @@ library LibOracle {
oracleValue = _firewallMint(targetPrice, oracleValue, mintDeviation);
}

/// @notice Reads the oracle value that will be used for a burn operation for an asset with `oracleConfig`
/// @notice Reads the oracle value used for a burn operation for an asset with `oracleConfig`
/// @return oracleValue The actual oracle value obtained
/// @return ratio If `oracle value < target price`, the ratio between the oracle value and the target
/// price, otherwise `BASE_18`
Expand Down Expand Up @@ -142,7 +132,7 @@ library LibOracle {
) internal view returns (uint256 oracleValue, uint256 targetPrice) {
targetPrice = read(targetType, BASE_18, targetData);
oracleValue = read(oracleType, targetPrice, oracleData);
// Post process of the oracle value, it tolerates small deviation from target
// Post process of the oracle value: system may tolerate small deviations from target
oracleValue = _userOracleProtection(targetPrice, oracleValue, deviation);
}

Expand Down Expand Up @@ -197,6 +187,9 @@ library LibOracle {
} else if (readType == OracleReadType.MAX) {
(uint256 maxValue, , , ) = abi.decode(data, (uint256, uint96, uint96, uint32));
return maxValue;
} else if (readType == OracleReadType.MORPHO_ORACLE) {
(address contractAddress, uint256 normalizationFactor) = abi.decode(data, (address, uint256));
return IMorphoOracle(contractAddress).price() / normalizationFactor;
}
// If the `OracleReadType` is `EXTERNAL`, it means that this function is called to compute a
// `targetPrice` in which case the `baseValue` is returned here
Expand Down Expand Up @@ -256,15 +249,18 @@ library LibOracle {
}

/// @notice Firewall in case the oracle value reported is too high compared to the target
/// --> disregard the oracle value and return the target price
/// TODO we may want something continuous ans therefore set
/// `oracleValue = targetPrice * (BASE_18 + deviation) / BASE_18`
/// @dev This mint firewall is useful in the case of assets for which the `targetPrice` is theorically defined as
/// the maximum value ever observed for the oracle, but this maximum value has simply not been updated.
/// Typically, imagine a case where target is 100 and oracle is 101, in this setup, during a mint, because
/// target should be put at 101 but hasn't been modified, the system uses for a price the oracle value
function _firewallMint(uint256 targetPrice, uint256 oracleValue, uint256 deviation) private pure returns (uint256) {
if (targetPrice * (BASE_18 + deviation) < oracleValue * BASE_18) oracleValue = targetPrice;
return oracleValue;
}

/// @notice Firewall in case the oracle value reported is low compared to the target
/// @dev If the oracle value is slightly below its target, then no deviation is reported for the oracle and
/// the price of burning the stablecoin for other assets is not impacted
function _burnRatio(
uint256 targetPrice,
uint256 oracleValue,
Expand All @@ -275,7 +271,7 @@ library LibOracle {
return ratio;
}

/// @notice Firewall in case the oracle value reported is under a reasonable threshold to the target
/// @notice Firewall in case the oracle value reported is reasonably close to the target
/// --> disregard the oracle value and return the target price
function _userOracleProtection(
uint256 targetPrice,
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@
"solhint-plugin-prettier": "^0.0.5"
},
"dependencies": {
"@angleprotocol/sdk": "^0.34.5"
"@angleprotocol/sdk": "^0.36.13"
}
}
}
18 changes: 17 additions & 1 deletion scripts/Constants.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ address constant EURE = 0x3231Cb76718CDeF2155FC47b5286d82e6eDA273f;
address constant BC3M = 0x2F123cF3F37CE3328CC9B5b8415f9EC5109b45e7;
address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
address constant BERNX = 0x3f95AA88dDbB7D9D484aa3D482bf0a80009c52c9;
address constant STEAK_USDC = 0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB;
address constant BIB01 = 0xCA30c93B02514f86d5C86a6e375E3A330B435Fb5;

// EUROC
uint80 constant FIREWALL_MINT_EUROC = uint80(0);
Expand All @@ -39,7 +41,21 @@ uint80 constant FIREWALL_BURN_RATIO_ERNX = uint80(100 * BPS);
uint80 constant USER_PROTECTION_ERNX = uint80(0);
uint96 constant DEVIATION_THRESHOLD_ERNX = uint96(100 * BPS);

uint32 constant HEARTBEAT = uint32(1 days);
uint32 constant HEARTBEAT = uint32(7 days);

uint80 constant FIREWALL_MINT_USDC = uint80(0);
uint80 constant FIREWALL_BURN_RATIO_USDC = uint80(0);
uint80 constant USER_PROTECTION_USDC = uint80(5 * BPS);

uint80 constant FIREWALL_MINT_STEAK_USDC = uint80(50 * BPS);
uint80 constant FIREWALL_BURN_RATIO_STEAK_USDC = uint80(50 * BPS);
uint80 constant USER_PROTECTION_STEAK_USDC = uint80(0);
uint256 constant DEVIATION_THRESHOLD_STEAKUSDC = 50 * BPS;

uint80 constant FIREWALL_MINT_IB01 = uint80(70 * BPS);
uint80 constant FIREWALL_BURN_RATIO_IB01 = uint80(50 * BPS);
uint80 constant USER_PROTECTION_IB01 = uint80(0);
uint256 constant DEVIATION_THRESHOLD_IB01 = 50 * BPS;

/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
FACET ADDRESSES
Expand Down
Loading
Loading