diff --git a/.github/workflows/add_issue_new_projects.yml b/.github/workflows/add_issue_new_projects.yml index 6fb940774..366c5dac1 100644 --- a/.github/workflows/add_issue_new_projects.yml +++ b/.github/workflows/add_issue_new_projects.yml @@ -8,7 +8,7 @@ name: "Add Issues To Project Board" types: - opened env: - GH_TOKEN: ${{ secrets.GH_NEW_CARD_TO_PROJECT }} + GH_TOKEN: ${{ secrets.PROJECT_MANAGE_ACTION }} PROJECT_ID: ${{ secrets.PROTOCOL_DESIGN_PROJECT_ID }} ISSUE_ID: ${{ github.event.issue.node_id }} USER: ${{ github.actor }} diff --git a/.github/workflows/add_pr_new_projects.yml b/.github/workflows/add_pr_new_projects.yml index 9557154a3..414147e66 100644 --- a/.github/workflows/add_pr_new_projects.yml +++ b/.github/workflows/add_pr_new_projects.yml @@ -8,7 +8,7 @@ name: "Add IPRs To Project Board" types: - opened env: - GH_TOKEN: ${{ secrets.GH_NEW_CARD_TO_PROJECT }} + GH_TOKEN: ${{ secrets.PROJECT_MANAGE_ACTION }} PROJECT_ID: ${{ secrets.PROTOCOL_DESIGN_PROJECT_ID }} PR_ID: ${{ github.event.pull_request.node_id }} USER: ${{ github.actor }} diff --git a/.github/workflows/config/markdownlint.json b/.github/workflows/config/markdownlint.json index ea3d61a0c..bad5db322 100644 --- a/.github/workflows/config/markdownlint.json +++ b/.github/workflows/config/markdownlint.json @@ -1,9 +1,15 @@ - { "default": true, "line-length": false, "commands-show-output": false, "no-bare-urls": true, "no-inline-html": false, - "no-hard-tabs": false + "no-hard-tabs": false, + "MD012": { + "maximum": 2 + }, + "MD024": { + "siblings_only": true + }, + "MD049": false, } diff --git a/.github/workflows/config/markdownlintignore b/.github/workflows/config/markdownlintignore index d7c52fd53..f0c429863 100644 --- a/.github/workflows/config/markdownlintignore +++ b/.github/workflows/config/markdownlintignore @@ -1,2 +1 @@ -user-interface/* LICENSE.md \ No newline at end of file diff --git a/.github/workflows/quality_check.yml b/.github/workflows/quality_check.yml index d90c2e85a..fea81880c 100644 --- a/.github/workflows/quality_check.yml +++ b/.github/workflows/quality_check.yml @@ -8,7 +8,8 @@ name: "Quality checks" branches: - master - cosmicelevator - - palazzomistero + - palazzo + - colosseo env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -19,7 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - name: "Check out" - uses: actions/checkout@v3 + uses: actions/checkout@v3.5.3 - name: "Run Yamllint" uses: ibiqlik/action-yamllint@v3.1.1 @@ -31,10 +32,10 @@ jobs: runs-on: ubuntu-latest steps: - name: "Check out" - uses: actions/checkout@v3 + uses: actions/checkout@v3.5.3 - name: "Run PySpelling" - uses: rojopolis/spellcheck-github-actions@0.24.0 + uses: rojopolis/spellcheck-github-actions@0.33.1 with: task_name: Markdown @@ -43,10 +44,10 @@ jobs: runs-on: ubuntu-latest steps: - name: "Check out" - uses: actions/checkout@v3 + uses: actions/checkout@v3.5.3 - name: "Run Markdownlint" - uses: nosborn/github-action-markdown-cli@v3.1.0 + uses: nosborn/github-action-markdown-cli@v3.3.0 with: files: . config_file: .github/workflows/config/markdownlint.json @@ -57,12 +58,30 @@ jobs: runs-on: ubuntu-latest steps: - name: "Check out" - uses: actions/checkout@v3 + uses: actions/checkout@v3.5.3 + + - name: Log in to the GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Log in to the GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - name: "Run Check AC codes" run: | - npx github:vegaprotocol/approbation check-codes --specs="{./non-protocol-specs/**/*.md,./protocol/**/*.md}" + npx github:vegaprotocol/approbation check-codes --specs="{./non-protocol-specs/**/*.md,./protocol/**/*.md,./protocol/**/*.ipynb}" - name: "Run Check file names" run: | - npx github:vegaprotocol/approbation check-filenames --specs="{./non-protocol-specs/**/*.md,./protocol/**/*.md}" + npx github:vegaprotocol/approbation check-filenames --specs="{./non-protocol-specs/**/*.md,./protocol/**/*.md,./protocol/**/*.ipynb}" + + - name: "Run Check Features" + run: | + npx github:vegaprotocol/approbation check-features --specs="{./non-protocol-specs/**/*.md,./protocol/**/*.md,./protocol/**/*.ipynb}" --features="./protocol/features.json" diff --git a/0000-TEMP-template-notes.md b/0000-TEMP-template-notes.md new file mode 100644 index 000000000..6159fdb77 --- /dev/null +++ b/0000-TEMP-template-notes.md @@ -0,0 +1,52 @@ +# 0000-TEMP decision notes + +## Context / Problem Statement 1 + +| Opened | Last updated | Status | +|:----------:|:------------:|:------------:| +| DD/MM/YYYY | DD/MM/YYYY | open/resoved | + +Describe the context and problem statement... + +### Considered Options + +* Option 1... +* Option 2... + +### Decision Outcome + +Chosen option: Option X, because... (include why paramemters are chosen, any dependancies and / or assumptions this decision is based upon) + +### Open risks + +* Risk 1 - details any risks or gaps that are not yet resolved + +### Limitations + +Are there any issues we wanted to address but could not e.g., A core rewrite or CometBFT limtitaion + + +## Context / Problem Statement 2 + +| Opened | Last updated | Status | +|:----------:|:------------:|:------------:| +| DD/MM/YYYY | DD/MM/YYYY | open/resoved | + +Describe the context and problem statement... + +### Considered Options + +* Option 1... +* Option 2... + +### Decision Outcome + +Chosen option: Option X, because... (include why paramemters are chosen, any dependancies and / or assumptions this decision is based upon) + +### Open risks + +* Risk 1 - details any risks or gaps that are not yet resolved + +### Limitations + +Are there any issues we wanted to address but could not e.g., A core rewrite or CometBFT limtitaion diff --git a/README.md b/README.md index 79e3209c8..91897eb9c 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This repository contains specifications and RFCs for future changes to the syste In order to ensure that there is a clear view on the specification of the protocol, both at the version in mainnet and the version being developed in testnet, the following branching is used: - `master` branch details the specification of the protocol in mainnet (from Alpha Mainnet onwards) -- `cosmicelevator` branch is the milestone grouping of features being developed AFTER Alpha Mainnet +- `cosmicelevator` branch is the milestone grouping of features being developed AFTER Alpha Mainnet. As features are deployed to mainnet by the validators the relevant spec changes will be merged into the `master` branch. - Further milestone branches will be created as development progresses and will be updated in this `README.md` To find out more see the [The specification lifecycle](WORKFLOW.md#the-specification-lifecycle) diff --git a/glossaries/trading-and-protocol-glossary.md b/glossaries/trading-and-protocol-glossary.md index 28307da7f..ca143777e 100644 --- a/glossaries/trading-and-protocol-glossary.md +++ b/glossaries/trading-and-protocol-glossary.md @@ -175,7 +175,9 @@ Parameters: ### Insurance Pool -A store of capital instantiated with the order book into which fines are contributed. It is utilised for financially covering [close out trades](#close-out-trades). +[Market insurance pool](../protocol/0015-INSR-market_insurance_pool_collateral.md#market-insurance-pool): a store of capital instantiated with the order book into which fines are contributed. It is utilised for financially covering [close out trades](#close-out-trades). + +[Global insurance pool](../protocol/0015-INSR-market_insurance_pool_collateral.md#global-insurance-pool): an insurance pool per given asset, not associated with any market. It receives part of remaining funds from insurance pool of the market using the same settlement asset. Funds can be transferred out of the global insurance pool using a governance initiated [transfer](../protocol/0028-GOVE-governance.md). ## L @@ -185,7 +187,7 @@ The net riskiest composition of a trader's open positions and live orders. For ### Liquidity Providers -Liquidity providers commit a bond and place a special Liquidity Commitment that automatically maintains orders on the book for a specific market. In return, liquidity providers earn a [fee](#fees) for ensuring that markets always have open volume. See [the liquidity provision spec](./../protocol/0044-LIME-lp_mechanics.md) for more detail. +Liquidity providers commit a bond which specifies their SLA obligations. In return for meeting these the liquidity providers earn a portion of the trading [fees](#fees) from the market in which they operate. See [the liquidity provision spec](./../protocol/0044-LIME-lp_mechanics.md) for more detail. ## M diff --git a/makefile b/makefile index 1cafcf49f..ae9bcacd0 100644 --- a/makefile +++ b/makefile @@ -1,19 +1,19 @@ # Set default to run all checks if none specified .DEFAULT_GOAL := all -all: spellcheck markdownlint names codes references links clean +all: spellcheck markdownlint names codes references links check-features clean # Check that all the specifications are named appropriately .PHONY: names names: @$(MAKE) clone-sources - npx github:vegaprotocol/approbation check-filenames --specs="{./non-protocol-specs/**/*.md,./protocol/**/*.md}" + npx github:vegaprotocol/approbation check-filenames --specs="{./non-protocol-specs/**/*.md,./protocol/**/*.md,./protocol/**/*.ipynb}" # Count how many Acceptance Criteria each specification has .PHONY: codes codes: @$(MAKE) clone-sources - npx github:vegaprotocol/approbation check-codes --specs="{./non-protocol-specs/**/*.md,./protocol/**/*.md}" + npx github:vegaprotocol/approbation check-codes --specs="{./non-protocol-specs/**/*.md,./protocol/**/*.md,./protocol/**/*.ipynb}" TEMP=./.build .PHONY:clone-sources @@ -61,5 +61,10 @@ markdownlint: spellcheck: @./spellcheck.sh +# Checks for duplicated ACs in the features.json file +.PHONY: check-features +check-features: + npx github:vegaprotocol/approbation check-features --specs="{./non-protocol-specs/**/*.md,./protocol/**/*.md,./protocol/**/*.ipynb}" --features="./protocol/features.json" + clean: rm -rf $(TEMP) diff --git a/non-protocol-specs/0001-NP-LIQB-liquidity_providing_bots.md b/non-protocol-specs/0001-NP-LIQB-liquidity_providing_bots.md index 37efa641e..db5c04387 100644 --- a/non-protocol-specs/0001-NP-LIQB-liquidity_providing_bots.md +++ b/non-protocol-specs/0001-NP-LIQB-liquidity_providing_bots.md @@ -7,21 +7,21 @@ At the moment bots on Vega run on certain markets to make them look "real". For that purpose they: 1. Are given large amounts of collateral via faucets. -1. Keep track of current spot or futures price on another exchange (at e.g. 30s, 5 min intervals) +1. Keep track of current futures price on another exchange (at e.g. 30s, 5 min intervals) 1. Post GTC limit orders randomly on both sides of the order book at random volumes using the above reference price as mid. This achieves the following: the price on the market looks "real" and there is volume for participants to trade. The downside is that if the bot is "unlucky" they can run out of even large amount of collateral and their orders / positions are liquidated. To avoid this they need regular collateral top-ups. -From Flamenco Tavern onwards any market on Vega will need a committed liquidity provider, see [LP mechanics spec](../protocol/0044-LIME-lp_mechanics.md) to function. See also [LP order type spec](../protocol/0038-OLIQ-liquidity_provision_order_type.md). +From Flamenco Tavern onwards any market on Vega will need a committed liquidity provider, see [LP mechanics spec](../protocol/0044-LIME-lp_mechanics.md) to function. See also [LP order type spec](../0044-LIME-lp_mechanics.md#commit-liquidity-network-transactiond). If a feature is marked as "optional" then the bot can be configured in such a way that it is not providing this functionality but still doing other tasks. The aim of this spec is bots that: 1. submit a market proposal (optional) or connects to an existing market -1. serve as a liquidity provider for the market by submitting the [LP order type](../protocol/0038-OLIQ-liquidity_provision_order_type.md) (optional). +1. serve as a liquidity provider for the market by submitting the [LP order type](../0044-LIME-lp_mechanics.md#commit-liquidity-network-transaction) (optional). 1. participate in an opening auction (optional) 1. create markets that look real with more-or-less correct price by placing limit orders that "steer" the price up-or-down as appropriate (optional) 1. manage their position in such a way so as to not require ever growing amount of collateral. This will mean changing the "shape" in the liquidity provision order and also being strategic about placing limit orders to steer the price. The bot can have an optional position limit. @@ -53,7 +53,7 @@ The bot needs to be able to query Vega to know the risk model and parameters for This is only relevant if the option to submit a market proposal is enabled. -The bot will read the required market proposal from a file (configuration option), decide if it has minimum LP stake in the right asset, check it's got enough vote tokens and then submit the proposal and vote for it. They will also need to submit [liquidity shapes](../protocol/0038-OLIQ-liquidity_provision_order_type.md) but that will be treated below. +The bot will read the required market proposal from a file (configuration option), decide if it has minimum LP stake in the right asset, check it's got enough vote tokens and then submit the proposal and vote for it. To decide that it will ask Vega for `assetBalance`, `quantum` for asset and `min_LP_stake_quantum_multiple` and proceed if `assetBalance x stakeFraction > min_LP_stake_quantum_multiple x quantum` It will then check whether it has enough collateral for maintaining the commitment but that will be described below as it applies below too. @@ -212,8 +212,8 @@ Don't use any of the pseudocode above! ### Acceptance criteria -1. Bot can submit a market proposal (optional), commit liquidity and then manage it's position as described above, see also [LP order type](../protocol/0038-OLIQ-liquidity_provision_order_type.md). (0001-NP-LIQB-001) -1. Bot can connect to an existing market, submit an [LP order type](../protocol/0038-OLIQ-liquidity_provision_order_type.md) and then manage it's position as described above. (0001-NP-LIQB-002) +1. Bot can submit a market proposal (optional), commit liquidity and then manage it's position as described above, see also [LP order type](../0044-LIME-lp_mechanics.md#commit-liquidity-network-transaction). (0001-NP-LIQB-001) +1. Bot can connect to an existing market, submit an [LP order type](../0044-LIME-lp_mechanics.md#commit-liquidity-network-transaction) and then manage it's position as described above. (0001-NP-LIQB-002) 1. Bot can participate in an opening auction placing orders around target price (set via parameters, see above).(0001-NP-LIQB-003) 1. Can read a price target from external source and and places limit orders that "steer" the price up-or-down as appropriate and have the right `targetLNVol` using one of the methods above (note that this has to take into account other identical bots trying to do the same on the same market).(0001-NP-LIQB-004) 1. Bot manages its position in such a way that it stays close to zero and starts placing market orders if configured maxima are breached.(0001-NP-LIQB-005) diff --git a/non-protocol-specs/0007-NP-SNTP-sweetwater_signed_netparams.md b/non-protocol-specs/0007-NP-SNTP-sweetwater_signed_netparams.md index 5ccba9522..77da509b5 100644 --- a/non-protocol-specs/0007-NP-SNTP-sweetwater_signed_netparams.md +++ b/non-protocol-specs/0007-NP-SNTP-sweetwater_signed_netparams.md @@ -3,7 +3,7 @@ There are parameters within Vega that influence the behaviour of the system: - some are set in genesis block but fixed once network is running, -- while others are changeable by on-chain [governance](../protocol/0028-GOVE-governance.md) but initialised to genesis values during network launch. For more info see [network paramters](../protocol/0054-NETP-network_parameters.md) +- while others are changeable by on-chain [governance](../protocol/0028-GOVE-governance.md) but initialised to genesis values during network launch. For more info see [network parameters](../protocol/0054-NETP-network_parameters.md) On [Sweetwater (Restricted Mainnet) Release](https://github.com/orgs/vegaprotocol/projects/114) Vega Team wishes to control how certain parameters are initialised while letting validators change others as they see fit. As the process of decentralisation progresses Vega Team the number of such parameters will be reduced. @@ -22,8 +22,8 @@ The values to be specified as a PR against ??? repo. | Name | Comment | Suggested value (optional) | |-------------------------------------------------------------|:------------------------------------------------------------------:| :-------------------------:| -| `min number of validators` (not in sweetwater) | Not in [network paramters](../protocol/0054-NETP-network_parameters.md) | | -| `validator min balance` | Not in [network paramters](../protocol/0054-NETP-network_parameters.md) | 3000 VEGA | +| `min number of validators` (not in sweetwater) | Not in [network parameters](../protocol/0054-NETP-network_parameters.md) | | +| `validator min balance` | Not in [network parameters](../protocol/0054-NETP-network_parameters.md) | 3000 VEGA | | `governance.proposal.updateNetParam.requiredMajority` | So that what is set in genesis cannot be changed too easily | 0.5 | | `governance.proposal.updateNetParam.requiredParticipation` | So that what is set in genesis cannot be changed too easily | 0.5 | | `validators.epoch.length` | Rewards currently make an assumption on epoch length, best fix it. | 1 day | diff --git a/non-protocol-specs/0012-NP-LIPE-liquidation-price-estimate.md b/non-protocol-specs/0012-NP-LIPE-liquidation-price-estimate.md new file mode 100644 index 000000000..6f47894f5 --- /dev/null +++ b/non-protocol-specs/0012-NP-LIPE-liquidation-price-estimate.md @@ -0,0 +1,63 @@ +# Liquidation price estimate + +## Summary + +Provide an estimate of the price range at which the liquidation of a specified position is likely to occur. + +## Overview + +Provide an estimated liquidation price range, where the lower bound assumes no slippage in the [margin level calculation](../protocol/0019-MCAL-margin_calculator.md) and the upper bound assumes that the slippage cap is applied. + +This amounts to carrying out the same computation twice, once with slippage factor set to `0` and once with the actual value used by the market for which the specified position is being considered. + +The system carries out [position resolution](../protocol/0012-POSR-position_resolution.md) when the available collateral (amount in margin account for the market along with amount in the general account denominated in the same asset) is less than the maintenance margin level for the position. The first step is to cancel any open orders that a distressed party may have. After that the margin requirement is re-evaluated to see if the position is still distressed. Therefore we provide three sets of estimates of a liquidation price range: current open volume only, current open volume with active buy orders, current open volume with active sell orders. + +## Calculation + +### Position only + +We start with the case which estimates the liquidation price given the current open volume and ignoring any orders a party may have. We need to keep in mind that as the mark price moves the maintenance margin changes and the collateral available a party has changes due to [mark to market](../protocol/0003-MTMK-mark_to_market_settlement.md) gains/losses. Therefore, to estimate the liquidation price we need to find $S^{\text{liquidation}}$ such that: + +$$ +\text{collateral available} + V(S^{\text{liquidation}}-S^{\text{current}}) = \text{maintenance margin}(S^{\text{liquidation}}), +$$ + +where $V$ is the open volume (negative for a short position) and $S^\text{current}$ is the current mark price. + +We assume margin is calculated as per continuous trading formula (as there are no closeouts in auctions) and that the slippage cap always applies, therefore we get: + +$$ +\text{collateral available} + V(S^{\text{liquidation}}-S^\text{current}) = S^{\text{liquidation}} (\abs{V} \cdot \text{linear slippage factor}+\abs{V} \cdot \text{risk factor}) + V \cdot \text{constant}, +$$ + +where $\text{risk factor}$ is the long risk factor when $V>0$ and the short risk factor otherwise. The $\text{constant}$ is an optional arbitrary constant scaling with open volume added to the maintenance margin, e.g. the funding payment portion of the margin for [perpetual futures](../protocol/0053-PERP-product_builtin_perpetual_future.md#5-margin-considerations). Solving for $S^{\text{liquidation}}$ we get: + +$$ +S^{\text{liquidation}} = \frac{\text{collateral available}-V \cdot S^\text{current} - V \cdot \text{constant}}{\abs{V} \cdot \text{linear slippage factor}+\abs{V} \cdot \text{risk factor}-V} +$$ + +if the denominator in the above expression evaluates to $0$ the liquidation price is undefined and we return an error, otherwise we return the result floored at $0$ (as the negative price is not attainable for any of the currently supported products). + +### Including orders + +When including orders we sort the orders in the order they will get filled in (descending for buy orders, ascending for sell orders) and assume any market orders get filled instantaneously at the current mark price. Then separately for each side: + +- Calculate open volume with including the remaining volume of all the market orders for a given side ($V$) and calculate the liquidation price ($S^{\text{liquidation}}$) using the formula outlined above and the current mark price or indicative uncrossing price if market is in auction ($S^{\text{current}}$). +- For each limit order: + - if the order price ($S^{\text{order}}$) is above (buy side) / below (sell side) the liquidation price ($S^{\text{liquidation}}$): + - recalculate $V$ to include the order's remaining volume (assumes order gets filled as soon as its price level is filled), + - update $\text{collateral available}$ to include the MTM gains/losses: $V(S^{\text{order}}-S^{\text{current}})$, + - update $S^{\text{current}}$ to equal $S^{\text{order}}$, + - otherwise return last calculated $S^{\text{liquidation}}$ (assumes other orders will get cancelled and the remaining position will be liquidated). + +### Acceptance criteria + +1. An estimate is obtained for a long position with no open orders, mark price keeps going down in small increments and the actual liquidation takes place within the estimated range. (0012-NP-LIPE-001) +1. An estimate is obtained for a short position with no open orders, mark price keeps going up in small increments and the actual liquidation takes place within the estimated range. (0012-NP-LIPE-002) +1. An estimate is obtained for a position with no open volume and a single limit buy order, after the order fills the mark price keeps going down in small increments and the actual liquidation takes place within the obtained estimated range. (0012-NP-LIPE-003) +1. An estimate for cross-margin mode with `include_collateral_increase_in_available_collateral` set to `true` is obtained for a long position with multiple limit sell orders with the absolute value of the total remaining size of the orders less than the open volume. The general account balance should be set to `0` in the query and the margin account balance should be set to the maintenance margin for the chosen position. Orders should be chosen so that the liquidation price estimate with sell orders is non-zero (otherwise the party is collateralised up to a point that it will never get liquidated if these orders fill). The estimated liquidation price with sell orders is lower than that for the open volume only. As the limit orders get filled the estimated liquidation price for the (updated) open volume converges to the estimate originally obtained with open sell orders. (0012-NP-LIPE-004) +1. An estimate for cross-margin mode with `include_collateral_increase_in_available_collateral` set to `true` is obtained for a short position with multiple limit buy orders with the absolute value of the total remaining size of the orders less than the open volume. The general account balance should be set to `0` in the query and the margin account balance should be set to the maintenance margin for the chosen position. The estimated liquidation price with buy orders is higher than that for the open volume only. As the limit orders get filled the estimated liquidation price for the (updated) open volume converges to the estimate originally obtained with open buy orders. As the price keeps moving in small increments the liquidation happens within the originally estimated range (with buy orders) (0012-NP-LIPE-005) +1. There's no difference in the estimate for an open volume (use `open_volume_only` field) and that with `0` open volume and market order of the same size (use `including_buy_orders` or `including_sell_orders` depending on the order side). (0012-NP-LIPE-006) +1. When margining mode gets successfully changed to isolated margin mode and party has non-zero general account balance afterwards than its liquidation price estimate for all cases (position only, with buy orders, with sell orders) moves closer to the current mark price (compared to cross-margin figure). (0012-NP-LIPE-007) +1. An estimate for isolated margin mode with `include_collateral_increase_in_available_collateral` set to `true` is obtained for a short position with open sell orders. As the price keeps moving in small increments the liquidation happens within the originally estimated range (`open_volume_only` estimate). The sell orders and order margin account balance remain unchanged. (0012-NP-LIPE-008) +1. An estimate for isolated margin mode with `include_collateral_increase_in_available_collateral` set to `true` is obtained for a long position with open buy orders. As the price keeps moving in small increments the liquidation happens within the originally estimated range (`open_volume_only` estimate). The buy orders and order margin account balance remain unchanged. (0012-NP-LIPE-009) diff --git a/non-protocol-specs/0013-NP-POSE-position-estimate.md b/non-protocol-specs/0013-NP-POSE-position-estimate.md new file mode 100644 index 000000000..b2c83f0b7 --- /dev/null +++ b/non-protocol-specs/0013-NP-POSE-position-estimate.md @@ -0,0 +1,58 @@ +# Position estimate + +## Summary + +Protocol provides an API endpoint which can estimate the following aspects of a theoretical position (open volume and open orders): + +- margin levels, +- expected collateral increase required to support the position, +- liquidation price. + +Each estimate is a range between a relevant figure obtained with the assumption of no slippage and maximum slippage configured for the market. + +## Details + +The endpoint does not access information related to any of the existing positions, it's based entirely on the information specified in the request. +The endpoint only distinguishes between market orders (these are assumed to fill instantly and in full at a current mark price) and limit orders (these are assumed to fill in full once mark price reaches their limit price). + +### Margin level + +Margin level estimate contains the levels specified in [0019-MCAL-margin_calculator](./../protocol/0019-MCAL-margin_calculator.md#reference-level-explanation) spec as well margin mode and margin factor (0 in cross margin mode). + +### Collateral increase estimate + +Collateral increase estimate provides an approximate difference between the current collateral and the resulting collateral for the specified theoretical position. + +In isolated margin mode it's the difference between collateral required to support the specified position and orders with the margin factor provided and the balance of margin and order margin accounts specified in the request. + +In cross-margin mode: + +- if the collateral required for the specified position is higher than the combined margin and order margin account balances then it's the difference between the initial margin level for the specified position and the sum of those account balances. +- if the collateral required for the specified position is lower than the combined margin and order margin account balances then: + - if the combined account balances are above the margin release level for the specified position: it's the difference between the initial margin level for the specified position and the sum of those account balances, + - otherwise it's `0`. + +### Liquidation price estimate + +Liquidation price estimate as specified in [0012-NP-LIPE-liquidation-price-estimate](./0012-NP-LIPE-liquidation-price-estimate.md). + +Depending on the [margining mode](../protocol/0019-MCAL-margin_calculator.md#margining-modes) selected by the party for the market on which its position is being considered the $\text{collateral available}$ will differ. + +Cross-margin mode: $\text{collateral available} = \text{margin account balance} + \text{general account balance} + \text{order margin account balance}$. + +Isolated margin mode: $\text{collateral available} = \text{margin account balance}$. + +The position estimate request has an additional `include_collateral_increase_in_available_collateral` argument. It's relevant for the isolated margin mode: when set to `false` the collateral available used in liquidation price estimate will be the margin account balance only. When set to `true` the portion of the collateral increase estimated for the specified position only (not for the additional orders) will also be included in the available collateral. + +The endpoint request contains additional optional argument `scale_liquidation_price_to_market_decimals`. When set to `false` the liquidation price estimates are scaled to asset decimal places, when set to `true` these estimates are scaled to market decimal places. + +## Acceptance criteria + +1. In isolated margin mode the request with `0` open volume and one or more limit orders specified results in a non-zero order margin in the margin level estimate and margin mode correctly representing isolated margin mode. (0013-NP-POSE-001) +1. When account balances are set to `0` and market has slippage factors set to `0`, the collateral increase figure per specified theoretical position correctly approximates (absolute relative difference of less than $10^{-6}$) the actual margin and order margin account balances for a party which opens such a position. (0013-NP-POSE-002) +1. For a market with slippage cap factor set to `0`, when the response for a given request contains figure `x` as the collateral increase (best and worst case should be the same) then resubmitting the request with margin account balance increased by `x` should result in `0` collateral increase estimate. When increasing the margin account balance in the request further the collateral increase should get negative by the amount of the increase when in isolated margin mode. In cross margin mode it should remain at `0` until the combined margin and order balances are above the margin release level for the theoretical position. Then the collateral increase amount should be negative and equal to: `initial margin level for the specified position - margin account balance - order account balance`. (0013-NP-POSE-009) +1. In isolated margin mode increasing general account balance specified in the request has no impact on the collateral increase estimate and the liquidation price estimate. (0013-NP-POSE-004) +1. In isolated margin mode the liquidation price estimate for a position with non-zero additional margin requirement with `include_collateral_increase_in_available_collateral` argument set to `true` results in liquidation price which is closer to the current mark price than the result obtained with argument set to `false`. (0013-NP-POSE-005) +1. When market is set with different number of decimal places then its settlement asset then setting `scale_liquidation_price_to_market_decimals` to `false` results in liquidation price estimates scaled to asset decimal places, when set to `true` these estimates get scaled to market decimal places. (0013-NP-POSE-006) +1. The estimates for open volume of `1` and no orders are the same as those for open volume of `0` and a single buy market order of size `1`. (0013-NP-POSE-007) +1. The estimates for open volume of `-1` and no orders are the same as those for open volume of `0` and a single sell market order of size `1`. (0013-NP-POSE-008) diff --git a/protocol/0001-MKTF-market_framework.md b/protocol/0001-MKTF-market_framework.md index 4a86e205b..1f4bc30d5 100644 --- a/protocol/0001-MKTF-market_framework.md +++ b/protocol/0001-MKTF-market_framework.md @@ -32,6 +32,7 @@ Data: - **Position Decimal Places**: number of decimal places for orders and positions, i.e. if this is 2 then the smallest increment that can be traded is 0.01, for example 0.01 BTC in a `BTSUSD` market. - If this is negative e.g. -3 this means that the smallest order and position is of size 1000. - Accepted values are `-6,...,-1,0,1,2,...,6`. +- **Liquidation strategy**: A field specifying the liquidation strategy for the market. Please refer to [0012-POSR-position_resolution](./0012-POSR-position_resolution.md#managing-networks-position) for supported strategies. Note: it is agreed that initially the integer representation of the full precision of both order and positions can be required to fit into an int64, so this means that the largest position/order size possible reduces by a factor of ten for every extra decimal place used. This also means that, for instance, it would not be possible to create a `BTCUSD` market that allows order/position sizes equivalent to 1 sat. @@ -157,7 +158,8 @@ struct InstrumentMetadata { } enum Product { - // maturity should be some sort of DateTime, settlement_asset is however we refer to crypto-assets (collateral) on Vega + // Oracle will include both info on how trading terminates and settlement data + // settlement_asset is asset id Future { oracle: Oracle, settlement_asset: String }, // EuropeanOption {}, // SmartProduct {}, @@ -216,9 +218,30 @@ Market { } ``` +## Successor market + +If a market proposal, see [governance](./0028-GOVE-governance.md), designates an existing market as a *parent market* then it must have the same *product*, *settlement asset(s)* and *margin asset(s)*. +It may propose new risk model and parameters, price monitoring parameters, position and market decimal places. +It must provide oracle definitions, both for trading terminated and for settlement data. +Each market can have exactly one market as a *successor* market. + +1. if there already is a market (possibly pending i.e. in opening auction, see [lifecycle spec](./0043-MKTL-market_lifecycle.md)) naming a parent market which is referenced in the proposal then the proposal is rejected. +1. if there are two proposals naming the same parent market then whichever one gets into the pending state first (i.e. passes governance vote) becomes the successor of the named parent; the other proposal is cancelled with reason "parent market not available". + + ## Acceptance criteria - Details of a market's instrument must be available for each market through the API (0001-MKTF-001) - Details of a market's product must be available for each market through the API (0001-MKTF-002) - Details of a market's tradable instrument must be available for each market through the API (0001-MKTF-003) - Market framework can report position decimal places 0001-MKTF-004 +- It is possible to designate a market as perpetual; this is visible via APIs in market data. + - GRPC 0001-MKTF-005 + - REST 0001-MKTF-011 + - GraphQL 0001-MKTF-012 +- A market may have a "parent" market; the parent market is visible via APIs in the form of the `marketID` of the parent market. 0001-MKTF-006 +- A market may have a "successor" market; the parent market is visible via APIs in the form of the `marketID` (or `proposalID`) of the successor market. 0001-MKTF-007 +- A parent and successor markets must have the same: + - product 0001-MKTF-008 + - settlement asset(s) 0001-MKTF-009 + - margin asset(s). 0001-MKTF-010 diff --git a/protocol/0002-STTL-settlement.md b/protocol/0002-STTL-settlement.md index 163c936a8..5ee9b2962 100644 --- a/protocol/0002-STTL-settlement.md +++ b/protocol/0002-STTL-settlement.md @@ -39,7 +39,7 @@ If all requested amounts are successfully transferred to the *market settlement #### Loss socialisation -If some of the collection transfers are not able to supply the full amount to the *market settlement account* due to some traders having insufficient collateral in their margin account and general account to handle the price / position (mark to market) move, and if the insurance pool can't cover the shortfall for some of these, then not enough funds will have been collected to distribute the full amount of the mark to market gains made by traders on the other side. Therefore, settlement needs to decide how to fairly distribute the funds that have been collected. This is called *loss socialisation*. +If some of the collection transfers are not able to supply the full amount to the *market settlement account* due to some traders having insufficient collateral in their margin account and general account to handle the price / position (mark to market) move, and if the market's insurance pool can't cover the shortfall for some of these, then not enough funds will have been collected to distribute the full amount of the mark to market gains made by traders on the other side. Therefore, settlement needs to decide how to fairly distribute the funds that have been collected. This is called *loss socialisation*. In future, a more sophisticated algorithm may be used for this (perhaps taking into account a trader's overall profit on their positions, for example) but initially this will be implemented by reducing the amount to distribute to each trader with an MTM gain pro-rata by relative position size: @@ -50,7 +50,7 @@ distribute_amount[trader] = mtm_gain[trader] * ( actual_collected_amount / targe ### Network orders -When a trader is distressed their position is closed out by the network placing an order to bring their position back to 0. This [network order](../protocol/0014-ORDT-order_types.md) will match against normal orders in the order book and will be part of a [mark-to-market settlement](./0003-MTMK-mark_to_market_settlement.md) action. As [the network user is a virtual user](./0017-PART-party.md#network-party) it does not have collateral accounts from which to provide or collect wins and loses. The [market insurance account](./0015-INSR-market_insurance_pool_collateral.md) is used in place of these. If a network order is settled as a win, the collateral will be transferred from the matched trader directly into the insurance account for the market. If the network order is a loss, the insurance pool will be used to pay the matched traders. [Loss socialisation](#loss-socialisation) is used if the insurance pool does not have enough collateral to cover the loss situation. +When a trader is distressed their position is closed out by the network placing an order to bring their position back to 0. This [network order](../protocol/0014-ORDT-order_types.md) will match against normal orders in the order book and will be part of a [mark-to-market settlement](./0003-MTMK-mark_to_market_settlement.md) action. As [the network user is a virtual user](./0017-PART-party.md#network-party) it does not have collateral accounts from which to provide or collect wins and loses. The [market insurance account](./0015-INSR-market_insurance_pool_collateral.md) is used in place of these. If a network order is settled as a win, the collateral will be transferred from the matched trader directly into the insurance account for the market. If the network order is a loss, the market's insurance pool will be used to pay the matched traders. [Loss socialisation](#loss-socialisation) is used if the market's insurance pool does not have enough collateral to cover the loss situation. ## Settlement at instrument expiry @@ -64,11 +64,11 @@ The [market lifecycle spec](./0043-MKTL-market_lifecycle.md) provides detail on ## Acceptance Criteria -### The typical "Happy Path" case (0002-STTL-001) +### The typical "Happy Path" case (0002-STTL-001,) - With a market configured to take an oracle termination time and settlement price and put into continuous trading mode. When there are traders with open positions on the market and the termination trigger from oracle is sent so the market is terminated. Send market settlement price and assert that it is no longer possible to trade on this market. -### Example 1 - A typical path of a cash settled futures market nearing expiry when market is trading in continuous session (0002-STTL-002) +### Example 1 - A typical path of a cash settled futures market nearing expiry when market is trading in continuous session (0002-STTL-011) 1. Market has a status of ACTIVE and is trading in default trading mode 1. The product's [trading terminated trigger is hit](./0016-PFUT-product_builtin_future.md#41-termination-of-trading) @@ -79,12 +79,12 @@ The [market lifecycle spec](./0043-MKTL-market_lifecycle.md) provides detail on 1. Any remaining balances in parties' margin and LP bond accounts are moved to their general account. 1. The margin accounts and LP bond accounts for these markets are no longer required. 1. Positions can be left as open, or set to zero (this isn't important for the protocol but should be made clear on the API either way). -1. The market's insurance pool is [redistributed](./0015-INSR-market_insurance_pool_collateral.md) to the on-chain treasury for the settlement asset of the market and other insurance pools using the same asset. +1. The market's [insurance pool balance is transferred](./0015-INSR-market_insurance_pool_collateral.md) into the global insurance pool using the same settlement asset. 1. Market status is now set to [SETTLED](./0043-MKTL-market_lifecycle.md). 1. Now the market can be deleted. 1. This mechanism does not incur fees to traders that have open positions that are settled at expiry. (0002-STTL-003) -### Example 2 - A less typical path of such a futures market nearing expiry when market is suspended (0002-STTL-004) +### Example 2 - A less typical path of such a futures market nearing expiry when market is suspended (0002-STTL-012) 1. Market has a status of SUSPENDED and in a protective auction 1. The product's [trading terminated trigger is hit](./0016-PFUT-product_builtin_future.md#41-termination-of-trading) @@ -95,7 +95,7 @@ The [market lifecycle spec](./0043-MKTL-market_lifecycle.md) provides detail on 1. Any remaining balances in parties' margin and LP bond accounts are moved to their general account. 1. The margin accounts and LP bond accounts for these markets are no longer required. 1. Positions can be left as open, or set to zero (this isn't important for the protocol but should be made clear on the API either way). -1. The market's insurance pool is [redistributed](./0015-INSR-market_insurance_pool_collateral.md) to the on-chain treasury for the settlement asset of the market and other insurance pools using the same asset. +1. The [market's insurance pool](./0015-INSR-market_insurance_pool_collateral.md) balance is transferred into the global insurance pool using the same settlement asset. 1. Market status is now set to [SETTLED](./0043-MKTL-market_lifecycle.md). 1. Now the market can be deleted. 1. This mechanism does not incur fees to traders that have open positions that are settled at expiry. (0002-STTL-005) @@ -107,7 +107,7 @@ The [market lifecycle spec](./0043-MKTL-market_lifecycle.md) provides detail on 1. If margin and general account of trader are insufficient to cover collateral transfers, then collateral is attempted to be taken from market's insurance pool. (0002-STTL-008) 1. If the full required amount for collateral cannot be collected from individual or combination of these accounts, then as much as possible in the above sequence of accounts is collected and loss socialisation occurs. (0002-STTL-009) -### Example 3 - Settlement data to cash settled future is submitted before trading is terminated (0002-STTL-010) +### Example 3 - Settlement data to cash settled future is submitted before trading is terminated (0002-STTL-013) 1. A [cash settled futures](0016-PFUT-product_builtin_future.md) market has a status of ACTIVE and is trading in default trading mode (continuous trading) 1. An [oracle event occurs](./0045-DSRC-data_sourcing.md) that is eligible to settle the market, as defined on the [Product](./0001-MKTF-market_framework.md) (see also [cash settled futures spec](./0016-PFUT-product_builtin_future.md)). In other words the settlement price is submitted to the market before trading is terminated. @@ -124,6 +124,6 @@ All of that happens while processing the trading terminated transaction. 1. Any remaining balances in parties' margin and LP bond accounts are moved to their general account. 1. The margin accounts and LP bond accounts for these markets are no longer required. 1. Positions can be left as open, or set to zero (this isn't important for the protocol but should be made clear on the API either way). -1. The market's insurance pool is [redistributed](./0015-INSR-market_insurance_pool_collateral.md) to the on-chain treasury for the settlement asset of the market and other insurance pools using the same asset. +1. The [market's insurance pool](./0015-INSR-market_insurance_pool_collateral.md) balance is transferred into the global insurance pool using the same settlement asset. 1. Market status is now set to [SETTLED](./0043-MKTL-market_lifecycle.md). 1. Now the market can be deleted. diff --git a/protocol/0003-MTMK-mark_to_market_settlement.md b/protocol/0003-MTMK-mark_to_market_settlement.md index 950c9068b..08ce35ec5 100644 --- a/protocol/0003-MTMK-mark_to_market_settlement.md +++ b/protocol/0003-MTMK-mark_to_market_settlement.md @@ -22,6 +22,10 @@ 1. If the mark price hasn't changed: 1. A trader with no change in open position size has no transfers in or out of their margin account (0003-MTMK-012) 1. An aggressive order to buy 2 units at 1010 which matches with two passive orders each of size one resting on the book with prices of 1000 and 1010 results in a transfer of 10 flowing from the party with order priced at 1000 to the aggressive party during the next MTM settlement 0003-MTMK-013) +1. If the network party has a non-zero position and the mark-price moves in favour of the network's position gains from mark-to-market settlement should be paid into the market insurance pool. (0003-MTMK-015) +1. If the network party has a non-zero position and the mark-price moves against the network's position, losses from mark-to-market settlement should be paid from the market insurance pool. (0003-MTMK-016) +1. If the network does not have enough funds in the market insurance pool to cover its mark-to-market losses, loss-socialisation occurs. (0003-MTMK-017) + ## Market with position decimal places > 0 scenario @@ -69,7 +73,7 @@ SETTLEMENT_AMT( party ) = PDPscaling x party.PREV_OPEN_VOLUME x (product.value( 1. The settlement function calculates how much to *collect* from the margin accounts of those whose change in mark to market has been negative / incurred loss. This will be collected into the market's *margin* account via a set of transfer requests. The collection instruction should first collect from a trader's margin account for the market and then the trader's general account and then the market's insurance pool. 1. This will result in ledger entries being formulated ( see [settlement](./0002-STTL-settlement.md) ) which adhere to double entry accounting and record the actual transfers that occurred on the ledger. 1. If the net amounts are what was requested, the settlement function will formulate instructions to *distribute* to the margin accounts of those whose moves have been positive according to the amount they are owed. These transfers will be requested to debit from the market's *margin* account and credit the traders who have a positive mark-to-market. -1. If there's not enough money for the reallocation due to some traders having insufficient money in their margin account and general account to handle the price / position move, and if the insurance pool can't cover the full *distribute* requirements, the settlement function will need to alter the "distribute" amounts accordingly. The amounts are altered using a formula which is out of scope for this ticket. As a stub implementation distribution can pro-rata the amount in the settlement account between positions by relative position size. +1. If there's not enough money for the reallocation due to some traders having insufficient money in their margin account and general account to handle the price / position move, and if the market's insurance pool can't cover the full *distribute* requirements, the settlement function will need to alter the "distribute" amounts accordingly. The amounts are altered using a formula which is out of scope for this ticket. As a stub implementation distribution can pro-rata the amount in the settlement account between positions by relative position size. Note: a close out profit / loss is automatically captured in trader margin accounts via mark-to-market reallocation described above. The reduction in open volume will be used when risk margins are next calculated. diff --git a/protocol/0004-AMND-amends.md b/protocol/0004-AMND-amends.md index 6a162c945..f383e0b50 100644 --- a/protocol/0004-AMND-amends.md +++ b/protocol/0004-AMND-amends.md @@ -2,36 +2,40 @@ ## Acceptance Criteria -- Only LIMIT or PEGGED orders can be amended. Any attempt to amend a MARKET order is rejected (0004-AMND-001) -- Price change amends remove the order from the book and insert the order at the back of the queue at the new price level (0004-AMND-002) -- Reducing the quantity leaves the order in its current spot but reduces the remaining amount accordingly (0004-AMND-003) -- Quantity after amendment must be a multiple of the smallest increment possible given the `Position Decimal Places` (PDP) specified in the [Market Framework](./0001-MKTF-market_framework.md), i.e. is PDP = 2 then quantity must be a whole multiple of 0.01. (0004-AMND-004) -- Increasing the quantity causes the order to be removed from the book and inserted at the back of the price level queue with the updated quantity (0004-AMND-005) -- Changing the `TIF` can only occur between `GTC` and `GTT`. Any attempt to amend to another `TIF` flag is rejected. A `GTT` must have an `expiresAt` value but a `GTC` must not have one. (0004-AMND-006) -- Any attempt to amend to or from the `TIF` values `GFA` and `GFN` will result in a rejected amend. (0004-AMND-007) -- All updates to an existing order update the `UpdatedAt` time stamp field in the order (0004-AMND-008) -- The `orderID` remains the same after an amend (0004-AMND-009) -- Amends can occur in continuous trading or in an auction (0004-AMND-010) -- All historic alteration to an order can be viewed from the order storage system (0004-AMND-011) -- All amendable fields can be amended in the same amend message (0004-AMND-012) -- Fields left with default values (0) are not handled as part of the amend action (0004-AMND-013) -- An amend with only the same values as the order still cause the `UpdateAt` field to update but nothing else (0004-AMND-014) -- Amending a pegged orders offset or reference will force a reprice (0004-AMND-015) -- Attempting to alter pegged details on a non pegged or will cause the amend to be rejected (0004-AMND-016) -- A parked pegged order can be amended. (0037-OPEG-014) -- Attempting to alter details on a filled order will cause the amend to be rejected (0004-AMND-017) -- Attempting to alter details on a cancelled order will cause the amend to be rejected (0004-AMND-018) -- Attempting to alter details on an expired order will cause the amend to be rejected (0004-AMND-019) -- Amending expiry time of an active GTT order to a past time whilst also simultaneously amending the price of the order will cause the order to immediately expire with the order details updated to reflect the order details requiring amendment (0004-AMND-029) +- Only LIMIT or PEGGED orders can be amended. Any attempt to amend a MARKET order is rejected (0004-AMND-001). +- Price change amends remove the order from the book and insert the order at the back of the queue at the new price level (0004-AMND-002). +- Reducing the quantity by specifying a `sizeDelta` leaves the order in its current spot but reduces the remaining amount accordingly (0004-AMND-003). +- Quantity after amendment using a `sizeDelta` must be a multiple of the smallest increment possible given the `Position Decimal Places` (PDP) specified in the [Market Framework](./0001-MKTF-market_framework.md), i.e. is PDP = 2 then quantity must be a whole multiple of 0.01. (0004-AMND-004). +- Increasing the quantity by specifying a `sizeDelta` causes the order to be removed from the book and inserted at the back of the price level queue with the updated quantity (0004-AMND-005). +- Size change amends specifying a `size` greater than the current size remove and reinsert the order at the back of the price level and increase the remaining amount accordingly (0004-AMND-056). +- Size change amends specifying a `size` lower than the current size leave the order in its current spot and reduce the remaining amount of the order accordingly (0004-AMND-057). +- Size change amends which would result in the remaining part of the order being reduced below zero should instead cancel the order (0004-AMND-058). +- A transaction specifying both a `sizeDelta` and `size` field should be rejected as invalid (0004-AMND-059). +- Changing the `TIF` can only occur between `GTC` and `GTT`. Any attempt to amend to another `TIF` flag is rejected. A `GTT` must have an `expiresAt` value but a `GTC` must not have one. (0004-AMND-006). +- Any attempt to amend to or from the `TIF` values `GFA` and `GFN` will result in a rejected amend. (0004-AMND-007). +- All updates to an existing order update the `UpdatedAt` time stamp field in the order (0004-AMND-008). +- The `orderID` remains the same after an amend (0004-AMND-009). +- Amends can occur in continuous trading or in an auction (0004-AMND-010). +- All historic alteration to an order can be viewed from the order storage system (0004-AMND-011). +- All amendable fields can be amended in the same amend message (0004-AMND-012). +- Fields left with default values (0) are not handled as part of the amend action (0004-AMND-013). +- An amend with only the same values as the order still cause the `UpdateAt` field to update but nothing else (0004-AMND-014). +- Amending a pegged orders offset or reference will force a reprice (0004-AMND-015). +- Attempting to alter pegged details on a non pegged or will cause the amend to be rejected (0004-AMND-016). +- A parked pegged order can be amended. (0037-OPEG-014). +- Attempting to alter details on a filled order will cause the amend to be rejected (0004-AMND-017). +- Attempting to alter details on a cancelled order will cause the amend to be rejected (0004-AMND-018). +- Attempting to alter details on an expired order will cause the amend to be rejected (0004-AMND-019). +- Amending expiry time of an active GTT order to a past time whilst also simultaneously amending the price of the order will cause the order to immediately expire with the order details updated to reflect the order details requiring amendment (0004-AMND-029). For a party with no position on a given market: -- amending an order in a way that increases the volume sufficiently leads to margin account balance increasing (0004-AMND-021) +- Amending an order in a way that increases the volume sufficiently leads to margin account balance increasing (0004-AMND-021) - Amending an order in a way that decreases the volume sufficiently leads to margin account balance decreasing (0004-AMND-022) -- It is possible to amend a fractional size order (0004-AMND-025) -- It is possible to amend a partially filled limit order (0004-AMND-026) -- It is possible to amend a versioned order (already amended several times) (0004-AMND-027) -- Attempts to amend order fields not in scope are rejected (0004-AMND-028) +- It is possible to amend a fractional size order (0004-AMND-025). +- It is possible to amend a partially filled limit order (0004-AMND-026). +- It is possible to amend a versioned order (already amended several times) (0004-AMND-027). +- Attempts to amend order fields not in scope are rejected (0004-AMND-028). ## Summary @@ -40,7 +44,7 @@ The amend order can alter the quantity, price and expiry time/`TIF` type. For pe ## Guide-level explanation -The amend order message is a custom message containing the `orderID` of the original order and optional fields that can be altered. Prices can be changed with a new absolute value, quantity can be reduced or increased from their current remaining size. Expiry time can be set to a new value and the `TIF` type can be toggled between `GTC` and `GTT`. Changing the `TIF` field will impact the value in the `ExpiryTime` field as it will either be blanked or set to a new valid value. +The amend order message is a custom message containing the `orderID` of the original order and optional fields that can be altered. Prices can be changed with a new absolute value, sizes can be changed by either specifying a size delta or a new absolute value. If a user attempts to amend the size of an order by specifying both a size delta and anew absolute value the order should be rejected. Expiry time can be set to a new value and the `TIF` type can be toggled between `GTC` and `GTT`. Changing the `TIF` field will impact the value in the `ExpiryTime` field as it will either be blanked or set to a new valid value. Some examples: A LIMIT order sitting on the bid side of the order book: @@ -56,6 +60,15 @@ amendOrder{ } ``` +or + +```json +amendOrder{ + orderID: "V0000000001-0000000001" + size: 300 +} +``` + This will be the resulting order book: `Bids: 300@1000 GTC (OrderID V0000000001-0000000001)` @@ -84,7 +97,9 @@ The fields which can be altered are: - `Price` - Amending the price causes the order to be removed from the book and re-inserted at the new price level. This can result in the order being filled if the price is moved to a level that would cross. - `SizeDelta` - - A size change is specified as a delta to the current amount. This will be applied to both the `Size` and `Remaining` part of the order. In the case that the remaining amount it reduced to zero or less, the order is cancelled. This must be a multiple of the smallest value allowed by the `Position Decimal Places` (PDP) specified in the [Market Framework](./0001-MKTF-market_framework.md), i.e. is PDP = 2 then `SizeDelta` must be a whole multiple of 0.01. (NB: `SizeDelta` may use an int64 where the int value 1 is the smallest multiple allowable given the configured dp). In case PDP is negative this again applies e.g. if PDP = -1 then `SizeDelta` must be a whole multiple of 10. + - Amending the size by specifying a `sizeDelta` will be applied to both the `Size` and `Remaining` part of the order. In the case that the remaining amount it reduced to zero or less, the order is cancelled. This must be a multiple of the smallest value allowed by the `Position Decimal Places` (PDP) specified in the [Market Framework](./0001-MKTF-market_framework.md), i.e. is PDP = 2 then `SizeDelta` must be a whole multiple of 0.01. (NB: `SizeDelta` may use an int64 where the int value 1 is the smallest multiple allowable given the configured dp). In case PDP is negative this again applies e.g. if PDP = -1 then `SizeDelta` must be a whole multiple of 10. +- `Size` + - Amending the size by specifying a new `size` causes the the `Size` and `Remaining` part of the order to be amended by the difference between the original and amended size. In the case that the remaining amount it reduced to zero or less, the order is cancelled. This must be a multiple of the smallest value allowed by the `Position Decimal Places` (PDP) specified in the [Market Framework](./0001-MKTF-market_framework.md), i.e. is PDP = 2 then `Size` must be a whole multiple of 0.01. (NB: `Size` may use an int64 where the int value 1 is the smallest multiple allowable given the configured dp). In case PDP is negative this again applies e.g. if PDP = -1 then `Size` must be a whole multiple of 10. - `TimeInForce` - The `TIF` enumeration can only be toggled between `GTT` and `GTC`. Amending to `GTT` requires an `expiryTime` value to be set. Amending to `GTC` removes the `expiryTime` value. - `ExpiryTime` @@ -108,10 +123,11 @@ message amendOrder { enum TIF 4; int64 expiryTime 5; PeggedOrder *peggedOrder 6; + int64 size 7; } ``` -An example of using a negative size is shown below: +An example of reducing the size is shown below: `Bids: 100@1000 GTC (OrderID V0000000001-0000000001)` @@ -122,6 +138,13 @@ amendOrder{ orderID:"V0000000001-0000000001", sizeDelta: -50 } ``` +or + +```json +amendOrder{ orderID:"V0000000001-0000000001", + size: 50 } +``` + The resulting order book will be: `Bids: 50@1000 GTC (OrderID V0000000001-0000000001)` diff --git a/protocol/0005-COLL-collateral.md b/protocol/0005-COLL-collateral.md index eb771c24c..c0cd35005 100644 --- a/protocol/0005-COLL-collateral.md +++ b/protocol/0005-COLL-collateral.md @@ -34,7 +34,7 @@ Data Structures ```json TransferRequest { - from: [Account], // This is an array of accounts in order of precedence, e.g. the first account in the list is emptied first when making transfers. For settlement at expiry scenarios, transferRequests will be sequenced to access 1. the trader's margin account for the Market, 2. the trader's collateral account and 3. the insurance pool. + from: [Account], // This is an array of accounts in order of precedence, e.g. the first account in the list is emptied first when making transfers. For settlement at expiry scenarios, transferRequests will be sequenced to access 1. the trader's margin account for the Market, 2. the trader's collateral account and 3. the market's insurance pool. to: Account, amount: FinancialAmount, reference: ???, // some way to link back to the causal event that created this transfer diff --git a/protocol/0008-TRAD-trading_workflow.md b/protocol/0008-TRAD-trading_workflow.md index 6eb0f2ceb..61ae1f8e4 100644 --- a/protocol/0008-TRAD-trading_workflow.md +++ b/protocol/0008-TRAD-trading_workflow.md @@ -10,7 +10,7 @@ The order of processing of transactions happens in the order defined in the diag 1. Before a valid order is processed in any other way by Vega, the [party](./0017-PART-party.md)'s [margin levels](./0011-MARA-check_order_allocate_margin.md) are checked as though they had the order in their position, and any transfers that are needed to support that order occur. (0008-TRAD-001) 1. Following all of the matching of trades resulting from a single order or the acceptance of an order onto the order book, there will be changes in positions for one or more traders. (0008-TRAD-002) -1. Following all of the matching of trades resulting from a single order or the acceptance of an order onto the order book, there may be a change to the Mark Price. (0008-TRAD-003) +1. Following all of the matching of trades resulting from a single order or the acceptance of an order onto the order book, there may be a change to the Mark Price. (0008-TRAD-003). 1. Following the above 3 actions, a [mark to market settlement](./0003-MTMK-mark_to_market_settlement.md) is run for all parties against their most recently updated positions and Mark Price. This will result in a set of transfers between the parties' accounts and possibly may result in loss socialisation occurring if a party has insufficient collateral to cover the move. (0008-TRAD-004) 1. Following the mark to market settlement, the margin liabilities for traders are evaluated against their collateral balances. Any traders that do not have sufficient collateral to support their positions (after collateral searches have been done to their main account) will undergo position resolution. (0008-TRAD-005) 1. After position resolution has occurred, margins are recalculated and evaluated against balances for any traders that gained positions as a result of supplying liquidity on the order book to the network during position resolution. (0008-TRAD-006) diff --git a/protocol/0009-MRKP-mark_price.md b/protocol/0009-MRKP-mark_price.md index 6c2f981ea..044624fbc 100644 --- a/protocol/0009-MRKP-mark_price.md +++ b/protocol/0009-MRKP-mark_price.md @@ -6,68 +6,154 @@ A *Mark Price* is a concept derived from traditional markets. It is a calculate Introduce a network parameter `network.markPriceUpdateMaximumFrequency` with minimum allowable value of `0s` maximum allowable value of `1h` and a default of `5s`. +The *Mark Price* represents the "current" market value for an instrument that is being traded on a market on Vega. It is a calculated value primarily used to value trader's open portfolios against the prices they executed their trades at. Specifically, it is used to calculate the cash flows for [mark-to-market settlement](./0003-MTMK-mark_to_market_settlement.md). + +The mark price is instantiated when a market opens via the [opening auction](./0026-AUCT-auctions.md). + +It will subsequently be calculated according to a methodology selected from a suite of algorithms. The selection of the algorithm for calculating the *Mark Price* is specified at the "market" level as a market parameter. + +## Algorithms for calculating the *Mark Price* + +### 1. Last Traded Price of a Sequence with same time stamp with maximum frequency set by `network.markPriceUpdateMaximumFrequency` + +The mark price for the instrument is set to the last trade price in the market following processing of each transaction (i.e. submit/amend/delete order) from a sequence of transactions with the same time stamp, provided that at least `network.markPriceUpdateMaximumFrequency` has elapsed since the last mark price update. + +>*Example:* Assume `network.markPriceUpdateMaximumFrequency = 10s`. + +Consider the situation where the mark price was last updated to $900 and this was 12s ago. There is a market sell order for -20 and a market buy order for +100 with the same time stamp. The sell order results in two trades: 15 @ 920 and 5 @ 910. The buy order results in 3 trades; 50 @ $1000, 25 @ $1100 and 25 @ $1200, the mark price changes **once** to a new value of $1200. + +Now 8s has elapsed since the last update and there is a market sell order for volume 3 which executes against book volume as 1 @ 1190 and 2 @ 1100. +The mark price isn't updated because `network.markPriceUpdateMaximumFrequency = 10s` has not elapsed yet. + +Now 10.1s has elapsed since the last update and there is a market buy order for volume 5 which executes against book volume as 1 @ 1220, 2 @ 1250 and 2 @ 1500. The mark price is updated to 1500. + +### 2. Flexible mark price methodology + +The calculations are specified in [composite prices spec](./0089-COMP-composing_prices.md). + +Here, we only write the acceptance criteria. +Note that for calculating the median with an even number of entries we sort, pick out the two values that are in the middle of the list and average those. So in particular with two values a median is the same as the average for our purposes. + ## Acceptance criteria -- The mark price must be set when the market leaves opening auction (0009-MRKP-002) +- The mark price must be set when the market leaves opening auction. (0009-MRKP-002) +- Each time the mark price changes the market data event containing the new mark price should be emitted.Specifically, the mark price set after leaving each auction, every interim mark price as well as the mark price based on last trade used at market termination and the one based on oracle data used for final settlement should all be observable from market data events. (0009-MRKP-009) +- If a market mark price is configured in such a way that the mark price methodology hasn't provided a price at the point of uncrossing the opening auction, then the auction uncrossing price is set as the first mark price, regardless of what the mark price methodology says. (0009-MRKP-001) and a perps market (0009-MRKP-003) -Algorithm 1: +### Algorithm 1 (last trade price, excluding network trades) -- If `network.markPriceUpdateMaximumFrequency=0s` then any transaction that results in one or more trades causes the mark price to change to the value of the last trade and only the last trade. (0009-MRKP-003) - If `network.markPriceUpdateMaximumFrequency>0` then out of a sequence of transactions with the same time-stamp the last transaction that results in one or more trades causes the mark price to change to the value of the last trade and only the last trade but only provided that at least `network.markPriceUpdateMaximumFrequency` has elapsed since the last update. (0009-MRKP-007) - A transaction that doesn't result in a trade does not cause the mark price to change. (0009-MRKP-004) - A transaction out of a sequence of transactions with the same time stamp which isn't the last trade-causing transaction will *not* result in a mark price change. (0009-MRKP-008) -- The mark price must be using market decimal place setting (0009-MRKP-006) +- The mark price must be using market decimal place setting. (0009-MRKP-006) +- It is possible to configure a cash settled futures market to use Algorithm 1 (ie last trade price) (0009-MRKP-010) and a perps market (0009-MRKP-011). -## Guide-level explanation +### Flexible mark price methodology, no combinations yet -The *Mark Price* represents the "current" market value for an instrument that is being traded on a market on Vega. It is a calculated value primarily used to value trader's open portfolios against the prices they executed their trades at. Specifically, it is used to calculate the cash flows for [mark-to-market settlement](./0003-MTMK-mark_to_market_settlement.md). +#### Trade-size-weighted average price -Note that a mark price may not be a true "price" in the financial sense of the word, i.e. if the `price` changes by `X` then the value of a position does not necessarily change by `X * position size`, instead, the product's quote-to-value function must be used to ascertain the change, i.e. `change_in_value == product.value(new_mark_price) - product.value(old_mark_price)`. +- It is possible to configure a cash settled futures market to use weighted average of trades over `network.markPriceUpdateMaximumFrequency` with decay weight `1` and power `1` (linear decay) (0009-MRKP-012) and a perps market (0009-MRKP-013). +- when choosing price type `weight` and set weight on `trade-size-weight price` only, set decay weight to `0` and decay power to `1`, and set up trade at price `15920`, `15940`, `15960`, `15990` at various size, and check if the weighted average mark price is corrected calculated (0009-MRKP-110) and on perp market (0009-MRKP-111) +-when choosing price type `weight` and set weight on `trade-size-weight price` only, set decay weight to `0` and decay power to `1`, and set up trade at price `15940`, `15960`, `15990` at various size, and check if the weighted average mark price is corrected calculated (0009-MRKP-112) and on perp market (0009-MRKP-113) +- when choosing price type `weight` and set weight on `trade-size-weight price` only, set decay weight to `1` and decay power to `1`, and set up trade at price `15920`, `15940`, `15960`, `15990` at various size, and check if the weighted average mark price is corrected calculated (0009-MRKP-114) and on perp market (0009-MRKP-115) +- when choosing price type `weight` and set weight on `trade-size-weight price` only, set decay weight to `1` and decay power to `0`, and set up trade at price `15920`, `15940`, `15960`, `15990` at various size, and check if the weighted average mark price is corrected calculated (0009-MRKP-116) and on perp market (0009-MRKP-117) +- when choosing price type `weight` and set weight on `trade-size-weight price` only, set decay weight to `0.5` and decay power to `1`, and set up trade at price `15920`, `15940`, `15960`, `15990` at various size, and check if the weighted average mark price is corrected calculated (0009-MRKP-118) and on perp market (0009-MRKP-119) -## Reference-level explanation +#### Leverage-notional book price -The mark price is instantiated when a market opens via the [opening auction](./0026-AUCT-auctions.md). +- It is possible to configure a cash settled futures market to use impact of leveraged notional on the order book with the value of USDT `100` for mark price (0009-MRKP-014) and a perps market (0009-MRKP-015). +- when choosing price type `weight` and set weight on `leverage-notional book price` only, set cash amount to `100` (which should make the notional volume at sell and buy round to `0`), and place a few orders on the book with best bid `15900` and best ask `16000` and the leverage-notional book price should be the mid-price (0009-MRKP-120) and on perp market (0009-MRKP-121) +- when choosing price type `weight` and set weight on `leverage-notional book price` only, set cash amount to `100,000` and place a few orders on the book with best bid `15900` and best ask `16000` and check if leverage-notional book price is corrected calculated (0009-MRKP-122) and on perp market (0009-MRKP-123) +- when choosing price type `weight` and set weight on `leverage-notional book price` only, set cash amount to `5,000,000` (which should make the notional volume too big for the book) and place a few orders on the book with best bid `15900` and best ask `16000` and the leverage-notional book price should be the mid-price (0009-MRKP-124) and on perp market (0009-MRKP-125) +- when choosing price type `weight` and set weight on `leverage-notional book price` only, set cash amount to `100,000` and place a few orders on the book with best bid `15900` and best ask `16000` and check leverage-notional book price, then change the order book by placing a trade, and check if the leverage-notional book price is updated when the time indicated by the mark price frequency is crossed(0009-MRKP-126) and on perp market (0009-MRKP-127) -It will subsequently be calculated according to a methodology selected from a suite of algorithms. The selection of the algorithm for calculating the *Mark Price* is specified at the "market" level as a market parameter. +#### Oracle source price -Mark price can also be adjusted by [product lifecycle events](./0051-PROD-product.md), in which case the mark price set by the product remains the mark price until it is next recalculated (e.g. if the methodology is last traded price, until there is a new trade). +- It is possible to configure a cash settled futures market to use an oracle source for the mark price (0009-MRKP-016) and a perps market (with the oracle source different to that used for the external price in the perps market) (0009-MRKP-017). -## Usage of the *Mark Price* +- when choosing price type `weight` and set weight on `oracle source price` only, set up 2 oracle prices at different time, and check if the oracle mark price is correctly calculated (0009-MRKP-130) -The most recently calculated *Mark Price* is used in the [mark-to-market settlement](./0003-MTMK-mark_to_market_settlement.md). A change in the *Mark Price* is one of the triggers for the mark-to-market settlement to run. +- when choosing price type `weight` and set weight on `oracle source price` only, set up oracle prices at different time, and check if the oracle mark price updated according to is correctly calculated according to `network.markPriceUpdateMaximumFrequency` (0009-MRKP-131) -## Algorithms for calculating the *Mark Price* +- when choosing price type `weight` and set weight on `oracle source price` only, set up 3 oracle prices at different time with 1 of them becomes stale, and check if the oracle mark price updated according to is correctly calculated by using the right price (0009-MRKP-132) -### 1. Last Traded Price of a Sequence with same time stamp with maximum frequency set by `network.markPriceUpdateMaximumFrequency` +- It is possible to obtain a time series for the market mark price from the data node from the time of the market proposal enactment onwards (subject to data node retention policies).(0009-MRKP-133) + + +### Flexible mark price methodology, combinations + +- It is possible to configure a cash settled futures market to use a weighted average of 1. weighted average of trades over `network.markPriceUpdateMaximumFrequency` and 2. impact of leveraged notional on the order book with the value of USDT `100` and 3. an oracle source and if last trade is last updated more than 1 minute ago then it is removed and the remaining re-weighted and if the oracle is last updated more than 5 minutes ago then it is removed and the remaining re-weighted (0009-MRKP-018) and a perps market (with the oracle source different to that used for the external price in the perps market) (0009-MRKP-019). + +- It is possible to configure a cash settled futures market to use a weighted average of 1. weighted average of trades over `network.markPriceUpdateMaximumFrequency` and 2. impact of leveraged notional on the order book with the value of USDT `100` and if last trade is last updated more than 1 minute ago then it is removed and the remaining re-weighted and if the oracle is last updated more than 5 minutes ago then it is removed and the remaining re-weighted and if both sources are stale than the mark price stops updating (0009-MRKP-020) and a perps market (0009-MRKP-021). + +- It is possible to configure a cash settled futures market to use a median of 1. weighted average of trades over `network.markPriceUpdateMaximumFrequency` and 2. impact of leveraged notional on the order book with the value of USDT `100` and 3. an oracle source and if last trade is last updated more than 1 minute ago then it is removed and if the oracle is last updated more than 5 minutes ago then it is removed (0009-MRKP-022) and a perps market (with the oracle source different to that used for the external price in the perps market) (0009-MRKP-023). + +- When market is leaving auction (including opening auction and monitoring auction), mark price should be recalculated (0009-MRKP-024) and a perps market (with the oracle source different to that used for the external price in the perps market) (0009-MRKP-025). + +- When market is at monitoring auction, book price should be indicative uncrossing price, mark price should only be recalculated when the auction exits, starting from only the last period indicated by `network.markPriceUpdateMaximumFrequency` (0009-MRKP-026) and a perps market (with the oracle source different to that used for the external price in the perps market) (0009-MRKP-027). + +- It is possible to configure a cash settled futures market to use a weighted average of 1. weighted average of trades over `network.markPriceUpdateMaximumFrequency` and 2. impact of leveraged notional on the order book with the value of USDT `100` and when the book does not have enough volume, then the book price should not be included (0009-MRKP-028) and a perps market (with the oracle source different to that used for the external price in the perps market) (0009-MRKP-029). + +- It is possible to configure a cash settled futures market to use a weighted average of 1. weighted average of trades over `network.markPriceUpdateMaximumFrequency` and 2. impact of leveraged notional on the order book with the value of USDT `0` and the book price should be mid price (0009-MRKP-030) and a perps market (with the oracle source different to that used for the external price in the perps market) (0009-MRKP-031). +- Set price type to "median", only have data source available from "Trade-size-weighted average price" and "Leverage-notional book price" and 1 trade at 15920, check the mark price is correctly calculated (0009-MRKP-032) and a perps market (0009-MRKP-033) + +- Set price type to "median", only have data source available from "Trade-size-weighted average price" and "Leverage-notional book price" and 1 trade at 15920, and 1 trade at 15940, move time, and check stale price should not be included (0009-MRKP-034) and a perps market (0009-MRKP-035) + +- A market can be configured with `markPriceConfiguration: price type` is `Weighted` without oracles (0009-MRKP-060) + +- A market can be configured with `markPriceConfiguration: price type` is `Median` without oracles (0009-MRKP-061) + +### Validation + +- Boundary values are respected for the market parameters + + - `markPriceConfiguration: decayWeight` valid values: `[0,1]`(0009-MRKP-050) + + - `markPriceConfiguration: decayPower` valid values: `{1,2,3}`(0009-MRKP-051) + + - `markPriceConfiguration: cashAmount` valid values: `>=0`(0009-MRKP-052) + + - `markPriceConfiguration: source weight` valid values: `>=0`(0009-MRKP-053) - The mark price for the instrument is set to the last trade price in the market following processing of each transaction (i.e. submit/amend/delete order) from a sequence of transactions with the same time stamp, provided that at least `network.markPriceUpdateMaximumFrequency` has elapsed since the last mark price update. + - `markPriceConfiguration: source staleness tolerance` valid values: `valid duration string, e.g. "5s", "24h"`(0009-MRKP-054) - >*Example:* Assume `network.markPriceUpdateMaximumFrequency = 10s`. + - `markPriceConfiguration: source weight` and `markPriceConfiguration: source staleness tolerance` should have the same length(0009-MRKP-055) - Consider the situation where the mark price was last updated to $900 and this was 12s ago. There is a market sell order for -20 and a market buy order for +100 with the same time stamp. The sell order results in two trades: 15 @ 920 and 5 @ 910. The buy order results in 3 trades; 50 @ $1000, 25 @ $1100 and 25 @ $1200, the mark price changes **once** to a new value of $1200. + - Mark price configuration `source_weight` length should be 3 plus number of oracle data sources if the price type is weighted (0009-MRKP-056). - Now 8s has elapsed since the last update and there is a market sell order for volume 3 which executes against book volume as 1 @ 1190 and 2 @ 1100. - The mark price isn't updated because `network.markPriceUpdateMaximumFrequency = 10s` has not elapsed yet. + - If the mark price type is not weighted the source weight must be empty (0009-MRKP-062). - Now 10.1s has elapsed since the last update and there is a market buy order for volume 5 which executes against book volume as 1 @ 1220, 2 @ 1250 and 2 @ 1500. The mark price is updated to 1500. + - Mark price configuration `source staleness tolerance` length must be 3 plus number of oracle data sources if price type is weighted or median (0009-MRKP-063). -### 2. Last Traded Price + Order Book + - If the mark price type is weighted, there must be at least one non zero weight (0009-MRKP-064). -The mark price is set to the higher / lower of the last traded price, bid/offer. + - When `markPriceConfiguration: price type` is **not** `Last Trade Price`, the `markPriceConfiguration: source staleness tolerance`, `markPriceConfiguration: source weight`, `markPriceConfiguration: decayPower` and `markPriceConfiguration: cashAmount` must be provided + - new market (0009-MRKP-057) + - update market (0009-MRKP-058) ->*Example a):* consider the last traded price was $1000 and the current best bid in the market is $1,100. The bid price is higher than the last traded price so the new Mark Price is $1,100. ->*Example b):* consider the last traded price was $1000 and the current best bid in the market is $999. The last traded price is higher than the bid price so the new Mark Price is $1,000. + - When `markPriceConfiguration: source weight` is provided then it must not be all `0` (0009-MRKP-059) -### 3. Oracle + - Launch a cash settled futures market which uses the "Last Traded Price" mark price method. Submit a market update proposal to change this to a composite price with some chosen configuration. Observe that the new methodology is applied correctly from enactment onwards. (0009-MRKP-134). - An oracle source external to the market provides updates to the Mark Price. See the [data sourcing spec](./0045-DSRC-data_sourcing.md). +- Launch a perpetual futures market which uses the "Last Traded Price" mark price method. Submit a market update proposal to change this to a composite price with some chosen configuration. Observe that the new methodology is applied correctly from enactment onwards. (0009-MRKP-135). -### 4. Model - The *Mark Price* may be calculated using a built in model. +### Example 1 - A typical path of a cash settled futures market from end of opening auction till expiry (use Algorithm 2 (ie median price))(0009-MRKP-040) - >*Example:* An option price will theoretically decay with time even if the market's order book or trade history does not reflect this. +1. Market is in opening auction, no mark price. +2. Order uncrossed, ends of opening auction, market is in active state. New event is emitted for new mark price. +3. New trade triggers new traded price, mark price recalculated when the time indicated by the mark price frequency is crossed, new event is emitted for new mark price. +4. New Oracle price comes, mark price recalculated when the time indicated by the mark price frequency is crossed, new event is emitted for new mark price. +5. Another Oracle price comes, mark price recalculated when the time indicated by the mark price frequency is crossed, new event is emitted for new mark price. +6. Traded price at step 2 is stale, and Oracle price at step 4 is stale, mark price recalculated when the time indicated by the mark price frequency is crossed, new event is emitted for new mark price. +7. market's status is set to trading terminated, An [oracle event occurs] that is eligible to settle the market, new event is emitted for new mark price. -### 5. Defined as part of the product +### Example 2 - A typical path of a cash settled perps market from end of opening auction (use Algorithm 2 (ie median price))(0009-MRKP-041) - The *Mark Price* may be calculated using an algorithm defined by the product -- and 'selected' by a market parameter. +1. Market is in opening auction, no mark price. +2. Order uncrossed, ends of opening auction, market is in active state. New event is emitted for new mark price. +3. New trade triggers new traded price, mark price recalculated when the time indicated by the mark price frequency is crossed, new event is emitted for new mark price. +4. New Oracle price comes, mark price recalculated when the time indicated by the mark price frequency is crossed, new event is emitted for new mark price. +5. Another Oracle price comes, mark price recalculated when the time indicated by the mark price frequency is crossed, new event is emitted for new mark price. +6. Oracle price comes for funding payments, mark price recalculated when the time indicated by the mark price frequency is crossed, new event is emitted for new mark price. +7. Traded price at step 2 is stale, and Oracle price at step 4 is stale, mark price recalculated, new event is emitted for new mark price. diff --git a/protocol/0010-MARG-margin_orchestration.md b/protocol/0010-MARG-margin_orchestration.md index 79acb0ee1..fad70db62 100644 --- a/protocol/0010-MARG-margin_orchestration.md +++ b/protocol/0010-MARG-margin_orchestration.md @@ -34,9 +34,10 @@ This specification outlines: ### **Background - how margin levels are calculated** -The [margin calculator](./0019-MCAL-margin_calculator.md) will calculate the margin levels when instructed to do so. It will return four margin levels for each trader: +The [margin calculator](./0019-MCAL-margin_calculator.md) will calculate the margin levels when instructed to do so. It will return five margin levels for each trader: 1. maintenance margin +1. order margin 1. collateral search level 1. initial margin 1. collateral release level @@ -71,11 +72,16 @@ The [margin calculator](./0019-MCAL-margin_calculator.md) returns four margin le The protocol compares these levels to the balance in the trader's margin account for a market. -| Traders Collateral | Protocol Action | Whitepaper Description +| Traders Collateral | Protocol Action | Whitepaper Description | | ------------- |:-------------:| -----:| -| less than _collateral search level_ | Collateral search, possibly close outs | Collateral search, Close out zone -| greater than _collateral release level_ | Collateral release | Collateral release -| greater than _collateral search level_ and less than _initial margin_ | no action | No financial risk to network +| less than _collateral search level_ | Collateral search, possibly close outs | Collateral search, Close out zone | +| greater than _collateral release level_ | Collateral release | Collateral release | +| greater than _collateral search level_ and less than _initial margin_ | no action | No financial risk to network | + +When posting a new order the initial margin for the overall position including the new order is calculated and order is only allowed to go ahead if party has enough funds to bring their margin account balance to that figure. An exception to this is when a party has an open position and tries to reduce it, but cannot afford the new margin. Then orders from the opposite side (short orders for a long position, long orders for a short position) are accepted as follows: + +- limit order: accept order when sum sizes of all the party's order's for that side of the book including the one being posted is less than or equal to the absolute open volume that a party has, +- market order: accept order as long as its size is less than or equal to the absolute open volume that a party has. #### Collateral search diff --git a/protocol/0012-POSR-position_resolution.md b/protocol/0012-POSR-position_resolution.md index 35b2028c7..a5e050644 100644 --- a/protocol/0012-POSR-position_resolution.md +++ b/protocol/0012-POSR-position_resolution.md @@ -2,167 +2,131 @@ ## Acceptance Criteria -- All orders of "distressed traders" are cancelled (0012-POSR-001) -- Open positions of distressed traders are closed (0012-POSR-002) -- One market order is submitted for the net liability (0012-POSR-003) +- All orders of "distressed traders" in cross-margin mode are cancelled (0012-POSR-001) +- Open positions of distressed traders are closed immediately (0012-POSR-002) - Mark Price is never updated during position resolution (0012-POSR-004) -- Non-distressed traders who trade with the network because their open orders are hit during the close out trade have their positions settled correctly. (0012-POSR-005) +- Non-distressed traders who trade with the network because their open orders are hit during the networks' trade have their positions settled correctly. (0012-POSR-005) - When a distressed party has a [staking account](./0013-ACCT-accounts.md) with the same currency as the settlement currency of the market where it's distressed the staking account is NOT used in margin search and liquidation. (0012-POSR-006) -- When a party is distressed at the point of leaving an auction it should get closed out as soon as the market returns to continuous trading mode and all the parked orders (pegged and LP) get added back to the order book. (0012-POSR-008) +- When a party is distressed and gets closed out the network's position gets modified to reflect that it's now the network party that holds that volume. (0012-POSR-009) -## Summary +- When the network party holds a non-zero position and there are not enough funds in market's insurance pool to meet the mark-to-market payment the network's position is unaffected and loss socialisation is applied. (0012-POSR-010) -Position resolution is the mechanism which deals with closing out distressed positions on a given market. It is instigated when one or more participant's collateral balance is insufficient to fulfil their settlement or margin liabilities. +- When the network party holds a non-zero position and the market's insurance pool balance is below the network party's maintenance margin for that market the network's position in that market remains unaffected. (0012-POSR-011) -## Guide-level explanation +- The liquidation strategy can be updated using the market update transaction (0012-POSR-012) -## Reference-level explanation +- When the market is configured to use: + - `disposal time step` = `10s`, + - `disposal fraction` = `0.5`, + - `full disposal size` = `50`, + - `max fraction of book side within liquidity bounds consumed` = `0.01` -Any trader that has insufficient collateral to cover their margin liability is referred to as a "distressed trader". + and the volume on the buy side of the book within the liquidity bounds is always `10,000` (as volume on the book gets filled new orders get placed) then liquidating a distressed party with an open volume of `280` results in 4 network trades in total spaced `10s` apart with volumes of: `100`, `90`, `45`, `45`. (0012-POSR-013) -### Position resolution algorithm +- It is possible to check the network party's open volume and margin level in any market via the API. (0012-POSR-014) + +- It is possible to check the time of the next liquidation trade attempt in any market via the API. (0012-POSR-015) + +### Network position disposal + +- When calculating the available volume, the full remaining size of iceberg orders should be considered. (0012-POSR-019) +- When calculating the available volume, volume outside price monitoring bounds should be considered. (0012-POSR-020) +- When calculating the available volume, volume outside the liquidity price range should not be considered. (0012-POSR-021) +- Given a highly liquid market, if the network’s position is greater than `full disposal size`. The network must attempt to dispose `position * disposal fraction` at the next disposal step. (0012-POSR-022) +- Given a highly liquid market, if the network’s position is less than or equal to `full disposal size`. The network must attempt to dispose of its full position at the next disposal step. (0012-POSR-023) +- Given a highly liquid market, if the network’s `disposal fraction<1` and `full disposal size`=0, the network must still eventually dispose of its full position. (0012-POSR-024) +- The network must never dispose more than `available volume * max fraction of book side within liquidity bounds consumed` in a single order. (0012-POSR-025) +- A network disposal order which generates trades must not affect the mark price. (0012-POSR-026) +- A network disposal order can not cross with orders outside the liquidity price range. (0012-POSR-027) +- A network disposal order can cross with orders outside price monitoring bounds but must not trigger a price monitoring auction. (0012-POSR-028) +- A network disposal order which crosses multiple orders should generate multiple atomic trades. (0012-POSR-029) + +### Network Profit and Loss + +- Given the network starts with no position and does not dispose any of it's position during the scenario: (0012-POSR-016) -See [Whitepaper](https://vega.xyz/papers/vega-protocol-whitepaper.pdf), Section 5.3 , steps 1 - 3 + - The mark price moves to `100`, the network liquidates a distressed party with a long position of `1`. The network should report a position of `1` and a realised and unrealised pnl of `0` and `0` respectively. + - The mark price moves to `120`, the network liquidates a distressed party with a short position of `-1`. The network should report a position of `-1` and a realised and unrealised pnl of `20` and `0` respectively. + - The mark price moves to `60`. The network should report a position of `-1` and a realised and unrealised pnl of `20` and `60` respectively. -1. A "distressed trader" has all their open orders on that market cancelled. Note, the network must then recalculate their margin requirement on their remaining open position and if they now have sufficient collateral (i.e. aren't in the close out zone) they are no longer considered a distressed trader and not subject to position resolution. The market may at any point in time have multiple distressed traders that require position resolution. They are 'resolved' together in a batch. -1. The batch of distressed open positions that require position resolution may be comprised of a collection of long and short positions. The network calculates the overall net long or short position. This tells the network how much volume (either long or short) needs to be sourced from the order book. For example, if there are 3 distressed traders with +5, -4 and +2 positions respectively. Then the net outstanding liability is +3. If this is a non-zero number, do Step 3. -1. This net outstanding liability is sourced from the market's order book via a single market order (in above example, that would be a market order to sell 3 on the order book) executed by the network as a counterpart. This internal entity is the counterpart of all trades that result from this single market order and now has a position which is comprised of a set of trades that transacted with the non-distressed traders on the order book. Note, the network's order should not incur a margin liability. Also, these new positions (including that incurred by the network) will need to be "MTM settled". This should happen after Step 5 to ensure we don't bankrupt the insurance pool before collecting the distressed trader's collateral. This has been included as Step 6. -1. The network then generates a set of trades with all the distressed traders all at the volume weighted average price of the network's (new) open position. These trades should be readily distinguished from the trades executed by the network counterpart in Step 3 (suggest by a flag on the trades) - 1. Note, If there was no market order (i.e step 3 didn't happen) the close-out price is the most recently calculated _Mark Price_. See Scenario 1 below for the list of resulting trades for the above example. The open positions of all the "distressed" traders is now zero and the networks position is also zero. Note, no updates to the _Mark Price_ should happen as a result of any of these trades (as this would result in a new market-wide mark to market settlement at this new price and potentially lead to cascade close outs). -1. All bankrupt trader's remaining collateral in their margin account for this market is confiscated to the market's insurance pool. -1. If an order was executed on the market (in Step 3), the resulting trade volume between the network and passive orders must be mark-to-market settled for all parties involved including the network's internal 'virtual' party. As the network's closeout counterparty doesn't have collateral, any funds it 'owes' will be transferred from the insurance fund during this settlement process (as defined in the [settlement spec](./0003-MTMK-mark_to_market_settlement.md).). It's worth noting that the network close-out party must never have margins calculated for it. This also should naturally happen because no margin calculations would happen during the period that the network temporarily (instantaneously) has an open position, as the entire position resolution process must happen atomically. +- Given the network starts with no position and does not dispose any of it's position during the scenario: (0012-POSR-017) -### Note + - The mark price moves to `100`, the network liquidates a distressed party with a long position of `1`. The network should report a position of `1` and a realised and unrealised pnl of `0` and `0` respectively. + - The mark price moves to `90`, the network liquidates a distressed party with a long position of `1` (average entry price now equals `95`). The network should report a position of `2` and a realised and unrealised pnl of `0` and `-10` respectively. + - The mark price moves to `60`. The network should report a position of `2` and a realised and unrealised pnl of `0` and `-70` respectively. -- Entire distressed position should always be liquidated - even if reducing position size, by say 50%, would result in the remaining portion being above the trader's maintenance margin. -- When there's insufficient volume on the order-book to close out a distressed position no action should be taken: the position remains open and any amounts in trader's margin account should stay there. Same principle should apply if upon next margin recalculation the position is still distressed. -- If the party is distressed at a point of leaving auction it should be closedout immediately (provided there's enough volume on the book once all the pegged and liquidity provision orders get redeployed). - -## Examples and Pseudo code +- Given an empty insurance pool and the liquidation strategy `disposal time step = 5`, `disposal fraction = 0.5`, `full disposal size=0` and `max fraction of book side within liquidity bounds consumed = 0.01` during the below scenario: (0012-POSR-018) + - The mark price moves to 100, the network liquidates a distressed party with a long position of 2. The network should report a position of 2 and a realised and unrealised pnl of 0 and 0 respectively. + - Given the order book: + | side | price | size | + |------|-------|------| + | buy | 90 | 1000 | + | sell | 110 | 1000 | + - The time updates to the next disposal time, the network reduces its position by 1. The network should report a position of 1 and a realised and unrealised pnl of -10 and 0 respectively. + - The time updates to the next disposal time, the network reduces its position by 1. The network should report a position of 1 and a realised and unrealised pnl of -20 and 0 respectively. + - Loss socialisation should be applied and the accumulated balance for all accounts should be unchanged. -### _Scenario - All steps_ - -`Trader1 open position: +5` -`Trader1 open orders: 0` -`Trader2 open position: -4` -`Trader2 open orders: 0` -`Trader3 open position: +2` -`Trader3 open orders: 0` - -#### STEP 1 -No traders are removed from the distressed trader list. +## Summary + +Position resolution is the mechanism which deals with closing out distressed positions on a given market. It is instigated when one or more participant's margin account balance falls below their latest maintenance margin level. + +## Guide-level explanation + +## Reference-level explanation + +Any trader that has insufficient collateral to cover their margin liability is referred to as a "distressed trader". + +### Position resolution algorithm -#### STEP 2 +#### Liquidating distressed party's position in a market -`NetOutstandingLiability = 5 - 4 + 2 = 3` +- Position resolution evaluation gets carried out each time the mark price is updated, strictly after the margin levels margin level update and mark-to-market settlement. -#### STEP 3 +- During position resolution evaluation party's margin account balance is compared to it's latest maintenance margin level. If the account balance is below the margin level then the party gets marked as distressed. -```json -LiquiditySourcingOrder: { - type: 'market', - direction: 'sell', - size: 3 -} +- If a party marked as "distressed" has the cross-margin mode selected for the market being considered: -LiquiditySourcingTrade1: { - buyer: Trader4, - seller: Network, - size: 2, - price: 120, - type: 'liquidity-sourcing' -} + - all their open orders on that market get cancelled without releasing any collateral from the margin account, + - their margin level then gets recalculated to account only for the open volume they hold in a market + - if party's margin account balance is now above or equal to their latest maintenance margin level then such party is no longer marked as "distressed". -LiquiditySourcingTrade2: { - buyer: Trader5, - seller: Network, - size: 1, - price: 100, - type: 'liquidity-sourcing' +- If party's is marked as "distressed" at this stage then: -} + - their open volume gets added to network's open volume for that market, + - party's open volume in that market gets set to 0, + - the full amount of party's margin account balance gets transferred to market's insurance pool. -``` +This concludes the position resolution from party's perspective. -#### STEP 4 +#### Managing network's position -Close out trades are generated with the distressed traders +Whilst network has a non-zero position in a given market it's treated as any other party with market's insurance in that market acting as its margin account and with and exception that margin search, margin release or liquidation are never attempted on a network party. -```json -CloseOutTrade1 { - buyer: Network, - seller: Trader1, - size: 5, - price: 113.33, - type: 'safety-provision' -} +Whenever the network party has a non-zero position it attempts to unload it using an [immediate or cancel](./0014-ORDT-order_types.md) limit order. If the the network has a `long` position it will submit a `sell` order. If it has a short position it will submit a buy order. The size of that order is chosen according to the liquidation strategy which forms a part of the market's configuration. The strategy can be updated at any point whilst market is active with a market change [governance vote](./0028-GOVE-governance.md#2-change-market-parameters). -CloseOutTrade2 { - buyer: Trade2, - seller: Network, - size: 4, - price: 113.33, - type: 'safety-provision' -} +Currently only one liquidation strategy is supported and its defined by the following parameters: -CloseOutTrade3 { - buyer: Network, - seller: Trader3, - size: 2, - price: 113.33, - type: 'safety-provision' -} -``` +- `disposal time step` (min: `1s`, max: `1h`): network attempts to unload its position in a given market every time it goes out of auction and then every `disposal time step` seconds as long as market is not in auction mode and while the network's position is not equal to `0`, +- `disposal fraction` (min: `0.01`, max: `1`): fraction of network's current open volume that it will try to reduce in a single disposal attempt, +- `full disposal size` (min: `0`, max: `max int`): once net absolute value of network's open volume is at or below that value, the network will attempt to dispose the remaining amount in one go, +- `max fraction of book side within liquidity bounds consumed` (min: `0`, max: `1`): once the network chooses the size of its order (`s_candidate`) the effective size will be calculated as `s_effective=min(m*N, s_candidate)`, where `N` is the sum of volume (on the side of the book with which the network's order will be matching) that falls within the range implied by the `market.liquidity.priceRange` [parameter](./0044-LIME-lp_mechanics.md#market-parameters) and `m` is the `max fraction of book side within liquidity bounds consumed`. -This results in the open position sizes for all distressed traders and the network entities to be zero. +Assume the price range implied by the `market.liquidity.priceRange` is `[a, b]`. Once the network has worked out a size of its immediate or cancel limit order it sets it's price to `a` if it's a sell order or `b` if it's a buy order, and it submits the order. -`// OpenPosition of Network = -3 +5 -4 +2 = 0` -`// OpenPosition of Trader1 = +5 -5 = 0` -`// OpenPosition of Trader2 = -4 +4 = 0` -`// OpenPosition of Trader3 = +2 - 2 = 0` +Note that setting: -#### STEP 5 +- `disposal time step` = `0s`, +- `disposal fraction` = `1`, +- `full disposal size` = `max int`, +- `max fraction of book side within liquidity bounds consumed` = `1` -The collateral from distressed traders is moved to the insurance pool - -```json -// sent by Settlement Engine to the Collateral Engine -TransferRequest1 { - from: [Trader1_MarginAccount], - to: MarketInsuranceAccount, - amount: Trader1_MarginAccount.size, // this needs to be the full amount -} +is closest to reproducing the legacy setup where party would get liquidated immediately (with a difference that closeout now happens immediately even if there's not enough volume on the book to fully absorb it) hence the above values should be used when migrating existing markets to a new version. For all new markets these values should be specified explicitly. -TransferRequest2 { - from: [Trader2_MarginAccount], - to: MarketInsuranceAccount, - amount: Trader2_MarginAccount.size, // this needs to be the full amount -} +Different liquidation strategies with different parameters might be proposed in the future, hence implementation should allow for easy substitution of strategies. -TransferRequest3 { - from: [Trader3_MarginAccount], - to: MarketInsuranceAccount, - amount: Trader3_MarginAccount.size, // this needs to be the full amount -} -``` - -#### STEP 6 +API requirements: -Traders from step 3 need to be settled. - -Prior to STEP 3 trades, assume Trader 4 and Trader 5 had the following open positions. - -`// OpenPosition of Trader4 = -3` -`// OpenPosition of Trader5 = 15` - -Trader 4 has therefore closed out 2 contracts through the `LiquiditySourcingTrade` 1. These need to be settled against the trade price. - -```json -TransferRequest4 { - from: [MarketInsuranceAccount], - to: Trader4_MarginAccount, - amount: (120 - PreviousMarkPrice) * -2, // this is the movement since the last settlement multiplied by the volume of the closed out amount -} - -``` +- create an endpoint to easily identify network's position in any given market, +- create an endpoint to easily identify network's margin levels in any given market, +- create an endpoint to easily check time of next liquidation trade attempt. diff --git a/protocol/0013-ACCT-accounts.md b/protocol/0013-ACCT-accounts.md index 9d82ff779..62c6ee24c 100644 --- a/protocol/0013-ACCT-accounts.md +++ b/protocol/0013-ACCT-accounts.md @@ -4,7 +4,7 @@ A party only has control over balances in the "general" account for each asset. [Parties](./0017-PART-party.md) are identified by Vega public keys. Each party that makes a deposit on one of the asset bridges, currently only [Ethereum ERC20 bridge](./0031-ETHB-ethereum_bridge_spec.md) will have a general account for the relevant [asset](./0040-ASSF-asset_framework.md) created. -In order to submit [orders](./0014-ORDT-order_types.md a non-zero general account balance is needed; Vega will transfer appropriate amount to the [margin account](./0011-MARA-check_order_allocate_margin.md) for the party and the market. +In order to submit [orders](./0014-ORDT-order_types.md) a non-zero general account balance is needed; Vega will transfer appropriate amount to the [margin account](./0011-MARA-check_order_allocate_margin.md) for the party and the market. Any party can submit a withdrawal transaction to withdraw assets from the general account to a specified address on another chain, currently only [Ethereum ERC20 bridge](./0031-ETHB-ethereum_bridge_spec.md). @@ -16,15 +16,17 @@ Note that a party can also associate the governance / staking asset via the [Veg 1. Mark-to-market settlement account per market: this is used for collecting and distributing mark-to-market settlement cashflows and is *zero* at the end of each mark-to-market settlement run. 1. Margin accounts for each party with open orders or positions on any [market](./0043-MKTL-market_lifecycle.md). -1. Bond account for any party that's an [LP on any market](0038-OLIQ-liquidity_provision_order_type.md). -1. [Insurance pool account](0015-INSR-market_insurance_pool_collateral.md) for any market. +1. Bond account for any party that's an [LP on any market](0044-LIME-lp_mechanics_type.md). +1. [Global insurance pool](0015-INSR-market_insurance_pool_collateral.md#global-insurance-pool) (1 per asset) +1. [Insurance pool account](0015-INSR-market_insurance_pool_collateral.md#market-insurance-pool) for any market. 1. [Liquidity fee pool](0042-LIQF-setting_fees_and_rewarding_lps.md) for any market. 1. [Infrastructure fee pool](0029-FEES-fees.md) for any asset. -1. [Reward accounts](0056-REWA-rewards_overview.md) which exist for *each* reward account per every Vega asset (settlement asset) and per every reward metric per every Vega asset (reward asset). There is an additional [staking rewards](0061-REWP-pos_rewards.md) account. -1. [Vega trasury accounts](0055-TREA-on_chain_treasury.md) per Vega asset. +1. [Reward accounts](0056-REWA-rewards_overview.md) which exist for *each* Vega asset (settlement asset) and per every reward metric per every Vega asset (reward asset). There is an additional [global rewards account](0056-REWA-rewards_overview.md#validator-ranking-metric) used for supplementary (on top of infrastructure fee split) validator rewards. One key difference with staking accounts is that the collateral is not held in an asset bridge, but in the [staking bridge](./0071-STAK-erc20_governance_token_staking.md). The balance is changed by events on Ethereum, rather than actions taken on the Vega chain. +Note that both the network treasury and the global rewards account use the same `0` address. Account type is used to differentiate where the funds should go into when making a transfer. The deposits made to the `0` account get credited to the global rewards account. + ## Summary Accounts are used on Vega to maintain a record of the amount of collateral that is deposited and deployed by participants in the market. @@ -73,9 +75,9 @@ If there is a positive balance in an account that is being deleted, that balance ## Bond accounts -Bond accounts are opened when a party opens a [Liquidity Provision order](./0038-OLIQ-liquidity_provision_order_type.md). The bond is held by the network to ensure that the Liquidity PRovider maintains enough collateral to cover their commitment. [0044-LIME - LP Mechanics](./0044-LIME-lp_mechanics.md) contains more detail on bond management. +Bond accounts are opened when a party opens a [Liquidity Provision order](./0044-LIME-lp_mechanics.md). The bond is held by the network to ensure that the Liquidity Provider meets their SLA obligations. [0044-LIME - LP Mechanics](./0044-LIME-lp_mechanics.md) contains more detail on bond management. -## Insurance pools +## Market insurance pools Every market will have at least one insurance pool account that holds collateral that can be used to cover losses in case of unreasonable market events. @@ -83,7 +85,26 @@ Every market will have at least one insurance pool account that holds collateral When a [market launches](./0043-MKTL-market_lifecycle.md), an insurance pool account is created for that market for each settlement asset. This account is used by the protocol during the collection of [margin requirements](./0010-MARG-margin_orchestration.md) and the collection of [mark to market settlement](./0003-MTMK-mark_to_market_settlement.md). -When a market is finalised / closed remaining funds are distributed to the on chain treasury. This occurs using ledger entries to preserve double entry accounting records within the collateral engine. +When a market is finalised / closed remaining funds are transferred into the global insurance pool using the same settlement asset. This occurs using ledger entries to preserve double entry accounting records within the collateral engine. + +## General insurance pool + +There is a general insurance pool for every asset which has been used by at least one market which was closed and had positive balance in its insurance pool. + +**Creation/Deletion:** + +When a market gets closed and positive balance remains in its insurance pool then part of the that balance gets moved to the global insurance pool for the asset which market used as its settlement asset. If the insurance pool for that asset doesn't exist yet then it gets created on the fly at the point of that transfer. + +Currently these accounts never get deleted. + +## Network treasury + +Network treasury holds assets which can only be moved to another account via the [governance initiated transfer](./0028-GOVE-governance.md#governance-initiated-transfer-proposals). +Funds are moved into the network treasury using (external) deposits or (internal) transfers. If the network treasury doesn't exist for an asset supported for deposits and/or transfers then it gets created on the fly at the point of that transfer. + +## Fee distribution accounts + +Additional accounts (one per each supported asset) associated with distribution of trading fees (infrastructure fees, maker fees, liquidity provision fees) exist. Please refer to the [fees](./0029-FEES-fees.md) and [LP](./0042-LIQF-setting_fees_and_rewarding_lps.md) specs for more details. ## Staking accounts @@ -93,12 +114,20 @@ In Vega governance is controlled by a [governance token](./0028-GOVE-governance. Note that it *is* possible to have markets in the governance asset, in which case all of the accounts detailed above will still apply. Staking accounts only relate to the balance of the governance asset that has been staked. +## Global rewards account + +A special account type used for distribution of rewards based on validator ranking metric. Funds are moved into the global rewards account using (external) deposits or (internal) transfers. Please refer to the [subsection of the rewards spec](./0056-REWA-rewards_overview.md#validator-ranking-metric) for details around distribution of funds from that account. + +## Rewards account + +Additional accounts associated with [distribution](./0056-REWA-rewards_overview.md) and [vesting](./0085-RVST-rewards_vesting.md) of other rewards exist, please refer to the relevant spec files for more details. + ## Acceptance Criteria ### All ordinary accounts -- Double entry accounting is maintained at all points i.e. every transfer event has a source account and destination account and the balance of the source account before the transfer equals to the balance of source account minus the transfer amount after the transfer and balance of the destination account before the transfer plus the transfer amount equals to the balance of the destination account after the transfer. (0013-ACCT-001) -- Only transfer requests move money between accounts. (0013-ACCT-002) +- Double entry accounting is maintained at all points i.e. every transfer event has a source account and destination account and the balance of the source account before the transfer equals to the balance of source account minus the transfer amount after the transfer and balance of the destination account before the transfer plus the transfer amount equals to the balance of the destination account after the transfer. (0013-ACCT-001). +- Only transfer requests move money between accounts. (0013-ACCT-002). ### Party asset accounts @@ -115,17 +144,18 @@ Note that it *is* possible to have markets in the governance asset, in which cas - Cannot have a non-zero balance on a margin account where there's no position / position size = 0 and no active orders. (0013-ACCT-009) - Cannot transfer into or out of a margin account where there's no position / position size = 0 and no active orders. (0013-ACCT-010) + ### Liquidity Provider bond accounts - A bond account holds collateral to maintain collateral for [Liquidity Providers](./0044-LIME-lp_mechanics.md). (0013-ACCT-023) -- Each party that has placed a [Liquidity Provision order](./0038-OLIQ-liquidity_provision_order_type.md) will have one bond account per market they have provided liquidity to (0013-ACCT-018) +- Each party that has placed a [Liquidity Provision order](./0044-LIME-lp_mechanics.md#commit-liquidity-network-transaction) will have one bond account per market they have provided liquidity to (0013-ACCT-018) - [Fees earned from liquidity provision](./0044-LIME-lp_mechanics.md#fees) are *not* paid in to this bond account - [they are paid in to the *margin* account for this trader](./0042-LIQF-setting_fees_and_rewarding_lps.md#distributing-fees) (0013-ACCT-019) ### Insurance pool accounts - When a market opens for trading, there is an insurance account that is able to be used by that market for every settlement asset of that market. (0013-ACCT-020) - Only protocol-initiated aka internal transfer requests move money in or out of the insurance account. User initiated transfer requests cannot be used to move funds in or out of insurance pool. (0013-ACCT-021) -- When all markets of a risk universe expire and/or are closed, the insurance pool account has its outstanding funds redistributed to the [network treasury](./0055-TREA-on_chain_treasury.md) account for the appropriate asset (if it doesn't exist create it) and other insurance pools using the same asset. (0013-ACCT-022) +- When a market terminates and settles (the final settlement cashflow happens), the insurance pool account has its outstanding balance transferred to the global insurance pool account for the appropriate asset (if it doesn't exist create it), other insurance pools using the same asset will not get the outstanding funds. (0013-ACCT-033) ### Special case: Staking accounts @@ -139,3 +169,12 @@ One key difference with staking accounts is that the collateral is not held in a - The balance can only be delegated to Validators (0013-ACCT-015) - The balance cannot be traded, or used as margin, or transferred, or withdrawn (0013-ACCT-016) - Delegated stake remains in the trader's staking account (0013-ACCT-017) + +### Network treasury + +- It is possible to transfer funds from a Vega general account to the network treasury account by specifying the `0` address and appropriate account type. (0013-ACCT-026) + +### Global rewards account + +- It is possible to deposit funds from Ethereum directly into the global rewards account by specifying the `0` Vega address. (0013-ACCT-027) +- It is possible to transfer funds from a Vega general account to the global rewards account by specifying the `0` address and appropriate account type. (0013-ACCT-028) diff --git a/protocol/0014-ORDT-order_types.md b/protocol/0014-ORDT-order_types.md index 773cf5de1..5881d2f53 100644 --- a/protocol/0014-ORDT-order_types.md +++ b/protocol/0014-ORDT-order_types.md @@ -7,7 +7,6 @@ A market using a limit order book will permit orders of various types to be subm Notes on scope of current version of this spec: - Includes only detailed specification for orders valid for *continuous trading*, does not specify behaviour of these order types in an auction. -- Does not include detailed specification for **stop** orders. Inclusion of stops in guide level explanation is a placeholder/indicator of future requirements. ## Guide-level explanation @@ -42,13 +41,193 @@ Notes on scope of current version of this spec: **Market-state:** 1. **Good For Auction (GFA):** This order will only be accepted by the system if it arrives during an auction period, otherwise it will be rejected. The order can act like either a GTC or GTT order depending on whether the `expiresAt` field is set. -1. **Good For Normal (GFN):** This order will only be accepted by the system if it arrived during normal trading, otherwise it will be rejected. Normal trading is defined as either continuous trading on a normal market or auction trading in a frequent batch auction market. The order can act like either a GTC or GTT order depending on whether the `expiresAt` field is set. +2. **Good For Normal (GFN):** This order will only be accepted by the system if it arrived during normal trading, otherwise it will be rejected. Normal trading is defined as either continuous trading on a normal market or auction trading in a frequent batch auction market. The order can act like either a GTC or GTT order depending on whether the `expiresAt` field is set. ### Execution flags -1. **Post-Only (True/False):** Only valid for Limit orders. Cannot be True at the same time as Reduce-Only. If set to true, once order reaches the orderbook, this order acts identically to a limit order set at the same price. However, prior to being placed a check is run to ensure that the order will not (neither totally nor in any part) immediately cross with anything already on the book. If the order would immediately trade, it is instead immediately `Stopped` with a reason informing the trader that the order was stopped to avoid a trade occurring. As a result, placing a Post-Only order will never incur taker fees. +1. **Post-Only (True/False):** Only valid for Limit orders. Cannot be True at the same time as Reduce-Only. If set to true, once an order reaches the orderbook, this order acts identically to a limit order set at the same price. However, prior to being placed a check is run to ensure that the order will not (neither totally nor in any part) immediately cross with anything already on the book. If the order would immediately trade, it is instead immediately `Stopped` with a reason informing the trader that the order was stopped to avoid a trade occurring. As a result, placing a Post-Only order will never incur taker fees, and will not incur fees in general if executed in continuous trading. It is possible for some liquidity and infrastructure fees to be paid if the resultant limit order trades at the uncrossing of an auction, as specified in [0029-FEES](https://github.com/vegaprotocol/specs/blob/master/protocol/0029-FEES-fees.md#normal-auctions-including-market-protection-and-opening-auctions). 1. **Reduce-Only (True/False):** Only valid for Non-Persistent orders. Cannot be True at the same time as Post-Only. If set, order will only be executed if the outcome of the trade moves the trader's position closer to 0. In addition, a Reduce-Only order will not move a position to the opposite side to the trader's current position (e.g. if short, a Reduce-Only order cannot make the trader long as a result). If submitted as IOC, where the full volume would switch sides, only the amount required to move the position to 0 will be executed. + +### Stop orders + +In addition to normal immediately executing orders, Vega should accept the submission of stop orders. +These differ from normal orders in that they sit off the order book until triggered, when they are entered as normal. +These are generally used to exit positions under pre-defined conditions, either as a "stop loss" order that controls the maximum losses a position may take, a "take profit" order that closes a position once a defined level of profit has been made, or both. + +A stop order submission can be made (stop loss or take profit are both just called a stop order internally). + +- Stop order submissions must include either a trigger price OR trailing stop distance as a % move from the reference price in addition to a normal order submission. + +- Stop order submissions must include a trigger direction. +Direction may be **rises above** or **falls below**. +**Rises above** stops trigger if the last traded price is higher than the trigger level, and **falls below** stops trigger if the last traded price is lower than the trigger level. + +- A stop trigger can have an optional expiry date/time. +If it has an expiry then it can be set either to cancel on expiry (i.e. it is deleted at that time) or trigger on expiry (i.e. the order wrapped in the submission is placed whether or not the trigger level is breached). + +- It is possible to make a single stop order submission or an OCO (One Cancels the Other) stop order submission. +An OCO contains TWO stop order submissions, and must include one in each trigger direction. +OCOs work exactly like two separate stop orders except that if one of the pair is triggered, cancelled, deleted, or rejected, the other one is automatically cancelled. +An OCO submission allows a user to have a stop loss and take profit applied to the same amount of their position without the risk of both trading and reducing their position by more than intended. + - An OCO submission can be set to have one of three different behaviours at expiry, either triggering one side, triggering the other, or expire without any action. This is configured through the setting of the expiry behaviour on each leg. Setting each leg to trade at expiration will result in the OCO being rejected. + +- The stop order submission wraps a normal order submission. + +- A stop order submission may have an optional `Size Override`: + - If unset, the size within the contained normal order submission will be used + - If set to `Position`, triggering should override the contained order's size with the trader's entire current position on the market. + - The `Position` override configuration should also include the option `position_fraction` which determines what proportion of the position is closed when the stop order is triggered. At time of triggering the size of the order will be determined by $fraction \cdot position$. + - All `Position` stop orders existing should be cancelled if a trader's position changes from long to short (or vice versa). + +- The submission is validated when it is received but does not initially interact with the order book unless it is triggered immediately (see below). + +- If and when the trigger price is breached in the specified direction the order provided in the stop order submission is created and enters the book or trades as normal, as if it was just submitted. + +- The order contained in a stop order submission is entered immediately if the trigger price is already breached on entry, except during an auction. + +- When the stop order is a trailing stop, the price at which it is triggered is calculated as the defined distance as a percentage from the highest price achieved since the order was entered if the direction is to trigger on price below the specified level, or the lowest price achieved since the order was entered if the direction is to trigger above the level. +Therefore the trigger level of a stop order moves with the market allowing the trader to lock in some amount of gains. + +- The order can't be triggered or trade at all during an auction (even if the current price would normally trigger it immediately on entry). + +- A stop order can be entered during an auction (except opening auction), and can then be triggered by the auction uncrossing price if the auction results in a trade, as well as any trades (including auction uncrossing trades) after that. + +- A stop order entered during opening auction will be rejected. + +- GFA is not a valid TIF for a stop order submission. + +- Spam prevention: + + - Stop orders will only be accepted from keys with either a non-zero open position or at least one active order. + + - A network parameter will control the maximum number of stop orders per party (suggested initial value: between 4 and 10). + + - If the trader's position size moves to zero exactly, and they have no open orders, all stop orders will be cancelled. + + +### Iceberg / transparent iceberg orders + +On centralised exchanges, icebergs are a type of order that enables a trader to make an order with a relatively small visible "display quantity" and a larger hidden total size. + +Like an iceberg, most of the order is not visible to other traders. + +After the full size of the visible portion of the order has traded away, the order is "refreshed" and reappears with its maximum display quantity. + +This is repeated until the order is cancelled or expires, or its full volume trades away. + +Some platforms also allow the display quantity to be randomised on each refresh to further frustrate traders trying to identify the existence of the iceberg order). + +Vega, being a decentralised and fully transparent protocol, cannot (in its current form) achieve the hidden characteristic of iceberg orders. But it can do the rest. + +Iceberg orders (or in other words, transparent iceberg orders) are still helpful, especially for market makers and in combination with pegged orders, as they allow a trader to remain competitively present on the order book through the refresh facility without needing to supply excessive volume into a large aggressive order. + + +#### Definitions + +These terms are used to refer to fields on an order: + +- `quantity` - the full initial size of the order on entry. + +- `displayed quantity` - the current displayed quantity, i.e. the amount of the remaining quantity that is active on the book and can be hit. +Note that for a non-iceberg order, `displayed quantity == remaining`. + +- `remaining` - the total quantity of the order remaining that could trade. + + +#### Creating iceberg orders + +Iceberg orders are created by populating three additional fields on any valid persistent limit order: + +- `initial peak size` - this specifies the amount displayed and available on the order book for a new or newly refreshed iceberg order. + +- `minimum peak size` - this determines when an iceberg order is eligible for refresh. +The iceberg is refreshed any time the order's displayed quantity is less than the minimum peak size. + + +#### Validity + +- The order's non-iceberg-related fields must be set so as to make a valid order. + +- Any persistent TIF (GTC, GTT, GFA, GFN) can be an iceberg order. + +- An iceberg order may have either an ordinary or pegged limit price. +Market iceberg orders are not supported, even if with a persistent TIF. + +- Icebergs may be post only. + +- `initial peak size` must be greater than or equal to minimum position size (i.e. minimum order size). + +- `minimum peak size` must be `>` 0 and `≤ initial peak size` + + +#### Execution and subsequent refresh + +- On entry, if an iceberg order is crossed with the best bid/ask, it trades first with its **full quantity**, i.e. the peak sizes do not come into play during aggressive execution. +This is to prevent an iceberg order ever being crossed after refreshing. + +- Once they enter the book passively, iceberg orders trade just like non-iceberg persistent order, as if the order entered the book with `quantity = initial peak size` on submission, and again each time they are refreshed until `remaining == 0` (or they are cancelled or expired, etc.). +That is: + + - On entry, unlike normal orders, `displayed quantity` is set to `initial peak size` not `quantity`. + + - As for any other order, `remaining == quantity` on entry. + +- When an iceberg order trades, both `remaining` and `displayed quantity` are reduced by the trade size. + +- Iceberg orders can trade many times without refresh, reducing `displayed quantity` each time. +The order will not be refreshed after each trade while `displayed quantity ≥ minimum peak size`. + +- When `displayed quantity < minimum peak size` and `remaining > displayed quantity` the order will be refreshed: + + - The refresh happens at the end of the transaction when the order becomes eligible for refresh. + + - On refresh `display quantity` is set to `min(remaining, initial peak size)`. + + - A refresh simulates a cancel/replace, which means that on refresh an iceberg order will always lose time priority relative to other orders at the same price. + + - If multiple iceberg orders need to be refreshed at the same time, they are refreshed in the order that their eligibility for refresh was triggered, so the iceberg that dropped below its `minimum peak size` first is refreshed first (even during the same transaction the sequence of execution must be respected). + +- Once the remaining quantity is equal to the displayed quantity, no further refresh is possible. +The order now behaves like a normal limit order and will leave the book if it trades away completely. + +- For an incoming order with size larger than the total displayed quantity at a given price level, the following procedure should be followed: + + - The incoming order trades with all visible volume at the price level, whether an iceberg order or a vanilla limit order + + - If there is still remaining volume in the order once all visible volume at the price level is used up, the remaining quantity is split between the non-visible components of all iceberg orders at this level according to their remaining volumes. For example if there are three iceberg orders with remaining volume 200 lots, 100 lots and 100 lots, an order for 300 lots would be split 150 to the first order and 75 to the two 100 lot orders. If the distribution doesn't divide exactly stick the extra onto the iceberg which is first in terms of time priority. + + - If there are still remaining iceberg orders at this point, refresh their volumes and continue trading. If the incoming order uses up all iceberg orders at this level, continue with any remaining volume to the next price level. + + +#### Amendment + +- Amending the size of an iceberg order amends the total `remaining` quantity and leaves the `displayed quantity` unaffected unless the new remaining quantity is smaller than the current displayed quantity, in which case the displayed quantity is reduced to the total remaining quantity. + +- Amending the size/quantity of an iceberg order does not cause it to lose time priority. +This is because the increase applies to the `remaining` quantity and not to the `displayed quantity`. +This is allowed because the order will lose time priority on refresh, i.e. before the increased quantity is available to trade. + + +#### Auctions + +- Icebergs can be entered or carried into auctions if the underlying TIF is supported. + +- Icebergs can trade in the auction uncrossing up to their full `remaining` amount as for any other transaction that would cause a trade with an iceberg order. +If the remainders of multiple icebergs sit at the same price and are not fully used up, the traded volume should be split between them pro-rata by their total total size. Any integer remainder should be allocated to the iceberg with the highest time priority. + +- Icebergs are refreshed after an auction uncrossing if they traded to below their `minimum peak size`, according to the same rules as for normal execution. + + +#### APIs + +- The fields `displayed quantity`, `remaining`, `quantity`, `initial peak size`, `minimum peak size`, `refresh policy` must be exposed by data node APIs in addition to all normal fields for an order. + +- An iceberg order refresh must generate an event of the event bus. + +- Any API that returns information about market-depth or the orderbook volume will include an iceberg order's full volume and not just its `display quantity`. + + ### Valid order entry combinations #### Continuous trading @@ -83,12 +262,161 @@ Network orders are used during [position resolution](./0012-POSR-position_resolu ## Acceptance Criteria - Immediate orders, continuous trading: - - An aggressive persistent (GTT, GTC) limit order that is not crossed with the order book is included on the order book at limit order price at the back of the queue of orders at that price. No trades are generated. (0014-ORDT-001) - - An aggressive persistent (GTT, GTC) limit order that crosses with trades >= to its volume is filled completely and does not appear on the order book or in the order book volume. Trades are atomically generated for the full volume. (0014-ORDT-002) - - An aggressive persistent (GTT, GTC) limit order that is partially filled generates trades commensurate with the filled volume. The remaining volume is placed on the order book at the limit order price, at the back of the queue of orders at that price. (0014-ORDT-003) - - Any GTT limit order that [still] resides on the order book at its expiry time is cancelled and removed from the book before any events are processed that rely on its being present on the book, including any calculation that incorporate its volume and/or price level. (0014-ORDT-004) - - A GTT order submitted at a time >= its expiry time is rejected. (0014-ORDT-005) -- No party can submit a [network order type](#network-orders) (0014-ORDT-006) + - An aggressive persistent (GTT, GTC) limit order that is not crossed with the order book is included on the order book at limit order price at the back of the queue of orders at that price. No trades are generated. (0014-ORDT-001). + - An aggressive persistent (GTT, GTC) limit order that crosses with trades >= to its volume is filled completely and does not appear on the order book or in the order book volume. Trades are atomically generated for the full volume. (0014-ORDT-002). + - An aggressive persistent (GTT, GTC) limit order that is partially filled generates trades commensurate with the filled volume. The remaining volume is placed on the order book at the limit order price, at the back of the queue of orders at that price. (0014-ORDT-003). + - Any GTT limit order that [still] resides on the order book at its expiry time is cancelled and removed from the book before any events are processed that rely on its being present on the book, including any calculation that incorporates its volume and/or price level. (0014-ORDT-004). + - A GTT order submitted at a time >= its expiry time is rejected. (0014-ORDT-005). +- No party can submit a [network order type](#network-orders) (0014-ORDT-006). +- A pegged order (including iceberg pegged orders) never has its price updated during the execution of an incoming aggressive order (even as price levels get consumed so that its reference price changes after the execution). (0014-ORDT-039) + +### Iceberg Orders AC's + +#### Iceberg Order Submission + +1. A persistent (GTC, GTT, GFA, GFN) iceberg order that is not crossed with the order book is included in the order book with order book volume == initial peak size. No trades are generated (0014-ORDT-007). +2. An iceberg order with either an ordinary or pegged limit price can be submitted (0014-ORDT-008). +3. An iceberg post only order can be submitted (0014-ORDT-009). +4. An iceberg reduce only order is rejected (0014-ORDT-010). +5. For an iceberg order that is submitted with total size x and display size y the margin taken should be identical to a regular order of size `x` rather than one of size `y` (0014-ORDT-011) +6. For an iceberg order, the orders are refreshed immediately after producing a trade. Every time volume is taken from the displayed quantity , the order is refreshed if display quantity < minimum peak size (0014-ORDT-012). + - If the order is successfully refreshed , then the order loses its time priority and is pushed to the back of the queue +7. For an iceberg order that's submitted when the market is in auction, iceberg orders trade according to their behaviour if they were already on the book (trading first the visible size, then additional if the full visible price level is exhausted in the uncrossing) (0014-ORDT-013). + +#### Iceberg Order Batch Submission + +1. For multiple iceberg orders submitted as a batch of orders with a mix of ordinary limit orders and market orders, the iceberg orders are processed atomically and the order book volume and price, margin calculations , order status are all correct (0014-ORDT-014) +2. For an iceberg order submitted in a batch that trades against multiple other orders sitting on the book, the iceberg order refreshes between each order in the batch (0014-ORDT-015). + +#### Iceberg Order Submission - Negative tests + +1. An iceberg order with a non persistent TIF (IOC, FOK) is rejected with a valid error message (0014-ORDT-016). +2. An iceberg market order with any TIF is rejected with a valid error message (0014-ORDT-017). +3. A reduce-only iceberg order with any TIF is rejected with a valid error message (0014-ORDT-018). +4. An iceberg order with initial peak size greater than the total order size is rejected with a valid error message (0014-ORDT-020). +5. An iceberg order with minimum peak size less than 0 is rejected with a valid error message (0014-ORDT-021). +6. An iceberg order with minimum peak size greater than initial peak size is rejected with a valid error message (0014-ORDT-022). + +#### Iceberg Order Amendment + +1. Amending an iceberg order to increase size will increase the total and remaining quantities of the order and time priority of the order is not lost (0014-ORDT-023). +2. Amending an iceberg order to decrease size will decrease the total and remaining quantities and time priority of the order is not lost (0014-ORDT-024). +3. Amend an iceberg order to decrease size so that the displayed quantity is decreased. Total, displayed and remaining quantity is decreased, margin is recalculated and released and time priority is not lost (0014-ORDT-025) + +#### Iceberg Order Cancellation + +1. Cancelling an iceberg order will cancel the order, remove it from the order book , release margin and update order book to reflect the change (0014-ORDT-026) + +#### Iceberg Order Execution + +1. An aggressive iceberg order that crosses with an order where volume > iceberg volume, the iceberg order gets fully filled on entry, the iceberg order status is filled, the remaining quantity = 0. Atomic trades are generated if matched against multiple orders (0014-ORDT-027). +2. An aggressive iceberg order that crosses with an order where volume < iceberg volume. The initial display quantity is filled and the remaining volume is unfilled. Status of iceberg order is active , the volume remaining = (quantity - initial volume) and the remaining volume sits on the book. When additional orders are submitted which consume the remaining volume on the iceberg order , the volume of the iceberg order is refreshed as and when the volume dips below the minimum peak size (0014-ORDT-028). +3. A passive iceberg order (the only order at a particular price level) when crossed with another order that comes in which consumes the full volume of the iceberg order is fully filled. Status of iceberg order is filled and the remaining = 0. Atomic trades are produced (0014-ORDT-029). +4. A passive iceberg order with a couple of order that sit behind the iceberg order at the same price that crosses with an order where volume > display quantity of iceberg order. After the first trade is produced , the iceberg order is pushed to the back of the queue and gets filled only when the other orders in front get fully filled (0014-ORDT-030). +5. Submit an aggressive iceberg order for size 100. There are multiple matching orders of size 30,40,50. Ensure the orders are matched and filled in time priority of the orders and any remaining volume on the orders is correctly left behind. (0014-ORDT-031). +6. Submit an aggressive iceberg order for size 100. There are multiple matching orders of size 20,30. Ensure the orders are matched and filled in time priority of the orders. Ensure remaining volume on the iceberg order is (100 - (20+30)) (0014-ORDT-032). +7. When a non iceberg order sitting on the book is amended such that it trades with with an iceberg order, then the iceberg order is refreshed (0014-ORDT-033). +8. Wash trading is not permitted for iceberg orders. The same party has one iceberg order that sits at the back of the queue, another normal order in opposite direction, when the iceberg at the back comes in front the normal order should be stopped. ( 0014-ORDT-034). +9. For a price level with multiple iceberg orders, if an aggressive order hits this price level, any volume greater than the displayed volume at a level is split proportionally between the hidden components of iceberg orders at that price level + 1. If there are three iceberg orders with remaining volume 200 lots, 100 lots and 100 lots, an order for 300 lots would be split 150 to the first order and 75 to the two 100 lot orders. (0014-ORDT-037). + 1. If there are three iceberg orders with remaining volume 200 lots, 100 lots and 100 lots, an order for 600 lots would be split 200 to the first order and 100 to the two 100 lot orders, with 200 lots then taking farther price levels. (0014-ORDT-038). + +### Snapshots + +1. All data pertaining to iceberg orders is saved and can be restored using the snapshot (0014-ORDT-035). + +### API + +1. API end points should be available to query initial peak size, minimum peak size, quantity, displayed quantity and remaining (0014-ORDT-036). +2. The additional fields relating to iceberg orders should be available in the streaming api end points (0014-ORDT-069). +3. API end points showing market-depth or price-level volume should include the full volume of iceberg orders (0014-ORDT-070). + +### Stop orders + +- Once triggered, a stop order is removed from the book and cannot be triggered again. (0014-ORDT-041) +- A stop order placed by a key with a zero position and no open orders will be rejected. (0014-ORDT-042) +- A stop order placed by a key with a zero position but open orders will be accepted. (0014-ORDT-043) +- Attempting to create more stop orders than is allowed by the relevant network parameter will result in the transaction failing to execute. (0014-ORDT-044) + +- A stop order wrapping a limit order will, once triggered, place the limit order as if it just arrived as an order without the stop order wrapping. (0014-ORDT-045) +- A stop order wrapping a market order will, once triggered, place the market order as if it just arrived as an order without the stop order wrapping. (0014-ORDT-046) + +- With a last traded price at 50, a stop order placed with `Rises Above` setting at 75 will be triggered by any trade at price 75 or higher. (0014-ORDT-047) +- With a last traded price at 50, a stop order placed with `Rises Above` setting at 25 will be triggered immediately (before another trade is even necessary). (0014-ORDT-048) +- With a last traded price at 50, a stop order placed with `Falls Below` setting at 25 will be triggered by any trade at price 25 or lower. (0014-ORDT-049) +- With a last traded price at 50, a stop order placed with `Falls Below` setting at 75 will be triggered immediately (before another trade is even necessary). (0014-ORDT-050) + +- With a last traded price at 50, a stop order placed with any trigger price which does not trigger immediately will trigger as soon as a trade occurs at a trigger price, and will not wait until the next mark price update to trigger. (0014-ORDT-051) +- A stop order with expiration time `T` set to expire at that time will expire at time `T` if reached without being triggered. (0014-ORDT-052) +- A stop order with expiration time `T` set to execute at that time will execute at time `T` if reached without being triggered. (0014-ORDT-053) + - If the order is triggered before reaching time `T`, the order will have been removed and will *not* trigger at time `T`. (0014-ORDT-054) + - An OCO stop order with expiration time `T` with one side set to execute at that time will execute at time `T` if reached without being triggered, with the specified side triggering and the other side cancelling. This must be tested both sides (fall below and rise above). (0014-ORDT-131) + - An OCO stop order with expiration time `T` with both sides set to execute at that time will be rejected on submission (0014-ORDT-130) + +- A stop order set to trade volume `x` with a trigger set to `Rises Above` at a given price will trigger at the first trade at or above that price. (0014-ORDT-055) +- If a pair of stop orders are specified as OCO, one being triggered also removes the other from the book. (0014-ORDT-056) +- If a pair of stop orders are specified as OCO and one triggers but is invalid at time of triggering (e.g. a buy when the trader is already long) the other will still be cancelled. (0014-ORDT-058) + +- A trailing stop order for a 5% drop placed when the price is `50`, followed by a price rise to `60` will: + - Be triggered by a fall to `57`. (0014-ORDT-059) + - Not be triggered by a fall to `58`. (0014-ORDT-060) +- A trailing stop order for a 5% rise placed when the price is `50`, followed by a drop to `40` will: + - Be triggered by a rise to `42`. (0014-ORDT-061) + - Not be triggered by a rise to `41`. (0014-ORDT-062) +- A trailing stop order for a 25% drop placed when the price is `50`, followed by a price rise to `60`, then to `50`, then another rise to `57` will: + - Be triggered by a fall to `45`. (0014-ORDT-063) + - Not be triggered by a fall to `46`. (0014-ORDT-064) + +- A stop order placed during an auction will not execute during an auction, nor will it participate in the uncrossing. (0014-ORDT-065) +- A stop order placed during an auction, where the uncrossing price is within the triggering range, will immediately execute following uncrossing. (0014-ORDT-066) + +- A stop order placed prior to an auction will not execute during an auction, nor will it participate in the uncrossing. (0014-ORDT-134) +- A stop order placed prior to an auction, where the uncrossing price is within the triggering range, will immediately execute following uncrossing. (0014-ORDT-135) +- An order with a stop is placed during continuous trading. The market goes into auction. The market exits auction, the condition for triggering the stop is not met. The stop order is still present. (0014-ORDT-136) + +- A party places a stop order on a market in continuous trading, the market moves to an auction and the party cancels the stop order. When the market exits the auction the party no longer has a stop order. (0014-ORDT-132) +- A stop order placed during the opening auction, will be rejected. (0014-ORDT-133) + +- If a trader has open stop orders and their position moves to zero whilst they still have open limit orders their stop orders will remain active. (0014-ORDT-067) +- If a trader has open stop orders and their position moves to zero with no open limit orders their stop orders are cancelled. (0014-ORDT-068) + +- A Stop order that hasn't been triggered can be cancelled. (0014-ORDT-071) +- All stop orders for a specific party can be cancelled by a single stop order cancellation. (0014-ORDT-072) +- All stop orders for a specific party for a specific market can be cancelled by a single stop order cancellation. (0014-ORDT-073) +- If a stop order is placed with a position_fraction equal to 0.5 and the position size is 5 then the rounding should be equal to 3 (0014-ORDT-138) + + +## Stop Orders - Negative Cases + +- Stop orders submitted with post_only=True are rejected. (0014-ORDT-074) +- Stop orders submitted with invalid values for trigger price (0, negative values) and trailing percentage (0, negative values) are rejected. (0014-ORDT-075) +- Stop orders submitted with expiry in the past are rejected. (0014-ORDT-076) +- GFA Stop orders submitted are rejected. (0014-ORDT-077) +- Stop orders once triggered can not be cancelled. (0014-ORDT-078) +- If a stop order is placed with a position_fraction equal to 0 the order should be rejected. (0014-ORDT-139) +- A party with a long position cannot enter a buy stop order, and a party with a short position cannot enter a sell stop order (0014-ORDT-137) + +## Stop Orders - Snapshots + +- Stop orders are saved and can be restored using the snapshot and will be triggered once the trigger conditions are met. (0014-ORDT-079) + +## Stop Orders - API + +- API end points should be available to query stop orders with all relevant fields. (0014-ORDT-080) + +## Stop Orders - Linked + +- A stop order with a size override linked to the position of the trader will use the current position as an override of the triggered order size. (0014-ORDT-127) +- All stop orders with a position size override should be cancelled if the trader's position flips sides (long->short or short->long). (0014-ORDT-128) +- A stop order with a position size override with a position_fraction set to 0.75, for a trader with long position 20, should create a stop order for selling size 15 when triggered (0014-ORDT-129) + +## Perpetuals + +- All order types should be able to be placed and act in the same way on a perpetual market as on an expiring future market. Specifically this includes: + - Limit orders (0014-ORDT-120) + - Market orders (0014-ORDT-121) + - Icebergs (0014-ORDT-122) + - All stop order types (0014-ORDT-123) ### See also diff --git a/protocol/0015-INSR-market_insurance_pool_collateral.md b/protocol/0015-INSR-market_insurance_pool_collateral.md index 0b9bd267e..34677e201 100644 --- a/protocol/0015-INSR-market_insurance_pool_collateral.md +++ b/protocol/0015-INSR-market_insurance_pool_collateral.md @@ -1,23 +1,28 @@ -# Market insurance pool +# Insurance pools -## Summary +## Market insurance pool Every market will have at least one insurance pool account that holds collateral that can be used to cover losses in case of unreasonable market events. -## Guide-level explanation - -## Reference-level explanation - Every [tradable instrument](./0001-MKTF-market_framework.md) has one or more settlement assets defined for that market. The market requires an insurance pool account for every settlement asset of the market. If no insurance pool account already exists in the risk universe that the tradable instrument sits within, then an insurance pool account needs to be created for all settlement assets of the market. These new insurance pool accounts will be instantiated in the settlement asset/s of the market, with a balance of zero (across all assets). -Only transfer requests can move collateral to or from the insurance account. +Only transfer requests can move collateral to or from the market's insurance account. When a market is finalised / closed remaining funds are distributed equally among other same-currency insurance pools (including the on-chain treasury for the asset, if no such treasury exists it gets created at this point). This occurs using ledger entries to preserve double entry accounting records within the collateral engine. +## Global insurance pool + +One global insurance pool per each of the settlement asset in which markets were terminated with remaining market-level insurance pool funds exists. It receives all the funds from market's insurance pool upon its closure. + +Global insurance pool is not included in the margin search process when party is insolvent - only the insurance pool of the market in which the insolvent party's liabilities are considered gets searched, if funds therein are insufficient then the remaining shortfall gets dealt with using loss socialisation. + +Funds can only be withdrawn from the global insurance pool via a [governance initiated transfer](./0028-GOVE-governance.md). + ## Acceptance Criteria - When a market proposal gets accepted and the opening auction commences, there is an insurance account that is available for use by that market for the settlement asset of that market and its balance is zero. (0015-INSR-001) -- When the market enters transitions from "trading terminated state" to "settled" state (see [market lifecyle](0043-MKTL-market_lifecycle.md)), the insurance pool account has its balance[redistributed](./0015-INSR-market_insurance_pool_collateral.md) to the on-chain treasury for the settlement asset of the market and other insurance pools using the same asset. (0015-INSR-002) +- After the market enters transitions from "trading terminated state" to "settled" state (see [market lifecyle](0043-MKTL-market_lifecycle.md)), and `market.liquidity.successorLaunchWindowLength` has elapsed from the settlement time, the insurance pool account has its balance transferred to the on-chain treasury for the settlement asset of the market. For markets that have a named successor market the appropriate fraction of the insurance pool balance is transferred to the insurance pool of the successor market. (0015-INSR-005) - The [insurance pool feature test](https://github.com/vegaprotocol/vega/blob/develop/core/integration/features/verified/0015-INSR-insurance_pool_balance_test.feature) is passing. (0015-INSR-003) +- When global insurance pool has positive balance for the settlement asset used by market with insolvent party, and that market's insurance pool has insufficient balance to cover the shortfall, the global insurance pool balance does NOT get used and the remaining balances are dealt with using the loss socialisation mechanism. (0015-INSR-004) diff --git a/protocol/0017-PART-party.md b/protocol/0017-PART-party.md index a2908e50a..cc63d3315 100644 --- a/protocol/0017-PART-party.md +++ b/protocol/0017-PART-party.md @@ -18,7 +18,7 @@ A standard party can: - [Cancel an order](./0033-OCAN-cancel_orders.md) - [Submit a governance proposal](./0028-GOVE-governance.md) - [Vote on a governance proposal](./0028-GOVE-governance.md) -- [Commit to provide liquidity on a market](./0038-OLIQ-liquidity_provision_order_type.md) +- [Commit to provide liquidity on a market](./0044-LIME-lp_mechanics.md#commit-liquidity-network-transaction) ## Guide-level explanation @@ -38,6 +38,7 @@ The `network` party is a pseudo party. It is used in [position resolution](./001 1. [x] When a [standard party](#standard-party) must be uniquely identified, it must be identified by the public key of an ed25519 keypair (0017-PART-001) + Note that when and how a party can submit transactions is set out and restricted in: - [Transactions](./0049-TVAL-validate_transaction_preconsensus.md) diff --git a/protocol/0019-MCAL-margin_calculator.md b/protocol/0019-MCAL-margin_calculator.md index 96d4ed045..f307aabd7 100644 --- a/protocol/0019-MCAL-margin_calculator.md +++ b/protocol/0019-MCAL-margin_calculator.md @@ -1,6 +1,6 @@ # Margin Calculator -## Acceptance Criteria +## Acceptance Criteria (Cross margin) - Get four margin levels for one or more parties (0019-MCAL-001) @@ -24,39 +24,605 @@ - A feature test that checks margin in case market PDP < 0 is created and passes. (0019-MCAL-010) -- If a party is short `1` unit and the mark price is `15 900` and `market.maxSlippageFraction[1] = 0.25`, `market.maxSlippageFraction[2] = 0.25` and `RF short = 0.1` and order book is +- If a party is short `1` unit and the mark price is `15 900` and `market.linearSlippageFactor = 0.25` and `RF short = 0.1` and order book is ```book - buy 1 @ 15 000 - buy 10 @ 14 900 + buy 1 @ 15 000 + buy 10 @ 14 900 and sell 1 @ 100 000 - sell 10 @ 100 100 + sell 10 @ 100 100 ``` - then the maintenance margin for the party is `15 900 x (0.25 x 1 + 0.25 x 1 x 1) + 0.1 x 1 x 15 900 = 9 540`. (0019-MCAL-011) + then the maintenance margin for the party is `min(1 x (100000-15900), 15900 x 0.25 x 1) + 0.1 x 1 x 15900 = 5565`. (0019-MCAL-024) -- In the same situation as above, if `market.maxSlippageFraction[1] = 100`, `market.maxSlippageFraction[2] = 100` (i.e. 10 000% for both) instead, then the margin for the party is `84 100 + 0.1 x 1 x 15900 = 85 690`. (0019-MCAL-012) +- In the same situation as above, if `market.linearSlippageFactor = 100`, (i.e. 10 000%) instead, then the margin for the party is `min(1 x (100000-15900), 15900 x 100 x 1) + 0.1 x 1 x 15900 = 85690`. (0019-MCAL-025) -- If the `market.maxSlippageFraction` is updated via governance then it will be used at the next margin evaluation i.e. at the first mark price update following the parameter update. (0019-MCAL-013) +- If the `market.linearSlippageFactor` is updated via governance then it will be used at the next margin evaluation i.e. at the first mark price update following the parameter update. (0019-MCAL-013) + +- For a perpetual future market, the maintenance margin is equal to the maintenance margin on an equivalent dated future market, plus a component related to the expected upcoming margin funding payment. Specifically: + - If a party is long `1` unit and the mark price is `15 900` and `market.linearSlippageFactor = 0.25` and `RF long = 0.1` and order book is + + ```book + buy 1 @ 15 000 + buy 10 @ 14 900 + and + sell 1 @ 100 000 + sell 10 @ 100 100 + ``` + + then the dated future maintenance margin component for the party is `min(1 x (100000-15900), 15900 x 0.25 x 1) + 0.1 x 1 x 15900 = 5565`. The current accrued funding payment for the perpetual component is calculated using + + ```book + delta_t = funding_period_end - max(funding_period_start, internal_data_points[0].t) + funding_payment = f_twap - s_twap + min(clamp_upper_bound*s_twap,max(clamp_lower_bound*s_twap, (1 + delta_t * interest_rate)*s_twap-f_twap)) + ``` + + Where `f_twap` represents the internal mark price TWAP and `s_twap` represents the TWAP from the external oracle feed. When clamp bounds are large we use: + + ```book + funding_payment = f_twap - s_twap + (1 + delta_t * interest_rate)*s_twap-f_twap + = s_twap * delta_t * interest_rate + ``` + + - If `s_twap = 1600`, `f_twap = 1590` `delta_t = 0.002` and `interest_rate = 0.05` then `funding_payment = 1600 * 0.002 * 0.05 = 0.16`. + - Thus, if `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 556.5 + 0.5 * 0.16 * 1 = 556.58` (0019-MCAL-026) + + - If instead + - `clamp_upper_bound*s_twap < max(clamp_lower_bound*s_twap, (1 + delta_t * interest_rate)*s_twap-f_twap)` + - `funding payment = f_twap - s_twap + clamp_upper_bound*s_twap = f_twap + s_twap * (clamp_upper_bound - 1)`. + - Then with `s_twap = 1600`, `clamp_upper_bound = 0.05` and `f_twap = 1500`, `funding_payment = 1500 + 1600 * (0.05 - 1) = -20` + - Thus, with `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 525 + 0.5 * max(0, -20 * 1) = 525` (0019-MCAL-027) + - However is position is instead `-1`, with the same margin requirement, if `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 525 + 0.5 * max(0, -20 * -1 = 535)`(0019-MCAL-030) + + - If instead + - `clamp_upper_bound*s_twap > clamp_lower_bound*s_twap > (1 + delta_t * interest_rate)*s_twap-f_twap)` + - `funding payment = f_twap - s_twap + clamp_lower_bound*s_twap = f_twap + s_twap * (clamp_lower_bound - 1)`. + - Then with `s_twap = 1600`, `clamp_lower_bound = -0.05` and `f_twap = 1700`, `funding_payment = 1700 + 1600 * (-0.05 - 1) = 20` + - Thus, with `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 595 + 0.5 * max(0, 20 * 1) = 605` (0019-MCAL-028) + - However is position is instead `-1`, with the same margin requirement, if `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 595 + 0.5 * max(0, 20 * -1) = 595`(0019-MCAL-029) + +## Acceptance Criteria (Isolated-margin) + +**When party has a newly created short position:** + +- If a party has a newly created short position of `1` and the mark price is `15 900` and `market.linearSlippageFactor = 0.25`, `RF short = 0.1` and `Initial margin factor = 1.5` and order book is + + ```book + buy 1 @ 15 000 + buy 10 @ 14 900 + and + sell 1 @ 100 000 + sell 10 @ 100 100 + ``` + +- When switching to isolated-margin mode and the `margin factor short = 0.11`, the maintenance margin should be updated to `average entry price x current position x new margin factor = 57500 x 1 x 0.11 = 6325`, the switching will be rejected with message "required position margin must be greater than initial margin". (0019-MCAL-032) + +- When switching to isolated-margin mode and the `margin factor short = 0.9`, the maintenance margin should be updated to `average entry price x current position x new margin factor = 15900 x 1 x 0.9 = 14310`, and margin account should be updated to `14310`. (0019-MCAL-033) + +- When decreasing the `margin factor short` from `0.9` to `0.7`, the maintenance margin should be updated to `average entry price x current position x new margin factor = 15900 x 1 x 0.7 = 11130`, and margin account should be updated to `11130`. (0019-MCAL-031) + +- When increasing the `margin factor short` from `0.7` to `0.9`, the maintenance margin should be updated to `average entry price x current position x new margin factor = 15900 x 1 x 0.9 = 14310`, and margin account should be updated to `14310`. (0019-MCAL-059) + +- Switching to isolated margin mode will be rejected if the party does not have enough asset in the general account to top up the margin account to the new required level (0019-MCAL-066) + +**When party has a position and an order which does not offset the position:** + +- When the party places a new short order of `10` with price `15910` which does not offset the existing position, and the market is in continuous trading. +There should be an additional amount `limit price x size x margin factor = 15910 x 10 x 0.9 = 143190` transferred into "order margin" account if the party has enough asset in the general account(0019-MCAL-034) + +- The order will be rejected if the party does not have enough asset in the general account (0019-MCAL-035) + +- The party amends the order size to `5`, and the amount `limit price x size x margin factor = 15912 x 5 x 0.9 = 71604` will be transferred from "order margin" account into general account (0019-MCAL-060) + +- Another trader places a buy order of `3` with price `15912`, party's position changes from `1` to `4`, party's margin account should have additional `15912 x 3 x 0.9 =42962` transferred from general account and order margin should be reduced to `15912x 2 x 0.9 = 28642`since party's order size has been reduced from `5` to `2` after the trade (0019-MCAL-061) + +- Switch margin mode from isolated margin to cross margin when party holds position only, the margin account should be updated to initial margin level in cross margin mode(0019-MCAL-065) + +- Switch from cross margin to isolated margin mode, both margin account and order margin should be updated (0019-MCAL-064) + +- When the party has no orders, their order margin account should be `0` (0019-MCAL-062) + +- When the mark price moves, the margin account should be updated while order margin account should not (0019-MCAL-067) + +- Amend the order (change size) so that new side margin + margin account balance < maintenance margin, the remaining should be stopped (0019-MCAL-068) + +**When a party has a position and an order which offsets the position:** + +- When the party places a new long order of `2` with price `15912` which offsets the existing position, and the market is in continuous trading. The margin account should not change as no additional margin is required (0019-MCAL-038) + +- When the party switches to cross margin mode, the margin accounts will not be updated until the next MTM (0019-MCAL-036) + +- The order will be rejected if the party does not have enough asset in the general account (0019-MCAL-037) + +- When the party place a new long order of `10` with price `145000` and the party has existing short position of `3`, and the market is in continuous trading. The margin account should have additional amount `limit price * size * margin factor = 145000 x (10-3) x 0.9 = 913500` added if the party has enough asset in the general account(0019-MCAL-039) + +- When increasing the `margin factor` and the party does not have enough asset in the general account to cover the new maintenance margin, then the new margin factor will be rejected (0019-MCAL-040) + +**Amending order:** + +- When the party submit a pegged order, it should be rejected(0019-MCAL-049) + +- When the party submit a iceberg pegged order, it should be rejected(0019-MCAL-052) + +- When the party has pegged orders and switches from cross margin mode to isolated margin mode, all the pegged orders will be cancelled. (0019-MCAL-050) + +- When the party has iceberg pegged orders and switches from cross margin mode to isolated margin mode, all the iceberg pegged orders will be cancelled. (0019-MCAL-051) + +- A party with multiple types of orders in cross margin mode switches to isolated margin only their pegged orders are cancelled. (0019-MCAL-057) + +- A market in continuous trading and a party with a partially filled pegged order in cross margin mode switches to isolated margin mode the unfilled portion of the pegged order is cancelled (0019-MCAL-075) + +- A market in continuous trading and a party with a partially filled iceberg pegged order in cross margin mode switches to isolated margin mode the unfilled portion of the pegged order is cancelled (0019-MCAL-078) + +- A market in auction trading and a party with a partially filled pegged order in cross margin mode switches to isolated margin mode the unfilled portion of the pegged order is cancelled (0019-MCAL-147) + +- A market in auction trading and a party with a partially filled iceberg pegged order in cross margin mode switches to isolated margin mode the unfilled portion of the pegged order is cancelled (0019-MCAL-148) + +- A party with a parked pegged order switches from cross margin mode to isolated margin mode, the parked pegged order is cancelled (0019-MCAL-149) + +- A party with a parked iceberg pegged order switches from cross margin mode to isolated margin mode, the parked iceberg pegged order is cancelled (0019-MCAL-144) + +- A market in auction and party with a partially filled pegged order switches from cross margin mode to isolated margin mode the unfilled portion of the pegged order is cancelled (0019-MCAL-145) + +- A market in an auction and party with a partially filled iceberg pegged order switches from cross margin mode to isolated margin mode the unfilled portion of the iceberg pegged order is cancelled (0019-MCAL-146) + +- When a party holds only orders, increases the orders price, the orders will not uncross (0019-MCAL-160) + +- When a party holds orders and positions, and increases the orders price, the orders will not uncross (0019-MCAL-161) + +- When a party holds only orders, increases the orders size, the orders will not uncross (0019-MCAL-162) + +- When a party holds orders and positions, increases the order size, the orders will not uncross (0019-MCAL-163) + +- When a party holds only orders, decreases the orders price, the orders will not uncross (0019-MCAL-164) + +- When a party holds orders and positions, and decreases the orders price, the orders will not uncross (0019-MCAL-165) + +- When a party holds only orders, decreases the orders size, the orders will not uncross (0019-MCAL-166) + +- When a party holds orders and positions, decreases the order size, the orders will not uncross (0019-MCAL-167) + +- When a party holds orders and positions, increase the order size, the order will be stopped (because their margin balance will be less than the margin maintenance level) (0019-MCAL-168) + +- When a party holds orders and positions, decreases the orders size, the order and order margin will be update (0019-MCAL-169) + +- When a party holds orders and positions, decreases the orders price, the order will be fully filled (0019-MCAL-172) + +- When a party holds orders and positions, decreases the orders price, the order will be partially filled (0019-MCAL-173) + +- When a party holds orders and positions, decreases the orders price, the order will be stopped (because if the orders is fully filled and then their margin balance will be less than the margin maintenance level) (0019-MCAL-174) + +- When a party holds orders and positions, decreases the orders price, the order will be stopped (because if the orders is partially filled and then their margin balance will be less than the margin maintenance level) (0019-MCAL-175) + +- When a party holds orders and positions, amend the orders price, the order will be partially filled but order margin level will increase, so the rest of the order is cancelled (0019-MCAL-176) + +**When a party is distressed:** + +- Open positions should be closed in the case of open positions dropping below maintenance margin level, active orders will remain active if closing positions does not lead order margin level to increase.(0019-MCAL-070) + +- Open positions should be closed in the case of open positions dropping below maintenance margin level, active orders will be cancelled if closing positions lead order margin level to increase.(0019-MCAL-071) + +- When a party (who holds open positions and bond account) gets distressed, open positions will be closed, the bond account will be emptied (0019-MCAL-072) + +- When a party (who holds open positions, open orders and bond account) gets distressed, the bond account will be treated as in cross margin mode, however active orders will remain active if closing positions does not lead order margin level to increase. (0019-MCAL-073) + +- When a party (who holds open positions, open orders and bond account) gets distressed, the bond account will be emptied, active orders will be cancelled if closing positions lead order margin level to increase. (0019-MCAL-074) + +**Switch between margin modes:** + +- switch to isolated margin with no position and no order (before the first order ever has been sent) in continuous mode(0019-MCAL-100) + +- switch back to cross margin with no position and no order in continuous mode(0019-MCAL-101) + +- switch to isolated margin with no position and no order (before the first order ever has been sent) in auction(0019-MCAL-102) + +- switch back to cross margin with no position and no order in continuous mode in auction(0019-MCAL-103) + +- switch to isolated margin with position and no orders with margin factor such that position margin is < initial should fail in continuous(0019-MCAL-104) + +- switch to isolated margin with position and no orders with margin factor such that position margin is < initial should fail in auction(0019-MCAL-105) + +- switch to isolated margin without position and with orders with margin factor such that position margin is < initial should fail in continuous(0019-MCAL-106) + +- switch to isolated margin without position and with orders with margin factor such that position margin is < initial should fail in auction(0019-MCAL-107) + +- switch to isolated margin with position and with orders with margin factor such that position margin is < initial should fail in continuous(0019-MCAL-108) + +- switch to isolated margin with position and with orders with margin factor such that position margin is < initial should fail in auction(0019-MCAL-109) + +- switch to isolated margin without position and no orders with margin factor such that there is insufficient balance in the general account in continuous mode(0019-MCAL-110) + +- switch to isolated margin with position and no orders with margin factor such that there is insufficient balance in the general account in continuous mode(0019-MCAL-112) + +- switch to isolated margin with position and no orders with margin factor such that there is insufficient balance in the general account in auction mode(0019-MCAL-113) + +- switch to isolated margin with position and with orders with margin factor such that there is insufficient balance in the general account in continuous mode(0019-MCAL-114) + +- switch to isolated margin with position and with orders with margin factor such that there is insufficient balance in the general account in auction mode(0019-MCAL-142/a>) +- switch to isolate margin with out of range margin factor(0019-MCAL-115) + +- submit update margin mode transaction with no state change (already in cross margin, "change" to cross margin, or already in isolated, submit with same margin factor)(0019-MCAL-116) + +- update margin factor when already in isolated mode to the same cases as in switch to isolated failures.(0019-MCAL-117) + +- switch to isolated margin without position and no orders successful in auction(0019-MCAL-119) + +- switch to isolated margin with position and no orders successful in continuous mode(0019-MCAL-120) + +- switch to isolated margin with position and no orders successful in auction(0019-MCAL-121) + +- switch to isolated margin without position and with orders successful in continuous mode(0019-MCAL-122) + +- switch to isolated margin without position and with orders successful in auction(0019-MCAL-123) + +- switch to isolated margin with position and with orders successful in continuous mode(0019-MCAL-124) + +- switch to isolated margin with position and with orders successful in auction(0019-MCAL-125) + +- increase margin factor in isolated margin without position and no orders successful in continuous mode(0019-MCAL-126) + +- increase margin factor in isolated margin without position and no orders successful in auction(0019-MCAL-127) + +- increase margin factor in isolated margin with position and no orders successful in continuous mode(0019-MCAL-128) + +- increase margin factor in isolated margin with position and no orders successful in auction(0019-MCAL-129) + +- increase margin factor in isolated margin without position and with orders successful in continuous mode(0019-MCAL-130) + +- increase margin factor in isolated margin without position and with orders successful in auction(0019-MCAL-131) + +- increase margin factor in isolated margin with position and with orders successful in continuous mode(0019-MCAL-132) + +- increase margin factor in isolated margin with position and with orders successful in auction(0019-MCAL-133) + +- In cross margin mode for a market with no price monitoring, a party `short 1`, `mark price = 15 900`, `market.linearSlippageFactor = 0.25`, `RF short = 0.1` and order book is + + ```book + buy 1 @ 15 000 + buy 10 @ 14 900 + and + sell 1 @ 100 000 + sell 10 @ 100 100 + ``` + + the maintenance margin for the party is `min(1 x (100 000-159 00), 159 00 x 0.25 x 1) + 0.1 x 1 x 159 00 = 5565` + + for this market the party switches to isolated margin with `margin factor=0.9` then the party will have margin account balance of + `average entry price x current position x new margin factor = 57 500 x 1 x 0.9 = 6325` + the difference topped up from the party’s general account(0019-MCAL-233) + +- In isolated margin mode, a party `short 1@15 900`, `margin factor=0.9` and order book is + + ```book + buy 1 @ 15 000 + buy 10 @ 14 900 + and + sell 1 @ 100 000 + sell 10 @ 100 100 + ``` + + the margin account will hold `average entry price x current position x new margin factor = 57 500 x 1 x 0.9 = 6325` + + for this market the party switches to cross margin and the market has `market.linearSlippageFactor = 0.25`, `RF short = 0.1` then the maintenance margin for the party is `min(1 x (100 000-159 00), 159 00 x 0.25 x 1) + 0.1 x 1 x 159 00 = 5565` + but if `5565 < collatoral release level` the maintenance margin will remain unchanged at `6325` + + the difference topped up from the party’s general account(0019-MCAL-232) + +- switch to cross margin without position and no orders successful in continuous mode(0019-MCAL-134) + +- switch to cross margin without position and no orders successful in auction(0019-MCAL-135) + +- switch to cross margin with position and no orders successful in continuous mode(0019-MCAL-136) + +- switch to cross margin with position and no orders successful in auction(0019-MCAL-137) + +- switch to cross margin without position and with orders successful in continuous mode(0019-MCAL-138) + +- switch to cross margin without position and with orders successful in auction(0019-MCAL-139) + +- switch to cross margin with position and with orders successful in continuous mode(0019-MCAL-140) + +- switch to cross margin with position and with orders successful in auction(0019-MCAL-141) + +**Check order margin:** + +- when party has no position, and place 2 short orders during auction, order margin should be updated(0019-MCAL-200) + +- when party has no position, and place short orders size -3 during auction, and long order size 1 which can offset, order margin should be updated using max(price, mark Price, indicative Price)(0019-MCAL-201) + +- when party has no position, and place short orders size -3 during auction, and long orders size 2 which can offset, order margin should be updated using max(price, mark Price, indicative Price)(0019-MCAL-202) + +- when party has no position, and place short orders size -3 during auction, and long orders size 3 which can offset, order margin should be updated using max(price, mark Price, indicative Price)(0019-MCAL-203) + +- when party has no position, and place short orders size -3 during auction, and long orders size 4, which is over the offset size, order margin should be updated using max(price, mark Price, indicative Price)(0019-MCAL-204) + +- When the party changes the order price during auction, order margin should be updated using max(price, mark Price, indicative Price)(0019-MCAL-205) + +- When the party reduces the order size only during auction, the order margin should be reduced (0019-MCAL-206) + +- when party has no position, and place 2 short orders size 3 and 4 long orders of size 4, which is over the offset size, order margin should be updated using max(price, mark Price, indicative Price)(0019-MCAL-207) + +- GFA order added during auction should not be used to count order margin in continuous(0019-MCAL-220) + +- when party has no position, and place 2 short orders during auction, order margin should be updated(0019-MCAL-221) + +- When the party cancel one of the two orders during continuous, order margin should be reduced. When the party increases the order price during continuous, order margin should increase(0019-MCAL-222) + +- When the party decreases the order price during continuous, order margin should decrease(0019-MCAL-223) + +- When the party decreases the order volume during continuous, order margin should decrease(0019-MCAL-224) + +- When the party increases the order volume while decrease price during continuous, order margin should update accordingly(0019-MCAL-225) + +- When the party's order is partially filled during continuous, order margin should update accordingly(0019-MCAL-226) + +- When the party cancel one of the two orders during continuous, order margin should be reduced(0019-MCAL-227) + +- place a GFA order during continuous, order should be rejected(0019-MCAL-228) + +- When the party has position -1 and order -3, and new long order with size 1 will be offset(0019-MCAL-229) + +- When the party has position -1 and order -3, and new long orders with size 2 will be offset(0019-MCAL-230) + +- When the party has position -1 and order -3, and new long orders with size 3 will be offset(0019-MCAL-231) + + +**Check decimals:** + +- A feature test that checks margin in case market PDP > 0 is created and passes. (0019-MCAL-090) + +- A feature test that checks margin in case market PDP < 0 is created and passes. (0019-MCAL-091) + +**Check API:** + +- For each market and each party which has positions or has switched between margin modes on the market, the API provides the maintenance margin levels. (0019-MCAL-092) + +- For each market and each party which has orders only and no positions or has switched between margin modes on the market + - cross margin to isolated margin, the API provides maintenance margin level of zero. (0019-MCAL-150) + - isolated margin to cross margin, the API provides expected maintenance margin level . (0019-MCAL-151) + +- For each market and each party which has either orders or positions on the market, the API provides the current margin mode and, when in isolated margin mode, margin factor. (0019-MCAL-143) + +## Acceptance Criteria (perpetual market in isolated margin mode) + +- For a perpetual future market, the maintenance margin is equal to the maintenance margin on an equivalent dated future market, plus a component related to the expected upcoming margin funding payment. Specifically: + - If a party is long `1` unit and the mark price is `15 900` and `market.linearSlippageFactor = 0.25` and `RF long = 0.1` and order book is + + ```book + buy 1 @ 15 000 + buy 10 @ 14 900 + and + sell 1 @ 100 000 + sell 10 @ 100 100 + ``` + + then the dated future maintenance margin component for the party is `min(1 x (100000-15900), 15900 x 0.25 x 1) + 0.1 x 1 x 15900 = 5565`. The current accrued funding payment for the perpetual component is calculated using + + ```book + delta_t = funding_period_end - max(funding_period_start, internal_data_points[0].t) + funding_payment = f_twap - s_twap + min(clamp_upper_bound*s_twap,max(clamp_lower_bound*s_twap, (1 + delta_t * interest_rate)*s_twap-f_twap)) + ``` + + Where `f_twap` represents the internal mark price TWAP and `s_twap` represents the TWAP from the external oracle feed. When clamp bounds are large we use: + + ```book + funding_payment = f_twap - s_twap + (1 + delta_t * interest_rate)*s_twap-f_twap + = s_twap * delta_t * interest_rate + ``` + + - If `s_twap = 1600`, `f_twap=1590`, `delta_t = 0.002` and `interest_rate = 0.05` then `funding_payment = 1600 * 0.002 * 0.05 = 0.16`. + - Thus, if `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 556.5 + 0.5 * 0.16 * 1 = 556.58` (0019-MCAL-053) + + - If instead + - `clamp_upper_bound*s_twap < max(clamp_lower_bound*s_twap, (1 + delta_t * interest_rate)*s_twap-f_twap)` + - `funding payment = f_twap - s_twap + clamp_upper_bound*s_twap = f_twap + s_twap * (clamp_upper_bound - 1)`. + - Then with `s_twap = 1600`, `clamp_upper_bound = 0.05` and `f_twap = 1550`, `funding_payment = 1590 + 1600 * (0.05 - 1) = 1590 - 1520 = 70` + - Thus, with `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 5565 + 0.5 * 70 * 1 = 5600` (0019-MCAL-058) + - However is position is instead `-1`, with the same margin requirement, if `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 5565 + 0.5 * max(0, 70 * -1) = 5565`(0019-MCAL-054) + + - If instead + - `clamp_upper_bound*s_twap > clamp_lower_bound*s_twap > (1 + delta_t * interest_rate)*s_twap-f_twap)` + - `funding payment = f_twap - s_twap + clamp_lower_bound*s_twap = f_twap + s_twap * (clamp_lower_bound - 1)`. + - Then with `s_twap = 1600`, `clamp_lower_bound = -0.05` and `f_twap = 1700`, `funding_payment = 1700 + 1600 * (-0.05 - 1) = 20` + - Thus, with `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 595 + 0.5 * max(0, 20 * 1) = 605` (0019-MCAL-055) + - However is position is instead `-1`, with the same margin requirement, if `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 595 + 0.5 * max(0, 20 * -1) = 595`(0019-MCAL-056) + + +## Acceptance Criteria (Protocol upgrade) + +- All order margin balances are restored after a protocol upgrade (0019-MCAL-152). +- The `margin mode` and `margin factor` of any given party must be preserved after a protocol upgrade (0019-MCAL-153). + +- For a perpetual future market, the maintenance margin is equal to the maintenance margin on an equivalent dated future market, plus a component related to the expected upcoming margin funding payment. Specifically: + - If a party is long `1` unit and the mark price is `15 900` and `market.maxSlippageFraction[1] = 0.25`, `market.maxSlippageFraction[2] = 0.25` and `RF long = 0.1` and order book is + + ```book + buy 1 @ 15 000 + buy 10 @ 14 900 + and + sell 1 @ 100 000 + sell 10 @ 100 100 + ``` + + then the dated future maintenance margin component for the party is `15 900 x (0.25 x 1 + 0.25 x 1 x 1) + 0.1 x 1 x 15 900 = 9 540`. The current accrued funding payment for the perpetual component is calculated using + + ```book + delta_t = funding_period_end - max(funding_period_start, internal_data_points[0].t) + funding_payment = f_twap - s_twap + min(clamp_upper_bound*s_twap,max(clamp_lower_bound*s_twap, (1 + delta_t * interest_rate)*s_twap-f_twap)) + ``` + + Where `f_twap` represents the internal mark price TWAP and `s_twap` represents the TWAP from the external oracle feed. When clamp bounds are large we use: + + ```book + funding_payment = f_twap - s_twap + (1 + delta_t * interest_rate)*s_twap-f_twap + = s_twap * delta_t * interest_rate + ``` + + - If `s_twap = 1600`, `delta_t = 0.002` and `interest_rate = 0.05` then `funding_payment = 1600 * 0.002 * 0.05 = 0.16`. + - Thus, if `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 9540 + 0.5 * 0.16 * 1 = 9540.08` (0019-MCAL-019) + + - If instead + - `clamp_upper_bound*s_twap < max(clamp_lower_bound*s_twap, (1 + delta_t * interest_rate)*s_twap-f_twap)` + - `funding payment = f_twap - s_twap + clamp_upper_bound*s_twap = f_twap + s_twap * (clamp_upper_bound - 1)`. + - Then with `s_twap = 1600`, `clamp_upper_bound = 0.05` and `f_twap = 1550`, `funding_payment = 1590 + 1600 * (0.05 - 1) = 1590 - 1520 = 70` + - Thus, with `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 9540 + 0.5 * 70 * 1 = 9575` (0019-MCAL-020) + - However is position is instead `-1`, with the same margin requirement, if `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 9540 + 0.5 * max(0, 70 * -1) = 9540`(0019-MCAL-021) + + - If instead + - `clamp_upper_bound*s_twap > clamp_lower_bound*s_twap > (1 + delta_t * interest_rate)*s_twap-f_twap)` + - `funding payment = f_twap - s_twap + clamp_lower_bound*s_twap = f_twap + s_twap * (clamp_lower_bound - 1)`. + - Then with `s_twap = 1600`, `clamp_lower_bound = -0.05` and `f_twap = 1550`, `funding_payment = 1590 + 1600 * (-0.05 - 1) = 1590 - 1680 = -90` + - Thus, with `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 9540 + 0.5 * max(0, -90 * 1) = 9540` (0019-MCAL-022) + - However is position is instead `-1`, with the same margin requirement, if `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 9540 + 0.5 * max(0, -90 * -1) = 9585`(0019-MCAL-023) ## Summary -The *margin calculator* returns the set of relevant margin levels for a given position and entry price: +The *margin calculator* returns the set of margin levels for a given *actual position*, along with the amount of additional margin (if any) required to support the party's *potential position* (i.e. active orders including any that are parked/untriggered/undeployed). + + +### Margining modes + +The system can operate in one of two margining modes for each position. +The current mode will be stored alongside of party's position record. + +1. **Cross-margin mode (default)**: this is the mode used by all newly created positions. +When in cross-margin mode, margin is dynamically acquired and released as a position is marked to market, allowing profitable positions to offset losing positions for higher capital efficiency (especially with e.g. pairs trades). -1. ***Maintenance margin*** -1. ***Collateral search level*** -1. ***Initial margin*** -1. ***Collateral release level*** +1. **Isolated margin mode**: this mode sacrifices capital efficiency for predictability and risk management by segregating positions. +In this mode, the entire margin for any newly opened position volume is transferred to the margin account when the trade is executed. +This includes completely new positions and increases to position size. Other than at time of future trades, the general account will then +*never* be searched for additional funds (a position will be allowed to be closed out instead), nor will profits be moved into the +general account from the margin account. + +### Actual position margin levels + +1. **Maintenance margin**: the minimum margin a party must have in their margin account to avoid the position being liquidated. + +1. **Collateral search level**: when in cross-margin mode, the margin account balance below which the system will seek to recollateralise the margin account back to the initial margin level. + +1. **Initial margin**: when in cross-margin mode, the margin account balance initially allocated for the position, and the balance that will be returned to the margin account after collateral search and release, if possible. When in isolated margin mode, the margin account balance initially allocated for the position. The balance will not be returned to the margin account while the position is open, as there is no collateral search and release. + +1. **Collateral release level**: when in cross-margin mode, the margin account balance above which the system will return collateral from a profitable position to the party's general account for potential use elsewhere. The protocol is designed such that ***Maintenance margin < Collateral search level < Initial margin < Collateral release level***. +### Potential position margin level + +1. **Order margin**: the amount of additional margin on top of the amount in the margin account that is required for the party's current active orders. +Note that this may be zero if the active orders can only decrease the position size. + + Margin levels are used by the protocol to ascertain whether a trader has sufficient collateral to maintain a margined trade. When the trader enters an open position, this required amount is equal to the *initial margin*. Subsequently, throughout the life of this open position, the minimum required amount is the *maintenance margin*. As a trader's collateral level dips below the *collateral search level* the protocol will automatically search for more collateral to be assigned to support this open position from the trader's general collateral accounts. In the event that a trader has collateral that is above the *collateral release level* the protocol will automatically release collateral to a trader's general collateral account for the relevant asset. **Whitepaper reference:** 6.1, section "Margin Calculation" In future there can be multiple margin calculator implementations that would be configurable in the market framework. This spec describes one implementation. +## Isolated margin mode + +When in isolated margin mode, the position on the market has an associated margin factor. +The margin factor must be greater than 0 and less than or equal to 1, and also greater than `max(risk factor long, risk factor short)`. + +Isolated margin mode can be enabled by placing an *update margin mode* transaction. +The protocol will attempt to set the funds within the margin account equal to `average entry price * current position * new margin factor`. +This value must be above the `initial margin` for the current position or the transaction will be rejected. + +The default when placing an order with no change to margin mode specified must be to retain the current margin mode of the position. + +### Placing an order + +When submitting, amending, or deleting an order in isolated margin mode and continuous trading, a two step process will be followed: + + 1. First, the core will check whether the order will trade, either fully or in part, immediately upon entry. If so: + 1. If the trade would increase the party's position, the required additional funds as specified in the Increasing Position section will be calculated. The total expected margin balance (current plus new funds) will then be compared to the `maintenance margin` for the expected position, if the margin balance would be less than maintenance, instead reject the order in it's entirety. If the margin will be greater than the maintenance margin their general account will be checked for sufficient funds. + 1. If they have sufficient, that amount will be moved into their margin account and the immediately matching portion of the order will trade. + 1. If they do not have sufficient, the order will be rejected in it's entirety for not meeting margin requirements. + 1. If the trade would decrease the party's position, that portion will trade and margin will be released as in the Decreasing Position section + 1. If the order is not persistent this is the end, if it is persistent any portion of the order which has not traded in step 1 will move to being placed on the order book. + 1. At this point, the party's general account will be checked for margin to cover the additional amount required for the new orders. Each side can be checked individually and the maximum for either side taken into the order margin pool. This calculation must be rerun every time the party's orders or position change. For each side: + 1. Sort all orders by price, starting from first to execute (highest price for buying, lowest price for selling). + 1. If the party currently has a position `x`, assign `0` margin requirement the first-to-trade `x` of volume on the opposite side as this would reduce their position (for example, if a party had a long position `10` and sell orders of `15` at a price of `$100` and `10` at a price of `$150`, the first `10` of the sell order at `$100` would not require any order margin). + 1. For any remaining volume, sum `side margin = limit price * size * margin factor` for each price level, as this is the worst-case trade price of the remaining component. + 2. Take the maximum margin from the two `side margin`s as the margin required in the order margin account. If the party's `general` account does not contain sufficient funds to cover any increases to the `order margin` account to be equal to `side margin` then: + 1. If a newly placed order is being evaluated, that order is `stopped` + 2. If the evaluation is the result of any other position/order update, all open orders are `stopped` and margin re-evaluated. + 3. The `order margin` account is now updated to the new `side margin` value and any new orders can be placed on the book. + +NB: This means that a party's order could partially match, with a trade executed and some funds moved to the margin account with correct leverage whilst the rest of the order is immediately stopped. + +When submitting, amending, or deleting an order in isolated margin mode and an auction is active there is no concept of an order trading immediately on entry, however the case of someone putting in a sell order for a very low price must be handled (as it is likely to execute at a much higher price). To handle this, when in an auction the amount taken into the order margin account should be the larger of either `limit price * size * margin factor` or `max(mark price, indicative uncrossing price) * size * margin factor`. After uncrossing, all remaining open order volume should be rebased back to simply `limit price * size * margin factor` in the order margin account. All other steps are as above. + +Pegged orders are not supported in isolated margin mode. + +### Increasing Position + +When an order trades which increases the position (increasing the absolute value of the trader's position), the target amount to be transferred is calculated as: + +$$ +\text{margin to add} = \text{margin factor} \cdot \text{sum across executed trades}(|\text{trade size}| \cdot \text{trade price}) +$$ + +This will be taken by performing three steps: + + 1. The margin to add as calculated here is compared to the margin which would have been placed into the order margin account for this order, `limit price * total traded size * margin factor`. + 2. If it is equal, this amount is taken from the order margin account and placed into the margin account + 3. If the order traded at a price which means less margin should be taken then the amount `margin to add` above is taken from the order margin account into the margin account and the excess is returned from the order margin account to the general account + +NB: In implementation, for any volume that trades immediately on entry, the additional margin may be transferred directly from the general account to the margin account. + +### Reducing Position + +When an order trades which reduces the trader's current position the amount to be withdrawn from the margin account is determined by the fraction of the position which is being closed. If the entire position is being closed (either as a result of changing sides as below or just moving to `0`) then the entirety of the margin balance will be moved back to the general account. + +However, if only a fraction is being closed then this fraction should also take into account that the entire position's margin may be due to change, since the current trading price may have diverged from the last mark price update. As such the margin released should be calculated by first calculating what the `theoretical account balance` of the margin account would be if the entire pre-existing position (i.e. the position prior to any reduction) were margined at the VWAP of the executed trade, then taking the proportion of that corresponding to the proportion of the position closed. + +$$ +\text{margin to remove} = \text{theoretical account balance} \cdot \frac{|\text{total size of new trades}|}{|\text{entire position prior to trade}|} +$$ + +Concretely, this should resolve to: + +$$ +\text{margin to remove} = (\text{balance before} + \text{position before} \cdot (\text{new trade VWAP} - \text{mark price})) \cdot \frac{|\text{total size of new trades}|}{|\text{entire position prior to trade}|} +$$ + +Note: This requires a calculation of the position's margin at trade time. + +### Changing Sides + +A single order could move a trader from a short to a long position (or vice versa) which either increases or decreases the required margin. When this occurs, the trade should be seen as two steps. One in which the position is reduced to zero (and so margin can be released) followed immediately by one which increases to the new position. (Note that these should *not* be two separate trades, merely modelled as such. The two account movements should occur at once with no other changes allowed between them). + + +### Position Resolution + +Isolated margin acts slightly differently to cross margining mode at times when the position becomes distressed. In the case of an open position dropping below maintenance margin levels active orders will remain active as these are margined separately and will not be cancelled. + +However in addition at each margin calculation update the returned `order margin` from the calculator should be compared to the balance in the `order margin account`. If the margin calculator `order margin > order margin account balance` all orders should be cancelled and the funds returned to the general account (any active positions will remain untouched and active). + +### Setting margin mode + +When isolated margin mode is enabled, amount to be transferred is a fraction of the position's notional size that must be specified by the user when enabling isolated margin mode. + +The transaction to update/change margin mode can be included in a batch transaction in order to allow updates when placing an order. + +When in isolated margin mode, it is possible to request to both increase or decrease the margin factor setting: + +- The protocol will attempt to set the funds within the margin account equal to `average entry price * current position * new margin factor`. This value must be above the `initial margin` for the current position or the transaction will be rejected. + - If this is less than the balance currently in the margin account, the difference will be moved from the margin account to the general account + - If this is larger than the balance currently in the margin account, the difference will be moved from the general account to the margin account. If there are not enough funds to complete this transfer, the transaction will be rejected and no change of margin factor will occur. + +When switching to isolated margin mode, the following steps will be taken: + + 1. For any active position, calculate `average entry price * abs(position) * margin factor`. Calculate the amount of funds which will be added to, or subtracted from, the general account in order to do this. If additional funds must be added which are not available, reject the transaction immediately. + 2. For any active orders, calculate the quantity `limit price * remaining size * margin factor` which needs to be placed in the order margin account. Add this amount to the difference calculated in step 1. If this amount is less than or equal to the amount in the general account, perform the transfers (first move funds into/out of margin account, then move funds into the order margin account). If there are insufficient funds, reject the transaction. + 3. Move account to isolated margin mode on this market + +When switching from isolated margin mode to cross margin mode, the following steps will be taken: + + 1. Any funds in the order margin account will be moved to the margin account. + 2. At this point trading can continue with the account switched to the cross margining account type. If there are excess funds in the margin account they will be freed at the next margin release cycle. + ## Reference Level Explanation The calculator takes as inputs: @@ -65,14 +631,15 @@ The calculator takes as inputs: - `mark price` - `scaling levels` defined in the risk parameters for a market - `quantitative risk factors` -- `market.maxSlippageFactors` which is a 2 dimensional decimal optional market creation parameter with a default of `[0.1,0.1]` i.e. `[10%,10%]` with the following validation: `0 <= market.maxSlippageFactors[1] <= 1 000 000` and `0 <= market.maxSlippageFactors[2] <= 1 000 000`. +- `market.linearSlippageFactor` which is decimal optional market creation parameter with a default of `0.1` i.e. `10%` with the following validation: `0 <= market.linearSlippageFactor <= 1 000 000`. Note: `open_volume` may be fractional, depending on the `Position Decimal Places` specified in the [Market Framework](./0001-MKTF-market_framework.md). If this is the case, it may also be that order/positions sizes and open volume are stored as integers (i.e. int64). In this case, **care must be taken** to ensure that the actual fractional sizes are used when calculating margins. For example, if Position Decimals Places (PDP) = 3, then an open volume of 12345 is actually 12.345 (`12345 / 10^3`). This is important to avoid margins being off by orders of magnitude. It is notable because outside of margin calculations, and display to end users, the integer values can generally be used as-is. Note also that if PDP is negative e.g. PDP = -2 then an integer open volume of 12345 is actually 1234500. -and returns 4 margin requirement levels +and returns 5 margin requirement levels 1. Maintenance margin +1. Order margin 1. Collateral search level 1. Initial margin 1. Collateral release level @@ -83,7 +650,7 @@ and returns 4 margin requirement levels 1. Calculate the maintenance margin for the riskiest short position. 1. Select the maintenance margin that is highest out of steps 1 & 2. 1. Scale this maintenance margin by the margin level scaling factors. -1. Return 4 numbers: the maintenance margin, collateral search level, initial margin and collateral release level. +1. Return 5 numbers: the maintenance margin, order margin, collateral search level, initial margin and collateral release level. ## Calculation of riskiest long and short positions @@ -103,20 +670,14 @@ If `riskiest long == 0` then `maintenance_margin_long = 0`. In this simple methodology, a linearised margin formula is used to return the maintenance margin, using risk factors returned by the [quantitative model](./0018-RSKM-quant_risk_models.ipynb). -`maintenance_margin_long = maintenance_margin_long_open_position + maintenance_margin_long_open_orders` - with ```formula -maintenance_margin_long_open_position - = max(min(slippage_volume * slippage_per_unit, mark_price * (slippage_volume * market.maxSlippageFraction[1] + slippage_volume^2 * market.maxSlippageFraction[2])), 0) - + slippage_volume * [ quantitative_model.risk_factors_long ] . [ Product.value(market_observable) ] +maintenance_margin_long + = max(min(riskiest_long * slippage_per_unit, product.value(market_observable) * (riskiest_long * market.linearSlippageFactor)), 0) + + max(open_volume, 0) * [ quantitative_model.risk_factors_long ] . [ Product.value(market_observable) ] + buy_orders * [ quantitative_model.risk_factors_long ] . [ Product.value(market_observable) ]`, ``` -and - -`maintenance_margin_long_open_orders = buy_orders * [ quantitative_model.risk_factors_long ] . [ Product.value(market_observable) ]`, - where `slippage_volume = max( open_volume, 0 )`, @@ -125,7 +686,7 @@ and if `open_volume > 0` then -`slippage_per_unit = Product.value(market_observable) - Product.value(exit_price)`, +`slippage_per_unit = max(0, Product.value(market_observable) - Product.value(exit_price))`, else `slippage_per_unit = 0`. @@ -141,7 +702,7 @@ where - **Short positions** are exited by the system considering what the volume weighted price of **buying** the size of the open short position (not riskiest short position) on the order book (i.e. by buying from the offers (asks) on the order book). If there is no open short position, the slippage per unit is zero. -If there is zero or insufficient order book volume on the relevant side of the order book to calculate the `exit_price`, then take `slippage_per_unit = +Infinity` which means that `min(slippage_volume * slippage_per_unit, mark_price * (slippage_volume * market.maxSlippageFraction[1] + slippage_volume^2 * market.maxSlippageFraction[2])) = mark_price * (slippage_volume * market.maxSlippageFraction[1] + slippage_volume^2 * market.maxSlippageFraction[2])` above. +If there is zero or insufficient order book volume on the relevant side of the order book to calculate the `exit_price`, then take `slippage_per_unit = +Infinity` which means that `min(slippage_volume * slippage_per_unit, mark_price * (slippage_volume * market.linearSlippageFactor)) = mark_price * (slippage_volume * market.linearSlippageFactor)` above. ### **Step 2** @@ -149,29 +710,47 @@ If `riskiest short == 0` then `maintenance_margin_short = 0`. Else -`maintenance_margin_short = maintenance_margin_short_open_position + maintenance_margin_short_open_orders` +```formula +maintenance_margin_short + = max(min(abs(riskiest short) * slippage_per_unit, mark_price * (abs(riskiest short) * market.linearSlippageFactor), 0) + + abs(min( open_volume, 0 )) * [ quantitative_model.risk_factors_short ] . [ Product.value(market_observable) ] + abs(sell_orders) * [ quantitative_model.risk_factors_short ] . [ Product.value(market_observable) ]` +``` -with +where meanings of terms in Step 1 apply except for: + +`slippage_per_unit = max(0, Product.value(exit_price)-Product.value(market_observable))` + +### **Step 3** + +If `open_volume > 0`: + +`maintenance_margin = max(min(open_volume * slippage_per_unit, product.value(market_observable) * (open_volume * market.maxSlippageFraction[1] + open_volume^2 * market.maxSlippageFraction[2])), 0) + + open_volume * [ quantitative_model.risk_factors_long ] . [ Product.value(market_observable) ]` +where + +`slippage_per_unit = max(0, Product.value(market_observable) - Product.value(exit_price))` + +If `open_volume < 0`: ```formula -maintenance_margin_short_open_position - = max(min(abs(slippage_volume) * slippage_per_unit, mark_price * (abs(slippage_volume) * market.maxSlippageFraction[1] + abs(slippage_volume)^2 * market.maxSlippageFraction[2])), 0) - + abs(slippage_volume) * [ quantitative_model.risk_factors_short ] . [ Product.value(market_observable) ] +maintenance_margin + = max(min(abs(open_volume) * slippage_per_unit, mark_price * (abs(open_volume) * market.maxSlippageFraction[1] + abs(slippage_volume)^2 * market.maxSlippageFraction[2])), 0) + + abs(open_volume) * [ quantitative_model.risk_factors_short ] . [ Product.value(market_observable) ]` ``` -and +where -`maintenance_margin_short_open_orders = abs(sell_orders) * [ quantitative_model.risk_factors_short ] . [ Product.value(market_observable) ]`, +`slippage_per_unit = max(0, Product.value(market_observable) - Product.value(exit_price))` -where meanings of terms in Step 1 apply except for: +If `open_volume == 0`: -`slippage_volume = min( open_volume, 0 )`, +`maintenance_margin = 0` -`slippage_per_unit = -1 * (Product.value(market_observable) - Product.value(exit_price) )` +### **Step 4** -### **Step 3** +`maintenance_margin_with_orders = max(maintenance_margin_long, maintenance_margin_short)` -`maintenance_margin = max ( maintenance_margin_long, maintenance_margin_short)` +`order_margin = maintenance_margin_with_orders - maintenance_margin` ## Margin calculation for auctions @@ -189,7 +768,7 @@ Note that because the order book is empty during auctions we will always end up ## Scaling other margin levels -### **Step 4** +### **Step 5** The other three margin levels are scaled relative to the maintenance margin level, using scaling levels defined in the risk parameters for a market. @@ -224,10 +803,12 @@ bids: [ {volume: 7, price: $108} ] +market.linearSlippageFactor = 0.25 + risk_factor_short = 0.11 risk_factor_long = 0.1 -last_trade = $144 +mark_price = $144 search_level_scaling_factor = 1.1 initial_margin_scaling_factor = 1.2 @@ -244,14 +825,14 @@ riskiest_short = min( open_volume + sell_orders, 0 ) = min( 10 - 8, 0 ) = 0 ## exit price considers what selling the open position (10) on the order book would achieve. -slippage_per_unit = Product.value(previous_mark_price) - Product.value(exit_price) = Product.value($144) - Product.value((1*120 + 4*110 + 5*108)/10) = 144 - 110 = 34 +slippage_per_unit = max(0, Product.value(previous_mark_price) - Product.value(exit_price)) = max(0, Product.value($144) - Product.value((1*120 + 4*110 + 5*108)/10)) = max(0, 144 - 110) = 34 -slippage_volume = max( open_volume, 0 ) = max ( 10, 0 ) = 10 +maintenance_margin_long =max(min(riskiest_long * slippage_per_unit, product.value(market_observable) * (riskiest_long * market.linearSlippageFactor)), 0) + + max(open_volume, 0 ) * [ quantitative_model.risk_factors_long ] . [ Product.value(market_observable) ] + buy_orders * [ quantitative_model.risk_factors_long ] . [ Product.value(market_observable) ] -maintenance_margin_long = max(slippage_volume * slippage_per_unit, 0) + slippage_volume * [ quantitative_model.risk_factors_long ] . [ Product.value(market_observable) ] + buy_orders * [ quantitative_model.risk_factors_long ] . [ Product.value(market_observable) ] -= max(10 * 34, 0) + 10 * 0.1 * 144 + 4 * 0.1 * 144 = 541.6 += max(min(14 * 34, 144*(14 * 0.25), 0) + 10 * 0.1 * 144 + 4 * 0.1 * 144 = max(min(476, 532.224), 0) + 10 * 0.1 * 144 + 4 * 0.1 * 144 = 677.6 # Step 2 @@ -259,14 +840,22 @@ Since riskiest short == 0 then maintenance_margin_short = 0 # Step 3 -maintenance_margin = max ( 541.6, 0) = 541.6 +Since open_volume == 10 + +maintenance_margin = max(min(open_volume * slippage_per_unit, product.value(market_observable) * (open_volume * market.maxSlippageFraction[1] + open_volume^2 * market.maxSlippageFraction[2])), 0) + + open_volume * [ quantitative_model.risk_factors_long ] . [ Product.value(market_observable) ] + = max(min(14 * 34, 144*(14 * 0.25 + 14 * 14 * 0.001), 0) + 10 * 0.1 * 144 = max(min(476, 532.224), 0) + 10 * 0.1 * 144 = 620 # Step 4 -collateral_release_level = 541.6 * collateral_release_scaling_factor = 541.6 * 1.1 -initial_margin = 541.6 * initial_margin_scaling_factor = 541.6 * 1.2 -search_level = 541.6 * search_level_scaling_factor = 541.6 * 1.3 +maintenance_margin_with_orders = max ( 677.6, 0) = 677.6 +order_margin = 677.6 - 620 = 57.6 + +# Step 5 +collateral_release_level = 677.6 * collateral_release_scaling_factor = 677.6 * 1.1 +initial_margin = 677.6 * initial_margin_scaling_factor = 677.6 * 1.2 +search_level = 677.6 * search_level_scaling_factor = 677.6 * 1.3 ``` @@ -276,9 +865,9 @@ Given the following trader positions: | Tables | Open | Buys | Sells | | ------------- |:-------------:| -----:| -----:| -| case-1 | 1 | 1 | -2 -| case-2 | -1 | 2| 0 -| case-3 | 1 | 0 | -2 +| case-1 | 1 | 1 | -2 | +| case-2 | -1 | 2| 0 | +| case-3 | 1 | 0 | -2 | #### *case-1* @@ -286,30 +875,18 @@ riskiest long: 2 riskiest short: -1 -slippage volume long: 1 - -slippage volume short: 0 - #### *case-2* riskiest long: 1 riskiest short: -1 -slippage volume long: 0 - -slippage volume short: -1 - #### *case-3* riskiest long: 1 riskiest short: -1 -slippage volume long: 1 - -slippage volume short: 0 - ## SCENARIOS Scenarios found [here](https://docs.google.com/spreadsheets/d/1VXMdpgyyA9jp0hoWcIQTUFrhOdtu-fak/edit#gid=1586131462) diff --git a/protocol/0021-MDAT-market_data_spec.md b/protocol/0021-MDAT-market_data_spec.md index bcf58bef0..2ceede050 100644 --- a/protocol/0021-MDAT-market_data_spec.md +++ b/protocol/0021-MDAT-market_data_spec.md @@ -2,18 +2,18 @@ ## Acceptance Criteria -- If there are no buy orders on the order book, the best bid price is empty / nothing. (0021-MDAT-001) -- If there are no sell orders on the order book, the best offer price is empty / nothing. (0021-MDAT-002) -- If there are multiple buy orders on the order book with a price equal to the best bid price, the best bid volume equals the sum of the sizes of these orders. (0021-MDAT-003) -- If there are multiple sell orders on the order book with a price equal to the best bid price, the best offer volume equals the sum of the sizes of these orders. (0021-MDAT-004) -- The mid price is empty / nothing if there is either no buy order or no sell orders. (0021-MDAT-005) -- The mid price is the arithmetic average of the best bid price and best offer price. (0021-MDAT-006) -- The mark price, if it has been set in the market, is available on APIs returning market data. The returned object makes clear if the mark price has not yet been set (for example market in opening auction that's not seen any trades yet). (0021-MDAT-007) +- If there are no buy orders on the order book, the best bid price is empty / nothing. (0021-MDAT-001). +- If there are no sell orders on the order book, the best offer price is empty / nothing. (0021-MDAT-002). +- If there are multiple buy orders on the order book with a price equal to the best bid price, the best bid volume equals the sum of the sizes of these orders. (0021-MDAT-003). +- If there are multiple sell orders on the order book with a price equal to the best bid price, the best offer volume equals the sum of the sizes of these orders. (0021-MDAT-004). +- The mid price is empty / nothing if there is either no buy order or no sell orders. (0021-MDAT-005). +- The mid price is the arithmetic average of the best bid price and best offer price. (0021-MDAT-006). +- The mark price, if it has been set in the market, is available on APIs returning market data. The returned object makes clear if the mark price has not yet been set (for example market in opening auction that's not seen any trades yet). (0021-MDAT-007). - The Open interest returns the sum of the size for all open positions where positions size is greater than 0. (0021-MDAT-008) - The Open interest returns 0 if there are no positions on the market (0021-MDAT-009) - Pegged orders are excluded from the best static price and best static volume calculations. (0021-MDAT-010) - Dynamic orders should be ignored when calculating the static values (0021-MDAT-011) -- The auction uncrossing price, if it has been set in the market, is available on APIs returning market data. The returned object makes clear if the auction uncrossing price has not been set (for example in continuous trading or auction with no bids / offers). (0021-MDAT-012) +- The auction uncrossing price, if it has been set in the market, is available on APIs returning market data. The returned object makes clear if the auction uncrossing price has not been set (for example in continuous trading or auction with no bids / offers). (0021-MDAT-012). ## Summary @@ -21,13 +21,13 @@ This data is a snapshot of the state of the market at a point in time. ## Guide-level explanation -Due to supporting dynamic orders such as [pegged orders](0037-OPEG-pegged_orders.md) and [LP provision orders](0038-OLIQ-liquidity_provision_order_type.md) the main market data fields are split up into two parts. Normal values and static values. Normal values for **Mid price**, **Best bid price** and **Best offer price** take into account all orders on the book (both normal and dynamic). Static values are calculated using only non-dynamic orders and so will not count any pegged orders in the calculation. +Due to supporting dynamic orders such as [pegged orders](0037-OPEG-pegged_orders.md), the main market data fields are split up into two parts. Normal values and static values. Normal values for **Mid price**, **Best bid price** and **Best offer price** take into account all orders on the book (both normal and dynamic). Static values are calculated using only non-dynamic orders and so will not count any pegged orders in the calculation. ## Reference-level explanation ### Definition of dynamic orders -A "dynamic" order is either a [pegged order](0037-OPEG-pegged_orders.md) or orders that are placed on the book by Vega as part of the [LP liquidity provision order](0038-OLIQ-liquidity_provision_order_type.md). +A "dynamic" order is a [pegged order](0037-OPEG-pegged_orders.md). ### Market data fields @@ -59,6 +59,10 @@ List of market data fields to be available via the API. All these values can be - **Price monitoring bounds** one or more price monitoring bounds for the current timestamp. - **Liquidity provider fee share** share of the accrued fees each liquidity provider is eligible to. +### Market data events + +Market data events should be emitted with sufficient frequency so that each mark price change happening in the market can be captured by just observing the market data events. + ## Pseudo-code / Examples See Test cases diff --git a/protocol/0024-OSTA-order_status.md b/protocol/0024-OSTA-order_status.md index cc092cbe1..bc0fc3436 100644 --- a/protocol/0024-OSTA-order_status.md +++ b/protocol/0024-OSTA-order_status.md @@ -45,6 +45,7 @@ Wash trading is allowed on [auction](0026-AUCT-auctions.md) uncrossing. ### Fill or Or Kill (0024-OSTA-001) + | Time In Force | Filled | Resulting status | |---------------|--------|------------------| | FOK | No | Stopped | @@ -52,6 +53,7 @@ Wash trading is allowed on [auction](0026-AUCT-auctions.md) uncrossing. ### Immediate Or Cancel (0024-OSTA-002) + | Time In Force | Filled | Resulting status | |---------------|---------|------------------| | IOC | No | Stopped | @@ -60,6 +62,7 @@ Wash trading is allowed on [auction](0026-AUCT-auctions.md) uncrossing. ### Good ’Til Cancelled (0024-OSTA-003) + | Time In Force | Filled | Cancelled by user | Stopped by system | Resulting status | |---------------|---------|-------------------|-------------------|------------------| | GTC | No | No | No | Active | @@ -72,6 +75,7 @@ Wash trading is allowed on [auction](0026-AUCT-auctions.md) uncrossing. ### Good ’Til Time (0024-OSTA-004) + | Time In Force | Filled | Expired | Cancelled by user | Stopped by system | Resulting status | |---------------|---------|---------|-------------------|-------------------|------------------| | GTT | No | No | No | No | Active | @@ -89,10 +93,10 @@ Note: The last row in the table above is added for clarity. If the order was fil ### Wash trading Acceptance Criteria -- If, during continuous trading, an order would be filled or partially filled with an existing order from the same [party](./0017-PART-party.md) aka "wash" trade, the order is rejected. The reason for rejection should be clear on the order status: "rejected to prevent a wash trade". (0024-OSTA-005) -- Any existing fills that happen before the wash trade is identified will be kept. The order should be market both "partially filled" and "rejected to prevent wash trade" (0024-OSTA-006) -- FOK rules still apply for wash trading so if a wash trade is identified before the full amount of the order is complete, the order will be stopped and nothing filled. (0024-OSTA-007) -- Wash trading is allowed on [auction](0026-AUCT-auctions.md) uncrossing. (0024-OSTA-008) +- If, during continuous trading, an order would be filled or partially filled with an existing order from the same [party](./0017-PART-party.md) aka "wash" trade, the order is rejected. The reason for rejection should be clear on the order status: "rejected to prevent a wash trade". (0024-OSTA-005). +- Any existing fills that happen before the wash trade is identified will be kept. The order should be market both "partially filled" and "rejected to prevent wash trade" (0024-OSTA-006). +- FOK rules still apply for wash trading so if a wash trade is identified before the full amount of the order is complete, the order will be stopped and nothing filled. (0024-OSTA-007). +- Wash trading is allowed on [auction](0026-AUCT-auctions.md) uncrossing. (0024-OSTA-008). ### Impact of order types on settlement @@ -102,20 +106,20 @@ Note: The last row in the table above is added for clarity. If the order was fil - Order reason of `ORDER_ERROR_INSUFFICIENT_ASSET_BALANCE` is given if a position is closed out because they do now have enough margin to cover the position (0024-OSTA-010) - Order reason of `ORDER_ERROR_MARGIN_CHECK_FAILED` is given if a new order is placed and the user does not have enough collateral to cover the initial margin requirements (0024-OSTA-011) -- Order reason of `ORDER_ERROR_NON_PERSISTENT_ORDER_OUT_OF_PRICE_BOUNDS` when a non persistent order would cause the price to move outside of the price bounds (0024-OSTA-012) -- Order reason of `ORDER_ERROR_GFN_ORDER_DURING_AN_AUCTION` when the market is in auction and a GFN order is sent in (0024-OSTA-013) -- Order reason of `ORDER_ERROR_CANNOT_SEND_IOC_ORDER_DURING_AUCTION` when trying to send an IOC order during auction (0024-OSTA-014) -- Order reason of `ORDER_ERROR_CANNOT_SEND_FOK_ORDER_DURING_AUCTION` when trying to send a FOK order during auction (0024-OSTA-015) -- Order reason of `ORDER_ERROR_GFA_ORDER_DURING_CONTINUOUS_TRADING` when trying to send a GFA order during normal trading (0024-OSTA-016) -- Order reason of `ORDER_ERROR_INVALID_EXPIRATION_DATETIME` when sending a GTT with the expiry is before the creation time (0024-OSTA-017) -- Order reason of `ORDER_ERROR_MARKET_CLOSED` when trying to send an order when the market is closed (0024-OSTA-018) -- Order reason of `ORDER_ERROR_INVALID_TYPE` when trying to send an order with a non valid order type (0024-OSTA-019) -- Order reason of `ORDER_ERROR_INVALID_MARKET_ID` when sending an order with an invalid market ID (0024-OSTA-020) +- Order reason of `ORDER_ERROR_NON_PERSISTENT_ORDER_OUT_OF_PRICE_BOUNDS` when a non persistent order would cause the price to move outside of the price bounds (0024-OSTA-012). +- Order reason of `ORDER_ERROR_GFN_ORDER_DURING_AN_AUCTION` when the market is in auction and a GFN order is sent in (0024-OSTA-013). +- Order reason of `ORDER_ERROR_CANNOT_SEND_IOC_ORDER_DURING_AUCTION` when trying to send an IOC order during auction (0024-OSTA-014). +- Order reason of `ORDER_ERROR_CANNOT_SEND_FOK_ORDER_DURING_AUCTION` when trying to send a FOK order during auction (0024-OSTA-015). +- Order reason of `ORDER_ERROR_GFA_ORDER_DURING_CONTINUOUS_TRADING` when trying to send a GFA order during normal trading (0024-OSTA-016). +- Order reason of `ORDER_ERROR_INVALID_EXPIRATION_DATETIME` when sending a GTT with the expiry is before the creation time (0024-OSTA-017). +- Order reason of `ORDER_ERROR_MARKET_CLOSED` when trying to send an order when the market is closed (0024-OSTA-018). +- Order reason of `ORDER_ERROR_INVALID_TYPE` when trying to send an order with a non valid order type (0024-OSTA-019). +- Order reason of `ORDER_ERROR_INVALID_MARKET_ID` when sending an order with an invalid market ID (0024-OSTA-020). - Order reason of `ORDER_ERROR_MUST_BE_LIMIT_ORDER` when sending a pegged order that is not a LIMIT order (0024-OSTA-021) - Order reason of `ORDER_ERROR_MUST_BE_GTT_OR_GTC` pegged order must be either GTC or GTT (0024-OSTA-022) - Order reason of `ORDER_ERROR_WITHOUT_REFERENCE_PRICE` pegged order must have a reference field (0024-OSTA-023) - Order reason of `ORDER_ERROR_BUY_CANNOT_REFERENCE_BEST_ASK_PRICE` buy pegged order cannot reference the ask price (0024-OSTA-024) - Order reason of `ORDER_ERROR_OFFSET_MUST_BE_GREATER_THAN_ZERO` pegged order offset must be > 0 when referencing `MID` price (0024-OSTA-025) - Order reason of `ORDER_ERROR_SELL_CANNOT_REFERENCE_BEST_BID_PRICE` sell pegged order cannot reference the bid price (0024-OSTA-026) -- Order reason of `ORDER_ERROR_INSUFFICIENT_ASSET_BALANCE` user does not have enough of the asset or does not have an account at all (0024-OSTA-027) -- Order reason of `ORDER_ERROR_SELF_TRADING` when the order would match with one from the same user while not in auction (0024-OSTA-029) +- Order reason of `ORDER_ERROR_INSUFFICIENT_ASSET_BALANCE` user does not have enough of the asset or does not have an account at all (0024-OSTA-027). +- Order reason of `ORDER_ERROR_SELF_TRADING` when the order would match with one from the same user while not in auction (0024-OSTA-029). diff --git a/protocol/0025-OCRE-order_submission.md b/protocol/0025-OCRE-order_submission.md index af6308731..1edee8821 100644 --- a/protocol/0025-OCRE-order_submission.md +++ b/protocol/0025-OCRE-order_submission.md @@ -8,7 +8,9 @@ To allow traders to interact with the market, they must be able to enter an orde - Orders can be submitted into any market that is active - i.e not in [a protective auction](./0026-AUCT-auctions.md) or [matured/expired/settled](./0043-MKTL-market_lifecycle.md). - Orders will only be accepted if sufficient margin can be allocated (see : [Margin Orchestration](./0010-MARG-margin_orchestration.md) and [Margin Calculator](./0019-MCAL-margin_calculator.md)) -- Amendments that change price or increase size will be executed as an atomic cancel/replace (i.e. as if the original order was cancelled and removed from the book and a new order submitted with the modified values - that is, time priority is lost) +- Amendments that change price or increase available (displayed) quantity will be executed as an atomic cancel/replace (i.e. as if the original order was cancelled and removed from the book and a new order submitted with the modified values - that is, time priority is lost) +Note that this means that increasing the quantity of an iceberg (transparent iceberg) order can be done without losing time priority, as the current displayed size will not be changed. +The order will lose time priority on its next refresh in any case (i.e. before the increased size becomes 'displayed' and tradable). - Execution of an order during continuous trading will be stopped and the order cancelled and removed (if on the book) if it is about to match with another order on the book for the same party (that is, execution can proceed up to the point a "wash" trade would be generated but stopped before that and the order cancelled). Self-trading / "wash" trading is allowed on auction uncrossing (i.e. to leave an auction). - Orders may be fractional in size with the maximum number of decimal places allowable being the `Position Decimal Places` specified in the [Market Framework](./0001-MKTF-market_framework.md), and any order containing more precision that this being rejected. (NB: orders may end up being specified as integers similar to how prices are, in which case this does not apply and 1 == the smallest increment given the configured position d.p.s for the market). @@ -35,10 +37,11 @@ Self-trading / "wash" trading is allowed on auction uncrossing (i.e. to leave an ## Acceptance Criteria -- An order's size must be valid according to the [Fractional Order Size spec](./0052-FPOS-fractional_orders_positions.md) (0025-OCRE-001) -- Margin will taken before the order is entered in to the book (0025-OCRE-002) +- An order's size must be valid according to the [Fractional Order Size spec](./0052-FPOS-fractional_orders_positions.md) (0025-OCRE-001). +- Margin will taken before the order is entered into the book (0025-OCRE-002) - If sufficient margin cannot be reserved, the order will have a status of `REJECTED` (0025-OCRE-003) - Fees are charged as per [0029-FEES](./0029-FEES-fees.md). + - If sufficient holding cannot be reserved, the order will have a status of `REJECTED` (0025-OCRE-006) ## Future Work diff --git a/protocol/0026-AUCT-auctions.md b/protocol/0026-AUCT-auctions.md index 4db5087ed..0657af648 100644 --- a/protocol/0026-AUCT-auctions.md +++ b/protocol/0026-AUCT-auctions.md @@ -6,7 +6,8 @@ Auctions are a trading mode that 'collect' orders during an *auction call period In comparison to continuous trading, the auction mode for a market, is a state of the orderbook where each order placed is just sitting on the book, for a given period of time or until some requirements are met (called `call period`), then the matching orders are uncrossed. -They are mostly useful in less liquid markets, or in specific scenarios where a price must be determined, i.e. at opening of a market, when a potentially excessively large price move might occur (price monitoring) or when liquidity needs to be sourced and aggregated (liquidity monitoring). In traditional markets (where markets open and close every day) we can run an open and closing auction for the price to stabilise at both ends. +They are mostly useful in less liquid markets, or in specific scenarios where a price must be determined, i.e. at opening of a market, when a potentially excessively large price move might occur (price monitoring). +In traditional markets (where markets open and close every day) we can run an open and closing auction for the price to stabilise at both ends. ## Reference-level explanation @@ -22,7 +23,7 @@ All auctions have a `min_auction_length` (which is a single network parameter fo - Any auction that would be less than `min_auction_length` seconds (network parameter) should not be started (e.g. if the market is nearing the end of its open period / active trading). This is to prevent auction calls that are too short given the network latency/granularity, so should be some multiple of the worst case expected block time at some confidence level, which is best maintained by governance voting (hence being a network parameter). - a proposal should be rejected if it would require an auction shorter `min_auction_length` -- for price/liquidity monitoring, etc. the auction must last for at least the `min_auction_length` and therefore we can avoid checking other conditions until that length is reached +- for price monitoring, etc. the auction must last for at least the `min_auction_length` and therefore we can avoid checking other conditions until that length is reached - if the parameter is changed it needs to be re-applied to any current auctions, this means that shortening it could trigger an auction ending ### Opening auctions (at creation of the market) @@ -39,7 +40,7 @@ The frequent batch auction mode is a trading mode in perpetual auction, meaning e.g: auctions could be set to last 10 minutes, then every 10 minutes the book would uncross, potentially generating trades. -Note that FBAs will still have an opening auction (which must have a duration equal to or greater than the minimum batch auction duration, as well as meeting the minimum opening auction duration. Price and liquidity monitoring will be able to override the trading mode and push the market into longer auctions to resolve the triggering event. +Note that FBAs will still have an opening auction (which must have a duration equal to or greater than the minimum batch auction duration, as well as meeting the minimum opening auction duration. Price monitoring will be able to override the trading mode and push the market into longer auctions to resolve the triggering event. #### Duration of frequent batch auctions @@ -87,7 +88,7 @@ Good for normal trading (GFN) orders are rejected during an auction. ### Upon exiting auction mode -- [Pegged orders](./0037-OPEG-pegged_orders.md) (all kinds, including the ones placed by [Liquidity Provision](./0038-OLIQ-liquidity_provision_order_type.md)) get reinstated in the order book they were originally submitted in. +- [Pegged orders](./0037-OPEG-pegged_orders.md) get reinstated in the order book they were originally submitted in. - Limit orders stay on the book (unless they have a `TIF:GFA` only good for auction, in this case they are removed from the book and have their status set to cancelled). ## Exiting the auction mode @@ -97,7 +98,7 @@ Auction periods may be ended with an uncrossing and the creation of any resultin - the auction call period end time being reached (if such a time is set); or - other functionality (related to the type of auction period) that triggers the end of auction. -Auction periods do not end if the resulting state would immediately cause another auction to occur. Instead the current auction gets extended. For example, if a liquidity monitoring auction would be triggered at the end of an opening auction, then the opening auction continues and the *auction extension trigger* field in the [market data API](./0021-MDAT-market_data_spec.md) is updated to account for the fact that the opening auction has been extended due to insufficient liquidity. +Auction periods do not end if the resulting state would immediately cause another auction to occur. Instead the current auction gets extended. ### Ending when a market is going to enter Trading Terminated status @@ -109,7 +110,7 @@ Functionality that either triggers the end of an auction or delays the auction e - opening auction (market creation): [governance](./0028-GOVE-governance.md) - [price monitoring](./0032-PRIM-price_monitoring.md) -- [liquidity monitoring](./0035-LIQM-liquidity_monitoring.md) + ## First/Naive implementation @@ -157,24 +158,24 @@ message Market { ## Acceptance Criteria -- The duration of the auction period (time between close of voting and enactment time) at market creation cannot be below the minimum auction period defined within the network (0026-AUCT-003) -- As the Vega network, in auction mode, all orders are placed in the book but never uncross until the end of the auction period. (0026-AUCT-004) -- As a user, I can cancel an order that it either live on the order book or parked. (0068-MATC-033) -- As a user, I can get information about the trading mode of the market (through the [market framework](./0001-MKTF-market_framework.md)) (0026-AUCT-005) -- As a user, I can get real time information through the API about a market in auction mode: indicative crossing price, indicative crossing volume. (0026-AUCT-006) -- As a user, the market depth API provides the same data that would be sent during continuous trading (0026-AUCT-007) -- As an API user, I can identify: (0026-AUCT-008) +- The duration of the auction period (time between close of voting and enactment time) at market creation cannot be below the minimum auction period defined within the network (0026-AUCT-003). +- As the Vega network, in auction mode, all orders are placed in the book but never uncross until the end of the auction period. (0026-AUCT-004). +- As a user, I can cancel an order that it either live on the order book or parked. (0068-MATC-033). +- As a user, I can get information about the trading mode of the market (through the [market framework](./0001-MKTF-market_framework.md)) (0026-AUCT-005). +- As a user, I can get information through the API about a market in auction mode: indicative uncrossing price, indicative uncrossing volume. (0026-AUCT-006). +- As a user, the market depth API provides the same data that would be sent during continuous trading (0026-AUCT-007). +- As an API user, I can identify: (0026-AUCT-008). - If a market is temporarily in an auction period - Why it is in that period (e.g. Auction at open, liquidity sourcing, price monitoring) - When the auction will next attempt to uncross or if the auction period ended and the auction cannot be resolved for whatever reason then this should come blank or otherwise indicating that the system doesn't know when the auction ought to end. - A market with default trading mode "continuous trading" will start with an opening auction. The opening auction will run from the close of voting on the market proposal (assumed to pass successfully) until: - 1. the enactment time assuming there are orders crossing on the book, [liquidity is supplied](./0038-OLIQ-liquidity_provision_order_type.md) and after the auction uncrossing we will have best bid and best ask so that [liquidity can be deployed](./0038-OLIQ-liquidity_provision_order_type.md). (0026-AUCT-009) - 1. past the enactment time if there is no [liquidity supplied](./0038-OLIQ-liquidity_provision_order_type.md). The auction won't end until sufficient liquidity is committed and we have limit orders such that after the auction uncrossing we will have best bid and best ask so that [liquidity can be deployed](./0038-OLIQ-liquidity_provision_order_type.md). (0026-AUCT-010) - 1. past the enactment time if [liquidity is supplied](./0038-OLIQ-liquidity_provision_order_type.md) and after the auction uncrossing we will have best bid and best ask but the uncrossing volume will create open interest that is larger than what the [supplied stake can support](./0041-TSTK-target_stake.md). It will only end if - - more liquidity is committed (0026-AUCT-011) - - or if orders are cancelled such that the uncrossing volume will create open interest sufficiently small so that the original stake can support it. (0026-AUCT-012) - 1. past the enactment time if there are orders crossing on the book and [liquidity is supplied](./0038-OLIQ-liquidity_provision_order_type.md) but after the auction uncrossing we will not have - - best bid; it will only end once an LO providing best bid is supplied. (0026-AUCT-013) - - or best ask; it will only end once an LO providing best bid is supplied. (0026-AUCT-014) -- When entering an auction, all GFN orders will be cancelled. (0026-AUCT-015) -- When leaving an auction, all GFA orders will be cancelled. (0026-AUCT-016) + 1. the enactment time assuming there are orders crossing on the book and [liquidity is supplied](./0044-LIME-lp_mechanics.md#commit-liquidity-network-transaction). (0026-AUCT-017). + 2. past the enactment time if there is no [liquidity supplied](./0044-LIME-lp_mechanics.md#commit-liquidity-network-transaction). The auction won't end until sufficient liquidity is committed. (0026-AUCT-018) + 3. past the enactment time if [liquidity is supplied](./0044-LIME-lp_mechanics.md#commit-liquidity-network-transaction) but the uncrossing volume will create open interest that is larger than what the [supplied stake can support](./0041-TSTK-target_stake.md). It will only end if + - more liquidity is committed (0026-AUCT-019) + - or if orders are cancelled such that the uncrossing volume will create open interest sufficiently small so that the original stake can support it. (0026-AUCT-020) + 4. past the enactment time if there are orders crossing on the book and [liquidity is supplied](./0044-LIME-lp_mechanics.md#commit-liquidity-network-transaction) but after the auction uncrossing we will not have + - best bid; it will still open. (0026-AUCT-021) + - or best ask; it will still open. (0026-AUCT-022) +- When entering an auction, all GFN orders will be cancelled. (0026-AUCT-015). +- When leaving an auction, all GFA orders will be cancelled. (0026-AUCT-016). diff --git a/protocol/0027-ASSP-asset_proposal.md b/protocol/0027-ASSP-asset_proposal.md index 4e22102eb..26455df6e 100644 --- a/protocol/0027-ASSP-asset_proposal.md +++ b/protocol/0027-ASSP-asset_proposal.md @@ -74,9 +74,9 @@ Contracts that do not meet this guarantee are not suitable as a basis for Vega b ```proto message ERC20 { - // contract address of an ERC20 token - string contractAddress = 1; - string maximumLifetimeDeposit = 2; // note that e.g: 100000 in here will be interpreted against the asset decimals + // contract address of an ERC20 token + string contractAddress = 1; + string maximumLifetimeDeposit = 2; // note that e.g: 100000 in here will be interpreted against the asset decimals string withdrawalDelayThreshold = 3; // this is will be interpreted against the asset decimals } @@ -90,10 +90,10 @@ message AssetSource { string name = 4; oneof source { - // vega internal assets - BuiltinAsset builtinAsset = 100; - // foreign chains assets - ERC20 erc20 = 200; + // vega internal assets + BuiltinAsset builtinAsset = 100; + // foreign chains assets + ERC20 erc20 = 200; } } @@ -135,9 +135,9 @@ message ProposalTerms { UpdateMarket updateMarket = 101; NewMarket newMarket = 102; UpdateNetwork updateNetwork = 103; - // new field: - NewAsset = newAsset = 104; - UpdateAsset = updateAsset = 105; + // new field: + NewAsset = newAsset = 104; + UpdateAsset = updateAsset = 105; }; } ``` @@ -146,12 +146,12 @@ message ProposalTerms { ```json { - "newAsset": { - "changes": { - "contractAddress": "0xsomething" - }, - "quantum": "10000000" // if the asset supports 5 decimals = 100.00000 - } + "newAsset": { + "changes": { + "contractAddress": "0xsomething" + }, + "quantum": "10000000" // if the asset supports 5 decimals = 100.00000 + } } ``` @@ -167,6 +167,7 @@ This must be an integer strictly greater than `0`. - As a user I can vote for an asset proposal. (0027-ASSP-002) - As a user, original submitter of the asset, I can call the node to get a signature of the asset, so I can send it to the asset bridge, and whitelist the asset. (0027-ASSP-003) - `quantum` is a required parameter (0027-ASSP-004) +- If an update asset proposal passed and it changes `quantum` _only_ then this new value becomes used immediately on enactment. (0027-ASSP-025) ### Node actions @@ -200,3 +201,4 @@ This must be an integer strictly greater than `0`. - There is an asset `X` on vega / bridge with withdrawal delay threshold `t1`. Withdrawal in asset `X` below `t1` has no delay i.e. can be finalised on Ethereum as soon as the withdrawal bundle is received. A withdrawal in asset `X` with amount greater than or equal to `t1` will be rejected by the bridge before time `bundle creation + delay` but can be finalised after `delay` time passes from bundle creation. Here `delay` is the global bridge delay parameter. (0027-ASSP-023) - There is an asset `X` on vega / bridge with withdrawal delay threshold `t1`. An asset update proposal is submitted to change these to `t2`; it passes voting and is submitted to Ethereum bridge contract. The new thresholds now apply i.e. withdrawal in asset `X` below `t2` has no delay i.e. can be finalised on Ethereum as soon as the withdrawal bundle is received. A withdrawal in asset `X` with amount greater than or equal to `t2` will be rejected by the bridge before time `bundle creation + delay` but can be finalised after `delay` time passes from bundle creation. Here `delay` is the global bridge delay parameter. (0027-ASSP-024) + diff --git a/protocol/0028-GOVE-governance.md b/protocol/0028-GOVE-governance.md index 8f0b14482..48e372655 100644 --- a/protocol/0028-GOVE-governance.md +++ b/protocol/0028-GOVE-governance.md @@ -19,11 +19,13 @@ Governance actions can be the end result of a passed proposal. The allowable typ The types of governance action are: 1. Create a new market -2. Change an existing market's parameters -3. Change network parameters -4. Add an external asset to Vega (covered in a [separate spec - see 0027](./0027-ASSP-asset_proposal.md)) -5. Authorise a transfer to or from the [Network Treasury](./0055-TREA-on_chain_treasury.md) -6. Freeform proposals +1. Change an existing market's parameters +1. Change network parameters +1. Add an external asset to Vega (covered in a [separate spec - see 0027](./0027-ASSP-asset_proposal.md)) +1. Authorise a transfer to or from the [Network Treasury](./0055-TREA-on_chain_treasury.md) +1. Authorise a transfer to or from the [global insurance pool](./0015-INSR-market_insurance_pool_collateral.md#global-insurance-pool) +1. Authorise a transfer to or from the [market insurance pool](./0015-INSR-market_insurance_pool_collateral.md#market-insurance-pool) +1. Freeform proposals ### Lifecycle of a proposal @@ -86,8 +88,10 @@ Anyone can create a proposal if the weighting of their vote on the proposal woul In a future iteration of the governance system we may restrict proposal submission by type of proposal based on a minimum weighting. e.g: only user with a certain number or percentage of the governance asset are allowed to open a "network parameter change" proposal. -Market change proposals additionally require certain minimum [Equity-like share](0042-LIQF-setting_fees_and_rewarding_lps.md) set by `governance.proposal.updateMarket.minProposerEquityLikeShare`. -So, for example, if `governance.proposal.updateMarket.minProposerEquityLikeShare = 0.05` and a party has `equity-like share` on the market of `0.3` then they can make a market change proposal. If, on the other hand, a party has `equity-like share` of `0.03` then they cannot submit a market change proposal. +### Market change proposal + +Market change proposals can also be submitted by any party which has at least the minimum [Equity-like share](0042-LIQF-setting_fees_and_rewarding_lps.md) set by `governance.proposal.updateMarket.minProposerEquityLikeShare`. Note that such a party can submit a proposal even if it doesn't hold any amount of the governance token. +So, for example, if `governance.proposal.updateMarket.minProposerEquityLikeShare = 0.05` and a party has `equity-like share` on the market of `0.3` and no governance tokens then they can make a market change proposal. If, on the other hand, a party has `equity-like share` of `0.03` and no governance tokens then they cannot submit a market change proposal. ### Duration of the proposal @@ -141,12 +145,16 @@ Note: see below for details on minimum participation rate and minimum required m Not in scope: minimum participation of active users, i.e. 90% of the _active_ users of the vega network have to take part in the vote. Minimum participation is currently always measured against the total possible participation. +### Market change proposal outcome + For market change proposals the network will additionally calculate 1. `LP participation rate = SUM (equity-like share of all LPs who cast a vote)` (no need to divide by anything as equity-like share sums up to `1`). -1. `LP for rate = SUM (all who voted for) / LP participation rate`. +1. `LP for rate = SUM (equity-like share of all LPs who cast a for vote))`. + +If the market that the proposal is changing is pending (so accepted but hasn't left opening auction yet) at the vote resolution time then only token holder votes are used. -A market parameter change is passed only when: +For a market that's out of the pending state (so the opening auction has concluded) a market parameter change is passed only when: - either the governance token holder vote is successful i.e. `participation_rate >= governance.proposal.updateMarketParam.requiredParticipation` AND `for_rate > governance.proposal.updateMarketParam.requiredMajority` (in this case the LPs were overridden by governance token holders) - or the governance token holder vote does not reach participation threshold but the LP vote does and approves the proposal `participation_rate < governance.proposal.updateMarketParam.requiredParticipation` AND `LP participation rate >= governance.proposal.updateMarketParam.requiredParticipationLP` AND `LP for rate >= governance.proposal.updateMarketParam.requiredMajorityLP`. @@ -215,21 +223,23 @@ Note the following key points from the market lifecycle spec: - A market enters a Pending status as soon as the proposal is Successful (before enactment) - A market usually enters Active status at the proposal's enactment date/time, but some conditions may delay this or cause the market to be Cancelled instead -A proposal to create a market contains: +A proposal to create a market contains -1. a complete market specification as per the Market Framework (see spec) that describes the market to be created. +1. a complete market specification as per the [Market Framework](./0001-MKTF-market_framework.md) that describes the market to be created. 1. an enactment time that is at least the _minimum auction duration_ after the vote closing time (see [auction spec](./0026-AUCT-auctions.md)) +1. if the market is meant to be a _successor_ of a given market then it contains the `marketID` of the market it's succeeding (parent market), a parameter called `insurancePoolFraction` which is a decimal in `[0,1]` (i.e. it can be `0` or `1` or anything in between) and certain entries in the market proposal must be identical to those of the market it's succeeding. +See [sucessor markets spec](./0081-SUCM-successor_markets.md for more details). All _new market proposals_ initially have their validation configured by the network parameters `Governance.CreateMarket.All.*`. These may be split from `All` to subtypes in future, for instance when other market types like RFQ are created. -A market in Proposed state accepts [liquidity commitments](./0038-OLIQ-liquidity_provision_order_type.md) from any party. The LP commitments can be added / amended / removed. +A market in Proposed state accepts [liquidity commitments](./0044-LIME-lp_mechanics.md#commit-liquidity-network-transaction) from any party. The LP commitments can be added / amended / removed. ## 2. Change market parameters [Market parameters](./0001-MKTF-market_framework.md#market) that may be changed are described in the spec for the Market Framework, and additionally the specs for the Risk Model and Product being used by the market. See the [Market Framework spec](./0001-MKTF-market_framework.md#market) for details on these parameters, including those that cannot be changed and the category of the parameters. -To change any market parameter the proposer submits the same data as to create a market with the desired updates to the fields / structures that should change. +To change any market parameter the proposer submits the same data as to create a market with the desired updates to the fields / structures that should change. Changes for markets in a closed/cancelled/terminated state should not be permitted. Ideally, it should be possible to not repeat things that are not changing or are immutable but we leave this to implementation detail. The following are immutable and cannot be changed: @@ -238,7 +248,6 @@ The following are immutable and cannot be changed: - market decimal places - position decimal places - `settlementAsset` -- name ## 3. Change network parameters @@ -248,7 +257,7 @@ All _change network parameter proposals_ have their validation configured by the ## 4.1 Add a new asset -New [assets](./0040-ASSF-asset_framework.md) can be proposed through the governance system. The procedure is covered in detail in the [asset proposal spec](./0027-ASSP-asset_proposal.md)). +New [assets](./0040-ASSF-asset_framework.md) can be proposed through the governance system. The procedure is covered in detail in the [asset proposal spec](./0027-ASSP-asset_proposal.md). All new asset proposals have their validation configured by the network parameters `governance.proposal.asset.`. ## 4.2 Modify an existing asset @@ -261,38 +270,43 @@ Enactment of an asset modification proposal is: - For data that must be synchronised with the asset blockchain (e.g. Ethereum): _only_ the emission of a signed bundle that can be submitted to the bridge contract; the changed values [asset framework spec](./0040-ASSF-asset_framework.md) only become reflected on the Vega chain once the usual number of confirmations of the effect of this change is emitted by the bridge chain. - For any data that is stored only on the Vega chain: the data is updated once the proposal is enacted. -## 5. Transfers initiated by Governance (post Oregon trail) +## 5. Transfers initiated by Governance ### Permitted source and destination account types The below table shows the allowable combinations of source and destination account types for a transfer that's initiated by a governance proposal. -| Source type | Destination type | Governance transfer permitted | +| Source type | Destination type | Transfer permitted | | --- | --- | --- | | Party account (any type) | Any | No | -| Network treasury | Reward pool account | Yes | +| Network treasury | Network treasury | No | | Network treasury | Party general account(s) | Yes | | Network treasury | Party other account types | No | -| Network treasury | Network insurance pool account | Yes | +| Network treasury | Global insurance pool account | Yes | | Network treasury | Market insurance pool account | Yes | +| Network treasury | Reward account | Yes | | Network treasury | Any other account | No | -| Network insurance pool account | Network treasury | Yes | -| Network insurance pool account | Market insurance pool account | Yes | -| Network insurance pool account | Any other account | No | | Market insurance pool account | Party account(s) | Yes | | Market insurance pool account | Network treasury | Yes | -| Market insurance pool account | Network insurance pool account | Yes | +| Market insurance pool account | Global insurance pool account | Yes | +| Market insurance pool account | Market insurance pool account | Yes | +| Market insurance pool account | Reward account | Yes | | Market insurance pool account | Any other account | No | +| Global insurance pool account | Party account(s) | Yes | +| Global insurance pool account | Network treasury | Yes | +| Global insurance pool account | Market insurance pool account | Yes | +| Global insurance pool account | Reward account | Yes | +| Global insurance pool account | Any other account | No | | Any other account | Any | No | ### Transfer proposal details The proposal specifies: -- `source_type`: the source account type (i.e. network treasury, network insurance pool, market insurance pool) +- `source_type`: the source account type (i.e. network treasury, global insurance pool, market insurance pool) - `source` specifies the account to transfer from, depending on the account type: - network treasury: leave blank (only one per asset) - - network insurance pool: leave blank (only one per asset) + - global insurance pool: leave blank (only one per asset) - market insurance pool: market ID - `type`, which can be either "all or nothing" or "best effort": - all or nothing: either transfers the specified amount or does not transfer anything @@ -302,10 +316,14 @@ The proposal specifies: - `fraction_of_balance`: the maximum fraction of the source account's balance to transfer as a decimal (i.e. 0.1 = 10% of the balance) - `destination_type` specifies the account type to transfer to (reward pool, party, network insurance pool, market insurance pool) - `destination` specifies the account to transfer to, depending on the account type: - - reward pool: the reward scheme ID + - network treasury: leave blank (only one per asset) - party: the party's public key - - network insurance pool: leave blank (there's only one per asset) + - global insurance pool: leave blank (only one per asset) - market insurance pool: market ID +- A proposal can be for a one off transfer or recurring. +- If the proposal is one off it can define a time for delivery. Whenever the block time is after the delivery time, the transfer will execute. If there is no delivery time the one off transfer will execute immediately. +- If the proposal is recurring it has to define a start epoch and an optional end epoch. In such case the transfer will be executed every epoch while still active. + - Plus the standard proposal fields (i.e. voting and enactment dates, etc.) ### Transfer proposal enactment @@ -324,7 +342,7 @@ The amount is calculated by Where: -- `NETWORK_MAX_AMOUNT` is a network parameter specifying the maximum absolute amount that can be transferred by governance for the source account type +- `NETWORK_MAX_AMOUNT` is a network parameter specifying the maximum amount that can be transferred by governance for the source account type, as a multiplier for the transfer asset's quantum - `NETWORK_MAX_FRACTION` is a network parameter specifying the maximum fraction of the balance that can be transferred by governance for the source account type (must be <= 1) If `type` is "all or nothing" then the transfer will only proceed if: @@ -335,7 +353,66 @@ transfer_amount == min( proposal.amount ) ``` -## 6. Freeform governance proposal +### Transfer cancellation + +This is done as a governance proposal. Takes a transfer ID (which is the proposal ID of the original transfer) and would cancel a recurring governance initiated transfer. Only recurring governance initiated transfers can be cancelled via governance initiated transfer cancellation proposal. Trying to cancel any other transfer should fail upon validation of the proposal. + +### Checkpoint/snapshot + +Enacted and active transfers (i.e. scheduled one off governance initiated transfers, or recurring governance initiated transfers) must be included in LNL banking checkpoint and resume after the checkpoint restore. + +All in memory active governance initiated transfers must be included in the snapshot of the banking engine. + +### Additional information + +1. When a transfer gets enacted it emits transfer event similar to regular transfer events from regular transfers, however with different type (i.e. similar to one-off, and recurring of regular transfers, there are governance-one-off and governance-recurring types). At the time of enactment no amount is attached to the transfer and it will show 0. +2. When a transfer is _made_ an event is emitted with the actual amount being transfers. The status of the transfer will depend on the type of the transfer. +3. When the transfer reaches a terminal state, being stopped, rejected, done, cancelled an event is emitted indicating the status. +4. Enacted governance initiated transfers are therefore available to be queried via the regular transfer API in data node. +5. Governance initiated transfers are subject to neither minimum transfer amounts nor to fees. + +## 6. Change market state + +A governance proposal to change the state of the market. +Multiple concurrent proposals are allowed. + +Market change proposal [creation](#market-change-proposal) and [voting](#market-change-proposal-outcome) rules apply. + +Refer to subsections below for allowed state changes. + +### 6.1. Move market to a closed state + +Any type of market (either fixed expiry market or perpetual) can be closed via a governance vote. + +A proposal to close a market contains: + +1. final settlement price (not required for spot markets) formatted accounting for market's decimal places + +Once market is closed the process cannot be reversed. Note that this implies that once a governance proposal to close the market has been voted in the market will definitely close at the enactment time of that vote at the latest. While the market is still open it's still possible to submit additional governance votes to close the market, however they'll only have any effect if their enactment date is prior to that of the market closure proposal which has already passed. + +If the market is in an auction of any type excluding the opening auction at the time the market closure governance proposal gets enacted, then the auction should uncross immediately, any trades resulting from it should be generated at the auction uncrossing price and then the system should proceed to close the market using the price (if applicable) provided by the proposal being enacted. If the market is in opening auction when the governance proposal to close it gets enacted then auction shouldn't uncross, market closes trivially as no trades have yet been generated. + +Attempting to enact the market closure governance proposal on a market in a `settled` [state](./0043-MKTL-market_lifecycle.md#market-status-descriptions) has no effect. When closing a market which needs the final price with a governance vote it's always the price supplied with the governance vote being enacted that gets used, even if the oracle price is available at that time. Please note that the price supplied with the vote needs no further conversion as it's already specified in market decimal places. + +The state of a market successfully closed by the governance vote should be `closed`. + +Please note that certain types of markets like [perpetual futures](./0053-PERP-product_builtin_perpetual_future.md) may perform additional actions during governance closure, refer to their specs for details. + +### 6.2. Suspend the market + +This proposal puts the market into an auction mode which can only be exit with a governance proposal to resume the market. It can be applied to a market that's in any of the active (accepting orders) states including the opening auction. + +A market that's been suspended can't have the open volume changed or margin account balances reduced for any of the parties within the market. Parties can submit the relevant order types just like in an other auction. + +If the market is already suspended via governance when another vote gets enacted then that vote has no effect. + +### 6.3. Resume the market + +This proposal removes the restrictions put in place by a successful [market suspension proposal](#62-suspend-the-market). Note that this does not necessarily mean the market that's in auction mode should leave it immediately, as other auction triggers may still be active. + +If the market is not suspended when the vote to resume the market gets enacted then that vote has no effect. + +## 7. Freeform governance proposal The aim of this is to allow community to provide votes on proposals which don't change any of the behaviour of the currently running Vega blockchain. That is to say, at enactment time, no changes are effected on the system, but the record of how token holders voted will be stored on chain. The proposal will contain only the fields common to all proposals i.e. @@ -375,6 +452,28 @@ Notes: - The categorisation of parameters is liable to change and be added to as the protocol evolves. - As these are themselves network parameters, a set of parameters will control these parameters for the actions that update these parameters (including being self-referential), i.e. the parameter `Governance.UpdateNetwork.GovernanceProposalValidation.MinimumRequiredParticipation` would control the amount of voting participation needed to change these parameters. See the Network Parameters spec. +## Batch Proposals + +A `BatchProposalSubmission` is a top-level proposal type (living at the same level in a `Transaction` object as a standard `ProposalSubmission`) which allows grouping of several individual proposals into a single proposal, ensuring that all changes will pass or fail governance voting together. +The batch proposal is a wrapper containing one `rationale` (i.e. `title` and `description`) field as a standard `ProposalSubmission`, one `closingTimestamp` field and a list of `ProposalSubmission`s which omit certain fields. +The individual `ProposalSubmission` have no `rationale` entry (i.e. no `title` and `description`). +The individual `ProposalSubmission` have no `closingTimestamp` entry. +Any governance proposal can be included in a batch _except_ proposals to add new assets. +For avoidance of doubt: asset _change_ proposals can be included. + +Validation should be applied by the protocol when accepting such a transaction to verify that all proposals within the batch meet their own minimum voting periods (if not transaction is rejected). +The enactment timestamp, is customisable and can be different for each proposal within the batch, as long as the minimum enactment time of each individual proposal within the batch is respected and as long as every `enactmentTimestamp` is greater than or equal to `closingTimestamp` of the entire batch. + +Once submitted, a single voting period should be run in which participants may place a single vote to approve/disapprove of the entire batch. It _must_ _not_ be possible to vote for components in the batch separately. +Once the closing timestamp is reached each individual proposal within the batch is evaluated against the votes received. +If all individual proposals would pass (given the votes received, based on their individual rules on participation, majority, LP-ELS voting etc.) then the entire batch passes. +If even one proposal within the batch would fail then the entire batch fails. + +If the batch passes, each of the component proposals should be enacted at their enactment timestamp exactly as if each had been proposed and passed individually. The enactment order of two proposals in the batch with the same enactment timestamp: they enact in the order they appear in the batch. +In particular asset update proposals may emit signed bundles to be submitted to the bridge on ethereum side (for withdrawal delay / threshold updates). + +The number of individual proposals that can be submitted within a single batch is limited to 250. + ## APIs The core should expose via core APIs: @@ -428,7 +527,6 @@ APIs should also exist for clients to: - As the vega network, if a proposal is accepted and the duration required before change takes effect is reached, the changes are applied (0028-GOVE-008) - New market proposals cannot be created before [`limits.markets.proposeEnabledFrom`](../non-protocol-specs/0003-NP-LIMI-limits_aka_training_wheels.md#network-parameters) is in the past (0028-GOVE-024) -- A market that has been proposed and successfully voted through doesn't leave the opening auction until the `enactment date/time` is reached and until sufficient [liquidity commitment](./0038-OLIQ-liquidity_provision_order_type.md) has been made for the market. Sufficient means that it meets all the criteria set in [liquidity monitoring](./0035-LIQM-liquidity_monitoring.md). (0028-GOVE-025) - A market proposal with a negative or non-integer value supplied for market decimal places gets rejected. (0028-GOVE-061) - A market proposal with position decimal places not in `{-6,...,-1,0,1,2,...,6}` gets rejected. (0028-GOVE-062) @@ -442,10 +540,36 @@ APIs should also exist for clients to: - Verify that an enacted market change proposal that changes price monitoring bounds enters a price monitoring auction upon the _new_ bound being breached (0028-GOVE-034) - Verify that an enacted market change proposal that reduces `market.stake.target.timeWindow` leads to a reduction in target stake if recent open interest is less than historical open interest (0028-GOVE-031) - Attempts to update immutable market parameter(s) cause the market change proposal to be rejected with an appropriate rejection message (0028-GOVE-058) -- Verify that if `governance.proposal.updateMarket.minProposerEquityLikeShare = 0` and if a party meets the `governance.proposal.updateMarket.minProposerBalance` threshold then said party can submit a market change proposal. (0028-GOVE-060) +- Verify that if `governance.proposal.updateMarket.minProposerEquityLikeShare = 0.00001` and if a party has no equity-like share in the market, but meets the `governance.proposal.updateMarket.minProposerBalance` threshold then said party can submit a market change proposal. (0028-GOVE-134) - Change of the network parameter `governance.proposal.updateMarket.minProposerEquityLikeShare` will immediately change the minimum proposer ELS for a market change proposal for all future proposals. Proposals that have already been submitted are not affected. (0028-GOVE-064) - Change of the network parameter `governance.proposal.updateMarket.requiredParticipationLP` will immediately change the required LP vote participation (measured in ELS) a market change proposal requires for all future proposals. Proposals that have already been submitted are not affected. (0028-GOVE-065) - Change of the network parameter `governance.proposal.updateMarket.requiredMajorityLP` will immediately change the required LP vote majority (measured in ELS) a market change proposal requires for all future proposals. Proposals that have already been submitted are not affected. (0028-GOVE-066) +- A market that's attempting to modify any parameters on a market in `proposed` state (i.e. voting hasn't completed) will be rejected. (0028-GOVE-069) +- A market change proposal that's to modify any parameters on a market in `pending` state (i.e. voting has successfully completed and the market is in the opening auction) will be accepted and if it's the enactment time happens to be before the opening auction ends then the proposed modification is enacted. (0028-GOVE-070) +- In particular a market change proposal that's to modify the parent market on a market in `pending` state (i.e. voting has successfully completed and the market is in the opening auction) will be accepted and if it's the enactment time happens to be before the opening auction ends then the parent is used (assuming the proposed parent doesn't already have a successor). (0028-GOVE-071) +- A market change that's to modify any parameters on a market in `pending` state (i.e. voting has successfully completed on the market creation and the market is in the opening auction) will run voting rules the same as market creation proposals i.e. LPs don't get a vote. (0028-GOVE-072) +- A market change proposal that aims to modify the market name in any state (apart from closed/terminated) will modify the market name at the time of vote enactment. (0028-GOVE-159) +- A market change proposal that aims to modify the market code in any state (apart from closed/terminated) will modify the market code at the time of vote enactment. (0028-GOVE-166) +- A governance proposal to close a market which doesn't specify the final settlement price gets rejected by the markets which require it (non-spot). (0028-GOVE-108) +- When there's already been a market closure governance proposal successfully voted in for a given market, but not yet enacted it is still possible to submit additional market closure governance proposals for that market. If another market closure governance proposal gets voted it and it has an earlier enactment time then it's the final settlement price of that proposal which gets used. (0028-GOVE-110) +- Governance vote to suspend a market that's currently in continuous trading mode puts it into auction mode at vote enactment time. The only way to put the market back into continuous trading mode is with a successful governance vote to resume the market. (0028-GOVE-113) +- Governance vote to suspend a market that's currently in auction trading mode keeps it in auction mode at vote enactment time. Even if the trigger that originally put the market into auction mode is no longer violated the market must remain in auction. Resuming the market will then put the market in the state it was in prior to it being suspended. + - monitoring auction (0028-GOVE-114) + - opening auction (0028-GOVE-167) +- Resuming a market with other auction triggers active does not put it out of auction until those triggers allow to do so. (0028-GOVE-115) +- A market suspended by the governance vote does not allow trade generation of margin account balance reduction. (0028-GOVE-116) +- Verify that a party with 0 balance of the governance token, but with sufficient ELS can submit a market change proposal successfully. (0028-GOVE-117) +- Verify that a party with 0 balance of the governance token and insufficient ELS sees their market change proposal rejected after submission. (0028-GOVE-118) +- Enacting a market closure governance proposal on a market which is in opening auction cancels it immediately without generating any trades. The market moves to a cancelled state and any open orders are also cancelled. (0028-GOVE-135) +- Enacting a market closure governance proposal on a market which is in auction (of any type except the opening auction) uncrosses that auction at the current uncrossing price, generates the trades and then proceeds to close it using the final price (if applicable to the market type). (0028-GOVE-136) +- Enacting a market closure governance proposal on a market that is in a settled state has no effect. (0028-GOVE-137) +- Enacting a market closure governance proposal on a market that is not in a settled state always uses the price supplied with the proposal for final settlement, even when the oracle settlement price is available at that time. (0028-GOVE-138) +- Successful enactment of a market closure proposal changes the state of the market to `closed`. (0028-GOVE-139) +- Attempt to enact a market closure proposal on a closed market has no effect. (0028-GOVE-111) +- Markets which have been suspended via a governance proposal can be resumed after a protocol upgrade restarts the network. (0028-GOVE-150) +- Markets which have been suspended via a governance proposal can be terminated after a protocol upgrade restarts the network. (0028-GOVE-151) +- Oracle data sources shared between multiple markets are not deactivated if one of the markets sharing the oracle data sources is terminated and settled using governance proposals. Now the status of the data sources should still be ACTIVE as Market2 is still using them. (0028-GOVE-152) +- Ensure that when a market is suspended and then resumed via a governance proposal we can still terminate and settle the market using ethereum oracle. (0028-GOVE-153) #### Network parameter change proposals @@ -470,3 +594,139 @@ Below `*` stands for any of `asset, market, updateMarket, updateNetParam, freeFo - Approved governance proposals sharing the same enactment time should be enacted in the order the proposals were created. (0028-GOVE-067) - Approved governance proposals sharing the same enactment time and changing the same parameter should all be applied, the oldest proposal will be applied first and the newest will be applied last, overwriting the changes made by the older proposals. (0028-GOVE-068) + + +#### Governance initiated transfer proposals + + +##### Proposer Requirements + +- The transfer proposer must have at a staking balance which matches or exceeds `minProposerBalance` network parameter for this proposal type (0028-GOVE-073) + + +##### APIs + +- Governance initiated transfer proposal and all associated data are returned via the governance APIs (0028-GOVE-074) + + +##### Transfer proposal submission validation + +- A proposal to transfer tokens between Network treasury and Party general account(s) is valid (0028-GOVE-128) +- A proposal to transfer tokens between Network treasury and market insurance pool account is valid (0028-GOVE-119) +- A proposal to transfer tokens between Market insurance pool account and Party account(s) is valid (0028-GOVE-120) +- A proposal to transfer tokens between Market insurance pool account and Network treasury is valid (0028-GOVE-132) +- A proposal to transfer tokens between Market insurance pool account and Market insurance pool account is valid (0028-GOVE-122) +- Governance initiated transfer proposals with invalid source or destination account types will get rejected by the blockchain. (0028-GOVE-077) +- Source can be left blank for a transfer type of Network Treasury (0028-GOVE-079) +- For proposal source/destination types of Market Insurance the source/destination must be a valid `marketID` else the proposal is rejected by the blockchain. (0028-GOVE-081) +- Type value can only hold “all or nothing" or "best effort” (0028-GOVE-082) +- Transfer amounts will be accepted and processed in asset precision (0028-GOVE-083) +- Asset specified must be a valid asset address else proposal is rejected (0028-GOVE-084) +- Fraction of balance must be submitted as a positive (else will cause the proposal to reject) and will be processed as a fraction of the source accounts balance (0028-GOVE-085) +- Destination Type can be any of the predefined types in the above table (0028-GOVE-086) +- Source and destination type cannot be the same value else the proposal will be rejected (0028-GOVE-087) +- Transfers can be proposed between market insurance accounts but source and destination accounts cannot be the same value else the proposal will be rejected (0028-GOVE-088) +- Destination must be a valid Vega public key for a transfer type of Party else is rejected (0028-GOVE-089) +- For transfer source types of Market Insurance the destination must be a valid market ID else is rejected (0028-GOVE-091) +- The proposal will allow standard proposal fields to control timings on closing the voting period and enactment time, these will be validated in the same way as other proposals (0028-GOVE-092) +- For successor markets we allow transfer between Market insurance pool account of parent market to Market insurance pool account of child market (0028-GOVE-093) +- During a recurring transfer ensure that the correct tokens continue to be distributed when the source account is funded (0028-GOVE-154) +- A proposal to transfer tokens between Network treasury and global insurance pool account is valid (0028-GOVE-155) +- A proposal to transfer tokens between global insurance pool account and Party account(s) is valid (0028-GOVE-156) +- A proposal to transfer tokens between global insurance pool account and Network treasury is valid (0028-GOVE-157) +- A proposal to transfer tokens between global insurance pool account and Market insurance pool account is valid (0028-GOVE-158) + +##### Governance initiated transfer enactment + +- For enacted proposals a token transfer will occur at the time of enactment between the source and destination account if sufficient tokens are held in the source account. A transaction result event will show the successful transfer between two accounts (0028-GOVE-094) +- A governance approved recurring transfer will continue even if the source account balance is `0`. In such case the amount transferred will be seen to be `0`. (0028-GOVE-095) +- Transfers can occur for pending, terminated markets, settled markets (0028-GOVE-096) + + +##### Transferred Amount + +- If the type of transfer is “All or nothing” then the minimum of either `fraction_of_balance * source_balance` and the transfer amount is transfers between accounts. The transfer is recorded in Vega ledger movements even if the amount is derived as zero (0028-GOVE-099) +- If the type of transfer is “Best effort” then the transfer amount is derived from the minimum of `proposal.fraction_of_balance * source.balance, proposal.amount, NETWORK_MAX_AMOUNT, NETWORK_MAX_FRACTION * source.balance`. The transfer is recorded in Vega ledger movements even if the amount is derived as zero (0028-GOVE-100) + + +##### Transfer Fees + +- No fees are incurred by the transfer and therefore the the number of tokens deducted from the source account should always equal the tokens added to the destination account (0028-GOVE-101) + + +##### Protocol Upgrade + +- Transfer proposals in either a pre or post enactment state are restored after a protocol upgrade (0028-GOVE-102) +- Recurring transfers proposed before an upgrade which start before, during or after an upgrade should complete on the proposed end epoch (0028-GOVE-130) +- One off delivery transfers proposed before an upgrade which are due to start during or after an upgrade should complete either when the network is available again or at the proposed delivery date/time (0028-GOVE-131) + + +##### Checkpoints and Snapshots + +- Active or dormant governance initiated transfer (one-off or recurring) must be included in checkpoint and where the network is down during the proposed delivery time, the transfer will occur as soon as the network is available. For recurring transfers the transfers spanning the restore will continue until the end epoch. (0028-GOVE-103) +- Active or dormant governance initiated transfer (one-off or recurring) must be included in snapshots and data nodes which join the network will support retrieval of the transfer data (0028-GOVE-133) + + +##### One Off Delivery transfers + +If the proposal is one off it can define a time for delivery. Whenever the block time is after the delivery time, the transfer will execute. If there is no delivery time the one off transfer will execute immediately. (0028-GOVE-129) +It is possible to submit a one off governance transfer proposal from network treasury into any non-metric based reward account (including staking rewards). (0028-GOVE-140) +It is possible to submit a one off governance transfer proposal from market's insurance pool into any non-metric based reward account (including staking rewards). (0028-GOVE-141) +It is NOT possible to submit a governance proposal where the source account is the reward account. (0028-GOVE-144) + +##### Recurring governance initiated transfers + +- For a recurring proposal, the proposal is only active from defined start epoch and optional end epoch, the transfer will be executed every epoch while the proposal is active. (0028-GOVE-104) + +- Enacted and active recurring governance initiated transfers must be included in LNL banking checkpoint and resume after the checkpoint restore.(0028-GOVE-105) + +- When a transfer gets enacted it emits transfer event similar to regular transfer events from regular transfers, however with governance-recurring types. At the time of enactment no amount is attached to the transfer and it will show 0.(0028-GOVE-106) + +- It is possible to submit a recurring governance transfer proposal from network treasury into any reward account (including staking rewards). (0028-GOVE-142) +- It is possible to submit a recurring governance transfer proposal from market's insurance pool into any reward account (including staking rewards). (0028-GOVE-143) + +##### Cancelling governance initiated transfers + +- Only recurring governance transfers can be cancelled via governance cancel transfer proposal. Trying to cancel any other transfer should fail upon validation of the proposal.(0028-GOVE-107) +- After a transfer is cancelled there will be no more transfers occurring in the block/seq following the cancellation. This applies to one off and recurring transfers. (0028-GOVE-123) +- Recurring transfers can be cancelled only after the transfer proposal reached an enacted state. Attempts to cancel before the recurring transfer proposal has enacted will result in a proposal rejection which states the transfer does not exist (0028-GOVE-124) +- Using a governance proposal to cancel, attempts to cancel an using an invalid transfer ID will result in a proposal rejection which states the transfer does not exist (0028-GOVE-125) +- When a transfer is cancelled vega will produce an event conveying the cancellation to datanode. This will contain a cancellation status and zero transfer amount. No ledger events will be produced.(0028-GOVE-126) + + +##### Batch Proposals + +- A batch proposal containing one or more component submissions mixing different proposal types can be submitted and is accepted as a valid proposal. (0028-GOVE-146) + +- A batch proposal submitted with component submissions having different one or more enactment timestamps lower than the closing timestamps will be rejected with an informative error message. (0028-GOVE-148) + +- A batch proposal submitted with component submissions with different enactment timestamps, all of which are valid for the type of change being proposed for that enactment time, will be accepted and move to voting. (0028-GOVE-149) + 1. If this proposal is accepted, each of the components will be enacted at the time of their differing enactment timestamps. (0028-GOVE-145) + +- A batch proposal containing + 1. freeform proposal, + 1. an update asset proposal changing the asset quantum, maximum lifetime deposit and withdrawal delay threshold + 1. a network parameter change, + 1. a market proposal, + 1. a change proposal for another market, + 1. volume discount program, + 1. referral program, + 1. governance transfer, + +can be submitted, voted through and each proposal enacted. +On top of that signed bundles for changing withdrawal delay and threshold on the bridge are emitted (0028-GOVE-160) + +- A batch proposal can be submitted changing the same network parameter twice to two different values with two different enactment timestamps. +The voting to approve the batch happens, the batch passes, both changes are observed at the desired time. (0028-GOVE-161) + +A vote cannot be submitted for an individual component in a batch (0028-GOVE-163) + +When a batch contains two proposals with identical enactment timestamps, they are executed in the order the appear in the batch. For example two network parameter update proposals in the same batch with the same enactment timestamp will both be executed, in the order specified. (0028-GOVE-164) + +- A batch proposal can be submitted changing the same network parameter twice to two different values with the same enactment timestamps. +The voting to approve the batch happens, the batch passes, the value of the proposal appearing later in the batch is observed at the desired time. (0028-GOVE-165) + + +##### Network History + +- A datanode restored from network history will contain any recurring and one-off transfers created prior to the restore and these can be retrieved via APIs on the new datanode.(0028-GOVE-127) diff --git a/protocol/0029-FEES-fees.md b/protocol/0029-FEES-fees.md index 64bef4130..cbce1dbed 100644 --- a/protocol/0029-FEES-fees.md +++ b/protocol/0029-FEES-fees.md @@ -21,6 +21,62 @@ Fees are calculated and collected in the settlement currency of the market, coll Note that maker_fee = 0 if there is no maker, taker relationship between the trading parties (in particular auctions). +## Applying benefit factors + +Before fees are transferred, if there is an [active referral program](./0083-RFPR-on_chain_referral_program.md) or [volume discount program](./0085-VDPR-volume_discount_program.md), each parties fee components must be modified as follows. + +Note, discounts are calculated and applied one after the other and **before** rewards are calculated. + +1. Calculate any referral discounts due to the party. + + ```pseudo + infrastructure_fee_referral_discount = floor(original_infrastructure_fee * referral_discount_factor) + liquidity_fee_referral_discount = floor(original_liquidity_fee * referral_discount_factor) + maker_fee_referral_discount = floor(original_maker_fee * referral_discount_factor) + ``` + +1. Apply referral discounts to the original fee. + + ```pseudo + infrastructure_fee_after_referral_discount = original_infrastructure_fee - infrastructure_fee_referral_discount + liquidity_fee_after_referral_discount = original_infrastructure_fee - liquidity_fee_referral_discount + maker_fee_after_referral_discount = original_infrastructure_fee - maker_fee_referral_discount + ``` + +1. Calculate any volume discounts due to the party. + + ```pseudo + infrastructure_fee_volume_discount = floor(infrastructure_fee_after_referral_discount * volume_discount_factor) + liquidity_fee_volume_discount = floor(liquidity_fee_after_referral_discount * volume_discount_factor) + maker_fee_volume_discount = floor(maker_fee_after_referral_discount * volume_discount_factor) + ``` + +1. Apply any volume discounts to the fee after referral discounts. + + ```pseudo + infrastructure_fee_after_volume_discount = infrastructure_fee_after_referral_discount - infrastructure_fee_volume_discount + liquidity_fee_after_volume_discount = liquidity_fee_after_referral_discount - liquidity_fee_volume_discount + maker_fee_after_volume_discount = maker_fee_after_referral_discount - maker_fee_volume_discount + ``` + +1. Calculate any referral rewards due to the parties referrer (Note we are using the updated fee components from step 4 and the `referralProgram.maxReferralRewardProportion` is the network parameter described in the [referral program spec](./0083-RFPR-on_chain_referral_program.md#network-parameters)) + + ```pseudo + infrastructure_fee_referral_reward = floor(infrastructure_fee_after_volume_discount * min(referral_reward_factor * referral_reward_multiplier, referralProgram.maxReferralRewardProportion)) + liquidity_fee_referral_reward = floor(liquidity_fee * min(liquidity_fee_after_volume_discount * min(referral_reward_factor * referral_reward_multiplier, referralProgram.maxReferralRewardProportion)) + maker_fee_referral_reward = floor(maker_fee * min(maker_fee_after_volume_discount * min(referral_reward_factor * referral_reward_multiplier, referralProgram.maxReferralRewardProportion)) + ``` + +1. Finally, update the fee components by applying the rewards. + + ```pseudo + final_infrastructure_fee = maker_fee_after_volume_discount - infrastructure_fee_referral_reward + final_liquidity_fee = maker_fee_after_volume_discount - liquidity_fee_referral_reward + final_maker_fee = maker_fee_after_volume_discount - maker_fee_referral_reward + ``` + +(Note the rewards and discounts are floored rather than raised to ensure the final fees cannot be negative.) + ### Factors - infrastructure: staking/governance system/engine (network wide) @@ -42,7 +98,7 @@ NB: size of trade needs to take into account Position Decimal Places specified i ### Collecting and Distributing Fees -We need to calculate the total fee for the transaction. +We need to calculate the total fee for the transaction (before applying benefit factors). Attempt to transfer the full fee from the trader into a temporary bucket, one bucket per trade (so we know who the maker is) from the trader general account. If insufficient, then take the remainder (possibly full fee) from the margin account. The margin account should have enough left after paying the fees to cover maintenance level of margin for the trades. @@ -55,11 +111,12 @@ Other than the criteria whether to proceed or discard, this is exactly the same The transfer of fees must be completed before performing the normal post-trade calculations (MTM Settlement, position resolution etc...). The transfers have to be identifiable as fee transfers and separate for the three components. -Now distribute funds from the "temporary fee bucket" as follows: +Now [apply benefit factors](#applying-benefit-factors) and then distribute funds from the "temporary fee bucket" as follows: -1. Infrastructure_fee is transferred to infrastructure fee pool for that asset. Its distribution is described in [0061 - Proof of Stake rewards](./0061-REWP-pos_rewards.md). In particular, at the end of each epoch the amount due to each validator and delegator is to be calculated and then distributed subject to validator score and type. -1. The maker_fee is transferred to the relevant party. -1. The liquidity_fee is distributed as described in [this spec](./0042-LIQF-setting_fees_and_rewarding_lps.md). +1. The `infrastructure_fee` is transferred to infrastructure fee pool for that asset. Its distribution is described in [0061 - Proof of Stake rewards](./0061-REWP-pos_rewards.md). In particular, at the end of each epoch the amount due to each validator and delegator is to be calculated and then distributed subject to validator score and type. +1. The `maker_fee` is transferred to the relevant party (the maker). +1. The `liquidity_fee` is distributed as described in [this spec](./0042-LIQF-setting_fees_and_rewarding_lps.md). +1. The referral fee components (if any) can then be individually transferred to the relevant party (the referee). ### During Continuous Trading @@ -83,7 +140,7 @@ Order that entered the book in the current batch are considered aggressive order The trades that were netted off against each other during position resolution incur no fees. During position resolution all of the parties being liquidated share the total fee for the network order, pro-rated by the size of position. -As for fees in other cases, the fee is taken out of the general + margin account for the liable traders (the insurance pool is not used to top up fees that cannot be paid). If the general + margin account is insufficient to cover the fee then the fee (or part of it) is not going to get paid. In this case we first pay out the maker_fee (or as much as possible), then then infrastructure_fee (or as much as possible) and finally the liquidity_fee. +As for fees in other cases, the fee is taken out of the general + margin account for the liable traders (the market's insurance pool is not used to top up fees that cannot be paid). If the general + margin account is insufficient to cover the fee then the fee (or part of it) is not going to get paid. In this case we first pay out the maker_fee (or as much as possible), then then infrastructure_fee (or as much as possible) and finally the liquidity_fee. ### Rounding @@ -93,17 +150,46 @@ For example, Ether is 18 decimals (wei). The smallest unit, non divisible is 1 w ## Acceptance Criteria -- Fees are collected during continuous trading and auction modes and distributed to the appropriate accounts, as described above. (0029-FEES-001) +- Fees are collected during continuous trading and auction modes and distributed to the appropriate accounts, as described above. (0029-FEES-001). - Fees are debited from the general (+ margin if needed) account on any market orders that during continuous trading, the price maker gets the appropriate fee credited to their general account and the remainder is split between the market making pool and infrastructure (staking) pool. (0029-FEES-002) - Fees are debited from the general (+ margin if needed) account on the volume that resulted in a trade on any "aggressive / price taking" limit order that executed during continuous trading, the price maker gets the appropriate fee credited to their general account and the remainder is split between the market making pool and staking pool. (0029-FEES-003) - Fees are debited from the general (+ margin if needed) account on any "aggressive / price taking" pegged order that executed during continuous trading, the price maker gets the appropriate fee credited to their general account and the remainder is split between the market making pool and staking pool. (0029-FEES-004) -- Fees are collected in one case of amends: you amend the price so far that it causes an immediate trade. (0029-FEES-005) +- Fees are collected in one case of amends: you amend the price so far that it causes an immediate trade. (0029-FEES-005). - During auctions, each side of a trade is debited 1/2 (infrastructure_fee + liquidity_fee) from their general (+ margin if needed) account. The infrastructure_fee fee is credited to the staking pool, the liquidity_fee is credited to the market making pool. (0029-FEES-006) - During continuous trading, if a trade is matched and the aggressor / price taker has insufficient balance in their general (+ margin if needed) account, then the trade doesn't execute if maintenance level of trade is not met. (0029-FEES-007) -- During auctions, if either of the two sides has insufficient balance in their general (+ margin if needed) account, the trade still goes ahead only if :-) the margin account should have enough left after paying the fees to cover maintenance level of margin for the orders and then converted trades. (0029-FEES-008) -- Changing parameters (via governance votes) does change the fees being collected appropriately even if the market is already running. (0029-FEES-009) -- A "buyer_fee" and "seller_fee" are exposed in APIs for every trade, split into the three components (after the trade definitely happened) (0029-FEES-010) -- Users should be able to understand the breakdown of the fee to the three components (by querying for fee payment transfers by trade ID, this requires enough metadata in the transfer API to see the transfer type and the associated trade.) (0029-FEES-011) -- The three component fee rates (fee_factor[infrastructure, fee_factor[maker], fee_factor[liquidity] are available via an API such as the market data API or market framework. (0029-FEES-012) -- A market is set with [Position Decimal Places" (PDP)](0052-FPOS-fractional_orders_positions.md) set to 2. A market order of size 1.23 is placed which is filled at VWAP of 100. We have fee_factor[infrastructure] = 0.001, fee_factor[maker] = 0.002, fee_factor[liquidity] = 0.05. The total fee charged to the party that placed this order is `1.23 x 100 x (0.001 + 0.002 + 0.05) = 6.519` and is correctly transferred to the appropriate accounts / pools. (0029-FEES-013) -- A market is set with [Position Decimal Places" (PDP)](0052-FPOS-fractional_orders_positions.md) set to -2. A market order of size 12300 is placed which is filled at VWAP of 0.01. We have fee_factor[infrastructure] = 0.001, fee_factor[maker] = 0.002, fee_factor[liquidity] = 0.05. The total fee charged to the party that placed this order is `12300 x 0.01 x (0.001 + 0.002 + 0.05) = 6.519` and is correctly transferred to the appropriate accounts / pools. (0029-FEES-014) +- During auctions, if either of the two sides has insufficient balance in their general (+ margin if needed) account, the trade still goes ahead only if the margin account should have enough left after paying the fees to cover maintenance level of margin for the orders and then converted trades. (0029-FEES-008) +- Changing parameters (via governance votes) does change the fees being collected appropriately even if the market is already running. (0029-FEES-009). +- A "buyer_fee" and "seller_fee" are exposed in APIs for every trade, split into the three components (after the trade definitely happened) (0029-FEES-010). +- Users should be able to understand the breakdown of the fee to the three components (by querying for fee payment transfers by trade ID, this requires enough metadata in the transfer API to see the transfer type and the associated trade.) (0029-FEES-011). +- The three component fee rates (fee_factor[infrastructure], fee_factor[maker], fee_factor[liquidity]) are available via an API such as the market data API or market framework. (0029-FEES-012). +- A market is set with [Position Decimal Places" (PDP)](0052-FPOS-fractional_orders_positions.md) set to 2. A market order of size 1.23 is placed which is filled at VWAP of 100. We have fee_factor[infrastructure] = 0.001, fee_factor[maker] = 0.002, fee_factor[liquidity] = 0.05. The total fee charged to the party that placed this order is `1.23 x 100 x (0.001 + 0.002 + 0.05) = 6.519` and is correctly transferred to the appropriate accounts / pools. (0029-FEES-013). +- A market is set with [Position Decimal Places" (PDP)](0052-FPOS-fractional_orders_positions.md) set to -2. A market order of size 12300 is placed which is filled at VWAP of 0.01. We have fee_factor[infrastructure] = 0.001, fee_factor[maker] = 0.002, fee_factor[liquidity] = 0.05. The total fee charged to the party that placed this order is `12300 x 0.01 x (0.001 + 0.002 + 0.05) = 6.519` and is correctly transferred to the appropriate accounts / pools. (0029-FEES-014). + +### Applying benefit factors + +1. Referee discounts are correctly calculated and applied for each taker fee component during continuous trading (assuming no volume discounts due to party) (0029-FEES-023) + - `infrastructure_referral_fee_discount` + - `liquidity_fee_referral_discount` + - `maker_fee_referral_discount` +1. Referee discounts are correctly calculated and applied for each fee component when exiting an auction (assuming no volume discounts due to party) (0029-FEES-024) + - `infrastructure_fee_referral_discount` + - `liquidity_fee_referral_discount` +1. Referrer rewards are correctly calculated and transferred for each fee component during continuous trading (assuming no volume discounts due to party) (0029-FEES-025) + - `infrastructure_fee_referral_reward` + - `liquidity_fee_referral_reward` + - `maker_fee_referral_reward` +1. Referrer rewards are correctly calculated and transferred for each fee component when exiting an auction (assuming no volume discounts due to party) (0029-FEES-026) + - `infrastructure_fee_referral_reward` + - `liquidity_fee_referral_reward` +1. If the referral reward due to the referrer is strictly less than `1`, no reward is transferred (0029-FEES-029). +1. If the referral discount due to the referee is strictly less than `1`, no discount is applied (0029-FEES-030). +1. The proportion of fees transferred to the referrer as a reward cannot be greater than the network parameter `referralProgram.maxReferralRewardProportion` (0029-FEES-031). +1. Volume discount rewards are correctly calculated and transferred for each taker fee component during continuous trading (assuming no referral discounts due to party) (0029-FEES-027) + - `infrastructure_fee_volume_discount` + - `liquidity_fee_volume_discount` + - `maker_fee_volume_discount` +1. Volume discount rewards are correctly calculated and transferred for each fee component when exiting an auction (assuming no referral discounts due to party) (0029-FEES-028) + - `infrastructure_fee_volume_discount` + - `liquidity_fee_volume_discount` +1. During continuous trading, discounts from multiple sources are correctly calculated and applied one after the other, each time using the resulting fee component after the previous discount was applied. (0029-FEES-032). +1. When exiting an auction, discounts from multiple sources are correctly calculated and applied one after the other, each time using the resulting fee component after the previous discount was applied. (0029-FEES-033). diff --git a/protocol/0031-ETHB-ethereum_bridge_spec.md b/protocol/0031-ETHB-ethereum_bridge_spec.md index 7a88be6cf..45222e5c8 100644 --- a/protocol/0031-ETHB-ethereum_bridge_spec.md +++ b/protocol/0031-ETHB-ethereum_bridge_spec.md @@ -26,9 +26,9 @@ Each bridge contains two primary functions and emits two primary events, each ta ### Block confirmations -It is normal behaviour when validating transfers to wait a certain number of blocks for a deposit to be 'confirmed'. We need to do the same, to have acceptably high probability that the event is on the longest chain and there won't be a fork in the future which will invalidate this. We achieve this by ensuring that enough time has passed. +It is normal behaviour when validating transfers to wait a certain number of blocks for a deposit to be 'confirmed'. We need to do the same, to have acceptably high probability that the event is on the longest chain and there won't be a fork in the future which will invalidate this. We achieve this by ensuring that enough time has passed. On Ethereum, there is now an additional finality mechanism, which is used to replace the confirmations. -This will need to be configured per chain that we connect to. ETH, ERC-20, ERC-XXX can all share a value, which should be configurable and changeable via governance. For Ethereum, this should be 20 confirmations. It is safe to lower this for development networks. +This will need to be configured per chain that we connect to. ETH, ERC-20, ERC-XXX can all use the finallity mechanism. For It is safe to use a lower security mechanism for development networks. ## Reference-level explanation @@ -109,7 +109,7 @@ The Ethereum Bridge uses 1 network parameter, `blockchains.ethereumConfig`, a JS | Property | Type | Example value | Description | |------------------|--------| ------------|--------------| -| `chain_id` | String | `"3"` | Ethereum [Chain ID](https://eips.ethereum.org/EIPS/eip-155) to connect to +| `chain_id` | String | `"3"` | Ethereum [Chain ID](https://eips.ethereum.org/EIPS/eip-155) to connect to | | `network_id` | String | `"3"` | Ethereum [Network ID](https://eips.ethereum.org/EIPS/eip-155) to connect to | | `collateral_bridge_contract` | {string, integer} | `{address: "0xCcB517899f714BD1B2f32931fF429aDEdDd02A93", deployment_height: 1}` | The address for a deployed instance of the bridge contract | | `staking_bridge_contract` | {string, integer} | `{address: "0xCcB517899f714BD1B2f32931fF429aDEdDd02A93", deployment_height: 1}` | The addresses to listen to for [staking events](./0059-STKG-simple_staking_and_delegating.md). | @@ -117,6 +117,8 @@ The Ethereum Bridge uses 1 network parameter, `blockchains.ethereumConfig`, a JS | `multisig_control_contract` | {string, integer} | `{address: "0xCcB517899f714BD1B2f32931fF429aDEdDd02A93", deployment_height: 1}` | The addresses to listen to for multisig control event | | `confirmations` | Integer | `3` | Block confirmations to wait for before confirming an action | +The confirmation part is not used anymore and is a legacy parameter. + ### Full example This example connects the network to Ropsten: @@ -153,12 +155,6 @@ This example connects the network to Ropsten: - A valid multisig bundle can be passed to the `list_asset` function to successfully add a token to the allowed list (0031-ETHB-016) - An invalid multisig bundle is rejected by the `list_asset` function (0031-ETHB-017) -### Blocklist a token (by eth address) - -- ERC-20 smart contract specific requirements: - - A valid multisig bundle can be passed to the `remove_asset` function to successfully remove a previously allow listed token (0031-COSMICELEVATOR-019) - - An invalid multisig bundle is rejected by the `remove_asset` function (0031-COSMICELEVATOR-020) - ### ERC20 Bridge Logic to Vega Integration Tests (Vega System Tests) To ensure complete coverage of public and external smart contract functions, listed below are all of the callable functions on `ERC20_Bridge_Logic` and their corresponding acceptance criteria. diff --git a/protocol/0032-PRIM-price_monitoring.md b/protocol/0032-PRIM-price_monitoring.md index 7dc3b3109..8b7e388d4 100644 --- a/protocol/0032-PRIM-price_monitoring.md +++ b/protocol/0032-PRIM-price_monitoring.md @@ -13,7 +13,7 @@ Please see the [auction spec](./0026-AUCT-auctions.md) for auction details. ### Note -Price monitoring likely won't be the only possible trigger of auction period ([liquidity monitoring](./0035-LIQM-liquidity_monitoring.md) or governance action could be the other ones). Thus the framework put in place as part of this spec should be flexible enough to easily accommodate other types of triggers. +Price monitoring likely won't be the only possible trigger of auction period e.g. governance action could be the other ones. Thus the framework put in place as part of this spec should be flexible enough to easily accommodate other types of triggers. Likewise, pre-processing transactions will be needed as part of the [fees spec](./0029-FEES-fees.md), hence it should be implemented in such a way that it's easy to repurpose it. @@ -87,7 +87,7 @@ Specifically: to the risk model and obtains the range of valid up/down price moves per each of the specified triggers. Please note that these can be expressed as either additive offsets or multiplicative factors depending on the risk model used. The reference price is the latest price such that it's at least τ old or the earliest available price should price history be shorter than τ. - It holds the history of volume weighted average prices looking back to the maximum τ configured in the market. - Every time a new price is received from the matching engine the price monitoring engine checks all the [τ, up factor, down factor] triplets relevant for the timestamp, looks-up the associated past (volume weighted) price and sends the signal back to the matching engine informing if the received price would breach the min/max move prescribed by the risk model. -- The bounds corresponding to the current time instant and the arrival price of the next transaction will be used to indicate if the price protection auction should commence, and if so, what should its' period be (see below). +- The bounds corresponding to the current time instant and the arrival price of the next transaction will be used to indicate if the price protection auction should commence. The triggers are sorted by period τ (increasing order) and probability level (decreasing order). As soon the first bound is violated the price monitoring auction with the extension period defined for the corresponding trigger is initiated. After that period elapses the indicative uncrossing price is checked against the remaining bounds and auction gets extended if another bound is violated. The process is repeated until the uncrossing price satisfies all the remaining bounds or there are no active triggers left. - To give an example, with 3 triggers the price protection auction can be calculated as follows: - \>=1% move in 10 min window -> 5 min auction, - \>=2% move in 30 min window -> 15 min auction (i.e. if after 5 min this trigger condition is satisfied by the price we'd uncross at, extend auction by 10 minutes), @@ -109,23 +109,23 @@ to the risk model and obtains the range of valid up/down price moves per each of ## Acceptance Criteria -- Market's price monitoring parameters and triggers (horizon, confidence level, auction extension triplet) can be queried via APIs. (0032-PRIM-001) -- Persistent order results in an auction (2 out of 2 triggers breached), no orders placed during auction, auction terminates with a trade from order that originally triggered the auction. (0032-PRIM-002) +- Market's price monitoring parameters and triggers (horizon, confidence level, auction extension triplet) can be queried via APIs. (0032-PRIM-001). - Non-persistent order does not result in an auction (1 out of 2 triggers breached), order gets cancelled (never makes it to the order book) -(0032-PRIM-003) -- Persistent order results in an auction (2 out of 2 triggers breached), more orders placed during auction, auction concludes after the combined `auctionExtension` time from both triggers elapses. (0032-PRIM-004) -- The market continues in regular fashion once price protection auction period ends and price monitoring bounds get reset based on last traded price (which may come from the auction itself if it resulted in trades) (0032-PRIM-005) -- Persistent order results in an auction (one trigger breached), no orders placed during auction, auction terminates with a trade from order that originally triggered the auction. (0032-PRIM-006) -- A maximum of `5` price monitoring triggers can be added per market (0032-PRIM-007) -- Persistent order results in an auction (1 out of 2 triggers breached), orders placed during auction result in trade with indicative price outside the price monitoring bounds of the 2nd trigger, hence auction get extended (by extension period specified for the 2nd trigger), additional orders resulting in more trades (indicative price still outside the 2nd trigger bounds) placed, auction concludes. (0032-PRIM-008) -- If the cumulative extensions period of various chained auctions is more than the "time horizon" in a given triplet then there is no relevant reference price and this triplet is ignored. (0032-PRIM-009) -- Change of `market.monitor.price.defaultParameters` will change the default market parameters used in price monitoring when a new market is proposed and market parameters don't get explicitly specified. (0032-PRIM-010) -- When market is in its default trading mode, change of `priceMonitoringParameters` results in price monitoring bounds being reset immediately. (0032-PRIM-011) -- When market is in its default trading mode, change of a risk model or any of its parameters results in price monitoring bounds being reset immediately. (0032-PRIM-012) -- When market is in price monitoring auction, change of `priceMonitoringParameters` doesn't affect the previously calculated auction end time, any remaining price monitoring bounds cannot extend the auction further. Upon uncrossing price monitoring bounds get reset using the updated parameter values. (0032-PRIM-013) -- When market is in price monitoring auction, change of a risk model or any of its parameters doesn't affect the previously calculated auction end time, any remaining price monitoring bounds cannot extend the auction further. Upon uncrossing price monitoring bounds get reset using the updated parameter values. (0032-PRIM-014) -- Specifying a non-positive horizon results in an error. (0032-PRIM-015) -- Specifying a probability outside the range (0.9,1) results in an error. (0032-PRIM-016) +(0032-PRIM-003). +- The market continues in regular fashion once price protection auction period ends and price monitoring bounds get reset based on last traded price (which may come from the auction itself if it resulted in trades) (0032-PRIM-005). +- Persistent order results in an auction (one trigger breached), no orders placed during auction, auction terminates with a trade from order that originally triggered the auction. (0032-PRIM-006). +- A maximum of `5` price monitoring triggers can be added per market (0032-PRIM-007). +- Persistent order results in an auction (1 out of 2 triggers breached), orders placed during auction result in trade with indicative price outside the price monitoring bounds of the 2nd trigger, hence auction get extended (by extension period specified for the 2nd trigger), additional orders resulting in more trades (indicative price still outside the 2nd trigger bounds) placed, auction concludes. (0032-PRIM-008). +- If the cumulative extensions period of various chained auctions is more than the "time horizon" in a given triplet then there is no relevant reference price and this triplet is ignored. (0032-PRIM-009). +- Change of `market.monitor.price.defaultParameters` will change the default market parameters used in price monitoring when a new market is proposed and market parameters don't get explicitly specified. (0032-PRIM-010). +- When market is in its default trading mode, change of `priceMonitoringParameters` results in price monitoring bounds being reset immediately. (0032-PRIM-011). +- When market is in its default trading mode, change of a risk model or any of its parameters results in price monitoring bounds being reset immediately. (0032-PRIM-012). +- When market is in price monitoring auction, change of `priceMonitoringParameters` doesn't affect the previously calculated auction end time, any remaining price monitoring bounds cannot extend the auction further. Upon uncrossing price monitoring bounds get reset using the updated parameter values. (0032-PRIM-013). +- When market is in price monitoring auction, change of a risk model or any of its parameters doesn't affect the previously calculated auction end time, any remaining price monitoring bounds cannot extend the auction further. Upon uncrossing price monitoring bounds get reset using the updated parameter values. (0032-PRIM-014). +- Specifying a non-positive horizon results in an error. (0032-PRIM-015). +- Specifying a probability outside the range (0.9,1) results in an error. (0032-PRIM-016). - Specifying a non-positive auction extension results in an error. (0032-PRIM-017) - Settlement price outside the current price monitoring bounds does not trigger an auction (0032-PRIM-018) - A network trade (during closeout) with a price outside price monitoring bounds does not trigger an auction. (0032-PRIM-019) +- Persistent order causing trade with the price outwith both bands triggers an auction. Initial auction duration is equal to the extension period of the first trigger. Once the initial period ends the auction gets extended by the extension period of the second trigger. No other orders placed during auction, auction terminates with a trade from order that originally triggered the auction. (0032-PRIM-020). +- Same as above, but more matching orders get placed during the auction extension. The volume of the trades generated by the later orders is larger than that of the original pair which triggered the auction. Hence the auction concludes generating the trades from the later orders. The overall auction duration is equal to the sum of the extension periods of the two triggers. (0032-PRIM-021). diff --git a/protocol/0033-OCAN-cancel_orders.md b/protocol/0033-OCAN-cancel_orders.md index cd137d1a6..7a48caf0b 100644 --- a/protocol/0033-OCAN-cancel_orders.md +++ b/protocol/0033-OCAN-cancel_orders.md @@ -2,12 +2,15 @@ ## Acceptance Criteria -- An order cancelled by `orderID+marketID+partyID` will be removed from the order book and an order update message will be emitted (0033-OCAN-001) -- All orders for a given `partyID` will be removed from a single market if a cancel all party orders per market message is sent (0033-OCAN-002) -- All orders for a given party across all markets will be removed from the vega system when a cancel all orders message is sent (0033-OCAN-003) -- Orders which are not currently on the orderbook but are `parked` due to being in auction should also be affected by cancels. (0033-OCAN-004) -- A cancellation for a party that does not match the party on the order will be rejected (0033-OCAN-005) +- An order cancelled by `orderID+marketID+partyID` will be removed from the order book and an order update message will be emitted (0033-OCAN-001). +- All orders for a given `partyID` will be removed from a single market if a cancel all party orders per market message is sent (0033-OCAN-002). +- All orders for a given party across all markets will be removed from the vega system when a cancel all orders message is sent (0033-OCAN-003). +- Orders which are not currently on the orderbook but are `parked` due to being in auction should also be affected by cancels. (0033-OCAN-004). +- A cancellation for a party that does not match the party on the order will be rejected (0033-OCAN-005). - Margins must be recalculated after a cancel event (0033-OCAN-007) +- An order which is partially traded (has remaining volume), but still active, can be cancelled. (0033-OCAN-008) +- Cancelling an order for a party leaves its other orders on the current market unaffected. (0033-OCAN-009). +- Cancelling all orders on a market for a party by the "cancel all party orders per market message" leaves orders on other markets unaffected. (0033-OCAN-010). ## Summary diff --git a/protocol/0034-PROB-prob_weighted_liquidity_measure.ipynb b/protocol/0034-PROB-prob_weighted_liquidity_measure.ipynb index ff4ce6c2f..48d77670c 100644 --- a/protocol/0034-PROB-prob_weighted_liquidity_measure.ipynb +++ b/protocol/0034-PROB-prob_weighted_liquidity_measure.ipynb @@ -1,6 +1,7 @@ { "cells": [ { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -14,7 +15,7 @@ "This gives view of liquidity at one instant of time; we then use exponential weighted average over time to obtain the desired measure.\n", "\n", "## Inputs\n", - "- network parameter `market.liquidity.probabilityOfTrading.tau.scaling` which has to be $\\geq 1$.\n", + "- network parameter `market.liquidity.probabilityOfTrading.tau.scaling` which has to be $>0$.\n", "- risk model for the market but with $\\tau$ (`tau`) replaced with `market.liquidity.probabilityOfTrading.tau.scaling x tau`.\n", "- order book volume\n", "\n", @@ -29,6 +30,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -78,6 +80,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -85,25 +88,32 @@ "\n", "Volume implied by the liquidity provision order is that given by [0034-PROB-liquidity_measure.feature](https://github.com/vegaprotocol/vega/blob/develop/integration/features/verified/0034-PROB-liquidity_measure.feature) The feature test has covered following scenarios:\n", "\n", - "1. Order from liquidity provision and from normal order submission are correctly cumulated in order book's total size(0034-PROB-001);\n", + "1. Orders are correctly cumulated in order book's total size(0034-PROB-002);\n", "\n", "2. Probability of trading decreases away from the mid-price (0034-PROB-005).\n", "\n", - "3. Change of `market.liquidity.probabilityOfTrading.tau.scaling` will immediately change the scaling parameter for $\\tau$, hence will change the probability of trading in LP orders. (0034-PROB-006).\n", + "3. Change of `market.liquidity.probabilityOfTrading.tau.scaling` will immediately change the scaling parameter, hence will change the probability of trading used for comparing quality of committed liquidity. (0034-PROB-004).\n", "\n", "\n", "\n" ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [] } ], "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, "language_info": { - "name": "python" + "name": "python", + "version": "3.8.15" }, "orig_nbformat": 4 }, diff --git a/protocol/0035-LIQM-liquidity_monitoring.md b/protocol/0035-LIQM-liquidity_monitoring.md deleted file mode 100644 index 8a7d58986..000000000 --- a/protocol/0035-LIQM-liquidity_monitoring.md +++ /dev/null @@ -1,86 +0,0 @@ -# Liquidity monitoring - -## Summary - -Liquidity in the market is not only a desirable feature from a trader's point of view, but also an important consideration from the risk-management standpoint. Position of a distressed trader can only be liquidated if there's enough volume on the order book to offload it, otherwise a potentially insolvent party remains part of the market. - -Similarly to [price monitoring](./0032-PRIM-price_monitoring.md), we need to be able to detect when the market liquidity drops below the safe level, launch a "liquidity seeking" auction (in which, due to the [liquidity mechanics](./0044-LIME-lp_mechanics.md), there is an incentive through the ability to set fees, to provide the missing liquidity) and terminate it when the market liquidity level is back at a sufficiently high level. - -Note that as long as all pegs that LP batch orders can peg to exists on the book there is one-to-one correspondence between the total stake committed by liquidity providers (LPs), see [LP mechanics](./0044-LIME-lp_mechanics.md) spec, and the total supplied liquidity. -Indeed - -`lp_liquidity_obligation_in_ccy_siskas = stake_to_ccy_siskas ⨉ stake`. - -Thus it is sufficient to compare `target_stake` with `total_stake` while also ensuring that `best_bid` and `best_offer` are present on the book (*). -Note that [target stake](./0041-TSTK-target_stake.md) is defined in a separate spec. - -(*) Having `best_bid` and `best_offer` implies that `mid` also exists. If, in the future, [LP batch orders](./0038-OLIQ-liquidity_provision_order_type.md) are updated to allow other pegs then the protocol must enforce that they are also on the book. - -## Liquidity auction parameters - -**c1** - constant multiple for [target stake](./0041-TSTK-target_stake.md) triggering the commencement of liquidity auction. In this spec it is referred to as `c_1` but in fact it `triggering_ratio` in `LiquidityMonitoringParameters` in market creation or update proposal. - -## Total stake - -`total_stake` is the sum the stake amounts committed by all the LPs in the market (see [LP mechanics](./0044-LIME-lp_mechanics.md)) for how LPs commit stake and what it obliges them to do. - -## Trigger for entering an auction - -The auction is triggered when - -`total_stake < c_1 x target_stake OR there is no best_bid OR there is no best offer`. - -Here 0 < c1 < 1, to reduce the chance of another auction getting triggered soon after e.g. c1 = 0.7. The parameter c1 is a network parameter. - -### Increasing target stake - -If an incoming order would match orders on the book resulting in trades increasing `target_stake` so that liquidity auction gets triggered then: - -- if the incoming order would stay on the book in auction mode the auction should get triggered preemptively (the order doesn't get matched in market's current trading mode, market switches to auction mode and the incoming order gets added to the book once market is in auction mode). - -### Decreasing supplied stake - -If the [liquidity provision transaction would decrease](./0044-LIME-lp_mechanics.md#liquidity-provider-proposes-to-amend-commitment-amount) `supplied_stake` so that liquidity auction gets triggered then the liquidity provision amendment should be rejected and market should stay in it's current trading mode. - -If the `supplied_stake` decreases as a result of a closeout of an insolvent liquidity provider, then closeout should proceed and market should go into liquidity auction. - -### Removing `best_bid` or `best_offer` - -If an incoming order would get matched so that entire side of the order book gets consumed and `best_bid` or `best_offer` no longer exists, then the order should be allowed to go through and market should go into liquidity auction after it gets matched and the resulting trades get generated. - -## Trigger for exiting the auction - -We exit if - -`total_stake >= target_stake AND there is best_bid AND there is best_offer`. - -During the liquidity monitoring auction new or existing LPs can commit more stake (and hence liquidity) through the special market making order type and enable this by posting enough margin - see the [liquidity provision mechanics](./0044-LIME-lp_mechanics.md) spec for details. These need to be monitored to see if auction mode can be exit. - -## What happens during the auction? - -The auction proceeds as usual. Please see the [auction spec](./0026-AUCT-auctions.md) for details. - -## Frequency of checking for liquidity auction entry conditions - - Through a sequence of actions which occur with the same timestamp the market may be moved into a state in which a liquidity auction is expected and then back out of said state. Ideally, liquidity auctions should only be entered when the market truly requires one as once entered a minimum auction length (controlled by `market.auction.minimumDuration`) must be observed. Even with a very short a minimum auction length, a market flickering between two states is suboptimal. - - To resolve this, the conditions for entering a liquidity auction should only be checked at the end of each batch of transactions occurring with an identical timestamp (in the current Tendermint implementation this is equivalent to once per block). At the end of each such period the auction conditions should be checked and the market moved into liquidity auction state if the conditions for entering a liquidity auction are satisfied. -The criteria for exiting any auction (liquidity or price monitoring) should be checked only on timestamp change (ie block boundary with Tendermint). This means that a market cannot leave a liquidity auction only to immediately re-enter it at the end of the block. - - A liquidity provider amending LP provision order can reduce their stake (as long as total stake >= target stake) even if doing so would mean that at the end of block the system enters liquidity auction (because e.g. max open interest increased or because another LP was removed due to not meeting margin commitments). -An implication is that within the same time stamp an aggressive order may remove the `best_bid` or `best_ask`. At that point all LP provision volume is removed from the book (and by implication at least one side of the book is empty). If a subsequent limit order re-creates the peg (still with the same timestamp / from the same block) then the LP volume is re-deployed. If no subsequent limit order places a limit order restoring the peg and we reach end of the block we end up in a liquidity auction. - -As mentioned, as a consequence, intrablock, we may end with one side of the book empty which means that a party not meeting margin requirement *cannot* be closed out. We accept this consequence, their margin will be checked again when the mark price changes and either the book is restored (and they can get closed out) or the market is in liquidity auction. - -## Acceptance Criteria - -1. The scenarios in the feature test [0026-AUCT-auction_interaction.feature](https://github.com/vegaprotocol/vega/blob/develop/core/integration/features/verified/0026-AUCT-auction_interaction.feature) are verified and pass. (0035-LIQM-001) -1. An incoming order that would consume `best_bid` or `best_offer` gets executed (unless it will also trigger price monitoring auction at the same time), the trades are generated. The volume implied by LP provision is removed (from both sides of the book and for all LPs). If `best_bid` is missing but `best_ask` is present then all the "normal pegged orders" (i.e. not the LP ones) which use `best_ask` as peg are still deployed. If `best_ask` is missing but `best_bid` is present then all the "normal pegged orders" (i.e. not the LP ones) which use `best_bid` as peg are still deployed. The market goes into a liquidity auction at the end of a block (because there is a peg missing and the liquidity provision volume is not deployed). (0035-LIQM-002) -1. A market which enters a state requiring liquidity auction at the end of a block through increased open interest remains in open trading between entering that state and the end of the block. (0035-LIQM-003) -1. A market which enters a state requiring liquidity auction at the end of a block through decreased total stake (e.g. through LP bankruptcy) remains in open trading between entering that state and the end of the block. (0035-LIQM-004) -1. A market which enters a state requiring liquidity auction through increased open interest during a block but then leaves state again prior to block completion never enters liquidity auction. (0035-LIQM-005) -1. A market which enters a state requiring liquidity auction through reduced current stake (e.g. through LP bankruptcy) during a block but then leaves state again prior to block completion never enters liquidity auction. (0035-LIQM-006) -1. A liquidity provider cannot remove their liquidity within the block if this would bring the current total stake below the target stake as of that transaction. (0035-LIQM-007) -1. If the Max Open Interest field decreases for a created block to a level such that a liquidity auction which is active at the start of a block can now be exited the block stays in auction within the block but leaves at the end. (0035-LIQM-008) -1. When the market parameter `triggeringRatio` for an existing market is updated via governance, the next time conditions for entering auction are evaluated, the new triggering ratio is applied. (0035-LIQM-010) -1. When proposing a new market if the triggering_ratio is not supplied then the current value of `market.liquidity.targetstake.triggering.ratio` is used in its place. (0035-LIQM-011) diff --git a/protocol/0036-BRIE-event_queue.md b/protocol/0036-BRIE-event_queue.md index dd13f8c2c..ca9c25670 100644 --- a/protocol/0036-BRIE-event_queue.md +++ b/protocol/0036-BRIE-event_queue.md @@ -2,65 +2,46 @@ ## Summary -Latency and throughput is of paramount importance to Vega validator nodes, so monitoring various 3rd party (and notably low transaction speed) blockchains and crypto-assets isn’t possible. -To counter this problem we have created an event queue management system. -This event queue allows the standardisation and propagation of transactions from 3rd party asset chains in the Vega event message format. +The vega network relies solely on other networks for issuing the of coins or tokens that are used for settlement. Because of this, bridges controlled by the Vega network are created on these external chains (as of the first Alpha Mainnet deployment only ERC-20 tokens are supported via a bridge to the Ethereum chain). + +In order for these bridges to operate correctly and for Vega to reflect their activity, each validator node embeds mechanisms to source, validate and monitor activity on these bridges. ## Guide-level explanation -Events and transactions are gathered and processed by the Event Queue from a given 3rd party blockchain, and only the ones subscribed to by Vega nodes will be propagated through consensus and then validated by the validator nodes. -The Event Queue continually scans local or hosted external blockchain nodes to discover on-chain events that are applicable to Vega. -Found external blockchain events are then sent to Vega validator nodes. -This makes the event queue works as a buffer between the slow/complicated world of various blockchains, and the high throughput, low latency of Vega Core. -This message queue will use gRPC to communicate with the Vega network via 3 main functions: +As of the first Alpha Mainnet deployment, four contracts are being monitored by the Vega network: -1. `GetSubscribedEventSources` returns a list of smart contract addresses and events that consensus has deemed as a valid source. -1. `PropagateChainEvent` allows an event queue to send events raised on 3rd party blockchains (deposits, withdrawals, etc) through Vega consensus to ensure an event has been seen by the network. This function must support multiple blockchains as sources of events and multiple sources on a single blockchain (such as multiple deployments of an ERC20 bridge). - - Each validator will individually process and validate the given transaction and process the specified event reported using their local chain node (such as Ethereum). -1. `GetEventAcceptanceStatus` returns the consensus acceptance status of a requested event. The event queue uses this function to determine if it should attempt to send the event again. +- ERC-20 collateral bridge +- staking contract +- vesting contract +- multisig control contract -## Reference-level explanation +Every time a method is being called successfully on these contracts (for example a deposit on the collateral bridge) an event is emitted by the smart contract. The validator nodes will be monitoring all blocks created by Ethereum, and be looking for this event. This process is the sourcing of events. -- The event queue calls `GetSubscribedEventSources` on a Vega node to get the list of subscribed smart contracts -- Using configured external blockchain nodes, the Event Queue filters for specific events provided in `GetSubscribedEventSourcesResponse` -- For each event it calls `GetEventAcceptanceStatus` on a Vega node -- Event Queue then creates an `PropagateChainEventRequest` for each applicable event that has yet to be accepted and submits them to `PropagateChainEvent` on a Vega validator node -- Vega validators each verify each event against local external blockchain nodes as the event is gossiped -- Consensus agrees and writes event into the Vega chain +Once an event has been sourced by a validator, it will be forwarded to the other validators in the network under the form of a vega transaction. -## Pseudo-code / Examples +Upon receipt of the event, and confirmation it was sent by a legitimate validator node, the other validators will then try to find and verify the same event on the external chain. -The protobuf of the service: +Once all validators have confirmed the event happened on the external chain, the action will be executed on the network (in the case of the deposit, the funds will be deposited into an account). -```proto +## Reference-level explanation -service trading{ - /*....*/ - rpc PropagateChainEvent(PropagateChainEventRequest) returns (PropagateChainEventResponse); - rpc GetSubscribedEventSources() returns (GetSubscribedEventSourcesResponse); - rpc GetEventAcceptanceStatus(GetEventAcceptanceStatusRequest) returns (GetEventAcceptanceStatusResponse); - /*....*/ -} +### Smart contracts in use -message GetSubscribedEventSourcesResponse { - repeated string subscribed_event_source = 1; -} +All the smart contracts monitored by the validator nodes are defined in the `ethereumConfig` network parameter. Along with the contract itself, the creation time of the contract is required for the very first launch of the network. This is required in order for the validator nodes to poll all blocks since the creation of the contract. +Later on in the life of the network, this information is stored in the snapshot state / checkpoint with last sourced block on Ethereum to avoid interpreting events twice. -message PropagateChainEventRequest { - // The event - vega.ChainEvent evt = 1; - string pubKey = 2; - bytes signature = 3; -} +Finally, the amount of confirmations expected for Ethereum is specified, this is set to 50 confirmation in mainnet. -// The response for a new event sent to vega -message PropagateChainEventResponse { - // Did the event get accepted by the node successfully - bool success = 1; -} +### Event sourcing + +Every validator node needs a constant connection to an Ethereum archival node. This allows the node to poll for Ethereum blocks as they are constructed, and scan for events emitted by the contracts related to the Vega network. +The core node will look for new blocks on Ethereum every 10 to 15 seconds. Once a relevant event is found, the block, log index and transaction hash are extracted from it. A `ChainEvent` transaction is constructed then forwarded to the rest of the nodes through the Vega chain. +Simplified chain event transaction: + +```proto // An event being forwarded to the vega network // providing information on things happening on other networks message ChainEvent { @@ -69,10 +50,9 @@ message ChainEvent { string txID = 1; oneof event { - BuiltinAssetEvent builtin = 1001; ERC20Event erc20 = 1002; - BTCEvent btc = 1003; ValidatorEvent validator = 1004; + // more in the future } } @@ -125,47 +105,77 @@ message ERC20Withdrawal { // The reference nonce used for the transaction string referenceNonce = 4; } +``` -message GetEventAcceptanceStatusRequest { - string transaction_hash = 1; - uint32 log_index = 2; -} +### Event validation -//can be expanded as needed -enum EventAcceptanceStatus { - EVENT_ACCEPTANCE_STATUS_UNSPECIFIED = 0; - EVENT_ACCEPTANCE_STATUS_ACCEPTED = 1; - EVENT_ACCEPTANCE_STATUS_REJECTED = 2; -} +Once the `ChainEvent` transaction is received by the other validator nodes a routine is started internally to verify the events. + +Specifically the nodes will: + +- Find the event for the contract address, transaction hash, block and event log. +- Ensure this event has not been seen before +- Ensure that the required number of confirmations has been seen on the network -message GetEventAcceptanceStatusResponse { - string transaction_hash = 1; - uint32 log_index = 2; - EventAcceptanceStatus acceptance_status = 3; +As soon as the validator nodes confirm the event, they emit a new transaction in the Vega network to confirm the event is legitimate and it can be processed. + +As soon as the protocol receive 2/3 of the votes for the event, the corresponding action related to this event will be executed (e.g., funding an account in the case of a deposit). The confirmation of each validator is weighted with the validator power. + +Example of the node vote transaction: + +```proto +// Used when a node votes for validating that a given resource exists or is valid, +// for example, an ERC20 deposit is valid and exists on ethereum. +message NodeVote { + // Reference, required field. + string reference = 2; + // type of NodeVote, also required. + Type type = 3; + enum Type { + // Represents an unspecified or missing value from the input + TYPE_UNSPECIFIED = 0; + // A node vote a new stake deposit + TYPE_STAKE_DEPOSITED = 1; + // A node vote for a new stake removed event + TYPE_STAKE_REMOVED = 2; + // A node vote for new collateral deposited + TYPE_FUNDS_DEPOSITED = 3; + // A node vote for a new signer added to the erc20 bridge + TYPE_SIGNER_ADDED = 4; + // A node vote for a signer removed from the erc20 bridge + TYPE_SIGNER_REMOVED = 5; + // A node vote for a bridge stopped event + TYPE_BRIDGE_STOPPED = 6; + // A node vote for a bridge resumed event + TYPE_BRIDGE_RESUMED = 7; + // A node vote for a newly listed asset + TYPE_ASSET_LISTED = 8; + // A node vote for an asset limits update + TYPE_LIMITS_UPDATED = 9; + // A node vote to share the total supply of the staking token + TYPE_STAKE_TOTAL_SUPPLY = 10; + // A node vote to update the threshold of the signer set for the multisig contract + TYPE_SIGNER_THRESHOLD_SET = 11; + // A node vote to validate a new assert governance proposal + TYPE_GOVERNANCE_VALIDATE_ASSET = 12; + } } ``` ## Acceptance Criteria -### Event Queue - -- Event Queue calls `GetSubscribedEventSources` and successfully parses the response (0036-BRIE-001) -- Event Queue connects to a configured local or hosted external blockchain node (0036-BRIE-002) -- Event Queue gathers and filters applicable events from configured external blockchain node (0036-BRIE-003) -- Event Queue calls `GetEventAcceptanceStatus` and marks accepted events internally as complete (0036-BRIE-004) -- Event Queue propagates unaccepted events to Vega node via `PropagateEvent` (0036-BRIE-005) -- Event Queue retries sending events that have gone too long without being accepted (0036-BRIE-006) - -### Vega Nodes - -- Vega nodes respond to `GetSubscribedEventSources` with a list of valid smart contract events (0036-BRIE-007) -- Vega nodes accept events submitted to `PropagateEvent` and verifies them against configured external blockchain node (0036-BRIE-008) -- Vega nodes write events to chain once verified (0036-BRIE-009) -- Vega nodes respond appropriately to `GetEventAcceptanceStatus` (0036-BRIE-010) -- Vega nodes verify Event existence and outcomes using local Ethereum node - - Verify balance changes (0036-BRIE-011) - - Verify account identities (0036-BRIE-012) - - Verify transaction hashes (0036-BRIE-013) -- Vega nodes reject invalid events that fail verification in the previous step (0036-BRIE-014) -- Vega nodes responds appropriately to event triggers - - Users are credited on deposit (see also [0013-ACCT](./0013-ACCT-accounts.md)) (0036-BRIE-015) +- A valid event is processed by vega (0036-BRIE-001) + - A transaction is successfully executed on the bridge (e.g deposit) + - A validator node successfully source the event and emit a chain event transaction on the vega chain + - The others validators successfully validates the event on the ethereum chain and send a node vote on chain + - The required amount of node votes, weighted by validator score is received + - The processing of the event have effect on the network (e.g: for a deposit funds are deposited on an account) +- A valid duplicated event is processed (0036-BRIE-002) + - A transaction is successfully executed on the bridge (e.g deposit) and successfully processed by vega + - A node sends again the chain event after sourcing it + - The nodes reject this event as duplicated, nothing else happens +- A invalid event is processed (0036-BRIE-003) + - A malicious node sends a chain event for a non existing transaction on the bridge + - The node start validating this event on chain, but cannot find it + - After a given delay this chain event is rejected, no node votes are being sent by the validators + - This event has no repercussion on the vega state. diff --git a/protocol/0037-OPEG-pegged_orders.md b/protocol/0037-OPEG-pegged_orders.md index b8968f4ec..dd254901f 100644 --- a/protocol/0037-OPEG-pegged_orders.md +++ b/protocol/0037-OPEG-pegged_orders.md @@ -15,7 +15,7 @@ - If the midprice is calculated to be a fraction (e.g. 102.5), it should be rounded up for a buy and rounded down for a sell. (0037-OPEG-011) - The order version is not updated during a repricing (0037-OPEG-012) - Pegged orders are included in the calculation of the BEST_BID, BEST_ASK and MID prices but excluded from BEST_STATIC_BID, BEST_STATIC_ASK and STATIC_MID (0037-OPEG-013) -- A parked pegged order can be amended. (0037-OPEG-014) +- A parked pegged order can be amended. (0037-OPEG-014). - A pegged order with an offset which would cause it to be priced <= 0 is parked. (0037-OPEG-017) - An active pegged order can be amended. (0037-OPEG-016) - A transaction submitting a pegged order with negative offset fails with an error explaining the cause was negative offset. (0037-OPEG-018) diff --git a/protocol/0038-OLIQ-liquidity_provision_order_type.md b/protocol/0038-OLIQ-liquidity_provision_order_type.md deleted file mode 100644 index f340d1972..000000000 --- a/protocol/0038-OLIQ-liquidity_provision_order_type.md +++ /dev/null @@ -1,217 +0,0 @@ -# Liquidity Provisioning Order Type - -## Summary - -When market makers commit to providing liquidity they are required to submit a set of valid buy shapes and sell shapes [Liquidity Provisioning mechanics](./0044-LIME-lp_mechanics.md). This commitment will ensure that they are eligible for portion of the market fees as set out in [Setting Fees and Rewarding Market Makers](./0042-LIQF-setting_fees_and_rewarding_lps.md). - -## Liquidity Provisioning order features - -LP orders are a special order type with the following features: - -- Is a batch order: allows simultaneously specifying multiple orders in one message/transaction -- Initially all are pegged orders but other price types may be available in future -- Are always priced limit orders that sit on the book -- Are “post only” and do not trade on entry (as per normal pegged orders) -- The order is always refreshed after it trades (once the tx is processed so not refreshed before closeouts, etc.) based on the above requirements so that the full commitment is always supplied. - -## How they are submitted - -2 x shapes are submitted as part of a market making transaction (one for buy side, one for sell side). Each entry in this shape must specify a proportion of the liquidity obligation applicable to that entry and a price peg. - -The network will translate these shapes into order book volume by creating an order set according to a set of rules (see below). - -Each entry must specify: - -1. **Liquidity proportion:** the relative proportion of the commitment to be allocated at a price level. Note, the network will normalise the liquidity proportions of the refined order set (see below). This must be a strictly positive number. - -2. A **price peg:** , as per normal [pegged orders](../protocol/0037-OPEG-pegged_orders.md), a price level specified by a reference point (e.g mid, best bid, best offer) and an amount of units away. The amount is always positive and is subtracted for buy orders and added for sell orders to the reference price. - -```proto -# Example 1: -Buy-shape: { - buy-entry-1: [buy-liquidity-proportion-1, [buy-price-peg-reference-1, buy-number-of-units-from-reference-1]], - buy-entry-2: [buy-liquidity-proportion-2, [buy-price-peg-reference-2, buy-number-of-units-from-reference-2]], -} -Sell-shape: { - sell-entry-1: [sell-liquidity-proportion-1, [sell-price-peg-reference-1, sell-number-of-units-from-reference-1]], - sell-entry-2: [sell-liquidity-proportion-2, [sell-price-peg-reference-2, sell-number-of-units-from-reference-2]], -} - -# Example 1 with values -Buy-shape: { - buy-entry-1: [2, [best-bid, 10]], - buy-entry-2: [13, [best-bid, 11]], -} -Sell-shape: { - sell-entry-1: [5, [best-ask, 8]], - sell-entry-2: [5, [best-ask, 9]], -} - -``` - -## How they are constructed for the order book - -Input data: - -1. The commitment, buy-shape, sell-shape (as submitted in the [liquidity provision network transaction](./0038-OLIQ-liquidity_provision_order_type.md).) -1. Any persistent orders that the liquidity provider has on the book at a point in time. - -### Refining list of orders - -Steps: - -1. From the market parameter - to be set as part of [market proposal](0028-GOVE-governance.md) `market.liquidity.priceRange` which is a percentage price move (e.g. `0.05 = 5%` and from `mid_price` calculate: - -`min_lp_price = (1.0 - market.liquidity.priceRange) x mid_price` - -and - -`max_lp_price = (1.0 + market.liquidity.priceRange) x mid_price` - -1. Calculate `liquidity_obligation`, as per calculation in the [market making mechanics spec](./0044-LIME-lp_mechanics.md). - -1. The `liquidity_obligation` calculated as per [LP mechanics](0044-LIME-lp_mechanics.md). Some of it may be fulfilled by persistent orders the liquidity provider has on the book at this point in time that are between and including `min_lp_vol_price` and `max_lp_vol_price`. -The contribution is `volume x price`; sum up the contribution across the relevant side of the book and subtract it from `liquidity_obligation` obtained above. -If you end up with 0 or a negative number, stop, you are done. - -1. Using the adjusted `liquidity_obligation`, calculate the `liquidity-normalised-proportion` for each of the remaining entries in the buy / sell shape (for clarity, this does not include any other persistent orders that the market maker has). - -1. Calculate the volume implied by each entry in the refined buy/sell order list. You will now create orders from this volume at the relevant price point and apply them to the order book. - -#### Normalising liquidity proportions for a set of market making orders (step 3) - -Calculate the `liquidity-normalised-proportion` for all entries, where for buy and sell side separately: - -`liquidity-normalised-proportion = liquidity-proportion-for-entry / sum-all-entries(liquidity-proportion-for-order)` - -```math -Example 1 (from above) where refined-order-list = [buy-entry-1, buy-entry-2, sell-entry-1, sell-entry-2]: - -liquidity-normalised-proportion-buy-order-1 = 2 / (2 + 13) = 0.13333... -liquidity-normalised-proportion-buy-order-2 = 13 / (2 + 13) = 0.86666... -liquidity-normalised-proportion-sell-order-1 = 5 / (5 + 5) = 0.5 -liquidity-normalised-proportion-sell-order-2 = 5 / (5 + 5) = 0.5 - -``` - -The sum of all normalised proportions must = 1 on each side. - -#### Calculating volumes for a set of market making orders (step 6) - -Any shape entry with a peg less than `min_lp_vol_price` should have the resulting volume implied at `min_lp_vol_price` (instead of what level the peg would be) while any shape entry with peg greater than `max_lp_vol_price` should have the resulting volume implied at `max_lp_vol_price`. - -Calculate the volume at the peg as - -`volume = ceiling(liquidity_obligation x liquidity-normalised-proportion / price)`. - -where `liquidity_obligation` is calculated as defined in the [market making mechanics spec](./0044-LIME-lp_mechanics.md) and `price` is the price level at which the `volume` will be placed. -At this point `volume` may have decimal places. - -Note: if the resulting quote price of any of the entries in the buy / sell shape leads to negative product value from the [product quote-to-value function](0051-PROD-product.md#quote-to-value-function) but strictly positive volume then the entire LP order for this LP is undeployed, their stake won't count towards target stake being met and they shall not receive any LP fees regardless of their equity-like share. This can lead to a [liquidity auction](0035-LIQM-liquidity_monitoring.md) if the supplied stake for the market is below the required level due to this LP. - -Note: calculating the order volumes needs take into account Position Decimal Places and create values (which may be int64s or similar) that are the correct size and precision given the number of Position Decimal Places specified in the [Market Framework](./0001-MKTF-market_framework.md). -This means that the `integerVolume = ceil(volume x 10^(PDP))`. -For example, if the offset, commitment and prob of trading imply volume of say `0.65` then the `integerVolume` we want to see on the book depends on position decimals. If we have: - -- `0dp` then round up to volume `1` -- `1dp` then round up to volume `7` (i.e. `0.7` i.e. `1dp`). -- `3dp` then no need for any rounding it's `650` (i.e. `0.650`) - -and so on. - -```proto -Example: - -best-static-bid-on-order-book = 103 - -shape-entry = { - peg: {reference: 'best-bid', units-from-ref: 2}, - liquidity-normalised-proportion-order: 0.32 -} - -peg-implied-price = 103 - 2 = 101 - -Call probability-of-trading function with best-static-bid-on-order-book = 105, time-horizon given by risk model tau multiplied by `market.liquidity.probabilityOfTrading.tau.scaling`, peg-implied-price. - -This will return probably of trading at price = 101. This can be used in the formula for volume, above. - -``` - -## Refreshing of orders / recalculating order volume - -Liquidity provider orders are recalculated and refreshed whenever an order that is part of the commitment has changed, including if a market maker's order(s) have traded (both persistent orders and shape implied orders), orders are amended, cancelled, or expired. - -In these cases, repeat all steps above, preserving the order as an order, but recalculating the volume and price of it. Note, this should only happen at the end of a transaction (that caused the trade), not immediately following the trade itself. - -### Time priority for refreshing - -1. For all orders that are repriced but not as a result of trading (i.e. pegged orders that move as a result of peg moving), treat as per normal pegged orders. - -1. The system should refresh the liquidity provider's pegged orders, in time priority according to which traded first (see below example). - -________________________ -**Example**: we have a buy side of an order book that looks like this: - -```proto -{ - [mm-1-order, buy-volume=3, buy-price=100, order-time=13007] - [mm-2-order, buy-volume=5, buy-price=99, order-time=13004] -} -``` - -and a new market order sells 8. Then, a plausible refreshed set of orders could look like this*: - -```proto -add this first - [mm-1-order, buy-volume=3, buy-price=97, order-time=16458] - -and then this - [mm-2-order, buy-volume=5, buy-price=96, order-time=16459] -``` - -*Note: the actual values of the buy-prices and buy-volumes are dependent on the result of step 2 above and this example is not to test that, so don't try to replicate this with numbers, it's for illustrative purposes only. -________________________ - -### Transfers in / out of margin account - -When the system refreshes orders (because a peg moved) and the implied volumes now sit at different price levels there may be different overall margin requirement for the LP party. -If the resulting amount is outside search / release then there will be *at most* one transfer in / out of the party's margin account for the entire LP order. - -## Amending the LP order - -Liquidity providers are always allowed to amend their shape generated orders by submitting a new liquidity provider order with a set of revised order shapes (see [Liquidity Provisioning mechanics](./0044-LIME-lp_mechanics.md)). They are not able to amend orders using "normal" amend orders. - -No cancellation of orders that arise from this LP batch order type other than by lowering commitment as per [[Liquidity Provisioning mechanics spec](./0044-LIME-lp_mechanics.md). - -Note that any other orders that the LP has on the book (limit orders, other pegged orders) that are *not* part of this LP batch order (call them "normal" in this paragraph) can be cancelled and amended as normal. When volume is removed / added / pegs moved (on "normal" orders) then as part of the normal peg updates the LP batch order may add or remove volume as described in section "How they are constructed for the order book" above. - -## Network Parameters - -- `market.liquidity.probabilityOfTrading.tau.scaling`: scaling factor multiplying risk model value of tau to imply probability of trading. -- `market.liquidity.minimum.probabilityOfTrading.lpOrders`: a minimum probability of trading; any shape proportions at pegs that would have smaller probability of trading are to be moved to pegs that imply price that have probability of trading no less than the `market.liquidity.minimum.probabilityOfTrading.lpOrders`. - -## APIs - -- Order datatype for LP orders. Any order APIs should contain these orders. - -## Acceptance Criteria - -- Volume implied by the liquidity provision order is that given by [0034-PROB-liquidity_measure.feature](https://github.com/vegaprotocol/vega/blob/develop/core/integration/features/verified/0034-PROB-liquidity_measure.feature) in all the various scenarios there. (0038-OLIQ-001); -- Volume implied by the liquidity provision order is that given by [0034-PROB-liquidity_measure.feature](https://github.com/vegaprotocol/vega/blob/develop/core/integration/features/verified/0034-PROB-liquidity_measure.feature) in all the various scenarios that test fractional order sizes (smallest order position of 0.01). (0038-OLIQ-002); -- If an LP order has offset set such that the resulting price falls outside `[min_lp_vol_price, max_lp_vol_price]` then the system adjusts it automatically so that it's placed on the bound (0038-OLIQ-011) - -### LP commitment order creation - -- A liquidity provisioning order must specify orders for both sides of the book (0038-OLIQ-003) -- All orders created by an LP commitment must be pegged orders (0038-OLIQ-004) -- Filled orders are replaced immediately to conform to the LP commitment shapes (0038-OLIQ-005) -- Change of the market parameter `market.liquidity.priceRange` which decreases the value will, when volumes are next recalculated, tighten `[min_lp_vol_price, max_lp_vol_price]` and volume that was previously pegged inside the valid range and would now be outside is shifted to the bounds. (0038-OLIQ-012) -- Change of the market parameter `market.liquidity.priceRange` which increases the value will, when volumes are next recalculated, widen `[min_lp_vol_price, max_lp_vol_price]` and volume that was previously being shifted to stay inside the range is now deployed at the desired peg. (0038-OLIQ-013) - -### LP commitment amendment - -- If amending a commitment size would reduce the market's supplied liquidity below the target stake, the amendment will be rejected (see [0035 Liquidity Monitoring](./0035-LIQM-liquidity_monitoring.md#decreasing-supplied-stake)) (0038-OLIQ-006) - -### LP commitment repricing due to peg price moves - -- If best bid / ask has changed and the LP order volume is moved around to match the shape / new peg levels then the margin requirement for the party may change. There is at most one transfer in / out of the margin account of the LP party as a result of one of the pegs moving. (0038-OLIQ-008) diff --git a/protocol/0039-MKTD-market_depth_calculation.md b/protocol/0039-MKTD-market_depth_calculation.md index 9271ae5ca..6af94f276 100644 --- a/protocol/0039-MKTD-market_depth_calculation.md +++ b/protocol/0039-MKTD-market_depth_calculation.md @@ -2,23 +2,21 @@ ## Acceptance Criteria -- The market depth builder must be able to handle all available order types (0039-MKTD-001) -- Entering and leaving auctions must be handled correctly (0039-MKTD-003) -- All subscribed clients must receive all the data necessary to build their own view of the market depth (0039-MKTD-004) -- Adding a new limit order to the book updates the market depth at the corresponding price and volume (0039-MKTD-005) -- Cancelling an existing order reduces the volume in the market depth view and removes the price level if the volume reaches zero (0039-MKTD-006) -- Fully or partially filling an order will reduce the market depth volume at that given price level (0039-MKTD-007) -- A GTT order that expires will cause the volume at its price to be reduced in the market depth view (0039-MKTD-008) -- Amending an order in place (price stays the same but the volume is reduced) will cause the volume at the given price to be reduced in the market depth view (0039-MKTD-009) -- Amending an order such that a cancel replace is performed will cause the volume in the market depth to be updated correctly (0039-MKTD-010) -- Entering an auction will cause any GFN orders to be removed from the market depth volume view (0039-MKTD-012) -- Market depth will show a crossed book if the market is in auction and the book is crossed (0039-MKTD-013) -- Leaving an auction will cause any GFA orders to be removed from the market depth view (0039-MKTD-014) +- The market depth builder must be able to handle all available order types (0039-MKTD-001). +- Entering and leaving auctions must be handled correctly (0039-MKTD-003). +- All subscribed clients must receive all the data necessary to build their own view of the market depth (0039-MKTD-004). +- Adding a new limit order to the book updates the market depth at the corresponding price and volume (0039-MKTD-005). +- Cancelling an existing order reduces the volume in the market depth view and removes the price level if the volume reaches zero (0039-MKTD-006). +- Fully or partially filling an order will reduce the market depth volume at that given price level (0039-MKTD-007). +- A GTT order that expires will cause the volume at its price to be reduced in the market depth view (0039-MKTD-008). +- Amending an order in place (price stays the same but the volume is reduced) will cause the volume at the given price to be reduced in the market depth view (0039-MKTD-009). +- Amending an order such that a cancel replace is performed will cause the volume in the market depth to be updated correctly (0039-MKTD-010). +- Entering an auction will cause any GFN orders to be removed from the market depth volume view (0039-MKTD-012). +- Market depth will show a crossed book if the market is in auction and the book is crossed (0039-MKTD-013). +- Leaving an auction will cause any GFA orders to be removed from the market depth view (0039-MKTD-014). - Pegged orders are part of the market depth view and should update the view when their orders are repriced (0039-MKTD-015) -- Liquidity orders are part of the market depth view and should update the view when commitment sizes change (0039-MKTD-016) -- Liquidity orders are part of the market depth view and should update the view when their reference prices move (0039-MKTD-017) -- Each delta update will have the new sequence number along with the previous sequence number which will match the previous delta update (0039-MKTD-018) -- The sequence number received as part of the market depth snapshot will match the sequence number of a delta update (0039-MKTD-019) +- Each delta update will have the new sequence number along with the previous sequence number which will match the previous delta update (0039-MKTD-018). +- The sequence number received as part of the market depth snapshot will match the sequence number of a delta update (0039-MKTD-019). ## Summary diff --git a/protocol/0040-ASSF-asset_framework.md b/protocol/0040-ASSF-asset_framework.md index ed2a82d75..0beb4bbaa 100644 --- a/protocol/0040-ASSF-asset_framework.md +++ b/protocol/0040-ASSF-asset_framework.md @@ -294,10 +294,9 @@ Once a withdrawal is complete and the appropriate events/transaction information For each asset class to be considered "supported" by Vega, the following must happen: 1. An asset of that class can Be voted into Vega (0040-ASSF-001) -2. An asset previously voted in can be voted out of Vega (0040-COSMICELEVATOR-002) -3. A voted-in asset can be deposited into a Vega bridge (0040-ASSF-003) -4. A properly deposited asset is credited to the appropriate user (0040-ASSF-004) -5. A withdrawal can be requested and verified by Vega validator nodes (0040-ASSF-005) -6. multisig withdrawal order signatures from Vega validator nodes can be aggregated at the request of the user (0040-ASSF-006) -7. A user can submit the withdrawal order and receive their asset (0040-ASSF-007) -8. Every asset must specify `quantum` and this must be an integer strictly greater than `0` (0040-ASSF-008) +1. A voted-in asset can be deposited into a Vega bridge (0040-ASSF-003) +1. A properly deposited asset is credited to the appropriate user (0040-ASSF-004) +1. A withdrawal can be requested and verified by Vega validator nodes (0040-ASSF-005) +1. multisig withdrawal order signatures from Vega validator nodes can be aggregated at the request of the user (0040-ASSF-006) +1. A user can submit the withdrawal order and receive their asset (0040-ASSF-007) +1. Every asset must specify `quantum` and this must be an integer strictly greater than `0` (0040-ASSF-008) diff --git a/protocol/0041-TSTK-target_stake.md b/protocol/0041-TSTK-target_stake.md index a7d77df0c..9b471de7f 100644 --- a/protocol/0041-TSTK-target_stake.md +++ b/protocol/0041-TSTK-target_stake.md @@ -1,27 +1,25 @@ # Target stake +## Target stake for derivatives markets (cash settled futures / perpetuals...) + This spec outlines how to measure how much stake we want committed to a market relative to what is happening on the market (currently open interest). The target stake is a calculated quantity, utilised by various mechanisms in the protocol: -- If the LPs total committed stake is less than c_1 x `target_stake` we trigger liquidity auction. See [Liquidity Monitoring](./0035-LIQM-liquidity_monitoring.md). Note that there is a one-to-one correspondence between the amount of stake LPs committed and the supplied liquidity. -The parameter c_1 is a market parameter (with network parameter `market.liquidity.targetstake.triggering.ratio` providing a default value) defined in the [liquidity Monitoring](./0035-LIQM-liquidity_monitoring.md) spec. -- It is used to set the fee factor for the LPs: see [Setting fees and rewarding LPs](./0042-LIQF-setting_fees_and_rewarding_lps.md). - -## Definitions / Parameters used +### Definitions / Parameters used - **Open interest**: the volume of all open positions in a given market. -- `market.stake.target.timeWindow` is a market parameter defining the length of window over which we measure open interest (see below). This should be measured in seconds and a typical value is one week i.e. `7 x 24 x 3600` seconds. -- `market.stake.target.scalingFactor` is a market parameter defining scaling between liquidity demand estimate based on open interest and target stake. +- `market.stake.target.timeWindow` is a network parameter providing the default length of window over which we measure open interest (see below). This should be measured in seconds and a typical value is one week i.e. `7 x 24 x 3600` seconds. A market proposal / update can override this by setting `timeWindow` in `liquidityMonitoringParameters.targetStakeParameters`. +- `market.stake.target.scalingFactor` is a network parameter providing the default scaling between liquidity demand estimate based on open interest and target stake. - `risk_factor_short`, `risk_factor_long` are the market risk factors, see [the Quant Risk Models spec](./0018-RSKM-quant_risk_models.ipynb). - `mark_price`, see [mark price](./0009-MRKP-mark_price.md) spec. - `indicative_uncrossing_price`, see [auction](./0026-AUCT-auctions.md) spec. -### Current definitions +#### Current definitions First, `max_oi` is defined maximum (open interest) measured over a time window, -`t_window = [max(t-market.stake.target.timeWindow,t0),t]`. Here `t` is current time with `t0` being the end of market opening auction. Note that `max_oi` should be calculated recorded per transaction, so if there are multiple OI changes withing the same block (which implies the same timestamp), we should pick the max one, NOT the last one that was processed. +`t_window = [max(t-market.liquidityMonitoringParameters.targetStakeParameters.timeWindow,t0),t]`. Here `t` is current time with `t0` being the end of market opening auction. Note that `max_oi` should be calculated recorded per transaction, so if there are multiple OI changes withing the same block (which implies the same timestamp), we should pick the max one, NOT the last one that was processed. -If the market is in auction mode the `max_oi` can only increase while `auction duration` <= `market.stake.target.timeWindow`. Once the market's been in the auction for more than `market.stake.target.timeWindow` the `max_oi` is whatever the current positions and `indicative_uncrossing_volume` imply - specifically, this allows the `target_stake` to drop as a result of trades generated in the auction so that `target_stake` > `supplied_stake` (even in absence of changes to `supplied_stake`) and the market can go back to its default trading mode. +If the market is in auction mode the `max_oi` can only increase while `auction duration` <= `market.stake.target.timeWindow`. Once the market's been in the auction for more than `market.liquidityMonitoringParameters.targetStakeParameters.timeWindow` the `max_oi` is whatever the current positions and `indicative_uncrossing_volume` imply - specifically, this allows the `target_stake` to drop as a result of trades generated in the auction so that `target_stake` > `supplied_stake` (even in absence of changes to `supplied_stake`) and the market can go back to its default trading mode. Example 1: `t_market.stake.target.timeWindow = 1 hour` @@ -45,23 +43,28 @@ Example 2: As above but the market opened at `t_0 = 4:15`. Then `t_window = [4:1 From `max_oi` we calculate -`target_stake = reference_price x max_oi x market.stake.target.scalingFactor x rf`, +`target_stake = reference_price x max_oi x market.liquidityMonitoringParameters.targetStakeParameters.scalingFactor x rf`, where `reference_price` is `mark_price` when market is in continuous trading mode and `indicative_uncrossing_price` during auctions (if it's available, otherwise use `mark_price` which may be 0 in case of an opening auction), and `rf = max(risk_factor_short, risk_factor_long)`. Note that currently we always have that `risk_factor_short >= risk_factor_long` but this could change once we go beyond futures... so safer to take a `max`. Note that the units of `target_stake` are the settlement currency of the market as those are the units of the `reference_price`. Example 3: if `market.stake.target.scalingFactor = 10`, `rf = 0.004` and `max_oi = 120` then `target_stake = 4.8`. -### APIs +#### APIs - target stake - return current (real-time) target stake when market is in default trading mode. - return theoretical (based on indicative uncrossing volume) target stake when market is in auction mode. -### Acceptance Criteria +#### Acceptance Criteria - examples showing a growing list (before we hit time window) (0041-TSTK-001) - examples showing a list that drops off values (0041-TSTK-002) - if open interest changes to a value that is less then or equal to the maximum open interest over the time window and if the mark price is unchanged, then the liquidity demand doesn't change. (0041-TSTK-003) - Change of `market.stake.target.scalingFactor` will immediately change the scaling between liquidity demand estimate based on open interest and target stake, hence immediately change the target stake. (0041-TSTK-004) - Change of `market.stake.target.timeWindow` will immediately change the length of time window over which open interest is measured, hence will immediately change the value of `max_oi`. (0041-TSTK-005) + + +### Acceptance criteria + +Too be decided. diff --git a/protocol/0042-LIQF-setting_fees_and_rewarding_lps.md b/protocol/0042-LIQF-setting_fees_and_rewarding_lps.md index 4a7d927fb..9e1ec4bb6 100644 --- a/protocol/0042-LIQF-setting_fees_and_rewarding_lps.md +++ b/protocol/0042-LIQF-setting_fees_and_rewarding_lps.md @@ -2,15 +2,19 @@ ## Summary -The aim of this specification is to set out how fees on Vega are set based on committed liquidity provider stake and prevailing open interest on the market leading to [target stake](../protocol/0041-TSTK-target_stake.md). Let us recall that liquidity providers can commit and withdraw stake by submitting / amending a special liquidity provider pegged order type [liquidity provider order spec](./0038-OLIQ-liquidity_provision_order_type.md). +The aim of this specification is to set out how fees on Vega are set based on committed liquidity provider stake and prevailing open interest on the market leading to [target stake](../protocol/0041-TSTK-target_stake.md). Let us recall that liquidity providers can commit and withdraw stake by submitting / amending a special [liquidity commitment transaction](./0044-LIME-lp_mechanics.md). ## Definitions / Glossary of terms used -- **Market value proxy window length `market.value.windowLength`**: sets the length of the window over which we estimate the market value. This is a network parameter. +- **`market.value.windowLength`**: sets the length of the window over which we estimate the market growth. This is a network parameter. - **Target stake**: as defined in [target stake spec](./0041-TSTK-target_stake.md). The ideal amount of stake LPs would commit to a market. - `market.liquidityProvision.minLpStakeQuantumMultiple`: There is a network wide parameter specifying the minimum LP stake as the `quantum` specified per asset, see [asset framework spec](../protocol/0040-ASSF-asset_framework.md). -## CALCULATING LIQUIDITY FEE FACTOR +## Calculating the Liquidity Fee Factor + +There are three methods for setting the liquidity fee factor, with the default method being the 'Marginal Cost method.' The liquidity fee setting mechanism is configured per market as part of the market proposal. + +### Marginal Cost method The [liquidity fee factor](./0029-FEES-fees.md) is an input to the total taker fee that a price taker of a trade pays: @@ -18,10 +22,10 @@ The [liquidity fee factor](./0029-FEES-fees.md) is an input to the total taker f `liquidity_fee = fee_factor[liquidity] x trade_value_for_fee_purposes` -As part of the [commit liquidity network transaction](./0038-OLIQ-liquidity_provision_order_type.md), the liquidity provider submits their desired level for the [liquidity fee factor](./0042-LIQF-setting_fees_and_rewarding_lps.md) for the market. Here we describe how this fee factor is set from the values submitted by all liquidity providers for a given market. +As part of the [commit liquidity network transaction](./0044-LIME-lp_mechanics.md#commit-liquidity-network-transaction), the liquidity provider submits their desired level for the [liquidity fee factor](./0042-LIQF-setting_fees_and_rewarding_lps.md) for the market. Here we describe how this fee factor is set from the values submitted by all liquidity providers for a given market. First, we produce a list of pairs which capture committed liquidity of each LP together with their desired liquidity fee factor and arrange this list in an increasing order by fee amount. Thus we have -```math +```text [LP-1-stake, LP-1-liquidity-fee-factor] [LP-2-stake, LP-2-liquidity-fee-factor] ... @@ -34,11 +38,11 @@ We now find the smallest integer `k` such that `[target stake] < sum from i=1 to Finally, we set the liquidity-fee-factor for this market to be the fee `LP-k-liquidity-fee-factor`. -### Example for fee setting mechanism +#### Example for fee setting mechanism using the marginal cost method In the example below there are 3 liquidity providers all bidding for their chosen fee level. The LP orders they submit are sorted into increasing fee order so that the lowest fee bid is at the top and the highest is at the bottom. The fee level chosen for the market is derived from the liquidity commitment of the market (`target stake`) and the amount of stake committed from each bidder. Vega processes the LP orders from top to bottom by adding up the commitment stake as it goes until it reaches a level greater than or equal to the `target stake`. When that point is reached the fee used is the fee of the last liquidity order processed. -```math +```text [LP 1 stake = 120 ETH, LP 1 liquidity-fee-factor = 0.5%] [LP 2 stake = 20 ETH, LP 2 liquidity-fee-factor = 0.75%] [LP 3 stake = 60 ETH, LP 3 liquidity-fee-factor = 3.75%] @@ -49,15 +53,50 @@ In the example below there are 3 liquidity providers all bidding for their chose 1. If the `target stake = 240` then even putting all the liquidity supplied above does not meet the estimated market liquidity demand and thus we set `k=N` and so the market's liquidity-fee-factor is `LP N fee = LP 3 fee = 3.75%`. 1. Initially (before market opened) the `[target stake]` is by definition zero (it's not possible to have a position on a market that's not opened yet). Hence by default the market's initial liquidity-fee-factor is the lowest liquidity-fee-factor. + +### Stake-weighted-average method for setting the liquidity fee factor + +The liquidity fee factor is set as the weighted average of the liquidity fee factors, with weights assigned based on the supplied stake from each liquidity provider, which can also account for the impact of one supplier's actions on others. + +#### Example for fee setting mechanism using the Stake-weighted-average method + +In the example below there are 3 liquidity providers all bidding for their chosen fee level. The overall liquidity fee factor is the weight-average of their nominations: + +```text +[LP 1 stake = 120 ETH, LP 1 liquidity-fee-factor = 0.5%] +[LP 2 stake = 20 ETH, LP 2 liquidity-fee-factor = 0.75%] +[LP 3 stake = 60 ETH, LP 3 liquidity-fee-factor = 3.75%] + +then + +liquidity-fee-factor = ((120 * 0.5%) + (20 * 0.75%) + (60 * 3.75%)) / (120 + 20 + 60) = 1.5% +``` + +### "Constant Liquidity Fee" Method + +The liquidity fee factor is set to a constant directly as part of the market proposal. + +#### Example for fee setting mechanism using the constant method + +In the example below there are 3 liquidity providers all bidding for their chosen fee level and the overall liquidity fee factor is: + +```text +[LP 1 stake = 120 ETH, LP 1 liquidity-fee-factor = 0.5%] +[LP 2 stake = 20 ETH, LP 2 liquidity-fee-factor = 0.75%] +[LP 3 stake = 60 ETH, LP 3 liquidity-fee-factor = 3.75%] +``` + +but the market was proposed with a constant fee of 0.8% so that is what the liquidity-fee-factor will be. + ### Timing market's liquidity-fee-factor changes -Once the market opens (opening auction starts) a clock starts ticking. We calculate the `[target stake]` using [target stake](./0041-TSTK-target_stake.md). The fee is continuously re-evaluated using the mechanism above. +Once the market opens (opening auction starts) a clock starts ticking. We calculate the `[target stake]` using [target stake](./0041-TSTK-target_stake.md). The fee is re-evaluated using the mechanism above at the start of each epoch using LPs commitments at start of epoch. ### APIs for fee factor calculations - what should core be exposing? At time of call: -- The `liquidity-fee-factor` for the market. +- The `liquidity-fee-factor` for the market, and the method used to calculate it. - Current liquidity provider commitments and their individually nominated fee factors - [Target stake](./0041-TSTK-target_stake.md) @@ -67,28 +106,35 @@ The guiding principle of this section is that by committing stake a liquidity pr ### Calculating liquidity provider equity-like share -The parameter which determines the period over which market value and hence growth is `market.value.windowLength` which could be e.g. a week. +The parameter which determines the period over which growth is estimated is `market.value.windowLength` which could be e.g. a week. From the end of the opening auction, which we will refer to as `t0` until `t0+market.value.windowLength` is the `0th` or "bootstrap period". Then from `t0+market.value.windowLength` until `t0 + 2 x market.value.windowLength` is the `1st` period and so on. For each LP we track the stake they have and also their virtual stake. -Before and during the 0th (bootstrap) any stake commitment or removal is mirrored in the virtual stake. +For markets that have no "parent" market, see [governance](./0028-GOVE-governance.md) we postulate that before and during the 0th (bootstrap) any stake commitment or removal is mirrored in the virtual stake. -For any period `n >= 1` LP can add stake or remove stake but virtual stake is treated differently: +For any period `n >= 1` LP can add stake or remove stake but virtual stake is treated differently for markets with "parent market": + +If the market has a "parent market" then each LP which commits liquidity (to this market) gets the virtual stake copied from the parent market as the 1st step of the process and the stake they are committing here minus the stake on parent market is treated as the `delta` here. Say an `LP i` wants increases their commitment by `delta > market.liquidityProvision.minLpStakeQuantumMultiple x quantum` (this could also be the initial commitment). Then we update -`LP i virtual stake <- LP i virtual stake + delta`. +```text +LP i virtual stake <- LP i virtual stake + delta. +``` Say an `LP i` wants to decrease their commitment by `delta < 0`. Then we update -`LP i virtual stake <- LP i virtual stake x (LP i stake + delta)/(LP i stake)`. +```text +LP i virtual stake <- LP i virtual stake x (LP i stake + delta)/(LP i stake). +``` Independently of the above we also update all virtual stakes at start of each new period. To that end "total value for fee purposes" is cumulated over the period set by `market.value.windowLength`. For a period `n` call this `T(n)`. We let the `0`th period start the moment the opening auction ends and last for `market.value.windowLength`. -We include the volume of the trades that resolved the opening auction in `T(0)`. +We include the volume of the trades that resolved the opening auction in `T(0)` for markets with no parent market. +For markets with a parent market we take `T(0)` to be the most recent `T_{parent}(latest)`. From this we calculate the running average trade value for fee purposes: -```math +```text A(0) <- T(0), A(n) <- A(n-1) x n/(n+1) + T(n)/(n+1), for `n=1,2,... ``` @@ -115,7 +161,7 @@ Moreover, in situations when trading volume was zero in the previous period or i The equity-like share for each LP is then -```math +```text (LP i equity-like share) = (LP i virtual stake) / (sum over j from 1 to N of (LP j virtual stake)). ``` @@ -128,7 +174,7 @@ The average entry valuation (which should be reported by the APIs and could be c So `Delta S > 0` (and so `S+Delta S > 0`) in what follows. 2. Calculate the entry valuation at the time stake `Delta S` is added / removed as -```math +```text (entry valuation) = sum over j from 1 to N of (LP j virtual stake) ``` @@ -136,21 +182,21 @@ Note, the `virtual stake` used in the calculation of `entry valuation` is after This in particular means that if this is the first LP commitment on the market then the `(entry valuation) = Delta S`. 3. Update the average entry valuation to -```math +```text (average entry valuation) <- (average entry valuation) x S / (S + Delta S) + (entry valuation) x (Delta S) / (S + Delta S) ``` Example 1: Currently the sum of all virtual stakes is `900`. A new LP has `0` stake and add stake `Delta S = 100`. The sum of all virtual stakes is now `1000`. The average entry valuation is -```math +```text (average entry valuation) <- 0 + 1000 x 100 / (0 + 100) = 1000 ``` Example 2: A new LP1 has `0` stake and they wish to add `Delta S = 8000` and a new LP2 has `0` stake and they wish to add `Delta S = 2000`. Currently the sum of all virtual stakes is `10000` after the LP commitments added. The average entry valuations are: -```math +```text (average entry valuation LP1) <- 0 + 8000 x 8000 / (0 + 8000) = 8000 (average entry valuation LP2) <- 0 + (8000 + 2000) x 2000 / (0 + 2000) = 10000 ``` @@ -158,93 +204,142 @@ A new LP1 has `0` stake and they wish to add `Delta S = 8000` and a new LP2 has Example 3: An existing LP has `average entry valuation 1000` and `S=100`. Currently the sum of all virtual stakes is `2000`. They wish to add `10` to their stake. -```math +```text (average entry valuation) <- 1000 x 100 / (100 + 10) + 2000 x 10 / (100 + 10) = 1090.9.... ``` Example 4: An existing LP has `average entry valuation 1090.9` and `S=110`. Currently the sum of all virtual stakes is `3000`. They wish to remove `20` from their stake. Their average entry valuation stays the same -```math +```text (average entry valuation) = 1090.9 ``` -### Calculating the liquidity score +### Calculating the instantaneous liquidity score At every vega time change calculate the liquidity score for each committed LP. -This is done by taking into account all orders they have deployed within the `[min_lp_price,max_lp_price]` [range](./0038-OLIQ-liquidity_provision_order_type.md#refining-list-of-orders) and then calculating the volume-weighted [probability of trading](./0034-PROB-prob_weighted_liquidity_measure.ipynb) at each price level - call it instantaneous liquidity score. For orders outside the tightest price monitoring bounds set probability of trading to 0. -When we say "all orders" we mean their limit orders, [pegged orders](./0037-OPEG-pegged_orders.md) and the volume deployed on their behalf as part of their [liquidity commitment order](./0038-OLIQ-liquidity_provision_order_type.md). +This is done by taking into account all orders they have deployed within the `[min_lp_price,max_lp_price]` [range](./0044-LIME-lp_mechanics.md) and then calculating the volume-weighted [probability of trading](./0034-PROB-prob_weighted_liquidity_measure.ipynb) at each price level - call it instantaneous liquidity score. + +For orders outside the tightest price monitoring bounds set probability of trading to 0. For orders which have less than 10% [probability of trading], we set the probability to 0 when calculating liquidity score. + +Note that parked [pegged orders](./0037-OPEG-pegged_orders.md) and not-yet-triggered [stop orders](./0014-ORDT-order_types.md) are not included. Now calculate the total of the instantaneous liquidity scores obtained for each committed LP: `total = the sum of instantaneous liquidity scores for all LPs that have an active liquidity commitment` Now, if the `total` comes out as `0` then set `fractional instantaneous liquidity score` to `1.0/n`, where `n` is the number of committed LPs. -Otherwise calculate fractional instantaneous liquidity score for each committed LP (i.e. a party that successfully submitted [LP order](./0038-OLIQ-liquidity_provision_order_type.md) as: +Otherwise calculate fractional instantaneous liquidity score for each committed LP. `fractional instantaneous liquidity score = instantaneous liquidity score / total` -If `market.liquidity.providers.fee.distributionTimeStep` is set to `0` then for each committed LP `liquidity score` is set to `fractional instantaneous liquidity score`. - -Otherwise whenever a new LP fee distribution period starts set a counter `n=1`. +Whenever a new LP fee distribution period starts set a counter `n=1`. Then on every Vega time change, after `fractional instantaneous liquidity score` has been obtained for all the committed LPs, update: `liquidity score <- ((n-1)/n) x liquidity score + (1/n) x fractional instantaneous liquidity score` The liquidity score should always be rounded to 10 decimal places to prevent spurious accuracy and overly long string representation of a number. -### Distributing fees +### Distributing fees into LP-per-market fee account -On every trade, liquidity fee should be collected immediately into an account for each liquidity provider (call it LP fee account). Each party will have an LP fee account on every market on which they committed liquidity by providing LP stake. +On every trade, liquidity fee should be collected immediately into the market's aggregate LP fee account. +The account is under control of the network and funds from this account will be transferred to the owning LP party according to the mechanism below. -This account is not under control of the LP party (they cannot initiate transfers in or out of the account). The account is under control of the network and funds from this account will be transferred to the owning LP party according to the mechanism below. +A network parameter `market.liquidity.providersFeeCalculationTimeStep` will control how often fees are distributed from the market's aggregate LP fee account. +Starting with the end of the opening auction the clock starts ticking and then rings every time `market.liquidity.providersFeeCalculationTimeStep` has passed. Every time this happens the balance in this account is transferred to the liquidity provider's general account for the market settlement asset. -A network parameter `market.liquidity.providers.fee.distributionTimeStep` will control how often fees are distributed from the LP fee account. Starting with the end of the opening auction the clock starts ticking and then rings every time `market.liquidity.providers.fee.distributionTimeStep` has passed. Every time this happens the balance in this account is transferred to the liquidity provider's general account for the market settlement asset. -If `market.liquidity.providers.fee.distributionTimeStep` is set to `0` then the balance is distributed either immediately upon collection or at then end of a block. +The liquidity fees are transferred from the market's aggregate LP fee account into the LP-per-market fee account, pro-rata depending on the `LP i equity-like share` multiplied by `LP i liquidity score` scaled back to `1` across all LPs at a given time. -The liquidity fees are distributed pro-rata depending on the `LP i equity-like share` multiplied by `LP i liquidity score` scaled back to `1` across all LPs at a given time. +The LP parties don't control the LP-per-market fee account; the fees from there are then transferred to the LPs' general account at the end epoch as described below. -#### Example +### Calculating SLA performance -We have `4` LPs with equity-like share shares: -share as below +#### Measuring time spent meeting their commitment in a single epoch -```math -LP 1 els = 0.65 -LP 2 els = 0.25 -LP 3 els = 0.1 -``` +During the epoch, the amount of time in nanoseconds (of Vega time) that each LP spends meeting the SLA is recorded. This can be done by maintaining a counter `s_i` as shown below: -Trade happened, and the trade value for fee purposes multiplied by the liquidity fee factor is `103.5 ETH`. The following amounts be collected immediately into the LP fee accounts for the market: +- At the start of a new epoch, `s_i = 0` -```math -0.65 x 103.5 = 67.275 ETH to LP 1's LP account -0.25 x 103.5 = 25.875 ETH to LP 2's LP account -0.10 x 103.5 = 10.350 ETH to LP 3's LP account -``` + - If the LP is meeting their commitment, store the Vega time of the start of the epoch as the time the LP began meeting their commitment, otherwise store `nothing`. -Then LP 4 made a delayed LP commitment, and updated share as below: +- At start of block, for LP `i`, first reset the running minimum valid volume an LP is providing and across the block and then keep updating the minimum volume that the LP is providing that is [within the valid range](./0044-LIME-lp_mechanics.md) (section "Meeting the committed volume of notional"). +- Note that the volume check must only happen _after_ iceberg orders, that need refreshing as a result of a transaction are refreshed. This means that while an iceberg order has sufficient `remaining` quantity, it will **never** be considered to be contributing less than its `minimum peak size`. -```math -LP 1 els = 0.43 -LP 2 els = 0.17 -LP 3 els = 0.07 -LP 4 els = 0.33 -``` +- At the end of each block: + - If an LP has started meeting their [committed volume of notional based on the minimum volume recorded during the block](./0044-LIME-lp_mechanics.md) (section "Calculating liquidity from commitment") after previously not doing so (i.e. `nothing` is stored as the time the LP began meeting their commitment): + - Store the current Vega time attached to the block being processed as the time the LP began meeting their commitment. + + - If an LP has stopped meeting their committed volume of notional after previously doing so: + - Add the difference in nanoseconds between the current Vega time attached to the block being processed and the time the LP began meeting their commitment (stored in the step above) to `s_i`. + - Store `nothing` as the time the LP began meeting their commitment, to signify the LP not meeting their commitment. + +- At the end of the epoch, calculate the actual observed epoch length `observed_epoch_length` = the difference in nanoseconds between the Vega time at the start of the epoch and the Vega time at the end of the epoch. + +Note that because vega time won't be progressing inside a block the above mechanism should ensure that `s_i` gets incremented only if the LP was meeting their commitment at every point this was checked within the block. + +#### Calculating the SLA performance penalty for a single epoch -When the time defined by `market.liquidity.providers.fee.distributionTimeStep` elapses we do transfers: +Calculate the fraction of the time the LP spent on the book: -```math -67.275 ETH from LP 1's LP account to LP 1's general account -25.875 ETH from LP 2's LP account to LP 2's general account -10.350 ETH from LP 3's LP account to LP 3's general account +```text +fraction_of_time_on_book = s_i / observed_epoch_length ``` +For any LP where `fraction_of_time_on_book < market.liquidity.commitmentMinTimeFraction` the SLA penalty fraction `p_i = 1` (that is, the penalty is 100% of their fees). + +For each LP where `fraction_of_time_on_book ≥ market.liquidity.commitmentMinTimeFraction`, the SLA penalty fraction `p_i` is calculated as follows: + +Let $t$ be `fraction_of_time_on_book` + +Let $s$ be `market.liquidity.commitmentMinTimeFraction`. + +Let $c$ be `market.liquidity.slaCompetitionFactor`. + +$$p_i = \begin{cases} + (1 - \frac{t - s}{1 - s}) \cdot c &\text{if } s < 1 \\ + 0 &\text{if } s = 1 +\end{cases}$$ + +#### Calculating the SLA performance penalty for over hysteresis period + +Now, for each LP $i$ take the $p_i$ values calculated over the last `market.liqudity.performanceHysteresisEpochs - 1`, call these $p_i^1, p_i^2, ..., p_i^{n-1}$ (if all the historical ones are not yet available, take as many as there are - i.e. expanding window till you get to the full length). + +Now calculate $p_i^n$ to be the arithmetic average of $p_i^k$ for $k = 1,2,...,n-1$. +Finally set + +$$ +p_i^n \leftarrow \max(p_i,p_i^n). +$$ + +i.e. your penalty is the bigger of current epoch and average over the hysteresis period + +### Applying LP SLA performance penalties to accrued fees + +As defined above, for each LP for each epoch you have "penalty fraction" $p_i^n$ which is between `[0,1]` with `0` indicating LP has met commitment 100% of the time and `1` indicating that LP was below `market.liquidity.commitmentMinTimeFraction` of the time. + +If for all $i$ (all the LPs) have $p_i^n = 1$ then all the fees go into the market insurance pool and we stop. + +Calculate + +$$ +w_i = \frac{\text{LP-per-market fee account } i}{\sum_k \text{LP-per-market fee account } k}. +$$ + +For each LP transfer $(1-p_i^n) \times \text{ amount in LP-per-market fee account}$ to their general account with a transfer type that marks this as the "LP net liquidity fee distribution". + +Transfer the remaining amount from each LP-per-market fee account back into the market's aggregate LP fee account. Record the total inflow as a result of that operation as $B$. +Let $b_i := (1-p_i^n) \times w_i$ and renormalise $b_i$s so that they sum up to $1$ i.e. + +$$ +b_i \leftarrow \frac{b_i}{\sum_k b_k}\,. +$$ + +Each LP further gets a performance bonus: $b_i \times B$ with a transfer type that marks this as the "LP relative SLA performance bonus distribution". + ### APIs for fee splits and payments - Each liquidity provider's equity-like share - Each liquidity provider's average entry valuation -- The `market-value-proxy` ## Acceptance Criteria @@ -257,19 +352,29 @@ When the time defined by `market.liquidity.providers.fee.distributionTimeStep` e - Liquidity fee factors are recalculated every time the liquidity demand estimate changes. (0042-LIQF-005) - If a change in the open interest causes the liquidity demand estimate to change, then fee factor is correctly recalculated. (0042-LIQF-006) - If passage of time causes the liquidity demand estimate to change, the fee factor is correctly recalculated. (0042-LIQF-007) +- A market can be proposed with a choice of liquidity fee settings. These settings can be updated by a subsequent market update proposal. Moreover, the correct fee value and liquidity fee setting method can be read from the data node APIs. Upon proposal enactment the new liquidity method is applied to recalculate the liquidity fee. The tests should be carried out with the following methods: + - Weighted average (0042-LIQF-056) + - Constant fee (0042-LIQF-061) + - Marginal cost (0042-LIQF-062) +- The above example for the liquidity fee when the method is weighted-average results in a fee-factor of 1.5% (0042-LIQF-057) +- The above example for the liquidity fee when the method is constant-fee results in a fee-factor of 0.8% (0042-LIQF-058) +- The above example for the liquidity fee when the method is marginal cost results in a fee-factor of `3.75%` (0042-LIQF-059) +- For the constant-fee method validate that the fee factor can only be between 0 and 1 inclusive (0042-LIQF-060) ### CHANGE OF NETWORK PARAMETERS TESTS - Change of network parameter `market.liquidityProvision.minLpStakeQuantumMultiple` will change the multiplier of the asset quantum that sets the minimum LP commitment amount. If `market.liquidityProvision.minLpStakeQuantumMultiple` is changed then no LP orders that have already been submitted are affected. However any new submissions or amendments must respect the new amount and those not meeting the new minimum will be rejected. (0042-LIQF-021) - Change of network parameter `market.value.windowLength` will affect equity-like share calculations from the next block. Decreasing it so that the current period is already longer then the new parameter value will end it immediately and the next period will have the length specified by the updated parameter. Increasing it will lengthen the current period up to the the length specified by the updated parameter. (0042-LIQF-022) + ### SPLITTING FEES BETWEEN liquidity providers - The examples provided result in the given outcomes. (0042-LIQF-008) - The total amount of liquidity fee distributed is equal to the most recent `liquidity-fee-factor` x `notional-value-of-all-trades` (0042-LIQF-011) -- Liquidity providers with a commitment of 0 will not receive a share ot the fees (0042-LIQF-012) -- If a market has `market.liquidity.providers.fee.distributionTimeStep` set to more than `0` and such market settles then the fees are distributed as part of the settlement process, see [market lifecycle](./0043-MKTL-market_lifecycle.md). Any settled market has zero balances in all the LP fee accounts. (0042-LIQF-014) +- Liquidity providers with a commitment of 0 will not receive a share of the fees (0042-LIQF-012) +- When a market settles any fees from any intermediate fee accounts are distributed as if the epoch ended as part of the settlement process, see [market lifecycle](./0043-MKTL-market_lifecycle.md). Any settled market has zero balances in all the LP fee accounts. (0042-LIQF-014) - All liquidity providers with `average fraction of liquidity provided by committed LP > 0` in the market receive a greater than zero amount of liquidity fee. The only exception is if a non-zero amount is rounded to zero due to integer representation. (0042-LIQF-015) +- After fee distribution, if there is a remainder in the liquidity fee account and the market is not being settled, it should be left in the liquidity fee account and carried over to the next distribution window. (0042-LIQF-032) ### LP JOINING AND LEAVING MARKETS @@ -281,14 +386,64 @@ When the time defined by `market.liquidity.providers.fee.distributionTimeStep` e - An LP joining a market that is above the target stake with a commitment large enough to push one of two higher bids above the target stake, and a higher fee bid than the current fee: the fee doesn't change (0042-LIQF-024) - An LP leaves a market that is above target stake when their fee bid is currently being used: fee changes to fee bid by the LP who takes their place in the bidding order (0042-LIQF-025) - An LP leaves a market that is above target stake when their fee bid is lower than the one currently being used and their commitment size changes the LP that meets the target stake: fee changes to fee bid by the LP that is now at the place in the bid order to provide the target stake (0042-LIQF-026) -- An LP leaves a market that is above target stake when their fee bid is lower than the one currently being used and their commitment size doesn't change the LP that meets the target stake: fee doesn't change (0042-LIQF-027) +- An LP leaves a market that is above target stake when their fee bid is lower than the one currently being used. The loss of their commitment doesn't change which LP meets the target stake: fee doesn't change (0042-LIQF-027) - An LP leaves a market that is above target stake when their fee bid is higher than the one currently being used: fee doesn't change (0042-LIQF-028) ### API - Equity-like share of each active LP can be obtained via the API (0042-LIQF-016) - Liquidity score of each active LP can be obtained via the API (0042-LIQF-017) +- Through the `LiquidityProviders` API, liquidity score, average entry valuation and equity-like share of each active LP can be obtained + - GRPC (0042-LIQF-050) + - GRAPHQL (0042-LIQF-051) + - REST (0042-LIQF-052) + + +### Successor markets + +- If an LP has virtual stake of `11000` and stake of `10000` on a parent marketID=`m1` and a new market is proposed and enacted as `m2` with `m1` as its parent market and the LP submits a commitment of `10000` to `m2` during the "Pending" period, see [lifecycle](./0043-MKTL-market_lifecycle.md) then for the duration of the first `market.value.windowLength` after the opening auction ends the LP has virtual stake of `11000` and stake of `10000` on `m2`. (0042-LIQF-031) +- If an LP has virtual stake of `11000` and stake of `10000` on a parent `marketID`=`m1` and a new market is proposed and enacted as `m2` with `m1` as its parent market and the LP submits a commitment of `20000` to `m2` during the "Pending" period, see [lifecycle](./0043-MKTL-market_lifecycle.md) then for the duration of the first `market.value.windowLength` after the opening auction ends the LP has virtual stake which must be result of the virtual stake obtained from `m1` with the `delta=10000` added on, so virtual stake of `21000`, assuming all other LPs committed exactly the stake they had on `m1`. (0042-LIQF-048) +- If an LP has virtual stake of `11000` and stake of `10000` on a parent `marketID`=`m1` and a new market is proposed and enacted as `m2` with `m1` as its parent market and the LP submits a commitment of `5000` to `m2` during the "Pending" period, see [lifecycle](./0043-MKTL-market_lifecycle.md) then for the duration of the first `market.value.windowLength` after the opening auction ends the LP has virtual stake obtained from `m1` with the `delta=-5000` added on (i.e. 5000 removed). (0042-LIQF-033) +- If `market.liquidity.providersFeeCalculationTimeStep > 0` for a given market and an LP submits a new liquidity commitment halfway through the time interval then they receive roughly 1/2 the fee income from that market compared with the next time interval when they maintain their commitment (and the traded value is the same in both time intervals). (0042-LIQF-034) + + +### Calculating SLA Performance + +- If an LP has an active liquidity provision at the start of an epoch, `market.liquidity.slaCompetitionFactor = 1`, `market.liquidity.commitmentMinTimeFraction = 0.5` and throughout the epoch meets their liquidity provision requirements such that the `fraction_of_time_on_book = 0.75` then their penalty from that epoch will be `0.5`. This will be true whether: + + - Their liquidity is all provided at the start of the epoch and then none is provided for the second half (0042-LIQF-037) + - Their liquidity is provided scattered throughout the epoch (0042-LIQF-038) + +- If an LP has an active liquidity provision at the start of an epoch, `market.liquidity.slaCompetitionFactor = 0`, `market.liquidity.commitmentMinTimeFraction = 0.5` and throughout the epoch meets their liquidity provision requirements such that the `fraction_of_time_on_book = 0.75` then their penalty from that epoch will be `0`. (0042-LIQF-041) +- If an LP has an active liquidity provision at the start of an epoch, `market.liquidity.slaCompetitionFactor = 0.5`, `market.liquidity.commitmentMinTimeFraction = 0.5` and throughout the epoch meets their liquidity provision requirements such that the `fraction_of_time_on_book = 0.75` then their penalty from that epoch will be `0.25`. (0042-LIQF-042) + +- When `market.liquidity.performanceHysteresisEpochs = 1`: + + - If an LP has an active liquidity provision at the start of an epoch and throughout the epoch always meets their liquidity provision requirements then they will have a `fraction_of_time_on_book == 1` and no penalty will be applied to their liquidity fee payments at the end of the epoch (0042-LIQF-035) + - If an LP has an active liquidity provision at the start of an epoch and throughout the epoch meets their liquidity provision requirements less than `market.liquidity.commitmentMinTimeFraction` fraction of the time then they will have a full penalty and will receive `0` liquidity fee payments at the end of the epoch (0042-LIQF-049) + - An LP has an active liquidity provision at the start of an epoch. The penalty rate for said LP over the previous `2` epochs is `0.75`. During the epoch `market.liquidity.performanceHysteresisEpochs` is set to `3`. Throughout the current epoch the LP meets their liquidity provision requirements so they will have `fraction_of_time_on_book == 1`. The penalty applied to fee distribution at epoch end will be `0` and will not consider the previous epochs. (0042-LIQF-053) + +- When `market.liquidity.performanceHysteresisEpochs > 1`: + + - If an LP has an active liquidity provision at the start of an epoch, the average `penalty rate` over the previous `n-1` epochs is `0.75` and throughout the epoch they always meet their liquidity provision requirements then they will have a `fraction_of_time_on_book == 1` for the latest epoch a penalty rate of `0.75` will be applied to liquidity fee payments at the end of the epoch (0042-LIQF-047) + - If an LP has an active liquidity provision at the start of an epoch, the average `penalty rate` over the previous `n-1` epochs is `0.5` and throughout the epoch they always meet their liquidity provision requirements then they will have a `fraction_of_time_on_book == 1` for the latest epoch a penalty rate of `0.5` will be applied to liquidity fee payments at the end of the epoch (0042-LIQF-039) + - If an LP has an active liquidity provision at the start of an epoch, the average `penalty rate` over the previous `n-1` epochs is `0.5` and throughout the epoch they never meet their liquidity provision requirements then they will have a `fraction_of_time_on_book == 0` for the latest epoch a penalty rate of `1` will be applied to liquidity fee payments at the end of the epoch (0042-LIQF-040) + - If an LP has an active liquidity provision at the start of an epoch and no previous performance penalties and throughout the epoch always meets their liquidity provision requirements then they will have a `fraction_of_time_on_book == 1` then no penalty will be applied to their liquidity fee payments at the end of the epoch. (0042-LIQF-054) + + +### SLA Performance bonus transfers + +- The net inflow and outflow into and out of the market's aggregate LP fee account should be zero as a result of penalty collection and bonus distribution. (0042-LIQF-043) +- With two liquidity providers, one with an effective penalty rate of `0.5` and earned fees of `n`, and the other with an effective rate of `0.75` and earned fees of `m`, `50% * n` and `25% * m` of the second provider's should be transferred back into market's aggregate LP fee account. Then the total provider bonus score should be `b = (m / (n + m)) * 0.25 + (n / (n + m)) * 0.5` and provider 1 should receive `(0.5 * n + 0.25 * m) * (n / (n + m)) * 0.5 / b` and provider 2 should receive `(0.5 * n + 0.25 * m) * (m / (n + m)) * 0.25 / b` as an additional bonus payment (0042-LIQF-044) +- With two liquidity providers, one with an effective penalty rate of `1` and earned fees of `n`, and the other with an effective rate of `0` and earned fees of `m`, the entirety of `n` should be transferred to the second liquidity provider as a bonus payment (0042-LIQF-045) +- With only one liquidity provider, with an effective penalty rate of `0.5`, `50%` of their initially earned fees will be taken initially but will be entirely paid back to them as a bonus payment (0042-LIQF-046) -### Distribution +### Transfers example -- If `market.liquidity.providers.fee.distributionTimeStep > 0` and an LP submits a new liquidity commitment halfway through the time interval then they receive roughly 1/2 the fee income compared with the next time interval when they maintain their commitment (and the traded value is the same in both time intervals). (0042-LIQF-018) +Example 1, generated with [supplementary worksheet](https://docs.google.com/spreadsheets/d/1PQC2WYv9qRlyjbvvCYpVWCzO5MzwkcEGOR5aS9rWGEY) [internal only]. Values should match up to rounding used by `core` (0042-LIQF-055): +| LP | penalty fraction | LP-per-market fee accounts balance | 1st transfer amt | 2nd (bonus) transfer amt | +| --- | -------------- | -------------- | -------------- | -------------- | +| LP1 | 0 | 1000 | 1000 | 24673.94095 | +| LP2 | 0.05 | 100 | 95 | 2344.02439 | +| LP3 | 0.6 | 7000 | 2800 | 69087.03466 | +| LP4 | 1 | 91900 | 0 | 0 | diff --git a/protocol/0043-MKTL-market_lifecycle.md b/protocol/0043-MKTL-market_lifecycle.md index 8d2c6901d..cd66ebce6 100644 --- a/protocol/0043-MKTL-market_lifecycle.md +++ b/protocol/0043-MKTL-market_lifecycle.md @@ -12,9 +12,8 @@ Markets on Vega are proposed, permissionless, using the [governance mechanism](. Markets proposed via [governance proposals](./0028-GOVE-governance.md#1-create-market) undergo certain additional validations. Note the distinctions between a proposal that is `valid` or `accepted` and a proposal that is `sucessful`. A `valid` proposal has passed or will pass validation checks; an `accepted` proposal has been received in a Vega transaction and passed validation checks; and a `successful` proposal has been voted for and won. The proposal becomes `enacted` when the action specified (i.e. for the purposes of this spec, market creation/update/close). -All markets are proposed without any [liquidity commitment](./0038-OLIQ-liquidity_provision_order_type.md). +All markets are proposed without any [liquidity commitment](./0044-LIME-lp_mechanics.md#commit-liquidity-network-transaction). If the proposal is successful the market will go into opening auction at least until the proposed `enactment` date. -However, the market may stay in an opening auction past the proposed `enactment` date until at least on party makes a liquidity commitment that meets criteria for exiting [liquidity auction](./0035-LIQM-liquidity_monitoring.md). ## Market lifecycle statuses @@ -26,13 +25,13 @@ A market can progress through a number of statuses through its life. The overall | Rejected | No | No trading | Outcome of governance votes is to reject the market | N/A | Pending | Yes | Opening auction | Governance vote passes/wins | Governance vote (to close) OR enactment date reached | Cancelled | No | No trading | Market triggers cancellation condition | N/A -| Active | Yes | Normal trading | Enactment date reached and usual auction exit checks pass | Governance vote (to close) OR maturity of market -| Suspended | Yes | Exceptional auction | Price monitoring or liquidity monitoring trigger, or product lifecycle trigger | Exit conditions met per monitoring spec. that triggered it, no other monitoring triggered or governance vote if allowed (see below) +| Active | Yes | Normal trading | Enactment date reached and usual auction exit checks pass | Governance vote (to close) OR trigger for trading terminated for of market +| Suspended | Yes | Exceptional auction | Price monitoring trigger, or product lifecycle trigger | Exit conditions met per monitoring spec. that triggered it, no other monitoring triggered or governance vote if allowed (see below) | Closed | No | No trading | Governance vote (to close) | N/A | Trading Terminated | No | No trading | Defined by the product (i.e. from a product parameter, specified in market definition, giving close date/time) | Settlement event commences | Settled | No | No trading | Settlement triggered and completed as defined by product | N/A -[1] Accepting LPs: it is possible to make or amend [Liquidity Provision Commitments](./0038-OLIQ-liquidity_provision_order_type.md) +[1] Accepting LPs: it is possible to make or amend [Liquidity Provision Commitments](./0044-LIME-lp_mechanics.md). ![Life cycle flow diagram](./0043-market-lifecycle-flow-diagram.svg) @@ -42,7 +41,7 @@ Note that there is no governance proposal to cancel a market. However it is poss ### Proposed -All Markets are first [proposed via the governance mechanism](./0028-GOVE-governance.md#1-create-market). Once the valid Market Proposal is accepted *the Market (see [market framework](./0001-MKTF-market_framework.md)) is created* and can accept [Liquidity Provision Commitments](./0038-OLIQ-liquidity_provision_order_type.md), voting begins and its state is `proposed`. +All Markets are first [proposed via the governance mechanism](./0028-GOVE-governance.md#1-create-market). Once the valid Market Proposal is accepted *the Market (see [market framework](./0001-MKTF-market_framework.md)) is created* and can accept [Liquidity Provision Commitments](./0044-LIME-lp_mechanics.md#commit-liquidity-network-transaction), voting begins and its state is `proposed`. **Entry:** @@ -81,9 +80,9 @@ When a Market Proposal is not successful, see [governance proposal](./0028-GOVE- ### Pending -When a Market Proposal is successful at the end of the voting period, the Market state becomes "Pending". Currently a Pending Market is always in an [auction call period](./0026-AUCT-auctions.md) that ends at the enactment date as specified in the Market Proposal. +When a Market Proposal is successful at the end of the voting period, the Market state becomes "Pending". Currently a Pending Market is always in an [auction call period](./0026-AUCT-auctions.md) that ends at the enactment date as specified in the Market Proposal if the other conditions for exiting auction period are met (liquidity committed, best static bid / ask present). If, initially, at the specified enactment date it was not possible to leave the auction period, the auction will conclude as soon as possible once the conditions for auction exit are met. -Note: this state represents any market that will be created, which currently means a Market Proposal vote has concluded successfully. In reasonably near future there will be automated market creation e.g. for a series of markets that is voted on once, market creation from a data source (oracle), etc. so market creation and the market lifecycle should be implemented independently of the governance framework and the lifecycle of a proposal. +Note: this state represents any market that will be created, which currently means a Market Proposal vote has concluded successfully. **Entry:** @@ -106,9 +105,8 @@ Auction period ends when any of the following occur: ### Cancelled -A market becomes Cancelled when a Market Proposal is successful and conditions are not met to transition the Market to the Active state during the Pending period, -and the trading terminated data source input rings, see [data sourcing](./0045-DSRC-data_sourcing.md). -When a market transitions to a cancelled state all orders should be cancelled and collateral returned to respective parties general account for the relevant asset, all LP commitments should be cancelled and their bond returned to the general account for the relevant asset and any insurance pool balance should be transferred into the network treasury account for that asset. +A market becomes Cancelled when a Market Proposal is successful and conditions are not met to transition the Market to the Active state during the Pending period, and the trading terminated data source input is triggered, see [data sourcing](./0045-DSRC-data_sourcing.md). +When a market transitions to a cancelled state all orders should be cancelled and collateral returned to respective parties general account for the relevant asset, all LP commitments should be cancelled and their bond returned to the general account for the relevant asset. After `market.liquidity.successorLaunchWindowLength` has elapsed since cancellation any [insurance pool](./0015-INSR-market_insurance_pool_collateral.md) balance should get transferred into the global insurance pool using the same settlement asset. Once "cancelled" there must be no open positions tracked by the protocol for the market and any open positions must have been closed including returning all margin and other related collateral if necessary and also notifying downstream event consumers that the positions are closed. Specific position related actions may be unnecessary if the cancelled state is being entered from a state in which there cannot possibly have been any open positions. All data sources that are only referenced by this market should be unregistered. @@ -129,12 +127,12 @@ All data sources that are only referenced by this market should be unregistered. ### Active -Once the enactment date is reached and the other conditions specified to exit the Pending state are met, the market becomes Active on conclusion of uncrossing of the opening auction. This status indicates it is trading via its normally configured trading mode according to the market framework (continuous trading, frequent batch auction, RFQ, block only, etc.). The specification for the trading mode should describe which orders are accepted and how trading proceeds. The market will terminate trading according to a product trigger (for futures, if the trading termination date is reached) and can be temporarily suspended automatically by various monitoring systems ([price monitoring](./0032-PRIM-price_monitoring.md), [liquidity monitoring](./0035-LIQM-liquidity_monitoring.md)). The market can also be closed via a governance vote (market parameter update) to change the status to closed [see the governance spec](./0028-GOVE-governance.md). +Once the enactment date is reached and the other conditions specified to exit the Pending state are met, the market becomes Active on conclusion of uncrossing of the opening auction. This status indicates it is trading via its normally configured trading mode according to the market framework (continuous trading, frequent batch auction, RFQ, block only, etc.). The specification for the trading mode should describe which orders are accepted and how trading proceeds. The market will terminate trading according to a product trigger (for futures, if the trading termination date is reached) and can be temporarily suspended automatically by ([price monitoring](./0032-PRIM-price_monitoring.md). The market can also be closed via a governance vote (market parameter update) to change the status to closed [see the governance spec](./0028-GOVE-governance.md). **Entry:** - From Pending: enactment date reached and conditions to transition from Pending state to Active as detailed above are met -- From Suspended: conditions specified in [price monitoring](./0032-PRIM-price_monitoring.md) and [liquidity monitoring](./0035-LIQM-liquidity_monitoring.md) are met for the market to exit the suspended status back to Active. +- From Suspended: conditions specified in [price monitoring](./0032-PRIM-price_monitoring.md) are met for the market to exit the suspended status back to Active. **Exit:** @@ -161,7 +159,7 @@ Suspension currently always operates as an auction call period. Depending on the **Exit:** -- Conditions specified in [price monitoring](./0032-PRIM-price_monitoring.md) and [liquidity monitoring](./0035-LIQM-liquidity_monitoring.md) and the usual [ending of auction checks](./0026-AUCT-auctions.md) pass → Active +- Conditions specified in [price monitoring](./0032-PRIM-price_monitoring.md) and the usual [ending of auction checks](./0026-AUCT-auctions.md) pass → Active - Governance vote to close a market passes → Closed - Market was suspended by governance vote of product lifecycle trigger and a governance vote passes to set the status to ACTIVE → Active @@ -173,7 +171,7 @@ Suspension currently always operates as an auction call period. Depending on the ### Closed -Note, this governance action is unspecified and not MVP. +Market goes into a closed state if a [governance vote to close it](./0028-GOVE-governance.md#6-close-market) passes and its enactment date is reached. **Entry:** @@ -185,7 +183,7 @@ No exit. This is a terminal state. **Behaviour:** -- Orders may be cancelled, no new orders accepted. Something will need to be done to unwind positions. +- If market is in auction that auction gets uncrossed and any orders matched within it result in trades. All other orders get cancelled, no new orders are accepted, liquidity commitments cannot be modified, final settlement is carried out using the price supplied by the governance vote. ### Trading Terminated @@ -210,17 +208,18 @@ A market moves from this termination state to Settled when enough information ex - During the transition out of this state: - All final settlement cashflows are calculated and applied (settled) - Margins are transferred back to general accounts - - Insurance pool funds are redistributed -- No risk management or price/liquidity monitoring occurs +- No risk management or price occurs ### Settled Once the required data to calculate the settlement cashflows is provided by oracle input for a market in status Trading Terminated, these cashflows are calculated and applied to all traders with an open position (settlement). The positions are then closed and all orders cleared. All money held in margin accounts after final settlement is returned to traders' general accounts. -[Insurance pool funds](./0015-INSR-market_insurance_pool_collateral.md) are transferred to the on-chain treasury for the asset. [LP fees](0042-LIQF-setting_fees_and_rewarding_lps.md) that have been cumulated but not yet paid out are distributed to the market LPs as per the LP spec. -The market can be deleted entirely at this point, from a core perspective. +After `market.liquidity.successorLaunchWindowLength` has elapsed since the settlement time + +- [Insurance pool funds](./0015-INSR-market_insurance_pool_collateral.md) are transferred (after window for successor creation elapsed) into the global insurance pool using the same settlement asset. For markets that have a named successor market the appropriate fraction of the insurance pool balance is transferred to the insurance pool of the successor market. +- The market can be deleted entirely at this point, from a core perspective. **Entry:** @@ -237,7 +236,6 @@ The market can be deleted entirely at this point, from a core perspective. - During the transition into this state: - All final settlement cashflows are calculated and applied (settled) - Margins are transferred back to general accounts - - All insurance pool funds are redistributed or moved to a network wide insurance fund account - All fees are distributed (after a delay/at the next relevant epoch if needed - this means the market may continue to need to be "tracked" by the core until this step is complete) - Market is over and can be removed from core data, nothing happens after the final settlement above is complete. @@ -266,12 +264,6 @@ Market state is `active`. Market state is `suspended`. 1. Price monitoring auction ends and the market is in continuous trading again. The market state is `active`. -1. Parties cancel orders so that there is no "best static bid" on the order book. -The market enters [liquidity monitoring auction](0035-LIQM-liquidity_monitoring.md). -The market state is `suspended`. -1. A party place bid; this becomes a best static bid. -After the specified time the liquidity auction ends. -The market state is `active`. 1. Make sure that trades happen so that at least two parties have open positions. The mark price is `p`. 1. The time specified at market proposal by the internal time oracle is reached. @@ -279,18 +271,68 @@ Any orders that parties submit get rejected. The parties with open position in the immediately preceding step still have open positions. Parties with open positions still have non-zero margin account balances. The market state is `trading terminated`. +1. A final market-to-market happens with the last mark price. 1. The settlement price oracle transaction is sent and it is *not* equal to `p`. Parties that had open positions see settlement cash-flows happen. Margin account balances are transferred to the general account. -Any insurance pool balance is [redistributed](./0015-INSR-market_insurance_pool_collateral.md) to the on-chain treasury for the settlement asset of the market and other insurance pools using the same asset. The market state is `settled`. +After `market.liquidity.successorLaunchWindowLength` has passed since market settlement, any [insurance pool](./0015-INSR-market_insurance_pool_collateral.md) balance is transferred into the global insurance pool using the same settlement asset. -### Market never leaves opening auction, trading terminated trigger rings, market cancelled (0043-MKTL-003) + +### Lifecycle happy path in Perpetual market(0043-MKTL-009) + +1. Market `m1` is proposed with an internal trading terminated oracle set for some time in the future. Price monitoring is configured (e.g. like `2668-price-monitoring.feature`). +Market state is `proposed`. +The LP bond of the party that proposed the market is transferred from general to bond account. +1. Market `m1` is accepted and enters opening auction. +Market state is `pending`. +1. Parties place orders and at least one trade happens in continuous trading mode. +Market state is `active`. +1. Parties place orders so that a [price monitoring auction is triggered](0032-PRIM-price_monitoring.md). +Market state is `suspended`. +1. Price monitoring auction ends and the market is in continuous trading again. +The market state is `active`. +1. Make sure that trades happen so that at least two parties have open positions. + +1. An oracle event arrives which triggers the perpetual market's interim settlement logic, causing cashflow transfers but the market remains open. +1. Further trades happen, with parties still having different positions to previously. The mark price is `p`. +1. A market state change proposal is created to terminate the market at a given price that is *not* equal to `p`. +When this is approved and enacted the market state is `closed`. +Parties that had open positions see settlement cash-flows happen to settle positions. +Margin account balances are transferred to the general account. +After `market.liquidity.successorLaunchWindowLength` has passed since market settlement, any [insurance pool](./0015-INSR-market_insurance_pool_collateral.md) balance is transferred into the global insurance pool using the same settlement asset. + +### Market never leaves opening auction, trading terminated triggered, market cancelled (0043-MKTL-010) 1. A market is proposed, approved by governance process and enters the opening auction (Pending state). -1. Trading terminated data source rings before the market leaves the opening auction (so market never left Pending state so far). +1. Trading terminated data source triggers before the market leaves the opening auction (so market never left Pending state so far). 1. All orders should be cancelled and collateral returned to respective parties general account for the relevant asset. 1. All LP commitments should be cancelled and their bond returned to the general account for the relevant asset. -1. Any insurance pool balance should be [redistributed](./0015-INSR-market_insurance_pool_collateral.md) to the on-chain treasury for the settlement asset of the market and other insurance pools using the same asset. +1. After `market.liquidity.successorLaunchWindowLength` has elapsed since market cancellation, any insurance pool balance should be transferred into the global insurance pool using the same settlement asset. 1. All data sources that are only referenced by that market are unregistered. 1. The market state is set to cancelled. + + +### Market gets closed via a governance proposal (0043-MKTL-004) + +1. Once the governance proposal to close the market gets enacted any auction that the market may be in gets uncrossed and trades get generated. +1. All the other orders are cancelled and no further trades get generated. +1. Any new orders get rejected. +1. Liquidity commitments cannot be modified or cancelled. +1. Final settlement is carried out and the transfers reflect the difference in the last mark price and the final settlement price supplied by the governance proposal. +1. All the funds from market specific accounts get released to appropriate accounts; the insurance pool perhaps after the delay to allow for transfer into a successor market. +1. Market gets deleted. + + +### Market gets suspended via a governance proposal + +1. Once the governance proposal to suspend the market gets enacted the market gets immediately put into auction mode, if market was already in auction mode it remains in it. +1. No cashflows are exchanged when market has been suspended via a governance proposal. +1. Parties cannot modify their open interest +1. The prerequisite for a market to go out of auction mode is now a successful enactment of a governance proposal to resume that market. + +### Market gets resumed via a governance proposal + +1. Once the governance proposal to resumed the market gets enacted the market can now leave the auction. +1. If no other auction triggers are active the market goes back into its default trading mode immediately (auction gets uncrossed and trades get generated). +1. If other auction triggers are active the market remains in auction mode until these allow it to leave it. diff --git a/protocol/0044-LIME-lp_mechanics.md b/protocol/0044-LIME-lp_mechanics.md index f2c5a0e88..a38ad7b91 100644 --- a/protocol/0044-LIME-lp_mechanics.md +++ b/protocol/0044-LIME-lp_mechanics.md @@ -1,16 +1,57 @@ # Liquidity provision mechanics -The point of liquidity provision on Vega is to incentivise people to place orders on the market that maintain liquidity on the book. This is done via a financial commitment and reward + penalty mechanics, and through the use of a special batch order type that automatically updates price/size as needed to meet the commitment and automatically refreshes its volume after trading to ensure continuous liquidity provision. +The point of liquidity provision on Vega is to incentivise people to place orders on the market that maintain liquidity on the book. +This is done via a financial commitment and reward + penalty mechanics, and through the LP commitment transaction that announces that a party is entering the liquidity provision (LP) service level agreement (SLA). -Each market on Vega must have at least one committed liquidity provider (LP). -This is enforced when a [governance proposal to create a market is submitted](./0028-GOVE-governance.md#1-create-market). -In particular the proposal has to include [liquidity provision commitment](./0038-OLIQ-liquidity_provision_order_type.md), -see also below. Important note on wording: - liquidity provision / liquidity COMMITMENTs are the amount of stake a liquidity provider places as a bond on the market to earn rewards. -- the COMMITMENT is converted to a liquidity OBLIGATION, measured in siskas. +- the COMMITMENT is converted via a multiplicative network parameter `market.liquidity.stakeToCcyVolume` to a liquidity OBLIGATION, measured in price level x volume i.e. settlement currency of the market. + +## Network and market parameters + +### Network parameters + +- `market.liquidity.bondPenaltyParameter` - used to calculate the penalty to liquidity providers when they fail to support their open position through sufficient `general+margin` balance. +Valid values: any decimal number `>= 0` with a default value of `0.1`. +- `market.liquidity.sla.nonPerformanceBondPenaltySlope` - used to calculate how much is the LP bond slashed if they fail to reach the minimum SLA. Valid values: any decimal number `>= 0` with a default value of `2.0`. +- `market.liquidity.sla.nonPerformanceBondPenaltyMax` - used to calculate how much is the LP bond slashed if they fail to reach the minimum SLA. Valid values: any decimal number `>= 0` and `<=1.0` with a default value of `0.5`. +- `market.liquidity.maximumLiquidityFeeFactorLevel` - used in validating fee amounts that are submitted as part of the LP commitment transaction. Note that a value of `0.05 = 5%`. Valid values are: any decimal number `>=0` and `<=1`. Default value `1`. +- `market.liquidity.stakeToCcyVolume` - used to translate a commitment to an obligation. Any decimal number `>0` with default value `1.0`. +- `validators.epoch.length` - LP rewards from liquidity fees are paid out once per epoch according to whether they met the "SLA" (implied by `market.liquidity.commitmentMinTimeFraction`) and their previous performance (for the last n epochs defined by `market.liquidity.performanceHysteresisEpochs`), see [epoch spec](./0050-EPOC-epochs.md). +- `market.liquidity.earlyExitPenalty` (decimal ≥0), sets how much LP forfeits of their bond in case the market is below target stake and they wish to reduce their commitment. If set to `0` there is no penalty for early exit, if set to `1` their entire bond is forfeited if they exit their entire commitment, if set >1, their entire bond will be forfeited for exiting `1/earlyExitPenalty` of their commitment amount. +- `market.liquidity.probabilityOfTrading.tau.scaling` sets how the probability of trading is calculated from the risk model; this is used to [measure the relative competitiveness of LPs supplied volume](0042-LIQF-setting_fees_and_rewarding_lps.md). +- `market.liquidity.minimum.probabilityOfTrading.lpOrders` sets a lower bound on the result of the probability of trading calculation. +- `market.liquidity.feeCalculationTimeStep` (time period e.g. `1m`) controls how often the quality of liquidity supplied by the LPs is evaluated and fees arising from that period are earmarked for specific parties. Minimum valid value is anything more than or equal `1s`. Maximum valid value `validators.epoch.length`. + +### Market parameters + +All following market parameters can be set / modified as part of [market proposal](0028-GOVE-governance.md) / market change proposal and the new value take effect at the first new epoch after enactment. + +- `market.liquidity.priceRange` (decimal) - this is a percentage price move (e.g. `0.05 = 5%`) from `mid_price` during continuous trading or indicative uncrossing price during auctions. + +- `market.liquidity.commitmentMinTimeFraction` (decimal) — minimum fraction of time LPs must spend "on the book" providing their committed liquidity. This is a decimal number in the interval $[0,1]$ i.e. both limits included. When set to $0$ the SLA mechanics are switched off for the market entirely. + +- `market.liquidity.performanceHysteresisEpochs` (uint) - number of liquidity epochs over which past performance will continue to affect rewards. + +- `market.liquidity.slaCompetitionFactor` - the maximum fraction of their accrued fees an LP that meets the SLA implied by `market.liquidity.commitmentMinTimeFraction` will lose to LPs that achieved a higher SLA performance than them. + +For LP reward calculations based on the SLA see the [0042-LIQF spec](./0042-LIQF-setting_fees_and_rewarding_lps.md). + + +## Mechanism overview + +At a high level, the liquidity mechanism in Vega allows Liquidity Providers (LPs) to commit liquidity to a market and "bid" to set the liquidity fee on the market. LPs that meet this commitment are rewarded from the fee revenue. This is done by: + +- Requiring LPs to meet an SLA (i.e. % of time spent providing liquidity within the defined range) in order to be rewarded. + +- Rewarding LPs more for better performance against the SLA vs other LPs, ensuring there is an incentive to do more than the bare minimum and more than other LPs, if market conditions allow. + +- Penalising LPs that commit and do not meet the SLA, to reduce the attractiveness of opportunistically going after rewards with no intention to meet the SLA in more challenging conditions, and of leeching style attacks on the rewards. + +Once committed LPs attempt to meet their commitment by placing and maintaining normal orders on the market. They may use pegged or priced limit orders, along with features like post only and iceberg (or more accurately, transparent iceberg) to control their risk. Non-persistent orders, parked pegged orders, and stop-loss orders do not count towards an LP's supplied liquidity and therefore cannot be used to meet the SLA. + ## Commit liquidity network transaction @@ -19,87 +60,106 @@ Any Vega participant can apply to become a liquidity provider (LP) on a market b 1. Market ID 1. COMMITMENT AMOUNT: liquidity commitment amount (specified as a unitless number that represents the amount of settlement asset of the market) 1. FEES: nominated [liquidity fee factor](./0029-FEES-fees.md), which is an input to the calculation of taker fees on the market, as per [setting fees and rewarding lps](./0042-LIQF-setting_fees_and_rewarding_lps.md) -1. ORDERS: a set of _liquidity buy orders_ and _liquidity sell orders_ ("buy shape" and "sell shape") to meet the liquidity provision obligation, see [MM orders spec](./0038-OLIQ-liquidity_provision_order_type.md). Accepted if all of the following are true: -- The order is valid - see [0038-OLIQ-Liquidity provision order type spec](./0038-OLIQ-liquidity_provision_order_type.md) for full details -- The participant has sufficient collateral in their general account to meet the size of their nominated commitment amount as well as the margin requirements +- The transaction is valid: the submitting party has sufficient collateral in their general account to meet the size of their nominated commitment amount. - The market is in a state that accepts new liquidity provision [market lifecycle spec](./0043-MKTL-market_lifecycle.md). -General notes: -- If market is in auction mode it won't be possible to check the margin requirements for orders generated from LP commitment. If on transition from auction the funds in margin and general accounts are insufficient to cover the margin requirements associated with those orders funds in bond account should be used to cover the shortfall (with no penalty applied as outlined in the [Penalties](#penalties) section). If even the entire bond account balance is insufficient to cover those margin requirement the liquidity commitment transaction should get cancelled. - -### Valid submission combinations - -Assume `MarketID` is always submitted, then a participant can submit the following combinations: - -1. A transaction containing all fields specified can be submitted at any time to either create or change a commitment (if commitment size is zero, the orders and fee bid cannot be supplied - i.e. tx is invalid) -1. Any other combination of a subset of fields can be supplied any time a liquidity provider has a non-zero commitment already, to request to amend part of their commitment. - -Example: it's possible to amend fee bid or orders individually or together without changing the commitment level. -Example: amending only a commitment amount but retaining old fee bid and orders is also allowed. - -## COMMITMENT AMOUNT +## Commitment amount ### Processing the commitment When a commitment is made the liquidity commitment amount is assumed to be specified in terms of the settlement currency of the market. There is an minimum LP stake which is `market.liquidityProvision.minLpStakeQuantumMultiple x quantum` where `quantum` is specified per asset, see [asset framework spec](./0040-ASSF-asset_framework.md). -If the participant has sufficient collateral to cover their commitment and margins for the orders generated from their proposed commitment, the commitment amount (stake) is transferred from the participant's general account to their (maybe newly created) [liquidity provision bond account](./0013-ACCT-accounts.md#liquidity-provider-bond-accounts) (new account type, 1 per liquidity provider per market and asset where they are commitment liquidity, created as needed). For clarity, liquidity providers will have a separate [margin account](./0013-ACCT-accounts.md#trader-margin-accounts) and [bond account](./0013-ACCT-accounts.md#liquidity-provider-bond-accounts). +If the participant has sufficient collateral to cover their commitment, the commitment amount (stake) is transferred from the participant's general account to their (maybe newly created) [liquidity provision bond account](./0013-ACCT-accounts.md#liquidity-provider-bond-accounts) (new account type, 1 per liquidity provider per market and asset where they are committing liquidity, created as needed). +For clarity, liquidity providers will have a separate [margin account](./0013-ACCT-accounts.md#trader-margin-accounts) and [bond account](./0013-ACCT-accounts.md#liquidity-provider-bond-accounts). + +A new or increased commitment will get activated in two stages.(*) First the commitment amount (increase) will get transferred from general to bond account. +Their obligation for providing liquidity under SLA is determined by their commitment from the beginning of the current epoch (so, in particular, for a new LP, it's 0). +Second, at the beginning of the next epoch (after the rewards/penalties for present LPs have been evaluated), the commitment amount is noted, and the LP is expected to provide sufficient liquidity for the epoch. + +(*) The exception is the end of the opening auction of the market. The LPs that submit a commitment during the opening auction become the market LPs as soon as the opening auction ends. + +The fee for the market is only [updated at the epoch boundary using the "auction" mechanism set here](0042-LIQF-setting_fees_and_rewarding_lps.md). Liquidity provider bond account: - Each active market has one bond account per liquidity provider, per settlement asset for that market. - When a liquidity provider transaction is approved, the size of their staked bond is immediately transferred from their general account to this bond account. -- A liquidity provider can only prompt a transfer of funds to or from this account by submitting a valid transaction to create, increase, or decrease their commitment to the market, which must be validated and pass all checks (e.g. including those around minimum liquidity commitment required, when trying to reduce commitment). +- A liquidity provider can only prompt a transfer of funds to or from this account by (re)submitting the LP commitment transaction: a valid transaction to create, increase, or decrease their commitment to the market. - Transfers to/from this account also occur when it is used for settlement or margin shortfall, when penalties are applied, and if the account is under-collateralised because of these uses and is subsequently topped up to the commitment amount during collateral search (see below) - Collateral withdrawn from this account may only be transferred to either: - - The insurance pool of the market (in event of penalties/slashing) + - The insurance pool of the market for markets trading on margin or the network treasury for the asset (for spot markets) (in event of penalties/slashing) - The liquidity provider's margin account or the network's settlement account/other participant's margin accounts (during a margin search and mark to market settlement) in the event that they have zero balance in their general account. - The liquidity provider's general account (in event of liquidity provider reducing their commitment) -### liquidity provider proposes to amend commitment amount +### Liquidity provider proposes to amend commitment amount The commitment transaction is also used to amend any aspect of their liquidity provision obligations. A participant may apply to amend their commitment amount by submitting a transaction for the market with a revised commitment amount. `proposed-commitment-variation = new-proposed-commitment-amount - old-commitment-amount` -#### INCREASING COMMITMENT +An increase in amendment is actioned immediately but only has implications for rewards / penalties at start of the next epoch. + +1) the amount is immediately transferred from the party's general account to their bond account. However we keep track of commitment at start of epoch and this is used for penalties / rewards. +2) at the beginning of the next epoch, the rewards / penalties for present LPs - including the party that's amending - are evaluated based on balance of bond account at start of epoch. + +A decrease in commitment is noted but the transfer out of the bond account is only actioned at the end of the current epoch. + +For each party only the most recent amendment should be considered. All the amendments get processed simultaneously, hence the relative arrival of amendments made by different LPs within the previous epoch is irrelevant (as far as commitment reduction is concerned, it still has implications for other aspects of the mechanism). + +#### Increasing commitment _Case:_ `proposed-commitment-variation >= 0` -A liquidity provider can always increase their commitment amount as long as they have sufficient collateral in the settlement asset of the market to meet the new commitment amount and cover the margins required. -If they do not have sufficient collateral the transaction is rejected in entirety. This means that any data from the fees or orders are not applied. This means that the `old-commitment-amount` is retained. +A liquidity provider can always increase their commitment amount as long as they have sufficient collateral in the settlement asset of the market to meet the new commitment amount. + +If they do not have sufficient collateral the transaction is rejected in entirety. This means that any change in fee bid is not applied and that the `old-commitment-amount` is retained. -#### DECREASING COMMITMENT +#### Decreasing commitment _Case:_ `proposed-commitment-variation < 0` -We to calculate whether the liquidity provider may lower their commitment amount and if so, by how much. To do this we first evaluate the maximum amount that the market can reduce by given the current liquidity demand in the market. -`maximum-reduction-amount = total_stake - target_stake` +At the beginning of each epoch, calculate actual commitment variation for each LP wishing to reduce their commitment as: + +$$ +\text{commitment-variation}_i=\min(-\text{proposed-commitment-variation}_i, \text{bond account balance}_i). +$$ + +Next, calculate how much the overall commitment within the market can be decreased by without incurring a penalty. +To do this we first evaluate the maximum amount that the `total_stake` can reduce by without penalty given the current liquidity demand in the market. + +`maximum-penalty-free-reduction-amount = max(0,total_stake - target_stake)` where: -- `total_stake` is the sum of all stake of all liquidity providers bonded to this market. +- `total_stake` is the sum of all stake of all liquidity providers bonded to this market including the amendments with positive commitment variation submitted in the previous epoch. - `target_stake` is a measure of the market's current stake requirements, as per the calculation in the [target stake](./0041-TSTK-target_stake.md). -- `actual-reduction-amount = min(-proposed-commitment-variation, maximum-reduction-amount)` -- `new-actual-commitment-amount = old-commitment-amount - actual-reduction-amount` -- `market.liquidityProvision.shapes.maxSize` is the maximum entry of the LP order shape on liquidity commitment. -i.e. liquidity providers are allowed to decrease the liquidity commitment subject to there being sufficient stake committed to the market so that it stays above the market's required stake threshold. The above formulae result in the fact that if `maximum-reduction-amount = 0`, then `actual-reduction-amount = 0` and therefore the liquidity provider is unable to reduce their commitment amount. +Then, for each $LP_i$ we calculate the pro rata penalty-free reduction amount: + +```math +\text{maximum-penalty-free-reduction-amount}_i=\frac{\text{commitment-variation}_i}{\sum_{j}\text{commitment-variation}_j} \cdot \text{maximum-penalty-free-reduction-amount}. +``` + +If $\text{commitment-variation}_i <= \text{maximum-penalty-free-reduction-amount}_i$ then we're done, the LP reduced commitment, the entire amount by which they decreased their commitment is transferred to their general account, their ELS got updated as per the [ELS calculation](0042-LIQF-setting_fees_and_rewarding_lps.md). + +If $\text{commitment-variation}_i > \text{maximum-penalty-free-reduction-amount}_i$ then first establish -When `actual-reduction-amount > 0`: +$$ +\text{penalty-incurring-reduction-amount}_i = \text{commitment-variation}_i - \text{maximum-penalty-free-reduction-amount}_i +$$ -- the difference between their actual staked amount and new commitment is transferred back to their general account, i.e. -`transferred-to-general-account-amount = actual-stake-amount - new-actual-commitment-amount` -- the revised fee amount and set of orders are processed. +Transfer $\text{maximum-penalty-free-reduction-amount}_i$ to their general account. + +Now transfer $(1-\text{market.liquidity.earlyExitPenalty}) \cdot \text{penalty-incurring-reduction-amount}_i$ to their general account and transfer $\text{market.liquidity.earlyExitPenalty} \cdot \text{penalty-incurring-reduction-amount}_i$ to the market insurance pool. + +Finally update the ELS as per the [ELS calculation](0042-LIQF-setting_fees_and_rewarding_lps.md) using the entire $\text{commitment-variation}_i$ as the `delta`. -Example: if you have a commitment of 500DAI and your bond account only has 400DAI in it (due to slashing - see below), and you submit a new commitment amount of 300DAI, then we only transfer 100DAI such that your bond account is now square. -When `actual-reduction-amount = 0` the transaction is still processed for any data and actions resulting from the transaction's new fees or order information. ## Fees @@ -113,42 +173,86 @@ The [liquidity_fee](./0029-FEES-fees.md) of a market on Vega takes as an input, ### Distributing fees between liquidity providers -When calculating fees for a trade, the size of a liquidity provider’s commitment along with when they committed and the market size are inputs that will be used to calculate how the liquidity fee is distributed between liquidity providers. See [setting fees and rewarding lps](./0042-LIQF-setting_fees_and_rewarding_lps.md) for the calculation of the split. +When calculating fees for a trade, the size of a liquidity provider’s commitment at the start of the epoch along with when they committed and the market size are inputs that will be used to calculate how the liquidity fee is distributed between liquidity providers. See [setting fees and rewarding lps](./0042-LIQF-setting_fees_and_rewarding_lps.md) for the calculation of the split. -## Orders (buy shape/sell shape) -In a market maker proposal transaction the participant must submit a valid set of liquidity provider orders (buy shape and sell shape). Liquidity provider orders are a special order type described in the [liquidity provision orders spec](./0038-OLIQ-liquidity_provision_order_type.md). Validity is also defined in that spec. Note, liquidity provider participants can place regular (non liquidity provider orders) and these are considered to be contributing to them meeting their obligation, but they must also have provided the set of valid buy/sell orders as described in the [liquidity provision orders spec](./0038-OLIQ-liquidity_provision_order_type.md). +## Liquidity provision and penalties -A liquidity provider can amend their orders by providing a new set of liquidity provision orders in the liquidity provider network transaction. If the amended orders are invalid the transaction is rejected, and the previous set of orders will be retained. +### Calculating liquidity from commitment -### Checking margins for orders +Committed Liquidity Providers are required to provide a multiple of their stake, at the start of the epoch, (supplied in the settlement currency of the market) in notional volume of orders within the range defined below on both sides of the order book. -As pegged orders are parked during an auction and not placed on the book, margin checks will not occur for these orders. This includes checking the orders margin when checking the validity of the transaction so orders are accepted. Open positions are treated the same as any other open positions and their liquidity provider orders are pegged orders and will be treated the same as any other pegged orders. +The multiple is controlled by a network parameter `market.liquidity.stakeToCcyVolume`: -## Liquidity provision and penalties +```text +liquidity_required = stake_amount ✖️ market.liquidity.stakeToCcyVolume +``` -### Calculating liquidity from commitment +### Meeting the committed volume of notional + +#### During continuous trading + +If there is no mid price then no LP is meeting their committed volume of notional. +If there is mid price then we calculate the volume of notional that is in the range + +```text +(1.0-market.liquidity.priceRange) x mid <= price levels <= (1+market.liquidity.priceRange)x mid. +``` + +If this is greater than or equal to `liquidity_required` then the LP is meeting the committed volume of notional. + +#### During auctions + +We calculate the volume of notional that is in the range -Each liquidity provider supplies an amount of liquidity which is calculated from their commitment (stake) and measured in 'currency siskas' (i.e. USD siskas, ETH siskas, etc.).This is calculated by multiplying the stake by the network parameter `market.liquidity.stakeToCcyVolume` as follows: +```text +(1.0-market.liquidity.priceRange) x min(last trade price, indicative uncrossing price) <= price levels <= (1.0+market.liquidity.priceRange) x max(last trade price, indicative uncrossing price). +``` -`lp_liquidity_obligation_in_ccy_volume = market.liquidity.stakeToCcyVolume ⨉ stake`. +If there is no 'indicative uncrossing price' then volume placed at any price should count towards the LP's commitment i.e the price range is interpreted as -Note here "ccy" stands for "currency". Liquidity measure units are 'currency x volume'. This is because the calculation is basically `volume ⨉ price of the volume` and the price of the volume is in the said currency. +```text +-infinity <= price levels <= infinity +``` -### How liquidity is supplied +If this is greater than or equal to `liquidity_required` then the LP is meeting the committed volume of notional. -When a liquidity provider commits to a market, the LP Commitment transaction includes a _buy shape_ and _sell shape_ which allow the LP to spread their liquidity provision over a number of pegged orders at varying distances from the best prices on each side of the book. These 'shapes' are used to generate pegged orders (see the [liquidity provision order type spec](./0038-OLIQ-liquidity_provision_order_type.md)). -Since liquidity provider orders automatically refresh, the protocol ensures that a liquidity provider always supplies their committed liquidity as long as they have sufficient capital to meet the margin requirements of these orders. +Note: we don't evaluate whether LPs meet the SLA during opening auctions so there will always be a mark price. -**During auction:** -- Pegged orders generated from liquidity provider Commitments are parked like all pegged orders during auctions. Limit orders placed by liquidity providers obey the normal rules for their specific order type in an auction. -- Liquidity providers are not required to supply any orders that offer liquidity or trade during an auction uncrossing. They must maintain their stake however and their liquidity will be placed back on the book when normal trading resumes. +### Penalty for not meeting the SLA -### Penalties +See the [Calculating the SLA performance penalty for a single epoch section in 0042-LIQF](./0042-LIQF-setting_fees_and_rewarding_lps.md) for how `fraction_of_time_on_book` is calculated. +This is available at the end of each epoch. +If, at the end of the epoch, `fraction_of_time_on_book >= market.liquidity.commitmentMinTimeFraction` then let $f=0$. +Otherwise we calculate a penalty to be applied to the bond as follows. -If at any point in time, a liquidity provider has insufficient capital to make the transfers for their mark to market or other settlement movements, and/or margin requirements arising from their orders and open positions, the network will utilise their liquidity provision commitment, held in the _liquidity provider bond account_ to cover the shortfall. The protocol will also apply a penalty proportional to the size of the shortfall, which will be transferred to the market's insurance pool. +Let $t$ be `fraction_of_time_on_book` + +Let $s$ be `market.liquidity.commitmentMinTimeFraction`. + +Let $p$ be `market.liquidity.sla.nonPerformanceBondPenaltySlope`. + +Let $m$ be `market.liquidity.sla.nonPerformanceBondPenaltyMax`. + +Let + +$$ +f = \max\left[0,\min\left(m, p \cdot (1 - \frac{t}{s})\right)\right]\,. +$$ + +Once you have $f$ transfer $f \times B$ into the insurance pool of the market, where $B$ is the LP bond account balance. + +Moreover, as this reduced the LP stake, update the ELS as per [Calculating liquidity provider equity-like share section in 0042-LIQF](./0042-LIQF-setting_fees_and_rewarding_lps.md). + +In the case of if a liquidity provider has `fraction_of_time_on_book = 0`, and `market.liquidity.commitmentMinTimeFraction = 0`, then `f = max[0,min(m,p)]` + + +### Penalty for not supporting open positions + +If at any point in time, a liquidity provider has insufficient capital to make the transfers for their mark to market or other settlement movements, and/or margin requirements arising from their orders and open positions, the network will utilise their liquidity provision commitment, held in the _liquidity provider bond account_ to cover the shortfall. +The protocol will also apply a penalty proportional to the size of the shortfall, which will be transferred to the market's insurance pool. Calculating the penalty: @@ -163,39 +267,25 @@ _Auctions:_ if this occurs at the transition from auction mode to continuous tra The network will: -1. _As part of the normal collateral "search" process:_ Access the liquidity provider's bond account to make up the shortfall. If there is insufficient funds to cover this amount, the full balance of the bond account will be used. Note that this means that the transfer request should include the liquidity provider's bond account in the list of accounts to search, and that the bond account would always be emptied before any insurance pool funds are used or loss socialisation occurs. +1. _As part of the normal collateral "search" process:_ Access first the liquidity provider's bond account to make up the shortfall. If there is insufficient funds to cover this amount, the full balance of both bond accounts will be used. Note that this means that the transfer request should include the liquidity provider's bond account in the list of accounts to search, and that these accounts would always be emptied before any market insurance pool funds are used or loss socialisation occurs. -1. _If there was a shortfall and the bond account was accessed:_ Transfer an amount equal to the `market.liquidity.bondPenaltyParameter` calculated above from the liquidity provider's bond account to the market's insurance pool. If there are insufficient funds in the bond account, the full amount will be used and the remainder of the penalty (or as much as possible) should be transferred from the liquidity provider's margin account. +1. _If there was a shortfall and the bond account was accessed:_ Transfer an amount equal to the `market.liquidity.bondPenaltyParameter` calculated above from the liquidity provider's bond account to the market's insurance pool. If there are insufficient funds in the bond account and the bond account, the full amount will be used and the remainder of the penalty (or as much as possible) should be transferred from the liquidity provider's margin account. 1. Initiate closeout of the LPs order and/or positions as normal if their margin does not meet the minimum maintenance margin level required. (NB: this should involve no change) -1. _If the liquidity provider's orders or positions were closed out, and they are therefore no longer supplying the liquidity implied by their Commitment:_ In this case the liquidity provider's Commitment size is set to zero and they are no longer a liquidity provider for fee/reward purposes, and their commitment can no longer be counted towards the supplied liquidity in the market. (From a Core perspective, it is as if the liquidity provider has exited their commitment entirely and they no longer need to be tracked as an LP.) +1. _The liquidity provider's bond account balance is always their current commitment level:_ This is strictly their "true" bond account. Note: -- As mentioned above, closeout should happen as per regular trader account (with the addition of cancelling the liquidity provision and the associated LP rewards & fees consequences). So, if after cancelling all open orders (both manually maintained and the ones created automatically as part of liquidity provision commitment) the party can afford to keep the open positions sufficiently collateralised they should be left open, otherwise the positions should get liquidated. -- Bond account balance should never get directly confiscated. It should only be used to cover margin shortfalls with appropriate penalty applied each time it's done. Once the funds are in margin account they should be treated as per normal rules involving that account. - -### Bond account top up by collateral search - -In the same way that a collateral search is initiated to attempt to top-up a trader's margin account if it drops below the _collateral search level_, the system must also attempt to top up the bond account if its balance drops below the size of the LP's commitment. +- As mentioned above, closeout should happen as per regular trader account (with the addition of cancelling the liquidity provision and the associated LP rewards & fees consequences). So, if after cancelling all open orders the party can afford to keep the open positions sufficiently collateralised they should be left open, otherwise the positions should get liquidated. -This should happen every time the network is performing a margin calculation and search. The system should prioritise topping up the margin account first, and then the bond account, if there are insufficient funds to do both. - -## Network parameters - -- `market.liquidity.bondPenaltyParameter` - used to calculate the penalty to liquidity providers when they fail to meet their obligations. -Valid values: any decimal number `>= 0` with a default value of `0.1`. -- `market.liquidity.maximumLiquidityFeeFactorLevel` - used in validating fee amounts that are submitted as part of [lp order type](./0038-OLIQ-liquidity_provision_order_type.md). Note that a value of `0.05 = 5%`. Valid values are: any decimal number `>0` and `<=1`. Default value `1`. -- `market.liquidity.stakeToCcySiskas` - used to translate a commitment to an obligation (in siskas). Any decimal number `>0` with default value `1`. ## What data do we keep relating to liquidity provision? -1. List of all liquidity providers and their commitment sizes and their “equity-like share” for each market [see 0042-setting-fees-and-rewarding-lps](./0042-LIQF-setting_fees_and_rewarding_lps.md) -1. Liquidity provision orders (probably need to be indexed somewhere in addition to the order book) -1. New account per market holding all committed liquidity provider bonds +1. List of all liquidity providers and their commitment sizes (bond account balance), the commitment at the start of epoch, their “equity-like share” and "liquidity score" for each market [see 0042-setting-fees-and-rewarding-lps](./0042-LIQF-setting_fees_and_rewarding_lps.md) +1. New bond account per LP per market 1. Actual amount of liquidity supplied (can be calculated from order book, [see 0034-prob-weighted-liquidity-measure](./0034-PROB-prob_weighted_liquidity_measure.ipynb)) -1. Each liquidity provider's actual bond amount (i.e. the balance of their bond account) + ## APIs @@ -204,12 +294,127 @@ Valid values: any decimal number `>= 0` with a default value of `0.1`. ## Acceptance Criteria -- Through the API, I can list all active liquidity providers for a market (0044-LIME-001) +- Through the `LiquidityProvisions` API, I can list all active liquidity providers for a market (0044-LIME-001) +- Through the `LiquidityProviders` API, I can list all active liquidity providers fee share information + - GRPC (0044-LIME-057) + - GRAPHQL (0044-LIME-058) + - REST (0044-LIME-059) +- When a LP commits liquidity on market 1, on market 2 this LP has no liquidity commitment when I request for all LP provisions through `ListLiquidityProvisions` api for this party, then only LP provisions for market 1 is returned. (0044-LIME-087) - The [bond slashing](https://github.com/vegaprotocol/vega/blob/develop/core/integration/features/verified/liquidity-provision-bond-account.feature) works as the feature test claims. (0044-LIME-002). -- Change of network parameter `market.liquidity.bondPenaltyParameter` will immediately change the amount by which the bond account will be 'slashed' when a liquidity provider has insufficient capital for Vega to make the transfers for their mark to market or other settlement movements, and/or margin requirements arising from their orders and open positions. (0044-LIME-003) -- Change of `market.liquidityProvision.shapes.maxSize` will change the maximum number of entries in the order shape of the LP commitment. If `market.liquidityProvision.shapes.maxSize` is decreased all the LP orders that have already been submitted are unaffected. However any new submissions or amendments must respect the new (lower) maximum. (0044-LIME-005) -- Change of `market.liquidity.maximumLiquidityFeeFactorLevel` will change the maximum liquidity fee factor. Any LP orders that have already been submitted are unaffected but any new submission or amendments must respect the new maximum (those that don't get rejected). (0044-LIME-006) +- Change of network parameter `market.liquidity.bondPenaltyParameter` will, as soon as the current epoch ends, change the amount by which the bond account will be 'slashed' when a liquidity provider has insufficient capital for Vega to make the transfers for their mark to market or other settlement movements, and/or margin requirements arising from their orders and open positions. (0044-LIME-003) +- Change of `market.liquidity.maximumLiquidityFeeFactorLevel` will change the maximum liquidity fee factor. Any new submission or amendments must respect the new maximum (those that don't get rejected). (0044-LIME-006) - Check that bond slashing works with non-default asset decimals, market decimals, position decimals. This can be done by following a similar story to [bond slashing feature test](https://github.com/vegaprotocol/vega/blob/develop/core/integration/features/verified/liquidity-provision-bond-account.feature). Should test at least three different combinations, each decimal settings different to each other. (0044-LIME-009) -- Change of `market.liquidity.stakeToCcyVolume` will change the liquidity obligation hence change the size of the LP orders on the order book. (0044-LIME-010) -- If `market.liquidity.stakeToCcyVolume` is set to `0.0` then the [LP provision order](./0038-OLIQ-liquidity_provision_order_type.md) places `0` volume on the book for the LP regardless of the shape submitted and regardless of the `stake` committed. (0044-LIME-011) - If `market.liquidity.stakeToCcyVolume` is set to `0.0`, there is [target stake](./0041-TSTK-target_stake.md) of `1000` and there are 3 LPs on the market with stake / fee bid submissions of `100, 0.01`, `1000, 0.02` and `200, 0.03` then the liquidity fee is `0.02`. (0044-LIME-012) + +- If a liquidity provider has `fraction_of_time_on_book` >= `market.liquidity.commitmentMinTimeFraction`, no penalty will be taken from their bond account (0044-LIME-013) +- If a liquidity provider has `fraction_of_time_on_book` = `0.3`, `market.liquidity.commitmentMinTimeFraction = 0.6`, `market.liquidity.sla.nonPerformanceBondPenaltySlope = 0.7`, `market.liquidity.sla.nonPerformanceBondPenaltyMax = 0.6` at the end of an epoch then they will forfeit `35%` of their bond stake, which will be transferred into the market's insurance pool (0044-LIME-014) +- If a liquidity provider has `fraction_of_time_on_book` = `0.3`, `market.liquidity.commitmentMinTimeFraction = 0.6`, `market.liquidity.sla.nonPerformanceBondPenaltySlope = 0.7`, `market.liquidity.sla.nonPerformanceBondPenaltyMax = 0.6`and the market parameter change `market.liquidity.commitmentMinTimeFraction = 0.3` is enacted during the epoch then at the end of the current epoch LP will have their bond slashed. If the LP has `fraction_of_time_on_book` = `0.3` at the end of the next epoch, they are meeting their commitment and will not forfeit any of their bond stake. (0044-LIME-088) +- If a liquidity provider has `fraction_of_time_on_book` = `0.3`, `market.liquidity.commitmentMinTimeFraction = 0.0`, `market.liquidity.sla.nonPerformanceBondPenaltySlope = 0.7`, `market.liquidity.sla.nonPerformanceBondPenaltyMax = 0.6`and the market parameter change `market.liquidity.commitmentMinTimeFraction = 0.6` is enacted during the epoch then at the end of the current epoch LP will not forfeit any of their bond stake. If the LP has `fraction_of_time_on_book` = `0.3` at the end of the next epoch at the end of the next epoch, the LP will have their bond slashed. (0044-LIME-089) +- If a liquidity provider has `fraction_of_time_on_book` = `0`, `market.liquidity.commitmentMinTimeFraction = 0.6`, `market.liquidity.sla.nonPerformanceBondPenaltySlope = 0.7`, `market.liquidity.sla.nonPerformanceBondPenaltyMax = 0.6` at the end of an epoch then they will forfeit `60%` of their bond stake, which will be transferred into the market's insurance pool (0044-LIME-015) +- If a liquidity provider has `fraction_of_time_on_book` = `0`, `market.liquidity.commitmentMinTimeFraction = 0.6`, `market.liquidity.sla.nonPerformanceBondPenaltySlope = 0.2`, `market.liquidity.sla.nonPerformanceBondPenaltyMax = 0.6` at the end of an epoch then they will forfeit `20%` of their bond stake, which will be transferred into the market's insurance pool (0044-LIME-016) + +- If a liquidity provider with an active liquidity provision at the start of an epoch reduces their liquidity provision staked commitment during the epoch the initial committed level at the start of the epoch will remain in effect until the end of the epoch, at which point the protocol will attempt to reduce the bond to the new level. (0044-LIME-018) +- If a liquidity provider with an active liquidity provision at the start of an epoch reduces their liquidity provision staked commitment during the epoch the initial committed level at the start of the epoch will remain in effect until the end of the epoch, at which point the protocol will attempt to reduce the bond to the new level. If the reduced level has been changed several times during an epoch, only the latest value will take effect (0044-LIME-019) +- If a liquidity provider with an active liquidity provision at the start of an epoch reduces their liquidity provision staked commitment during the epoch the initial committed level at the start of the epoch will remain in effect until the end of the epoch, at which point the protocol will attempt to reduce the bond to the new level. If the bond stake has been slashed to a level lower than the amendment, this slashed level will be retained (i.e. the protocol will not attempt to now increase the commitment) (0044-LIME-020) +- If a liquidity provider with an active liquidity provision at the start of an epoch amends the fee level associated to this commitment during the epoch, this change will only take effect at the end of the epoch. (0044-LIME-021) +- If a liquidity provider with an active liquidity provision at the start of an epoch increases their liquidity provision staked commitment during the epoch, the amended committed level will take affect immediately and the protocol will attempt to increase the bond to the new level if they do not have sufficient collateral in the settlement asset of the market to meet new commitment amount then the amendment will be rejected and old commitment amount is retained (0044-LIME-030) +- If a liquidity provider with an active liquidity provision at the start of an epoch increases their liquidity provision staked commitment during the epoch, the amended committed level will take affect immediately and + - the protocol will increase the bond to the new level if they have sufficient collateral in the settlement asset of the market to meet new commitment amount (0044-LIME-031) + - at the end of the current epoch rewards / penalties are evaluated based on the balance of the bond account at start of epoch (0044-LIME-049) + +- A liquidity provider who reduces their liquidity provision such that the total stake on the market is still above the target stake after reduction will have no penalty applied and will receive their full reduction in stake back at the end of the epoch. (0044-LIME-022) +- For a market with `market.liquidity.earlyExitPenalty = 0.25` and `target stake > total stake` already, a liquidity provider who reduces their commitment by `100` will only receive `75` back into their general account with `25` transferred into the market's insurance account. (0044-LIME-023) +- For a market with `market.liquidity.earlyExitPenalty = 0.25` and `total stake = target stake + 40` already, a liquidity provider who reduces their commitment by `100` will receive a total of `85` back into their general account with `15` transferred into the market's insurance account (`40` received without penalty, then the remaining `60` receiving a `25%` penalty). (0044-LIME-024) + +- For a market with `market.liquidity.earlyExitPenalty = 0.25` and `total stake = target stake + 140` already, if one liquidity provider places a transaction to reduce their stake by `100` followed by a second liquidity provider who reduces their commitment by `100`, the first liquidity provider will receive a full `100` stake back whilst the second will receive a total of `85` back into their general account with `15` transferred into the market's insurance account (`40` received without penalty, then the remaining `60` receiving a `25%` penalty). (0044-LIME-025) + + +- For a futures market with `market.liquidity.earlyExitPenalty = 0.25` and `total stake = target stake + 140` already, if the following transactions occur: + + - `LP1` places a transaction to reduce their stake by `30` + - `LP2` places a transaction to reduce their stake by `100`, and then wait until end of the epoch, + - `LP1` places a transaction to update their reduction to `100` + `LP2` will receive a full `100` stake back whilst `LP1` will receive a total of `85` back into their general account with `15` transferred into the market's insurance account (0044-LIME-026) +- When LP is committed they are obliged to provide liquidity equal to their commitment size on both sides of the order book (0044-LIME-027) +- For a market that is in opening auction and LP has committed liquidity: + - When a LP increases their commitment then: + - It takes effect immediately for the purposes of LP stake supplied to the market + - In terms of the liquidity they are expected to supply: this only takes effect from the start of the next epoch + (0044-LIME-050). + - LP can decrease or cancel their commitment and it will take effect immediately without incurring penalties (0044-LIME-051). + - If target stake is 0 then any LP can cancel their commitment without incurring penalties (0044-LIME-053) + +- For a market that is in continuous trading and a single LP has committed liquidity: + - The LP can cancel their commitment at any time (though this may involve incurring a penalty) (0044-LIME-060). +- During continuous trading an LP can submit a transaction to decrease commitment but it will only happen at the end of current epoch. (0044-LIME-101) + +- For a market that is in continuous trading and LP has committed liquidity + - if `market.liquidity.providersFeeCalculationTimeStep` is set to `10s` and `validators.epoch.length` is set to `15s`, during the first `10` seconds of the current epoch parameter change `market.liquidity.providersFeeCalculationTimeStep = 3s` is enacted, at the end of the epoch any funds that are in `ACCOUNT_TYPE_FEES_LIQUIDITY` account will be distributed to `ACCOUNT_TYPE_LP_LIQUIDITY_FEES` on the next block. For the next epoch the distribution will take place at `3` second intervals (0044-LIME-062) +- For a market that is in continuous trading if a new LP has active buy and sell orders on the market then makes a liquidity commitment to that market, at the start of the next epoch the active orders will count towards the LPs liquidity commitment. (0044-LIME-090) +- If an LP with a liquidity provision and active orders on a market cancels their liquidity provision (orders remain active), after the cancellation penalty (if it applies at end of epoch) at the end of the next epoch the LP will not accrue any rewards for trades on the market. (0044-LIME-097) +- Consider a market where `market.liquidity.priceRange = 0.05 (5%)` and which is in continuous trading with a mid price of 5. There is an LP who's committed to provide liquidity. They place a buy order at a price of 4.75 and a sell order at a price of 5.25 (with sufficient volume). As the epoch progresses, if the market parameter is altered to `market.liquidity.priceRange = 0.01` (1%), then upon the culmination of the current epoch, the LP will still have fulfilled their committed notional volume, rendering them exempt from a bond penalty. (0044-LIME-091) + +- Consider a market, where `market.liquidity.priceRange = 0.05`, and which is in continuous trading with a mid price set at 5. There is an LP who's committed to provide liquidity. They place a buy order at a price of 4.74 and a sell order at a price of 5.25 (with sufficient volume). As the epoch progresses, if the market parameter is altered to `market.liquidity.priceRange = 0.01` (1%), then upon the culmination of the ongoing epoch, the LP will not have met their committed notional volume, resulting in the imposition of a bond penalty. (0044-LIME-093) + +- Consider a market, where `market.liquidity.priceRange = 0.05`, and which is in monitoring auction with the last trade price set at 5, the indicative uncrossing price is set at 4. There is an LP who's committed to provide liquidity. They place a buy order at a price of 3.79 (which is less than `5%` of `1-0.05 x min(5, 4) = 3.80`), and a sell order at a price of 5.25 (with sufficient volume). At the end of the epoch, the LP has not fulfilled their committed notional volume, resulting in the imposition of a bond penalty. (0044-LIME-094) + +- Consider a market, where `market.liquidity.priceRange = 0.05`, and which is in monitoring auction with `last trade price` set to `5`, `indicative uncrossing price` is set to `4`. There is a LP who's committed to provide liquidity. They place a buy order at price `3.8` and a sell order at price `5.25` (with sufficient volume). At the end of the epoch, the LP is meeting their committed volume of notional rendering them exempt from a bond penalty. (0044-LIME-095) + +- Consider a market, where `market.liquidity.priceRange = 0.05`, and which is in monitoring auction with `last trade price` set to `5`, `indicative uncrossing price` is set to `6`. There is a LP who's committed to provide liquidity. They place a buy orders at price `4.75` and sell order at price `6.31` (which is larger than `5%` of `1+ 0.05 x max (5, 6) = 6.30`). At the end of the epoch, the LP is not meeting their committed volume of notional, resulting in the imposition of a bond penalty. (0044-LIME-096) + +- Consider a market, where `market.liquidity.priceRange = 0.05`, and which is in monitoring auction with `last trade price` set to `5` and we do not have `indicative uncrossing price`. There is a LP who's committed to provide liquidity. They place a buy orders at price `4.74` (which is less than `5%` of `1-0.05 x min(5, n/a) = 4.75`) and sell order at price `5.25`. At the end of the epoch, the LP is not meeting their committed volume of notional, resulting in the imposition of a bond penalty. (0044-LIME-098) + +- Consider a market, where `market.liquidity.priceRange = 0.05`, and which is in monitoring auction with `last trade price` set to `5` and we do not have `indicative uncrossing price`. There is a LP who's committed to provide liquidity. They place a buy orders at price `4.75` and sell order at price `5.26` (which is is more than `5%` of `1+ 0.05 x max (5, n/a) = 5.25`). At the end of the epoch, the LP is not meeting their committed volume of notional, resulting in the imposition of a bond penalty. (0044-LIME-099) + +- Consider a market, where `market.liquidity.priceRange = 0.05`, and which is in monitoring auction with `last trade price` set to `5` and we do not have `indicative uncrossing price`. There is a LP who's committed to provide liquidity. They place a buy orders at price `4.75` and sell order at price `5.25`. At the end of the epoch, the LP is meeting their committed volume of notional rendering them exempt from a bond penalty. (0044-LIME-092) + +- When the LP increases its commitment and the increment is higher than its general account balance, the increments are rejected, and the old provision persists. (0044-LIME-063). +- When LP decreases its commitment so that $\text{commitment-variation}_i <= \text{maximum-penalty-free-reduction-amount}_i$, then the entire amount by which they decreased their commitment is transferred to their general account, their ELS got updated as per the [ELS calculation](0042-LIQF-setting_fees_and_rewarding_lps.md)(0044-LIME-065). +- When LP decreases its commitment so that $\text{commitment-variation}_i > \text{maximum-penalty-free-reduction-amount}_i$ , $(1-\text{market.liquidity.earlyExitPenalty}) \cdot \text{penalty-incurring-reduction-amount}_i$ should be transferred into its general account and $\text{market.liquidity.earlyExitPenalty} \cdot \text{penalty-incurring-reduction-amount}_i$ should be transferred into market insurance pool (0044-LIME-067). +- When an LP creates a new provision with zero commitment, it should be rejected with an error message stating that the commitment amount is zero. (0044-LIME-069). +- When an LP amends the Fee Factor to a value greater than `market.liquidity.maximumLiquidityFeeFactorLevel`, the amendments are rejected (0044-LIME-071). +- A distressed LP (when `bond account == 0 && general account == 0 && margin account < maintenance margin`) will have their orders cancelled. If the LP remains distressed, the network takes over any positions the LP may hold and zeroes them out (closes them out) (0044-LIME-073). +- If a party submits LP provisions in multiple markets then multiple bond accounts are created and managed by Vega.(0044-LIME-075). + + +### Qualifying Order Types + +- Once liquidity is committed, LPs can meet their commitment by placing limit orders, pegged limit orders, and iceberg orders. For iceberg orders, all the volume (including displayed and remaining volume) counts towards the commitment. (0044-LIME-028). +- Parked pegged limit orders and stop-loss orders do not count towards an LPs liquidity commitment. (0044-LIME-077). +- GFA orders during auction from LP count towards LPs liquidity commitment (0044-LIME-079). +- GFA orders during continuous trading mode from LP do not count towards the LP's liquidity commitment (0044-LIME-081). + +### Snapshot + +- A snapshot must include the aggregate LP fee accounts and their balances so that after a node is started using the snapshot it can retain the aggregate LP fee accounts and their balances for each market. (0044-LIME-032) + +### Protocol upgrade + +- After a protocol upgrade each market's aggregate LP fee accounts and their balances are retained (0044-LIME-033) + +### Checkpoint + +- Each market's aggregate LP fee accounts must be included in the checkpoint and where the network is restored, the aggregate LP fee account balance will be transferred to the LP's general account. (0044-LIME-034) + +#### Network History - Data node restored from network history segments + +- A datanode restored from network history will contain each market's aggregate LP fee accounts which were created prior to the restore and these can be retrieved via APIs on the new datanode. (0044-LIME-036) + +#### Network parameters validation + +- Boundary values are respected for the network parameters + - `market.liquidityV2.bondPenaltyParameter` valid values: `>=0`, `<=1000` default value of `0.1` (0044-LIME-037) + - `market.liquidityV2.earlyExitPenalty` valid values: `>=0`, `<=1000` default value of `0.1` (0044-LIME-038) + - `market.liquidityV2.maximumLiquidityFeeFactorLevel` valid values: `>=0`, `<=1` default value of `1` (0044-LIME-039) + - `market.liquidityV2.sla.nonPerformanceBondPenaltySlope` valid values: `>=0`, `<=1000` default value of `2` (0044-LIME-040) + - `market.liquidityV2.sla.nonPerformanceBondPenaltyMax` valid values: `>=0`, `<=1` default value of `0.5` (0044-LIME-041) + - `market.liquidityV2.stakeToCcyVolume` valid values: `>=0`, `<=100` default value of `1` (0044-LIME-042) + - `market.liquidity.providersFeeCalculationTimeStep` valid values: `>0`, `<= validators.epoch.length` default value of `60m` (0044-LIME-100) + +#### Market parameters validation + +- Boundary values are respected for the market parameters + - `market.liquidity.commitmentMinTimeFraction` valid values: `>=0`, `<=1` (0044-LIME-083) + - `market.liquidity.priceRange` valid values: `>0`, `<=20` (0044-LIME-084) + - `market.liquidity.slaCompetitionFactor` valid values: `>=0`, `<=1` (0044-LIME-085) + - `market.liquidity.performanceHysteresisEpochs` valid values: `>=0`, `<=366` (0044-LIME-086) diff --git a/protocol/0045-DSRC-data_sourcing.md b/protocol/0045-DSRC-data_sourcing.md index 63f44d465..81001341c 100644 --- a/protocol/0045-DSRC-data_sourcing.md +++ b/protocol/0045-DSRC-data_sourcing.md @@ -236,13 +236,17 @@ Vega should reject any data source tx that is not explicitly required, so this w 1. Data is not applied retrospectively, i.e. if a previous historic data point or data transaction would have matched a newly created data source, it must not be identified and applied to the new data source (and therefore need not be stored by the core), only active data and new events created after the activation of the data source would be considered for the source. (0045-DSRC-009) 1. Two data sources with the same definition that are active at the same time must always select and receive exactly the same data, in the same order. (0045-DSRC-010) 1. Rejection of data sources either before submission/sequencing as transactions or when/if data is filtered/rejected after being sequenced on chain (if this happens - it should be avoided wherever possible to prevent spam attacks and reduce network load) must either result in an event queue message, return an error to the client, or enable the rejection (or not) to be confirmed by querying a core API (e.g. the filter, selector, or type check that failed). (0045-DSRC-011) -1. It's possible to query an API and see all active data sources. (0045-DSRC-012) +1. It's possible to query all active and deactivated data sources for a market via REST, gRPC and GraphQL. (0045-DSRC-012) 1. Party submitting an oracle transaction that gets rejected (e.g. because no data source is listening for transactions from such key) can receive an error message detailing reason for rejection. (0045-DSRC-013) 1. It's possible to listen to events and see all data that is supplied across all data sources or for any specific source. (0045-DSRC-014) 1. Data node carries historic data of at least all valid data that was supplied for each data source. (0045-DSRC-015) -1. Data sources can be composed/nested arbitrarily (as long as the definition is valid), for example selecting a field on filtered data that itself was sourced by selecting a field on a message sent by a signed data source (for example this might be processing a complex object in the source data. (0045-COSMICELEVATOR-016) +1. Data sources can be composed/nested arbitrarily (as long as the definition is valid), for example selecting a field on filtered data that itself was sourced by selecting a field on a message sent by a signed data source (for example this might be processing a complex object in the source data). (0045-DSRC-016) 1. A market proposal specifies data source where value used for settlement is integer with implied decimals; the implied decimals are included in the oracle spec; once trading terminated and settlement data is submitted the price is interpreted correctly for settlement purposes. E.g. market decimals `1`, market uses asset for settlement with `10` decimals, oracle implied decimals `5`, submitted value `10156789` interpreted as `101.56789`. In asset decimals this is `1015678900000` and this is used for settlement. (0045-DSRC-017) 1. Data source transactions can be submitted by a party with zero balance in all assets. (0045-DSRC-018) 1. After trading termination has been triggered the trading terminated data source is no longer active (assuming it is not used anywhere else) and data from that source is no longer processed (0045-DSRC-019) 1. After settlement data has been received and the market has settled, the settlement data source is no longer active (assuming it is not used anywhere else) and data from that source is no longer processed (0045-DSRC-020) 1. Where the same oracle definition is used for trading terminated and settlement, and data has been received. Trading is terminated and the market is settled with the same data event (a single message does both). The data source is no longer active (assuming it is not used anywhere else) and data from that source is no longer processed. (0045-DSRC-021) +1. It should be possible to update the market termination spec from external to internal and prove that the original termination key and filter no longer supports termination and that the updated market termination key and filter supports termination. This should apply to filter key types Timestamp/Boolean and Integer. (0045-DSRC-022) +1. It should be possible to update the market termination spec from internal to external and prove that the original termination key and filter no longer supports termination and that the updated market termination key and filter supports termination. This should apply to filter key types Timestamp/Boolean and Integer. (0045-DSRC-023) +1. It should not be possible to update the market settlement data spec to change the type from an external data source to an internal Vega timestamp data source. NB: this is because the internal Vega timestamp data source does not emit a value that can be used as a price, only a date/time. (0045-DSRC-024) +1. When using the update market transaction to switch between oracle types the protocol should identify invalid supplied combinations of data source data and reject the transaction via an appropriate message either within the rejected proposal or via the wallet response. (0045-DSRC-025) diff --git a/protocol/0046-DSRM-data_source_signed_message.md b/protocol/0046-DSRM-data_source_signed_message.md index 5d8d2af87..8283308c1 100644 --- a/protocol/0046-DSRM-data_source_signed_message.md +++ b/protocol/0046-DSRM-data_source_signed_message.md @@ -122,6 +122,8 @@ Other acceptance: - Must work with Coinbase oracle (0046-DSRM-012) - Ignore any `SubmitData` tx that is a duplicate (i.e. contains exactly the same data payload and is for the same data source), even if it is signed by a different signer (assuming the source has multiple configured signers) or was submitted by a different Vega key. (0046-DSRM-016) - Messages are accepted that contain the data and the signature (conforming to the Open Oracle specification) Note: do not support (or need to) direct connections to REST APIs, Ethereum smart contracts, etc. conforming to the open oracle spec. (0046-DSRM-017) +- Set up a [builtin product futures](./0016-PFUT-product_builtin_future.md) market with vega (internal) time triggered trading terminated oracle and an settlement oracle with a with key `k1`. Wait for time to pass for the market to move to trading terminated. Now submit a market change proposal to change the oracle to key `k2`. Wait for the vote to pass and enact. Verify that `k1` cannot settle the market. Now settle the market with `k2` and verify it settled at the correct price. (0046-DSRM-018). + ## Notes @@ -131,3 +133,4 @@ Other acceptance: - There is no explicit block list for unreliable signed message data sources or malicious public keys or addresses (signers). - There is no API required for signed message data sources except for the APIs defined for all data sources in the data sourcing framework spec. - There is no requirement for a party operating a signed message data source (i.e. the holder of the private key) to hold any collateral or any of the governance asset. + diff --git a/protocol/0047-DSRF-data_source_filter.md b/protocol/0047-DSRF-data_source_filter.md index 3028a7338..fcb4113dc 100644 --- a/protocol/0047-DSRF-data_source_filter.md +++ b/protocol/0047-DSRF-data_source_filter.md @@ -9,30 +9,30 @@ For example, a [signed message](./0046-DSRM-data_source_signed_message.md) data ```proto DATA_SOURCE = SignedMessage{ pubkey=0xA45e...d6 }, gives: - { ticker: 'TSLA', timestamp: '2021-12-31T00:00:00Z', price: 420.69} - { ticker: 'BTCUSD', timestamp: '2021-12-31T00:00:00Z', price: 42069.303} - { ticker: 'ETHGAS', timestamp: '2021-12-31T00:00:00Z', price: 100.1} - ... - { ticker: 'TSLA', timestamp: '2021-12-31T01:00:00Z', price: 469.20} - { ticker: 'BTCUSD', timestamp: '2021-12-31T01:00:00Z', price: 52069.42} - { ticker: 'ETHGAS', timestamp: '2021-12-31T01:00:00Z', price: 101.0} - ... - { ticker: 'TSLA', timestamp: '2021-12-31T02:00:00Z', price: 440.20} - { ticker: 'BTCUSD', timestamp: '2021-12-31T02:00:00Z', price: 501.666} - { ticker: 'ETHGAS', timestamp: '2021-12-31T02:00:00Z', price: 90.92} - ... and so on ... + { ticker: 'TSLA', timestamp: '2021-12-31T00:00:00Z', price: 420.69} + { ticker: 'BTCUSD', timestamp: '2021-12-31T00:00:00Z', price: 42069.303} + { ticker: 'ETHGAS', timestamp: '2021-12-31T00:00:00Z', price: 100.1} + ... + { ticker: 'TSLA', timestamp: '2021-12-31T01:00:00Z', price: 469.20} + { ticker: 'BTCUSD', timestamp: '2021-12-31T01:00:00Z', price: 52069.42} + { ticker: 'ETHGAS', timestamp: '2021-12-31T01:00:00Z', price: 101.0} + ... + { ticker: 'TSLA', timestamp: '2021-12-31T02:00:00Z', price: 440.20} + { ticker: 'BTCUSD', timestamp: '2021-12-31T02:00:00Z', price: 501.666} + { ticker: 'ETHGAS', timestamp: '2021-12-31T02:00:00Z', price: 90.92} + ... and so on ... ``` In order to use messages from this signer as, for example, the settlement trigger and data for a [futures](./0016-PFUT-product_builtin_future.md) market, Vega needs a way to define a data source that will trigger settlement when a price is received for the correct underlying and the right expiry timestamp. For example: ```proto DATA_SOURCE = Filter { data=SignedMessage{ pubkey=0xA45e...d6 }, filters=[ - Equal { key='ticker', value='TSLA' }, - Equal { key='timestamp', value='2021-12-31T23:59:59Z' } + Equal { key='ticker', value='TSLA' }, + Equal { key='timestamp', value='2021-12-31T23:59:59Z' } ]} gives: - { ticker: 'TSLA', timestamp: '2021-12-31T23:59:59Z', price: 694.20 } + { ticker: 'TSLA', timestamp: '2021-12-31T23:59:59Z', price: 694.20 } ``` Unlike the first example, this would be useful for trigger final settlement of a futures market. @@ -41,14 +41,14 @@ Note that to extract the price value, this would need to be wrapped in a 'select ```proto DATA_SOURCE = select { - field: 'price' - data: filter { - data: SignedMessage{ pubkey=0xA45e...d6 }, - filters: [ - equal { key: 'ticker', value: 'TSLA' }, - equal { key: 'timestamp', value: '2021-12-31T23:59:59Z' } - ] - } + field: 'price' + data: filter { + data: SignedMessage{ pubkey=0xA45e...d6 }, + filters: [ + equal { key: 'ticker', value: 'TSLA' }, + equal { key: 'timestamp', value: '2021-12-31T23:59:59Z' } + ] + } } gives: 694.20 @@ -86,32 +86,32 @@ To be clear, this also means that if the input data is the wrong "shape" or type ## Acceptance criteria 1. Filters can be used with any data source provider (internal, signed message, Ethereum etc.) - 1. Create a filter for each type of source provider and ensure that only data matching the filter gets through. (0047-DSRF-001) - 1. Create the same filter for multiple types of provider and ensure that with the same input data, the output is the same. (0047-DSRF-002) + 1. Create a filter for each type of source provider and ensure that only data matching the filter gets through. (0047-DSRF-001) + 1. Create the same filter for multiple types of provider and ensure that with the same input data, the output is the same. (0047-DSRF-002) 1. All filter conditions are applied - 1. Create a filter with multiple "AND" conditions and ensure that data is only passed through if all conditions are met. (0047-DSRF-003) - 1. Create a filter using an "OR" sub-filter (if implemented) and ensure that data is passed through if any of the OR conditions are met. (0047-COSMICELEVATOR-004) - 1. Create a "greater than or equal" filter on the "timestamp" field of the signed message (_not_ on the timestamp when oracle transaction is submitted) (e.g. greater than or equal "2022-04-01" _and_ on "equal" filter on the "asset" field (e.g. equals ETH) of the signed message from Coinbase oracle. Ensure these are applied correctly (0047-DSRF-0041). + 1. Create a filter with multiple "AND" conditions and ensure that data is only passed through if all conditions are met. (0047-DSRF-003) + 1. Create a filter using an "OR" sub-filter (if implemented) and ensure that data is passed through if any of the OR conditions are met. (0047-DSRF-004) + 1. Create a "greater than or equal" filter on the "timestamp" field of the signed message (_not_ on the timestamp when oracle transaction is submitted) (e.g. greater than or equal "2022-04-01" _and_ on "equal" filter on the "asset" field (e.g. equals ETH) of the signed message from Coinbase oracle. Ensure these are applied correctly (0047-DSRF-041). 1. Data that is filtered out does not result in a data event but is recorded - 1. No data source event is emitted for a data source if the triggering event (`SubmitData` transaction, internal source, etc.) does not pass through the filter for that source. (0047-DSRF-005) - 1. No product/market processing is triggered by a data source when the event does not pass through the filters. (0047-DSRF-006) - 1. When data is filtered out and no event is emitted this is recorded either in logs or on the event bus (this may only happen on the receiving node if the event is a transaction that is rejected prior to being sequenced in a block). (0047-DSRF-007) + 1. No data source event is emitted for a data source if the triggering event (`SubmitData` transaction, internal source, etc.) does not pass through the filter for that source. (0047-DSRF-005) + 1. No product/market processing is triggered by a data source when the event does not pass through the filters. (0047-DSRF-006) + 1. When data is filtered out and no event is emitted this is recorded either in logs or on the event bus (this may only happen on the receiving node if the event is a transaction that is rejected prior to being sequenced in a block). (0047-DSRF-007) 1. Data sources are defined by the FULL definition including filters - 1. If two data sources originate from the same data point (transaction, event, etc.) and provider (`SignedMessage` signer group, internal market/object, etc.) but have different filters then data filtered out by one source can still be received by another, and vice versa. (0047-DSRF-008) - 1. If two data sources originate from the same data point (transaction, event, etc.) and provider (`SignedMessage` signer group, internal market/object, etc.) but have different filters or other properties (i.e. they are not exactly the same definition) then any data that passes through and is emitted by both data sources results in a separate event/emission for each that references the appropriate source in each case. (0047-DSRF-009) - 1. If two data sources originate from the same data point (transaction, event, etc.) and provider (`SignedMessage` signer group, internal market/object, etc.) but have different filters or other properties (i.e. they are not exactly the same definition) then any data that is filtered out by both data sources results in a separate log/event for each that references the appropriate source in each case. (0047-DSRF-010) - 1. If two data sources originate from the same data point (transaction, event, etc.) and provider (`SignedMessage` signer group, internal market/object, etc.) but have different filters or other properties (i.e. they are not exactly the same definition) and the data is filtered out by one and emitted/passes through the other, then both the filtering out and the emission of the data are recorded in logs/events that reference the appropriate source. (0047-DSRF-011) + 1. If two data sources originate from the same data point (transaction, event, etc.) and provider (`SignedMessage` signer group, internal market/object, etc.) but have different filters then data filtered out by one source can still be received by another, and vice versa. (0047-DSRF-008) + 1. If two data sources originate from the same data point (transaction, event, etc.) and provider (`SignedMessage` signer group, internal market/object, etc.) but have different filters or other properties (i.e. they are not exactly the same definition) then any data that passes through and is emitted by both data sources results in a separate event/emission for each that references the appropriate source in each case. (0047-DSRF-009) + 1. If two data sources originate from the same data point (transaction, event, etc.) and provider (`SignedMessage` signer group, internal market/object, etc.) but have different filters or other properties (i.e. they are not exactly the same definition) then any data that is filtered out by both data sources results in a separate log/event for each that references the appropriate source in each case. (0047-DSRF-010) + 1. If two data sources originate from the same data point (transaction, event, etc.) and provider (`SignedMessage` signer group, internal market/object, etc.) but have different filters or other properties (i.e. they are not exactly the same definition) and the data is filtered out by one and emitted/passes through the other, then both the filtering out and the emission of the data are recorded in logs/events that reference the appropriate source. (0047-DSRF-011) 1. Data types and condition types - 1. Text fields can be filtered by equality (text matches filter data exactly). (0047-DSRF-012) - 1. Number fields can be filtered by equality (number matches filter data exactly). (0047-DSRF-013) - 1. Date + time fields can be filtered by equality (datetime matches filter data exactly). (0047-DSRF-014) - 1. Number fields can be filtered by less than (number is less than filter data). (0047-DSRF-015) - 1. Date + time fields can be filtered by less than (datetime is less than filter data). (0047-DSRF-016) - 1. Number fields can be filtered by less than or equal (number is less than or equal to filter data). (0047-DSRF-017) - 1. Date + time fields can be filtered by less than or equal (datetime is less than or equal to filter data). (0047-DSRF-018) - 1. Number fields can be filtered by greater than (number is greater than filter data). (0047-DSRF-019) - 1. Date + time fields can be filtered by greater than (datetime is greater than filter data). (0047-DSRF-020) - 1. Number fields can be filtered by greater than or equal (number is greater than or equal to filter data). (0047-DSRF-021) - 1. Date + time fields can be filtered by greater than or equal (datetime is greater than or equal to filter data). (0047-DSRF-022 - 1. Oracle data filters can be combined together using AND operation (0047-DSRF-023) - 1. Filtering should cause the transaction containing the data source definition to be rejected when using filters which are deemed out scope (0047-DSRF-024) + 1. Text fields can be filtered by equality (text matches filter data exactly). (0047-DSRF-012) + 1. Number fields can be filtered by equality (number matches filter data exactly). (0047-DSRF-013) + 1. Date + time fields can be filtered by equality (datetime matches filter data exactly). (0047-DSRF-014) + 1. Number fields can be filtered by less than (number is less than filter data). (0047-DSRF-015) + 1. Date + time fields can be filtered by less than (datetime is less than filter data). (0047-DSRF-016) + 1. Number fields can be filtered by less than or equal (number is less than or equal to filter data). (0047-DSRF-017) + 1. Date + time fields can be filtered by less than or equal (datetime is less than or equal to filter data). (0047-DSRF-018) + 1. Number fields can be filtered by greater than (number is greater than filter data). (0047-DSRF-019) + 1. Date + time fields can be filtered by greater than (datetime is greater than filter data). (0047-DSRF-020) + 1. Number fields can be filtered by greater than or equal (number is greater than or equal to filter data). (0047-DSRF-021) + 1. Date + time fields can be filtered by greater than or equal (datetime is greater than or equal to filter data). (0047-DSRF-022 + 1. Oracle data filters can be combined together using AND operation (0047-DSRF-023) + 1. Filtering should cause the transaction containing the data source definition to be rejected when using filters which are deemed out scope (0047-DSRF-024) diff --git a/protocol/0048-DSRI-data_source_internal.md b/protocol/0048-DSRI-data_source_internal.md index ffa3333f3..927e37c3d 100644 --- a/protocol/0048-DSRI-data_source_internal.md +++ b/protocol/0048-DSRI-data_source_internal.md @@ -24,20 +24,22 @@ value { type: number, value: 1400.5 } ## 1.2 Time triggered -This data source would be used to emit an event/value at/after a given Vega time (i.e. the time printed on the block). This would be used to trigger "trading terminated" for futures, for example. +### 1.2.1 One-off + +This data source would be used to emit a a single event/value at/after a given Vega time (i.e. the time printed on the block). This would be used to trigger "trading terminated" for futures, for example. This trigger will emit the contents of the specified data source (could be omitted if just triggering trading termination, or could be a value as described in 1.1, or another data source in order to implement a delay/ensure the value from the data source is not emitted before a certain time). Note that trading terminated in the futures definition uses a data source as a trigger intentionally to (a) demonstrate that this is how time based product events would work; and (b) because although the trigger MAY be time based, it could also be another data source such as a signed message oracle, if the trading terminates at an unknown time. -In future, there will be a need to support repeating time based triggers, for example every 2 days or at 04:00, 12:00 and 20:00 every day, etc. (as some products will have triggers that happen regularly). +Once the data source emits the event it should become inactive. Pseudocode example: ```rust on: { - timestamp: '20210401T09:00:00' - data: value { type: number, value: 420.69 } + timestamp: '20210401T09:00:00' + data: value { type: number, value: 420.69 } } ``` @@ -46,11 +48,15 @@ Pseudocode example: (no data, just used to trigger event like trading terminated ```rust on: { - timestamp: '202112311T23:59:59' + timestamp: '202112311T23:59:59' } ``` +### 1.2.2 Repeating + +The repeating internal time triggered oracles will be used by the [perpetual futures](protocol/0053-PERP-product_builtin_perpetual_future.md) product, hence it must be possible to set them up to model a schedule like: every day at 04:00, 12:00 and 20:00. It should also be possible to model a completely arbitrary time schedule with a fixed number of events (e.g. 01/02/2023 08:52, 11/03/2023 15:45, 20/04/2023 21:37). Appropriate anti-spam measures should be considered to prevent the ability to specify an internal time triggered oracle that puts exceedingly high strain on the resources. + ## 1.3 Vega time changed This data source will emit the current Vega time *once* (and once only) whenever the Vega time changes. @@ -67,10 +73,10 @@ Pseudocode example: (with filter - i.e. for trading terminated trigger) ```rust filter { - data: vegaprotocol.builtin.timestamp, - filters: [ - greaterOrEqual { key: 'timestamp', value: '2023-12-31T23:55:00Z' } - ] + data: vegaprotocol.builtin.timestamp, + filters: [ + greaterOrEqual { key: 'timestamp', value: '2023-12-31T23:55:00Z' } + ] } ``` @@ -85,16 +91,16 @@ Currently (as of Oregon Trail), only the *Vega time changed (1.3 above)* interna ```proto “oracleSpecForTradingTermination”:{ filters”:[ - { - “key”:{ - “name”:“vegaprotocol.builtin.timestamp”, - “type”:“TYPE_TIMESTAMP” - }, - “conditions”:[{ - “operator”:“OPERATOR_GREATER_THAN_OR_EQUAL”, - “value”:“1650447351" - }] - } + { + “key”:{ + “name”:“vegaprotocol.builtin.timestamp”, + “type”:“TYPE_TIMESTAMP” + }, + “conditions”:[{ + “operator”:“OPERATOR_GREATER_THAN_OR_EQUAL”, + “value”:“1650447351" + }] + } ] } ``` @@ -102,29 +108,32 @@ Currently (as of Oregon Trail), only the *Vega time changed (1.3 above)* interna ## Acceptance criteria 1. A simple value data source can be provided - 1. Change a cash settled futures market that is already in Trading Terminated state so that the settlement data source is a Value source. The market settles immediately with the value provided as the settlement data. (0048-COSMICELEVATOR-001) - 1. Change a cash settled futures market's trading terminated trigger source with a market governance proposal to a blank Value data source (or one with any value, to be discarded) and ensure the market state changes to trading terminated. (0048-COSMICELEVATOR-002) + 1. Change a cash settled futures market that is already in Trading Terminated state so that the settlement data source is a Value source. The market settles immediately with the value provided as the settlement data. (0048-DSRI-001) + 1. Change a cash settled futures market's trading terminated trigger source with a market governance proposal to a blank Value data source (or one with any value, to be discarded) and ensure the market state changes to trading terminated. (0048-DSRI-002) 1. A time triggered value data source can be provided - 1. Use a market governance proposal to change a cash settled futures market that is already in Trading Terminated state and has a signed message data source configured for settlement data (where no signed message is ever received) so that the settlement data source is a time triggered Value source with the trigger time in the future after the proposal is enacted. The market settles at the trigger time with the value provided as the settlement data (this allows governance to settle a market with a dead oracle). (0048-COSMICELEVATOR-009) - 1. Create a cash settled futures market with a time triggered value data source for the settlement data. Trigger trading terminated before the time specified in the trigger for the settlement data source. The market settles at the time specified in the trigger. (0048-COSMICELEVATOR-003) - 1. Create a cash settled futures market with a time triggered value data source for the settlement data. Trigger trading terminated after the time specified in the trigger for the settlement data source. The market settles immediately once trading terminated is triggered. (0048-COSMICELEVATOR-004) - 1. Create a cash settled futures market with the trading terminated trigger source being a time triggered blank Value data source (or one with any value, to be discarded) with the trigger time being in the future. The market state changes to trading terminated at the time of the trigger. (0048-COSMICELEVATOR-005) - 1. Change a cash settled futures market so the trading terminated trigger source becomes a time triggered blank Value data source (or one with any value, to be discarded) with the trigger time being in the future. The market state changes to trading terminated at the time of the trigger. (0048-COSMICELEVATOR-006) - 1. Change a cash settled futures market so the trading terminated trigger source becomes a time triggered blank Value data source (or one with any value, to be discarded) with the trigger time being in the past. The market state changes to trading terminated immediately. (0048-COSMICELEVATOR-007) - 1. Change a cash settled futures market that is already in Trading Terminated state so that the settlement data source is a time triggered Value source with the trigger time in the past. The market settles immediately with the value provided as the settlement data. (0048-COSMICELEVATOR-008) + 1. Use a market governance proposal to change a cash settled futures market that is already in Trading Terminated state and has a signed message data source configured for settlement data (where no signed message is ever received) so that the settlement data source is a time triggered Value source with the trigger time in the future after the proposal is enacted. The market settles at the trigger time with the value provided as the settlement data (this allows governance to settle a market with a dead oracle). (0048-DSRI-009) + 1. Create a cash settled futures market with a time triggered value data source for the settlement data. Trigger trading terminated before the time specified in the trigger for the settlement data source. The market settles at the time specified in the trigger. (0048-DSRI-003) + 1. Create a cash settled futures market with a time triggered value data source for the settlement data. Trigger trading terminated after the time specified in the trigger for the settlement data source. The market settles immediately once trading terminated is triggered. (0048-DSRI-004) + 1. Create a cash settled futures market with the trading terminated trigger source being a time triggered blank Value data source (or one with any value, to be discarded) with the trigger time being in the future. The market state changes to trading terminated at the time of the trigger. (0048-DSRI-005) + 1. Change a cash settled futures market so the trading terminated trigger source becomes a time triggered blank Value data source (or one with any value, to be discarded) with the trigger time being in the future. The market state changes to trading terminated at the time of the trigger. (0048-DSRI-006) + 1. Change a cash settled futures market so the trading terminated trigger source becomes a time triggered blank Value data source (or one with any value, to be discarded) with the trigger time being in the past. The market state changes to trading terminated immediately. (0048-DSRI-007) + 1. Change a cash settled futures market that is already in Trading Terminated state so that the settlement data source is a time triggered Value source with the trigger time in the past. The market settles immediately with the value provided as the settlement data. (0048-DSRI-008) 1. A Vega time changed value data source can be provided - 1. Create a cash settled futures market with the trading terminated trigger source being a Vega time changed value data source with a greater than or greater than or equal filter against a time in the future. The market state changes to trading terminated at the time of the trigger. (0048-DSRI-010) - 1. Change a cash settled futures market so the trading terminated trigger source becomes a Vega time changed value data source with a greater than or greater than or equal filter against a time in the future. The market state changes to trading terminated at the time of the trigger. (0048-DSRI-011) - 1. Change a cash settled futures market so the trading terminated trigger source becomes a Vega time changed value data source with a greater than or greater than or equal filter against a time in the past. The market state changes to trading terminated immediately. (0048-DSRI-012) + 1. Create a cash settled futures market with the trading terminated trigger source being a Vega time changed value data source with a greater than or greater than or equal filter against a time in the future. The market state changes to trading terminated at the time of the trigger. (0048-DSRI-010) + 1. Change a cash settled futures market so the trading terminated trigger source becomes a Vega time changed value data source with a greater than or greater than or equal filter against a time in the future. The market state changes to trading terminated at the time of the trigger. (0048-DSRI-011) + 1. Change a cash settled futures market so the trading terminated trigger source becomes a Vega time changed value data source with a greater than or greater than or equal filter against a time in the past. The market state changes to trading terminated immediately. (0048-DSRI-012) 1. Termination oracle updated after market is terminated (0048-DSRI-015) - - setup one market with a boolean termination - - terminate the market (but do not settle it) - - update the market to have a time based termination - - update the market to have an earlier time based termination - - wait until the first timer to tick - - send through valid settlement data - - assert the the market settles successfully + - setup one market with a boolean termination + - terminate the market (but do not settle it) + - update the market to have a time based termination + - update the market to have an earlier time based termination + - wait until the first timer to tick + - send through valid settlement data + - assert the the market settles successfully 1. Time based termination across multiple markets (0048-DSRI-014) - - setup 3 markets, all with time based termination with identical signer details, two with the same time, one with a later time - - wait to all of them to terminate successfully - - assert they all settle successfully + - setup 3 markets, all with time based termination with identical signer details, two with the same time, one with a later time + - wait to all of them to terminate successfully + - assert they all settle successfully +1. The repeating internal time triggered oracle can be used to model a time schedule of the form: every day at 12:00, 15:00 and 18:00. (0048-DSRI-018) +1. The repeating internal time triggered oracle can be used to model a time schedule of the form: 01/02/2023 08:52, 11/03/2023 15:45, 20/04/2023 21:37. (0048-DSRI-016) +1. The repeating internal time triggered oracle with a schedule of "every day at 12:00", always sends an event as soon as the block with a timestamp with time of 12:00 or higher is received (the time the oracle sends an event doesn't drift forward even after many days). (0048-DSRI-017) diff --git a/protocol/0049-TVAL-validate_transaction_preconsensus.md b/protocol/0049-TVAL-validate_transaction_preconsensus.md index 85cd84999..251d67517 100644 --- a/protocol/0049-TVAL-validate_transaction_preconsensus.md +++ b/protocol/0049-TVAL-validate_transaction_preconsensus.md @@ -30,7 +30,7 @@ Note that separate pre-consensus validation is carried out as part of PoW anti-s 1. Transaction is included in the block if signed with a non-validator's key, includes [correct PoW data](./0072-SPPW-spam-protection-PoW.md) and is not a governance transaction. (0049-TVAL-001) 1. Transaction is with wrong / missing key is rejected. (0049-TVAL-002) -1. Transaction is rejected (never included in a block) if it is a transfer and is from a party with less than the [quantum](./0041-TSTK-target_stake.md) balance of the source asset (0049-COSMICELEVATOR-003) -1. Transaction is rejected (never included in a block) if a party has strictly less than the [quantum](./0041-TSTK-target_stake.md) balance in the settlement asset of the market and it is submitting any kind of orders (limit, market, LP provision) (0049-COSMICELEVATOR-004) -1. Transaction interacting in a market is included in a block if signed with a key from a non-validator party with a balance >= [quantum](./0041-TSTK-target_stake.md) of the settlement asset for the market, where an identical (apart from PoW proof and block data details) transaction was rejected from a previous block when the party had relevant balance < less than relevant [quantum](./0041-TSTK-target_stake.md) (0049-COSMICELEVATOR-005) +1. Transaction is rejected (never included in a block) if it is a transfer and is from a party with less than the [quantum](./0041-TSTK-target_stake.md) balance of the source asset (0049-TVAL-003) +1. Transaction is rejected (never included in a block) if a party has strictly less than the [quantum](./0041-TSTK-target_stake.md) balance in the settlement asset of the market and it is submitting any kind of orders (limit, market, LP provision) (0049-TVAL-004) +1. Transaction interacting in a market is included in a block if signed with a key from a non-validator party with a balance >= [quantum](./0041-TSTK-target_stake.md) of the settlement asset for the market, where an identical (apart from PoW proof and block data details) transaction was rejected from a previous block when the party had relevant balance < less than relevant [quantum](./0041-TSTK-target_stake.md) (0049-TVAL-005) 1. Transaction sent to a non-validator node is propagated to validator and included in a block. (0049-TVAL-006) diff --git a/protocol/0050-EPOC-epochs.md b/protocol/0050-EPOC-epochs.md index 517da9c74..065bfa3bf 100644 --- a/protocol/0050-EPOC-epochs.md +++ b/protocol/0050-EPOC-epochs.md @@ -91,28 +91,14 @@ The delegator cannot move the tokens before the epoch ends, they remain locked. ## Undelegate now The action can be announced at any time and is executed immediately following the block -it is announced in. However, the stake is still counted as delegated to the validator until -the last block of the epoch, though the delegator rewards are not paid to the delegator, but -into an appropriate vega pool (the insurance pool, for example). The tokens are -released though, and the delegator can transfer their tokens in the smart contract. +it is announced in. The delegator is no longer considered for rewards. The stake is no longer counted as delegated to the previously chosen validator. +The tokens are released immediately, and the delegator can transfer their tokens in the smart contract. Rationale: This allows a delegator to sell their tokens in a rush, without requiring any interaction between the smart contract and the details of the delegation system. This also allows the delegator to change their mind about a delegation before it is activated. -## Undelegate in anger - -This action is announced at any time and is executed immediately following the block it -is announced in. The delegator loses the delegated stake and the income with it, as well -as their voting weight. As this is not required for first mainnet, and involves more subtleties -(weights need to be recalculated on the fly, there may be a mixture of normal undelegated -and undelegate in anger, ...), this feature does not need to be implemented right away for -Mainnet alpha. - -Rationale: A validator is found to have done something outrageous, and needs to be removed -right away. - ## Undelegation of locked stake Furthermore, the validators watch the smart contract, and observe the following actions: @@ -211,7 +197,7 @@ Edge case: Multiple epochs can pass within the same block (0050-EPOC-005) (note: this can be tested by trying to delegate again, which will be rejected) + - `party 1`s staking balanced is reduced immediately upon execution of the transaction (0050-EPOC-005)(note: this can be tested by trying to delegate again, which will be rejected) - `validator 1`s nominated balance is not increased in epoch 1 (0050-EPOC-006) - `validator 1`s nominated balance is increased in the first block of epoch 2 (0050-EPOC-007) diff --git a/protocol/0051-PROD-product.md b/protocol/0051-PROD-product.md index b64a11abb..065647167 100644 --- a/protocol/0051-PROD-product.md +++ b/protocol/0051-PROD-product.md @@ -119,12 +119,12 @@ APIS should be available to: Settlement assets: -- A product of any type cannot be created without specifying at least one settlement asset (0051-PROD-001) -- The settlement asset or assets must exist at the time when the product is created (0051-PROD-002) +- A product of any type cannot be created without specifying at least one settlement asset (0051-PROD-001). For product perpetual: (0051-PROD-007) +- The settlement asset or assets must exist at the time when the product is created (0051-PROD-002).For product perpetual: (0051-PROD-008) Product updates via governance: -- The settlement asset / settlement assets cannot be changed on a product via governance (0051-PROD-003) +- The settlement asset / settlement assets cannot be changed on a product via governance (0051-PROD-003).For product perpetual: (0051-PROD-009) ## See also diff --git a/protocol/0052-FPOS-fractional_orders_positions.md b/protocol/0052-FPOS-fractional_orders_positions.md index bd78af7fb..3699a4fd3 100644 --- a/protocol/0052-FPOS-fractional_orders_positions.md +++ b/protocol/0052-FPOS-fractional_orders_positions.md @@ -16,14 +16,12 @@ Specs affected by this change (Note: in many cases the implementation may not ch - [0003 - Mark to mark settlement](./0003-MTMK-mark_to_market_settlement.md) - [0019 - Margin Caculator](./0019-MCAL-margin_calculator.md) - [0029 - Fees](./0029-FEES-fees.md) -- [0038 - Liquidity Provision order type](./0038-OLIQ-liquidity_provision_order_type.md) ## Acceptance Criteria -- All proposed markets will have a decimal places property available via the API (0052-FPOS-001) -- An order created on the client with a price of `1` results in an order being created with a price of `1 * 10^[Market.DecimalPlaces]` (0052-FPOS-002) +- All proposed markets will have a decimal places property available via the API (0052-FPOS-001). +- An order created on the client with a price of `1` results in an order being created with a price of `1 * 10^[Market.DecimalPlaces]` (0052-FPOS-002). - Fees are calculated as per ([0029-FEES-013](./0029-FEES-fees.md#0029-FEES-013)) -- LP order volume is implied correctly using fractional volume amounts as per ([0038-OLIQ-006](./0038-OLIQ-liquidity_provision_order_type.md#0038-OLIQ-006)) - Mark-to-market settlement happens correctly with PDP > 0 ([0003-MTMK-0015](./0003-MTMK-mark_to_market_settlement.md#0003-MTMK-015)) - Margins are correctly calculated for markets with PDP > 0 ([0019-MCAL-008](./0019-MCAL-margin_calculator.md#0019-MCAL-008)). - Market framework reports position decimal places ([0001-MKTF-001](./0001-MKTF-market_framework.md#0001-MTMF-001)). diff --git a/protocol/0053-PERP-product_builtin_perpetual_future.md b/protocol/0053-PERP-product_builtin_perpetual_future.md index fa1189de9..4febc048d 100644 --- a/protocol/0053-PERP-product_builtin_perpetual_future.md +++ b/protocol/0053-PERP-product_builtin_perpetual_future.md @@ -1,17 +1,71 @@ -# Built-in [Product](./0051-PROD-product.md): Cash Settled Perpetual Futures (CSF) +# Built-in [Product](./0051-PROD-product.md): Cash Settled Perpetual Futures -This built-in product provides perpetual futures that are cash-settled, i.e. they are margined and settled in a single asset. +This built-in product provides perpetual futures contracts that are cash-settled, i.e. they are margined and settled in a single asset, and they never expire. -[Background reading](https://www.paradigm.xyz/2021/05/everlasting-options/#Perpetual_Futures) +Background reading: [1](https://www.paradigm.xyz/2021/05/everlasting-options/#Perpetual_Futures), [2](https://arxiv.org/pdf/2212.06888.pdf). -Perpetual futures are a simple "delta one" product. +Perpetual futures are a simple "delta one" product. Mark-to-market settlement occurs with a predefined frequency as per [0003-MTMK-mark_to_market_settlement](0003-MTMK-mark_to_market_settlement.md). Additionally, a settlement using external data (funding payment) is carried out whenever `settlement_schedule` is triggered. Data obtained from the `settlement_data` oracle between two consecutive `settlement_schedule` events is used to calculate the funding payment and exchange cashflows between parties with open positions in the market. + +Unlike traditional futures contracts, the perpetual futures never expire. Without the settlement at expiry there would be nothing in the fixed-expiry futures to tether the contract price to the underlying spot market it's based on. To assure that the perpetuals market tracks the underlying spot market sufficiently well a periodic cashflow is exchanged based on the relative prices in the two markets. Such payment covering the time period $t_{i-1}$ to $t_i$ takes the basic form $G_i = \frac{1}{t_i-t_{i-1}} \int_{t_{i-1}}^{t_i}(F_u-S_u)du$, where $F_u$ and $S_u$ are respectively: the perpetual futures price and the spot price at time $u$. +We choose to use either: + +- the mark price of the market to approximate $F_u$ or +- configure the "market price for funding purposes" as part of the market proposal using [composite prices method](./0089-COMP-composing_prices.md) and use this methodology to approximate $F_u$ and oracle to approximate $S_u$, so this is effectively the difference between the time-weighted average prices (TWAP) of the two. An optional interest rate and clamp function are included in the funding rate calculation, see the [funding payment calculation](#funding-payment-calculation) section for details. ## 1. Product parameters -1. `settlement_data (Data Source: number)`: this data is used by the product to calculate periodic settlement cashflows. The receipt of this data triggers this calculation and the transfers between parties to "true up" to the external reference price. 1. `settlement_asset (Settlement Asset)`: this is used to specify the single asset that an instrument using this product settles in. +1. `settlement_schedule (Data Source: datetime)`: this data is used to indicate when the next periodic settlement should be carried out. +1. `settlement_data (Data Source: number)`: this data is used by the product to calculate periodic settlement cashflows. +1. `margin_funding_factor`: a parameter controlling how much the upcoming funding payment liability contributes to party's margin. +1. `interest_rate`: a continuously compounded interest rate used in funding rate calculation. +1. `clamp_lower_bound`: a lower bound for the clamp function used as part of the funding rate calculation. +1. `clamp_upper_bound`: an upper bound for the clamp function used as part of the funding rate calculation. +1. `scaling_factor`: optional scaling factor applied to funding payment. +1. `rate_lower_bound`: optional lower bound applied to funding payment such that the resulting funding rate will never be lower than the specified value. +1. `rate_upper_bound`: optional upper bound applied to funding payment such that the resulting funding rate will never be greater than than the specified value. + +Validation: + +- `margin_funding_factor` in range `[0,1]`, +- `interest_rate` in range `[-1,1]`, +- `clamp_lower_bound` in range `[-1,1]`, +- `clamp_upper_bound` in range `[-1,1]`, +- `scaling_factor` any positive real number, +- `rate_lower_bound` any real number, +- `rate_upper_bound` any real number, +- `clamp_upper_bound` >= `clamp_lower_bound`, +- `rate_upper_bound` >= `rate_lower_bound`. + +When migrating legacy markets the following value should be used: + +- `scaling_factor` = `1.0`, +- `rate_lower_bound` = -`max supported value`, +- `rate_upper_bound` = `max supported value`. + +### Example specification -Validation: none required as these are validated by the asset and data source frameworks. +The pseudocode below specifies a possible configuration of the built-in perpetual futures product. The emphasis is on modelling required properties of this product, not the exact semantics used as these will most likely differ in the implementation. + +```yaml + product: built-in perpetual futures contract + settlement_asset: XYZ + settlement_schedule: + internal_time_oracle: + repeating: + - every 8h from 20230201T09:30:00 + - every 168h from 20230203T12:00:00 + settlement_data: + data_source: SignedMessage{ pubkey=0xA45e...d6 } + filters: + - 'timestamp': >= vegaprotocol.builtin.timestamp + - 'timestamp': <= vegaprotocol.builtin.timestamp + "10s" + - 'ticker': 'TSLA' + price: + field: 'price' + timestamp: + field: 'timestamp' +``` ## 2. Settlement assets @@ -28,23 +82,291 @@ cash_settled_perpetual_future.value(quote) { ## 4. Lifecycle triggers -### 4.1 Settlement ("periodic funding") +No data relating to periodic settlement gets stored by the market prior to a successful uncrossing of the opening auction. Once the auction uncrosses an internal `funding_period_start` field gets populated with the current vega time (`vegaprotocol.builtin.timestamp`) -```javascript -cash_settled_perpetual_future.settlement_data(event) { - cashflow = cash_settled_perpetual_future.value(event.data) - cash_settled_perpetual_future.value(market.mark_price)) - settle(cash_settled_perpetual_future.settlement_asset, cashflow) - setMarkPrice(event.data) +### 4.1. Periodic settlement data point received + +If the periodic settlement data received satisfies all the filters that have been specified for it then a data point containing price (`s`) along with timestamp (`t`) gets stored as the oracle data point within the market. Note that both the price and timestamp should come from the same oracle. The implementation has to allow specifying the following types of timestamps: + +- a field on the oracle payload, +- a timestamp from the oracle's blockchain, +- an internal vega time. + +### 4.2. Mark to market settlement + +Every time a [mark to market settlement](./0003-MTMK-mark_to_market_settlement.md) is carried out the value of mark price (`f`) and the current `vegaprotocol.builtin.timestamp` gets stored as an internal data point within the market. + +### 4.3. Periodic settlement + +When the `settlement_schedule` event is received we need to calculate the funding payment. Store the current vega time as `funding_period_end`. + +Skip the funding payment calculation (set payment to `0`) if no spot (external) data has been ingested since market was created, otherwise calculate the funding payment as outlined below. + +#### TWAP calculation + +Same methodology applies to spot (external) and perps (internal). The available prices (spot and perps considered separately of course) should be used to calculate the time weighted average price within the funding period. If no observations at or prior to the funding period start exist then the start of the period used for calculation should be moved forward to the time of the first observation. An observation is assumed to be applying until a subsequent observation is available. Periods spent in auction should be excluded from the calculation. This implies that spot datapoints received during the auction except for the latest one should be disregarded. Please refer to the acceptance criteria for a detailed example. + +Calculation of the TWAP is carried out by maintaining the following variables: `numerator`, `denominator`, `previous_price` and `previous_time`. It's also assumed that a function `current_time()` is available which returns the current Vega time, and a function `in_auction()` exists which returns `true` if the market being considered is currently in auction and `false` otherwise. +The variables are maintained as follows. + +When a new `price` observation arrives: + +```go +if previous_price != nil && in_auction() { + time_delta = current_time()-previous_time + numerator += previous_price*time_delta + denominator += time_delta +} +previous_price = price +previous_time = current_time() +``` + +When the market goes into auction: + +```go +time_delta = current_time()-previous_time +numerator += previous_price*time_delta +denominator += time_delta +``` + +When the market goes out of auction: + +```go +previous_time = current_time() +``` + +When the funding payment cue arrives TWAP gets calculated and returned as: + +```go +if !in_auction() { + time_delta = current_time()-previous_time + numerator += previous_price*time_delta + denominator += time_delta +} +previous_time = current_time +if denominator == 0 { + return 0 } +return numerator / denominator + +``` + +Note that depending on what type of oracle is used for the spot price it may be that the oracle points only become known shortly before or at the funding payment cue time, so the above pseudocode is just an illustration of how these quantities should be calculated and the implementation will need to be able to apply such calculation retrospectively. + +#### Funding payment calculation + +The next step is to calculate the periodic settlement funding payment. We allow the optional interest rate and clamp component, where $\text{clamp}(a,b;x)=min(b,max(a, x))$. The funding payment then takes the form: + +```go +delta_t = funding_period_end - max(funding_period_start, internal_data_points[0].t) +funding_payment = f_twap - s_twap + min(clamp_upper_bound*s_twap,max(clamp_lower_bound*s_twap, (1 + delta_t * interest_rate)*s_twap-f_twap)) +``` + +where `(1 + delta_t * interest_rate)` is the linearisation of `exp(delta_t*interest_rate)` and `delta_t` is expressed as a year fraction. + +Furthermore, if any time was spent in auction during the funding period then the funding payment should be scaled down by the fraction of the period spent outside of auction: + +`period_duration = period_end - period_start` + +```go +funding_payment = (period_duration-time_spent_in_auction)/period_duration * funding_payment +``` + +Please note that this implies no funding payments for periods during which the market has been in auction / suspended for their entire duration. + +If `scaling_factor` is specified set: + +```go +funding_payment = scaling_factor * funding_payment +``` + +If `rate_lower_bound` is specified set: + +```go +funding_payment = max(rate_lower_bound*s_twap, funding_payment) +``` + +If `rate_upper_bound` is specified set: + +```go +funding_payment = min(rate_upper_bound*s_twap, funding_payment) ``` +Please note that scaling should happen strictly before any of the bounds are applied, i.e. if all 3 parameters are specified then the resulting funding rate is guaranteed to fall within the specified bounds irrespective of how big the scaling factor may be. + +#### Funding rate calculation + +While not needed for calculation of cashflows to be exchanged by market participants, the funding rate is useful for tracking market's relation to the underlying spot market over time. + +Funding rate should be calculated as: + +```go +funding_rate = funding_payment / s_twap +``` + +and emitted as an event. + +#### Exchanging funding payments between parties + +Last step is to calculate each party's cash flows as $-\text{open volume} * \text{funding payment}$ where cashflows are first collected from parties that are making the payment (negative value of the cashflow, i.e. longs when the funding payment is positive) and distributed to those receiving it. Any shortfall should be made-up from the market's insurance pool and if that's not possible loss socialisation should be applied (exactly as per mark-to-market settlement methodology). + +### 4.3.1. Periodic settlement during [auction](0026-AUCT-auctions.md) + +Periodic settlement is not allowed during the opening auction and it's extensions. +If periodic settlement data happens whilst market is in auction of any other type then periodic settlement should be carried out as per above methodology and the market should remain in auction until it's allowed to move back into market's default trading mode. + +### 5. Margin considerations + +To assure adequate solvency we need to include the estimate of the upcoming funding payment in maintenance margin estimate for the party. Let $t_{k-1}$ be the time of the last funding payment. Let $t$ be current time ($t < t_k$). +Calculate $G_t$ as the [funding payment](#43-periodic-settlement) between $t_{k-1}$ and $t$, and consider open volume of the party for which the margin is being calculated. +For perpetual futures markets set the maintenance margin as: + +```math +m^{\text{maint (perps)}}_t = m^{\text{maint}}_t + \text{margin funding factor} \cdot \max(0, \text{open volume}\ cdot G_t), +``` + +where $m^{\text{maint}}_t$ is the current maintenance margin as per the [margin spec](./0019-MCAL-margin_calculator.md) + +### 6. Market closure + +Should a perpetual futures market get closed using the [governance proposal](./0028-GOVE-governance.md#61-move-market-to-a-closed-state) an final funding payment should be calculated using the data available at that time and exchanged right before the final settlement using the price contained in the proposal is carried out. + +### API considerations + +For every completed funding period the following data should be emitted: + +- funding period start time, +- funding period end time, +- funding rate, +- funding payment, +- external (spot) price TWAP, +- internal (mark) price TWAP. + +Furthermore, within the ongoing funding period the following data should be emitted at least every time the mark price is updated: + +- funding period start time, +- estimate time, +- funding rate estimate, +- funding payment estimate, +- external (spot) price TWAP to-date, +- internal (mark) price TWAP to-date. + + The estimates are obtained assuming the current period ended now. The time for which the estimate was obtained is recorded as `estimate time`. + Please note that the above estimates calculated within the ongoing funding period should be available internally for inclusion in the margin calculation as outlined in the [margin considerations](#5-margin-considerations) subsection as well as on the data-node. Only the most recent observation should be kept in both these places. + +In both cases the estimates are for a hypothetical position of size 1. + ## Acceptance Criteria -1. Create a Cash Settled Perpetual Future with the settlement data provided by an external data source (0053-COSMICELEVATOR-001) -1. Create a Cash Settled Perpetual Future for any settlement asset that's configured in Vega (0053-COSMICELEVATOR-002) -1. The data source can be changed via governance (0053-COSMICELEVATOR-003) -1. It is not possible to change settlement asset via governance (0053-COSMICELEVATOR-004) -1. Mark to [market settlement](./0003-MTMK-mark_to_market_settlement.md) works correctly (0053-COSMICELEVATOR-005) -1. Settlement at each oracle event (periodic funding) works correctly (0053-COSMICELEVATOR-006) -1. Every valid lifecycle event (i.e. every oracle price update matching the data source specified) triggers a periodic funding settlement and causes settlement cashflows to be created and funds to be transferred. (0053-COSMICELEVATOR-007) -1. Directly after receipt of oracle data for periodic funding, the mark price is equal to the settlement data price provided and this is exposed on event bus and market data APIs (0053-COSMICELEVATOR-008) +1. Create a Cash Settled Perpetual Future with the settlement data provided by an external data source. (0053-PERP-001) +1. Create a Cash Settled Perpetual Future for any settlement asset that's configured in Vega. (0053-PERP-002) +1. Any of the data sources used by the product can be changed via governance. (0053-PERP-003) +1. It is not possible to change settlement asset via governance. (0053-PERP-004) +1. [Mark to market settlement](./0003-MTMK-mark_to_market_settlement.md) works correctly with a predefined frequency irrespective of the behaviour of any of the oracles specified for the market. (0053-PERP-005) +1. Receiving an event from the settlement schedule oracle during the opening auction does not cause settlement. (0053-PERP-006) +1. Receiving correctly formatted data from settlement data oracles and settlement schedule oracles during continuous trading results in periodic settlement. (0053-PERP-007) +1. Receiving correctly formatted data from the settlement data and settlement schedule oracles during price monitoring auction results in the exchange of periodic settlement cashflows. Market remains in price monitoring auction until its original duration elapses, uncrosses the auction and goes back to continuous trading mode. (0053-PERP-009) +1. When the funding payment is positive the margin levels of parties with long positions are larger than what the basic margin calculations imply. Parties with short positions are not impacted. (0053-PERP-015) +1. When the funding payment is negative the margin levels of parties with short positions are larger than what the basic margin calculations imply. Parties with long positions are not impacted. (0053-PERP-016) +1. An event containing funding rate should be emitted each time the funding payment is calculated (0053-PERP-017) +1. No data relating to funding payment is available until the perpetual futures market leaves the opening auction. (0053-PERP-018) +1. For the ongoing period the following data is available via the API: funding period start time, estimate time, funding rate estimate, funding payment estimate, external (spot) price TWAP to-date, internal (mark) price TWAP to-date. (0053-PERP-019) +1. For each of the fully completed past funding periods the following data is available (subject to data-node's retention settings): funding period start time, funding period end time, funding rate, funding payment, external (spot) price TWAP, internal (mark) price TWAP. (0053-PERP-020) +1. A perpetual market which is active and has open orders, continues to function after protocol upgrade, and preserves all market settings and statistics. (0053-PERP-021) +1. A perpetual market which is active and has open orders, after checkpoint restart, is in opening auction. All margin accounts are transferred to general accounts. (0053-PERP-022) +1. A perpetual market which is active and has open orders. Wait for a new network history snapshot to be created. Load a new data node from network history. All market data is preserved. (0053-PERP-023) +1. When the funding payment does not coincide with mark to market settlement time, a party has insufficient funds to fully cover their funding payment such that the shortfall amount if $x$ and the balance of market's insurance pool is $\frac{x}{3}$, then the entire insurance pool balance gets used to cover the shortfall and the remaining missing amount $\frac{2x}{3}$ gets dealt with using loss socialisation. (0053-PERP-024) + +1. Assume a market trades steadily generating a stream in mark price observations, but the first spot price observation only arrives during the 4th funding period of that market. Then funding payments for periods 1, 2 and 3 all equal 0. (0053-PERP-025) + +1. Assume the market has been in a long auction so that a funding period has started and ended while the market never went back into continuous trading. In that case the funding payment should be equal to 0 and no transfers should be exchanged. (0053-PERP-026) + +- It is possible to obtain a time series for the price used for “vega side price” of the funding twap from the data node from the time of the market proposal enactment onwards (subject to data node retention policies).(0053-PERP-043) + +1. Assume a 10 minute funding period. Assume a few funding periods have already passed for this market. + +Assume the last known mark price before the start of the period to be `10` and that it gets updated every 2 minutes as follows: +| Time (min) since period start | mark price | +| ----------------------------- | ----------- | +| 1 | 11 | +| 3 | 10 | +| 5 | 9 | +| 7 | 8 | +| 9 | 7 | + +Assume the last known spot price before this funding period is `11`. Then assume the subsequent spot price observations get ingested according to the schedule specified below: +| Time (min) since period start | spot price | +| ----------------------------- | ----------- | +| 1 | 9 | +| 3 | 10 | +| 5 | 12 | +| 6 | 11 | +| 7 | 8 | +| 9 | 14 | + +Then, assuming no auctions during the period we get: +$\text{internal TWAP}= \frac{10\cdot(1-0)+11\cdot(3-1)+10\cdot(5-3)+9\cdot(7-5)+8\cdot(9-7)+7\cdot(10-9)}{10}=9.3$, +$\text{external TWAP}=\frac{11\cdot(1-0)+9\cdot(3-1)+10\cdot(5-3)+12\cdot(6-5)+11\cdot(7-6)+8\cdot(9-7)+14\cdot(10-9)}{10}=10.2$. (0053-PERP-027) + +1. Assume a 10 minute funding period. Assume a few funding periods have already passed for this market. Furthermore, assume that in this period that market is in an auction which starts 5 minutes into the period and ends 7 minutes into the period. Assume `interest_rate`=`clamp_lower_bound`=`clamp_upper_bound`=`0`, `scaling_factor`=`1` and no rate upper or lower bound. + +Assume the last known mark price before the start of the period to be `10` and that it gets updated as follows: +| Time (min) since period start | mark price | +| ----------------------------- | ----------- | +| 1 | 11 | +| 3 | 11 | +| 7 | 9 | +| 8 | 8 | +| 10 | 30 | + +Assume the last known spot price before this funding period is `11`. Then assume the subsequent spot price observations get ingested according to the schedule specified below: +| Time (min) since period start | spot price | +| ----------------------------- | ----------- | +| 1 | 9 | +| 3 | 10 | +| 5 | 30 | +| 6 | 11 | +| 8 | 8 | +| 9 | 14 | + +Then, taking the auction into account we get: +$\text{internal TWAP}=\frac{10\cdot(1-0)+11\cdot(3-1)+11\cdot(5-3)+9\cdot(8-7)+8\cdot(10-8)+30\cdot(10-10)}{8}=9.875$, +$\text{external TWAP}=\frac{11\cdot(1-0)+9\cdot(3-1)+10\cdot(5-3)+11\cdot(8-7)+8\cdot(9-8)+14\cdot(10-9)}{8}=10.25$, +$\text{funding payment}=(10-(7-5))/10 * (9.875 - 10.25) = -0.3$. (0053-PERP-036) + +When $\text{clamp lower bound}=\text{clamp upper bound}=0$, $\text{scaling factor}=2.5$ and the funding period ends with $\text{internal TWAP}=99$, $\text{external TWAP} = 100$ then the resulting funding rate equals $-0.025$. (0053-PERP-029) + +When $\text{clamp lower bound}=\text{clamp upper bound}=0$, $\text{scaling factor}=1$, $\text{rate lower bound}=-0.005$, $\text{rate upper bound}=0.015$ and the funding period ends with $\text{internal TWAP}=99$, $\text{external TWAP} = 100$ then the resulting funding rate equals $-0.005$. (0053-PERP-030) + +When $\text{clamp lower bound}=\text{clamp upper bound}=0$, $\text{scaling factor}=1$, $\text{rate lower bound}=-0.015$, $\text{rate upper bound}=0.005$ and the funding period ends with $\text{internal TWAP}=101$, $\text{external TWAP} = 100$ then the resulting funding rate equals $0.005$. (0053-PERP-031) + +When migrating the market existing prior to introduction of the additional parameters their values get set to: + +- $\text{scaling factor}=1$, +- $\text{rate lower bound}= -\text{max supported value}$, +- $\text{rate upper bound}= \text{max supported value}$ +(0053-PERP-032). + +It is possible to create a perpetual futures market which uses the last traded price algorithm for its mark price but uses "impact volume of notional of 1000 USDT" for the purpose of calculating the TWAP of the market price for funding payments (0053-PERP-033). + +It is possible to create a perpetual futures market which uses an oracle source (same as that used for funding) for the mark price determining the mark-to-market cashflows and that uses "impact volume of notional of 1000 USDT" for the purpose of calculating the TWAP of the market price for funding payments (0053-PERP-034). + +It is possible to create a perpetual futures market which uses an oracle source (same as that used for funding) for the mark price determining the mark-to-market cashflows and that uses "time-weighted trade prices in over `network.markPriceUpdateMaximumFrequency` if these have been updated within the last 30s but falls back onto impact volume of notional of 1000 USDT" for the purpose of calculating the TWAP of the market price for funding payments (0053-NEBULA-035). + +When funding payments are due to the network party they are paid into the market insurance pool (0053-PERP-037). + +When funding payments are due from the network party they are paid from the market insurance pool (0053-PERP-038). + +If a market insurance pool does not have enough funds to cover a funding payment, loss socialisation occurs and the total balances across the network remains constant (0053-PERP-039). + +Assert that the scaling factor is applied before the funding cap is applied (0053-PERP-040). + +Assert all funding payments are correct when a perpetual market is suspended and then terminated via governance. (0053-PERP-041). + +The upper and lower clamp values are being correctly validated as per the [parameters defined in the spec](https://github.com/vegaprotocol/specs/blob/palazzo/protocol/0053-PERP-product_builtin_perpetual_future.md#1-product-parameters). (0053-PERP-042). + +Launch a perpetual futures market which sets `internalCompositePrice` to `Nil` (mark price is used) for the "vega side price" for funding calculation. Submit a market update proposal to change this to a composite price with a configuration which uses the impact notional price from the order book. Observe that the new methodology for funding calculations is applied correctly from enactment onwards. (0053-PERP-044). + +Launch a perpetual futures market which sets `internalCompositePrice` to a configuration which uses the impact notional price from the order book. for the "vega side price" for funding calculation. Submit a market update proposal to change this `Nil` (so that mark price gets used for the vega side price). Observe that the new methodology for funding calculations is applied correctly from enactment onwards. (0053-PERP-045). + +Launch a perpetual futures market which uses the "Last Traded Price" for the "vega side price" for funding calculation. Submit a market update proposal to change this to a composite price with a configuration which uses the impact notional price from the order book. Observe that the new methodology for funding calculations is applied correctly from enactment onwards. (0053-PERP-046). diff --git a/protocol/0054-NETP-network_parameters.md b/protocol/0054-NETP-network_parameters.md index 747716c29..8d6da001d 100644 --- a/protocol/0054-NETP-network_parameters.md +++ b/protocol/0054-NETP-network_parameters.md @@ -91,9 +91,9 @@ The current network parameters are specified in the code specifiying the min and ## Acceptance criteria -- All network parameter set in `genesis.json` can be queried and the values returned are the correct ones (unless overridden by [LNL checkpoint](./0073-LIMN-limited_network_life.md) value). (0054-NETP-001) -- For `blockchains.ethereumConfig` set in `genesis.json` a governance proposal to change this parameter will be rejected with a rejection error `network parameter update disabled for blockchains.ethereumConfig`. (0054-NETP-002) +- All network parameter set in `genesis.json` can be queried and the values returned are the correct ones (unless overridden by [LNL checkpoint](./0073-LIMN-limited_network_life.md) value). (0054-NETP-001). +- For `blockchains.ethereumConfig` set in `genesis.json` a governance proposal to change this parameter will be rejected with a rejection error `network parameter update disabled for blockchains.ethereumConfig`. (0054-NETP-002). - For `market.margin.scalingFactors` set in `genesis.json` or in a governance proposal we validate the format and the fact that "1.0 <= search <= initial <= release"; if these are invalid a useful error is returned. (0054-NETP-003) -- For `market.monitor.price.defaultParameters` set in `genesis.json` or in a governance proposal we validate the format; if these are invalid a useful error is returned. (0054-NETP-004) -- For each of the remaining parameter whether set in `genesis.json` or in a governance proposal we validate the data type, reject invalid and validate the range of allowable values; if these are invalid a useful error is returned. (0054-NETP-005) -- All network parameter ranges, as specified in the [defaults](https://github.com/vegaprotocol/vega/blob/develop/core/netparams/defaults.go) file, are not able to be set less or greater than the range bondaries. (0054-NETP-006) +- For `market.monitor.price.defaultParameters` set in `genesis.json` or in a governance proposal we validate the format; if these are invalid a useful error is returned. (0054-NETP-004). +- For each of the remaining parameter whether set in `genesis.json` or in a governance proposal we validate the data type, reject invalid and validate the range of allowable values; if these are invalid a useful error is returned. (0054-NETP-005). +- All network parameter ranges, as specified in the [defaults](https://github.com/vegaprotocol/vega/blob/develop/core/netparams/defaults.go) file, are not able to be set less or greater than the range bondaries. (0054-NETP-006). diff --git a/protocol/0055-TREA-on_chain_treasury.md b/protocol/0055-TREA-on_chain_treasury.md index 965dd83f5..b79786cd7 100644 --- a/protocol/0055-TREA-on_chain_treasury.md +++ b/protocol/0055-TREA-on_chain_treasury.md @@ -1,13 +1,12 @@ # Network Treasury -The Network Treasury is a set of accounts (up to 1 per asset supported by the network via the asset framework) that are funded by parties, deposits, or by direct transfers (e.g. a portion of fees, or from insurance pools at market closure). +The Network Treasury is a set of accounts (up to 1 per asset supported by the network via the asset framework) that are funded by parties, deposits, by direct transfers (e.g. a portion of fees) or [governance transfers](./0028-GOVE-governance.md#5-transfers-initiated-by-governance). Please note that the network treasury, rewards accounts (including the global rewards account) and the global insurance pool are 3 separate concepts and funds can only flow between any of these 3 accounts via governance transfers. Please refer to the [accounts spec](./0013-ACCT-accounts.md) for more details. The purpose of the Network Treasury is to allow funding to be allocated to rewards, grants, etc. by token holder governance. -The funds in the network treasury are spent by being transferred to another account, either by direct governance action (i.e. voting on a specific proposed transfer) or by mechanisms controlled by governance, such as a periodic transfer, which may have network parameters that control the frequency of transfers, calculation of the amount, etc.. -These transfers may be to a party general account, reward pool account, or insurance pool account for a market. +The funds in the network treasury are spent by being transferred to another account, either by direct governance action (i.e. voting on a specific proposed transfer) or by mechanisms controlled by governance, such as a periodic transfer, which may have network parameters that control the frequency of transfers, calculation of the amount, etc. +These transfers may be to a party general account, reward pool account, global insurance pool or insurance pool account for a market. There is no requirement or expectation of symmetry between funds flowing into the Network Treasury and funds flowing out. For example, the treasury account may be seeded by funds held by the team or investors, or through the issuance of tokens at various irregular points in time, and these funds may then be allocated to incentives/rewards, grants, etc. on a different schedule. -Note that governance initiated transfers are not in scope for Oregon trail and as such at the moment funds can only flow into network treasure but cannot flow out ;-). ## Funding @@ -18,11 +17,11 @@ Funding is how the on-chain treasury account receives collateral to be allocated A transfer may specify the network treasury as the destination of the transfer. The funds, if available would be transferred instantly and irrevocably to the network treasury account for the asset in question (the treasury account for the asset will be created if it doesn’t exist). -- Transfer from protocol mechanics: there may be a protocol feature such as the charging of fees or handling of expired insurance pool balances that specifies the Network Treasury as destination in a transfer. (Charging of fees is placeholder, currently not to be implemented.) +- Transfer from protocol mechanics: there may be a protocol feature such as the charging of fees that specifies the Network Treasury as destination in a transfer. (Charging of fees is placeholder, currently not to be implemented.) -- Transfer by governance: a [governance proposal](./0028-GOVE-governance.md) can be submitted to transfer funds from a market's insurance pool into the on chain treasury account for the asset. (not required for Oregon Trail as transfer by governance is not in scope) +- Transfer by governance: a [governance proposal](./0028-GOVE-governance.md) can be submitted to transfer funds from the global or a market insurance pool into the on chain treasury account for the asset. -- Transfer transaction: a transaction submitted to the network may request to transfer funds from the general account for an asset, controlled by the owner’s private key, to the Network Treasury. (see [transfers spec](????.md)) +- Transfer transaction: a transaction submitted to the network may request to transfer funds from the general account for an asset, controlled by the owner’s private key, to the Network Treasury. (see [transfers spec](./0057-TRAN-transfers.md)) ### Funding by deposit @@ -37,9 +36,9 @@ In future a fee factor (controlled by governance) may be added to allow the trea In future a tax rate and/or inflation rate (controlled by governance) may be used to allow funding the network treasury with governance tokens. This would either involve transferring a fraction of each staked user's tokens to the network treasury per epoch (it is implied that this fraction would be a significantly lower value than the other assets they receive in fees), or periodic issuance of new tokens into the treasury (this would not be possible before the inflation cut-off date in the token contract). -### Direct allocation by governance (not for Oregon trail) +### Direct allocation by governance -A governance proposal may be submitted to transfer funds on enactment from the on-chain treasury to certain account types. Please see [the governance spec](./0028-GOVE-governance.md) for a description of this. +A governance proposal may be submitted to transfer funds on enactment from the on-chain treasury to certain account types. Such a transfer may be one-off or recurring. There is no other way to withdraw the funds from the network treasury account. Please see [the governance spec](./0028-GOVE-governance.md#5-transfers-initiated-by-governance) for a description of this. ## Acceptance criteria @@ -59,9 +58,9 @@ A governance proposal may be submitted to transfer funds on enactment from the o - The Network Treasury accounts API returns the correct balance for the new account (0055-TREA-007) - The network treasury account balances [are restored after a network restart](./0073-LIMN-limited_network_life.md) (0055-TREA-010) -### Post Oregon Trail +### ☄️ Cosmic Elevator - If a governance proposal for a single transfer from a network treasury account to some other account is enacted then - - if the amount in the proposal greater than or equal amount in network treasury for the asset then the entire balance of the net treasury account is transferred to the destination account (party address). (0055-COSMICELEVATOR-050) - - if the balance in the network treasury for the asset is greater than the amount specified in the transfer then the network treasury balance is decreased by the said amount and the destination account (party address) account is incremented by the right amount. (0055-COSMICELEVATOR-051) -- If a governance proposal for a single periodic transfer from a network treasury account to some other account is enacted then the transfers run as individual transfers as specified by the schedule / amounts until the schedule ends. (0055-COSMICELEVATOR-060) + - if the amount in the proposal greater than or equal amount in network treasury for the asset then the entire balance of the net treasury account is transferred to the destination account (party address). (0055-TREA-008) + - if the balance in the network treasury for the asset is greater than the amount specified in the transfer then the network treasury balance is decreased by the said amount and the destination account (party address) account is incremented by the right amount. (0055-TREA-009) +- If a governance proposal for a single periodic transfer from a network treasury account to some other account is enacted then the transfers run as individual transfers as specified by the schedule / amounts until the schedule ends. (0055-TREA-011) diff --git a/protocol/0056-REWA-rewards_overview.md b/protocol/0056-REWA-rewards_overview.md index 65792570a..52f0ab470 100644 --- a/protocol/0056-REWA-rewards_overview.md +++ b/protocol/0056-REWA-rewards_overview.md @@ -1,12 +1,11 @@ # Reward framework -The reward framework provides a mechanism for measuring and rewarding a number of key activities on the Vega network. +The reward framework provides a mechanism for measuring and rewarding individuals or [teams](./0083-RFPR-on_chain_referral_program.md#glossary) (collectively referred to within this spec as entities) for a number of key activities on the Vega network. These rewards operate in addition to the main protocol economic incentives which come from [fees](0029-FEES-fees.md) on every trade. These fees are the fundamental income stream for [liquidity providers LPs](0042-LIQF-setting_fees_and_rewarding_lps.md) and [validators](./0061-REWP-pos_rewards.md). The additional rewards described here can be funded arbitrarily by users of the network and may be used by the project team, token holders (via governance), and individual traders and market makers to incentivise mutually beneficial behaviour. -Note that transfers via governance, including to fund rewards, is a post-Oregon Trail feature. Note that validator rewards (and the reward account for those) is covered in [validator rewards](./0061-REWP-pos_rewards.md) and is separate from the trading reward framework described here. @@ -20,28 +19,32 @@ Therefore, for example, to reward futures markets when they reach a lifetime tra At a high level, rewards work as follows: -- Reward metrics are calculated for each combination of [reward type, party, market]. - - The calculation used for the reward metric is specific to each reward type. +- Individual reward metrics are calculated for each combination of [reward type, party, market] and team reward metrics for each combination of [reward type, team, market]. At the end of the epoch: -1. Recurring reward transfers (set up by the parties funding the rewards) are made to the reward account(s) for a specific reward type, for one or more markets in scope where the total reward metric is `>0`. See [transfers](./0057-TRAN-transfers.md#recurring-transfers-to-reward-accounts). -1. Then the entire balance of each reward account is distributed to the parties with a non-zero reward metric for that reward type and market, pro-rata by their reward metric. +1. Recurring reward transfers (set up by the parties funding the rewards or via governance) are made to the reward account(s) for a specific reward type, for one or more markets in scope where the total reward metric is `>0`. See [transfers](./0057-TRAN-transfers.md#recurring-transfers-to-reward-accounts). +1. Then the entire balance of each reward account is distributed amongst entities with a non-zero reward metric for that reward type and market using the mechanism specified in the recurring transfer. +1. Distributed rewards are transferred to a [vesting account](./0085-RVST-rewards_vesting.md). -## Reward metrics +## Individual reward metrics -Fee-based reward metrics are scoped by [`reward type`, `market`, `party`] (this triplet can be thought of as a primary key for fee-based reward metrics). -Therefore a party may be in scope for the same reward type multiple times but no more than once per market per epoch. -Metrics will be calculated at the end of every epoch, for every eligible party, in each market for each reward type. +Individual reward metrics are scoped by [`recurring transfer`, `market`, `party`] (this triplet can be thought of as a primary key for fee-based reward metrics). + +Therefore a party may be in scope for the same reward type multiple times per epoch. +Metrics will be calculated at the end of every epoch, for every eligible party, in each market, for each recurring transfer. Metrics only need to be calculated where the [market, reward type] reward account has a non-zero balance of at least one asset. Reward metrics will be calculated once for each party/market combination in the reward metric asset which is the [settlement asset](0070-MKTD-market-decimal-places.md) of the market. This is the original precision for the metric source data. -### Market activity (fee based) reward metrics +For reward metrics relating to trading, an individual must meet the [staking requirement](./0057-TRAN-transfers.md#recurring-transfers-to-reward-accounts) **AND** [notional time-weighted average position requirement](./0057-TRAN-transfers.md#recurring-transfers-to-reward-accounts)) set in the recurring transfer. If they do not then their reward metric is set to `0`. Note, these requirements do not apply to the [validator ranking metric](#validator-ranking-metric) or the [market creation reward metric](#market-creation-reward-metrics). + +For reward transfers where the [scope](./0057-TRAN-transfers.md#recurring-transfers-to-reward-accounts) is set to teams, each party must meet the minimum time in team requirement. That is, given a party has been in a team for $N$ epochs, if $N$ is strictly less than the network parameter `rewards.minimumEpochsInTeam` (an integer defaulting to `0`) their reward metric is set to `0`. -There will be three market activity reward metrics calculated based on fees (as a proxy for activity). -Each of these represents a reward type with its own segregated reward accounts for each market. +### Fee-based reward metrics + +There will be three reward metrics calculated based on fees. 1. Sum of maker fees paid by the party on the market this epoch 1. Sum of maker fees received by the party on the market this epoch @@ -49,9 +52,84 @@ Each of these represents a reward type with its own segregated reward accounts f These metrics apply only to the sum of fees for the epoch in question. That is, the metrics are reset to zero for all parties at the end of the epoch. -If the reward account balance is 0 at the end of the epoch for a given market, any parties with non-zero metrics will not be rewarded for that epoch and their metric scores do not roll over (they are still zeroed). +If the reward account balance is `0` at the end of the epoch for a given recurring transfer, any parties with non-zero metrics will not be rewarded for that epoch and their metric scores do not roll over (they are still zeroed). + +Fee-based reward metrics (the total fees paid/received by each party as defined above) are stored in [LNL checkpoints](./0073-LIMN-limited_network_life.md) and are restored after a checkpoint restart to ensure rewards are not lost. + +### Average position metric + +The average position metric, $m_{ap}$, measures each parties time-weighted average position over a number of epochs. + +At the start of each epoch, the network must reset each parties time weighted average position for the epoch ($\bar{P}$) to `0`. Whenever a parties position changes during an epoch, **and** at the end of the epoch, this value should be updated as follows. + +Let: + +- $\bar{P}$ be the parties time weighted average position in the epoch so far +- $P_{n}$ be the parties position before their position changed +- $t_{n}$ be the time the party held the previous position in seconds +- $t$ be the amount of time elapsed in the current epoch so far + + +$$\bar{P} \leftarrow \bar{P} \cdot \left(1 - \frac{t_{n}}{t}\right) + \frac{|P_{n}| \cdot t_{n}}{t}$$ + +At the end of the epoch, the network must store the parties time weighted average position and then calculate their average position reward metric as follows. + +Let: + +- $m_{ap}$ be the parties average position reward metric +- $\bar{P_{i}}$ be the parties time weighted average position in the $i$-th epoch +- $N$ be the window length specified in the recurring transfer. + +$$m_{ap} = \frac{\sum_{i}^{n}\bar{P_{i}}}{N}$$ + +### Relative return metric + +The relative return metric, $m_{rr}$, measures each parties average relative return, weighted by their [time-weighted average position](#average-position-metric), over a number of epochs. + +At the end of each epoch, the network must calculate and store the parties relative returns as follows. + +Let: + +- $r_i$ be the parties relative returns in the epoch +- $m2m_{wins}$ be the sum of all mark-to-market win transfers in the epoch +- $m2m_{losses}$ be the sum of all mark-to-market loss transfers in the epoch +- $\bar{P}$ be the parties time-weighted average position in the epoch. + +$$r = \frac{|m2m_{wins}| - |m2m_{losses}|}{\bar{P}}$$ + +And calculate their average relative returns over the last $N$ epochs as follows. + +Let: + +- $m_{rr}$ be the parties relative return reward metric +- $r_i$ be the parties change in pnl in the i th epoch +- $N$ be the window length specified in the recurring transfer. + +$$m_{rr} = \max(\frac{\sum_{i}^{n}{r_{i}}}{N}, 0)$$ + +### Returns volatility metric + +The return volatility metric, $m_{rv}$, measures the volatility of a parties returns across a number of epochs. + +At the end of an epoch, if a party has had net returns less than or equal to `0` over the last $N$ epochs (where $N$ is the window length specified in the recurring transfer), their reward metric $m_{rv}$ is set to `0`. Otherwise, the network should calculate the variance of the set of each parties returns over the last $N$ epochs. -Market activity (fee based) reward metrics (the total fees paid/received by each party as defined above) are stored in [LNL checkpoints](./0073-LIMN-limited_network_life.md) and are restored after a checkpoint restart to ensure rewards are not lost. +Given the set: + +$$R = \{r_i \mid i = 1, 2, \ldots, N\}$$ + +The reward metric $m_{rv}$ is the variance of the set $R$. + +### Validator ranking metric + +The validator ranking metric, $m_v$, measures the ranking score of consensus and standby validators. + +At the end of each epoch, for each party who **is** a consensus or standby validator set their reward metric as follows. + +$$m_v = ranking_score$$ + +If a party **is not** a consensus or standby validator, their reward metric is simply: + +$$m_v = 0$$ ### Market creation reward metrics @@ -82,46 +160,129 @@ This flag is used to prevent any given funder from funding a creation reward in Market creation reward metrics (both each market's `cumulative volume` and the payout record flags to identify [funder, market scope, reward asset] combinations that have already been rewarded) are stored in [LNL checkpoints](./0073-LIMN-limited_network_life.md) and will be restored after a checkpoint restart. +Note this reward metric **is not** available for team rewards. + +## Team reward metrics + +All metrics (except [market creation](#market-creation-reward-metrics)) can be used to define the distribution of both individual rewards and team rewards. + +A team’s reward metric is the average metric score of the top performing `n` % of team members by number where `n` is specified when creating the recurring transfer (i.e. for a team of 100 parties with `n=0.1`, the 10 members with the highest metric score). + ## Reward accounts -Trading reward accounts are defined by the reward asset (the asset in which the reward is paid out), the market, and the reward type (metric). -That is, there can be multiple rewards with the same type paid in different assets for the same market. +Trading reward accounts are defined by a hash of the fields specified in the recurring transfer funding the reward account (see the [transfers](./0057-TRAN-transfers.md#recurring-transfers-to-reward-accounts) spec for relevant details about each field). This allows multiple recurring transfers to fund the same reward pool. + +Note as part of the recurring transfer a user specifies a settlement asset. The market settlement asset has nothing to do in in particular with the asset used to pay out a reward. -Note that the market settlement asset has nothing to do in particular with the asset used to pay out a reward for a market. That is, a participant might receive rewards in the settlement asset of the market, in VEGA governance tokens, and in any number of other unrelated tokens (perhaps governance of "loyalty"/reward tokens issued by LPs or market creators, or stablecoins like DAI). -Reward accounts are funded by setting up recurring transfers, which may be set to occur only once for a one off reward. -These allow a reward type to be automatically funded on an ongoing basis from a pool of assets. -Recurring transfers can target groups of markets, or all markets for a settlement asset, in which case the amount paid to each market is determined pro-rata by the markets' relative total reward metrics for the given reward type. See [transfers](./0057-TRAN-transfers.md) for more detail. +Reward accounts are funded by setting up recurring transfers, which may be set to occur only once for a one off reward. These allow a reward type to be automatically funded on an ongoing basis from a pool of assets. +Recurring transfers can target groups of markets, or all markets for a settlement asset. See [transfers](./0057-TRAN-transfers.md) for more detail. Reward accounts and balances must be saved in [LNL checkpoints](./0073-LIMN-limited_network_life.md) to ensure all funds remain accounted for across a restart. ## Reward distribution -All rewards are paid out at the end of each epoch *after* [recurring transfers](0057-TRAN-transfers.md) have been executed. -The entire reward account balance is paid out every epoch unless the total value of the metric over all parties is zero, in which case the balance will also be zero anyway (there are no fractional payouts). -There are no payout delays, rewards are paid out instantly at epoch end. +All rewards are distributed to [vesting accounts](./0085-RVST-rewards_vesting.md) at the end of each epoch *after* [recurring transfers](0057-TRAN-transfers.md) have been executed. Funds distributed to the vesting account will not start vesting until the [`lock period`](./0057-TRAN-transfers.md#recurring-transfers-to-reward-accounts) defined in the recurring transfer has expired. -Rewards will be distributed pro-rata by the party's reward metric value to all parties that have metric values `>0`. Note that for the market creation reward, the metric is defined to either be `0` or `1`, which will lead to equal payments for each eligible market under the pro-rata calculation. If we have reward account balance `R` and parties `p_1 – p_n` with non-zero metrics `m_1 – m_n` on the market in question: +The entire reward account balance is paid out every epoch unless the total value of the metric over all entities is zero, in which case the balance will also be zero anyway (there are no fractional payouts). -```math -[p_1, m_1] -[p_2, m_2] -... -[p_n, m_n] +Rewards are first [distributed amongst entities](#distributing-rewards-amongst-entities) (individuals or teams) and then any rewards distributed to teams are [distributed amongst team members](#distributing-rewards-amongst-team-members). + +### Distributing rewards amongst entities + +Rewards are distributed amongst entities based on the distribution method defined in the recurring transfer. + +The protocol currently supports the following distribution strategies: + +- [pro-rata]:(#distributing-pro-rata) distributed pro-rata by reward metric +- [rank]:(#distributing-based-on-rank) distributed by entities rank when ordered by reward metric + +#### Distributing pro-rata + +Rewards funded using the pro-rata strategy should be distributed pro-rata by each entities reward metric scaled by any active multipliers that party has, i.e. + +Let: + +- $d_{i}$ be the payout factor for entity $i$ +- $r_{i}$ be the reward metric value for entity $i$ +- $M_{i}$ be the sum of all reward payout multipliers for entity $i$ (reward payout multipliers include the [activity streak multiplier](./0086-ASPR-activity_streak_program.md#applying-the-activity-reward-multiplier) and [bonus rewards multiplier](./0085-RVST-rewards_vesting.md#determining-the-rewards-bonus-multiplier)). +- $s_{i}$ be the share of the rewards for entity $i$ + +$$d_{i}=r_{i} M_{i}$$ + +Note if the entity is a team, $M_{i}$ is set to `1` as reward payout multipliers are considered later when distributing rewards [amongst the team members](#distributing-rewards-amongst-team-members). + +Calculate each entities share of the rewards, $s_{i}$ pro-rata based on $d_{i}$, i.e. + +$$s_{i} = \frac{d_{i}}{\sum_{i=1}^{n}d_{i}}$$ + +#### Distributing based on rank + +Rewards funded using the rank-distribution strategy should be distributed as follows. + +1. Calculate each entity's reward metric. +2. Arrange all entities in a list in descending order based on their reward metric values and determine their rank. If multiple entities share the same reward metric value, they should be assigned the same rank. The next entity's rank should be adjusted to account for the shared rank among the previous entities. For instance, if two entities share rank 2, the next entity should be assigned rank 4 (since there are two entities with rank 2). +3. Set the entities `share_ratio` based on their position in the `rank_table` specified in the recurring transfer. +4. Calculate each entities share of the rewards. + +```pseudo +Given: + rank_table = [ + {"start_rank": 1, "share_ratio": 10}, + {"start_rank": 2, "share_ratio": 5}, + {"start_rank": 4, "share_ratio": 2}, + {"start_rank": 10, "share_ratio": 1}, + {"start_rank": 20, "share_ratio": 0}, + ] + rank=6 + +Then: + share_ratio=2 ``` -Then calculate `M := m_1 + m_2 + … + m_n` and transfer `R ✖️ m_i / M` to party `p_i` (for each `p_i`) at the end of the epoch. +Calculate each entities share of the rewards as follows. + +Let: + +- $d_{i}$ be the payout factor for entity $i$ +- $s_{i}$ be the share of the rewards for entity $i$ +- $r_{i}$ be the share ratio of entity $i$ as determined from the rank table +- $M_{i}$ be the sum of all reward payout multipliers for entity $i$ (reward payout multipliers include the [activity streak multiplier](./0086-ASPR-activity_streak_program.md#applying-the-activity-reward-multiplier) and [bonus rewards multiplier](./0085-RVST-rewards_vesting.md#determining-the-rewards-bonus-multiplier)). + +$$d_{i}=M_{i} * r_{i}$$ + +Note if the entity is a team, $M_{i}$ is set to 1 as reward payout multipliers are considered later when distributing rewards [amongst the team members](#distributing-rewards-amongst-team-members). + +Calculate each entities share of the rewards, $s_{i}$ pro-rata based on $d_{i}$, i.e. + +$$s_{i} = \frac{d_{i}}{\sum_{i=1}^{n}d_{i}}$$ + +### Distributing rewards amongst team members + +If rewards are distributed to a team, rewards must then be distributed between team members who had a reward metric, $m$, greater than `0` based on their payout multipliers. + +Let: + +- $d_{i}$ be the payout for team member $i$ +- $s_{i}$ be the share of the rewards for team member $i$ +- $m$ be the reward metric of the team member +- $M_{i}$ be the sum of all reward payout multipliers for entity $i$ (reward payout multipliers include the [activity streak multiplier](./0086-ASPR-activity_streak_program.md#applying-the-activity-reward-multiplier) and [bonus rewards multiplier](./0085-RVST-rewards_vesting.md#determining-the-rewards-bonus-multiplier)). + +$$d_{i} = \begin{cases} + 0 &\text{if } m = 0 \\ + M_{i} &\text{if } m > 0 +\end{cases}$$ -If `M=0` (no-one incurred or received fees as specified by the metric type for the given market) then no transfer will have been made to the reward account and therefore there are no rewards to pay out. -The transfer will be retried the next epoch if it is still active. +Calculate each parties share of the rewards, $s_{i}$ pro-rata based on $d_{i}$, i.e. -Reward payouts will be calculated using the decimal precision of the reward payout asset. If this allows less precision than the reward metric asset (the market's settlement asset) then the ratios between reward payouts may not match exactly the ratio between the reward metrics for any two parties. All funds will always be paid out. +$$s_{i} = \frac{d_{i}}{\sum_{i=1}^{n}d_{i}}$$ ## Acceptance criteria ### Funding reward accounts (0056-REWA-001) + Trading reward accounts are defined by a pair: [`payout_asset, dispatch_strategy`]. There are two assets configured on the Vega chain: $VEGA and USDT. @@ -134,6 +295,7 @@ Run for another epoch with no fee generated. Expect no transfer to be made to th ### Funding reward accounts - with markets in scope (0056-REWA-002) + There are two assets configured on the Vega chain: $VEGA and USDT. Setup a recurring transfer of 1000 $VEGA with the following dispatch strategy: asset=`USDT`, metric=`DISPATCH_METRIC_TAKER_FEES_PAID`, markets=[`market1`, `market2`]. @@ -144,6 +306,7 @@ Run for another epoch with no fee generated. Expect no transfer to be made to th ### Distributing fees paid rewards (0056-REWA-010) + #### Rationale 1 A market has 2 reward accounts for the metric, one paying in $VEGA and the other paying in USDC. @@ -191,13 +354,14 @@ At the end of epoch 2: ### Distributing fees paid rewards - unfunded account (0056-REWA-011) + #### Rationale 2 -This is identical to [acceptance code REWA-010](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-fees-paid-rewards-0056-rewa-010) just without funding the corresponding reward account. +This is identical to [acceptance code `REWA 010`](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-fees-paid-rewards-0056-rewa-010) just without funding the corresponding reward account. #### Setup 2 -Identical to [acceptance code REWA-010](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-fees-paid-rewards-0056-rewa-010) +Identical to [acceptance code `REWA 010`](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-fees-paid-rewards-0056-rewa-010) #### Funding reward accounts 2 @@ -209,17 +373,18 @@ At the end of epoch 2 although there was trading in the market `ETHUSD-MAR22`, n ### Distributing fees paid rewards - funded account - no trading activity (0056-REWA-012) + #### Rationale 3 After having an epoch with trading activity, fund the reward account, but have no trading activity and assert that no payout is made. #### Setup 3 -Identical to [acceptance code REWA-010](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-fees-paid-rewards-0056-rewa-010) +Identical to [acceptance code `REWA 010`](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-fees-paid-rewards-0056-rewa-010) #### Funding reward accounts 3 -Identical to [acceptance code REWA-010](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-fees-paid-rewards-0056-rewa-010) +Identical to [acceptance code `REWA 010`](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-fees-paid-rewards-0056-rewa-010) Then, during epoch 3 we fund the reward accounts for the metric: @@ -233,6 +398,7 @@ Looking only at epoch 3 - as no trading activity was done, we expect the reward ### Distributing fees paid rewards - multiple markets (0056-REWA-013) + #### Rationale 4 There are multiple markets, each paying its own reward where due. @@ -261,7 +427,7 @@ There are no markets. #### Expectation 4 -The calculation of eligibility is identical to [acceptance code REWA-010](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-fees-paid-rewards-0056-rewa-010) but the expected payout is: +The calculation of eligibility is identical to [acceptance code `REWA 010`](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-fees-paid-rewards-0056-rewa-010) but the expected payout is: - for market `ETHUSD-MAR22`: - `party_1` is paid `90 x 3.36 / 4.98 = 60.72.` $VEGA from the reward account into its $VEGA general account. @@ -272,6 +438,7 @@ The calculation of eligibility is identical to [acceptance code REWA-010](https: ### Distributing maker fees received rewards (0056-REWA-020) + #### Rationale 5 A market has 2 reward accounts for the metric, one paying in $VEGA and the other paying in USDC. @@ -317,13 +484,14 @@ At the end of epoch `2` `party_0` is paid `120 x 2.8 / (2.79+2.8)` USDC from the ### Distributing maker fees received rewards - unfunded account (0056-REWA-021) + #### Rationale 6 -This is identical to [acceptance code REWA-020](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-maker-fees-received-rewards-0056-rewa-020) just without funding the corresponding reward account. +This is identical to [acceptance code `REWA 020`](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-maker-fees-received-rewards-0056-rewa-020) just without funding the corresponding reward account. #### Setup 6 -Identical to [acceptance code REWA-020](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-maker-fees-received-rewards-0056-rewa-020). +Identical to [acceptance code `REWA 020`](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-maker-fees-received-rewards-0056-rewa-020). #### Funding reward accounts 6 @@ -341,11 +509,11 @@ After having an epoch with trading activity, fund the reward account, but have n #### Setup 7 -Identical to [acceptance code REWA-020](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-maker-fees-received-rewards-0056-rewa-020) +Identical to [acceptance code `REWA 020`](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-maker-fees-received-rewards-0056-rewa-020) #### Funding reward accounts 7 -Identical to [acceptance code REWA-020](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-maker-fees-received-rewards-0056-rewa-020) +Identical to [acceptance code `REWA 020`](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-maker-fees-received-rewards-0056-rewa-020) Then, during epoch 3 we fund the reward accounts for the metric: @@ -359,6 +527,7 @@ Looking only at epoch 3 - as no trading activity was done, we expect the reward ### Distributing maker fees received rewards - multiple markets (0056-REWA-023) + #### Rationale 8 There are multiple markets, each paying its own reward where due. @@ -387,7 +556,7 @@ There are no markets. #### Expectation 8 -The calculation of eligibility is identical to [acceptance code REWA-020](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-maker-fees-received-rewards-0056-rewa-020) but the expected payout is: +The calculation of eligibility is identical to [acceptance code `REWA 020`](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-maker-fees-received-rewards-0056-rewa-020) but the expected payout is: - for market `ETHUSD-MAR22`: - At the end of epoch `2` `party_1` is paid `90 x 2.79 / (2.79+2.8)` $VEGA from the reward account into its `$VEGA` general account. @@ -398,17 +567,18 @@ The calculation of eligibility is identical to [acceptance code REWA-020](https: ### Distributing LP fees received rewards (0056-REWA-030) + #### Rationale 9 A market has 2 reward accounts for the metric, one paying in $VEGA and the other paying in USDC. #### Setup 9 -Identical to [acceptance code REWA-020](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-maker-fees-received-rewards-0056-rewa-020). +Identical to [acceptance code `REWA 020`](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-maker-fees-received-rewards-0056-rewa-020). #### Funding reward accounts 9 -Identical to [acceptance code REWA-020](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-maker-fees-received-rewards-0056-rewa-020). +Identical to [acceptance code `REWA 020`](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-maker-fees-received-rewards-0056-rewa-020). #### Expectation 9 @@ -423,13 +593,14 @@ At the end of epoch `2` `party_0` is paid `120` `USDC` from the reward account i ### Distributing LP fees received rewards - unfunded account (0056-REWA-031) + #### Rationale 10 -Identical to [acceptance code REWA-030](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-lp-fees-received-rewards-0056-rewa-030), but without funding the corresponding reward account. +Identical to [acceptance code `REWA-030`](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-lp-fees-received-rewards-0056-rewa-030), but without funding the corresponding reward account. #### Setup 10 -Identical to [acceptance code REWA-030](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-lp-fees-received-rewards-0056-rewa-030) +Identical to [acceptance code `REWA-030`](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-lp-fees-received-rewards-0056-rewa-030) #### Funding reward accounts 10 @@ -441,17 +612,18 @@ At the end of epoch 2 although there was trading in the market `ETHUSD-MAR22`, n ### Distributing maker fees received rewards - funded account - no trading activity (0056-REWA-032) + #### Rationale 11 After having an epoch with trading activity, fund the reward account, but have no trading activity and assert that no payout is made. #### Setup 11 -Identical to [acceptance code REWA-030](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-lp-fees-received-rewards-0056-rewa-030) +Identical to [acceptance code `REWA-030`](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-lp-fees-received-rewards-0056-rewa-030) #### Funding reward accounts 11 -Identical to [acceptance code REWA-030](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-lp-fees-received-rewards-0056-rewa-030) +Identical to [acceptance code `REWA-030`](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-lp-fees-received-rewards-0056-rewa-030) Then, during epoch 3 we fund the reward accounts for the metric: @@ -471,7 +643,7 @@ There are multiple markets, each paying its own reward where due. #### Setup 12 -Identical to [acceptance code REWA-023](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-maker-fees-received-rewards---multiple-markets-0056-rewa-023) +Identical to [acceptance code `REWA-023`](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-maker-fees-received-rewards---multiple-markets-0056-rewa-023) #### Funding reward accounts 12 @@ -481,7 +653,7 @@ Identical to [acceptance code REWA-023](https://github.com/vegaprotocol/specs/bl #### Expectation 12 -The calculation of eligibility is identical to [acceptance code REWA-030](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-lp-fees-received-rewards-0056-rewa-030) but the expected payout is: +The calculation of eligibility is identical to [acceptance code `REWA-030`](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-lp-fees-received-rewards-0056-rewa-030) but the expected payout is: - for market `ETHUSD-MAR22`: - At the end of epoch `2` `party_0` is paid `90` `$VEGA` from the reward account into its `$VEGA` general account. @@ -490,6 +662,7 @@ The calculation of eligibility is identical to [acceptance code REWA-030](https: ### Distributing market creation rewards - no eligibility (0056-REWA-040) + #### Rationale 13 Market has been trading but not yet eligible for proposer bonus. @@ -509,6 +682,7 @@ At the end of the epoch no payout has been made for the market `ETHUSDT` and the ### Distributing market creation rewards - eligible are paid no more than once (0056-REWA-041) + #### Rationale 14 Once a market creator has been paid, they are not paid again from the same reward pool @@ -531,6 +705,7 @@ At the end of epoch 3 make sure that no transfer is made to the reward account a ### Distributing market creation rewards - account funded after reaching requirement (0056-REWA-042) + #### Rationale 15 Market goes above the threshold in trading value in an epoch before the reward account for the market for the reward type has any balance - proposer does receive reward even if account is funded at a later epoch. @@ -552,6 +727,7 @@ The reward pool balance should be 0. ### Distributing market creation rewards - multiple asset rewards (0056-REWA-043) + #### Rationale 16 A market should be able to be rewarded multiple times if several reward pools are created with different payout assets. @@ -574,6 +750,7 @@ The reward pool balance should be 0. ### Distributing market creation rewards - multiple asset rewards simultaneous payout (0056-REWA-045) + #### Rationale 17 A market should be able to be rewarded multiple times if several reward pools are created with different payout assets. @@ -597,6 +774,7 @@ The reward pool balance should be 0. ### Distributing market creation rewards - Same asset multiple party rewards (0056-REWA-044) + #### Rationale 18 A market reward pool funded with the same asset by different parties should pay out to eligible markets as many times as there are parties, assuming threshold is reached. @@ -625,6 +803,7 @@ The reward account balance should still be empty, as there were no eligible mark ### Distributing market creation rewards - Multiple markets eligible, one already paid (0056-REWA-046) + #### Rationale 19 A market reward pool funded with the same asset by the same party with different market scopes should pay to all markets even if already paid @@ -657,6 +836,7 @@ At the end of epoch 3, 10000 VEGA should be split between the `BTCDAI` creator a ### Reward accounts cannot be topped up with a one-off transfer (0056-REWA-049) + The following account types require metric-based distribution. As a one-off transfer cannot specify how it is rewarded, one-off transfers to metric-based reward pools must be **rejected**. A one-off transfer from a user to any of the following account types is rejected. No assets are transferred: @@ -667,6 +847,7 @@ A one-off transfer from a user to any of the following account types is rejected ### Distributing market creation rewards - Market ineligible through metric asset (0056-REWA-048) + #### Rationale 20 A market reward pool funded with the a specific metric asset should not pay out to markets not trading in that asset @@ -691,6 +872,7 @@ At the end of epoch 2, 10000 VEGA rewards should be distributed to only the `ETH ### Distributing market creation rewards - Multiple markets eligible, one already paid, specified asset (0056-REWA-047) + #### Rationale 21 A market reward pool funded with the same asset by the same party with different market scopes should pay to all markets even if already paid @@ -725,6 +907,7 @@ At the end of epoch 3, 10000 VEGA should be distributed split between the `BTCUS ### Updating the network parameter `rewards.marketCreationQuantumMultiple` (0056-REWA-050) + #### Rationale 22 When the network parameter `rewards.marketCreationQuantumMultiple` is changed via governance, the change should take affect @@ -738,10 +921,72 @@ immediately and the new value used at the end of the epoch to decide if market c - Transfer 10000 $VEGA to `ETHUSDT | market creation | $VEGA` - During epoch 1 start trading such that traded value for fee purposes in USDT is less than 10^6 but greater than 10^5 - During epoch 2 update the value of `marketCreationQuantumMultiple` via governance to `10^5`. - + #### Expectation 22 At the end of epoch 2, 10000 VEGA rewards should be distributed to the `ETHUSDT` creator. - The general account balance of the `ETHUSDT` creator should be 10000. - The reward pool balance should be 0. + +### Reward Eligibility + +- If a parties staked governance tokens ($VEGA) is strictly less than the `staking_requirement` specified in the recurring transfer funding the reward pool, then their reward metric should be `0` and they should receive no rewards (0056-REWA-076). +- If a parties time-weighted average position (across all in scope-markets) is strictly less than the `notional_time_weighted_average_position_requirement` specified in the recurring transfer funding the reward pool, then their reward metric should be `0` and they should receive no rewards (0056-REWA-077). + +### Average Position + +- If an eligible party opens a position at the beginning of the epoch, their average position reward metric should be equal to the size of the position at the end of the epoch (0056-REWA-078). +- If an eligible party held an open position at the start of the epoch, their average position reward metric should be equal to the size of the position at the end of the epoch (0056-REWA-079). +- If an eligible party opens a position half way through the epoch, their average position reward metric should be half the size of the position at the end of the epoch (0056-REWA-080). +- If an eligible party held an open position at the start of the epoch and closes it half-way through the epoch, their average position reward metric should be equal to the size of that position at the end of the epoch (0056-REWA-081). +- If an eligible party held positions in multiple in-scope markets, their average position reward metric should be the sum of the size of their time-weighted-average-position in each market (0056-REWA-082). +- If a `window_length>1` is specified in the recurring transfer, an eligible parties average position reward metric should be the average of their reward metrics over the last `window_length` epochs (0056-REWA-083). + +### Relative returns + +- If an eligible party has negative net returns, their relative returns reward metric should be zero (0056-REWA-084). +- If an eligible party has positive net returns, their relative returns reward metric should be equal to the size of their returns divided by their time-weighted average position (0056-REWA-085). +- If an eligible party is participating in multiple in-scope markets, their relative returns reward metric should be the sum of their relative returns from each market (0056-REWA-086). +- If a `window_length>1` is specified in the recurring transfer, an eligible parties relative returns reward metric should be the average of their reward metrics over the last `window_length` epochs (0056-REWA-087). + +### Returns volatility + +- If an eligible party has net relative returns less than or equal to `0` over the last `window_length` epochs, their returns volatility reward metric should be zero (0056-REWA-088). +- If an eligible party has net relative returns strictly greater than `0` over the last `window_length` epochs, their returns volatility reward metric should equal the variance of their relative returns over the last `window_length` epochs (0056-REWA-089). +- If an eligible party has net relative returns strictly greater than `0` over the last `window_length` epochs in multiple in-scope markets, their return volatility reward metric should be the variance of their relative returns in each market (0056-REWA-090). + +### Validator ranking metric + +- If a party is a consensus or standby validator their validator ranking reward metric should be set to their ranking score (0056-REWA-091). +- If a party is not a consensus or standby validator their validator ranking reward metric should be set to `0` (0056-REWA-092). +- For a party that is a consensus or standby validator, the [staking requirement](https://github.com/vegaprotocol/specs/blob/palazzo/protocol/0057-TRAN-transfers.md#recurring-transfers-to-reward-accounts) and [notional time-weighted average position requirement](https://github.com/vegaprotocol/specs/blob/palazzo/protocol/0057-TRAN-transfers.md#recurring-transfers-to-reward-accounts) do not apply to their validator ranking metric. (0056-REWA-109). +- A party does not need to meet the staking requirements and notional time-weighted average position set in the recurring transfer for market creation reward metric. (0056-REWA-110). + +### Distribution Strategy + +- If the pro-rata distribution strategy was specified in the recurring transfer, each eligible parties share of the rewards pool should be equal to their reward metric (assuming no other multipliers) (0056-REWA-093). +- If the rank distribution strategy was specified in the recurring transfer, each eligible parties share of the reward pool should be equal to the `share_ratio` defined by their position in the `rank_table` (assuming no other multipliers) (0056-REWA-094). + + +### Entity Scope + +#### Individuals + +- If the entity scope is `ENTITY_SCOPE_INDIVIDUALS`, transfers setting the `teams_scope` field should be rejected as invalid (0056-REWA-095). +- If the entity scope is `ENTITY_SCOPE_INDIVIDUALS`, transfers not setting the `individual_scope` field should be rejected as invalid (0056-REWA-096). +- If the entity scope is `ENTITY_SCOPE_INDIVIDUALS` and the individual scope is `INDIVIDUAL_SCOPE_ALL`, all individual parties should be eligible for rewards providing they meet all other eligibility conditions (0056-REWA-097). +- If the entity scope is `ENTITY_SCOPE_INDIVIDUALS` and the individual scope is `INDIVIDUAL_SCOPE_IN_A_TEAM`, only individual parties who are in a team should be eligible for rewards providing they meet all other eligibility conditions (0056-REWA-098). +- If the entity scope is `ENTITY_SCOPE_INDIVIDUALS` and the individual scope is `INDIVIDUAL_SCOPE_NOT_IN_A_TEAM`, only individual parties not in a team should be eligible for rewards providing they meet all other eligibility conditions (0056-REWA-099). +- If the entity scope is `ENTITY_SCOPE_INDIVIDUALS`, rewards should be distributed among eligible individual parties according to each parties reward metric value (0056-REWA-100). + +#### Teams + +- If the entity scope is `ENTITY_SCOPE_TEAMS`, transfers setting the `individual_scope` field should be rejected as invalid (0056-REWA-101). +- If the entity scope is `ENTITY_SCOPE_TEAMS` transfers not setting the `n_top_performers` field should be rejected as invalid (0056-REWA-102). +- If the entity scope is `ENTITY_SCOPE_TEAMS` and the teams scope is not-set, then all teams are eligible for rewards (0056-REWA-103). +- If the entity scope is `ENTITY_SCOPE_TEAMS` and the teams scope is set, then only the teams specified in the teams scope are eligible for rewards (0056-REWA-104). +- If the entity scope is `ENTITY_SCOPE_TEAMS`, then rewards should be allocated to teams according to each teams reward metric value (0056-REWA-105). +- Each team’s reward metric should be the average metric of the top `n_top_performers` % of team members, e.g. for a team of 100 parties with `n_top_performers=0.1`, the 10 members with the highest metric (0056-REWA-106). +- If a team member has a non-zero reward metric, they should receive a share of the rewards proportional to their individual payout multipliers (0056-REWA-107). +- If a team member has a zero reward metric, they should receive no share of the rewards allocated to the team (0056-REWA-108). diff --git a/protocol/0057-TRAN-transfers.md b/protocol/0057-TRAN-transfers.md index 52bf0480e..4cffbc22f 100644 --- a/protocol/0057-TRAN-transfers.md +++ b/protocol/0057-TRAN-transfers.md @@ -39,9 +39,11 @@ In order to prevent the abuse of user-initiated transfers as spam attack there w ## Minimum transfer amount -This is controlled by the `transfer.minTransferQuantumMultiple` and quantum specified for the [asset](0040-ASSF-asset_framework.md)). +This is controlled by the `transfer.minTransferQuantumMultiple` and quantum specified for the [asset](0040-ASSF-asset_framework.md). The minimum transfer amount is `transfer.minTransferQuantumMultiple x quantum`. +If a user is transferring funds from a vested account, if their balance (expressed in quantum) is less than the minimum amount, they should be able to transfer the full balance (note, transferring less than the full balance is not permitted). + ## Recurring transfers A party can also setup recurring transfers which will happen at the end of every epoch, before the next epoch begins. @@ -79,14 +81,49 @@ If the `amount` is less than `transfer.minTransferQuantumMultiple x quantum` the Read this section alongside the [rewards](./0056-REWA-rewards_overview.md) specification. -To be able to dispatch rewards to reward pools of multiple markets pro rata to the contribution of the reward metric (e.g. received maker fees) in the market vs the total of the measured metric across all in scope markets, recurring transfers support auto dispatch in the following way: +When funding a reward account with a recurring transfer, the reward account funded is the hash of the fields in the recurring transfer specific to funding reward accounts (listed below). -- When transferring to a reward account, the transaction must also include the following: +When transferring to a reward account, the transaction must include the following: - `reward metric` — the type of reward (see [rewards](./0056-REWA-rewards_overview.md)) - `reward metric asset` — (the settlement asset of all markets that will be in scope for the transfer) - `market scope` — a subset of markets in which parties are eligible to be rewarded from this transfer. - If the market scope is not defined / an empty list, it is taken as all the markets that settle in the reward metric asset. + +A party can further control their recurring transfer funding the reward pool by defining the entities which are within scope. Entities within scope can be either individual parties or [teams](./0083-RFPR-on_chain_referral_program.md#glossary). When scoping individuals, a subset of keys can be detailed, and when scoping teams a specific set of team ids can be detailed. + +To support entity scoping, the transaction include the following fields: + +- `entity scope` - mandatory enum which defines the entities within scope. + - `ENTITY_SCOPE_INDIVIDUALS` - the rewards must be distributed directly amongst eligible parties + - `ENTITY_SCOPE_TEAMS` - the rewards must be distributed amongst directly eligible teams (and then amongst team members) +- `individual scope` - optional enum if the entity scope is `ENTITY_SCOPE_INDIVIDUALS` which defines the subset of individuals which are eligible to be rewarded. + - `INDIVIDUAL_SCOPE_ALL` - all parties on the network are within the scope of this reward + - `INDIVIDUAL_SCOPE_IN_TEAM` - all parties which are part of a team are within the scope of this reward + - `INDIVIDUAL_SCOPE_NOT_IN_TEAM` - all parties which are not part of a team are within the scope of this reward +- `team scope` - optional list if the reward type is `ENTITY_SCOPE_TEAMS`, field allows the funder to define a list of team ids which are eligible to be rewarded from this transfer +- `staking_requirement` - the required minimum number of governance (e.g. VEGA) tokens staked for a party to be considered eligible. Defaults to `0`. +- `notional_time_weighted_average_position_requirement` - the required minimum notional time-weighted averaged position required for a party to be considered eligible. Defaults to `0`. + +A party should be able to configure the distribution of rewards by specifying the following fields: + +- `window_length` - the number of epochs over which to evaluate the reward metric. The value should be limited to 100 epochs. +- `lock_period` - the number of epochs after distribution to delay [vesting of rewards](./0085-RVST-rewards_vesting.md#vesting-mechanics) by. +- `distribution_strategy` - enum defining which [distribution strategy](./0056-REWA-rewards_overview.md#distributing-rewards-between-entities) to use. + - `DISTRIBUTION_STRATEGY_PRO_RATA` - rewards should be distributed among entities [pro-rata](./0056-REWA-rewards_overview.md#distributing-pro-rata) by reward-metric. + - `DISTRIBUTION_STRATEGY_RANK` - rewards should be distributed among entities [based on their rank](./0056-REWA-rewards_overview.md#distributing-based-on-rank) when ordered by reward-metric. +- `rank_table` - if the distribution strategy is `DISTRIBUTION_STRATEGY_RANK`, an ordered list dictionaries defining the rank bands and share ratio for each band should be specified. Note, the `start_rank` values must be integers in an ascending order and the table can have strictly no more than 500 rows. + + ```pseudo + rank_table = [ + {"start_rank": 1, "share_ratio": 10}, + {"start_rank": 2, "share_ratio": 5}, + {"start_rank": 4, "share_ratio": 2}, + {"start_rank": 10, "share_ratio": 1}, + {"start_rank": 20, "share_ratio": 0}, + ] + ``` + - At the end of the epoch when the transfer is about to be distributed, it first calculates the contribution of each market to the sum total reward metric for all markets in the `market scope` and then distributes the transfer amount to the corresponding accounts of the markets pro-rata by their contribution to the total. Where the reward metric type is "market creation rewards", it is important that no market creator will receive more than one market creation reward paid in the same asset from the same source account (reward funder). @@ -118,13 +155,39 @@ Note: if there is no market with contribution to the reward metric - no transfer ## Fees -A fee is taken from all transfers, and paid out to validators in a similar manner to the existing [infrastructure fees](0061-REWP-pos_rewards.md). For recurring transfers, the fee is charged each time the transfer occurs. +A fee is taken from all transfers (except transfers from a vested account to a general account held by the same key), and paid out to validators in a similar manner to the existing [infrastructure fees](0061-REWP-pos_rewards.md). For recurring transfers, the fee is charged each time the transfer occurs. + +The fee is determined by the `transfer.fee.factor` and is subject to a cap defined by the multiplier `transfer.fee.maxQuantumAmount` as specified in the network parameters, which governs the proportion of each transfer taken as a fee. -The fee is set by the `transfer.fee.factor` [network parameter](#network-parameters) that defines the proportion of each transfer taken as a fee. +As such, the transfer fee value used will be: `min(transfer amount x transfer.fee.factor, transfer.fee.maxQuantumAmount x quantum)`, `quantum` is for asset The fee is taken from the transfer initiator's account immediately on execution, and is taken on top of the total amount transferred. It is [paid in to the infrastructure fee pool](./0029-FEES-fees.md#collecting-and-distributing-fees). Fees are charged in the asset that is being transferred. +Fee are primarily a spam-protection mechanism, so for accounts generating "useful activity" discounts apply. + +### Transfer fee discounts + +Let `D` stand for `transfer.feeDiscountDecayFraction`. This is a network parameter that specifies the how cumulated trading fees decay for the purpose of being used to do transfer-fee-free transfers. Minimum value is `0`, maximum value is any decimal strictly less than `1` and default it `0.5`. +Let `M` stand for network parameter `transfer.feeDiscountMinimumTrackedAmount`. Minimum value is `0`, there is no maximum beyond that dictated by the data type used and the default is `0.001`. + +For each party and for each asset store the an amount which tracks all trading fees paid and received by the party with transfer fees subtracted and the amount decayed as specified below. + +For each key for each asset assume you store a value denoted `c`. +During the epoch `k`: + +- if the party makes a transfer and `f` would be the theoretical fee the party should pay then the fee on the transfer that is actually charged is `-min(f-c,0)`. The system subsequently updates `c <- max(0,c-f)`. + +At the end of epoch `k`: + +1. update `c <- c x D`, i.e. apply the decay factor `D` + +1. update `c <- c + all_trading_fees_for_trades_involved_in`, where `all_trading_fees_for_trades_involved_in` are the cumulated trading fees paid by the aggressive party (taker fees) but also cumulated (with a +sign) the trading fees result from any trade in which the party was involved as the passive party (i.e. their limit order got lifted). + +1. if `c` is less than `M x quantum` (where quantum is the asset quantum) then set `c <- 0`. + +We need appropriate APIs to enable the frontend to display the amount eligible for fee-free transfers / correctly display the fee on any transfer a party is proposing. + ## Proposed command This new functionality requires the introduction of a new command in the transaction API. The payload is as follows: @@ -182,6 +245,9 @@ message CancelTransfer { | `spam.protection.maxUserTransfersPerEpoch` | String (integer) | strictly greater than `0` | `"20"` | The most transfers a use can initiate per epoch | | `transfer.minTransferQuantumMultiple` | String (decimal) | greater than or equal to `0`| `"0.1"` | This, when multiplied by `quantum` (which is specified per asset) determines the minimum transfer amount | | `transfer.fee.factor` | String (decimal) | in `[0.0,1.0]` | `"0.001"` | The proportion of the transfer charged as a fee | +| `transfer.fee.maxQuantumAmount` | String (decimal) | greater than or equal to `0` | `"100"` | The cap of the transfer fee | +| `transfer.feeDiscountMinimumTrackedAmount` | String (decimal) | greater than or equal to `0` | `"0.001"` | The lower bound of transfer fee tracked | +| `transfer.feeDiscountDecayFraction` | String (decimal) | greater than or equal to `0` and strictly less than `1` | `"0.5"` | The speed of cumulated trading fees decay for the purpose of being used to do transfer-fee-free transfers | ## Acceptance criteria @@ -189,21 +255,48 @@ message CancelTransfer { - As a user I can transfer funds from a general account I control to an other party's general account. Such transfer can be immediate or delayed. (0057-TRAN-001) - As a user I **cannot** transfer funds from a general account I control to reward account with a one-off transfer. (0057-TRAN-002) -- As a user I can transfer funds from a general account I control to an locked_for_staking. Such transfer can be immediate or delayed. This functionality is currently not implemented (so don't try to test) (0057-COSMICELEVATOR-TRAN-003). -- As a user I can transfer funds from a locked_from_staking account under my control to any party's general_account. Such transfer can be immediate or delayed. This functionality is currently not implemented (so don't try to test) (0057-COSMICELEVATOR-TRAN-004) +- As a user I can transfer funds from a general account I control to a locked_for_staking. Such transfer can be immediate or delayed. This functionality is currently not implemented (so don't try to test) (0057-PALAZZO-003). +- As a user I can transfer funds from a locked_from_staking account under my control to any party's general_account. Such transfer can be immediate or delayed. This functionality is currently not implemented (so don't try to test) (0057-PALAZZO-004) - As a user I cannot transfer funds from accounts that I do not control. (0057-TRAN-005) -- As a user I cannot transfer funds from accounts I own but from the type is not supported (e.g. margin, staking). (0057-TRAN-006) -- As a user I can do a transfer from any of the valid accounts (I control them and they're a valid source), and fees are taken from the source account when the transfer is executed. (0057-TRAN-007) - - The fee cost is correctly calculated using the network parameter +- As a user I cannot transfer funds from accounts I own but from the type is not supported: + - for accounts created in a futures market, bond and margin (0057-TRAN-006) +- As a user I can do a transfer from any of the valid accounts (I control them and they're a valid source), and fees are taken from the source account when the transfer is executed (when `transfer amount x transfer.fee.factor <= transfer.fee.maxQuantumAmount x quantum`). (0057-TRAN-007) + - The fee cost is correctly calculated using the network parameters listed above. + - If I have enough funds to pay transfer and fees, the transfer happens. + - If I do not have enough funds to pay transfer and fees, the transfer is cancelled. + - The fees are being paid into the infrastructure pool. + - The transfer fee discount is correctly applied with network parameter`transfer.feeDiscountDecayFraction`(0057-TRAN-014) + - The fee-free transfer amount is accessible through the API (0057-TRAN-017) +- As a user I can do a transfer from any of the valid accounts (I control them and they're a valid source), and fees are taken from the source account when the transfer is executed (when `transfer amount x transfer.fee.factor > transfer.fee.maxQuantumAmount x quantum`). (0057-TRAN-011) + - The fee cost is correctly calculated using the network parameters listed above. - If I have enough funds to pay transfer and fees, the transfer happens. - If I do not have enough funds to pay transfer and fees, the transfer is cancelled. - - The fees are being paid into the infrastructure pool + - The fees are being paid into the infrastructure pool. + - The transfer fee discount is correctly applied with network parameter `transfer.feeDiscountDecayFraction` (0057-TRAN-015) + - The fee-free transfer amount is accessible through the API (0057-TRAN-018) +- when a party makes a transfer and fee-free discount is `c = 0`, then the full transfer fee amount is paid (0057-TRAN-016) + - The fee cost is correctly calculated using the network parameter `transfer.fee.factor`. + - If I have enough funds to pay transfer and fees, the transfer happens. + - If I do not have enough funds to pay transfer and fees, the transfer is cancelled. + - The fees are being paid into the infrastructure pool. +- when a party paid taker fee `g` in previous epoch, and `transfer.feeDiscountDecayFraction = 0.9`, then in the next epoch when a party (did not generate any fees) makes a transfer and the theoretical fee the party should pay is `f`, fee-free amount is then `c = 0.9 x g`. If `c > f`, then no transfer fee is paid (0057-TRAN-019) +- when a party made maker fee `g` in previous epoch, and `transfer.feeDiscountDecayFraction = 0.9`, then in the next epoch when a party (did not generate any fees) makes a transfer and the theoretical fee the party should pay is `f`, fee-free amount is then `c = 0.9 x g`. If `c > f`, then no transfer fee is paid (0057-TRAN-020) +- when a party paid taker fee `g` in previous epoch, and `transfer.feeDiscountDecayFraction = 0.9`, then in the next epoch when a party (did not generate any fees) makes a transfer and the theoretical fee the party should pay is `f`, fee-free amount is then `c = 0.9 x g`. If `c > f`, then no transfer fee is paid. And a party makes another transfer, and the theoretical fee the party should pay is `f`, then the party is not getting any fee-free discount(0057-TRAN-021) +- when a party made maker fee `g` in previous epoch, and `transfer.feeDiscountDecayFraction = 0.9`, then in the next epoch when a party (did not generate any fees) makes a transfer and the theoretical fee the party should pay is `f`, fee-free amount is then `c = 0.9 x g`. If `c > f`, then no transfer fee is paid. And a party makes another transfer, and the theoretical fee the party should pay is `f`, then the party is not getting any fee-free discount(0057-TRAN-022) +- when a party paid taker fee `f` in previous epoch, and `transfer.feeDiscountDecayFraction = 0.9`, then in 3 epochs the fee-free discount amount would be `c = 0.9^3 x f`, when a party makes a transfer and the theoretical fee the party should pay is `f1`, and `f1 <= 0.729 x f`, then no amount is paid for transfer (0057-TRAN-023) +- when a party received maker fee `f` in previous epoch, and `transfer.feeDiscountDecayFraction = 0.9`, then in 3 epochs the fee-free discount amount would be `c = 0.9^3 x f`, when a party makes a transfer and the theoretical fee the party should pay is `f1`, and `f1 <= 0.729 x f`, then no amount is paid for transfer (0057-TRAN-024) +- when a party makes a transfer and `f` would be the theoretical fee the party should pay then the fee on the transfer that is actually charged is `-min(f-c,0)`. The system subsequently updates `c <- max(0,c-f)`. At the end of epoch, update `c <- c x D` and `c <- c + all_trading_fees_for_trades_involved_in`, if `c < M x quantum`(M is `transfer.feeDiscountMinimumTrackedAmount`), then set `c <- 0` (0057-TRAN-027) +- As a user I can do a transfer from a vested account to a general account held by the same key without incurring any fees (0057-TRAN-066). +- If a user transfers funds from their vested account to any valid account other than their general account for that asset, they will incur fees. This includes accounts not owned by the user. (0057-TRAN-069). +- As a user, I **can not** transfer a quantum amount less than `transfer.minTransferQuantumAmount` from any of the valid accounts excluding a vested account (0057-TRAN-067). +- As a user, I **can** transfer a quantum amount less than `transfer.minTransferQuantumAmount` from a vested account if and only if I transfer the full balance (0057-TRAN-068). - As a user, when I initiate a delayed transfer, the funds are taken from my account immediately (0057-TRAN-008) - The funds arrive in the target account when the transaction is processed (i.e. with the correct delay), which is not before the timestamp occurs - A delayed transfer that is invalid (to an invalid account type) is rejected when it is received, and the funds are not taken from the origin account. - The spam protection mechanics prevent me to do more than `spam.protection.maxUserTransfersPerEpoch` transfers per epoch. (0057-TRAN-009) - A delayed one-off transfer cannot be cancelled once set-up. (0057-TRAN-010) - A one-off transfer `to` a non-`000000000...0`, and an account type that a party cannot have, must be rejected (0057-TRAN-059) +- As a user, I can accumulate the fees I collect over an epoch. When I initiate a transfer that incurs a transfer fee, I have the ability to view the amount that is exempt from transfer fees through the API. (0057-TRAN-012) ### Recurring transfer tests @@ -214,15 +307,24 @@ As a user I can create a recurring transfer _which expires after a specified epo - The same amount is transferred every epoch. - In the epoch after the `end epoch`, no transfers are executed. -As a user I can create a recurring transfer _that decreases over time_ (0057-TRAN-051) +As a user I can create a recurring transfer _that decreases over time_ (0057-TRAN-051) when `start amount x transfer.fee.factor <= transfer.fee.maxQuantumAmount x quantum` - I specify a start and end epoch, and a factor of `0.7` - Until the start epoch is reached not transfers are executed -- Once I reach the start epoch transfers happen and the first transfer is for the `start amount`. The fee amount taken from the source account is `start amount x transfer.fee.factor` and transferred to the infrastructure fee account for the asset. +- Once I reach the start epoch transfers happen and the first transfer is for the `start amount`. The fee amount taken from the source account is `min(start amount x transfer.fee.factor, transfer.fee.maxQuantumAmount x quantum)` and transferred to the infrastructure fee account for the asset. - The transfer at end of `start epoch + 1` is `0.7 x start amount` and the fee amount is `0.7 x start amount x transfer.fee.factor`. - The amount transferred every epoch decreases. - After I reach the epoch `?`, no transfers are executed anymore +As a user I can create a recurring transfer _that decreases over time_ (0057-TRAN-065) when `start amount x transfer.fee.factor > transfer.fee.maxQuantumAmount x quantum` + +- I specify a start and end epoch, and a factor of `0.7` +- Until the start epoch is reached not transfers are executed +- Once I reach the start epoch transfers happen and the first transfer is for the `start amount`. The fee amount taken from the source account is `min(start amount x transfer.fee.factor, transfer.fee.maxQuantumAmount x quantum)` and transferred to the infrastructure fee account for the asset. +- The transfer at end of `start epoch + 1` is `0.7 x start amount` and the fee amount is `0.7 x transfer.fee.maxQuantumAmount x quantum`. +- The amount transferred every epoch decreases. +- After I reach the epoch `?`, no transfers are executed anymore + As a user I can create a recurring transfer that recurs forever, with the same balance transferred each time (0057-TRAN-052) - I specify a start and no end epoch, and a factor of `1` @@ -270,12 +372,15 @@ A user's recurring transfer to a reward account does not occur if there are no p - The value of `marketCreationQuantumMultiple` is `10^6` and `quantum` for `USDT` is `1`. - I specify a start and no end epoch, and a factor of 1 to a reward account `ETHUSDT | market creation | $VEGA` - In the first epoch no trading occurs and nothing is transferred to the reward account at the end of the epoch -- In the second epoch, 2 * 10^6 trading occurs, and at the end of the epoch the transfer to the reward account occurs +- In the second epoch, 2 x 10^6 trading occurs, and at the end of the epoch the transfer to the reward account occurs - At the end of the third epoch, no transfer occurs If the network parameter `transfer.fee.factor` is modified, this modification is applied immediately, i.e., transfers are accepted/rejected according to the new parameter. This holds for both increase and decrease. (0057-TRAN-062) +If the network parameter `transfer.fee.maxQuantumAmount` is modified, this modification is applied +immediately, i.e., transfers are accepted/rejected according to the new parameter. This holds for both increase and decrease. (0057-TRAN-064) + If the network parameter `spam.protection.maxUserTransfersPerEpoch` is modified, this modification is applied immediately, i.e., transfers are accepted/rejected according to the new parameter. This holds for both increase and decrease. In the case of a decrease, existing recurring transfers are not cancelled. (0057-TRAN-060) If the network parameter `transfer.minTransferQuantumMultiple` is modified, this modification is applied diff --git a/protocol/0059-STKG-simple_staking_and_delegating.md b/protocol/0059-STKG-simple_staking_and_delegating.md index 3cb481662..f99f68a59 100644 --- a/protocol/0059-STKG-simple_staking_and_delegating.md +++ b/protocol/0059-STKG-simple_staking_and_delegating.md @@ -164,7 +164,7 @@ Another edge case is the following: during the epoch the party had x tokens asso Actual validator score calculation is in [simple scheme for Sweetwater](0061-simple-POS-rewards\ -\ SweetWater.md) and it introduces its own network parameters. -See the [network paramters spec](./0054-NETP-network_parameters.md#current-network-parameters) for a full list of parameters. +See the [network parameters spec](./0054-NETP-network_parameters.md#current-network-parameters) for a full list of parameters. ## Acceptance Criteria diff --git a/protocol/0061-REWP-pos_rewards.md b/protocol/0061-REWP-pos_rewards.md index d01dcfe56..afb1ef313 100644 --- a/protocol/0061-REWP-pos_rewards.md +++ b/protocol/0061-REWP-pos_rewards.md @@ -2,7 +2,7 @@ This describes the SweetWater requirements for calculation and distribution of rewards to delegators and validators. For more information on the overall approach, please see the relevant research document. -This applies both the rewards coming from the [on-chain-treasury](./0055-TREA-on_chain_treasury.md) as well rewards resulting from trading via [infrastructure fees](./0029-FEES-fees.md). +This applies to the rewards resulting from trading via [infrastructure fees](./0029-FEES-fees.md). ## Network parameters used for `score_val` calculation @@ -11,7 +11,7 @@ This applies both the rewards coming from the [on-chain-treasury](./0055-TREA-on 1. `totalStake` - the total number of units of the staking and governance asset (VEGA) associated to the Vega chain (but not necessarily delegated to a specific validator). 1. `compLevel` - This is a Network parameter that can be changed through a governance vote. Full name: `reward.staking.delegation.competitionLevel`. Default `1.1`. 1. `reward.staking.delegation.optimalStakeMultiplier` - another network parameter which together with `compLevel` control how much the validators "compete" for delegated stake. -1. `network.ersatzvalidators.reward.factor` - a decimal in `[0,1]` with default of `1`. It controls how much the ersatz validator own + delegated stake counts for reward purposes. +1. `network.ersatzvalidators.reward.factor` - a decimal in `[0,1]` with default of `1`. It controls how much the ersatz validator(standby validator) own + delegated stake counts for reward purposes. ## Other network parameters @@ -22,7 +22,7 @@ This applies both the rewards coming from the [on-chain-treasury](./0055-TREA-on ## Calculation -This applies to the on-chain-treasury for each asset as well as network infrastructure fee pool for each asset. +This applies to the network infrastructure fee pool for each asset. At the end of an [epoch](./0050-EPOC-epochs.md), payments are calculated. As step *zero*: Vega keeps track of validators currently on the Ethereum multisig contract by knowing the initial state and by observing `validator added` and `validator removed` events emitted by the contract, see [multisig ethereum contract](./0033-OCAN-cancel_orders.md). @@ -33,7 +33,7 @@ This is to ensure that validators (all validators) have incentive to pay Ethereu ## Primary (consensus forming) Nodes, Ersatz Nodes, Non-validator nodes -From the point of view of proof of stake rewards three are three types of nodes: +From the point of view of proof of stake rewards there are three types of nodes: 1. Non-validator nodes that process transactions and can run the [data node](./0076-DANO-data-node.md) for client use but they don't determine which transactions go into blocks and they get no proof of stake rewards. Any such validator [can submit a transaction](./0069-VCBS-validators_chosen_by_stake.md) to join the ersatz nodes / validator nodes set. Once they submit such transaction they become [pending nodes](./0064-VALP-validator_performance_based_rewards.md) and their performance is measured to determine their suitability. If they meet staking and performance criteria will get "promoted" to the next level. 1. The [ersatz validators](./0069-VCBS-validators_chosen_by_stake.md) who, from the point of view of consensus protocol are non-validator nodes but they have sufficient stake (own or delegated) and meet performance criteria. Their role is to be readily available if any of the primary (Tendermint) validators was to drop out in which case they become primary validators. They can also become primary validators if the stake composition [changes sufficiently](./0069-VCBS-validators_chosen_by_stake.md). They receive proof of stake rewards. If their performance score or amount of delegated stake drops they can be demoted to a pending non-validator node. @@ -41,15 +41,15 @@ From the point of view of proof of stake rewards three are three types of nodes: ### Proof of stake reward split between primary (tendermint) validators and ersatz validators -The reward pool is split into two parts, proportional to the total own+delegated stake the primary- and ersatz validators have. -Thus, if `s_t = network.ersatzvalidators.reward.factor x s_e + s_p` is the total amount of own+delegated stake to both sets (with ersatz scaling taken into account), `s_p` the total stake delegated to the primary / Tendermint validators and `s_e x network.ersatzvalidators.reward.factor` the total stake delegated to the ersatz validators (scaled appropriately), then the primary / Tendermint pool has a fraction of `s_p/s_t` of the total reward, while the ersatz pool has `network.ersatzvalidators.reward.factor x s_e / s_t` (both rounded down appropriately). +The reward pool is split into two parts, proportional to the total own+delegated stake the primary and ersatz validators have. +Thus, if `s_t = network.ersatzvalidators.reward.factor x s_e + s_p` is the total amount of own+delegated stake to both sets (with ersatz scaling taken into account), `s_p` the total stake delegated to the primary / Tendermint validators and `s_e x network.ersatzvalidators.reward.factor` the total stake delegated to the ersatz validators (scaled appropriately), then the primary / Tendermint pool has a fraction of `s_p / s_t` of the total reward, while the ersatz pool has `network.ersatzvalidators.reward.factor x s_e / s_t` (both rounded down appropriately). The following formulas then apply to both primary and ersatz validators, where 'total available reward' and 'total delegation', total_stake and 'number_of_validators' or `s_total` refer to the corresponding reward pool and the total own+delegated corresponding set of validators (i.e., `s_p` or `s_e`, respectively). ## For each validator we then do 1. First, `validatorScore` is calculated to obtain the relative weight of the validator given `stake_val` is both own and delegated tokens, that is `stake_val = allDelegatedTokens + validatorsOwnTokens`. -Here `allDelegatedTokens` is the count of the tokes delegated to this validator. +Here `allDelegatedTokens` is the count of the tokens delegated to this validator. Note `validatorScore` also depends on the other network parameters, see below where the exact `validatorScore` function is defined. 1. Obtain the performance score as per [validator performance specification](./0064-VALP-validator_performance_based_rewards.md). Update `validatorScore <- validatorScore x performance_score`. 1. The fraction of the total available reward that goes to a node (some of this will be for the validator , some is for their delegators) is then `nodeAmount := stakingRewardAmtForEpoch x validatorScore / sumAllValidatorScores` where `sumAllValidatorScores` is the sum of all scores achieved by the validators. Note that this is subject to `min_own_stake` criteria being met. (see below). @@ -64,12 +64,6 @@ Each delegator should now receive `delegatorTokens / (allDelegatedTokens + valid If the validator (i.e. the associated key) does not have sufficient stake self-delegated (at least the network parameter `min_own_stake`), then the reward for the validator is set to zero. The corresponding amount is kept by the network, not distributed among the other validators. Note this only applies to the part of the reward attributable directly to such a validator, its delegators should still receive their rewards. If a Vega key which defines a validator delegates any amount to a different validator then the reward associated with that delegation will be paid out just like for any other delegator. -### Maximum payout per participant - -If the reward pool in question is the on-chain treasury for the staking and governance asset then payments are subject to `reward.staking.delegation.maxPayoutPerParticipant`. -The maximum per participant is the maximum a single party (public key) on Vega can receive as a staking and delegation reward for one epoch. Each participant receives their due, capped by the max. The unpaid amount remain in the treasury. -Setting this to `0` means no cap. - ## `validatorScore` functions This is defined as follows: @@ -129,4 +123,4 @@ For ersatz validators, the same formula is used. 1. Change of network parameter `reward.staking.delegation.competitionLevel` will change the level of competition of the validators (influences how much stake is be needed for all validators to reach optimal revenue) at the end of the next epoch. Default value 3.1. Minimum value 1 (inclusive). (0061-REWP-008) 1. Change of network parameter `reward.staking.delegation.minimumValidatorStake` will change minimum amount required of own stake a validator has. Minimum stake applies to all validators. it’s referred to as a prerequisite to being considered a validator. Validators not met with the minimum stake will not be all thrown, and in fact unless there’s someone who can replace them no one will be kicked out. If there is an ersatz ready to replace them only one will be replaced every epoch. (0061-REWP-009) 1. Change of the network parameter `reward.staking.delegation.optstakemultiplier` is changed to 0 (the reward curve is flat for a validator that exceeds optimal stake), to 0.5 (the reward curve goes down), and 0.1 (the reward curve goes down slightly).(0061-REWP-010) -1. Change of network parameter `reward.staking.delegation.delegatorShare` to 0 (no reward for delegators, 0.99, and 0.5. The share for delegators at the end of the epochs changes accordingly. (0061-REWP-011) +1. Change of network parameter `reward.staking.delegation.delegatorShare` to 0 (no reward for delegators), 0.99, and 0.5. The share for delegators at the end of the epochs changes accordingly. (0061-REWP-011) diff --git a/protocol/0062-SPAM-spam_protection.md b/protocol/0062-SPAM-spam_protection.md index b6209beaa..fd5b559a6 100644 --- a/protocol/0062-SPAM-spam_protection.md +++ b/protocol/0062-SPAM-spam_protection.md @@ -1,15 +1,5 @@ # Spam protection -At this point, the network cannot reject a transaction based on any data that is not the shared state of the blockchain. This means, it is unavoidable that one spammer can essentially fill a block. - -What the network can do is: - -- remove the offending transactions after the block is scheduled, i.e., not process them -- update the state once a block is finalised and block transactions based on the new state -- delete transactions from every (honest) validator's mempool based on the new state. - -Thus, no matter what the anti-spam policy is, there is a scenario where someone creates a lot of identities and spams one block with each. Therefore, we have to enforce a minimum investment to be allowed to send anything to the Vega network. - ## Governance spam The spam protection enforcement for governance actions require that a public key must have a set minimum amount of tokens to be allowed to issue a proposal or vote on a proposal (`spam.protection.proposal.min.tokens`/`spam.protection.voting.min.tokens`). If the network detects successful spam in spite of this minimum, then the limit can be increased automatically. @@ -20,18 +10,16 @@ The following three policies are also specific to governance actions: - Any governance proposal transaction can be rejected if a party has less than `spam.protection.proposal.min.tokens`. Setting these reasonably high provides some level of protection. - Any qualified voter can vote `spam.protection.max.votes` times per epoch per active proposal (e.g., if it's `3` then one initial vote and 2 follow-on votes to change their mind. -If 3 blocks in a row are filled with spam, i.e., parties send substantially more than 3 votes, let's say 50 votes), then the number of required tokens is doubled, up to a maximum of 1600. - All are network parameters and thus up for discussion/governance vote. A change of parameters takes effect in the epoch following the acceptance of the corresponding proposal. ### Policy Enforcement The policy enforcement mechanism rejects governance messages that do not follow the anti-spam rules. This can happen in two different ways: -- pre-block rejection: A transaction is rejected before it enters the validators' mempool. For Tendermint-internal reasons, this can only happen based on the global state coordinated through the previous block; in particular, it cannot be based on any other transactions received by the validator but not yet put into a block (e.g., only three transactions per party per block). Once a block is scheduled, all validators also test all transactions in their mempool to confirm they are still passing the test, and remove them otherwise. -- post-block-rejection: A transaction has made it into the block, but is rejected before it is passed to the application layer. This mechanism allows for more fine-grained policies than the previous one, but at the price that the offending transaction has already taken up space in the blockchain. +- **pre-block rejection**: A transaction is rejected before it enters the validators' mempool. For Tendermint-internal reasons, this can only happen based on the global state coordinated through the previous block; in particular, it cannot be based on any other transactions received by the validator but not yet put into a block (e.g., only three transactions per party per block). Once a block is scheduled, all validators also test all transactions in their mempool to confirm they are still passing the test, and remove them otherwise. +- **post-block-rejection**: A transaction has made it into the block, but is rejected before it is passed to the application layer. This mechanism allows for more fine-grained policies than the previous one, but at the price that the offending transaction has already taken up space in the blockchain. This is currently not used and only kept in here for reference. -The policies enforced are relatively simple: +The policies enforced are the following thresholds: ```text num_votes = 3 // maximum number of times per epoch a tokenholder van change their vote on an issue @@ -48,11 +36,8 @@ max_batch_size = 15 // maximal number of transactions allowed (for consistency reasons, the prevailing source for all parameter values is the [defaults](https://github.com/vegaprotocol/vega/blob/develop/core/netparams/defaults.go)code file. In case of differences, the information in that file is the valid one). -As (due to Tendermint constraints) it is currently possible to exceed all thresholds within one block, an attacker can always spam one block; to mitigate this, a attacker that does so is temporarily banned. For now, all bans are independent, i.e., a ban due to excessive voting only affects further votes. - -- Any tokenholder with more than `min_voting_tokens` tokens on a public key has `num_votes` voting attempts per epoch and proposal, i.e., they can change their mind `num_votes-1` times in one epoch. This means a transaction is pre-block rejected if there are `num_votes` or more on the same proposal in the blockchain in the same epoch, and post_block rejected if there are `num_votes` or more on the same proposal in the blockchain plus earlier in the current block. -- Any tokenholder that had more than 50% of its governance transactions post-rejected is banned for max (30 seconds, 1/48 of an epoch) or until the next epoch starts, and all of its governance related transactions (but no trading related transactions) are immediately rejected. E.g., if the epoch duration is 1 day, then the ban period is 30 minutes. If however the epoch is 10 seconds, then the ban period is 30 seconds (or until the start of the next epoch). The test for 50% of the governance transactions is repeated once the next governance related transaction is post-rejected, so it is possible for a violating party to get banned quite quickly again; the test is only done in case of a new post-rejection, so the account does not get banned twice just because the 50% quota is still exceeded when the ban ends. The voting counters are unaffected by the ban, so voting again on a proposal that already had the full number of votes in the epoch will lead to a rejection of the new vote; this is now unlikely to trigger a new ban, as this rejection will happen pre-consensus, and thus not affect the 50% rule. -- A proposal can only be issued by a tokenholder with more than `min_proposing_tokens` associated with one public key at the start of the epoch. Also (like above), only `num_proposals` proposals can be made per tokenholder per epoch. For example, every proposal past `num_proposals` in an epoch is rejected by post-block-rejected if the sum of their proposals in past blocks and the ones in the current block exceed `num_proposals`, or pre-block rejected if the sum of proposals already in the blockchain for that epoch equals or exceeds `num_proposals`. This parameter is the same for **all proposals**. There also is a separate parameter to the same end that is enforced in the core. For Sweetwater, both these parameters had the same value, but the spam protection value can be set lower, as the amplification effect of a proposal (i.e., a proposal resulting in a very large number of votes) would also then be covered by the core. +- Any tokenholder with more than `min_voting_tokens` tokens on a public key has `num_votes` voting attempts per epoch and proposal, i.e., they can change their mind `num_votes-1` times in one epoch. This means a transaction is **pre-block rejected** if there are `num_votes` or more on the same proposal in the blockchain in the same epoch. +- A proposal can only be issued by a tokenholder with more than `min_proposing_tokens` associated with one public key at the start of the epoch. Also (like above), only `num_proposals` proposals can be made per tokenholder per epoch. Thus, proposals get **pre-block rejected** if the sum of proposals already in the blockchain for that epoch equals or exceeds `num_proposals`. This parameter is the same for **all proposals**. There also is a separate parameter to the same end that is enforced in the core. For Sweetwater, both these parameters had the same value, but the spam protection value can be set lower, as the amplification effect of a proposal (i.e., a proposal resulting in a very large number of votes) would also then be covered by the core. ### Notes @@ -60,16 +45,6 @@ As (due to Tendermint constraints) it is currently possible to exceed all thresh - Every tokenholder with more than `min_voting_tokens` can spam exactly one block. - There is some likelihood that policies will change. It would thus be good to have a clean separation of policy definition and enforcement, so a change in the policies can be implemented and tested independently of the enforcement code. -### Increasing thresholds - -If on average for the last 10 blocks, more than 30% of all voting and proposal transactions need to be post-rejected, then the network is under spam attack. In this case, the `min_voting_tokens` value is doubled, until it reaches 1600. The threshold is then not increased for another 10 blocks. At the beginning of every epoch, the value of `min_voting_tokens` is reset to its original. - -### Issues - -**It is possible for a tokenholder to deliberately spam the network to block poorer parties from voting.** - -Due to the banning policy this is not doable from one key, but with a sybil attack it can be done. If this ends up being a problem, we can address it by increasing the ban-time. - ## Withdrawal spam As unclaimed withdrawals do not automatically expire, an attacker could generate a large number of messages as well as an ever-growing data structure through [withdrawal requests](0030-ETHM-multisig_control_spec.md). @@ -80,6 +55,35 @@ The minimum allowed withdrawal amount is `spam.protection.minimumWithdrawalQuant Any withdrawal request for a smaller amount is immediately rejected. +### Referral spam + +The [on-chain referral program](./0083-RFPR-on_chain_referral_program.md) adds three transaction types which can be submitted with no cost/risk to the party: + +- `CreateReferralSet` +- `UpdateReferralSet` +- `ApplyReferralCode` + +To avoid spamming of `CreateReferralSet` and `UpdateReferralSet` transactions, a party must meet the staked governance tokens ($VEGA) threshold set by the network parameter `referralProgram.minStakedVegaTokens`. A party also must meet the balance holding threshold set by the network parameter `spam.protection.referralProgram.min.funds`. All assets count towards this threshold and balances should be scaled appropriately by the assets quantum. A party who does not meet this requirements should have any transactions of the aforementioned types pre-block rejected. + +To avoid spamming of `ApplyReferralCode`, a party must meet the deposited funds threshold set by the network parameter `spam.protection.applyReferral.min.funds`. All assets count towards this threshold and balances should be scaled appropriately by the assets quantum. A party who does not meet this requirement should have any transactions of the aforementioned type pre-block rejected. This requirement will be checked against snapshots of account balances taken at a frequency determined by the network parameter `spam.protection.balanceSnapshotFrequency`. This network parameter is a duration (e.g. `5s`, `1m5s`). + +Further, each party is allowed to submit up to `n` transactions per epoch where `n` is controlled by the respective network parameter for that transaction type (`spam.protection.max.CreateReferralSet`, `spam.protection.max.UpdateReferralSet`, `spam.protection.max.ApplyReferralCode`). + +### Party Profile spam + +The [party profile feature](./0088-PPRF-party_profile.md) adds a transaction types which can be submitted with no cost/risk to the party: + +- `UpdatePartyProfile` + +To avoid spamming of `UpdatePartyProfile`, a party must meet the deposited funds threshold set by the network parameter `spam.protection.updatePartyProfile.min.funds`. All assets count towards this threshold and balances should be scaled appropriately by the assets quantum. A party who does not meet this requirement should have any transactions of the aforementioned type pre-block rejected. This requirement will be checked against snapshots of account balances taken at a frequency determined by the network parameter `spam.protection.balanceSnapshotFrequency`. This network parameter is a duration (e.g. `5s`, `1m5s`). + +**Note** `spam.protection.updatePartyProfile.min.funds` must be an integer greater than or equal to `0` (and default to `10`). + +Further, each party is allowed to submit up to `n` transactions per epoch where `n` is controlled by the respective network parameter for the transaction type (`spam.protection.max.updatePartyProfile`). + +**Note** `spam.protection.max.updatePartyProfile` must be an integer greater than or equal to `0` (and default to `5`). + + ### Related topics - [Spam protection: Proof of work](https://github.com/vegaprotocol/specs/blob/master/protocol/0072-SPPW-spam-protection-PoW.md) @@ -100,22 +104,29 @@ More than 360 delegation changes in one epoch (or, respectively, the value of `s - Transactions creating more than `spam.protection.max.proposals` proposals in one epoch are rejected. (0062-SPAM-004) - Transactions submitting votes by parties with less than `spam.protection.voting.min.tokens` of Vega associated are rejected. (0062-SPAM-005) - Transactions submitting a vote more than `spam.protection.max.votes` times on any one proposal are rejected. (0062-SPAM-006) -- Above thresholds are exceeded in one block, leading to a post-block-rejection (0062-SPAM-007) -- If 50% of a parties votes/transactions are post-block-rejected, it is blocked for 4 Epochs and unblocked afterwards again (0062-SPAM-008) -- It is possible for spam transactions to fill a block (0062-SPAM-010) -- Parties that continue spamming are blocked and eventually unblocked again (0062-SPAM-011) - Any rejection due to spam protection is reported to the user upon transaction submission detailing which criteria the key exceeded / not met (0062-SPAM-013) -- If a party is banned for too many voting-rejections, it still can send trading related transactions which are not banned. (0062-SPAM-025) -- If the ban of a party ends because the banning time is up, transactions from that party are no longer rejected (0062-SPAM-015) -- If the ban of a party ends because the epoch ends, transactions from that party are no longer rejected (0062-SPAM-016) -- If a party gets banned, the ban ends due to the epoch ending, and it gets banned again at the beginning of the new epoch, the ban still lasts the entire time (or until the next epoch end), i.e., the ban-expiration timer is reset. (0062-SPAM-017) -- If a party gets banned several times during an epoch, all banns last for the defined time or until the epoch ends (try with at least three banns) (0062-SPAM-018) -- A ban only affects transactions of the type that caused the ban, i.e., a voting ban only affects further votes.(0062-SPAM-019) -- After having been banned for too many votes and unbanned, with the maximum number of votes in that epoch exceeded, any additional votes are rejected without a new ban. (0062-SPAM-020) - Try to create a withdrawal bundle for an amount smaller than defined by `spam.protection.minimumWithdrawalQuantumMultiple x quantum` and verify that it is rejected (0062-SPAM-021) - Try to set `spam.protection.minimumWithdrawalQuantumMultiple` to `0` and verify that the parameter is rejected.(0062-SPAM-022) - Increase `spam.protection.minimumWithdrawalQuantumMultiple` and verify that a withdrawal transaction that would have been valid according to the former parameter value is rejected with the new one. (0062-SPAM-023) - Decrease `spam.protection.minimumWithdrawalQuantumMultiple` and verify that a withdrawal transaction that would have been invalid with the old parameter and is valid with the new value and is accepted.(0062-SPAM-024) -- Issue a valid withdrawal bundle. Increase `spam.protection.minimumWithdrawalQuantumMultiple` to a value that would no longer allow the creation of the bundle. Ask for the bundle to be re-issued and verify that it's not rejected. (0062-COSMICELEVATOR-001) +- Issue a valid withdrawal bundle. Increase `spam.protection.minimumWithdrawalQuantumMultiple` to a value that would no longer allow the creation of the bundle. Ask for the bundle to be re-issued and verify that it's not rejected. (0062-PALAZZO-001) +- A party staking less than `referralProgram.minStakedVegaTokens` should have any `CreateReferralSet` transactions **pre-block** rejected (0062-SPAM-026). +- A party staking less than `referralProgram.minStakedVegaTokens` should have any `UpdateReferral` transactions **pre-block** rejected (0062-SPAM-027). +- A party holding less than `spam.protection.referralSet.min.funds x quantum` should have any `CreateReferralSet` transactions **pre-block** rejected (0062-SPAM-033). +- A party holding no less than `spam.protection.referralSet.min.funds x quantum` should not have any `CreateReferralSet` transactions **pre-block** rejected (0062-SPAM-035). +- Given longer than `spam.protection.balanceSnapshotFrequency` has elapsed since a party deposited or transferred funds, a party who has less then `spam.protection.applyReferral.min.funds` in their accounts should have any `ApplyReferralCode` transactions **pre-block** rejected. All assets count towards this threshold and balances should be scaled appropriately by the assets quantum. (0062-SPAM-028). +- A party who has submitted strictly more than `spam.protection.max.CreateReferralSet` `CreateReferralSet` transactions in an epoch should have any future `CreateReferralSet` transactions in that epoch **pre-block** rejected (0062-SPAM-029). +- A party who has submitted more than `spam.protection.max.CreateReferralSet` transactions in the current epoch plus in the current block, should have their transactions submitted in the current block **pre-block** rejected (0062-SPAM-032). +- A party who has submitted strictly more than `spam.protection.max.updateReferralSet` `UpdateReferralSet` transactions in an epoch should have any future `UpdateReferralSet` transactions in that epoch **pre-block** rejected (0062-SPAM-030). +- A party who has submitted more than `spam.protection.max.updateReferralSet` transactions in the current epoch plus in the current block, should have their transactions submitted in the current block **pre-block** rejected (0062-SPAM-034). +- A party who has submitted strictly more than `spam.protection.max.applyReferralCode` `ApplyReferralCode` transactions in an epoch should have any future `ApplyReferralCode` transactions in that epoch **pre-block** rejected (0062-SPAM-031). +- A party who has submitted more than `spam.protection.max.applyReferralCode` transactions in the current epoch plus in the current block, should have their transactions submitted in the current block **pre-block** rejected (0062-SPAM-036). +- Given longer than `spam.protection.balanceSnapshotFrequency` has elapsed since a party deposited or transferred funds, a party who has less then `spam.protection.updatePartyProfile.min.funds` in their accounts should have any `UpdatePartyProfile` transactions **pre-block** rejected. All assets count towards this threshold and balances should be scaled appropriately by the assets quantum. (0062-SPAM-037). +- A party holding 2 assets, and each of the assets are less than `spam.protection.referralSet.min.funds` x quantum, but the sum of the 2 assets are greater than `spam.protection.referralSet.min.funds` x quantum, then the party should not have any `UpdateReferralSettransactions` pre-block rejected (0062-SPAM-042). +- A party holding less than `spam.protection.referralProgram.min.funds` x quantum should have any `UpdateReferralSettransactions` pre-block rejected (0062-SPAM-040). +- A party holding no less than `spam.protection.referralSet.min.funds` x quantum and staking no less than `referralProgram.minStakedVegaTokens` should not have any `UpdateReferralSettransactions` pre-block rejected (0062-SPAM-041). +- A party who has submitted strictly more than `spam.protection.max.updatePartyProfile` `UpdatePartyProfile` transactions in an epoch should have any future `UpdatePartyProfile` transactions in that epoch **pre-block** rejected (0062-SPAM-038). +- A party who has submitted more than `spam.protection.max.updatePartyProfile` `UpdatePartyProfile` transactions in the current epoch plus in the current block, should have their `UpdatePartyProfile` transactions submitted in the current block **pre-block** rejected (0062-SPAM-039). + > **Note**: If other governance functionality (beyond delegation-changes, votes, and proposals) are added, the spec and its acceptance criteria need to be augmented accordingly. This issue will be fixed in a follow up version. diff --git a/protocol/0063-VALK-validator_vega_master_keys.md b/protocol/0063-VALK-validator_vega_master_keys.md index 2f753a209..2082626d7 100644 --- a/protocol/0063-VALK-validator_vega_master_keys.md +++ b/protocol/0063-VALK-validator_vega_master_keys.md @@ -43,7 +43,7 @@ The public master key has to be added to validators' identities in the genesis c - Once a validator hot key has been rotated all applicable rewards are correctly received. (0063-VALK-007) - Once a validator hot key has been rotated staking and delegation to the validator works as before. (0063-VALK-008) - Once a validator hot key has been rotated self-staking and self-delegation for the validator works as before. (0063-VALK-009) -- If several key change transactions are submitted simultaneously (i.e., a new key change is submitted before the target block of a previous one is reached, if key change message 1 has a lower sequence number and a higher/the same target block as change message 2, then the key established by message 2 is not replaced by the key from message 1. This can be done either by managing the parallel executions appropriately or by rejecting additional submissions(0063-VALK-010) +- If several key change transactions are submitted simultaneously (i.e., a new key change is submitted before the target block of a previous one is reached), if key change message 1 has a lower sequence number and a higher/the same target block as change message 2, then the key established by message 2 is not replaced by the key from message 1. This can be done either by managing the parallel executions appropriately or by rejecting additional submissions(0063-VALK-010) - Once the target block of a key change action us reached, all preceding keys (the original key as well as all hot keys rotated in through a key-change ,messager with a lower sequence number) are invalid. (0063-VALK-011) - Key change message with a lower sequence number than the last one executed are rejected. (0063-VALK-012) - Once a validator hot key has been rotated, it is possible to withdraw all rewards in the original keys account or submit a transfer to move them to the new key's account i.e the old key is still functional as a non-validator vega key. (0063-VALK-013) diff --git a/protocol/0064-VALP-validator_performance_based_rewards.md b/protocol/0064-VALP-validator_performance_based_rewards.md index ff169cdf3..e3fc1bd14 100644 --- a/protocol/0064-VALP-validator_performance_based_rewards.md +++ b/protocol/0064-VALP-validator_performance_based_rewards.md @@ -121,7 +121,7 @@ If we had another reward pool we could share it according to `f/n`. ## `PM5`: Validator only acts as a Tendermint leader -Finding this requires a statistic we don't have at this point (as we would like to also include messages that were sent but don't contribute to consensus anymore to avoid discriminating against geographically far away servers. Once we figured out how to do this, we can build a formula for the reward. +Finding this requires a statistic we don't have at this point (as we would like to also include messages that were sent but don't contribute to consensus anymore to avoid discriminating against geographically far away servers). Once we figured out how to do this, we can build a formula for the reward. ## `PM6`: Validator doesn't run the Vega app, just signs everything the others do diff --git a/protocol/0065-FTCO-floating_point_consensus.md b/protocol/0065-FTCO-floating_point_consensus.md index f068ef9a2..b21e061fd 100644 --- a/protocol/0065-FTCO-floating_point_consensus.md +++ b/protocol/0065-FTCO-floating_point_consensus.md @@ -84,7 +84,7 @@ This section outlines floating-point quantities `vega` currently relies on: ## Acceptance criteria -1. Floating-point values get initialised and updated correctly (0065-FTCO-001) +1. Floating-point values get initialised and updated correctly (0065-FTCO-001). - A market is proposed and initially it has the following default values: - Risk factors: - Short: 1.0 @@ -98,14 +98,13 @@ This section outlines floating-point quantities `vega` currently relies on: - When the opening auction ends (choose uncrossing price that's different from first indicative uncrossing price) price monitoring bounds and probability of trading get recalculated. - When the market goes into price monitoring auction the state variables stay the same as prior to its' start, when that auction concludes (choose a price that's not been traded at before) price monitoring bounds and probability of trading get recalculated again and the time-based trigger countdown gets reset. - When the time-based trigger elapses price monitoring bounds and probability of trading get recalculated. - - When the market goes into liquidity monitoring auction the state variables stay the same as prior to its' start, when that auction concludes (choose a price that's not been traded at before) price monitoring bounds and probability of trading get recalculated again and the time-based trigger countdown gets reset. -1. Event announcing diverging values gets emitted (0065-FTCO-004) +1. Event announcing diverging values gets emitted (0065-FTCO-004). - For all the state variables nodes submit candidate values that differ by up to half the tolerance. - The event announcing the fact that at least one of the values differed gets emitted. - Since differences are within tolerance the consensus successfully chooses a consensus value and systems keeps running as expected (market goes into continuous trading mode accepts orders and generates trades). -1. Consensus failure event gets emitted (0065-FTCO-002) +1. Consensus failure event gets emitted (0065-FTCO-002). - A market is proposed and initially has default values specified in the scenario above. - Upon market enactment risk factors get submitted by nodes, one of the nodes submits a value that is higher than tolerance. - An appropriate event is sent to signal that at least one of the values differed. @@ -115,7 +114,7 @@ This section outlines floating-point quantities `vega` currently relies on: - Situation continues for 2 more risk factor update attempts (can be time-based or auction). - Market still runs with previously calculated risk factors, but an event informing that the market is using stale values gets emitted. -1. Market cannot leave opening auction with default values (0065-FTCO-003) +1. Market cannot leave opening auction with default values (0065-FTCO-003). - A market is proposed and initially has default values specified in the scenario above. - Upon market enactment risk factors get calculated (their values change from defaults). - When the opening auction sees uncrossing price for the first time (there are two overlapping orders from buy and sell side on the order book) price monitoring bounds get calculated, but probability of trading get doesn't pass consensus (all nodes submit conflicting values) diff --git a/protocol/0068-MATC-matching_engine.md b/protocol/0068-MATC-matching_engine.md index 12e175fcd..9173435cc 100644 --- a/protocol/0068-MATC-matching_engine.md +++ b/protocol/0068-MATC-matching_engine.md @@ -8,13 +8,14 @@ The matching engine co-ordinates the trading of incoming orders with existing or An [Immediate or Cancel (IOC)](./0014-ORDT-order_types.md#time-in-force--validity) order: -- Incoming [MARKET](./0014-ORDT-order_types.md#order-pricing-methods) orders will be matched against the opposite side of the book (0068-MATC-001) - - If not enough volume is available to **fully** fill the order, the remaining will be cancelled (0068-MATC-002) -- Incoming [LIMIT](./0014-ORDT-order_types.md#order-pricing-methods) orders will be matched against the opposite side of the book, (0068-MATC-003) - - If there is no match the order will be cancelled. (0068-MATC-004) - - If there is a partial match then the remaining will be cancelled. (0068-MATC-005) +- Incoming [MARKET](./0014-ORDT-order_types.md#order-pricing-methods) orders will be matched against the opposite side of the book (0068-MATC-001). + - If not enough volume is available to **fully** fill the order, the remaining will be cancelled (0068-MATC-002). +- Incoming [LIMIT](./0014-ORDT-order_types.md#order-pricing-methods) orders will be matched against the opposite side of the book, (0068-MATC-003). + - If there is no match the order will be cancelled. (0068-MATC-004). + - If there is a partial match then the remaining will be cancelled. (0068-MATC-005). - Incoming [PEGGED](./0014-ORDT-order_types.md#order-pricing-methods) orders will be rejected by the wallet as they are not valid. (0068-MATC-006) -- Incoming [LIMIT: POST-ONLY TRUE](./0014-ORDT-order_types.md#order-pricing-methods) orders will be rejected by the wallet as they are not valid. (0068-MATC-057) +- Incoming [LIMIT: POST-ONLY TRUE](./0014-ORDT-order_types.md#order-pricing-methods) orders will be rejected by the wallet as they are not valid. (0068-MATC-057). + - For Reduce-Only = True orders: - Incoming [MARKET](./0014-ORDT-order_types.md#order-pricing-methods) orders which reduce the trader's absolute position will be matched against the opposite side of the book (0068-MATC-056) - If not enough volume is available to **fully** fill the order, the remaining will be cancelled (0068-MATC-043) @@ -26,15 +27,16 @@ An [Immediate or Cancel (IOC)](./0014-ORDT-order_types.md#time-in-force--validit A [Fill or KILL (FOK)](./0014-ORDT-order_types.md#time-in-force--validity) order: -- Incoming [MARKET](./0014-ORDT-order_types.md#order-pricing-methods) MARKET orders will be matched fully if the volume is available, otherwise the order is cancelled. (0068-MATC-008) +- Incoming [MARKET](./0014-ORDT-order_types.md#order-pricing-methods) MARKET orders will be matched fully if the volume is available, otherwise the order is cancelled. (0068-MATC-008). - Incoming [LIMIT](./0014-ORDT-order_types.md#order-pricing-methods) orders will either be: - - Fully matched if possible to the other side of the book (0068-MATC-009) - - if a complete fill is not possible the order is stopped without trading at all. (0068-MATC-010) + - Fully matched if possible to the other side of the book (0068-MATC-009). + - if a complete fill is not possible the order is stopped without trading at all. (0068-MATC-010). - Incoming [PEGGED](./0014-ORDT-order_types.md#order-pricing-methods) orders will be rejected by the wallet as they are not valid. (0068-MATC-011) -- Incoming [LIMIT: POST-ONLY TRUE](./0014-ORDT-order_types.md#order-pricing-methods) orders will be rejected by the wallet as they are not valid. (0068-MATC-039) +- Incoming [LIMIT: POST-ONLY TRUE](./0014-ORDT-order_types.md#order-pricing-methods) orders will be rejected by the wallet as they are not valid. (0068-MATC-039). + - For Reduce-Only = TRUE orders: - Incoming [MARKET](./0014-ORDT-order_types.md#order-pricing-methods) orders which reduce the trader's absolute position will be matched against the opposite side of the book (0068-MATC-047) - - If not enough volume is available to **fully** fill the order, the remaining will be cancelled (0068-MATC-048) + - If not enough volume is available to **fully** fill the order, the order will be cancelled(0068-MATC-048) - Incoming [MARKET](./0014-ORDT-order_types.md#order-pricing-methods) orders which increase the trader's absolute position will be stopped (0068-MATC-049) - Incoming [LIMIT](./0014-ORDT-order_types.md#order-pricing-methods) orders which reduce the trader's absolute position will be matched against the opposite side of the book (0068-MATC-050) - Incoming [LIMIT](./0014-ORDT-order_types.md#order-pricing-methods) orders which increase the trader's absolute position will be stopped (0068-MATC-051) @@ -43,48 +45,48 @@ A [Fill or KILL (FOK)](./0014-ORDT-order_types.md#time-in-force--validity) order For [Good 'Til Time (GTT) / Good 'Till Cancelled (GTC) / Good For Normal (GFN)](./0014-ORDT-order_types.md#time-in-force--validity) orders: -- Incoming [MARKET](./0014-ORDT-order_types.md#order-pricing-methods) orders are rejected by the wallet validation layer. (0068-MATC-013) -- Incoming [LIMIT](./0014-ORDT-order_types.md#order-pricing-methods) orders match if possible, any remaining is placed on the book. (0068-MATC-014) +- Incoming [MARKET](./0014-ORDT-order_types.md#order-pricing-methods) orders are rejected by the wallet validation layer. (0068-MATC-013). +- Incoming [LIMIT](./0014-ORDT-order_types.md#order-pricing-methods) orders match if possible, any remaining is placed on the book. (0068-MATC-014). - Incoming [PEGGED](./0014-ORDT-order_types.md#order-pricing-methods) orders are repriced and placed on the book if the price is valid, except GFN which are rejected by the wallet validation layer. (0068-MATC-015) - otherwise they are parked. (0068-MATC-016) -- Incoming [LIMIT: POST-ONLY TRUE](./0014-ORDT-order_types.md#order-pricing-methods) orders will be placed fully on the book if no orders currently cross. (0068-MATC-040) - - An order which totally crosses with an existing order on the book will be STOPPED in full with none executed. (0068-MATC-041) - - An order partially crossing with an existing order on the book will be STOPPED in full with none executed. (0068-MATC-042) +- Incoming [LIMIT: POST-ONLY TRUE](./0014-ORDT-order_types.md#order-pricing-methods) orders will be placed fully on the book if no orders currently cross. (0068-MATC-040). + - An order which totally crosses with an existing order on the book will be STOPPED in full with none executed. (0068-MATC-041). + - An order partially crossing with an existing order on the book will be STOPPED in full with none executed. (0068-MATC-042). - A market will enter auction if the volume on either side of the book is empty. (0068-MATC-017) -- A market will enter auction if the mark price moves by a larger amount than the price monitoring settings allow. (0068-MATC-018) -- All attempts to [self trade](./0024-OSTA-order_status.md#wash-trading) are prevented and the aggressive side is STOPPED even if partially filled. The passive side is left untouched. (0068-MATC-019) +- A market will enter auction if the mark price moves by a larger amount than the price monitoring settings allow. (0068-MATC-018). +- All attempts to [self trade](./0024-OSTA-order_status.md#wash-trading) are prevented and the aggressive side is STOPPED even if partially filled. The passive side is left untouched. (0068-MATC-019). - All orders with Reduce-Only set to TRUE are rejected as invalid. (0068-MATC-054) In a market that is currently in [Auction Trading](./0026-AUCT-auctions.md): - [IOC/FOK/GFN](./0014-ORDT-order_types.md#time-in-force--validity) - - Incoming orders have their status set to REJECTED and are not processed further. (0068-MATC-021) + - Incoming orders have their status set to REJECTED and are not processed further. (0068-MATC-021). - [GTC/GTT/GFA](./0014-ORDT-order_types.md#time-in-force--validity) - - All [MARKET](./0014-ORDT-order_types.md#order-pricing-methods) orders are rejected. (0068-MATC-022) - - [LIMIT](./0014-ORDT-order_types.md#order-pricing-methods) orders are placed into the book and no matching takes place. (0068-MATC-023) - - [LIMIT: POST-ONLY TRUE](./0014-ORDT-order_types.md#order-pricing-methods) orders are placed into the book and no matching takes place. (0068-MATC-055) - - The indicative price and volume values are updated after every change to the order book. (0068-MATC-024) + - All [MARKET](./0014-ORDT-order_types.md#order-pricing-methods) orders are rejected. (0068-MATC-022). + - [LIMIT](./0014-ORDT-order_types.md#order-pricing-methods) orders are placed into the book and no matching takes place. (0068-MATC-023). + - [LIMIT: POST-ONLY TRUE](./0014-ORDT-order_types.md#order-pricing-methods) orders are placed into the book and no matching takes place. (0068-MATC-055). + - The indicative price and volume values are updated after every change to the order book. (0068-MATC-024). - [PEGGED](./0014-ORDT-order_types.md#order-pricing-methods) orders are parked (and have their status set to PARKED). (0068-MATC-025) - - It is possible to [self trade](./0024-OSTA-order_status.md#wash-trading) to uncross an auction. (0068-MATC-038) + - It is possible to [self trade](./0024-OSTA-order_status.md#wash-trading) to uncross an auction. (0068-MATC-038). When a [market moves into an auction](./0026-AUCT-auctions.md#upon-entering-auction-mode): - All [PEGGED](./0014-ORDT-order_types.md#auction) orders are parked (and have their status set to PARKED). (0068-MATC-026) -- All [GFN](./0014-ORDT-order_types.md#time-in-force---validity) orders are cancelled. (0068-MATC-027) +- All [GFN](./0014-ORDT-order_types.md#time-in-force---validity) orders are cancelled. (0068-MATC-027). - All [GTC/GTT](./0014-ORDT-order_types.md#time-in-force---validity) orders remain on the book untouched. (0068-MATC-028) When a market [market exits an auction](./0026-AUCT-auctions.md#upon-exiting-auction-mode): -- The book is uncrossed. (0068-MATC-029) - - Self trading is allowed during uncrossing. (0068-MATC-030) -- All [GFA](./0014-ORDT-order_types.md#time-in-force---validity) orders are cancelled. (0068-MATC-031) +- The book is uncrossed. (0068-MATC-029). + - Self trading is allowed during uncrossing. (0068-MATC-030). +- All [GFA](./0014-ORDT-order_types.md#time-in-force---validity) orders are cancelled. (0068-MATC-031). - [PEGGED](./0014-ORDT-order_types.md#order-pricing-methods) orders are repriced where possible. (0068-MATC-032) -- Any persistent order that is currently [ACTIVE or PARKED](./0024-OSTA-order_status.md) can be [cancelled](./0033-OCAN-cancel_orders.md). (0068-MATC-033) -- The price of any persistent order can be updated (0068-MATC-034) -- The size of any persistent order can be updated (0068-MATC-035) -- The TIF of any persistent order can be updated to and from GTC and GTT only. Expiry time is required if amending to GTT and must not be given if amending to GTC. (0068-MATC-036) -- An update to an order that is not [ACTIVE or PARKED](./0024-OSTA-order_status.md) (Stopped, Cancelled, Expired, Filled) will be rejected (0068-MATC-037) +- Any persistent order that is currently [ACTIVE or PARKED](./0024-OSTA-order_status.md) can be [cancelled](./0033-OCAN-cancel_orders.md). (0068-MATC-033). +- The price of any persistent order can be updated (0068-MATC-034). +- The size of any persistent order can be updated (0068-MATC-035). +- The TIF of any persistent order can be updated to and from GTC and GTT only. Expiry time is required if amending to GTT and must not be given if amending to GTC. (0068-MATC-036). +- An update to an order that is not [ACTIVE or PARKED](./0024-OSTA-order_status.md) (Stopped, Cancelled, Expired, Filled) will be rejected (0068-MATC-037). ## Summary diff --git a/protocol/0069-VCBS-validators_chosen_by_stake.md b/protocol/0069-VCBS-validators_chosen_by_stake.md index 34c448bbe..09891ea03 100644 --- a/protocol/0069-VCBS-validators_chosen_by_stake.md +++ b/protocol/0069-VCBS-validators_chosen_by_stake.md @@ -21,7 +21,7 @@ Note that this number combines own + delegated stake together with `performance_ Vega will sort all current consensus forming (also called Tendermint) validators as `[v_1, ..., v_n]` with `v_1` with the highest and `v_n` with the lowest score. If for any `l,m=1,...,n` we have `v_l == v_m` then we place higher the one who's been validator for longer (so this is a mechanism for resolving ties). Vega will sort all those who submitted a transaction wishing to be validators using `validator_score` as `[w_1, ..., w_k]`. -These may be ersatz validators (ie getting rewards) or others who just submitted the transaction to join. +These may be ersatz validators (i.e. standby validators who are getting rewards) or others who just submitted the transaction to join. If `empty_slots := network.validators.tendermint.number - n > 0` (we have empty consensus (Tendermint) validator slots) then the top `empty_slots` from `[w_1, ..., w_k]` are promoted to consensus (Tendermint) validators. If `w_1>v_n` (i.e. the highest scored potential validator has more than the lowest score incumbent validator) then in the new epoch `w_1` becomes a consensus forming (Tendermint) validator, and the lowest scoring incumbent becomes an ersatz validator. @@ -104,6 +104,7 @@ Thus a validator who is not there but should be has incentive to pay gas to upda As a consequence, if a potential validator joined the Vega chain validators but has *not* updated the Multisig members (and/or weights) then at the end of the epoch their score will be `0`. They will not get any rewards. In the case where a node is removed due reduced delegation, or due to not meeting self-delegation criteria, or due to lack of performance, or due to a reduction in the value of `network.validators.tendermint.number`, the onus is on all of the remaining validators to remove the demoted member from the Multisig contract. They are incentivised to do so by all receiving a `validator_score` of `0` *in the reward calculation* until the excess member is removed. +Bear in mind that currently in this situation the unpaid rewards stay in the reward pool and eventually everything gets distributed at the end of any epoch where the multisig is updated. Note that this could become obsolete if a future version of the protocol implements threshold signatures or another method that allows all validators to approve Ethereum actions. @@ -111,7 +112,7 @@ Note that this could become obsolete if a future version of the protocol impleme In addition to the normal validators, there is an additional set of Ersatz validators (also referred to as Standby Validators) as defined by the corresponding network parameter. These are validators that do not contribute to the chain, but are on standby to jump in if a normal validator drops off. The network will have -`n' := ceil(network.validators.multipleOfTendermintValidators x network.validators.tendermint.number)` +`n' := floor(network.validators.multipleOfTendermintValidators x network.validators.tendermint.number)` ersatz validators. The value range for the decimal `network.validators.multipleOfTendermintValidators` is `0.0` to `infinity`. @@ -391,7 +392,7 @@ See [limited network life spec](./0073-LIMN-limited_network_life.md). - In the middle of the epoch, shut node 1 down. - Verify that at the beginning of the next epoch the announced node replaced node 1 as a Tendermint validator. - Restart node 1 again from a snapshot - - Verify that node 1 is in a pending state and it’s ranking score is ~ 0.006666666667. + - Verify that node 1 is in a pending state. 1. 2 empty spots, only one available to replace (0069-VCBS-043): - Setup a network with 5 slots for Tendermint validators and 3 actual Tendermint validators. - Self-delegate to all of them. @@ -552,12 +553,17 @@ Setup a network with 6 nodes (3 validators, 2 ersatz validators, 1 pending valid - Restart the network - Verify that v2-v6 are tendermint validators, v7 is ersatz and v1 is pending. - Verify that all stakes and delegations are correct for each node. +1. Validator, ersatz and pending node scores for current epoch are persisted in checkpoints (0069-VCBS-088): + - Setup a network with 5 validators with non-equal delegation (v1-v5), 1 ersatz validator (v6) and 1 pending validator (v7). + - Take a checkpoint. + - Wait until the current epoch will have expired. + - Restart the network. (This will result in a 1-block epoch) + - Verify that v1-v5 are tendermint validators, v6 is ersatz and v7 is pending. + - Verify that all tendermint validators have non-zero performance scores, which will reflect the data that was collected pre-checkpoint to calculate scores at the end of the last epoch. + - Verify that ersatz and pending validators have non-zero performance scores. ### Multisig update -1. Vega network receives the ethereum events updating the weights and stores them (`key`,`value`). (0069-COSMICELEVATOR-002) -1. For validators up to `network.validators.multisig.numberOfSigners` the `validator_score` is capped by the value on `Ethereum`, if available and it's `0` for those who should have value on Ethereum but don't (they are one of the top `network.validators.multisig.numberOfSigners` by `validator_score` on VEGA). (0069-COSMICELEVATOR-003) -1. It is possible to submit a transaction to update the weights. (0069-COSMICELEVATOR-004) 1. Can update multisig for new validator, and expect rewards (0069-VCBS-066) - Arrange a network with N validators and 1 ersatz validator. - Set `network.validators.multisig.numberOfSigners` = N. diff --git a/protocol/0070-MKTD-market-decimal-places.md b/protocol/0070-MKTD-market-decimal-places.md index ce3083e23..b072fc15b 100644 --- a/protocol/0070-MKTD-market-decimal-places.md +++ b/protocol/0070-MKTD-market-decimal-places.md @@ -70,11 +70,16 @@ Trades of course result in transfers. The amounts transferred (for the trade as ## Acceptance criteria - As a user, I can propose a market with a different precision than its settlement asset - - This proposal is valid if the precision is NOT greater than the settlement asset (0070-MKTD-001) - - This proposal is NOT valid if the precision is greater than the settlement asset (0070-MKTD-002) -- As a user all orders placed (either directly or through LP) are shown in events with prices in market precision (0070-MKTD-003) -- As a user all transfers (margin top-up, release, MTM settlement) are calculated and communicated (via events) in asset precision (0070-MKTD-004) -- As a user I should see the market data prices using market precision. (0070-MKTD-005) -- Price bounds are calculated in asset precision, but enforced rounded to the closest value in market precision in range (0070-MKTD-006) -- As a user, offsets specified in pegged orders and LP shapes represent the smallest value according to the market precision (0070-MKTD-007) -- Trades prices, like orders, are shown in market precision. The transfers and margin requirements are in asset precision. ( 0070-MKTD-008) + - This proposal is valid if the precision is NOT greater than the settlement asset (0070-MKTD-001). + - This proposal is NOT valid if the precision is greater than the settlement asset (0070-MKTD-002). +- Assert that the settlement calculation can be correctly calculated when: + - settlement data precision is > than the settlement asset precision (i.e. settlement data has more precision (decimal places) than the settlement asset and precision will be lost) (0070-MKTD-018) + - settlement data precision is < than the settlement asset precision (i.e. settlement data has less precision (decimal places) than the settlement asset and no precision will be lost) (0070-MKTD-019) + - settlement data precision is equal to the settlement asset precision (i.e. settlement data has less precision (decimal places) than the settlement asset and no precision will be lost) (0070-MKTD-020) +- As a user all orders placed (either directly or through LP) are shown in events with prices in market precision (0070-MKTD-003). +- As a user all transfers (margin top-up, release, MTM settlement) are calculated and communicated (via events) in asset precision (0070-MKTD-004). +- As a user I should see the market data prices using market precision. (0070-MKTD-005). +- Price bounds are calculated in asset precision, but enforced rounded to the closest value in market precision in range (0070-MKTD-006). +- As a user, offsets specified in pegged orders represent the smallest incremental value to tick away from the pegged price of a pegged order according to the market precision (0070-MKTD-007). +- Trades prices, like orders, are shown in market precision. The transfers and margin requirements are in asset precision. ( 0070-MKTD-008). +- Settlement data received during trading on a perpetuals market is correctly handled according to the specified precision (0070-MKTD-017) diff --git a/protocol/0072-SPPW-spam-protection-PoW.md b/protocol/0072-SPPW-spam-protection-PoW.md index 5dce3a40b..c251bfac5 100644 --- a/protocol/0072-SPPW-spam-protection-PoW.md +++ b/protocol/0072-SPPW-spam-protection-PoW.md @@ -47,26 +47,13 @@ Furthermore, the validators check that: - The same identifier has not been used for another transaction from a previously committed block. If the same identifier is used for several transactions in the same block, these transactions need to be removed during post-processing, and the initiator blocked as a spammer. - The same block has not been used for more than `spam.pow.numberOfTxPerBlock` transactions by the same party per spam difficulty level (i.e., if `spam.pow.increaseDifficulty` is `= 1`, the same block can be used for more transactions if the PoW accordingly increases in difficulty). -Violations of the latter rules cannot lead to a transaction being removed, as different validators have a different view on this; however, they can be verified post-agreement, and the offending vega-key can be banished for the duration of 1/48 of an Epoch with a minimum duration of 30 seconds. E.g. if the epoch duration is 1 day, then the ban period is 30 minutes. If however the epoch is 10 seconds, then the ban period is 30 seconds; this is measured starting at the blocktime in which the violation occurs, and transactions are allowed again in the first block after. Validators should return a meaningful error message to the wallet to let it know that/why a transaction got rejected. -Linking a transaction to a too old block will not lead to a banishment, but only to a rejection of the offending transaction. - Notes: - We do not require feeding the hash of the actual transaction into the hash function; this allows users to pre-compute the PoW and thus allows them to perform low latency transactions. - As for replay protection, there is a danger that a trader communicates with a slow validator, and thus gets a wrong block number. The safest is to check validators worth > 1/3 of the weight and take the highest block hash. -- Due to Tendermint constraints, a decision if a transaction is to be rejected or not can only be done based on information that is either synchronised through the chain or contained in the transaction itself, but not based on any other transactions in the mempool. Thus, if a client ties too many transactions to the same block or does not execute the increased difficulty properly, we can not stop this pre-agreement, only detect it post-agreement. This is the reason why some violations are punished with banishment rather than prevented. -- In the [0062 spam protection spec](./0062-SPAM-spam_protection.md), we want to do anti-spam before verifying signatures; this order, however, cannot be done if the consequence of spam is banishment. - -Thus, here the order is: -1. check if the account is banished and (if so) ignore the transaction -2. check if the basic PoW with lowest difficulty is done properly -3. verify the signatures -4. put the transaction on the blockchain -5. if the signed transactions violate the conditions, issue the banishment -6. if the signed transactions in a block violate the conditions, remove the offending ones from the block before calling vega [May need discussion] Depending on how things pan out, we may have an issue with the timing; to make sure traders have sufficient time to get the block height needs us to have a large parameter of `spam.pow.numberOfPastBlocks`, which may allow too many transactions. There are ways to fix this (e.g., the block height needs to end with the same bit as the validator ID), but for now we assume this doesn't cause an issue. @@ -76,15 +63,23 @@ Depending on how things pan out, we may have an issue with the timing; to make s The initial hash-function used is SHA3. To allow for a more fine-grained control over the difficulty of the PoW (the number of zeros only allows halving/doubling), the parameter `spam.pow.hashFunction` allows increasing the number of rounds of the hash function (currently 24), e.g., `spam.pow.hashFunction` = `sha3_36_rounds`. The parameter can in the future also be used to replace the SHA-3 through a governance vote (assuming other functions have been made available by then) should this prove necessary. +## Mempool pruning + +Vega nodes will periodically inspect the mempool. Any transaction sitting in the mempool with PoW tied to a historical block with number `N_old` which satisfies that `N_old + spam.pow.numberOfPastBlocks < N_current` will be removed from the mempool. Here `N_current` is the current block the vega node is processing (has just processed). + +Clients can use this simulate "time-to-live" for transactions. If the PoW is tied to a very recent block then the transaction will remain valid for (almost) the full `spam.pow.numberOfPastBlocks`. If, on the other hand, the PoW is tied to very old block then the transaction will remain valid only for a few blocks; it will be included in a block soon or not at all. + +All Vega clients that submitted transactions can verify that their transaction has succeeded by waiting that it's been included in a block; if they submitted a transaction with PoW tied to `N_old` and `N_old + spam.pow.numberOfPastBlocks < N_current` then they know the transaction was pruned and will never be included on chain. + + ## Acceptance Criteria - A message with a missing/wrong PoW is rejected (0072-SPPW-001) - Reusing the same PoW for several messages is detected and the messages are rejected (0072-SPPW-002) -- Linking too many transactions to the same block is detected and leads to a blocking of that account (if the increasing difficulty is turned off) (0072-SPPW-003) -- Linking too many transactions with a low difficulty level to a block is detected and leads to blocking of the account (if increasing difficulty is turned on) (0072-SPPW-004) +- Linking too many transactions to the same block is detected and leads to rejecting the transactions that are too many (if the increasing difficulty is turned off) (0072-SPPW-003) +- Linking too many transactions with a low difficulty level to a block is detected and leads rejecting the transactions that are too many (0072-SPPW-004) - Reusing a transaction identifier in a way that several transactions with the same ID end up in the same block is detected and the transactions are rejected (0072-SPPW-005) -- A blocked account is unblocked after the maximum of 1/48 of an Epoch or 30 seconds. For transactions sent in the meantime, a meaningful error message is returned. (0072-SPPW-006) -- PoW attached to a valid transaction will be accepted provided it's using correct chain ID and, at time of submission, the block hash is one of the last `spam.pow.numberOfPastBlocks` blocks. (0072-COSMICELEVATOR-007) +- PoW attached to a valid transaction will be accepted provided it's using correct chain ID and, at time of submission, the block hash is one of the last `spam.pow.numberOfPastBlocks` blocks. (0072-SPPW-007) - For each transaction less than or equal to `spam.pow.numberOfTxPerBlock` in a block `spam.pow.difficulty` zeros are needed in the proof-of-work (0072-SPPW-008) - For each `spam.pow.numberOfTxPerBlock` sized block of transactions greater than `spam.pow.numberOfTxPerBlock` an additional 0 is required in the proof-of-work (1 additional zero for the first batch, two additional for the second batch etc) (0072-SPPW-009) - For a given block, a user is able to submit more than `spam.pow.numberOfTxPerBlock` transactions with only `spam.pow.difficulty` zeros by tying them to one or more historic blocks all of which are within `spam.pow.numberOfPastBlocks` blocks (0072-SPPW-010) @@ -92,26 +87,29 @@ The initial hash-function used is SHA3. To allow for a more fine-grained control - The parameter `spam.pow.difficulty` is increased. Verify that - Transactions tied to such a block using the original difficulty are rejected with an error message. - - The effect of `pow.increaseDifficulty` is still applied, i.e., a wallet key that had the increased difficulty still has the increased difficulty w.r.t. the new baseline. (0072-COSMICELEVATOR-008) + - The effect of `pow.increaseDifficulty` is still applied, i.e., a wallet key that had the increased difficulty still has the increased difficulty w.r.t. the new baseline. (0072-SPPW-013) - The parameter `spam.pow.difficulty` is decreased. Verify that - Transactions tied to such a block using the new difficulty are not rejected. - - The effect of `pow.increaseDifficulty` is still applied, i.e., a wallet key that had the increased difficulty still has the increased difficulty w.r.t. the new baseline. (0072-COSMICELEVATOR-009) + - The effect of `pow.increaseDifficulty` is still applied, i.e., a wallet key that had the increased difficulty still has the increased difficulty w.r.t. the new baseline. (0072-SPPW-014) - The parameter `spam.pow.increaseDifficulty` is changed from 0 to 1. Verify that - - Transactions tied to such a block using insufficient difficulty due to the new parameter are rejected with an error message. (0072-COSMICELEVATOR-010) + - Transactions tied to such a block using insufficient difficulty due to the new parameter are rejected with an error message. (0072-SPPW-015) - The parameter `spam.pow.increaseDifficulty` is changed from 1 to 0. Verify that - - Transactions tied to such a block using the base difficulty are not rejected. (0072-COSMICELEVATOR-011) + - Transactions tied to such a block using the base difficulty are not rejected. (0072-SPPW-016) - The parameter `spam.pow.numberofTxPerBlock` is decreased. Verify that - - The new parameter is used for all blocks with a block height higher than the current one, i.e., a number of transactions that was allowed before and is no longer due to the new parameter is rejected (leaving the spam difficulty level constant) (0072-COSMICELEVATOR-012) + - The new parameter is used for all blocks with a block height higher than the current one, i.e., a number of transactions that was allowed before and is no longer due to the new parameter is rejected (leaving the spam difficulty level constant) (0072-SPPW-017) - The parameter `spam.pow.numberofTxPerBlock` is increased. Verify that - - If a number of transactions is submitted with the lower lever PoW that would have exceeded the `spam.pow.numberOfTXPerBlock` before the change and not after, no transaction is rejected. (0072-COSMICELEVATOR-013) + - If a number of transactions is submitted with the lower lever PoW that would have exceeded the `spam.pow.numberOfTXPerBlock` before the change and not after, no transaction is rejected. (0072-SPPW-018) - Repeat tests 008-013, where in the same block, - `Spam.pow.difficulty` is increased and `spam.pow.increaseDifficulty` is increased (0 to 1), and `spam.pow.numberOfTXPerBlock` is increased. - `Spam.pow.difficulty` is decreased and `spam.pow.increaseDifficulty` is increased (0 to 1), and `spam.pow.numberOfTXPerBlock` is increased. - `Spam.pow.difficulty` is increased and `spam.pow.increaseDifficulty` is increased (0 to 1), and `spam.pow.numberOfTXPerBlock` is decreased. - - `Spam.pow.difficulty` is decreased and `spam.pow.increaseDifficulty` is increased (0 to 1), and `spam.pow.numberOfTXPerBlock` is decreased. (0072-COSMICELEVATOR-014) + - `Spam.pow.difficulty` is decreased and `spam.pow.increaseDifficulty` is increased (0 to 1), and `spam.pow.numberOfTXPerBlock` is decreased. (0072-SPPW-019) + + +- *Mempool pruning* Cause congestion in the mempool by submitting many transactions (perhaps from several parties). Submit a transaction `T` tied to block number `N_old`. Make sure the transactions causing congestion create sufficiently large `N_current`. At some point we'll have `N_old + spam.pow.numberOfPastBlocks < N_current` and the transaction `T` is removed from the mempool and never scheduled. (0072-SPPW-012) diff --git a/protocol/0073-LIMN-limited_network_life.md b/protocol/0073-LIMN-limited_network_life.md index 74c1ac985..756469fc3 100644 --- a/protocol/0073-LIMN-limited_network_life.md +++ b/protocol/0073-LIMN-limited_network_life.md @@ -30,10 +30,11 @@ Information to store: - All [network parameters](../protocol/0054-NETP-network_parameters.md), including those defined [below](#network-parameters). - All [asset definitions](../protocol/0040-ASSF-asset_framework.md#asset-definition). -Insurance pool balances, [Reward account balance](../protocol/0056-REWA-rewards_overview.md), [LP committed liquidity](../protocol/0038-OLIQ-liquidity_provision_order_type.md) and [LP fee pool](../protocol/0029-FEES-fees.md) balances for the markets that have been enacted will be stored with the accepted market proposal that must have preceded the market. +- Insurance pool balances (global for each asset and per-market), [Reward account balance](../protocol/0056-REWA-rewards_overview.md) and [LP committed liquidity](./0044-LIME-lp_mechanics.md) balances for the markets that have been enacted will be stored with the accepted market proposal that must have preceded the market. - All market proposals ([creation](../protocol/0028-GOVE-governance.md#1-create-market) and [update](../protocol/0028-GOVE-governance.md#2-change-market-parameters)) that have been *accepted* but not those where the market already started trading and reached *trading terminated* state. - All [asset proposals](../protocol/0028-GOVE-governance.md) that have been *accepted*. - All delegation info. +- [LP fee pool](../protocol/0029-FEES-fees.md) for undistributed LP fee balances on a market will be added to the LP's general account balances (without applying any SLA penalties). - On chain treasury balances and on-chain rewards for staking and delegation [Staking and delegation](../protocol/0056-REWA-rewards_overview.md). - [Account balances](../protocol/0013-ACCT-accounts.md) for all parties per asset: sum of general, margin and LP bond accounts. - Event ID of the last processed deposit event for all bridged chains @@ -134,8 +135,7 @@ If for `network.checkpoint.timeElapsedBetweenCheckpoints` the value is set to `0 1. The network is restarted with the checkpoint hash from the above checkpoint in genesis. The checkpoint restore transaction is submitted and processed. 1. There is an asset USD. 1. There is a market `id_xxx` in status "pending". -1. The party LP has a `USD` general account balance equal to `LP_gen_bal + LP_margin_bal`. -1. The party LP has `LP_bond_bal` committed to market `id_xxx`. +1. The party LP has a `USD` general account balance equal to `LP_gen_bal + LP_margin_bal` + `LP_bond_bal`. 1. The other party has a `USD` general account balance equal to `other_gen_bal + other_margin_bal`. ### Test case 3: Governance proposals are maintained across resets, votes are not @@ -158,8 +158,28 @@ If for `network.checkpoint.timeElapsedBetweenCheckpoints` the value is set to `0 1. The LP party has general account balance in USD of `9000` and bond account balance `1000` on the market `id_xxx`. 1. The other party has no open orders anywhere and general account balance in USD of `other_gen_bal + other_margin_bal`. +#### Test case 3.1.2: Perpetual market is proposed, accepted, restored (0073-LIMN-105) + +1. There is an asset USD and no asset proposals. +1. There are no markets and no market proposals. +1. There is a party a party called `LP party` with general balance of 10 000 USD. +1. A market is proposed by a party called `LP party` and has enactment date 1 year in the future. The market has id `id_xxx`. +1. `LP party` commits a stake of 1000 USD to `id_xxx`. +1. Other parties vote on the market and the proposal is accepted (passes rules for vote majority and participation). The market has id `id_xxx`. +1. The market is in `pending` state, see [market lifecycle](../protocol/0043-MKTL-market_lifecycle.md). +1. Another party places a limit sell order on the market and has `other_gen_bal`, holding balance `other_hold_bal`. +1. Enough time passes so a checkpoint is created and no party submitted any withdrawal transactions throughout. +1. The network is shut down. +1. The network is restarted with the checkpoint hash from the above checkpoint in genesis. The checkpoint restore transaction is submitted and processed. +1. There is an asset USD. +1. There is a market with `id_xxx` with all the same parameters as the accepted proposal had. +1. The LP party has general account balance in USD of `9000` and bond account balance `1000` on the market `id_xxx`. +1. The other party has no open orders anywhere and general account balance in USD of `other_gen_bal + other_hold_bal`. + #### Test case 3.2: Market is proposed, voting hasn't closed, not restored (0073-LIMN-010) +for product perpetuals: (0073-LIMN-106) + 1. There is an asset USD and no asset proposals. 1. There are no markets and no market proposals. 1. There is a party a party called `LP party` with general balance of 10 000 USD. @@ -174,6 +194,8 @@ If for `network.checkpoint.timeElapsedBetweenCheckpoints` the value is set to `0 #### Test case 3.3: Market is proposed, voting has closed, market rejected, proposal not restored (0073-LIMN-011) +for product perpetuals:(0073-LIMN-107) + 1. There is an asset USD and no asset proposals. 1. There are no markets and no market proposals. 1. There is a party a party called `LP party` with general balance of `10 000` USD. @@ -189,6 +211,9 @@ If for `network.checkpoint.timeElapsedBetweenCheckpoints` the value is set to `0 #### Test case 3.4: Recovery from proposed Markets with no votes, voting is open, proposal not restored (0073-LIMN-012) + +for product perpetuals:(0073-LIMN-108) + 1. There is an asset USD and no asset proposals. 1. There are no markets and no market proposals. 1. There is a party a party called `LP party` with general balance of 10 000 USD. @@ -202,6 +227,10 @@ If for `network.checkpoint.timeElapsedBetweenCheckpoints` the value is set to `0 #### Test case 3.5: Recovery from proposed Markets with votes, voting is open, proposal not restored (0073-LIMN-013) + +for product perpetuals:(0073-LIMN-109) + + 1. There is an asset USD and no asset proposals. 1. There are no markets and no market proposals. 1. There is a party a party called `LP party` with general balance of 10 000 USD. @@ -215,6 +244,9 @@ If for `network.checkpoint.timeElapsedBetweenCheckpoints` the value is set to `0 #### Test case 3.6: Market proposals ignored when restoring twice from same checkpoint (0073-LIMN-014) +for product perpetuals:(0073-LIMN-110) + + 1. A party has general account balance of 100 USD. 1. The party submits a withdrawal transaction for 100 USD. A checkpoint is immediately created. 1. The network is shut down. @@ -225,7 +257,7 @@ If for `network.checkpoint.timeElapsedBetweenCheckpoints` the value is set to `0 1. There is no market and there are no market proposals. 1. The party has general account balance in USD of `0` and The party has "signed for withdrawal" `100`. -### Test case 4: Party's Margin Account balance is put in to a General Account balance for that asset after a reset (0073-LIMN-016) +### Test case 4a: Party's Margin Account balance is put in to a General Account balance for that asset after a reset (0073-LIMN-016) (for perpetuals: 0073-LIMN-111) 1. A party has USD general account balance of 100 USD. 2. That party has USD margin account balance of 100 USD. @@ -233,7 +265,7 @@ If for `network.checkpoint.timeElapsedBetweenCheckpoints` the value is set to `0 4. The network is restarted with the checkpoint hash from the above checkpoint in genesis. The checkpoint restore transaction is submitted and processed. 5. That party has a USD general account balance of 200 USD -### Test case 5: Delegation (test with N=5, 10, 20000) (0073-LIMN-017) +### Test case 5: Add or remove stake during checkpoint restart (0073-LIMN-017) 1. There is a Vega token asset. 1. There are `5` validators on the network. @@ -241,17 +273,17 @@ If for `network.checkpoint.timeElapsedBetweenCheckpoints` the value is set to `0 1. There are `N` other parties. Each of the other parties has `other_party_i`, `i=1,2,...,N` has locked exactly `i` tokens on that staking Ethereum bridge and these tokens are undelegated at this point. 1. Other party `i` delegates all its tokens to `validator_party_j` with `j = i mod 5` (i.e. the remainder after integer division of `j` by `i`.). For example if `N=20000` then party `i=15123` will delegate all its `15123` tokens to validator `validator_party_3` since `15123 mod 5 = 3`. 1. The `Staking and delegation` rewards are active so that every hour each party that has delegated tokens receives `0.01` of the delegated amount as a reward. -1. The network runs for 5 hours. 1. Each of the `other_party_i` has Vega token general account balance equal to `5 x 0.01 x i`. Note that these are separate from the tokens locked on the staking Ethereum bridge. -1. Enough time passes after the 5 hour period so that a checkpoint is created and no party submitted any withdrawal transactions throughout. +1. Enough time passes so that a checkpoint is created and no party submitted any withdrawal transactions throughout. 1. The network is shut down. -1. Validator `1` has freed `500` tokens from the Vega Ethereum staking contract. +1. One party `1` with stake delegated has freed `500` tokens from the Vega Ethereum staking contract. +1. One party `2` with stake delegated adds `500` tokens to the Vega Ethereum staking contract. 1. The network is restarted with the same `5` validators and checkpoint hash from the above checkpoint in genesis. The checkpoint restore transaction is submitted and processed. 1. There is a Vega token asset. -1. Validator parties `validator_party_2`,...,`validator_party_5` has `1000` Vega tokens locked on the staking Ethereum bridge and this is reflected in Vega core. -1. Validator party `validator_party_1` has `500` Vega tokens locked on the staking Ethereum bridge and this is reflected in Vega core. -1. There are `N` other parties and the delegation info in core says that other party `i` has delegated all its tokens to `validator_party_j` with `j = i mod 5`. -1. Each of the `other_party_i` has Vega token general account balance equal to `5 x 0.01 x i`. Note that these are separate from the tokens locked on the staking Ethereum bridge. +1. Validator parties `validator_party_1`,...,`validator_party_5` has `1000` Vega tokens locked on the staking Ethereum bridge and this is reflected in Vega core. +1. Other party `1` has `-500` Vega tokens locked on the staking Ethereum bridge and this is reflected in Vega core, including updated delegation amounts. +1. Other party `2` has `+500` Vega tokens locked on the staking Ethereum bridge and this is reflected in Vega core, including updated delegation amounts via auto delegation. +1. There are `N-2` other parties and the delegation info in core says that other party `i` has delegated all its tokens to `validator_party_j` with `j = i mod 5`. 1. Each of the `other_party_i` has Vega token general account balance equal to `5 x 0.01 x i`. Note that these are separate from the tokens locked on the staking Ethereum bridge. ### Test case 6: Network Parameters / Exceptional case handling @@ -277,7 +309,7 @@ If for `network.checkpoint.timeElapsedBetweenCheckpoints` the value is set to `0 1. Enacted, listed ERC-20 asset is remembered in checkpoint (0073-LIMN-023) 1. An ERC-20 asset loaded from checkpoint can be used in a market loaded from a checkpoint (0073-LIMN-024) 1. An ERC-20 asset loaded from checkpoint can be updated (0073-LIMN-025) -1. An ERC-20 asset loaded from checkpoint can be used in newly proposed markets (0073-LIMN-026) +1. An ERC-20 asset loaded from checkpoint can be used in newly proposed markets (0073-LIMN-026). 1. Can deposit and withdraw funds to/from ERC-20 asset loaded from checkpoint (0073-LIMN-027) 1. Propose a valid ERC-20 asset. @@ -312,26 +344,27 @@ If for `network.checkpoint.timeElapsedBetweenCheckpoints` the value is set to `0 ### Test case 14: Market with trading terminated is not restored, collateral moved correctly 1. Set LP fee distribution time step to non-zero value. -1. Propose, enact, trade in the market, close out distressed party so that insurance pool balance > 0, submit trading terminated. +1. Propose, enact, trade in the market, close out distressed party so that market's insurance pool balance > 0, submit trading terminated. 1. System saves LNL checkpoint at a time when undistributed LP fees for the market are > 0. 1. Restart Vega, load LNL checkpoint. -1. The market is not restored (it doesn't exist in core i.e. it's not possible to submit orders or LP provisions to this market) (0073-LIMN-029) +1. The market is not restored (it doesn't exist in core i.e. it's not possible to submit orders or LP provisions to this market) (0073-LIMN-029). 1. If the market exists in the data node it is marked as settled with no settlement price info (0073-LIMN-030) 1. For parties that had margin balance position on the market this is now in their general account for the asset. (0073-LIMN-031) -1. The LP fees that were not distributed have been transferred to the Vega treasury for the asset. (0073-LIMN-032) -1. The insurance pool balance has been transferred to the Vega treasury for the asset. (0073-LIMN-033) -1. The LP bond account balance has been transferred to the party's general account for the asset. (0073-LIMN-034) +1. The LP fees that were not distributed have been transferred to the Vega treasury for the asset. (0073-LIMN-032). +1. The insurance pool balance has been transferred into the global insurance pool using the same settlement asset. (0073-LIMN-115) +1. The LP bond account balance has been transferred to the party's general account for the asset. (0073-LIMN-034). + ### Test case 15: Market with trading terminated that settled is not restored, collateral moved correctly 1. Propose, enact, trade in the market, submit trading terminated and settlement data, observe final settlement cashflows for at least 2 parties. 1. System saves LNL checkpoint. 1. Restart Vega, load LNL checkpoint. -1. The market is not restored (it doesn't exist in core i.e. it's not possible to submit orders or LP provisions to this market) (0073-LIMN-040) +1. The market is not restored (it doesn't exist in core i.e. it's not possible to submit orders or LP provisions to this market) (0073-LIMN-040). 1. If the market exists in the data node it is marked as settled with correct settlement data. (0073-LIMN-041) -1. For parties that had margin balance position on the market this is now in their general account for the asset. (0073-LIMN-042) -1. The insurance pool balance has been transferred to the Vega treasury for the asset. (0073-LIMN-043) -1. The LP bond account balance has been transferred to the party's general account for the asset. (0073-LIMN-044) +1. For parties that had margin balance position on the market this is now in their general account for the asset. (0073-LIMN-042 +1. The insurance pool balance has been transferred into the global insurance pool using the same settlement asset. (0073-LIMN-116) +1. The LP bond account balance has been transferred to the party's general account for the asset. (0073-LIMN-044). ### Test case 16: Markets can be settled and terminated after restore as proposed @@ -340,19 +373,19 @@ If for `network.checkpoint.timeElapsedBetweenCheckpoints` the value is set to `0 1. Restart Vega, load LNL checkpoint. 1. A party submits liquidity provision to the market, orders are submitted to the opening auction to allow uncrossing; at least two parties now have a position. 1. Submit the trading terminated transaction and settlement date transaction as set out in the proposal and observe the final settlement cashflows for the parties with positions. (0073-LIMN-050) -1. It's not possible to submit orders or LP provisions to this market). (0073-LIMN-051) +1. It's not possible to submit orders or LP provisions to this market. (0073-LIMN-051). -### Test case 17: Markets with internal time trigger for trading terminated that rings between shutdown and restore +### Test case 17: Markets with internal time trigger for trading terminated that fires between shutdown and restore 1. Propose, enact a market with some trading terminated given by internal time trigger. Trade in the market creating positions for at least 2 parties. -1. System saves LNL checkpoint before the trading terminated trigger rings. +1. System saves LNL checkpoint before the trading terminated trigger is set off. 1. Restart Vega, load LNL checkpoint at a time which is after trading terminated trigger should have rung. -1. The market is not restored (it doesn't exist in core i.e. it's not possible to submit orders or LP provisions to this market) (0073-LIMN-060); if it exists it in `cancelled` state. -1. If the market exists in the data node it is labelled as `cancelled` (0073-LIMN-061) +1. The market is not restored (it doesn't exist in core i.e. it's not possible to submit orders or LP provisions to this market) (0073-LIMN-060). +1. If the market exists in the data node it is labelled as `cancelled` (0073-LIMN-061). 1. For parties that had margin balance position on the market this is now in their general account for the asset. (0073-LIMN-062) -1. The LP fees that were not distributed have been transferred to the Vega treasury for the asset. (0073-LIMN-063) -1. The insurance pool balance has been transferred to the Vega treasury for the asset. (0073-LIMN-064) -1. The LP bond account balance has been transferred to the party's general account for the asset. (0073-LIMN-065) +1. The LP fees that were not distributed have been transferred to the Vega treasury for the asset. (0073-LIMN-063). +1. The insurance pool balance has been transferred into the global insurance pool using the same settlement asset. (0073-LIMN-117) +1. The LP bond account balance has been transferred to the party's general account for the asset. (0073-LIMN-065). ### Test case 18: market definition is the same pre and post LNL restore @@ -360,7 +393,36 @@ If for `network.checkpoint.timeElapsedBetweenCheckpoints` the value is set to `0 - System saves LNL checkpoint. - Restart Vega, load LNL checkpoint. - The market has the same: - - risk model and parameters (0073-LIMN-070) - - price monitoring bounds (0073-LIMN-071) - - oracle settings (0073-LIMN-072) + - risk model and parameters (0073-LIMN-070). + - price monitoring bounds (0073-LIMN-071). + - oracle settings (0073-LIMN-072). - margin scaling factors (0073-LIMN-073) + +### Test case 19: Deposit tokens during checkpoint restore + +1. On a vega network which has some ERC20 tokens enabled. +1. Wait for a checkpoint to be available for checkpoint restart. +1. Stop the network. +1. Deposit tokens to a vega party via the ERC20 assert bridge. +1. Restart the vega network from the checkpoint created earlier. +1. There party's newly deposited assets are available. (0073-LIMN-074). + +### Test case 20: Multisig updates during checkpoint restart + +1. On a vega network where one validator has been promoted in favour of another (do not update multisig contract to reflect this), and there are tokens in reward accounts ready for distribution. +1. Wait for a checkpoint to be available for checkpoint restart. +1. Retrieve the signatures to update the multisig contract (do not update yet). +1. Stop the network. +1. Update the multisig contract. +1. Restart the vega network from the checkpoint created earlier. +1. Vega observes the multisig change and rewards are paid at the end of the current epoch. (0073-LIMN-075) + +### Test case 21: Loading from checkpoint with invalid multisig + +1. On a vega network where one validator has been promoted in favour of another (do not update multisig contract to reflect this), and there are tokens in reward accounts ready for distribution. +1. Wait for a checkpoint to be available for checkpoint restart. +1. Retrieve the signatures to update the multisig contract (do not update yet). +1. Stop the network. +1. Do not update the multisig contract. +1. Restart the vega network from the checkpoint created earlier. +1. Vega observes the incorrect multisig, and rewards are not paid at the end of the current epoch. (0073-LIMN-076) diff --git a/protocol/0074-BTCH-batch-market-instructions.md b/protocol/0074-BTCH-batch-market-instructions.md index 31c6f3f4b..c0413bf07 100644 --- a/protocol/0074-BTCH-batch-market-instructions.md +++ b/protocol/0074-BTCH-batch-market-instructions.md @@ -25,12 +25,13 @@ Overall, building the ability to handle batches of market instructions in a sing - **Cancellations**: this is a list (repeated field) of Cancel Order instructions - **Amendments**: this is a list (repeated field) of Amend Order instructions - **Submissions**: this is a list (repeated field) of Submit Order instructions -- The total number of instructions across all three lists (i.e. sum of the lengths of the lists) must be less than or equal to the current value of the network parameter `network.spam_protection.max.batch.size`. +- Additionally the batch may contain a single transaction to change the current margin mode. If this transaction fails, all later transactions within the batch (cancellations/amendments/submissions) for the market referred to in the update are Stopped for reason MARGIN_MODE_UPDATE_FAILED. +- The total number of instructions across all three lists (i.e. sum of the lengths of the lists) must be less than or equal to the current value of the network parameter `network.spam_protection.max.batch.size`. The margin mode update transaction is not included in this limit. ### Processing a batch - A batch is considered a single transaction, with a single transaction ID and a single timestamp applying to all instructions within it. Each instruction should be given a sub-identifier and index allowing it to be placed sequentially in the transaction (e.g. by consumers of the event stream). These identifiers must be sufficient for a user to determine which instruction within a batch any result (order updates, trades, errors, etc.) relates to. -- The batches must be processed in the order **all cancellations, then all amendments, then all submissions**. This is to prevent gaming the system, and to prevent any order being modified by more than one action in the batch. +- The batches must be processed in the order **all cancellations, then all amendments, any margin mode update, then all submissions**. This is to prevent gaming the system, and to prevent any order being modified by more than one action in the batch. Updating the margin mode after cancellations and amendments allows the party to have control of which orders are outstanding when the margin mode changes. - When processing each list, the instructions within the list must be processed in the order they appear in the list (i.e. in the order prescribed by the submitter). (Notwithstanding that each list is processed in its entirety before moving onto the next list, in the order specified above). - All instructions within each list must be validated as normal **at the time that the instruction is processed**. That is, instructions cannot be pre-validated as a batch. If a prior instruction, would create a state that would cause a later instruction to fail validation, the later instruction must fail validation (and vice verse). If validation fails, that instruction must be skipped and the subsequent instructions must still be processed. Any validation or other errors should be returned, as well as a reference to the instruction to which they relate, in the response. - Any errors encountered in processing an instruction after it passes validation must cause it to be skipped, and the errors, as well as the instruction to which they relate, must be available in the result of the transaction. @@ -48,15 +49,17 @@ After entering or exiting an auction mid-batch, the full batch must be processed ## Acceptance criteria -- Given a market with a party having two orders, A and B, a batch transaction to cancel A, amend B to B' and place a limit order which does not immediately execute C should result in a market with orders B' and C. (0074-BTCH-001) +- Given a market with a party having two orders, A and B, a batch transaction to cancel A, amend B to B' and place a limit order which does not immediately execute C should result in a market with orders B' and C. (0074-BTCH-001). - Any batch transaction containing more than one amend to the same order ID should attempt to execute the first as normal but all further amends should error without being executed. (0074-BTCH-002) - An error in any instruction should be logged and returned to the caller but later instructions should still be attempted. (0074-BTCH-003) -- If an instruction causes the market to enter a Price Monitoring Auction the market should enter the auction immediately before continuing with later instructions. (0074-BTCH-005) -- An instruction which is valid at the start of the batch execution but becomes invalid before it is executed should fail. (0074-BTCH-006) In particular: +- If an instruction causes the market to enter a Price Monitoring Auction the market should enter the auction immediately before continuing with later instructions. (0074-BTCH-005). +- An instruction which is valid at the start of the batch execution but becomes invalid before it is executed should fail. (0074-BTCH-006). In particular: - A batch consisting of two limit order placements C1 and C2 where the party has enough balance to place either of them individually but not both should place C1 but reject C2. - A batch transaction containing aggressive limit order C1 which moves the market into price monitoring auction and a C2 which is marked `GFN` (good for normal) should execute C1 but reject C2. - A batch transaction with more instructions than `network.spam_protection.max_batch_size` should fail. (0074-BTCH-007) - Instructions in the batch should be executed in the order Cancellations -> Amendments -> Creations. (0074-BTCH-008) -- Margin released by cancellations or amendments within the batch should be immediately available for later instructions (0074-BTCH-009) +- Funds released by cancellations or amendments within the batch should be immediately available for later instructions (0074-BTCH-009). - If an instruction within a batch causes another party to become distressed, position resolution should be attempted before further instructions within the batch are executed (0074-BTCH-010) - Instructions within the same category within a batch should be executed in the order they are received. For example, if two Cancellations instructions are submitted in a batch: [C1, C2], then C1 should be executed before C2. (0074-BTCH-011) +- If the margin mode update transaction fails all transactions in the batch referring to the same market are Stopped. (0074-BTCH-020) +- If the margin mode update transaction fails all transactions in the batch referring to a different market are attempted as usual. (0074-BTCH-021) diff --git a/protocol/0075-PLUP-protocol_upgrades.md b/protocol/0075-PLUP-protocol_upgrades.md index 3054b8612..d3279ccbf 100644 --- a/protocol/0075-PLUP-protocol_upgrades.md +++ b/protocol/0075-PLUP-protocol_upgrades.md @@ -108,7 +108,7 @@ message ProtocolUpgradeEvent { - (0075-PLUP-002) Validator proposes a protocol upgrade on a block height preceding the current block - should result in an error - (0075-PLUP-003) Propose and enact a version downgrade - (0075-PLUP-004) Non-validator attempts to propose upgrade -- (0075-PLUP-005) Ersatz validator attempts to propose upgrade +- (0075-PLUP-005) Ersatz validator (standby validator) attempts to propose upgrade ### Block height validation @@ -200,3 +200,53 @@ For the purposes of protocol upgrade each validator that participates in consens - (0075-PLUP-042) Trader balances available prior to upgrade is still available post upgrade. - (0075-PLUP-043) Pending and active assets available prior to upgrade is still available post upgrade. - (0075-PLUP-044) Network parameter, market and asset proposals can span a protocol upgrade. + +### Ethereum events during outage + +- (0075-PLUP-051) Deposit events that take place during protocol upgrade are registered by the network once the upgrade is complete. + 1. Schedule an upgrade on a network that is not using visor. + 1. When the nodes stop processing blocks for the upgrade, shut down the nodes. + 1. Deposit tokens via the ERC20 bridge. + 1. Start the network using the upgrade binary. + 1. Balance reported as added in the appropriate account(s). +- (0075-PLUP-052) Staking events that take place during protocol upgrade are registered by the network once the upgrade is complete. + 1. Ensure parties A & B have some stake, which is delegated to a/some node(s). + 1. Schedule an upgrade on a network that is not using visor. + 1. When the nodes stop processing blocks for the upgrade, shut down the nodes. + 1. Add stake to party A. + 1. Remove some (not all) stake from party B. + 1. Start the network using the upgrade binary. + 1. Additional stake reported for party A and auto-delegated. Stake removed for party B and delegation reduced. +- (0075-PLUP-047) Multisig events that take place during protocol upgrade are registered by the network once the upgrade is complete. + 1. Arrange a network where one validator is promoted to replace another validator. Collect signatures to update the multisig contract, but do not yet update the multisig. + 1. Schedule an upgrade on the network (should not be using visor). + 1. When the nodes stop processing blocks for the upgrade, shut down the nodes. + 1. Update the multisig contract to reflect the correct validators. + 1. Start the network using the upgrade binary. + 1. At the end of the current epoch, rewards are paid out. +- (0075-PLUP-048) Multisig events that take place during protocol upgrade are registered by the network once the upgrade is complete. + 1. Arrange a network where one validator is promoted to replace another validator. Collect signatures to update the multisig contract, but do not yet update the multisig. + 1. Schedule an upgrade on the network (should not be using visor). + 1. When the nodes stop processing blocks for the upgrade, shut down the nodes. + 1. Do not update the multisig contract to reflect the correct validators. + 1. Start the network using the upgrade binary. + 1. At the end of the current epoch, rewards are not paid out. + 1. Update the multisig contract to reflect the correct validators. + 1. At the end of the current epoch, rewards are paid out. + +### Transactions during upgrade + +- (0075-PLUP-049) Network handles filled mempool during upgrade. + 1. Schedule a protocol upgrade in a network with no nodes using visor. + 1. When the nodes stop processing blocks for the upgrade, shut down the nodes. + 1. Start one node on the new binary. + 1. Send enough transactions to the node to fill the tendermint mempool. (Expect sane rejection once mempool is full) + 1. Start the other nodes on the correct upgrade binary. + 1. Expect all transactions that reached the mempool without being rejected to be correctly processed over several blocks. +- (0075-PLUP-050) Transactions can be made in block immediately before protocol upgrade. + 1. Schedule a protocol upgrade in a network with no nodes using visor. + 1. Continuously send transactions as the upgrade block approaches. + 1. When the nodes stop processing blocks for the upgrade, make a note of all transactions which reached blocks already (transactions which did not are expected to be discarded). + 1. Shut down the nodes. + 1. Start all nodes on the new binary. + 1. Expect all transactions that reached blocks prior to upgrade to have taken effect. None of the other transactions did. diff --git a/protocol/0076-DANO-data-node.md b/protocol/0076-DANO-data-node.md index af5e19252..40349959d 100644 --- a/protocol/0076-DANO-data-node.md +++ b/protocol/0076-DANO-data-node.md @@ -107,7 +107,6 @@ All of these should be available at various time resolutions: on every change, o - LP order submissions - Equity-like share changes -- Market value proxy - Target stake - Supplied stake @@ -146,11 +145,12 @@ It must be possible to add to the data node APIs that return the result of calcu ### Data synchronisation -1. To ensure no loss of historical data access; data nodes must be able to have access to and synchronise all historical data since genesis block or LNL restart (0076-COSMICELEVATOR-001) +1. To ensure no loss of historical data access; data nodes must be able to have access to and synchronise all historical data since genesis block or LNL restart (0076-DANO-001) 1. To ensure that new nodes joining the network have access to all historical data; nodes must be able to have access to and synchronise all historical data across the network without having to replay the full chain (0076-DANO-003) 1. Nodes must be able to start processing new blocks having loaded the only the most recent history (0076-DANO-023) 1. Nodes that have been temporarily disconnected from the network should be able to load the missed history to get back up to the current network height (or most recently produced history) and then be able to start processing new blocks (0076-DANO-024) 1. It must be possible to fetch history from the network whilst the node processes new blocks. So for example, if setting up a new Archive node, the node can keep up to date with the network whilst retrieving history all the way back to the first block. Once this is done the node should be able to reconcile the fetched history with that produced whilst the history was being retrieved such that the node will have a full history from the first block all the way to the networks current height. (0076-DANO-025) +1. It must be possible to rollback the data-node to a previous block height and have it process events from this height onwards. The state of the datanode at the rollback height must match exactly the state of the node as it was when it originally reached the given height. (0076-DANO-039) ### Data integrity @@ -166,6 +166,7 @@ It must be possible to add to the data node APIs that return the result of calcu 1. Starting a core node at block height greater than the data-nodes block height must result in an error and a refusal to start (0076-DANO-014) 1. If a data-node snapshot fails during the restore the process, it should error and the node(s) won't start (0076-DANO-009) 1. When queried via the APIs a node restored from decentralised history should return identical results to a node with the same block span which has been populated by event consumption. [project front end dApps](https://github.com/vegaprotocol/frontend-monorepo/actions/workflows/generate-queries.yml). (0076-DANO-022) +1. All network history retained by a node for a given block span and type must be downloadable in CSV format. (0076-DANO-040) ### Data-node network determinism @@ -174,8 +175,8 @@ It must be possible to add to the data node APIs that return the result of calcu ### Schema compatibility -1. It is possible to identify if schema versions are NOT backwards compatible. Pull existing network snapshots start network, run a protocol upgrade to at later version and ensure both the core state and data-node data is correct (0076-COSMICELEVATOR-XXX) -1. Restoring a node from decentralised history should work across schema upgrade boundaries and the state of the datanode should match that of a datanode populated purely by event consumption (0076-COSMICELEVATOR-XXX) +1. It is possible to identify if schema versions are NOT backwards compatible. Pull existing network snapshots start network, run a protocol upgrade to at later version and ensure both the core state and data-node data is correct (0076-DANO-041) +1. Restoring a node from decentralised history should work across schema upgrade boundaries and the state of the datanode should match that of a datanode populated purely by event consumption (0076-DANO-042) ### Data Retention @@ -205,3 +206,4 @@ It must be possible to add to the data node APIs that return the result of calcu 1. The event bus stream is available from validators, non validators and the DataNode (0076-DANO-017) 1. If a DataNode loses connection to a Vega node if will attempt to reconnect and if the cached data received from the Vega node is enough to continue working it can resume being a DataNode. (0076-DANO-019) 1. The DataNode must provide its current block height and vega time on responses to client requests so the client can determine whether or not the data is stale. (0076-DANO-021) + diff --git a/protocol/0077-SNAP-snapshots.md b/protocol/0077-SNAP-snapshots.md index 4607b67eb..bb6034e3a 100644 --- a/protocol/0077-SNAP-snapshots.md +++ b/protocol/0077-SNAP-snapshots.md @@ -63,3 +63,4 @@ A bad node can swamp the network by requesting snapshots from other nodes which - The state of a node that is started from a snapshot should be identical to a node that had reached the same block height via replay. (0077-SNAP-004) - Post a checkpoint restore we see snapshots continuing to be produced as before and can be used to add a node to the network (0077-SNAP-005) - With `snapshot.interval.length` set to `k` all the nodes in a network will create a snapshot at block height `k`, `2k`, `3k`, ... (0077-SNAP-006) + diff --git a/protocol/0078-NWLI-network_wide_limits.md b/protocol/0078-NWLI-network_wide_limits.md index 4dbdb0de6..3529bac9d 100644 --- a/protocol/0078-NWLI-network_wide_limits.md +++ b/protocol/0078-NWLI-network_wide_limits.md @@ -13,18 +13,6 @@ Default value: `1,500`. If the network parameter gets increased via a governance vote no further actions are needed. If it gets decreased below the current total number of pegged orders across all active markets then no further actions are needed. Specifically: all pegged orders already present in the markets remain unaffected, but no new pegged orders are allowed until the **total pegged order count** drops below the new limit. -[Liquidity provision orders](./0038-OLIQ-liquidity_provision_order_type.md) do not get counted towards the limit. - -## LP order shapes - -Each [LP order shape](./0038-OLIQ-liquidity_provision_order_type.md#how-they-are-submitted) has a limit of entries (offsets) driven by `market.liquidityProvision.shapes.maxSize`. -Default value: `5`. - -### Change of `market.liquidityProvision.shapes.maxSize` network parameter - -If the network parameter gets increased via a governance vote no further actions are needed. -If it gets decreased below the current total number of pegged orders across all active markets then no further actions are needed. Specifically: all existing LP orders remain unaffected, but any new LP orders need to respect it. - ## Acceptance Criteria ### Pegged order tests @@ -33,10 +21,3 @@ If it gets decreased below the current total number of pegged orders across all - Attempt to place a pegged order by a new party in a new market when the number of pegged orders on the book is equal to `limits.markets.maxPeggedOrders` results in a rejection. Error message attributes the rejection to the limit. (0078-NWLI-002) - Lowering `limits.markets.maxPeggedOrders` to the number number of pegged orders on the book minus 2 results in no changes to the order book composition. Once 2 of the orders get filled and 1 gets cancelled in one market it is possible to successfully submit one additional pegged order in any of the active markets. (0078-NWLI-003) - When the limit is reached [API](./0020-APIS-core_api.md#network-wide-limits) correctly indicates that. (0078-NWLI-004) - -### LP order shape tests - -- Submitting a [liquidity provision order](./0038-OLIQ-liquidity_provision_order_type.md) with 5 buy shapes and 5 sell shapes proceeds without any errors and liquidity provision becomes active. (0078-NWLI-005) -- Submitting a liquidity provision order with 6 buy shapes and 5 sell shapes results in a failure. Error message attributes the rejection to the limit. (0078-NWLI-006) -- Submitting a liquidity provision order with 5 buy shapes and 6 sell shapes results in a failure. Error message attributes the rejection to the limit. (0078-NWLI-007) -- Lowering the `market.liquidityProvision.shapes.maxSize` network parameter doesn't affect the existing orders, but a new LP order that exceeds it gets rejected. (0078-NWLI-008) diff --git a/protocol/0079-TGAP-transaction_gas_and_priority.md b/protocol/0079-TGAP-transaction_gas_and_priority.md index 19554a206..ad00f423c 100644 --- a/protocol/0079-TGAP-transaction_gas_and_priority.md +++ b/protocol/0079-TGAP-transaction_gas_and_priority.md @@ -33,7 +33,7 @@ Variables needed: - `network.transactions.maxgasperblock` - `maxGas` - number of price levels on the order book taken, this can count just static volume or static plus dynamic(*) - `levels` - number of pegged orders - `pegs` -- number of LP shape levels on the market - `shapes` (this means that if LP A has 3 pegs on buy and 2 on sell and LP B has 1 buy peg and 4 sell pegs and they're the only LPs then this value is 10 ) +- number of stop orders on the market - `stops` - number of positions on the market - `positions` (*) update after implementation @@ -41,26 +41,26 @@ Variables needed: Constants needed: - `peg cost factor = 50` non-negative decimal -- `LP shape cost factor = 100` non-negative decimal -- `position factor = 1` non-negative integer -- `level factor = 0.1` non-negative integer +- `stop cost factor = 0.2` non-negative decimal +- `position factor = 1` non-negative decimal +- `level factor = 0.1` non-negative decimal - `batchFactor = 0.5` decimal between `0.1 and 0.9`. -### Any type of limit or market order +### Any type of limit or market order, or liquidity provision transaction ```go gasOrder = network.transaction.defaultgas + peg cost factor x pegs - + LP shape cost factor x shapes + + stop cost factor x stops + position factor x positions + level factor x levels gas = min((maxGas/minBlockCapacity)-1,gasOrder) ``` -### Cancellation of any single order +### Cancellation of any single order or liquidity provision transaction ```go gasCancel = network.transaction.defaultgas + peg cost factor x pegs - + LP shape cost factor x shapes + + stop cost factor x stops + level factor x levels gas = min((maxGas/minBlockCapacity)-1,gasCancel) ``` @@ -77,22 +77,12 @@ Here `gasBatch` is 1. plus the full cost of the first amendment at `gasOrder` 1. plus `batchFactor` sum of all subsequent amendments added together (each costing `gasOrder`) 1. plus the full cost of the first limit order at `gasOrder` -1. plus `batchFactor` sum of all subsequent limit orders added together (each costing `gasOrder`) +1. plus `batchFactor` sum of all subsequent submissions added together (each costing `gasOrder`) ```go gas = min((maxGas/minBlockCapacity)-1,batchGas) ``` -### LP provision, new or amendment or cancellation - -```go -gasOliq = network.transaction.defaultgas + peg cost factor x pegs - + LP shape cost factor x shapes - + position factor x positions - + level factor x levels -gas = min((maxGas/minBlockCapacity)-1,gasOliq) -``` - ## Transaction priorities Transactions with higher priorities that are present in the mempool will get placed into a block before transactions with lower priority are considered. @@ -111,14 +101,19 @@ There are three priority categories: 1. Set `network.transactions.maxgasperblock = 100` and `network.transaction.defaultgas = 20`. 1. Send `100` transactions with default gas cost to a node (e.g. votes on a proposal) and observe that most block have 5 of these transactions each. -### Test max with a market (0079-TGAP-002) + +### Test max with a market (0079-TGAP-004) 1. Set `network.transactions.maxgasperblock = 100` and `network.transaction.defaultgas = 1`. -1. Create a market with 1 LP using 2 shape offsets on each side, just best static bid / ask on the book and 2 parties with a position. -1. Another party submits a transaction to place a limit order. A block will be created containing the transaction (even though the gas cost of a limit order is `1 + 100 x 4 + 2 + 0.1 x 6` which is well over `100`.) +1. Create a market with 1 LP +1. Place 2 matching orders, 1 buy order below the matching price and 1 sell order above the matching price. Uncross the opening auction. +1. Place 3 pegged orders with different non-zero offsets. +1. Another party submits a transaction to place a limit order. A block will be created containing the transaction (even though the gas cost of a limit order is `1 + 50 x 3 + 2 x 1 + 0.1 x 5` which is well over `100`.) + -### Test we don't overfill a block with a market (0079-TGAP-003) +### Test we don't overfill a block with a market (0079-TGAP-005) 1. Set `network.transactions.maxgasperblock = 500` and `network.transaction.defaultgas = 1`. -1. Create a market with 1 LP using 2 shape offsets on each side, just best static bid / ask on the book and 2 parties with a position. -1. Another party submits 10 transaction to place 10 limit order. A separate party submits `100` transactions with default gas cost. Block will be created but each only containing one limit order placement transaction and including some number of vote transactions. +1. Place 2 matching orders, 1 buy order below the matching price and 1 sell order above the matching price. Uncross the opening auction. +1. Place 3 pegged orders with different non-zero offsets. +1. Another party submits 10 transaction to place 10 limit order. A separate party submits `100` transactions with default gas cost. Blocks will be created but each only containing one limit order placement transaction and including some number of vote transactions. diff --git a/protocol/0081-SUCM-successor_markets.md b/protocol/0081-SUCM-successor_markets.md new file mode 100644 index 000000000..89641c0a3 --- /dev/null +++ b/protocol/0081-SUCM-successor_markets.md @@ -0,0 +1,133 @@ +# Successor markets + +## Overview + +On Vega anyone can propose a market via on-chain [governance](./0028-GOVE-governance.md). +The markets are created and in many cases terminated and settled following the [market lifecycle](./0043-MKTL-market_lifecycle.md). +On every market liquidity is provided by various parties, liquidity providers (LPs), who [commit to provide liquidity](./0044-LIME-lp_mechanics.md) by depositing a bond / stake. +As part of this process the LPs build-up virtual stake on the market, which may be higher than the stake they committed if the market grew. +For [details of virtual stake calculation see how LPs are rewarded](./0042-LIQF-setting_fees_and_rewarding_lps.md). + +Many derivative markets would terminate and settle periodically but would be part of a lineage. +Think e.g. of a [cash-settled future](./0016-PFUT-product_builtin_future.md) written on the same underlying that settles every three months. +Successor markets are a feature that allows for markets to have a lineage, but most importantly allows LPs to keep their virtual stake built up on one market (parent) in the lineage to be transferred to the next one (successor). +Moreover, part of the insurance pool of a parent market can be earmarked for transfer to the successor market instead of being transferred into the global insurance pool. + +## Relevant network / market parameters + +- `market.liquidity.successorLaunchWindowLength` is a network parameter specifying how long, after a market has settled, the LPs virtual stakes are retained and the insurance pool is left undistributed to allow a successor to be defined. +- `parent market Id` is part of market proposal which can optionally specify a parent market; see [governance](./0028-GOVE-governance.md). +- `insurancePoolFraction` is is part of market proposal which can optionally specify how much of the insurance pool of the parent is to be transferred to the successor; see [governance](./0028-GOVE-governance.md). + +## Specifying a parent market and timing details + +A market [governance] proposal for a successor market must contain all the information of a full proposal with additionally specified `parent market Id` and `insurancePoolFraction`. +The product type, settlement asset, and margin asset must match but all other inputs can be different (e.g. position and price decimal places, risk model, price monitoring, termination and settlement oracles etc.). + +The parent market must be either: a) in one of `proposed`, `pending`, `active`, `suspended` or `trading terminated` +or b) `settled` state but with time since settlement less than or equal `market.liquidity.successorLaunchWindowLength` +or c) `cancelled` (closed by governance) but with the closing time less than or equal `market.liquidity.successorLaunchWindowLength`. +The point of setting up a market to be successor of an existing market is to +a) allow LPs continue claim their virtual stake / equity-like-share (ELS) by committing liquidity to the successor market during the pending period if they wish to, and +b) allow the successor market to inherit the insurance pool of the parent market. When the successor market leaves the opening auction (moves from pending to active) the amount equal to `insurancePoolFraction x parent market insurance pool balance` is transferred to the successor market insurance pool. Once the parent market moves from "trading terminated" to "settled" state, the entire remaining insurance pool of the successor market is transferred to the successor market insurance pool. + +If the parent market is `proposed` or `pending` or the opening auction ends after the settlement time / cancellation time plus `market.liquidity.successorLaunchWindowLength` then the parent marketID may no longer exist in core or there may be no virtual stake to claim (copy). In that case the successor market virtual stakes are initialised as if the market has no parent (and we set the parent market field in market data to null / empty indicating no parent market). + +Note that each market can have exactly one market as a _successor_ market. + +- if there already is a market (possibly pending, i.e. in opening auction, see [lifecycle spec](./0043-MKTL-market_lifecycle.md)), naming a parent market, then a subsequent proposal referencing that market is rejected. +- if there are two proposals naming the same parent market then whichever one gets into the _active_ state first (i.e. passes governance vote and clears the opening auction) becomes the successor of the named parent; the other proposal is kept but the parent market id field is cleared and when opening auction ends no virtual stake will get carried over. +- if there is a successor market naming a parent market and the parent terminates and settles or is cancelled by governance before the parent market (for whatever reason) then the parent market can again act as successor to a different market proposed by a another market proposal. + +## Carrying over virtual stake + +While a successor market is in opening auction any LP party can submit liquidity commitments to it. +LP parties that exist on the parent market will get special treatment. + +At the end of opening auction, if the parent market still exists, the following will happen. + +1. For each LP that exists on the parent market their virtual stake is carried over. +1. For each LP that exists on the parent market we update their virtual stake using the difference (delta) between the physical stake present on the parent market and the stake committed to the successor market using the update rule given in [the spec detailing LP rewards](./0042-LIQF-setting_fees_and_rewarding_lps.md). + +## Transferring insurance pool balance + +At the end of opening auction, if the parent market still exists, the fraction of the parent insurance pool balance given by `insurancePoolFraction` is transferred to the successor market. + + +## Acceptance criteria + +### Proposals and timing + +Market proposal may specify parent market ID. If it does then: + +- It must also specify insurance pool fraction (0081-SUCM-001) +- The product type, settlement asset and margin asset must match between parent and successor; if not proposal is rejected: + - futures to perpetuals (0081-SUCM-002) + - perpetuals to spot (0081-SUCM-033) + - spot to futures (0081-SUCM-034) +- It is possible for the successor to specify different trading termination and settlement oracle data (0081-SUCM-003). + + +It is possibly to cancel a [perpetual futures](./0053-PERP-product_builtin_perpetual_future.md) market via governance and propose a new perpetual futures market as a successor of the aforementioned cancelled / to be cancelled with different `market_decimal_places` and `position_decimal_places`; the LPs virtual stakes are carried over (0081-SUCM-015). + +Two proposals that name the same parent can be submitted. Both can be approved by governance. The proposed market that clears the opening auction first gets a share of the insurance pool, and the virtual stakes get carried over. Once the first market clears the opening auction, the other market is "Rejected," and all assets committed into LP bond accounts will be immediately released. Orders placed into the opening auction will be cancelled, and the assets held to support any party's orders will be released. (0081-SUCM-005). + +A new market proposal sets parent market Id to a market that has settled. The parent market has non-zero insurance pool balance. If the new market clears the opening auction before `parent settlement time + market.liquidity.successorLaunchWindowLength` then the virtual stakes are carried over and the relevant fraction of the insurance pool is transferred over (0081-SUCM-006). + +A new market proposal sets parent market Id to a market that has settled. The parent market has non-zero insurance pool balance. If the new market clears the opening auction after `parent settlement time + market.liquidity.successorLaunchWindowLength` then no virtual stakes are carried over, the successor market is not a successor market anymore, it's just a market like any other, and the insurance pool balance will be transferred into the global insurance pool (0081-SUCM-036) + +Successor markets cannot be enacted if the parent market is still in the "proposed" state. Successor market proposals can be submitted when the parent market is still in proposed state. When the voting period for the successor market ends then either: the parent market is already enacted in which case the successor market moves from "proposed" in to opening auction/"pending" state. Or the parent market is still in "proposed" state in which case successor market is rejected. (0081-SUCM-008) + +Successor markets which are proposed whilst the parent is also still in a "proposed" state, will be rejected if the parent is rejected. (0081-SUCM-027) + +Successor markets can be enacted when the parent market is in opening auction. There is no virtual stake to copy over, and no insurance pool balance to transfer. (0081-SUCM-009) + +A successor market proposal can be enacted when the parent market is in one of the following states: Pending, Suspended, Active, Trading terminated or Settled (settled within the successor time window) (0081-SUCM-010) + +When a successor market is enacted, all other related successor market proposals, in the state "pending" or "proposed", are automatically rejected. Any LP submissions associated with these proposals are cancelled, and the funds are released (0081-SUCM-011) + +With two successor markets in opening auction, that have the same parent market, and one additional market in the state "Proposed". Get one of the two markets to leave the opening auction (passage of time, LP commitment, crossing trade). The other market in auction and the proposed market should both be "Rejected" and all LP funds will be released (0081-SUCM-014) + +Propose two markets which are attempting to succeed the same parent, and which have an overlapping voting period. Ensure the first child passes governance and enters opening auction. Ensure that the second child is also able to enter opening auction. The first to complete opening auction becomes the successor, and the other is rejected.(0081-SUCM-028) + +Propose a successor market which specifies a parent which is settled, and for which the successor time window has expired. The proposal is declined. (0081-SUCM-018) + +### APIs + +It is possible to fetch a market "parent / successor chain" containing the initial market and the full successor line via: + +- GRPC (0081-SUCM-012) +- GraphQL (0081-SUCM-023) +- REST (0081-SUCM-024) + +When fetching a market that is part of a "parent / successor chain", we should see both the parent and each successor `marketID` (0081-SUCM-013) + + +### Snapshots / checkpoints / Protocol Upgrade / Network History + +After a LNL checkpoint restart the successor (child) / parent market state is preserved where applicable inc. the LPs ELS (0081-SUCM-016) + +A market which has been settled, but is still inside successor expiry window, is retained in a checkpoint, and can be used by a successor market after restart(0081-SUCM-029) + +A market which has been settled, and beyond the successor expiry window, is not retained in a checkpoint, and cannot be used by a successor market after restart(0081-SUCM-030) + +A market which has been settled, and already has a child which has succeeded it, is retained in a checkpoint. Market can be queried via APIs and settled market state can be retrieved. Both child and parent retain parent/child links in market state, and are listed in "successor chain" API request(0081-SUCM-031) + +For a parent and child (explicitly: the child has left opening auction), after a checkpoint restart, parent and child both enter opening auction again. It is not possible to propose a new market which attempts to succeed that parent.(0081-SUCM-032) + +After snapshot restart the successor (child) / parent market state is preserved where applicable including the LPs ELS (0081-SUCM-017) + +A market which has expired before a protocol upgrade is still eligible to be used as a successor market after the upgrade, if it is inside the successor time window (0081-SUCM-025) + +A data node restored from network history includes the full succession chain for a market. (0081-SUCM-026) + + +### Virtual stake + +A new market is set with a parent market Id. On the parent there are two parties `A` and `B` with virtual stakes `v1` and `v2` and physical stakes `s1` and `s2` + +If + +- Both `A` and `B` submit a liquidity commitment of `s1` and `s2` to the new market before the opening auction ends. No other LP submits liquidity to the new market. Then, once the opening auction resolved the LPs `A` and `B` have virtual stakes `v1` and `v2` (0081-SUCM-020). +- As above but `A` submits `s1` and `B` doesn't submit anything. Then `A` has virtual stake `v1` and `B` has virtual stake `0` (0081-SUCM-021). +- As above but `A` submits more than `s1`. Then `A` has virtual stake larger than `v1`. (0081-SUCM-022) diff --git a/protocol/0082-ETHD-ethereum-data-source.md b/protocol/0082-ETHD-ethereum-data-source.md new file mode 100644 index 000000000..314f96533 --- /dev/null +++ b/protocol/0082-ETHD-ethereum-data-source.md @@ -0,0 +1,189 @@ +# Ethereum data source + + +## Summary + +This specification adds a new way of sourcing data from the Ethereum blockchain to allow arbitrary data from the Ethereum blockchain to be ingested as a [data source](./0045-DSRC-data_sourcing.md). +This is in addition to the existing [Ethereum bridge](./0031-ETHB-ethereum_bridge_spec.md), which is unchanged by this spec. + + +## Description + +Currently, data is sourced from Ethereum only as a result of watching for pre-defined events, on a specific contract, at a specific address. +Namely, the ERC20 bridge contract. +These events are picked up by the [event queue](./0036-BRIE-event_queue.md) processor and submitted by each node as transactions to the Vega chain. +Once a quorum of nodes have ratified the transaction, it is reflected in the Vega state. + +Ethereum data sources extend this in three important ways: + +1. In addition to listening for events defined on a contract, `read` methods can be called on a contract. + +2. The event to listen for or function to be read (along with any parameters that must be passed) are specified as part of the data source, rather than being pre-defined across the whole system. + +3. The address of the contract being observed is also specified as part of the data source. + + +Like all data sources, Ethereum oracles may be subject to filters or other processing or aggregation functions. +Once observed, the data is treated as any other data source and available to any part of the system that accepts a data source as input. +The event queue may evaluate filters before submitting the observation to the Vega chain, in order to avoid submitting transactions containing data that will be dropped due to filters in any case. + + +## Functional design + +### Contracts and ABI + +An Ethereum oracle has as its subject a smart contract that is deployed on the Ethereum blockchain. +All such contracts have an address, and an "ABI" that defines the methods an events exposed by the contract. +In order to interpret the oracle specification and interact with the smart contract, both the contract address and Ethereum ABI JSON for the contract (or a subset, covering the relevant parts) must therefore be included in the oracle specification. + +Event data and data returned from functions will be emitted by the Ethereum node in an ABI-encoded format. +This data, including any structs should be decoded using the ABI into a JSON-like representation. + +Note: as with any data source containing JSON formatted (or other arbitrary structured) data, the data required by the consumer of the data source may be a number of fields or sub-objects (including nested fields and objects). +The data sourcing framework therefore requires functionality to apply a query/selector extract the relevant subset of the observed data and pass it to the next consumer. +This would be expected to use `JSONPath`/`JSONPointer` or similar, and be applicable to any arbitrary JSON. +Regardless, the specification of this functionality is out of scope for this document and the approach must be standardised across the data sourcing framework (for example, specifying the target of filters must use the same format as selecting data to pass on). + + +### Ethereum chain data enrichment + +All data sourced from Ethereum should be structured as an object containing both a payload and Ethereum chain metadata, namely: + +- Ethereum block height at which the data was observed/event occurred + +- Ethereum block timestamp at which the data was observed/event occurred + + +This data can be used as the subject of filters or even extracted as the oracle data of interest. +Filters on Ethereum block height, Ethereum timestamp, or Vega timestamp should be applied prior to submitting the data to the Vega chain, in addition to being re-applied when the data is confirmed on chain. +This would prevent spamming the Vega chain with event data that is not relevant. + + +### Events + +When the data source is an event emitted by the Ethereum contract, the event name must be specified in the data source specification. +The specified event must be defined in the supplied ABI. + + +### Contract read + +When the data source is a contract read, the method name and any arguments to be passed must be specified in the data source specification. +The specified method must be defined in the supplied ABI. + + +## Error checking and handling + +Errors in the data source specification should be caught where possible during validation. +Errors that occur or are detected later (e.g. when data arrives) must not propagate to other parts of the system. +That is, they must be contained within the data sourcing subsystem. +It should be possible to determine if such errors have occurred by listening for events or querying the data source APIs. + +- Attempts to select data from non-existent fields or structures in observed data should be recorded as errors on the event bus and in APIs, and the system must not emit data to the receiver. + +- Incorrect ABI (where this cannot be validated at the time the ABI is submitted) and/or the inability to decode data with the provided ABI should be recorded as errors on the event bus and in APIs, and the system must not emit data to the receiver. + +- A mismatch in data types between a data field and the data required by the receiver should be recorded as errors on the event bus and in APIs, and the system must not emit data to the receiver. + + +## Pseudocode examples + +Event data source specification: + +```json +Select { + source: Filter { + source: EthereumEvent { + contract: 0xDEADBEEF + ABI: "...JSON..." + event: "StakeDeposited" + } + where: [ + { selector: '$.ethereum_block_height, condition: '>69420' } + ] + } + selector: '$.amount_deposited' +} +``` + + +## Acceptance criteria + +### External Oracles - Creation + +1. Using the existing ways to create or update a market via governance proposals, define data sources for settlement and termination as the result of calling a read method of a smart contract on ethereum network. (0082-ETHD-001) +2. Phase 2 of the above step would be defining an oracle that is based on listening for events on ethereum network) (0082-PLAZZO-001) + +### External Oracles - Amendments + +1. Update an existing market using the market update proposal to change the smart contract address and read method. The changes take effect after the market update proposal is enacted and data is sourced from the new smart contract. The old data source will be deactivated when the proposal is enacted (0082-ETHD-002) +2. Using the market update proposal all data elements for the ethereum oracles can be updated. On successful enactment , a new oracle data source is created for the market. In case any existing data source matches the new data source , then a new data source is not created and the existing one is used (0082-ETHD-003) +3. Phase 2 - Update an existing market using the market update proposal to change the events that the market is listening to. The changes take effect after the market update proposal is enacted and data is sourced from the new events (0082-PLAZZO-002) +4. Ensure existing oracle data sources are deactivated when market data sources are amended on another market. Create 2 markets to use different ethereum oracles for termination and settlement. Two sets of ethereum oracles are created and are ACTIVE. Then amend Market 2 to use exactly the same ethereum oracles for termination and settlement as Market1. Now ,the ethereum oracles originally created for for Market2 should be set to DEACTIVATED. No new ethereum oracles should be created and the Market2 should use the existing ethereum oracles created for Market1 (0082-ETHD-005) +5. Ensure that when a market data source type is amended from internal, external, ethereum or open (coinbase) to an alternative for both termination and settlement we see that old data source is deactivated (if no other market is using) and we see the new data source created and it supports termination and settlement specific to its data source type (0082-ETHD-006) + +### External Oracles - Deactivation + +1. Aligned with the existing logic, when no market listens to a data source, whatever that source is, it is automatically disregarded by the engine and the status of the data source is set to DEACTIVATED. Same applies for ethereum oracles (0082-ETHD-007) + +### External Oracles - Validations + +1. Validate if the smart contract address is valid (0082-ETHD-010) +2. Validate if the data elements of the oracle data source is valid - e.g. call the smart contract and check if the types in the ABI match whats provided in the oracle spec (0082-ETHD-011) +3. Validations for min / max frequency of listening for events / read a smart contract (0082-ETHD-012) +4. When a proposal that uses ethereum oracles, defines incorrect data (contract address, ABI) the system should return an error and the proposal should not pass validation (0082-ETHD-013) +5. Any mismatch between expected fields/field types and received fields/field types should emit an error event (0082-ETHD-014) + +### Usage + +1. It should be possible to use only ethereum oracle data sources in a market proposal, or create any combination with any of the other types of currently existing external or internal data sources (0082-ETHD-015) +2. Create a market to use an internal data source to terminate a market and an ethereum oracle to settle the market (0082-ETHD-016) +3. Create a market to use an external data source to terminate a market and an ethereum oracle to settle the market (0082-ETHD-017) +4. Create a market to use an open oracle data source to settle a market and an ethereum oracle to terminate the market (0082-ETHD-018) +5. Chain events should only be sent when the filter is matched, this can be verified using an API and the core/data node events (BUS_EVENT_TYPE_ORACLE_DATA) (0082-ETHD-019) +6. Ethereum oracle data sources should only forward data after a configurable number of confirmations (0082-ETHD-020) +7. Create 2 markets to use the same ethereum oracle for termination say DS-T1 but two different ethereum oracles for settlement DS-S1 and DS-S2. Now trigger the termination ethereum oracle data source. Both markets should be terminated and the data source DS-T1 is set to DEACTIVATED and the data sources DS-S1 and DS-S2 are still ACTIVE. Now settle market1. DS-S1 is set to DEACTIVATED and DS-S2 is still active. (0082-ETHD-021) +8. Create a market to use an ethereum oracle for termination configured such that - it expects a boolean value True for termination and the contract supplying the termination value is polled every 5 seconds. Set the contract to return False for termination. The market is not terminated. The data source is still ACTIVE and no BUS_EVENT_TYPE_ORACLE_DATA events for that ethereum oracle spec are emitted. Then set the contract to return True for termination. The market is terminated and an event for BUS_EVENT_TYPE_ORACLE_DATA for the ethereum oracle data spec is received and the ethereum oracle is set to DEACTIVATED. (0082-ETHD-022) +9. One oracle data event is emitted for data that matches each data source - Create 2 markets with ethereum oracle settlement specs that use the same settlement key such that - the first settlement spec expects settlement data to be greater than 100 and the second expects greater than 200. Now send it a settlement data of 300. One single event BUS_EVENT_TYPE_ORACLE_DATA for the settlement data is emitted for each matching ethereum oracle data sources i.e. in this case, two oracle data events will be emitted - one for each settlement data source. Both markets are settled and both the data sources are DEACTIVATED. (0082-ETHD-023) +10. Different oracle data events for multiple spec id's with non matching filter values - Create 2 markets with ethereum oracle settlement specs that use the same settlement key such that - the first settlement spec expects settlement data to be greater than 100 and the second expects greater than 200. Now send it a settlement data of 50. NO data events for BUS_EVENT_TYPE_ORACLE_DATA. Send settlement data of 150. One single event BUS_EVENT_TYPE_ORACLE_DATA emitted for the settlement data is emitted with matching ethereum oracle data spec for Market1, market1 is settled and the data source is set to DEACTIVATED. Send settlement data of 250. One single event BUS_EVENT_TYPE_ORACLE_DATA emitted for the settlement data is emitted with matching ethereum oracle data spec for Market2, Market2 is settled and the data source is set to DEACTIVATED. (0082-ETHD-024) +11. Network wide contract error should be reported via oracle data events (0082-ETHD-025) +12. Different contracts on different markets - Create 2 markets with ethereum oracle settlement data sources containing different contract addresses and with *different* settlement keys, but with all conditions and filters the same. Confirm that sending settlement value that passes the market spec filter only settles one market. (0082-ETHD-050) +13. Different contracts on different markets - Create 2 markets with ethereum oracle settlement data sources containing different contract addresses and with the *same* settlement keys, but with all conditions and filters the same. Confirm that sending settlement value that passes the market spec filter only settles one market. (0082-ETHD-051) +14. Phase 2 - System needs to emit an error via the TX RESULT event if the data source does NOT emit events in a timely fashion. e.g. if the data source is expected to emit events every 5 minutes and if we do not receive 3 consecutive events , then raise an error via the TX RESULT event (0082-PLAZZO-003) +15. Phase 2 - Define behaviour for missed events / missed scheduled smart contract calls - e.g. if an oracle data source is scheduled to emit events every 10 minutes and we miss 5 events because of protocol upgrade or some other outage - then do we catch up those events or skip those events ? Maybe this is defined in the oracle data source definition (0082-PLAZZO-004) +16. Create a perpetual futures market which uses Ethereum oracle reads/events contract data for settlement payment schedule from; the trigger for the countdown to the first funding payment being publication of a valid value for the index price. The index price must not be available at the time the market is created and leaves opening auction; it must only become available sometime after. The aim is to test futures markets for underlyings that don't trade *yet* be where there is an agreed oracle source that will start publishing the price *once* they begin trading. (0082-ETHD-052). + +### New Network parameters + +1. New network parameter - ethereum.oracles.enabled. Setting this to 0 should NOT allow market creation and market updates with ethereum oracles. (0082-ETHD-028) +2. New network parameter - ethereum.oracles.enabled. Setting this to 1 should allow market creation amd market updates with ethereum oracles. (0082-ETHD-029) + +### Negative Tests + +1. Set up a new data source with invalid contract address - should fail validations (Phase 2 - listening for invalid event ) (0082-ETHD-030) +2. Phase 2 - Set up a data source for listening to a particular event sent at a frequency of 2 secs. The oracle data source stops emitting events after emitting a couple of events. Raise and error via the TX RESULT event if 5 consecutive events are missed - need to ratify / expand on this (0082-PLAZZO-005) +3. Phase 2 - Create an oracle source listening for a particular event and specify an incorrect ABI format for the event. Proposal should fail validation and should return an error (0082-PLAZZO-006) +4. Create an oracle that calls a read method of a smart contract and specify an incorrect ABI format for the event. Proposal should fail validation and should return an error (0082-ETHD-034) +5. Set up a network such that different vega nodes receive conflicting results from an identical ethereum contract call. Attempt to settle a market using that contract. Observe that if there are not enough nodes voting in agreement, the market is not settled (0082-ETHD-035) + +### API + +1. Ability to query data source specs defined for ethereum oracle sources, for settlement and termination, via an API endpoint (REST, gRPC and graphQL) - filters should be available for data source - internal OR external, status - Active / Inactive / Expired (0082-ETHD-038) +2. Ability to query historic data sent by an ethereum oracle source, for settlement and termination, and processed by a market in vega network (0082-ETHD-039) + +### Checkpoints + +1. Oracle data sources should be stored in checkpoints and should be restored when restarting a network from checkpoints. Therefore enacted markets with termination or settlement ethereum data sources are able to terminate and settle correctly post restart. (0082-ETHD-040) +2. Ensure that any ethereum oracle events that were generated during network downtime are correctly processed as soon as the network is restored and operational. This means that any termination or settlement actions that would of occurred during downtime are immediately actioned when network is up and we ensure they are processed in sequenced that they were received by the core polling. (0082-ETHD-041) + +### Snapshots + +1. Oracle data sources linked to markets should be stored on snapshots and should be able to be restored from snapshots. The states of the oracle data sources should be maintained across any markets where they are linked to ethereum data sources. (0082-ETHD-042) + +### Protocol Upgrade + +1. Have a network running with a couple of futures markets with a mix of internal, external, open and ethereum oracles. Perform a protocol upgrade. Once the network is up , the state of the various data sources should be the same as before the protocol upgrade (0082-ETHD-043) +2. Have a network running with a couple of perpetual markets with a mix of internal, external, open and ethereum oracles. Perform a protocol upgrade. Once the network is up , the state of the various data sources should be the same as before the protocol upgrade (0082-ETHD-044) +3. Create a futures market with an ethereum oracle for termination such that it polls at a specific time. Perform a protocol upgrade such that the termination triggers in the middle of the protocol upgrade. Once the network is up , the termination should be triggered and the market should be terminated. (0082-ETHD-045) +4. Create a futures market with an ethereum oracle for settlement such that it polls at a specific time. Perform a protocol upgrade such that the settlement price matching the filters is triggered in the middle of the protocol upgrade. Once the network is up , the settlement should be triggered and the market should be terminated. (0082-ETHD-047) +5. Create a perpetual market with an ethereum oracle for settlement such that it polls at a specific time. Perform a protocol upgrade such that the settlement price matching the filters is triggered in the middle of the protocol upgrade. Once the network is up , the settlement should be triggered and the market should be terminated. (0082-ETHD-048) +6. Ensure that markets with ethereum termination and settlement data sources continue to successfully terminate and settle markets after the protocol upgrade. (0082-ETHD-049) diff --git a/protocol/0083-RFPR-on_chain_referral_program.md b/protocol/0083-RFPR-on_chain_referral_program.md new file mode 100644 index 000000000..43ac39928 --- /dev/null +++ b/protocol/0083-RFPR-on_chain_referral_program.md @@ -0,0 +1,530 @@ +# On Chain Referral Program Specification + +## Summary + +The aim of the on-chain referral program is to allow users of the protocol to incentivise users and community members to refer new traders by voting to provide benefits for referrers and/or referees. + +A party will be able to [create a referral code](#creating-a-referral-set) and share this code with referees. Referees who [apply the code](#applying-a-referral-code) will be added to the referrer's "referral set". + +Whilst a referral program is active, the following benefits may be available to members of a referral set: + +- a **referrer** may receive a proportion of all referee taker fees as a **reward**. +- a **referee** may be eligible for a **discount** on any taker fees they incur. + +Providing a party has been associated with a referral set for long enough, they will become eligible for greater benefits as their referral sets running taker volume increases. + +Referrers will also have the ability to increase the proportion of referee taker fees they receive by staking governance tokens ($VEGA). + +To create an emphasis on community, collaboration, and competition. Referrers will be able to create a team from their referral set. Teams will have fields which allow them to be visible on leaderboards and to compete for team based rewards. + +Note, if a referee wants to compete as a member of a different team, they are able to move between teams by "reapplying" a referral code. However, their referral rewards will still be computed based their original referral set performance and, thus, paid out to the their original referrer. + +## Glossary + +- `referrer`: a party who has generated a referral code for a referral set +- `referee`: a party who has applied a referral code to join a referral set +- `referral_set`: a group comprised of a single referrer and all their referees +- `team`: a group created from a `referral_set` which was designated as a team. A team will be visible on leaderboards and eligible for team rewards. + +## Network Parameters + +- `referralProgram.maxReferralTiers` - limits the maximum number of [benefit tiers](#governance-proposals) and [staking tiers](#governance-proposals) which can be specified as part of a referral program +- `referralProgram.maxReferralRewardFactor` - limits the maximum reward factor which can be specified as part of a referral program +- `referralProgram.maxReferralDiscountFactor` - limits the maximum discount factor which can be specified as part of a referral program governance proposal +- `referralProgram.maxReferralRewardProportion` - limits the proportion (`referee_reward_factor` * `referee_reward_multiplier`) of referee taker fees which can be given to the referrer. +- `referralProgram.maxPartyNotionalVolumeByQuantumPerEpoch` - limits the notional volume in quantum units which is eligible each epoch for referral program mechanisms +- `referralProgram.minStakedVegaTokens` - limits referral code generation to parties staking at least this number of tokens + +Note, if any of the above mentioned network parameters are updated whilst a referral program is active, the active program will not be affected in any way even if the active program breaches the new network parameter value. The new network parameter value however will be checked on any future [referral program proposals](#governance-proposals). + +If the community wish to update the referral program limits **and** apply these to the existing program, they can do so by first updating the network parameters and then submitting a proposal to update the program (adhering to the new limits). + +## Governance Proposals + +Enabling or changing the terms of the on-chain referral program can be proposed via governance. As part of the proposal, the proposer specifies the following fields: + +- `benefit_tiers`: a list of dictionaries (with the below fields) defining the reward and discount factors from referrals + - `minimum_running_notional_taker_volume`: the required [`referral_set_running_notional_taker_volume`](#referral-set-volumes) in quantum units for parties to access this tier + - `minimum_epochs`: the required number of epochs a referee must have been in a referral set to access this tier + - `referral_reward_factor`: the proportion of the referee's taker fees to be rewarded to the referrer + - `referral_discount_factor`: the proportion of the referee's taker fees to be discounted +- `staking_tiers`: a list of dictionaries (with the below fields) defining the multipliers from staking + - `minimum_staked_tokens`: the required number of governance tokens ($VEGA) a referrer must be staking to receive the multiplier + - `referral_reward_multiplier`: the multiplier applied to the referral_reward_factor when calculating referral rewards due to the referrer. +- `end_of_program_timestamp`: the timestamp after which when the current epoch ends, the program will be closed and benefits will be disabled +- `window_length`: the number of epochs over which to evaluate a referral set's running notional taker volume + +```protobuf +message UpdateReferralProgram{ + changes: ReferralProgram{ + benefit_tiers: [ + { + "minimum_running_notional_taker_volume": 10000, + "minimum_epochs": 1, + "referral_reward_factor": 0.001, + "referral_discount_factor": 0.001, + }, + { + "minimum_running_notional_taker_volume": 20000, + "minimum_epochs": 7, + "referral_reward_factor": 0.005, + "referral_discount_factor": 0.005, + }, + { + "minimum_running_notional_taker_volume": 30000, + "minimum_epochs": 31, + "referral_reward_factor": 0.010, + "referral_discount_factor": 0.010, + }, + ], + staking_tiers: [ + { + "minimum_staked_tokens": 100, + "referral_reward_multiplier": 1, + }, + { + "minimum_staked_tokens": 1000, + "referral_reward_multiplier": 2, + }. + { + "minimum_staked_tokens": 1000, + "referral_reward_multiplier": 2, + } + ], + end_of_program_timestamp: 123456789, + window_length: 7, + } +} +``` + +When submitting a referral program proposal through governance the following conditions apply: + +- a proposer cannot set an `end_of_program_timestamp` less than the proposals `enactment_time`. +- the number of tiers in `benefit_tiers` must be less than or equal to the network parameter `referralProgram.maxReferralTiers`. +- all `minimum_running_notional_taker_volume` values must be an integer value strictly greater than `0`. +- all `minimum_epochs` values must be an integer strictly greater than 0 +- all `referral_reward_factor` values must be greater than `0` and less than or equal to the network parameter `referralProgram.maxReferralRewardFactor`. +- the number of tiers in `staking_tiers` must be less than or equal to the network parameter `referralProgram.maxReferralTiers`. +- all `minimum_staked_tokens` values must be an integer value strictly greater than `0`. +- all `referral_reward_multiplier` values must be a float value greater than or equal to `1`. +- all `referral_discount_factor` values must be greater than `0` and be less than or equal to the network parameter `referralProgram.maxReferralDiscountFactor`. +- `window_length` must be an integer strictly greater than zero. + +The referral program will start the epoch after the `enactment_timestamp` is reached. + +## Referral program lifecycle + +After a referral program [proposal](#governance-proposals) is validated and accepted by the network, the network referral program is created / updated and can be one of the following states. + +| State | Benefits Enabled | Condition for entry | Condition for exit | +| -------------------- | ---------------- | --------------------------------------------------------- | ----------------------------------------------------------------- | +| Inactive | No | No proposal ever submitted, or previous proposal ended | New governance proposal submitted to the network | +| Proposed | No | Governance proposal valid and accepted | Governance proposal voting period ends (or proposal is invalid) | +| Pending | No | Governance vote passes | End of epoch after network reaches proposal `enactment_timestamp` | +| Active | Yes | Previously `pending` | End of epoch after network reaches proposal `end_of_program_timestamp` | + +## Referral set mechanics + +A referral set is comprised of a referrer and all the referees who have applied the associated referral code. There can only ever be one referrer per referral set but the number of referees is unlimited. Referees can move between referral sets by applying a new referral code. + +### Creating a referral set + +To create a new referral set and become a referrer, a party must fulfil the following criteria: + +- party must not currently be a **referrer** +- party must not currently be a **referee** +- party must be staking at least `referralProgram.minStakedVegaTokens` tokens + +The staking requirement is constant. If a referrer un-stakes enough tokens to fall below the requirement, they and their referees will immediately no longer be eligible for referral benefits. If the referrer re-stakes enough tokens to fulfil the staking requirement, they and their referees will become eligible for referral benefits **at the start of the next epoch**. Note, for the case where a party does not re-stake, the protocol will still allow referees to "move" referral sets by [applying](#applying-a-referral-code) a new referral code as normal. + +To create a referral set and generate a referral code, the party must submit a signed `CreateReferralSet` transaction. When creating a referral set, a party can optionally designate it to as a [team](#glossary) and provide additional team details. When designated as a team a separate team will be created in addition to the referral set. The team will be visible on leaderboards and eligible for team rewards. A `CreateReferralSet` transaction has the following fields: + +- `is_team`: a boolean defining whether the referral set should be designated as a team (and a team created from it) +- `team_details`: an optional dictionary defining the teams details (non-optional if `is_team` is `True`) + - `name`: mandatory string team name + - `team_url`: optional string of a link to a team forum, discord, etc. (defaults to empty string / none-type) + - `avatar_url`: optional string of a link to an image to be used as the teams avatar (defaults to empty string / none-type) + - `closed`: optional boolean, defines whether a team is closed to members not specified in the `allow_list`, i.e. when `True` the team is joinable by invite only. + - `allow_list`: optional list of public keys which defines which parties are allowed to join if the team is joinable by invite only. + +*Example: if party wants to create a simple referral set.* + +```protobuf +message CreateReferralSet{ + is_team: False + team_details: None +} +``` + +*Example: if party wants to create a referral set and team.* + +```protobuf +message CreateReferralSet{ + is_team: True + team_details: { + name: "VegaRocks", + team_url: "https://discord.com/channels/vegarocks", + avatar_url: "https://vega-rocks/logo-360x360.jpg", + closed: False, + } +} +``` + +*Example: if party wants to create a referral set and team.* + +```protobuf +message CreateReferralSet{ + is_team: True + team_details: { + name: "VegaRocks", + team_url: "https://discord.com/channels/vegarocks", + avatar_url: "https://vega-rocks/logo-360x360.jpg", + allow_list: ["publ1ck3y001", "publ1ck3y002", publ1ck3y003] + closed: False, + } +} +``` + +When the network receives a valid `CreateReferralSet` transaction, the network will create a referral set with the referral set `id` as the referral code. Any future parties who [apply](#applying-a-referral-code) the referral code will be added to the referral set. + +### Updating a referral set + +There are two cases where a referrer may want to update their referral set: + +- they want to designate their `referral_set` as a team +- their `referral_set` is already designated as a team and they want to update their `team_details`. + +To update a referral set the party submit a signed `UpdateReferralSet` transaction. For the transaction to be valid, the party must be the referrer associated with the referral set. An `UpdateReferralSet` transaction must have the following fields. + +- `id`: id of the referral set to update +- `is_team`: a boolean defining whether the party should made into a team visible on leaderboards +- `team_details`: an optional dictionary defining the team + - `name`: optional string team name + - `team_url`: optional string of a link to a team forum, discord, etc. + - `avatar_url`: optional string of a link to an image to be used as the teams avatar + - `closed`: optional boolean, defines whether a team is closed to members not specified in the `allow_list`, i.e. when `True` the team can be joined by invite only. + - `allow_list`: optional list of public keys which defines which parties are allowed to join if the team is joinable by invite only. + +```protobuf +message UpdateReferralSet{ + id: "mYr3f3rra15et1d" + is_team: True + team_details: { + name: "VegaRocks", + team_url: "https://discord.com/channels/vegarocks" + avatar_url: "https://vega-rocks/logo-360x360.jpg" + closed: True, +} +``` + +If a referral set is currently designated as a team, a referrer should be able to "close" their team to any new members not specified in the `allow_list` by setting the `closed` field to `True`. + +Note, if a referrer updates `closed` or the `allow_list` defining the parties which are allowed to join the team, the updated list is only used to validate attempts to join the team, i.e. existing members are not removed from the team. + +If a referral set is currently designated as a team, a party is able to effectively "disband" a team by updating their referral set and setting their `is_team` value to `False`. Note a team should only be "disbanded" and removed from leaderboards at the end of the current epoch after rewards have been distributed. + +### Applying a referral code + +To apply a referral code and become a referee, a party must fulfil the following criteria: + +- party must not currently be a **referrer** +- party must not currently be a **referee** associated with a valid referral set (Note: for a set to be valid, the sets referrer must be meeting the staking requirement.) + +To become a referee, a referee must submit a signed `ApplyReferralCode` transaction with the following fields: + +- `referral_code`: the referral code they wish to apply + +```protobuf +message ApplyReferralCode{ + id: "mYr3f3rra1c0d3" +} +``` + +If a party is not currently a referee, they must immediately be added to the referral set and [benefit factors and reward multipliers updated](#setting-benefit-factors-and-reward-multipliers) accordingly. If a team exists for this referral set, they will also be added to that team. + + +If a party is already a referee, and submits another `ApplyReferralCode` transaction, the transaction will be rejected unless the referrer of the current referral set is not meeting the [staking requirement](#creating-a-referral-set). In this case, the party will be removed from the current referral set and added to the new referral set. + + +### Joining a team + +If a party is already a referee in a valid set, to join or move between teams, a party can submit a `JoinTeam` transaction with the following fields: + +- `id`: the id of the team they wish to join + +```protobuf +message JoinTeam{ + id: "mYr3f3rra1c0d3" +} +``` + +The party will be added to the team providing the team is not `closed` (anyone can join) or the team is `closed` (joinable by invite only) and the party is specified in the `allow_list`. + +### Party volumes + +The network must now track the cumulative notional volume of taker trades for each party in an epoch, call this value `party_epoch_notional_taker_volume`. Note, trades generated by auction uncrossing are not counted. Each time a eligible trade is generated, the network should increment a parties `party_epoch_notional_taker_volume` by the quantum notional volume of the trade. + +```pseudo +party_epoch_notional_taker_volume = party_epoch_notional_taker_volume + (trade_price * trade_size / settlement_asset_quantum) +``` + +At the end of an epoch, the `party_epoch_notional_taker_volume` is stored by the network and each parties `party_epoch_notional_taker_volume` is reset to `0` ready for the next epoch. + +### Referral set volumes + +At the end of an epoch, for each referral set, a `referral_set_epoch_notional_taker_volume` is calculated by summing the `party_epoch_notional_taker_volume` of each party in the referral set (include both referrers and referees). The amount a party can contribute to their referral set is capped by the network parameter `referralProgram.maxPartyNotionalVolumeByQuantumPerEpoch`. Note this cap is not applied directly to `party_epoch_notional_taker_volume` in case the network parameter is updated during an epoch. + +```pseudo +referral_set_epoch_notional_taker_volume = sum[min(party_epoch_notional_taker_volume, referralProgram.maxPartyNotionalVolumeByQuantumPerEpoch) for each party in team] +``` + +After the values are calculated, the `referral_set_epoch_notional_taker_volume` is stored by the network. + +The network can then calculate the set's `referral_set_running_notional_taker_volume` by summing the set's `referral_set_epoch_notional_taker_volume` values over the last n epochs where n is the `window_length` set in the [governance proposal](#governance-proposals). + +## Benefit mechanics + +### Setting benefit factors and reward multipliers + +Whilst a referral program is active, at the start of an epoch (after pending `ApplyReferralCode` transactions have been processed) the network must set the `referral_reward_factor` and `referral_discount_factor` for each referee. + +Note, when setting a referee's benefit factors we compare a sets `referral_set_running_notional_taker_volume` to a `minimum_running_notional_taker_volume` value. To prevent parties self-referring and moving teams, this `referral_set_running_notional_taker_volume` is always the value of the referee's original referral set. + +#### Setting the referral reward factor + +The `referral_reward_factor` should be set by identifying the "highest" benefit tier where the following conditions are fulfilled. + +- `referral_set_running_notional_taker_volume` of the referee's **original** referral set is greater than or equal to the tier's `minimum_running_notional_taker_volume`. + +The referee's `referral_reward_factor` is then set to the `referral_reward_factor` defined in the selected benefit tier. + +Note the **original** referrer is defined as the team of the referrer associated with the referee. See section [applying a referral code](#applying-a-referral-code) for more detail. + +#### Setting the referral discount factor + +The `referral_discount_factor` should be set by identifying the "highest" benefit tier where **BOTH** the following conditions are fulfilled. + +- `referral_set_running_notional_taker_volume` of the referee's **original** referral set is greater than or equal to the tier's `minimum_running_notional_taker_volume`. +- the referee has been a associated with the referral set for at least the tier's `minimum_epochs`. + +The referee's `referral_discount_factor` is then set to the `referral_discount_factor` defined in the selected benefit tier. + +Note the **original** referrer is defined as the team of the referrer associated with the referee. See section [applying a referral code](#applying-a-referral-code) for more detail. + +### Setting the referral reward multiplier + +The `referral_reward_multiplier` should be set by identifying the "highest" staking tier where the following conditions are fulfilled. + +- the referee's **original** referrer is staking greater than or equal to the tier's `minimum_staked_tokens`. + +The referee's `referral_reward_multiplier` is then set to the `referral_reward_multiplier` defined in the selected benefit tier. + +Note the **original** referrer is defined as the team of the referrer associated with the referee. See section [applying a referral code](#applying-a-referral-code) for more detail. + +#### Example + +```pseudo +Given: + benefit_tiers: [ + { + "minimum_running_notional_taker_volume": 10000, + "minimum_epochs": 0, + "referral_reward_factor": 0.001, + "referral_discount_factor": 0.001, + }, + { + "minimum_running_notional_taker_volume": 20000, + "minimum_epochs": 7, + "referral_reward_factor": 0.005, + "referral_discount_factor": 0.005, + }, + { + "minimum_running_notional_taker_volume": 30000, + "minimum_epochs": 31, + "referral_reward_factor": 0.010, + "referral_discount_factor": 0.010, + }, + ] + staking_tiers: [ + { + "minimum_staked_tokens": 100, + "referral_reward_multiplier": 1, + }, + { + "minimum_staked_tokens": 1000, + "referral_reward_multiplier": 2, + }. + { + "minimum_staked_tokens": 1000, + "referral_reward_multiplier": 2, + } + ] + +And: + referrer_staked_tokens=1023 + referral_set_running_notional_taker_volume=22353 + party_epochs_in_referral_set=4 + +Then: + referral_reward_factor=0.005 + referral_discount_factor=0.001 + referral_reward_multiplier=0.001 +``` + +These benefit factors are then fixed for the duration of the next epoch. + +### Applying benefit factors + +Referral program benefit factors are applied by modifying [the fees](./0029-FEES-fees.md) paid by a party (either during continuous trading or on auction exit). + + +## APIs + +The Parties API should now return a list of all **parties** (which can be filtered by party `id`) with the following additional information: + +- current `id` of the referral set the party is currently associated with +- current `id` of the team the party is currently associated with +- current `epochs_in_referral_set` +- current `party_epoch_notional_taker_volume` +- current `referral_reward_factor` +- current `referral_discount_factor` +- for each asset, the total referral rewards generated by the parties taker fees +- for each asset, the total referral discounts applied to the parties taker fees + +The ReferralSet API should now expose a list of all **referral sets** (which can be filtered by referral set `id`) with the following information: + +- the set's founding **referrer** +- the set's **referees** +- current `referral_set_running_notional_taker_volume` +- current `referral_reward_factor` applied to referee taker fees +- current **maximum possible** `referral_discount_factor` applied to referee taker fees +- for each asset, the total referral rewards paid to the referrer of the referral set +- for each asset, the total referral rewards generated by all referee taker fees +- for each asset, the total referral discounts applied to all referee taker fees +- whether the referral set has been designated as a team +- any `team_details` if the referral set has been designated as a team. + +The Trades API should now expose a list of all **trades** (which can be filtered by trade `id`) with the following additional information: + +- Referral program rewards + - `infrastructure_fee_referral_reward` + - `liquidity_fee_referral_reward` + - `maker_fee_referral_reward` +- Referral program discounts + - `infrastructure_fee_referral_discount` + - `liquidity_fee_referral_discount` + - `maker_fee_referral_discount` +- Referral program totals + - `total_referral_reward` + - `total_referral_discount` + +The Estimate Fees API should now calculate the following additional information: + +- Expected referral program rewards + - `infrastructure_fee_referral_reward` + - `liquidity_fee_referral_reward` + - `maker_fee_referral_reward` +- Expected referral program discounts + - `infrastructure_fee_referral_discount` + - `liquidity_fee_referral_discount` + - `maker_fee_referral_discount` +- Expected referral program totals + - `total_referral_reward` + - `total_referral_discount` + + +## Acceptance Criteria + +### Governance Proposals + +1. If an `UpdateReferralProgram` proposal does not fulfil one or more of the following conditions, the proposal should be `STATUS_REJECTED`: + - the `end_of_program_timestamp` must be greater than or equal to the proposal's `enactment_time` (0083-RFPR-001). + - the number of tiers in `benefit_tiers` must be less than or equal to the network parameter `referralProgram.maxReferralTiers` (0083-RFPR-002). + - all `minimum_running_notional_taker_volume` values must be an integer strictly greater than 0 (0083-RFPR-051). + - all `minimum_epochs_in_team` values must be an integer strictly greater than 0 (0083-RFPR-003). + - all `referral_reward_factor` values must be greater than `0` and less than or equal to the network parameter `referralProgram.maxReferralRewardFactor` (0083-RFPR-004). + - all `referral_discount_factor` values must be greater than `0` and be less than or equal to the network parameter `referralProgram.maxReferralDiscountFactor` (0083-RFPR-005). + - the `window_length` must be an integer strictly greater than zero (0083-RFPR-006). +1. A referral program should be started the first epoch change after the `enactment_datetime` is reached (0083-RFPR-007). +1. A referral program should be closed the first epoch change after the `end_of_program_timestamp` is reached (0083-RFPR-008). +1. If a referral program is already active and a proposal `enactment_datetime` is reached, the referral program is updated at the next epoch change. + - Propose program A with `enactment_timestamp` 1st Jan and `end_of_program_timestamp` 31st Dec (0083-RFPR-009). + - Proposal for program A accepted and begins first epoch after 1st Jan (0083-RFPR-010). + - Propose program B with `enactment_timestamp` 1st June and `end_of_program_timestamp` 31st Aug (0083-RFPR-011). + - Proposal for program B accepted and overrides program A the first epoch after 1st June (0083-RFPR-012). + - Program is closed first epoch after 31st Aug, there should be no active proposals (0083-RFPR-013). +1. Updating any of the following network parameters whilst there is an active referral program will not modify or cancel the active program in any way. The updated parameters will however be used to validate future referral program proposals. + - `referralProgram.maxReferralTiers` (0083-RFPR-041) + - `referralProgram.maxReferralRewardFactor` (0083-RFPR-042) + - `referralProgram.maxReferralDiscountFactor` (0083-RFPR-043) + +### Referral set mechanics + +#### Creating a referral set + +1. If a party **is not** currently a referrer, the party can **create** a referral set, by submitting a signed `CreateReferralSet` transaction (0083-RFPR-014). +1. If one or more of the following conditions are not met, any `CreateReferralCode` transaction should be rejected. + - party must not currently be a **referrer**.CreateReferralSet (0083-RFPR-015). + - party must not currently be a **referee** (0083-RFPR-016). + - party must be staking at least `referralProgram.minStakedVegaTokens` tokens (0083-RFPR-017). +1. If a referrer removes sufficient stake to not meet the required tokens, the referral set should not be eligible for the following referral benefits: + - the referrer should not be rewarded for any referee taker fees (0083-RFPR-018). + - all referees should not receive any discount on their taker fees (0083-RFPR-019). +1. If the referrer of a referral set currently not eligible for benefits re-stakes enough tokens, their team will become eligible for benefits from the start of the next epoch (0083-RFPR-020). +1. When creating a referral set a party should be able to designate it as a team. If they do, `team_details` and all nested fields are mandatory (0083-RFPR-021). + +#### Updating a referral set + +1. If a party is currently the referrer of a referral set from which a team **has not** yet been created, the party can **create** a team by submitting a signed `UpdateReferralSet` transaction and setting `is_team=True` (0083-RFPR-022). +1. If a party is currently the referrer of a referral set from which a team **has** already been created, the party can **update** a team by submitting a signed `UpdateReferralSet` transaction specifying the fields they want to update (0083-RFPR-023). +1. If a party submits an `UpdateReferralSet` transaction for a referral set they are not the referrer off, the transaction should be rejected (0083-RFPR-024). +1. If a referrer updates the `allow_list` associated with the team, existing members who are no longer on the allow_list should **not** be removed from the team (0083-RFPR-067). + +#### Applying a referral code + +1. If a party **is not** currently a **referee**, if they submit a signed `ApplyReferralCode` transaction then: (0083-RFPR-059) + + - the party **will** be added to the associated referral set. + - the party **will** be added to the associated team (if one exists) and the team is not `closed` or the team is `closed` and the party is allowed by the `allow_list`. + + +1. If a party **is** currently a **referee** (and the referrer **is not** meeting the staking requirement), if they submit a signed `ApplyReferralCode` transaction then: (0083-RFPR-060). + + - the party **will** be added to the associated referral set. + - the party **will** be added to the associated team (if one exists) and the team is not `closed` or the team is `closed` and the party is allowed by the `allow_list`. + +1. If a party **is** currently a **referee** (and the referrer **is** meeting the staking requirement), if they submit a signed `ApplyReferralCode` transaction then the transaction will be rejected. (0083-RFPR-052). + +1. If a party submits an `ApplyReferralCode` transaction, a team exists for the specified `id` and the team is not `closed` then the party **should** be added to the team. (0083-RFPR-061). +1. If a party submits an `ApplyReferralCode` transaction, a team exists for the specified `id` and the team is `closed` then the party **will** be added to the team if **they are** specified in the `allow_list`. (0083-RFPR-062). +1. If a party submits an `ApplyReferralCode` transaction, a team exists for the specified `id` and the team is `closed` then the party **will not** be added to the team if **they are not** specified in the `allow_list`. (0083-RFPR-063). +1. An `ApplyReferralCode` transaction should be rejected if the party is a **referrer** (0083-RFPR-029). + +### Joining a team + +1. If a party submits a `JoinTeam` transaction and the team is not `closed` then the party **should** be added to the team. (0083-RFPR-064). +1. If a party submits a `JoinTeam` transaction and the team is `closed` then the party **will** be added to the team if **they are** specified in the `allow_list`. (0083-RFPR-065). +1. If a party submits a `JoinTeam` transaction and the team is `closed` then the party **will not** be added to the team if **they are not** specified in the `allow_list`. (0083-RFPR-066). + +#### Epoch and running volumes + +1. Each trade in which a party is the "maker" **should not** increment the taker parties `party_epoch_notional_taker_volume` by the volume of the trade (expressed in quantum units) (0083-RFPR-048). +1. Each trade in which a party is the "taker" **should** increment the taker parties `party_epoch_notional_taker_volume` by the volume of the trade (expressed in quantum units) (0083-RFPR-031). +1. A trade generated during auction uncrossing should not contribute to either parties `party_epoch_notional_taker_volume` (0083-RFPR-032). +1. A trader is the taker of two prices in separate markets using settlement assets with different quantum values. Each trades value should be scaled correctly by the assets quantum and added to the parties `party_epoch_notional_taker_volume` (0083-RFPR-049). +1. At the end of the epoch, the `referral_set_epoch_notional_taker_volume` should be correctly calculated by summing each team members `party_epoch_notional_taker_volume` (0083-RFPR-033). +1. A party cannot contribute more than the current network parameter `referralProgram.maxPartyNotionalVolumeByQuantumPerEpoch` to their sets `referral_set_epoch_notional_taker_volume` (0083-RFPR-034). +1. If `referralProgram.maxPartyNotionalVolumeByQuantumPerEpoch` is updated during an epoch, the new value should be used to cap party contributions to referral sets **at the end of the epoch** (i.e. a parties individual `party_epoch_notional_taker_volume` should not be capped during the epoch) (0083-RFPR-050). +1. A referral sets `referral_set_running_notional_taker_volume` is calculated as the sum of all `referral_set_epoch_notional_taker_volumes` over the last `epoch_window` epochs (0083-RFPR-035). + +### Benefit Mechanics + +#### Setting benefit factors and reward multipliers + +1. At the start of an epoch, each referees `referral_reward_factor` and `referral_discount_factor` is reevaluated and fixed for the epoch (0083-RFPR-036). +1. At the start of an epoch, a referees `referral_reward_factor` is set equal to the factor in the highest benefit tier they qualify for (0083-RFPR-037). +1. At the start of an epoch, a referees `referral_discount_factor` is set equal to the factor in the highest benefit tier they qualify for (0083-RFPR-038). +1. At the start of an epoch, a referees `referral_reward_multiplier` is set equal to the multiplier in the highest staking tier they qualify for (0083-RFPR-046). +1. If when evaluating the tier to set the `referral_reward_factor`, a referee does not qualify for any tier, their `referral_reward_factor` is set to `0` (0083-RFPR-039). +1. If when evaluating the tier to set the `referral_discount_factor`, a referee does not qualify for any tier, their `referral_reward_factor` is set to `0` (0083-RFPR-040). +1. If when evaluating the tier to set the `referral_reward_multiplier`, a referee does not qualify for any tier, their `referral_reward_multiplier` is set to `1` (0083-RFPR-047). diff --git a/protocol/0084-VDPR-volume_discount_program.md b/protocol/0084-VDPR-volume_discount_program.md new file mode 100644 index 000000000..efbd1ef26 --- /dev/null +++ b/protocol/0084-VDPR-volume_discount_program.md @@ -0,0 +1,148 @@ +# Volume Discount Program + +The volume discount program provides tiered discounts on taker fees to traders. A trader accesses greater discounts by increasing their taker volume over a specified number of epochs. + +## Network parameters + +- `volumeDiscountProgram.maxBenefitTiers` - limits the maximum number of [benefit tiers](#governance-proposals) which can be specified as part of a volume discount program +- `volumeDiscountProgram.maxVolumeDiscountFactor` - limits the maximum volume discount factor which can be specified as part of a volume discount program + +Note, if any of the above mentioned network parameters are updated whilst a volume discount program is active, the active program will not be affected in any way even if the active program breaches the new network parameter value. The new network parameter value however will be checked on any future [volume discount program proposals](#governance-proposals). + +If the community wish to update the volume discount program limits **and** apply these to the existing program, they can do so by first updating the network parameters and then submitting a proposal to update the program (adhering to the new limits). + +## Governance proposals + +Enabling or changing the terms of the volume discount program can be proposed via governance. As part of the proposal, the proposer specifies the following fields: + +- `benefit_tiers`: a list of dictionaries with the following fields + - `minimum_party_running_notional_taker_volume`: the required `party_running_notional_taker_volume` in quantum units for a party to access this tier + - `volume_discount_factor`: the proportion of the referees taker fees to be rewarded to the referrer +- `end_of_program_timestamp`: the timestamp after which when the current epoch ends, the program will become inactive and benefits will be disabled. If this field is empty, the program runs indefinitely. +- `window_length`: the number of epochs over which to evaluate a parties notional running volume + +```protobuf +message UpdateVolumeDiscountProgram{ + changes: VolumeDiscountProgram{ + benefit_tiers: [ + { + "minimum_party_running_notional_taker_volume": 1000, + "volume_discount_factor": 0.001, + }, + { + "minimum_party_running_notional_taker_volume": 20000, + "volume_discount_factor": 0.002, + }, + { + "minimum_party_running_notional_taker_volume": 30000, + "volume_discount_factor": 0.003, + }, + ], + end_of_program_timestamp: 123456789, + window_length: 7, + } +} +``` + +When submitting a volume discount program proposal through governance the following conditions apply: + +- a proposer cannot set an `end_of_program_timestamp` less than the proposals `enactment_time`. +- the number of tiers in `benefit_tiers` must be less than or equal to the network parameter `volumeDiscountProgram.maxBenefitTiers`. +- all `minimum_party_running_notional_taker_volume` values must be an integer value strictly greater than `0`. +- all `volume_discount_factor` values must be greater than or equal to `0` and less than or equal to the network parameter `volumeDiscountProgram.maxVolumeDiscountFactor`. +- `window_length` must be an integer strictly greater than zero. + +The volume discount program will start the epoch after the `enactment_timestamp` is reached. + +## Volume discount program lifecycle + +After a volume discount program [proposal](#governance-proposals) is validated and accepted by the network, the network volume discount program is created / updated and can be one of the following states. + +| Status | Benefits Enabled | Condition for entry | Condition for exit | +| -------------------- | ---------------- | --------------------------------------------------------- | ----------------------------------------------------------------- | +| Inactive | No | No proposal ever submitted, or previous proposal ended | New governance proposal submitted to the network | +| Proposed | No | Governance proposal valid and accepted | Governance proposal voting period ends (or proposal is invalid) | +| Pending | No | Governance vote passes | End of epoch after network reaches proposal `enactment_timestamp` | +| Active | Yes | Previously Pending | End of epoch after network reaches proposal `end_of_program_timestamp` | + +## Benefit Mechanics + +### Setting benefit factors + +At the start of an epoch the network should calculate each parties `party_running_notional_taker_volume` by summing each parties `party_epoch_notional_volume` [values](./0082-RFPR-on_chain_referral_program.md#party-epoch-volumes) over the last n epochs where n is the `window_length` set in the volume discount program [governance proposal](#governance-proposals). + +Each parties `volume_discount_factor` is then fixed to the value in the highest benefit tier they qualify for. A parties benefit tier is defined as the highest tier for which their `party_running_notional_taker_volume` is greater or equal to the tiers `minimum_party_running_notional_taker_volume`. If a party does not qualify for any tier, their `volume_discount_factor` is set to `0`. + +```pseudo +Given: + benefit_tiers=[ + { + "minimum_party_running_notional_taker_volume": 10000, + "volume_discount_factor": 0.001, + }, + { + "minimum_party_running_notional_taker_volume": 20000, + "volume_discount_factor": 0.005, + }, + { + "minimum_party_running_notional_taker_volume": 30000, + "volume_discount_factor": 0.010, + }, + ] + +And: + party_running_notional_taker_volume=22353 + +Then: + volume_discount_factor=0.005 +``` + +This benefit factor is then fixed for the duration of the next epoch. + +### Applying benefit factors + +Volume discount program benefit factors are applied by modifying [the fees](./0029-FEES-fees.md) paid by a party (either during continuous trading or on auction exit). + +## APIs + +The Parties API should expose the following information: + +- a list of all **parties** (by `id`) and the following metrics: + - current `party_running_notional_taker_volume` (value at the start of the epoch) + - current `volume_discount_factor` applied to fees + - the total amount discounted for the party + +The Trades API should now also expose the following additional information for every trade: + +- Volume discount program discounts + - `infrastructure_fee_volume_discount` + - `liquidity_fee_volume_discount` + - `maker_fee_volume_discount` + +## Acceptance Criteria + +### Governance Proposals + +1. If an `UpdateVolumeDiscount` proposal does not fulfil one or more of the following conditions, the proposal should be `STATUS_REJECTED`: + - the `end_of_program_timestamp` must be less than or equal to the proposals `enactment_time` (0084-VDPR-001). + - the number of tiers in `benefit_tiers` must be less than or equal to the network parameter `volumeDiscountProgram.maxBenefitTiers` (0084-VDPR-002). + - all `minimum_party_running_notional_taker_volume` values must be an integer strictly greater than 0 (0084-VDPR-017). + - all `volume_discount_factor` values must be greater than or equal to `0` and less than or equal to the network parameter `volumeDiscountProgram.maxVolumeDiscountFactor` (0084-VDPR-003). + - the `window_length` must be an integer strictly greater than zero (0084-VDPR-004). +1. A volume discount program should be started the first epoch change after the `enactment_datetime` is reached (0084-VDPR-005). +1. A volume discount program should be closed the first epoch change after the `end_of_program_timestamp` is reached (0084-VDPR-006). +1. If a volume discount program is already active and a proposal `enactment_datetime` is reached, the volume discount program is updated at the next epoch change. + - Propose program A with `enactment_timestamp` ET1 and `end_of_program_timestamp` CT1 (0084-VDPR-007). + - Proposal for program A accepted and begins first epoch after ET1 (0084-VDPR-008). + - Propose program B with `enactment_timestamp` ET2 (ET2 > ET1 && ET2 < CT1) and `end_of_program_timestamp` CT1 (CT1 < CT2) (0084-VDPR-009). + - Proposal for program B accepted and overrides program A the first epoch after ET2 (0084-VDPR-010). + - Program is closed first epoch after CT2, there should be no active proposals (0084-VDPR-011). +1. Updating any of the following network parameters whilst there is an active volume discount program will not modify or cancel the active program in any way. The updated parameters will however be used to validate future volume discount program proposals. + - `volumeDiscountProgram.maxBenefitTiers` (0084-VDPR-015). + - `volumeDiscountProgram.maxVolumeDiscountFactor` (0084-VDPR-016). + +### Setting benefit factors + +1. At the start of an epoch, each parties `volume_discount_factor` is reevaluated and fixed for the epoch (0084-VDPR-012). +1. A parties `volume_discount_factor` is set equal to the factors in the highest benefit tier they qualify for (0084-VDPR-013). +1. If a party does not qualify for the lowest tier, their `volume_discount_factor`is set to `0` (0084-VDPR-014). diff --git a/protocol/0085-RVST-rewards_vesting.md b/protocol/0085-RVST-rewards_vesting.md new file mode 100644 index 000000000..2148d392f --- /dev/null +++ b/protocol/0085-RVST-rewards_vesting.md @@ -0,0 +1,107 @@ +# Rewards Vesting Specification + +## Summary + +The aim of the rewards vesting mechanics is to prevent farming rewards by delaying the payout of rewards through vesting. To encourage longer term behaviour parties can accelerate their rewards vesting rate through the [activity streak program](./0086-ASPR-activity_streak_program.md). + +## Network Parameters + +- `rewards.vesting.baseRate`: the proportion of rewards in a vesting account which are vested each epoch, value defaults to `0.1` and must be a float strictly greater than 0. +- `rewards.vesting.minimumTransfer`: the minimum amount (expressed in quantum) which can be vested each epoch, value defaults to 100 and must be an integer greater or equal than `0`. +- `rewards.vesting.rewardPayoutTiers`: is an ordered list of dictionaries defining the requirements and multipliers for each tier. + +## Vesting mechanics + +As detailed in [distributing rewards](./0056-REWA-rewards_overview.md#distributing-rewards-amongst-entities), each party has their rewards paid into vesting rewards accounts (one for each asset). + +At the end of each epoch, a proportion of the rewards accumulated in each "vesting" account should be released and transferred to the respective "vested" account. The percentage released can be scaled by the account owner increasing their [activity streak](./0086-ASPR-activity_streak_program.md) and a minimum transfer amount will be applied to ensure the account is eventually emptied. The proportion released and minimum applied are controlled for parameters for the asset. + +Now, let: + +- $T$ be the amount to be "vested" (transferred from the vesting account to the vested account) +- $B_{vested}$ be the total quantum amount in the vesting account +- $r$ be the network parameter `rewards.vesting.baseRate` +- $a$ be the account owners current [`activity_streak_vesting_multiplier`](./0086-ASPR-activity_streak_program.md#setting-activity-benefits) +- $m$ be the network parameter `rewards.vesting.minimumTransfer` + +The quantum amount to be transferred from each "vesting" account to the relevant "vested" account is defined as: + +$$T = max(B_{vesting} * r * a, m)$$ + +When transferring funds from the vesting account to the vested account, a new transfer type should be used, `TRANSFER_TYPE_REWARDS_VESTED`. + +## Rewards bonus multiplier + +Once vested rewards are transferred to the vested account, the party will be able to transfer funds to their general account using a normal transfer. + +When transferring funds from a vested account to the general account held by their key a party will incur no transfer fees and if transferring the full balance they will not be subject to the minimum quantum transfer amount. + +Alternatively, they can leave their rewards in the vested account to increase their total rewards balance and receive a multiplier on their reward payout share. The size of this multiplier is dependent on their total rewards balance, i.e. the sum of the parties locked rewards, vesting rewards and vested rewards. Note, funds removed from the vested account are not included in this total. + +Note, a party will be unable to transfer funds in to the vested account. + +### Determining the rewards bonus multiplier + +Before [distributing rewards](./0056-REWA-rewards_overview.md#distributing-rewards-amongst-entities), each parties `reward_distribution_bonus_multiplier` should be set according to the highest tier they qualify for. + +```pseudo +Given: + rewards.vesting.benefitTiers: [ + [ + {"minimum_quantum_balance": 10000, "reward_multiplier": 1.0}, + {"minimum_quantum_balance": 100000, "reward_multiplier": 5.0}, + {"minimum_quantum_balance": 1000000, "reward_multiplier": 10.0}, + ], + ] + +And: + locked_quantum_amount=2 + vesting_quantum_amount=999 + vested_quantum_amount=99000 + +Then: + reward_distribution_bonus_multiplier=5.0 +``` + +## APIs + +### Accounts API + +Must expose the following: + +- every account with `ACCOUNT_TYPE_VESTING_REWARDS` for each party +- every account with `ACCOUNT_TYPE_VESTED_REWARDS` for each party + +### Ledger Entries API + +Must expose the following: + +- every transfer with `TRANSFER_TYPE_REWARDS_VESTED` for each party + +## Acceptance Criteria + +### Network parameters + +1. When `rewards.vesting.baseTransfer` is updated, the new value should be applied to rewards vesting at the end of the current epoch. (0085-RVST-001) +1. When `rewards.vesting.minimumTransfer` is updated, the new value should be applied to rewards vesting at the end of the current epoch. (0085-RVST-002) +1. When `rewards.vesting.rewardPayoutTiers` is updated, the new value should be applied when distributing rewards at the end of the current epoch. (0085-RVST-003) + +### Vesting / vested accounts + +1. A party should have one vesting account per asset. Rewards distributed from reward pools should be transferred to the correct vesting account. (0085-RVST-004) +1. A party should have one vested account per asset. Rewards distributed from vesting accounts should be transferred to the correct vested account. (0085-RVST-005) +1. Funds **cannot** be transferred from a vesting account by a user. (0085-RVST-006) +1. Funds **can** be transferred from a vested account by a user. (0085-RVST-007) +1. Funds **cannot** be transferred to a vested account by a user. (0085-RVST-008) + +### Vesting mechanics + +1. If a party has unlocked rewards in a vesting account (expressed in quantum) strictly greater than the network parameter `rewards.vesting.minimumTransfer` then rewards should be transferred to the respective vested account for the asset at the end of the epoch as per the formula defined in the specification. (0085-RVST-009) +1. If a party has unlocked rewards in a vesting account (expressed in quantum) less than or equal to the network parameter `rewards.vesting.minimumTransfer` then the entire balance should be transferred to the respective vested account for the asset at the end of the epoch. (0085-RVST-010) +1. Locked rewards in the vesting account should not start vesting un till the lock period has expired. (0085-RVST-011) + +### Rewards bonus multiplier + +1. A parties `reward_distribution_bonus_multiplier` should be set equal to the value in the highest tier where they fulfil the `minimum_quantum_balance` required. (0085-RVST-012) +1. Funds in both the parties vesting account and vested account should contribute to their `minimum_quantum_balance`. (0085-RVST-013) +1. Assuming all parties perform equally, a party with a greater `reward_distribution_bonus_multiplier` should receive a larger share of a reward pool. (0085-RVST-014) diff --git a/protocol/0086-ASPR-activity_streak_program.md b/protocol/0086-ASPR-activity_streak_program.md new file mode 100644 index 000000000..c3877cebf --- /dev/null +++ b/protocol/0086-ASPR-activity_streak_program.md @@ -0,0 +1,143 @@ +# Activity Streak Program + +The purpose of the activity streak program is to reward loyal, active traders with the following tiered benefits: + +- a greater share of rewards schemes +- an accelerated vesting rate on locked rewards + +Parties access higher tiers and greater benefits by maintaining an activity streak. The length of a streak is measured in epochs and a party is considered active if they made a trade or held an open position during the epoch. If a party is inactive for more than a specified number of epochs they lose their streak. + +## Network parameters + +- `rewards.activityStreak.benefitTiers`: is an ordered list of dictionaries defining the requirements and benefits for each tier. +- `rewards.activityStreak.inactivityLimit`: the maximum number of epochs a trader can be inactive before loosing their streak. +- `rewards.activityStreak.minQuantumOpenNotionalVolume`: the minimum open notional volume (expressed in quantum) for a trader to be considered active in an epoch +- `rewards.activityStreak.minQuantumTradeVolume`: the minimum trade volume (expressed in quantum) for a trader to be considered active in an epoch + + +## Governance proposals + +The network parameter [`rewards.activityStreak.benefitTiers`](#network-parameters) can be updated via a `UpdateNetworkParameter` governance proposal. Each tier in the ordered list must have the following fields: + +- `minimum_activity_streak`: int greater or equal to `0` defining the minimum activity streak a party must have to access this tier +- `reward_multiplier`: float greater or equal to `1` defining the factor to scale a parties [reward shares](./0056-REWA-rewards_overview.md#distributing-rewards-amongst-entities) by +- `vesting_multiplier`: float greater or equal to `1` defining the factor to scale a parties [base vesting rate](./0086-ASPR-rewards_vesting.md#vesting-mechanics) by + +*Example:* + +```proto +message UpdateNetworkParameter{ + changes: NetworkParameter{ + key: "rewards.activityStreak.benefitTiers", + value: [ + {"minimum_activity_streak": 1, "reward_multiplier": 1.0, "vesting_multiplier": 1.05}, + {"minimum_activity_streak": 7, "reward_multiplier": 5.0, "vesting_multiplier": 1.25}, + {"minimum_activity_streak": 31, "reward_multiplier": 10.0, "vesting_multiplier": 1.50}, + {"minimum_activity_streak": 365, "reward_multiplier": 20.0, "vesting_multiplier": 2.00}, + + ], + } +} +``` + +## Activity streak mechanics + +The following steps should occur **before** rewards are [distributed] and [vested]. + +### Setting activity / inactivity streak + +For the feature, the network must track each parties "activity streak". At the end of an epoch: + +- if a party was "active" in the epoch + + - increment their `activity_streak` by `1` + - reset their `inactivity_streak` to `0`. + +- if a party was "inactive" in the epoch + + - increment their `inactivity_streak` streak by `1` + - if their `inactivity_streak` is greater than or equal to the `rewards.activityStreak.inactivityLimit`, reset their `activity_streak` to `0`. + +A party is defined as active if they fulfil **either** of the following criteria: + +- their open interest was strictly greater than `rewards.activityStreak.minQuantumOpenNotionalVolume` at any point in the epoch +- their total trade volume was strictly greater than `rewards.activityStreak.minQuantumTradeVolume` at the end of the epoch + +### Setting activity benefits + +After determining a parties "activity streak" there `reward_distribution_activity_multiplier` and `reward_vesting_activity_multiplier` should be set according to the highest tier they qualify for. + +```pseudo +Given: + rewards.activityStreak.benefitTiers: [ + [ + {"minimum_activity_streak": 1, "reward_multiplier": 1.0, "vesting_multiplier": 1.05}, + {"minimum_activity_streak": 7, "reward_multiplier": 5.0, "vesting_multiplier": 1.25}, + {"minimum_activity_streak": 31, "reward_multiplier": 10.0, "vesting_multiplier": 1.50}, + {"minimum_activity_streak": 365, "reward_multiplier": 20.0, "vesting_multiplier": 2.00}, + + ], + ] + +And: + activity_streak=48 + inactivity_streak=3 + +Then: + reward_distribution_activity_multiplier=10.0 + reward_vesting_activity_multiplier=1.50 +``` + +### Applying activity benefits + +#### Applying the activity reward multiplier + +The `activity_streak_reward_multiplier` scales the parties [reward share](./0056-REWA-rewards_overview.md#distributing-rewards-amongs-entities) for all rewards they are eligible for. + +#### Applying the activity vesting multiplier + +The `activity_streak_vesting_multiplier` scales the parties [vesting rate](./0086-ASPR-rewards_vesting.md#vesting-mechanics) of all funds locked in the parties vesting accounts. + + +## APIs + +### Parties API + +Must expose the following: + +- a parties `activity_streak` +- a parties `inactivity_streak` +- whether a party has been considered "active" in the current epoch +- a parties current `reward_distribution_activity_multiplier` +- a parties current `reward_vesting_activity_multiplier` + +## Acceptance Criteria + +### Network parameters + +1. If any of the following network parameters are updated, the new values will be used when distributing or vesting rewards at the end of the current epoch: + + - `rewards.activityStreak.inactivityLimit` (0086-ASPR-001) + - `rewards.activityStreak.minQuantumOpenNotionalVolume` (0086-ASPR-002) + - `rewards.activityStreak.minQuantumTradeVolume` (0086-ASPR-003) + +### Setting activity / inactivity streak + +1. At the end of an epoch, before rewards are distributed, a parties activity steak is incremented if they fulfil **either** of the following conditions: + + - their notional open volume summed across all markets (in quantum) was strictly greater than `rewards.activityStreak.minQuantumOpenNotionalVolume` at any point in the epoch. (0086-ASPR-004) + - their notional trade volume summed across all markets (in quantum) was strictly greater than `rewards.activityStreak.minQuantumTradeVolume` at the end of the epoch. (0086-ASPR-005) + +1. At the end of an epoch, before rewards are distributed, if a party was deemed active then their `inactivity_streak` should be reset to `0`. +1. At the end of an epoch, before rewards are distributed, a parties inactivity streak is incremented if they fulfil **both** of the following conditions: (0086-ASPR-006) + + - their notional open volume summed across all markets (in quantum) was less than `rewards.activityStreak.minQuantumOpenNotionalVolume` at any point in the epoch. + - their notional trade volume summed across all markets (in quantum) was less than `rewards.activityStreak.minQuantumTradeVolume` at the end of the epoch. + +1. At the end of an epoch if a party was deemed inactive and after incrementing their `inactivity_streak` it is greater than `rewards.activityStreak.inactivityLimit` then their `activity_streak` should be reset to `0`. (0086-ASPR-007) + +### Setting activity benefits + +1. At the end of the epoch, before rewards are distributed, the parties `reward_distribution_activity_multiplier` should be set equal to the value in the highest tier where their activity streak is greater or equal than the `minimum_activity_streak`. (0086-ASPR-008) +1. At the end of the epoch, before rewards are distributed, the parties `reward_vesting_activity_multiplier` should be set equal to the value in the highest tier where their `activity_streak` is greater or equal than the `minimum_activity_streak`. (0086-ASPR-009) +1. Assuming all parties perform equally, a party with a greater `reward_distribution_activity_multiplier` should receive a larger share of a reward pool. (0086-ASPR-010) diff --git a/protocol/0087-EVMD-eth-rpc-and-evm-data-source.md b/protocol/0087-EVMD-eth-rpc-and-evm-data-source.md new file mode 100644 index 000000000..55b12ffa3 --- /dev/null +++ b/protocol/0087-EVMD-eth-rpc-and-evm-data-source.md @@ -0,0 +1,146 @@ +# Ethereum RPC and EVM based data sources + +## Summary + +This specification adds a new way of sourcing data from any chain or Ethereum Layer 2 blockchain (L2) that supports Ethereum RPC calls and runs an EVM. + +> [!TIP] +> A Layer 2 blockchain refers to network protocols that are layered on top of a Layer 1 solution. Layer 2 protocols use the Layer 1 blockchain for network and security infrastructure. + +The data is to be ingested as an Ethereum [data source](./0045-DSRC-data_sourcing.md) but pointing at a different RPC endpoint. +Hence this is in addition to and building upon [Ethereum data source](./0082-ETHD-ethereum-data-source.md). + +## Description + +In addition to listening to Ethereum events and reading from Ethereum contracts as described in [Ethereum data source](./0082-ETHD-ethereum-data-source.md) it will be possible for Vega nodes to listen to events from and read from other chains that implement Ethereum RPC and run EVM, in particular Ethereum L2s. + +The overarching principle is that the chain provides ethereum RPC / EVMs and thus contracts and ABIs are assumed to be functionally the same as on Ethereum itself. + +## Registration / removal + +A new network parameter, a JSON list of network id / chain id / name one per Ethereum RPC and EVM chain will be used for setting supported L2. This is set via [governance](./0028-GOVE-governance.md). +Name `blockchains.ethereumRpcAndEvmCompatDataSourcesConfig`. +Example value: + +```json +{"configs": [{"network_id": 10, "chain_id": 10, "confirmations": 3, "name":"optimism"}]} +``` + +Duplicate values of `network_id`, `chain_id` or `name` are not allowed (an update will be rejected at validation stage). +Any update must always change the entire JSON (it's not possible to change individual entries). +In current minimal scope, at proposal validation, check that only change is + +1. changing number of confirmation or +1. adding another source. + +For later release: A proposal to *remove* a registered Ethereum RPC+EVM compatible chain / L2 must fail at enactment stage if a market is referencing an `EthRpcEvmCompatible` data source. + +In current minimal scope: A proposal for a new market will fail at validation stage if it's referencing an `EthRpcEvmCompatible` that's not registered. + +For later release: A proposal for a new market will fail at enactment stage if it's referencing an `EthRpcEvmCompatible` that's not registered. + + +## Acceptance criteria + +### External Oracles - Creation + +- It is possible to add `EthRpcEvmCompatible` via governance (0087-EVMD-001). + +### External Oracles - External Chain Config Changes + +- At network proposal validation step we check that the only change to `blockchains.ethereumRpcAndEvmCompatDataSourcesConfig` is to either change number of confirmations or add another external chain. (0087-EVMD-043) + +### External Oracles - Deactivation (not scoped in Palazzo milestone) + +- It is possible to remove an `EthRpcEvmCompatible` via governance. The proposal will fail at enactment stage if there is any market that's not settled / closed that reference the `EthRpcEvmCompatible`. This is a future requirement and does not have an AC code. + + +### External Oracles - Market Amendments + +- It may happen that an `EthRpcEvmCompatible` that cannot be read from is proposed (because not enough validator nodes have it configured etc.). In that case a proposed / enacted market will not see the oracle inputs but it must be possible to change the said market via on-chain governance (0087-EVMD-003). +- Update an existing futures market using the market update proposal to change the `EthRpcEvmCompatible` chain referenced, smart contract address and read method. The changes take effect after the market update proposal is enacted and data is sourced from the new smart contract. The old data source will be deactivated when the proposal is enacted (0087-EVMD-004) +- Using the market update proposal all data elements for the ethereum oracles can be updated. On successful enactment, a new oracle data source is created for the market. In case any existing data source matches the new data source, then a new data source is not created and the existing one is used (0087-EVMD-005) +- Ensure existing oracle data sources are deactivated when market data sources are amended on another market. Create 2 markets to use different ethereum oracles for termination and settlement. Two sets of ethereum oracles are created and are ACTIVE. Then amend Market 2 to use exactly the same ethereum oracles for termination and settlement as Market1. Now ,the ethereum oracles originally created for for Market2 should be set to DEACTIVATED. No new ethereum oracles should be created and the Market2 should use the existing ethereum oracles created for Market1 (0087-EVMD-006) +- Ensure that when a market data source type is amended from internal, external, ethereum or open (coinbase) to an alternative for both termination and settlement we see that old data source is deactivated (if no other market is using) and we see the new data source created and it supports termination and settlement specific to its data source type (0087-EVMD-007) + + +### External Oracles - Validations + +- A new market proposal that reference an `EthRpcEvmCompatible` that's not active will fail at validation stage (0087-EVMD-008) +- A market change proposal that reference an `EthRpcEvmCompatible` that's not active will fail at validation stage (0087-EVMD-044) +- Validate if the smart contract address is valid (0087-EVMD-009) +- Validate if the data elements of the oracle data source is valid - e.g. call the smart contract and check if the types in the ABI match whats provided in the oracle spec (0087-EVMD-010) +- Validations for min / max frequency of listening for events / read a smart contract (0087-EVMD-011) +- When a proposal that uses ethereum oracles, defines incorrect data (contract address, ABI) the system should return an error and the proposal should not pass validation (0087-EVMD-012) +- Any mismatch between expected fields/field types and received fields/field types should emit an error event (0087-EVMD-013) + + +### Usage + +- Two different markets may reference two identical `EthRpcEvmCompatible` contracts and ABIs, even with same contract address *but* on two different Ethereum L2s and they each get the correct values i.e. if we have market 1 with + +```yaml +source: EthereumEvent { + source_chain_id: 0x123 + contract: 0xDEADBEEF + ABI: "...JSON..." + event: "MyFaveEvent" +``` + +and market 2 with + +```yaml +source: EthereumEvent { + source_chain_id: 0x789 + contract: 0xDEADBEEF + ABI: "...JSON..." + event: "MyFaveEvent" +``` + +Then market 1 only sees events of that type from `EthRpcEvmCompatible` 0x123 while market 2 only sees events of that type from `EthRpcEvmCompatible` 0x789 (0087-EVMD-014). + +- It should be possible to use only `EthRpcEvmCompatible` data sources in a market proposal, or create any combination with any of the other types of currently existing external or internal data sources (0087-EVMD-015) +- Create a market to use an internal data source to terminate a market and an `EthRpcEvmCompatible` to settle the market (0087-EVMD-016) +- Create a market to use an external data source to terminate a market and an `EthRpcEvmCompatible` to settle the market (0087-EVMD-017) +- Create a market to use an open oracle data source to settle a market and an `EthRpcEvmCompatible` to terminate the market (0087-EVMD-018) +- `EthRpcEvmCompatible` events should only be sent when the filter is matched, this can be verified using an API and the core/data node events (BUS_EVENT_TYPE_ORACLE_DATA) (0087-EVMD-019) +- `EthRpcEvmCompatible` data sources should only forward data after a configurable number of confirmations (0087-EVMD-020) +- Create 2 markets to use the same `EthRpcEvmCompatible` data source for termination say DS-T1 but two different `EthRpcEvmCompatible` data sources for settlement DS-S1 and DS-S2. Now trigger the termination ethereum oracle data source. Both markets should be terminated and the data source DS-T1 is set to DEACTIVATED and the data sources DS-S1 and DS-S2 are still ACTIVE. Now settle market1. DS-S1 is set to DEACTIVATED and DS-S2 is still active. (0087-EVMD-021) +- Create a market to use an `EthRpcEvmCompatible` data source for termination configured such that - it expects a boolean value True for termination and the contract supplying the termination value is polled every 5 seconds. Set the contract to return False for termination. The market is not terminated. The data source is still ACTIVE and no BUS_EVENT_TYPE_ORACLE_DATA events for that ethereum oracle spec are emitted. Then set the contract to return True for termination. The market is terminated and an event for BUS_EVENT_TYPE_ORACLE_DATA for the ethereum oracle data spec is received and the ethereum oracle is set to DEACTIVATED. (0087-EVMD-022) +- One oracle data event is emitted for data that matches each data source - Create 2 markets with ethereum oracle settlement specs that use the same settlement key such that - the first settlement spec expects settlement data to be greater than 100 and the second expects greater than 200. Now send it a settlement data of 300. One single event BUS_EVENT_TYPE_ORACLE_DATA for the settlement data is emitted for each matching `EthRpcEvmCompatible` data source i.e. in this case, two oracle data events will be emitted - one for each settlement data source. Both markets are settled and both the data sources are DEACTIVATED. (0087-EVMD-023) +- Different oracle data events for multiple spec id's with non matching filter values - Create 2 markets with ethereum oracle settlement specs that use the same settlement key such that - the first settlement spec expects settlement data to be greater than 100 and the second expects greater than 200. Now send it a settlement data of 50. NO data events for BUS_EVENT_TYPE_ORACLE_DATA. Send settlement data of 150. One single event BUS_EVENT_TYPE_ORACLE_DATA emitted for the settlement data is emitted with matching ethereum oracle data spec for Market1, market1 is settled and the data source is set to DEACTIVATED. Send settlement data of 250. One single event BUS_EVENT_TYPE_ORACLE_DATA emitted for the settlement data is emitted with matching ethereum oracle data spec for Market2, Market2 is settled and the data source is set to DEACTIVATED. (0087-EVMD-024) +- Network wide contract error should be reported via oracle data events (0087-EVMD-025) +- Different contracts on different markets - Create 2 markets with `EthRpcEvmCompatible` settlement data sources containing different contract addresses and with *different* settlement keys, but with all conditions and filters the same. Confirm that sending settlement value that passes the market spec filter only settles one market. (0087-EVMD-026) +- Different contracts on different markets - Create 2 markets with `EthRpcEvmCompatible` settlement data sources containing different contract addresses and with the *same* settlement keys, but with all conditions and filters the same. Confirm that sending settlement value that passes the market spec filter only settles one market. (0087-EVMD-027) + + +### Negative Tests + +- Set up a new data source with invalid contract address - should fail validations (0087-EVMD-028). +- Create an oracle that calls a read method of a smart contract and specify an incorrect ABI format for the event. Proposal should fail validation and should return an error (0087-EVMD-029) +- Set up a network such that different vega nodes receive conflicting results from an identical `EthRpcEvmCompatible` contract call. Attempt to settle a market using that contract. Observe that if there are not enough nodes voting in agreement, the market is not settled (0087-EVMD-030) + +### API + +- Ability to query data source specs defined for ethereum oracle sources, for settlement and termination, via an API endpoint (REST, gRPC and graphQL) - filters should be available for data source - internal OR external, status - Active / Inactive / Expired (0087-EVMD-031) +- Ability to query historic data sent by an ethereum oracle source, for settlement and termination, and processed by a market in vega network (0087-EVMD-032) + + +### Snapshots + +- Oracle data sources linked to markets should be stored on snapshots and should be able to be restored from snapshots. The states of the oracle data sources should be maintained across any markets where they are linked to `EthRpcEvmCompatible` data sources. (0087-EVMD-033) + +### Protocol Upgrade + +- Have a network running with a couple of futures markets with a mix of internal, external, open ethereum and `EthRpcEvmCompatible` oracles . Perform a protocol upgrade. Once the network is up , the state of the various data sources should be the same as before the protocol upgrade (0087-EVMD-034) +- Have a network running with a couple of perpetual markets with a mix of internal, external, open, ethereum and `EthRpcEvmCompatible` oracles . Perform a protocol upgrade. Once the network is up , the state of the various data sources should be the same as before the protocol upgrade (0087-EVMD-035) +- Create a futures market with an `EthRpcEvmCompatible` for termination such that it polls at a specific time. Perform a protocol upgrade such that the termination triggers in the middle of the protocol upgrade. Once the network is up , the termination should be triggered and the market should be terminated. (0087-EVMD-036) +- Create a futures market with an `EthRpcEvmCompatible` for settlement such that it polls at a specific time. Perform a protocol upgrade such that the settlement price matching the filters is triggered in the middle of the protocol upgrade. Once the network is up , the settlement should be triggered and the market should be terminated. (0087-EVMD-037) +- Create a perpetual market with an `EthRpcEvmCompatible` for settlement such that it polls at a specific time. Perform a protocol upgrade such that the settlement price matching the filters is triggered in the middle of the protocol upgrade. Once the network is up , the settlement should be triggered and the market should be terminated. (0087-EVMD-038) +- Ensure that markets with `EthRpcEvmCompatible` termination and settlement data sources continue to successfully terminate and settle markets after the protocol upgrade. (0087-EVMD-039) + +### Perpetual futures focused tests + +- Update an existing perpetuals market using the market update proposal to change from Ethereum to `EthRpcEvmCompatible` chain referenced, smart contract address and read method. The changes take effect after the market update proposal is enacted and data is sourced from the new smart contract. The old data source will be deactivated when the proposal is enacted (0087-EVMD-040). +- Create a perpetual futures market which uses an `EthRpcEvmCompatible` chain reads/events contract data for settlement payment schedule from chain `A` and `EthRpcEvmCompatible` chain reads/events contract data for the index price from chain `B` (0087-EVMD-041). +- Create a perpetual futures market which uses an `EthRpcEvmCompatible` chain reads/events contract data for settlement payment schedule from; the trigger for the countdown to the first funding payment being publication of a valid value for the index price. The index price must not be available at the time the market is created and leaves opening auction; it must only become available sometime after. The aim is to test futures markets for underlyings that don't trade *yet* be where there is an agreed oracle source that will start publishing the price *once* they begin trading. (0087-EVMD-042). diff --git a/protocol/0088-PPRF-party_profile.md b/protocol/0088-PPRF-party_profile.md new file mode 100644 index 000000000..9fb2e81ca --- /dev/null +++ b/protocol/0088-PPRF-party_profile.md @@ -0,0 +1,37 @@ +# On-chain Party Profile + +The on-chain party profile feature allows users to associate a unique alias and free-form metadata with a public key they own. This data is stored on-chain and is exposed to anyone through APIs. + + +## Updating a party profile + +Any user with a public key is able to update their party profile by submitting a signed `UpdatePartyProfile` transaction. + +To prevent spam attacks, the number of transactions which can be sent in an epoch per party is restricted and a parties total balance across all assets (expressed in quantum) must exceed `spam.protection.updatePartyProfile.min.funds`. Refer to the [spam protection specification](./0062-SPAM-spam_protection.md#party-profile-spam) for further details on these restrictions. + +An `UpdatePartyProfile` has the following fields. + +- `alias`: an optional string which can be as an alternative identifier for the public key in any dApp (e.g. in competition leaderboards). +- `metadata`: an optional list of metadata key value pairs to further describe a public key. + +If metadata is specified, the metadata must adhere to the following rules: + +- no more than 10 key value pairs can be specified +- a key must be no longer than 32 characters +- a value must be no longer than 255 characters + +If an alias is specified, it must adhere to the following rules: + +- the alias must be no longer than 32 characters +- the alias must be unique (i.e. the alias must not already be associated with an existing party profile) + +**Note:** +In order to validate uniqueness of an alias, core must keep a record of the aliases associated with each party. If a party is no longer active. + +### Acceptance Criteria + +- If a party updates there profile with an alias of a length greater than 32 characters, the transaction is rejected. (0088-PPRF-001) +- If a party updates there profile with an alias which is already associated with another key, the transaction is rejected. (0088-PPRF-002) +- If a party update there profile with metadata with more then 10 key pairs, the transaction is rejected. (0088-PPRF-003) +- If a party update there profile with metadata with a key of a length more than 32 characters, the transaction is rejected. (0088-PPRF-004) +- If a party update there profile with metadata with a value of a length more than 255 characters, the transaction is rejected. (0088-PPRF-005) diff --git a/protocol/0089-COMP-composing_prices.md b/protocol/0089-COMP-composing_prices.md new file mode 100644 index 000000000..49900369d --- /dev/null +++ b/protocol/0089-COMP-composing_prices.md @@ -0,0 +1,128 @@ +# Composite prices + +## Introduction + +Prices are composed to create mark price and on perpetual futures markets a view of what the price on the vega side is for purposes of comparing to the underlying price and an eventual funding calculation. +Mark price is used by Vega protocol to calculate mark-to-market cashflows, feed the risk calculation, to provide ``unrealised'' profit-and-loss (PnL) information and drive price monitoring. + +For perpetual futures markets there should be a composite *mark price* configuration and a composite *market price for funding* configuration so that the market can, potentially use different mark price for mark-to-market and price monitoring and completely different price for calculating funding. + +Every market will have a mark price upon leaving opening auction. +This means that either (a) the proposed mark price methodology has produced a value during the opening auction or (b) if it has not, upon leaving the opening auction the first mark price is set to the auction uncrossing price regardless of the mark price methodology. +Subsequent updates will follow the set methodology. + +## Proposed composite price methodology + +We update the mark at the end of every mark price period and the funding price possibly at a different frequency (also set as network parameter specifying a period). +In the sequel both will be referred to as "mark price period". + +### Price from observed trades + +Existing network or market parameters that enter into this: + +- Length of mark price period given by $\delta = $`network.markPriceUpdateMaximumFrequency`. + +New market parameters that enter into this: + +- $\alpha \in [0,1]$ decay weight. +- $p \in \mathbb N$ a decay power and in practice we'd want to support only $p \in \{1,2,3\}$. + +Calculate $\hat P^{\text{trades}}$ which is simply trade-size-weighted average of all eligible trades over the mark price period. +Let $P_s$ to be price at time $s$ and $w_s$ the weight set as the volume traded at the price at time $s$. +Let $\delta > 0$ be the mark price period length. +Let $\alpha \geq 0$ decay weight and $p\in \mathbb N$ a decay power. +With this define +$$ +K(s,t) = 1 - \alpha\frac{(t-s)^p}{\delta^p}\,. +$$ +and +$$ +W(t-\delta, t) := \int_{(t-\delta) \vee 0}^t K(s,t) w_s\, ds +$$ +so that finally +$$ +\hat P_t := \frac{1}{W(t-\delta, t)} \int_{(t-\delta) \vee 0}^t K(s,t) w_s \,P_s \,ds\,. +$$ +As a sanity check note that if $P_s = P$ for all $s\in (t-\delta, t]$ we get +$$ +\hat P_t := \frac{P}{W(t-\delta, t)} \int_{(t-\delta) \vee 0}^t K(s,t) w_s \,ds = P\,. +$$ + +### Price observed from the order book + +Existing network or market parameters or calculation outputs that enter into this: + +- $f_\text{initial scaling}$ is the `initial\_margin` in `market.margin.scalingFactors`. +- $f_\text{slippage}$ is the linear slippage factor in market proposal. +- $f_\text{risk}$ refers to either the long or short risk factors output by the risk model. + +New market parameters that enter into this: + +- Let $C$ be some cash amount e.g. $500$ USDT would be entered as $500 00000$ respecting the $5$ asset decimals. + +Let $C$ be some cash amount e.g. $500$ USDT. +Calculate how much this can be leveraged to $N = C\frac{1}{f_{\text{risk}} + f_{\text{slippage}}} \frac{1}{f_{\text{initial scaling}}}$ i.e. you multiply $C$ by the maximum possible leverage. +For sell side calculate $V_{\text{sell}} = \frac{N}{P_{\text{best ask}}}$ where you set $f_{\text{risk}}$ to be the one for long. +For buy side calculate $V_{\text{buy}} = \frac{N}{P_{\text{best bid}}}$ where you set $f_{\text{risk}}$ to be the one for short. +Calculate $\hat P^{\text{book}}$ which is the time average ``mid'' price seen on the book: if there is at least volume $V_{\text{sell}}$ on the sell side and at least $V_{\text{buy}}$ on the buy side: +$$ +P^{\text{book}}_s := +\frac12 \left(\text{sell vwap for volume $V_{\text{sell}}$}+\text{buy vwap for volume $V_{\text{buy}}$}\right)\,, +$$ +if not, don't include it in the time average. +If $C$ (the initial cash amount to consider) is set to $0$ then it's the usual mid price. +During auctions $P^{\text{book}}_s$ is set to the indicative uncrossing price. + +### Prices observed from data sourcing framework (oracles) + +New market parameters that enter into this: + +- Entire oracle definition for each of the data sources, in particular source and update freq / schedule. + +Obtain $(P^{\text{oracle}}_i)_{i=1}^n$ if $n \in \{0\}\cup \mathbb N$ oracle sources are defined. + +### Median of observations + +Set $L^{i}=L^{i}(s)$, $i=1,\ldots,n+2$ to be the functions which reports when a given price source from the list above was last updated and $(\delta_i)_{i=1}^d$ values defining when a given price is too old. +If for all $t-L_i(t) > \delta_i$ then all sources are stale and we do not update this source. +If at least for one $i$ we have $t-L_i(t) < \delta_i$ we do +$$ +M((P_i)_{i=1}^{2+n}) = \text{median}(\{P_i : t-L_i(t) < \delta_i\}) \,. +$$ +The median is calculated as follows: sort the prices in ascending or descending order. If $n$ is an odd number then choose the value that's in the middle of the sorted list. +If you have even number add the two in the middle up and divide by $2$. +The median price source value should update whenever any of its input price sources have been updated or whenever time has passed and a previously current source has gone stale. +The median price source will have update time set to the latest time any of the input sources were updated (it will not be set to the time when it was last recalculated just because an input has gone stale). + +### Composing with weighted average + +New market parameters that enter into this: + +- $(w_i)_{i=1}^{3+n}$ weights. +- $(\delta_i)_{i=1}^d$ specifying how old a source update can be before source is considered stale. If set to $0$ we'd want an update with the same vega time. + +We allow weights: take $(w_i)_{i=1}^{3+n}$. This allows in particular picking individual sources. +We also set $L^{i}=L^{i}(s)$, $i=1,\ldots,n+3$ be the functions which reports when a given price was last updated and $(\delta_i)_{i=1}^d$ values defining when a given price is too old. +From this we update the weights as follows: +$$ +\hat w^i := \frac{w_i \mathbf{1}_{t-L^i(t)<\delta^i}}{\sum_j w_j\mathbf{1}_{t-L^j(t)<\delta^j}}\,. +$$ +I.e. we pick those that have been updated recently enough and we re-weight. +$$ +M((P_i)_{i=1}^{3+n}) = \sum_{i=1}^{n+3} \hat w^i P_i \,. +$$ + +### Composing with median + +New market parameters that enter into this: + +- $(\delta_i)_{i=1}^d$ specifying how old a source update can be before source is considered stale. If set to $0$ we'd want an update with the same vega time. + +Set $L^{i}=L^{i}(s)$, $i=1,\ldots,n+3$ to be the functions which reports when a given price was last updated and $(\delta_i)_{i=1}^d$ values defining when a given price is too old. +If for all $t-L_i(t) > \delta_i$ then all sources are stale and we do not update the mark price. +If at least for one $i$ we have $t-L_i(t) < \delta_i$ we do +$$ +M((P_i)_{i=1}^{3+n}) = \text{median}(\{P_i : t-L_i(t) < \delta_i\}) \,. +$$ + +I.e. we do a median of the non-stale prices. diff --git a/protocol/README.md b/protocol/README.md index 90d265af2..1961d9d08 100644 --- a/protocol/README.md +++ b/protocol/README.md @@ -29,7 +29,6 @@ ## Market protections - [Price monitoring](./0032-PRIM-price_monitoring.md) -- [Liquidity monitoring](./0035-LIQM-liquidity_monitoring.md) ## Liquidity @@ -50,7 +49,6 @@ - [Order submission](./0025-OCRE-order_submission.md) - [Pegged orders](./0037-OPEG-pegged_orders.md) - [Cancelling orders](./0033-OCAN-cancel_orders.md) -- [Liquidity provisions](./0038-OLIQ-liquidity_provision_order_type.md) ## Margin, collateral & risk @@ -58,7 +56,8 @@ - [Collateral](./0005-COLL-collateral.md) - [Margin orchestration](./0010-MARG-margin_orchestration.md) - [Check order, allocate margin](./0011-MARA-check_order_allocate_margin.md) -- [Market insurance pool](./0015-INSR-market_insurance_pool_collateral.md) +- [Market insurance pool](./0015-INSR-market_insurance_pool_collateral.md#market-insurance-pool) +- [Global insurance pool](./0015-INSR-market_insurance_pool_collateral.md#global-insurance-pool) - [Margin calculator](./0019-MCAL-margin_calculator.md) - [Quant risk models (Python notebook)](./0018-RSKM-quant_risk_models.ipynb) diff --git a/protocol/categories.json b/protocol/categories.json index 09c07a63c..3491d10f7 100644 --- a/protocol/categories.json +++ b/protocol/categories.json @@ -1,18 +1,18 @@ { "Fundamentals": { - "specs": ["0017-PART", "0022-AUTH", "0051-PROD", "0016-PFUT", "0053-PERP", "0040-ASSF", "0013-ACCT", "0054-NETP", "0068-MATC", "0067-KEYS", "0057-TRAN", "0052-FPOS"] + "specs": ["0017-PART", "0022-AUTH", "0051-PROD", "0016-PFUT", "0053-PERP", "0040-ASSF", "0013-ACCT", "0054-NETP", "0068-MATC", "0067-KEYS", "0057-TRAN", "0052-FPOS", "0080-SPOT"] }, "Markets": { - "specs": ["0035-LIQM", "0032-PRIM", "0043-MKTL", "0026-AUCT", "0006-POSI", "0008-TRAD", "0001-MKTF", "0009-MRKP", "0012-POSR", "0021-MDAT", "0039-MKTD", "0070-MKTD"] + "specs": ["0035-LIQM", "0032-PRIM", "0043-MKTL", "0026-AUCT", "0006-POSI", "0008-TRAD", "0001-MKTF", "0009-MRKP", "0012-POSR", "0021-MDAT", "0039-MKTD", "0070-MKTD", "0081-SUCM"] }, "Settlement & Oracles": { - "specs": ["0002-STTL", "0003-MTMK", "0045-DSRC", "0046-DSRM", "0047-DSRF", "0048-DSRI"] + "specs": ["0002-STTL", "0003-MTMK", "0045-DSRC", "0046-DSRM", "0047-DSRF", "0048-DSRI", "0082-ETHD", "0087-EVMD"] }, "Protections": { "specs": ["0073-LIMN", "0072-SPPW", "0062-SPAM", "0060-WEND", "0003-NP-LIMI", "0072-SPPW", "0078-NWLI", "0079-TGAP"] }, "Liquidity": { - "specs": ["0044-LIME", "0042-LIQF", "0034-PROB"] + "specs": ["0044-LIME", "0042-LIQF", "0034-PROB", "0012-NP-LIPE"] }, "Governance": { "specs": ["0028-GOVE", "0027-ASSP", "0058-REWS", "0056-REWA", "0055-TREA"] @@ -35,6 +35,9 @@ "Data": { "specs": ["0076-DANO", "0020-APIS", "0007-POSN"] }, + "Referral": { + "specs": ["0083-RFPR", "0084-VDPR", "0085-RVST", "0086-ASPR"] + }, "UI": { "specs": ["1000-ASSO", "1001-VEST", "1002-STAK", "1003-INCO", "1004-VOTE", "3002-PROP", "1006-NETW", "1007-WALL", "3000-DEPO", "3001-WITH", "5000-MARK", "6000-COLL", "6001-SORD", "6002-MORD", "6003-POSI", "6004-FILL"] }, diff --git a/protocol/cosmic-features.json b/protocol/cosmic-features.json new file mode 100644 index 000000000..571708e67 --- /dev/null +++ b/protocol/cosmic-features.json @@ -0,0 +1,943 @@ +{ + "Iceberg Orders": { + "milestone": "deployment-1", + "acs": [ + "0014-ORDT-039", + "0014-ORDT-007", + "0014-ORDT-008", + "0014-ORDT-009", + "0014-ORDT-010", + "0014-ORDT-011", + "0014-ORDT-012", + "0014-ORDT-013", + "0014-ORDT-014", + "0014-ORDT-015", + "0014-ORDT-016", + "0014-ORDT-017", + "0014-ORDT-018", + "0014-ORDT-020", + "0014-ORDT-021", + "0014-ORDT-022", + "0014-ORDT-023", + "0014-ORDT-024", + "0014-ORDT-025", + "0014-ORDT-026", + "0014-ORDT-027", + "0014-ORDT-028", + "0014-ORDT-029", + "0014-ORDT-030", + "0014-ORDT-031", + "0014-ORDT-032", + "0014-ORDT-033", + "0014-ORDT-034", + "0014-ORDT-037", + "0014-ORDT-038", + "0014-ORDT-035", + "0014-ORDT-036", + "0014-ORDT-069", + "0014-ORDT-070" + ] + }, + "Stop Orders": { + "milestone": "deployment-1", + "acs": [ + "0014-ORDT-041", + "0014-ORDT-042", + "0014-ORDT-043", + "0014-ORDT-044", + "0014-ORDT-045", + "0014-ORDT-046", + "0014-ORDT-047", + "0014-ORDT-048", + "0014-ORDT-049", + "0014-ORDT-050", + "0014-ORDT-051", + "0014-ORDT-052", + "0014-ORDT-053", + "0014-ORDT-054", + "0014-ORDT-055", + "0014-ORDT-056", + "0014-ORDT-058", + "0014-ORDT-059", + "0014-ORDT-060", + "0014-ORDT-061", + "0014-ORDT-062", + "0014-ORDT-063", + "0014-ORDT-064", + "0014-ORDT-065", + "0014-ORDT-066", + "0014-ORDT-067", + "0014-ORDT-068", + "0014-ORDT-071", + "0014-ORDT-072", + "0014-ORDT-073", + "0014-ORDT-074", + "0014-ORDT-075", + "0014-ORDT-076", + "0014-ORDT-077", + "0014-ORDT-078", + "0014-ORDT-079", + "0014-ORDT-080", + "0079-TGAP-004", + "0079-TGAP-005" + ] + }, + "Successor Markets": { + "milestone": "deployment-1", + "acs": [ + "0001-MKTF-006", + "0001-MKTF-007", + "0001-MKTF-008", + "0001-MKTF-009", + "0001-MKTF-010", + "0028-GOVE-071", + "0028-GOVE-093", + "0042-LIQF-031", + "0042-LIQF-048", + "0042-LIQF-033", + "0042-LIQF-034", + "0081-SUCM-001", + "0081-SUCM-003", + "0081-SUCM-005", + "0081-SUCM-006", + "0081-SUCM-008", + "0081-SUCM-027", + "0081-SUCM-009", + "0081-SUCM-010", + "0081-SUCM-011", + "0081-SUCM-014", + "0081-SUCM-028", + "0081-SUCM-018", + "0081-SUCM-012", + "0081-SUCM-023", + "0081-SUCM-024", + "0081-SUCM-013", + "0081-SUCM-016", + "0081-SUCM-029", + "0081-SUCM-030", + "0081-SUCM-031", + "0081-SUCM-032", + "0081-SUCM-017", + "0081-SUCM-025", + "0081-SUCM-026", + "0081-SUCM-020", + "0081-SUCM-021", + "0081-SUCM-022", + "0081-SUCM-035" + ] + }, + "Transfers by governance": { + "milestone": "deployment-2", + "acs": [ + "0028-GOVE-073", + "0028-GOVE-074", + "0028-GOVE-128", + "0028-GOVE-119", + "0028-GOVE-120", + "0028-GOVE-132", + "0028-GOVE-122", + "0028-GOVE-077", + "0028-GOVE-079", + "0028-GOVE-081", + "0028-GOVE-082", + "0028-GOVE-083", + "0028-GOVE-084", + "0028-GOVE-085", + "0028-GOVE-086", + "0028-GOVE-087", + "0028-GOVE-088", + "0028-GOVE-089", + "0028-GOVE-091", + "0028-GOVE-092", + "0028-GOVE-094", + "0028-GOVE-095", + "0028-GOVE-096", + "0028-GOVE-099", + "0028-GOVE-100", + "0028-GOVE-101", + "0028-GOVE-102", + "0028-GOVE-130", + "0028-GOVE-131", + "0028-GOVE-103", + "0028-GOVE-133", + "0028-GOVE-129", + "0028-GOVE-104", + "0028-GOVE-105", + "0028-GOVE-106", + "0028-GOVE-107", + "0028-GOVE-123", + "0028-GOVE-124", + "0028-GOVE-125", + "0028-GOVE-126", + "0028-GOVE-127", + "0028-GOVE-140", + "0028-GOVE-141", + "0028-GOVE-142", + "0028-GOVE-143", + "0028-GOVE-144", + "0028-GOVE-154", + "0028-GOVE-155", + "0028-GOVE-156", + "0028-GOVE-157", + "0028-GOVE-158", + "0055-TREA-011", + "0013-ACCT-032", + "0013-ACCT-026", + "0013-ACCT-027", + "0013-ACCT-028" + ] + }, + "Perpetuals": { + "milestone": "deployment-2", + "acs": [ + "0001-MKTF-005", + "0001-MKTF-011", + "0001-MKTF-012", + "0053-PERP-001", + "0053-PERP-002", + "0053-PERP-003", + "0053-PERP-004", + "0053-PERP-005", + "0053-PERP-006", + "0053-PERP-007", + "0053-PERP-008", + "0053-PERP-009", + "0053-PERP-015", + "0053-PERP-016", + "0053-PERP-017", + "0053-PERP-018", + "0053-PERP-019", + "0053-PERP-020", + "0053-PERP-021", + "0053-PERP-022", + "0053-PERP-023", + "0043-MKTL-009", + "0051-PROD-007", + "0051-PROD-008", + "0051-PROD-009", + "0070-MKTD-017", + "0070-MKTD-018", + "0070-MKTD-019", + "0073-LIMN-105", + "0073-LIMN-106", + "0073-LIMN-107", + "0073-LIMN-108", + "0073-LIMN-109", + "0073-LIMN-110", + "0073-LIMN-111", + "0081-SUCM-002", + "0081-SUCM-015", + "0014-ORDT-120", + "0014-ORDT-121", + "0014-ORDT-122", + "0014-ORDT-123", + "0053-PERP-024" + ] + }, + "Ethereum Oracles": { + "milestone": "deployment-2", + "acs": [ + "0082-ETHD-001", + "0082-ETHD-002", + "0082-ETHD-003", + "0082-ETHD-005", + "0082-ETHD-006", + "0082-ETHD-007", + "0082-ETHD-010", + "0082-ETHD-011", + "0082-ETHD-012", + "0082-ETHD-013", + "0082-ETHD-014", + "0082-ETHD-015", + "0082-ETHD-016", + "0082-ETHD-017", + "0082-ETHD-018", + "0082-ETHD-019", + "0082-ETHD-020", + "0082-ETHD-021", + "0082-ETHD-022", + "0082-ETHD-023", + "0082-ETHD-024", + "0082-ETHD-025", + "0082-ETHD-028", + "0082-ETHD-029", + "0082-ETHD-030", + "0082-ETHD-034", + "0082-ETHD-035", + "0082-ETHD-038", + "0082-ETHD-039", + "0082-ETHD-040", + "0082-ETHD-041", + "0082-ETHD-042", + "0082-ETHD-043", + "0082-ETHD-044", + "0082-ETHD-045", + "0082-ETHD-047", + "0082-ETHD-048", + "0082-ETHD-049", + "0082-ETHD-050", + "0082-ETHD-051" + ] + }, + "SLA": { + "milestone": "deployment-2", + "acs": [ + "0042-LIQF-032", + "0042-LIQF-050", + "0042-LIQF-051", + "0042-LIQF-052", + "0042-LIQF-037", + "0042-LIQF-038", + "0042-LIQF-041", + "0042-LIQF-042", + "0042-LIQF-035", + "0042-LIQF-049", + "0042-LIQF-047", + "0042-LIQF-039", + "0042-LIQF-040", + "0042-LIQF-043", + "0042-LIQF-044", + "0042-LIQF-045", + "0042-LIQF-046", + "0042-LIQF-053", + "0042-LIQF-054", + "0044-LIME-057", + "0044-LIME-058", + "0044-LIME-059", + "0044-LIME-013", + "0044-LIME-014", + "0044-LIME-015", + "0044-LIME-016", + "0044-LIME-018", + "0044-LIME-019", + "0044-LIME-020", + "0044-LIME-021", + "0044-LIME-030", + "0044-LIME-031", + "0044-LIME-049", + "0044-LIME-022", + "0044-LIME-023", + "0044-LIME-024", + "0044-LIME-025", + "0044-LIME-026", + "0044-LIME-027", + "0044-LIME-050", + "0044-LIME-051", + "0044-LIME-053", + "0044-LIME-060", + "0044-LIME-028", + "0044-LIME-032", + "0044-LIME-033", + "0044-LIME-034", + "0044-LIME-036", + "0044-LIME-062", + "0044-LIME-063", + "0044-LIME-065", + "0044-LIME-067", + "0044-LIME-069", + "0044-LIME-071", + "0044-LIME-073", + "0044-LIME-075", + "0044-LIME-077", + "0044-LIME-079", + "0044-LIME-081", + "0044-LIME-083", + "0044-LIME-084", + "0044-LIME-085", + "0044-LIME-086", + "0044-LIME-087", + "0044-LIME-088", + "0044-LIME-089", + "0044-LIME-090", + "0044-LIME-091", + "0044-LIME-092", + "0044-LIME-093", + "0044-LIME-094", + "0044-LIME-095", + "0044-LIME-096", + "0044-LIME-097", + "0044-LIME-098", + "0044-LIME-099", + "0044-LIME-100", + "0044-LIME-101", + "0044-LIME-102", + "0026-AUCT-016", + "0026-AUCT-017", + "0026-AUCT-018", + "0026-AUCT-019", + "0026-AUCT-020", + "0026-AUCT-021", + "0026-AUCT-022", + "0034-PROB-004", + "0042-LIQF-055" + ] + }, + "Referral program": { + "milestone": "deployment-2", + "acs": [ + "0083-RFPR-001", + "0083-RFPR-002", + "0083-RFPR-003", + "0083-RFPR-004", + "0083-RFPR-005", + "0083-RFPR-006", + "0083-RFPR-007", + "0083-RFPR-008", + "0083-RFPR-009", + "0083-RFPR-010", + "0083-RFPR-011", + "0083-RFPR-012", + "0083-RFPR-013", + "0083-RFPR-014", + "0083-RFPR-015", + "0083-RFPR-016", + "0083-RFPR-017", + "0083-RFPR-018", + "0083-RFPR-019", + "0083-RFPR-020", + "0083-RFPR-021", + "0083-RFPR-022", + "0083-RFPR-023", + "0083-RFPR-024", + "0083-RFPR-025", + "0083-RFPR-026", + "0083-RFPR-027", + "0083-RFPR-029", + "0083-RFPR-030", + "0083-RFPR-031", + "0083-RFPR-032", + "0083-RFPR-033", + "0083-RFPR-034", + "0083-RFPR-035", + "0083-RFPR-036", + "0083-RFPR-037", + "0083-RFPR-038", + "0083-RFPR-039", + "0083-RFPR-040", + "0083-RFPR-041", + "0083-RFPR-042", + "0083-RFPR-043", + "0083-RFPR-046", + "0083-RFPR-047", + "0083-RFPR-048", + "0083-RFPR-049", + "0083-RFPR-050", + "0083-RFPR-051", + "0084-VDPR-001", + "0084-VDPR-002", + "0084-VDPR-003", + "0084-VDPR-004", + "0084-VDPR-005", + "0084-VDPR-006", + "0084-VDPR-007", + "0084-VDPR-008", + "0084-VDPR-009", + "0084-VDPR-010", + "0084-VDPR-011", + "0084-VDPR-012", + "0084-VDPR-013", + "0084-VDPR-014", + "0084-VDPR-015", + "0084-VDPR-016", + "0084-VDPR-017", + "0029-FEES-023", + "0029-FEES-024", + "0029-FEES-025", + "0029-FEES-026", + "0029-FEES-027", + "0029-FEES-028", + "0029-FEES-029", + "0029-FEES-030", + "0029-FEES-031", + "0029-FEES-032", + "0029-FEES-033", + "0062-SPAM-026", + "0062-SPAM-027", + "0062-SPAM-028", + "0062-SPAM-029", + "0062-SPAM-030", + "0062-SPAM-031", + "0062-SPAM-032", + "0062-SPAM-033", + "0062-SPAM-034", + "0062-SPAM-035", + "0062-SPAM-036", + "0062-SPAM-037" + ] + }, +"Rewards": { + "milestone": "deployment-2", + "acs": [ + "0085-RVST-001", + "0085-RVST-002", + "0085-RVST-003", + "0085-RVST-004", + "0085-RVST-005", + "0085-RVST-006", + "0085-RVST-007", + "0085-RVST-008", + "0085-RVST-009", + "0085-RVST-010", + "0085-RVST-011", + "0085-RVST-012", + "0085-RVST-013", + "0085-RVST-014", + "0086-ASPR-001", + "0086-ASPR-002", + "0086-ASPR-003", + "0086-ASPR-004", + "0086-ASPR-005", + "0086-ASPR-006", + "0086-ASPR-007", + "0086-ASPR-008", + "0086-ASPR-009", + "0086-ASPR-010", + "0056-REWA-076", + "0056-REWA-077", + "0056-REWA-078", + "0056-REWA-079", + "0056-REWA-080", + "0056-REWA-081", + "0056-REWA-082", + "0056-REWA-083", + "0056-REWA-084", + "0056-REWA-085", + "0056-REWA-086", + "0056-REWA-087", + "0056-REWA-088", + "0056-REWA-089", + "0056-REWA-090", + "0056-REWA-091", + "0056-REWA-092", + "0056-REWA-093", + "0056-REWA-094" + ] + }, + "Team rewards": { + "milestone": "deployment-3", + "acs": [ + "0056-REWA-095", + "0056-REWA-096", + "0056-REWA-097", + "0056-REWA-098", + "0056-REWA-099", + "0056-REWA-100", + "0056-REWA-101", + "0056-REWA-102", + "0056-REWA-103", + "0056-REWA-104", + "0056-REWA-105", + "0056-REWA-106", + "0056-REWA-107", + "0056-REWA-108" + ] + }, + "Market governance": { + "milestone": "deployment-2", + "acs": [ + "0028-GOVE-064", + "0028-GOVE-069", + "0028-GOVE-070", + "0028-GOVE-072", + "0028-GOVE-108", + "0028-GOVE-110", + "0028-GOVE-113", + "0028-GOVE-114", + "0028-GOVE-115", + "0028-GOVE-116", + "0028-GOVE-117", + "0028-GOVE-118", + "0028-GOVE-135", + "0028-GOVE-136", + "0028-GOVE-137", + "0028-GOVE-138", + "0028-GOVE-139", + "0028-GOVE-111", + "0028-GOVE-150", + "0028-GOVE-151", + "0028-GOVE-152" + ] + }, + "Batch change proposals": { + "milestone": "deployment-3", + "acs": [ + "0028-GOVE-145", + "0028-GOVE-146", + "0028-GOVE-147", + "0028-GOVE-148", + "0028-GOVE-149" + ] + }, + "Spot": { + "milestone": "deployment-3", + "acs": [ + "0080-SPOT-001", + "0080-SPOT-002", + "0080-SPOT-003", + "0080-SPOT-004", + "0080-SPOT-006", + "0080-SPOT-007", + "0080-SPOT-009", + "0080-SPOT-010", + "0080-SPOT-012", + "0080-SPOT-013", + "0080-SPOT-015", + "0080-SPOT-016", + "0080-SPOT-017", + "0080-SPOT-018", + "0080-SPOT-019", + "0080-SPOT-020", + "0081-SUCM-004", + "0044-LIME-029", + "0044-LIME-043", + "0044-LIME-044", + "0044-LIME-045", + "0044-LIME-046", + "0044-LIME-047", + "0044-LIME-048", + "0044-LIME-054", + "0044-LIME-055", + "0044-LIME-056", + "0044-LIME-064", + "0044-LIME-066", + "0044-LIME-068", + "0044-LIME-070", + "0044-LIME-072", + "0044-LIME-074", + "0044-LIME-076", + "0044-LIME-078", + "0044-LIME-080", + "0044-LIME-082", + "0056-REWA-051", + "0056-REWA-052", + "0056-REWA-053", + "0056-REWA-054", + "0056-REWA-055", + "0056-REWA-056", + "0056-REWA-057", + "0056-REWA-058", + "0056-REWA-059", + "0056-REWA-060", + "0056-REWA-061", + "0056-REWA-062", + "0056-REWA-063", + "0056-REWA-064", + "0056-REWA-065", + "0056-REWA-066", + "0056-REWA-067", + "0056-REWA-068", + "0056-REWA-069", + "0056-REWA-070", + "0056-REWA-071", + "0056-REWA-072", + "0056-REWA-073", + "0056-REWA-074", + "0056-REWA-075", + "0073-LIMN-077", + "0073-LIMN-078", + "0073-LIMN-079", + "0073-LIMN-080", + "0073-LIMN-081", + "0073-LIMN-082", + "0073-LIMN-083", + "0073-LIMN-084", + "0073-LIMN-085", + "0073-LIMN-086", + "0073-LIMN-088", + "0073-LIMN-089", + "0037-OPEG-019", + "0008-TRAD-008", + "0004-AMND-030", + "0004-AMND-031", + "0004-AMND-034", + "0004-AMND-035", + "0004-AMND-036", + "0004-AMND-037", + "0004-AMND-038", + "0004-AMND-039", + "0004-AMND-040", + "0004-AMND-041", + "0004-AMND-042", + "0004-AMND-043", + "0004-AMND-044", + "0004-AMND-045", + "0004-AMND-046", + "0004-AMND-047", + "0004-AMND-048", + "0004-AMND-049", + "0004-AMND-050", + "0004-AMND-051", + "0004-AMND-052", + "0004-AMND-053", + "0004-AMND-054", + "0004-AMND-055", + "0011-MARA-018", + "0011-MARA-019", + "0011-MARA-020", + "0011-MARA-021", + "0011-MARA-022", + "0011-MARA-023", + "0011-MARA-024", + "0011-MARA-025", + "0011-MARA-026", + "0011-MARA-027", + "0011-MARA-028", + "0011-MARA-029", + "0011-MARA-030", + "0011-MARA-031", + "0011-MARA-032", + "0013-ACCT-024", + "0013-ACCT-025", + "0013-ACCT-030", + "0013-ACCT-031", + "0014-ORDT-081", + "0014-ORDT-082", + "0014-ORDT-083", + "0014-ORDT-084", + "0014-ORDT-085", + "0014-ORDT-086", + "0014-ORDT-087", + "0014-ORDT-088", + "0014-ORDT-089", + "0014-ORDT-090", + "0014-ORDT-091", + "0014-ORDT-092", + "0014-ORDT-093", + "0014-ORDT-094", + "0014-ORDT-095", + "0014-ORDT-096", + "0014-ORDT-097", + "0014-ORDT-098", + "0014-ORDT-099", + "0014-ORDT-100", + "0014-ORDT-101", + "0014-ORDT-102", + "0014-ORDT-103", + "0014-ORDT-104", + "0014-ORDT-105", + "0014-ORDT-106", + "0014-ORDT-107", + "0014-ORDT-108", + "0014-ORDT-109", + "0014-ORDT-110", + "0014-ORDT-111", + "0014-ORDT-112", + "0014-ORDT-113", + "0014-ORDT-114", + "0014-ORDT-115", + "0014-ORDT-116", + "0014-ORDT-117", + "0014-ORDT-118", + "0014-ORDT-119", + "0021-MDAT-013", + "0021-MDAT-014", + "0021-MDAT-015", + "0021-MDAT-016", + "0021-MDAT-017", + "0021-MDAT-018", + "0021-MDAT-019", + "0021-MDAT-020", + "0024-OSTA-030", + "0024-OSTA-031", + "0024-OSTA-032", + "0024-OSTA-033", + "0024-OSTA-034", + "0024-OSTA-035", + "0024-OSTA-036", + "0024-OSTA-037", + "0024-OSTA-038", + "0024-OSTA-039", + "0024-OSTA-040", + "0024-OSTA-041", + "0024-OSTA-042", + "0024-OSTA-043", + "0024-OSTA-044", + "0024-OSTA-045", + "0024-OSTA-046", + "0024-OSTA-047", + "0024-OSTA-048", + "0025-OCRE-004", + "0025-OCRE-005", + "0025-OCRE-006", + "0026-AUCT-023", + "0026-AUCT-024", + "0026-AUCT-025", + "0026-AUCT-026", + "0026-AUCT-027", + "0026-AUCT-028", + "0026-AUCT-029", + "0026-AUCT-031", + "0026-AUCT-032", + "0029-FEES-015", + "0029-FEES-016", + "0029-FEES-017", + "0029-FEES-018", + "0029-FEES-019", + "0029-FEES-020", + "0029-FEES-021", + "0029-FEES-022", + "0032-PRIM-022", + "0032-PRIM-023", + "0032-PRIM-024", + "0032-PRIM-025", + "0032-PRIM-026", + "0032-PRIM-027", + "0032-PRIM-028", + "0032-PRIM-029", + "0032-PRIM-030", + "0032-PRIM-031", + "0032-PRIM-032", + "0032-PRIM-033", + "0032-PRIM-034", + "0032-PRIM-035", + "0032-PRIM-036", + "0032-PRIM-037", + "0032-PRIM-038", + "0033-OCAN-011", + "0033-OCAN-012", + "0033-OCAN-013", + "0033-OCAN-014", + "0033-OCAN-015", + "0033-OCAN-016", + "0033-OCAN-017", + "0034-PROB-003", + "0034-PROB-008", + "0034-PROB-010", + "0039-MKTD-020", + "0039-MKTD-021", + "0039-MKTD-022", + "0039-MKTD-023", + "0039-MKTD-024", + "0039-MKTD-025", + "0039-MKTD-026", + "0039-MKTD-027", + "0039-MKTD-028", + "0039-MKTD-029", + "0039-MKTD-030", + "0039-MKTD-031", + "0039-MKTD-032", + "0039-MKTD-033", + "0043-MKTL-005", + "0043-MKTL-006", + "0043-MKTL-007", + "0043-MKTL-008", + "0049-TVAL-007", + "0049-TVAL-008", + "0049-TVAL-009", + "0049-TVAL-010", + "0049-TVAL-011", + "0049-TVAL-012", + "0051-PROD-004", + "0051-PROD-005", + "0051-PROD-006", + "0052-FPOS-003", + "0052-FPOS-004", + "0054-NETP-007", + "0054-NETP-008", + "0054-NETP-009", + "0054-NETP-010", + "0054-NETP-011", + "0057-TRAN-063", + "0065-FTCO-005", + "0065-FTCO-006", + "0065-FTCO-007", + "0065-FTCO-008", + "0066-VALW-004", + "0066-VALW-005", + "0068-MATC-060", + "0068-MATC-061", + "0068-MATC-062", + "0068-MATC-063", + "0068-MATC-064", + "0068-MATC-065", + "0068-MATC-066", + "0068-MATC-067", + "0068-MATC-068", + "0068-MATC-069", + "0068-MATC-070", + "0068-MATC-071", + "0068-MATC-072", + "0068-MATC-073", + "0068-MATC-074", + "0068-MATC-075", + "0068-MATC-076", + "0068-MATC-077", + "0068-MATC-078", + "0068-MATC-079", + "0068-MATC-080", + "0068-MATC-081", + "0068-MATC-082", + "0068-MATC-083", + "0068-MATC-084", + "0068-MATC-085", + "0068-MATC-086", + "0068-MATC-087", + "0068-MATC-088", + "0068-MATC-089", + "0068-MATC-090", + "0068-MATC-091", + "0068-MATC-092", + "0070-MKTD-008", + "0070-MKTD-009", + "0070-MKTD-010", + "0070-MKTD-011", + "0070-MKTD-012", + "0070-MKTD-013", + "0070-MKTD-014", + "0070-MKTD-015", + "0073-LIMN-087", + "0073-LIMN-090", + "0073-LIMN-091", + "0073-LIMN-092", + "0073-LIMN-093", + "0073-LIMN-094", + "0073-LIMN-095", + "0073-LIMN-096", + "0073-LIMN-097", + "0073-LIMN-098", + "0073-LIMN-099", + "0073-LIMN-100", + "0073-LIMN-101", + "0073-LIMN-102", + "0073-LIMN-103", + "0073-LIMN-104", + "0074-BTCH-011", + "0074-BTCH-012", + "0074-BTCH-015", + "0074-BTCH-016", + "0074-BTCH-019", + "0079-TGAP-006", + "0079-TGAP-007", + "0081-SUCM-033", + "0081-SUCM-034" + ] + }, + "Fixed Size Amendments": { + "milestone": "deployment-3", + "acs": [ + "0004-AMND-056", + "0004-AMND-057", + "0004-AMND-058" + ] + }, + "Unknown": { + "milestone": "unknown", + "acs": [] + }, + "Iceberg Orders": { + "milestone": "cosmic-carryover", + "acs": ["0014-ORDT-069"] + }, + "Successor Markets": { + "milestone": "cosmic-carryover", + "acs": ["0001-MKTF-008"] + }, + "Ethereum oracles": { + "milestone": "cosmic-carryover", + "acs": ["0082-ETHD-035", "0082-ETHD-041"] + }, + "Stop Orders": { + "milestone": "cosmic-carryover", + "acs": ["0079-TGAP-004", "0079-TGAP-005", "0014-ORDT-132", "0014-ORDT-133", "0014-ORDT-134", "0014-ORDT-135", "0014-ORDT-136"] + } +} diff --git a/protocol/features.json b/protocol/features.json new file mode 100644 index 000000000..b5b6a4677 --- /dev/null +++ b/protocol/features.json @@ -0,0 +1,490 @@ +{ + "Perpetuals": { + "milestone": "palazzo", + "acs": [ + "0014-ORDT-120", + "0014-ORDT-121", + "0014-ORDT-122", + "0014-ORDT-123", + "0082-ETHD-052", + "0053-PERP-024", + "0053-PERP-025", + "0053-PERP-026", + "0053-PERP-027", + "0053-PERP-029", + "0053-PERP-030", + "0053-PERP-031", + "0053-PERP-032", + "0053-PERP-040", + "0053-PERP-041", + "0053-PERP-042" + ] + }, + "Team rewards": { + "milestone": "palazzo", + "acs": [ + "0056-REWA-095", + "0056-REWA-096", + "0056-REWA-097", + "0056-REWA-098", + "0056-REWA-099", + "0056-REWA-100", + "0056-REWA-101", + "0056-REWA-102", + "0056-REWA-103", + "0056-REWA-104", + "0056-REWA-105", + "0056-REWA-106", + "0056-REWA-107", + "0056-REWA-108", + "0056-REWA-109", + "0056-REWA-110", + "0083-RFPR-052", + "0083-RFPR-059", + "0083-RFPR-060", + "0083-RFPR-061", + "0083-RFPR-062", + "0083-RFPR-063", + "0083-RFPR-064", + "0083-RFPR-065", + "0083-RFPR-066", + "0083-RFPR-067", + "0088-PPRF-001", + "0088-PPRF-002", + "0088-PPRF-003", + "0088-PPRF-004", + "0088-PPRF-005", + "0062-SPAM-033", + "0062-SPAM-035", + "0062-SPAM-036", + "0062-SPAM-037", + "0062-SPAM-038", + "0062-SPAM-039", + "0062-SPAM-040", + "0062-SPAM-041", + "0062-SPAM-042" + ] + }, + "Fixed size amendments": { + "milestone": "palazzo", + "acs": [ + "0004-AMND-056", + "0004-AMND-057", + "0004-AMND-058", + "0004-AMND-059" + ] + }, + "Margin isolation": { + "milestone": "palazzo", + "acs": [ + "0019-MCAL-031", + "0019-MCAL-032", + "0019-MCAL-033", + "0019-MCAL-034", + "0019-MCAL-035", + "0019-MCAL-036", + "0019-MCAL-037", + "0019-MCAL-038", + "0019-MCAL-039", + "0019-MCAL-040", + "0019-MCAL-049", + "0019-MCAL-050", + "0019-MCAL-051", + "0019-MCAL-052", + "0019-MCAL-053", + "0019-MCAL-054", + "0019-MCAL-055", + "0019-MCAL-056", + "0019-MCAL-057", + "0019-MCAL-058", + "0019-MCAL-059", + "0019-MCAL-060", + "0019-MCAL-061", + "0019-MCAL-062", + "0019-MCAL-064", + "0019-MCAL-065", + "0019-MCAL-066", + "0019-MCAL-067", + "0019-MCAL-068", + "0019-MCAL-070", + "0019-MCAL-071", + "0019-MCAL-072", + "0019-MCAL-073", + "0019-MCAL-074", + "0019-MCAL-075", + "0019-MCAL-078", + "0019-MCAL-100", + "0019-MCAL-101", + "0019-MCAL-102", + "0019-MCAL-103", + "0019-MCAL-104", + "0019-MCAL-105", + "0019-MCAL-106", + "0019-MCAL-107", + "0019-MCAL-108", + "0019-MCAL-109", + "0019-MCAL-110", + "0019-MCAL-112", + "0019-MCAL-113", + "0019-MCAL-114", + "0019-MCAL-115", + "0019-MCAL-116", + "0019-MCAL-117", + "0019-MCAL-119", + "0019-MCAL-120", + "0019-MCAL-121", + "0019-MCAL-122", + "0019-MCAL-123", + "0019-MCAL-124", + "0019-MCAL-125", + "0019-MCAL-126", + "0019-MCAL-127", + "0019-MCAL-128", + "0019-MCAL-129", + "0019-MCAL-130", + "0019-MCAL-131", + "0019-MCAL-132", + "0019-MCAL-133", + "0019-MCAL-134", + "0019-MCAL-135", + "0019-MCAL-136", + "0019-MCAL-137", + "0019-MCAL-138", + "0019-MCAL-139", + "0019-MCAL-140", + "0019-MCAL-141", + "0019-MCAL-142", + "0019-MCAL-143", + "0019-MCAL-144", + "0019-MCAL-145", + "0019-MCAL-146", + "0019-MCAL-147", + "0019-MCAL-148", + "0019-MCAL-149", + "0019-MCAL-150", + "0019-MCAL-151", + "0019-MCAL-152", + "0019-MCAL-153", + "0019-MCAL-160", + "0019-MCAL-161", + "0019-MCAL-162", + "0019-MCAL-163", + "0019-MCAL-164", + "0019-MCAL-165", + "0019-MCAL-166", + "0019-MCAL-167", + "0019-MCAL-168", + "0019-MCAL-169", + "0019-MCAL-172", + "0019-MCAL-173", + "0019-MCAL-174", + "0019-MCAL-175", + "0019-MCAL-176", + "0019-MCAL-200", + "0019-MCAL-201", + "0019-MCAL-202", + "0019-MCAL-203", + "0019-MCAL-204", + "0019-MCAL-205", + "0019-MCAL-206", + "0019-MCAL-207", + "0019-MCAL-220", + "0019-MCAL-221", + "0019-MCAL-222", + "0019-MCAL-223", + "0019-MCAL-224", + "0019-MCAL-225", + "0019-MCAL-226", + "0019-MCAL-227", + "0019-MCAL-228", + "0019-MCAL-229", + "0019-MCAL-230", + "0019-MCAL-231", + "0019-MCAL-232", + "0019-MCAL-233", + "0074-BTCH-020", + "0074-BTCH-021" + ] + }, + "Quadratic slippage removal": { + "milestone": "palazzo", + "acs": [ + "0019-MCAL-024", + "0019-MCAL-025", + "0019-MCAL-026", + "0019-MCAL-027", + "0019-MCAL-028", + "0019-MCAL-029", + "0019-MCAL-030" + ] + }, + "Modify liquidation mechanics": { + "milestone": "palazzo", + "acs": [ + "0012-POSR-001", + "0012-POSR-005", + "0012-POSR-009", + "0012-POSR-010", + "0012-POSR-011", + "0012-POSR-012", + "0012-POSR-013", + "0012-POSR-014", + "0012-POSR-015", + "0012-POSR-016", + "0012-POSR-017", + "0012-POSR-018", + "0012-POSR-019", + "0012-POSR-020", + "0012-POSR-021", + "0012-POSR-022", + "0012-POSR-023", + "0012-POSR-024", + "0012-POSR-025", + "0012-POSR-026", + "0012-POSR-027", + "0012-POSR-028", + "0012-POSR-029", + "0003-MTMK-015", + "0003-MTMK-016", + "0003-MTMK-017", + "0053-PERP-037", + "0053-PERP-038", + "0053-PERP-039" + ] + }, + "Batch governance proposals": { + "milestone": "palazzo", + "acs": [ + "0028-GOVE-067", + "0028-GOVE-068", + "0028-GOVE-145", + "0028-GOVE-146", + "0028-GOVE-148", + "0028-GOVE-149", + "0028-GOVE-160", + "0028-GOVE-161", + "0028-GOVE-163", + "0028-GOVE-164", + "0028-GOVE-165" + ] + }, + "Transfer fee cap": { + "milestone": "palazzo", + "acs": [ + "0057-TRAN-011", + "0057-TRAN-012", + "0057-TRAN-014", + "0057-TRAN-015", + "0057-TRAN-016", + "0057-TRAN-017", + "0057-TRAN-018", + "0057-TRAN-019", + "0057-TRAN-020", + "0057-TRAN-021", + "0057-TRAN-022", + "0057-TRAN-023", + "0057-TRAN-024", + "0057-TRAN-027", + "0057-TRAN-064", + "0057-TRAN-065" + ] + }, + "Transfers from vesting": { + "milestone": "palazzo", + "acs": [ + "0057-TRAN-066", + "0057-TRAN-067", + "0057-TRAN-068", + "0057-TRAN-069" + ] + }, + "Liquidity fee setting": { + "milestone": "palazzo", + "acs": [ + "0042-LIQF-056", + "0042-LIQF-057", + "0042-LIQF-058", + "0042-LIQF-059", + "0042-LIQF-060", + "0042-LIQF-061", + "0042-LIQF-062" + ] + }, + "Governance market name change": { + "milestone": "palazzo", + "acs": [ + "0028-GOVE-159", + "0028-GOVE-166" + ] + }, + "Insurance pools redistribution": { + "milestone": "palazzo", + "acs": [ + "0002-STTL-011", + "0002-STTL-012", + "0002-STTL-013", + "0013-ACCT-033", + "0043-MKTL-010" + ] + }, + "Position Linked Stop-Orders": { + "milestone": "palazzo", + "acs": [ + "0014-ORDT-127", + "0014-ORDT-128", + "0014-ORDT-129", + "0014-ORDT-130", + "0014-ORDT-131", + "0014-ORDT-137", + "0014-ORDT-138", + "0014-ORDT-139" + ] + }, + "Markprice updates": { + "milestone": "palazzo", + "acs": [ + "0009-MRKP-001", + "0009-MRKP-003", + "0009-MRKP-010", + "0009-MRKP-011", + "0009-MRKP-012", + "0009-MRKP-013", + "0009-MRKP-014", + "0009-MRKP-015", + "0009-MRKP-016", + "0009-MRKP-017", + "0009-MRKP-018", + "0009-MRKP-019", + "0009-MRKP-020", + "0009-MRKP-021", + "0009-MRKP-022", + "0009-MRKP-023", + "0009-MRKP-024", + "0009-MRKP-025", + "0009-MRKP-026", + "0009-MRKP-027", + "0009-MRKP-028", + "0009-MRKP-029", + "0009-MRKP-030", + "0009-MRKP-031", + "0009-MRKP-032", + "0053-PERP-033", + "0053-PERP-034", + "0009-MRKP-040", + "0009-MRKP-041", + "0009-MRKP-050", + "0009-MRKP-051", + "0009-MRKP-052", + "0009-MRKP-053", + "0009-MRKP-054", + "0009-MRKP-055", + "0009-MRKP-056", + "0009-MRKP-057", + "0009-MRKP-058", + "0009-MRKP-059", + "0009-MRKP-060", + "0009-MRKP-061", + "0009-MRKP-062", + "0009-MRKP-063", + "0009-MRKP-064", + "0009-MRKP-110", + "0009-MRKP-111", + "0009-MRKP-112", + "0009-MRKP-113", + "0009-MRKP-114", + "0009-MRKP-115", + "0009-MRKP-116", + "0009-MRKP-117", + "0009-MRKP-118", + "0009-MRKP-119", + "0009-MRKP-120", + "0009-MRKP-121", + "0009-MRKP-122", + "0009-MRKP-123", + "0009-MRKP-124", + "0009-MRKP-125", + "0009-MRKP-126", + "0009-MRKP-127", + "0009-MRKP-130", + "0009-MRKP-131", + "0009-MRKP-132", + "0009-MRKP-134", + "0009-MRKP-135", + "0053-PERP-044", + "0053-PERP-045", + "0053-PERP-046" + ] + }, + "Ethereum RPC and EVM based data sources": { + "milestone": "palazzo", + "acs": [ + "0087-EVMD-001", + "0087-EVMD-003", + "0087-EVMD-004", + "0087-EVMD-005", + "0087-EVMD-006", + "0087-EVMD-007", + "0087-EVMD-008", + "0087-EVMD-009", + "0087-EVMD-010", + "0087-EVMD-011", + "0087-EVMD-012", + "0087-EVMD-013", + "0087-EVMD-014", + "0087-EVMD-015", + "0087-EVMD-016", + "0087-EVMD-017", + "0087-EVMD-018", + "0087-EVMD-019", + "0087-EVMD-020", + "0087-EVMD-021", + "0087-EVMD-022", + "0087-EVMD-023", + "0087-EVMD-024", + "0087-EVMD-025", + "0087-EVMD-026", + "0087-EVMD-027", + "0087-EVMD-028", + "0087-EVMD-029", + "0087-EVMD-030", + "0087-EVMD-031", + "0087-EVMD-032", + "0087-EVMD-033", + "0087-EVMD-034", + "0087-EVMD-035", + "0087-EVMD-036", + "0087-EVMD-037", + "0087-EVMD-038", + "0087-EVMD-039", + "0087-EVMD-040", + "0087-EVMD-041", + "0087-EVMD-042", + "0087-EVMD-043", + "0087-EVMD-044" + ] + }, + "Price estimate": { + "milestone": "palazzo", + "acs": [ + "0012-NP-LIPE-001", + "0012-NP-LIPE-002", + "0012-NP-LIPE-003", + "0012-NP-LIPE-004", + "0012-NP-LIPE-005", + "0012-NP-LIPE-006", + "0012-NP-LIPE-007", + "0013-NP-POSE-001", + "0013-NP-POSE-002", + "0013-NP-POSE-004", + "0013-NP-POSE-005", + "0013-NP-POSE-006", + "0013-NP-POSE-007", + "0013-NP-POSE-008", + "0013-NP-POSE-009" + ] + }, + "Unknown": { + "milestone": "unknown", + "acs": ["0028-GOVE-167"] + } +} \ No newline at end of file diff --git a/spellcheck.sh b/spellcheck.sh index 84df29653..6113819e9 100755 --- a/spellcheck.sh +++ b/spellcheck.sh @@ -10,7 +10,7 @@ brew install aspell echo "Running the spell checker..." -pyspelling --config spellcheck.yaml +python3 -m pyspelling --config spellcheck.yaml echo "Remove dictionary binary..." diff --git a/user-interface/0001-WALL-wallet.md b/user-interface/0001-WALL-wallet.md deleted file mode 100644 index 1c5290ff0..000000000 --- a/user-interface/0001-WALL-wallet.md +++ /dev/null @@ -1,123 +0,0 @@ -# Wallet -A Vega wallet is required to prepare and submit transaction on Vega (place, cancel, orders etc). See the [wallet docs](https://docs.vega.xyz/docs/mainnet/concepts/vega-wallet) for more on how "crypto" wallets work. - -A wallet can contain many public/private key pairs. The public part of each key pair is known the [Party](../protocol/0017-PART-party.md) sometimes just referred to as a key or public key. - -The primary job(s) of a wallet is to [sign/encrypt transaction](../protocol/0022-AUTH-auth.md) (so the network can be sure they were sent by a given party) and to broadcast these transactions to a node on the network. - -## Set up wallet / Restore wallet -When opening the wallet for the first time, I... - -- if the wallet sends telemetry/analytics: **must** be prompted to opt into (or stay out of) analytics (0001-WALL-003) -- I can restore a wallet from a seed phrase (0001-WALL-004) -- I can create a new wallet (0001-WALL-005) - - I can view the back up phrase (0001-WALL-006) - - I can view the version number of the algorithm used to derive key pairs from the back up phrase (0001-WALL-007) - - I can see the first key without having to "add key". (i.e. The wallet auto generates the first key from the seed phrase) (0001-WALL-008) - -...so I can sign transactions - -## Configure network -When using the wallet on a network, I... - -- I can have Mainnet and Fairground (testnet) pre-configured (with Mainnet being the default network) (0001-WALL-009) -- I can create a new network configuration (0001-WALL-010) -- I can refine the configuration for existing networks (including the ones that come pre-configured) (0001-WALL-011) -- I can remove networks (0001-WALL-013) - -...so I can broadcast transactions to, and read information from a vega network in my wallet - -## Update wallet -When using an older version of a Vega wallet than the current official release, I... - -- I am warned if the version I am using is not compatible with the version of Vega on the selected network, and I am given a link to get latest compatible version on github (0001-WALL-015) - -... so the version of the wallet app I am using works with the network I am using - -## Log in to a wallet -When using a given wallet, I... - -- I can select a wallet and enter the passphrase only once per "session" (0001-WALL-016) - -... so that other users of my machine can not use my wallet, and I am not asked to re-enter frequently - -## Connecting to Dapps -When a dapp requests use of a wallet, I... - -- I am prompted to either select a wallet or dismiss the prompt (0001-WALL-017) - - I can select whole wallet (so that new keys are automatically shared) (0001-WALL-019) -- I can enter wallet passphrase before wallet details are shared (assuming a password has not recently been entered)(0001-WALL-022) -- I can retrospectively revoke Dapp's access to a Wallet (0001-WALL-023) - -... so that I can control what public keys are shared with a dapp and what dapps can prompt me to sign transactions - -## Approving transactions -When a dapp sends a transaction to the wallet for signing and broadcast, I... - -- I am prompted to confirm, reject or ignore the transaction (if auto-confirm is not on) (0001-WALL-024) -- I can see the details of the transaction. See [details of transaction](#transaction-detail). (0001-WALL-025) - -... so I can verify that the transaction being sent is the one I want - -## Transactions -When thinking about a recent or specific transaction, I ... - -- I can see a history of transactions the wallet has signed. As read from the local app (Current "session" only, as persistent data storage has other requirements (see commented out ACs)) (0001-WALL-034) -- I can see pending transactions (Transactions I have not yet confirmed/rejected) (0001-WALL-035) -- I can see transactions that were rejected by the wallet user (me) (0001-WALL-037) -- (for tainted keys) I can see attempts to use a tainted key (these did not prompt the user, but allows a user to change permissions) (0001-WALL-038) - -... so that I can ensure my wallet is being used appropriately and find transaction I might have missed - -## Transaction detail -when looking at a specific transaction... - -- I can see [status of broadcasted transactions](0003-WTXN-submit_vega_transaction.md#track-transaction-on-network) - -.. so I can find all the information about what has happened with mined and un-mined transactions - -## Key management -When using a Vega wallet, I... - -- I can create new keys (derived from the source of wallet) (0001-WALL-052) -- I can see full public key or be able to copy it to clipboard (0001-WALL-054) -- I can change key name/alias (0001-WALL-055) -- I can amend other arbitrary key meta data (0001-WALL-056) -- I can control whether the wallet app queries for data (e.g. asset balances) on each key (to prevent info leaking about what keys belong to a wallet) (0001-WALL-078) - -... so I can manage risk (e.g. isolate margin), mitigate the damage of a key being compromised, or use multiple trading strategies - -## Taint keys -When protecting myself from use of keys that may be compromised, I.. - -- I can select a key I wish to taint (0001-WALL-057) -- I am prompted to enter wallet password to taint key (0001-WALL-058) -- (Dapps that request use of tainted keys **must** not prompt a prompt user to sign transaction) (0001-WALL-059) -- I can see tainted keys flagged as tainted (0001-WALL-060) - -... so that tainted keys must not be used - -When I have accidentally tainted a key I... - -- I can select a key to un-taint and be required to enter wallet password (0001-WALL-061) - -...so that I must use the key again - -## Manually sign a message -When wishing to use my wallet to sign arbitrary messages, I... - -- I can enter content to be signed with key (0001-WALL-062) -- I can submit/sign the content (0001-WALL-065) - - I can [track progress](0003-WTXN-submit_vega_transaction.md#track-transaction-on-network) of broadcast transaction either by being given a hash that I can use in block explorer, or see the transaction status - -.. so I can control of the message being signed, and can use the message elsewhere (for example to prove I own a wallet) - -## Wallet management -When seeking to reduce risk of compromise I... - -- I can create multiple wallets (0001-WALL-066) -- I can switch between wallets (0001-WALL-067) -- I can remove a wallet (0001-WALL-068) -- I can change wallet name (0001-WALL-069) - -... so that I must administrate my wallets diff --git a/user-interface/0002-WCON-connect_vega_wallet.md b/user-interface/0002-WCON-connect_vega_wallet.md deleted file mode 100644 index eb567b672..000000000 --- a/user-interface/0002-WCON-connect_vega_wallet.md +++ /dev/null @@ -1,80 +0,0 @@ -# Connect Vega wallet & select keys - -## Connect wallet - -When looking to use Vega via a user interface e.g. Dapp (Decentralized web App), I... - -- If the app loads and already has a connection it can restore "eagerly" (without the user having to click connect) it **could** do so -- **should** see a link to get a Vega wallet (in case I don't already have one) -- **should** see a link to connect that opens up connection options -- **should** have a way to easily access help should I need to troubleshoot my connection e.g. link to the docs -- **must** select a connection method / wallet type: (0002-WCON-002) -- if Rest: - - **must** have the option to input a non-default Wallet location (0002-WCON-003) - - **should** warn if the dapp is unable the see a wallet is running at the wallet location - - **must** submit attempt to connect to wallet (0002-WCON-005) - - **could** trigger the app to open on the user's machine with a `vegawallet://` prompt - - - if the dapp DOES already have a permission with the wallet: **must** see that wallet is connected (0002-WCON-007) note: if the user want to connect to a different wallet to the one that they were previously connected with, they will have to hit logout. - - if the app uses one key at a time: **should** show what key is active (re-use the last active key) (0002-WCON-008) - - - if the wallet does NOT have an existing permission with the wallet: **must** prompt user to check wallet app to approve the request to connect wallet: See [Connecting to Dapps](0002-WCON-connect_vega_wallet.md#connect-wallet) for what should happen in wallet app (0002-WCON-009) - - - if new keys are given permission: **must** show the user the keys have been approved (0002-WCON-010) - - **should** see [public key(s)](DATA-data_display.md#public-keys) - - **should** see alias(es) - - **could** see assets on key(s) - - **would like to** see positions on key(s) - - if the dapp uses one key at a time: **should** prompt me to select key. See [select/switch keys](#select-and-switch-keys). (0002-WCON-014) - - - if user rejects connection: **must** see a message saying that the request to connect was denied (0002-WCON-015) - - - if the dapp is unable to connect for technical reason (e.g. CORS): **must** see an explanation of the error, and a method of fixing the issue (0002-WCON-016) - - -- ~~Browser wallet~~ `not available yet` - -- Fairground hosted wallet - - **must** only be be shown this option if the dapp is connected to fairground (0002-WCON-039) - - **must** input a wallet name (0002-WCON-017) - - **must** input a password (0002-WCON-018) - - if success: **must** see that the wallet is connected and details of connected key (0002-WCON-019) - - if failure: **must** see reason for failure (0002-WCON-020) - - *note: the fairground hosted wallet is configured to automatically approve connections from dapps so there is no need for key selection.* - -- **must** have the option to select a different method / wallet type if I change my mind (0002-WCON-021) - -... so I can use the interface to read data about my key/party or request my wallet to broadcast transactions to a Vega network. - -## Disconnect wallet - -When wishing to disconnect my wallet, I... - -- **must** see an option to disconnect wallet (0002-WCON-022) -- **must** see confirmation that wallet has been disconnected (0002-WCON-023) -- **should** see prompt to connect a wallet, after disconnect - -... so that I can protect my wallet from malicious use or select a different wallet to connect to - - -## Select and switch keys - -when looking to do something with a specific key (or set of keys) from my wallet, I... - -- **must** see what key is currently selected (if any) (0002-WCON-025) -- **must** see a list of keys that are approved from the connected wallet (0002-WCON-026) - -- for each key: - - **must** see the first and last 6 digits of the [public key](DATA-data_display.md#public-keys). (0002-WCON-027) - - **should** be able to see the whole public key - - **must** be able to copy to clipboard the whole public key (0002-WCON-029) - - **must** see the key name/alias (meta data) (0002-WCON-030) - - **should** see what non-zero assets that key has - - **could** see the total asset balances (including associated) - - **would like to see** a breakdown of the accounts. See [collateral / accounts](6001-COLL-collateral.md) - - **would like to** see any active orders or positions. See [collateral / accounts](6001-COLL-collateral.md) - - **would like to** see my nominations to validators for associated tokens - -- **must** see the option to trigger a re-authenticate so I can use newly created keys (0002-WCON-035) - -...so that I can select the key(s) that I want to use. diff --git a/user-interface/0003-WTXN-submit_vega_transaction.md b/user-interface/0003-WTXN-submit_vega_transaction.md deleted file mode 100644 index dfb0ad4e9..000000000 --- a/user-interface/0003-WTXN-submit_vega_transaction.md +++ /dev/null @@ -1,49 +0,0 @@ -# Submit Vega transaction - -A dapp sends a transaction to a wallet, that wallet then broadcasts the transaction to a network. Therefore the following is broken up into two steps. The transaction could fail at either. Generally: Once the transaction has gone to the network a user can use block explorer to track the transaction, but some tracking in Dapp or wallet will help. - -When submitting a Vega transaction of any kind, I... - -## Track transaction to wallet - -if not connected to a Vega wallet: - -- **must** be told that I am not connected, so can not submit a transaction, and given the [option to connect](0012-WCON-connect_vega_wallet.md). (Note: this may have happened if the wallet has become disconnected without the user knowing) (0003-WTXN-001) - -if transaction not auto approved by wallet: - -- **must** see a prompt to check connected vega wallet to approve transaction (0003-WTXN-002) -- **could** see the transaction details that has been passed to the wallet for broadcast - -if transaction is approved by wallet: - -- **must** see A [transaction hash](DATA-data_display.md#transaction-hash) (0003-WTXN-003) -- **must** see the public key that this transaction was submitted for (0003-WTXN-004) -- **should** see the alias for the key that submitted this transaction -- **could** see a prompt to set this app to [auto approve](0001-WALL-wallet.md#approving-transactions) in wallet app - -if transaction is rejected by wallet: - -- **could** see that the order was rejected by the connected wallet (closing the window automatically may be appropriate) (0003-WTXN-007) - -if the wallet does not respond: - -- **must** not be able prevented from using the app, e.g. you can (0003-WTXN-008) -- **would** like to be able to cancel the transaction from the dapp so that the wallet is no longer in the state where it is asking user to confirm - -if the wallet highlights an issue with the transaction: - -- **must** show that the transaction was marked as invalid by the wallet and not broadcast (aka an error was returned from Wallet) (0003-WTXN-009) -- **should** see the error returned highlighted in context of the form that submitted the transaction in Dapp -- **must** show error returned by wallet (0003-WTXN-011) - -## Track transaction on network - -- **must** see a link to that transaction in a block explorer for the appropriate network (0003-WTXN-012) -- **should** see an indication transaction status -- **should** see the network the transaction was broadcast to -- **should** see the block the transaction was processed in -- **should** show the node the transaction was broadcast to -- **could** see the validator that processed the block the transaction was processed in - -... so I am aware of the transactions status of the transactions my wallet is sending and that are being processed by the network \ No newline at end of file diff --git a/user-interface/0004-EWAL-connect_ethereum_wallet.md b/user-interface/0004-EWAL-connect_ethereum_wallet.md deleted file mode 100644 index 6c9567ae1..000000000 --- a/user-interface/0004-EWAL-connect_ethereum_wallet.md +++ /dev/null @@ -1,35 +0,0 @@ -# Connect Ethereum wallet - -Dapps can connect to an Ethereum wallet to complete Ethereum transactions such as Deposits and withdraws to/from Vega, Association and more. - -## Connecting wallet - -When wanting or needing to write to Ethereum, I... - -- will have seen a link to connect that opens connection options (this always happens in context so should be covered by WITH, DEPO, ASSO ACS) - -- if first time: - - **must** select a connection method / wallet type: (e.g. wallet connect, injected / metamask) (0004-EWAL-001) - - **must** be prompt to check eth wallet (while the dapp waits for a response) (0004-EWAL-002) - - **must** see an option to cancel the attempted connection (if the wallet fails to respond) (0004-EWAL-003) - - if the app gets multiple keys: the user: - - **should** be shown the keys returned and given a UI to select a key for use (but the pattern is often just to select the first in the array) - - **should** be prompted to select one (in many cases Dapps default to key 0 in the array) -- after first use (if there is a connection to restore): - - **must** prompt wallet to grant access (0004-EWAL-004) - - **should** see previous connection has been recovered - - **should** see a link to trigger a fresh connection / fetch new keys (in in the case where I now want to use a different wallet to the one I was connected with) -- once connected: - - **must** see the connected ethereum wallet Public key (0004-EWAL-005) - -... so I can sign and broadcast Ethereum transactions, use a key address as in input, or read data from ethereum via my connected wallet - -## Disconnecting - -When I'm finished using a connected Ethereum wallet I may wish to disconnect... - -- **must** see a link to disconnect (0004-EWAL-006) -- **must** destroy all session so that hitting connect again connects as if it is the first use (0004-EWAL-007) -- **should** see a connect button to start a fresh connection (e.g. to a different wallet but via Wallet connect) - -... so that I can use a different wallet, or ensure may wallet can not be used by other apps \ No newline at end of file diff --git a/user-interface/0005-ETXN-submit_ethereum_transaction.md b/user-interface/0005-ETXN-submit_ethereum_transaction.md deleted file mode 100644 index c1d34b163..000000000 --- a/user-interface/0005-ETXN-submit_ethereum_transaction.md +++ /dev/null @@ -1,56 +0,0 @@ -# Submit Ethereum transaction - -## Know what transaction I'm signing -When about to click to prompt Ethereum wallet to sign a transaction, I... - -- **should** see the contract address I am about to interact with -- **should** see the function name I am about to interact with - -...so I know what to expect when my wallet asks me to sign -## Track transactions to wallet - -after clicking to submit an eth transaction to a connected wallet, I... - -- **could** see an estimate for gas prices compared to a recent history -- **could** see an estimated gas price for the function in question -- **must** see prompt to check Ethereum wallet to approve transactions - -... so I know I need to go to my wallet app to approve the transaction - -## ERC20 approval/permit - -> The approval/permit step is part of the [ERC20 standard](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/). It's intention is to provide additional security when interacting with smart contracts / using Dapps. It sets a maximum amount that the given eth key can send to a given address. It means that before end "spend" of ERC20 tokens I will have need to submit a "approve" transaction to tell the network how much is approved. An attempt to spend more than the approved amount will fail. -> For example: In my approve transaction, I approve 10, I then spend 5. In my next transaction I attempt to spend 6, this will fail as my approve amount was reduced by the first transaction. -> It is common for dapps to have a approve button that simply asks to approve a massive amount and leave it up to the wallet UI (e.g. metamask) to ask the user if they would like to change this. -> Some ERC20 contracts have an additional function that runs both the approve and deposit function in one, but this is not standard. - -If the transaction in question requires an ERC20 approval, I... - -- if the current approved amount is less than the amount being "spent": **must** see be prompt to approve -- **could** see the current approved amount -- **must** be able to set the amount to be approved (in case the connected wallet does not handle this) (0005-ETXN-006) -- **must** send an approve transaction with either a user specified amount or a very large number (0005-ETXN-001) -- **must** see feedback of the state of approve transaction see "tracking ethereum transactions" below. (0005-ETXN-002) - -... so I can control the maximum permitted transfer to the contract in question - -## Tracking Ethereum transactions on network - -After approving a transaction in my wallet app, I... - -- **should** see link to the transaction on etherscan -- **must** see the transactions status (Pending, confirmed, etc) on Ethereum by reading Ethereum (via connected wallet or the back up node specified in the app) (0005-ETXN-003) -- if failed: **must** see why the transaction failed (e.g. didn't pay enough gas) (0005-ETXN-004) -- if success: **should** see how many blocks ago the transaction was confirmed by the eth node being read - -... so I can see the status of the transaction and debug as appropriate -## Tracking Ethereum transactions having their affect on Vega -Note: it is common for inter-blockchain applications to wait a certain amount of blocks before crediting money, as this reduces the risk of double spend in the case of forks or chain roll backs. There is a Vega environment variable the defines how long Vega waits. - -If the ethereum transaction I've just submitted changes the state of the Vega network (e.g. a deposit from eth appearing as credited to my vega key on vega), I... - -- **should** see how many Ethereum blocks Vega needs to wait before changing the state of Vega -- **should** see how many blocks have passed or remain until the required number has been met -- **must** see whether the expect action has taken place on Vega (e.g. credited Vega key) (0005-ETXN-005) - -... so I know vega has been updated as appropriate \ No newline at end of file diff --git a/user-interface/0006-NETW-network-and-nodes.md b/user-interface/0006-NETW-network-and-nodes.md deleted file mode 100644 index 50d1cfacc..000000000 --- a/user-interface/0006-NETW-network-and-nodes.md +++ /dev/null @@ -1 +0,0 @@ -# Select network and nodes \ No newline at end of file diff --git a/user-interface/1001-DEPO-desposit.md b/user-interface/1001-DEPO-desposit.md deleted file mode 100644 index 86041b1cd..000000000 --- a/user-interface/1001-DEPO-desposit.md +++ /dev/null @@ -1,39 +0,0 @@ -# Deposit - -The Vega network has no native assets. All settlement assets exist on another chain and are "bridged" to Vega in one way or another. - -In the case of [ERC20 tokens](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/) there is a smart contract on the Ethereum network that acts as a vault (aka bridge) for the tokens that are deposited to Vega. The Vega network then reads the information from this vault about what Vega key to credit these tokens to. While in the Vault the Vega key that owns them (and consequently the ethereum key) may change. The vault then manages how much each ethereum key is able to withdraw from the vault given then changes in ownership that may have happened on Vega. The keys to this vault and managed by the different nodes that make up the Vega network. They verify the the appropriate amounts can be withdrawn by each Ethereum key. At time of writing only ERC20 tokens have been implemented but the pattern is likely the same for other assets/networks. -## ERC20 deposits - -Note: ERC20 assets require an approval transaction to be finalised before funds can be credited to another key. Read more about approvals [link 1](https://medium.com/ethex-market/erc20-approve-allow-explained-88d6de921ce9), [link 2](https://hackernoon.com/erc20-infinite-approval-a-battle-between-convenience-and-security-lk60350r). - -When making to deposit ERC20 assets to an Vega key, I... - -- **should** be able to follow a link from an asset (e.g. on a market page) to the deposit form pre-populated with the given asset -- **must** see a link to [connect an ethereum wallet](0004-EWAL-connect_ethereum_wallet.md) that I want to deposit from (1001-DEPO-001) -- **must** select the [asset](9001-DATA-data_display.md#asset) that I want to deposit (1001-DEPO-002) - - **should** easily see the assets that I have a non-zero balance for (in the connected eth wallet) - - **should** see the ERC20 token address of the asset - - **should** see the [Vega asset symbol](9001-DATA-data_display.md#asset-symbol) - - **should** see the [Vega asset name](9001-DATA-data_display.md#asset-name) -- **must** select the [amount of the asset](9001-DATA-data_display.md#asset-balances) that I want to deposit (1001-DEPO-003) - - **should** see an ability to populate the input with the full balance in the connected wallet - - **must** be warned if the amount being deposited is greater than the balance of the token in the connected Eth wallet (1001-DEPO-004) -- **must** select the [Vega key](9001-DATA-data_display.md#public-keys) that I wish to deposit to (1001-DEPO-005) - - **should** be able to [connect to a Vega wallet and select a key](0002-WCON-connect_vega_wallet.md#select-and-switch-keys) - - **should** be easily (if not automatically) pre-populated with a [currently connected and active Vega key](0002-WCON-connect_vega_wallet.md#select-and-switch-keys) - - **should** be able to input a Vega key other than one I am connected with (even without being connected) - - if approved amount is less than deposit: - - **must** see that an approval is needed and be prompted to approve more (1001-DEPO-006) - - **should** see the approved [asset amount](9001-DATA-data_display.md#asset-balances) - - **should** be able to input an amount to approve - - **must** [submit eth transaction to approve](0005-ETXN-submit_ethereum_transaction.md) (1001-DEPO-007) - - **must** see feedback for the approve transaction - - if approved amount is more than deposit amount: - - **could** see the approved [asset amount](9001-DATA-data_display.md#asset-balances) - - **could** set a submit a new [eth transaction to approve more or less](0005-ETXN-submit_ethereum_transaction.md) - - **must** submit the Deposit [eth transaction](0005-ETXN-submit_ethereum_transaction.md) (1001-DEPO-008) - - **must** see feedback on the deposit [ETH transaction](0003-WTXN-submit_vega_transaction.md) (1001-DEPO-009) - - **must** see feedback that the deposit has or has not been credited to the Vega key (1001-DEPO-010) - -...so that my Vega key can use these assets on Vega \ No newline at end of file diff --git a/user-interface/1002-WITH-withdraw.md b/user-interface/1002-WITH-withdraw.md deleted file mode 100644 index bd102784f..000000000 --- a/user-interface/1002-WITH-withdraw.md +++ /dev/null @@ -1,110 +0,0 @@ -# Withdraw - -Withdrawing funds is a two step process. - -First the Vega network needs to approve that the funds can be released (not required for margin on open positions or in liquidity bond etc). If they are not, a withdraw is prepared and set aside so that it can not be used for positions etc. This also define what ethereum address will be credited the funds in step 2. - -Second the user will need to run an ethereum function on the bridge contract to release the funds (and pay the gas to do so). They do this using a signature supplied by nodes of the Vega network in Step 1. - -Although this is a two step process technically effort should be put into making it feel like one, then handle exceptions (like delays on withdrawals) as required. - -See [Specs for eth bridge](../protocol/0031-ETHB-ethereum_bridge_spec.md) and [docs](https://docs.vega.xyz/docs/mainnet/concepts/vega-protocol#withdrawals) on withdrawals. See also the [specs on delays to withdrawals](../non-protocol-specs/0003-NP-LIMI-limits_aka_training_wheels.md#withdrawal-limits). - -## Prepare an ERC20 withdraw from Vega - -When wishing to withdraw some of an ERC20 asset from Vega, I... - -- **should** be prompted to complete any existing incomplete withdrawals that exist for connected keys (see [complete withdrawal](#complete-erc20-withdraw-from-ethereum-bridge)) - -Note: It is better to encourage the completion of started withdraws as soon as possible after preparing them. This is because the validator set could theoretically change enough to make the node signatures that authorize the withdrawal invalid. - -- **should** be warned that they will need to pay gas on the withdrawal before starting -- **could** show the current gas fees BEFORE preparing the withdrawal (note: shows gas estimate is a general should for all [ethereum transactions](0005-ETXN-submit_ethereum_transaction.md) but this is so a user gets to see the gas costs at step 1 assuming they will do step 2 immediately. ) - -Note: A user may want to delay preparing a withdrawal if gas fees on the network are particularly high at the time - -- **must** select the asset to withdraw (1002-WITH-001) - - **should not** see option to select assets where I a zero [total balance](9001-DATA-data_display.md#asset-balances) (note this should also avoid `Pending` assets from appearing in the list) - - **must** see the general balance I have for that asset (1002-WITH-002) - - **should** see balances to the full number of decimal places possible for that asset - - **should** see the total balances of the assets I have - - **could** see a breakdown of other accounts I have in this asset and their balances - -- **must** select the [amount](9001-DATA-data_display.md#asset-balances) of the asset I wish to withdraw (1002-WITH-003) - - **should** have an easy option (link/button) to input the full amount in general balance - - **must** be able to specify as many decimal places as the asset supports (1002-WITH-004) -- **must** be warned if the amount is greater than general balance (including if the general balance amount changes while the user is looking at the form) (1002-WITH-005) -- **should** see a link to a faucet on the selected asset (only if there is one) - -- **must** specify the Ethereum address that can claim the withdrawal (e.g. where you are withdrawing too) (1002-WITH-006) - - **should** be able to easily select an Ethereum key the app is already connected to - - **should** be able to withdraw to a different Ethereum key to the one the app is connected to - - **should** be warned if the input does not look like an ethereum address (wrong number of characters, not starting with 0x etc) - -- if there is a withdraw delay on the selected asset: - - **should** see how large a withdrawal (or sum of withdrawals) needs to be to hit the `withdraw delay threshold` - - **should** see what the withdraw delay is in hours and mins (if hit) - - **should** see how much I have withdrawn in the last `withdraw delay period` - - **must** be warned if this withdraw will hit a the delay before hitting withdraw (1002-WITH-007) - -- **must** be warned if there are known reasons that the prepared withdrawal will not work (1002-WITH-008) -- **must** submit a withdraw [vega transaction](0003-WTXN-submit_vega_transaction.md) (1002-WITH-009) - -- if the preparing the withdraw on Vega fails: - - **must** be directed back to the withdraw form (containing the submitted values) and see an explanation of why the transaction failed, so I can fix and resubmit (1002-WITH-010) - -- if the preparing the withdraw on Vega is successful: - - **must** see that withdraw is prepared (1002-WITH-011) - - if this withdraw will not hit the withdrawal threshold: - - **should** be prompted to complete the transaction on ethereum (see [complete ERC20 withdraw](#complete-erc20-withdraw-from-ethereum-bridge)) - - **could** be directed to a list of incomplete withdrawals - - if this withdraw will hit withdrawal threshold: - - **must** see that the withdraw has been complete and is in the list waiting for the delay to pass (1002-WITH-024) - -...so that I can get the details required to release my funds from the the Ethereum ERC20 bridge. - -## Withdraws list / history - -When looking to either complete a withdraw or view past withdraws, I... - -- **must** be able to navigate to a list prepared withdrawals for the [connected to a vega wallet + key(s)](0002-WCON-connect_vega_wallet.md) - -- for each prepared withdraw: - - **must** see the asset being withdrawn (1002-WITH-012) - - **must** see the [amount](7001-DATA-data_display.md#asset-balances) being withdrawn (1002-WITH-013) - - **must** see the destination of the withdrawal (e.g. Recipient Eth address) (1002-WITH-014) - - **should** see the date with withdraw was prepared - - **could** see the full signature bundle from Vega node (for use on Ethereum) - - for withdraws that are in progress: - - **must** see the status of the withdraw (e.g. pending) (1002-WITH-015) - - for completed withdraws: - - **could** see when it was completed on native chain (e.g. ethereum) - - **must** see a link to the transaction on native block explorer (e.g. etherscan) (1002-WITH-016) - - for withdraws that have not been completed on the external chain, but are not delayed (e.g. Ethereum): - - **must** see a link to complete the withdraw. See [complete ERC20 withdrawal](#complete-erc20-withdraw-from-ethereum-bridge) (1002-WITH-017) - - for withdrawals that have a delay in place before the transaction can be completed: - - **should** see much of the delay remains before it can be completed - - for withdraws that failed to be prepared (e.g. there was not enough in the general account): - - **must** show that the withdraw preparation failed (1002-WITH-025) - -... so I can complete withdrawals or find details of previous ones - -## Complete ERC20 withdraw from Ethereum bridge - -When looking to submit the Ethereum transaction to release funds from the Vega bridge into my Ethereum wallet, I... - -- **must** see a link to [connect an ethereum wallet](0004-EWAL-connect_ethereum_wallet.md) if not already connected (1002-WITH-018) -- **must** see a link to [submit the ethereum transaction to finish withdrawal](0005-ETXN-submit_ethereum_transaction.md) (1002-WITH-019) -- **could** be warned if the connected ethereum wallet is different to the one that the withdraw is going to credit (this is permitted but is a good reminder to the user about what to expect) - -- if successful: - - **must** see asset balances have been updated post withdrawal (1002-WITH-020) - - **must** see the list of withdrawals (with updated status) (1002-WITH-021) - - **could** see prompt to start another transaction or complete another incomplete one -- if failed: - - **must** see a description of why the transaction failed, and advised what to do (e.g. bad signature) (1002-WITH-022) - - **must** be returned to a state where I can correct anything that is wrong, and attempt to submit the transaction again (1002-WITH-023) - - **should** see a link to docs about withdrawals for trouble shooting (e.g. if the signer set has changed significantly since the withdraw was prepared) - - **should** see status of incomplete withdrawals (so I can confirm the withdraw I attempted to complete is incomplete) - -... so the funds I withdrew from Vega are credited to my Ethereum key \ No newline at end of file diff --git a/user-interface/1003-TRAN-transfer.md b/user-interface/1003-TRAN-transfer.md deleted file mode 100644 index 9d49b14e6..000000000 --- a/user-interface/1003-TRAN-transfer.md +++ /dev/null @@ -1 +0,0 @@ -# Transfer \ No newline at end of file diff --git a/user-interface/1004-ASSO-associate.md b/user-interface/1004-ASSO-associate.md deleted file mode 100644 index 354da296f..000000000 --- a/user-interface/1004-ASSO-associate.md +++ /dev/null @@ -1,71 +0,0 @@ -# Associate and disassociate governance tokens with a Vega key - -The Governance token on a Vega network is an ERC20 ethereum token. It has two utilities on Vega, - -- Staking the Proof of stake network, -- Participating in Governance. - -To use the Governance token on a Vega network it first needs to be associated with a Vega key/party. This Vega key can then stake, propose and vote. - -The word "associate" is used in some user interfaces, as apposed the word "stake" in function names. Stake can be avoided to prevent users thinking they would get a return only after the staking step. On Vega `Staking = Association + Nomination`, as in you need to run the "stake " function on Ethereum but then the nominate step on Vega before you get staking income. See [Glossary](../glossaries/staking-and-governance.md). - -Associated tokens also count as vote weight in on-Vega governance (new markets etc), A parties vote weight is backed by the number of Governance tokens associated with that Vega key/party. - -Associating tokens to a Vega key is a little like depositing, except, a deposit can only be released by the Vega network, where as an association can be revoked on ethereum by the the eth key that did the association. - -Governance tokens may be held by a [Vesting contract](1005-VEST-vesting.md). - -## Token discovery - -When looking to acquire governance tokens, I... - -- **must** see the contract address for the governance token of the Vega network 1004-ASSO-001 - -...so I can participate in governance and staking. - -## Associate - -When looking to stake validators or participate in governance, I first need to associate governance tokens with a Vega wallet/key, I... - -- **must** [connect an Ethereum wallet/key](0004-EWAL-connect_ethereum_wallet.md) to see tokens it may have in wallet or attributed to it in the vesting contract 1004-ASSO-002 -- **must** select a Vega key to associate to 1004-ASSO-003 - - **must** be able use a [connected Vega wallet](0002-WCON-connect_vega_wallet.md) as instead of manually inputting a public key 1004-ASSO-004 - - **should** be able to populate field with a string, so I can associate to a wallet I without connecting it -- if the connected ethereum wallet has vesting tokens: **must** be able to select to associate from either the vesting contract or the wallet (The default should be wallet and the option to use vesting tokens should only appear if there are tokens in a tranche (associated or not)) 1004-ASSO-006 -- **must** see the number of associated and un-associated tokens in the selected wallet/vesting contract 1004-ASSO-007 -- **must** select the amount of tokens to associate 1004-ASSO-008 - - **must** be able to populate the input with the amount of un-associated tokens for the selected wallet/vesting contract 1004-ASSO-009 -- **must** be warned if the amount being associated is greater than the amount available in the connected ethereum wallet 1004-ASSO-010 -- **must** submit the association on [Ethereum transaction(s) inc ERC20 approval if required](0005-ETXN-submit_ethereum_transaction.md) 1004-ASSO-011 -- **must** see feedback whether my association has been registered on Ethereum 1004-ASSO-012 -- **must** see feedback that the association has been registered by Vega and that it can be used after the number of Ethereum block confirmations required (typically 50, check network param) 1004-ASSO-013 - - **should** be able to see a balance for the number of tokens associated that are ready for use 1004-ASSO-014 - - **should** be able to see a balance for the number of tokens for each pending association 1004-ASSO-015 -- on completion: **should** be prompted to go on to [nominate](2001-STKE-staking.md) and/or participate in [Governance](1004-GOVE-governance_list.md) -- **must** see the balances of tokens available in Ethereum wallet updated (both associated and un-associated) 1004-ASSO-030 -- **must** see the balances of tokens available in vesting contract updated (both associated and un-associated) 1004-ASSO-032 - -...so I can then use the Vega wallet to use my tokens. - -## Disassociate - -When wanting to remove governance tokens, I... - -- **must** [connect an Ethereum wallet/key](0004-EWAL-connect_ethereum_wallet.md) to see tokens it may have in wallet or attributed to it in the vesting contract 1004-ASSO-018 -- **must** see a list Vega keys that the connected Ethereum wallet has associated too 1004-ASSO-019 - - **must** see an amount associated with each key 1004-ASSO-020 - - **must** see the full Vega public key associated too 1004-ASSO-021 - - **must** see the the origin of the association: wallet or vesting contract 1004-ASSO-022 - - **should** be able to select one row (vega key + amount) to populate disassociate form with the key you want to disassociate from and how much 1004-ASSO-023 -- If some of the tokens for the given Eth key are held by the vesting contract: **must** select to return tokens to Vesting contract 1004-ASSO-024 -- **must** select an amount of tokens to disassociate 1004-ASSO-031 - - **must** be able to populate the input with the amount of associated tokens for the selected Vega wallet/contract 1004-ASSO-025 -- **should** be warned that disassociating will forfeit and rewards for the current epoch and reduce the Vote weigh on any open proposals -- **must** be warned if the inputs on the form will result in an invalid withdraw, before submitting 1004-ASSO-026 -- **must** action the disassociation [Ethereum transaction](0005-ETXN-submit_ethereum_transaction.md) 1004-ASSO-027 -- **must** see feedback on the progress of the disassociation on ethereum 1004-ASSO-028 -- **must** see new associated balances in Vega (theses should be applied instantly, rather than wait for the 50 eth blocks like associate) 1004-ASSO-029 -- on completion (if tokens were returned to vesting contract): **could** be prompted to go on to [redeem](1001-VEST-vesting.md). - -...so that I can transfer them to another Ethereum wallet (e.g. sell them on an exchange). - \ No newline at end of file diff --git a/user-interface/1005-VEST-vesting.md b/user-interface/1005-VEST-vesting.md deleted file mode 100644 index cf9d472da..000000000 --- a/user-interface/1005-VEST-vesting.md +++ /dev/null @@ -1,80 +0,0 @@ -# Vesting - -Some governance tokens may be held by a Vesting contract. This means that can be "owned" by an Ethereum key but not freely transferred until a vesting terms are complete. - -## list of tranches - -When looking to understand to overall vesting schedule for tokens, I... - -- **must** see a list of tranches 1005-VEST-001 -- **should** see a visualization of the vesting schedule with a break down of the type of token holders (e.g. team, investors, community) 1005-VEST-002 - -For each tranche: - -- **must** see a tranche number 1005-VEST-003 -- **could** see any annotation of what this tranche is about (e.g. community schedule A) -- **must** see a sum of tokens in the tranche 1005-VEST-005 - - **must** see how many tokens in the tranche are locked 1005-VEST-006 - - **must** see how how many tokens in the tranche are redeemable 1005-VEST-007 -- **must** see the vesting terms for each tranche (when unlocking stats and ends) 1005-VEST-008 - -... so I can understand how circulating supply could change over time. - -## Details of a tranche - -When looking into a specific tranche, I... - -- **must** see all the same details as the [list of tranches](#details-of-a-tranche) -- **should** see a list of ethereum wallets with tokens in this tranche - -for each ethereum wallet: - -- **should** see the full eth address of the wallet -- **should** see the total tokens this address holds in this tranche - - **should** see how many tokens in the tranche are locked - - **should** see how how many tokens in the tranche are redeemable - -... so I can see the details of how tokens are distributed in this tranche - -## See summary for a given Ethereum key - -When looking to see how many tokens I have in total, and how many I might be able to redeem, I... - -- **must** be able to [Connect and ethereum wallet](0004-EWAL-connect_ethereum_wallet.md) 1005-VEST-018 -- **should** be able input an ethereum address - -for the a given Ethereum wallet/address/key: - -- **must** see a total of tokens across all tranches 1005-VEST-020 - - **must** see how many tokens across all tranches are locked 1005-VEST-021 - - **must** see how many tokens across all tranches are redeemable 1005-VEST-022 -- **must** see a list of tranches this key has tokens in 1005-VEST-023 -- **must** see a total of tokens in each tranche 1005-VEST-024 - - **must** see how many tokens in each tranche are locked 1005-VEST-025 - - **must** see how many tokens in each tranche are redeemable 1005-VEST-026 - - **must** see an option to redeem from tranche 1005-VEST-027 - - **must** be warned if amount that can be redeemed from that tranche is greater than the un-associated balance for that Eth key (because this will cause the redeem function to fail) 1005-VEST-028 - - **should** see how many tokens I'd need to disassociate to be able to run the redeem function (this should be rounded up to avoid the transaction failing due to more tokens having unlocked since the user looked at the form) - - **should** see link to [disassociate](1004-ASSO-associate.md) - -... so I can easily see how many tokens I have, and can redeem. - -## Redeem tokens from a tranche - -Note: it is not possible to choose how many tokens you redeem from a tranche, instead you select a tranche and the smart contract will attempt to redeem all. However, it will fail if some of the amount it attempts to redeem have been associated to a Vega key. Therefore the job of this page is to help the user work out how many tokens to disassociate before they can successfully redeem. - -When looking to redeem tokens, I... - -- **must** [connect the ethereum wallet](0004-EWAL-connect_ethereum_wallet.md) that holds tokens 1005-VEST-029 -- must see see all tranches that you have tokens in (including tranche 0) 1005-VEST-036 -- must see a total of tokens across all tranches (including tranche 0) 1005-VEST-037 -- **must** select a tranche to redeem from 1005-VEST-030 -- **must** see the number of tokens that can be redeemed 1005-VEST-031 -- **must** be warned if the number of tokens you would be attempting to redeem is greater than you have unassociated 1005-VEST-035 - - **should** tell you how many tokens to disassociate for the redeem function to work (should round up to create a buffer for the tokens that may unlock between now and when the user gets to the disassociate form) - - **should** see a link to disassociate -- **must** submit the redeem from tranche [ethereum transaction](0005-ETXN-submit_ethereum_transaction.md) 1005-VEST-032 -- **must** get feedback on the progress of the Ethereum transaction 1005-VEST-033 -- **must** see updated balances (in the trance and my eth wallet) after redemption 1005-VEST-034 - -... so that I can use this tokens more generally on Ethereum (transfer to another key etc) \ No newline at end of file diff --git a/user-interface/2001-STKE-staking.md b/user-interface/2001-STKE-staking.md deleted file mode 100644 index e2f61b840..000000000 --- a/user-interface/2001-STKE-staking.md +++ /dev/null @@ -1,121 +0,0 @@ -# Staking - -Staking is the act of securing a Vega network by nominating good validators with the [governance token](../protocol/0071-STAK-erc20_governance_token_staking.md). Staking is rewarded with a share of trading fees (and [treasury rewards](../0056-REWA-rewards_overview.md)). See the [glossary](../glossaries/staking-and-governance.md) and [these specs](../protocol#delegation-staking-and-rewards) for more on staking. - -When staking a user may be motivated to select validators to maximize the rewards they get for the tokens they hold, this means selecting validator(s) who are less likely to be penalized (e.g. over staked, poor performance). Users may wish to stake more than one validator to diversify. Users will want/need to manage their stake over time to ensure they are getting a good return, e.g. move stake between validators. Staking is also important for facilitating protocol upgrades. - -## Understand staking on Vega - -When considering whether to stake on Vega, I... - -- **should** see information to help inform me what return I might expect from staking (other protocols might show a typical APY) -- **must** see that the governance token is an ethereum ERC20 token and needs to attributed (or associated) to a Vega wallet for use on Vega 1002-STKE-002 -- **must** see detailed documentation on how staking works on Vega 1002-STKE-003 - -...so I can decide if I want to stake on Vega, and how to go about doing it. - -Note: There are many ways that "understanding the return" can be done, and this does not impose a particular solution. Solutions could... - -- look at previous epochs, -- average this over a period, -- select a range of validators or just one, -- could have a calculator that allows the user to enter some values or just show this on the list of validators - -Note: Income may come in a range of different tokens, as markets can settle in different assets, and there may be rewards paid out by the treasury. - -## Associate tokens - -Before I stake, I need to [Associate tokens](./1000-ASSO-associate.md) with a Vega wallet/key... - -- **must** see link[Associate tokens](./1000-ASSO-associate.md) -- **should** see that if no further action is taken, newly associated tokens will be nominated to validators based on existing distribution - -...so that I can nominate validators. - -## Select validator(s) - -When selecting what validators to nominate with my stake, I... - -- **should** be able to select any data that is available on Validators in a table - -- **must** see all validator information without having to connect Vega wallet 1002-STKE-050 -- see "static" information about the validator (these can change, just not frequently) - - **must** see name 1002-STKE-006 - - **must** see Vega public key 1002-STKE-008 - - **should** see a URL where to find more information about the validator (if there is one) - - **must** see Ethereum address1002-STKE-010 -- can see data for the current/next epoch, for each validator - - **must** see the current "status" (consensus, Ersatz, New etc) 1002-STKE-011 - - **must** see a total stake (inc self stake) 1002-STKE-012 - - **must** see self stake 1002-STKE-013 - - **must** see nominated stake 1002-STKE-014 - - **must** see total stake as a % of total staked across all nodes 1002-STKE-051 - - **must** see total stake change next epoch 1002-STKE-015 - - **must** see self stake 1002-STKE-016 - - **must** see nominated stake 1002-STKE-017 - - **must** see total stake as a % change 1002-STKE-052 - - **should** see the version of Vega they are currently running - - **should** see the version of Vega they propose running -- can see data for the previous epoch - - **must** see the overall "score" for a validator for the previous epoch 1002-STKE-020 - - can see all the inputs to that "score" - - **must** see Ranking score 1002-STKE-021 - - **must** see stake score 1002-STKE-022 - - **must** see performance score 1002-STKE-023 - - **must** see voting score 1002-STKE-024 -- can see data for previous epochs - - **should** see the the overall "score" for all previous epochs for each validator 1002-STKE-025 - - can see a breakdown of all the inputs to that "score" for all previous epochs - - **should** see Ranking score - - **should** see stake score - - **should** see performance score - - **should** see voting score - -...so I can select validators that should give me the biggest return. - -## Nominate a validator - -Note: User interfaces may use the term "Nominate", technically the function is called "delegate". Delegating tokens to a validator may imply that you also give that validator your vote on proposals, at time of writing, it does not. It only gives them the potential for more "voting power" in the production of blocks. - -Within a staking epoch (typically 24 hours) a user can change their nominations many times, however the changes are only effective at the end of the epoch. You will only get rewards for a full epoch staked. - -When attributing some (or all of my governance tokens to a given validator), I... - -- **must** select a validator I want to nominate 1002-STKE-031 -- **must** see link to [connect to a Vega wallet/key](0002-WCON-connect_vega_wallet.md) (if not already) that has associated Vega (or Pending association) 1002-STKE-032 -- **must** select an amount of tokens 1002-STKE-033 - - **must** be able to populate this with the amount of governance tokens that will be associated but not nominated at the beginning of the next epoch 1002-STKE-034 - - **must** be warned if the amount I am about to nominate is below a minimum amount (spam protection) 1002-STKE-035 - - **must** be warned if the amount I am about to nominate is more than I have associated + un-nominated at the end of current epoch/beginning of next 1002-STKE-036 -- **must** submit the nomination [Vega transactions](0003-WTXN-submit_vega_transaction.md) 1002-STKE-037 -- **must** see feedback that my nomination has been registered, and will be processed at the next epoch 1002-STKE-038 -- **must** see all my pending nomination changes for the next epoch 1002-STKE-039 - -...so that I am rewarded for a share based on this validators performance. - -## Monitor staking rewards - -When checking if im getting the staking return that I was expecting, I... - -- See [Staking income](2002-SINC-staking-income.md) - -...so that I can make decisions about my staking, e.g. whether to re-distribute my stake. - -## Un-nominate validator - -When removing stake from a validator, I... - -- **must** select a validator I want to un-nominate 1002-STKE-040 -- **must** [connect to a Vega wallet/key](0002-WCON-connect_vega_wallet.md) with nominated stake, if not already 1002-STKE-041 -- - **must** have the option of withdrawing nominated amount at the end of the epoch (and maintain the staking income for the current epoch) 1002-STKE-053 -- **should** have the option of withdrawing nomination amount now immediately (and forfeit the staking income) -- **must** set an amount to remove from a validator 1002-STKE-044 - - **must** be able populate with the total delegated amount at the time where un-nominate will happen 1002-STKE-045 - - **must** be warned if amount is greater than the amount that will be on that validator at the end of the epoch 1002-STKE-046 - 1002-STKE-047 -- **must** submit un-nominate [Vega transaction](0003-WTXN-submit_vega_transaction.md) 1002-STKE-048 -- **must** see feedback that the un-nomination has been registered, and that the un-nominated amount is now available for re-nomination 1002-STKE-049 - -... so that I can use this stake for another validator etc. - -note: if a user just wishes to seel their tokens and not wait for the end of an epoch they could simply [disassociate](1004-ASSO-associate.md#disassociate) (as long as tokens are not held by vesting contract) \ No newline at end of file diff --git a/user-interface/2002-SINC-staking-income.md b/user-interface/2002-SINC-staking-income.md deleted file mode 100644 index 79a1a709a..000000000 --- a/user-interface/2002-SINC-staking-income.md +++ /dev/null @@ -1,32 +0,0 @@ -# Staking income - -## Monitor staking rewards - -When checking the staking rewards, I... - -- must be [connected to a Vega wallet](0002-WCON-connect_vega_wallet.md) - -- **must** see when the current epoch ends 2002-SINC-001 -- **should** see when the current epoch started -- **should** see how much the connected wallet might earn at the end of the epoch (with some assumptions) - - **should** see the balance of all infrastructure fee accounts for the current epoch - - **should** see real time validator score for the current epoch - - **should** see if any validators have had a penalty this epoch -- **must** see the sum of Tokens I have nominated to each validator in the current epoch 2002-SINC-007 -- **should** see what percentage of my Tokens I have nominated to each validator in the current epoch -- **must** see the staking income I have received for each epoch previous 2002-SINC-009 - - **must** see asset name 2002-SINC-010 - - **must** see balance of asset 2002-SINC-011 - - **must** see type (organic or treasury) 2002-SINC-012 -- **should** see the staking income by epoch, broken down by validator -- **should** see the staking income by epoch, broken down by market - -- **should** see all income values expressed in a single currency - -- **must** see current asset balances for connected wallets 2002-SINC-016 -- **must** see link to [withdraw](1002-WITH-withdraw.md) assets 2002-SINC-017 - -- **must** see where I can see where I did not receive full income because the validator suffered penalties 2002-SINC-018 - -...so that I can make decisions about my staking - \ No newline at end of file diff --git a/user-interface/3001-VOTE-vote.md b/user-interface/3001-VOTE-vote.md deleted file mode 100644 index 006fb7416..000000000 --- a/user-interface/3001-VOTE-vote.md +++ /dev/null @@ -1,157 +0,0 @@ -# Vote - -Background: [Governance spec](../protocol/0028-GOVE-governance.md) -and [docs](https://s.vega.xyz/s/mainnet/concepts/vega-protocol#governance). - -There are a few things that can be governed on Vega... - -- Network parameters (variables used by the network) -- Markets (creation and changes to existing) -- Assets (creation on changes to existing) -- "Freeform", which has no affect on the network but can be used to to measure token holders views - -To make proposal: a user will require an amount of the Governance token [associated](1004-ASSO-associate.md) with their key. - -To vote: a user will require [associated](1004-ASSO-associate.md) Governance tokens (or in the case of market change proposals they could have an active liquidity provision). - -Each vote has a weight behind it based on the number of associate tokens or the liquidity provision's equity like share at the point in time that the vote closes. - -Each proposal type will have a majority required (vote weight in favour) and a participation required (the turn out for the vote needs to be sufficient to be valid). - -A proposal has a life cycle and various statuses to track it's progress. proposals that accept votes can have a few different statuses. A short hand is used in these ACs: - -- Open = Accepting votes (includes `waitingForNodeVote`) -- To enact = passed but not yet enacted (`pending) -- Closed = was accepting votes but deadline has passed (e.g. `passed`, `rejected` etc) -- `Failed` = did not get to the point of accepting votes. - -## list of proposals - -When looking for a particular proposal or wanting to see what proposals are open, soon to enact or closed, I... - -- **must** see link to details on how governance works in docs (3001-VOTE-001) -- **must** see link(s) to make proposals (3001-VOTE-002) -- **must** if there are no proposals, see that there have been no proposals since the last chain checkpoint restore (3001-VOTE-003) -- **must** see open proposals (and ones due for enactment) distinct from others (e.g grouped by "open", "to enact" "closed") (note: freeform proposals do not enact so should be shown as "closed" when "passed") (3001-VOTE-004) -- **should** see proposals sorted with the ones closest to enactment first (within each group) -- **must** see a history of all "closed" proposals (3001-VOTE-006) -- can search for a proposal by: - - **should** be able to search by proposal ID - - **should** be able to search by public key of the proposer - - **should** be abel to search by market ID/name/code (ID may be the same as proposal ID) - - **should** be able to search by asset name/symbol - - **should** be able to search by network parameter - - **should** be able to search by content of proposal description - -for each proposal: - -- **must** see the type of proposal (3001-VOTE-007) -- **must** see the proposal title (3001-VOTE-097) -- **should** see a summary of what the type of proposed change is, without looking at details (network, new market etc) - - for network parameters: **should** see what parameter is being changed and new value - - for network parameters: **could** see what the current values are for that parameter - - for network parameters: **could** see if there are other open proposals for the same parameter - - for new markets: **should** see the type of market (e.g. Future) - - for new markets: **could** see the type trading mode of the market (e.g. auction, continuous) - - for new markets: **should** see the name of the new market - - for new markets: **should** see the code of the new market - - for new markets: **should** see the settlement asset of the new market (not just asset ID but asset Symbol) - - for new markets: **could** see a summary of the oracle used for settlement - - for market changes: **should** see the name of the market being changed - - for market changes: **should** see a summary of what parameters are being changed - - for market changes: **should** see a the proposed values for parameters - - for market changes: **should** see a the current values for that parameter - - for market changes: **could** see if there are other open proposals for the same market - - for new assets: **must** see the name of the new asset (3001-VOTE-026) - - for new assets: **must** see the code of the new asset (3001-VOTE-027) - - for new assets: **must** see the source of the new asset (e.g. ERC20) (3001-VOTE-028) - - for new assets (if source is ERC20): **must** see contract address (3001-VOTE-095) - - for new assets (if source is ERC20): **must** see if the Asset has been whitelisted on the bridge (3001-VOTE-096) - - for asset changes: **must** see name of asset being changed (3001-VOTE-029) - - for asset changes: **must** see the parameter(s) being changed (3001-VOTE-030) - - for asset changes; **must** see the new value for the parameters being changed (3001-VOTE-031) - - for asset changes: **could** see if there are other open proposals for the same parameter(s) - - for asset changes: **should** see the current values for these parameters - - for freeform: **must** see a summary of the proposal (suggest the first x characters of the proposal blob) -- **must** see the proposal status e.g. passed, open, waiting for node to vote) (3001-VOTE-035) - - for new asset proposals: **must** see if an asset has not yet been whitelisted on the bridge (3001-VOTE-036) -- for open proposals: **must** see a summary of how the vote count stands and if it looks like proposal will pass or not (note some of these are repeated in more details in the [details section](#details-of-a-proposal)) (3001-VOTE-037) - - if the proposal failed (had the status of "failed", because it was an invalid on submission) they **should not** appear in the list (instead the proposer will see this after submission) - - if the proposal looks like it will fail due to insufficient participation: **should** see "participation not reached" - - else if: proposal looks like it might fail due to insufficient majority (and is not a market change proposal): **should** see "Majority not reached" - - else if (is a market change proposal) and is likely to pass because of liquidity providers vote: **should** see "set to pass by Liquidity provider vote" - - else if: proposal is likely to pass: **should** see "set to pass" - - **must** see when (date/time) voting closes on proposal (3001-VOTE-043) -- for (non-freefrom) proposals that have passed but not enacted: **must** see when they will enact (3001-VOTE-044) -- for (non-freefrom) proposals that have passed but not enacted: **should** see when (date/time) voting closed -- for (non-freeform) proposals that enacted: **must** see when they enacted (3001-VOTE-046) -- for freeform proposals that have passed: **must** see when they passed (3001-VOTE-047) -- for proposals that did not pass due to lack of participation: **must** see "Participation not reached" (3001-VOTE-048) -- for proposals that did not pass due to lack of majority: **must** see "Majority not reached" (3001-VOTE-049) -- for proposals that did not pass due to failure: **must** see "Failed" (3001-VOTE-050) -- for proposals that I ([connected Vega](./0002-WCON-connect_vega_wallet.md) key) have voted on: **should** see my vote (for or against) - -...so I can see select one to view and vote, or view outcome. - -## Details of a proposal - -When looking at a particular proposal, I... - -- see [the same details in the list of proposals](#list-of-proposals) and: -- **must** have option to see raw JSON of proposal (3001-VOTE-052) -- **should** display the proposed change details displayed in a human readable format (e.g. with market id, shown along with that market name) - -- **must** see the rationale title (3001-VOTE-054) -- **must** see the full rationale description if there is one (3001-VOTE-055) -- **must** see rationale description rendered with markdown (3001-VOTE-101) - -For open proposals: - -- **must** show a summary of vote status (base on the current total amount associated tokens, note this could change before the vote ends) (3001-VOTE-057) -- **must** see if the token vote has met a required participation threshold (3001-VOTE-058) -- **must** see the sum of tokens that have voted so far (3001-VOTE-059) -- **should** see sum of tokens that have voted as a percentage of total voted -- **should** see what the participation threshold is for this proposal (note this is set per proposal once the proposal hits the chain based on the current network params, incase a proposal is set to enact that changes threshold) -- **must** see if the Token vote has met the required majority threshold (3001-VOTE-062) -- **must** see the sum of tokens that have voted in favour of the proposal (3001-VOTE-064) -- **should** see sum of tokens that have votes in favour of proposal as percentage of total associated -- **should** see what the majority threshold is for this proposal (note this is see per proposal, incase a proposal is set to enact that changes threshold) - -For open market change proposals, all of the above and: - -- **must** show a summary of vote status (base on the current equality like share, note this could change before the vote ends) (3001-VOTE-067) -- **must** see if the equity like share vote has met the required participation threshold (3001-VOTE-068) -- **must** see the equity like share % that has voted so far (3001-VOTE-069) -- **should** see what the equity like share threshold is for this proposal (note this is see per proposal, incase a proposal is set to enact that changes threshold) -- **must** see if the equity like share vote has met the required majority threshold (3001-VOTE-071) -- **must** see the equity like share as percentage that has voted in favour of the proposal (3001-VOTE-072) -- **must** see what the majority threshold is for this proposal (note this is see per proposal, incase a proposal is set to enact that changes threshold) (3001-VOTE-073) - -For `closed` market change proposals, all of the above and: - -- **must** see all of above but values at time of vote close (3001-VOTE-074) - -... so I can see what I am voting for and the status of the vote. - -## Can vote on an open proposals - -When looking to vote on the proposal, I... - -- **must** see an option to [connect to a Vega wallet/key](./0002-WCON-connect_vega_wallet.md) (3001-VOTE-075) -- **must** be [connected to a Vega wallet/key](./0002-WCON-connect_vega_wallet.md) (3001-VOTE-076) - - **must** see sum of tokens I have [associated](1027-ASSO-associate.md) (3001-VOTE-100) - - **should** see what percentage of the total [associated](1027-ASSO-associate.md) tokens I hold - - **should**, if I have 0 tokens, see link to [associate](1027-ASSO-associate.md) - - **must** see my current vote for, against, or not voted (3001-VOTE-079) - - **must** see option to vote for or against (3001-VOTE-080) - - **must** see option to change my vote (vote again in same or different direction) (3001-VOTE-090) - -For open market change proposals, all of the above and: - - **must** see your equity like share on the market you are voting on (3001-VOTE-092) - -for both: - -- **must** see feedback of my vote [Vega transaction](0003-WTXN-submit_vega_transaction.md) (3001-VOTE-093) - -...so that I can cast my vote and see the impact it might have. - \ No newline at end of file diff --git a/user-interface/3002-PROP-propose.md b/user-interface/3002-PROP-propose.md deleted file mode 100644 index f5111308d..000000000 --- a/user-interface/3002-PROP-propose.md +++ /dev/null @@ -1,62 +0,0 @@ -# Propose - -Background: [Governance spec](../protocol/0028-GOVE-governance.md) -and [docs](https://docs.vega.xyz/docs/mainnet/concepts/vega-protocol#governance). - -## Associate tokens - -When looking to propose something, I... - -- **must** see see a link to detailed explanation of Governance in the docs (3002-PROP-001) -- **must** see that the governance token is an ethereum ERC20 token and needs to attributed (or [associated](1004-ASSO-associate.md)) to a Vega wallet for use on Vega (3002-PROP-002) -- **must** see that I need some associated tokens, and a link to [associate](1004-ASSO-associate.md) (3002-PROP-003) - -... so I have a sufficient vote weight to propose. - -## Select proposal type - -When making a proposal, I... - -- **must** select a proposal type (3002-PROP-005) - -...so I get the appropriate form and information about rules for that type of proposal. e.g. min enactment and vote duration. - -## Populate a proposal form - -When making a proposal, I... - -- **must** input a rationale title and description (3002-PROP-006) -- **must** see the rules (min vote duration and enactment delay) for this proposal type (3002-PROP-008) -- **should** see the minimum required tokens or LP vote required to make a proposal Spam protection and network parameters -- if anything except market change: **must** be warned if the amount I have associated is less the the minimum required to propose for this proposal type (due to Spam protection or network parm, whichever is greater) (3002-PROP-009) -- if market change: **must** be warned if the amount of tokens I have less than the minimum tokens required to propose a change (due to Spam protection or network params) (3002-PROP-020) -- if market change: **must** be warned if the amount of I have less than the minimum required equity like share to propose a change (due to Spam protection or network params) (3002-PROP-022) -- **should** see the balance of associated Governance tokens - -### Detail on specific proposals - -Go to the following for detail on each proposal type: - -- [Propose new Market](./1006-PMARK-propose_new_market.md) -- [Propose change(s) to market](./1007-PMAC-propose_market_change.md) -- [Propose new asset](1008-PASN-propose_new_asset.md) -- [Propose change(s) to asset](1009-PASC-propose_asset_change.md) -- [Propose change to network parameter(s)](1010-PNEC-propose_network.md) -- [Propose something "Freeform"](1011-PFRO-propose_freeform.md) - -### Submit proposal - -- **must** submit the proposal [Vega transaction](0003-WTXN-submit_vega_transaction.md) (3002-PROP-011) -- **must** see the feedback on the [Vega transaction](0003-WTXN-submit_vega_transaction.md) (3002-PROP-012) -- If there is an error on the proposal: - - **must** be shown an error message will all of the error details from the API (3002-PROP-013) - - **must** see the proposal form populated with all the same values just submitted (3002-PROP-014) - - **should** see error messages highlighted on the inputs that require user attention -- If the proposal was successful: - - **must** be shown it was successful (3002-PROP-016) - - **should** be prompted to vote on the proposal - - **should** be prompted to share the proposal detail page to encourage others to vote - - **must** see a link to the proposal detail page (3002-PROP-019) - -...so that the proposal is listed on the chain and I and others can vote for or against it. - \ No newline at end of file diff --git a/user-interface/3003-PMAN-propose_new_market.md b/user-interface/3003-PMAN-propose_new_market.md deleted file mode 100644 index 37b0d5c78..000000000 --- a/user-interface/3003-PMAN-propose_new_market.md +++ /dev/null @@ -1,8 +0,0 @@ -# Propose new Market - -- **must** input the new market definition (3003-PMAN-001) - -## Submit proposal - -back to [Propose](./3002-PROP-propose.md) for details on proposing - \ No newline at end of file diff --git a/user-interface/3004-PMAC-propose_market_change.md b/user-interface/3004-PMAC-propose_market_change.md deleted file mode 100644 index 0cf8144bc..000000000 --- a/user-interface/3004-PMAC-propose_market_change.md +++ /dev/null @@ -1,13 +0,0 @@ -## Propose market change - -- select a market to change - - **must** by market ID (3004-PMAC-001) - - **should** by searching for market name - - **should** by searching for market code -- **must** link to existing details on block explorer (3004-PMAC-004) -- **should** see any open or to be enacted changes on same market - -## Submit proposal - -back to [Propose](./3002-PROP-propose.md) for details on proposing - \ No newline at end of file diff --git a/user-interface/3005-PASN-propose_new_asset.md b/user-interface/3005-PASN-propose_new_asset.md deleted file mode 100644 index b532acc27..000000000 --- a/user-interface/3005-PASN-propose_new_asset.md +++ /dev/null @@ -1,8 +0,0 @@ -# Propose new Asset - -- **must** input the new asset definition (3005-PASN-001) - -## Submit proposal - -back to [Propose](./3002-PROP-propose.md) for details on proposing - \ No newline at end of file diff --git a/user-interface/3006-PASC-propose_asset_change.md b/user-interface/3006-PASC-propose_asset_change.md deleted file mode 100644 index 884bfacf7..000000000 --- a/user-interface/3006-PASC-propose_asset_change.md +++ /dev/null @@ -1,13 +0,0 @@ -## Propose asset change - -- select a asset to change - - **must** by market ID (3006-PASC-001) - - **should** by searching for asset name - - **should** by searching for asset symbol -- **must** see link to existing details on block explorer (3006-PASC-004) -- **should** see any open or to be enacted changes on this asset - -## Submit proposal - -back to [Propose](./3002-PROP-propose.md) for details on proposing - \ No newline at end of file diff --git a/user-interface/3007-PNEC-propose_network.md b/user-interface/3007-PNEC-propose_network.md deleted file mode 100644 index f6548c45b..000000000 --- a/user-interface/3007-PNEC-propose_network.md +++ /dev/null @@ -1,14 +0,0 @@ -# Propose change to network parameter(s) - -When proposing a change to network parameter, I... - -- **should** see any other "open or passed but not enacted" proposals to the same parameter -- **must** input the parameter I want to change (3007-PNEC-002) -- **must** input the proposed value (3007-PNEC-003) - -- **should** see the the current value - -## Submit proposal - -back to [Propose](./3002-PROP-propose.md) for details on proposing - \ No newline at end of file diff --git a/user-interface/3008-PFRO-propose_freeform.md b/user-interface/3008-PFRO-propose_freeform.md deleted file mode 100644 index 6d897b5bc..000000000 --- a/user-interface/3008-PFRO-propose_freeform.md +++ /dev/null @@ -1,8 +0,0 @@ -# Propose freeform - -- **must** not see anything felids enactment (because freeform do not enact) (3008-PFRO-001) - -## Submit proposal - -back to [Propose](./3002-PROP-propose.md) for details on proposing - \ No newline at end of file diff --git a/user-interface/4001-TRES-view_treasury_rewards.md b/user-interface/4001-TRES-view_treasury_rewards.md deleted file mode 100644 index 3a567de14..000000000 --- a/user-interface/4001-TRES-view_treasury_rewards.md +++ /dev/null @@ -1 +0,0 @@ -# View treasury rewards \ No newline at end of file diff --git a/user-interface/5001-LIQF-liquidity_opportunities.md b/user-interface/5001-LIQF-liquidity_opportunities.md deleted file mode 100644 index 0273bd132..000000000 --- a/user-interface/5001-LIQF-liquidity_opportunities.md +++ /dev/null @@ -1 +0,0 @@ -# Find liquidity provision opportunities \ No newline at end of file diff --git a/user-interface/5002-LIQP-provide_liquidity.md b/user-interface/5002-LIQP-provide_liquidity.md deleted file mode 100644 index b802fd92f..000000000 --- a/user-interface/5002-LIQP-provide_liquidity.md +++ /dev/null @@ -1 +0,0 @@ -# Provide liquidity \ No newline at end of file diff --git a/user-interface/5003-LIQI-liquidity_income.md b/user-interface/5003-LIQI-liquidity_income.md deleted file mode 100644 index 0d797b1e5..000000000 --- a/user-interface/5003-LIQI-liquidity_income.md +++ /dev/null @@ -1 +0,0 @@ -# Review liquidity income \ No newline at end of file diff --git a/user-interface/6001-MARK-find_markets.md b/user-interface/6001-MARK-find_markets.md deleted file mode 100644 index 6eef2ecbf..000000000 --- a/user-interface/6001-MARK-find_markets.md +++ /dev/null @@ -1 +0,0 @@ -# Find markets \ No newline at end of file diff --git a/user-interface/6002-MDET-market-details.md b/user-interface/6002-MDET-market-details.md deleted file mode 100644 index 7dc5614fa..000000000 --- a/user-interface/6002-MDET-market-details.md +++ /dev/null @@ -1 +0,0 @@ -# Market details \ No newline at end of file diff --git a/user-interface/6003-ORDB-order_book.md b/user-interface/6003-ORDB-order_book.md deleted file mode 100644 index 411962b16..000000000 --- a/user-interface/6003-ORDB-order_book.md +++ /dev/null @@ -1 +0,0 @@ -# Order book \ No newline at end of file diff --git a/user-interface/6004-PHIS-price_history.md b/user-interface/6004-PHIS-price_history.md deleted file mode 100644 index 9ee7f2edf..000000000 --- a/user-interface/6004-PHIS-price_history.md +++ /dev/null @@ -1 +0,0 @@ -# Price history \ No newline at end of file diff --git a/user-interface/6005-THIS-trade_history.md b/user-interface/6005-THIS-trade_history.md deleted file mode 100644 index f967cb4b5..000000000 --- a/user-interface/6005-THIS-trade_history.md +++ /dev/null @@ -1 +0,0 @@ -# Trade history \ No newline at end of file diff --git a/user-interface/7001-COLL-collateral.md b/user-interface/7001-COLL-collateral.md deleted file mode 100644 index a569541c4..000000000 --- a/user-interface/7001-COLL-collateral.md +++ /dev/null @@ -1 +0,0 @@ -# Collateral \ No newline at end of file diff --git a/user-interface/7002-SORD-submit_orders.md b/user-interface/7002-SORD-submit_orders.md deleted file mode 100644 index b785c0a15..000000000 --- a/user-interface/7002-SORD-submit_orders.md +++ /dev/null @@ -1,185 +0,0 @@ -# Submit order -As a user I want change my exposure on a market (e.g. open a position, increase or decrease my open volume), I want to submit an order with instructions for how my order should be executed so I have some control over the price that I get, as well as if when/my order should stay on the book. See [specs about orders](../protocol#orders) for more info. - -## Before seeing a "deal ticket" - -When looking at a market, I... - -- **must** see/select the [Market](./7001-DATA-data_display.md#market) I am submitting the order for (7002-SORD-001) -- **must** see the current `status` of the market (7002-SORD-061) - - if the market is in a state of `rejected`, `canceled` or `closed`: - - - **must** see that the market is not accepting orders and never will be (7002-SORD-062) - - if the market is in a state of `tradingTerminated`: - - - **must** see that the market is not accepting orders and never will be (7002-SORD-063) - - **should** see the [price](7001-DATA-data_display.md#quote-price) that was used to settle the market - - **should** see a link to oracle spec and data - - if the market is in a state of `settled`: - - - **must** see that the market is not accepting orders and never will be (7002-SORD-066) - - **should** see the oracle events that terminated the market - - **should** see a link to oracle spec and data - - if the market is in a state of `suspended`: - - - **should** see what suspended the market - - **should** see the conditioned required for the auction to end - - **should** see the current data values that the auction end is measured against (e.g. Supplied stake) - -...so I know if the market is accepting orders. - -The rest of this document only applies if the state of the market is `pending`, `active` or `suspended`: - -## Deal ticket - -When populating a deal ticket I... - -- **must** see the current market trading mode (Continuous, Auction etc) (7002-SORD-002) - -- If I have a 0 total balance of the settlement asset: **must** be warned that I have insufficient collateral (but also allow you to populate ticket because I might want to try before I deposit) (7002-SORD-003) - - **should** have a way to easily deposit the required collateral - -- **must** select a side/direction e.g. long/short (note: some implementations may do this with two different submit buttons long/short rather than a toggle) (7002-SORD-004) - -- **must** be able to select the [order type](../protocol/0014-ORDT-order_types.md) that I wish to submit (7002-SORD-005) - - **must** see limit order (7002-SORD-006) - - **must** see market order (7002-SORD-007) - - **should** see pegged order - - **should** see liquidity provision - -## Order size - -...need to select a size, when selecting a size for my order, I... - -- **must** input an order [size](7001-DATA-data_display.md#size) (aka amount or contracts) (7002-SORD-010) - - **should** have the previous value for the selected market available e.g. pre-populated (last submitted or last changed) - - **should** be able to quickly change the size by the market's min-contract size e.g. hit up/down on the keyboard to increase - - **should** be able to use modifier keys (SHIFT, ALT etc) to increase/decrease in larger increments with arrows - - **would** like to be able to enter a number followed be "k" or "m" or "e2" etc. to make it thousands or millions or hundreds, etc. -- **must** be warned (pre-submit) if input has too many digits after the decimal place for the market's ["position" decimal places](7001-DATA-data_display.md#size) (7002-SORD-016) - -... so I get the size of exposure (open volume) that I want - -## Price - Limit order - -... if wanting to place a limit on the price that I trade at, I... - -- **must** enter a [price](7001-DATA-data_display.md#quote-price). (7002-SORD-017) -- **must** see the price unit (as defined in market) (7002-SORD-018) - - **should** be able quickly pre-populate the price with the current mark price (if there is one, 0 if not) e.g. by focusing the input and hitting up/down - - **should** have the previous value for the selected market pre-populated (last submitted or last changed) - - **should** be able to hit up/down on the keyboard to increase the price by the market's tick size (if set, or smallest increment) - - **should** be able to use modifier keys (SHIFT, ALT etc) to increase/decrease in larger increments with arrows - - **would** like to be able to enter a number followed be "k" or "m" or "e2" etc. to make it thousands or millions or hundreds, etc. -- **must** be warned (pre-submit) if the input price has too many digits after the decimal place for the market ["quote"](7001-DATA-data_display.md#quote-price) (7002-SORD-059) - -... so that my order only trades at up/down to a particular price - -## Market order - -... if wanting to trade regardless of price (or assuming that the market is liquid enough that the current best prices are enough of an indication of the price I'll get)... - -- **must not** see a price input (7002-SORD-019) -- **should** be warning if the market is in auction and the market order may be rejected - -... so I can quickly submit an order without populating the ticket with elements I don't care about - -## Pegged - -... submit an order where the price is offset from a price in system (best bid etc) - -- TODO - -... so my order will move with the market - -## Time in force - -... should to select a time in force, when selecting a time in force, I... - -- **must** select a time in force - - Good till canceled `GTC` - not applicable to Market orders (7002-SORD-023) - - Good till time `GTT` - not applicable to Market orders (7002-SORD-024) - - Fill or kill `FOK` (7002-SORD-025) - - Immediate or cancel `IOC` (7002-SORD-026) - - Good for normal trading only `GFN` - not applicable to Market orders (7002-SORD-027) - - Good for auction only `GFA` - not applicable to Market orders (7002-SORD-028) -- **should** only be warned if the time in force is not applicable to the order type I have selected -- **should** only be warned if the time in force is not applicable to current period's trading mode -- if the user has not set a preference: market orders **must** default to `IOC` (7002-SORD-030) -- if the user has not set a preference: limit orders **must** default to `GTC` (7002-SORD-031) - -... so I can control if and how my order stays on the order book - -## Auto Populating a deal ticket non-manual methods - -- TODO Populate by selecting a size/price in the order book -- TODO Populate by selecting a size/price in the chart -- TODO Populate by selecting a size/price in the depth chart -- TODO Input price as a % of account, given the current price field -- **should** be able to determine how much leverage I'd like (given general balance, and other inputs) - -## See the potential consequences of an order before it is submit -... based on the current inputs I'd like an indication of the consequences of my order based on my position and the state of the market, I... - -- **could** see my resulting open volume -- **could** see the amount this order might move the market in percentage terms -- **could** see what the new best prices of the market would be after placing this order (assuming my order moves the market) -- **could** see new volume weighted average entry price if not 0 -- **could** see and indication the volume weighted price that this particular order -- **could** see an indication of how much of the order will trade when it hits the book and how much might remain passive -- **could** see a new liquidation level -- **could** see an estimate of the fees that will be paid (if any) -- **could** see my "position leverage" TODO - define this -- **could** see my "account leverage" TODO - define this -- **could** see an amount of realized Profit / Loss -- **could** see any change in margin requirements (if more or less margin will be required) -- **could** see the notional value of my order - -... so that I can adjust my inputs before submitting - -## Submit an order - -... need to submit my order, when submitting my order, I... - -- if not already connected: **must** see a prompt to [connect a Vega wallet](0002-WCON-connect_vega_wallet.md) - -- **must** submit the [Vega submit order transaction](0013-WTXN-submit_vega_transaction.md). (7002-SORD-039) - -- **must** see feedback on my order [status](https://docs.vega.xyz/docs/mainnet/grpc/vega/vega.proto#orderstatus) (not just transaction status above) (7002-SORD-040) - - Active (aka Open) (7002-SORD-041) - - Expired (7002-SORD-042) - - Cancelled. see the txn that cancelled it and a link to the block explorer, if cancelled by a user transaction. (7002-SORD-043) - - Stopped. see an explanation of why stopped (7002-SORD-044) - - Partially filled. **must** see how much of the [size](7001-DATA-data_display.md#size) if filled/remaining (7002-SORD-045) - - Filled. Must be able to see/link to all trades that were created from this order. (7002-SORD-046) - - Rejected: **must** see the reason it was rejected (7002-SORD-047) - - Parked: **must** see an explanation of why parked orders happen (7002-SORD-048) -- All feedback must be a subscription so is updated as the status changes (7002-SORD-053) - - **could** repeat the values that were submitted (order type + all fields) - -... so that I am aware of the status of my order before seeing it in the [orders table](6002-MORD-manage_orders.md). - -... so I get the sort of order, and price, I wish. - -## Manage positions and order -After submitting orders I'll want to [manage orders](7003-MORD-manage_orders.md). If my orders resulted in a position I may wish to [manage positions](7004-POSI-positions.md)). - -_____ - -# Typical order scenarios to design/test for - -Market in continuous trading: -- Limit order, Long, GTC, with a price that is lower than the current price -- Limit order, Short, GFN, that crosses the book but only gets a partial fill when order is processed -- Market order, IOC, that increases open volume (aka size of position) -- a limit order GFA when market is in Auction -- an limit that reduces exposure from something to 0 -- a limit order, FOK, that squares and reverses e.g. I'm long 10, I short 20 to end short 10 - -Market in auction: -- Attempt Market order in Auction mode: should warn order is invalid -- Attempt limit order GFN when market is normally Continuous (but currently in auction), should warn that GFN will not work diff --git a/user-interface/7003-MORD-manage_orders.md b/user-interface/7003-MORD-manage_orders.md deleted file mode 100644 index 38e4b0e5e..000000000 --- a/user-interface/7003-MORD-manage_orders.md +++ /dev/null @@ -1,202 +0,0 @@ -# Manage orders - -Users place orders to describe the trades they would like to make: buy or sell, at what price, how long it is valid for etc. In many cases they can [amend](#amend-order---price) or [cancel](#cancel-orders) these orders while they are still active, for example: changing price. - -Once a user has placed an order they may wish to confirm it's [status](https://docs.vega.xyz/docs/mainnet/graphql/enums/order-status) in a [list](#orders-list) of other orders. e.g. whether it has been accepted, filled, how close it is to being filled etc. Users may be interested in the price of their orders relative to the price of the market and how much of the order's size has been filled. - -Orders can also be placed on behalf of a user/party via [liquidity](#liquidity-order-shapes) or [pegged](#pegged-order-shapes) order shapes. These order cannot be amended on canceled in the same way as other orders. - -Markets also have [statuses](https://docs.vega.xyz/docs/mainnet/graphql/enums/market-state) that may affect how a user perceives the state of an order, e.g if the order was placed while in "normal" continuous trading, but the market is now in auction. - -## Orders list - -User will have differing needs/preferences in terms of what they want to see about an order and how orders are grouped and listed. It is common for interfaces to allow users to customize how orders are displayed. - -### Field customization - -- **should** have the ability to select what [fields/data](#fields) are shown for each order in the list -- **should** have the ability to change the order of fields (e.g. table columns) -- **should** have the ability to give each column in the list more or less space - -### Fields - -When looking at a list of orders, I... - -- **must** see [status](https://docs.vega.xyz/docs/mainnet/graphql/enums/order-status) of the order (7003-MORD-001), and therefore (nested bullets added for context, ACs are on fields that follow)... - - `Active​` - - How much of the order is filled / remains unfilled - - How close the mark price is to order - - If this order was filled at the limit price what would what effect would it have on realized PnL - - I may want to amend or cancel this order - - `Expired​` - - When did it expire - - How much was filled / remaining - - `Cancelled​` - - What canceled it? When did I cancel it TODO double check that "cancelled" only comes from user action - - `Stopped​` - - What stopped it (e.g. was it because an [FOC](9001-DATA-data_display.md#time-in-force) that was not filled, or because of margin availability) - - `Filled​` - - What was the average fill price I got for this order - - `Rejected​` - - Why was this order rejected (show `rejectedReason`) - - `PartiallyFilled​` - - How much was filled before the order was canceled - - What was the average fill price I got for this order - - `Parked​` - - Why is the market currently in auction - - Link to pegged shape (see below) - -- **must** see what [market](9001-DATA-data_display.md#market) an order is related to (either code, ID or name, preferable name) (7003-MORD-002) - - **should** see what the `status` is of the market (particularly if it is not "normal") -- **must** see the [size](9001-DATA-data_display.md#size) of the order (7003-MORD-003) -- **must** see the [direction/side](9001-DATA-data_display.md#direction--side) (Long or Short) of the order (this can be implied with a + or negative suffix on the size, + for Long, - for short) (7003-MORD-004) -- **must** see [order type](9001-DATA-data_display.md#order-type) (7003-MORD-005) -- if order created by [pegged or liquidity provision shape](9001-DATA-data_display.md#order-origin): **should** see order origin - - **could** see what part of the liquidity shape or pegged order shape this relates to or it's offset+reference See [pegged orders](#pegged-order-shapes) and [liquidity provisions](#liquidity-order-shapes) shapes below. - - **could** see link to full shape - -- **should** see how much of the order's [size](9001-DATA-data_display.md#size) has been filled e.g. if the order was for `50` but so far only 10 have traded I should see Filled = `10`. Note: this is marked as a should because in the case of Rejected order and some other scenarios it isn't relevant. -- **should** see how much of the order's [size](9001-DATA-data_display.md#size) remains. - -- if order type = `Limit`: **must** see the Limit [price](9001-DATA-data_display.md#quote-price) that was set on the order -- if order type = `Market`: **must** not see a price for active or parked orders, a `-`, `Market` or `n/a` is more appropriate (API may return `0`). - -- **must** see the [time in force](9001-DATA-data_display.md#time-in-force) applied to the order (can be abbreviated here) (7003-MORD-006) -- **should** see "created at" [time](9001-DATA-data_display.md#time) -- **could** see updated at (this is used by the system when an order is amended, or repriced (in pegged and LP) not sure this in needed) - -- **should** see time priority (how many orders are before mine at this price) - -- if the order is `Active` & **not** part of a liquidity or peg shape: **must** see an option to [amend](#amend-order---price) the individual order (7003-MORD-007) -- if the order is `Active` & is part of a liquidity or peg shape: **must** **not** see an option to amend the individual order (7003-MORD-008) - - **could** see a link to amend shape -- if the order is `Active` & **not** part of a liquidity or peg shape: **must** see an option to [cancel](#cancel-orders) the individual order -- if the order is `Active` & is part of a liquidity or peg shape: **must** **not** see an option to cancel the individual order - - **could** see a link to cancel shape - -... so I can understand the state of my orders and decide what to do next -### Filters - -When looking at a list of orders, I... - -- **should** have the ability to see all orders regardless of status in one list -- **should** have the ability to see only active & parked orders -- **should** have the ability to see only non-active & parked order (i.e. all orders that do not have the status of Active & Parked) -- **could** have the ability to filter by any field(s) - - where a field is an enum: **should** be able to select one on or more values for a field that should be included - -... so I can focus on the type of orders statuses etc that I want to at that time - -### Sorting - -When looking at a list of orders, I... - -- **should** be able to sort the list (both directions) by any field in the order list - - **should** be able to add a secondary sort. e.g. by market then by date -- **should** have the default sorted by created time (or updated time if newer), with newest at the top -- **should** retain sorting preferences between switching views / browser reload - -... so the sort order matches my mental model and or makes it easy for me to find the orders I am looking for -### Grouping - -When looking at a list of orders, I... - -- **should** be able to group orders by any field e.g. by market, lp etc -- **should** have default grouping by market - -... so I can make decisions easily on a set of orders -## Cancel orders - -When looking to take order out of the book, I... - -- **must** select whether to cancel an individual order or all orders on a market (7003-MORD-009) -- **could** be able to cancel all orders for markets in a given settlement asset -- **would** like to cancel all orders for all markets -- **must** be able to submit the [Vega transaction](0003-WTXN-submit_vega_transaction.md) to cancel order(s) (7003-MORD-010) - - **could** show the margin requirement reduction/increase that will take place before submitting -- **must** see feedback on order status after the [Vega transaction](0003-WTXN-submit_vega_transaction.md) (7003-MORD-011) - - -... so that the order will not be filled and I don't end up with a change in my position that I was not expecting -## Amend order - price - -Read more about [order amends](../protocol/0004-AMND-amends.md). - -When looking to amend an order, I... - -- **must** be able to amend the price of an order (7003-MORD-012) - - **could** be warned if the price change will, given the current market, fill the order right away - - **must** be warned (pre-submit) if the input price has too many digits after the decimal place for the market ["quote"](DATA-data_display.md#quote-price) (7003-MORD-013) -- **must** submit the Amend order [Vega transaction](0003-WTXN-submit_vega_transaction.md) (7003-MORD-014) -- **must** see the status after the transaction (see [submit order](7002-SORD-submit_orders.md#submit-an-order)) (7003-MORD-015) - -... so the order is more likely to get filled or will be filled at a more competitive price - -## Amend order - other than price - -`TODO` - Acceptance criteria for other types of order amend - -## On a price history chart - -when looking at a price history chart, I... - -- **would** like to see all my active orders shown on the vertical axis -- **would** like to drag orders to change their price - -... so I can see my orders in context of price history -## On a depth chart - -when looking at a depth chart, I... - -- **would** like to see all my active orders shown on the horizontal axis -- **would** like to drag orders to change their price - -... so I can see my orders in context of price history - -## In order book - -when looking at an order book, I... - -- **would** like to see all my active orders shown next to the prices they have - -... so I can see my orders in context of price history - -## Pegged order shapes - -When looking to understand the state of a pegged order shape... - -- **should** see the whole shape of a pegged order -- **must** see the reference, offset and direction for each part pegged order (7003-MORD-016) -- **should** see the current price for each buy/sell -- **should** see the filled/remaining for each part of the order -- when order is not `Active`: **should** show the order status (perhaps instead of price) -- **should** be able to cancel the whole pegged order -- **would** like to see link to edit the shape of a pegged order -- **would** like to see the date submitted/updated - -... so I can decide if I wish to amend or cancel my pegged order shape - -## Cancel Pegged order shapes - -`TODO` - -## Liquidity order shapes - -When looking to understand the state of a liquidity provision, with a provided shape... - -- **would** like to see the liquidity commitment order status (`pending`, `active`, `parked`, `canceled` etc) -- **would** like to see the fee bid - -- **should** see the whole shape of a liquidity order -- **must** see the reference, offset and direction for each part liquidity order order (7003-MORD-017) -- **should** see the current price for each buy/sell -- when order is not `Active`: **should** show the order status (perhaps instead of price) -- **should** see link to cancel the whole liquidity order shape (see [liquidity provision](5002-LIQP-provide_liquidity.md)) -- **would** like to see link to edit the shape of a pegged order (see [liquidity provision](5002-LIQP-provide_liquidity.md)) -- **would** like to see the date submitted/updated - -... so I can decide if I wish to amend or cancel my shape - -## Cancel or amend Pegged order shapes - -See [liquidity provision](5002-LIQP-provide_liquidity.md), \ No newline at end of file diff --git a/user-interface/7004-POSI-positions.md b/user-interface/7004-POSI-positions.md deleted file mode 100644 index beaed90c1..000000000 --- a/user-interface/7004-POSI-positions.md +++ /dev/null @@ -1 +0,0 @@ -# View my Position(s) \ No newline at end of file diff --git a/user-interface/7005-FILL-fills.md b/user-interface/7005-FILL-fills.md deleted file mode 100644 index 1ca8f3e59..000000000 --- a/user-interface/7005-FILL-fills.md +++ /dev/null @@ -1 +0,0 @@ -# My fills \ No newline at end of file diff --git a/user-interface/7006-FEES-fees.md b/user-interface/7006-FEES-fees.md deleted file mode 100644 index 569b4e81d..000000000 --- a/user-interface/7006-FEES-fees.md +++ /dev/null @@ -1 +0,0 @@ -# My Trading fees \ No newline at end of file diff --git a/user-interface/8001-BLOX-transaction_and_blocks.md b/user-interface/8001-BLOX-transaction_and_blocks.md deleted file mode 100644 index 2ab883af0..000000000 --- a/user-interface/8001-BLOX-transaction_and_blocks.md +++ /dev/null @@ -1 +0,0 @@ -# Transaction and block content \ No newline at end of file diff --git a/user-interface/9001-DATA-data_display.md b/user-interface/9001-DATA-data_display.md deleted file mode 100644 index 49721b7f3..000000000 --- a/user-interface/9001-DATA-data_display.md +++ /dev/null @@ -1,65 +0,0 @@ -# Data display - -This is a definition of some common data types and the rules about the displaying them. These are referenced in other acceptance criteria to avoid repetition. - -## Size - ->aka contracts, volume, amount. - -This is set per-market and represent the number of contracts that are being brought or sold. - -`Market.positionDecimalPlaces` tells us where to put the decimal when displaying the number. Size can be a whole number if `Market.positionDecimalPlaces` = 0, or a [fractional order](../protocol/0052-FPOS-fractional_orders_positions.md) if > 0. -It **should** always be displayed to the full number of decimal places. however, there may be exceptions, e.g. when visualizing on a depth chart, where the precision is not required. - -## Quote price - -> aka. price, quote, level. - -This is set per-market and represent the "price" of an asset. It can have a 1-1 relationship with the settlement asset but it is also possible that products will have different payoff methods, which is one of the reasons we don't just use settlement asset, another being in the future some markets could have multiple settlement assets, another being that we don't want 18DP quotes. - -`Market.decimalPlaces` tells us where to put the decimal when displaying the number. It can be a whole number if `Market.decimalPlaces` = 0, but will not have more decimal places than the [settlement asset](#asset-balances) of a market. - -`Market...quoteName` is used to tell us what to display next to the quote price. For example the `quoteName` could be `USD` but the settlement asset = `DAI`. The Market framework allows for other types of quote (e.g. %, cm and ETC). When looking at a single market it may not be necessary to show the quote name each time you show the price. - - -## Asset balances - -> aka Collateral, account balance, Profit and loss, PnL fees, transfers. - -The is set per Asset and represents the amount of an asset that is held in the bridge. - -Once deposited assets appear in a `general account`. Other account types are created when opening positions, providing liquidity etc. -Vega does not return a `total balance` that is a sum of all accounts in a currency, but users will expect to see one. See the [Collateral spec](../protocol/0005-COLL-collateral.md) for other account types. - -`Asset.decimals` tells the UIs where to put the decimal place. Ethereum assets often have 18 decimal places, but can have less. Forms where you deposit, withdraw or transfer assets must show all decimal places. It may be appropriate to truncate at a certain number of DP in many cases (e.g. `0.01` instead of `0.012345678912345678` event though `0.001 wBTC` may be worth as much as than `0.01 ETH`). At the moment Vega does not have a source of information that allows conversion of currencies or way of knowing that the significant value of an asset is. - -## Market - -Markets do not have names, technically it is the instrument within a market that has the name. Theoretically the same instrument can be traded in multiple markets. if/when this happens a user needs to be able to disambiguate between markets. Each market does have a unique ID, Note: this is a hash of the definition of the market when it was created. -Instruments have both a Name and Code, see [market framework](../protocol/0001-MKTF-market_framework.md) for how these are used. Generally the Code can save space once a user is familiar with the market. The Name is more descriptive and should be the default when discovering markets. It remains to be seen how the community will use these exactly. -Markets can have several statuses and it may be sensible when listing markets to highlight their status. e.g. if a market is usually in continuous trading mode, but is currently in an auction due to low liquidity. The market name field could be augmented to show the status (add an icon etc). - -## Public keys - -> aka Party - -When looking at a public key it is important that the user can get the full public key but it is often appropriate just to show an abbreviated form. The first 6 and last 6 characters are preferable, with an indication that it is truncated e.g. `56d1e6739deac3c5c1ddc6fee876b3217e504a161b5b00fda96b40ed3e8f89b8` as `56d1e6...8f89b8` or just `8f89b8` if enough of a convention has been established. In cases where the key being shows comes from your connected wallet it should also show the name (aka alias) of the key. -Vega public keys are hexadecimal, but the convention is to display them without the preceding `0x` as this is what the Vega API returns. - -## Transaction hash - -> aka Transaction ID, txn, tx - -The transaction [hash](https://www.investopedia.com/terms/h/hash.asp) acts as an identifier for a transaction network. It is hexadecimal and should be displayed with the preceding `0x`. - -## Asset - -> aka Collateral - -### Asset name - -e.g. United states Dollar. Each asset has a name this should be shown (as well as symbol) in places where the user may need to distinguish one asset from another or if the symbol is ambiguous. Vega assets names aught to match their name on their native chain. - -### Asset symbol - -e.g USD. Symbols are short codes for an asset to be used in places low on space where the user is likely to be familiar with the asset. Often all caps but not always, so it is important to display upper and lower case. Vega assets codes aught to match their name on their native chain. \ No newline at end of file diff --git a/user-interface/README.md b/user-interface/README.md deleted file mode 100644 index fe6e03d80..000000000 --- a/user-interface/README.md +++ /dev/null @@ -1,88 +0,0 @@ -# User interface acceptance criteria - -This folder contains descriptions of things that users do when interacting with Vega. The information they need and why they are doing them. These can be referenced in testing and used as input for building new interfaces. - -The acceptance criteria are organized into files, with each file representing a high level user task. These have been listed with the most "upstream" being first, and grouped into similar tasks. - -Each file contains blocks that relate to a low level user task. The block states what the user is trying to do or the context they are in, has a bullet for each thing they need, then states why they are doing it... - -> When doing a thing, I... -> - **must** be able to see some particular number [0000-CODE-000] -> ...so I can decide if I want to continue. - -Each bullet is worded so that it contains a **must**, **should**, **could**, or **would like to**. This gives app developers some indication of the priority of user needs. At the end of each bullet is a code that can be referenced in tests etc. - -These acceptance criteria are not final or intended to be "the truth" but a useful tool, they will be improved over time as more people feedback on using Vega. - -A user is normally interacting with at least 2 applications when doing tasks on Vega, A **Dapp** or interface designed to help users complete specific tasks and a **Wallet** that is only used to authenticate a user's actions and broadcast them to the network. - -## `0000` Wallets, signing transactions and network selection - -- `0001-WALL` [Get and use a Vega wallet](0001-WALL-wallet.md) (This mostly relates to use of a wallet app, for cryptography and broadcast to network) - -These files contain generic user needs for interacting with wallets that are true for all types of interactions that require a wallet. More specific requirements are mentioned where these are referenced. They describe what the user needs from the dapp not the wallet. - -- `0002-WCON` [Connect Vega wallet to a Dapp & select keys](0002-WCON-connect_vega_wallet.md) -- `0003-WTXN` [Submit Vega transaction](0003-WTXN-submit_vega_transaction.md) -- `0004-EWAL` [Connect Ethereum wallet to a Dapp](0004-EWAL-connect_ethereum_wallet.md) -- `0005-ETXN` [Submit Ethereum transaction](0005-ETXN-submit_ethereum_transaction.md) -- `0006-NETW` [Network and node selection](0006-NETW-network-and-nodes.md) `TODO` - -## `1000` Bridges, Transfers and Vesting - -- `1001-DEPO` [Deposit](1001-DEPO-desposit.md) -- `1002-WITH` [Withdraw](1002-WITH-withdraw.md) -- `1003-TRAN` [Transfer](1003-TRAN-transfer.md) `TODO` -- `1004-ASSO` [Associate governance token with a Vega key](1004-ASSO-associate.md) -- `1005-VEST` [View and redeem vested tokens](1005-VEST-vesting.md) - -## `2000` Staking - -- `2001-STKE` [Staking validators](2001-STKE-staking.md) -- `2002-SINC` [Review staking income](2002-SINC-staking-income.md) - -## `3000` Governance - -- `3001-VOTE` [See proposals and Vote on changes](3001-VOTE-vote.md) -- `3002-PROP` [Select proposal type](3002-PROP-propose.md) -- `3003-PMAN` [Propose new Market](3003-PMAN-propose_new_market.md) -- `3004-PMAC` [Propose change(s) to market](3004-PMAC-propose_market_change.md) -- `3005-PASN` [Propose new asset](3005-PASN-propose_new_asset.md) -- `3006-PASC` [Propose change(s) to asset](3006-PASC-propose_asset_change.md) -- `3007-PNEC` [Propose change to network parameter(s)](3007-PNEC-propose_network.md) -- `3008-PFRO` [Propose something "Freeform"](3008-PFRO-propose_freeform.md) - -## `4000` Treasury - -- `4001-TRES` [View treasury rewards](4001-TRES-view_treasury_rewards.md) `TODO` - -## `5000` Liquidity provision - -- `5001-LIQF` [Find and understand liquidity provision opportunities](5001-LIQF-liquidity_opportunities.md) `TODO` -- `5002-LIQP` [Provide liquidity](5002-LIQP-provide_liquidity.md) `TODO` -- `5003-LIQI` [View liquidity provision rewards](5003-LIQI-liquidity_income.md) `TODO` - -## `6000` Markets and analysis - -- `6001-MARK` [Find markets](6001-MARK-find_markets.md) `TODO` -- `6002-MARD` [View market specification](6002-MDET-market-details.md) `TODO` -- `6003-ORDB` [Analyze Order book](6003-ORDB-order_book.md) `TODO` -- `6004-PHIS` [Analyze price history](6004-PHIS-price_history.md) `TODO` -- `6005-THIS` [Analyze trade history](6005-THIS-trade_history.md) `TODO` - -## `7000` Collateral, Orders, Positions and Fills - -- `7001-COLL` [View my collateral / accounts](7001-COLL-collateral.md) `TODO` -- `7002-SORD` [Submit an order](7002-SORD-submit_orders.md) -- `7003-MORD` [Manage my orders](7003-MORD-manage_orders.md) -- `7004-POSI` [View my positions](7004-POSI-positions.md) `TODO` -- `7005-FILL` [View my trades/fills](7005-FILL-fills.md) `TODO` -- `7006-FEES` [View my trading fees](7006-FEES-fees.md) `TODO` - -## `8000` Understand transactions and blocks - -- `8001-BLOX` [Transaction and block content](8001-BLOX-transaction_and_blocks.md) `TODO` -- -## Appendixes - -- [Display display rules](9001-DATA-data_display.md) diff --git a/user-interface/apps.json b/user-interface/apps.json deleted file mode 100644 index 46ee14c81..000000000 --- a/user-interface/apps.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "Desktop-Wallet": { - "specs": ["0001-WALL"] - }, - "Token": { - "specs": ["9001-DATA", "1002-WITH", "1004-ASSO", "1005-VEST", "2001-STKE", "2002-SINC", "7001-COLL", "3001-VOTE", "3002-PROP", "3003-PMAN", "3004-PMAC", "3005-PASN", "3006-PASC", "3007-PNEC", "3008-PFRO", "0002-WCON", "0003-WTXN", "0004-EWAL", "005-ETXN", "0006-NETW" ] - }, - "Console": { - "specs": ["9001-DATA", "1001-DEPO", "1002-WITH", "1003-TRAN", "6000-COLL", "6001-MARK", "6002-MDET", "6001-MARK", "6002-MDET", "6003-ORDB", "6004-PHIS", "6005-THIS", "7001-COLL", "7002-SORD", "7003-MORD", "7004-POSI", "7005-FILL", "7006-FEES","0002-WCON", "0003-WTXN", "0004-EWAL", "005-ETXN", "0006-NETW"] - }, - "Block-explorer": { - "specs": ["8001-BLOX"] - }, - "Vega.xyz": { - "specs": ["0001-WALL", "4001-TRES", "5001-LIQF"] - } - } \ No newline at end of file diff --git a/user-interface/categories.json b/user-interface/categories.json deleted file mode 100644 index 77b023b2f..000000000 --- a/user-interface/categories.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "Wallets & Signing Transactions & Network Selection": { - "specs": ["0001-WALL", "0002-WCON", "0003-WTXN", "0004-EWAL", "0005-ETXN", "0006-NETW"] - }, - "Bridges & Transfers & Vesting": { - "specs": ["1001-DEPO", "1002-WITH", "1003-TRAN", "1004-ASSO", "1005-VEST"] - }, - "Staking": { - "specs": ["2001-STKE", "2002-SINC"] - }, - "Governance": { - "specs": ["3001-VOTE", "3002-PROP", "3003-PMAN", "3004-PMAC", "3005-PASN", "3006-PASC", "3007-PNEC", "3008-PFRO"] - }, - "Treasury": { - "specs": ["4001-TRES"] - }, - "Liquidity Provision": { - "specs": ["5001-LIQF", "5002-LIQP", "5003-LIQI"] - }, - "Markets & Analysis": { - "specs": ["6001-MARK", "6002-MDET", "6003-ORDB", "6004-PHIS", "6005-THIS"] - }, - "Collateral & Orders & Positions & Fills": { - "specs": ["7001-COLL", "7002-SORD", "7003-MORD", "7004-POSI", "7005-FILL", "7006-FEES"] - }, - "Block Exploring": { - "specs": ["8001-BLOX"] - }, - "Data Display": { - "specs": ["9001-DATA"] - } - } diff --git a/wordlist.txt b/wordlist.txt index 9b8fac7bd..3a07904ef 100644 --- a/wordlist.txt +++ b/wordlist.txt @@ -1,6 +1,9 @@ +$VEGA ABCI ABCI++ ABI +ABIs +actioned Allowlist allowlisted antiwhaling @@ -11,6 +14,7 @@ asynchrony atomicity Auth authorisable +avatarUrl AVL bip blockchain @@ -25,10 +29,10 @@ cashflow cashflows CCP ccy +cdot ceil ceiled CFD -CSF checkpointing CLEF closedout @@ -42,14 +46,21 @@ composable config cosmovisor counterparty +CQRS +CreateReferralSet crypto cryptocurrencies cryptographic -CQRS +CSF +CSV cumulated +customisable customised DAI +dApp datanode +datapoint +datapoints datatypes datetime decentralised @@ -65,25 +76,29 @@ discoverability disincentivise DLT DPOS +DS durations +earlyExitPenalty ECDSA EEF EIP ELS encodings ENE +enum enums EOS ERC +ERC-1155 ERC-20 ERC-721 -ERC-1155 ETH ETH's ETHB Ethereum eventbus EVM +EVMs executables familiarise FBAs @@ -91,6 +106,7 @@ FIFO filesystem FOK FOREX +frac freeform frontend funder @@ -119,14 +135,15 @@ IBC incentivise incentivised incrementing +infty init initialisation initialise initialised initialising +int64 integrations intrablock -int64 IOC JSON keyholder @@ -134,7 +151,12 @@ keypair keypairs keystore KMS +ldots +leaderboards +leftarrow +leq lifecycle +linearisation linearised linkings linux @@ -146,13 +168,15 @@ LP LPs MacOS mainnet +maint malus margined margining -messager +marketID math mempool Merkle +messager MetaMask microstructure midprice @@ -165,6 +189,8 @@ multisignature NFTs nonfungible OAUTH +OCO +OCOs OKR onwards OpenOracle @@ -177,6 +203,8 @@ PDP performant permissioned permissionless +PERP +perps PME PnL PoS @@ -186,9 +214,15 @@ pre pro protobuf Pseudocode +pseudorandom pubkey +quant rata +rebased +recollateralise +ReferralSet reimplemented +renormalise repo reponse repurpose @@ -200,26 +234,28 @@ Ropsten RPC runnable runtime -scalable scalability +scalable Scholes selectable sepolia serialisation SHA -sharded SHA3 +sharded siskas +SLA Solana src +SSD +SSL stablecoin stablecoins stakers statebridge stateful statesync -SSD -SSL +structs suboptimal subtype subtypes @@ -229,40 +265,44 @@ sybil sybils tau TBC +teamUrl Tendermint testnet testnets TIF +TODO tokenholder tokenholders -tradable trackable +tradable Tron trustless +TWAP tx TXs -quant UI uint -unmarshal unamended unassociated unbanned undelegate -undelegates undelegated +undelegates undelegating undelegation undelegations undeployed +underlyings unencrypted unitless +unmarshal unnominated unnormalised unstake unstaked unstakes unstaking +untriggered unvested url USD @@ -270,8 +310,8 @@ USDC USDT UTC validator -validators validator's +validators vega vegaprotocol vegatools @@ -284,8 +324,12 @@ VW VWAP walkthrough walkthroughs -wei wBTC +wei whitepaper Yubikey -$VEGA +joinable +mathbb +geq +vee +mathbf \ No newline at end of file