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
Tradercan set a newTrader. - Can initialize a
Vault(which includes a contract-owned dYdX subaccount). - Is the only address allowed to call the
market_makeentrypoint. - 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
Vaultand 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-chaingit checkout feature/cosmwasmor ensure that the branch has Wasm support (typically by seeing if/protocol/wasmbindingis present)- Run the chain locally using the
README.mdfrom dYdX's repo - Use the messages in
example_messages.mdfrom this repo, but replacewasmdwith./build/dydxprotocold