Admin Privileges
Understanding how the owners of the Dolomite Margin protocol can effectuate changes is important for disclosing risk.
Primer
In the initial stages of the protocol, the admin rights are owned by a simple multi signature wallet (Gnosis Safe) that is stood up behind a delayed multi signature wallet (custom-made by the dYdX team). The Gnosis Safe is a 2/3 wallet. Meaning, 2 signers are needed out of the 3 owners to execute a transaction.
The delayed multi signature wallet is solely owned by the Gnosis Safe, which means the security and ownership of the delayed multi signature wallet falls back on to the Gnosis Safe. On the other hand, the time delay falls completely on the delayed multi signature wallet; whatever delay is set for the delayed multi signature wallet indiscriminately requires that all transactions sent (except certain whitelisted ones) to it wait the same delay before the transaction can be executed. Meaning, all admin transactions involving the DolomiteMargin
protocol require that secondsTimeLocked
amount of time must be waited before the transaction effectuates.
At the time of launch, the delay on the delayed multi signature wallet is 1 day (86,400 seconds). The intention is to raise it incrementally until the protocol is more battle-tested and ownership of the protocol becomes much more decentralized.
To check the current delay (in case these docs ever go out of sync!) you can convert the secondsTimeLocked
value from seconds to minutes/hours/days on Arbiscan.
Special Function Bypass
Certain functions are able to bypass the secondsTimeLocked
restriction and be executed immediately. The reason for having this is it allows the administrators to make quick changes in the event market conditions change whimsically, DolomiteMargin
is exploited, a bug is uncovered, or some other black swan event occurs. The following table showcases which functions can be bypassed.
Function Name | Method ID | Can Bypass Timelock? |
---|---|---|
|
| TRUE |
|
| TRUE |
|
| TRUE |
|
| TRUE |
Risk Limits
DolomiteMargin initializes an immutable struct called RiskLimits
upon deployment that defines that absolute maximum that certain parameters can ever be. Meaning, under no possible circumstance can these values change and under no possible circumstance can RiskParams
(the mutable counterpart of the RiskLimit
struct) be changed to a value that exceeds a value set in RiskLimits
. The following values are set in RiskLimits
:
uint64 marginRatioMax
The highest that the ratio can be for liquidating under-water accounts. Permanently set at
2000000000000000000
, which is equal to 200% collateralization.
uint64 liquidationSpreadMax
The highest that the liquidation rewards can be when a liquidator liquidates an account. Permanently set at
500000000000000000
, which is equal to a 50% reward.
uint64 earningsRateMax
The highest that the supply APR can be for a market, as a proportion of the borrow rate. Meaning, a rate of 100% (1e18) would give suppliers all the interest that borrowers are paying. A rate of 90% would give suppliers 90% of the interest that borrowers pay. Permanently set at
1000000000000000000
, which is equal to 100% of the borrow rate going to suppliers.
uint64 marginPremiumMax
The highest min margin ratio premium that can be applied to a particular market. Meaning, a value of 10% (1e17) would require borrowers to maintain an extra 10% collateral to maintain a healthy margin ratio. This value works by increasing the debt owed and decreasing the supply held for the particular market by this amount, by this the value. Permanently set at
2000000000000000000
, which equals 200%. Meaning, if this value were set for an individual market'sRiskParams
to2000000000000000000
, and the protocol's minimum collateralization is 115%, that particular market will require a min collateralization of 345% in maintain sufficient collateralization.
uint64 spreadPremiumMax
The highest liquidation reward that can be applied to a particular market. This percentage is applied in addition to the liquidation spread in
RiskParams
. This value has 18 decimals, meaning a value of 1e18 is 100%. It is applied to each market as follows:liquidationSpread * (1 + spreadPremium)
. This value is permanently set at2000000000000000000
, which equals 200%.
uint128 minBorrowedValueMax
The highest minimum borrowed value that can be set by the protocol (this is confusingly worded, we know!). This value is permanently set at
100000000000000000000
or $1e-16. Meaning, the minimum borrowed value can never be set beyond100000000000000000000 / 1e36
dollars.
To verify any of the above values, you can visit Arbiscan and click the Query
button for the function getRiskLimits
.
Risk Params
DolomiteMargin
uses a struct for storing global risk-values called RiskParams
which is changeable by the admin of the protocol (subject to any time delays, of course). Many of these values have a counterpart in RiskLimits
that enforces limitations on what these numbers can be changed to. These values include the following:
Decimal.D256 marginRatio
The required ratio of over-collateralization. This value is currently set at
150000000000000000
which corresponds with 115% collateralization. This value cannot be set belowliquidationSpread
nor abovemarginRatioMax
(which is permanently set at 200%) and has a theoretical lower limit of 0 (100% collateralization).
Decimal.D256 liquidationSpread
The liquidation reward paid from liquidated accounts to liquidators. This value is currently set at
50000000000000000
which corresponds with a 5% liquidation reward. This value cannot be set below 0% nor abovemarginRatio
orspreadPremiumMax
(which is permanently set at 200%).
Decimal.D256 earningsRate
The percentage of the borrower's interest fee that is passed to the suppliers. This value is currently set at
850000000000000000
which corresponds with 85%. The remaining 10% is paid to the protocol as a fee.
Monetary.Value minBorrowedValue
The minimum borrow value that an account may have. This value is measured in dollars and has 36 decimals of precision. This value is currently set at 0 ($0).
uint256 accountMaxNumberOfMarketsWithBalances
The maximum number of markets a user can have a non-zero balance for in a given account. Recall, an account is defined as a user's address, partitioned by an
index
. This value is currently set at 32. Meaning, a user can deposit 32 assets into each ofwallet-index[0]
,wallet-index[1]
...wallet-index[n]
, etc.
Market-Specific Risk Params
The following functions and parameters can be called or changed by the protocol's admin and are subject to the same, universal, time delays as well as any applicable limits defined above.
To verify any of these parameters for a particular market, you can first get the marketId
from the Markets section of the docs. Alternatively, you can visit the DolomiteMargin smart contract on Arbiscan and get the marketId
by calling the getMarketIdByTokenAddress
function, passing in the token address whose market ID you're seeking. Then, pass the marketId
into the getMarket
function or into individual market risk-oriented functions like:
getMarketIsClosing
getMarketIsRecyclable
getMarketPriceOracle
getMarketInterestSetter
getMarketMarginPremium
getMarketSpreadPremium
getMarketMaxWei
Add Market
This function allows a new market to be added to DolomiteMargin. Upon initialization, isRecyclable
(explanation below) and the token's address cannot be changed. Other values that are initialized and can be changed include isClosing
, priceOracle
, interestSetter
, marginPremium
, spreadPremium
, and maxWei
.
bool isClosing
This value defaults to
false
. Setting this value totrue
disallows any new borrows from occurring for that market. This value is checked after each interaction with DolomiteMargin settles, meaning funds can still be flash borrowed (as long as they're returned in full), once the interaction settles.
bool isRecyclable
This value cannot be changed after initialization of a new market. This value dictates whether a market is allowed to be removed altogether. This value exists mainly for the efficiency of the protocol, allowing market IDs to be reclaimed. The main use-case for this is for expirable markets, like if an option token is added that expires at a certain timestamp.
IPriceOracle priceOracle
The contract address of the price oracle for this market. Must conform to the
IPriceOracle
interface. The price this contract returns has36 - tokenDecimals
decimals. For example,1000000000000000000000000000000
equals $1.00 for the USDC market, because USDC has 6 decimals.
IInterestSetter interestSetter
Contract address of the interest setter for this market. Must conform to the
IInterestSetter
interface. This contract takes the utilization (the total amount borrowed and the total amount supplied) for a given market and determines the amount of interest paid by borrowers every second. Meaning, a value returned by this contract of325000000
is equivalent to1.02% APR
(325000000 * 86400 * 365
). The result has 18 decimals.
Decimal.D256 marginPremium
The multiplier to apply to the global
marginRatio
. This value works by increasing the debt owed and decreasing the supply held for the particular market by1 + marginPremium
. Meaning, amarginPremium
of 100000000000000000 (10%) and a marginRatio of100000000000000000
(110% collateralization) results in that particular market'smarginRatio
equalling110% * 1.1 == 121%
.
Decimal.D256 spreadPremium
The multiplier to apply to the global
liquidationSpread
. This value works by increasing theliquidationSpread
by (Decimal.one + spreadPremium
). Meaning, aspreadPremium
of100000000000000000
(10%) and aliquidationSpread
of50000000000000000
, results in that particular market'sliquidationSpread
equalling5% * 1.1 == 5.5%
.
Types.Wei maxWei
The maximum value that can be deposited into DolomiteMargin for this particular market. This allows the protocol to cap any additional risk that is inferred by allowing borrowing against assets with a lower market capitalization or assets with increased volatility. Setting this value to 0 is analogous to having no limit. This value can never be below 0. This value's number of decimals corresponds with the number of decimals this market's token has.
Other Admin Functions
Set Global Operator
What is an operator?
An operator is an external address that has the same permissions to manipulate an account within DolomiteMargin
as the owner of the account. Operators are simply addresses and therefore may either be externally-owned Ethereum accounts (EoAs) OR smart contracts. Operators are also able to act as AutoTrader contracts on behalf of the account owner if the operator is a smart contract and implements the IAutoTrader
interface.
What kinds of operators exist?
There are three kinds of operators that DolomiteMargin
supports:
Self operators - analogous to
msg.sender
or the userLocal operators - users approve each of them manually, like a token approval
Global operators - users do not need to approve them manually, they have permission automatically
Why do global operators exist?
Global operators exist to increase the UX of Dolomite for the users. Without them, users would be forced to manually approve a lot of local operator contracts to achieve a competitive and smooth UX.
This function allows the admin to approve or disapprove a smart contract that has the permission to be an operator for all accounts on DolomiteMargin.
A non-exhaustive list of global operators include the LiquidatorProxyV1 and DepositWithdrawalProxy, which exist to simplify the user experience or lower gas costs for Dolomite users by consuming less calldata.
With regard to the safety of user's funds, this function likely poses the largest risk on the system. If a buggy or malicious global operator is ever set, all of the users' funds could be at risk.
Therefore, all global operators must undergo extensive testing and review before being added to the system. Moreover, they should always be stuck behind a time delay to allow users to react to any new global operator contracts being added.
Set Auto Trader Special
This function sets or unsets an instance of IAutoTrader
from needing to be called by a global operator. This exists to ensure certain contracts, like Expiry
can eventually only be called by trusted Keepers on the Chainlink Oracle network.
Withdraw Excess Token
This function allows the admin to withdraw an ERC20 token for which there is an associated market. Only excess tokens can be withdrawn. The number of excess tokens is calculated by taking the current number of tokens held in DolomiteMargin, adding the number of tokens owed to DolomiteMargin by borrowers, and subtracting the number of tokens owed to suppliers by DolomiteMargin.
Withdraw Unsupported Tokens
This function allows the admin to withdraw an ERC20 token for which there is no associated market. This is analogous to rescuing tokens sent to the protocol by accident.
Last updated