41 lines
3.1 KiB
Markdown
41 lines
3.1 KiB
Markdown
# General Design
|
|
|
|
## Vault
|
|
Creating a separate contract for each user address allows users to deposit coins into their "account" using standard
|
|
ERC20 sends without extra approvals. Withdrawals require a contract call, but again no approval step. Furthermore,
|
|
this clarifies the no-custody nature of the setup, since DexOrder never has any claim to ownership of the user's
|
|
contract. Of course this costs extra gas up front to create the contract for the user, but on L2's it should be
|
|
minimal. What about ETH? Hmmm... The alternative is to have a single contract which keeps an accounting of everyone's
|
|
everything. Deposits would require approvals and a contract call. Using separate vaults will be an easier, more secure
|
|
experience for frequent traders who are more likely to be our users rather than casual, occasional traders.
|
|
|
|
## Orders
|
|
Orders are defined using an in-token, an out-token, a route/pool, and an amount of either input or output currency.
|
|
Each order may have many tranches, and each tranch may have its own price/time constraints. Each tranche tracks its
|
|
own filled amount in addition to incrementing the global filled counter. OCO support. Conditional orders (stoploss)
|
|
are implemented as a separate order which starts with amount 0 but receives any filled amounts from orders that
|
|
reference it.
|
|
|
|
## Reorg Resilience
|
|
Blockchain reorganizations happen, so we need a system that can revert recent activity, restore to a previous state,
|
|
and replay a new set of truths. We need to track the current set of open orders with their computable pool
|
|
dependencies, and we need to ensure that sent/mined transactions are not lost after a reorg but retried or safely
|
|
discarded.
|
|
|
|
We run a single synchronous process which generates a batch of all the transactions and event logs, tagged
|
|
with the block height, hash, and parent, plus a dexorder global sequence number. All subsequent processing is done
|
|
within that block context, reading the state from the parent block and writing the state for the new block. State
|
|
is kept entirely in-memory as a base state from an old block plus diffs organized in a tree of blockchain branches.
|
|
All getters of a state object return the base value plus any diffs found by walking the current worker context's
|
|
blockchain path up through its parent block. All setters write update requests to the current block hash, including
|
|
its global seq. After all layers of jobs have provably completed, the actions are executed in seq order, and thte block
|
|
may be marked as completed. As blocks age out, orphans/uncles are simply dropped from memory while old main-chain blocks
|
|
become new root blocks for the in-memory diff tree.
|
|
|
|
Active orders with an opportunity to trade are poked whenever they appear on any fork.
|
|
|
|
Transactions generated by jobs carry their original block context. If a sent tx has an origin block that gets too old,
|
|
the tx will be retried if the origin block is main chain but not if the origin block was a reorged fork. Whenever we
|
|
get a transaction receipt, the receipt is added to the order. As the order ages out, transaction receipts are
|
|
re-checked for validity
|