From 35b87db422b0ef4138101ba73b0f00d16780ba89 Mon Sep 17 00:00:00 2001 From: dydxwill <119354122+dydxwill@users.noreply.github.com> Date: Tue, 12 Dec 2023 13:24:43 -0500 Subject: [PATCH 01/12] bump stargate version (#886) --- v4-proto-js/package-lock.json | 395 ++++++++++++++++++---------------- v4-proto-js/package.json | 2 +- 2 files changed, 213 insertions(+), 184 deletions(-) diff --git a/v4-proto-js/package-lock.json b/v4-proto-js/package-lock.json index 69d6e8034c..d710b2820f 100644 --- a/v4-proto-js/package-lock.json +++ b/v4-proto-js/package-lock.json @@ -12,7 +12,7 @@ "protobufjs": "^6.11.2" }, "devDependencies": { - "@cosmjs/stargate": "^0.30.1", + "@cosmjs/stargate": "^0.32.1", "@dydxprotocol/node-service-base-dev": "^0.3.2", "@osmonauts/lcd": "^0.6.0", "@osmonauts/telescope": "0.86.0", @@ -1681,36 +1681,36 @@ } }, "node_modules/@cosmjs/amino": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.30.1.tgz", - "integrity": "sha512-yNHnzmvAlkETDYIpeCTdVqgvrdt1qgkOXwuRVi8s27UKI5hfqyE9fJ/fuunXE6ZZPnKkjIecDznmuUOMrMvw4w==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.32.1.tgz", + "integrity": "sha512-5l2xQ2XuAhV/B3kTIMPBcVZ/OQ+9Yyddzw/lIVs4qE5e/oBI0PVNWXw1oyR0wgfGHrMUxgKjsoOOqE2IbXVyCw==", "dev": true, "dependencies": { - "@cosmjs/crypto": "^0.30.1", - "@cosmjs/encoding": "^0.30.1", - "@cosmjs/math": "^0.30.1", - "@cosmjs/utils": "^0.30.1" + "@cosmjs/crypto": "^0.32.1", + "@cosmjs/encoding": "^0.32.1", + "@cosmjs/math": "^0.32.1", + "@cosmjs/utils": "^0.32.1" } }, "node_modules/@cosmjs/crypto": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.30.1.tgz", - "integrity": "sha512-rAljUlake3MSXs9xAm87mu34GfBLN0h/1uPPV6jEwClWjNkAMotzjC0ab9MARy5FFAvYHL3lWb57bhkbt2GtzQ==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.32.1.tgz", + "integrity": "sha512-AsKucEg5o8evU0wXF/lDwX+ZSwCKF4bbc57nFzraHywlp3sNu4dfPPURoMrT0r7kT7wQZAy4Pdnvmm9nnCCm/Q==", "dev": true, "dependencies": { - "@cosmjs/encoding": "^0.30.1", - "@cosmjs/math": "^0.30.1", - "@cosmjs/utils": "^0.30.1", + "@cosmjs/encoding": "^0.32.1", + "@cosmjs/math": "^0.32.1", + "@cosmjs/utils": "^0.32.1", "@noble/hashes": "^1", "bn.js": "^5.2.0", "elliptic": "^6.5.4", - "libsodium-wrappers": "^0.7.6" + "libsodium-wrappers-sumo": "^0.7.11" } }, "node_modules/@cosmjs/encoding": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.30.1.tgz", - "integrity": "sha512-rXmrTbgqwihORwJ3xYhIgQFfMSrwLu1s43RIK9I8EBudPx3KmnmyAKzMOVsRDo9edLFNuZ9GIvysUCwQfq3WlQ==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.32.1.tgz", + "integrity": "sha512-x60Lfds+Eq42rVV29NaoIAson3kBhATBI3zPp7X3GJTryBc5HFHQ6L/976tE1WB2DrvkfUdWS3ayCMVOY/qm1g==", "dev": true, "dependencies": { "base64-js": "^1.3.0", @@ -1719,102 +1719,99 @@ } }, "node_modules/@cosmjs/json-rpc": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.30.1.tgz", - "integrity": "sha512-pitfC/2YN9t+kXZCbNuyrZ6M8abnCC2n62m+JtU9vQUfaEtVsgy+1Fk4TRQ175+pIWSdBMFi2wT8FWVEE4RhxQ==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.32.1.tgz", + "integrity": "sha512-Hsj3Sg+m/JF8qfISp/G4TXQ0FAO01mzDKtNcgKufIHCrvJNDiE69xGyGgSm/qKwsXLBmzRTSxHWK0+yZef3LNQ==", "dev": true, "dependencies": { - "@cosmjs/stream": "^0.30.1", + "@cosmjs/stream": "^0.32.1", "xstream": "^11.14.0" } }, "node_modules/@cosmjs/math": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.30.1.tgz", - "integrity": "sha512-yaoeI23pin9ZiPHIisa6qqLngfnBR/25tSaWpkTm8Cy10MX70UF5oN4+/t1heLaM6SSmRrhk3psRkV4+7mH51Q==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.32.1.tgz", + "integrity": "sha512-sqJgDjPh49rxe06apzwKYLxAw4LLFKmEd4yQtHqH16BxVVUrvK5UH9TEBpUrRErdjqENowekecDCDBZspGXHNA==", "dev": true, "dependencies": { "bn.js": "^5.2.0" } }, "node_modules/@cosmjs/proto-signing": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.30.1.tgz", - "integrity": "sha512-tXh8pPYXV4aiJVhTKHGyeZekjj+K9s2KKojMB93Gcob2DxUjfKapFYBMJSgfKPuWUPEmyr8Q9km2hplI38ILgQ==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.32.1.tgz", + "integrity": "sha512-IHJMXQ8XnfzR5K1hWb8VV/jEfJof6BL2mgGIA7X4hSPegwoVfb9hnFKPEPgFjGCTTvGZ8SfnCdXxpsOjianVIA==", "dev": true, "dependencies": { - "@cosmjs/amino": "^0.30.1", - "@cosmjs/crypto": "^0.30.1", - "@cosmjs/encoding": "^0.30.1", - "@cosmjs/math": "^0.30.1", - "@cosmjs/utils": "^0.30.1", - "cosmjs-types": "^0.7.1", - "long": "^4.0.0" + "@cosmjs/amino": "^0.32.1", + "@cosmjs/crypto": "^0.32.1", + "@cosmjs/encoding": "^0.32.1", + "@cosmjs/math": "^0.32.1", + "@cosmjs/utils": "^0.32.1", + "cosmjs-types": "^0.9.0" } }, "node_modules/@cosmjs/socket": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.30.1.tgz", - "integrity": "sha512-r6MpDL+9N+qOS/D5VaxnPaMJ3flwQ36G+vPvYJsXArj93BjgyFB7BwWwXCQDzZ+23cfChPUfhbINOenr8N2Kow==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.32.1.tgz", + "integrity": "sha512-thPCLCmnCuZvrsDW4YmsADI/MliOXWuMnflbzX+3OhoTuEav2I4/1aOXY0jdy0bbqL0l1opx+JfmwdWptMgKzg==", "dev": true, "dependencies": { - "@cosmjs/stream": "^0.30.1", + "@cosmjs/stream": "^0.32.1", "isomorphic-ws": "^4.0.1", "ws": "^7", "xstream": "^11.14.0" } }, "node_modules/@cosmjs/stargate": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.30.1.tgz", - "integrity": "sha512-RdbYKZCGOH8gWebO7r6WvNnQMxHrNXInY/gPHPzMjbQF6UatA6fNM2G2tdgS5j5u7FTqlCI10stNXrknaNdzog==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.32.1.tgz", + "integrity": "sha512-S0E1qKQ2CMJU79G8bQTquTyrbU03gFsvCkbo3RvK8v2OltVCByjFNh+0nGN5do+uDOzwwmDvnNLhR+SaIyNQoQ==", "dev": true, "dependencies": { "@confio/ics23": "^0.6.8", - "@cosmjs/amino": "^0.30.1", - "@cosmjs/encoding": "^0.30.1", - "@cosmjs/math": "^0.30.1", - "@cosmjs/proto-signing": "^0.30.1", - "@cosmjs/stream": "^0.30.1", - "@cosmjs/tendermint-rpc": "^0.30.1", - "@cosmjs/utils": "^0.30.1", - "cosmjs-types": "^0.7.1", - "long": "^4.0.0", - "protobufjs": "~6.11.3", + "@cosmjs/amino": "^0.32.1", + "@cosmjs/encoding": "^0.32.1", + "@cosmjs/math": "^0.32.1", + "@cosmjs/proto-signing": "^0.32.1", + "@cosmjs/stream": "^0.32.1", + "@cosmjs/tendermint-rpc": "^0.32.1", + "@cosmjs/utils": "^0.32.1", + "cosmjs-types": "^0.9.0", "xstream": "^11.14.0" } }, "node_modules/@cosmjs/stream": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.30.1.tgz", - "integrity": "sha512-Fg0pWz1zXQdoxQZpdHRMGvUH5RqS6tPv+j9Eh7Q953UjMlrwZVo0YFLC8OTf/HKVf10E4i0u6aM8D69Q6cNkgQ==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.32.1.tgz", + "integrity": "sha512-6RwHaGxWbIG0y++aCYP/doa4ex/Up8Q8G+ehwDzAq3aKl3zbDe9L0FmycclnMuwPm/baPIkEZ6+IVmJoNLX79Q==", "dev": true, "dependencies": { "xstream": "^11.14.0" } }, "node_modules/@cosmjs/tendermint-rpc": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.30.1.tgz", - "integrity": "sha512-Z3nCwhXSbPZJ++v85zHObeUggrEHVfm1u18ZRwXxFE9ZMl5mXTybnwYhczuYOl7KRskgwlB+rID0WYACxj4wdQ==", - "dev": true, - "dependencies": { - "@cosmjs/crypto": "^0.30.1", - "@cosmjs/encoding": "^0.30.1", - "@cosmjs/json-rpc": "^0.30.1", - "@cosmjs/math": "^0.30.1", - "@cosmjs/socket": "^0.30.1", - "@cosmjs/stream": "^0.30.1", - "@cosmjs/utils": "^0.30.1", - "axios": "^0.21.2", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.32.1.tgz", + "integrity": "sha512-4uGSxB2JejWhwBUgxca4GqcK/BGnCFMIP7ptwEledrC3AY/shPeIYcPXWEBwO7sfwCta8DhAOCLrc9zhVC+VAQ==", + "dev": true, + "dependencies": { + "@cosmjs/crypto": "^0.32.1", + "@cosmjs/encoding": "^0.32.1", + "@cosmjs/json-rpc": "^0.32.1", + "@cosmjs/math": "^0.32.1", + "@cosmjs/socket": "^0.32.1", + "@cosmjs/stream": "^0.32.1", + "@cosmjs/utils": "^0.32.1", + "axios": "^1.6.0", "readonly-date": "^1.0.0", "xstream": "^11.14.0" } }, "node_modules/@cosmjs/utils": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.30.1.tgz", - "integrity": "sha512-KvvX58MGMWh7xA+N+deCfunkA/ZNDvFLw4YbOmX3f/XBIkqrVY7qlotfy2aNb1kgp6h4B6Yc8YawJPDTfvWX7g==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.32.1.tgz", + "integrity": "sha512-PV9pa0cVPFCNgfQKEOc6RcNFHr5wMQLcDqWoo/ekIoj1AfzAaqnojdnL80u1C9Qf+vOfRGIXubqiU7Tl7QZuig==", "dev": true }, "node_modules/@cosmwasm/ts-codegen": { @@ -5263,12 +5260,28 @@ } }, "node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dev": true, "dependencies": { - "follow-redirects": "^1.14.0" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" } }, "node_modules/axobject-query": { @@ -5795,14 +5808,10 @@ "dev": true }, "node_modules/cosmjs-types": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.7.2.tgz", - "integrity": "sha512-vf2uLyktjr/XVAgEq0DjMxeAWh1yYREe7AMHDKd7EiHVqxBPCaBS+qEEQUkXbR9ndnckqr1sUG8BQhazh4X5lA==", - "dev": true, - "dependencies": { - "long": "^4.0.0", - "protobufjs": "~6.11.2" - } + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.9.0.tgz", + "integrity": "sha512-MN/yUe6mkJwHnCFfsNPeCfXVhyxHYW6c/xDUzrSbBycYzw++XvWDMJArXp2pLdgD6FQ8DW79vkPjeNKVrXaHeQ==", + "dev": true }, "node_modules/coveralls": { "version": "3.1.1", @@ -9055,19 +9064,19 @@ "node": ">= 0.8.0" } }, - "node_modules/libsodium": { - "version": "0.7.11", - "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.11.tgz", - "integrity": "sha512-WPfJ7sS53I2s4iM58QxY3Inb83/6mjlYgcmZs7DJsvDlnmVUwNinBCi5vBT43P6bHRy01O4zsMU2CoVR6xJ40A==", + "node_modules/libsodium-sumo": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/libsodium-sumo/-/libsodium-sumo-0.7.13.tgz", + "integrity": "sha512-zTGdLu4b9zSNLfovImpBCbdAA4xkpkZbMnSQjP8HShyOutnGjRHmSOKlsylh1okao6QhLiz7nG98EGn+04cZjQ==", "dev": true }, - "node_modules/libsodium-wrappers": { - "version": "0.7.11", - "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.11.tgz", - "integrity": "sha512-SrcLtXj7BM19vUKtQuyQKiQCRJPgbpauzl3s0rSwD+60wtHqSUuqcoawlMDheCJga85nKOQwxNYQxf/CKAvs6Q==", + "node_modules/libsodium-wrappers-sumo": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/libsodium-wrappers-sumo/-/libsodium-wrappers-sumo-0.7.13.tgz", + "integrity": "sha512-lz4YdplzDRh6AhnLGF2Dj2IUj94xRN6Bh8T0HLNwzYGwPehQJX6c7iYVrFUPZ3QqxE0bqC+K0IIqqZJYWumwSQ==", "dev": true, "dependencies": { - "libsodium": "^0.7.11" + "libsodium-sumo": "^0.7.13" } }, "node_modules/lines-and-columns": { @@ -9882,6 +9891,12 @@ "pbts": "bin/pbts" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -12957,36 +12972,36 @@ } }, "@cosmjs/amino": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.30.1.tgz", - "integrity": "sha512-yNHnzmvAlkETDYIpeCTdVqgvrdt1qgkOXwuRVi8s27UKI5hfqyE9fJ/fuunXE6ZZPnKkjIecDznmuUOMrMvw4w==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.32.1.tgz", + "integrity": "sha512-5l2xQ2XuAhV/B3kTIMPBcVZ/OQ+9Yyddzw/lIVs4qE5e/oBI0PVNWXw1oyR0wgfGHrMUxgKjsoOOqE2IbXVyCw==", "dev": true, "requires": { - "@cosmjs/crypto": "^0.30.1", - "@cosmjs/encoding": "^0.30.1", - "@cosmjs/math": "^0.30.1", - "@cosmjs/utils": "^0.30.1" + "@cosmjs/crypto": "^0.32.1", + "@cosmjs/encoding": "^0.32.1", + "@cosmjs/math": "^0.32.1", + "@cosmjs/utils": "^0.32.1" } }, "@cosmjs/crypto": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.30.1.tgz", - "integrity": "sha512-rAljUlake3MSXs9xAm87mu34GfBLN0h/1uPPV6jEwClWjNkAMotzjC0ab9MARy5FFAvYHL3lWb57bhkbt2GtzQ==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.32.1.tgz", + "integrity": "sha512-AsKucEg5o8evU0wXF/lDwX+ZSwCKF4bbc57nFzraHywlp3sNu4dfPPURoMrT0r7kT7wQZAy4Pdnvmm9nnCCm/Q==", "dev": true, "requires": { - "@cosmjs/encoding": "^0.30.1", - "@cosmjs/math": "^0.30.1", - "@cosmjs/utils": "^0.30.1", + "@cosmjs/encoding": "^0.32.1", + "@cosmjs/math": "^0.32.1", + "@cosmjs/utils": "^0.32.1", "@noble/hashes": "^1", "bn.js": "^5.2.0", "elliptic": "^6.5.4", - "libsodium-wrappers": "^0.7.6" + "libsodium-wrappers-sumo": "^0.7.11" } }, "@cosmjs/encoding": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.30.1.tgz", - "integrity": "sha512-rXmrTbgqwihORwJ3xYhIgQFfMSrwLu1s43RIK9I8EBudPx3KmnmyAKzMOVsRDo9edLFNuZ9GIvysUCwQfq3WlQ==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.32.1.tgz", + "integrity": "sha512-x60Lfds+Eq42rVV29NaoIAson3kBhATBI3zPp7X3GJTryBc5HFHQ6L/976tE1WB2DrvkfUdWS3ayCMVOY/qm1g==", "dev": true, "requires": { "base64-js": "^1.3.0", @@ -12995,102 +13010,99 @@ } }, "@cosmjs/json-rpc": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.30.1.tgz", - "integrity": "sha512-pitfC/2YN9t+kXZCbNuyrZ6M8abnCC2n62m+JtU9vQUfaEtVsgy+1Fk4TRQ175+pIWSdBMFi2wT8FWVEE4RhxQ==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.32.1.tgz", + "integrity": "sha512-Hsj3Sg+m/JF8qfISp/G4TXQ0FAO01mzDKtNcgKufIHCrvJNDiE69xGyGgSm/qKwsXLBmzRTSxHWK0+yZef3LNQ==", "dev": true, "requires": { - "@cosmjs/stream": "^0.30.1", + "@cosmjs/stream": "^0.32.1", "xstream": "^11.14.0" } }, "@cosmjs/math": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.30.1.tgz", - "integrity": "sha512-yaoeI23pin9ZiPHIisa6qqLngfnBR/25tSaWpkTm8Cy10MX70UF5oN4+/t1heLaM6SSmRrhk3psRkV4+7mH51Q==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.32.1.tgz", + "integrity": "sha512-sqJgDjPh49rxe06apzwKYLxAw4LLFKmEd4yQtHqH16BxVVUrvK5UH9TEBpUrRErdjqENowekecDCDBZspGXHNA==", "dev": true, "requires": { "bn.js": "^5.2.0" } }, "@cosmjs/proto-signing": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.30.1.tgz", - "integrity": "sha512-tXh8pPYXV4aiJVhTKHGyeZekjj+K9s2KKojMB93Gcob2DxUjfKapFYBMJSgfKPuWUPEmyr8Q9km2hplI38ILgQ==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.32.1.tgz", + "integrity": "sha512-IHJMXQ8XnfzR5K1hWb8VV/jEfJof6BL2mgGIA7X4hSPegwoVfb9hnFKPEPgFjGCTTvGZ8SfnCdXxpsOjianVIA==", "dev": true, "requires": { - "@cosmjs/amino": "^0.30.1", - "@cosmjs/crypto": "^0.30.1", - "@cosmjs/encoding": "^0.30.1", - "@cosmjs/math": "^0.30.1", - "@cosmjs/utils": "^0.30.1", - "cosmjs-types": "^0.7.1", - "long": "^4.0.0" + "@cosmjs/amino": "^0.32.1", + "@cosmjs/crypto": "^0.32.1", + "@cosmjs/encoding": "^0.32.1", + "@cosmjs/math": "^0.32.1", + "@cosmjs/utils": "^0.32.1", + "cosmjs-types": "^0.9.0" } }, "@cosmjs/socket": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.30.1.tgz", - "integrity": "sha512-r6MpDL+9N+qOS/D5VaxnPaMJ3flwQ36G+vPvYJsXArj93BjgyFB7BwWwXCQDzZ+23cfChPUfhbINOenr8N2Kow==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.32.1.tgz", + "integrity": "sha512-thPCLCmnCuZvrsDW4YmsADI/MliOXWuMnflbzX+3OhoTuEav2I4/1aOXY0jdy0bbqL0l1opx+JfmwdWptMgKzg==", "dev": true, "requires": { - "@cosmjs/stream": "^0.30.1", + "@cosmjs/stream": "^0.32.1", "isomorphic-ws": "^4.0.1", "ws": "^7", "xstream": "^11.14.0" } }, "@cosmjs/stargate": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.30.1.tgz", - "integrity": "sha512-RdbYKZCGOH8gWebO7r6WvNnQMxHrNXInY/gPHPzMjbQF6UatA6fNM2G2tdgS5j5u7FTqlCI10stNXrknaNdzog==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.32.1.tgz", + "integrity": "sha512-S0E1qKQ2CMJU79G8bQTquTyrbU03gFsvCkbo3RvK8v2OltVCByjFNh+0nGN5do+uDOzwwmDvnNLhR+SaIyNQoQ==", "dev": true, "requires": { "@confio/ics23": "^0.6.8", - "@cosmjs/amino": "^0.30.1", - "@cosmjs/encoding": "^0.30.1", - "@cosmjs/math": "^0.30.1", - "@cosmjs/proto-signing": "^0.30.1", - "@cosmjs/stream": "^0.30.1", - "@cosmjs/tendermint-rpc": "^0.30.1", - "@cosmjs/utils": "^0.30.1", - "cosmjs-types": "^0.7.1", - "long": "^4.0.0", - "protobufjs": "~6.11.3", + "@cosmjs/amino": "^0.32.1", + "@cosmjs/encoding": "^0.32.1", + "@cosmjs/math": "^0.32.1", + "@cosmjs/proto-signing": "^0.32.1", + "@cosmjs/stream": "^0.32.1", + "@cosmjs/tendermint-rpc": "^0.32.1", + "@cosmjs/utils": "^0.32.1", + "cosmjs-types": "^0.9.0", "xstream": "^11.14.0" } }, "@cosmjs/stream": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.30.1.tgz", - "integrity": "sha512-Fg0pWz1zXQdoxQZpdHRMGvUH5RqS6tPv+j9Eh7Q953UjMlrwZVo0YFLC8OTf/HKVf10E4i0u6aM8D69Q6cNkgQ==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.32.1.tgz", + "integrity": "sha512-6RwHaGxWbIG0y++aCYP/doa4ex/Up8Q8G+ehwDzAq3aKl3zbDe9L0FmycclnMuwPm/baPIkEZ6+IVmJoNLX79Q==", "dev": true, "requires": { "xstream": "^11.14.0" } }, "@cosmjs/tendermint-rpc": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.30.1.tgz", - "integrity": "sha512-Z3nCwhXSbPZJ++v85zHObeUggrEHVfm1u18ZRwXxFE9ZMl5mXTybnwYhczuYOl7KRskgwlB+rID0WYACxj4wdQ==", - "dev": true, - "requires": { - "@cosmjs/crypto": "^0.30.1", - "@cosmjs/encoding": "^0.30.1", - "@cosmjs/json-rpc": "^0.30.1", - "@cosmjs/math": "^0.30.1", - "@cosmjs/socket": "^0.30.1", - "@cosmjs/stream": "^0.30.1", - "@cosmjs/utils": "^0.30.1", - "axios": "^0.21.2", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.32.1.tgz", + "integrity": "sha512-4uGSxB2JejWhwBUgxca4GqcK/BGnCFMIP7ptwEledrC3AY/shPeIYcPXWEBwO7sfwCta8DhAOCLrc9zhVC+VAQ==", + "dev": true, + "requires": { + "@cosmjs/crypto": "^0.32.1", + "@cosmjs/encoding": "^0.32.1", + "@cosmjs/json-rpc": "^0.32.1", + "@cosmjs/math": "^0.32.1", + "@cosmjs/socket": "^0.32.1", + "@cosmjs/stream": "^0.32.1", + "@cosmjs/utils": "^0.32.1", + "axios": "^1.6.0", "readonly-date": "^1.0.0", "xstream": "^11.14.0" } }, "@cosmjs/utils": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.30.1.tgz", - "integrity": "sha512-KvvX58MGMWh7xA+N+deCfunkA/ZNDvFLw4YbOmX3f/XBIkqrVY7qlotfy2aNb1kgp6h4B6Yc8YawJPDTfvWX7g==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.32.1.tgz", + "integrity": "sha512-PV9pa0cVPFCNgfQKEOc6RcNFHr5wMQLcDqWoo/ekIoj1AfzAaqnojdnL80u1C9Qf+vOfRGIXubqiU7Tl7QZuig==", "dev": true }, "@cosmwasm/ts-codegen": { @@ -15823,12 +15835,27 @@ "peer": true }, "axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", "dev": true, "requires": { - "follow-redirects": "^1.14.0" + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } } }, "axobject-query": { @@ -16225,14 +16252,10 @@ "dev": true }, "cosmjs-types": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.7.2.tgz", - "integrity": "sha512-vf2uLyktjr/XVAgEq0DjMxeAWh1yYREe7AMHDKd7EiHVqxBPCaBS+qEEQUkXbR9ndnckqr1sUG8BQhazh4X5lA==", - "dev": true, - "requires": { - "long": "^4.0.0", - "protobufjs": "~6.11.2" - } + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.9.0.tgz", + "integrity": "sha512-MN/yUe6mkJwHnCFfsNPeCfXVhyxHYW6c/xDUzrSbBycYzw++XvWDMJArXp2pLdgD6FQ8DW79vkPjeNKVrXaHeQ==", + "dev": true }, "coveralls": { "version": "3.1.1", @@ -18722,19 +18745,19 @@ "type-check": "~0.4.0" } }, - "libsodium": { - "version": "0.7.11", - "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.11.tgz", - "integrity": "sha512-WPfJ7sS53I2s4iM58QxY3Inb83/6mjlYgcmZs7DJsvDlnmVUwNinBCi5vBT43P6bHRy01O4zsMU2CoVR6xJ40A==", + "libsodium-sumo": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/libsodium-sumo/-/libsodium-sumo-0.7.13.tgz", + "integrity": "sha512-zTGdLu4b9zSNLfovImpBCbdAA4xkpkZbMnSQjP8HShyOutnGjRHmSOKlsylh1okao6QhLiz7nG98EGn+04cZjQ==", "dev": true }, - "libsodium-wrappers": { - "version": "0.7.11", - "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.11.tgz", - "integrity": "sha512-SrcLtXj7BM19vUKtQuyQKiQCRJPgbpauzl3s0rSwD+60wtHqSUuqcoawlMDheCJga85nKOQwxNYQxf/CKAvs6Q==", + "libsodium-wrappers-sumo": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/libsodium-wrappers-sumo/-/libsodium-wrappers-sumo-0.7.13.tgz", + "integrity": "sha512-lz4YdplzDRh6AhnLGF2Dj2IUj94xRN6Bh8T0HLNwzYGwPehQJX6c7iYVrFUPZ3QqxE0bqC+K0IIqqZJYWumwSQ==", "dev": true, "requires": { - "libsodium": "^0.7.11" + "libsodium-sumo": "^0.7.13" } }, "lines-and-columns": { @@ -19368,6 +19391,12 @@ "long": "^4.0.0" } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, "psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", diff --git a/v4-proto-js/package.json b/v4-proto-js/package.json index 7816006aac..6b91fadccc 100644 --- a/v4-proto-js/package.json +++ b/v4-proto-js/package.json @@ -35,7 +35,7 @@ "url": "https://github.com/dydxprotocol/v4-chain/issues" }, "devDependencies": { - "@cosmjs/stargate": "^0.30.1", + "@cosmjs/stargate": "^0.32.1", "@dydxprotocol/node-service-base-dev": "^0.3.2", "@osmonauts/lcd": "^0.6.0", "@osmonauts/telescope": "0.86.0", From bffa5b61966855d041bc5952db297c7138d95b31 Mon Sep 17 00:00:00 2001 From: jayy04 <103467857+jayy04@users.noreply.github.com> Date: Tue, 12 Dec 2023 14:14:34 -0500 Subject: [PATCH 02/12] [CLOB-1045] proto updates for daemon liquidation request (#885) * [CLOB-1045] proto updates for daemon liquidation request * fix proto format * switch from map to slice --- .../daemons/liquidation/liquidation.ts | 61 ++++- .../daemons/liquidation/liquidation.proto | 15 +- .../daemons/liquidation/api/liquidation.pb.go | 234 +++++++++++++++--- .../daemons/liquidation/client/client_test.go | 6 +- .../daemons/liquidation/client/grpc_helper.go | 2 +- .../liquidation/client/grpc_helper_test.go | 6 +- protocol/daemons/server/liquidation.go | 4 +- protocol/daemons/server/liquidation_test.go | 4 +- protocol/x/clob/abci_test.go | 2 +- .../clob/e2e/liquidation_deleveraging_test.go | 4 +- 10 files changed, 285 insertions(+), 53 deletions(-) diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/daemons/liquidation/liquidation.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/daemons/liquidation/liquidation.ts index 6f2b41c449..3a40c90879 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/daemons/liquidation/liquidation.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/daemons/liquidation/liquidation.ts @@ -1,4 +1,5 @@ import { SubaccountId, SubaccountIdSDKType } from "../../subaccounts/subaccount"; +import { SubaccountOpenPositionInfo, SubaccountOpenPositionInfoSDKType } from "../../clob/liquidations"; import * as _m0 from "protobufjs/minimal"; import { DeepPartial } from "../../../helpers"; /** @@ -9,7 +10,15 @@ import { DeepPartial } from "../../../helpers"; */ export interface LiquidateSubaccountsRequest { - subaccountIds: SubaccountId[]; + /** The block height at which the liquidation daemon is processing. */ + blockHeight: number; + /** The list of liquidatable subaccount ids. */ + + liquidatableSubaccountIds: SubaccountId[]; + /** The list of subaccount ids with negative total net collateral. */ + + negativeTncSubaccountIds: SubaccountId[]; + subaccountOpenPositionInfo: SubaccountOpenPositionInfo[]; } /** * LiquidateSubaccountsRequest is a request message that contains a list of @@ -19,7 +28,15 @@ export interface LiquidateSubaccountsRequest { */ export interface LiquidateSubaccountsRequestSDKType { - subaccount_ids: SubaccountIdSDKType[]; + /** The block height at which the liquidation daemon is processing. */ + block_height: number; + /** The list of liquidatable subaccount ids. */ + + liquidatable_subaccount_ids: SubaccountIdSDKType[]; + /** The list of subaccount ids with negative total net collateral. */ + + negative_tnc_subaccount_ids: SubaccountIdSDKType[]; + subaccount_open_position_info: SubaccountOpenPositionInfoSDKType[]; } /** * LiquidateSubaccountsResponse is a response message for @@ -36,14 +53,29 @@ export interface LiquidateSubaccountsResponseSDKType {} function createBaseLiquidateSubaccountsRequest(): LiquidateSubaccountsRequest { return { - subaccountIds: [] + blockHeight: 0, + liquidatableSubaccountIds: [], + negativeTncSubaccountIds: [], + subaccountOpenPositionInfo: [] }; } export const LiquidateSubaccountsRequest = { encode(message: LiquidateSubaccountsRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - for (const v of message.subaccountIds) { - SubaccountId.encode(v!, writer.uint32(10).fork()).ldelim(); + if (message.blockHeight !== 0) { + writer.uint32(8).uint32(message.blockHeight); + } + + for (const v of message.liquidatableSubaccountIds) { + SubaccountId.encode(v!, writer.uint32(18).fork()).ldelim(); + } + + for (const v of message.negativeTncSubaccountIds) { + SubaccountId.encode(v!, writer.uint32(26).fork()).ldelim(); + } + + for (const v of message.subaccountOpenPositionInfo) { + SubaccountOpenPositionInfo.encode(v!, writer.uint32(34).fork()).ldelim(); } return writer; @@ -59,7 +91,19 @@ export const LiquidateSubaccountsRequest = { switch (tag >>> 3) { case 1: - message.subaccountIds.push(SubaccountId.decode(reader, reader.uint32())); + message.blockHeight = reader.uint32(); + break; + + case 2: + message.liquidatableSubaccountIds.push(SubaccountId.decode(reader, reader.uint32())); + break; + + case 3: + message.negativeTncSubaccountIds.push(SubaccountId.decode(reader, reader.uint32())); + break; + + case 4: + message.subaccountOpenPositionInfo.push(SubaccountOpenPositionInfo.decode(reader, reader.uint32())); break; default: @@ -73,7 +117,10 @@ export const LiquidateSubaccountsRequest = { fromPartial(object: DeepPartial): LiquidateSubaccountsRequest { const message = createBaseLiquidateSubaccountsRequest(); - message.subaccountIds = object.subaccountIds?.map(e => SubaccountId.fromPartial(e)) || []; + message.blockHeight = object.blockHeight ?? 0; + message.liquidatableSubaccountIds = object.liquidatableSubaccountIds?.map(e => SubaccountId.fromPartial(e)) || []; + message.negativeTncSubaccountIds = object.negativeTncSubaccountIds?.map(e => SubaccountId.fromPartial(e)) || []; + message.subaccountOpenPositionInfo = object.subaccountOpenPositionInfo?.map(e => SubaccountOpenPositionInfo.fromPartial(e)) || []; return message; } diff --git a/proto/dydxprotocol/daemons/liquidation/liquidation.proto b/proto/dydxprotocol/daemons/liquidation/liquidation.proto index 5f57786101..b48584fadd 100644 --- a/proto/dydxprotocol/daemons/liquidation/liquidation.proto +++ b/proto/dydxprotocol/daemons/liquidation/liquidation.proto @@ -3,6 +3,7 @@ package dydxprotocol.daemons.liquidation; import "gogoproto/gogo.proto"; import "dydxprotocol/subaccounts/subaccount.proto"; +import "dydxprotocol/clob/liquidations.proto"; option go_package = "github.com/dydxprotocol/v4-chain/protocol/daemons/liquidation/api"; @@ -18,8 +19,20 @@ service LiquidationService { // ids should not contain duplicates. The application should re-verify these // subaccount ids against current state before liquidating their positions. message LiquidateSubaccountsRequest { - repeated dydxprotocol.subaccounts.SubaccountId subaccount_ids = 1 + // The block height at which the liquidation daemon is processing. + uint32 block_height = 1; + + // The list of liquidatable subaccount ids. + repeated dydxprotocol.subaccounts.SubaccountId liquidatable_subaccount_ids = 2 + [ (gogoproto.nullable) = false ]; + + // The list of subaccount ids with negative total net collateral. + repeated dydxprotocol.subaccounts.SubaccountId negative_tnc_subaccount_ids = 3 [ (gogoproto.nullable) = false ]; + + // A map of perpetual id to subaccount open position info. + repeated dydxprotocol.clob.SubaccountOpenPositionInfo + subaccount_open_position_info = 4 [ (gogoproto.nullable) = false ]; } // LiquidateSubaccountsResponse is a response message for diff --git a/protocol/daemons/liquidation/api/liquidation.pb.go b/protocol/daemons/liquidation/api/liquidation.pb.go index 3ea453af57..5eafd7a644 100644 --- a/protocol/daemons/liquidation/api/liquidation.pb.go +++ b/protocol/daemons/liquidation/api/liquidation.pb.go @@ -9,6 +9,7 @@ import ( _ "github.com/cosmos/gogoproto/gogoproto" grpc1 "github.com/cosmos/gogoproto/grpc" proto "github.com/cosmos/gogoproto/proto" + types1 "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" types "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" @@ -34,7 +35,14 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // ids should not contain duplicates. The application should re-verify these // subaccount ids against current state before liquidating their positions. type LiquidateSubaccountsRequest struct { - SubaccountIds []types.SubaccountId `protobuf:"bytes,1,rep,name=subaccount_ids,json=subaccountIds,proto3" json:"subaccount_ids"` + // The block height at which the liquidation daemon is processing. + BlockHeight uint32 `protobuf:"varint,1,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"` + // The list of liquidatable subaccount ids. + LiquidatableSubaccountIds []types.SubaccountId `protobuf:"bytes,2,rep,name=liquidatable_subaccount_ids,json=liquidatableSubaccountIds,proto3" json:"liquidatable_subaccount_ids"` + // The list of subaccount ids with negative total net collateral. + NegativeTncSubaccountIds []types.SubaccountId `protobuf:"bytes,3,rep,name=negative_tnc_subaccount_ids,json=negativeTncSubaccountIds,proto3" json:"negative_tnc_subaccount_ids"` + // A map of perpetual id to subaccount open position info. + SubaccountOpenPositionInfo []types1.SubaccountOpenPositionInfo `protobuf:"bytes,4,rep,name=subaccount_open_position_info,json=subaccountOpenPositionInfo,proto3" json:"subaccount_open_position_info"` } func (m *LiquidateSubaccountsRequest) Reset() { *m = LiquidateSubaccountsRequest{} } @@ -70,9 +78,30 @@ func (m *LiquidateSubaccountsRequest) XXX_DiscardUnknown() { var xxx_messageInfo_LiquidateSubaccountsRequest proto.InternalMessageInfo -func (m *LiquidateSubaccountsRequest) GetSubaccountIds() []types.SubaccountId { +func (m *LiquidateSubaccountsRequest) GetBlockHeight() uint32 { if m != nil { - return m.SubaccountIds + return m.BlockHeight + } + return 0 +} + +func (m *LiquidateSubaccountsRequest) GetLiquidatableSubaccountIds() []types.SubaccountId { + if m != nil { + return m.LiquidatableSubaccountIds + } + return nil +} + +func (m *LiquidateSubaccountsRequest) GetNegativeTncSubaccountIds() []types.SubaccountId { + if m != nil { + return m.NegativeTncSubaccountIds + } + return nil +} + +func (m *LiquidateSubaccountsRequest) GetSubaccountOpenPositionInfo() []types1.SubaccountOpenPositionInfo { + if m != nil { + return m.SubaccountOpenPositionInfo } return nil } @@ -125,25 +154,33 @@ func init() { } var fileDescriptor_66068592911cfa5a = []byte{ - // 278 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x4a, 0xa9, 0x4c, 0xa9, - 0x28, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0xce, 0xcf, 0xd1, 0x4f, 0x49, 0x4c, 0xcd, 0xcd, 0xcf, 0x2b, - 0xd6, 0xcf, 0xc9, 0x2c, 0x2c, 0xcd, 0x4c, 0x49, 0x2c, 0xc9, 0xcc, 0xcf, 0x43, 0x66, 0xeb, 0x81, - 0x15, 0x0a, 0x29, 0x20, 0xeb, 0xd1, 0x83, 0xea, 0xd1, 0x43, 0x52, 0x27, 0x25, 0x92, 0x9e, 0x9f, - 0x9e, 0x0f, 0x56, 0xa1, 0x0f, 0x62, 0x41, 0xf4, 0x49, 0x69, 0xa2, 0xd8, 0x55, 0x5c, 0x9a, 0x94, - 0x98, 0x9c, 0x9c, 0x5f, 0x9a, 0x57, 0x52, 0x8c, 0xc4, 0x86, 0x28, 0x55, 0x2a, 0xe2, 0x92, 0xf6, - 0x81, 0x9a, 0x97, 0x1a, 0x8c, 0x50, 0x18, 0x94, 0x5a, 0x58, 0x9a, 0x5a, 0x5c, 0x22, 0x14, 0xcc, - 0xc5, 0x87, 0xd0, 0x12, 0x9f, 0x99, 0x52, 0x2c, 0xc1, 0xa8, 0xc0, 0xac, 0xc1, 0x6d, 0xa4, 0xa6, - 0x87, 0xe2, 0x34, 0x24, 0x2b, 0xf4, 0x10, 0xa6, 0x78, 0xa6, 0x38, 0xb1, 0x9c, 0xb8, 0x27, 0xcf, - 0x10, 0xc4, 0x5b, 0x8c, 0x24, 0x56, 0xac, 0x24, 0xc7, 0x25, 0x83, 0xdd, 0xce, 0xe2, 0x82, 0xfc, - 0xbc, 0xe2, 0x54, 0xa3, 0x35, 0x8c, 0x5c, 0x42, 0x3e, 0x08, 0x4f, 0x06, 0xa7, 0x16, 0x95, 0x65, - 0x26, 0xa7, 0x0a, 0x4d, 0x65, 0xe4, 0x12, 0xc1, 0xa6, 0x4f, 0xc8, 0x56, 0x8f, 0x50, 0x38, 0xe9, - 0xe1, 0xf1, 0xa3, 0x94, 0x1d, 0xb9, 0xda, 0x21, 0xce, 0x75, 0x8a, 0x3e, 0xf1, 0x48, 0x8e, 0xf1, - 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0, 0x58, 0x8e, - 0xe1, 0xc6, 0x63, 0x39, 0x86, 0x28, 0xc7, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, - 0x5c, 0x7d, 0x94, 0x28, 0x29, 0x33, 0xd1, 0x4d, 0xce, 0x48, 0xcc, 0xcc, 0xd3, 0xc7, 0x9b, 0x20, - 0x12, 0x0b, 0x32, 0x93, 0xd8, 0xc0, 0x2a, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x1f, 0x71, - 0x5f, 0x4e, 0x3f, 0x02, 0x00, 0x00, + // 404 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x93, 0x41, 0x8b, 0xda, 0x40, + 0x14, 0xc7, 0x93, 0x2a, 0x3d, 0x8c, 0xed, 0x25, 0x78, 0x48, 0x63, 0x9b, 0x5a, 0x29, 0xc5, 0x1e, + 0x9c, 0x80, 0xed, 0xb5, 0x85, 0x7a, 0xaa, 0x20, 0xb4, 0x68, 0x4f, 0xed, 0x21, 0x24, 0x93, 0x31, + 0x19, 0x8c, 0xf3, 0xa2, 0x33, 0x09, 0xdd, 0x6f, 0xb1, 0xb0, 0xec, 0xb7, 0xd8, 0x0f, 0xe2, 0xd1, + 0xe3, 0x9e, 0x96, 0x45, 0xbf, 0xc8, 0x62, 0x8c, 0x64, 0xb2, 0xb8, 0x2e, 0x78, 0x7b, 0xbc, 0xfc, + 0xff, 0xff, 0xdf, 0xcb, 0xcc, 0x1b, 0xd4, 0x0f, 0x2e, 0x82, 0xff, 0xc9, 0x12, 0x24, 0x10, 0x88, + 0x9d, 0xc0, 0xa3, 0x73, 0xe0, 0xc2, 0x89, 0xd9, 0x22, 0x65, 0x81, 0x27, 0x19, 0x70, 0xb5, 0xc6, + 0xb9, 0xd0, 0x68, 0xab, 0x1e, 0x5c, 0x78, 0xb0, 0xa2, 0xb3, 0x9a, 0x21, 0x84, 0x90, 0x2b, 0x9c, + 0x5d, 0xb5, 0xf7, 0x59, 0x9f, 0x2b, 0x2c, 0x91, 0xfa, 0x1e, 0x21, 0x90, 0x72, 0x29, 0x94, 0xba, + 0x90, 0x7e, 0xac, 0x48, 0x49, 0x0c, 0xbe, 0x3a, 0x87, 0xd8, 0xab, 0x3a, 0x57, 0x35, 0xd4, 0x1a, + 0x15, 0x6d, 0x3a, 0x29, 0xf3, 0xc6, 0x74, 0x91, 0x52, 0x21, 0x8d, 0x0f, 0xe8, 0x95, 0x1f, 0x03, + 0x99, 0xb9, 0x11, 0x65, 0x61, 0x24, 0x4d, 0xbd, 0xad, 0x77, 0x5f, 0x8f, 0x1b, 0x79, 0xef, 0x67, + 0xde, 0x32, 0x62, 0xd4, 0x3a, 0x04, 0x7b, 0x7e, 0x4c, 0xdd, 0x72, 0x12, 0x97, 0x05, 0xc2, 0x7c, + 0xd1, 0xae, 0x75, 0x1b, 0xfd, 0x4f, 0xb8, 0xf2, 0xc7, 0xca, 0xe4, 0xb8, 0xa4, 0x0e, 0x83, 0x41, + 0x7d, 0x75, 0xf7, 0x5e, 0x1b, 0xbf, 0x51, 0x03, 0xd5, 0xef, 0xc2, 0x98, 0xa1, 0x16, 0xa7, 0xa1, + 0x27, 0x59, 0x46, 0x5d, 0xc9, 0xc9, 0x63, 0x5a, 0xed, 0x0c, 0x9a, 0x79, 0x08, 0xfc, 0xc3, 0x49, + 0x15, 0x96, 0xa1, 0x77, 0x4a, 0x3e, 0x24, 0x94, 0xbb, 0x09, 0x08, 0xb6, 0x3b, 0x40, 0x97, 0xf1, + 0x29, 0x98, 0xf5, 0x1c, 0xd7, 0xab, 0xe2, 0x76, 0x67, 0xad, 0x70, 0x7e, 0x25, 0x94, 0xff, 0x2e, + 0x5c, 0x43, 0x3e, 0x85, 0x82, 0x6a, 0x89, 0x27, 0x15, 0x1d, 0x1b, 0xbd, 0x3d, 0x7e, 0x29, 0x22, + 0x01, 0x2e, 0x68, 0xff, 0x46, 0x47, 0xc6, 0xa8, 0xbc, 0xcc, 0x09, 0x5d, 0x66, 0x8c, 0x50, 0xe3, + 0x5a, 0x47, 0xcd, 0x63, 0x3e, 0xe3, 0x1b, 0x7e, 0x6e, 0xdf, 0xf0, 0x89, 0x25, 0xb0, 0xbe, 0x9f, + 0x6b, 0xdf, 0x8f, 0x3b, 0xf8, 0xb7, 0xda, 0xd8, 0xfa, 0x7a, 0x63, 0xeb, 0xf7, 0x1b, 0x5b, 0xbf, + 0xdc, 0xda, 0xda, 0x7a, 0x6b, 0x6b, 0xb7, 0x5b, 0x5b, 0xfb, 0xfb, 0x23, 0x64, 0x32, 0x4a, 0x7d, + 0x4c, 0x60, 0xee, 0x54, 0xf6, 0x35, 0xfb, 0xda, 0x23, 0x91, 0xc7, 0xb8, 0x73, 0xf2, 0x61, 0x79, + 0x09, 0xf3, 0x5f, 0xe6, 0x8a, 0x2f, 0x0f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x9f, 0x26, 0xab, 0xa0, + 0x87, 0x03, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -248,10 +285,10 @@ func (m *LiquidateSubaccountsRequest) MarshalToSizedBuffer(dAtA []byte) (int, er _ = i var l int _ = l - if len(m.SubaccountIds) > 0 { - for iNdEx := len(m.SubaccountIds) - 1; iNdEx >= 0; iNdEx-- { + if len(m.SubaccountOpenPositionInfo) > 0 { + for iNdEx := len(m.SubaccountOpenPositionInfo) - 1; iNdEx >= 0; iNdEx-- { { - size, err := m.SubaccountIds[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + size, err := m.SubaccountOpenPositionInfo[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -259,9 +296,42 @@ func (m *LiquidateSubaccountsRequest) MarshalToSizedBuffer(dAtA []byte) (int, er i = encodeVarintLiquidation(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0xa + dAtA[i] = 0x22 } } + if len(m.NegativeTncSubaccountIds) > 0 { + for iNdEx := len(m.NegativeTncSubaccountIds) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.NegativeTncSubaccountIds[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintLiquidation(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.LiquidatableSubaccountIds) > 0 { + for iNdEx := len(m.LiquidatableSubaccountIds) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.LiquidatableSubaccountIds[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintLiquidation(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.BlockHeight != 0 { + i = encodeVarintLiquidation(dAtA, i, uint64(m.BlockHeight)) + i-- + dAtA[i] = 0x8 + } return len(dAtA) - i, nil } @@ -305,8 +375,23 @@ func (m *LiquidateSubaccountsRequest) Size() (n int) { } var l int _ = l - if len(m.SubaccountIds) > 0 { - for _, e := range m.SubaccountIds { + if m.BlockHeight != 0 { + n += 1 + sovLiquidation(uint64(m.BlockHeight)) + } + if len(m.LiquidatableSubaccountIds) > 0 { + for _, e := range m.LiquidatableSubaccountIds { + l = e.Size() + n += 1 + l + sovLiquidation(uint64(l)) + } + } + if len(m.NegativeTncSubaccountIds) > 0 { + for _, e := range m.NegativeTncSubaccountIds { + l = e.Size() + n += 1 + l + sovLiquidation(uint64(l)) + } + } + if len(m.SubaccountOpenPositionInfo) > 0 { + for _, e := range m.SubaccountOpenPositionInfo { l = e.Size() n += 1 + l + sovLiquidation(uint64(l)) } @@ -359,8 +444,95 @@ func (m *LiquidateSubaccountsRequest) Unmarshal(dAtA []byte) error { } switch fieldNum { case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockHeight", wireType) + } + m.BlockHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLiquidation + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlockHeight |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LiquidatableSubaccountIds", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLiquidation + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthLiquidation + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLiquidation + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LiquidatableSubaccountIds = append(m.LiquidatableSubaccountIds, types.SubaccountId{}) + if err := m.LiquidatableSubaccountIds[len(m.LiquidatableSubaccountIds)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NegativeTncSubaccountIds", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLiquidation + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthLiquidation + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLiquidation + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NegativeTncSubaccountIds = append(m.NegativeTncSubaccountIds, types.SubaccountId{}) + if err := m.NegativeTncSubaccountIds[len(m.NegativeTncSubaccountIds)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SubaccountIds", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field SubaccountOpenPositionInfo", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -387,8 +559,8 @@ func (m *LiquidateSubaccountsRequest) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.SubaccountIds = append(m.SubaccountIds, types.SubaccountId{}) - if err := m.SubaccountIds[len(m.SubaccountIds)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.SubaccountOpenPositionInfo = append(m.SubaccountOpenPositionInfo, types1.SubaccountOpenPositionInfo{}) + if err := m.SubaccountOpenPositionInfo[len(m.SubaccountOpenPositionInfo)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex diff --git a/protocol/daemons/liquidation/client/client_test.go b/protocol/daemons/liquidation/client/client_test.go index 6461f12cb0..81b4943558 100644 --- a/protocol/daemons/liquidation/client/client_test.go +++ b/protocol/daemons/liquidation/client/client_test.go @@ -116,7 +116,7 @@ func TestRunLiquidationDaemonTaskLoop(t *testing.T) { mck.On("AreSubaccountsLiquidatable", ctx, req2).Return(response2, nil) req3 := &api.LiquidateSubaccountsRequest{ - SubaccountIds: []satypes.SubaccountId{ + LiquidatableSubaccountIds: []satypes.SubaccountId{ constants.Carl_Num0, }, } @@ -139,7 +139,7 @@ func TestRunLiquidationDaemonTaskLoop(t *testing.T) { } mck.On("SubaccountAll", ctx, req).Return(response, nil) req2 := &api.LiquidateSubaccountsRequest{ - SubaccountIds: []satypes.SubaccountId{}, + LiquidatableSubaccountIds: []satypes.SubaccountId{}, } response2 := &api.LiquidateSubaccountsResponse{} mck.On("LiquidateSubaccounts", ctx, req2).Return(response2, nil) @@ -180,7 +180,7 @@ func TestRunLiquidationDaemonTaskLoop(t *testing.T) { } mck.On("AreSubaccountsLiquidatable", ctx, req2).Return(response2, nil) req3 := &api.LiquidateSubaccountsRequest{ - SubaccountIds: []satypes.SubaccountId{}, + LiquidatableSubaccountIds: []satypes.SubaccountId{}, } response3 := &api.LiquidateSubaccountsResponse{} mck.On("LiquidateSubaccounts", ctx, req3).Return(response3, nil) diff --git a/protocol/daemons/liquidation/client/grpc_helper.go b/protocol/daemons/liquidation/client/grpc_helper.go index 21c7d8c930..27a57b4c3a 100644 --- a/protocol/daemons/liquidation/client/grpc_helper.go +++ b/protocol/daemons/liquidation/client/grpc_helper.go @@ -253,7 +253,7 @@ func (c *Client) SendLiquidatableSubaccountIds( ) request := &api.LiquidateSubaccountsRequest{ - SubaccountIds: subaccountIds, + LiquidatableSubaccountIds: subaccountIds, } if _, err := c.LiquidationServiceClient.LiquidateSubaccounts(ctx, request); err != nil { diff --git a/protocol/daemons/liquidation/client/grpc_helper_test.go b/protocol/daemons/liquidation/client/grpc_helper_test.go index 764be97688..3707c3f45b 100644 --- a/protocol/daemons/liquidation/client/grpc_helper_test.go +++ b/protocol/daemons/liquidation/client/grpc_helper_test.go @@ -566,7 +566,7 @@ func TestSendLiquidatableSubaccountIds(t *testing.T) { "Success": { setupMocks: func(ctx context.Context, mck *mocks.QueryClient, ids []satypes.SubaccountId) { req := &api.LiquidateSubaccountsRequest{ - SubaccountIds: ids, + LiquidatableSubaccountIds: ids, } response := &api.LiquidateSubaccountsResponse{} mck.On("LiquidateSubaccounts", ctx, req).Return(response, nil) @@ -579,7 +579,7 @@ func TestSendLiquidatableSubaccountIds(t *testing.T) { "Success Empty": { setupMocks: func(ctx context.Context, mck *mocks.QueryClient, ids []satypes.SubaccountId) { req := &api.LiquidateSubaccountsRequest{ - SubaccountIds: ids, + LiquidatableSubaccountIds: ids, } response := &api.LiquidateSubaccountsResponse{} mck.On("LiquidateSubaccounts", ctx, req).Return(response, nil) @@ -589,7 +589,7 @@ func TestSendLiquidatableSubaccountIds(t *testing.T) { "Errors are propagated": { setupMocks: func(ctx context.Context, mck *mocks.QueryClient, ids []satypes.SubaccountId) { req := &api.LiquidateSubaccountsRequest{ - SubaccountIds: ids, + LiquidatableSubaccountIds: ids, } mck.On("LiquidateSubaccounts", ctx, req).Return(nil, errors.New("test error")) }, diff --git a/protocol/daemons/server/liquidation.go b/protocol/daemons/server/liquidation.go index 86f1231dcf..753609ccb8 100644 --- a/protocol/daemons/server/liquidation.go +++ b/protocol/daemons/server/liquidation.go @@ -36,13 +36,13 @@ func (s *Server) LiquidateSubaccounts( ) { telemetry.ModuleSetGauge( metrics.LiquidationDaemon, - float32(len(req.SubaccountIds)), + float32(len(req.LiquidatableSubaccountIds)), metrics.LiquidatableSubaccountIds, metrics.Received, metrics.Count, ) - s.daemonLiquidationInfo.UpdateLiquidatableSubaccountIds(req.SubaccountIds) + s.daemonLiquidationInfo.UpdateLiquidatableSubaccountIds(req.LiquidatableSubaccountIds) // Capture valid responses in metrics. s.reportValidResponse(types.LiquidationsDaemonServiceName) diff --git a/protocol/daemons/server/liquidation_test.go b/protocol/daemons/server/liquidation_test.go index ee85eeef47..f6b3186294 100644 --- a/protocol/daemons/server/liquidation_test.go +++ b/protocol/daemons/server/liquidation_test.go @@ -25,7 +25,7 @@ func TestLiquidateSubaccounts_Empty_Update(t *testing.T) { daemonLiquidationInfo, ) _, err := s.LiquidateSubaccounts(grpc.Ctx, &api.LiquidateSubaccountsRequest{ - SubaccountIds: []satypes.SubaccountId{}, + LiquidatableSubaccountIds: []satypes.SubaccountId{}, }) require.NoError(t, err) require.Empty(t, daemonLiquidationInfo.GetLiquidatableSubaccountIds()) @@ -50,7 +50,7 @@ func TestLiquidateSubaccounts_Multiple_Subaccount_Ids(t *testing.T) { constants.Carl_Num0, } _, err := s.LiquidateSubaccounts(grpc.Ctx, &api.LiquidateSubaccountsRequest{ - SubaccountIds: expectedSubaccountIds, + LiquidatableSubaccountIds: expectedSubaccountIds, }) require.NoError(t, err) diff --git a/protocol/x/clob/abci_test.go b/protocol/x/clob/abci_test.go index 07b3626a22..a5df03a616 100644 --- a/protocol/x/clob/abci_test.go +++ b/protocol/x/clob/abci_test.go @@ -1063,7 +1063,7 @@ func TestLiquidateSubaccounts(t *testing.T) { // Update the liquidatable subaccount IDs. _, err := tApp.App.Server.LiquidateSubaccounts(ctx, &api.LiquidateSubaccountsRequest{ - SubaccountIds: tc.liquidatableSubaccounts, + LiquidatableSubaccountIds: tc.liquidatableSubaccounts, }) require.NoError(t, err) diff --git a/protocol/x/clob/e2e/liquidation_deleveraging_test.go b/protocol/x/clob/e2e/liquidation_deleveraging_test.go index d5aa68d78d..abded06f7b 100644 --- a/protocol/x/clob/e2e/liquidation_deleveraging_test.go +++ b/protocol/x/clob/e2e/liquidation_deleveraging_test.go @@ -619,7 +619,7 @@ func TestLiquidationConfig(t *testing.T) { } _, err := tApp.App.Server.LiquidateSubaccounts(ctx, &api.LiquidateSubaccountsRequest{ - SubaccountIds: tc.liquidatableSubaccountIds, + LiquidatableSubaccountIds: tc.liquidatableSubaccountIds, }) require.NoError(t, err) @@ -1123,7 +1123,7 @@ func TestPlacePerpetualLiquidation_Deleveraging(t *testing.T) { } _, err := tApp.App.Server.LiquidateSubaccounts(ctx, &api.LiquidateSubaccountsRequest{ - SubaccountIds: tc.liquidatableSubaccountIds, + LiquidatableSubaccountIds: tc.liquidatableSubaccountIds, }) require.NoError(t, err) From 5e72896719e2f8d2fe6e10fddbde18b363a6bbe3 Mon Sep 17 00:00:00 2001 From: dydxwill <119354122+dydxwill@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:35:26 -0500 Subject: [PATCH 03/12] [IND-519] upgrade Indexer cosmos dependency versions (#888) --- indexer/packages/v4-protos/package.json | 8 +- indexer/pnpm-lock.yaml | 160 ++++++++++++------------ 2 files changed, 82 insertions(+), 86 deletions(-) diff --git a/indexer/packages/v4-protos/package.json b/indexer/packages/v4-protos/package.json index b67cea8e61..9a91a5fb6f 100644 --- a/indexer/packages/v4-protos/package.json +++ b/indexer/packages/v4-protos/package.json @@ -32,10 +32,10 @@ "homepage": "https://github.com/dydxprotocol/indexer#readme", "dependencies": { "@bufbuild/buf": "1.19.0-1", - "@cosmjs/amino": "^0.29.3", - "@cosmjs/proto-signing": "^0.29.3", - "@cosmjs/stargate": "^0.29.3", - "@cosmjs/tendermint-rpc": "^0.29.3", + "@cosmjs/amino": "^0.32.1", + "@cosmjs/proto-signing": "^0.32.1", + "@cosmjs/stargate": "^0.32.1", + "@cosmjs/tendermint-rpc": "^0.32.1", "@osmonauts/lcd": "^0.6.0", "bech32": "^2.0.0", "dotenv-flow": "^3.2.0", diff --git a/indexer/pnpm-lock.yaml b/indexer/pnpm-lock.yaml index 1736e463a7..ea974da4c6 100644 --- a/indexer/pnpm-lock.yaml +++ b/indexer/pnpm-lock.yaml @@ -274,10 +274,10 @@ importers: packages/v4-protos: specifiers: '@bufbuild/buf': 1.19.0-1 - '@cosmjs/amino': ^0.29.3 - '@cosmjs/proto-signing': ^0.29.3 - '@cosmjs/stargate': ^0.29.3 - '@cosmjs/tendermint-rpc': ^0.29.3 + '@cosmjs/amino': ^0.32.1 + '@cosmjs/proto-signing': ^0.32.1 + '@cosmjs/stargate': ^0.32.1 + '@cosmjs/tendermint-rpc': ^0.32.1 '@dydxprotocol-indexer/dev': workspace:^0.0.1 '@osmonauts/lcd': ^0.6.0 '@osmonauts/telescope': 0.80.0 @@ -290,10 +290,10 @@ importers: typescript: ^4.7.4 dependencies: '@bufbuild/buf': 1.19.0-1 - '@cosmjs/amino': 0.29.5 - '@cosmjs/proto-signing': 0.29.5 - '@cosmjs/stargate': 0.29.5 - '@cosmjs/tendermint-rpc': 0.29.5 + '@cosmjs/amino': 0.32.1 + '@cosmjs/proto-signing': 0.32.1 + '@cosmjs/stargate': 0.32.1 + '@cosmjs/tendermint-rpc': 0.32.1 '@osmonauts/lcd': 0.6.0 bech32: 2.0.0 dotenv-flow: 3.2.0 @@ -4674,64 +4674,63 @@ packages: protobufjs: 6.11.3 dev: false - /@cosmjs/amino/0.29.5: - resolution: {integrity: sha512-Qo8jpC0BiziTSUqpkNatBcwtKNhCovUnFul9SlT/74JUCdLYaeG5hxr3q1cssQt++l4LvlcpF+OUXL48XjNjLw==} + /@cosmjs/amino/0.32.1: + resolution: {integrity: sha512-5l2xQ2XuAhV/B3kTIMPBcVZ/OQ+9Yyddzw/lIVs4qE5e/oBI0PVNWXw1oyR0wgfGHrMUxgKjsoOOqE2IbXVyCw==} dependencies: - '@cosmjs/crypto': 0.29.5 - '@cosmjs/encoding': 0.29.5 - '@cosmjs/math': 0.29.5 - '@cosmjs/utils': 0.29.5 + '@cosmjs/crypto': 0.32.1 + '@cosmjs/encoding': 0.32.1 + '@cosmjs/math': 0.32.1 + '@cosmjs/utils': 0.32.1 dev: false - /@cosmjs/crypto/0.29.5: - resolution: {integrity: sha512-2bKkaLGictaNL0UipQCL6C1afaisv6k8Wr/GCLx9FqiyFkh9ZgRHDyetD64ZsjnWV/N/D44s/esI+k6oPREaiQ==} + /@cosmjs/crypto/0.32.1: + resolution: {integrity: sha512-AsKucEg5o8evU0wXF/lDwX+ZSwCKF4bbc57nFzraHywlp3sNu4dfPPURoMrT0r7kT7wQZAy4Pdnvmm9nnCCm/Q==} dependencies: - '@cosmjs/encoding': 0.29.5 - '@cosmjs/math': 0.29.5 - '@cosmjs/utils': 0.29.5 + '@cosmjs/encoding': 0.32.1 + '@cosmjs/math': 0.32.1 + '@cosmjs/utils': 0.32.1 '@noble/hashes': 1.3.0 bn.js: 5.2.1 elliptic: 6.5.4 - libsodium-wrappers: 0.7.11 + libsodium-wrappers-sumo: 0.7.13 dev: false - /@cosmjs/encoding/0.29.5: - resolution: {integrity: sha512-G4rGl/Jg4dMCw5u6PEZHZcoHnUBlukZODHbm/wcL4Uu91fkn5jVo5cXXZcvs4VCkArVGrEj/52eUgTZCmOBGWQ==} + /@cosmjs/encoding/0.32.1: + resolution: {integrity: sha512-x60Lfds+Eq42rVV29NaoIAson3kBhATBI3zPp7X3GJTryBc5HFHQ6L/976tE1WB2DrvkfUdWS3ayCMVOY/qm1g==} dependencies: base64-js: 1.5.1 bech32: 1.1.4 readonly-date: 1.0.0 dev: false - /@cosmjs/json-rpc/0.29.5: - resolution: {integrity: sha512-C78+X06l+r9xwdM1yFWIpGl03LhB9NdM1xvZpQHwgCOl0Ir/WV8pw48y3Ez2awAoUBRfTeejPe4KvrE6NoIi/w==} + /@cosmjs/json-rpc/0.32.1: + resolution: {integrity: sha512-Hsj3Sg+m/JF8qfISp/G4TXQ0FAO01mzDKtNcgKufIHCrvJNDiE69xGyGgSm/qKwsXLBmzRTSxHWK0+yZef3LNQ==} dependencies: - '@cosmjs/stream': 0.29.5 + '@cosmjs/stream': 0.32.1 xstream: 11.14.0 dev: false - /@cosmjs/math/0.29.5: - resolution: {integrity: sha512-2GjKcv+A9f86MAWYLUkjhw1/WpRl2R1BTb3m9qPG7lzMA7ioYff9jY5SPCfafKdxM4TIQGxXQlYGewQL16O68Q==} + /@cosmjs/math/0.32.1: + resolution: {integrity: sha512-sqJgDjPh49rxe06apzwKYLxAw4LLFKmEd4yQtHqH16BxVVUrvK5UH9TEBpUrRErdjqENowekecDCDBZspGXHNA==} dependencies: bn.js: 5.2.1 dev: false - /@cosmjs/proto-signing/0.29.5: - resolution: {integrity: sha512-QRrS7CiKaoETdgIqvi/7JC2qCwCR7lnWaUsTzh/XfRy3McLkEd+cXbKAW3cygykv7IN0VAEIhZd2lyIfT8KwNA==} + /@cosmjs/proto-signing/0.32.1: + resolution: {integrity: sha512-IHJMXQ8XnfzR5K1hWb8VV/jEfJof6BL2mgGIA7X4hSPegwoVfb9hnFKPEPgFjGCTTvGZ8SfnCdXxpsOjianVIA==} dependencies: - '@cosmjs/amino': 0.29.5 - '@cosmjs/crypto': 0.29.5 - '@cosmjs/encoding': 0.29.5 - '@cosmjs/math': 0.29.5 - '@cosmjs/utils': 0.29.5 - cosmjs-types: 0.5.2 - long: 4.0.0 + '@cosmjs/amino': 0.32.1 + '@cosmjs/crypto': 0.32.1 + '@cosmjs/encoding': 0.32.1 + '@cosmjs/math': 0.32.1 + '@cosmjs/utils': 0.32.1 + cosmjs-types: 0.9.0 dev: false - /@cosmjs/socket/0.29.5: - resolution: {integrity: sha512-5VYDupIWbIXq3ftPV1LkS5Ya/T7Ol/AzWVhNxZ79hPe/mBfv1bGau/LqIYOm2zxGlgm9hBHOTmWGqNYDwr9LNQ==} + /@cosmjs/socket/0.32.1: + resolution: {integrity: sha512-thPCLCmnCuZvrsDW4YmsADI/MliOXWuMnflbzX+3OhoTuEav2I4/1aOXY0jdy0bbqL0l1opx+JfmwdWptMgKzg==} dependencies: - '@cosmjs/stream': 0.29.5 + '@cosmjs/stream': 0.32.1 isomorphic-ws: 4.0.1_ws@7.5.9 ws: 7.5.9 xstream: 11.14.0 @@ -4740,20 +4739,18 @@ packages: - utf-8-validate dev: false - /@cosmjs/stargate/0.29.5: - resolution: {integrity: sha512-hjEv8UUlJruLrYGJcUZXM/CziaINOKwfVm2BoSdUnNTMxGvY/jC1ABHKeZUYt9oXHxEJ1n9+pDqzbKc8pT0nBw==} + /@cosmjs/stargate/0.32.1: + resolution: {integrity: sha512-S0E1qKQ2CMJU79G8bQTquTyrbU03gFsvCkbo3RvK8v2OltVCByjFNh+0nGN5do+uDOzwwmDvnNLhR+SaIyNQoQ==} dependencies: '@confio/ics23': 0.6.8 - '@cosmjs/amino': 0.29.5 - '@cosmjs/encoding': 0.29.5 - '@cosmjs/math': 0.29.5 - '@cosmjs/proto-signing': 0.29.5 - '@cosmjs/stream': 0.29.5 - '@cosmjs/tendermint-rpc': 0.29.5 - '@cosmjs/utils': 0.29.5 - cosmjs-types: 0.5.2 - long: 4.0.0 - protobufjs: 6.11.3 + '@cosmjs/amino': 0.32.1 + '@cosmjs/encoding': 0.32.1 + '@cosmjs/math': 0.32.1 + '@cosmjs/proto-signing': 0.32.1 + '@cosmjs/stream': 0.32.1 + '@cosmjs/tendermint-rpc': 0.32.1 + '@cosmjs/utils': 0.32.1 + cosmjs-types: 0.9.0 xstream: 11.14.0 transitivePeerDependencies: - bufferutil @@ -4761,23 +4758,23 @@ packages: - utf-8-validate dev: false - /@cosmjs/stream/0.29.5: - resolution: {integrity: sha512-TToTDWyH1p05GBtF0Y8jFw2C+4783ueDCmDyxOMM6EU82IqpmIbfwcdMOCAm0JhnyMh+ocdebbFvnX/sGKzRAA==} + /@cosmjs/stream/0.32.1: + resolution: {integrity: sha512-6RwHaGxWbIG0y++aCYP/doa4ex/Up8Q8G+ehwDzAq3aKl3zbDe9L0FmycclnMuwPm/baPIkEZ6+IVmJoNLX79Q==} dependencies: xstream: 11.14.0 dev: false - /@cosmjs/tendermint-rpc/0.29.5: - resolution: {integrity: sha512-ar80twieuAxsy0x2za/aO3kBr2DFPAXDmk2ikDbmkda+qqfXgl35l9CVAAjKRqd9d+cRvbQyb5M4wy6XQpEV6w==} + /@cosmjs/tendermint-rpc/0.32.1: + resolution: {integrity: sha512-4uGSxB2JejWhwBUgxca4GqcK/BGnCFMIP7ptwEledrC3AY/shPeIYcPXWEBwO7sfwCta8DhAOCLrc9zhVC+VAQ==} dependencies: - '@cosmjs/crypto': 0.29.5 - '@cosmjs/encoding': 0.29.5 - '@cosmjs/json-rpc': 0.29.5 - '@cosmjs/math': 0.29.5 - '@cosmjs/socket': 0.29.5 - '@cosmjs/stream': 0.29.5 - '@cosmjs/utils': 0.29.5 - axios: 0.21.4 + '@cosmjs/crypto': 0.32.1 + '@cosmjs/encoding': 0.32.1 + '@cosmjs/json-rpc': 0.32.1 + '@cosmjs/math': 0.32.1 + '@cosmjs/socket': 0.32.1 + '@cosmjs/stream': 0.32.1 + '@cosmjs/utils': 0.32.1 + axios: 1.6.2 readonly-date: 1.0.0 xstream: 11.14.0 transitivePeerDependencies: @@ -4786,8 +4783,8 @@ packages: - utf-8-validate dev: false - /@cosmjs/utils/0.29.5: - resolution: {integrity: sha512-m7h+RXDUxOzEOGt4P+3OVPX7PuakZT3GBmaM/Y2u+abN3xZkziykD/NvedYFvvCCdQo714XcGl33bwifS9FZPQ==} + /@cosmjs/utils/0.32.1: + resolution: {integrity: sha512-PV9pa0cVPFCNgfQKEOc6RcNFHr5wMQLcDqWoo/ekIoj1AfzAaqnojdnL80u1C9Qf+vOfRGIXubqiU7Tl7QZuig==} dev: false /@cosmwasm/ts-codegen/0.21.1: @@ -6379,25 +6376,27 @@ packages: engines: {node: '>=12'} dev: true - /axios/0.21.4: - resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==} + /axios/0.27.2: + resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} dependencies: follow-redirects: 1.15.1 + form-data: 4.0.0 transitivePeerDependencies: - debug dev: false - /axios/0.27.2: - resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} + /axios/1.2.1: + resolution: {integrity: sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==} dependencies: follow-redirects: 1.15.1 form-data: 4.0.0 + proxy-from-env: 1.1.0 transitivePeerDependencies: - debug dev: false - /axios/1.2.1: - resolution: {integrity: sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==} + /axios/1.6.2: + resolution: {integrity: sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==} dependencies: follow-redirects: 1.15.1 form-data: 4.0.0 @@ -7060,11 +7059,8 @@ packages: vary: 1.1.2 dev: false - /cosmjs-types/0.5.2: - resolution: {integrity: sha512-zxCtIJj8v3Di7s39uN4LNcN3HIE1z0B9Z0SPE8ZNQR0oSzsuSe1ACgxoFkvhkS7WBasCAFcglS11G2hyfd5tPg==} - dependencies: - long: 4.0.0 - protobufjs: 6.11.3 + /cosmjs-types/0.9.0: + resolution: {integrity: sha512-MN/yUe6mkJwHnCFfsNPeCfXVhyxHYW6c/xDUzrSbBycYzw++XvWDMJArXp2pLdgD6FQ8DW79vkPjeNKVrXaHeQ==} dev: false /coveralls/3.1.1: @@ -10493,14 +10489,14 @@ packages: type-check: 0.4.0 dev: true - /libsodium-wrappers/0.7.11: - resolution: {integrity: sha512-SrcLtXj7BM19vUKtQuyQKiQCRJPgbpauzl3s0rSwD+60wtHqSUuqcoawlMDheCJga85nKOQwxNYQxf/CKAvs6Q==} - dependencies: - libsodium: 0.7.11 + /libsodium-sumo/0.7.13: + resolution: {integrity: sha512-zTGdLu4b9zSNLfovImpBCbdAA4xkpkZbMnSQjP8HShyOutnGjRHmSOKlsylh1okao6QhLiz7nG98EGn+04cZjQ==} dev: false - /libsodium/0.7.11: - resolution: {integrity: sha512-WPfJ7sS53I2s4iM58QxY3Inb83/6mjlYgcmZs7DJsvDlnmVUwNinBCi5vBT43P6bHRy01O4zsMU2CoVR6xJ40A==} + /libsodium-wrappers-sumo/0.7.13: + resolution: {integrity: sha512-lz4YdplzDRh6AhnLGF2Dj2IUj94xRN6Bh8T0HLNwzYGwPehQJX6c7iYVrFUPZ3QqxE0bqC+K0IIqqZJYWumwSQ==} + dependencies: + libsodium-sumo: 0.7.13 dev: false /liftoff/3.1.0: From eba7f8135da59899bee5b816671a92886d5fba4f Mon Sep 17 00:00:00 2001 From: roy-dydx <133032749+roy-dydx@users.noreply.github.com> Date: Thu, 14 Dec 2023 15:45:47 -0500 Subject: [PATCH 04/12] Add upgradable testnet to container test (#887) * Add upgradable testnet to container test * add better comment --- protocol/testing/containertest/Dockerfile | 4 + .../testing/containertest/containertest.sh | 38 +- .../containertest/preupgrade_entrypoint.sh | 36 + .../containertest/preupgrade_genesis.json | 2164 +++++++++++++++++ protocol/testing/containertest/testnet.go | 35 +- 5 files changed, 2271 insertions(+), 6 deletions(-) create mode 100755 protocol/testing/containertest/preupgrade_entrypoint.sh create mode 100644 protocol/testing/containertest/preupgrade_genesis.json diff --git a/protocol/testing/containertest/Dockerfile b/protocol/testing/containertest/Dockerfile index 4e442d839d..a62ab15a82 100644 --- a/protocol/testing/containertest/Dockerfile +++ b/protocol/testing/containertest/Dockerfile @@ -1,10 +1,14 @@ FROM dydxprotocol-base COPY ./testing/containertest/containertest.sh /dydxprotocol/containertest.sh +COPY ./testing/containertest/preupgrade_entrypoint.sh /dydxprotocol/preupgrade_entrypoint.sh +COPY ./testing/containertest/preupgrade_genesis.json /dydxprotocol/preupgrade_genesis.json COPY ./testing/genesis.sh /dydxprotocol/genesis.sh COPY ./daemons/pricefeed/client/constants/testdata /dydxprotocol/exchange_config COPY ./testing/delaymsg_config /dydxprotocol/delaymsg_config +RUN go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@v1.4.0 + RUN /dydxprotocol/containertest.sh COPY ./testing/containertest/config/pricefeed_exchange_config.toml /dydxprotocol/chain/.alice/config/pricefeed_exchange_config.toml diff --git a/protocol/testing/containertest/containertest.sh b/protocol/testing/containertest/containertest.sh index 619eb01f1c..994a1ca40e 100755 --- a/protocol/testing/containertest/containertest.sh +++ b/protocol/testing/containertest/containertest.sh @@ -7,6 +7,7 @@ set -eo pipefail source "./genesis.sh" CHAIN_ID="localdydxprotocol" +PREUPGRADE_VERSION="v2.0.0" # Define mnemonics for all validators. MNEMONICS=( @@ -66,7 +67,7 @@ FAUCET_ACCOUNTS=( # Define dependencies for this script. # `jq` and `dasel` are used to manipulate json and yaml files respectively. install_prerequisites() { - apk add dasel jq + apk add curl dasel jq } # Create all validators for the chain including a full-node. @@ -143,6 +144,39 @@ create_validators() { done } +setup_cosmovisor() { + for i in "${!MONIKERS[@]}"; do + VAL_HOME_DIR="$HOME/chain/.${MONIKERS[$i]}" + export DAEMON_NAME=dydxprotocold + export DAEMON_HOME="$HOME/chain/.${MONIKERS[$i]}" + + cosmovisor init /bin/dydxprotocold + done +} + +download_preupgrade_binary() { + arch="$(apk --print-arch)" + url_arch="" + case "$arch" in + 'x86_64') + url_arch='amd64' + ;; + 'aarch64') + url_arch='arm64' + ;; + *) + echo >&2 "unexpected architecture '$arch'" + exit 1 + ;; + esac + tar_url="https://github.com/dydxprotocol/v4-chain/releases/download/protocol%2F$PREUPGRADE_VERSION/dydxprotocold-$PREUPGRADE_VERSION-linux-$url_arch.tar.gz" + tar_path='/tmp/dydxprotocold/dydxprotocold.tar.gz' + mkdir -p /tmp/dydxprotocold + curl -vL $tar_url -o $tar_path + dydxprotocold_path=$(tar -xvf $tar_path --directory /tmp/dydxprotocold) + cp /tmp/dydxprotocold/$dydxprotocold_path /bin/dydxprotocold_preupgrade +} + # TODO(DEC-1894): remove this function once we migrate off of persistent peers. # Note: DO NOT add more config modifications in this method. Use `cmd/config.go` to configure # the default config values. @@ -158,4 +192,6 @@ edit_config() { } install_prerequisites +setup_cosmovisor +download_preupgrade_binary create_validators diff --git a/protocol/testing/containertest/preupgrade_entrypoint.sh b/protocol/testing/containertest/preupgrade_entrypoint.sh new file mode 100755 index 0000000000..425ff89990 --- /dev/null +++ b/protocol/testing/containertest/preupgrade_entrypoint.sh @@ -0,0 +1,36 @@ +#!/bin/bash +set -eo pipefail + +# This entrypoint configures the chain to start in a preupgrade state before forwarding args to cosmovisor. +# The following need to be done: +# - Set the preupgrade binary (downloaded in containertest.sh) to be the genesis binary +# - Set the binary at the current commit to be the ugprade binary +# - Set the genesis to be the preupgrade genesis. This is generated by genesis generation script at the +# preupgrade commit. It is copied directly from a container. + +if [[ -z "${UPGRADE_TO_VERSION}" ]]; then + echo >&2 "UPGRADE_TO_VERSION must be set" + exit 1 +fi + +MONIKERS=( + "alice" + "bob" + "carl" + "dave" +) + +for i in "${!MONIKERS[@]}"; do + DAEMON_NAME="dydxprotocold" + DAEMON_HOME="$HOME/chain/.${MONIKERS[$i]}" + + rm "$DAEMON_HOME/cosmovisor/genesis/bin/dydxprotocold" + ln -s /bin/dydxprotocold_preupgrade "$DAEMON_HOME/cosmovisor/genesis/bin/dydxprotocold" + mkdir -p "$DAEMON_HOME/cosmovisor/upgrades/$UPGRADE_TO_VERSION/bin/" + ln -s /bin/dydxprotocold "$DAEMON_HOME/cosmovisor/upgrades/$UPGRADE_TO_VERSION/bin/dydxprotocold" + + rm "$DAEMON_HOME/config/genesis.json" + cp "$HOME/preupgrade_genesis.json" "$DAEMON_HOME/config/genesis.json" +done + +cosmovisor run "$@" diff --git a/protocol/testing/containertest/preupgrade_genesis.json b/protocol/testing/containertest/preupgrade_genesis.json new file mode 100644 index 0000000000..14d2ed8007 --- /dev/null +++ b/protocol/testing/containertest/preupgrade_genesis.json @@ -0,0 +1,2164 @@ +{ + "genesis_time": "2023-12-12T17:59:46.05659705Z", + "chain_id": "localdydxprotocol", + "initial_height": "1", + "consensus_params": { + "block": { + "max_bytes": "4194304", + "max_gas": "-1" + }, + "evidence": { + "max_age_num_blocks": "100000", + "max_age_duration": "172800000000000", + "max_bytes": "1048576" + }, + "validator": { + "pub_key_types": [ + "ed25519" + ] + }, + "version": { + "app": "0" + } + }, + "app_hash": "", + "app_state": { + "assets": { + "assets": [ + { + "atomic_resolution": -6, + "denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5", + "denom_exponent": "-6", + "has_market": false, + "id": 0, + "market_id": 0, + "symbol": "USDC" + } + ] + }, + "auth": { + "params": { + "max_memo_characters": "256", + "tx_sig_limit": "7", + "tx_size_cost_per_byte": "10", + "sig_verify_cost_ed25519": "590", + "sig_verify_cost_secp256k1": "1000" + }, + "accounts": [ + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "dydx199tqg4wdlnu4qjlxchpd7seg454937hjrknju4", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "dydx10fx7sy6ywd5senxae9dwytf8jxek3t2gcen2vs", + "pub_key": null, + "account_number": "1", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "dydx1fjg6zp6vv8t9wvy4lps03r5l4g7tkjw9wvmh70", + "pub_key": null, + "account_number": "2", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "dydx1wau5mja7j7zdavtfq9lu7ejef05hm6ffenlcsn", + "pub_key": null, + "account_number": "3", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "dydx1nzuttarf5k2j0nug5yzhr6p74t9avehn9hlh8m", + "pub_key": null, + "account_number": "4", + "sequence": "0" + } + ] + }, + "bank": { + "params": { + "send_enabled": [], + "default_send_enabled": true + }, + "balances": [ + { + "address": "dydx1zlefkpe3g0vvm9a4h0jf9000lmqutlh9jwjnsv", + "coins": [ + { + "denom": "adv4tnt", + "amount": "995000000000000000000000000" + } + ] + }, + { + "address": "dydx199tqg4wdlnu4qjlxchpd7seg454937hjrknju4", + "coins": [ + { + "denom": "adv4tnt", + "amount": "1000000000000000000000000" + }, + { + "denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5", + "amount": "100000000000000000" + } + ] + }, + { + "address": "dydx1fjg6zp6vv8t9wvy4lps03r5l4g7tkjw9wvmh70", + "coins": [ + { + "denom": "adv4tnt", + "amount": "1000000000000000000000000" + }, + { + "denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5", + "amount": "100000000000000000" + } + ] + }, + { + "address": "dydx1v88c3xv9xyv3eetdx0tvcmq7ung3dywp5upwc6", + "coins": [ + { + "denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5", + "amount": "1300000000000000000" + } + ] + }, + { + "address": "dydx1wau5mja7j7zdavtfq9lu7ejef05hm6ffenlcsn", + "coins": [ + { + "denom": "adv4tnt", + "amount": "1000000000000000000000000" + }, + { + "denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5", + "amount": "100000000000000000" + } + ] + }, + { + "address": "dydx10fx7sy6ywd5senxae9dwytf8jxek3t2gcen2vs", + "coins": [ + { + "denom": "adv4tnt", + "amount": "1000000000000000000000000" + }, + { + "denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5", + "amount": "100000000000000000" + } + ] + }, + { + "address": "dydx1nzuttarf5k2j0nug5yzhr6p74t9avehn9hlh8m", + "coins": [ + { + "denom": "adv4tnt", + "amount": "1000000000000000000000000" + }, + { + "denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5", + "amount": "900000000000000000" + } + ] + }, + { + "address": "dydx1ltyc6y4skclzafvpznpt2qjwmfwgsndp458rmp", + "coins": [ + { + "denom": "adv4tnt", + "amount": "10000000000000000000000000" + } + ] + } + ], + "supply": [], + "denom_metadata": [], + "send_enabled": [] + }, + "blocktime": { + "params": { + "durations": [ + "300s", + "1800s" + ] + } + }, + "bridge": { + "acknowledged_event_info": { + "eth_block_height": 4322136, + "next_id": 5 + }, + "event_params": { + "denom": "adv4tnt", + "eth_address": "0xf75012c350e4ad55be2048bd67ce6e03b20de82d", + "eth_chain_id": 11155111 + }, + "propose_params": { + "max_bridges_per_block": 10, + "propose_delay_duration": "60s", + "skip_if_block_delayed_by_duration": "5s", + "skip_rate_ppm": 800000 + }, + "safety_params": { + "delay_blocks": 86400, + "is_disabled": false + } + }, + "capability": { + "index": "1", + "owners": [] + }, + "clob": { + "block_rate_limit_config": { + "max_short_term_order_cancellations_per_n_blocks": [ + { + "limit": 200, + "num_blocks": 1 + } + ], + "max_short_term_orders_per_n_blocks": [ + { + "limit": 200, + "num_blocks": 1 + } + ], + "max_stateful_orders_per_n_blocks": [ + { + "limit": 2, + "num_blocks": 1 + }, + { + "limit": 20, + "num_blocks": 100 + } + ] + }, + "clob_pairs": [ + { + "id": 0, + "perpetual_clob_metadata": { + "perpetual_id": 0 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 100000 + }, + { + "id": 1, + "perpetual_clob_metadata": { + "perpetual_id": 1 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 100000 + }, + { + "id": 2, + "perpetual_clob_metadata": { + "perpetual_id": 2 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 3, + "perpetual_clob_metadata": { + "perpetual_id": 3 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 4, + "perpetual_clob_metadata": { + "perpetual_id": 4 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 5, + "perpetual_clob_metadata": { + "perpetual_id": 5 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 6, + "perpetual_clob_metadata": { + "perpetual_id": 6 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 7, + "perpetual_clob_metadata": { + "perpetual_id": 7 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 8, + "perpetual_clob_metadata": { + "perpetual_id": 8 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 9, + "perpetual_clob_metadata": { + "perpetual_id": 9 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 10, + "perpetual_clob_metadata": { + "perpetual_id": 10 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 11, + "perpetual_clob_metadata": { + "perpetual_id": 11 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 12, + "perpetual_clob_metadata": { + "perpetual_id": 12 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 13, + "perpetual_clob_metadata": { + "perpetual_id": 13 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 14, + "perpetual_clob_metadata": { + "perpetual_id": 14 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 15, + "perpetual_clob_metadata": { + "perpetual_id": 15 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 16, + "perpetual_clob_metadata": { + "perpetual_id": 16 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 17, + "perpetual_clob_metadata": { + "perpetual_id": 17 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 18, + "perpetual_clob_metadata": { + "perpetual_id": 18 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 19, + "perpetual_clob_metadata": { + "perpetual_id": 19 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 20, + "perpetual_clob_metadata": { + "perpetual_id": 20 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 21, + "perpetual_clob_metadata": { + "perpetual_id": 21 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 22, + "perpetual_clob_metadata": { + "perpetual_id": 22 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 23, + "perpetual_clob_metadata": { + "perpetual_id": 23 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 24, + "perpetual_clob_metadata": { + "perpetual_id": 24 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 25, + "perpetual_clob_metadata": { + "perpetual_id": 25 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 26, + "perpetual_clob_metadata": { + "perpetual_id": 26 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 27, + "perpetual_clob_metadata": { + "perpetual_id": 27 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 28, + "perpetual_clob_metadata": { + "perpetual_id": 28 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 29, + "perpetual_clob_metadata": { + "perpetual_id": 29 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 30, + "perpetual_clob_metadata": { + "perpetual_id": 30 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 31, + "perpetual_clob_metadata": { + "perpetual_id": 31 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + }, + { + "id": 32, + "perpetual_clob_metadata": { + "perpetual_id": 32 + }, + "quantum_conversion_exponent": -9, + "status": "STATUS_ACTIVE", + "step_base_quantums": 1000000, + "subticks_per_tick": 1000000 + } + ], + "equity_tier_limit_config": { + "short_term_order_equity_tiers": [ + { + "limit": 0, + "usd_tnc_required": "0" + }, + { + "limit": 1, + "usd_tnc_required": "20000000" + }, + { + "limit": 5, + "usd_tnc_required": "100000000" + }, + { + "limit": 10, + "usd_tnc_required": "1000000000" + }, + { + "limit": 100, + "usd_tnc_required": "10000000000" + }, + { + "limit": 1000, + "usd_tnc_required": "100000000000" + } + ], + "stateful_order_equity_tiers": [ + { + "limit": 0, + "usd_tnc_required": "0" + }, + { + "limit": 1, + "usd_tnc_required": "20000000" + }, + { + "limit": 5, + "usd_tnc_required": "100000000" + }, + { + "limit": 10, + "usd_tnc_required": "1000000000" + }, + { + "limit": 100, + "usd_tnc_required": "10000000000" + }, + { + "limit": 200, + "usd_tnc_required": "100000000000" + } + ] + }, + "liquidations_config": { + "fillable_price_config": { + "bankruptcy_adjustment_ppm": 1000000, + "spread_to_maintenance_margin_ratio_ppm": 1500000 + }, + "max_liquidation_fee_ppm": 15000, + "position_block_limits": { + "max_position_portion_liquidated_ppm": 100000, + "min_position_notional_liquidated": 1000000000 + }, + "subaccount_block_limits": { + "max_notional_liquidated": 100000000000, + "max_quantums_insurance_lost": 1000000000000 + } + } + }, + "crisis": { + "constant_fee": { + "amount": "1000", + "denom": "adv4tnt" + } + }, + "delaymsg": { + "delayed_messages": [ + { + "block_height": 378000, + "id": 0, + "msg": { + "@type": "/dydxprotocol.feetiers.MsgUpdatePerpetualFeeParams", + "authority": "dydx1mkkvp26dngu6n8rmalaxyp3gwkjuzztq5zx6tr", + "params": { + "tiers": [ + { + "absolute_volume_requirement": "0", + "maker_fee_ppm": 100, + "maker_volume_share_requirement_ppm": 0, + "name": "1", + "taker_fee_ppm": 500, + "total_volume_share_requirement_ppm": 0 + }, + { + "absolute_volume_requirement": "1000000000000", + "maker_fee_ppm": 100, + "maker_volume_share_requirement_ppm": 0, + "name": "2", + "taker_fee_ppm": 450, + "total_volume_share_requirement_ppm": 0 + }, + { + "absolute_volume_requirement": "5000000000000", + "maker_fee_ppm": 50, + "maker_volume_share_requirement_ppm": 0, + "name": "3", + "taker_fee_ppm": 400, + "total_volume_share_requirement_ppm": 0 + }, + { + "absolute_volume_requirement": "25000000000000", + "maker_fee_ppm": 0, + "maker_volume_share_requirement_ppm": 0, + "name": "4", + "taker_fee_ppm": 350, + "total_volume_share_requirement_ppm": 0 + }, + { + "absolute_volume_requirement": "125000000000000", + "maker_fee_ppm": 0, + "maker_volume_share_requirement_ppm": 0, + "name": "5", + "taker_fee_ppm": 300, + "total_volume_share_requirement_ppm": 0 + }, + { + "absolute_volume_requirement": "125000000000000", + "maker_fee_ppm": -50, + "maker_volume_share_requirement_ppm": 0, + "name": "6", + "taker_fee_ppm": 250, + "total_volume_share_requirement_ppm": 5000 + }, + { + "absolute_volume_requirement": "125000000000000", + "maker_fee_ppm": -70, + "maker_volume_share_requirement_ppm": 10000, + "name": "7", + "taker_fee_ppm": 250, + "total_volume_share_requirement_ppm": 5000 + }, + { + "absolute_volume_requirement": "125000000000000", + "maker_fee_ppm": -90, + "maker_volume_share_requirement_ppm": 20000, + "name": "8", + "taker_fee_ppm": 250, + "total_volume_share_requirement_ppm": 5000 + }, + { + "absolute_volume_requirement": "125000000000000", + "maker_fee_ppm": -110, + "maker_volume_share_requirement_ppm": 40000, + "name": "9", + "taker_fee_ppm": 250, + "total_volume_share_requirement_ppm": 5000 + } + ] + } + } + } + ], + "next_delayed_message_id": 1 + }, + "distribution": { + "delegator_starting_infos": [], + "delegator_withdraw_infos": [], + "fee_pool": { + "community_pool": [] + }, + "outstanding_rewards": [], + "params": { + "base_proposer_reward": "0.000000000000000000", + "bonus_proposer_reward": "0.000000000000000000", + "community_tax": "0.020000000000000000", + "withdraw_addr_enabled": true + }, + "previous_proposer": "", + "validator_accumulated_commissions": [], + "validator_current_rewards": [], + "validator_historical_rewards": [], + "validator_slash_events": [] + }, + "epochs": { + "epoch_info_list": [ + { + "current_epoch": 0, + "current_epoch_start_block": 0, + "duration": 60, + "fast_forward_next_tick": true, + "is_initialized": false, + "name": "funding-sample", + "next_tick": 30 + }, + { + "current_epoch": 0, + "current_epoch_start_block": 0, + "duration": 3600, + "fast_forward_next_tick": true, + "is_initialized": false, + "name": "funding-tick", + "next_tick": 0 + }, + { + "current_epoch": 0, + "current_epoch_start_block": 0, + "duration": 3600, + "fast_forward_next_tick": true, + "is_initialized": false, + "name": "stats-epoch", + "next_tick": 0 + } + ] + }, + "evidence": { + "evidence": [] + }, + "feegrant": { + "allowances": [] + }, + "feetiers": { + "params": { + "tiers": [ + { + "absolute_volume_requirement": "0", + "maker_fee_ppm": -110, + "maker_volume_share_requirement_ppm": 0, + "name": "1", + "taker_fee_ppm": 500, + "total_volume_share_requirement_ppm": 0 + }, + { + "absolute_volume_requirement": "1000000000000", + "maker_fee_ppm": -110, + "maker_volume_share_requirement_ppm": 0, + "name": "2", + "taker_fee_ppm": 450, + "total_volume_share_requirement_ppm": 0 + }, + { + "absolute_volume_requirement": "5000000000000", + "maker_fee_ppm": -110, + "maker_volume_share_requirement_ppm": 0, + "name": "3", + "taker_fee_ppm": 400, + "total_volume_share_requirement_ppm": 0 + }, + { + "absolute_volume_requirement": "25000000000000", + "maker_fee_ppm": -110, + "maker_volume_share_requirement_ppm": 0, + "name": "4", + "taker_fee_ppm": 350, + "total_volume_share_requirement_ppm": 0 + }, + { + "absolute_volume_requirement": "125000000000000", + "maker_fee_ppm": -110, + "maker_volume_share_requirement_ppm": 0, + "name": "5", + "taker_fee_ppm": 300, + "total_volume_share_requirement_ppm": 0 + }, + { + "absolute_volume_requirement": "125000000000000", + "maker_fee_ppm": -110, + "maker_volume_share_requirement_ppm": 0, + "name": "6", + "taker_fee_ppm": 250, + "total_volume_share_requirement_ppm": 5000 + }, + { + "absolute_volume_requirement": "125000000000000", + "maker_fee_ppm": -110, + "maker_volume_share_requirement_ppm": 10000, + "name": "7", + "taker_fee_ppm": 250, + "total_volume_share_requirement_ppm": 5000 + }, + { + "absolute_volume_requirement": "125000000000000", + "maker_fee_ppm": -110, + "maker_volume_share_requirement_ppm": 20000, + "name": "8", + "taker_fee_ppm": 250, + "total_volume_share_requirement_ppm": 5000 + }, + { + "absolute_volume_requirement": "125000000000000", + "maker_fee_ppm": -110, + "maker_volume_share_requirement_ppm": 40000, + "name": "9", + "taker_fee_ppm": 250, + "total_volume_share_requirement_ppm": 5000 + } + ] + } + }, + "genutil": { + "gen_txs": [ + { + "body": { + "messages": [ + { + "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", + "description": { + "moniker": "alice", + "identity": "", + "website": "", + "security_contact": "", + "details": "" + }, + "commission": { + "rate": "1.000000000000000000", + "max_rate": "1.000000000000000000", + "max_change_rate": "0.010000000000000000" + }, + "min_self_delegation": "1", + "delegator_address": "dydx199tqg4wdlnu4qjlxchpd7seg454937hjrknju4", + "validator_address": "dydxvaloper199tqg4wdlnu4qjlxchpd7seg454937hjxg9yhy", + "pubkey": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": "YiARx8259Z+fGFUxQLrz/5FU2RYRT6f5yzvt7D7CrQM=" + }, + "value": { + "denom": "adv4tnt", + "amount": "500000000000000000000000" + } + } + ], + "memo": "17e5e45691f0d01449c84fd4ae87279578cdd7ec@172.17.0.2:26656", + "timeout_height": "0", + "extension_options": [], + "non_critical_extension_options": [] + }, + "auth_info": { + "signer_infos": [ + { + "public_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "A0iQ+HpUfJGcgcH7iiEzY9VwCYWCTwg5LsTjc/q1XwSc" + }, + "mode_info": { + "single": { + "mode": "SIGN_MODE_DIRECT" + } + }, + "sequence": "0" + } + ], + "fee": { + "amount": [], + "gas_limit": "200000", + "payer": "", + "granter": "" + }, + "tip": null + }, + "signatures": [ + "OPf+X9U5GJwJOeTpBgW4HBpEP9PEivbMoUzy3WZiriMEghRJLuP3LGVpp4ePr8tEBoEEDFFu11ThiiyzjcmxIQ==" + ] + }, + { + "body": { + "messages": [ + { + "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", + "description": { + "moniker": "carl", + "identity": "", + "website": "", + "security_contact": "", + "details": "" + }, + "commission": { + "rate": "1.000000000000000000", + "max_rate": "1.000000000000000000", + "max_change_rate": "0.010000000000000000" + }, + "min_self_delegation": "1", + "delegator_address": "dydx1fjg6zp6vv8t9wvy4lps03r5l4g7tkjw9wvmh70", + "validator_address": "dydxvaloper1fjg6zp6vv8t9wvy4lps03r5l4g7tkjw9tjdp47", + "pubkey": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": "ytLfs1W6E2I41iteKC/YwjyZ/51+CAYCHYxmRHiBeY4=" + }, + "value": { + "denom": "adv4tnt", + "amount": "500000000000000000000000" + } + } + ], + "memo": "47539956aaa8e624e0f1d926040e54908ad0eb44@172.17.0.2:26656", + "timeout_height": "0", + "extension_options": [], + "non_critical_extension_options": [] + }, + "auth_info": { + "signer_infos": [ + { + "public_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "AkA1fsLUhCSWbnemBIAR9CPkK1Ra1LlYZcrAKm/Ymvqn" + }, + "mode_info": { + "single": { + "mode": "SIGN_MODE_DIRECT" + } + }, + "sequence": "0" + } + ], + "fee": { + "amount": [], + "gas_limit": "200000", + "payer": "", + "granter": "" + }, + "tip": null + }, + "signatures": [ + "IWmBNPto7CR7GE5Qc/Vce6uRJvgmxAifUfnqsuk/9bU0a/efjPl9wCVqXoV4tBN5h8YUJTwtU7Ll2Lwiq7z9ew==" + ] + }, + { + "body": { + "messages": [ + { + "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", + "description": { + "moniker": "dave", + "identity": "", + "website": "", + "security_contact": "", + "details": "" + }, + "commission": { + "rate": "1.000000000000000000", + "max_rate": "1.000000000000000000", + "max_change_rate": "0.010000000000000000" + }, + "min_self_delegation": "1", + "delegator_address": "dydx1wau5mja7j7zdavtfq9lu7ejef05hm6ffenlcsn", + "validator_address": "dydxvaloper1wau5mja7j7zdavtfq9lu7ejef05hm6ffudfwmz", + "pubkey": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": "yG29kRfZ/hgAE1I7uWjbKQJJL4/gX/05XBnfB+m196A=" + }, + "value": { + "denom": "adv4tnt", + "amount": "500000000000000000000000" + } + } + ], + "memo": "5882428984d83b03d0c907c1f0af343534987052@172.17.0.2:26656", + "timeout_height": "0", + "extension_options": [], + "non_critical_extension_options": [] + }, + "auth_info": { + "signer_infos": [ + { + "public_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "A87MchHGMj7i1xBwUfECtXzXJIgli/JVFoSaxUqIN86R" + }, + "mode_info": { + "single": { + "mode": "SIGN_MODE_DIRECT" + } + }, + "sequence": "0" + } + ], + "fee": { + "amount": [], + "gas_limit": "200000", + "payer": "", + "granter": "" + }, + "tip": null + }, + "signatures": [ + "PbkEltEhqvhavA99UFdnI1IoRPw/8mKAR/Wif4zNRDwCPs19Yu0GitohSQVAxJGs3xK9whKmGIJoQQd8cYQz+Q==" + ] + }, + { + "body": { + "messages": [ + { + "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", + "description": { + "moniker": "bob", + "identity": "", + "website": "", + "security_contact": "", + "details": "" + }, + "commission": { + "rate": "1.000000000000000000", + "max_rate": "1.000000000000000000", + "max_change_rate": "0.010000000000000000" + }, + "min_self_delegation": "1", + "delegator_address": "dydx10fx7sy6ywd5senxae9dwytf8jxek3t2gcen2vs", + "validator_address": "dydxvaloper10fx7sy6ywd5senxae9dwytf8jxek3t2ga89u8p", + "pubkey": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": "+P8YiogqqQY+iD96yEa9OJx6EgieU95u9eR3pzxfDp0=" + }, + "value": { + "denom": "adv4tnt", + "amount": "500000000000000000000000" + } + } + ], + "memo": "b69182310be02559483e42c77b7b104352713166@172.17.0.2:26656", + "timeout_height": "0", + "extension_options": [], + "non_critical_extension_options": [] + }, + "auth_info": { + "signer_infos": [ + { + "public_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "AlamQtNuTEHlCbn4ZQ20em/bbQNcaAJO54yMOCoE8OTy" + }, + "mode_info": { + "single": { + "mode": "SIGN_MODE_DIRECT" + } + }, + "sequence": "0" + } + ], + "fee": { + "amount": [], + "gas_limit": "200000", + "payer": "", + "granter": "" + }, + "tip": null + }, + "signatures": [ + "Hoo2es537f4zoxDFfrIbhPPBvWIiWudssh34cdidg7NnckC8bl8o+cWsdRP8BO/JkwT67Tn7vQXV+RHU6H5opw==" + ] + } + ] + }, + "gov": { + "deposits": [], + "params": { + "burn_proposal_deposit_prevote": false, + "burn_vote_quorum": false, + "burn_vote_veto": true, + "max_deposit_period": "300s", + "min_deposit": [ + { + "amount": "10000000", + "denom": "adv4tnt" + } + ], + "min_initial_deposit_ratio": "0.20000", + "quorum": "0.334000000000000000", + "threshold": "0.500000000000000000", + "veto_threshold": "0.334000000000000000", + "voting_period": "300s" + }, + "proposals": [], + "starting_proposal_id": "1", + "votes": [] + }, + "ibc": { + "channel_genesis": { + "ack_sequences": [], + "acknowledgements": [], + "channels": [], + "commitments": [], + "next_channel_sequence": "0", + "receipts": [], + "recv_sequences": [], + "send_sequences": [] + }, + "client_genesis": { + "clients": [], + "clients_consensus": [], + "clients_metadata": [], + "create_localhost": false, + "next_client_sequence": "0", + "params": { + "allowed_clients": [ + "07-tendermint" + ] + } + }, + "connection_genesis": { + "client_connection_paths": [], + "connections": [], + "next_connection_sequence": "0", + "params": { + "max_expected_time_per_block": "30000000000" + } + } + }, + "perpetuals": { + "liquidity_tiers": [ + { + "base_position_notional": 1000000000000, + "id": 0, + "impact_notional": 10000000000, + "initial_margin_ppm": 50000, + "maintenance_fraction_ppm": 600000, + "name": "Large-Cap" + }, + { + "base_position_notional": 250000000000, + "id": 1, + "impact_notional": 5000000000, + "initial_margin_ppm": 100000, + "maintenance_fraction_ppm": 500000, + "name": "Mid-Cap" + }, + { + "base_position_notional": 100000000000, + "id": 2, + "impact_notional": 2500000000, + "initial_margin_ppm": 200000, + "maintenance_fraction_ppm": 500000, + "name": "Long-Tail" + }, + { + "base_position_notional": 1000000000, + "id": 3, + "impact_notional": 2500000000, + "initial_margin_ppm": 1000000, + "maintenance_fraction_ppm": 200000, + "name": "Safety" + } + ], + "params": { + "funding_rate_clamp_factor_ppm": 6000000, + "min_num_votes_per_sample": 15, + "premium_vote_clamp_factor_ppm": 60000000 + }, + "perpetuals": [ + { + "params": { + "atomic_resolution": -10, + "default_funding_ppm": 0, + "id": 0, + "liquidity_tier": 0, + "market_id": 0, + "ticker": "BTC-USD" + } + }, + { + "params": { + "atomic_resolution": -9, + "default_funding_ppm": 0, + "id": 1, + "liquidity_tier": 0, + "market_id": 1, + "ticker": "ETH-USD" + } + }, + { + "params": { + "atomic_resolution": -6, + "default_funding_ppm": 0, + "id": 2, + "liquidity_tier": 1, + "market_id": 2, + "ticker": "LINK-USD" + } + }, + { + "params": { + "atomic_resolution": -5, + "default_funding_ppm": 0, + "id": 3, + "liquidity_tier": 1, + "market_id": 3, + "ticker": "MATIC-USD" + } + }, + { + "params": { + "atomic_resolution": -5, + "default_funding_ppm": 0, + "id": 4, + "liquidity_tier": 1, + "market_id": 4, + "ticker": "CRV-USD" + } + }, + { + "params": { + "atomic_resolution": -7, + "default_funding_ppm": 0, + "id": 5, + "liquidity_tier": 1, + "market_id": 5, + "ticker": "SOL-USD" + } + }, + { + "params": { + "atomic_resolution": -5, + "default_funding_ppm": 0, + "id": 6, + "liquidity_tier": 1, + "market_id": 6, + "ticker": "ADA-USD" + } + }, + { + "params": { + "atomic_resolution": -7, + "default_funding_ppm": 0, + "id": 7, + "liquidity_tier": 1, + "market_id": 7, + "ticker": "AVAX-USD" + } + }, + { + "params": { + "atomic_resolution": -6, + "default_funding_ppm": 0, + "id": 8, + "liquidity_tier": 1, + "market_id": 8, + "ticker": "FIL-USD" + } + }, + { + "params": { + "atomic_resolution": -7, + "default_funding_ppm": 0, + "id": 9, + "liquidity_tier": 1, + "market_id": 9, + "ticker": "LTC-USD" + } + }, + { + "params": { + "atomic_resolution": -4, + "default_funding_ppm": 0, + "id": 10, + "liquidity_tier": 1, + "market_id": 10, + "ticker": "DOGE-USD" + } + }, + { + "params": { + "atomic_resolution": -6, + "default_funding_ppm": 0, + "id": 11, + "liquidity_tier": 1, + "market_id": 11, + "ticker": "ATOM-USD" + } + }, + { + "params": { + "atomic_resolution": -6, + "default_funding_ppm": 0, + "id": 12, + "liquidity_tier": 1, + "market_id": 12, + "ticker": "DOT-USD" + } + }, + { + "params": { + "atomic_resolution": -6, + "default_funding_ppm": 0, + "id": 13, + "liquidity_tier": 1, + "market_id": 13, + "ticker": "UNI-USD" + } + }, + { + "params": { + "atomic_resolution": -8, + "default_funding_ppm": 0, + "id": 14, + "liquidity_tier": 1, + "market_id": 14, + "ticker": "BCH-USD" + } + }, + { + "params": { + "atomic_resolution": -4, + "default_funding_ppm": 0, + "id": 15, + "liquidity_tier": 1, + "market_id": 15, + "ticker": "TRX-USD" + } + }, + { + "params": { + "atomic_resolution": -6, + "default_funding_ppm": 0, + "id": 16, + "liquidity_tier": 1, + "market_id": 16, + "ticker": "NEAR-USD" + } + }, + { + "params": { + "atomic_resolution": -9, + "default_funding_ppm": 0, + "id": 17, + "liquidity_tier": 2, + "market_id": 17, + "ticker": "MKR-USD" + } + }, + { + "params": { + "atomic_resolution": -5, + "default_funding_ppm": 0, + "id": 18, + "liquidity_tier": 1, + "market_id": 18, + "ticker": "XLM-USD" + } + }, + { + "params": { + "atomic_resolution": -7, + "default_funding_ppm": 0, + "id": 19, + "liquidity_tier": 1, + "market_id": 19, + "ticker": "ETC-USD" + } + }, + { + "params": { + "atomic_resolution": -7, + "default_funding_ppm": 0, + "id": 20, + "liquidity_tier": 2, + "market_id": 20, + "ticker": "COMP-USD" + } + }, + { + "params": { + "atomic_resolution": -6, + "default_funding_ppm": 0, + "id": 21, + "liquidity_tier": 1, + "market_id": 21, + "ticker": "WLD-USD" + } + }, + { + "params": { + "atomic_resolution": -6, + "default_funding_ppm": 0, + "id": 22, + "liquidity_tier": 2, + "market_id": 22, + "ticker": "APE-USD" + } + }, + { + "params": { + "atomic_resolution": -6, + "default_funding_ppm": 0, + "id": 23, + "liquidity_tier": 1, + "market_id": 23, + "ticker": "APT-USD" + } + }, + { + "params": { + "atomic_resolution": -6, + "default_funding_ppm": 0, + "id": 24, + "liquidity_tier": 1, + "market_id": 24, + "ticker": "ARB-USD" + } + }, + { + "params": { + "atomic_resolution": -5, + "default_funding_ppm": 0, + "id": 25, + "liquidity_tier": 2, + "market_id": 25, + "ticker": "BLUR-USD" + } + }, + { + "params": { + "atomic_resolution": -6, + "default_funding_ppm": 0, + "id": 26, + "liquidity_tier": 2, + "market_id": 26, + "ticker": "LDO-USD" + } + }, + { + "params": { + "atomic_resolution": -6, + "default_funding_ppm": 0, + "id": 27, + "liquidity_tier": 1, + "market_id": 27, + "ticker": "OP-USD" + } + }, + { + "params": { + "atomic_resolution": 1, + "default_funding_ppm": 0, + "id": 28, + "liquidity_tier": 1, + "market_id": 28, + "ticker": "PEPE-USD" + } + }, + { + "params": { + "atomic_resolution": -5, + "default_funding_ppm": 0, + "id": 29, + "liquidity_tier": 2, + "market_id": 29, + "ticker": "SEI-USD" + } + }, + { + "params": { + "atomic_resolution": 0, + "default_funding_ppm": 0, + "id": 30, + "liquidity_tier": 1, + "market_id": 30, + "ticker": "SHIB-USD" + } + }, + { + "params": { + "atomic_resolution": -5, + "default_funding_ppm": 0, + "id": 31, + "liquidity_tier": 1, + "market_id": 31, + "ticker": "SUI-USD" + } + }, + { + "params": { + "atomic_resolution": -5, + "default_funding_ppm": 0, + "id": 32, + "liquidity_tier": 1, + "market_id": 32, + "ticker": "XRP-USD" + } + } + ] + }, + "prices": { + "market_params": [ + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"BTC-USD\"\n}\n]\n}", + "exponent": -5, + "id": 0, + "min_exchanges": 1, + "min_price_change_ppm": 1000, + "pair": "BTC-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"ETH-USD\"\n}\n]\n}", + "exponent": -6, + "id": 1, + "min_exchanges": 1, + "min_price_change_ppm": 1000, + "pair": "ETH-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -9, + "id": 2, + "min_exchanges": 1, + "min_price_change_ppm": 2500, + "pair": "LINK-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -10, + "id": 3, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "MATIC-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -10, + "id": 4, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "CRV-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -8, + "id": 5, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "SOL-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -10, + "id": 6, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "ADA-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -8, + "id": 7, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "AVAX-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -9, + "id": 8, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "FIL-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -8, + "id": 9, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "LTC-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -11, + "id": 10, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "DOGE-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -9, + "id": 11, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "ATOM-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -9, + "id": 12, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "DOT-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -9, + "id": 13, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "UNI-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -7, + "id": 14, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "BCH-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -11, + "id": 15, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "TRX-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -9, + "id": 16, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "NEAR-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -6, + "id": 17, + "min_exchanges": 3, + "min_price_change_ppm": 4000, + "pair": "MKR-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -10, + "id": 18, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "XLM-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -8, + "id": 19, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "ETC-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -8, + "id": 20, + "min_exchanges": 3, + "min_price_change_ppm": 4000, + "pair": "COMP-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -9, + "id": 21, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "WLD-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -9, + "id": 22, + "min_exchanges": 3, + "min_price_change_ppm": 4000, + "pair": "APE-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -9, + "id": 23, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "APT-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -9, + "id": 24, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "ARB-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -10, + "id": 25, + "min_exchanges": 3, + "min_price_change_ppm": 4000, + "pair": "BLUR-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -9, + "id": 26, + "min_exchanges": 3, + "min_price_change_ppm": 4000, + "pair": "LDO-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -9, + "id": 27, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "OP-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -16, + "id": 28, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "PEPE-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -10, + "id": 29, + "min_exchanges": 3, + "min_price_change_ppm": 4000, + "pair": "SEI-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -15, + "id": 30, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "SHIB-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -10, + "id": 31, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "SUI-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -10, + "id": 32, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "XRP-USD" + }, + { + "exchange_config_json": "{\n\"exchanges\": [\n{\n\"exchangeName\": \"TestExchange\",\n\"ticker\": \"LINK-USD\"\n}\n]\n}", + "exponent": -9, + "id": 1000000, + "min_exchanges": 3, + "min_price_change_ppm": 1000, + "pair": "USDT-USD" + }, + { + "exchange_config_json": "{\"exchanges\":[{\"exchangeName\":\"Binance\",\"ticker\":\"DYDXUSDT\",\"adjustByMarket\":\"USDT-USD\"},{\"exchangeName\":\"Bybit\",\"ticker\":\"DYDXUSDT\",\"adjustByMarket\":\"USDT-USD\"},{\"exchangeName\":\"Gate\",\"ticker\":\"DYDX_USDT\",\"adjustByMarket\":\"USDT-USD\"},{\"exchangeName\":\"Kucoin\",\"ticker\":\"DYDX-USDT\",\"adjustByMarket\":\"USDT-USD\"},{\"exchangeName\":\"Mexc\",\"ticker\":\"DYDX_USDT\",\"adjustByMarket\":\"USDT-USD\"},{\"exchangeName\":\"Okx\",\"ticker\":\"DYDX-USDT\",\"adjustByMarket\":\"USDT-USD\"}]}", + "exponent": -9, + "id": 1000001, + "min_exchanges": 3, + "min_price_change_ppm": 2500, + "pair": "DYDX-USD" + } + ], + "market_prices": [ + { + "exponent": -5, + "id": 0, + "price": 2868819524 + }, + { + "exponent": -6, + "id": 1, + "price": 1811985252 + }, + { + "exponent": -9, + "id": 2, + "price": 7204646989 + }, + { + "exponent": -10, + "id": 3, + "price": 6665746387 + }, + { + "exponent": -10, + "id": 4, + "price": 6029316660 + }, + { + "exponent": -8, + "id": 5, + "price": 2350695125 + }, + { + "exponent": -10, + "id": 6, + "price": 2918831290 + }, + { + "exponent": -8, + "id": 7, + "price": 1223293720 + }, + { + "exponent": -9, + "id": 8, + "price": 4050336602 + }, + { + "exponent": -8, + "id": 9, + "price": 8193604950 + }, + { + "exponent": -11, + "id": 10, + "price": 7320836895 + }, + { + "exponent": -9, + "id": 11, + "price": 8433494428 + }, + { + "exponent": -9, + "id": 12, + "price": 4937186533 + }, + { + "exponent": -9, + "id": 13, + "price": 5852293356 + }, + { + "exponent": -7, + "id": 14, + "price": 2255676327 + }, + { + "exponent": -11, + "id": 15, + "price": 7795369902 + }, + { + "exponent": -9, + "id": 16, + "price": 1312325536 + }, + { + "exponent": -6, + "id": 17, + "price": 1199517382 + }, + { + "exponent": -10, + "id": 18, + "price": 1398578933 + }, + { + "exponent": -8, + "id": 19, + "price": 1741060746 + }, + { + "exponent": -8, + "id": 20, + "price": 5717635307 + }, + { + "exponent": -9, + "id": 21, + "price": 1943019371 + }, + { + "exponent": -9, + "id": 22, + "price": 1842365656 + }, + { + "exponent": -9, + "id": 23, + "price": 6787621897 + }, + { + "exponent": -9, + "id": 24, + "price": 1127629325 + }, + { + "exponent": -10, + "id": 25, + "price": 2779565892 + }, + { + "exponent": -9, + "id": 26, + "price": 1855061997 + }, + { + "exponent": -9, + "id": 27, + "price": 1562218603 + }, + { + "exponent": -16, + "id": 28, + "price": 2481900353 + }, + { + "exponent": -10, + "id": 29, + "price": 1686998025 + }, + { + "exponent": -15, + "id": 30, + "price": 8895882688 + }, + { + "exponent": -10, + "id": 31, + "price": 5896318772 + }, + { + "exponent": -10, + "id": 32, + "price": 6327613800 + }, + { + "exponent": -9, + "id": 1000000, + "price": 1000000000 + }, + { + "exponent": -9, + "id": 1000001, + "price": 2050000000 + } + ] + }, + "rewards": { + "params": { + "denom": "adv4tnt", + "denom_exponent": -18, + "fee_multiplier_ppm": 990000, + "market_id": 11, + "treasury_account": "rewards_treasury" + } + }, + "sending": {}, + "slashing": { + "missed_blocks": [], + "params": { + "downtime_jail_duration": "60s", + "min_signed_per_window": "0.050000000000000000", + "signed_blocks_window": "3000", + "slash_fraction_double_sign": "0.000000000000000000", + "slash_fraction_downtime": "0.000000000000000000" + }, + "signing_infos": [] + }, + "staking": { + "delegations": [], + "exported": false, + "last_total_power": "0", + "last_validator_powers": [], + "params": { + "bond_denom": "adv4tnt", + "historical_entries": 10000, + "max_entries": 7, + "max_validators": 100, + "min_commission_rate": "0.000000000000000000", + "unbonding_time": "1814400s" + }, + "redelegations": [], + "unbonding_delegations": [], + "validators": [] + }, + "stats": { + "params": { + "window_duration": "2592000s" + } + }, + "subaccounts": { + "subaccounts": [ + { + "asset_positions": [ + { + "asset_id": 0, + "index": 0, + "quantums": "100000000000000000" + } + ], + "id": { + "number": 0, + "owner": "dydx199tqg4wdlnu4qjlxchpd7seg454937hjrknju4" + }, + "margin_enabled": true + }, + { + "asset_positions": [ + { + "asset_id": 0, + "index": 0, + "quantums": "100000000000000000" + } + ], + "id": { + "number": 0, + "owner": "dydx10fx7sy6ywd5senxae9dwytf8jxek3t2gcen2vs" + }, + "margin_enabled": true + }, + { + "asset_positions": [ + { + "asset_id": 0, + "index": 0, + "quantums": "100000000000000000" + } + ], + "id": { + "number": 0, + "owner": "dydx1fjg6zp6vv8t9wvy4lps03r5l4g7tkjw9wvmh70" + }, + "margin_enabled": true + }, + { + "asset_positions": [ + { + "asset_id": 0, + "index": 0, + "quantums": "100000000000000000" + } + ], + "id": { + "number": 0, + "owner": "dydx1wau5mja7j7zdavtfq9lu7ejef05hm6ffenlcsn" + }, + "margin_enabled": true + }, + { + "asset_positions": [ + { + "asset_id": 0, + "index": 0, + "quantums": "900000000000000000" + } + ], + "id": { + "number": 0, + "owner": "dydx1nzuttarf5k2j0nug5yzhr6p74t9avehn9hlh8m" + }, + "margin_enabled": true + } + ] + }, + "transfer": { + "denom_traces": [], + "params": { + "receive_enabled": true, + "send_enabled": true + }, + "port_id": "transfer", + "total_escrowed": [] + }, + "upgrade": {}, + "vest": { + "vest_entries": [ + { + "denom": "adv4tnt", + "end_time": "2025-01-01T00:00:00Z", + "start_time": "2023-01-01T00:00:00Z", + "treasury_account": "community_treasury", + "vester_account": "community_vester" + }, + { + "denom": "adv4tnt", + "end_time": "2025-01-01T00:00:00Z", + "start_time": "2023-01-01T00:00:00Z", + "treasury_account": "rewards_treasury", + "vester_account": "rewards_vester" + } + ] + } + } + } diff --git a/protocol/testing/containertest/testnet.go b/protocol/testing/containertest/testnet.go index ca64a9c2ed..70a44a47d0 100644 --- a/protocol/testing/containertest/testnet.go +++ b/protocol/testing/containertest/testnet.go @@ -24,6 +24,9 @@ const persistentPeers = "17e5e45691f0d01449c84fd4ae87279578cdd7ec@testnet-local- // Resources will expire in 10 minutes const resourceLifetimeSecs = 600 +// The version of that we're upgrading to (aka the current commit) +const UpgradeToVersion = "v3.0.0" + func monikers() map[string]string { return map[string]string{ "alice": constants.AliceMnenomic, @@ -36,10 +39,11 @@ func monikers() map[string]string { type Testnet struct { Nodes map[string]*Node - keyring keyring.Keyring - pool *dockertest.Pool - network *dockertest.Network - exchangeServer *pricefeed_testutil.ExchangeServer + isPreupgradeGenesis bool + keyring keyring.Keyring + pool *dockertest.Pool + network *dockertest.Network + exchangeServer *pricefeed_testutil.ExchangeServer } // NewTestnet returns a new Testnet. If creation fails, an error is returned. @@ -69,6 +73,15 @@ func NewTestnet() (testnet *Testnet, err error) { return testnet, nil } +func NewTestnetWithPreupgradeGenesis() (testnet *Testnet, err error) { + testnet, err = NewTestnet() + if err != nil { + return nil, err + } + testnet.isPreupgradeGenesis = true + return testnet, err +} + func (t *Testnet) Start() (err error) { err = t.initialize() if err != nil { @@ -112,6 +125,13 @@ func (t *Testnet) initialize() (err error) { } func (t *Testnet) initializeNode(moniker string) (*Node, error) { + var entrypointCommand string + if t.isPreupgradeGenesis { + entrypointCommand = "/dydxprotocol/preupgrade_entrypoint.sh" + } else { + entrypointCommand = "dydxprotocold" + } + resource, err := t.pool.RunWithOptions( &dockertest.RunOptions{ Name: fmt.Sprintf("testnet-local-%s", moniker), @@ -122,7 +142,7 @@ func (t *Testnet) initializeNode(moniker string) (*Node, error) { "26657", }, Entrypoint: []string{ - "dydxprotocold", + entrypointCommand, "start", "--home", fmt.Sprintf("/dydxprotocol/chain/.%s", moniker), @@ -131,6 +151,11 @@ func (t *Testnet) initializeNode(moniker string) (*Node, error) { "--bridge-daemon-eth-rpc-endpoint", "https://eth-sepolia.g.alchemy.com/v2/demo", }, + Env: []string{ + "DAEMON_NAME=dydxprotocold", + fmt.Sprintf("DAEMON_HOME=/dydxprotocol/chain/.%s", moniker), + fmt.Sprintf("UPGRADE_TO_VERSION=%s", UpgradeToVersion), + }, ExtraHosts: []string{ fmt.Sprintf("%s:host-gateway", testexchange.TestExchangeHost), }, From 0d121a7bf91eb79cbc73f2ee63a176e2f1b5c31c Mon Sep 17 00:00:00 2001 From: Christopher-Li Date: Thu, 14 Dec 2023 22:00:06 -0500 Subject: [PATCH 05/12] Update commit scripts (#893) --- indexer/scripts/deploy-commit-to-env.sh | 2 ++ indexer/scripts/push-commit-to-ecr.sh | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/indexer/scripts/deploy-commit-to-env.sh b/indexer/scripts/deploy-commit-to-env.sh index 8ec82d6a35..0339576879 100755 --- a/indexer/scripts/deploy-commit-to-env.sh +++ b/indexer/scripts/deploy-commit-to-env.sh @@ -23,6 +23,8 @@ case $env in "dev4") account=525975847385;; "dev5") account=917958511744;; "staging") account=677285201534;; + "public-testnet") account=013339450148;; # public testnet + "mainnet") account=332066407361;; # mainnet *) account=329916310755;; esac diff --git a/indexer/scripts/push-commit-to-ecr.sh b/indexer/scripts/push-commit-to-ecr.sh index 902d40d2d9..3e205bcafc 100755 --- a/indexer/scripts/push-commit-to-ecr.sh +++ b/indexer/scripts/push-commit-to-ecr.sh @@ -24,7 +24,7 @@ case $env in "dev5") account=917958511744;; "staging") account=677285201534;; "public-testnet") account=013339450148;; # public testnet - "testnet3") account=013339450148;; # public testnet + "mainnet") account=332066407361;; # mainnet *) account=329916310755;; esac From 98e239163d9a4bd23394d581eed16f3f695d89f1 Mon Sep 17 00:00:00 2001 From: roy-dydx <133032749+roy-dydx@users.noreply.github.com> Date: Mon, 18 Dec 2023 13:25:53 -0500 Subject: [PATCH 06/12] Add upgrade test (#894) * Add upgrade test * Address comments --- protocol/Makefile | 4 +- protocol/app/upgrades/v3.0.0/constants.go | 9 ++- protocol/app/upgrades/v3.0.0/upgrade.go | 2 + protocol/testing/containertest/node.go | 47 ++++++------ .../containertest/preupgrade_entrypoint.sh | 32 ++++----- .../testing/containertest/testnet_test.go | 71 +++++++++++++++++++ 6 files changed, 123 insertions(+), 42 deletions(-) diff --git a/protocol/Makefile b/protocol/Makefile index c74912db58..86e8d1b7ca 100644 --- a/protocol/Makefile +++ b/protocol/Makefile @@ -238,10 +238,10 @@ benchmark: @VERSION=$(VERSION) go test -mod=readonly -tags='$(build_tags)' -bench=. ./... test-container: - @VERSION=$(VERSION) go test -mod=readonly -tags='container_test $(build_tags)' ./testing/containertest + @SKIP_DISABLED=true VERSION=$(VERSION) go test -mod=readonly -tags='container_test $(build_tags)' ./testing/containertest test-container-accept: - @VERSION=$(VERSION) go test -mod=readonly -tags='container_test $(build_tags)' ./testing/containertest -args -accept + @SKIP_DISABLED=true VERSION=$(VERSION) go test -mod=readonly -tags='container_test $(build_tags)' ./testing/containertest -args -accept test-container-build: $(MAKE) localnet-build diff --git a/protocol/app/upgrades/v3.0.0/constants.go b/protocol/app/upgrades/v3.0.0/constants.go index ce08cea2a2..dff9f6df95 100644 --- a/protocol/app/upgrades/v3.0.0/constants.go +++ b/protocol/app/upgrades/v3.0.0/constants.go @@ -3,6 +3,7 @@ package v_3_0_0 import ( store "github.com/cosmos/cosmos-sdk/store/types" "github.com/dydxprotocol/v4-chain/protocol/app/upgrades" + ratelimittypes "github.com/dydxprotocol/v4-chain/protocol/x/ratelimit/types" ) const ( @@ -10,6 +11,10 @@ const ( ) var Upgrade = upgrades.Upgrade{ - UpgradeName: UpgradeName, - StoreUpgrades: store.StoreUpgrades{}, + UpgradeName: UpgradeName, + StoreUpgrades: store.StoreUpgrades{ + Added: []string{ + ratelimittypes.StoreKey, + }, + }, } diff --git a/protocol/app/upgrades/v3.0.0/upgrade.go b/protocol/app/upgrades/v3.0.0/upgrade.go index af80f7eac5..6cf6242494 100644 --- a/protocol/app/upgrades/v3.0.0/upgrade.go +++ b/protocol/app/upgrades/v3.0.0/upgrade.go @@ -101,6 +101,8 @@ func CreateUpgradeHandler( return func(ctx sdk.Context, plan upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { ctx.Logger().Info("Running %s Upgrade...", UpgradeName) InitializeModuleAccs(ctx, ak) + + // TODO(CORE-824): Initialize ratelimit module params to desired state. return mm.RunMigrations(ctx, configurator, vm) } } diff --git a/protocol/testing/containertest/node.go b/protocol/testing/containertest/node.go index 0a405362c3..b4159101f8 100644 --- a/protocol/testing/containertest/node.go +++ b/protocol/testing/containertest/node.go @@ -26,16 +26,16 @@ import ( const ( // When polling a node, poll every `pollFrequencyNs` and give up after `pollAttempts` attempts. pollFrequencyNs = time.Second - pollAttempts = 30 + pollAttempts = 60 cometPort = "26657/tcp" grpcPort = "9090/tcp" ) type Node struct { - keyring *keyring.Keyring - cometClient *comethttp.HTTP - grpcConn *grpc.ClientConn - resource *dockertest.Resource + keyring *keyring.Keyring + cometPort string + grpcPort string + resource *dockertest.Resource } func newNode(keyring *keyring.Keyring, resource *dockertest.Resource) (node *Node, err error) { @@ -43,19 +43,17 @@ func newNode(keyring *keyring.Keyring, resource *dockertest.Resource) (node *Nod keyring: keyring, resource: resource, } - cometPort := resource.GetHostPort(cometPort) - node.cometClient, err = comethttp.New("tcp://"+cometPort, "/websocket") - if err != nil { - return nil, err - } + node.cometPort = resource.GetHostPort(cometPort) + node.grpcPort = resource.GetHostPort(grpcPort) + return node, err +} - grpcPort := resource.GetHostPort(grpcPort) - node.grpcConn, err = grpc.Dial(grpcPort, grpc.WithTransportCredentials(insecure.NewCredentials())) - if err != nil { - return nil, err - } +func (n *Node) createCometClient() (*comethttp.HTTP, error) { + return comethttp.New("tcp://"+n.cometPort, "/websocket") +} - return node, err +func (n *Node) createGrpcConn() (*grpc.ClientConn, error) { + return grpc.Dial(n.grpcPort, grpc.WithTransportCredentials(insecure.NewCredentials())) } // Wait for current block height has advanced by at least `numBlocks` @@ -71,11 +69,8 @@ func (n *Node) Wait(numBlocks int64) error { func (n *Node) WaitUntilBlockHeight(height int64) error { for i := 0; i < pollAttempts; i++ { latestHeight, err := n.LatestBlockHeight() - if err != nil { - return err - } - if latestHeight >= height { + if err == nil && latestHeight >= height { return nil } time.Sleep(pollFrequencyNs) @@ -84,7 +79,11 @@ func (n *Node) WaitUntilBlockHeight(height int64) error { } func (n *Node) LatestBlockHeight() (int64, error) { - status, err := n.cometClient.Status(context.Background()) + cometClient, err := n.createCometClient() + if err != nil { + return 0, err + } + status, err := cometClient.Status(context.Background()) if err != nil { return 0, err } @@ -171,6 +170,10 @@ func Query[Request proto.Message, Response proto.Message, Client interface{}]( clientConstructor func(gogogrpc.ClientConn) Client, requestFn func(Client, context.Context, Request, ...grpc.CallOption) (Response, error), request Request) (proto.Message, error) { - client := clientConstructor(n.grpcConn) + conn, err := n.createGrpcConn() + if err != nil { + return nil, err + } + client := clientConstructor(conn) return requestFn(client, context.Background(), request) } diff --git a/protocol/testing/containertest/preupgrade_entrypoint.sh b/protocol/testing/containertest/preupgrade_entrypoint.sh index 425ff89990..c5967f1f78 100755 --- a/protocol/testing/containertest/preupgrade_entrypoint.sh +++ b/protocol/testing/containertest/preupgrade_entrypoint.sh @@ -7,30 +7,30 @@ set -eo pipefail # - Set the binary at the current commit to be the ugprade binary # - Set the genesis to be the preupgrade genesis. This is generated by genesis generation script at the # preupgrade commit. It is copied directly from a container. +# - Set the voting period of the preupgrade genesis to 15s. if [[ -z "${UPGRADE_TO_VERSION}" ]]; then echo >&2 "UPGRADE_TO_VERSION must be set" exit 1 fi -MONIKERS=( - "alice" - "bob" - "carl" - "dave" -) +if [[ -z "${DAEMON_NAME}" ]]; then + echo >&2 "DAEMON_NAME must be set" + exit 1 +fi -for i in "${!MONIKERS[@]}"; do - DAEMON_NAME="dydxprotocold" - DAEMON_HOME="$HOME/chain/.${MONIKERS[$i]}" +if [[ -z "${DAEMON_HOME}" ]]; then + echo >&2 "DAEMON_HOME must be set" + exit 1 +fi - rm "$DAEMON_HOME/cosmovisor/genesis/bin/dydxprotocold" - ln -s /bin/dydxprotocold_preupgrade "$DAEMON_HOME/cosmovisor/genesis/bin/dydxprotocold" - mkdir -p "$DAEMON_HOME/cosmovisor/upgrades/$UPGRADE_TO_VERSION/bin/" - ln -s /bin/dydxprotocold "$DAEMON_HOME/cosmovisor/upgrades/$UPGRADE_TO_VERSION/bin/dydxprotocold" +rm "$DAEMON_HOME/cosmovisor/genesis/bin/dydxprotocold" +ln -s /bin/dydxprotocold_preupgrade "$DAEMON_HOME/cosmovisor/genesis/bin/dydxprotocold" +mkdir -p "$DAEMON_HOME/cosmovisor/upgrades/$UPGRADE_TO_VERSION/bin/" +ln -s /bin/dydxprotocold "$DAEMON_HOME/cosmovisor/upgrades/$UPGRADE_TO_VERSION/bin/dydxprotocold" - rm "$DAEMON_HOME/config/genesis.json" - cp "$HOME/preupgrade_genesis.json" "$DAEMON_HOME/config/genesis.json" -done +rm "$DAEMON_HOME/config/genesis.json" +cp "$HOME/preupgrade_genesis.json" "$DAEMON_HOME/config/genesis.json" +dasel put -t string -f "$DAEMON_HOME/config/genesis.json" '.app_state.gov.params.voting_period' -v '15s' cosmovisor run "$@" diff --git a/protocol/testing/containertest/testnet_test.go b/protocol/testing/containertest/testnet_test.go index 75dbfc64c2..fcdbe1604e 100644 --- a/protocol/testing/containertest/testnet_test.go +++ b/protocol/testing/containertest/testnet_test.go @@ -14,9 +14,12 @@ import ( sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" bank "github.com/cosmos/cosmos-sdk/x/bank/types" + gov "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + upgrade "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/cosmos/gogoproto/jsonpb" "github.com/cosmos/gogoproto/proto" "github.com/dydxprotocol/v4-chain/protocol/daemons/pricefeed/client/types" + testapp "github.com/dydxprotocol/v4-chain/protocol/testutil/app" "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" "github.com/dydxprotocol/v4-chain/protocol/testutil/daemons/pricefeed/exchange_config" assets "github.com/dydxprotocol/v4-chain/protocol/x/assets/types" @@ -28,8 +31,15 @@ import ( ) const expectDirName = "expect" +const govModuleAddress = "dydx10d07y265gmmuvt4z0w9aw880jnsr700jnmapky" var acceptFlag = flag.Bool("accept", false, "Accept new values for expect files") +var nodeAddresses = []string{ + constants.AliceAccAddress.String(), + constants.BobAccAddress.String(), + constants.CarlAccAddress.String(), + constants.DaveAccAddress.String(), +} // Compare a message against an expected output. Use flag `-accept` to write or modify expected output. // Expected output will read/written from `expect/{testName}_{tag}.expect`. @@ -80,6 +90,11 @@ func expectProto(t *testing.T, tag string, message proto.Message) bool { } func TestPlaceOrder(t *testing.T) { + // TODO(DEC-2198): Reenable these tests after fixing flakiness on CI. + // Seems to occur only because multiple container tests run. + if os.Getenv("SKIP_DISABLED") != "" { + t.Skip("Skipping disabled test") + } testnet, err := NewTestnet() require.NoError(t, err, "failed to create testnet - is docker daemon running?") err = testnet.Start() @@ -113,6 +128,9 @@ func TestPlaceOrder(t *testing.T) { } func TestBankSend(t *testing.T) { + if os.Getenv("SKIP_DISABLED") != "" { + t.Skip("Skipping disabled test") + } testnet, err := NewTestnet() require.NoError(t, err, "failed to create testnet - is docker daemon running?") err = testnet.Start() @@ -244,6 +262,9 @@ func assertPricesWithTimeout(t *testing.T, node *Node, marketTags map[types.Mark } func TestMarketPrices(t *testing.T) { + if os.Getenv("SKIP_DISABLED") != "" { + t.Skip("Skipping disabled test") + } testnet, err := NewTestnet() require.NoError(t, err, "failed to create testnet - is docker daemon running?") testnet.setPrice(exchange_config.MARKET_BTC_USD, 50001) @@ -262,3 +283,53 @@ func TestMarketPrices(t *testing.T) { } assertPricesWithTimeout(t, node, expectedPrices, 30*time.Second) } + +func TestUpgrade(t *testing.T) { + testnet, err := NewTestnetWithPreupgradeGenesis() + require.NoError(t, err, "failed to create testnet - is docker daemon running?") + err = testnet.Start() + require.NoError(t, err) + defer testnet.MustCleanUp() + node := testnet.Nodes["alice"] + + proposal, err := gov.NewMsgSubmitProposal( + []sdk.Msg{ + &upgrade.MsgSoftwareUpgrade{ + Authority: govModuleAddress, + Plan: upgrade.Plan{ + Name: UpgradeToVersion, + Height: 10, + }, + }, + }, + testapp.TestDeposit, + constants.AliceAccAddress.String(), + testapp.TestMetadata, + testapp.TestTitle, + testapp.TestSummary, + ) + require.NoError(t, err) + + require.NoError(t, BroadcastTx( + node, + proposal, + constants.AliceAccAddress.String(), + )) + err = node.Wait(2) + require.NoError(t, err) + + for _, address := range nodeAddresses { + require.NoError(t, BroadcastTx( + node, + &gov.MsgVote{ + ProposalId: 1, + Voter: address, + Option: gov.VoteOption_VOTE_OPTION_YES, + }, + address, + )) + } + + err = node.WaitUntilBlockHeight(12) + require.NoError(t, err) +} From e618bf71eedcf1c54e8adb92242ec97838d35ce1 Mon Sep 17 00:00:00 2001 From: Jakob Herlitz Date: Mon, 18 Dec 2023 11:42:20 -0800 Subject: [PATCH 07/12] pr nits --- protocol/x/clob/keeper/clob_pair.go | 25 +++++++++++----------- protocol/x/clob/keeper/final_settlement.go | 1 + protocol/x/clob/types/clob_pair.go | 5 +++++ 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/protocol/x/clob/keeper/clob_pair.go b/protocol/x/clob/keeper/clob_pair.go index d6f0fa5a49..2541a6d5f4 100644 --- a/protocol/x/clob/keeper/clob_pair.go +++ b/protocol/x/clob/keeper/clob_pair.go @@ -468,19 +468,13 @@ func (k Keeper) UpdateClobPair( oldStatus := oldClobPair.Status newStatus := clobPair.Status - if oldStatus != newStatus { - if !types.IsSupportedClobPairStatusTransition(oldStatus, newStatus) { - return errorsmod.Wrapf( - types.ErrInvalidClobPairStatusTransition, - "Cannot transition from status %+v to status %+v", - oldStatus, - newStatus, - ) - } - - if newStatus == types.ClobPair_STATUS_FINAL_SETTLEMENT { - k.mustEnterFinalSettlement(ctx, clobPair.GetClobPairId()) - } + if !types.IsSupportedClobPairStatusTransition(oldStatus, newStatus) { + return errorsmod.Wrapf( + types.ErrInvalidClobPairStatusTransition, + "Cannot transition from status %+v to status %+v", + oldStatus, + newStatus, + ) } if err := k.validateClobPair(ctx, &clobPair); err != nil { @@ -505,6 +499,11 @@ func (k Keeper) UpdateClobPair( ), ) + // If newly transitioning to final settlement, enter final settlement. + if newStatus == types.ClobPair_STATUS_FINAL_SETTLEMENT && oldStatus != newStatus { + k.mustEnterFinalSettlement(ctx, clobPair.GetClobPairId()) + } + return nil } diff --git a/protocol/x/clob/keeper/final_settlement.go b/protocol/x/clob/keeper/final_settlement.go index 0660e203c9..03f1145e8b 100644 --- a/protocol/x/clob/keeper/final_settlement.go +++ b/protocol/x/clob/keeper/final_settlement.go @@ -40,6 +40,7 @@ func (k Keeper) mustCancelStatefulOrdersForFinalSettlement(ctx sdk.Context, clob k.MustRemoveStatefulOrder(ctx, orderId) } + // TODO(CLOB-1053): Iterate over stateful orders for only specified clob pair for _, order := range statefulOrders { if order.GetClobPairId() == clobPairId { // Remove from state, recovering from panic if necessary diff --git a/protocol/x/clob/types/clob_pair.go b/protocol/x/clob/types/clob_pair.go index 064d484f9f..1e7368d6a2 100644 --- a/protocol/x/clob/types/clob_pair.go +++ b/protocol/x/clob/types/clob_pair.go @@ -31,7 +31,12 @@ func IsSupportedClobPairStatus(clobPairStatus ClobPair_Status) bool { // IsSupportedClobPairStatusTransition returns true if it is considered valid to transition from // the first provided ClobPair_Status to the second provided ClobPair_Status. Else, returns false. +// Transitions from a ClobPair_Status to itself are considered valid. func IsSupportedClobPairStatusTransition(from ClobPair_Status, to ClobPair_Status) bool { + if from == to { + return true + } + _, exists := SupportedClobPairStatusTransitions[from][to] return exists } From a795a81bd3bf52dcc6e2d4b184d42d6be7eeeeeb Mon Sep 17 00:00:00 2001 From: lucas-dydx <76970939+lucas-dydx@users.noreply.github.com> Date: Mon, 18 Dec 2023 15:13:57 -0500 Subject: [PATCH 08/12] Add empty string check to string flag parsing (#883) --- protocol/app/app.go | 2 +- protocol/app/flags/flags.go | 5 +++-- protocol/daemons/flags/flags.go | 4 ++-- protocol/x/clob/flags/flags.go | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/protocol/app/app.go b/protocol/app/app.go index 42e448468b..0c414ae3ce 100644 --- a/protocol/app/app.go +++ b/protocol/app/app.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "errors" - "github.com/dydxprotocol/v4-chain/protocol/daemons/configs" "io" "math/big" "net/http" @@ -107,6 +106,7 @@ import ( // Daemons bridgeclient "github.com/dydxprotocol/v4-chain/protocol/daemons/bridge/client" + "github.com/dydxprotocol/v4-chain/protocol/daemons/configs" daemonflags "github.com/dydxprotocol/v4-chain/protocol/daemons/flags" liquidationclient "github.com/dydxprotocol/v4-chain/protocol/daemons/liquidation/client" metricsclient "github.com/dydxprotocol/v4-chain/protocol/daemons/metrics/client" diff --git a/protocol/app/flags/flags.go b/protocol/app/flags/flags.go index b7a7780cfe..6f67611a22 100644 --- a/protocol/app/flags/flags.go +++ b/protocol/app/flags/flags.go @@ -2,6 +2,7 @@ package flags import ( "fmt" + "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" "github.com/spf13/cast" @@ -102,7 +103,7 @@ func GetFlagValuesFromOptions( } if option := appOpts.Get(DdAgentHost); option != nil { - if v, err := cast.ToStringE(option); err == nil { + if v, err := cast.ToStringE(option); err == nil && len(v) > 0 { result.DdAgentHost = v } } @@ -120,7 +121,7 @@ func GetFlagValuesFromOptions( } if option := appOpts.Get(GrpcAddress); option != nil { - if v, err := cast.ToStringE(option); err == nil { + if v, err := cast.ToStringE(option); err == nil && len(v) > 0 { result.GrpcAddress = v } } diff --git a/protocol/daemons/flags/flags.go b/protocol/daemons/flags/flags.go index e386f379ab..6fbbcd7c3c 100644 --- a/protocol/daemons/flags/flags.go +++ b/protocol/daemons/flags/flags.go @@ -192,7 +192,7 @@ func GetDaemonFlagValuesFromOptions( // Shared Flags if option := appOpts.Get(FlagUnixSocketAddress); option != nil { - if v, err := cast.ToStringE(option); err == nil { + if v, err := cast.ToStringE(option); err == nil && len(v) > 0 { result.Shared.SocketAddress = v } } @@ -219,7 +219,7 @@ func GetDaemonFlagValuesFromOptions( } } if option := appOpts.Get(FlagBridgeDaemonEthRpcEndpoint); option != nil { - if v, err := cast.ToStringE(option); err == nil { + if v, err := cast.ToStringE(option); err == nil && len(v) > 0 { result.Bridge.EthRpcEndpoint = v } } diff --git a/protocol/x/clob/flags/flags.go b/protocol/x/clob/flags/flags.go index 4564fa32b7..902d4eae63 100644 --- a/protocol/x/clob/flags/flags.go +++ b/protocol/x/clob/flags/flags.go @@ -119,13 +119,13 @@ func GetClobFlagValuesFromOptions( } if option := appOpts.Get(MevTelemetryHosts); option != nil { - if v, err := cast.ToStringE(option); err == nil { + if v, err := cast.ToStringE(option); err == nil && len(v) > 0 { result.MevTelemetryHosts = strings.Split(v, ",") } } if option := appOpts.Get(MevTelemetryIdentifier); option != nil { - if v, err := cast.ToStringE(option); err == nil { + if v, err := cast.ToStringE(option); err == nil && len(v) > 0 { result.MevTelemetryIdentifier = v } } From 9df2f55de172b9c793c8536bc6572d2774ded3cd Mon Sep 17 00:00:00 2001 From: Jakob Herlitz Date: Mon, 18 Dec 2023 15:27:03 -0800 Subject: [PATCH 09/12] pr nits --- .../dydxprotocol/indexer/protocol/v1/clob.ts | 10 ++-- .../indexer/protocol/v1/clob.proto | 6 +-- protocol/indexer/protocol/v1/clob.pb.go | 6 +-- protocol/x/clob/keeper/clob_pair.go | 2 +- protocol/x/clob/keeper/final_settlement.go | 50 ++++++++++--------- 5 files changed, 39 insertions(+), 35 deletions(-) diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/indexer/protocol/v1/clob.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/indexer/protocol/v1/clob.ts index 92995bc19d..09c933e0d9 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/indexer/protocol/v1/clob.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/indexer/protocol/v1/clob.ts @@ -311,8 +311,9 @@ export enum ClobPairStatus { /** * CLOB_PAIR_STATUS_FINAL_SETTLEMENT - CLOB_PAIR_STATUS_FINAL_SETTLEMENT represents a clob pair that has been * deactivated. Clob pairs in this state do not accept new orders and trading - * is blocked. All open positions are closed by the protocol when the clob - * pair gains this status. + * is blocked. All open positions are closed and open stateful orders canceled + * by the protocol when the clob pair transitions to this status. All + * short-term orders are left to expire. */ CLOB_PAIR_STATUS_FINAL_SETTLEMENT = 6, UNRECOGNIZED = -1, @@ -360,8 +361,9 @@ export enum ClobPairStatusSDKType { /** * CLOB_PAIR_STATUS_FINAL_SETTLEMENT - CLOB_PAIR_STATUS_FINAL_SETTLEMENT represents a clob pair that has been * deactivated. Clob pairs in this state do not accept new orders and trading - * is blocked. All open positions are closed by the protocol when the clob - * pair gains this status. + * is blocked. All open positions are closed and open stateful orders canceled + * by the protocol when the clob pair transitions to this status. All + * short-term orders are left to expire. */ CLOB_PAIR_STATUS_FINAL_SETTLEMENT = 6, UNRECOGNIZED = -1, diff --git a/proto/dydxprotocol/indexer/protocol/v1/clob.proto b/proto/dydxprotocol/indexer/protocol/v1/clob.proto index 60fd2b0367..b061dae39b 100644 --- a/proto/dydxprotocol/indexer/protocol/v1/clob.proto +++ b/proto/dydxprotocol/indexer/protocol/v1/clob.proto @@ -175,8 +175,8 @@ enum ClobPairStatus { CLOB_PAIR_STATUS_INITIALIZING = 5; // CLOB_PAIR_STATUS_FINAL_SETTLEMENT represents a clob pair that has been // deactivated. Clob pairs in this state do not accept new orders and trading - // is blocked. All open positions are closed by the protocol when the clob - // pair gains this status. All open stateful orders are closed and short-term - // orders are left to expire. + // is blocked. All open positions are closed and open stateful orders canceled + // by the protocol when the clob pair transitions to this status. All + // short-term orders are left to expire. CLOB_PAIR_STATUS_FINAL_SETTLEMENT = 6; } diff --git a/protocol/indexer/protocol/v1/clob.pb.go b/protocol/indexer/protocol/v1/clob.pb.go index a512c08e4c..1c4958743c 100644 --- a/protocol/indexer/protocol/v1/clob.pb.go +++ b/protocol/indexer/protocol/v1/clob.pb.go @@ -49,9 +49,9 @@ const ( ClobPairStatus_CLOB_PAIR_STATUS_INITIALIZING ClobPairStatus = 5 // CLOB_PAIR_STATUS_FINAL_SETTLEMENT represents a clob pair that has been // deactivated. Clob pairs in this state do not accept new orders and trading - // is blocked. All open positions are closed by the protocol when the clob - // pair gains this status. All open stateful orders are closed and short-term - // orders are left to expire. + // is blocked. All open positions are closed and open stateful orders canceled + // by the protocol when the clob pair transitions to this status. All + // short-term orders are left to expire. ClobPairStatus_CLOB_PAIR_STATUS_FINAL_SETTLEMENT ClobPairStatus = 6 ) diff --git a/protocol/x/clob/keeper/clob_pair.go b/protocol/x/clob/keeper/clob_pair.go index 2541a6d5f4..d9b1457f06 100644 --- a/protocol/x/clob/keeper/clob_pair.go +++ b/protocol/x/clob/keeper/clob_pair.go @@ -501,7 +501,7 @@ func (k Keeper) UpdateClobPair( // If newly transitioning to final settlement, enter final settlement. if newStatus == types.ClobPair_STATUS_FINAL_SETTLEMENT && oldStatus != newStatus { - k.mustEnterFinalSettlement(ctx, clobPair.GetClobPairId()) + k.mustTransitionToFinalSettlement(ctx, clobPair.GetClobPairId()) } return nil diff --git a/protocol/x/clob/keeper/final_settlement.go b/protocol/x/clob/keeper/final_settlement.go index 03f1145e8b..0ccf7b6b9e 100644 --- a/protocol/x/clob/keeper/final_settlement.go +++ b/protocol/x/clob/keeper/final_settlement.go @@ -8,13 +8,13 @@ import ( "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" ) -// MustEnterFinalSettlement holds logic executed when a market transitions to FINAL_SETTLEMENT status. +// mustTransitionToFinalSettlement holds logic executed when a market transitions to FINAL_SETTLEMENT status. // This function will forcefully cancel all stateful open orders for the clob pair. -func (k Keeper) mustEnterFinalSettlement(ctx sdk.Context, clobPairId types.ClobPairId) { - // Forcefully cancel all stateful orders from state for this clob pair +func (k Keeper) mustTransitionToFinalSettlement(ctx sdk.Context, clobPairId types.ClobPairId) { + // Forcefully cancel all stateful orders from state for this clob pair. k.mustCancelStatefulOrdersForFinalSettlement(ctx, clobPairId) - // Delete untriggered conditional orders for this clob pair from memory + // Delete untriggered conditional orders for this clob pair from memory. delete(k.UntriggeredConditionalOrders, clobPairId) } @@ -42,29 +42,31 @@ func (k Keeper) mustCancelStatefulOrdersForFinalSettlement(ctx sdk.Context, clob // TODO(CLOB-1053): Iterate over stateful orders for only specified clob pair for _, order := range statefulOrders { - if order.GetClobPairId() == clobPairId { - // Remove from state, recovering from panic if necessary - safelyRemoveStatefulOrder(ctx, order.OrderId) + if order.GetClobPairId() != clobPairId { + continue + } + + // Remove from state, recovering from panic if necessary + safelyRemoveStatefulOrder(ctx, order.OrderId) - // Append to RemovedStatefulOrderIds so this order gets removed - // from the memclob in PrepareCheckState during the PurgeInvalidMemclobState step - processProposerMatchesEvents.RemovedStatefulOrderIds = append( - processProposerMatchesEvents.RemovedStatefulOrderIds, - order.OrderId, - ) + // Append to RemovedStatefulOrderIds so this order gets removed + // from the memclob in PrepareCheckState during the PurgeInvalidMemclobState step + processProposerMatchesEvents.RemovedStatefulOrderIds = append( + processProposerMatchesEvents.RemovedStatefulOrderIds, + order.OrderId, + ) - k.GetIndexerEventManager().AddTxnEvent( - ctx, - indexerevents.SubtypeStatefulOrder, - indexerevents.StatefulOrderEventVersion, - indexer_manager.GetBytes( - indexerevents.NewStatefulOrderRemovalEvent( - order.OrderId, - indexershared.OrderRemovalReason_ORDER_REMOVAL_REASON_FINAL_SETTLEMENT, - ), + k.GetIndexerEventManager().AddTxnEvent( + ctx, + indexerevents.SubtypeStatefulOrder, + indexerevents.StatefulOrderEventVersion, + indexer_manager.GetBytes( + indexerevents.NewStatefulOrderRemovalEvent( + order.OrderId, + indexershared.OrderRemovalReason_ORDER_REMOVAL_REASON_FINAL_SETTLEMENT, ), - ) - } + ), + ) } k.MustSetProcessProposerMatchesEvents(ctx, processProposerMatchesEvents) From 4a0aad81a56adf9435fc35daa2c0b68b64c56e3a Mon Sep 17 00:00:00 2001 From: vincentwschau <99756290+vincentwschau@users.noreply.github.com> Date: Tue, 19 Dec 2023 07:57:16 -0600 Subject: [PATCH 10/12] [IND-526] Block subscribing to subaccounts from restricted regions for read-only mode. (#896) --- indexer/packages/compliance/src/constants.ts | 8 ++- .../socks/__tests__/lib/subscriptions.test.ts | 52 +++++++++++++++++++ .../socks/__tests__/websocket/index.test.ts | 8 ++- .../services/socks/src/lib/subscription.ts | 24 +++++++-- indexer/services/socks/src/types.ts | 1 + indexer/services/socks/src/websocket/index.ts | 2 + .../socks/src/websocket/restrict-countries.ts | 5 ++ 7 files changed, 92 insertions(+), 8 deletions(-) diff --git a/indexer/packages/compliance/src/constants.ts b/indexer/packages/compliance/src/constants.ts index a203b609f5..1d5556ec9c 100644 --- a/indexer/packages/compliance/src/constants.ts +++ b/indexer/packages/compliance/src/constants.ts @@ -1,2 +1,6 @@ -export const INDEXER_GEOBLOCKED_PAYLOAD = 'Because you appear to be a resident of, or trading from, a jurisdiction that violates our terms of use, or have engaged in activity that violates our terms of use, you have been blocked. You may withdraw your funds from the protocol at any time.'; -export const INDEXER_COMPLIANCE_BLOCKED_PAYLOAD = 'Because this address appears to be a resident of, or trading from, a jurisdiction that violates our terms of use, or has engaged in activity that violates our terms of use, this address has been blocked.'; +export const INDEXER_GEOBLOCKED_PAYLOAD: string = 'Because you appear to be a resident of, or trading from, a jurisdiction that violates our terms of use, or have engaged in activity that violates our terms of use, you have been blocked. You may withdraw your funds from the protocol at any time.'; +export const INDEXER_COMPLIANCE_BLOCKED_PAYLOAD: string = 'Because this address appears to be a resident of, or trading from, a jurisdiction that violates our terms of use, or has engaged in activity that violates our terms of use, this address has been blocked.'; + +// For use by other services packages, can't be used to index on the actual requests +// object as that needs to be a string literal. +export const COUNTRY_HEADER_KEY: string = 'cf-ipcountry'; diff --git a/indexer/services/socks/__tests__/lib/subscriptions.test.ts b/indexer/services/socks/__tests__/lib/subscriptions.test.ts index 8cd3711ff1..04fdf84587 100644 --- a/indexer/services/socks/__tests__/lib/subscriptions.test.ts +++ b/indexer/services/socks/__tests__/lib/subscriptions.test.ts @@ -10,10 +10,12 @@ import { btcTicker, invalidChannel, invalidTicker } from '../constants'; import { axiosRequest } from '../../src/lib/axios'; import { AxiosSafeServerError, makeAxiosSafeServerError } from '@dydxprotocol-indexer/base'; import { BlockedError } from '../../src/lib/errors'; +import { isRestrictedCountry } from '@dydxprotocol-indexer/compliance'; jest.mock('ws'); jest.mock('../../src/helpers/wss'); jest.mock('../../src/lib/axios'); +jest.mock('@dydxprotocol-indexer/compliance'); describe('Subscriptions', () => { let subscriptions: Subscriptions; @@ -56,6 +58,8 @@ describe('Subscriptions', () => { [Channel.V4_TRADES]: ['/v4/trades/perpetualMarket/.+'], }; const initialMessage: Object = { a: 'b' }; + const restrictedCountry: string = 'US'; + const nonRestrictedCountry: string = 'AR'; beforeAll(async () => { await dbHelpers.migrate(); @@ -79,6 +83,9 @@ describe('Subscriptions', () => { axiosRequestMock = (axiosRequest as jest.Mock); axiosRequestMock.mockClear(); axiosRequestMock.mockImplementation(() => (JSON.stringify(initialMessage))); + (isRestrictedCountry as jest.Mock).mockImplementation((country: string): boolean => { + return country === restrictedCountry; + }); }); describe('subscribe', () => { @@ -98,6 +105,8 @@ describe('Subscriptions', () => { connectionId, initialMsgId, id, + false, + nonRestrictedCountry, ); expect(sendMessageStringMock).toHaveBeenCalledTimes(1); @@ -140,6 +149,8 @@ describe('Subscriptions', () => { connectionId, initialMsgId, id, + false, + nonRestrictedCountry, ); expect(sendMessageMock).toHaveBeenCalledTimes(1); @@ -167,6 +178,8 @@ describe('Subscriptions', () => { connectionId, initialMsgId, defaultId, + false, + nonRestrictedCountry, ); }, ).rejects.toEqual(new Error(`Invalid channel: ${invalidChannel}`)); @@ -180,6 +193,8 @@ describe('Subscriptions', () => { connectionId, initialMsgId, mockSubaccountId, + false, + nonRestrictedCountry, ); expect(sendMessageMock).toHaveBeenCalledTimes(1); @@ -201,6 +216,8 @@ describe('Subscriptions', () => { connectionId, initialMsgId, mockSubaccountId, + false, + nonRestrictedCountry, ); expect(sendMessageMock).toHaveBeenCalledTimes(1); @@ -235,6 +252,33 @@ describe('Subscriptions', () => { connectionId, initialMsgId, mockSubaccountId, + false, + nonRestrictedCountry, + ); + + expect(sendMessageMock).toHaveBeenCalledTimes(1); + expect(sendMessageMock).toHaveBeenCalledWith( + mockWs, + connectionId, + expect.objectContaining({ + connection_id: connectionId, + type: 'error', + message: expectedError.message, + })); + expect(subscriptions.subscriptions[Channel.V4_ACCOUNTS]).toBeUndefined(); + expect(subscriptions.subscriptionLists[connectionId]).toBeUndefined(); + }); + + it('sends blocked error if subscribing to subaccount from restricted country', async () => { + const expectedError: BlockedError = new BlockedError(); + await subscriptions.subscribe( + mockWs, + Channel.V4_ACCOUNTS, + connectionId, + initialMsgId, + mockSubaccountId, + false, + restrictedCountry, ); expect(sendMessageMock).toHaveBeenCalledTimes(1); @@ -260,6 +304,8 @@ describe('Subscriptions', () => { connectionId, initialMsgId, mockSubaccountId, + false, + nonRestrictedCountry, ); expect(sendMessageStringMock).toHaveBeenCalledTimes(1); @@ -295,6 +341,8 @@ describe('Subscriptions', () => { connectionId, initialMsgId, id, + false, + nonRestrictedCountry, ); subscriptions.unsubscribe( connectionId, @@ -313,6 +361,8 @@ describe('Subscriptions', () => { connectionId, initialMsgId, mockSubaccountId, + false, + nonRestrictedCountry, ); subscriptions.unsubscribe( connectionId, @@ -335,6 +385,8 @@ describe('Subscriptions', () => { connectionId, initialMsgId, validIds[channel], + false, + nonRestrictedCountry, ); })); diff --git a/indexer/services/socks/__tests__/websocket/index.test.ts b/indexer/services/socks/__tests__/websocket/index.test.ts index 4270f0939b..06dde78ca0 100644 --- a/indexer/services/socks/__tests__/websocket/index.test.ts +++ b/indexer/services/socks/__tests__/websocket/index.test.ts @@ -15,7 +15,7 @@ import { import { InvalidMessageHandler } from '../../src/lib/invalid-message'; import { PingHandler } from '../../src/lib/ping'; import config from '../../src/config'; -import { isRestrictedCountryHeaders } from '@dydxprotocol-indexer/compliance'; +import { isRestrictedCountryHeaders, COUNTRY_HEADER_KEY } from '@dydxprotocol-indexer/compliance'; jest.mock('uuid'); jest.mock('../../src/helpers/wss'); @@ -38,6 +38,7 @@ describe('Index', () => { const connectionId: string = 'conId'; const defaultGeoblockingEnabled: boolean = config.INDEXER_LEVEL_GEOBLOCKING_ENABLED; + const countryCode: string = 'AR'; beforeAll(() => { jest.useFakeTimers(); @@ -142,7 +143,9 @@ describe('Index', () => { beforeEach(() => { // Connect to the index before starting each test. (v4 as unknown as jest.Mock).mockReturnValueOnce(connectionId); - mockConnect(websocket, new IncomingMessage(new Socket())); + const incomingMessage: IncomingMessage = new IncomingMessage(new Socket()); + incomingMessage.headers[COUNTRY_HEADER_KEY] = countryCode; + mockConnect(websocket, incomingMessage); }); describe('message', () => { @@ -257,6 +260,7 @@ describe('Index', () => { index.connections[connectionId].messageId, id, isBatched, + countryCode, ); }); diff --git a/indexer/services/socks/src/lib/subscription.ts b/indexer/services/socks/src/lib/subscription.ts index 71a8a02ecb..02aaff590a 100644 --- a/indexer/services/socks/src/lib/subscription.ts +++ b/indexer/services/socks/src/lib/subscription.ts @@ -3,6 +3,7 @@ import { logger, stats, } from '@dydxprotocol-indexer/base'; +import { isRestrictedCountry } from '@dydxprotocol-indexer/compliance'; import { CandleResolution, perpetualMarketRefresher } from '@dydxprotocol-indexer/postgres'; import WebSocket from 'ws'; @@ -77,6 +78,7 @@ export class Subscriptions { messageId: number, id?: string, batched?: boolean, + country?: string, ): Promise { if (this.forwardMessage === undefined) { throw new Error('Unexpected error, subscription object is uninitialized.'); @@ -129,7 +131,7 @@ export class Subscriptions { let initialResponse: string; const startGetInitialResponse: number = Date.now(); try { - initialResponse = await this.getInitialResponsesForChannels(channel, id); + initialResponse = await this.getInitialResponsesForChannels(channel, id, country); } catch (error) { logger.info({ at: 'Subscription#subscribe', @@ -481,11 +483,21 @@ export class Subscriptions { } } - private async getInitialResponseForSubaccountSubscription(id?: string): Promise { + private async getInitialResponseForSubaccountSubscription( + id?: string, + country?: string, + ): Promise { if (id === undefined) { throw new Error('Invalid undefined id'); } + // TODO(IND-508): Change this to match technical spec for persistent geo-blocking. This may + // either have to replicate any blocking logic added on comlink, or re-direct to comlink to + // determine if subscribing to a specific subaccount is blocked. + if (country !== undefined && isRestrictedCountry(country)) { + throw new BlockedError(); + } + try { const { address, @@ -567,9 +579,13 @@ export class Subscriptions { * @param id Id fo the subscription to get the initial response for. * @returns The initial response for the channel. */ - private async getInitialResponsesForChannels(channel: Channel, id?: string): Promise { + private async getInitialResponsesForChannels( + channel: Channel, + id?: string, + country?: string, + ): Promise { if (channel === Channel.V4_ACCOUNTS) { - return this.getInitialResponseForSubaccountSubscription(id); + return this.getInitialResponseForSubaccountSubscription(id, country); } const endpoint: string | undefined = this.getInitialEndpointForSubscription(channel, id); // If no endpoint exists, return an empty initial response. diff --git a/indexer/services/socks/src/types.ts b/indexer/services/socks/src/types.ts index c4ca479268..bb49eda969 100644 --- a/indexer/services/socks/src/types.ts +++ b/indexer/services/socks/src/types.ts @@ -119,6 +119,7 @@ export interface Connection { messageId: number; heartbeat?: NodeJS.Timeout; disconnect?: NodeJS.Timeout; + countryCode?: string; } export interface MessageToForward { diff --git a/indexer/services/socks/src/websocket/index.ts b/indexer/services/socks/src/websocket/index.ts index 29f0fdd8b0..944e4243b9 100644 --- a/indexer/services/socks/src/websocket/index.ts +++ b/indexer/services/socks/src/websocket/index.ts @@ -109,6 +109,7 @@ export class Index { this.connections[connectionId] = { ws, messageId: 0, + countryCode: this.countryRestrictor.getCountry(req), }; const numConcurrentConnections: number = Object.keys(this.connections).length; @@ -287,6 +288,7 @@ export class Index { this.connections[connectionId].messageId, subscribeMessage.id, subscribeMessage.batched, + this.connections[connectionId].countryCode, ).catch((error: Error) => logger.error({ at: 'Subscription#subscribe', message: `Subscribing threw error: ${error.message}`, diff --git a/indexer/services/socks/src/websocket/restrict-countries.ts b/indexer/services/socks/src/websocket/restrict-countries.ts index e64d060d98..f079c90660 100644 --- a/indexer/services/socks/src/websocket/restrict-countries.ts +++ b/indexer/services/socks/src/websocket/restrict-countries.ts @@ -13,4 +13,9 @@ export class CountryRestrictor { return false; } + + public getCountry(req: IncomingMessage): string | undefined { + const countryHeaders: CountryHeaders = req.headers as CountryHeaders; + return countryHeaders['cf-ipcountry']; + } } From ece92b4216ce61c6e28090124aae1c4f1144ec2f Mon Sep 17 00:00:00 2001 From: jayy04 <103467857+jayy04@users.noreply.github.com> Date: Tue, 19 Dec 2023 13:29:05 -0500 Subject: [PATCH 11/12] [CLOB-1043] replicate IsLiquidatable logic on daemon (#873) * [CLOB-1043] replicate IsLiquidatable logic on daemon * comments * comments * update to return error * [CLOB-1044] replicate funding settlement on liquidation daemon (#882) * [CLOB-1043] deprecate unused grpc query (#889) * [CLOB-1043] deprecate unused grpc query * fix lint * [CLOB-1046] populate negative tnc subaccounts in grpc request (#890) * [CLOB-1046] populate negative tnc subaccounts in grpc request * [CLOB-1047] populate subaccounts with open positions in grpc request (#892) * [CLOB-1047] populate subaccounts with open positions in grpc request * comments --- .../dydxprotocol/clob/query.rpc.Query.ts | 16 +- .../src/codegen/dydxprotocol/clob/query.ts | 192 ----- proto/dydxprotocol/clob/query.proto | 25 - protocol/daemons/flags/flags.go | 39 +- protocol/daemons/flags/flags_test.go | 8 +- .../daemons/liquidation/client/client_test.go | 183 ----- .../daemons/liquidation/client/grpc_helper.go | 59 +- .../liquidation/client/grpc_helper_test.go | 192 ++--- .../liquidation/client/sub_task_runner.go | 306 ++++++- .../client/sub_task_runner_test.go | 735 +++++++++++++++++ protocol/daemons/server/liquidation.go | 3 + protocol/daemons/server/liquidation_test.go | 70 +- .../liquidations/daemon_liquidation_info.go | 8 +- .../daemon_liquidation_info_test.go | 119 +-- protocol/lib/collections.go | 18 + protocol/lib/collections_test.go | 52 ++ protocol/lib/metrics/constants.go | 3 + protocol/mocks/QueryClient.go | 30 - protocol/testutil/constants/perpetuals.go | 1 + protocol/testutil/constants/subaccounts.go | 36 +- ...grpc_query_are_subaccounts_liquidatable.go | 40 - ...query_are_subaccounts_liquidatable_test.go | 166 ---- protocol/x/clob/keeper/liquidations.go | 21 +- protocol/x/clob/types/query.pb.go | 772 ++---------------- protocol/x/perpetuals/keeper/perpetual.go | 69 +- protocol/x/subaccounts/keeper/subaccount.go | 47 +- .../x/subaccounts/types/expected_keepers.go | 7 + 27 files changed, 1567 insertions(+), 1650 deletions(-) create mode 100644 protocol/daemons/liquidation/client/sub_task_runner_test.go delete mode 100644 protocol/x/clob/keeper/grpc_query_are_subaccounts_liquidatable.go delete mode 100644 protocol/x/clob/keeper/grpc_query_are_subaccounts_liquidatable_test.go diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/clob/query.rpc.Query.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/clob/query.rpc.Query.ts index bdc2213141..643117c5ea 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/clob/query.rpc.Query.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/clob/query.rpc.Query.ts @@ -1,7 +1,7 @@ import { Rpc } from "../../helpers"; import * as _m0 from "protobufjs/minimal"; import { QueryClient, createProtobufRpcClient } from "@cosmjs/stargate"; -import { QueryGetClobPairRequest, QueryClobPairResponse, QueryAllClobPairRequest, QueryClobPairAllResponse, AreSubaccountsLiquidatableRequest, AreSubaccountsLiquidatableResponse, MevNodeToNodeCalculationRequest, MevNodeToNodeCalculationResponse, QueryEquityTierLimitConfigurationRequest, QueryEquityTierLimitConfigurationResponse, QueryBlockRateLimitConfigurationRequest, QueryBlockRateLimitConfigurationResponse, QueryLiquidationsConfigurationRequest, QueryLiquidationsConfigurationResponse } from "./query"; +import { QueryGetClobPairRequest, QueryClobPairResponse, QueryAllClobPairRequest, QueryClobPairAllResponse, MevNodeToNodeCalculationRequest, MevNodeToNodeCalculationResponse, QueryEquityTierLimitConfigurationRequest, QueryEquityTierLimitConfigurationResponse, QueryBlockRateLimitConfigurationRequest, QueryBlockRateLimitConfigurationResponse, QueryLiquidationsConfigurationRequest, QueryLiquidationsConfigurationResponse } from "./query"; /** Query defines the gRPC querier service. */ export interface Query { @@ -10,9 +10,6 @@ export interface Query { /** Queries a list of ClobPair items. */ clobPairAll(request?: QueryAllClobPairRequest): Promise; - /** Returns whether a subaccount is liquidatable. */ - - areSubaccountsLiquidatable(request: AreSubaccountsLiquidatableRequest): Promise; /** Runs the MEV node <> node calculation with the provided parameters. */ mevNodeToNodeCalculation(request: MevNodeToNodeCalculationRequest): Promise; @@ -33,7 +30,6 @@ export class QueryClientImpl implements Query { this.rpc = rpc; this.clobPair = this.clobPair.bind(this); this.clobPairAll = this.clobPairAll.bind(this); - this.areSubaccountsLiquidatable = this.areSubaccountsLiquidatable.bind(this); this.mevNodeToNodeCalculation = this.mevNodeToNodeCalculation.bind(this); this.equityTierLimitConfiguration = this.equityTierLimitConfiguration.bind(this); this.blockRateLimitConfiguration = this.blockRateLimitConfiguration.bind(this); @@ -54,12 +50,6 @@ export class QueryClientImpl implements Query { return promise.then(data => QueryClobPairAllResponse.decode(new _m0.Reader(data))); } - areSubaccountsLiquidatable(request: AreSubaccountsLiquidatableRequest): Promise { - const data = AreSubaccountsLiquidatableRequest.encode(request).finish(); - const promise = this.rpc.request("dydxprotocol.clob.Query", "AreSubaccountsLiquidatable", data); - return promise.then(data => AreSubaccountsLiquidatableResponse.decode(new _m0.Reader(data))); - } - mevNodeToNodeCalculation(request: MevNodeToNodeCalculationRequest): Promise { const data = MevNodeToNodeCalculationRequest.encode(request).finish(); const promise = this.rpc.request("dydxprotocol.clob.Query", "MevNodeToNodeCalculation", data); @@ -97,10 +87,6 @@ export const createRpcQueryExtension = (base: QueryClient) => { return queryService.clobPairAll(request); }, - areSubaccountsLiquidatable(request: AreSubaccountsLiquidatableRequest): Promise { - return queryService.areSubaccountsLiquidatable(request); - }, - mevNodeToNodeCalculation(request: MevNodeToNodeCalculationRequest): Promise { return queryService.mevNodeToNodeCalculation(request); }, diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/clob/query.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/clob/query.ts index f2b798bc09..f0ef3987b8 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/clob/query.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/clob/query.ts @@ -1,5 +1,4 @@ import { PageRequest, PageRequestSDKType, PageResponse, PageResponseSDKType } from "../../cosmos/base/query/v1beta1/pagination"; -import { SubaccountId, SubaccountIdSDKType } from "../subaccounts/subaccount"; import { ValidatorMevMatches, ValidatorMevMatchesSDKType, MevNodeToNodeMetrics, MevNodeToNodeMetricsSDKType } from "./mev"; import { ClobPair, ClobPairSDKType } from "./clob_pair"; import { EquityTierLimitConfiguration, EquityTierLimitConfigurationSDKType } from "./equity_tier_limit_config"; @@ -51,52 +50,6 @@ export interface QueryClobPairAllResponseSDKType { clob_pair: ClobPairSDKType[]; pagination?: PageResponseSDKType; } -/** - * AreSubaccountsLiquidatableRequest is a request message used to check whether - * the given subaccounts are liquidatable. - * The subaccount ids should not contain duplicates. - */ - -export interface AreSubaccountsLiquidatableRequest { - subaccountIds: SubaccountId[]; -} -/** - * AreSubaccountsLiquidatableRequest is a request message used to check whether - * the given subaccounts are liquidatable. - * The subaccount ids should not contain duplicates. - */ - -export interface AreSubaccountsLiquidatableRequestSDKType { - subaccount_ids: SubaccountIdSDKType[]; -} -/** - * AreSubaccountsLiquidatableResponse is a response message that contains the - * liquidation status for each subaccount. - */ - -export interface AreSubaccountsLiquidatableResponse { - results: AreSubaccountsLiquidatableResponse_Result[]; -} -/** - * AreSubaccountsLiquidatableResponse is a response message that contains the - * liquidation status for each subaccount. - */ - -export interface AreSubaccountsLiquidatableResponseSDKType { - results: AreSubaccountsLiquidatableResponse_ResultSDKType[]; -} -/** Result returns whether a subaccount should be liquidated. */ - -export interface AreSubaccountsLiquidatableResponse_Result { - subaccountId?: SubaccountId; - isLiquidatable: boolean; -} -/** Result returns whether a subaccount should be liquidated. */ - -export interface AreSubaccountsLiquidatableResponse_ResultSDKType { - subaccount_id?: SubaccountIdSDKType; - is_liquidatable: boolean; -} /** * MevNodeToNodeCalculationRequest is a request message used to run the * MEV node <> node calculation. @@ -436,151 +389,6 @@ export const QueryClobPairAllResponse = { }; -function createBaseAreSubaccountsLiquidatableRequest(): AreSubaccountsLiquidatableRequest { - return { - subaccountIds: [] - }; -} - -export const AreSubaccountsLiquidatableRequest = { - encode(message: AreSubaccountsLiquidatableRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - for (const v of message.subaccountIds) { - SubaccountId.encode(v!, writer.uint32(10).fork()).ldelim(); - } - - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): AreSubaccountsLiquidatableRequest { - const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseAreSubaccountsLiquidatableRequest(); - - while (reader.pos < end) { - const tag = reader.uint32(); - - switch (tag >>> 3) { - case 1: - message.subaccountIds.push(SubaccountId.decode(reader, reader.uint32())); - break; - - default: - reader.skipType(tag & 7); - break; - } - } - - return message; - }, - - fromPartial(object: DeepPartial): AreSubaccountsLiquidatableRequest { - const message = createBaseAreSubaccountsLiquidatableRequest(); - message.subaccountIds = object.subaccountIds?.map(e => SubaccountId.fromPartial(e)) || []; - return message; - } - -}; - -function createBaseAreSubaccountsLiquidatableResponse(): AreSubaccountsLiquidatableResponse { - return { - results: [] - }; -} - -export const AreSubaccountsLiquidatableResponse = { - encode(message: AreSubaccountsLiquidatableResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - for (const v of message.results) { - AreSubaccountsLiquidatableResponse_Result.encode(v!, writer.uint32(10).fork()).ldelim(); - } - - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): AreSubaccountsLiquidatableResponse { - const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseAreSubaccountsLiquidatableResponse(); - - while (reader.pos < end) { - const tag = reader.uint32(); - - switch (tag >>> 3) { - case 1: - message.results.push(AreSubaccountsLiquidatableResponse_Result.decode(reader, reader.uint32())); - break; - - default: - reader.skipType(tag & 7); - break; - } - } - - return message; - }, - - fromPartial(object: DeepPartial): AreSubaccountsLiquidatableResponse { - const message = createBaseAreSubaccountsLiquidatableResponse(); - message.results = object.results?.map(e => AreSubaccountsLiquidatableResponse_Result.fromPartial(e)) || []; - return message; - } - -}; - -function createBaseAreSubaccountsLiquidatableResponse_Result(): AreSubaccountsLiquidatableResponse_Result { - return { - subaccountId: undefined, - isLiquidatable: false - }; -} - -export const AreSubaccountsLiquidatableResponse_Result = { - encode(message: AreSubaccountsLiquidatableResponse_Result, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.subaccountId !== undefined) { - SubaccountId.encode(message.subaccountId, writer.uint32(10).fork()).ldelim(); - } - - if (message.isLiquidatable === true) { - writer.uint32(16).bool(message.isLiquidatable); - } - - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): AreSubaccountsLiquidatableResponse_Result { - const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseAreSubaccountsLiquidatableResponse_Result(); - - while (reader.pos < end) { - const tag = reader.uint32(); - - switch (tag >>> 3) { - case 1: - message.subaccountId = SubaccountId.decode(reader, reader.uint32()); - break; - - case 2: - message.isLiquidatable = reader.bool(); - break; - - default: - reader.skipType(tag & 7); - break; - } - } - - return message; - }, - - fromPartial(object: DeepPartial): AreSubaccountsLiquidatableResponse_Result { - const message = createBaseAreSubaccountsLiquidatableResponse_Result(); - message.subaccountId = object.subaccountId !== undefined && object.subaccountId !== null ? SubaccountId.fromPartial(object.subaccountId) : undefined; - message.isLiquidatable = object.isLiquidatable ?? false; - return message; - } - -}; - function createBaseMevNodeToNodeCalculationRequest(): MevNodeToNodeCalculationRequest { return { blockProposerMatches: undefined, diff --git a/proto/dydxprotocol/clob/query.proto b/proto/dydxprotocol/clob/query.proto index 9193122953..3fc37479c0 100644 --- a/proto/dydxprotocol/clob/query.proto +++ b/proto/dydxprotocol/clob/query.proto @@ -9,7 +9,6 @@ import "dydxprotocol/clob/clob_pair.proto"; import "dydxprotocol/clob/equity_tier_limit_config.proto"; import "dydxprotocol/clob/liquidations_config.proto"; import "dydxprotocol/clob/mev.proto"; -import "dydxprotocol/subaccounts/subaccount.proto"; option go_package = "github.com/dydxprotocol/v4-chain/protocol/x/clob/types"; @@ -25,10 +24,6 @@ service Query { option (google.api.http).get = "/dydxprotocol/clob/clob_pair"; } - // Returns whether a subaccount is liquidatable. - rpc AreSubaccountsLiquidatable(AreSubaccountsLiquidatableRequest) - returns (AreSubaccountsLiquidatableResponse); - // Runs the MEV node <> node calculation with the provided parameters. rpc MevNodeToNodeCalculation(MevNodeToNodeCalculationRequest) returns (MevNodeToNodeCalculationResponse) { @@ -76,26 +71,6 @@ message QueryClobPairAllResponse { cosmos.base.query.v1beta1.PageResponse pagination = 2; } -// AreSubaccountsLiquidatableRequest is a request message used to check whether -// the given subaccounts are liquidatable. -// The subaccount ids should not contain duplicates. -message AreSubaccountsLiquidatableRequest { - repeated dydxprotocol.subaccounts.SubaccountId subaccount_ids = 1 - [ (gogoproto.nullable) = false ]; -} - -// AreSubaccountsLiquidatableResponse is a response message that contains the -// liquidation status for each subaccount. -message AreSubaccountsLiquidatableResponse { - // Result returns whether a subaccount should be liquidated. - message Result { - dydxprotocol.subaccounts.SubaccountId subaccount_id = 1 - [ (gogoproto.nullable) = false ]; - bool is_liquidatable = 2; - } - repeated Result results = 1 [ (gogoproto.nullable) = false ]; -} - // MevNodeToNodeCalculationRequest is a request message used to run the // MEV node <> node calculation. message MevNodeToNodeCalculationRequest { diff --git a/protocol/daemons/flags/flags.go b/protocol/daemons/flags/flags.go index 6fbbcd7c3c..3bae6c44e6 100644 --- a/protocol/daemons/flags/flags.go +++ b/protocol/daemons/flags/flags.go @@ -20,10 +20,9 @@ const ( FlagBridgeDaemonLoopDelayMs = "bridge-daemon-loop-delay-ms" FlagBridgeDaemonEthRpcEndpoint = "bridge-daemon-eth-rpc-endpoint" - FlagLiquidationDaemonEnabled = "liquidation-daemon-enabled" - FlagLiquidationDaemonLoopDelayMs = "liquidation-daemon-loop-delay-ms" - FlagLiquidationDaemonSubaccountPageLimit = "liquidation-daemon-subaccount-page-limit" - FlagLiquidationDaemonRequestChunkSize = "liquidation-daemon-request-chunk-size" + FlagLiquidationDaemonEnabled = "liquidation-daemon-enabled" + FlagLiquidationDaemonLoopDelayMs = "liquidation-daemon-loop-delay-ms" + FlagLiquidationDaemonQueryPageLimit = "liquidation-daemon-query-page-limit" ) // Shared flags contains configuration flags shared by all daemons. @@ -52,9 +51,8 @@ type LiquidationFlags struct { Enabled bool // LoopDelayMs configures the update frequency of the liquidation daemon. LoopDelayMs uint32 - // SubaccountPageLimit configures the pagination limit for fetching subaccounts. - SubaccountPageLimit uint64 - RequestChunkSize uint64 + // QueryPageLimit configures the pagination limit for fetching subaccounts. + QueryPageLimit uint64 } // PriceFlags contains configuration flags for the Price Daemon. @@ -90,10 +88,9 @@ func GetDefaultDaemonFlags() DaemonFlags { EthRpcEndpoint: "", }, Liquidation: LiquidationFlags{ - Enabled: true, - LoopDelayMs: 1_600, - SubaccountPageLimit: 1_000, - RequestChunkSize: 50, + Enabled: true, + LoopDelayMs: 1_600, + QueryPageLimit: 1_000, }, Price: PriceFlags{ Enabled: true, @@ -160,14 +157,9 @@ func AddDaemonFlagsToCmd( "Delay in milliseconds between running the Liquidation Daemon task loop.", ) cmd.Flags().Uint64( - FlagLiquidationDaemonSubaccountPageLimit, - df.Liquidation.SubaccountPageLimit, - "Limit on the number of subaccounts to fetch per query in the Liquidation Daemon task loop.", - ) - cmd.Flags().Uint64( - FlagLiquidationDaemonRequestChunkSize, - df.Liquidation.RequestChunkSize, - "Limit on the number of subaccounts per collateralization check in the Liquidation Daemon task loop.", + FlagLiquidationDaemonQueryPageLimit, + df.Liquidation.QueryPageLimit, + "Limit on the number of items to fetch per query in the Liquidation Daemon task loop.", ) // Price Daemon. @@ -235,14 +227,9 @@ func GetDaemonFlagValuesFromOptions( result.Liquidation.LoopDelayMs = v } } - if option := appOpts.Get(FlagLiquidationDaemonSubaccountPageLimit); option != nil { - if v, err := cast.ToUint64E(option); err == nil { - result.Liquidation.SubaccountPageLimit = v - } - } - if option := appOpts.Get(FlagLiquidationDaemonRequestChunkSize); option != nil { + if option := appOpts.Get(FlagLiquidationDaemonQueryPageLimit); option != nil { if v, err := cast.ToUint64E(option); err == nil { - result.Liquidation.RequestChunkSize = v + result.Liquidation.QueryPageLimit = v } } diff --git a/protocol/daemons/flags/flags_test.go b/protocol/daemons/flags/flags_test.go index 04191032f6..e94a055d45 100644 --- a/protocol/daemons/flags/flags_test.go +++ b/protocol/daemons/flags/flags_test.go @@ -25,7 +25,7 @@ func TestAddDaemonFlagsToCmd(t *testing.T) { flags.FlagLiquidationDaemonEnabled, flags.FlagLiquidationDaemonLoopDelayMs, - flags.FlagLiquidationDaemonSubaccountPageLimit, + flags.FlagLiquidationDaemonQueryPageLimit, flags.FlagPriceDaemonEnabled, flags.FlagPriceDaemonLoopDelayMs, @@ -52,8 +52,7 @@ func TestGetDaemonFlagValuesFromOptions_Custom(t *testing.T) { optsMap[flags.FlagLiquidationDaemonEnabled] = true optsMap[flags.FlagLiquidationDaemonLoopDelayMs] = uint32(2222) - optsMap[flags.FlagLiquidationDaemonSubaccountPageLimit] = uint64(3333) - optsMap[flags.FlagLiquidationDaemonRequestChunkSize] = uint64(4444) + optsMap[flags.FlagLiquidationDaemonQueryPageLimit] = uint64(3333) optsMap[flags.FlagPriceDaemonEnabled] = true optsMap[flags.FlagPriceDaemonLoopDelayMs] = uint32(4444) @@ -83,8 +82,7 @@ func TestGetDaemonFlagValuesFromOptions_Custom(t *testing.T) { // Liquidation Daemon. require.Equal(t, optsMap[flags.FlagLiquidationDaemonEnabled], r.Liquidation.Enabled) require.Equal(t, optsMap[flags.FlagLiquidationDaemonLoopDelayMs], r.Liquidation.LoopDelayMs) - require.Equal(t, optsMap[flags.FlagLiquidationDaemonSubaccountPageLimit], r.Liquidation.SubaccountPageLimit) - require.Equal(t, optsMap[flags.FlagLiquidationDaemonRequestChunkSize], r.Liquidation.RequestChunkSize) + require.Equal(t, optsMap[flags.FlagLiquidationDaemonQueryPageLimit], r.Liquidation.QueryPageLimit) // Price Daemon. require.Equal(t, optsMap[flags.FlagPriceDaemonEnabled], r.Price.Enabled) diff --git a/protocol/daemons/liquidation/client/client_test.go b/protocol/daemons/liquidation/client/client_test.go index 81b4943558..d925852287 100644 --- a/protocol/daemons/liquidation/client/client_test.go +++ b/protocol/daemons/liquidation/client/client_test.go @@ -7,20 +7,14 @@ import ( "testing" "github.com/cometbft/cometbft/libs/log" - "github.com/cosmos/cosmos-sdk/types/query" appflags "github.com/dydxprotocol/v4-chain/protocol/app/flags" d_constants "github.com/dydxprotocol/v4-chain/protocol/daemons/constants" "github.com/dydxprotocol/v4-chain/protocol/daemons/flags" - "github.com/dydxprotocol/v4-chain/protocol/daemons/liquidation/api" "github.com/dydxprotocol/v4-chain/protocol/daemons/liquidation/client" "github.com/dydxprotocol/v4-chain/protocol/mocks" "github.com/dydxprotocol/v4-chain/protocol/testutil/appoptions" - "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" daemontestutils "github.com/dydxprotocol/v4-chain/protocol/testutil/daemons" "github.com/dydxprotocol/v4-chain/protocol/testutil/grpc" - clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" - satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) @@ -70,183 +64,6 @@ func TestStart_UnixSocketConnectionFails(t *testing.T) { mockGrpcClient.AssertNumberOfCalls(t, "CloseConnection", 1) } -func TestRunLiquidationDaemonTaskLoop(t *testing.T) { - df := flags.GetDefaultDaemonFlags() - tests := map[string]struct { - // mocks - setupMocks func(ctx context.Context, mck *mocks.QueryClient) - - // expectations - expectedLiquidatableSubaccountIds []satypes.SubaccountId - expectedError error - }{ - "Success": { - setupMocks: func(ctx context.Context, mck *mocks.QueryClient) { - req := &satypes.QueryAllSubaccountRequest{ - Pagination: &query.PageRequest{ - Limit: df.Liquidation.SubaccountPageLimit, - }, - } - response := &satypes.QuerySubaccountAllResponse{ - Subaccount: []satypes.Subaccount{ - constants.Carl_Num0_1BTC_Short, - constants.Dave_Num0_1BTC_Long_50000USD, - }, - } - mck.On("SubaccountAll", ctx, req).Return(response, nil) - - req2 := &clobtypes.AreSubaccountsLiquidatableRequest{ - SubaccountIds: []satypes.SubaccountId{ - constants.Carl_Num0, - constants.Dave_Num0, - }, - } - response2 := &clobtypes.AreSubaccountsLiquidatableResponse{ - Results: []clobtypes.AreSubaccountsLiquidatableResponse_Result{ - { - SubaccountId: constants.Carl_Num0, - IsLiquidatable: true, - }, - { - SubaccountId: constants.Dave_Num0, - IsLiquidatable: false, - }, - }, - } - mck.On("AreSubaccountsLiquidatable", ctx, req2).Return(response2, nil) - - req3 := &api.LiquidateSubaccountsRequest{ - LiquidatableSubaccountIds: []satypes.SubaccountId{ - constants.Carl_Num0, - }, - } - response3 := &api.LiquidateSubaccountsResponse{} - mck.On("LiquidateSubaccounts", ctx, req3).Return(response3, nil) - }, - }, - "Success - no open position": { - setupMocks: func(ctx context.Context, mck *mocks.QueryClient) { - req := &satypes.QueryAllSubaccountRequest{ - Pagination: &query.PageRequest{ - Limit: df.Liquidation.SubaccountPageLimit, - }, - } - response := &satypes.QuerySubaccountAllResponse{ - Subaccount: []satypes.Subaccount{ - constants.Carl_Num0_599USD, // no open positions - constants.Dave_Num0_599USD, // no open positions - }, - } - mck.On("SubaccountAll", ctx, req).Return(response, nil) - req2 := &api.LiquidateSubaccountsRequest{ - LiquidatableSubaccountIds: []satypes.SubaccountId{}, - } - response2 := &api.LiquidateSubaccountsResponse{} - mck.On("LiquidateSubaccounts", ctx, req2).Return(response2, nil) - }, - }, - "Success - no liquidatable subaccounts": { - setupMocks: func(ctx context.Context, mck *mocks.QueryClient) { - req := &satypes.QueryAllSubaccountRequest{ - Pagination: &query.PageRequest{ - Limit: df.Liquidation.SubaccountPageLimit, - }, - } - response := &satypes.QuerySubaccountAllResponse{ - Subaccount: []satypes.Subaccount{ - constants.Carl_Num0_1BTC_Short, - constants.Dave_Num0_1BTC_Long_50000USD, - }, - } - mck.On("SubaccountAll", ctx, req).Return(response, nil) - - req2 := &clobtypes.AreSubaccountsLiquidatableRequest{ - SubaccountIds: []satypes.SubaccountId{ - constants.Carl_Num0, - constants.Dave_Num0, - }, - } - response2 := &clobtypes.AreSubaccountsLiquidatableResponse{ - Results: []clobtypes.AreSubaccountsLiquidatableResponse_Result{ - { - SubaccountId: constants.Carl_Num0, - IsLiquidatable: false, - }, - { - SubaccountId: constants.Dave_Num0, - IsLiquidatable: false, - }, - }, - } - mck.On("AreSubaccountsLiquidatable", ctx, req2).Return(response2, nil) - req3 := &api.LiquidateSubaccountsRequest{ - LiquidatableSubaccountIds: []satypes.SubaccountId{}, - } - response3 := &api.LiquidateSubaccountsResponse{} - mck.On("LiquidateSubaccounts", ctx, req3).Return(response3, nil) - }, - }, - "Panics on error - SubaccountAll": { - setupMocks: func(ctx context.Context, mck *mocks.QueryClient) { - mck.On("SubaccountAll", mock.Anything, mock.Anything).Return(nil, errors.New("test error")) - }, - expectedError: errors.New("test error"), - }, - "Panics on error - AreSubaccountsLiquidatable": { - setupMocks: func(ctx context.Context, mck *mocks.QueryClient) { - mck.On("SubaccountAll", mock.Anything, mock.Anything).Return(&satypes.QuerySubaccountAllResponse{ - Subaccount: []satypes.Subaccount{ - constants.Carl_Num0_1BTC_Short, - }, - }, nil) - mck.On("AreSubaccountsLiquidatable", mock.Anything, mock.Anything).Return(nil, errors.New("test error")) - }, - expectedError: errors.New("test error"), - }, - "Panics on error - LiquidateSubaccounts": { - setupMocks: func(ctx context.Context, mck *mocks.QueryClient) { - mck.On("SubaccountAll", mock.Anything, mock.Anything).Return(&satypes.QuerySubaccountAllResponse{ - Subaccount: []satypes.Subaccount{ - constants.Carl_Num0_1BTC_Short, - }, - }, nil, - ) - mck.On("AreSubaccountsLiquidatable", mock.Anything, mock.Anything).Return( - &clobtypes.AreSubaccountsLiquidatableResponse{}, - nil, - ) - mck.On("LiquidateSubaccounts", mock.Anything, mock.Anything).Return(nil, errors.New("test error")) - }, - expectedError: errors.New("test error"), - }, - } - - for name, tc := range tests { - t.Run(name, func(t *testing.T) { - queryClientMock := &mocks.QueryClient{} - tc.setupMocks(grpc.Ctx, queryClientMock) - s := client.SubTaskRunnerImpl{} - - c := client.NewClient(log.NewNopLogger()) - c.SubaccountQueryClient = queryClientMock - c.ClobQueryClient = queryClientMock - c.LiquidationServiceClient = queryClientMock - - err := s.RunLiquidationDaemonTaskLoop( - grpc.Ctx, - c, - flags.GetDefaultDaemonFlags().Liquidation, - ) - if tc.expectedError != nil { - require.EqualError(t, err, tc.expectedError.Error()) - } else { - require.NoError(t, err) - queryClientMock.AssertExpectations(t) - } - }) - } -} - // FakeSubTaskRunner is a mock implementation of the SubTaskRunner interface for testing. type FakeSubTaskRunner struct { err error diff --git a/protocol/daemons/liquidation/client/grpc_helper.go b/protocol/daemons/liquidation/client/grpc_helper.go index 27a57b4c3a..92e49ba4bb 100644 --- a/protocol/daemons/liquidation/client/grpc_helper.go +++ b/protocol/daemons/liquidation/client/grpc_helper.go @@ -10,6 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/grpc" "github.com/cosmos/cosmos-sdk/types/query" "github.com/dydxprotocol/v4-chain/protocol/daemons/liquidation/api" + "github.com/dydxprotocol/v4-chain/protocol/lib" "github.com/dydxprotocol/v4-chain/protocol/lib/metrics" blocktimetypes "github.com/dydxprotocol/v4-chain/protocol/x/blocktime/types" clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" @@ -45,7 +46,6 @@ func (c *Client) GetPreviousBlockInfo( // GetAllPerpetuals queries gRPC server and returns a list of perpetuals. func (c *Client) GetAllPerpetuals( ctx context.Context, - blockHeight uint32, pageLimit uint64, ) ( perpetuals []perptypes.Perpetual, @@ -85,7 +85,6 @@ func (c *Client) GetAllPerpetuals( // GetAllLiquidityTiers queries gRPC server and returns a list of liquidityTiers. func (c *Client) GetAllLiquidityTiers( ctx context.Context, - blockHeight uint32, pageLimit uint64, ) ( liquidityTiers []perptypes.LiquidityTier, @@ -125,7 +124,6 @@ func (c *Client) GetAllLiquidityTiers( // GetAllMarketPrices queries gRPC server and returns a list of market prices. func (c *Client) GetAllMarketPrices( ctx context.Context, - blockHeight uint32, pageLimit uint64, ) ( marketPrices []pricestypes.MarketPrice, @@ -205,38 +203,14 @@ func (c *Client) GetAllSubaccounts( return subaccounts, nil } -// CheckCollateralizationForSubaccounts queries a gRPC server using `AreSubaccountsLiquidatable` -// and returns a list of collateralization statuses for the given list of subaccount ids. -func (c *Client) CheckCollateralizationForSubaccounts( - ctx context.Context, - subaccountIds []satypes.SubaccountId, -) ( - results []clobtypes.AreSubaccountsLiquidatableResponse_Result, - err error, -) { - defer telemetry.ModuleMeasureSince( - metrics.LiquidationDaemon, - time.Now(), - metrics.CheckCollateralizationForSubaccounts, - metrics.Latency, - ) - - query := &clobtypes.AreSubaccountsLiquidatableRequest{ - SubaccountIds: subaccountIds, - } - response, err := c.ClobQueryClient.AreSubaccountsLiquidatable(ctx, query) - if err != nil { - return nil, err - } - - return response.Results, nil -} - // SendLiquidatableSubaccountIds sends a list of unique and potentially liquidatable // subaccount ids to a gRPC server via `LiquidateSubaccounts`. func (c *Client) SendLiquidatableSubaccountIds( ctx context.Context, - subaccountIds []satypes.SubaccountId, + blockHeight uint32, + liquidatableSubaccountIds []satypes.SubaccountId, + negativeTncSubaccountIds []satypes.SubaccountId, + openPositionInfoMap map[uint32]*clobtypes.SubaccountOpenPositionInfo, ) error { defer telemetry.ModuleMeasureSince( metrics.LiquidationDaemon, @@ -247,13 +221,31 @@ func (c *Client) SendLiquidatableSubaccountIds( telemetry.ModuleSetGauge( metrics.LiquidationDaemon, - float32(len(subaccountIds)), + float32(len(liquidatableSubaccountIds)), metrics.LiquidatableSubaccountIds, metrics.Count, ) + telemetry.ModuleSetGauge( + metrics.LiquidationDaemon, + float32(len(negativeTncSubaccountIds)), + metrics.NegativeTncSubaccountIds, + metrics.Count, + ) + + // Convert the map to a slice. + // Note that sorting here is not strictly necessary but is done for safety and to avoid making + // any assumptions on the server side. + sortedPerpetualIds := lib.GetSortedKeys[lib.Sortable[uint32]](openPositionInfoMap) + subaccountOpenPositionInfo := make([]clobtypes.SubaccountOpenPositionInfo, 0) + for _, perpetualId := range sortedPerpetualIds { + subaccountOpenPositionInfo = append(subaccountOpenPositionInfo, *openPositionInfoMap[perpetualId]) + } request := &api.LiquidateSubaccountsRequest{ - LiquidatableSubaccountIds: subaccountIds, + BlockHeight: blockHeight, + LiquidatableSubaccountIds: liquidatableSubaccountIds, + NegativeTncSubaccountIds: negativeTncSubaccountIds, + SubaccountOpenPositionInfo: subaccountOpenPositionInfo, } if _, err := c.LiquidationServiceClient.LiquidateSubaccounts(ctx, request); err != nil { @@ -262,7 +254,6 @@ func (c *Client) SendLiquidatableSubaccountIds( return nil } -// nolint:unused func newContextWithQueryBlockHeight( ctx context.Context, blockHeight uint32, diff --git a/protocol/daemons/liquidation/client/grpc_helper_test.go b/protocol/daemons/liquidation/client/grpc_helper_test.go index 3707c3f45b..2e6f0ab041 100644 --- a/protocol/daemons/liquidation/client/grpc_helper_test.go +++ b/protocol/daemons/liquidation/client/grpc_helper_test.go @@ -93,7 +93,7 @@ func TestGetAllSubaccounts(t *testing.T) { setupMocks: func(ctx context.Context, mck *mocks.QueryClient) { req := &satypes.QueryAllSubaccountRequest{ Pagination: &query.PageRequest{ - Limit: df.Liquidation.SubaccountPageLimit, + Limit: df.Liquidation.QueryPageLimit, }, } response := &satypes.QuerySubaccountAllResponse{ @@ -113,7 +113,7 @@ func TestGetAllSubaccounts(t *testing.T) { setupMocks: func(ctx context.Context, mck *mocks.QueryClient) { req := &satypes.QueryAllSubaccountRequest{ Pagination: &query.PageRequest{ - Limit: df.Liquidation.SubaccountPageLimit, + Limit: df.Liquidation.QueryPageLimit, }, } nextKey := []byte("next key") @@ -129,7 +129,7 @@ func TestGetAllSubaccounts(t *testing.T) { req2 := &satypes.QueryAllSubaccountRequest{ Pagination: &query.PageRequest{ Key: nextKey, - Limit: df.Liquidation.SubaccountPageLimit, + Limit: df.Liquidation.QueryPageLimit, }, } response2 := &satypes.QuerySubaccountAllResponse{ @@ -148,7 +148,7 @@ func TestGetAllSubaccounts(t *testing.T) { setupMocks: func(ctx context.Context, mck *mocks.QueryClient) { req := &satypes.QueryAllSubaccountRequest{ Pagination: &query.PageRequest{ - Limit: df.Liquidation.SubaccountPageLimit, + Limit: df.Liquidation.QueryPageLimit, }, } mck.On("SubaccountAll", ctx, req).Return(nil, errors.New("test error")) @@ -166,7 +166,7 @@ func TestGetAllSubaccounts(t *testing.T) { daemon.SubaccountQueryClient = queryClientMock actual, err := daemon.GetAllSubaccounts( grpc.Ctx, - df.Liquidation.SubaccountPageLimit, + df.Liquidation.QueryPageLimit, ) if err != nil { require.EqualError(t, err, tc.expectedError.Error()) @@ -258,7 +258,6 @@ func TestGetAllPerpetuals(t *testing.T) { daemon.PerpetualsQueryClient = queryClientMock actual, err := daemon.GetAllPerpetuals( grpc.Ctx, - uint32(50), tc.limit, ) if err != nil { @@ -347,7 +346,6 @@ func TestGetAllLiquidityTiers(t *testing.T) { daemon.PerpetualsQueryClient = queryClientMock actual, err := daemon.GetAllLiquidityTiers( grpc.Ctx, - uint32(50), tc.limit, ) if err != nil { @@ -441,7 +439,6 @@ func TestGetAllMarketPrices(t *testing.T) { daemon.PricesQueryClient = queryClientMock actual, err := daemon.GetAllMarketPrices( grpc.Ctx, - uint32(50), tc.limit, ) if err != nil { @@ -453,160 +450,109 @@ func TestGetAllMarketPrices(t *testing.T) { } } -func TestCheckCollateralizationForSubaccounts(t *testing.T) { - tests := map[string]struct { - // mocks - setupMocks func( - ctx context.Context, - mck *mocks.QueryClient, - results []clobtypes.AreSubaccountsLiquidatableResponse_Result, - ) - subaccountIds []satypes.SubaccountId - - // expectations - expectedResults []clobtypes.AreSubaccountsLiquidatableResponse_Result - expectedError error - }{ - "Success": { - setupMocks: func( - ctx context.Context, - mck *mocks.QueryClient, - results []clobtypes.AreSubaccountsLiquidatableResponse_Result, - ) { - query := &clobtypes.AreSubaccountsLiquidatableRequest{ - SubaccountIds: []satypes.SubaccountId{ - constants.Alice_Num0, - constants.Bob_Num0, - }, - } - response := &clobtypes.AreSubaccountsLiquidatableResponse{ - Results: results, - } - mck.On("AreSubaccountsLiquidatable", ctx, query).Return(response, nil) - }, - subaccountIds: []satypes.SubaccountId{ - constants.Alice_Num0, - constants.Bob_Num0, - }, - expectedResults: []clobtypes.AreSubaccountsLiquidatableResponse_Result{ - { - SubaccountId: constants.Alice_Num0, - IsLiquidatable: true, - }, - { - SubaccountId: constants.Bob_Num0, - IsLiquidatable: false, - }, - }, - }, - "Success - Empty": { - setupMocks: func( - ctx context.Context, - mck *mocks.QueryClient, - results []clobtypes.AreSubaccountsLiquidatableResponse_Result, - ) { - query := &clobtypes.AreSubaccountsLiquidatableRequest{ - SubaccountIds: []satypes.SubaccountId{}, - } - response := &clobtypes.AreSubaccountsLiquidatableResponse{ - Results: results, - } - mck.On("AreSubaccountsLiquidatable", ctx, query).Return(response, nil) - }, - subaccountIds: []satypes.SubaccountId{}, - expectedResults: []clobtypes.AreSubaccountsLiquidatableResponse_Result{}, - }, - "Errors are propagated": { - setupMocks: func( - ctx context.Context, - mck *mocks.QueryClient, - results []clobtypes.AreSubaccountsLiquidatableResponse_Result, - ) { - query := &clobtypes.AreSubaccountsLiquidatableRequest{ - SubaccountIds: []satypes.SubaccountId{}, - } - mck.On("AreSubaccountsLiquidatable", ctx, query).Return(nil, errors.New("test error")) - }, - subaccountIds: []satypes.SubaccountId{}, - expectedResults: []clobtypes.AreSubaccountsLiquidatableResponse_Result{}, - expectedError: errors.New("test error"), - }, - } - - for name, tc := range tests { - t.Run(name, func(t *testing.T) { - queryClientMock := &mocks.QueryClient{} - tc.setupMocks(grpc.Ctx, queryClientMock, tc.expectedResults) - - daemon := client.NewClient(log.NewNopLogger()) - daemon.ClobQueryClient = queryClientMock - actual, err := daemon.CheckCollateralizationForSubaccounts( - grpc.Ctx, - tc.subaccountIds, - ) - - if err != nil { - require.EqualError(t, err, tc.expectedError.Error()) - } else { - require.Equal(t, tc.expectedResults, actual) - } - }) - } -} - func TestSendLiquidatableSubaccountIds(t *testing.T) { tests := map[string]struct { // mocks - setupMocks func(ctx context.Context, mck *mocks.QueryClient, ids []satypes.SubaccountId) - subaccountIds []satypes.SubaccountId + setupMocks func(context.Context, *mocks.QueryClient) + liquidatableSubaccountIds []satypes.SubaccountId + negativeTncSubaccountIds []satypes.SubaccountId + subaccountOpenPositionInfo map[uint32]*clobtypes.SubaccountOpenPositionInfo // expectations expectedError error }{ "Success": { - setupMocks: func(ctx context.Context, mck *mocks.QueryClient, ids []satypes.SubaccountId) { + setupMocks: func(ctx context.Context, mck *mocks.QueryClient) { req := &api.LiquidateSubaccountsRequest{ - LiquidatableSubaccountIds: ids, + BlockHeight: uint32(50), + LiquidatableSubaccountIds: []satypes.SubaccountId{constants.Alice_Num0, constants.Bob_Num0}, + NegativeTncSubaccountIds: []satypes.SubaccountId{constants.Carl_Num0, constants.Dave_Num0}, + SubaccountOpenPositionInfo: []clobtypes.SubaccountOpenPositionInfo{ + { + PerpetualId: 0, + SubaccountsWithLongPosition: []satypes.SubaccountId{ + constants.Alice_Num0, + constants.Carl_Num0, + }, + SubaccountsWithShortPosition: []satypes.SubaccountId{ + constants.Bob_Num0, + constants.Dave_Num0, + }, + }, + }, } response := &api.LiquidateSubaccountsResponse{} mck.On("LiquidateSubaccounts", ctx, req).Return(response, nil) }, - subaccountIds: []satypes.SubaccountId{ + liquidatableSubaccountIds: []satypes.SubaccountId{ constants.Alice_Num0, constants.Bob_Num0, }, + negativeTncSubaccountIds: []satypes.SubaccountId{ + constants.Carl_Num0, + constants.Dave_Num0, + }, + subaccountOpenPositionInfo: map[uint32]*clobtypes.SubaccountOpenPositionInfo{ + 0: { + PerpetualId: 0, + SubaccountsWithLongPosition: []satypes.SubaccountId{ + constants.Alice_Num0, + constants.Carl_Num0, + }, + SubaccountsWithShortPosition: []satypes.SubaccountId{ + constants.Bob_Num0, + constants.Dave_Num0, + }, + }, + }, }, "Success Empty": { - setupMocks: func(ctx context.Context, mck *mocks.QueryClient, ids []satypes.SubaccountId) { + setupMocks: func(ctx context.Context, mck *mocks.QueryClient) { req := &api.LiquidateSubaccountsRequest{ - LiquidatableSubaccountIds: ids, + BlockHeight: uint32(50), + LiquidatableSubaccountIds: []satypes.SubaccountId{}, + NegativeTncSubaccountIds: []satypes.SubaccountId{}, + SubaccountOpenPositionInfo: []clobtypes.SubaccountOpenPositionInfo{}, } response := &api.LiquidateSubaccountsResponse{} mck.On("LiquidateSubaccounts", ctx, req).Return(response, nil) }, - subaccountIds: []satypes.SubaccountId{}, + liquidatableSubaccountIds: []satypes.SubaccountId{}, + negativeTncSubaccountIds: []satypes.SubaccountId{}, + subaccountOpenPositionInfo: map[uint32]*clobtypes.SubaccountOpenPositionInfo{}, }, "Errors are propagated": { - setupMocks: func(ctx context.Context, mck *mocks.QueryClient, ids []satypes.SubaccountId) { + setupMocks: func(ctx context.Context, mck *mocks.QueryClient) { req := &api.LiquidateSubaccountsRequest{ - LiquidatableSubaccountIds: ids, + BlockHeight: uint32(50), + LiquidatableSubaccountIds: []satypes.SubaccountId{}, + NegativeTncSubaccountIds: []satypes.SubaccountId{}, + SubaccountOpenPositionInfo: []clobtypes.SubaccountOpenPositionInfo{}, } mck.On("LiquidateSubaccounts", ctx, req).Return(nil, errors.New("test error")) }, - subaccountIds: []satypes.SubaccountId{}, - expectedError: errors.New("test error"), + liquidatableSubaccountIds: []satypes.SubaccountId{}, + negativeTncSubaccountIds: []satypes.SubaccountId{}, + subaccountOpenPositionInfo: map[uint32]*clobtypes.SubaccountOpenPositionInfo{}, + expectedError: errors.New("test error"), }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { queryClientMock := &mocks.QueryClient{} - tc.setupMocks(grpc.Ctx, queryClientMock, tc.subaccountIds) + tc.setupMocks(grpc.Ctx, queryClientMock) daemon := client.NewClient(log.NewNopLogger()) daemon.LiquidationServiceClient = queryClientMock - err := daemon.SendLiquidatableSubaccountIds(grpc.Ctx, tc.subaccountIds) + err := daemon.SendLiquidatableSubaccountIds( + grpc.Ctx, + uint32(50), + tc.liquidatableSubaccountIds, + tc.negativeTncSubaccountIds, + tc.subaccountOpenPositionInfo, + ) require.Equal(t, tc.expectedError, err) }) } diff --git a/protocol/daemons/liquidation/client/sub_task_runner.go b/protocol/daemons/liquidation/client/sub_task_runner.go index e7ae323f79..e768db2819 100644 --- a/protocol/daemons/liquidation/client/sub_task_runner.go +++ b/protocol/daemons/liquidation/client/sub_task_runner.go @@ -2,12 +2,21 @@ package client import ( "context" + "math/big" "time" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/telemetry" "github.com/dydxprotocol/v4-chain/protocol/daemons/flags" "github.com/dydxprotocol/v4-chain/protocol/lib" "github.com/dydxprotocol/v4-chain/protocol/lib/metrics" + assetstypes "github.com/dydxprotocol/v4-chain/protocol/x/assets/types" + clobkeeper "github.com/dydxprotocol/v4-chain/protocol/x/clob/keeper" + clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" + perpkeeper "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/keeper" + perptypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types" + pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" + sakeeper "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/keeper" satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" ) @@ -40,24 +49,49 @@ func (s *SubTaskRunnerImpl) RunLiquidationDaemonTaskLoop( metrics.Latency, ) - // 1. Fetch all subaccounts from query service. - subaccounts, err := daemonClient.GetAllSubaccounts(ctx, liqFlags.SubaccountPageLimit) + lastCommittedBlockHeight, err := daemonClient.GetPreviousBlockInfo(ctx) if err != nil { return err } - // 2. Check collateralization statuses of subaccounts with at least one open position. - liquidatableSubaccountIds, err := daemonClient.GetLiquidatableSubaccountIds( + // 1. Fetch all information needed to calculate total net collateral and margin requirements. + subaccounts, + marketPrices, + perpetuals, + liquidityTiers, + err := daemonClient.FetchApplicationStateAtBlockHeight( ctx, + lastCommittedBlockHeight, liqFlags, + ) + if err != nil { + return err + } + + // 2. Check collateralization statuses of subaccounts with at least one open position. + liquidatableSubaccountIds, + negativeTncSubaccountIds, + err := daemonClient.GetLiquidatableSubaccountIds( subaccounts, + marketPrices, + perpetuals, + liquidityTiers, ) if err != nil { return err } + // Build a map of perpetual id to subaccounts with open positions in that perpetual. + subaccountOpenPositionInfo := daemonClient.GetSubaccountOpenPositionInfo(subaccounts) + // 3. Send the list of liquidatable subaccount ids to the daemon server. - err = daemonClient.SendLiquidatableSubaccountIds(ctx, liquidatableSubaccountIds) + err = daemonClient.SendLiquidatableSubaccountIds( + ctx, + lastCommittedBlockHeight, + liquidatableSubaccountIds, + negativeTncSubaccountIds, + subaccountOpenPositionInfo, + ) if err != nil { return err } @@ -65,14 +99,79 @@ func (s *SubTaskRunnerImpl) RunLiquidationDaemonTaskLoop( return nil } +// FetchApplicationStateAtBlockHeight queries a gRPC server and fetches the following information given a block height: +// - Last committed block height. +// - Subaccounts including their open positions. +// - Market prices. +// - Perpetuals. +// - Liquidity tiers. +func (c *Client) FetchApplicationStateAtBlockHeight( + ctx context.Context, + blockHeight uint32, + liqFlags flags.LiquidationFlags, +) ( + subaccounts []satypes.Subaccount, + marketPricesMap map[uint32]pricestypes.MarketPrice, + perpetualsMap map[uint32]perptypes.Perpetual, + liquidityTiersMap map[uint32]perptypes.LiquidityTier, + err error, +) { + defer telemetry.ModuleMeasureSince( + metrics.LiquidationDaemon, + time.Now(), + metrics.FetchApplicationStateAtBlockHeight, + metrics.Latency, + ) + + // Execute all queries at the given block height. + queryCtx := newContextWithQueryBlockHeight(ctx, blockHeight) + + // Subaccounts + subaccounts, err = c.GetAllSubaccounts(queryCtx, liqFlags.QueryPageLimit) + if err != nil { + return nil, nil, nil, nil, err + } + + // Market prices + marketPrices, err := c.GetAllMarketPrices(queryCtx, liqFlags.QueryPageLimit) + if err != nil { + return nil, nil, nil, nil, err + } + marketPricesMap = lib.UniqueSliceToMap(marketPrices, func(m pricestypes.MarketPrice) uint32 { + return m.Id + }) + + // Perpetuals + perpetuals, err := c.GetAllPerpetuals(queryCtx, liqFlags.QueryPageLimit) + if err != nil { + return nil, nil, nil, nil, err + } + perpetualsMap = lib.UniqueSliceToMap(perpetuals, func(p perptypes.Perpetual) uint32 { + return p.Params.Id + }) + + // Liquidity tiers + liquidityTiers, err := c.GetAllLiquidityTiers(queryCtx, liqFlags.QueryPageLimit) + if err != nil { + return nil, nil, nil, nil, err + } + liquidityTiersMap = lib.UniqueSliceToMap(liquidityTiers, func(l perptypes.LiquidityTier) uint32 { + return l.Id + }) + + return subaccounts, marketPricesMap, perpetualsMap, liquidityTiersMap, nil +} + // GetLiquidatableSubaccountIds verifies collateralization statuses of subaccounts with // at least one open position and returns a list of unique and potentially liquidatable subaccount ids. func (c *Client) GetLiquidatableSubaccountIds( - ctx context.Context, - liqFlags flags.LiquidationFlags, subaccounts []satypes.Subaccount, + marketPrices map[uint32]pricestypes.MarketPrice, + perpetuals map[uint32]perptypes.Perpetual, + liquidityTiers map[uint32]perptypes.LiquidityTier, ) ( liquidatableSubaccountIds []satypes.SubaccountId, + negativeTncSubaccountIds []satypes.SubaccountId, err error, ) { defer telemetry.ModuleMeasureSince( @@ -82,39 +181,192 @@ func (c *Client) GetLiquidatableSubaccountIds( metrics.Latency, ) - // Filter out subaccounts with no open positions. - subaccountsToCheck := make([]satypes.SubaccountId, 0) + liquidatableSubaccountIds = make([]satypes.SubaccountId, 0) + negativeTncSubaccountIds = make([]satypes.SubaccountId, 0) for _, subaccount := range subaccounts { - if len(subaccount.PerpetualPositions) > 0 { - subaccountsToCheck = append(subaccountsToCheck, *subaccount.Id) + // Skip subaccounts with no open positions. + if len(subaccount.PerpetualPositions) == 0 { + continue + } + + // Check if the subaccount is liquidatable. + isLiquidatable, hasNegativeTnc, err := c.CheckSubaccountCollateralization( + subaccount, + marketPrices, + perpetuals, + liquidityTiers, + ) + if err != nil { + c.logger.Error("Error checking collateralization status", "error", err) + return nil, nil, err + } + + if isLiquidatable { + liquidatableSubaccountIds = append(liquidatableSubaccountIds, *subaccount.Id) + } + if hasNegativeTnc { + negativeTncSubaccountIds = append(negativeTncSubaccountIds, *subaccount.Id) } } + return liquidatableSubaccountIds, negativeTncSubaccountIds, nil +} + +// GetSubaccountOpenPositionInfo iterates over the given subaccounts and returns a map of +// perpetual id to open position info. +func (c *Client) GetSubaccountOpenPositionInfo( + subaccounts []satypes.Subaccount, +) ( + subaccountOpenPositionInfo map[uint32]*clobtypes.SubaccountOpenPositionInfo, +) { + defer telemetry.ModuleMeasureSince( + metrics.LiquidationDaemon, + time.Now(), + metrics.GetSubaccountOpenPositionInfo, + metrics.Latency, + ) + + numSubaccountsWithOpenPositions := 0 + subaccountOpenPositionInfo = make(map[uint32]*clobtypes.SubaccountOpenPositionInfo) + for _, subaccount := range subaccounts { + // Skip subaccounts with no open positions. + if len(subaccount.PerpetualPositions) == 0 { + continue + } + + for _, perpetualPosition := range subaccount.PerpetualPositions { + openPositionInfo, ok := subaccountOpenPositionInfo[perpetualPosition.PerpetualId] + if !ok { + openPositionInfo = &clobtypes.SubaccountOpenPositionInfo{ + PerpetualId: perpetualPosition.PerpetualId, + SubaccountsWithLongPosition: make([]satypes.SubaccountId, 0), + SubaccountsWithShortPosition: make([]satypes.SubaccountId, 0), + } + subaccountOpenPositionInfo[perpetualPosition.PerpetualId] = openPositionInfo + } + + if perpetualPosition.GetIsLong() { + openPositionInfo.SubaccountsWithLongPosition = append( + openPositionInfo.SubaccountsWithLongPosition, + *subaccount.Id, + ) + } else { + openPositionInfo.SubaccountsWithShortPosition = append( + openPositionInfo.SubaccountsWithShortPosition, + *subaccount.Id, + ) + } + } + + numSubaccountsWithOpenPositions++ + } + telemetry.ModuleSetGauge( metrics.LiquidationDaemon, - float32(len(subaccountsToCheck)), + float32(numSubaccountsWithOpenPositions), metrics.SubaccountsWithOpenPositions, metrics.Count, ) - // Query the gRPC server in chunks of size `liqFlags.RequestChunkSize`. - liquidatableSubaccountIds = make([]satypes.SubaccountId, 0) - for start := 0; start < len(subaccountsToCheck); start += int(liqFlags.RequestChunkSize) { - end := lib.Min(start+int(liqFlags.RequestChunkSize), len(subaccountsToCheck)) + return subaccountOpenPositionInfo +} - results, err := c.CheckCollateralizationForSubaccounts( - ctx, - subaccountsToCheck[start:end], - ) - if err != nil { - return nil, err +// CheckSubaccountCollateralization performs the same collateralization check as the application +// using the provided market prices, perpetuals, and liquidity tiers. +// +// Note that current implementation assumes that the only asset is USDC and multi-collateral support +// is not yet implemented. +func (c *Client) CheckSubaccountCollateralization( + unsettledSubaccount satypes.Subaccount, + marketPrices map[uint32]pricestypes.MarketPrice, + perpetuals map[uint32]perptypes.Perpetual, + liquidityTiers map[uint32]perptypes.LiquidityTier, +) ( + isLiquidatable bool, + hasNegativeTnc bool, + err error, +) { + defer telemetry.ModuleMeasureSince( + metrics.LiquidationDaemon, + time.Now(), + metrics.CheckCollateralizationForSubaccounts, + metrics.Latency, + ) + + // Funding payments are lazily settled, so get the settled subaccount + // to ensure that the funding payments are included in the net collateral calculation. + settledSubaccount, _, err := sakeeper.GetSettledSubaccountWithPerpetuals( + unsettledSubaccount, + perpetuals, + ) + if err != nil { + return false, false, err + } + + bigTotalNetCollateral := big.NewInt(0) + bigTotalMaintenanceMargin := big.NewInt(0) + + // Calculate the net collateral and maintenance margin for each of the asset positions. + // Note that we only expect USDC before multi-collateral support is added. + for _, assetPosition := range settledSubaccount.AssetPositions { + if assetPosition.AssetId != assetstypes.AssetUsdc.Id { + return false, false, errorsmod.Wrapf( + assetstypes.ErrNotImplementedMulticollateral, + "Asset %d is not supported", + assetPosition.AssetId, + ) } + // Net collateral for USDC is the quantums of the position. + // Margin requirements for USDC are zero. + bigTotalNetCollateral.Add(bigTotalNetCollateral, assetPosition.GetBigQuantums()) + } - for _, result := range results { - if result.IsLiquidatable { - liquidatableSubaccountIds = append(liquidatableSubaccountIds, result.SubaccountId) - } + // Calculate the net collateral and maintenance margin for each of the perpetual positions. + for _, perpetualPosition := range settledSubaccount.PerpetualPositions { + perpetual, ok := perpetuals[perpetualPosition.PerpetualId] + if !ok { + return false, false, errorsmod.Wrapf( + perptypes.ErrPerpetualDoesNotExist, + "Perpetual not found for perpetual id %d", + perpetualPosition.PerpetualId, + ) + } + + marketPrice, ok := marketPrices[perpetual.Params.MarketId] + if !ok { + return false, false, errorsmod.Wrapf( + pricestypes.ErrMarketPriceDoesNotExist, + "MarketPrice not found for perpetual %+v", + perpetual, + ) + } + + bigQuantums := perpetualPosition.GetBigQuantums() + + // Get the net collateral for the position. + bigNetCollateralQuoteQuantums := perpkeeper.GetNetNotionalInQuoteQuantums(perpetual, marketPrice, bigQuantums) + bigTotalNetCollateral.Add(bigTotalNetCollateral, bigNetCollateralQuoteQuantums) + + liquidityTier, ok := liquidityTiers[perpetual.Params.LiquidityTier] + if !ok { + return false, false, errorsmod.Wrapf( + perptypes.ErrLiquidityTierDoesNotExist, + "LiquidityTier not found for perpetual %+v", + perpetual, + ) } + + // Get the maintenance margin requirement for the position. + _, bigMaintenanceMarginQuoteQuantums := perpkeeper.GetMarginRequirementsInQuoteQuantums( + perpetual, + marketPrice, + liquidityTier, + bigQuantums, + ) + bigTotalMaintenanceMargin.Add(bigTotalMaintenanceMargin, bigMaintenanceMarginQuoteQuantums) } - return liquidatableSubaccountIds, nil + + return clobkeeper.CanLiquidateSubaccount(bigTotalNetCollateral, bigTotalMaintenanceMargin), + bigTotalNetCollateral.Sign() == -1, + nil } diff --git a/protocol/daemons/liquidation/client/sub_task_runner_test.go b/protocol/daemons/liquidation/client/sub_task_runner_test.go new file mode 100644 index 0000000000..2914e3389d --- /dev/null +++ b/protocol/daemons/liquidation/client/sub_task_runner_test.go @@ -0,0 +1,735 @@ +package client_test + +import ( + "context" + "testing" + + "github.com/cometbft/cometbft/libs/log" + "github.com/dydxprotocol/v4-chain/protocol/daemons/flags" + "github.com/dydxprotocol/v4-chain/protocol/daemons/liquidation/api" + "github.com/dydxprotocol/v4-chain/protocol/daemons/liquidation/client" + "github.com/dydxprotocol/v4-chain/protocol/dtypes" + "github.com/dydxprotocol/v4-chain/protocol/mocks" + "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" + "github.com/dydxprotocol/v4-chain/protocol/testutil/grpc" + blocktimetypes "github.com/dydxprotocol/v4-chain/protocol/x/blocktime/types" + clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" + perptypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types" + pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" + satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestRunLiquidationDaemonTaskLoop(t *testing.T) { + tests := map[string]struct { + // mocks + setupMocks func(ctx context.Context, mck *mocks.QueryClient) + + // expectations + expectedLiquidatableSubaccountIds []satypes.SubaccountId + expectedError error + }{ + "Can get liquidatable subaccount with short position": { + setupMocks: func(ctx context.Context, mck *mocks.QueryClient) { + // Block height. + res := &blocktimetypes.QueryPreviousBlockInfoResponse{ + Info: &blocktimetypes.BlockInfo{ + Height: uint32(50), + Timestamp: constants.TimeTen, + }, + } + mck.On("PreviousBlockInfo", mock.Anything, mock.Anything).Return(res, nil) + + // Subaccount. + res2 := &satypes.QuerySubaccountAllResponse{ + Subaccount: []satypes.Subaccount{ + constants.Carl_Num0_1BTC_Short_54999USD, + }, + } + mck.On("SubaccountAll", mock.Anything, mock.Anything).Return(res2, nil) + + // Market prices. + res3 := &pricestypes.QueryAllMarketPricesResponse{ + MarketPrices: constants.TestMarketPrices, + } + mck.On("AllMarketPrices", mock.Anything, mock.Anything).Return(res3, nil) + + // Perpetuals. + res4 := &perptypes.QueryAllPerpetualsResponse{ + Perpetual: []perptypes.Perpetual{ + constants.BtcUsd_20PercentInitial_10PercentMaintenance, + }, + } + mck.On("AllPerpetuals", mock.Anything, mock.Anything).Return(res4, nil) + + // Liquidity tiers. + res5 := &perptypes.QueryAllLiquidityTiersResponse{ + LiquidityTiers: constants.LiquidityTiers, + } + mck.On("AllLiquidityTiers", mock.Anything, mock.Anything).Return(res5, nil) + + // Sends liquidatable subaccount ids to the server. + req := &api.LiquidateSubaccountsRequest{ + BlockHeight: uint32(50), + LiquidatableSubaccountIds: []satypes.SubaccountId{ + constants.Carl_Num0, + }, + NegativeTncSubaccountIds: []satypes.SubaccountId{}, + SubaccountOpenPositionInfo: []clobtypes.SubaccountOpenPositionInfo{ + { + PerpetualId: 0, + SubaccountsWithLongPosition: []satypes.SubaccountId{}, + SubaccountsWithShortPosition: []satypes.SubaccountId{ + constants.Carl_Num0, + }, + }, + }, + } + response3 := &api.LiquidateSubaccountsResponse{} + mck.On("LiquidateSubaccounts", ctx, req).Return(response3, nil) + }, + }, + "Can get liquidatable subaccount with long position": { + setupMocks: func(ctx context.Context, mck *mocks.QueryClient) { + // Block height. + res := &blocktimetypes.QueryPreviousBlockInfoResponse{ + Info: &blocktimetypes.BlockInfo{ + Height: uint32(50), + Timestamp: constants.TimeTen, + }, + } + mck.On("PreviousBlockInfo", mock.Anything, mock.Anything).Return(res, nil) + + // Subaccount. + res2 := &satypes.QuerySubaccountAllResponse{ + Subaccount: []satypes.Subaccount{ + constants.Dave_Num0_1BTC_Long_45001USD_Short, + }, + } + mck.On("SubaccountAll", mock.Anything, mock.Anything).Return(res2, nil) + + // Market prices. + res3 := &pricestypes.QueryAllMarketPricesResponse{ + MarketPrices: constants.TestMarketPrices, + } + mck.On("AllMarketPrices", mock.Anything, mock.Anything).Return(res3, nil) + + // Perpetuals. + res4 := &perptypes.QueryAllPerpetualsResponse{ + Perpetual: []perptypes.Perpetual{ + constants.BtcUsd_20PercentInitial_10PercentMaintenance, + }, + } + mck.On("AllPerpetuals", mock.Anything, mock.Anything).Return(res4, nil) + + // Liquidity tiers. + res5 := &perptypes.QueryAllLiquidityTiersResponse{ + LiquidityTiers: constants.LiquidityTiers, + } + mck.On("AllLiquidityTiers", mock.Anything, mock.Anything).Return(res5, nil) + + // Sends liquidatable subaccount ids to the server. + req := &api.LiquidateSubaccountsRequest{ + BlockHeight: uint32(50), + LiquidatableSubaccountIds: []satypes.SubaccountId{ + constants.Dave_Num0, + }, + NegativeTncSubaccountIds: []satypes.SubaccountId{}, + SubaccountOpenPositionInfo: []clobtypes.SubaccountOpenPositionInfo{ + { + PerpetualId: 0, + SubaccountsWithLongPosition: []satypes.SubaccountId{ + constants.Dave_Num0, + }, + SubaccountsWithShortPosition: []satypes.SubaccountId{}, + }, + }, + } + response3 := &api.LiquidateSubaccountsResponse{} + mck.On("LiquidateSubaccounts", ctx, req).Return(response3, nil) + }, + }, + "Skip well collateralized subaccounts": { + setupMocks: func(ctx context.Context, mck *mocks.QueryClient) { + // Block height. + res := &blocktimetypes.QueryPreviousBlockInfoResponse{ + Info: &blocktimetypes.BlockInfo{ + Height: uint32(50), + Timestamp: constants.TimeTen, + }, + } + mck.On("PreviousBlockInfo", mock.Anything, mock.Anything).Return(res, nil) + + // Subaccount. + res2 := &satypes.QuerySubaccountAllResponse{ + Subaccount: []satypes.Subaccount{ + constants.Carl_Num0_1BTC_Short_55000USD, + constants.Dave_Num0_1BTC_Long_45000USD_Short, + }, + } + mck.On("SubaccountAll", mock.Anything, mock.Anything).Return(res2, nil) + + // Market prices. + res3 := &pricestypes.QueryAllMarketPricesResponse{ + MarketPrices: constants.TestMarketPrices, + } + mck.On("AllMarketPrices", mock.Anything, mock.Anything).Return(res3, nil) + + // Perpetuals. + res4 := &perptypes.QueryAllPerpetualsResponse{ + Perpetual: []perptypes.Perpetual{ + constants.BtcUsd_20PercentInitial_10PercentMaintenance, + }, + } + mck.On("AllPerpetuals", mock.Anything, mock.Anything).Return(res4, nil) + + // Liquidity tiers. + res5 := &perptypes.QueryAllLiquidityTiersResponse{ + LiquidityTiers: constants.LiquidityTiers, + } + mck.On("AllLiquidityTiers", mock.Anything, mock.Anything).Return(res5, nil) + + // Sends liquidatable subaccount ids to the server. + req := &api.LiquidateSubaccountsRequest{ + BlockHeight: uint32(50), + LiquidatableSubaccountIds: []satypes.SubaccountId{}, + NegativeTncSubaccountIds: []satypes.SubaccountId{}, + SubaccountOpenPositionInfo: []clobtypes.SubaccountOpenPositionInfo{ + { + PerpetualId: 0, + SubaccountsWithLongPosition: []satypes.SubaccountId{ + constants.Dave_Num0, + }, + SubaccountsWithShortPosition: []satypes.SubaccountId{ + constants.Carl_Num0, + }, + }, + }, + } + response3 := &api.LiquidateSubaccountsResponse{} + mck.On("LiquidateSubaccounts", ctx, req).Return(response3, nil) + }, + }, + "Skip subaccounts with no open positions": { + setupMocks: func(ctx context.Context, mck *mocks.QueryClient) { + // Block height. + res := &blocktimetypes.QueryPreviousBlockInfoResponse{ + Info: &blocktimetypes.BlockInfo{ + Height: uint32(50), + Timestamp: constants.TimeTen, + }, + } + mck.On("PreviousBlockInfo", mock.Anything, mock.Anything).Return(res, nil) + + // Subaccount. + res2 := &satypes.QuerySubaccountAllResponse{ + Subaccount: []satypes.Subaccount{ + constants.Alice_Num0_100_000USD, + }, + } + mck.On("SubaccountAll", mock.Anything, mock.Anything).Return(res2, nil) + + // Market prices. + res3 := &pricestypes.QueryAllMarketPricesResponse{ + MarketPrices: constants.TestMarketPrices, + } + mck.On("AllMarketPrices", mock.Anything, mock.Anything).Return(res3, nil) + + // Perpetuals. + res4 := &perptypes.QueryAllPerpetualsResponse{ + Perpetual: []perptypes.Perpetual{ + constants.BtcUsd_20PercentInitial_10PercentMaintenance, + }, + } + mck.On("AllPerpetuals", mock.Anything, mock.Anything).Return(res4, nil) + + // Liquidity tiers. + res5 := &perptypes.QueryAllLiquidityTiersResponse{ + LiquidityTiers: constants.LiquidityTiers, + } + mck.On("AllLiquidityTiers", mock.Anything, mock.Anything).Return(res5, nil) + + // Sends liquidatable subaccount ids to the server. + req := &api.LiquidateSubaccountsRequest{ + BlockHeight: uint32(50), + LiquidatableSubaccountIds: []satypes.SubaccountId{}, + NegativeTncSubaccountIds: []satypes.SubaccountId{}, + SubaccountOpenPositionInfo: []clobtypes.SubaccountOpenPositionInfo{}, + } + response3 := &api.LiquidateSubaccountsResponse{} + mck.On("LiquidateSubaccounts", ctx, req).Return(response3, nil) + }, + }, + "Can get subaccount that become undercollateralized with funding payments (short)": { + setupMocks: func(ctx context.Context, mck *mocks.QueryClient) { + // Block height. + res := &blocktimetypes.QueryPreviousBlockInfoResponse{ + Info: &blocktimetypes.BlockInfo{ + Height: uint32(50), + Timestamp: constants.TimeTen, + }, + } + mck.On("PreviousBlockInfo", mock.Anything, mock.Anything).Return(res, nil) + + // Subaccount. + res2 := &satypes.QuerySubaccountAllResponse{ + Subaccount: []satypes.Subaccount{ + // Without funding, Carl has a TNC of $5,000, MMR of $5,000, and is + // well-collateralized. + // However, funding index for Carl's position is 10,000 and perpetual's funding index + // is 0. Index delta is -10,000, so Carl has to make a funding payment of $1 and + // become under-collateralized. + { + Id: &constants.Carl_Num0, + AssetPositions: []*satypes.AssetPosition{ + { + AssetId: 0, + Quantums: dtypes.NewInt(55_000_000_000), // $55,000 + }, + }, + PerpetualPositions: []*satypes.PerpetualPosition{ + { + PerpetualId: 0, + Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + FundingIndex: dtypes.NewInt(10_000), + }, + }, + }, + }, + } + mck.On("SubaccountAll", mock.Anything, mock.Anything).Return(res2, nil) + + // Market prices. + res3 := &pricestypes.QueryAllMarketPricesResponse{ + MarketPrices: constants.TestMarketPrices, + } + mck.On("AllMarketPrices", mock.Anything, mock.Anything).Return(res3, nil) + + // Perpetuals. + res4 := &perptypes.QueryAllPerpetualsResponse{ + Perpetual: []perptypes.Perpetual{ + constants.BtcUsd_20PercentInitial_10PercentMaintenance, + }, + } + mck.On("AllPerpetuals", mock.Anything, mock.Anything).Return(res4, nil) + + // Liquidity tiers. + res5 := &perptypes.QueryAllLiquidityTiersResponse{ + LiquidityTiers: constants.LiquidityTiers, + } + mck.On("AllLiquidityTiers", mock.Anything, mock.Anything).Return(res5, nil) + + // Sends liquidatable subaccount ids to the server. + req := &api.LiquidateSubaccountsRequest{ + BlockHeight: uint32(50), + LiquidatableSubaccountIds: []satypes.SubaccountId{ + constants.Carl_Num0, + }, + NegativeTncSubaccountIds: []satypes.SubaccountId{}, + SubaccountOpenPositionInfo: []clobtypes.SubaccountOpenPositionInfo{ + { + PerpetualId: 0, + SubaccountsWithLongPosition: []satypes.SubaccountId{}, + SubaccountsWithShortPosition: []satypes.SubaccountId{ + constants.Carl_Num0, + }, + }, + }, + } + response3 := &api.LiquidateSubaccountsResponse{} + mck.On("LiquidateSubaccounts", ctx, req).Return(response3, nil) + }, + }, + "Can get subaccount that become liquidatable with funding payments (long)": { + setupMocks: func(ctx context.Context, mck *mocks.QueryClient) { + // Block height. + res := &blocktimetypes.QueryPreviousBlockInfoResponse{ + Info: &blocktimetypes.BlockInfo{ + Height: uint32(50), + Timestamp: constants.TimeTen, + }, + } + mck.On("PreviousBlockInfo", mock.Anything, mock.Anything).Return(res, nil) + + // Subaccount. + res2 := &satypes.QuerySubaccountAllResponse{ + Subaccount: []satypes.Subaccount{ + // Without funding, Dave has a TNC of $5,000, MMR of $5,000, and is + // well-collateralized. + // However, funding index for Dave's position is -10,000 and perpetual's funding index + // is 0. Index delta is 10,000, so Dave has to make a funding payment of $1 and + // become under-collateralized. + { + Id: &constants.Dave_Num0, + AssetPositions: []*satypes.AssetPosition{ + { + AssetId: 0, + Quantums: dtypes.NewInt(-45_000_000_000), // -$45,000 + }, + }, + PerpetualPositions: []*satypes.PerpetualPosition{ + { + PerpetualId: 0, + Quantums: dtypes.NewInt(100_000_000), // 1 BTC + FundingIndex: dtypes.NewInt(-10_000), + }, + }, + }, + }, + } + mck.On("SubaccountAll", mock.Anything, mock.Anything).Return(res2, nil) + + // Market prices. + res3 := &pricestypes.QueryAllMarketPricesResponse{ + MarketPrices: constants.TestMarketPrices, + } + mck.On("AllMarketPrices", mock.Anything, mock.Anything).Return(res3, nil) + + // Perpetuals. + res4 := &perptypes.QueryAllPerpetualsResponse{ + Perpetual: []perptypes.Perpetual{ + constants.BtcUsd_20PercentInitial_10PercentMaintenance, + }, + } + mck.On("AllPerpetuals", mock.Anything, mock.Anything).Return(res4, nil) + + // Liquidity tiers. + res5 := &perptypes.QueryAllLiquidityTiersResponse{ + LiquidityTiers: constants.LiquidityTiers, + } + mck.On("AllLiquidityTiers", mock.Anything, mock.Anything).Return(res5, nil) + + // Sends liquidatable subaccount ids to the server. + req := &api.LiquidateSubaccountsRequest{ + BlockHeight: uint32(50), + LiquidatableSubaccountIds: []satypes.SubaccountId{ + constants.Dave_Num0, + }, + NegativeTncSubaccountIds: []satypes.SubaccountId{}, + SubaccountOpenPositionInfo: []clobtypes.SubaccountOpenPositionInfo{ + { + PerpetualId: 0, + SubaccountsWithLongPosition: []satypes.SubaccountId{ + constants.Dave_Num0, + }, + SubaccountsWithShortPosition: []satypes.SubaccountId{}, + }, + }, + } + response3 := &api.LiquidateSubaccountsResponse{} + mck.On("LiquidateSubaccounts", ctx, req).Return(response3, nil) + }, + }, + "Skips subaccount that become well-collateralized with funding payments (short)": { + setupMocks: func(ctx context.Context, mck *mocks.QueryClient) { + // Block height. + res := &blocktimetypes.QueryPreviousBlockInfoResponse{ + Info: &blocktimetypes.BlockInfo{ + Height: uint32(50), + Timestamp: constants.TimeTen, + }, + } + mck.On("PreviousBlockInfo", mock.Anything, mock.Anything).Return(res, nil) + + // Subaccount. + res2 := &satypes.QuerySubaccountAllResponse{ + Subaccount: []satypes.Subaccount{ + // Without funding, Carl has a TNC of $4,999, MMR of $5,000, and is + // under-collateralized. + // However, funding index for Carl's position is -10,000 and perpetual's funding index + // is 0. Index delta is 10,000, so Carl would receive a funding payment of $1 and + // become well-collateralized. + { + Id: &constants.Carl_Num0, + AssetPositions: []*satypes.AssetPosition{ + { + AssetId: 0, + Quantums: dtypes.NewInt(54_999_000_000), // $54,999 + }, + }, + PerpetualPositions: []*satypes.PerpetualPosition{ + { + PerpetualId: 0, + Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + FundingIndex: dtypes.NewInt(-10_000), + }, + }, + }, + }, + } + mck.On("SubaccountAll", mock.Anything, mock.Anything).Return(res2, nil) + + // Market prices. + res3 := &pricestypes.QueryAllMarketPricesResponse{ + MarketPrices: constants.TestMarketPrices, + } + mck.On("AllMarketPrices", mock.Anything, mock.Anything).Return(res3, nil) + + // Perpetuals. + res4 := &perptypes.QueryAllPerpetualsResponse{ + Perpetual: []perptypes.Perpetual{ + constants.BtcUsd_20PercentInitial_10PercentMaintenance, + }, + } + mck.On("AllPerpetuals", mock.Anything, mock.Anything).Return(res4, nil) + + // Liquidity tiers. + res5 := &perptypes.QueryAllLiquidityTiersResponse{ + LiquidityTiers: constants.LiquidityTiers, + } + mck.On("AllLiquidityTiers", mock.Anything, mock.Anything).Return(res5, nil) + + // Sends liquidatable subaccount ids to the server. + req := &api.LiquidateSubaccountsRequest{ + BlockHeight: uint32(50), + LiquidatableSubaccountIds: []satypes.SubaccountId{}, + NegativeTncSubaccountIds: []satypes.SubaccountId{}, + SubaccountOpenPositionInfo: []clobtypes.SubaccountOpenPositionInfo{ + { + PerpetualId: 0, + SubaccountsWithLongPosition: []satypes.SubaccountId{}, + SubaccountsWithShortPosition: []satypes.SubaccountId{ + constants.Carl_Num0, + }, + }, + }, + } + response3 := &api.LiquidateSubaccountsResponse{} + mck.On("LiquidateSubaccounts", ctx, req).Return(response3, nil) + }, + }, + "Skips subaccount that become well-collateralized with funding payments (long)": { + setupMocks: func(ctx context.Context, mck *mocks.QueryClient) { + // Block height. + res := &blocktimetypes.QueryPreviousBlockInfoResponse{ + Info: &blocktimetypes.BlockInfo{ + Height: uint32(50), + Timestamp: constants.TimeTen, + }, + } + mck.On("PreviousBlockInfo", mock.Anything, mock.Anything).Return(res, nil) + + // Subaccount. + res2 := &satypes.QuerySubaccountAllResponse{ + Subaccount: []satypes.Subaccount{ + // Without funding, Dave has a TNC of $4,999, MMR of $5,000, and is + // under-collateralized. + // However, funding index for Dave's position is 10,000 and perpetual's funding index + // is 0. Index delta is -10,000, so Dave would receive a funding payment of $1 and + // become well-collateralized. + { + Id: &constants.Dave_Num0, + AssetPositions: []*satypes.AssetPosition{ + { + AssetId: 0, + Quantums: dtypes.NewInt(-44_999_000_000), // -$44,999 + }, + }, + PerpetualPositions: []*satypes.PerpetualPosition{ + { + PerpetualId: 0, + Quantums: dtypes.NewInt(100_000_000), // 1 BTC + FundingIndex: dtypes.NewInt(10_000), + }, + }, + }, + }, + } + mck.On("SubaccountAll", mock.Anything, mock.Anything).Return(res2, nil) + + // Market prices. + res3 := &pricestypes.QueryAllMarketPricesResponse{ + MarketPrices: constants.TestMarketPrices, + } + mck.On("AllMarketPrices", mock.Anything, mock.Anything).Return(res3, nil) + + // Perpetuals. + res4 := &perptypes.QueryAllPerpetualsResponse{ + Perpetual: []perptypes.Perpetual{ + constants.BtcUsd_20PercentInitial_10PercentMaintenance, + }, + } + mck.On("AllPerpetuals", mock.Anything, mock.Anything).Return(res4, nil) + + // Liquidity tiers. + res5 := &perptypes.QueryAllLiquidityTiersResponse{ + LiquidityTiers: constants.LiquidityTiers, + } + mck.On("AllLiquidityTiers", mock.Anything, mock.Anything).Return(res5, nil) + + // Sends liquidatable subaccount ids to the server. + req := &api.LiquidateSubaccountsRequest{ + BlockHeight: uint32(50), + LiquidatableSubaccountIds: []satypes.SubaccountId{}, + NegativeTncSubaccountIds: []satypes.SubaccountId{}, + SubaccountOpenPositionInfo: []clobtypes.SubaccountOpenPositionInfo{ + { + PerpetualId: 0, + SubaccountsWithLongPosition: []satypes.SubaccountId{ + constants.Dave_Num0, + }, + SubaccountsWithShortPosition: []satypes.SubaccountId{}, + }, + }, + } + response3 := &api.LiquidateSubaccountsResponse{} + mck.On("LiquidateSubaccounts", ctx, req).Return(response3, nil) + }, + }, + "Can get negative tnc subaccount with short position": { + setupMocks: func(ctx context.Context, mck *mocks.QueryClient) { + // Block height. + res := &blocktimetypes.QueryPreviousBlockInfoResponse{ + Info: &blocktimetypes.BlockInfo{ + Height: uint32(50), + Timestamp: constants.TimeTen, + }, + } + mck.On("PreviousBlockInfo", mock.Anything, mock.Anything).Return(res, nil) + + // Subaccount. + res2 := &satypes.QuerySubaccountAllResponse{ + Subaccount: []satypes.Subaccount{ + // Carl has TNC of -$1. + constants.Carl_Num0_1BTC_Short_49999USD, + }, + } + mck.On("SubaccountAll", mock.Anything, mock.Anything).Return(res2, nil) + + // Market prices. + res3 := &pricestypes.QueryAllMarketPricesResponse{ + MarketPrices: constants.TestMarketPrices, + } + mck.On("AllMarketPrices", mock.Anything, mock.Anything).Return(res3, nil) + + // Perpetuals. + res4 := &perptypes.QueryAllPerpetualsResponse{ + Perpetual: []perptypes.Perpetual{ + constants.BtcUsd_20PercentInitial_10PercentMaintenance, + }, + } + mck.On("AllPerpetuals", mock.Anything, mock.Anything).Return(res4, nil) + + // Liquidity tiers. + res5 := &perptypes.QueryAllLiquidityTiersResponse{ + LiquidityTiers: constants.LiquidityTiers, + } + mck.On("AllLiquidityTiers", mock.Anything, mock.Anything).Return(res5, nil) + + // Sends liquidatable subaccount ids to the server. + req := &api.LiquidateSubaccountsRequest{ + BlockHeight: uint32(50), + LiquidatableSubaccountIds: []satypes.SubaccountId{ + constants.Carl_Num0, + }, + NegativeTncSubaccountIds: []satypes.SubaccountId{ + constants.Carl_Num0, + }, + SubaccountOpenPositionInfo: []clobtypes.SubaccountOpenPositionInfo{ + { + PerpetualId: 0, + SubaccountsWithLongPosition: []satypes.SubaccountId{}, + SubaccountsWithShortPosition: []satypes.SubaccountId{ + constants.Carl_Num0, + }, + }, + }, + } + response3 := &api.LiquidateSubaccountsResponse{} + mck.On("LiquidateSubaccounts", ctx, req).Return(response3, nil) + }, + }, + "Can get negative tnc subaccount with long position": { + setupMocks: func(ctx context.Context, mck *mocks.QueryClient) { + // Block height. + res := &blocktimetypes.QueryPreviousBlockInfoResponse{ + Info: &blocktimetypes.BlockInfo{ + Height: uint32(50), + Timestamp: constants.TimeTen, + }, + } + mck.On("PreviousBlockInfo", mock.Anything, mock.Anything).Return(res, nil) + + // Subaccount. + res2 := &satypes.QuerySubaccountAllResponse{ + Subaccount: []satypes.Subaccount{ + // Dave has TNC of -$1. + constants.Dave_Num0_1BTC_Long_50001USD_Short, + }, + } + mck.On("SubaccountAll", mock.Anything, mock.Anything).Return(res2, nil) + + // Market prices. + res3 := &pricestypes.QueryAllMarketPricesResponse{ + MarketPrices: constants.TestMarketPrices, + } + mck.On("AllMarketPrices", mock.Anything, mock.Anything).Return(res3, nil) + + // Perpetuals. + res4 := &perptypes.QueryAllPerpetualsResponse{ + Perpetual: []perptypes.Perpetual{ + constants.BtcUsd_20PercentInitial_10PercentMaintenance, + }, + } + mck.On("AllPerpetuals", mock.Anything, mock.Anything).Return(res4, nil) + + // Liquidity tiers. + res5 := &perptypes.QueryAllLiquidityTiersResponse{ + LiquidityTiers: constants.LiquidityTiers, + } + mck.On("AllLiquidityTiers", mock.Anything, mock.Anything).Return(res5, nil) + + // Sends liquidatable subaccount ids to the server. + req := &api.LiquidateSubaccountsRequest{ + BlockHeight: uint32(50), + LiquidatableSubaccountIds: []satypes.SubaccountId{ + constants.Dave_Num0, + }, + NegativeTncSubaccountIds: []satypes.SubaccountId{ + constants.Dave_Num0, + }, + SubaccountOpenPositionInfo: []clobtypes.SubaccountOpenPositionInfo{ + { + PerpetualId: 0, + SubaccountsWithLongPosition: []satypes.SubaccountId{ + constants.Dave_Num0, + }, + SubaccountsWithShortPosition: []satypes.SubaccountId{}, + }, + }, + } + response3 := &api.LiquidateSubaccountsResponse{} + mck.On("LiquidateSubaccounts", ctx, req).Return(response3, nil) + }, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + queryClientMock := &mocks.QueryClient{} + tc.setupMocks(grpc.Ctx, queryClientMock) + s := client.SubTaskRunnerImpl{} + + c := client.NewClient(log.NewNopLogger()) + c.SubaccountQueryClient = queryClientMock + c.ClobQueryClient = queryClientMock + c.LiquidationServiceClient = queryClientMock + c.PerpetualsQueryClient = queryClientMock + c.PricesQueryClient = queryClientMock + c.BlocktimeQueryClient = queryClientMock + + err := s.RunLiquidationDaemonTaskLoop( + grpc.Ctx, + c, + flags.GetDefaultDaemonFlags().Liquidation, + ) + if tc.expectedError != nil { + require.EqualError(t, err, tc.expectedError.Error()) + } else { + require.NoError(t, err) + queryClientMock.AssertExpectations(t) + } + }) + } +} diff --git a/protocol/daemons/server/liquidation.go b/protocol/daemons/server/liquidation.go index 753609ccb8..573ede7eb7 100644 --- a/protocol/daemons/server/liquidation.go +++ b/protocol/daemons/server/liquidation.go @@ -42,7 +42,10 @@ func (s *Server) LiquidateSubaccounts( metrics.Count, ) + s.daemonLiquidationInfo.UpdateBlockHeight(req.BlockHeight) s.daemonLiquidationInfo.UpdateLiquidatableSubaccountIds(req.LiquidatableSubaccountIds) + s.daemonLiquidationInfo.UpdateNegativeTncSubaccountIds(req.NegativeTncSubaccountIds) + s.daemonLiquidationInfo.UpdateSubaccountsWithPositions(req.SubaccountOpenPositionInfo) // Capture valid responses in metrics. s.reportValidResponse(types.LiquidationsDaemonServiceName) diff --git a/protocol/daemons/server/liquidation_test.go b/protocol/daemons/server/liquidation_test.go index f6b3186294..7fca88b369 100644 --- a/protocol/daemons/server/liquidation_test.go +++ b/protocol/daemons/server/liquidation_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestLiquidateSubaccounts_Empty_Update(t *testing.T) { +func TestLiquidateSubaccounts_Empty_Update_Liquidatable_SubaccountIds(t *testing.T) { mockGrpcServer := &mocks.GrpcServer{} mockFileHandler := &mocks.FileHandler{} daemonLiquidationInfo := liquidationtypes.NewDaemonLiquidationInfo() @@ -31,7 +31,7 @@ func TestLiquidateSubaccounts_Empty_Update(t *testing.T) { require.Empty(t, daemonLiquidationInfo.GetLiquidatableSubaccountIds()) } -func TestLiquidateSubaccounts_Multiple_Subaccount_Ids(t *testing.T) { +func TestLiquidateSubaccounts_Multiple_Liquidatable_Subaccount_Ids(t *testing.T) { mockGrpcServer := &mocks.GrpcServer{} mockFileHandler := &mocks.FileHandler{} daemonLiquidationInfo := liquidationtypes.NewDaemonLiquidationInfo() @@ -57,3 +57,69 @@ func TestLiquidateSubaccounts_Multiple_Subaccount_Ids(t *testing.T) { actualSubaccountIds := daemonLiquidationInfo.GetLiquidatableSubaccountIds() require.Equal(t, expectedSubaccountIds, actualSubaccountIds) } + +func TestLiquidateSubaccounts_GetSetBlockHeight(t *testing.T) { + mockGrpcServer := &mocks.GrpcServer{} + mockFileHandler := &mocks.FileHandler{} + daemonLiquidationInfo := liquidationtypes.NewDaemonLiquidationInfo() + + s := createServerWithMocks( + t, + mockGrpcServer, + mockFileHandler, + ).WithDaemonLiquidationInfo( + daemonLiquidationInfo, + ) + _, err := s.LiquidateSubaccounts(grpc.Ctx, &api.LiquidateSubaccountsRequest{ + BlockHeight: uint32(123), + LiquidatableSubaccountIds: []satypes.SubaccountId{}, + }) + require.NoError(t, err) + require.Equal(t, uint32(123), daemonLiquidationInfo.GetBlockHeight()) +} + +func TestLiquidateSubaccounts_Empty_Update_Negative_TNC_SubaccountIds(t *testing.T) { + mockGrpcServer := &mocks.GrpcServer{} + mockFileHandler := &mocks.FileHandler{} + daemonLiquidationInfo := liquidationtypes.NewDaemonLiquidationInfo() + + s := createServerWithMocks( + t, + mockGrpcServer, + mockFileHandler, + ).WithDaemonLiquidationInfo( + daemonLiquidationInfo, + ) + _, err := s.LiquidateSubaccounts(grpc.Ctx, &api.LiquidateSubaccountsRequest{ + NegativeTncSubaccountIds: []satypes.SubaccountId{}, + }) + require.NoError(t, err) + require.Empty(t, daemonLiquidationInfo.GetNegativeTncSubaccountIds()) +} + +func TestLiquidateSubaccounts_Multiple_Negative_TNC_Subaccount_Ids(t *testing.T) { + mockGrpcServer := &mocks.GrpcServer{} + mockFileHandler := &mocks.FileHandler{} + daemonLiquidationInfo := liquidationtypes.NewDaemonLiquidationInfo() + + s := createServerWithMocks( + t, + mockGrpcServer, + mockFileHandler, + ).WithDaemonLiquidationInfo( + daemonLiquidationInfo, + ) + + expectedSubaccountIds := []satypes.SubaccountId{ + constants.Alice_Num1, + constants.Bob_Num0, + constants.Carl_Num0, + } + _, err := s.LiquidateSubaccounts(grpc.Ctx, &api.LiquidateSubaccountsRequest{ + NegativeTncSubaccountIds: expectedSubaccountIds, + }) + require.NoError(t, err) + + actualSubaccountIds := daemonLiquidationInfo.GetNegativeTncSubaccountIds() + require.Equal(t, expectedSubaccountIds, actualSubaccountIds) +} diff --git a/protocol/daemons/server/types/liquidations/daemon_liquidation_info.go b/protocol/daemons/server/types/liquidations/daemon_liquidation_info.go index a416bfb499..68f887abf7 100644 --- a/protocol/daemons/server/types/liquidations/daemon_liquidation_info.go +++ b/protocol/daemons/server/types/liquidations/daemon_liquidation_info.go @@ -80,20 +80,20 @@ func (ls *DaemonLiquidationInfo) GetNegativeTncSubaccountIds() []satypes.Subacco // UpdateSubaccountsWithPositions updates the struct with the given a list of subaccount ids with open positions. func (ls *DaemonLiquidationInfo) UpdateSubaccountsWithPositions( - subaccountsWithPositions map[uint32]*clobtypes.SubaccountOpenPositionInfo, + subaccountsWithPositions []clobtypes.SubaccountOpenPositionInfo, ) { ls.Lock() defer ls.Unlock() ls.subaccountsWithPositions = make(map[uint32]*clobtypes.SubaccountOpenPositionInfo) - for perpetualId, info := range subaccountsWithPositions { + for _, info := range subaccountsWithPositions { clone := &clobtypes.SubaccountOpenPositionInfo{ - PerpetualId: perpetualId, + PerpetualId: info.PerpetualId, SubaccountsWithLongPosition: make([]satypes.SubaccountId, len(info.SubaccountsWithLongPosition)), SubaccountsWithShortPosition: make([]satypes.SubaccountId, len(info.SubaccountsWithShortPosition)), } copy(clone.SubaccountsWithLongPosition, info.SubaccountsWithLongPosition) copy(clone.SubaccountsWithShortPosition, info.SubaccountsWithShortPosition) - ls.subaccountsWithPositions[perpetualId] = clone + ls.subaccountsWithPositions[info.PerpetualId] = clone } } diff --git a/protocol/daemons/server/types/liquidations/daemon_liquidation_info_test.go b/protocol/daemons/server/types/liquidations/daemon_liquidation_info_test.go index 87b6b275b3..e699d28473 100644 --- a/protocol/daemons/server/types/liquidations/daemon_liquidation_info_test.go +++ b/protocol/daemons/server/types/liquidations/daemon_liquidation_info_test.go @@ -49,18 +49,22 @@ func TestSubaccountsWithOpenPositions_Multiple_Reads(t *testing.T) { ls := liquidationstypes.NewDaemonLiquidationInfo() require.Empty(t, ls.GetNegativeTncSubaccountIds()) - expected := map[uint32]*clobtypes.SubaccountOpenPositionInfo{ - 0: { - PerpetualId: 0, - SubaccountsWithLongPosition: []satypes.SubaccountId{ - constants.Alice_Num1, - }, - SubaccountsWithShortPosition: []satypes.SubaccountId{ - constants.Bob_Num0, - }, + info := clobtypes.SubaccountOpenPositionInfo{ + PerpetualId: 0, + SubaccountsWithLongPosition: []satypes.SubaccountId{ + constants.Alice_Num1, + }, + SubaccountsWithShortPosition: []satypes.SubaccountId{ + constants.Bob_Num0, }, } - ls.UpdateSubaccountsWithPositions(expected) + + input := []clobtypes.SubaccountOpenPositionInfo{info} + ls.UpdateSubaccountsWithPositions(input) + + expected := map[uint32]*clobtypes.SubaccountOpenPositionInfo{ + 0: &info, + } require.Equal(t, expected, ls.GetSubaccountsWithPositions()) require.Equal(t, expected, ls.GetSubaccountsWithPositions()) require.Equal(t, expected, ls.GetSubaccountsWithPositions()) @@ -116,46 +120,55 @@ func TestSubaccountsWithOpenPositions_Multiple_Writes(t *testing.T) { ls := liquidationstypes.NewDaemonLiquidationInfo() require.Empty(t, ls.GetSubaccountsWithPositions()) - expected := map[uint32]*clobtypes.SubaccountOpenPositionInfo{ - 0: { - PerpetualId: 0, - SubaccountsWithLongPosition: []satypes.SubaccountId{ - constants.Alice_Num1, - }, - SubaccountsWithShortPosition: []satypes.SubaccountId{ - constants.Bob_Num0, - }, + info := clobtypes.SubaccountOpenPositionInfo{ + PerpetualId: 0, + SubaccountsWithLongPosition: []satypes.SubaccountId{ + constants.Alice_Num1, }, + SubaccountsWithShortPosition: []satypes.SubaccountId{ + constants.Bob_Num0, + }, + } + + input := []clobtypes.SubaccountOpenPositionInfo{info} + ls.UpdateSubaccountsWithPositions(input) + expected := map[uint32]*clobtypes.SubaccountOpenPositionInfo{ + 0: &info, } - ls.UpdateSubaccountsWithPositions(expected) require.Equal(t, expected, ls.GetSubaccountsWithPositions()) - expected = map[uint32]*clobtypes.SubaccountOpenPositionInfo{ - 0: { - PerpetualId: 0, - SubaccountsWithLongPosition: []satypes.SubaccountId{ - constants.Carl_Num0, - }, - SubaccountsWithShortPosition: []satypes.SubaccountId{ - constants.Dave_Num0, - }, + info2 := clobtypes.SubaccountOpenPositionInfo{ + PerpetualId: 0, + SubaccountsWithLongPosition: []satypes.SubaccountId{ + constants.Carl_Num0, + }, + SubaccountsWithShortPosition: []satypes.SubaccountId{ + constants.Dave_Num0, }, } - ls.UpdateSubaccountsWithPositions(expected) - require.Equal(t, expected, ls.GetSubaccountsWithPositions()) + input2 := []clobtypes.SubaccountOpenPositionInfo{info2} + ls.UpdateSubaccountsWithPositions(input2) expected = map[uint32]*clobtypes.SubaccountOpenPositionInfo{ - 0: { - PerpetualId: 0, - SubaccountsWithLongPosition: []satypes.SubaccountId{ - constants.Dave_Num1, - }, - SubaccountsWithShortPosition: []satypes.SubaccountId{ - constants.Alice_Num1, - }, + 0: &info2, + } + require.Equal(t, expected, ls.GetSubaccountsWithPositions()) + + info3 := clobtypes.SubaccountOpenPositionInfo{ + PerpetualId: 0, + SubaccountsWithLongPosition: []satypes.SubaccountId{ + constants.Dave_Num1, + }, + SubaccountsWithShortPosition: []satypes.SubaccountId{ + constants.Alice_Num1, }, } - ls.UpdateSubaccountsWithPositions(expected) + + input3 := []clobtypes.SubaccountOpenPositionInfo{info3} + ls.UpdateSubaccountsWithPositions(input3) + expected = map[uint32]*clobtypes.SubaccountOpenPositionInfo{ + 0: &info3, + } require.Equal(t, expected, ls.GetSubaccountsWithPositions()) } @@ -193,21 +206,23 @@ func TestSubaccountsWithOpenPosition_Empty_Update(t *testing.T) { ls := liquidationstypes.NewDaemonLiquidationInfo() require.Empty(t, ls.GetSubaccountsWithPositions()) - expected := map[uint32]*clobtypes.SubaccountOpenPositionInfo{ - 0: { - PerpetualId: 0, - SubaccountsWithLongPosition: []satypes.SubaccountId{ - constants.Alice_Num1, - }, - SubaccountsWithShortPosition: []satypes.SubaccountId{ - constants.Bob_Num0, - }, + info := clobtypes.SubaccountOpenPositionInfo{ + PerpetualId: 0, + SubaccountsWithLongPosition: []satypes.SubaccountId{ + constants.Alice_Num1, }, + SubaccountsWithShortPosition: []satypes.SubaccountId{ + constants.Bob_Num0, + }, + } + input := []clobtypes.SubaccountOpenPositionInfo{info} + ls.UpdateSubaccountsWithPositions(input) + expected := map[uint32]*clobtypes.SubaccountOpenPositionInfo{ + 0: &info, } - ls.UpdateSubaccountsWithPositions(expected) require.Equal(t, expected, ls.GetSubaccountsWithPositions()) - expected = map[uint32]*clobtypes.SubaccountOpenPositionInfo{} - ls.UpdateSubaccountsWithPositions(expected) + input2 := []clobtypes.SubaccountOpenPositionInfo{} + ls.UpdateSubaccountsWithPositions(input2) require.Empty(t, ls.GetSubaccountsWithPositions()) } diff --git a/protocol/lib/collections.go b/protocol/lib/collections.go index a00a0347df..5f57e7ff42 100644 --- a/protocol/lib/collections.go +++ b/protocol/lib/collections.go @@ -50,6 +50,24 @@ func UniqueSliceToSet[K comparable](values []K) map[K]struct{} { return set } +// UniqueSliceToMap converts a slice to a map using the provided keyFunc to generate the key. +func UniqueSliceToMap[K comparable, V any](slice []V, keyFunc func(V) K) map[K]V { + m := make(map[K]V) + for _, v := range slice { + k := keyFunc(v) + if _, exists := m[k]; exists { + panic( + fmt.Sprintf( + "UniqueSliceToMap: duplicate value: %+v", + v, + ), + ) + } + m[k] = v + } + return m +} + // MapSlice takes a function and executes that function on each element of a slice, returning the result. // Note the function must return one result for each element of the slice. func MapSlice[V any, E any](values []V, mapFunc func(V) E) []E { diff --git a/protocol/lib/collections_test.go b/protocol/lib/collections_test.go index 14878e8365..6b0f75443a 100644 --- a/protocol/lib/collections_test.go +++ b/protocol/lib/collections_test.go @@ -104,6 +104,58 @@ func TestUniqueSliceToSet(t *testing.T) { } } +func TestUniqueSliceToMap(t *testing.T) { + type testStruct struct { + Id uint32 + } + + tests := map[string]struct { + input []testStruct + expected map[uint32]testStruct + panicWith string + }{ + "Empty": { + input: []testStruct{}, + expected: map[uint32]testStruct{}, + }, + "Basic": { + input: []testStruct{ + {Id: 0}, {Id: 1}, {Id: 2}, + }, + expected: map[uint32]testStruct{ + 0: {Id: 0}, + 1: {Id: 1}, + 2: {Id: 2}, + }, + }, + "Duplicate": { + input: []testStruct{ + {Id: 0}, {Id: 0}, + }, + panicWith: "UniqueSliceToMap: duplicate value: {Id:0}", + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + if tc.panicWith != "" { + require.PanicsWithValue( + t, + tc.panicWith, + func() { + lib.UniqueSliceToMap(tc.input, func(t testStruct) uint32 { return t.Id }) + }, + ) + } else { + require.Equal( + t, + tc.expected, + lib.UniqueSliceToMap(tc.input, func(t testStruct) uint32 { return t.Id }), + ) + } + }) + } +} + func TestMapSlice(t *testing.T) { // Can increment all numbers in a slice by 1, and change type to `uint64`. require.Equal( diff --git a/protocol/lib/metrics/constants.go b/protocol/lib/metrics/constants.go index 2b7c511198..ee3f61a02e 100644 --- a/protocol/lib/metrics/constants.go +++ b/protocol/lib/metrics/constants.go @@ -293,11 +293,14 @@ const ( // Liquidation Daemon. CheckCollateralizationForSubaccounts = "check_collateralization_for_subaccounts" + FetchApplicationStateAtBlockHeight = "fetch_application_state_at_block_height" GetAllSubaccounts = "get_all_subaccounts" GetLiquidatableSubaccountIds = "get_liquidatable_subaccount_ids" + GetSubaccountOpenPositionInfo = "get_subaccount_open_position_info" GetSubaccountsFromKey = "get_subaccounts_from_key" LiquidatableSubaccountIds = "liquidatable_subaccount_ids" LiquidationDaemon = "liquidation_daemon" + NegativeTncSubaccountIds = "negative_tnc_subaccount_ids" PageLimit = "page_limit" SendLiquidatableSubaccountIds = "send_liquidatable_subaccount_ids" SubaccountsWithOpenPositions = "subaccounts_with_open_positions" diff --git a/protocol/mocks/QueryClient.go b/protocol/mocks/QueryClient.go index 45a231ab3d..995b2ea62c 100644 --- a/protocol/mocks/QueryClient.go +++ b/protocol/mocks/QueryClient.go @@ -210,36 +210,6 @@ func (_m *QueryClient) AllPerpetuals(ctx context.Context, in *perpetualstypes.Qu return r0, r1 } -// AreSubaccountsLiquidatable provides a mock function with given fields: ctx, in, opts -func (_m *QueryClient) AreSubaccountsLiquidatable(ctx context.Context, in *clobtypes.AreSubaccountsLiquidatableRequest, opts ...grpc.CallOption) (*clobtypes.AreSubaccountsLiquidatableResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *clobtypes.AreSubaccountsLiquidatableResponse - if rf, ok := ret.Get(0).(func(context.Context, *clobtypes.AreSubaccountsLiquidatableRequest, ...grpc.CallOption) *clobtypes.AreSubaccountsLiquidatableResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*clobtypes.AreSubaccountsLiquidatableResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *clobtypes.AreSubaccountsLiquidatableRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // BlockRateLimitConfiguration provides a mock function with given fields: ctx, in, opts func (_m *QueryClient) BlockRateLimitConfiguration(ctx context.Context, in *clobtypes.QueryBlockRateLimitConfigurationRequest, opts ...grpc.CallOption) (*clobtypes.QueryBlockRateLimitConfigurationResponse, error) { _va := make([]interface{}, len(opts)) diff --git a/protocol/testutil/constants/perpetuals.go b/protocol/testutil/constants/perpetuals.go index ccf11f1189..9f9739d64c 100644 --- a/protocol/testutil/constants/perpetuals.go +++ b/protocol/testutil/constants/perpetuals.go @@ -215,6 +215,7 @@ var ( DefaultFundingPpm: int32(0), LiquidityTier: uint32(3), }, + FundingIndex: dtypes.ZeroInt(), } BtcUsd_NoMarginRequirement = perptypes.Perpetual{ Params: perptypes.PerpetualParams{ diff --git a/protocol/testutil/constants/subaccounts.go b/protocol/testutil/constants/subaccounts.go index 28575e8c0d..832db4057d 100644 --- a/protocol/testutil/constants/subaccounts.go +++ b/protocol/testutil/constants/subaccounts.go @@ -141,8 +141,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -171,8 +172,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -328,6 +330,22 @@ var ( }, }, } + Dave_Num0_1BTC_Long_45000USD_Short = satypes.Subaccount{ + Id: &Dave_Num0, + AssetPositions: []*satypes.AssetPosition{ + { + AssetId: 0, + Quantums: dtypes.NewInt(-45_000_000_000), // -$45,000 + }, + }, + PerpetualPositions: []*satypes.PerpetualPosition{ + { + PerpetualId: 0, + Quantums: dtypes.NewInt(100_000_000), // 1 BTC + FundingIndex: dtypes.NewInt(0), + }, + }, + } Dave_Num0_1BTC_Long_45001USD_Short = satypes.Subaccount{ Id: &Dave_Num0, AssetPositions: []*satypes.AssetPosition{ @@ -338,8 +356,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(100_000_000), // 1 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(100_000_000), // 1 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -384,8 +403,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(100_000_000), // 1 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(100_000_000), // 1 BTC + FundingIndex: dtypes.NewInt(0), }, }, } diff --git a/protocol/x/clob/keeper/grpc_query_are_subaccounts_liquidatable.go b/protocol/x/clob/keeper/grpc_query_are_subaccounts_liquidatable.go deleted file mode 100644 index 7713c4dbe5..0000000000 --- a/protocol/x/clob/keeper/grpc_query_are_subaccounts_liquidatable.go +++ /dev/null @@ -1,40 +0,0 @@ -package keeper - -import ( - "context" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -func (k Keeper) AreSubaccountsLiquidatable( - c context.Context, - req *types.AreSubaccountsLiquidatableRequest, -) ( - *types.AreSubaccountsLiquidatableResponse, - error, -) { - if req == nil { - return nil, status.Error(codes.InvalidArgument, "invalid request") - } - - ctx := sdk.UnwrapSDKContext(c) - - results := make([]types.AreSubaccountsLiquidatableResponse_Result, len(req.SubaccountIds)) - for i, subaccountId := range req.SubaccountIds { - isLiquidatable, err := k.IsLiquidatable(ctx, subaccountId) - - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } - results[i] = types.AreSubaccountsLiquidatableResponse_Result{ - SubaccountId: subaccountId, - IsLiquidatable: isLiquidatable, - } - } - return &types.AreSubaccountsLiquidatableResponse{ - Results: results, - }, nil -} diff --git a/protocol/x/clob/keeper/grpc_query_are_subaccounts_liquidatable_test.go b/protocol/x/clob/keeper/grpc_query_are_subaccounts_liquidatable_test.go deleted file mode 100644 index 1194fd7b30..0000000000 --- a/protocol/x/clob/keeper/grpc_query_are_subaccounts_liquidatable_test.go +++ /dev/null @@ -1,166 +0,0 @@ -package keeper_test - -import ( - "testing" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/require" - - "github.com/dydxprotocol/v4-chain/protocol/dtypes" - "github.com/dydxprotocol/v4-chain/protocol/mocks" - "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" - keepertest "github.com/dydxprotocol/v4-chain/protocol/testutil/keeper" - "github.com/dydxprotocol/v4-chain/protocol/testutil/nullify" - "github.com/dydxprotocol/v4-chain/protocol/x/clob/memclob" - "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" - perptypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types" - satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" -) - -func TestAreSubaccountsLiquidatable(t *testing.T) { - for _, tc := range []struct { - desc string - subaccounts []satypes.Subaccount - perpetuals []perptypes.Perpetual - request *types.AreSubaccountsLiquidatableRequest - response *types.AreSubaccountsLiquidatableResponse - err error - }{ - { - desc: "No errors", - perpetuals: []perptypes.Perpetual{ - constants.BtcUsd_100PercentMarginRequirement, - }, - subaccounts: []satypes.Subaccount{ - { - Id: &constants.Alice_Num0, - AssetPositions: []*satypes.AssetPosition{ - &constants.Usdc_Asset_10_000, - }, - PerpetualPositions: []*satypes.PerpetualPosition{ - { - PerpetualId: 0, - Quantums: dtypes.NewInt(-1_000_000_000), // 1 BTC - }, - }, - }, - { - Id: &constants.Bob_Num0, - AssetPositions: []*satypes.AssetPosition{ - &constants.Usdc_Asset_10_000, - }, - PerpetualPositions: []*satypes.PerpetualPosition{ - { - PerpetualId: 0, - Quantums: dtypes.NewInt(100_000_000), // 1 BTC - }, - }, - }, - }, - request: &types.AreSubaccountsLiquidatableRequest{ - SubaccountIds: []satypes.SubaccountId{ - constants.Alice_Num0, - constants.Bob_Num0, - }, - }, - response: &types.AreSubaccountsLiquidatableResponse{ - Results: []types.AreSubaccountsLiquidatableResponse_Result{ - { - SubaccountId: constants.Alice_Num0, - IsLiquidatable: true, - }, - { - SubaccountId: constants.Bob_Num0, - IsLiquidatable: false, - }, - }, - }, - }, - { - desc: "Non-existent subaccount", - perpetuals: []perptypes.Perpetual{ - constants.BtcUsd_100PercentMarginRequirement, - }, - subaccounts: []satypes.Subaccount{}, - request: &types.AreSubaccountsLiquidatableRequest{ - SubaccountIds: []satypes.SubaccountId{ - constants.Alice_Num0, - }, - }, - response: &types.AreSubaccountsLiquidatableResponse{ - Results: []types.AreSubaccountsLiquidatableResponse_Result{ - { - SubaccountId: constants.Alice_Num0, - IsLiquidatable: false, - }, - }, - }, - }, - { - desc: "Errors are propagated", - subaccounts: []satypes.Subaccount{ - { - Id: &constants.Alice_Num0, - AssetPositions: []*satypes.AssetPosition{ - &constants.Usdc_Asset_10_000, - }, - PerpetualPositions: []*satypes.PerpetualPosition{ - { - PerpetualId: 0, - Quantums: dtypes.NewInt(-1_000_000_000), // 1 BTC - }, - }, - }, - }, - perpetuals: []perptypes.Perpetual{}, - request: &types.AreSubaccountsLiquidatableRequest{ - SubaccountIds: []satypes.SubaccountId{ - constants.Alice_Num0, - }, - }, - err: perptypes.ErrPerpetualDoesNotExist, - }, - } { - t.Run(tc.desc, func(t *testing.T) { - memClob := memclob.NewMemClobPriceTimePriority(false) - ks := keepertest.NewClobKeepersTestContext(t, memClob, &mocks.BankKeeper{}, &mocks.IndexerEventManager{}) - - // Create the default markets. - keepertest.CreateTestMarkets(t, ks.Ctx, ks.PricesKeeper) - - // Create liquidity tiers. - keepertest.CreateTestLiquidityTiers(t, ks.Ctx, ks.PerpetualsKeeper) - - // Create all perpetuals. - for _, p := range tc.perpetuals { - _, err := ks.PerpetualsKeeper.CreatePerpetual( - ks.Ctx, - p.Params.Id, - p.Params.Ticker, - p.Params.MarketId, - p.Params.AtomicResolution, - p.Params.DefaultFundingPpm, - p.Params.LiquidityTier, - ) - require.NoError(t, err) - } - - for _, subaccount := range tc.subaccounts { - ks.SubaccountsKeeper.SetSubaccount(ks.Ctx, subaccount) - } - - wctx := sdk.WrapSDKContext(ks.Ctx) - response, err := ks.ClobKeeper.AreSubaccountsLiquidatable(wctx, tc.request) - - if tc.err != nil { - require.ErrorContains(t, err, tc.err.Error()) - } else { - require.NoError(t, err) - require.Equal(t, - nullify.Fill(tc.response), //nolint:staticcheck - nullify.Fill(response), //nolint:staticcheck - ) - } - }) - } -} diff --git a/protocol/x/clob/keeper/liquidations.go b/protocol/x/clob/keeper/liquidations.go index 8017370cfa..6c2136c0fb 100644 --- a/protocol/x/clob/keeper/liquidations.go +++ b/protocol/x/clob/keeper/liquidations.go @@ -360,11 +360,22 @@ func (k Keeper) IsLiquidatable( return false, err } - // The subaccount is liquidatable if both of the following are true: - // - The maintenance margin requirements are greater than zero (note that they can never be negative). - // - The maintenance margin requirements are greater than the subaccount's net collateral. - isLiquidatable := bigMaintenanceMargin.Sign() > 0 && bigMaintenanceMargin.Cmp(bigNetCollateral) == 1 - return isLiquidatable, nil + return CanLiquidateSubaccount(bigNetCollateral, bigMaintenanceMargin), nil +} + +// CanLiquidateSubaccount returns true if a subaccount is liquidatable given its total net collateral and +// maintenance margin requirement. +// +// The subaccount is liquidatable if both of the following are true: +// - The maintenance margin requirements are greater than zero (note that they can never be negative). +// - The maintenance margin requirements are greater than the subaccount's net collateral. +// +// Note that this is a stateless function. +func CanLiquidateSubaccount( + bigNetCollateral *big.Int, + bigMaintenanceMargin *big.Int, +) bool { + return bigMaintenanceMargin.Sign() > 0 && bigMaintenanceMargin.Cmp(bigNetCollateral) == 1 } // EnsureIsLiquidatable returns an error if the subaccount is not liquidatable. diff --git a/protocol/x/clob/types/query.pb.go b/protocol/x/clob/types/query.pb.go index 40751a9ac1..78f490f800 100644 --- a/protocol/x/clob/types/query.pb.go +++ b/protocol/x/clob/types/query.pb.go @@ -11,7 +11,6 @@ import ( _ "github.com/cosmos/gogoproto/gogoproto" grpc1 "github.com/cosmos/gogoproto/grpc" proto "github.com/cosmos/gogoproto/proto" - types "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" _ "google.golang.org/genproto/googleapis/api/annotations" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" @@ -220,156 +219,6 @@ func (m *QueryClobPairAllResponse) GetPagination() *query.PageResponse { return nil } -// AreSubaccountsLiquidatableRequest is a request message used to check whether -// the given subaccounts are liquidatable. -// The subaccount ids should not contain duplicates. -type AreSubaccountsLiquidatableRequest struct { - SubaccountIds []types.SubaccountId `protobuf:"bytes,1,rep,name=subaccount_ids,json=subaccountIds,proto3" json:"subaccount_ids"` -} - -func (m *AreSubaccountsLiquidatableRequest) Reset() { *m = AreSubaccountsLiquidatableRequest{} } -func (m *AreSubaccountsLiquidatableRequest) String() string { return proto.CompactTextString(m) } -func (*AreSubaccountsLiquidatableRequest) ProtoMessage() {} -func (*AreSubaccountsLiquidatableRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_3365c195b25c5bc0, []int{4} -} -func (m *AreSubaccountsLiquidatableRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *AreSubaccountsLiquidatableRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_AreSubaccountsLiquidatableRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *AreSubaccountsLiquidatableRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_AreSubaccountsLiquidatableRequest.Merge(m, src) -} -func (m *AreSubaccountsLiquidatableRequest) XXX_Size() int { - return m.Size() -} -func (m *AreSubaccountsLiquidatableRequest) XXX_DiscardUnknown() { - xxx_messageInfo_AreSubaccountsLiquidatableRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_AreSubaccountsLiquidatableRequest proto.InternalMessageInfo - -func (m *AreSubaccountsLiquidatableRequest) GetSubaccountIds() []types.SubaccountId { - if m != nil { - return m.SubaccountIds - } - return nil -} - -// AreSubaccountsLiquidatableResponse is a response message that contains the -// liquidation status for each subaccount. -type AreSubaccountsLiquidatableResponse struct { - Results []AreSubaccountsLiquidatableResponse_Result `protobuf:"bytes,1,rep,name=results,proto3" json:"results"` -} - -func (m *AreSubaccountsLiquidatableResponse) Reset() { *m = AreSubaccountsLiquidatableResponse{} } -func (m *AreSubaccountsLiquidatableResponse) String() string { return proto.CompactTextString(m) } -func (*AreSubaccountsLiquidatableResponse) ProtoMessage() {} -func (*AreSubaccountsLiquidatableResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_3365c195b25c5bc0, []int{5} -} -func (m *AreSubaccountsLiquidatableResponse) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *AreSubaccountsLiquidatableResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_AreSubaccountsLiquidatableResponse.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *AreSubaccountsLiquidatableResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_AreSubaccountsLiquidatableResponse.Merge(m, src) -} -func (m *AreSubaccountsLiquidatableResponse) XXX_Size() int { - return m.Size() -} -func (m *AreSubaccountsLiquidatableResponse) XXX_DiscardUnknown() { - xxx_messageInfo_AreSubaccountsLiquidatableResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_AreSubaccountsLiquidatableResponse proto.InternalMessageInfo - -func (m *AreSubaccountsLiquidatableResponse) GetResults() []AreSubaccountsLiquidatableResponse_Result { - if m != nil { - return m.Results - } - return nil -} - -// Result returns whether a subaccount should be liquidated. -type AreSubaccountsLiquidatableResponse_Result struct { - SubaccountId types.SubaccountId `protobuf:"bytes,1,opt,name=subaccount_id,json=subaccountId,proto3" json:"subaccount_id"` - IsLiquidatable bool `protobuf:"varint,2,opt,name=is_liquidatable,json=isLiquidatable,proto3" json:"is_liquidatable,omitempty"` -} - -func (m *AreSubaccountsLiquidatableResponse_Result) Reset() { - *m = AreSubaccountsLiquidatableResponse_Result{} -} -func (m *AreSubaccountsLiquidatableResponse_Result) String() string { - return proto.CompactTextString(m) -} -func (*AreSubaccountsLiquidatableResponse_Result) ProtoMessage() {} -func (*AreSubaccountsLiquidatableResponse_Result) Descriptor() ([]byte, []int) { - return fileDescriptor_3365c195b25c5bc0, []int{5, 0} -} -func (m *AreSubaccountsLiquidatableResponse_Result) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *AreSubaccountsLiquidatableResponse_Result) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_AreSubaccountsLiquidatableResponse_Result.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *AreSubaccountsLiquidatableResponse_Result) XXX_Merge(src proto.Message) { - xxx_messageInfo_AreSubaccountsLiquidatableResponse_Result.Merge(m, src) -} -func (m *AreSubaccountsLiquidatableResponse_Result) XXX_Size() int { - return m.Size() -} -func (m *AreSubaccountsLiquidatableResponse_Result) XXX_DiscardUnknown() { - xxx_messageInfo_AreSubaccountsLiquidatableResponse_Result.DiscardUnknown(m) -} - -var xxx_messageInfo_AreSubaccountsLiquidatableResponse_Result proto.InternalMessageInfo - -func (m *AreSubaccountsLiquidatableResponse_Result) GetSubaccountId() types.SubaccountId { - if m != nil { - return m.SubaccountId - } - return types.SubaccountId{} -} - -func (m *AreSubaccountsLiquidatableResponse_Result) GetIsLiquidatable() bool { - if m != nil { - return m.IsLiquidatable - } - return false -} - // MevNodeToNodeCalculationRequest is a request message used to run the // MEV node <> node calculation. type MevNodeToNodeCalculationRequest struct { @@ -386,7 +235,7 @@ func (m *MevNodeToNodeCalculationRequest) Reset() { *m = MevNodeToNodeCa func (m *MevNodeToNodeCalculationRequest) String() string { return proto.CompactTextString(m) } func (*MevNodeToNodeCalculationRequest) ProtoMessage() {} func (*MevNodeToNodeCalculationRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_3365c195b25c5bc0, []int{6} + return fileDescriptor_3365c195b25c5bc0, []int{4} } func (m *MevNodeToNodeCalculationRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -439,7 +288,7 @@ func (m *MevNodeToNodeCalculationResponse) Reset() { *m = MevNodeToNodeC func (m *MevNodeToNodeCalculationResponse) String() string { return proto.CompactTextString(m) } func (*MevNodeToNodeCalculationResponse) ProtoMessage() {} func (*MevNodeToNodeCalculationResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_3365c195b25c5bc0, []int{7} + return fileDescriptor_3365c195b25c5bc0, []int{5} } func (m *MevNodeToNodeCalculationResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -490,7 +339,7 @@ func (m *MevNodeToNodeCalculationResponse_MevAndVolumePerClob) String() string { } func (*MevNodeToNodeCalculationResponse_MevAndVolumePerClob) ProtoMessage() {} func (*MevNodeToNodeCalculationResponse_MevAndVolumePerClob) Descriptor() ([]byte, []int) { - return fileDescriptor_3365c195b25c5bc0, []int{7, 0} + return fileDescriptor_3365c195b25c5bc0, []int{5, 0} } func (m *MevNodeToNodeCalculationResponse_MevAndVolumePerClob) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -551,7 +400,7 @@ func (m *QueryEquityTierLimitConfigurationRequest) Reset() { func (m *QueryEquityTierLimitConfigurationRequest) String() string { return proto.CompactTextString(m) } func (*QueryEquityTierLimitConfigurationRequest) ProtoMessage() {} func (*QueryEquityTierLimitConfigurationRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_3365c195b25c5bc0, []int{8} + return fileDescriptor_3365c195b25c5bc0, []int{6} } func (m *QueryEquityTierLimitConfigurationRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -594,7 +443,7 @@ func (m *QueryEquityTierLimitConfigurationResponse) String() string { } func (*QueryEquityTierLimitConfigurationResponse) ProtoMessage() {} func (*QueryEquityTierLimitConfigurationResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_3365c195b25c5bc0, []int{9} + return fileDescriptor_3365c195b25c5bc0, []int{7} } func (m *QueryEquityTierLimitConfigurationResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -641,7 +490,7 @@ func (m *QueryBlockRateLimitConfigurationRequest) Reset() { func (m *QueryBlockRateLimitConfigurationRequest) String() string { return proto.CompactTextString(m) } func (*QueryBlockRateLimitConfigurationRequest) ProtoMessage() {} func (*QueryBlockRateLimitConfigurationRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_3365c195b25c5bc0, []int{10} + return fileDescriptor_3365c195b25c5bc0, []int{8} } func (m *QueryBlockRateLimitConfigurationRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -682,7 +531,7 @@ func (m *QueryBlockRateLimitConfigurationResponse) Reset() { func (m *QueryBlockRateLimitConfigurationResponse) String() string { return proto.CompactTextString(m) } func (*QueryBlockRateLimitConfigurationResponse) ProtoMessage() {} func (*QueryBlockRateLimitConfigurationResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_3365c195b25c5bc0, []int{11} + return fileDescriptor_3365c195b25c5bc0, []int{9} } func (m *QueryBlockRateLimitConfigurationResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -727,7 +576,7 @@ func (m *QueryLiquidationsConfigurationRequest) Reset() { *m = QueryLiqu func (m *QueryLiquidationsConfigurationRequest) String() string { return proto.CompactTextString(m) } func (*QueryLiquidationsConfigurationRequest) ProtoMessage() {} func (*QueryLiquidationsConfigurationRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_3365c195b25c5bc0, []int{12} + return fileDescriptor_3365c195b25c5bc0, []int{10} } func (m *QueryLiquidationsConfigurationRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -768,7 +617,7 @@ func (m *QueryLiquidationsConfigurationResponse) Reset() { func (m *QueryLiquidationsConfigurationResponse) String() string { return proto.CompactTextString(m) } func (*QueryLiquidationsConfigurationResponse) ProtoMessage() {} func (*QueryLiquidationsConfigurationResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_3365c195b25c5bc0, []int{13} + return fileDescriptor_3365c195b25c5bc0, []int{11} } func (m *QueryLiquidationsConfigurationResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -809,9 +658,6 @@ func init() { proto.RegisterType((*QueryClobPairResponse)(nil), "dydxprotocol.clob.QueryClobPairResponse") proto.RegisterType((*QueryAllClobPairRequest)(nil), "dydxprotocol.clob.QueryAllClobPairRequest") proto.RegisterType((*QueryClobPairAllResponse)(nil), "dydxprotocol.clob.QueryClobPairAllResponse") - proto.RegisterType((*AreSubaccountsLiquidatableRequest)(nil), "dydxprotocol.clob.AreSubaccountsLiquidatableRequest") - proto.RegisterType((*AreSubaccountsLiquidatableResponse)(nil), "dydxprotocol.clob.AreSubaccountsLiquidatableResponse") - proto.RegisterType((*AreSubaccountsLiquidatableResponse_Result)(nil), "dydxprotocol.clob.AreSubaccountsLiquidatableResponse.Result") proto.RegisterType((*MevNodeToNodeCalculationRequest)(nil), "dydxprotocol.clob.MevNodeToNodeCalculationRequest") proto.RegisterType((*MevNodeToNodeCalculationResponse)(nil), "dydxprotocol.clob.MevNodeToNodeCalculationResponse") proto.RegisterType((*MevNodeToNodeCalculationResponse_MevAndVolumePerClob)(nil), "dydxprotocol.clob.MevNodeToNodeCalculationResponse.MevAndVolumePerClob") @@ -826,75 +672,67 @@ func init() { func init() { proto.RegisterFile("dydxprotocol/clob/query.proto", fileDescriptor_3365c195b25c5bc0) } var fileDescriptor_3365c195b25c5bc0 = []byte{ - // 1084 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0xcf, 0x6f, 0x1b, 0xc5, - 0x1f, 0xcd, 0x38, 0xfd, 0xe6, 0x9b, 0x7e, 0xda, 0x04, 0x98, 0x34, 0x6d, 0x70, 0x52, 0xc7, 0x59, - 0x88, 0x63, 0xa7, 0x62, 0x97, 0xa6, 0x01, 0x41, 0x5a, 0x21, 0x25, 0x11, 0x44, 0x95, 0x12, 0xe4, - 0x6e, 0xab, 0x22, 0x41, 0xa5, 0xd5, 0x7a, 0x77, 0x70, 0x46, 0x5d, 0xef, 0x38, 0xfb, 0x4b, 0x89, - 0x10, 0x17, 0x84, 0x90, 0x2a, 0x38, 0x20, 0x71, 0xe0, 0xc0, 0x91, 0xbf, 0x82, 0x03, 0x02, 0x6e, - 0x3d, 0x56, 0xe2, 0xc2, 0x01, 0x21, 0x94, 0x70, 0xe6, 0x6f, 0x40, 0x3b, 0x3b, 0xb6, 0x77, 0xbd, - 0x3f, 0x9c, 0xf8, 0xe2, 0xd8, 0x33, 0x6f, 0xde, 0xbc, 0x37, 0x6f, 0xf6, 0xf3, 0xd9, 0xc0, 0x4d, - 0xf3, 0xc4, 0x3c, 0xee, 0x3a, 0xcc, 0x63, 0x06, 0xb3, 0x14, 0xc3, 0x62, 0x2d, 0xe5, 0xc8, 0x27, - 0xce, 0x89, 0xcc, 0xc7, 0xf0, 0x2b, 0xf1, 0x69, 0x39, 0x9c, 0x2e, 0x5f, 0x6b, 0xb3, 0x36, 0xe3, - 0x43, 0x4a, 0xf8, 0x2d, 0x02, 0x96, 0x97, 0xda, 0x8c, 0xb5, 0x2d, 0xa2, 0xe8, 0x5d, 0xaa, 0xe8, - 0xb6, 0xcd, 0x3c, 0xdd, 0xa3, 0xcc, 0x76, 0xc5, 0xec, 0xba, 0xc1, 0xdc, 0x0e, 0x73, 0x95, 0x96, - 0xee, 0x92, 0x88, 0x5f, 0x09, 0x6e, 0xb7, 0x88, 0xa7, 0xdf, 0x56, 0xba, 0x7a, 0x9b, 0xda, 0x1c, - 0x2c, 0xb0, 0x4a, 0x5a, 0x51, 0xcb, 0x62, 0xc6, 0x53, 0xcd, 0xd1, 0x3d, 0xa2, 0x59, 0xb4, 0x43, - 0x3d, 0xcd, 0x60, 0xf6, 0xa7, 0xb4, 0x2d, 0x16, 0xac, 0xa4, 0x17, 0x84, 0x1f, 0x5a, 0x57, 0xa7, - 0x8e, 0x80, 0xbc, 0x99, 0x86, 0x90, 0x23, 0x9f, 0x7a, 0x27, 0x9a, 0x47, 0x89, 0x93, 0x45, 0x7a, - 0x2b, 0xbd, 0xc2, 0xa2, 0x47, 0x3e, 0x35, 0x23, 0x5f, 0x49, 0xf0, 0x62, 0x1a, 0xdc, 0x21, 0x81, - 0x98, 0x6c, 0x24, 0x26, 0x5d, 0xbf, 0xa5, 0x1b, 0x06, 0xf3, 0x6d, 0xcf, 0x8d, 0x7d, 0x8f, 0xa0, - 0x52, 0x03, 0x6e, 0x3c, 0x08, 0x0f, 0x67, 0x8f, 0x78, 0xbb, 0x16, 0x6b, 0x35, 0x75, 0xea, 0xa8, - 0xe4, 0xc8, 0x27, 0xae, 0x87, 0x67, 0xa1, 0x44, 0xcd, 0x05, 0x54, 0x45, 0xf5, 0x19, 0xb5, 0x44, - 0x4d, 0xe9, 0x23, 0x98, 0xe7, 0xd0, 0x01, 0xce, 0xed, 0x32, 0xdb, 0x25, 0xf8, 0x3d, 0xb8, 0xdc, - 0x77, 0xcf, 0xf1, 0x57, 0x36, 0x16, 0xe5, 0x54, 0x8a, 0x72, 0x6f, 0xdd, 0xce, 0xa5, 0xe7, 0x7f, - 0x2d, 0x4f, 0xa8, 0xd3, 0x86, 0xf8, 0x2d, 0xe9, 0x42, 0xc3, 0xb6, 0x65, 0x0d, 0x6b, 0xf8, 0x00, - 0x60, 0x90, 0x96, 0xe0, 0xae, 0xc9, 0x51, 0xb4, 0x72, 0x18, 0xad, 0x1c, 0x5d, 0x1d, 0x11, 0xad, - 0xdc, 0xd4, 0xdb, 0x44, 0xac, 0x55, 0x63, 0x2b, 0xa5, 0x1f, 0x11, 0x2c, 0x24, 0xc4, 0x6f, 0x5b, - 0x56, 0x9e, 0xfe, 0xc9, 0x0b, 0xea, 0xc7, 0x7b, 0x09, 0x91, 0x25, 0x2e, 0x72, 0x6d, 0xa4, 0xc8, - 0x68, 0xf3, 0x84, 0xca, 0x63, 0x58, 0xd9, 0x76, 0xc8, 0xc3, 0x41, 0x5e, 0xfb, 0x22, 0x7f, 0xbd, - 0x65, 0xf5, 0x6c, 0xe1, 0x87, 0x30, 0x3b, 0x48, 0x51, 0xa3, 0xa6, 0x2b, 0x24, 0xd7, 0x92, 0x92, - 0x63, 0xa9, 0xcb, 0x03, 0xc6, 0xfb, 0xa6, 0x50, 0x3f, 0xe3, 0xc6, 0xc6, 0x5c, 0xe9, 0x59, 0x09, - 0xa4, 0xa2, 0xad, 0xc5, 0x49, 0x3d, 0x81, 0xff, 0x3b, 0xc4, 0xf5, 0x2d, 0xaf, 0xb7, 0xe9, 0xbd, - 0x8c, 0x73, 0x1a, 0xcd, 0x23, 0xab, 0x9c, 0x44, 0x48, 0xe9, 0x51, 0x96, 0xbf, 0x44, 0x30, 0x15, - 0xcd, 0xe0, 0x07, 0x30, 0x93, 0x30, 0xd9, 0x8f, 0xfe, 0x22, 0x1e, 0xaf, 0xc6, 0x3d, 0xe2, 0x35, - 0x78, 0x89, 0xba, 0x9a, 0x15, 0x93, 0xc3, 0xa3, 0x9a, 0x56, 0x67, 0x69, 0x42, 0xa4, 0xf4, 0x27, - 0x82, 0xe5, 0x03, 0x12, 0x7c, 0xc8, 0x4c, 0xf2, 0x88, 0x85, 0x9f, 0xbb, 0xba, 0x65, 0xf8, 0x16, - 0x8f, 0xa8, 0x17, 0xc2, 0x13, 0xb8, 0x1e, 0x55, 0x88, 0xae, 0xc3, 0xba, 0xcc, 0x25, 0x8e, 0xd6, - 0xd1, 0x3d, 0xe3, 0x90, 0xb8, 0xd9, 0x42, 0xf9, 0xb9, 0x3c, 0xd6, 0xad, 0x70, 0x0f, 0xe6, 0x1c, - 0x90, 0xe0, 0x20, 0x42, 0xab, 0xd7, 0x38, 0x4b, 0x53, 0x90, 0x88, 0x51, 0xfc, 0x09, 0xcc, 0x07, - 0x3d, 0xb0, 0xd6, 0x21, 0x81, 0xd6, 0x21, 0x9e, 0x43, 0x0d, 0xb7, 0x7f, 0xb7, 0xd2, 0xe4, 0x09, - 0xc1, 0x07, 0x11, 0x5c, 0x9d, 0x0b, 0xe2, 0x5b, 0x46, 0x83, 0xd2, 0xbf, 0x08, 0xaa, 0xf9, 0xf6, - 0x44, 0xd0, 0xed, 0xe1, 0xa0, 0xf7, 0x46, 0xed, 0x99, 0xc1, 0x12, 0x02, 0xb6, 0x6d, 0xf3, 0x31, - 0xb3, 0xfc, 0x0e, 0x69, 0x12, 0x27, 0x7c, 0x80, 0x86, 0x33, 0xd7, 0x61, 0x2e, 0x03, 0x85, 0xab, - 0x70, 0xb5, 0xff, 0x48, 0x6a, 0xfd, 0x2a, 0x04, 0xbd, 0x47, 0xee, 0xbe, 0x89, 0x5f, 0x86, 0xc9, - 0x0e, 0x09, 0xf8, 0x89, 0x94, 0xd4, 0xf0, 0x2b, 0xbe, 0x0e, 0x53, 0x01, 0x27, 0x59, 0x98, 0xac, - 0xa2, 0xfa, 0x25, 0x55, 0xfc, 0x92, 0xd6, 0xa1, 0xce, 0x1f, 0xfd, 0xf7, 0x79, 0xf9, 0x7d, 0x44, - 0x89, 0xb3, 0x1f, 0x16, 0xdf, 0x5d, 0x5e, 0x4e, 0x7d, 0x27, 0x9e, 0xab, 0xf4, 0x03, 0x82, 0xc6, - 0x39, 0xc0, 0xe2, 0x94, 0x6c, 0x58, 0xc8, 0xab, 0xe9, 0xe2, 0x1e, 0x28, 0x19, 0xc7, 0x56, 0x44, - 0x2d, 0x8e, 0x67, 0x9e, 0x64, 0x61, 0xa4, 0x06, 0xac, 0x71, 0x71, 0x3b, 0xe1, 0xa5, 0x51, 0x75, - 0x8f, 0xe4, 0x1b, 0xf9, 0x1e, 0x09, 0xd7, 0x85, 0x58, 0xe1, 0xe3, 0x29, 0xdc, 0xc8, 0xe9, 0x77, - 0xc2, 0x86, 0x9c, 0x61, 0xa3, 0x80, 0x58, 0xb8, 0x88, 0x2e, 0xf7, 0x10, 0x44, 0x5a, 0x83, 0x55, - 0x2e, 0x6c, 0x3f, 0xd6, 0xdb, 0x32, 0x2d, 0x7c, 0x85, 0xa0, 0x36, 0x0a, 0xd9, 0xaf, 0x4b, 0x73, - 0x19, 0xad, 0x52, 0x88, 0x5f, 0xcd, 0x10, 0x9f, 0xa6, 0x14, 0x9a, 0xb1, 0x95, 0x9a, 0xd9, 0xf8, - 0xe9, 0x32, 0xfc, 0x8f, 0x0b, 0xc1, 0x5f, 0x23, 0x98, 0xee, 0xb5, 0x01, 0xbc, 0x9e, 0xc1, 0x9b, - 0xd3, 0x4b, 0xcb, 0xf5, 0x3c, 0xec, 0x70, 0x33, 0x95, 0x1a, 0x5f, 0xfc, 0xfe, 0xcf, 0x77, 0xa5, - 0xd7, 0xf0, 0x8a, 0x52, 0xf0, 0x8e, 0xa1, 0x7c, 0x46, 0xcd, 0xcf, 0xf1, 0x37, 0x08, 0xae, 0xc4, - 0xfa, 0x59, 0xbe, 0xa0, 0x74, 0x63, 0x2d, 0xdf, 0x1a, 0x25, 0x28, 0xd6, 0x20, 0xa5, 0xd7, 0xb9, - 0xa6, 0x0a, 0x5e, 0x2a, 0xd2, 0x84, 0x9f, 0x21, 0x28, 0xe7, 0xd7, 0x7e, 0xbc, 0x79, 0xc1, 0x56, - 0x11, 0xe9, 0x7c, 0x6b, 0xac, 0x06, 0x83, 0x7f, 0x41, 0xb0, 0x90, 0x57, 0x9e, 0xf0, 0xc6, 0x85, - 0x6a, 0x59, 0xa4, 0xe3, 0xce, 0x18, 0xf5, 0x4f, 0xda, 0xe2, 0xe7, 0xb6, 0xb9, 0x85, 0xd6, 0x25, - 0x45, 0xc9, 0x7c, 0x61, 0xd3, 0x6c, 0x66, 0x12, 0xcd, 0x63, 0xd1, 0x5f, 0x23, 0x26, 0xf2, 0x37, - 0x04, 0x4b, 0x45, 0x95, 0x02, 0xdf, 0xcd, 0x4b, 0xf0, 0x1c, 0x75, 0xae, 0x7c, 0x6f, 0xbc, 0xc5, - 0xc2, 0x57, 0x8d, 0xfb, 0xaa, 0xe2, 0x8a, 0x52, 0xf8, 0x92, 0x8b, 0x7f, 0x46, 0xb0, 0x58, 0x50, - 0x26, 0xf0, 0x56, 0x9e, 0x8a, 0xd1, 0x05, 0xae, 0x7c, 0x77, 0xac, 0xb5, 0xc2, 0xc0, 0x2a, 0x37, - 0xb0, 0x8c, 0x6f, 0x16, 0xbe, 0xf9, 0xe3, 0x5f, 0x11, 0xbc, 0x9a, 0x5b, 0x7c, 0xf0, 0x3b, 0x79, - 0x0a, 0x46, 0x55, 0xb6, 0xf2, 0xbb, 0x63, 0xac, 0x14, 0xca, 0x65, 0xae, 0xbc, 0x8e, 0x6b, 0xca, - 0xb9, 0xfe, 0x5b, 0xd8, 0x69, 0x3e, 0x3f, 0xad, 0xa0, 0x17, 0xa7, 0x15, 0xf4, 0xf7, 0x69, 0x05, - 0x7d, 0x7b, 0x56, 0x99, 0x78, 0x71, 0x56, 0x99, 0xf8, 0xe3, 0xac, 0x32, 0xf1, 0xf1, 0xdb, 0x6d, - 0xea, 0x1d, 0xfa, 0x2d, 0xd9, 0x60, 0x9d, 0x24, 0x57, 0xb0, 0xf9, 0x86, 0x71, 0xa8, 0x53, 0x5b, - 0xe9, 0x8f, 0x1c, 0x47, 0xfc, 0xde, 0x49, 0x97, 0xb8, 0xad, 0x29, 0x3e, 0x7c, 0xe7, 0xbf, 0x00, - 0x00, 0x00, 0xff, 0xff, 0xd2, 0x5a, 0xb5, 0xd4, 0xc7, 0x0d, 0x00, 0x00, + // 946 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xcf, 0x6f, 0xdc, 0x44, + 0x14, 0xce, 0x6c, 0x4a, 0x09, 0x53, 0x40, 0x30, 0x69, 0xda, 0x65, 0x93, 0x6e, 0xb6, 0x86, 0x24, + 0x9b, 0x54, 0x78, 0x68, 0x5a, 0x21, 0x48, 0x11, 0x52, 0x12, 0x41, 0x84, 0xd4, 0xa0, 0xc5, 0xaa, + 0x8a, 0x04, 0x95, 0xac, 0x59, 0x7b, 0x70, 0x46, 0x1d, 0x7b, 0x36, 0xf6, 0xd8, 0x6a, 0x84, 0xb8, + 0x70, 0xe0, 0x02, 0x07, 0x24, 0x0e, 0x1c, 0x38, 0x72, 0xe6, 0x4f, 0x40, 0xc0, 0xad, 0xc7, 0x4a, + 0x5c, 0x38, 0x20, 0x84, 0x12, 0xce, 0xfc, 0x0d, 0x95, 0xc7, 0xb3, 0x5b, 0x6f, 0xfc, 0x63, 0x93, + 0xbd, 0xec, 0xda, 0x33, 0xdf, 0x7b, 0xfe, 0xbe, 0xf7, 0xde, 0x7c, 0x36, 0xbc, 0xe6, 0x1e, 0xb9, + 0x8f, 0x06, 0xa1, 0x90, 0xc2, 0x11, 0x1c, 0x3b, 0x5c, 0xf4, 0xf1, 0x61, 0x4c, 0xc3, 0x23, 0x53, + 0xad, 0xa1, 0x57, 0xf3, 0xdb, 0x66, 0xba, 0xdd, 0xba, 0xec, 0x09, 0x4f, 0xa8, 0x25, 0x9c, 0x5e, + 0x65, 0xc0, 0xd6, 0x92, 0x27, 0x84, 0xc7, 0x29, 0x26, 0x03, 0x86, 0x49, 0x10, 0x08, 0x49, 0x24, + 0x13, 0x41, 0xa4, 0x77, 0x37, 0x1c, 0x11, 0xf9, 0x22, 0xc2, 0x7d, 0x12, 0xd1, 0x2c, 0x3f, 0x4e, + 0x6e, 0xf6, 0xa9, 0x24, 0x37, 0xf1, 0x80, 0x78, 0x2c, 0x50, 0x60, 0x8d, 0xc5, 0x45, 0x46, 0x7d, + 0x2e, 0x9c, 0x87, 0x76, 0x48, 0x24, 0xb5, 0x39, 0xf3, 0x99, 0xb4, 0x1d, 0x11, 0x7c, 0xc1, 0x3c, + 0x1d, 0x70, 0xbd, 0x18, 0x90, 0xfe, 0xd8, 0x03, 0xc2, 0x42, 0x0d, 0x79, 0xab, 0x08, 0xa1, 0x87, + 0x31, 0x93, 0x47, 0xb6, 0x64, 0x34, 0x2c, 0x4b, 0x7a, 0xa3, 0x18, 0xc1, 0xd9, 0x61, 0xcc, 0xdc, + 0x4c, 0xd7, 0x38, 0x78, 0xb1, 0x08, 0xf6, 0x69, 0x92, 0x6d, 0x1a, 0xeb, 0xf0, 0xea, 0x27, 0xa9, + 0xe2, 0x3d, 0x2a, 0x77, 0xb9, 0xe8, 0xf7, 0x08, 0x0b, 0x2d, 0x7a, 0x18, 0xd3, 0x48, 0xa2, 0x97, + 0x61, 0x83, 0xb9, 0x4d, 0xd0, 0x01, 0xdd, 0x97, 0xac, 0x06, 0x73, 0x8d, 0x4f, 0xe1, 0x82, 0x82, + 0x3e, 0xc3, 0x45, 0x03, 0x11, 0x44, 0x14, 0xbd, 0x0f, 0x5f, 0x18, 0x49, 0x52, 0xf8, 0x4b, 0x9b, + 0x8b, 0x66, 0xa1, 0x35, 0xe6, 0x30, 0x6e, 0xe7, 0xc2, 0xe3, 0x7f, 0x96, 0x67, 0xac, 0x39, 0x47, + 0xdf, 0x1b, 0x44, 0x73, 0xd8, 0xe6, 0xfc, 0x34, 0x87, 0x0f, 0x21, 0x7c, 0xd6, 0x02, 0x9d, 0x7b, + 0xd5, 0xcc, 0xfa, 0x65, 0xa6, 0xfd, 0x32, 0xb3, 0x79, 0xd0, 0xfd, 0x32, 0x7b, 0xc4, 0xa3, 0x3a, + 0xd6, 0xca, 0x45, 0x1a, 0x3f, 0x03, 0xd8, 0x1c, 0x23, 0xbf, 0xcd, 0x79, 0x15, 0xff, 0xd9, 0x73, + 0xf2, 0x47, 0x7b, 0x63, 0x24, 0x1b, 0x8a, 0xe4, 0xda, 0x44, 0x92, 0xd9, 0xc3, 0xc7, 0x58, 0xfe, + 0x0d, 0xe0, 0xf2, 0x3e, 0x4d, 0x3e, 0x16, 0x2e, 0xbd, 0x27, 0xd2, 0xdf, 0x5d, 0xc2, 0x9d, 0x98, + 0xab, 0xcd, 0x61, 0x45, 0x1e, 0xc0, 0x2b, 0xd9, 0xc0, 0x0d, 0x42, 0x31, 0x10, 0x11, 0x0d, 0x6d, + 0x9f, 0x48, 0xe7, 0x80, 0x46, 0xa3, 0xea, 0x14, 0x99, 0xdf, 0x27, 0x3c, 0x1d, 0x0d, 0x11, 0xee, + 0xd3, 0x64, 0x3f, 0x43, 0x5b, 0x97, 0x55, 0x96, 0x9e, 0x4e, 0xa2, 0x57, 0xd1, 0xe7, 0x70, 0x21, + 0x19, 0x82, 0x6d, 0x9f, 0x26, 0xb6, 0x4f, 0x65, 0xc8, 0x9c, 0x68, 0xa4, 0xaa, 0x98, 0x7c, 0x8c, + 0xf0, 0x7e, 0x06, 0xb7, 0xe6, 0x93, 0xfc, 0x23, 0xb3, 0x45, 0xe3, 0x7f, 0x00, 0x3b, 0xd5, 0xf2, + 0x74, 0x33, 0x3c, 0xf8, 0x7c, 0x48, 0xa3, 0x98, 0xcb, 0x48, 0xb7, 0x62, 0x6f, 0xd2, 0x33, 0x4b, + 0xb2, 0xa4, 0x80, 0xed, 0xc0, 0xbd, 0x2f, 0x78, 0xec, 0xd3, 0x1e, 0x0d, 0xd3, 0xd6, 0xe9, 0xb6, + 0x0d, 0xb3, 0xb7, 0x08, 0x9c, 0x2f, 0x41, 0xa1, 0x0e, 0x7c, 0x71, 0x34, 0x0c, 0xf6, 0x68, 0xfe, + 0xe1, 0xb0, 0xd9, 0x1f, 0xb9, 0xe8, 0x15, 0x38, 0xeb, 0xd3, 0x44, 0x55, 0xa4, 0x61, 0xa5, 0x97, + 0xe8, 0x0a, 0xbc, 0x98, 0xa8, 0x24, 0xcd, 0xd9, 0x0e, 0xe8, 0x5e, 0xb0, 0xf4, 0x9d, 0xb1, 0x01, + 0xbb, 0x6a, 0xe8, 0x3e, 0x50, 0xa7, 0xf9, 0x1e, 0xa3, 0xe1, 0xdd, 0xf4, 0x2c, 0xef, 0xaa, 0xd3, + 0x19, 0x87, 0xf9, 0xbe, 0x1a, 0x3f, 0x01, 0xb8, 0x7e, 0x06, 0xb0, 0xae, 0x52, 0x00, 0x9b, 0x55, + 0x16, 0xa1, 0xe7, 0x00, 0x97, 0x94, 0xad, 0x2e, 0xb5, 0x2e, 0xcf, 0x02, 0x2d, 0xc3, 0x18, 0xeb, + 0x70, 0x4d, 0x91, 0xdb, 0x49, 0x87, 0xc6, 0x22, 0x92, 0x56, 0x0b, 0xf9, 0x11, 0x68, 0xd5, 0xb5, + 0x58, 0xad, 0xe3, 0x21, 0xbc, 0x5a, 0x61, 0x9f, 0x5a, 0x86, 0x59, 0x22, 0xa3, 0x26, 0xb1, 0x56, + 0x91, 0x0d, 0xf7, 0x29, 0x88, 0xb1, 0x06, 0x57, 0x14, 0xb1, 0xbb, 0x39, 0xab, 0x2c, 0x95, 0xf0, + 0x0d, 0x80, 0xab, 0x93, 0x90, 0x5a, 0xc0, 0x03, 0x38, 0x5f, 0xe2, 0xbc, 0x9a, 0xfc, 0x4a, 0x09, + 0xf9, 0x62, 0x4a, 0xcd, 0x19, 0xf1, 0xc2, 0xce, 0xe6, 0x2f, 0x73, 0xf0, 0x39, 0x45, 0x04, 0x7d, + 0x0b, 0xe0, 0xdc, 0xd0, 0x80, 0xd0, 0x46, 0x49, 0xde, 0x0a, 0x17, 0x6f, 0x75, 0xab, 0xb0, 0xa7, + 0x6d, 0xdc, 0x58, 0xff, 0xfa, 0xcf, 0xff, 0x7e, 0x68, 0xbc, 0x8e, 0xae, 0xe3, 0x9a, 0x57, 0x16, + 0xfe, 0x92, 0xb9, 0x5f, 0xa1, 0xef, 0x00, 0xbc, 0x94, 0x73, 0xd2, 0x6a, 0x42, 0x45, 0x4b, 0x6f, + 0xdd, 0x98, 0x44, 0x28, 0x67, 0xcd, 0xc6, 0x1b, 0x8a, 0x53, 0x1b, 0x2d, 0xd5, 0x71, 0x42, 0xbf, + 0x01, 0xd8, 0xac, 0xb2, 0x04, 0xb4, 0x79, 0x2e, 0xff, 0xc8, 0x38, 0xde, 0x9a, 0xc2, 0x73, 0x8c, + 0x2d, 0xc5, 0xf5, 0xf6, 0x16, 0xd8, 0x30, 0x30, 0x2e, 0x7d, 0xe7, 0xda, 0x81, 0x70, 0xa9, 0x2d, + 0x45, 0xf6, 0xef, 0xe4, 0x48, 0xfe, 0x01, 0xe0, 0x52, 0xdd, 0xe9, 0x44, 0x77, 0xaa, 0xaa, 0x76, + 0x06, 0x6f, 0x69, 0xbd, 0x37, 0x5d, 0xb0, 0xd6, 0xb5, 0xaa, 0x74, 0x75, 0x50, 0x1b, 0xd7, 0x7e, + 0xa7, 0xa0, 0x5f, 0x01, 0x5c, 0xac, 0x39, 0x9a, 0x68, 0xab, 0x8a, 0xc5, 0x64, 0x53, 0x69, 0xdd, + 0x99, 0x2a, 0x56, 0x0b, 0x58, 0x51, 0x02, 0x96, 0xd1, 0xb5, 0xda, 0x8f, 0x37, 0xf4, 0x3b, 0x80, + 0xaf, 0x55, 0x1e, 0x78, 0xf4, 0x4e, 0x15, 0x83, 0x49, 0x6e, 0xd2, 0x7a, 0x77, 0x8a, 0x48, 0xcd, + 0xdc, 0x54, 0xcc, 0xbb, 0x68, 0x15, 0x9f, 0xe9, 0x83, 0x6f, 0xa7, 0xf7, 0xf8, 0xb8, 0x0d, 0x9e, + 0x1c, 0xb7, 0xc1, 0xbf, 0xc7, 0x6d, 0xf0, 0xfd, 0x49, 0x7b, 0xe6, 0xc9, 0x49, 0x7b, 0xe6, 0xaf, + 0x93, 0xf6, 0xcc, 0x67, 0x6f, 0x7b, 0x4c, 0x1e, 0xc4, 0x7d, 0xd3, 0x11, 0xfe, 0x78, 0xae, 0xe4, + 0xf6, 0x9b, 0xce, 0x01, 0x61, 0x01, 0x1e, 0xad, 0x3c, 0xca, 0xf2, 0xcb, 0xa3, 0x01, 0x8d, 0xfa, + 0x17, 0xd5, 0xf2, 0xad, 0xa7, 0x01, 0x00, 0x00, 0xff, 0xff, 0x75, 0xf9, 0x20, 0xb1, 0x8a, 0x0b, + 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -913,8 +751,6 @@ type QueryClient interface { ClobPair(ctx context.Context, in *QueryGetClobPairRequest, opts ...grpc.CallOption) (*QueryClobPairResponse, error) // Queries a list of ClobPair items. ClobPairAll(ctx context.Context, in *QueryAllClobPairRequest, opts ...grpc.CallOption) (*QueryClobPairAllResponse, error) - // Returns whether a subaccount is liquidatable. - AreSubaccountsLiquidatable(ctx context.Context, in *AreSubaccountsLiquidatableRequest, opts ...grpc.CallOption) (*AreSubaccountsLiquidatableResponse, error) // Runs the MEV node <> node calculation with the provided parameters. MevNodeToNodeCalculation(ctx context.Context, in *MevNodeToNodeCalculationRequest, opts ...grpc.CallOption) (*MevNodeToNodeCalculationResponse, error) // Queries EquityTierLimitConfiguration. @@ -951,15 +787,6 @@ func (c *queryClient) ClobPairAll(ctx context.Context, in *QueryAllClobPairReque return out, nil } -func (c *queryClient) AreSubaccountsLiquidatable(ctx context.Context, in *AreSubaccountsLiquidatableRequest, opts ...grpc.CallOption) (*AreSubaccountsLiquidatableResponse, error) { - out := new(AreSubaccountsLiquidatableResponse) - err := c.cc.Invoke(ctx, "/dydxprotocol.clob.Query/AreSubaccountsLiquidatable", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *queryClient) MevNodeToNodeCalculation(ctx context.Context, in *MevNodeToNodeCalculationRequest, opts ...grpc.CallOption) (*MevNodeToNodeCalculationResponse, error) { out := new(MevNodeToNodeCalculationResponse) err := c.cc.Invoke(ctx, "/dydxprotocol.clob.Query/MevNodeToNodeCalculation", in, out, opts...) @@ -1002,8 +829,6 @@ type QueryServer interface { ClobPair(context.Context, *QueryGetClobPairRequest) (*QueryClobPairResponse, error) // Queries a list of ClobPair items. ClobPairAll(context.Context, *QueryAllClobPairRequest) (*QueryClobPairAllResponse, error) - // Returns whether a subaccount is liquidatable. - AreSubaccountsLiquidatable(context.Context, *AreSubaccountsLiquidatableRequest) (*AreSubaccountsLiquidatableResponse, error) // Runs the MEV node <> node calculation with the provided parameters. MevNodeToNodeCalculation(context.Context, *MevNodeToNodeCalculationRequest) (*MevNodeToNodeCalculationResponse, error) // Queries EquityTierLimitConfiguration. @@ -1024,9 +849,6 @@ func (*UnimplementedQueryServer) ClobPair(ctx context.Context, req *QueryGetClob func (*UnimplementedQueryServer) ClobPairAll(ctx context.Context, req *QueryAllClobPairRequest) (*QueryClobPairAllResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ClobPairAll not implemented") } -func (*UnimplementedQueryServer) AreSubaccountsLiquidatable(ctx context.Context, req *AreSubaccountsLiquidatableRequest) (*AreSubaccountsLiquidatableResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method AreSubaccountsLiquidatable not implemented") -} func (*UnimplementedQueryServer) MevNodeToNodeCalculation(ctx context.Context, req *MevNodeToNodeCalculationRequest) (*MevNodeToNodeCalculationResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method MevNodeToNodeCalculation not implemented") } @@ -1080,24 +902,6 @@ func _Query_ClobPairAll_Handler(srv interface{}, ctx context.Context, dec func(i return interceptor(ctx, in, info, handler) } -func _Query_AreSubaccountsLiquidatable_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(AreSubaccountsLiquidatableRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(QueryServer).AreSubaccountsLiquidatable(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/dydxprotocol.clob.Query/AreSubaccountsLiquidatable", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).AreSubaccountsLiquidatable(ctx, req.(*AreSubaccountsLiquidatableRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _Query_MevNodeToNodeCalculation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(MevNodeToNodeCalculationRequest) if err := dec(in); err != nil { @@ -1182,10 +986,6 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "ClobPairAll", Handler: _Query_ClobPairAll_Handler, }, - { - MethodName: "AreSubaccountsLiquidatable", - Handler: _Query_AreSubaccountsLiquidatable_Handler, - }, { MethodName: "MevNodeToNodeCalculation", Handler: _Query_MevNodeToNodeCalculation_Handler, @@ -1352,123 +1152,6 @@ func (m *QueryClobPairAllResponse) MarshalToSizedBuffer(dAtA []byte) (int, error return len(dAtA) - i, nil } -func (m *AreSubaccountsLiquidatableRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *AreSubaccountsLiquidatableRequest) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *AreSubaccountsLiquidatableRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.SubaccountIds) > 0 { - for iNdEx := len(m.SubaccountIds) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.SubaccountIds[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintQuery(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - } - } - return len(dAtA) - i, nil -} - -func (m *AreSubaccountsLiquidatableResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *AreSubaccountsLiquidatableResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *AreSubaccountsLiquidatableResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Results) > 0 { - for iNdEx := len(m.Results) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.Results[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintQuery(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - } - } - return len(dAtA) - i, nil -} - -func (m *AreSubaccountsLiquidatableResponse_Result) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *AreSubaccountsLiquidatableResponse_Result) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *AreSubaccountsLiquidatableResponse_Result) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.IsLiquidatable { - i-- - if m.IsLiquidatable { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x10 - } - { - size, err := m.SubaccountId.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintQuery(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - return len(dAtA) - i, nil -} - func (m *MevNodeToNodeCalculationRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1826,50 +1509,6 @@ func (m *QueryClobPairAllResponse) Size() (n int) { return n } -func (m *AreSubaccountsLiquidatableRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.SubaccountIds) > 0 { - for _, e := range m.SubaccountIds { - l = e.Size() - n += 1 + l + sovQuery(uint64(l)) - } - } - return n -} - -func (m *AreSubaccountsLiquidatableResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.Results) > 0 { - for _, e := range m.Results { - l = e.Size() - n += 1 + l + sovQuery(uint64(l)) - } - } - return n -} - -func (m *AreSubaccountsLiquidatableResponse_Result) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = m.SubaccountId.Size() - n += 1 + l + sovQuery(uint64(l)) - if m.IsLiquidatable { - n += 2 - } - return n -} - func (m *MevNodeToNodeCalculationRequest) Size() (n int) { if m == nil { return 0 @@ -2344,277 +1983,6 @@ func (m *QueryClobPairAllResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *AreSubaccountsLiquidatableRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQuery - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: AreSubaccountsLiquidatableRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: AreSubaccountsLiquidatableRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SubaccountIds", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQuery - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthQuery - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthQuery - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.SubaccountIds = append(m.SubaccountIds, types.SubaccountId{}) - if err := m.SubaccountIds[len(m.SubaccountIds)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipQuery(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *AreSubaccountsLiquidatableResponse) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQuery - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: AreSubaccountsLiquidatableResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: AreSubaccountsLiquidatableResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Results", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQuery - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthQuery - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthQuery - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Results = append(m.Results, AreSubaccountsLiquidatableResponse_Result{}) - if err := m.Results[len(m.Results)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipQuery(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *AreSubaccountsLiquidatableResponse_Result) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQuery - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Result: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Result: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SubaccountId", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQuery - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthQuery - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthQuery - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := m.SubaccountId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field IsLiquidatable", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQuery - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.IsLiquidatable = bool(v != 0) - default: - iNdEx = preIndex - skippy, err := skipQuery(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} func (m *MevNodeToNodeCalculationRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/protocol/x/perpetuals/keeper/perpetual.go b/protocol/x/perpetuals/keeper/perpetual.go index 9d03408c48..c170c6c31b 100644 --- a/protocol/x/perpetuals/keeper/perpetual.go +++ b/protocol/x/perpetuals/keeper/perpetual.go @@ -768,6 +768,23 @@ func (k Keeper) GetNetNotional( return new(big.Int), err } + return GetNetNotionalInQuoteQuantums(perpetual, marketPrice, bigQuantums), nil +} + +// GetNetNotionalInQuoteQuantums returns the net notional in quote quantums, which can be +// represented by the following equation: +// +// `quantums / 10^baseAtomicResolution * marketPrice * 10^marketExponent * 10^quoteAtomicResolution`. +// Note that longs are positive, and shorts are negative. +// +// Also note that this is a stateless function. +func GetNetNotionalInQuoteQuantums( + perpetual types.Perpetual, + marketPrice pricestypes.MarketPrice, + bigQuantums *big.Int, +) ( + bigNetNotionalQuoteQuantums *big.Int, +) { bigQuoteQuantums := lib.BaseToQuoteQuantums( bigQuantums, perpetual.Params.AtomicResolution, @@ -775,7 +792,7 @@ func (k Keeper) GetNetNotional( marketPrice.Exponent, ) - return bigQuoteQuantums, nil + return bigQuoteQuantums } // GetNotionalInBaseQuantums returns the net notional in base quantums, which can be represented @@ -879,6 +896,29 @@ func (k Keeper) GetMarginRequirements( return nil, nil, err } + bigInitialMarginQuoteQuantums, + bigMaintenanceMarginQuoteQuantums = GetMarginRequirementsInQuoteQuantums( + perpetual, + marketPrice, + liquidityTier, + bigQuantums, + ) + return bigInitialMarginQuoteQuantums, bigMaintenanceMarginQuoteQuantums, nil +} + +// GetMarginRequirementsInQuoteQuantums returns initial and maintenance margin requirements +// in quote quantums, given the position size in base quantums. +// +// Note that this is a stateless function. +func GetMarginRequirementsInQuoteQuantums( + perpetual types.Perpetual, + marketPrice pricestypes.MarketPrice, + liquidityTier types.LiquidityTier, + bigQuantums *big.Int, +) ( + bigInitialMarginQuoteQuantums *big.Int, + bigMaintenanceMarginQuoteQuantums *big.Int, +) { // Always consider the magnitude of the position regardless of whether it is long/short. bigAbsQuantums := new(big.Int).Set(bigQuantums).Abs(bigQuantums) @@ -900,8 +940,7 @@ func (k Keeper) GetMarginRequirements( ), true, ) - - return bigInitialMarginQuoteQuantums, bigMaintenanceMarginQuoteQuantums, nil + return bigInitialMarginQuoteQuantums, bigMaintenanceMarginQuoteQuantums } // GetSettlementPpm returns the net settlement amount ppm (in quote quantums) given @@ -930,11 +969,31 @@ func (k Keeper) GetSettlementPpm( return big.NewInt(0), big.NewInt(0), err } + bigNetSettlementPpm, newFundingIndex = GetSettlementPpmWithPerpetual( + perpetual, + quantums, + index, + ) + return bigNetSettlementPpm, newFundingIndex, nil +} + +// GetSettlementPpm returns the net settlement amount ppm (in quote quantums) given +// the perpetual and position size (in base quantums). +// +// Note that this function is a stateless utility function. +func GetSettlementPpmWithPerpetual( + perpetual types.Perpetual, + quantums *big.Int, + index *big.Int, +) ( + bigNetSettlementPpm *big.Int, + newFundingIndex *big.Int, +) { indexDelta := new(big.Int).Sub(perpetual.FundingIndex.BigInt(), index) // if indexDelta is zero, then net settlement is zero. if indexDelta.Sign() == 0 { - return big.NewInt(0), perpetual.FundingIndex.BigInt(), nil + return big.NewInt(0), perpetual.FundingIndex.BigInt() } bigNetSettlementPpm = new(big.Int).Mul(indexDelta, quantums) @@ -944,7 +1003,7 @@ func (k Keeper) GetSettlementPpm( // Thus, always negate `bigNetSettlementPpm` here. bigNetSettlementPpm = bigNetSettlementPpm.Neg(bigNetSettlementPpm) - return bigNetSettlementPpm, perpetual.FundingIndex.BigInt(), nil + return bigNetSettlementPpm, perpetual.FundingIndex.BigInt() } // GetPremiumSamples reads premium samples from the current `funding-tick` epoch, diff --git a/protocol/x/subaccounts/keeper/subaccount.go b/protocol/x/subaccounts/keeper/subaccount.go index f387649059..4716ad3072 100644 --- a/protocol/x/subaccounts/keeper/subaccount.go +++ b/protocol/x/subaccounts/keeper/subaccount.go @@ -17,6 +17,8 @@ import ( indexer_manager "github.com/dydxprotocol/v4-chain/protocol/indexer/indexer_manager" "github.com/dydxprotocol/v4-chain/protocol/lib" "github.com/dydxprotocol/v4-chain/protocol/lib/metrics" + perpkeeper "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/keeper" + perptypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types" "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" ) @@ -376,6 +378,33 @@ func (k Keeper) getSettledSubaccount( settledSubaccount types.Subaccount, fundingPayments map[uint32]dtypes.SerializableInt, err error, +) { + // Fetch all relevant perpetuals. + perpetuals := make(map[uint32]perptypes.Perpetual) + for _, p := range subaccount.PerpetualPositions { + perpetual, err := k.perpetualsKeeper.GetPerpetual(ctx, p.PerpetualId) + if err != nil { + return types.Subaccount{}, nil, err + } + perpetuals[p.PerpetualId] = perpetual + } + + return GetSettledSubaccountWithPerpetuals(subaccount, perpetuals) +} + +// GetSettledSubaccountWithPerpetuals returns 1. a new settled subaccount given an unsettled subaccount, +// updating the USDC AssetPosition, FundingIndex, and LastFundingPayment fields accordingly +// (does not persist any changes) and 2. a map with perpetual ID as key and last funding +// payment as value (for emitting funding payments to indexer). +// +// Note that this is a stateless utility function. +func GetSettledSubaccountWithPerpetuals( + subaccount types.Subaccount, + perpetuals map[uint32]perptypes.Perpetual, +) ( + settledSubaccount types.Subaccount, + fundingPayments map[uint32]dtypes.SerializableInt, + err error, ) { totalNetSettlementPpm := big.NewInt(0) @@ -384,15 +413,21 @@ func (k Keeper) getSettledSubaccount( // Iterate through and settle all perpetual positions. for _, p := range subaccount.PerpetualPositions { - bigNetSettlementPpm, newFundingIndex, err := k.perpetualsKeeper.GetSettlementPpm( - ctx, - p.PerpetualId, + perpetual, found := perpetuals[p.PerpetualId] + if !found { + return types.Subaccount{}, + nil, + errorsmod.Wrap( + perptypes.ErrPerpetualDoesNotExist, lib.UintToString(p.PerpetualId), + ) + } + + // Call the stateless utility function to get the net settlement and new funding index. + bigNetSettlementPpm, newFundingIndex := perpkeeper.GetSettlementPpmWithPerpetual( + perpetual, p.GetBigQuantums(), p.FundingIndex.BigInt(), ) - if err != nil { - return types.Subaccount{}, nil, err - } // Record non-zero funding payment (to be later emitted in SubaccountUpdateEvent to indexer). // Note: Funding payment is the negative of settlement, i.e. positive settlement is equivalent // to a negative funding payment (position received funding payment) and vice versa. diff --git a/protocol/x/subaccounts/types/expected_keepers.go b/protocol/x/subaccounts/types/expected_keepers.go index a8890d649e..56d2c3e25e 100644 --- a/protocol/x/subaccounts/types/expected_keepers.go +++ b/protocol/x/subaccounts/types/expected_keepers.go @@ -62,6 +62,13 @@ type PerpetualsKeeper interface { newFundingIndex *big.Int, err error, ) + GetPerpetual( + ctx sdk.Context, + perpetualId uint32, + ) ( + perpetual perptypes.Perpetual, + err error, + ) GetAllPerpetuals(ctx sdk.Context) []perptypes.Perpetual } From 30718505ef150a367040d43efbab2d9073ec2e92 Mon Sep 17 00:00:00 2001 From: Jakob Herlitz <125316911+jakob-dydx@users.noreply.github.com> Date: Tue, 19 Dec 2023 12:53:47 -0800 Subject: [PATCH 12/12] [CLOB-1010] final settlement checktx validation (#842) * checktx logic to reject order placements for final settlement markets * fix test with incorrect order --- protocol/testutil/constants/clob_pair.go | 12 ++++++++++++ protocol/x/clob/keeper/clob_pair.go | 7 +++++++ protocol/x/clob/keeper/orders_test.go | 21 +++++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/protocol/testutil/constants/clob_pair.go b/protocol/testutil/constants/clob_pair.go index f1fc88945a..10b42e79bf 100644 --- a/protocol/testutil/constants/clob_pair.go +++ b/protocol/testutil/constants/clob_pair.go @@ -103,6 +103,18 @@ var ( QuantumConversionExponent: -8, Status: clobtypes.ClobPair_STATUS_INITIALIZING, } + ClobPair_Btc_Final_Settlement = clobtypes.ClobPair{ + Id: 0, + Metadata: &clobtypes.ClobPair_PerpetualClobMetadata{ + PerpetualClobMetadata: &clobtypes.PerpetualClobMetadata{ + PerpetualId: 0, + }, + }, + StepBaseQuantums: 5, + SubticksPerTick: 5, + QuantumConversionExponent: -8, + Status: clobtypes.ClobPair_STATUS_FINAL_SETTLEMENT, + } ClobPair_Btc_Paused = clobtypes.ClobPair{ Id: 0, Metadata: &clobtypes.ClobPair_PerpetualClobMetadata{ diff --git a/protocol/x/clob/keeper/clob_pair.go b/protocol/x/clob/keeper/clob_pair.go index d9b1457f06..d35ddd2f7e 100644 --- a/protocol/x/clob/keeper/clob_pair.go +++ b/protocol/x/clob/keeper/clob_pair.go @@ -373,6 +373,13 @@ func (k Keeper) validateOrderAgainstClobPairStatus( clobPair.Status, ) } + case types.ClobPair_STATUS_FINAL_SETTLEMENT: + return errorsmod.Wrapf( + types.ErrOrderConflictsWithClobPairStatus, + "Order %+v disallowed, trading is disabled for clob pair with status %+v", + order, + clobPair.Status, + ) } return nil diff --git a/protocol/x/clob/keeper/orders_test.go b/protocol/x/clob/keeper/orders_test.go index a9f77d58af..5948c6562a 100644 --- a/protocol/x/clob/keeper/orders_test.go +++ b/protocol/x/clob/keeper/orders_test.go @@ -1670,6 +1670,27 @@ func TestPerformStatefulOrderValidation(t *testing.T) { TimeInForce: types.Order_TIME_IN_FORCE_POST_ONLY, }, }, + "Fails with short-term order and ClobPair_Status of FINAL_SETTLEMENT": { + clobPairs: []types.ClobPair{ + constants.ClobPair_Btc_Final_Settlement, + }, + order: constants.Order_Alice_Num0_Id0_Clob0_Buy10_Price10_GTB16, + expectedErr: "trading is disabled for clob pair", + }, + "Fails with long-term order and ClobPair_Status of FINAL_SETTLEMENT": { + clobPairs: []types.ClobPair{ + constants.ClobPair_Btc_Final_Settlement, + }, + order: constants.LongTermOrder_Alice_Num0_Id0_Clob0_Buy100_Price10_GTBT15, + expectedErr: "trading is disabled for clob pair", + }, + "Fails with conditional order and ClobPair_Status of FINAL_SETTLEMENT": { + clobPairs: []types.ClobPair{ + constants.ClobPair_Btc_Final_Settlement, + }, + order: constants.ConditionalOrder_Alice_Num0_Id0_Clob0_Buy1BTC_Price50000_GTBT10_SL_50001, + expectedErr: "trading is disabled for clob pair", + }, } for name, tc := range tests {