Getting Started
Contract Design
Integration Tests
To setup the elixir-dydx contracts:
- Ensure that Rust is installed
- Install the Wasm Rust compiler backend using:
rustup target add wasm32-unknown-unknown
- Build using
cargo wasm
- Run unit tests using
cargo test
For more details on how CosmWasm environments are configured, see the CosmWasm Book.
The smart contract contained in this repository is intended to enable the Elixir protocol to engage in market-making activities with user funds in a permissionless manner. Specifically the contract must:- Enable Elixir to trade on behalf of users. Trading should be done independently on multiple markets.
- Allow users to deposit and withdraw their funds (subject to restrictions based on Elixir's trading strategy).
- Track account value and user deposits such that changes in the PnL of the account are reflected when a user withdraws their funds. For example if PnL is positive, the user should be able to withdraw more than they deposited.
Before going into more detail about the integration contract, it is useful to uderstand some details about how dYdX subaccounts work. dYdX subaccounts are cross-margin by default and USDC is the preferred collateral. They only accept messages (e.g deposit, place_order) sent by their owner
. For dYdX's CosmWasm integration the creator of a subaccount is always the smart contract who sent the message. Note that for dYdX a deposit results in subaccount creation.
The smart contract will always have an address with trading permissions. Typically this account/address is referred to as the Trader
. The Trader
:
- Is initialized as the contract deployer (but can be modified). Only the current
Trader
can set a newTrader
. - Can initialize a
Vault
(which includes a contract-owned dYdX subaccount). - Is the only address allowed to call the
market_make
entrypoint. - Has permission to trade on all vaults/perp markets.
- Should always be an Elixir owned account.
Since the underlying dYdX subaccount is owned by the smart contract itself, the Trader
does not have permission to withdraw user funds. For the same reason, the smart contract's market_make
endpoint must be called to place/cancel orders.
A Vault
is the concept that the smart contract uses to coordinate tracking user deposits and trading. Each Vault
:
- Corresponds to a dYdx perp market. See
perp_id
. - Has one contract-owned subaccount associated with it.
- Has a unique LP token that is minted when users deposit into the
Vault
and burned when users withdraw. The LP token is used to determine a user's share in theVault
. - Has a withdrawal queue associated with it.
- Can only be created by a
Trader
Despite the fact that dYdX subaccounts are cross-margined by default, 1 and 2 implies that each Vault
is isolated to its associated market. Due to this, perp_id
and subaccount_number
are interchangeable.
Users may only deposit and withdraw USDC. As stated above, the contract uses the minting and burning of LP tokens to keep track of deposits. LP tokens are managed according to the invariant:
(user LP tokens / total LP tokens) = (user deposit-or-withdraw value USDC / vault value USDC)
.
As a simple example, if a user deposited $10 USDC into the Vault
and the USDC value of the Vault
was $100 as a result, the depositor would own 10% of all outstanding LP tokens. If a user owns 10% of all outstanding LP tokens, they are entitled to withdraw 10% of the USDC value of the Vault
. This mechanism ensures that withdrawals properly reflect the changes in Vault
value during the lifetime of a user's deposit. Users can deposit at any time, but withdrawals are queued and later fulfilled by the Trader
. This is done to prevent withdrawals from disrupting Elixir's trading.
All trading is done by the Trader
using the market_make
entrypoint. market_make
sends multiple PlaceOrderV1
and CancelOrderV1
messages for the specified subaccount/perp market (again perp_id
and subaccount_number
are interchangeable). Due to gas considerations, dYdX has restricted the amount of orders placed to be at most 3 bids and 3 asks. The market_make
entrypoint also has a check to keep leverage <= 1x. If leverage is already over 1x due to market movements, the check will just enforce that any new orders woulld decrease leverage.
Since the smart contract is intended to be run on dYdX chain, the unit tests require heavy mocking. Fortunately, dYdX provides a dockerized version of their blockchain that can be used to run integration tests. To setup this environment:
git clone https://github.com/dydxprotocol/v4-chain
git checkout feature/cosmwasm
or ensure that the branch has Wasm support (typically by seeing if/protocol/wasmbinding
is present)- Run the chain locally using the
README.md
from dYdX's repo - Use the messages in
example_messages.md
from this repo, but replacewasmd
with./build/dydxprotocold