From e3e9539292f939e883dc7acd9b624ace8e9f4187 Mon Sep 17 00:00:00 2001 From: Gordsport <83510148+gordsport@users.noreply.github.com> Date: Wed, 1 Nov 2023 11:06:23 +0000 Subject: [PATCH] chore: Merge cosmic into master (#2028) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Adding generic AC for perp order types (#1908) * feat: Adding generic AC for perp order types * refactor: clarify network parameter behaviour * feat: add acceptance criteria for program updates * feat: update features * fix spellcheck * fix: mkt param to be a netparam * refactor: update spelling as per feedback Co-authored-by: Tom * feat: add vesting spec * feat: add activity spec * feat: update reward distribution * feat: address vesting feedback * feat: update reward metrics * feat: add validator power metric * feat: add new entity scoping * refactor: linting and spellcheck fixes * refactor: address network param feedback * refactor: fix formatting * feat: update the validator ranking metric * fix: correct variable name * feat: add vested multiplier * feat: change total rewards calc as per feedback * feat: remove logarithmic functions * refactor: rename open-interest metric to position metric * fix: fix linting and spellcheck * feat: add validation for k * feat: address feedback Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * feat: address exponential feedback Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * feat: switch rank exponential distribution to table * feat: address feedback * feat: apply suggestion Co-authored-by: Barnaby M * feat: add limits * feat: clarify requirements not applied for certain metrics * feat: switch reward accounts to per unique transfer * refactor: clarification of lock period * feat: address comments * feat: address comments * feat: update json file * feat: remove 061062 * feat: typo * feat: formatting * feat: update format for formula * feat: Update AC 080 Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * feat: update space * Update protocol/0042-LIQF-setting_fees_and_rewarding_lps.md Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * feat: typo Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * feat: typo Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * feat: address comments * feat: benefit factors set by original referral set * feat: add reward multipliers for staking * refactor: rename maxBenefitTiers to maxReferralTiers * fix: update referral codes * refactor: address review comments * fix: correct equation for converting to quantum * feat: align with core implementation * feat: update market parameter part * feat: Adding perp margin calcs (#1915) * feat: Adding perp margin calcs * fixed: removed one ac * fix: ac numbers * fix: add quote * Update 0044-LIME-lp_mechanics.md (#1918) SLA remove 0044-LIME-010 * chore: Delete incorrect acceptance criteria relating to perps and market termination * chore: Update features.json to reflect removed ac * feat: update ACs in 0034 * feat: update AC code * fix: appprobation from pr#1921 * fix: features.json for PR 1921 (#1924) Updates the features.json as pt2 of #1921 . * Update 0044-LIME-lp_mechanics.md (#1925) * refactor: bring specs in line with implementation * refactor: remove undelegate in anger * refactor: add `0` account bit * refactor: general -> global * refactor: address comments from PR review * refactor: review suggestion Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * refactor: clarify details around `0` account * feat: update AC 0044-LIME-028 * Update protocol/0044-LIME-lp_mechanics.md Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * refactor: remove redundant sentences * Update protocol/0044-LIME-lp_mechanics.md Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * Update protocol/0044-LIME-lp_mechanics.md Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * addressed comments * chore: add two eth oracles ACs related to multiple contracts (#1926) * chore: add two eth oracles ACs related to multiple contracts * fix: Update protocol/0082-ETHD-ethereum-data-source.md --------- Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * modified for clarity and consistency * chore: tidy up the features.json file (#1929) * fix: reword ACs 38, 39 (#1923) Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * test: add a concrete example * fix: Spot ACs in features.json file (#1931) * fix: Spot ACs in features.json file moves the spot related SLA ACs into the spot feature for tracking * fix: dupes * feat: update AC 0044-LIME-026 * feat: update AC Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * fix: Minor spelling corrections, plus two new ACs for testing perp data is visible in REST and GRPC. * fix: logic fix * fix: align AC with implementation * Fix/fee calc step is netparam (#1937) * fix: fee distribution time step is a netparam * fix: fee distribution time step is a netparam * fix: fee distribution time step is a netparam * fix: fee distribution time step is a netparam --------- Co-authored-by: Paul Webb <86229849+vega-paul@users.noreply.github.com> * fix: revert the deployment name changes in the features.json file (#1943) * fix: align with core * fix: small correction * fix: Add tests for perpetuals doing protocol upgrade, checkpoint and network history. (#1940) Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * chore: add AC codes to referral ACs in fees spec (#1947) * chore: add AC codes to referral ACs in fees spec * chore: add Codes * refactor: move acs to correct section * feat: align acs with implementation * fix: remove legacy ac * chore: move batch transfers out of deployment 2 (#1948) * refactor: amend margin formula * feat: add clarification when no indicative price * fix/enacted doesn't mean leaves opening auction * fix: remove mentions of maturity * Update comsic from master (#1956) * refactor: floor slippage * User interface: add Post only and Reduce only (#1640) * draft for SORD * add AC for showing rejected reason * typo * execution flags on orders table * add codes to SORD * Update user-interface/7002-SORD-submit_orders.md Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * add link to docs --------- Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * Clarify rewards if multisig contains a key it should not (#1643) * Clarify rewards if multisig contains a key it should not * fix: MD lint error --------- Co-authored-by: gordsport * feat: update FOK MO reduce-only * fix: Add some more cancel order ACs. * Update protocol/0033-OCAN-cancel_orders.md Co-authored-by: Tom * feat: Add a test for checking performance scores when checkpoint rest… (#1637) * feat: Add a test for checking performance scores when checkpoint restart happens in a new epoch. * fix: Typo. * chore: Fix indentation (tabs/spaces). * feat: Add a test for checking performance scores when checkpoint rest… (#1637) * feat: Add a test for checking performance scores when checkpoint restart happens in a new epoch. * fix: Typo. * chore: Fix indentation (tabs/spaces). * Update 0069-VCBS-validators_chosen_by_stake.md (#1653) Deleted half an AC as discussed on slack (as the number for the performance score didn't make sense here) * Amend price monitoring ACs (#1660) * refactor: update ACs * chore: tell approbation to leave me alone * chore: try again * refactor: rephrase AC * refactor: clarify * refactor: fix spelling * feat: update datanode spec with functionality to rollback node and functionality to fetch csv files containing network history (#1659) * feat: update datanode spec with functionality to rollback node and functionality to fetch csv files containing network history * fix: spell checker --------- Co-authored-by: gordsport Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * spec: draft for time-based mempool pruning (#1645) * spec: draft for time-based mempool pruning Signed-off-by: Elias Van Ootegem * spec: spelling Signed-off-by: Elias Van Ootegem * Spelling + clarify how block height is determined Signed-off-by: Elias Van Ootegem * spec: add some additional ACs, remove mention of TxV4, clarify that transaction data includes block hash in addition to block height Signed-off-by: Elias Van Ootegem * complete acceptance criteria Signed-off-by: Elias Van Ootegem * fix typo Signed-off-by: Elias Van Ootegem --------- Signed-off-by: Elias Van Ootegem Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * re-word ac * chore: Add decision notes template (#1658) * chore: Add decision notes template Added a decision template based loosely on the [Any Decision Records](https://adr.github.io/madr/) template Also updated some MD linting to allow sibling duplicated header titles and double spacing to make reading easier. * fix: Spelling error * feat: update for distributing remainder of liq fee * fix: correct AC * refactor: address feedback * add spec for current collateral * clarify total margin available * Update user-interface/7001-COLL-collateral.md Co-authored-by: Michael * Update user-interface/7001-COLL-collateral.md Co-authored-by: Michael * Update user-interface/7001-COLL-collateral.md Co-authored-by: Michael * Update user-interface/7001-COLL-collateral.md Co-authored-by: Michael * add asset file * Add ACs for asset detail dialog and link up with collateral spec * add suggested sort order per account type * fix: 005 AC code link (#1671) * fix: mempool pruning with PoW only * fix: linting errors * refactor: add liquidation price estimate * refactor: add ACs * refactor: another AC * refactor: cosmetic change * Update 0045-DSRC-data_sourcing.md (#1674) * Update 0045-DSRC-data_sourcing.md * Update 0045-DSRC-data_sourcing.md * Wording of timestamp AC --------- Co-authored-by: AnExsomnis <86832315+AnExsomnis@users.noreply.github.com> Co-authored-by: Barney Mannerings * add spec for current functionality fo fills data grid * Update user-interface/7005-FILL-fills.md Co-authored-by: Michael * Update user-interface/7005-FILL-fills.md Co-authored-by: Michael * Update user-interface/7005-FILL-fills.md Co-authored-by: Michael * Make notional value of feels a could * Update user-interface/7005-FILL-fills.md Co-authored-by: Michael * add clarification on user role for trades during continuos vs auction * fix: typo in ac number * feat: update typo in 0061 * feat: add explaination on ersatz validators * feat: correct typos * feat: typo * Update user-interface/7005-FILL-fills.md * add browser wallet spec as part of approbation set up * chore: fix typo and add browser specs to app.json * chore: add browser wallet to categories, hopefully in the right place * chore: Update 0033-OCAN-cancel_orders.md (#1686) * feat: update BRIE specs (#1654) * feat: update BRIE specs Signed-off-by: Jeremy Letang * chore: update spelling and linting * feat: update BRIE specs Signed-off-by: Jeremy Letang * chore: update spelling and linting * fix: spelling and lint and rebase * fix: spelling --------- Signed-off-by: Jeremy Letang Co-authored-by: gordsport Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * chore: Update cosmic elevator branch from master (#1688) * refactor: let max oi drop during auciton * fix: duplicate AC codes in 0069-VCBS (#1536) (#1537) * Update `cosmicelevator` branch from `master` branch (#1550) * fix: bring market.liquidity.targetstake.triggering.ratio in line with implementation * fix: duplicate AC codes in 0069-VCBS (#1536) * chore: trailing spaces * fix: remove the unused netparam; not liking that * chore: remove untestable AC from 0018 (#1530) * chore: reword 0063-VALK-013 * Update 0035-LIQM-liquidity_monitoring.md (#1539) * Update 0035-LIQM-liquidity_monitoring.md * Update 0035-LIQM-liquidity_monitoring.md * fix AC numbering on 0035-LIQM-liquidity_monitoring.md (#1541) * Update 0011-NP-CLIE-client-interaction-tests.md * feat: allow any key to request multisg signers update bundle * feat: clarify spam status * refactor: let max oi drop during auciton * Wording tweaks for spam protection spec (#1548) * grammar etc fixes to spam protection * fix linting errors * chore: remove brackets * docs: another round of rewording * add related topics section Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> Co-authored-by: wwestgarth Co-authored-by: wwestgarth Co-authored-by: Paul Webb <86229849+vega-paul@users.noreply.github.com> Co-authored-by: RomanLeca Co-authored-by: Witold Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * Update cosmicelevator branch from master to keep up-to-date (#1560) * fix: bring market.liquidity.targetstake.triggering.ratio in line with implementation * fix: duplicate AC codes in 0069-VCBS (#1536) * chore: trailing spaces * fix: remove the unused netparam; not liking that * chore: remove untestable AC from 0018 (#1530) * chore: reword 0063-VALK-013 * Update 0035-LIQM-liquidity_monitoring.md (#1539) * Update 0035-LIQM-liquidity_monitoring.md * Update 0035-LIQM-liquidity_monitoring.md * fix AC numbering on 0035-LIQM-liquidity_monitoring.md (#1541) * Update 0011-NP-CLIE-client-interaction-tests.md * feat: allow any key to request multisg signers update bundle * feat: clarify spam status * refactor: let max oi drop during auciton * Wording tweaks for spam protection spec (#1548) * grammar etc fixes to spam protection * fix linting errors * chore: remove brackets * docs: another round of rewording * add related topics section * fix spelling in spam protection spec (#1553) * Update 0069-VCBS-validators_chosen_by_stake.md Added pending validator to VCBS-59 * Update 0069-VCBS-validators_chosen_by_stake.md Clarification on 0059 * Update 0062-SPAM-spam_protection.md * Update 0062-SPAM-spam_protection.md fixed link to defaults.go * Add quality checks to the makefile (#1556) * fix: broken links in the specs (#1557) * fix: remove broken kraken link --------- Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> Co-authored-by: wwestgarth Co-authored-by: wwestgarth Co-authored-by: Paul Webb <86229849+vega-paul@users.noreply.github.com> Co-authored-by: RomanLeca Co-authored-by: Witold Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> Co-authored-by: Vegaklaus <62546834+Vegaklaus@users.noreply.github.com> * chore: Update cosmicelevator from master branch (#1564) * fix: bring market.liquidity.targetstake.triggering.ratio in line with implementation * fix: duplicate AC codes in 0069-VCBS (#1536) * chore: trailing spaces * fix: remove the unused netparam; not liking that * chore: remove untestable AC from 0018 (#1530) * chore: reword 0063-VALK-013 * Update 0035-LIQM-liquidity_monitoring.md (#1539) * Update 0035-LIQM-liquidity_monitoring.md * Update 0035-LIQM-liquidity_monitoring.md * fix AC numbering on 0035-LIQM-liquidity_monitoring.md (#1541) * Update 0011-NP-CLIE-client-interaction-tests.md * feat: allow any key to request multisg signers update bundle * feat: clarify spam status * refactor: let max oi drop during auciton * Wording tweaks for spam protection spec (#1548) * grammar etc fixes to spam protection * fix linting errors * chore: remove brackets * docs: another round of rewording * add related topics section * fix spelling in spam protection spec (#1553) * Update 0069-VCBS-validators_chosen_by_stake.md Added pending validator to VCBS-59 * Update 0069-VCBS-validators_chosen_by_stake.md Clarification on 0059 * Update 0062-SPAM-spam_protection.md * Update 0062-SPAM-spam_protection.md fixed link to defaults.go * Add quality checks to the makefile (#1556) * fix: broken links in the specs (#1557) * fix: remove broken kraken link * refactor: clarify 0042-LIQF-018 * feat: fix non-protocol linting and add makefile (#1563) * feat: fix non-protocol linting and add makefile * fix: remove binary file * fix: remove binary * fix: revert ui changes * fix: remove package files * Update glossaries/distributed-ledger-glossary.md --------- Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> Co-authored-by: wwestgarth Co-authored-by: wwestgarth Co-authored-by: Paul Webb <86229849+vega-paul@users.noreply.github.com> Co-authored-by: RomanLeca Co-authored-by: Witold Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> Co-authored-by: Vegaklaus <62546834+Vegaklaus@users.noreply.github.com> * chore: Update cosmicelevator branch from master (#1573) * docs: post only orders (#1562) * docs: Adding specs for post-only orders * Update from master (#1602) * fix: bring market.liquidity.targetstake.triggering.ratio in line with implementation * fix: duplicate AC codes in 0069-VCBS (#1536) * chore: trailing spaces * fix: remove the unused netparam; not liking that * chore: remove untestable AC from 0018 (#1530) * chore: reword 0063-VALK-013 * Update 0035-LIQM-liquidity_monitoring.md (#1539) * Update 0035-LIQM-liquidity_monitoring.md * Update 0035-LIQM-liquidity_monitoring.md * fix AC numbering on 0035-LIQM-liquidity_monitoring.md (#1541) * Update 0011-NP-CLIE-client-interaction-tests.md * feat: allow any key to request multisg signers update bundle * feat: clarify spam status * refactor: let max oi drop during auciton * Wording tweaks for spam protection spec (#1548) * grammar etc fixes to spam protection * fix linting errors * chore: remove brackets * docs: another round of rewording * add related topics section * fix spelling in spam protection spec (#1553) * Update 0069-VCBS-validators_chosen_by_stake.md Added pending validator to VCBS-59 * Update 0069-VCBS-validators_chosen_by_stake.md Clarification on 0059 * Update 0062-SPAM-spam_protection.md * Update 0062-SPAM-spam_protection.md fixed link to defaults.go * Add quality checks to the makefile (#1556) * fix: broken links in the specs (#1557) * fix: remove broken kraken link * refactor: clarify 0042-LIQF-018 * feat: fix non-protocol linting and add makefile (#1563) * feat: fix non-protocol linting and add makefile * fix: remove binary file * fix: remove binary * fix: revert ui changes * fix: remove package files * Update glossaries/distributed-ledger-glossary.md * Update 0069-VCBS-validators_chosen_by_stake.md Added ACs to match the implementation so its codified (i.e., if an Ersatzvalidator drops below ownstake and there's no-one there to replace it, it is not demoted to pending. * Update 0069-VCBS-validators_chosen_by_stake.md * fix: Update acceptance criteria to correctly reflect implementation (#1549) Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * fix: update epoch ACs (#1555) Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * feat: update 0042, LP fee Transfer, Distribution * Add build folder to gitigore The approbation checks in `makefile` use this folder. Make sure it's not checked in. * Update protocol/0042-LIQF-setting_fees_and_rewarding_lps.md Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * Ignore dictionary Spellcheck creates a dictionary file. Ensure it isn't committed. * Un parallelise clone-sources in makefile The makefile tried to be clever and run all the remote-repo clones for Approbation checks in parallel. Due to my limited makefile skills, the check could run before a clone was finished. Run them in series instead. This also improves the output a little * Add specs param to names and codes in makefile The old behaviour (default paths) should have been deprecated ages ago, but hasn't been. This change makes it provide the same paths as the defaults. * feat: update maker fee transfer account * Update 0069-VCBS-validators_chosen_by_stake.md * Update protocol/0029-FEES-fees.md Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * Update protocol/0029-FEES-fees.md Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * Update protocol/0029-FEES-fees.md Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * Update 0048-DSRI-data_source_internal.md * fix: refactor 0069-VCBS-052 (#1570) Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> Co-authored-by: Vegaklaus <62546834+Vegaklaus@users.noreply.github.com> * fix: MD and spelling lint checks * fix: MD and spelling lint errors * fix: update wording for AC 0059-VCBS-016- (#1566) * Update 0062-SPAM-spam_protection.md (#1571) * Update 0062-SPAM-spam_protection.md * Update protocol/0062-SPAM-spam_protection.md * Update protocol/0062-SPAM-spam_protection.md --------- Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * Update 0011-NP-CLIE-client-interaction-tests.md (#1542) * Update 0011-NP-CLIE-client-interaction-tests.md Added ACs to test static spam protection (wallet doesn't allow you to get banned) * Update non-protocol-specs/0011-NP-CLIE-client-interaction-tests.md Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * Update non-protocol-specs/0011-NP-CLIE-client-interaction-tests.md Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * Update non-protocol-specs/0011-NP-CLIE-client-interaction-tests.md Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * Update non-protocol-specs/0011-NP-CLIE-client-interaction-tests.md Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * Update non-protocol-specs/0011-NP-CLIE-client-interaction-tests.md Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * Update non-protocol-specs/0011-NP-CLIE-client-interaction-tests.md Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * Update 0011-NP-CLIE-client-interaction-tests.md * Update non-protocol-specs/0011-NP-CLIE-client-interaction-tests.md Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * Update non-protocol-specs/0011-NP-CLIE-client-interaction-tests.md Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * Update non-protocol-specs/0011-NP-CLIE-client-interaction-tests.md Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * Update non-protocol-specs/0011-NP-CLIE-client-interaction-tests.md Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * Update 0011-NP-CLIE-client-interaction-tests.md * Update 0011-NP-CLIE-client-interaction-tests.md * chore: fix spelling and MD lint checks --------- Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> Co-authored-by: gordsport * Update 0011-NP-CLIE-client-interaction-tests.md (#1579) * refactor: clarify AC * fix: adding bound on slippage part of margin * fix: adding bound on slippage part of margin - ACs * chore: lint * chore: format * chore: add ac codes * chore: lint * feat: make it nonlinear * fix: remove obsolete ac * fix: typo * fix: typo from comments Co-authored-by: Tom * fix: code block in list formatting (#1584) * chore: Remove untestable acceptance criteria * feat: add rate limit spec * Update 0057-TRAN-transfers.md (#1588) * Update 0057-TRAN-transfers.md * Update 0057-TRAN-transfers.md * chore: add AC codes * Update 0057-TRAN-transfers.md (#1590) * Update 0069-VCBS-validators_chosen_by_stake.md Added Acs for IssueSignature * Update 0069-VCBS-validators_chosen_by_stake.md * fix: Expand acceptance criteria to make them clear for 0011-NP-CLIE-client-interaction (#1585) * fix: Expand acceptance criteria to make them clear * chore: Fix formatting * fix: Address review comments --------- Co-authored-by: Paul Webb <86229849+vega-paul@users.noreply.github.com> * feat: Add new PLUP AC for state-sync in protocol upgrade. * feat: add closeout API * chore: tidy up * chore: remove blank line * chore: by more specific about when a client should be banned * feat: update formula line 140 * fix: Add extra cases for various things that might be affected by validators joining and leaving. * fix: Review comments for new ACs. * fix: Satisfy the linter. * refactor: add slippage to orders * refactor: update examples * chore: fix linter issues * refactor: fix typo * refactor: formlise margin level treatment * refactor: amend/add ACs * Update 0042-LIQF-setting_fees_and_rewarding_lps.md (#1598) * Update 0042-LIQF-setting_fees_and_rewarding_lps.md * fix: linting errors --------- Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> Co-authored-by: gordsport * fix: linting error * fix: MD lint and spelling * Update 0069-VCBS-validators_chosen_by_stake.md (#1604) * Update 0069-VCBS-validators_chosen_by_stake.md * Update 0069-VCBS-validators_chosen_by_stake.md * Update 0069-VCBS-validators_chosen_by_stake.md * Update 0069-VCBS-validators_chosen_by_stake.md * Update 0069-VCBS-validators_chosen_by_stake.md * Update 0069-VCBS-validators_chosen_by_stake.md * Update 0069-VCBS-validators_chosen_by_stake.md * Update 0069-VCBS-validators_chosen_by_stake.md * Update 0069-VCBS-validators_chosen_by_stake.md * fix: MD linting and spelling errors --------- Co-authored-by: gordsport * Update 0069-VCBS-validators_chosen_by_stake.md (#1596) * Update 0069-VCBS-validators_chosen_by_stake.md * fix: spelling errors --------- Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> Co-authored-by: gordsport * fix: More validator join/leave test cases involving checkpoints. (#1601) * fix: More validator join/leave test cases involving checkpoints. * fix: spelling error * fix: Update AC codes to not be duplicates. --------- Co-authored-by: gordsport Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * feat: update make file and add approabtion to CI (#1605) * feat: update make file and add approabtion to CI * chore: add more checks and fix errors * fix: checkout PR branch * fix: get branch * fix: approbation errors --------- Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> Co-authored-by: wwestgarth Co-authored-by: wwestgarth Co-authored-by: Paul Webb <86229849+vega-paul@users.noreply.github.com> Co-authored-by: RomanLeca Co-authored-by: Witold Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> Co-authored-by: Vegaklaus <62546834+Vegaklaus@users.noreply.github.com> Co-authored-by: MuthuVega <91467655+MuthuVega@users.noreply.github.com> Co-authored-by: AnExsomnis <86832315+AnExsomnis@users.noreply.github.com> Co-authored-by: Jiajia-Cui Co-authored-by: Edd Co-authored-by: Jiajia-Cui <92106936+Jiajia-Cui@users.noreply.github.com> Co-authored-by: claudiumilea Co-authored-by: Tom Co-authored-by: MuthuVega Co-authored-by: Philip Scott Co-authored-by: Philip Scott <94362991+pscott31@users.noreply.github.com> Co-authored-by: Jake Bennett * fix: lint errors and duplicate paragraph (#1630) * fix: lint errors and duplicate paragraph * fix: MD lint error * Chore: Update cosmicelevator from master (#1646) * feat: Add new cases, mostly around eth changes during outages. A few extra cases for PUP. * chore: Linting fixes. * fix: More linting issues. * fix: spelling and AC code lint errors * fix: spelling * feat: Clarifying fees paid (or not paid) by post-only orders (#1635) * chore: remove invalid and unimplemented use cases * refactor: floor slippage * User interface: add Post only and Reduce only (#1640) * draft for SORD * add AC for showing rejected reason * typo * execution flags on orders table * add codes to SORD * Update user-interface/7002-SORD-submit_orders.md Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * add link to docs --------- Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * Clarify rewards if multisig contains a key it should not (#1643) * Clarify rewards if multisig contains a key it should not * fix: MD lint error --------- Co-authored-by: gordsport --------- Co-authored-by: Jake Bennett Co-authored-by: Tom Co-authored-by: Rado Co-authored-by: Witold Co-authored-by: Michael Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> --------- Co-authored-by: Witold Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> Co-authored-by: wwestgarth Co-authored-by: wwestgarth Co-authored-by: Paul Webb <86229849+vega-paul@users.noreply.github.com> Co-authored-by: RomanLeca Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> Co-authored-by: Vegaklaus <62546834+Vegaklaus@users.noreply.github.com> Co-authored-by: Tom Co-authored-by: MuthuVega <91467655+MuthuVega@users.noreply.github.com> Co-authored-by: AnExsomnis <86832315+AnExsomnis@users.noreply.github.com> Co-authored-by: Jiajia-Cui Co-authored-by: Edd Co-authored-by: Jiajia-Cui <92106936+Jiajia-Cui@users.noreply.github.com> Co-authored-by: claudiumilea Co-authored-by: MuthuVega Co-authored-by: Philip Scott Co-authored-by: Philip Scott <94362991+pscott31@users.noreply.github.com> Co-authored-by: Jake Bennett Co-authored-by: Rado Co-authored-by: Michael * fix: fix prefix for browser wallet * chore: fixed wrong file.. * chore: now undo my formatting for the other file * chore: undo formatting change * add missing AC * add more ac * typoe * chore: add ac for the sprint * add ui specs for finding closed markets * fix number sequence * chore: add acs for market selector * chore: added ACs for transaction log * chore: removed navigation AC * add key pair ac that slipped the net via a ticket going straight to the done column * Add UI ACs for network and nodes (#1700) * Update 0002-WCON-connect_vega_wallet.md Removing all coulds, shoulds and would like tos. These are captured here and will be pulled into tickets as applicable later down the line if / when these features are implemented * Update user-interface/0002-WCON-connect_vega_wallet.md Co-authored-by: Penny Andrews <47143694+pennyandrews@users.noreply.github.com> * add settings ac * Update user-interface/1101-BWAL-browser_wallet.md * remove ac for recovery phrase and add new connection and transaction ac when not onboarded * add ac * add and increment * chore: update readme (#1709) Co-authored-by: peterbarrow <62435083+peterbarrow@users.noreply.github.com> * feat: typo * chore(trading): Add transfer ACs * chore(trading): Add transfer ACs number fix * chore: update proposal acs * fix(trading): Fix transfer specs ID * added all markets and proposal markets ACs * Fix: bring ersatz number in line with core. * increment and add ac * format * rework * chore: withdraw minimal amount * chore: add new market proposal acs * Update 1101-BWAL-browser_wallet.md Removed: - It is visually similar to other transaction types but essentially has less of the human readable detail(s) (design note) (1101-BWAL-057) as it is a design note - When I approve a transaction I can see confirmation that the transaction has been approved (1101-BWAL-045) - separate story in backlog - When I reject a transaction I can see confirmation that the transaction has been rejected (1101-BWAL-049) - separate story in backlog * chore:update todo * amend spec numbers * update acs * update numeration * 6501-ASSE-assets.md * add ac for import wallet * add list connections ac * update acs * A community settlement story. * fix: another AC for changing oracle after trading termination * fix: another AC for changing oracle after trading termination * chore: ACs added to 6002-MDET-market-details * chore: few more details added * Update user-interface/6002-MDET-market-details.md Co-authored-by: daro-maj <119658839+daro-maj@users.noreply.github.com> * chore: info about tooltips added * Update user-interface/6002-MDET-market-details.md Co-authored-by: Ben * liquidity ACs * updated numeration * chore(trading): Add Position ACs (#1733) * chore(trading): Add Position ACs * chore: pr suggestion * remove outdated ac * update * chore: add AC for quantum formatting * chore: fix bold typo * chore: filled ACs in 6003-ORDB * disconnect ac * finger slip * test: market selector acs update * update console space * chore: remove cosmic feature from master (#1766) There has been some confusion when designing FE/UXs as the spec for governance transfers has changed slightly from whats in master. Its also the case that master should now be what is live in mainnet this feature is not one that is live. * chore: 6005-THIS-trade_history filled with ACs * popout windoiw * fix: clarify what's a market and what's a network param. * fix: clarify what's a market and what's a network param. * fix: clarify what's a market and what's a network param. * fix: typo Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * add browser wallet loading state ACs * chore: add AC for the button always being disabled (#1746) * Update 5002-LIQP-liquidity.md with 5002-LIQP-013 (#1769) * user-interface/ * depth chart ACs * update acs * update numbers * update name of reference * update * re-arrange browser wallet acs to save mainetenance (#1781) * re-arrange ac to save mainetenance * add file extension * onboarding spec tweak * final tweaks and removals * add specs to categories and apps --------- Co-authored-by: Dexter Edwards * chore(trading): 6004-CHAR-chart ACs created (#1775) * chore(trading): fix spec links * chore(trading): add code to apps.json * chore(trading): fix numbering * chore(trading): fix Char having duplicated id * chore(trading): fix id duplication * chore)trading): Fixing missed 0 * feat: use ghcr image to run approbation * feat: fix flags * fix: descriptions * chore(trading):add chart and orderbook ACs * add telemtry ac * update ac description * remove browser wallet * update README * Update 0036-BRIE-event_queue.md Added clarification on event forwarding * stop orders acs (#1859) * Update 6002-MDET-market-details.md Add some market successor specs * feat: fix some specs * Closed market list changes (#1828) * chore: remove spec on showing user pnl in closed market list * chore: add successor market acs * chore: update data display for mark price, liquidation price and pnl * `DATA`. Quantum decimals and accounts (#1512) * add more synonyms * introduce accounts * accounts are displayed as asset balances * introduce quantum * fix links to old DATA file * more detail * typo Co-authored-by: Edd --------- Co-authored-by: Edd * chore: add acs for network upgrade proposal (#1857) * chore: add acs for network upgrade proposal * chore: add acs for proposal enhancements * chore: proposal acs * chore: acs for successor market * Update the GitHub action token add_issue_new_projects.yml * chore: remove user-interface specs (#1877) * fix: add 048-COSMICELEVATOR-009 * fix: reword 0045-DSRC-012 * chore: update cosmic from matster * fix: missed conflict --------- Signed-off-by: Elias Van Ootegem Signed-off-by: Jeremy Letang Co-authored-by: Witold Co-authored-by: Michael Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> Co-authored-by: Jiajia-Cui Co-authored-by: Jiajia-Cui <92106936+Jiajia-Cui@users.noreply.github.com> Co-authored-by: Jake Bennett Co-authored-by: Tom Co-authored-by: Vegaklaus <62546834+Vegaklaus@users.noreply.github.com> Co-authored-by: Matthew Pendrey Co-authored-by: Elias Van Ootegem Co-authored-by: Charlie Co-authored-by: Charlie <99198652+cdummett@users.noreply.github.com> Co-authored-by: Matthew Russell Co-authored-by: Paul Webb <86229849+vega-paul@users.noreply.github.com> Co-authored-by: AnExsomnis <86832315+AnExsomnis@users.noreply.github.com> Co-authored-by: Barney Mannerings Co-authored-by: daro-maj <119658839+daro-maj@users.noreply.github.com> Co-authored-by: dalebennett1992 Co-authored-by: dalebennett1992 <37066267+dalebennett1992@users.noreply.github.com> Co-authored-by: Jeremy Letang Co-authored-by: wwestgarth Co-authored-by: wwestgarth Co-authored-by: RomanLeca Co-authored-by: MuthuVega <91467655+MuthuVega@users.noreply.github.com> Co-authored-by: Edd Co-authored-by: claudiumilea Co-authored-by: MuthuVega Co-authored-by: Philip Scott Co-authored-by: Philip Scott <94362991+pscott31@users.noreply.github.com> Co-authored-by: Rado Co-authored-by: Penny Andrews <47143694+pennyandrews@users.noreply.github.com> Co-authored-by: Calum Pringle <46783148+ckpringle@users.noreply.github.com> Co-authored-by: peterbarrow <62435083+peterbarrow@users.noreply.github.com> Co-authored-by: Ben Wallace Co-authored-by: Joe Co-authored-by: m.ray <16125548+MadalinaRaicu@users.noreply.github.com> Co-authored-by: Dariusz Majcherczyk Co-authored-by: Zohar Etzioni Co-authored-by: asiaznik Co-authored-by: Art Co-authored-by: Dexter Edwards Co-authored-by: Joe Tsang <30622993+jtsang586@users.noreply.github.com> Co-authored-by: Sam Keen Co-authored-by: Mikołaj Młodzikowski Co-authored-by: Maciek Co-authored-by: AnExsomnis * fix: small correction * feat: add vesting acs * feat: add activity streak acs * feat: update features.json * feat: additional ACs * fix: correct approbation failures * refactor: improve wording with connectors * fix: align with core * fix: align with core * Update 0044-LIME-lp_mechanics.md (#1916) SLA 0044-LIME-006 Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * refactor: add funding payment * Fix example in 0083-RFPR-on_chain_referral_program.md From the same docs: > all `minimum_epochs` values must be an integer strictly greater than 0 * Update 0035-LIQM-liquidity_monitoring.md (#1964) * Update 0035-LIQM-liquidity_monitoring.md https://vegaprotocol.slack.com/archives/C9E6XA8GK/p1695151499045989?thread_ts=1695146384.224289&cid=C9E6XA8GK * fix: remove market.liquidity.targetstake.triggering.ratio * fix: linting * refactor: add perps AC * refactor: scale constant with volume * chore: add ac to feature * feat: add explanation on market life cycle * chore: align ACs with core implementation * feat: add additional referral ACs * feat: update features * fix: correct approbation * refactor: clarify 0083-RFPR-049 * fix: 0044-LIME-003 effect after epoch end (#1968) Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * fix: link (#1954) Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * chore: align closing_timestamp to end_of_program_timestamp * chore: update GOVE-140 to clarify reward account types update AC GOVE-140 to clarify reward account types and exclude metric based rewards from the referral feature * fix: update gov spec 141 (#1974) * chore: Update acceptance criteria to match implementation (#1967) * feat: Remove mention of market value proxy (#1975) * Update 0044-LIME-lp_mechanics.md (#1976) min value for market.liquidity.performanceHysteresisEpochs valid is 0 * chore: correct typos * chore: align protos with core implementation * feat: remove legacy ac * fix: update features * chore: add staking tiers * Update features.json to align milestone names (#1982) Updates features.json to align milestone names and report out correctly * chore: align spec with core implementation * feat: add AC to cover october reward features * fix: correct for markdownlint * refactor: correct AC behaviour * chore: correct typo * feat: add spam rules for referral program * chore: clarify all assets count towards deposit requirement * feat: add separate netparams for each transaction type * refactor: apply suggestion correcting typo Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * feat: add snapshot network parameter * feat: update spec with banning mechanics * chore: correct applyReferralCode typo * feat: clarify rejection and banning mechanisms * feat: remove non-vega restriction * chore: update 0070-MKTD-018 and 19 and add a new AC for exact precision (#1985) * chore: update 0070-MKTD-018 and 19 and add a new AC for exact presision * fix: dupe AC code * chore: make ACs 18 and 19 more valuable * fix: make wording more clear * Update protocol/0070-MKTD-market-decimal-places.md Co-authored-by: Jiajia-Cui <92106936+Jiajia-Cui@users.noreply.github.com> --------- Co-authored-by: Jiajia-Cui <92106936+Jiajia-Cui@users.noreply.github.com> * fix: remove specific time from the ACs (#1983) * fix: remove specific time from the ACs * fix: update * feat: add SLA edge case * feat: update format * chore: format * chore: remove empty space * Update 0062-SPAM-spam_protection.md (#1988) param names * feat: add entitiy scope acs * feat: update features.json * chore: clarify ac by removing weighted * Update 0028-GOVE-governance.md (#1992) * Update 0028-GOVE-governance.md auction termination AC changes 0028-GOVE-135 * Update 0028-GOVE-governance.md * Make referrals spec line up with implementation (#2009) * chore: spelling errors * feat: add validation for running volume values * chore: remove mention of program states * fix: correct validation * fix: corrrect ac code * fix: correct AC code to match implementation and tests --------- Co-authored-by: Charlie Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * chore: align spec with implementation (#2002) Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * update gov-transfer max amount (#2014) * chore: align spec with implementation * chore: clarify acs to match existing tests * feat: add acs to cover multiple discount sources * fix: markdownlint failing * refactor: commit suggestion Co-authored-by: Valentin Trinqué * chore: removed descoped features from cosmic branch * chore: remove spot from cosmic approbation * chore: remove Batch change proposals from cosmic * chore: remove AC from wrong feature Its been put into spot for palazzo or later in chore: - https://github.com/vegaprotocol/specs/pull/2004 --------- Signed-off-by: Elias Van Ootegem Signed-off-by: Jeremy Letang Co-authored-by: Tom Co-authored-by: Charlie Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> Co-authored-by: Sohill-Patel <85170301+Sohill-Patel@users.noreply.github.com> Co-authored-by: Charlie <99198652+cdummett@users.noreply.github.com> Co-authored-by: Jiajia-Cui <92106936+Jiajia-Cui@users.noreply.github.com> Co-authored-by: Barnaby M Co-authored-by: Jiajia-Cui Co-authored-by: Paul Webb <86229849+vega-paul@users.noreply.github.com> Co-authored-by: MuthuVega Co-authored-by: MuthuVega <91467655+MuthuVega@users.noreply.github.com> Co-authored-by: Witold Co-authored-by: wwestgarth Co-authored-by: AnExsomnis <86832315+AnExsomnis@users.noreply.github.com> Co-authored-by: Jake Bennett Co-authored-by: Peter Barrow Co-authored-by: Pete Barrow <62435083+peterbarrow@users.noreply.github.com> Co-authored-by: Karel Moravec Co-authored-by: Michael Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> Co-authored-by: Vegaklaus <62546834+Vegaklaus@users.noreply.github.com> Co-authored-by: Matthew Pendrey Co-authored-by: Elias Van Ootegem Co-authored-by: Charlie Co-authored-by: Matthew Russell Co-authored-by: daro-maj <119658839+daro-maj@users.noreply.github.com> Co-authored-by: dalebennett1992 Co-authored-by: dalebennett1992 <37066267+dalebennett1992@users.noreply.github.com> Co-authored-by: Jeremy Letang Co-authored-by: wwestgarth Co-authored-by: RomanLeca Co-authored-by: Edd Co-authored-by: claudiumilea Co-authored-by: Philip Scott Co-authored-by: Philip Scott <94362991+pscott31@users.noreply.github.com> Co-authored-by: Rado Co-authored-by: Penny Andrews <47143694+pennyandrews@users.noreply.github.com> Co-authored-by: Calum Pringle <46783148+ckpringle@users.noreply.github.com> Co-authored-by: Ben Wallace Co-authored-by: Joe Co-authored-by: m.ray <16125548+MadalinaRaicu@users.noreply.github.com> Co-authored-by: Dariusz Majcherczyk Co-authored-by: Zohar Etzioni Co-authored-by: asiaznik Co-authored-by: Art Co-authored-by: Dexter Edwards Co-authored-by: Joe Tsang <30622993+jtsang586@users.noreply.github.com> Co-authored-by: Sam Keen Co-authored-by: Mikołaj Młodzikowski Co-authored-by: Maciek Co-authored-by: AnExsomnis Co-authored-by: fryd Co-authored-by: Valentin Trinqué --- .github/workflows/add_pr_new_projects.yml | 2 +- .github/workflows/quality_check.yml | 27 +- glossaries/trading-and-protocol-glossary.md | 6 +- makefile | 11 +- .../0001-NP-LIQB-liquidity_providing_bots.md | 12 +- ...0012-NP-LIPE-liquidation-price-estimate.md | 8 +- protocol/0001-MKTF-market_framework.md | 24 +- protocol/0002-STTL-settlement.md | 12 +- .../0003-MTMK-mark_to_market_settlement.md | 2 +- protocol/0004-AMND-amends.md | 52 +- protocol/0005-COLL-collateral.md | 2 +- protocol/0008-TRAD-trading_workflow.md | 2 +- protocol/0009-MRKP-mark_price.md | 6 +- .../0011-MARA-check_order_allocate_margin.md | 17 + protocol/0012-POSR-position_resolution.md | 6 +- protocol/0013-ACCT-accounts.md | 65 +- protocol/0014-ORDT-order_types.md | 321 +++++++++- ...5-INSR-market_insurance_pool_collateral.md | 21 +- protocol/0017-PART-party.md | 3 +- protocol/0019-MCAL-margin_calculator.md | 63 +- protocol/0020-APIS-core_api.md | 2 +- protocol/0021-MDAT-market_data_spec.md | 24 +- protocol/0024-OSTA-order_status.md | 34 +- protocol/0025-OCRE-order_submission.md | 10 +- protocol/0026-AUCT-auctions.md | 36 +- protocol/0027-ASSP-asset_proposal.md | 33 +- protocol/0028-GOVE-governance.md | 307 +++++++++- protocol/0029-FEES-fees.md | 116 +++- protocol/0031-ETHB-ethereum_bridge_spec.md | 6 - protocol/0032-PRIM-price_monitoring.md | 34 +- protocol/0033-OCAN-cancel_orders.md | 14 +- ...PROB-prob_weighted_liquidity_measure.ipynb | 16 +- protocol/0035-LIQM-liquidity_monitoring.md | 28 +- protocol/0036-BRIE-event_queue.md | 2 +- protocol/0037-OPEG-pegged_orders.md | 2 +- ...038-OLIQ-liquidity_provision_order_type.md | 217 ------- .../0039-MKTD-market_depth_calculation.md | 30 +- protocol/0040-ASSF-asset_framework.md | 13 +- protocol/0041-TSTK-target_stake.md | 17 +- ...042-LIQF-setting_fees_and_rewarding_lps.md | 239 +++++--- protocol/0043-MKTL-market_lifecycle.md | 123 +++- protocol/0044-LIME-lp_mechanics.md | 390 +++++++++--- protocol/0045-DSRC-data_sourcing.md | 2 +- .../0046-DSRM-data_source_signed_message.md | 1 + protocol/0047-DSRF-data_source_filter.md | 96 +-- protocol/0048-DSRI-data_source_internal.md | 92 +-- ...-TVAL-validate_transaction_preconsensus.md | 6 +- protocol/0050-EPOC-epochs.md | 20 +- protocol/0051-PROD-product.md | 6 +- .../0052-FPOS-fractional_orders_positions.md | 6 +- ...3-PERP-product_builtin_perpetual_future.md | 226 ++++++- protocol/0054-NETP-network_parameters.md | 10 +- protocol/0055-TREA-on_chain_treasury.md | 25 +- protocol/0056-REWA-rewards_overview.md | 342 +++++++++-- protocol/0057-TRAN-transfers.md | 46 +- protocol/0061-REWP-pos_rewards.md | 14 +- protocol/0062-SPAM-spam_protection.md | 31 +- .../0063-VALK-validator_vega_master_keys.md | 2 +- ...ALP-validator_performance_based_rewards.md | 2 +- .../0065-FTCO-floating_point_consensus.md | 8 +- protocol/0068-MATC-matching_engine.md | 68 ++- .../0069-VCBS-validators_chosen_by_stake.md | 3 - protocol/0070-MKTD-market-decimal-places.md | 21 +- protocol/0072-SPPW-spam-protection-PoW.md | 18 +- protocol/0073-LIMN-limited_network_life.md | 112 +++- .../0074-BTCH-batch-market-instructions.md | 8 +- protocol/0076-DANO-data-node.md | 8 +- protocol/0077-SNAP-snapshots.md | 1 + protocol/0078-NWLI-network_wide_limits.md | 19 - .../0079-TGAP-transaction_gas_and_priority.md | 46 +- protocol/0081-SUCM-successor_markets.md | 130 ++++ protocol/0082-ETHD-ethereum-data-source.md | 188 ++++++ .../0083-RFPR-on_chain_referral_program.md | 487 +++++++++++++++ protocol/0084-VDPR-volume_discount_program.md | 148 +++++ protocol/0085-RVST-rewards_vesting.md | 106 ++++ protocol/0086-ASPR-activity_streak_program.md | 143 +++++ protocol/README.md | 4 +- protocol/categories.json | 4 +- protocol/features.json | 570 ++++++++++++++++++ wordlist.txt | 27 + 80 files changed, 4361 insertions(+), 1040 deletions(-) delete mode 100644 protocol/0038-OLIQ-liquidity_provision_order_type.md create mode 100644 protocol/0081-SUCM-successor_markets.md create mode 100644 protocol/0082-ETHD-ethereum-data-source.md create mode 100644 protocol/0083-RFPR-on_chain_referral_program.md create mode 100644 protocol/0084-VDPR-volume_discount_program.md create mode 100644 protocol/0085-RVST-rewards_vesting.md create mode 100644 protocol/0086-ASPR-activity_streak_program.md create mode 100644 protocol/features.json 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/quality_check.yml b/.github/workflows/quality_check.yml index a44d58c42..f649a0c47 100644 --- a/.github/workflows/quality_check.yml +++ b/.github/workflows/quality_check.yml @@ -19,7 +19,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 +31,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 +43,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,7 +57,14 @@ 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 @@ -68,8 +75,12 @@ jobs: - name: "Run Check AC codes" run: | - docker run -v $PWD:/workspace ghcr.io/vegaprotocol/approbation:main check-codes --specs="{/workspace/non-protocol-specs/**/*.md,/workspace/protocol/**/*.md}" + npx github:vegaprotocol/approbation check-codes --specs="{./non-protocol-specs/**/*.md,./protocol/**/*.md,./protocol/**/*.ipynb}" - name: "Run Check file names" run: | - docker run -v $PWD:/workspace ghcr.io/vegaprotocol/approbation:main check-filenames --specs="{/workspace/non-protocol-specs/**/*.md,/workspace/protocol/**/*.md}" \ No newline at end of file + 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/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/0012-NP-LIPE-liquidation-price-estimate.md b/non-protocol-specs/0012-NP-LIPE-liquidation-price-estimate.md index 39ce7906e..b1a0e973f 100644 --- a/non-protocol-specs/0012-NP-LIPE-liquidation-price-estimate.md +++ b/non-protocol-specs/0012-NP-LIPE-liquidation-price-estimate.md @@ -6,7 +6,7 @@ Provide an estimate of the price range at which the liquidation of a specified p ## Overview -Provide a range of liquidation price, 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. +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 both linear and quadratic slippage factor set to `0` and once with the actual values used by the market for which the specified position is being considered. @@ -27,13 +27,13 @@ where $V$ is the open volume (negative for a short position) and $S^\text{curren 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}} (V \cdot \text{linear slippage factor}+V^2 \cdot \text{quadratic slippage factor}+V \cdot \text{risk factor}), +\text{collateral available} + V(S^{\text{liquidation}}-S^\text{current}) = S^{\text{liquidation}} (V \cdot \text{linear slippage factor}+V^2 \cdot \text{quadratic slippage factor}+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. Solving for $S^{\text{liquidation}}$ we get: +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{linear slippage factor}+V^2 \cdot \text{quadratic slippage factor}+V \cdot \text{risk factor}-V} +S^{\text{liquidation}} = \frac{\text{collateral available}-V \cdot S^\text{current} - V \cdot \text{constant}}{V \cdot \text{linear slippage factor}+V^2 \cdot \text{quadratic slippage factor}+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). diff --git a/protocol/0001-MKTF-market_framework.md b/protocol/0001-MKTF-market_framework.md index 4a86e205b..1d0599a5f 100644 --- a/protocol/0001-MKTF-market_framework.md +++ b/protocol/0001-MKTF-market_framework.md @@ -157,7 +157,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 +217,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..fc6458f63 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,7 +64,7 @@ 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. @@ -79,7 +79,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 is [redistributed](./0015-INSR-market_insurance_pool_collateral.md) equally between the global insurance pool and the insurance pools of the remaining active markets 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) @@ -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 is [redistributed](./0015-INSR-market_insurance_pool_collateral.md) equally between the global insurance pool and the insurance pools of the remaining active markets 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) @@ -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 is [redistributed](./0015-INSR-market_insurance_pool_collateral.md) equally between the global insurance pool and the insurance pools of the remaining active markets 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..7b7355f2b 100644 --- a/protocol/0003-MTMK-mark_to_market_settlement.md +++ b/protocol/0003-MTMK-mark_to_market_settlement.md @@ -69,7 +69,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..6259abe41 100644 --- a/protocol/0004-AMND-amends.md +++ b/protocol/0004-AMND-amends.md @@ -2,36 +2,36 @@ ## 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 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). 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 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..353389907 100644 --- a/protocol/0009-MRKP-mark_price.md +++ b/protocol/0009-MRKP-mark_price.md @@ -8,7 +8,8 @@ Introduce a network parameter `network.markPriceUpdateMaximumFrequency` with min ## 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) Algorithm 1: @@ -16,7 +17,7 @@ Algorithm 1: - 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) ## Guide-level explanation @@ -71,3 +72,4 @@ The mark price is set to the higher / lower of the last traded price, bid/offer. ### 5. Defined as part of the product The *Mark Price* may be calculated using an algorithm defined by the product -- and 'selected' by a market parameter. + diff --git a/protocol/0011-MARA-check_order_allocate_margin.md b/protocol/0011-MARA-check_order_allocate_margin.md index 722afe1f6..a4cb68573 100644 --- a/protocol/0011-MARA-check_order_allocate_margin.md +++ b/protocol/0011-MARA-check_order_allocate_margin.md @@ -9,10 +9,14 @@ Orders should be rejected if we can’t allocate sufficient margin. ## Acceptance criteria 1. If an order is amended such that margin requirement is increased and user has sufficient balance in the general account to top up their margin account then the amendment is executed successfully. (0011-MARA-001) +1. In Spot market, if an order is amended such that holding requirement is increased and user has sufficient balance in the general account to top up their holding account then the amendment is executed successfully. (0011-MARA-018) 1. If an order is amended such that margin requirement is increased and user doesn't have sufficient balance in the general account to top up their margin account then their amend is not executed but the unamended order stays on the book. (0011-MARA-002) +1. In Spot market, if an order is amended such that holding requirement is increased and user doesn't have sufficient balance in the general account to top up their holding account then their amend is not executed but the unamended order stays on the book. (0011-MARA-019) 1. Cancelling an order releases the margin amount back to user's general account, provided the user has no other orders or positions (0011-MARA-003) +In Spot market, cancelling an order releases the holding amount back to user's general account. (0011-MARA-020) 1. If an order is amended such that margin requirement is decreased then the amendment is executed successfully. (0011-MARA-004) 1. If an order is partially filled then the margin requirements are recalculated reflecting the reduced order size and new position size. (0011-MARA-005) +In Spot market, if an order is partially filled then the holding requirements are recalculated reflecting the reduced order size. (0011-MARA-021) 1. If an order is partially filled and if this leads to a reduced position and reduced riskiest long / short then the margin requirements are seen to be reduced and if margin balance is above release level then the excess amount is transferred to the general account. (0011-MARA-006) 1. Margin is correctly calculated for [all order types](./0014-ORDT-order_types.md) in continuous trading: 1. Limit GTT (0011-MARA-007) @@ -27,6 +31,19 @@ Orders should be rejected if we can’t allocate sufficient margin. 1.Limit GFA (0011-MARA-015) 1.Pegged GTT (parked in auction \*) (0011-MARA-016) 1.Pegged GTC (parked in auction \* ) (0011-MARA-017) +1. In Spot market, holding in holding account is correctly calculated for [all order types](./0014-ORDT-order_types.md) in continuous trading: + 1. Limit GTT (0011-MARA-022) + 1. Limit GTC (0011-MARA-023) + 1. Limit GFN (0011-MARA-024) + 1. Pegged GTT (0011-MARA-025) + 1. Pegged GTC (0011-MARA-026) + 1. Pegged GFN (0011-MARA-027) +1. In Spot market, holding in holding account is correctly calculated for [all order types](./0014-ORDT-order_types.md) in auction mode: + 1.Limit GTT (0011-MARA-028) + 1.Limit GTC (0011-MARA-029) + 1.Limit GFA (0011-MARA-030) + 1.Pegged GTT (parked in auction \*) (0011-MARA-031) + 1.Pegged GTC (parked in auction \* ) (0011-MARA-032) ## Pseudocode diff --git a/protocol/0012-POSR-position_resolution.md b/protocol/0012-POSR-position_resolution.md index 35b2028c7..6c69b452c 100644 --- a/protocol/0012-POSR-position_resolution.md +++ b/protocol/0012-POSR-position_resolution.md @@ -26,11 +26,11 @@ See [Whitepaper](https://vega.xyz/papers/vega-protocol-whitepaper.pdf), Section 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. 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 market's 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. +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 market's 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. ### Note @@ -124,7 +124,7 @@ This results in the open position sizes for all distressed traders and the netwo #### STEP 5 -The collateral from distressed traders is moved to the insurance pool +The collateral from distressed traders is moved to the market's insurance pool ```json // sent by Settlement Engine to the Collateral Engine diff --git a/protocol/0013-ACCT-accounts.md b/protocol/0013-ACCT-accounts.md index 9d82ff779..1c4243b41 100644 --- a/protocol/0013-ACCT-accounts.md +++ b/protocol/0013-ACCT-accounts.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 redistributed equally between the global insurance pool and the insurance pools of the remaining active markets 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,22 @@ 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) +### Party holding accounts in Spot market + +- Every party that submits an order on a Spot market will have a holding account created for the relevant market asset pair. (0013-ACCT-030) +- Each party should only have two holding accounts per market: one for the the base_asset and one for the quote_asset. (0013-ACCT-031) + ### 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 all markets of a risk universe expire and/or are closed, the insurance pool account has its outstanding funds redistributed to the global insurance pool account for the appropriate asset (if it doesn't exist create it) and other insurance pools using the same asset. (0013-ACCT-032) ### Special case: Staking accounts @@ -139,3 +173,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 72d9568ca..b9190402a 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 @@ -46,9 +45,182 @@ Notes on scope of current version of this spec: ### 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, 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. **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 order, 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 probably 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 cannot be set to execute at expiry. + +- The stop order submission wraps a normal order submission. + +- 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, 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. + +- 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 +255,145 @@ 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) +In Spot market, for an iceberg order that is submitted with total size x and display size y the holding asset taken should be identical to a regular order of size `x` rather than one of size `y` (0014-ORDT-091) +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) +4. In Spot market, 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-103) + +#### 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) +1. In Spot market, cancelling an iceberg order will cancel the order, remove it from the order book , release holding asset and update order book to reflect the change (0014-ORDT-104) + +#### 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) + +- 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 either prior to or during an auction will not execute during an auction, nor will it participate in the uncrossing. (0014-ORDT-065) +- A stop order placed either prior to or during an auction, where the uncrossing price is within the triggering range, will immediately execute following uncrossing. (0014-ORDT-066) + +- 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) + +## 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) + +## 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) + +## 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..be3dee548 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 a portion of the funds from market's insurance pool upon its closure (funds get distributed equally between the global insurance pool and remaining active markets using the same settlement asset, e.g. if there are 4 such markets then the global insurance pool receives 1/5 of the funds). + +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[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) - 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 e652f1a4e..334d9a286 100644 --- a/protocol/0019-MCAL-margin_calculator.md +++ b/protocol/0019-MCAL-margin_calculator.md @@ -27,11 +27,11 @@ - 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 ```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) @@ -40,6 +40,48 @@ - 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) +- 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: @@ -106,8 +148,8 @@ In this simple methodology, a linearised margin formula is used to return the ma with ```formula -maintenance_margin_long - = max(min(riskiest_long * slippage_per_unit, product.value(market_observable) * (riskiest_long * market.maxSlippageFraction[1] + riskiest_long^2 * market.maxSlippageFraction[2])), 0) +maintenance_margin_long + = max(min(riskiest_long * slippage_per_unit, product.value(market_observable) * (riskiest_long * market.maxSlippageFraction[1] + riskiest_long^2 * market.maxSlippageFraction[2])), 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) ]`, ``` @@ -135,7 +177,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.maxSlippageFraction[1] + slippage_volume^2 * market.maxSlippageFraction[2])) = mark_price * (slippage_volume * market.maxSlippageFraction[1] + slippage_volume^2 * market.maxSlippageFraction[2])` above. ### **Step 2** @@ -144,15 +186,13 @@ If `riskiest short == 0` then `maintenance_margin_short = 0`. Else ```formula -maintenance_margin_short - = max(min(abs(riskiest short) * slippage_per_unit, mark_price * (abs(riskiest short) * market.maxSlippageFraction[1] + abs(slippage_volume)^2 * market.maxSlippageFraction[2])), 0) +maintenance_margin_short + = max(min(abs(riskiest short) * slippage_per_unit, mark_price * (abs(riskiest short) * market.maxSlippageFraction[1] + abs(slippage_volume)^2 * market.maxSlippageFraction[2])), 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) ]` ``` where meanings of terms in Step 1 apply except for: -`slippage_volume = min( open_volume, 0 )`, - `slippage_per_unit = max(0, Product.value(exit_price)-Product.value(market_observable))` ### **Step 3** @@ -235,8 +275,7 @@ riskiest_short = min( open_volume + sell_orders, 0 ) = min( 10 - 8, 0 ) = 0 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 - -maintenance_margin_long =max(min(riskiest_long * slippage_per_unit, product.value(market_observable) * (riskiest_long * market.maxSlippageFraction[1] + riskiest_long^2 * market.maxSlippageFraction[2])), 0) +maintenance_margin_long =max(min(riskiest_long * slippage_per_unit, product.value(market_observable) * (riskiest_long * market.maxSlippageFraction[1] + riskiest_long^2 * market.maxSlippageFraction[2])), 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) ] diff --git a/protocol/0020-APIS-core_api.md b/protocol/0020-APIS-core_api.md index d129cc0b6..c29a57d09 100644 --- a/protocol/0020-APIS-core_api.md +++ b/protocol/0020-APIS-core_api.md @@ -63,7 +63,7 @@ On any Vega node, I can: | Requirement | Acceptance Criteria code | |-----------|:------------------------:| | List all governance proposals via REST & GRPC |0020-APIS-001| -| List all governance proposals by a specified party via REST & GRPC |0020-APIS-002 | +| List all governance proposals by a specified party via REST & GRPC |0020-APIS-002 | Retrieve a specific governance proposals by id via REST & GRPC |0020-APIS-003 | | Retrieve a list of votes via REST & GRPC |0020-APIS-004| | Retrieve the governance stake for a specified party via REST & GRPC |0020-APIS-005| 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..14be7ce9b 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,12 @@ 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). +- In Spot market, holding will taken before the order is entered into the book(0025-OCRE-005) + - 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..dc190b537 100644 --- a/protocol/0026-AUCT-auctions.md +++ b/protocol/0026-AUCT-auctions.md @@ -87,7 +87,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 @@ -157,24 +157,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..159c5d745 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 + } } ``` @@ -200,3 +200,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 789f1f308..d1afeadfc 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,14 +223,16 @@ 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 @@ -261,7 +271,149 @@ 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. Freeform governance proposal +## 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 | Transfer permitted | +| --- | --- | --- | +| Party account (any type) | Any | No | +| Network treasury | Network treasury | No | +| Network treasury | Party general account(s) | Yes | +| Network treasury | Party other account types | No | +| 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 | +| Market insurance pool account | Party account(s) | Yes | +| Market insurance pool account | Network treasury | 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, 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) + - 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 + - best effort: transfers the specified amount or the max allowable amount if this is less than the specified amount +- `amount`: the maximum amount to transfer +- `asset`: the asset to transfer +- `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: + - network treasury: leave blank (only one per asset) + - party: the party's public key + - 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 + +If the proposal is successful and enacted, the amount will be transferred from the source account to the destination account on the enactment date. + +The amount is calculated by + +```go + transfer_amount = min( + proposal.fraction_of_balance * source.balance, + proposal.amount, + NETWORK_MAX_AMOUNT, + NETWORK_MAX_FRACTION * source.balance ) +``` + +Where: + +- `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: + +```go +transfer_amount == min( + proposal.fraction_of_balance * source.balance, + proposal.amount ) +``` + +### 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 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. @@ -354,7 +506,7 @@ 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 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](./0044-LIME-lp_mechanics.md#commit-liquidity-network-transaction) 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) @@ -368,11 +520,32 @@ 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) -- 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 filter that requires the price to be both strictly greater than `0` and strictly less than `0`. Verify that after the trading terminates it is impossible to settle the market with various inputs. That's intentional. Now submit a market change proposal to settle the market with a fixed key filtering on a specific value e.g. it's only possible to settle with a price of `1000`. Wait for the vote to pass and enact. Now settle the market and verify it settled at the pre-specified price. (0028-GOVE-069) +- 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 governance proposal to close a market which doesn't specify the final settlement price gets rejected by the markets which require it. (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. (0028-GOVE-114) +- 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 @@ -397,3 +570,105 @@ 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) + +##### 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..fb20caa84 100644 --- a/protocol/0031-ETHB-ethereum_bridge_spec.md +++ b/protocol/0031-ETHB-ethereum_bridge_spec.md @@ -153,12 +153,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 0bbf71e76..582e68a6b 100644 --- a/protocol/0032-PRIM-price_monitoring.md +++ b/protocol/0032-PRIM-price_monitoring.md @@ -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) +- 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) -- 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) +(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) +- 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 1a8670988..7a48caf0b 100644 --- a/protocol/0033-OCAN-cancel_orders.md +++ b/protocol/0033-OCAN-cancel_orders.md @@ -2,15 +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) +- 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..990e3f6ed 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": [ @@ -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 index 8a7d58986..a2392fc87 100644 --- a/protocol/0035-LIQM-liquidity_monitoring.md +++ b/protocol/0035-LIQM-liquidity_monitoring.md @@ -9,12 +9,11 @@ Similarly to [price monitoring](./0032-PRIM-price_monitoring.md), we need to be 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`. +`lp_liquidity_obligation_in_ccy_volume = market.liquidity.stakeToCcyVolume ⨉ 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 (*). +Thus it is sufficient to compare `target_stake` with `total_stake`. 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 @@ -28,7 +27,7 @@ Note that [target stake](./0041-TSTK-target_stake.md) is defined in a separate s The auction is triggered when -`total_stake < c_1 x target_stake OR there is no best_bid OR there is no best offer`. +`total_stake < c_1 x target_stake`. 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. @@ -40,21 +39,17 @@ If an incoming order would match orders on the book resulting in trades increasi ### 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 [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 a liquidity auction is triggered the next time conditions for liquidity auctions are evaluated. -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. +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 the next time conditions for liquidity auctions are evaluated. ## Trigger for exiting the auction We exit if -`total_stake >= target_stake AND there is best_bid AND there is best_offer`. +`total_stake >= target_stake`. -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. +During the liquidity monitoring auction new or existing LPs can commit more stake (and hence liquidity) through the special market making transaction 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 exited. ## What happens during the auction? @@ -67,20 +62,15 @@ The auction proceeds as usual. Please see the [auction spec](./0026-AUCT-auction 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. +A liquidity provider amending LP provision order can reduce their stake even if doing so would mean that at the end of block the system enters 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) +1. When proposing a market the supplied triggering ratio value must be >= 0 and <= 1 else the proposal is rejected diff --git a/protocol/0036-BRIE-event_queue.md b/protocol/0036-BRIE-event_queue.md index b81272c73..ca9c25670 100644 --- a/protocol/0036-BRIE-event_queue.md +++ b/protocol/0036-BRIE-event_queue.md @@ -37,7 +37,7 @@ Finally, the amount of confirmations expected for Ethereum is specified, this is 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. The Validator responsible for forwarding an event is determined through a hash of the event details and Vega time, i.e., every event is usually only forwarded by one validator. +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: 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 685826581..40a7cd340 100644 --- a/protocol/0041-TSTK-target_stake.md +++ b/protocol/0041-TSTK-target_stake.md @@ -1,13 +1,15 @@ # 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. +The parameter c_1 is a market parameter 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 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`. @@ -16,7 +18,7 @@ The parameter c_1 is a market parameter (with network parameter `market.liquidit - `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.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. @@ -52,16 +54,21 @@ Note that the units of `target_stake` are the settlement currency of the market 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 9e9347e55..18556f624 100644 --- a/protocol/0042-LIQF-setting_fees_and_rewarding_lps.md +++ b/protocol/0042-LIQF-setting_fees_and_rewarding_lps.md @@ -2,15 +2,15 @@ ## 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 liquidity fee factor 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 +18,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] ... @@ -38,7 +38,7 @@ Finally, we set the liquidity-fee-factor for this market to be the fee `LP-k-liq 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%] @@ -51,7 +51,7 @@ In the example below there are 3 liquidity providers all bidding for their chose ### 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? @@ -67,28 +67,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 +122,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 +135,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 +143,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,95 +165,140 @@ 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. +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 that market (call it the liquidity fee account). The account is under control of the network and funds from this account will be transferred to a LP party according to the mechanism below. +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. -A network parameter `market.liquidity.providers.fee.distributionTimeStep` will control how often fees are distributed from the liquidity 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 funds in this account are transferred to the liquidity providers general accounts 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. +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. -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 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. -Flooring non-zero amounts due to integer representation can result in a remainder in the liquidity fee account. The remainder should be left in the market liquidity fee account and carried over to the next distribution window. +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. -If the fees are being distributed as a part of market settlement and distribution results in a remainder in the liquidity fee account. The remainder should be transferred to the network treasury for that asset. +### Calculating SLA performance -#### Example +#### Measuring time spent meeting their commitment in a single epoch -We have `4` LPs with equity-like share shares: -share as below +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: -```math -LP 1 els = 0.65 -LP 2 els = 0.25 -LP 3 els = 0.1 -``` +- At the start of a new epoch, `s_i = 0` -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: + - 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`. -```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 -``` +- 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`. -Then LP 4 made a delayed LP commitment, and updated share as below: +- 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. -```math -LP 1 els = 0.43 -LP 2 els = 0.17 -LP 3 els = 0.07 -LP 4 els = 0.33 -``` + - 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. -When the time defined by `market.liquidity.providers.fee.distributionTimeStep` elapses we do transfers: +#### Calculating the SLA performance penalty for a single epoch -```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 +Calculate the fraction of the time the LP spent on the book: + +```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 @@ -260,18 +312,19 @@ When the time defined by `market.liquidity.providers.fee.distributionTimeStep` e - 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) + ### 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) -- If after fee distribution during market settlement, there is a remainder in the market liquidity fee account, the remainder should be transferred to the network treasury for that asset. (0042-LIQF-031) +- 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) @@ -285,14 +338,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..4fd63f259 100644 --- a/protocol/0043-MKTL-market_lifecycle.md +++ b/protocol/0043-MKTL-market_lifecycle.md @@ -12,7 +12,7 @@ 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). @@ -26,13 +26,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 +| 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 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) | 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 +42,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 +81,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 +106,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 balance should get [redistributed](./0015-INSR-market_insurance_pool_collateral.md) equally between the global insurance pool and the insurance pools of the remaining active markets 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. @@ -173,7 +172,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 +184,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,7 +209,6 @@ 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 ### Settled @@ -218,9 +216,11 @@ A market moves from this termination state to Settled when enough information ex 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 redistributed equally between the global insurance pool and the insurance pools of the remaining active markets using the same settlement asset. For markets that have a named successor market 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 +237,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. @@ -245,6 +244,7 @@ The market can be deleted entirely at this point, from a core perspective. ### Market is proposed but rejected (0043-MKTL-001) + 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`. 1. Parties vote against the market proposal. @@ -279,18 +279,103 @@ 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 balance is [redistributed](./0015-INSR-market_insurance_pool_collateral.md) equally between the global insurance pool and the insurance pools of the remaining active markets using the same settlement asset. -### Market never leaves opening auction, trading terminated trigger rings, market cancelled (0043-MKTL-003) +### Lifecycle happy path in Spot market (0043-MKTL-006) + +1. Market `m1` is proposed. 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. When a new governance proposal for "closing" the Spot market, then market state is `trading terminated`. + +### 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. 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. + +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 balance is [redistributed](./0015-INSR-market_insurance_pool_collateral.md) equally between the global insurance pool and the insurance pools of the remaining active markets using the same settlement asset. + +### Market never leaves opening auction, trading terminated triggered, market cancelled (0043-MKTL-003) 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 [redistributed](./0015-INSR-market_insurance_pool_collateral.md) equally between the global insurance pool and the insurance pools of the remaining active markets 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 (Spot) never leaves opening auction, market cancelled by governance proposal(0043-MKTL-007) + +1. A market is proposed, approved by governance process and enters the opening auction (Pending state). +1. Market cancelled before the market leaves the opening auction (so market never left Pending state so far). +1. All orders should be cancelled and holdings 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. 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 (Spot) gets closed via a governance proposal (0043-MKTL-008) + +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. All the funds from market specific accounts get released to appropriate accounts. +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..8ee5da6df 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,107 @@ 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 (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. -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. +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`. + +Note that as a consequence the market may land in a liquidity auction the next time conditions for liquidity auctions are evaluated (but there is no need to tie the event of LP(s) reducing their commitment to an immediate liquidity auction evaluation). ## Fees @@ -113,42 +174,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 +268,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 +295,131 @@ 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) + +- Consider a market in liquidity auction, when a LP increases their commitment it will take effect immediate for the purposes of LP stake supplied to the market. Where LP `supplied stake > target stake` the market will leave liquidity auction when the liquidity auction ends + - In terms of the liquidity they are expected to supply: this only takes effect from the start of the next epoch + (0044-LIME-102) + +- 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 ce176eb33..81001341c 100644 --- a/protocol/0045-DSRC-data_sourcing.md +++ b/protocol/0045-DSRC-data_sourcing.md @@ -240,7 +240,7 @@ Vega should reject any data source tx that is not explicitly required, so this w 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) diff --git a/protocol/0046-DSRM-data_source_signed_message.md b/protocol/0046-DSRM-data_source_signed_message.md index 5aeead235..8283308c1 100644 --- a/protocol/0046-DSRM-data_source_signed_message.md +++ b/protocol/0046-DSRM-data_source_signed_message.md @@ -133,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 2ecbda0d1..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,30 +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. It's possible to query all active and deactivated internal data sources for a market via REST, gRPC and GraphQL. (0048-COSMICELEVATOR-009) + 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..4d186b9aa 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). F 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..7323fd116 100644 --- a/protocol/0053-PERP-product_builtin_perpetual_future.md +++ b/protocol/0053-PERP-product_builtin_perpetual_future.md @@ -1,17 +1,54 @@ -# 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 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 the mark price 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. + +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]`, +- `clamp_upper_bound` >= `clamp_lower_bound`. + +### 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 +65,172 @@ 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`. + +If there are no oracle data points with a timestamp less than `funding_period_end` available then funding payment is skipped and `funding_period_start` gets overwritten with `funding_period_end`. + +If such points are available then the calculations discussed in the following subsections get executed and funding payments get exchanged. + +#### TWAP spot price calculation + +Traverse all the available oracle data point tuples `(s,t)` and calculate the time-weighted average spot price (`s_twap`) as: + +```go +var previous_point +sum_product := 0 + +for p := range oracle_data_points { + if p.t <= funding_period_start { + previous_point = p + continue + } + if p.t >= funding_period_end { + break + } + if previous_point != nil { + sum_product += previous_point*(p.t-max(funding_period_start,previous_point.t)) + } + previous_point = p +} + +sum_product += previous_point.s*(funding_period_end-max(funding_period_start,previous_point.t)) +s_twap = sum_product / (funding_period_end - max(funding_period_start, oracle_data_points[0].t)) +``` + +Only the oracle data point with largest timestamp that's less than or equal to `funding_period_end` (and any data points with larger timestamps) need to be kept from that point on. + +#### TWAP mark price calculation + +Traverse all the available internal data point tuples `(f,t)` and calculated the time-weighted average mark price (`f_twap`) as: + +```go +var previous_point +sum_product := 0 + +for p := range internal_data_points { + if p.t <= funding_period_start { + previous_point = p + continue + } + if previous_point != nil { + sum_product += previous_point*(p.t-max(funding_period_start,previous_point.t)) + } + previous_point = p } + +sum_product += previous_point.f*(funding_period_end-max(funding_period_start,previous_point.t)) +f_twap = sum_product / (funding_period_end - max(funding_period_start, internal_data_points[0].t)) +``` + +Only the internal data point with largest timestamp needs to be kept from that point on. + +#### 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. + +#### 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 = (f_twap - s_twap) / 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 liquidity monitoring auction results in the exchange of periodic settlement cashflows. Market remains in liquidity monitoring auction until enough additional liquidity gets committed to the market. (0053-PERP-008) +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) 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..5f2ede06f 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. -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. +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. + +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,41 +160,123 @@ 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 `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. +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. -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. +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}$$ + +Calculate each parties 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}}$$ ## Acceptance criteria @@ -193,11 +353,11 @@ At the end of epoch 2: #### 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 @@ -215,11 +375,11 @@ After having an epoch with trading activity, fund the reward account, but have n #### 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: @@ -261,7 +421,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. @@ -319,11 +479,11 @@ At the end of epoch `2` `party_0` is paid `120 x 2.8 / (2.79+2.8)` USDC from the #### 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 @@ -335,17 +495,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-022) + #### Rationale 7 After having an epoch with trading activity, fund the reward account, but have no trading activity and assert that no payout is made. #### 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 +520,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 +549,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 +560,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 +586,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 +605,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: @@ -465,13 +630,14 @@ Looking only at epoch 3 - as no trading activity was done, we expect the reward ### Distributing LP fees received - multiple markets (0056-REWA-033) + #### Rationale 12 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 +647,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 +656,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 +676,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 +699,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 +721,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 +744,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 +768,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 +797,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 +830,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 +841,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 +866,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 @@ -738,10 +914,70 @@ 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). + +### 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..146d2402e 100644 --- a/protocol/0057-TRAN-transfers.md +++ b/protocol/0057-TRAN-transfers.md @@ -79,14 +79,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). @@ -189,10 +224,11 @@ 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 an 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 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. (0057-TRAN-007) - The fee cost is correctly calculated using the network parameter - If I have enough funds to pay transfer and fees, the transfer happens. diff --git a/protocol/0061-REWP-pos_rewards.md b/protocol/0061-REWP-pos_rewards.md index 608457d2d..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 @@ -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). @@ -49,7 +49,7 @@ The following formulas then apply to both primary and ersatz validators, where ' ## 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..57a715b82 100644 --- a/protocol/0062-SPAM-spam_protection.md +++ b/protocol/0062-SPAM-spam_protection.md @@ -80,6 +80,22 @@ 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 who does not meet this requirement 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`). Any party who submits more than `n` transactions of a single referral transaction type in a single epoch will have all future transactions of that type pre-block rejected. + +Any party who manages to fit more then `n` transactions of a single type into a single block will have their excess transactions post-block rejected. A party who has more than 50% of their transactions post-block rejected will be banned for 1/48th of an epoch, or un till the end of the current epoch, whichever comes first. + ### Related topics - [Spam protection: Proof of work](https://github.com/vegaprotocol/specs/blob/master/protocol/0072-SPPW-spam-protection-PoW.md) @@ -116,6 +132,19 @@ More than 360 delegation changes in one epoch (or, respectively, the value of `s - 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). +- 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 **post-block** rejected (0062-SPAM-032). +- A party who has more than 50% of their `CreateReferralSet` transactions post-block rejected should be banned for 1/48th of an epoch or un till the end of the current epoch (whichever comes first). When banned for the above reason, `CreateReferralSet` transactions should be pre-block rejected (0062-SPAM-033). +- 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 **post-block** rejected (0062-SPAM-034). +- A party who has more than 50% of their `UpdateReferralSet` transactions post-block rejected should be banned for 1/48th of an epoch or un till the end of the current epoch (whichever comes first). When banned for the above reason, `UpdateReferralSet` transactions should be pre-block rejected (0062-SPAM-035). +- 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 **post-block** rejected (0062-SPAM-036). +- A party who has more than 50% of their `ApplyReferralCode` transactions post-block rejected should be banned for 1/48th of an epoch or un till the end of the current epoch (whichever comes first). When banned for the above reason, `ApplyReferralCode` transactions should be pre-block rejected (0062-SPAM-037). + > **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..f9f88609e 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 @@ -100,12 +100,12 @@ This section outlines floating-point quantities `vega` currently relies on: - 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 +115,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 864c65e02..faf8fd03a 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,12 +27,13 @@ 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 order will be cancelled(0068-MATC-048) @@ -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 [GTC/GTT](./0014-ORDT-order_types.md#time-in-force---validity) orders remain on the book untouched. (0068-MATC-028) +- 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 5a2e72185..09891ea03 100644 --- a/protocol/0069-VCBS-validators_chosen_by_stake.md +++ b/protocol/0069-VCBS-validators_chosen_by_stake.md @@ -564,9 +564,6 @@ Setup a network with 6 nodes (3 validators, 2 ersatz validators, 1 pending valid ### 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 001cb67d8..d0db667ef 100644 --- a/protocol/0072-SPPW-spam-protection-PoW.md +++ b/protocol/0072-SPPW-spam-protection-PoW.md @@ -93,7 +93,7 @@ All Vega clients that submitted transactions can verify that their transaction h - 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) - 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) @@ -101,30 +101,30 @@ All Vega clients that submitted transactions can verify that their transaction h - 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) +- *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 7cf68d112..45991078f 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,46 @@ 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.1: Spot market is proposed, accepted, restored (0073-LIMN-102) + +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.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 +212,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 +229,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 +245,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 +262,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 +275,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,6 +283,14 @@ 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 4b: In Spot market, party's Holding Account balance is put in to a General Account balance for that asset after a reset (0073-LIMN-080) + +1. A party has USD general account balance of 100 USD. +2. That party has USD holding account balance of 50 USD. +3. The network is shut down. +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 150 USD + ### Test case 5: Add or remove stake during checkpoint restart (0073-LIMN-017) 1. There is a Vega token asset. @@ -277,7 +335,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. @@ -292,6 +350,7 @@ If for `network.checkpoint.timeElapsedBetweenCheckpoints` the value is set to `0 ### Test case 13: A market with future enactment date can become enacted after being restored from checkpoint (0073-LIMN-028) + 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. @@ -312,26 +371,28 @@ 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. In Spot market, for parties that had holdings in the holding account on the market this is now in their general account for the asset. (0073-LIMN-084) +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 redistributed equally between the global insurance pool and the insurance pools of the remaining active markets using the same settlement asset. (0073-LIMN-112) +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. In Spot market, for parties that had holdings in their holding accounts on the market this is now in their general account for the asset. (0073-LIMN-088) +1. The insurance pool balance has been redistributed equally between the global insurance pool and the insurance pools of the remaining active markets using the same settlement asset. (0073-LIMN-113) +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 +401,20 @@ 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. In Spot market, for parties that had holdings in their holding accounts on the market this is now in their general account for the asset. (0073-LIMN-094) +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 redistributed equally between the global insurance pool and the insurance pools of the remaining active markets using the same settlement asset. (0073-LIMN-114) +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,9 +422,9 @@ 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 @@ -372,7 +434,7 @@ If for `network.checkpoint.timeElapsedBetweenCheckpoints` the value is set to `0 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) +1. There party's newly deposited assets are available. (0073-LIMN-074). ### Test case 20: Multisig updates during checkpoint restart diff --git a/protocol/0074-BTCH-batch-market-instructions.md b/protocol/0074-BTCH-batch-market-instructions.md index 31c6f3f4b..3deffde49 100644 --- a/protocol/0074-BTCH-batch-market-instructions.md +++ b/protocol/0074-BTCH-batch-market-instructions.md @@ -48,15 +48,15 @@ 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) diff --git a/protocol/0076-DANO-data-node.md b/protocol/0076-DANO-data-node.md index 4dc6a1d2d..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,7 +145,7 @@ 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) @@ -176,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 @@ -207,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..6e8e6c579 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,20 @@ 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-005) -### Test we don't overfill a block with a market (0079-TGAP-003) 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..417e417e5 --- /dev/null +++ b/protocol/0081-SUCM-successor_markets.md @@ -0,0 +1,130 @@ +# 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 distributed network wide (other markets in same settlement asset, 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) +- 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 distributed equally across the global insurance pool and all markets with the same settlement asset, including those markets which are still in opening auction (0081-SUCM-035) + +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: a) 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 inc. 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..4cb4bb620 --- /dev/null +++ b/protocol/0082-ETHD-ethereum-data-source.md @@ -0,0 +1,188 @@ +# 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) + +### 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..8b9b9d7b9 --- /dev/null +++ b/protocol/0083-RFPR-on_chain_referral_program.md @@ -0,0 +1,487 @@ +# 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 or equal to `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 or equal to `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 accepting new members (defaults to false) + +*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, + } +} +``` + +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 accepting new members + +```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 by setting the `closed` field to `True`. Note, closing a team is the same as closing a referral set and as such all `ApplyReferralCode` transactions applying the referral code associated with the closed referrals set should be rejected. + +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** + +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. Their key must then become associated with the referrer's key. All referral rewards will be transferred to this referrer's key, regardless of whether the party reapplies a new referral code. + +If a party is already a referee, and submits another `ApplyReferralCode` transaction, they will not be transferred to the new referral set but they will be added to the associated team at the start of the next epoch (providing a team exists). Note, if the referee has submitted multiple transactions in an epoch, the referee will be added to the new team specified in the latest valid transaction. + +### 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 or equal to `0` and less than or equal to the network parameter `referralProgram.maxReferralRewardFactor` (0083-RFPR-004). + - all `referral_discount_factor` values must be greater than or equal to `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). + +#### Applying a referral code + +1. If a party **is not** currently a **referee**, if they submit a signed `ApplyReferralCode` transaction then: (0083-RFPR-025) + + - 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). + +1. If a party **is** currently a **referee** (and the referrer **is** meeting the staking requirement), if they submit a signed `ApplyReferralCode` transaction then: (0083-RFPR-026) + + - the party **will not** 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). + +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-027). + + - 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). + +1. An `ApplyReferralCode` transaction should be rejected if the party is a **referrer** (0083-RFPR-029). +1. An `ApplyReferralCode` transaction should be rejected if the `id` in the `ApplyReferralCode` transaction is for a referral set which is designated as a team and has set the team to be closed (0083-RFPR-030). + +#### 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..c94c4ae84 --- /dev/null +++ b/protocol/0085-RVST-rewards_vesting.md @@ -0,0 +1,106 @@ +# 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. + +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/README.md b/protocol/README.md index 90d265af2..14f8dc393 100644 --- a/protocol/README.md +++ b/protocol/README.md @@ -50,7 +50,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 +57,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..1ec59f955 100644 --- a/protocol/categories.json +++ b/protocol/categories.json @@ -1,6 +1,6 @@ { "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"] @@ -12,7 +12,7 @@ "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"] diff --git a/protocol/features.json b/protocol/features.json new file mode 100644 index 000000000..94910d269 --- /dev/null +++ b/protocol/features.json @@ -0,0 +1,570 @@ +{ + "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", + "0019-MCAL-019", + "0019-MCAL-020", + "0019-MCAL-021", + "0019-MCAL-022", + "0019-MCAL-023", + "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" + ] + }, + "Unknown": { + "milestone": "unknown", + "acs": [] + } +} diff --git a/wordlist.txt b/wordlist.txt index 400440dc7..7ca46810b 100644 --- a/wordlist.txt +++ b/wordlist.txt @@ -1,6 +1,7 @@ ABCI ABCI++ ABI +actioned Allowlist allowlisted antiwhaling @@ -49,9 +50,11 @@ cryptocurrencies cryptographic CQRS cumulated +customisable customised DAI datanode +datapoint datatypes datetime decentralised @@ -68,6 +71,7 @@ disincentivise DLT DPOS durations +earlyExitPenalty ECDSA EEF EIP @@ -137,7 +141,9 @@ keypair keypairs keystore KMS +leftarrow lifecycle +linearisation linearised linkings linux @@ -149,9 +155,11 @@ LP LPs MacOS mainnet +maint malus margined margining +marketID messager math mempool @@ -168,6 +176,8 @@ multisignature NFTs nonfungible OAUTH +OCO +OCOs OKR onwards OpenOracle @@ -189,10 +199,12 @@ pre pro protobuf Pseudocode +pseudorandom pubkey rata reimplemented repo +renormalise reponse repurpose resync @@ -213,6 +225,7 @@ SHA sharded SHA3 siskas +SLA Solana src stablecoin @@ -221,6 +234,7 @@ stakers statebridge stateful statesync +structs SSD SSL suboptimal @@ -236,12 +250,14 @@ Tendermint testnet testnets TIF +TODO tokenholder tokenholders tradable trackable Tron trustless +TWAP tx TXs quant @@ -292,3 +308,14 @@ wBTC whitepaper Yubikey $VEGA +DS +teamUrl +avatarUrl +dApp +leaderboards +ReferralSet +CreateReferralSet +enum +ldots +infty +leq