// SPDX-License-Identifier: BUSL-1.1pragmasolidity >=0.5.0;import { ICbETH } from"interfaces/external/coinbase/ICbETH.sol";
import { ISfrxETH } from"interfaces/external/frax/ISfrxETH.sol";
import { IStETH } from"interfaces/external/lido/IStETH.sol";
import { IRETH } from"interfaces/external/rocketPool/IRETH.sol";
/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
STORAGE SLOTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////*//// @dev Storage position of `DiamondStorage` structure/// @dev Equals `keccak256("diamond.standard.diamond.storage") - 1`bytes32constant DIAMOND_STORAGE_POSITION =0xc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131b;
/// @dev Storage position of `TransmuterStorage` structure/// @dev Equals `keccak256("diamond.standard.transmuter.storage") - 1`bytes32constant TRANSMUTER_STORAGE_POSITION =0xc1f2f38dde3351ac0a64934139e816326caa800303a1235dc53707d0de05d8bd;
/// @dev Storage position of `ImplementationStorage` structure/// @dev Equals `keccak256("eip1967.proxy.implementation") - 1`bytes32constant IMPLEMENTATION_STORAGE_POSITION =0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MATHS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/uint256constant BASE_6 =1e6;
uint256constant BASE_8 =1e8;
uint256constant BASE_9 =1e9;
uint256constant BASE_12 =1e12;
uint256constant BPS =1e14;
uint256constant BASE_18 =1e18;
uint256constant HALF_BASE_27 =1e27/2;
uint256constant BASE_27 =1e27;
uint256constant BASE_36 =1e36;
uint256constant MAX_BURN_FEE =999_000_000;
uint256constant MAX_MINT_FEE = BASE_12 -1;
/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
REENTRANT
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/// The values being non-zero value makes deployment a bit more expensive,// but in exchange the refund on every call to nonReentrant will be lower in// amount. Since refunds are capped to a percentage of the total// transaction's gas, it is best to keep them low in cases like this one, to// increase the likelihood of the full refund coming into effect.uint8constant NOT_ENTERED =1;
uint8constant ENTERED =2;
/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
COMMON ADDRESSES
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/addressconstant PERMIT_2 =0x000000000022D473030F116dDEE9F6B43aC78BA3;
addressconstant ONE_INCH_ROUTER =0x1111111254EEB25477B68fb85Ed929f73A960582;
addressconstant AGEUR =0x1a7e4e63778B4f12a199C062f3eFdD288afCBce8;
ICbETH constant CBETH = ICbETH(0xBe9895146f7AF43049ca1c1AE358B0541Ea49704);
IRETH constant RETH = IRETH(0xae78736Cd615f374D3085123A210448E74Fc6393);
IStETH constant STETH = IStETH(0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84);
ISfrxETH constant SFRXETH = ISfrxETH(0xac3E018457B222d93114458476f3E3416Abbe38F);
Contract Source Code
File 2 of 13: DiamondProxy.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.19;import { LibDiamond } from"./libraries/LibDiamond.sol";
import { LibStorageass } from"./libraries/LibStorage.sol";
import"../utils/Errors.sol";
import"./Storage.sol";
/// @title DiamondProxy/// @author Angle Labs, Inc./// @notice Implementation of a Diamond Proxy/// @dev Reference: EIP-2535 Diamonds/// @dev Forked from https://github.com/mudgen/diamond-3/blob/master/contracts/Diamond.sol by mudgencontractDiamondProxy{
constructor(FacetCut[] memory _diamondCut, address _init, bytesmemory _calldata) payable{
LibDiamond.diamondCut(_diamondCut, _init, _calldata);
}
/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
FALLBACK
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////*//// @dev 1. Find the facet for the function that is called./// @dev 2. Delegate the execution to the found facet via `delegatecall`.fallback() externalpayable{
DiamondStorage storage ds = s.diamondStorage();
// Get facet from function selectoraddress facetAddress = ds.selectorInfo[msg.sig].facetAddress;
if (facetAddress ==address(0)) {
revert FunctionNotFound(msg.sig);
}
assembly {
// The pointer to the free memory slotlet ptr :=mload(0x40)
// Copy function signature and arguments from calldata at zero position into memory at pointer positioncalldatacopy(ptr, 0, calldatasize())
// Delegatecall method of the implementation contract returns 0 on errorlet result :=delegatecall(gas(), facetAddress, ptr, calldatasize(), 0, 0)
// Get the size of the last return datalet size :=returndatasize()
// Copy the size length of bytes from return data at zero position to pointer positionreturndatacopy(ptr, 0, size)
// Depending on the result valueswitch result
case0 {
// End execution and revert state changesrevert(ptr, size)
}
default {
// Return data with length of size at pointers positionreturn(ptr, size)
}
}
}
}
// SPDX-License-Identifier: GPL-3.0pragmasolidity >=0.5.0;/// @title IAccessControlManager/// @author Angle Labs, Inc.interfaceIAccessControlManager{
/// @notice Checks whether an address is governor of the Angle Protocol or not/// @param admin Address to check/// @return Whether the address has the `GOVERNOR_ROLE` or notfunctionisGovernor(address admin) externalviewreturns (bool);
/// @notice Checks whether an address is governor or a guardian of the Angle Protocol or not/// @param admin Address to check/// @return Whether the address has the `GUARDIAN_ROLE` or not/// @dev Governance should make sure when adding a governor to also give this governor the guardian/// role by calling the `addGovernor` functionfunctionisGovernorOrGuardian(address admin) externalviewreturns (bool);
}
Contract Source Code
File 5 of 13: IAgToken.sol
// SPDX-License-Identifier: GPL-3.0pragmasolidity >=0.5.0;import { IERC20 } from"oz/token/ERC20/IERC20.sol";
/// @title IAgToken/// @author Angle Labs, Inc./// @notice Interface for the stablecoins `AgToken` contractsinterfaceIAgTokenisIERC20{
/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MINTER ROLE ONLY FUNCTIONS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////*//// @notice Lets a whitelisted contract mint agTokens/// @param account Address to mint to/// @param amount Amount to mintfunctionmint(address account, uint256 amount) external;
/// @notice Burns `amount` tokens from a `burner` address after being asked to by `sender`/// @param amount Amount of tokens to burn/// @param burner Address to burn from/// @param sender Address which requested the burn from `burner`/// @dev This method is to be called by a contract with the minter right after being requested/// to do so by a `sender` address willing to burn tokens from another `burner` address/// @dev The method checks the allowance between the `sender` and the `burner`functionburnFrom(uint256 amount, address burner, address sender) external;
/// @notice Burns `amount` tokens from a `burner` address/// @param amount Amount of tokens to burn/// @param burner Address to burn from/// @dev This method is to be called by a contract with a minter right on the AgToken after being/// requested to do so by an address willing to burn tokens from its addressfunctionburnSelf(uint256 amount, address burner) external;
/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TREASURY ONLY FUNCTIONS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////*//// @notice Adds a minter in the contract/// @param minter Minter address to add/// @dev Zero address checks are performed directly in the `Treasury` contractfunctionaddMinter(address minter) external;
/// @notice Removes a minter from the contract/// @param minter Minter address to remove/// @dev This function can also be called by a minter wishing to revoke itselffunctionremoveMinter(address minter) external;
/// @notice Sets a new treasury contract/// @param _treasury New treasury addressfunctionsetTreasury(address _treasury) external;
/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
EXTERNAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////*//// @notice Checks whether an address has the right to mint agTokens/// @param minter Address for which the minting right should be checked/// @return Whether the address has the right to mint agTokens or notfunctionisMinter(address minter) externalviewreturns (bool);
/// @notice Amount of decimals of the stablecoinfunctiondecimals() externalviewreturns (uint8);
}
Contract Source Code
File 6 of 13: ICbETH.sol
// SPDX-License-Identifier: GPL-3.0pragmasolidity >=0.5.0;/// @title ICbETH/// @notice Interface for the `cbETH` contractinterfaceICbETH{
functionexchangeRate() externalviewreturns (uint256);
}
Contract Source Code
File 7 of 13: IERC20.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/interfaceIERC20{
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/eventTransfer(addressindexedfrom, addressindexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/eventApproval(addressindexed owner, addressindexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/functiontotalSupply() externalviewreturns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/functionbalanceOf(address account) externalviewreturns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransfer(address to, uint256 amount) externalreturns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/functionallowance(address owner, address spender) externalviewreturns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/functionapprove(address spender, uint256 amount) externalreturns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransferFrom(addressfrom,
address to,
uint256 amount
) externalreturns (bool);
}
Contract Source Code
File 8 of 13: IRETH.sol
// SPDX-License-Identifier: GPL-3.0pragmasolidity >=0.5.0;/// @title IRETH/// @notice Interface for the `rETH` contractinterfaceIRETH{
functiongetExchangeRate() externalviewreturns (uint256);
}
Contract Source Code
File 9 of 13: ISfrxETH.sol
// SPDX-License-Identifier: GPL-3.0pragmasolidity >=0.5.0;/// @title ISfrxETH/// @notice Interface for the `sfrxETH` contractinterfaceISfrxETH{
functionpricePerShare() externalviewreturns (uint256);
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;import { LibStorageass } from"./LibStorage.sol";
import"../../utils/Errors.sol";
import"../Storage.sol";
/// @title LibDiamond/// @author Angle Labs, Inc./// @notice Helper library to deal with diamond proxies./// @dev Reference: EIP-2535 Diamonds/// @dev Forked from https://github.com/mudgen/diamond-3/blob/master/contracts/libraries/LibDiamond.sol by mudgenlibraryLibDiamond{
eventDiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);
/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
INTERNAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////*//// @notice Checks whether `admin` has the governor rolefunctionisGovernor(address admin) internalviewreturns (bool) {
return s.diamondStorage().accessControlManager.isGovernor(admin);
}
/// @notice Checks whether `admin` has the guardian rolefunctionisGovernorOrGuardian(address admin) internalviewreturns (bool) {
return s.diamondStorage().accessControlManager.isGovernorOrGuardian(admin);
}
/// @notice Internal function version of `diamondCut`functiondiamondCut(FacetCut[] memory _diamondCut, address _init, bytesmemory _calldata) internal{
uint256 diamondCutLength = _diamondCut.length;
for (uint256 facetIndex; facetIndex < diamondCutLength; facetIndex++) {
bytes4[] memory functionSelectors = _diamondCut[facetIndex].functionSelectors;
address facetAddress = _diamondCut[facetIndex].facetAddress;
if (functionSelectors.length==0) {
revert NoSelectorsProvidedForFacetForCut(facetAddress);
}
FacetCutAction action = _diamondCut[facetIndex].action;
if (action == FacetCutAction.Add) {
_addFunctions(facetAddress, functionSelectors);
} elseif (action == FacetCutAction.Replace) {
_replaceFunctions(facetAddress, functionSelectors);
} elseif (action == FacetCutAction.Remove) {
_removeFunctions(facetAddress, functionSelectors);
}
}
emit DiamondCut(_diamondCut, _init, _calldata);
_initializeDiamondCut(_init, _calldata);
}
/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
PRIVATE FUNCTIONS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////*//// @notice Does a delegate call on `_init` with `_calldata`function_initializeDiamondCut(address _init, bytesmemory _calldata) private{
if (_init ==address(0)) {
return;
}
_enforceHasContractCode(_init);
(bool success, bytesmemoryerror) = _init.delegatecall(_calldata);
if (!success) {
if (error.length>0) {
assembly {
let returndata_size :=mload(error)
revert(add(32, error), returndata_size)
}
} else {
revert InitializationFunctionReverted(_init, _calldata);
}
}
}
/// @notice Adds a new function to the diamond proxy/// @dev Reverts if selectors are already existingfunction_addFunctions(address _facetAddress, bytes4[] memory _functionSelectors) private{
if (_facetAddress ==address(0)) {
revert CannotAddSelectorsToZeroAddress(_functionSelectors);
}
DiamondStorage storage ds = s.diamondStorage();
uint16 selectorCount =uint16(ds.selectors.length);
_enforceHasContractCode(_facetAddress);
uint256 functionSelectorsLength = _functionSelectors.length;
for (uint256 selectorIndex; selectorIndex < functionSelectorsLength; selectorIndex++) {
bytes4 selector = _functionSelectors[selectorIndex];
address oldFacetAddress = ds.selectorInfo[selector].facetAddress;
if (oldFacetAddress !=address(0)) {
revert CannotAddFunctionToDiamondThatAlreadyExists(selector);
}
ds.selectorInfo[selector] = FacetInfo(_facetAddress, selectorCount);
ds.selectors.push(selector);
selectorCount++;
}
}
/// @notice Upgrades a function in the diamond proxy/// @dev Reverts if selectors do not already existfunction_replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) private{
DiamondStorage storage ds = s.diamondStorage();
if (_facetAddress ==address(0)) {
revert CannotReplaceFunctionsFromFacetWithZeroAddress(_functionSelectors);
}
_enforceHasContractCode(_facetAddress);
uint256 functionSelectorsLength = _functionSelectors.length;
for (uint256 selectorIndex; selectorIndex < functionSelectorsLength; selectorIndex++) {
bytes4 selector = _functionSelectors[selectorIndex];
address oldFacetAddress = ds.selectorInfo[selector].facetAddress;
// Can't replace immutable functions -- functions defined directly in the diamond in this caseif (oldFacetAddress ==address(this)) {
revert CannotReplaceImmutableFunction(selector);
}
if (oldFacetAddress == _facetAddress) {
revert CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(selector);
}
if (oldFacetAddress ==address(0)) {
revert CannotReplaceFunctionThatDoesNotExists(selector);
}
// Replace old facet address
ds.selectorInfo[selector].facetAddress = _facetAddress;
}
}
/// @notice Removes a function in the diamond proxy/// @dev Reverts if selectors do not already existfunction_removeFunctions(address _facetAddress, bytes4[] memory _functionSelectors) private{
DiamondStorage storage ds = s.diamondStorage();
uint256 selectorCount = ds.selectors.length;
if (_facetAddress !=address(0)) {
revert RemoveFacetAddressMustBeZeroAddress(_facetAddress);
}
uint256 functionSelectorsLength = _functionSelectors.length;
for (uint256 selectorIndex; selectorIndex < functionSelectorsLength; selectorIndex++) {
bytes4 selector = _functionSelectors[selectorIndex];
FacetInfo memory oldFacetAddressAndSelectorPosition = ds.selectorInfo[selector];
if (oldFacetAddressAndSelectorPosition.facetAddress ==address(0)) {
revert CannotRemoveFunctionThatDoesNotExist(selector);
}
// Can't remove immutable functions -- functions defined directly in the diamondif (oldFacetAddressAndSelectorPosition.facetAddress ==address(this)) {
revert CannotRemoveImmutableFunction(selector);
}
// Replace selector with last selector
selectorCount--;
if (oldFacetAddressAndSelectorPosition.selectorPosition != selectorCount) {
bytes4 lastSelector = ds.selectors[selectorCount];
ds.selectors[oldFacetAddressAndSelectorPosition.selectorPosition] = lastSelector;
ds.selectorInfo[lastSelector].selectorPosition = oldFacetAddressAndSelectorPosition.selectorPosition;
}
// Delete last selector
ds.selectors.pop();
delete ds.selectorInfo[selector];
}
}
/// @notice Checks that an address has a non void bytecodefunction_enforceHasContractCode(address _contract) privateview{
uint256 contractSize;
assembly {
contractSize :=extcodesize(_contract)
}
if (contractSize ==0) {
revert ContractHasNoCode();
}
}
}
Contract Source Code
File 12 of 13: LibStorage.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.19;import"../../utils/Constants.sol";
import { DiamondStorage, ImplementationStorage, TransmuterStorage } from"../Storage.sol";
/// @title LibStorage/// @author Angle Labs, Inc.libraryLibStorage{
/// @notice Returns the storage struct stored at the `DIAMOND_STORAGE_POSITION` slot/// @dev This struct handles the logic of the different facets used in the diamond proxyfunctiondiamondStorage() internalpurereturns (DiamondStorage storage ds) {
bytes32 position = DIAMOND_STORAGE_POSITION;
assembly {
ds.slot:= position
}
}
/// @notice Returns the storage struct stored at the `TRANSMUTER_STORAGE_POSITION` slot/// @dev This struct handles the particular logic of the Transmuter systemfunctiontransmuterStorage() internalpurereturns (TransmuterStorage storage ts) {
bytes32 position = TRANSMUTER_STORAGE_POSITION;
assembly {
ts.slot:= position
}
}
/// @notice Returns the storage struct stored at the `IMPLEMENTATION_STORAGE_POSITION` slot/// @dev This struct handles the logic for making the contract easily usable on EtherscanfunctionimplementationStorage() internalpurereturns (ImplementationStorage storage ims) {
bytes32 position = IMPLEMENTATION_STORAGE_POSITION;
assembly {
ims.slot:= position
}
}
}
Contract Source Code
File 13 of 13: Storage.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.19;import { IERC20 } from"oz/token/ERC20/IERC20.sol";
import { IAccessControlManager } from"interfaces/IAccessControlManager.sol";
import { IAgToken } from"interfaces/IAgToken.sol";
/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ENUMS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/enumFacetCutAction {
Add,
Replace,
Remove
}
enumManagerType {
EXTERNAL
}
enumActionType {
Mint,
Burn,
Redeem
}
enumTrustedType {
Updater,
Seller
}
enumQuoteType {
MintExactInput,
MintExactOutput,
BurnExactInput,
BurnExactOutput
}
enumOracleReadType {
CHAINLINK_FEEDS,
EXTERNAL,
NO_ORACLE,
STABLE,
WSTETH,
CBETH,
RETH,
SFRXETH,
PYTH,
MAX,
MORPHO_ORACLE
}
enumOracleQuoteType {
UNIT,
TARGET
}
enumWhitelistType {
BACKED
}
/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
STRUCTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/structPermit2Details {
address to; // Address that will receive the fundsuint256 nonce; // Nonce of the transactionbytes signature; // Permit signature of the user
}
structFacetCut {
address facetAddress; // Facet contract address
FacetCutAction action; // Can be add, remove or replacebytes4[] functionSelectors; // Ex. bytes4(keccak256("transfer(address,uint256)"))
}
structFacet {
address facetAddress; // Facet contract addressbytes4[] functionSelectors; // Ex. bytes4(keccak256("transfer(address,uint256)"))
}
structFacetInfo {
address facetAddress; // Facet contract addressuint16 selectorPosition; // Position in the list of all selectors
}
structDiamondStorage {
bytes4[] selectors; // List of all available selectorsmapping(bytes4=> FacetInfo) selectorInfo; // Selector to (address, position in list)
IAccessControlManager accessControlManager; // Contract handling access control
}
structImplementationStorage {
address implementation; // Dummy implementation address for Etherscan usability
}
structManagerStorage {
IERC20[] subCollaterals; // Subtokens handled by the manager or strategiesbytes config; // Additional configuration data
}
structCollateral {
uint8 isManaged; // If the collateral is managed through external strategiesuint8 isMintLive; // If minting from this asset is unpauseduint8 isBurnLive; // If burning to this asset is unpauseduint8 decimals; // IERC20Metadata(collateral).decimals()uint8 onlyWhitelisted; // If only whitelisted addresses can burn or redeem for this tokenuint216 normalizedStables; // Normalized amount of stablecoins issued from this collateraluint64[] 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 collateralbytes whitelistData; // For whitelisted collateral, data used to verify whitelists
ManagerStorage managerData; // For managed collateral, data used to handle the strategiesuint256 stablecoinCap; // Cap on the amount of stablecoins that can be issued from this collateral
}
structTransmuterStorage {
IAgToken agToken; // agToken handled by the systemuint8 isRedemptionLive; // If redemption is unpauseduint8 statusReentrant; // If call is reentrant or notuint128 normalizedStables; // Normalized amount of stablecoins issued by the systemuint128 normalizer; // To reconcile `normalizedStables` values with the actual amountaddress[] collateralList; // List of collateral assets supported by the systemuint64[] xRedemptionCurve; // Increasing collateral ratios > 0int64[] yRedemptionCurve; // Value of the redemption fees at `xRedemptionCurve`mapping(address=> Collateral) collaterals; // Maps a collateral asset to its parametersmapping(address=>uint256) isTrusted; // If an address is trusted to update the normalizer valuemapping(address=>uint256) isSellerTrusted; // If an address is trusted to sell accruing reward tokens or to run keeper jobs on oraclesmapping(WhitelistType =>mapping(address=>uint256)) isWhitelistedForType;
// Whether an address is whitelisted for a specific whitelist type
}