Dolomite Margin - Glossary
To understand how Dolomite Margin works, it's essential to understand the following glossary of terms, which makes up its architecture.
Accounts
DolomiteMargin is Account based. Each Account is referenced by its owner wallet address and an account number unique to that owner address. Accounts have balances on each asset supported by DolomiteMargin, which can be either positive (indicating a net supply of the asset) or negative (indicating a net borrow of an asset). Accounts must maintain a certain level of collateralization, or they will be liquidated.
Example
Wallet address 0x6b5Bb4E60821eCE7363CaFf836Be1A4f9e3559B3
has the following balances in its account number 123456
:
ETH: 1,000
DAI: -10,000
USDC: -5,000
This account is borrowing 10,000 DAI and 5,000 USDC. These borrows are collateralized by 1,000 ETH. The account will earn interest on ETH and pay interest on DAI and USDC.
Markets
Please see the Markets page of the Gitbook.
Interest
Interest rates in DolomiteMargin are dynamic and set per Market. Each interest rate is automatically and algorithmically set based on the ratio of (total borrow) / (total supply)
of that Market. Account balances either continuously earn (if positive) or pay (if negative) interest.
Interest is earned / paid continuously (down to the second). Rates on the protocol are denominated in interest paid per second, in APR.
value
: Auint256
integer that represents the interest per second, with 18 decimals of precision. Meaning a value of3,170,979,198
equals ~10% APR. This is exemplified by the math below:10%
or100000000000000000
=100000000000000000 / 365 days per year / 86400 seconds per day
Accounting - Wei & Par
There are two types of balances amounts on DolomiteMargin: Wei and Par.
Wei
Wei refers to the actual token amount of an asset held in or owed by an account. Wei amounts are constantly changing as interest accrues on the balance. For example, if Bob deposits 10 DAI to a DolomiteMargin Account, its Wei balance would initially be 10. The balance would start increasing every second as Bob started earning interest on his DAI.
Likely, most times you will want to use Wei balances when reading data from DolomiteMargin.
sign
: A boolean flag that indicates whether the value is positive (true
) or negative (false
). This is relevant for tracking whether a value represents a net deposit (positive) or a borrowed amount (negative).value
: Auint256
integer that holds the magnitude of the value in Wei. This field captures the actual amount of Wei being represented. The number of decimals corresponds with the corresponding token's number of decimals
Par
Par refers to an interest adjusted amount that is static and does not change on the protocol. These are the balances that are actually stored on the protocol smart contracts. The protocol uses the current market index (see below) to transform Par to Wei values.
sign
: A boolean flag that indicates whether the value is positive (true
) or negative (false
). This is relevant for tracking whether a value represents a net deposit (positive) or a borrowed amount (negative).value
: Auint256
integer that holds the magnitude of the value in Par. This field captures the actual amount of Par being represented. The number of decimals corresponds with the corresponding token's number of decimals
Amounts
Amounts in DolomiteMargin are denominated by 3 things:
value
- the numerical value of the Amountreference
- one of:AmountReference.Delta
Indicates an amount relative to the existing balanceAmountReference.Target
Indicates an absolute amount
denomination
- one of:AmountDenomination.Wei
Indicates the amount is denominated in the actual units of the token being transferred ( See Wei)AmountDenomination.Par
Indicates the amount is denominated in "principal". DolomiteMargin uses these types of amounts in its internal accounting, and they do not change over time (See Par)
A very important thing to note is that amounts are always relative to how the balance of the Account being Operated on will change, not the amount of the Action occurring. So, for example you'd say withdraw(-10)
, because when you Withdraw, the balance of the Account will decrease. Attempting to do withdraw(+10)
will cause DolomiteMargin to throw an error.
Index
Each Market has a global borrow index and supply index. These indexes are used to transform Par <-> Wei values using the formula:
and
Indexes start at 1 upon the addition of the Market to the protocol. They increase based on how much interest has accrued for that asset. For example upon adding USDC both the borrow index and supply index for USDC were 1. Say over the next month 2% interest accrues to borrowers and 1% interest accrues to lenders (based on the interest rates and time that has passed). After this, the supply index will be 1.01, and the borrow index will be 1.02. These indexes will keep increasing based on interest accrued, forever.
Example
Alice deposits 10 DAI to the protocol (10 DAI in Wei). The supply index on DAI is currently 2. Using Supply Par Balance = (Supply Wei Balance) / (Supply Market Index) = 10 / 2 = 5
, the protocol credits 5 Par balance to Alice's account.
Later, interest has accrued for DAI on the protocol, and now the supply index for DAI is 3. Now, Alice goes to withdraw her DAI. Her DAI Par balance is still 5 (Par does not change over time). Now the protocol calculates Supply Wei Balance = (Supply Par Balance) * (Supply Market Index) = 5 * 3 = 15
, and sends Alice 15 DAI.
borrow
: Auint96
integer representing the scaled index value for borrowed assets in the market. This value reflects the current state of borrowing activity within the market.supply
: Auint96
integer representing the scaled index value for supplied assets in the market. This value reflects the current state of asset supply within the market.lastUpdate
: Auint32
integer representing the timestamp of the last update to the interest index. This timestamp indicates when the index was last modified or recalculated.
Operators
Operators are entities that perform actions on behalf of an account. They come in three forms:
The calling address (
msg.sender
) which interacts withDolomiteMargin
A local operator, which is set when an Account calls
setOperators(...)
on DolomiteMargin. This is similar to setting a token approval, where an Account specifies which contracts (operators) can interact on their behalf.A global operator, which is set by the owner of
DolomiteMargin
to automatically allow a contract to interact on behalf of any account onDolomiteMargin
. Global operators generally exist for two reasons:To provide liquidation smart contracts for underwater accounts
To offer a smoother UX, since less set up needs to be taken per-user to enable certain features on the platform.
Actions
All state changes to an account happen through Actions. Actions can modify the balances of 1 or more Accounts. There is no such thing as a "Borrow" action on DolomiteMargin. Rather, actions can automatically borrow funds if Account balances decrease a user's balance below 0. The following Actions are supported by DolomiteMargin:
Deposit
Deposit funds into an Account. Funds are moved from the sender or an approved address to DolomiteMargin, and the Account's balance is incremented.
amount
: An instance of theTypes.AssetAmount
struct, which represents the amount of assets to be deposited. If usingDelta
the amount must be positive.account
: An instance of theAccount.Info
struct that identifies the account to which the deposit will be made. This includes the account owner's address and a unique account number. Thesender
must be an approved operator ofaccount.owner
.market
: Auint256
integer representing the ID of the market into which the deposit will occur. This market ID specifies the particular asset market to interact with.from
: The address from which the assets for the deposit will be sourced. A token approval must be made from this address to DolomiteMargin
Withdraw
Withdraw funds from an Account. Funds are sent from DolomiteMargin to a specified address and the Account's balance is decremented.
amount
: An instance of theTypes.AssetAmount
struct, which represents the amount of assets to be withdrawn. If usingDelta
the amount must be negative.account
: An instance of theAccount.Info
struct that identifies the account from which the withdrawal will be performed. This includes the account owner's address and a unique account number. Thesender
must be an approved operator ofaccount.owner
.market
: Auint256
integer representing the ID of the market from which the withdrawal will occur. This market ID specifies the particular asset market to interact with.to
: The address to which the withdrawn assets will be transferred.
Transfer
Transfer funds internally between two DolomiteMargin accounts. This action can only be invoked by an approved operator for both accounts.
amount
: An instance of theTypes.AssetAmount
struct, which represents the amount of assets to be transferred. This includes information about the value and whether it is positive (transfer from accountOne to accountTwo) or negative (transfer from accountTwo to accountOne).accountOne
: An instance of theAccount.Info
struct that identifies the source account from which the assets will be transferred. This includes the account owner's address and a unique account number. Thesender
must be an approved operator ofaccountOne.owner
.accountTwo
: An instance of theAccount.Info
struct that identifies the destination account to which the assets will be transferred. This includes the account owner's address and a unique account number. Thesender
must be an approved operator ofaccountTwo.owner
.market
: Auint256
integer representing the ID of the market in which the transfer will occur. This market ID specifies the particular asset market in which the transfer will take place.
Buy
Buy an asset on a decentralized exchange using another asset. Uses an Exchange Wrapper to interact with different decentralized exchanges. Causes the bought asset's balance to go up, and the asset used to do the buy's balance to go down. Example: Buy 1 WETH on Uniswap using DAI.
This action is analogous to swapTokensForExactTokens
on Uniswap. To do swapExactTokensForTokens
, refer to Sell, below.
There are no fees charged by Dolomite for performing this action. Performing a Buy
action that reduces the user's balance to less than 0 (therefore making the Buy
a Flash Loan) do not incur any Dolomite-specific fees. Users are still responsible for paying the fees charged by the external decentralized exchange they are using.
amount
: An instance of theTypes.AssetAmount
struct, representing the amount of the asset that the buyer wants to exchange. This amount must resolve to a positive number.account
: An instance of theAccount.Info
struct, identifying the account initiating the Buy Action. This includes the account owner's address and a unique account number. Thesender
must be an approved operator ofaccount.owner
.makerMarket
: Auint256
integer indicating the market ID of theoutputMarket
.takerMarket
: Auint256
integer representing the market ID of theinputMarket
.exchangeWrapper
: The address of the exchange wrapper contract that facilitates the trade execution. This is the contract responsible for interacting with the external protocol to execute the trade. Must conform to theIExchangeWrapper
interface.orderData
: Additional data that might be needed for the trade execution. This can include order parameters specific to the exchange being used.
Sell
Sell an asset on a decentralized exchange for another asset. Uses an instance of IExchangeWrapper to interact with different decentralized exchanges. Causes the sold asset's balance to go down, and the received assets balance to go up. Example: Sell 1 WETH on Uniswap for DAI.
This action is analogous to swapExactTokensForTokens
on Uniswap.
There are no fees charged by Dolomite for performing this action. Performing a Sell
action that reduces the user's balance to less than 0 (therefore making the Sell
a Flash Loan) do not incur any Dolomite-specific fees. Users are still responsible for paying the fees charged by the external decentralized exchange they are using.
amount
: An instance of theTypes.AssetAmount
struct, representing the amount of the asset that the buyer wants to exchange. This amount must resolve to a negative number.account
: An instance of theAccount.Info
struct, identifying the account initiating the Sell Action. This includes the account owner's address and a unique account number. Thesender
must be an approved operator ofaccount.owner
.takerMarket
: Auint256
integer indicating the market ID of theinutMarket
.makerMarket
: Auint256
integer representing the market ID of theoutputMarket
.exchangeWrapper
: The address of the exchange wrapper contract that facilitates the trade execution. This is the contract responsible for interacting with the external protocol to execute the trade. Must conform to theIExchangeWrapper
interface.orderData
: Additional data that might be needed for the trade execution. This can include order parameters specific to the exchange being used.
Trade
Trade assets with another account on DolomiteMargin internally. No actual tokens are moved, but Account balances are updated. Uses the IAutoTrader
interface, which allows a smart contract to be specified which is called to determine the price of the trade.
The fees charged by Dolomite for performing this action depend on the implementation of the specific IAutoTrader
contract. For example, the simple x * y = k
AMM pools used by Dolomite charge a 0.3% fee for trades, which is factored into the trade price. If the instance of IAutoTrader is set to special
on Dolomite Margin, then the instance of IAutoTrader can only be interacted with via a Global Operator.
amount
: An instance of theTypes.AssetAmount
struct, indicating the amount of the asset being traded. Can resolve to a positive or negative number. If usingAssetReference.Target
, it is calculated using the balances of themakerAccount
.takerAccount
: An instance of theAccount.Info
struct, identifying the account that is initiating the trade as the taker. This includes the account owner's address and a unique account number. Thesender
must be an approved operator oftakerAccount.owner
.makerAccount
: An instance of theAccount.Info
struct, identifying the account that is providing the assets for the trade as the maker. This includes the account owner's address and a unique account number. TheautoTrader
must be an approved operator ofmakerAccount.owner
.inputMarket
: Auint256
integer representing the market ID of the asset being given as input to the trade. This specifies the market where the taker's asset is listed.outputMarket
: Auint256
integer indicating the market ID of the asset to be received as output from the trade. This specifies the market where the maker's asset is listed.autoTrader
: The address of the auto-trading contract responsible for executing the trade between the taker and maker accounts. Must conform to theIAutoTrader
interface.tradeData
: Additional data that might be required for the trade execution. This can include parameters specific to the auto-trading mechanism being used.
Call
Calls a function specified by the ICallee
interface through the context of an Account. Does not modify Account balances. An example of how this can be used is for setting expiration on the Expiry
contract.
account
: An instance of theAccount.Info
struct, identifying the account that is initiating the call action. This includes the account owner's address and a unique account number. Thesender
to DolomiteMargin must be an approved operator ofaccount.owner
.callee
: The address of the smart contract that the call action will be directed to. This contract is the recipient of the function call. Must implement theICallee
interface.data
: Additional data that is required for the function call. This can include any arbitrary data needed to execute the call.
Liquidate
Liquidates an undercollateralized Account. Operates on two Accounts: the liquidating Account, and the undercollateralized Account. Does not transfer any tokens, but just internally updates balances of accounts. This action can only be invoked on DolomiteMargin
through a Global Operator
. Liquidates at the price specified by the oracle. Example:
Starting Account Balances:
Liquidating Account (L): +100 DAI Undercollateralized Account (U): -1 ETH, +140 DAI ETH oracle price: $125 DAI oracle price: $1 Liquidation spread: 5%
The liquidate action causes 1 ETH to be transferred from L -> U, and 1 ETH * (($125/ETH) / ($1/DAI)) * 1.05 = 131.25 DAI
to be transferred from U -> L. After the liquidation the balances will be:
Liquidating Account (L): +231.25 DAI, -1 ETH Undercollateralized Account (U): +8.75 DAI
amount
: An instance of theTypes.AssetAmount
struct, representing the amount ofowedMarket
that will be repaid onliquidAccount
. This amount must resolve to a positive number.solidAccount
: An instance of theAccount.Info
struct, identifying the account that is in a healthy (solvent) state and initiating the liquidation action.liquidAccount
: An instance of theAccount.Info
struct, identifying the account that is undercollateralized and subject to being liquidated.owedMarket
: The market ID that corresponds to the market from which the undercollateralized account owes tokens.heldMarket
: The market ID that corresponds to the market in which the solvent account holds tokens that will be used as collateral.
Vaporize
Pulls extra funds from the insurance fund to re-collateralize an underwater account with only negative balances.
amount
: An instance of theTypes.AssetAmount
struct, representing the amountowedMarket
that will be repaid forvaporAccount
. The amount must resolve to a positive number.solidAccount
: An instance of theAccount.Info
struct, identifying the account that is in a healthy (solvent) state and initiating the vaporization action. Thesender
must be an operator ofsolidAccount.owner
.vaporAccount
: An instance of theAccount.Info
struct, identifying the account that has bad debt and is being vaporized.owedMarket
: The market ID that corresponds to the market from which the undercollateralized account owes tokens.heldMarket
: The market ID that corresponds to the market in which the solvent account holds tokens that will be used as collateral.
Operations
Every state changing action to the protocol occurs through an Operation. Operations contain a series of Actions that each operate on an Account.
Multiple Actions can be strung together in an Operation to achieve more complex interactions with the protocol. For example, taking a short ETH position on DolomiteMargin could be achieved with an Operation containing the following Actions:
Importantly collateralization is only checked at the end of an operation, so accounts are allowed to be transiently undercollateralized in the scope of one Operation. This allows for Operations like a Sell -> Deposit, where an asset is first sold, and the collateral is locked up as the second Action in the Operation.
The operate
function in DolomiteMargin serves as the primary entry point for users and contracts to manage accounts and execute various actions within the DolomiteMargin protocol. This function enables the execution of a sequence of actions on one or more accounts, ensuring that account collateralization is maintained throughout the entire operation. Here's a breakdown of its parameters and functionality:
accounts
: An array ofAccount.Info
structs that represent the accounts on which the operation will be performed. Each account is specified by its owner's address and a unique account number. The accounts provided here should not contain duplicates, and each action within theactions
parameter will refer to these accounts by their index in this array.actions
: An ordered array ofActions.ActionArgs
structs that define the specific actions to be executed in the operation. The actions are processed sequentially in the order they appear in this array. Each action includes details such as the action type, the account to which it applies (referred to by its index in theaccounts
array), the market(s) involved, the amount of assets to be operated upon, and additional data specific to the action type.
Upon execution of the operate
function, the following rules apply:
The caller (
msg.sender
) must be the owner or operator of all accounts involved in the operation, except for accounts being liquidated, vaporized, or traded with.The series of actions provided in the
actions
array are executed in the order they are presented, forming a single "operation."Account collateralization is verified and maintained only after the entire operation is completed. This ensures that account health is considered holistically.
Last updated