Smart Contracts
Smart contract documentation for the Orion Protocol
Last updated
Smart contract documentation for the Orion Protocol
Last updated
Orion's smart contracts use OpenZeppelin's contract library frameworks as a reference.
The blockchain aspect of Orion Protocol consists of a set of smart contract on the Ethereum blockchain which are used for trustless asset deposit/withdrawal, check users' authorization of orders, match orders with safety checks, short-term covered loan granting (to facilitate broker activity) and liquidation of such loans if they become overdue.
Those contracts operate under the following rules:
Only the user can control their funds: no central authority, including the Orion Protocol matcher/aggregator engine can't seize any assets owned by user even if the user doesn't participate in marginal trading.
Any marginal trading is intended to be short-term, that is, any negative position is only available for the period of time long enough to withdraw funds from other liquidity pools.
Any short-term loan is covered by a collateral with substantial excess, to anticipate possible price fluctuations. Only widely trusted assets can be used as a collateral.
The liquidation mechanism is intended to be accessible to as much parties as possible to make liquidation fast. Liquidation speed and collateral excess ensure solvency of exchange contract during price fluctuations.
The smart contract architecture is illustrated as follows:
All assets on an exchange level are treated as divisible to 10^8 indivisible units. Conversion to real decimals corresponding to those assets occurs on deposit/withdrawal. The conversion functionality is implemented in the LibUnitConversion library.
SafeMath logic is not used for gas-cost concerns, instead we only allow those integer variables which can not overflow. The balances inside the main contract are stored as int192 (note: the balance may be negative). Allowed input amounts are int112 or uint112: it is enough for all practically used tokens. For instance, if a decimal unit is 1e18, int112 allow to encode up to 2.5e15 decimal units. That way, adding/subtracting any amount from balances won't overflow, since the minimum number of operations to reach max int is practically infinite: ~1e24. Allowed prices are uint64. Note that the price is represented as price per 1e8 tokens. That means that amount*price
always fits uint256, while amount*price/1e8
not only fit int192, but also can be added and subtracted without overflow checks: number of malicious operations to overflow ~1e13.
Order validation is made in LibValidator library. validateV3
and checkOrdersInfo
check that orders are signed by the user and also check that orders are valid at the moment of signing and match with each other.
The implementation of the short-term loan mechanism is presented mostly in MarginalFunctionality. In short we allow the user to have a limited number of liabilities (assets with negative balance). The list of liabilities is stored explicitly and is updated on each trade. Also, for each trade, we calculate the position as the weighted sum of all assets which can be used as collateral (by iterating through the list of allowed collateral assets) and subtract the value of liabilities (by iterating through list of liabilities).
All calculation are done in ORN. If after the trade, the weighted position is not positive - this trade is incorrect and will be reverted. A Broker may cover the liability by deposit or trade. We store the time of liability creation and value of this liability. The Broker should close this liability during overduePeriod
or he can be liquidated; however broker also may deepen liability during that period and if he manages to cover the initial liability value while having additional loan time counters for those new loans, the loan time will start from zero. Besides negativity, there are possible other incorrect position state: OVERDUE
, NOPRICE
, INCORRECT
, when the loan is overdue, the price provided by PriceOracle is too old, or the position doesn't satisfy other conditions.
The ORN locked in OrionVault
work as an "addition" asset type which has a higher risk coefficient, that means it has a higher weight during position calculation. However, in contrast with simple ORN on the exchange balance they can not be arbitrarily withdrawn: first, the user has to close all liabilities and wait for the release period. The idea behind that mechanism is that Brokers with locked ORN have skin in the game, that is, they share risks with all of the orion ecosystem and thus it is more expensive for them to try to harm it; in exchange they have better collateral rates.
To have the ability to upgrade contracts, the exchange is developed behind a Proxy. We use the standard OpenZeppelin proxy framework for that matter. However, for safe upgrades, we need to preserve the contract storage layout. To do so, we use the inheritance approach. That way, the initial storage layout is determined by ExchangeStorage
. The current scheme is as follows: Proxy
use code of the Exchange
, and Exchange
itself is the child of the following inheritance chain: Exchange
<-OrionVault
<-ExchangeStorage
. If during an upgrade, we need to add a new state variable, a new contract inherited from Exchange
should be implemented and the proxy should be pointed to this new contract.
PriceOracle
The price oracle works as the cache of prices obtained from various sources. Initially there were three possible methods to provide price data:
1) signed data by oraclePublicKey
2) data provided by authorized address
3) chainlink aggregators
Currently, the first option is commented out. The price oracle retrieve prices and, if necessary, calculates prices relative to ORN from other pairs and stores them to be consumed by the Exchange contract.
The standard workflow is shown below. The mono
font labels on arrows are contract method names for corresponding actions:
However, if a Broker failed to cover short-term loans in a timely manner, the liquidator will cover them as their Broker at the expense of an additional liquidation premium:
Testing and compiling are done using Truffle and Hardhat, instructions can be found in of the exchange contract repository.