0.12.1 • Published 4 days ago

@dolomite-exchange/dolomite-margin v0.12.1

Weekly downloads
-
License
Apache-2.0
Repository
github
Last release
4 days ago

Ethereum Smart Contracts and TypeScript library used for the Dolomite Trading Protocol. Currently used by app.dolomite.io

TODO re-write API using subgraph data!

Table of Contents

Changes from dYdX's original deployment

Most of the changes made to the protocol are auxiliary and don't impact the core contracts. These core changes are rooted in fixing a bug with the protocol and making the process of adding a large number of markets much more gas efficient. Prior to the changes, adding a large number of markets, around 10+, would result in an n increase in gas consumption, since all markets needed to be read into memory. With the changes outlined below, now only the necessary markets are loaded into memory. This allows the protocol to support potentially hundreds of markets in the same deployment, which will allow DolomiteMargin to become one of the most flexible and largest (in terms of number of non-isolated markets) margin systems in DeFi. The detailed changes are outlined below:

  • Upgraded the Solidity compiler version from 0.5.7 to 0.5.16.
  • Added a getPartialRoundHalfUp function that's used when converting between Wei & Par values. The reason for this change is that there would be truncation issues when using getPartial or getPartialRoundUp, which would lead to lossy conversions to and from Wei and Par that would be incorrect by 1 unit.
  • Added a numberOfMarketsWithDebt field to Account.Storage, which makes checking collateralization for accounts that do not have an active borrow much more gas efficient. If numberOfMarketsWithDebt is 0, state.isCollateralized(...) always returns true. Else, it does the normal collateralization check.
  • Added a marketsWithNonZeroBalanceSet which function as an enumerable hash set. Its implementation mimics Open Zeppelin's with adjustments made to support only the uint256 type (for gas efficiency's sake).
    • The purpose for this set is to track, in O(1) time, a user's active markets, for reading markets into memory in OperationImpl. These markets are needed at the end of each transaction for checking the user's collateralization. It's understood that reading this user's array into memory can be more costly gas-wise than the old algorithm, but as the number of markets listed grows to the tens or hundreds, the new algorithm will be much more efficient.
    • Most importantly, it's understood that a user can inadvertently DOS themselves by depositing too many unique markets into a single account number (recall, user's deposits are partitioned first by their address and second by a uint256 account number). Through UI patterns and organizing the protocol such that a lot of these markets ( at scale) won't be for ordinary use by end-users, the protocol will fight against these DOS attacks.
    • Reading these markets into memory is done using initially populating a bitmap, where each index in the bitmap corresponds with the market's ID. Since IDs are auto-incremented, we can store 256 in just one uint256 variable. Once populated, the bitmap is read into an array that's pre-sorted in O(m), where m represents the number of items in the bitmap, not the total length of it (where the length equals the number of total bits, 256).
      • This is done by reading the least significant bit, truncating it out of the bitmap, and repeating the process until the bitmap equals 0.
      • The process of reading the least significant bit is done in O(1) time using crafty bit math. Then, since the final array that the bitmap is read into is sorted, it can be searched in later parts of OperationImpl in O(log(n)) time, and iterated in it entirety in O(m), where m represents the number of items.
  • Separated each action's logic into separate libraries, to save bytecode (compilation size) in OperationImpl. Otherwise, the OperationImpl bytecode was too large and could not be deployed. These files can be found in contracts/protocol/impl and are named after the action(s) they represent.
  • Separated the Getters logic into a library, GettersImpl (similar to Admin and AdminImpl), to reduce the bytecode size of DolomiteMargin.
  • Added a require statement in LiquidateOrVaporizeImpl that forces liquidations to come from a global operator.
    • This will allow for Chainlink nodes to be the sole liquidator in the future, allowing the DAO to receive liquidation rewards (thus, socializing the reward), instead of having gas wars amongst liquidators to receive the reward while simultaneously clogging the network.
  • Similar to the prior point, added a require statement in TradeImpl that forces expirations to come from a global operator. This requirement is done by first checking if the internal trader is considered special through a new mapping specialAutoTraders mapping (address => bool). If it is, interactions with DolomiteMargin must be done through a global operator.
  • Added the option of limiting the quantity of deposits and borrows for a particular asset, via the addition of the maxSupplyWei and maxBorrowWei fields in the Market struct in Storage.
    • This helps alleviate risk for assets that could be deposited in large quantity into DolomiteMargin such that there isn't enough liquidity to perform timely liquidations.
      • For example, if the current market size were $50M in TVL, and a whale deposited $1B in UST, it would put too much stress on the system, since that much UST would outweigh every other asset deposited by orders of magnitude.
    • If a maxSupplyWei or maxBorrowWei is set that is higher than the current TVL, all new actions involving that currency must lower the TVL or keep it the same (not accounting for any increase in Wei value that occurs from users paying the borrow rate on that asset).
  • Added earningsRateOverride to the Market struct so a particular market can fine-tune the fees paid to the protocol for borrowing.
  • Added accountMaxNumberOfMarketsWithBalances to RiskParams which limits how many assets a user can hold in the same account index.
    • This number was initialized to be sufficiently high, at 32, meaning a user could use up to 32 unique assets within the same margin account.
    • This risk param limits the stress that can be put on the system gas-wise, whereby a user could add many unique assets to the same account index that has an active position, causing maintenance gas costs for any action that interacts with that user's account index to increase.
  • Added oracleSentinel to RiskParams which allows DolomiteMargin to disable borrowing or liquidations when the sequencer is down for a L2.
  • Added accountRiskOverrideSetterMap to RiskParams which allows an address to override the default margin ratio, margin ratio premium, liquidation spread, and liquidation spread premium for a given market.
    • This effectively allows the protocol to offer efficiency mode (e-mode).
    • The intention is to allow certain smart contract vaults hold a user's assets for particular pairings, like:
      • Stablecoins
      • Liquid staking tokens + their underlying token(s)
      • LP tokens and their underlying token(s)
  • Added interestRateMax to RiskLimits to prevent the interest rate from ever being too high.
    • If the rate returned by a market is ever higher, the rate is capped at this value instead of reverting.
    • The goal is to keep Dolomite operational under all circumstances instead of inadvertently DOS'ing the protocol by setting a high interest rate.
    • This field was added to reduce an attack vector whereby a malicious (or negligent) market could return a very high interest rate that would cause the protocol to increase the users' owed amount unreasonably quickly.

Documentation

Documentation can be found at docs.dolomite.io.

Install

npm i @dolomite-exchange/dolomite-margin

Contracts

Arbitrum One (Mainnet)

https://docs.dolomite.io/#/contracts?id=arbitrum-mainnet

Arbitrum Rinkeby

https://docs.dolomite.io/#/contracts?id=arbitrum-rinkeby

Security

Independent Audits

The original DolomiteMargin smart contracts were audited independently by both Zeppelin Solutions and Bramah Systems.

Zeppelin Solutions Audit Report

Bramah Systems Audit Report

Some changes discussed above were audited by SECBIT Labs. We plan on performing at least one more audit of the system before the new Recyclable feature is used in production.

SECBIT Audit Report(./docs/Dolomite Margin - SECBIT - 2021-08-02.pdf)

Code Coverage

All production smart contracts are tested and have the vast majority of line and branch coverage.

This repository uses solidity-coverage to generate code coverage reports.

To run code coverage, first start an instance of the local RPC using npm run coverage_node

Then, run test coverage script in a separate terminal instance: npm run coverage. Note, this script takes a long time to execute!

Vulnerability Disclosure Policy

The disclosure of security vulnerabilities helps us ensure the security of all DolomiteMargin users.

How to report a security vulnerability?

If you believe you’ve found a security vulnerability in one of our contracts or platforms, send it to us by emailing security@dolomite.io. Please include the following details with your report:

  • A description of the location and potential impact of the vulnerability.

  • A detailed description of the steps required to reproduce the vulnerability.

Scope

Any vulnerability not previously disclosed by us or our independent auditors in their reports.

Guidelines

We require that all reporters:

  • Make every effort to avoid privacy violations, degradation of user experience, disruption to production systems, and destruction of data during security testing.

  • Use the identified communication channels to report vulnerability information to us.

  • Keep information about any vulnerabilities you’ve discovered confidential between yourself and Dolomite until we’ve had 30 days to resolve the issue.

If you follow these guidelines when reporting an issue to us, we commit to:

  • Not pursue or support any legal action related to your findings.

  • Work with you to understand and resolve the issue quickly (including an initial confirmation of your report within 72 hours of submission).

  • Grant a monetary reward based on the OWASP risk assessment methodology.

Development

Compile Contracts

Requires a running docker engine.

npm run build

Compile TypeScript

npm run build:js

Test

Requires a running docker engine.

Start test node:

docker-compose up

Deploy contracts to test node & run tests:

npm test

Just run tests (contracts must already be deployed to test node):

npm run test_only

Just deploy contracts to test node:

npm run deploy_test

Contributing

You may open a pull request with any added or modified code. The pull request should state the rationale behind any changes or the motivation behind any additions. All pull requests should contain adequate test coverage too.

Maintainers

License

Apache-2.0

0.12.0

4 days ago

0.12.1

4 days ago

0.11.4

1 month ago

0.11.2

1 month ago

0.11.3

1 month ago

0.11.1

3 months ago

0.11.0

3 months ago

0.10.0

4 months ago

0.9.33

4 months ago

0.9.32

4 months ago

0.9.31

4 months ago

0.9.30

5 months ago

0.9.27

10 months ago

0.9.28

10 months ago

0.9.29

10 months ago

0.9.23

11 months ago

0.9.24

11 months ago

0.9.25

11 months ago

0.9.26

11 months ago

0.9.20

11 months ago

0.9.21

11 months ago

0.9.22

11 months ago

0.9.13

1 year ago

0.9.14

1 year ago

0.9.15

1 year ago

0.9.16

1 year ago

0.9.17

1 year ago

0.9.18

1 year ago

0.9.19

1 year ago

0.9.12

1 year ago

0.9.10

1 year ago

0.9.11

1 year ago

0.9.8

1 year ago

0.9.7

1 year ago

0.9.9

1 year ago

0.9.6

1 year ago

0.9.5

1 year ago

0.9.4

1 year ago

0.9.3

1 year ago

0.9.2

2 years ago

0.9.1

2 years ago

0.9.0

2 years ago

0.8.0

2 years ago

0.5.0

2 years ago

0.7.0

2 years ago

0.6.0

2 years ago

0.4.8

2 years ago

0.4.7

2 years ago

0.4.5

2 years ago

0.4.6

2 years ago

0.4.4

2 years ago

0.4.3

2 years ago

0.4.2

2 years ago

0.4.1

2 years ago

0.4.0

2 years ago

0.3.3

2 years ago

0.3.2

2 years ago

0.3.1

2 years ago

0.3.0

2 years ago

0.2.9

2 years ago

0.2.8

2 years ago

0.2.7

2 years ago

0.2.6

2 years ago

0.2.5

2 years ago

0.2.4

2 years ago

0.2.3

2 years ago

0.2.2

2 years ago

0.2.1

2 years ago

0.2.0

2 years ago

0.1.12

2 years ago

0.1.11

2 years ago

0.1.10

2 years ago

0.1.9

2 years ago

0.1.8

2 years ago

0.1.7

2 years ago

0.1.6

2 years ago

0.1.5

2 years ago

0.1.4

2 years ago

0.1.3

2 years ago

0.1.2

2 years ago

0.1.1

2 years ago

0.1.0

2 years ago