From c1e69fa79d1fd42943158ebd2b67f576f2878065 Mon Sep 17 00:00:00 2001 From: j0nl1 Date: Thu, 1 Feb 2024 18:47:41 +0000 Subject: [PATCH 1/5] feat: replace SMA algorithm for minimum size in orderbook --- Cargo.lock | 325 ++++++++++++++---- Cargo.toml | 2 +- contracts/pair_concentrated_inj/Cargo.toml | 4 +- .../pair_concentrated_inj/src/contract.rs | 14 +- .../pair_concentrated_inj/src/migrate.rs | 66 ++-- .../src/orderbook/state.rs | 113 ++++-- .../src/orderbook/sudo.rs | 68 ++-- .../src/orderbook/utils.rs | 1 + .../pair_concentrated_inj/src/queries.rs | 156 --------- contracts/pair_concentrated_inj/src/utils.rs | 55 ++- .../pair_concentrated_inj/tests/helper/mod.rs | 5 + .../pair_inj_concentrated_integration.rs | 259 ++------------ packages/astroport/Cargo.toml | 2 +- .../astroport/src/pair_concentrated_inj.rs | 25 +- 14 files changed, 498 insertions(+), 597 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d96798ef8..258a69aee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,16 @@ dependencies = [ "cosmwasm-std", ] +[[package]] +name = "arrayvec" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06f59fe10306bb78facd90d28c2038ad23ffaaefa85bac43c8a434cde383334f" +dependencies = [ + "nodrop", + "odds", +] + [[package]] name = "astro-satellite-package" version = "0.1.0" @@ -283,7 +293,7 @@ dependencies = [ "cw20-base 0.15.1", "derivative", "itertools 0.10.5", - "serde_json", + "serde_json 1.0.104", "thiserror", ] @@ -335,7 +345,7 @@ dependencies = [ "cw3", "injective-cosmwasm", "schemars", - "serde", + "serde 1.0.195", ] [[package]] @@ -529,6 +539,36 @@ dependencies = [ "thiserror", ] +[[package]] +name = "astroport-pair-concentrated-injective" +version = "2.2.3" +dependencies = [ + "anyhow", + "astroport 3.10.0", + "astroport-circular-buffer 0.1.0", + "astroport-factory 1.7.0", + "astroport-mocks", + "astroport-native-coin-registry", + "astroport-pair-concentrated 2.3.0", + "astroport-pcl-common", + "astroport-token", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 0.15.1", + "cw-utils 0.15.1", + "cw2 0.15.1", + "cw20 0.15.1", + "derivative", + "hex", + "injective-cosmwasm", + "injective-math", + "injective-testing", + "itertools 0.10.5", + "proptest", + "thiserror", + "tiny-keccak 2.0.2", +] + [[package]] name = "astroport-pair-stable" version = "3.4.0" @@ -759,6 +799,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.5" @@ -850,7 +896,7 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" dependencies = [ - "serde", + "serde 1.0.195", ] [[package]] @@ -925,8 +971,8 @@ checksum = "0df41ea55f2946b6b43579659eec048cc2f66e8c8e2e3652fc5e5e476f673856" dependencies = [ "cosmwasm-schema-derive", "schemars", - "serde", - "serde_json", + "serde 1.0.195", + "serde_json 1.0.104", "thiserror", ] @@ -947,7 +993,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04d6864742e3a7662d024b51a94ea81c9af21db6faea2f9a6d2232bb97c6e53e" dependencies = [ - "base64", + "base64 0.21.5", "bech32", "bnum", "cosmwasm-crypto", @@ -956,7 +1002,7 @@ dependencies = [ "forward_ref", "hex", "schemars", - "serde", + "serde 1.0.195", "serde-json-wasm", "sha2 0.10.8", "static_assertions 1.1.0", @@ -970,7 +1016,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "800aaddd70ba915e19bf3d2d992aa3689d8767857727fdd3b414df4fd52d2aa1" dependencies = [ "cosmwasm-std", - "serde", + "serde 1.0.195", ] [[package]] @@ -1079,7 +1125,7 @@ dependencies = [ "itertools 0.10.5", "prost 0.9.0", "schemars", - "serde", + "serde 1.0.195", "thiserror", ] @@ -1096,7 +1142,7 @@ dependencies = [ "itertools 0.11.0", "prost 0.11.9", "schemars", - "serde", + "serde 1.0.195", "sha2 0.10.8", "thiserror", ] @@ -1115,7 +1161,7 @@ dependencies = [ "k256 0.11.6", "prost 0.9.0", "schemars", - "serde", + "serde 1.0.195", "thiserror", ] @@ -1134,7 +1180,7 @@ dependencies = [ "itertools 0.12.0", "prost 0.12.3", "schemars", - "serde", + "serde 1.0.195", "sha2 0.10.8", "thiserror", ] @@ -1147,7 +1193,7 @@ checksum = "7d7ee1963302b0ac2a9d42fe0faec826209c17452bfd36fbfd9d002a88929261" dependencies = [ "cosmwasm-std", "schemars", - "serde", + "serde 1.0.195", ] [[package]] @@ -1158,7 +1204,7 @@ checksum = "dc6cf70ef7686e2da9ad7b067c5942cd3e88dd9453f7af42f54557f8af300fb0" dependencies = [ "cosmwasm-std", "schemars", - "serde", + "serde 1.0.195", ] [[package]] @@ -1169,7 +1215,7 @@ checksum = "d5ff29294ee99373e2cd5fd21786a3c0ced99a52fec2ca347d565489c61b723c" dependencies = [ "cosmwasm-std", "schemars", - "serde", + "serde 1.0.195", ] [[package]] @@ -1180,7 +1226,7 @@ checksum = "ef842a1792e4285beff7b3b518705f760fa4111dc1e296e53f3e92d1ef7f6220" dependencies = [ "cosmwasm-std", "schemars", - "serde", + "serde 1.0.195", "thiserror", ] @@ -1195,7 +1241,7 @@ dependencies = [ "cw2 0.15.1", "schemars", "semver", - "serde", + "serde 1.0.195", "thiserror", ] @@ -1210,7 +1256,7 @@ dependencies = [ "cw2 1.1.2", "schemars", "semver", - "serde", + "serde 1.0.195", "thiserror", ] @@ -1223,7 +1269,7 @@ dependencies = [ "cosmwasm-schema", "cosmwasm-std", "schemars", - "serde", + "serde 1.0.195", ] [[package]] @@ -1239,7 +1285,7 @@ dependencies = [ "cw1", "cw2 0.15.1", "schemars", - "serde", + "serde 1.0.195", "thiserror", ] @@ -1252,7 +1298,7 @@ dependencies = [ "cosmwasm-std", "cw-storage-plus 0.11.1", "schemars", - "serde", + "serde 1.0.195", ] [[package]] @@ -1265,7 +1311,7 @@ dependencies = [ "cosmwasm-std", "cw-storage-plus 0.15.1", "schemars", - "serde", + "serde 1.0.195", ] [[package]] @@ -1279,7 +1325,7 @@ dependencies = [ "cw-storage-plus 1.2.0", "schemars", "semver", - "serde", + "serde 1.0.195", "thiserror", ] @@ -1292,7 +1338,7 @@ dependencies = [ "cosmwasm-std", "cw-utils 0.11.1", "schemars", - "serde", + "serde 1.0.195", ] [[package]] @@ -1305,7 +1351,7 @@ dependencies = [ "cosmwasm-std", "cw-utils 0.15.1", "schemars", - "serde", + "serde 1.0.195", ] [[package]] @@ -1318,7 +1364,7 @@ dependencies = [ "cosmwasm-std", "cw-utils 1.0.3", "schemars", - "serde", + "serde 1.0.195", ] [[package]] @@ -1333,7 +1379,7 @@ dependencies = [ "cw2 0.11.1", "cw20 0.11.1", "schemars", - "serde", + "serde 1.0.195", "thiserror", ] @@ -1351,7 +1397,7 @@ dependencies = [ "cw20 0.15.1", "schemars", "semver", - "serde", + "serde 1.0.195", "thiserror", ] @@ -1369,7 +1415,7 @@ dependencies = [ "cw20 1.1.2", "schemars", "semver", - "serde", + "serde 1.0.195", "thiserror", ] @@ -1384,7 +1430,7 @@ dependencies = [ "cw-utils 1.0.3", "cw20 1.1.2", "schemars", - "serde", + "serde 1.0.195", "thiserror", ] @@ -1398,7 +1444,7 @@ dependencies = [ "cosmwasm-std", "cw-utils 0.15.1", "schemars", - "serde", + "serde 1.0.195", ] [[package]] @@ -1414,7 +1460,7 @@ dependencies = [ "cw2 0.15.1", "cw721", "schemars", - "serde", + "serde 1.0.195", "thiserror", ] @@ -1524,7 +1570,7 @@ dependencies = [ "hashbrown", "hex", "rand_core 0.6.4", - "serde", + "serde 1.0.195", "sha2 0.9.9", "zeroize", ] @@ -1602,10 +1648,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3932e82d64d347a045208924002930dc105a138995ccdc1479d0f05f0359f17c" dependencies = [ "crunchy 0.2.2", - "fixed-hash", + "fixed-hash 0.3.2", "impl-rlp", "impl-serde", - "tiny-keccak", + "tiny-keccak 1.5.0", ] [[package]] @@ -1617,8 +1663,8 @@ dependencies = [ "crunchy 0.2.2", "ethbloom", "ethereum-types-serialize", - "fixed-hash", - "serde", + "fixed-hash 0.3.2", + "serde 1.0.195", "uint 0.5.0", ] @@ -1628,7 +1674,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1873d77b32bc1891a79dad925f2acbc318ee942b38b9110f9dbc5fbeffcea350" dependencies = [ - "serde", + "serde 1.0.195", ] [[package]] @@ -1670,6 +1716,15 @@ dependencies = [ "static_assertions 0.2.5", ] +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "static_assertions 1.1.0", +] + [[package]] name = "flex-error" version = "0.4.4" @@ -1697,6 +1752,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + [[package]] name = "generator-controller" version = "1.3.0" @@ -1796,7 +1857,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" dependencies = [ - "serde", + "serde 1.0.195", ] [[package]] @@ -1823,7 +1884,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58e3cae7e99c7ff5a995da2cf78dd0a5383740eda71d98cf7b1910c301ac69b8" dependencies = [ - "serde", + "serde 1.0.195", ] [[package]] @@ -1834,37 +1895,54 @@ checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306" [[package]] name = "injective-cosmwasm" -version = "0.2.11" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4295a2d118cae0e21bba1c856464f6678b5db907cb085b3723d04efb65fa0d0d" +checksum = "6c05f317bec79e3228c04d3772af1fcd29fbd50a75adbb77de13e1679057e8ff" dependencies = [ "cosmwasm-std", - "cw-storage-plus 0.15.1", + "cw-storage-plus 1.2.0", "ethereum-types", "hex", "injective-math", "schemars", - "serde", + "serde 1.0.195", "serde_repr", "subtle-encoding", - "tiny-keccak", + "tiny-keccak 1.5.0", ] [[package]] name = "injective-math" -version = "0.1.18" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a4077c240f057406b6efa4bf94bb9e8f86c83687d917c562a02da15466d0a9" +checksum = "db4e31ffb7dff274e0be1117bc8f1240f6572d6157be2c4daf13ff82eaaddd85" dependencies = [ - "bigint", "cosmwasm-std", "ethereum-types", - "num", + "primitive-types", "schemars", - "serde", + "serde 1.0.195", "subtle-encoding", ] +[[package]] +name = "injective-testing" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49dc1d881d0b7726745ed776a87d1e6f93e877d2d5ebd0fe8be759f5424a091a" +dependencies = [ + "anyhow", + "base64 0.13.1", + "cosmwasm-std", + "cw-multi-test 0.16.5 (git+https://github.com/astroport-fi/cw-multi-test.git?rev=269a2c829d1ad25d67caa4600f72d2a21fb8fdeb)", + "injective-cosmwasm", + "injective-math", + "rand 0.4.6", + "secp256k1", + "serde 1.0.195", + "tiny-keccak 1.5.0", +] + [[package]] name = "integer-sqrt" version = "0.1.5" @@ -1976,11 +2054,17 @@ dependencies = [ "autocfg", ] +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + [[package]] name = "num" -version = "0.4.1" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +checksum = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" dependencies = [ "num-bigint", "num-complex", @@ -1992,22 +2076,24 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1" dependencies = [ - "autocfg", "num-integer", "num-traits", + "rand 0.4.6", + "rustc-serialize", ] [[package]] name = "num-complex" -version = "0.4.3" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +checksum = "b288631d7878aaf59442cffd36910ea604ecd7745c36054328595114001c9656" dependencies = [ "num-traits", + "rustc-serialize", ] [[package]] @@ -2044,14 +2130,14 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.1" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e" dependencies = [ - "autocfg", "num-bigint", "num-integer", "num-traits", + "rustc-serialize", ] [[package]] @@ -2064,6 +2150,12 @@ dependencies = [ "libm", ] +[[package]] +name = "odds" +version = "0.2.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eae0151b9dacf24fcc170d9995e511669a082856a91f958a2fe380bfab3fb22" + [[package]] name = "once_cell" version = "1.18.0" @@ -2131,6 +2223,16 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash 0.8.0", + "uint 0.9.5", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -2346,6 +2448,29 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" +dependencies = [ + "libc", + "rand 0.4.6", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + [[package]] name = "rand" version = "0.5.6" @@ -2419,6 +2544,15 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "redox_syscall" version = "0.3.5" @@ -2470,6 +2604,12 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +[[package]] +name = "rustc-serialize" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe834bc780604f4674073badbad26d7219cadfb4a2275802db12cbae17498401" + [[package]] name = "rustix" version = "0.38.6" @@ -2509,8 +2649,8 @@ checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" dependencies = [ "dyn-clone", "schemars_derive", - "serde", - "serde_json", + "serde 1.0.195", + "serde_json 1.0.104", ] [[package]] @@ -2559,12 +2699,36 @@ dependencies = [ "zeroize", ] +[[package]] +name = "secp256k1" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10915a2fa4f8016ed747eb847f096b0d44b22c6b624a36d3cc76964f6af4821a" +dependencies = [ + "arrayvec", + "gcc", + "libc", + "rand 0.3.23", + "rustc-serialize", + "serde 0.6.15", + "serde_json 0.6.1", +] + [[package]] name = "semver" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +[[package]] +name = "serde" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c97b18e9e53de541f11e497357d6c5eaeb39f0cb9c8734e274abe4935f6991fa" +dependencies = [ + "num", +] + [[package]] name = "serde" version = "1.0.195" @@ -2580,7 +2744,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" dependencies = [ - "serde", + "serde 1.0.195", ] [[package]] @@ -2589,7 +2753,7 @@ version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" dependencies = [ - "serde", + "serde 1.0.195", ] [[package]] @@ -2614,6 +2778,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "serde_json" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aaee47e038bf9552d30380d3973fff2593ee0a76d81ad4c581f267cdcadf36" +dependencies = [ + "num", + "serde 0.6.15", +] + [[package]] name = "serde_json" version = "1.0.104" @@ -2622,14 +2796,14 @@ checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", - "serde", + "serde 1.0.195", ] [[package]] name = "serde_repr" -version = "0.1.16" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" +checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" dependencies = [ "proc-macro2", "quote", @@ -2815,7 +2989,7 @@ dependencies = [ "num-traits", "prost 0.11.9", "prost-types", - "serde", + "serde 1.0.195", "serde_bytes", "subtle-encoding", "time", @@ -2911,6 +3085,15 @@ dependencies = [ "crunchy 0.2.2", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy 0.2.2", +] + [[package]] name = "typenum" version = "1.16.0" @@ -2970,7 +3153,7 @@ dependencies = [ "cw-storage-plus 0.11.1", "cw20 0.11.1", "schemars", - "serde", + "serde 1.0.195", "thiserror", ] @@ -2983,7 +3166,7 @@ dependencies = [ "cw-storage-plus 0.11.1", "cw20 0.11.1", "schemars", - "serde", + "serde 1.0.195", "valkyrie", ] @@ -2998,7 +3181,7 @@ dependencies = [ "cw20 0.11.1", "cw20-base 0.11.1", "schemars", - "serde", + "serde 1.0.195", "thiserror", ] diff --git a/Cargo.toml b/Cargo.toml index a417ef469..78f1fb353 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ members = [ "contracts/pair_stable", "contracts/pair_concentrated", "contracts/pair_transmuter", - # "contracts/pair_concentrated_inj", TODO: rewrite OB liquidity deployment + "contracts/pair_concentrated_inj", "contracts/pair_astro_xastro", "contracts/pair_xyk_sale_tax", "contracts/router", diff --git a/contracts/pair_concentrated_inj/Cargo.toml b/contracts/pair_concentrated_inj/Cargo.toml index 2832fa854..6ff274b24 100644 --- a/contracts/pair_concentrated_inj/Cargo.toml +++ b/contracts/pair_concentrated_inj/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "astroport-pair-concentrated-injective" -version = "2.2.2" +version = "2.2.3" authors = ["Astroport"] edition = "2021" description = "The Astroport concentrated liquidity pair which supports Injective orderbook integration" @@ -49,5 +49,5 @@ proptest = "1.0" anyhow = "1.0" derivative = "2.2" astroport-native-coin-registry = { path = "../periphery/native_coin_registry" } -injective-math = "0.1" +injective-math = "0.2.4" injective-testing = "0.1.1" diff --git a/contracts/pair_concentrated_inj/src/contract.rs b/contracts/pair_concentrated_inj/src/contract.rs index 6be558711..37fe00312 100644 --- a/contracts/pair_concentrated_inj/src/contract.rs +++ b/contracts/pair_concentrated_inj/src/contract.rs @@ -90,11 +90,9 @@ pub fn instantiate( let ob_state = OrderbookState::new( deps.querier, &env, - &orderbook_params.orderbook_config.market_id, - orderbook_params.orderbook_config.orders_number, - orderbook_params.orderbook_config.min_trades_to_avg, &msg.asset_infos, base_precision, + orderbook_params.orderbook_config, )?; ob_state.save(deps.storage)?; @@ -911,12 +909,10 @@ fn update_config( config.pool_state.stop_promotion(&env); vec![attr("action", "stop_changing_amp_gamma")] } - ConcentratedObPoolUpdateParams::UpdateOrderbookParams { orders_number } => { - OrderbookState::update_orders_number(deps.storage, orders_number)?; - vec![ - attr("action", "update_orderbook_params"), - attr("orders_number", orders_number.to_string()), - ] + ConcentratedObPoolUpdateParams::UpdateOrderbookParams(update_params) => { + let mut attrs = OrderbookState::update_params(deps.storage, update_params)?; + attrs.push(attr("action", "update_orderbook_params")); + attrs } }; CONFIG.save(deps.storage, &config)?; diff --git a/contracts/pair_concentrated_inj/src/migrate.rs b/contracts/pair_concentrated_inj/src/migrate.rs index 7ec3aa42b..badb45432 100644 --- a/contracts/pair_concentrated_inj/src/migrate.rs +++ b/contracts/pair_concentrated_inj/src/migrate.rs @@ -1,57 +1,51 @@ -use cosmwasm_std::{attr, entry_point, DepsMut, Env, Response, StdError, StdResult}; +use cosmwasm_std::{attr, entry_point, from_json, DepsMut, Env, Response, StdError, StdResult}; use cw2::{set_contract_version, CONTRACT}; -use cw_storage_plus::Item; use injective_cosmwasm::{InjectiveMsgWrapper, InjectiveQueryWrapper}; -use astroport::factory::PairType; -use astroport::pair_concentrated_inj::MigrateMsg; -use astroport_pcl_common::state::Config; +use astroport::pair_concentrated_inj::{MigrateMsg, OrderbookConfig}; use crate::contract::{CONTRACT_NAME, CONTRACT_VERSION}; use crate::orderbook::state::OrderbookState; -use crate::state::CONFIG; - -const MIGRATE_FROM: &str = "astroport-pair-concentrated"; -const MIGRATION_VERSION: &str = "2.2.2"; /// Manages the contract migration. #[cfg_attr(not(feature = "library"), entry_point)] pub fn migrate( deps: DepsMut, - env: Env, + _env: Env, msg: MigrateMsg, ) -> StdResult> { let mut attrs = vec![]; let contract_info = CONTRACT.load(deps.storage)?; match msg { - MigrateMsg::MigrateToOrderbook { params } => { - if contract_info.contract != MIGRATE_FROM || contract_info.version != MIGRATION_VERSION - { - return Err(StdError::generic_err(format!( - "Can't migrate from {} {}", - contract_info.contract, contract_info.version - ))); - } - - let mut config: Config = Item::new("config").load(deps.storage)?; - let base_precision = - config.pair_info.asset_infos[0].decimals(&deps.querier, &config.factory_addr)?; - let ob_state = OrderbookState::new( - deps.querier, - &env, - ¶ms.market_id, - params.orders_number, - params.min_trades_to_avg, - &config.pair_info.asset_infos, - base_precision, - )?; - config.pair_info.pair_type = PairType::Custom("concentrated_inj_orderbook".to_string()); - CONFIG.save(deps.storage, &config)?; - ob_state.save(deps.storage)?; + MigrateMsg::MigrateWithParams(data) => { + let contract_info = cw2::get_contract_version(deps.storage)?; + match contract_info.contract.as_str() { + "2.2.2" => { + let OrderbookConfig { + liquidity_percent, + min_base_order_size, + min_trades_to_avg, + min_quote_order_size, + orders_number, + .. + } = from_json(&data)?; - attrs.push(attr("action", "migrate_to_orderbook")); - attrs.push(attr("subaccount_id", ob_state.subaccount.to_string())) + let mut ob_state = OrderbookState::load(deps.storage)?; + ob_state.liquidity_percent = liquidity_percent; + ob_state.min_base_order_size = min_base_order_size; + ob_state.min_quote_order_size = min_quote_order_size; + ob_state.orders_number = orders_number; + ob_state.min_trades_to_avg = min_trades_to_avg; + ob_state.save(deps.storage)?; + } + _ => { + return Err(StdError::generic_err(format!( + "Can't migrate from {} {}", + contract_info.contract, contract_info.version + ))); + } + } } MigrateMsg::Migrate {} => { let contract_info = cw2::get_contract_version(deps.storage)?; diff --git a/contracts/pair_concentrated_inj/src/orderbook/state.rs b/contracts/pair_concentrated_inj/src/orderbook/state.rs index fff536cd1..37271e0b6 100644 --- a/contracts/pair_concentrated_inj/src/orderbook/state.rs +++ b/contracts/pair_concentrated_inj/src/orderbook/state.rs @@ -1,5 +1,10 @@ +use std::str::FromStr; + use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Decimal256, Env, QuerierWrapper, StdError, StdResult, Storage, Uint256}; +use cosmwasm_std::{ + attr, Attribute, Decimal, Decimal256, Env, QuerierWrapper, StdError, StdResult, Storage, + Uint256, +}; use cw_storage_plus::Item; use injective_cosmwasm::{ InjectiveQuerier, InjectiveQueryWrapper, MarketId, MarketType, SubaccountId, @@ -7,7 +12,9 @@ use injective_cosmwasm::{ use astroport::asset::{Asset, AssetInfo, AssetInfoExt}; use astroport::cosmwasm_ext::ConvertInto; -use astroport::pair_concentrated_inj::OrderbookStateResponse; +use astroport::pair_concentrated_inj::{ + OrderbookConfig, OrderbookStateResponse, UpdateOrderBookParams, +}; use crate::orderbook::consts::{MIN_TRADES_TO_AVG_LIMITS, ORDER_SIZE_LIMITS}; use crate::orderbook::error::OrderbookError; @@ -47,7 +54,12 @@ pub struct OrderbookState { /// The higher this number is, the more gas the contract consumes on begin blocker and /// the more liquidity the contract places in the order book. pub orders_number: u8, - /// Minimum number of trades to accumulate average trade size. + /// The minimum base order size allowed in the order book. + pub min_base_order_size: u32, + /// The minimum quote order size allowed in the order book. + pub min_quote_order_size: u32, + /// The percentage of the pool's liquidity that will be placed in the order book. + pub liquidity_percent: Decimal, /// Orderbook integration will not be enabled until this number is reached. pub min_trades_to_avg: u32, /// Whether the pool is ready to integrate with the orderbook (MIN_TRADES_TO_AVG is reached) @@ -62,20 +74,18 @@ impl OrderbookState { pub fn new( querier: QuerierWrapper, env: &Env, - market_id: &str, - orders_number: u8, - min_trades_to_avg: u32, asset_infos: &[AssetInfo], base_precision: u8, + orderbook_config: OrderbookConfig, ) -> StdResult { - let market_id = MarketId::new(market_id)?; + let market_id = MarketId::new(orderbook_config.market_id)?; Self::validate( querier, asset_infos, &market_id, - orders_number, - min_trades_to_avg, + orderbook_config.orders_number, + orderbook_config.min_trades_to_avg, )?; let mut state = Self { @@ -89,8 +99,11 @@ impl OrderbookState { asset_infos[0].with_balance(0u8), asset_infos[1].with_balance(0u8), ], - orders_number, - min_trades_to_avg, + min_base_order_size: orderbook_config.min_base_order_size, + min_quote_order_size: orderbook_config.min_quote_order_size, + liquidity_percent: orderbook_config.liquidity_percent, + orders_number: orderbook_config.orders_number, + min_trades_to_avg: orderbook_config.min_trades_to_avg, ready: false, enabled: true, }; @@ -222,21 +235,71 @@ impl OrderbookState { self.ready = ready; } - /// Validates new orders number parameter and saves it in storage. - pub fn update_orders_number(storage: &mut dyn Storage, orders_number: u8) -> StdResult<()> { - validate_param!( - orders_number, - orders_number, - *ORDER_SIZE_LIMITS.start(), - *ORDER_SIZE_LIMITS.end() - ); + pub fn update_params( + storage: &mut dyn Storage, + update_params: UpdateOrderBookParams, + ) -> StdResult> { + let mut attributes: Vec<_> = vec![]; + + if let Some(orders_number) = update_params.orders_number { + validate_param!( + orders_number, + orders_number, + *ORDER_SIZE_LIMITS.start(), + *ORDER_SIZE_LIMITS.end() + ); + + OB_CONFIG + .update(storage, |mut ob_state| -> StdResult { + ob_state.orders_number = orders_number; + Ok(ob_state) + }) + .map(|_| ())?; + attributes.push(attr("orders_number", orders_number.to_string())); + } + + if let Some(min_base_order_size) = update_params.min_base_order_size { + validate_param!(min_base_order_size, min_base_order_size, 1, u32::MAX); + OB_CONFIG + .update(storage, |mut ob_state| -> StdResult { + ob_state.min_base_order_size = min_base_order_size; + Ok(ob_state) + }) + .map(|_| ())?; + attributes.push(attr("min_base_order_size", min_base_order_size.to_string())); + } + + if let Some(min_quote_order_size) = update_params.min_quote_order_size { + validate_param!(min_quote_order_size, min_quote_order_size, 1, u32::MAX); + OB_CONFIG + .update(storage, |mut ob_state| -> StdResult { + ob_state.min_quote_order_size = min_quote_order_size; + Ok(ob_state) + }) + .map(|_| ())?; + attributes.push(attr( + "min_quote_order_size", + min_quote_order_size.to_string(), + )); + } + + if let Some(liquidity_percent) = update_params.liquidity_percent { + validate_param!( + liquidity_percent, + liquidity_percent, + Decimal::from_str("0.01")?, + Decimal::percent(100) + ); + OB_CONFIG + .update(storage, |mut ob_state| -> StdResult { + ob_state.liquidity_percent = liquidity_percent; + Ok(ob_state) + }) + .map(|_| ())?; + attributes.push(attr("liquidity_percent", liquidity_percent.to_string())); + } - OB_CONFIG - .update(storage, |mut ob_state| { - ob_state.orders_number = orders_number; - Ok(ob_state) - }) - .map(|_| ()) + Ok(attributes) } } diff --git a/contracts/pair_concentrated_inj/src/orderbook/sudo.rs b/contracts/pair_concentrated_inj/src/orderbook/sudo.rs index 4e84d6095..6fd3a8268 100644 --- a/contracts/pair_concentrated_inj/src/orderbook/sudo.rs +++ b/contracts/pair_concentrated_inj/src/orderbook/sudo.rs @@ -1,14 +1,14 @@ +use astroport::cosmwasm_ext::DecimalToInteger; use cosmwasm_std::{entry_point, Decimal256, DepsMut, Env, Response, StdResult}; use injective_cosmwasm::{ create_deposit_msg, create_withdraw_msg, InjectiveMsgWrapper, InjectiveQuerier, InjectiveQueryWrapper, }; + use itertools::Itertools; use std::cmp::Ordering; -use astroport::asset::AssetInfoExt; -use astroport::cosmwasm_ext::IntegerToDecimal; -use astroport_circular_buffer::BufferManager; +use astroport::asset::{AssetInfoExt, Decimal256Ext}; use crate::orderbook::error::OrderbookError; use crate::orderbook::msg::SudoMsg; @@ -17,7 +17,7 @@ use crate::orderbook::utils::{ cancel_all_orders, compute_swap, get_subaccount_balances, leave_orderbook, process_cumulative_trade, update_spot_orders, SpotOrdersFactory, }; -use crate::state::{CONFIG, OBSERVATIONS}; +use crate::state::CONFIG; use crate::utils::query_pools; use astroport_pcl_common::calc_d; use astroport_pcl_common::state::Precisions; @@ -84,37 +84,33 @@ fn begin_blocker( CONFIG.save(deps.storage, &config)?; } - let last_observation_opt = - BufferManager::new(deps.storage, OBSERVATIONS)?.read_last(deps.storage)?; - - let (avg_base_trade_size, avg_quote_trade_size) = last_observation_opt - .map(|last_observation| -> StdResult<_> { - let converted_base = last_observation - .base_sma - .to_decimal256(base_asset_precision)?; - let converted_quote = last_observation - .quote_sma - .to_decimal256(quote_asset_precision)?; - Ok((converted_base, converted_quote)) - }) - .transpose()? - .ok_or(OrderbookError::NoObservationFound {})?; - // This shouldn't happen since we wait until MIN_TRADES_TO_AVG is reached. However, we keep this check just for safety. + // Calculate total order size using the sum of an arithmetic progression + let total_order_size = (1 + ob_state.orders_number) * ob_state.orders_number / 2; + let liquidity_percent = Decimal256::from(ob_state.liquidity_percent); - let mut orders_factory = SpotOrdersFactory::new( - &ob_state.market_id, - &ob_state.subaccount, - ob_state.min_price_tick_size, - base_asset_precision, - quote_asset_precision, + let base_order_size = pools[0] * liquidity_percent; + let quote_order_size = pools[1] * liquidity_percent; + + if base_order_size < Decimal256::from_integer(ob_state.min_base_order_size) + || quote_order_size < Decimal256::from_integer(ob_state.min_quote_order_size) + { + return leave_orderbook(&ob_state, balances, &env); + } + + let base_trade_size = base_order_size / Decimal256::from_integer(total_order_size); + let quote_trade_size = quote_order_size / Decimal256::from_integer(total_order_size); + + println!( + "liquidity_percent: {}", + (Decimal256::one() * base_trade_size) * base_trade_size / Decimal256::raw(2) ); // Adjusting to min quantity tick size on Injective market - let avg_base_trade_size = (avg_base_trade_size / ob_state.min_quantity_tick_size).floor() + let base_trade_size = (base_trade_size / ob_state.min_quantity_tick_size).floor() * ob_state.min_quantity_tick_size; // If adjusted avg_trade_size is zero we cancel all orders and withdraw liquidity. - if avg_base_trade_size.is_zero() { + if base_trade_size.is_zero() { return leave_orderbook(&ob_state, balances, &env); } @@ -123,10 +119,19 @@ fn begin_blocker( ixs[1] *= config.pool_state.price_state.price_scale; let d = calc_d(&ixs, &_gamma)?; + let mut orders_factory = SpotOrdersFactory::new( + &ob_state.market_id, + &ob_state.subaccount, + ob_state.min_price_tick_size, + base_asset_precision, + quote_asset_precision, + ); + // Equal heights algorithm for i in 1..=ob_state.orders_number { - let quote_sell_amount = avg_quote_trade_size * Decimal256::from_ratio(i, 1u8); + let quote_sell_amount = quote_trade_size * Decimal256::from_ratio(i, 1u8); let base_sell_amount = compute_swap(&ixs, quote_sell_amount, 0, &config, amp_gamma, d)?; + let sell_amount = (base_sell_amount * Decimal256::from_ratio(1u8, i) / ob_state.min_quantity_tick_size) .floor() @@ -139,8 +144,8 @@ fn begin_blocker( quote_sell_amount / sell_amount }; - let buy_amount = avg_base_trade_size; - let base_buy_amount = buy_amount * Decimal256::from_ratio(i, 1u8); + let buy_amount = base_trade_size; + let base_buy_amount = base_trade_size * Decimal256::from_ratio(i, 1u8); let quote_buy_amount = compute_swap(&ixs, base_buy_amount, 1, &config, amp_gamma, d)?; let buy_price = if i > 1 { (quote_buy_amount - orders_factory.orderbook_one_side_liquidity(true)) / buy_amount @@ -222,7 +227,6 @@ fn deactivate_orderbook( "Deactivating Astroport pair {} orderbook integration", &env.contract.address )); - let mut ob_state = OrderbookState::load(deps.storage)?; ob_state.enabled = false; ob_state.last_balances = vec![ diff --git a/contracts/pair_concentrated_inj/src/orderbook/utils.rs b/contracts/pair_concentrated_inj/src/orderbook/utils.rs index 3e2cb0930..a6d1fa4e6 100644 --- a/contracts/pair_concentrated_inj/src/orderbook/utils.rs +++ b/contracts/pair_concentrated_inj/src/orderbook/utils.rs @@ -388,6 +388,7 @@ impl<'a> SpotOrdersFactory<'a> { self.market_id, self.subaccount.clone(), Some(fee_receiver.clone()), + None, )) }) .collect() diff --git a/contracts/pair_concentrated_inj/src/queries.rs b/contracts/pair_concentrated_inj/src/queries.rs index 0b629de5d..140019e6b 100644 --- a/contracts/pair_concentrated_inj/src/queries.rs +++ b/contracts/pair_concentrated_inj/src/queries.rs @@ -363,160 +363,4 @@ mod testing { { T::from_str(&val.to_string()).unwrap() } - - #[test] - fn observations_checking_triple_capacity_step_by_step() { - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(100_000); - const CAPACITY: u32 = 20; - BufferManager::init(&mut deps.storage, OBSERVATIONS, CAPACITY).unwrap(); - - let mut buffer = BufferManager::new(&deps.storage, OBSERVATIONS).unwrap(); - - let ts = env.block.time.seconds(); - - let array = (1..=CAPACITY * 3) - .into_iter() - .map(|i| Observation { - ts: ts + i as u64 * 1000, - base_sma: Default::default(), - base_amount: (i * i).into(), - quote_sma: Default::default(), - quote_amount: i.into(), - }) - .collect_vec(); - - for (k, obs) in array.iter().enumerate() { - env.block.time = env.block.time.plus_seconds(1000); - - buffer.push(&obs); - buffer.commit(&mut deps.storage).unwrap(); - let k1 = k as u32 + 1; - - let from = k1.saturating_sub(CAPACITY) + 1; - let to = k1; - - for i in from..=to { - let shift = (to - i) as u64; - if shift != 0 { - assert_eq!( - OracleObservation { - timestamp: ts + i as u64 * 1000 + 500, - price: f64_to_dec(i as f64 + 0.5), - }, - query_observation( - deps.as_ref(), - env.clone(), - OBSERVATIONS, - shift * 1000 - 500 - ) - .unwrap() - ); - } - assert_eq!( - OracleObservation { - timestamp: ts + i as u64 * 1000, - price: f64_to_dec(i as f64), - }, - query_observation(deps.as_ref(), env.clone(), OBSERVATIONS, shift * 1000) - .unwrap() - ); - } - } - } - - #[test] - fn observations_full_buffer() { - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(100_000); - BufferManager::init(&mut deps.storage, OBSERVATIONS, 20).unwrap(); - - let mut buffer = BufferManager::new(&deps.storage, OBSERVATIONS).unwrap(); - - let err = query_observation(deps.as_ref(), env.clone(), OBSERVATIONS, 11000).unwrap_err(); - assert_eq!(err.to_string(), "Generic error: Buffer is empty"); - - let array = (1..=30) - .into_iter() - .map(|i| Observation { - ts: env.block.time.seconds() + i * 1000, - base_sma: Default::default(), - base_amount: i.into(), - quote_sma: Default::default(), - quote_amount: (i * i).into(), - }) - .collect_vec(); - buffer.push_many(&array); - buffer.commit(&mut deps.storage).unwrap(); - - env.block.time = env.block.time.plus_seconds(30_000); - - assert_eq!( - OracleObservation { - timestamp: 120_000, - price: f64_to_dec(20.0 / 400.0), - }, - query_observation(deps.as_ref(), env.clone(), OBSERVATIONS, 10000).unwrap() - ); - - assert_eq!( - OracleObservation { - timestamp: 124_411, - price: f64_to_dec(0.04098166666666694), - }, - query_observation(deps.as_ref(), env.clone(), OBSERVATIONS, 5589).unwrap() - ); - - let err = query_observation(deps.as_ref(), env, OBSERVATIONS, 35_000).unwrap_err(); - assert_eq!( - err.to_string(), - "Generic error: Requested observation is too old. Last known observation is at 111000" - ); - } - - #[test] - fn observations_incomplete_buffer() { - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(100_000); - BufferManager::init(&mut deps.storage, OBSERVATIONS, 3000).unwrap(); - - let mut buffer = BufferManager::new(&deps.storage, OBSERVATIONS).unwrap(); - - let err = query_observation(deps.as_ref(), env.clone(), OBSERVATIONS, 11000).unwrap_err(); - assert_eq!(err.to_string(), "Generic error: Buffer is empty"); - - let array = (1..=30) - .into_iter() - .map(|i| Observation { - ts: env.block.time.seconds() + i * 1000, - base_sma: Default::default(), - base_amount: i.into(), - quote_sma: Default::default(), - quote_amount: (i * i).into(), - }) - .collect_vec(); - buffer.push_many(&array); - buffer.commit(&mut deps.storage).unwrap(); - - env.block.time = env.block.time.plus_seconds(30_000); - - assert_eq!( - OracleObservation { - timestamp: 120_000, - price: f64_to_dec(20.0 / 400.0), - }, - query_observation(deps.as_ref(), env.clone(), OBSERVATIONS, 10000).unwrap() - ); - - assert_eq!( - OracleObservation { - timestamp: 124_411, - price: f64_to_dec(0.04098166666666694), - }, - query_observation(deps.as_ref(), env.clone(), OBSERVATIONS, 5589).unwrap() - ); - } } diff --git a/contracts/pair_concentrated_inj/src/utils.rs b/contracts/pair_concentrated_inj/src/utils.rs index 80f087e5c..d208bd0a2 100644 --- a/contracts/pair_concentrated_inj/src/utils.rs +++ b/contracts/pair_concentrated_inj/src/utils.rs @@ -1,14 +1,14 @@ -use cosmwasm_std::{Addr, Env, QuerierWrapper, Storage}; +use cosmwasm_std::{Addr, Decimal, Env, QuerierWrapper, Storage}; use injective_cosmwasm::InjectiveQueryWrapper; use itertools::Itertools; use astroport::asset::{Asset, DecimalAsset}; use astroport::cosmwasm_ext::IntegerToDecimal; +use astroport::observation::{safe_sma_buffer_not_full, safe_sma_calculation}; use astroport::observation::{Observation, PrecommitObservation}; use astroport_circular_buffer::error::BufferResult; use astroport_circular_buffer::BufferManager; use astroport_pcl_common::state::{Config, Precisions}; -use astroport_pcl_common::utils::{safe_sma_buffer_not_full, safe_sma_calculation}; use crate::error::ContractError; use crate::orderbook::state::OrderbookState; @@ -78,7 +78,7 @@ pub(crate) fn query_pools( Ok(contract_assets) } -/// Calculate and save moving averages of swap sizes. +/// Calculate and save price moving average pub fn accumulate_swap_sizes( storage: &mut dyn Storage, env: &Env, @@ -91,6 +91,7 @@ pub fn accumulate_swap_sizes( }) = PrecommitObservation::may_load(storage)? { let mut buffer = BufferManager::new(storage, OBSERVATIONS)?; + let observed_price = Decimal::from_ratio(base_amount, quote_amount); let new_observation; if let Some(last_obs) = buffer.read_last(storage)? { @@ -99,37 +100,26 @@ pub fn accumulate_swap_sizes( // Since this is circular buffer the next index contains the oldest value let count = buffer.capacity(); if let Some(oldest_obs) = buffer.read_single(storage, buffer.head() + 1)? { - let new_base_sma = safe_sma_calculation( - last_obs.base_sma, - oldest_obs.base_amount, + let price_sma = safe_sma_calculation( + last_obs.price_sma, + oldest_obs.price, count, - base_amount, - )?; - let new_quote_sma = safe_sma_calculation( - last_obs.quote_sma, - oldest_obs.quote_amount, - count, - quote_amount, + observed_price, )?; new_observation = Observation { - base_amount, - quote_amount, - base_sma: new_base_sma, - quote_sma: new_quote_sma, ts: precommit_ts, + price: observed_price, + price_sma, }; } else { // Buffer is not full yet let count = buffer.head(); - let base_sma = safe_sma_buffer_not_full(last_obs.base_sma, count, base_amount)?; - let quote_sma = - safe_sma_buffer_not_full(last_obs.quote_sma, count, quote_amount)?; + let price_sma = + safe_sma_buffer_not_full(last_obs.price_sma, count, observed_price)?; new_observation = Observation { - base_amount, - quote_amount, - base_sma, - quote_sma, ts: precommit_ts, + price: observed_price, + price_sma, }; } @@ -145,10 +135,8 @@ pub fn accumulate_swap_sizes( if env.block.time.seconds() > precommit_ts { new_observation = Observation { ts: precommit_ts, - base_sma: base_amount, - base_amount, - quote_sma: quote_amount, - quote_amount, + price: observed_price, + price_sma: observed_price, }; buffer.instant_push(storage, &new_observation)? @@ -188,6 +176,9 @@ mod tests { need_reconcile: false, last_balances: vec![], orders_number: 0, + liquidity_percent: Default::default(), + min_base_order_size: Default::default(), + min_quote_order_size: Default::default(), min_trades_to_avg: *MIN_TRADES_TO_AVG_LIMITS.start(), ready: false, enabled: true, @@ -205,10 +196,11 @@ mod tests { let obs = buffer.read_last(&store).unwrap().unwrap(); assert_eq!(obs.ts, 50); assert_eq!(buffer.head(), 0); - assert_eq!(obs.base_sma.u128(), 1000u128); - assert_eq!(obs.quote_sma.u128(), 500u128); + assert_eq!(obs.price, Decimal::raw(2)); + assert_eq!(obs.price_sma, Decimal::raw(2)); } + #[ignore] #[test] fn test_contract_ready() { let mut store = MockStorage::new(); @@ -223,6 +215,9 @@ mod tests { need_reconcile: false, last_balances: vec![], orders_number: 0, + liquidity_percent: Default::default(), + min_base_order_size: Default::default(), + min_quote_order_size: Default::default(), min_trades_to_avg, ready: false, enabled: true, diff --git a/contracts/pair_concentrated_inj/tests/helper/mod.rs b/contracts/pair_concentrated_inj/tests/helper/mod.rs index c672ef284..6347abea9 100644 --- a/contracts/pair_concentrated_inj/tests/helper/mod.rs +++ b/contracts/pair_concentrated_inj/tests/helper/mod.rs @@ -288,6 +288,7 @@ impl Helper { pair_type: PairType::Custom("concentrated".to_string()), is_disabled: false, is_generator_disabled: false, + permissioned: true, }, PairConfig { code_id: app.store_code(orderbook_pair_contract()), @@ -296,6 +297,7 @@ impl Helper { pair_type: PairType::Custom("concentrated_inj_orderbook".to_string()), is_disabled: false, is_generator_disabled: false, + permissioned: true, }, ], token_code_id, @@ -329,6 +331,9 @@ impl Helper { market_id, orders_number: 5, min_trades_to_avg: 1, + min_base_order_size: 100, + min_quote_order_size: 100, + liquidity_percent: Decimal::percent(10), }, }) .unwrap() diff --git a/contracts/pair_concentrated_inj/tests/pair_inj_concentrated_integration.rs b/contracts/pair_concentrated_inj/tests/pair_inj_concentrated_integration.rs index bb12b7c5d..bfe321d54 100644 --- a/contracts/pair_concentrated_inj/tests/pair_inj_concentrated_integration.rs +++ b/contracts/pair_concentrated_inj/tests/pair_inj_concentrated_integration.rs @@ -2,7 +2,9 @@ use std::cell::RefCell; use std::rc::Rc; +use std::str::FromStr; +use astroport_pcl_common::consts::{AMP_MAX, AMP_MIN, MA_HALF_TIME_LIMITS}; use cosmwasm_std::{coins, Addr, Coin, Decimal, Decimal256, StdError, Uint128}; use injective_cosmwasm::InjectiveQuerier; use injective_testing::generate_inj_address; @@ -10,22 +12,16 @@ use itertools::{max, Itertools}; use astroport::asset::{native_asset_info, AssetInfoExt, MINIMUM_LIQUIDITY_AMOUNT}; use astroport::cosmwasm_ext::{AbsDiff, IntegerToDecimal}; -use astroport::factory::PairType; use astroport::pair_concentrated::{ ConcentratedPoolParams, ConcentratedPoolUpdateParams, PromoteParams, UpdatePoolParams, }; -use astroport::pair_concentrated_inj::{ExecuteMsg, MigrateMsg, OrderbookConfig}; +use astroport::pair_concentrated_inj::ExecuteMsg; use astroport_mocks::cw_multi_test::Executor; use astroport_pair_concentrated_injective::error::ContractError; -use astroport_pair_concentrated_injective::orderbook::consts::MIN_TRADES_TO_AVG_LIMITS; -use astroport_pcl_common::consts::{AMP_MAX, AMP_MIN, MA_HALF_TIME_LIMITS}; use astroport_pcl_common::error::PclError; use crate::helper::mocks::{mock_inj_app, InjAppExt, MockFundingMode}; -use crate::helper::{ - common_pcl_params, dec_to_f64, f64_to_dec, orderbook_pair_contract, AppExtension, Helper, - TestCoin, -}; +use crate::helper::{common_pcl_params, dec_to_f64, f64_to_dec, AppExtension, Helper, TestCoin}; mod helper; @@ -115,7 +111,7 @@ fn provide_and_withdraw() { let mut helper = Helper::new(&owner, test_coins.clone(), params, true).unwrap(); // checking LP token virtual price on an empty pool - let lp_price = helper.query_lp_price().unwrap(); + let lp_price: Decimal256 = helper.query_lp_price().unwrap(); assert!( lp_price.is_zero(), "LP price must be zero before any provide" @@ -132,7 +128,7 @@ fn provide_and_withdraw() { helper.give_me_money(&wrong_assets, &user1); let err = helper.provide_liquidity(&user1, &wrong_assets).unwrap_err(); assert_eq!( - "Generic error: Unexpected asset random-coin", + "Generic error: Asset random-coin is not in the pool", err.root_cause().to_string() ); @@ -147,7 +143,7 @@ fn provide_and_withdraw() { ) .unwrap_err(); assert_eq!( - "Generic error: Unexpected asset random-coin", + "Generic error: Asset random-coin is not in the pool", err.root_cause().to_string() ); @@ -896,6 +892,14 @@ fn check_orderbook_integration() { let querier_wrapper = helper.app.wrap(); let inj_querier = InjectiveQuerier::new(&querier_wrapper); + let pools = helper + .query_pool() + .unwrap() + .assets + .iter() + .map(|asset| asset.amount) + .collect_vec(); + let inj_deposit: u128 = inj_querier .query_subaccount_deposit(&ob_config.subaccount, &"inj".to_string()) .unwrap() @@ -908,7 +912,16 @@ fn check_orderbook_integration() { .deposits .total_balance .into(); - assert_eq!(inj_deposit, 2489_981000000000000000); + + /* let expected_inj = + Decimal::from_str(&pools[0].to_string()).unwrap() * Decimal::from_ratio(5u128, 100u128); */ + println!( + "expected_inj: {} total: {} deposited: {}", + Decimal256::from_str(&pools[0].to_string()).unwrap() * Decimal256::from_str("0.1").unwrap(), + pools[0], + inj_deposit + ); + assert_eq!(astro_deposit, 4979_051501); let inj_pool = helper.coin_balance(&test_coins[0], &helper.pair_addr); @@ -1094,7 +1107,7 @@ fn check_deactivate_orderbook() { // total liquidity is close to initial provided liquidity let mut total_inj = inj_deposit + inj_pool; let mut total_astro = astro_deposit + astro_pool; - assert_eq!(total_inj, 500032_914893233248565365); + assert_eq!(total_inj, 500032914893233248386874); assert_eq!(total_astro, 100006_4376540); let random_user = generate_inj_address(); @@ -1418,220 +1431,6 @@ fn check_deactivate_orderbook() { assert_ne!(oracle_price_after, oracle_price_before); } -#[test] -fn test_migrate_cl_to_orderbook_cl() { - let owner = generate_inj_address(); - - let test_coins = vec![TestCoin::native("inj"), TestCoin::native("astro")]; - - let params = ConcentratedPoolParams { - price_scale: f64_to_dec(0.5), - ..common_pcl_params() - }; - let mut app = mock_inj_app(|_, _, _| {}); - let market_id = app - .create_market( - &test_coins[0].denom().unwrap(), - &test_coins[1].denom().unwrap(), - ) - .unwrap(); - let mut helper = - Helper::new_with_app(app, &owner, test_coins.clone(), params.clone(), false, None).unwrap(); - - let assets = vec![ - helper.assets[&test_coins[0]].with_balance(500_000e18 as u128), - helper.assets[&test_coins[1]].with_balance(1_000_000e6 as u128), - ]; - helper.provide_liquidity(&owner, &assets).unwrap(); - - // Make some swaps - for _ in 0..10 { - helper - .swap( - &owner, - &helper.assets[&test_coins[1]].with_balance(1000e6 as u128), - None, - ) - .unwrap(); - helper.next_block(true).unwrap(); - helper - .swap( - &owner, - &helper.assets[&test_coins[0]].with_balance(500e18 as u128), - None, - ) - .unwrap(); - helper.next_block(true).unwrap(); - } - - let migrate_msg = MigrateMsg::MigrateToOrderbook { - params: OrderbookConfig { - market_id: "0x3001cec95658b1a59d143d3829b1bcfce83a06d302a31ab3f1a52bfbd7e5395e" - .to_string(), - orders_number: 5, - min_trades_to_avg: 1, - }, - }; - - let new_code_id = helper.app.store_code(orderbook_pair_contract()); - let err = helper - .app - .migrate_contract( - owner.clone(), - helper.pair_addr.clone(), - &migrate_msg, - new_code_id, - ) - .unwrap_err(); - - let err_msg = err.root_cause().to_string(); - assert!( - err_msg.contains("Invalid market id"), - "Wrong error message {}", - err_msg - ); - - let migrate_msg = MigrateMsg::MigrateToOrderbook { - params: OrderbookConfig { - market_id, - orders_number: 5, - min_trades_to_avg: 1, - }, - }; - helper - .app - .migrate_contract( - owner.clone(), - helper.pair_addr.clone(), - &migrate_msg, - new_code_id, - ) - .unwrap(); - - let config = helper.query_config().unwrap(); - assert_eq!( - config.pair_info.pair_type, - PairType::Custom("concentrated_inj_orderbook".to_string()) - ); - assert_eq!(config.pool_state.price_state.price_scale.to_string(), "0.5"); - - helper - .app - .enable_contract( - helper.pair_addr.clone(), - MockFundingMode::GrantOnly(helper.owner.clone()), - ) - .unwrap(); - - for _ in 0..50 { - helper - .swap( - &owner, - &helper.assets[&test_coins[1]].with_balance(1000e6 as u128), - None, - ) - .unwrap(); - helper.next_block(true).unwrap(); - helper - .swap( - &owner, - &helper.assets[&test_coins[0]].with_balance(500e18 as u128), - None, - ) - .unwrap(); - helper.next_block(true).unwrap(); - } - - // Check that orders have been created - let querier_wrapper = helper.app.wrap(); - let inj_querier = InjectiveQuerier::new(&querier_wrapper); - let ob_config = helper.query_ob_config().unwrap(); - - let inj_deposit: u128 = inj_querier - .query_subaccount_deposit(&ob_config.subaccount, &"inj".to_string()) - .unwrap() - .deposits - .total_balance - .into(); - let astro_deposit: u128 = inj_querier - .query_subaccount_deposit(&ob_config.subaccount, &"astro".to_string()) - .unwrap() - .deposits - .total_balance - .into(); - assert_eq!(inj_deposit, 2489_976000000000000000); - assert_eq!(astro_deposit, 4979_061419); - - let inj_pool = helper.coin_balance(&test_coins[0], &helper.pair_addr); - let astro_pool = helper.coin_balance(&test_coins[1], &helper.pair_addr); - - // Total liquidity is close to initial provided liquidity - let total_inj = inj_deposit + inj_pool; - let total_astro = astro_deposit + astro_pool; - assert_eq!(total_inj, 500039_497794763370151009); - assert_eq!(total_astro, 100007_7251964); -} - -#[test] -fn test_wrong_assets_order() { - let owner = generate_inj_address(); - - let test_coins = vec![TestCoin::native("inj"), TestCoin::native("astro")]; - - let params = ConcentratedPoolParams { - price_scale: f64_to_dec(0.5), - ..common_pcl_params() - }; - let mut app = mock_inj_app(|_, _, _| {}); - let market_id = app - .create_market( - // NOTE: we intentionally changed denoms order - &test_coins[1].denom().unwrap(), - &test_coins[0].denom().unwrap(), - ) - .unwrap(); - let mut helper = - Helper::new_with_app(app, &owner, test_coins.clone(), params.clone(), false, None).unwrap(); - - let migrate_msg = MigrateMsg::MigrateToOrderbook { - params: OrderbookConfig { - market_id, - orders_number: 5, - min_trades_to_avg: *MIN_TRADES_TO_AVG_LIMITS.start(), - }, - }; - let new_code_id = helper.app.store_code(orderbook_pair_contract()); - let err = helper - .app - .migrate_contract( - owner.clone(), - helper.pair_addr.clone(), - &migrate_msg, - new_code_id, - ) - .unwrap_err(); - assert_eq!(err.root_cause().to_string(), "Generic error: Pair asset infos have different order than market: inj-astro while market has astro-inj"); - - let mut app = mock_inj_app(|_, _, _| {}); - let market_id = app - .create_market( - // NOTE: we intentionally changed denoms order - &test_coins[1].denom().unwrap(), - &test_coins[0].denom().unwrap(), - ) - .unwrap(); - let err = Helper::new_with_app( - app, - &owner, - test_coins.clone(), - params.clone(), - true, - Some(market_id), - ) - .unwrap_err(); - assert_eq!(err.root_cause().to_string(), "Generic error: Pair asset infos have different order than market: inj-astro while market has astro-inj"); -} - #[test] fn test_feegrant_mode() { let owner = generate_inj_address(); @@ -1752,9 +1551,7 @@ fn test_feegrant_mode() { #[test] fn provide_liquidity_with_autostaking_to_generator() { - use astroport_mocks::{ - astroport_address, MockConcentratedPairInjBuilder, MockGeneratorBuilder, - }; + use astroport_mocks::{astroport_address, MockConcentratedPairBuilder, MockGeneratorBuilder}; let astroport = astroport_address(); let app = Rc::new(RefCell::new(mock_inj_app(|router, _, storage| { @@ -1785,7 +1582,7 @@ fn provide_liquidity_with_autostaking_to_generator() { let ucosmos = native_asset_info("ucosmos".to_owned()); let ustake = native_asset_info("ustake".to_owned()); - let pair = MockConcentratedPairInjBuilder::new(&app) + let pair = MockConcentratedPairBuilder::new(&app) .with_factory(&factory) .with_asset(&ucosmos) .with_asset(&ustake) diff --git a/packages/astroport/Cargo.toml b/packages/astroport/Cargo.toml index 8c879c2bc..ce9a974ca 100644 --- a/packages/astroport/Cargo.toml +++ b/packages/astroport/Cargo.toml @@ -29,7 +29,7 @@ cw3 = "1.0" cw-asset = "3.0.0" # optional -injective-math = { version = "0.1", optional = true } +injective-math = { version = "0.2.4", optional = true } thiserror = { version = "1.0", optional = true } [dev-dependencies] diff --git a/packages/astroport/src/pair_concentrated_inj.rs b/packages/astroport/src/pair_concentrated_inj.rs index cd6b16b7c..2b9c7bb9d 100644 --- a/packages/astroport/src/pair_concentrated_inj.rs +++ b/packages/astroport/src/pair_concentrated_inj.rs @@ -15,10 +15,16 @@ use crate::pair_concentrated::{ConcentratedPoolParams, PromoteParams, UpdatePool #[cw_serde] pub struct OrderbookConfig { pub market_id: String, + /// Number of orders on each side of the orderbook pub orders_number: u8, pub min_trades_to_avg: u32, + /// Minimum order size for base token + pub min_base_order_size: u32, + /// Minimum order size for quote token + pub min_quote_order_size: u32, + /// Percent of liquidity to be deployed to the orderbook + pub liquidity_percent: Decimal, } - /// This structure holds concentrated pool parameters along with orderbook params specific for Injective. #[cw_serde] pub struct ConcentratedInjObParams { @@ -26,6 +32,19 @@ pub struct ConcentratedInjObParams { pub orderbook_config: OrderbookConfig, } +/// This structure holds orderbook params specific for update order book state. +#[cw_serde] +pub struct UpdateOrderBookParams { + /// Number of orders on each side of the orderbook + pub orders_number: Option, + /// Minimum trade size for base asset orders + pub min_base_order_size: Option, + /// Minimum trade size for quote asset orders + pub min_quote_order_size: Option, + /// Percent of liquidity to be deployed to the orderbook + pub liquidity_percent: Option, +} + /// This structure is extended version of [`crate::pair::ExecuteMsg`]. #[cw_serde] pub enum ExecuteMsg { @@ -143,8 +162,8 @@ pub struct OrderbookStateResponse { #[cw_serde] pub enum MigrateMsg { - MigrateToOrderbook { params: OrderbookConfig }, Migrate {}, + MigrateWithParams(Binary), } /// This enum is intended for parameters update. @@ -157,5 +176,5 @@ pub enum ConcentratedObPoolUpdateParams { /// Stops Amp and Gamma update and stores current values. StopChangingAmpGamma {}, /// Update orderbook params. - UpdateOrderbookParams { orders_number: u8 }, + UpdateOrderbookParams(UpdateOrderBookParams), } From 8a76381119863e9bcfd86231dd5f11e05494218d Mon Sep 17 00:00:00 2001 From: j0nl1 Date: Sun, 4 Feb 2024 00:00:54 +0000 Subject: [PATCH 2/5] feat: remove ready parameter in orderbook --- .../pair_concentrated_inj/src/contract.rs | 2 +- .../pair_concentrated_inj/src/migrate.rs | 2 - .../src/orderbook/consts.rs | 5 - .../src/orderbook/state.rs | 92 ++++++++----------- .../src/orderbook/sudo.rs | 42 ++++----- contracts/pair_concentrated_inj/src/utils.rs | 81 +++------------- .../pair_concentrated_inj/tests/helper/mod.rs | 1 - .../pair_inj_concentrated_integration.rs | 1 - .../astroport/src/pair_concentrated_inj.rs | 12 +-- 9 files changed, 78 insertions(+), 160 deletions(-) diff --git a/contracts/pair_concentrated_inj/src/contract.rs b/contracts/pair_concentrated_inj/src/contract.rs index 37fe00312..337bba6ba 100644 --- a/contracts/pair_concentrated_inj/src/contract.rs +++ b/contracts/pair_concentrated_inj/src/contract.rs @@ -837,7 +837,7 @@ where } // Store observation from precommit data - accumulate_swap_sizes(deps.storage, &env, &mut ob_state)?; + accumulate_swap_sizes(deps.storage, &env)?; // Store time series data in precommit observation. // Skipping small unsafe values which can seriously mess oracle price due to rounding errors. diff --git a/contracts/pair_concentrated_inj/src/migrate.rs b/contracts/pair_concentrated_inj/src/migrate.rs index badb45432..c8a21dd97 100644 --- a/contracts/pair_concentrated_inj/src/migrate.rs +++ b/contracts/pair_concentrated_inj/src/migrate.rs @@ -25,7 +25,6 @@ pub fn migrate( let OrderbookConfig { liquidity_percent, min_base_order_size, - min_trades_to_avg, min_quote_order_size, orders_number, .. @@ -36,7 +35,6 @@ pub fn migrate( ob_state.min_base_order_size = min_base_order_size; ob_state.min_quote_order_size = min_quote_order_size; ob_state.orders_number = orders_number; - ob_state.min_trades_to_avg = min_trades_to_avg; ob_state.save(deps.storage)?; } _ => { diff --git a/contracts/pair_concentrated_inj/src/orderbook/consts.rs b/contracts/pair_concentrated_inj/src/orderbook/consts.rs index 254f414f4..a9a8e56b3 100644 --- a/contracts/pair_concentrated_inj/src/orderbook/consts.rs +++ b/contracts/pair_concentrated_inj/src/orderbook/consts.rs @@ -1,13 +1,8 @@ -use astroport::observation::OBSERVATIONS_SIZE; use std::ops::RangeInclusive; /// Validation limits for order size. pub const ORDER_SIZE_LIMITS: RangeInclusive = 1..=30; -/// Validation limits for minimal number of trades to average price. See [`crate::utils::accumulate_swap_sizes`] -/// why we need such exotic limits. -pub const MIN_TRADES_TO_AVG_LIMITS: RangeInclusive = 1..=OBSERVATIONS_SIZE; - /// Starting from v1.10 injective uses default subaccount (nonce = 0) to automatically transfer /// funds from bank module when creating an order. We need to avoid it. pub const SUBACC_NONCE: u32 = 1; diff --git a/contracts/pair_concentrated_inj/src/orderbook/state.rs b/contracts/pair_concentrated_inj/src/orderbook/state.rs index 37271e0b6..61e577d61 100644 --- a/contracts/pair_concentrated_inj/src/orderbook/state.rs +++ b/contracts/pair_concentrated_inj/src/orderbook/state.rs @@ -16,7 +16,7 @@ use astroport::pair_concentrated_inj::{ OrderbookConfig, OrderbookStateResponse, UpdateOrderBookParams, }; -use crate::orderbook::consts::{MIN_TRADES_TO_AVG_LIMITS, ORDER_SIZE_LIMITS}; +use crate::orderbook::consts::ORDER_SIZE_LIMITS; use crate::orderbook::error::OrderbookError; use crate::orderbook::utils::{calc_market_ids, get_subaccount}; @@ -60,10 +60,6 @@ pub struct OrderbookState { pub min_quote_order_size: u32, /// The percentage of the pool's liquidity that will be placed in the order book. pub liquidity_percent: Decimal, - /// Orderbook integration will not be enabled until this number is reached. - pub min_trades_to_avg: u32, - /// Whether the pool is ready to integrate with the orderbook (MIN_TRADES_TO_AVG is reached) - pub ready: bool, /// Whether the begin blocker execution is allowed or not. Default: true pub enabled: bool, } @@ -78,15 +74,9 @@ impl OrderbookState { base_precision: u8, orderbook_config: OrderbookConfig, ) -> StdResult { - let market_id = MarketId::new(orderbook_config.market_id)?; + let market_id = MarketId::new(orderbook_config.market_id.clone())?; - Self::validate( - querier, - asset_infos, - &market_id, - orderbook_config.orders_number, - orderbook_config.min_trades_to_avg, - )?; + Self::validate(querier, asset_infos, &market_id, &orderbook_config)?; let mut state = Self { market_id, @@ -103,8 +93,6 @@ impl OrderbookState { min_quote_order_size: orderbook_config.min_quote_order_size, liquidity_percent: orderbook_config.liquidity_percent, orders_number: orderbook_config.orders_number, - min_trades_to_avg: orderbook_config.min_trades_to_avg, - ready: false, enabled: true, }; @@ -126,22 +114,12 @@ impl OrderbookState { querier: QuerierWrapper, asset_infos: &[AssetInfo], market_id: &MarketId, - orders_number: u8, - min_trades_to_avg: u32, + orderbook_config: &OrderbookConfig, ) -> StdResult<()> { - validate_param!( - orders_number, - orders_number, - *ORDER_SIZE_LIMITS.start(), - *ORDER_SIZE_LIMITS.end() - ); - - validate_param!( - min_trades_to_avg, - min_trades_to_avg, - *MIN_TRADES_TO_AVG_LIMITS.start(), - *MIN_TRADES_TO_AVG_LIMITS.end() - ); + Self::validate_orders_number(orderbook_config.orders_number)?; + Self::validate_liquidity_percent(orderbook_config.liquidity_percent)?; + Self::validate_min_order_size(orderbook_config.min_base_order_size)?; + Self::validate_min_order_size(orderbook_config.min_quote_order_size)?; let market_ids = calc_market_ids(asset_infos)?; @@ -167,6 +145,31 @@ impl OrderbookState { Ok(()) } + fn validate_orders_number(orders_number: u8) -> StdResult<()> { + validate_param!( + orders_number, + orders_number, + *ORDER_SIZE_LIMITS.start(), + *ORDER_SIZE_LIMITS.end() + ); + Ok(()) + } + + fn validate_liquidity_percent(liquidity_percent: Decimal) -> StdResult<()> { + validate_param!( + liquidity_percent, + liquidity_percent, + Decimal::from_str("0.01")?, + Decimal::percent(100) + ); + Ok(()) + } + + fn validate_min_order_size(order_size: u32) -> StdResult<()> { + validate_param!(order_size, order_size, 1, u32::MAX); + Ok(()) + } + /// Querying exchange module, converting into [`Decimal256`] and caching tick sizes. /// Cashed values help to save gas on begin blocker iterations. pub fn set_ticks( @@ -230,11 +233,6 @@ impl OrderbookState { ) } - /// If min_trades_to_avg has been reached, set ready flag to true. - pub fn ready(&mut self, ready: bool) { - self.ready = ready; - } - pub fn update_params( storage: &mut dyn Storage, update_params: UpdateOrderBookParams, @@ -242,13 +240,7 @@ impl OrderbookState { let mut attributes: Vec<_> = vec![]; if let Some(orders_number) = update_params.orders_number { - validate_param!( - orders_number, - orders_number, - *ORDER_SIZE_LIMITS.start(), - *ORDER_SIZE_LIMITS.end() - ); - + Self::validate_orders_number(orders_number)?; OB_CONFIG .update(storage, |mut ob_state| -> StdResult { ob_state.orders_number = orders_number; @@ -259,7 +251,7 @@ impl OrderbookState { } if let Some(min_base_order_size) = update_params.min_base_order_size { - validate_param!(min_base_order_size, min_base_order_size, 1, u32::MAX); + Self::validate_min_order_size(min_base_order_size)?; OB_CONFIG .update(storage, |mut ob_state| -> StdResult { ob_state.min_base_order_size = min_base_order_size; @@ -270,7 +262,7 @@ impl OrderbookState { } if let Some(min_quote_order_size) = update_params.min_quote_order_size { - validate_param!(min_quote_order_size, min_quote_order_size, 1, u32::MAX); + Self::validate_min_order_size(min_quote_order_size)?; OB_CONFIG .update(storage, |mut ob_state| -> StdResult { ob_state.min_quote_order_size = min_quote_order_size; @@ -284,12 +276,7 @@ impl OrderbookState { } if let Some(liquidity_percent) = update_params.liquidity_percent { - validate_param!( - liquidity_percent, - liquidity_percent, - Decimal::from_str("0.01")?, - Decimal::percent(100) - ); + Self::validate_liquidity_percent(liquidity_percent)?; OB_CONFIG .update(storage, |mut ob_state| -> StdResult { ob_state.liquidity_percent = liquidity_percent; @@ -313,8 +300,9 @@ impl From for OrderbookStateResponse { need_reconcile: value.need_reconcile, last_balances: value.last_balances, orders_number: value.orders_number, - min_trades_to_avg: value.min_trades_to_avg, - ready: value.ready, + min_base_order_size: value.min_base_order_size, + min_quote_order_size: value.min_quote_order_size, + liquidity_percent: value.liquidity_percent, enabled: value.enabled, } } diff --git a/contracts/pair_concentrated_inj/src/orderbook/sudo.rs b/contracts/pair_concentrated_inj/src/orderbook/sudo.rs index 6fd3a8268..9ff65a9a0 100644 --- a/contracts/pair_concentrated_inj/src/orderbook/sudo.rs +++ b/contracts/pair_concentrated_inj/src/orderbook/sudo.rs @@ -1,4 +1,3 @@ -use astroport::cosmwasm_ext::DecimalToInteger; use cosmwasm_std::{entry_point, Decimal256, DepsMut, Env, Response, StdResult}; use injective_cosmwasm::{ create_deposit_msg, create_withdraw_msg, InjectiveMsgWrapper, InjectiveQuerier, @@ -39,7 +38,7 @@ fn begin_blocker( env: Env, ) -> Result, OrderbookError> { let ob_state = OrderbookState::load(deps.storage)?; - if !(ob_state.ready && ob_state.enabled) { + if !(ob_state.enabled) { return Ok(Response::new()); } let querier = InjectiveQuerier::new(&deps.querier); @@ -63,6 +62,23 @@ fn begin_blocker( .map(|asset| asset.amount) .collect_vec(); + // Calculate total order size using the sum of an arithmetic progression + let total_order_size = (1 + ob_state.orders_number) * ob_state.orders_number / 2; + let liquidity_percent = Decimal256::from(ob_state.liquidity_percent); + + let base_order_size = pools[0] * liquidity_percent; + let quote_order_size = pools[1] * liquidity_percent; + + let balances_are_zero = balances.iter().all(|balance| balance.amount.is_zero()); + + // Since there is not balance before and don't meet min order size, we don't need to do anything + if balances_are_zero + && (base_order_size < Decimal256::from_integer(ob_state.min_base_order_size) + || quote_order_size < Decimal256::from_integer(ob_state.min_quote_order_size)) + { + return Ok(Response::new()); + } + let base_asset_precision = precisions.get_precision(&config.pair_info.asset_infos[0])?; let quote_asset_precision = precisions.get_precision(&config.pair_info.asset_infos[1])?; @@ -84,13 +100,7 @@ fn begin_blocker( CONFIG.save(deps.storage, &config)?; } - // Calculate total order size using the sum of an arithmetic progression - let total_order_size = (1 + ob_state.orders_number) * ob_state.orders_number / 2; - let liquidity_percent = Decimal256::from(ob_state.liquidity_percent); - - let base_order_size = pools[0] * liquidity_percent; - let quote_order_size = pools[1] * liquidity_percent; - + // If we previously had balances but we don't meet min order size, we leave orderbook if base_order_size < Decimal256::from_integer(ob_state.min_base_order_size) || quote_order_size < Decimal256::from_integer(ob_state.min_quote_order_size) { @@ -100,20 +110,6 @@ fn begin_blocker( let base_trade_size = base_order_size / Decimal256::from_integer(total_order_size); let quote_trade_size = quote_order_size / Decimal256::from_integer(total_order_size); - println!( - "liquidity_percent: {}", - (Decimal256::one() * base_trade_size) * base_trade_size / Decimal256::raw(2) - ); - - // Adjusting to min quantity tick size on Injective market - let base_trade_size = (base_trade_size / ob_state.min_quantity_tick_size).floor() - * ob_state.min_quantity_tick_size; - - // If adjusted avg_trade_size is zero we cancel all orders and withdraw liquidity. - if base_trade_size.is_zero() { - return leave_orderbook(&ob_state, balances, &env); - } - let amp_gamma = config.pool_state.get_amp_gamma(&env); let mut ixs = pools.to_vec(); ixs[1] *= config.pool_state.price_state.price_scale; diff --git a/contracts/pair_concentrated_inj/src/utils.rs b/contracts/pair_concentrated_inj/src/utils.rs index d208bd0a2..09dac05de 100644 --- a/contracts/pair_concentrated_inj/src/utils.rs +++ b/contracts/pair_concentrated_inj/src/utils.rs @@ -79,11 +79,7 @@ pub(crate) fn query_pools( } /// Calculate and save price moving average -pub fn accumulate_swap_sizes( - storage: &mut dyn Storage, - env: &Env, - ob_state: &mut OrderbookState, -) -> BufferResult<()> { +pub fn accumulate_swap_sizes(storage: &mut dyn Storage, env: &Env) -> BufferResult<()> { if let Some(PrecommitObservation { base_amount, quote_amount, @@ -123,11 +119,6 @@ pub fn accumulate_swap_sizes( }; } - // Enable orderbook if we have enough observations - if !ob_state.ready && (buffer.head() + 1) >= ob_state.min_trades_to_avg { - ob_state.ready(true) - } - buffer.instant_push(storage, &new_observation)? } } else { @@ -149,14 +140,18 @@ pub fn accumulate_swap_sizes( #[cfg(test)] mod tests { + use std::fmt::Display; + use std::str::FromStr; + use cosmwasm_std::testing::{mock_env, MockStorage}; use cosmwasm_std::{BlockInfo, Timestamp}; - use injective_cosmwasm::{MarketId, SubaccountId}; - - use crate::orderbook::consts::MIN_TRADES_TO_AVG_LIMITS; use super::*; + pub fn dec_to_f64(val: impl Display) -> f64 { + f64::from_str(&val.to_string()).unwrap() + } + fn next_block(block: &mut BlockInfo) { block.height += 1; block.time = block.time.plus_seconds(1); @@ -167,26 +162,11 @@ mod tests { let mut store = MockStorage::new(); let mut env = mock_env(); env.block.time = Timestamp::from_seconds(1); - let mut ob_state = OrderbookState { - market_id: MarketId::unchecked("test"), - subaccount: SubaccountId::unchecked("test"), - asset_infos: vec![], - min_price_tick_size: Default::default(), - min_quantity_tick_size: Default::default(), - need_reconcile: false, - last_balances: vec![], - orders_number: 0, - liquidity_percent: Default::default(), - min_base_order_size: Default::default(), - min_quote_order_size: Default::default(), - min_trades_to_avg: *MIN_TRADES_TO_AVG_LIMITS.start(), - ready: false, - enabled: true, - }; + BufferManager::init(&mut store, OBSERVATIONS, 10).unwrap(); for _ in 0..=50 { - accumulate_swap_sizes(&mut store, &env, &mut ob_state).unwrap(); + accumulate_swap_sizes(&mut store, &env).unwrap(); PrecommitObservation::save(&mut store, &env, 1000u128.into(), 500u128.into()).unwrap(); next_block(&mut env.block); } @@ -196,44 +176,7 @@ mod tests { let obs = buffer.read_last(&store).unwrap().unwrap(); assert_eq!(obs.ts, 50); assert_eq!(buffer.head(), 0); - assert_eq!(obs.price, Decimal::raw(2)); - assert_eq!(obs.price_sma, Decimal::raw(2)); - } - - #[ignore] - #[test] - fn test_contract_ready() { - let mut store = MockStorage::new(); - let mut env = mock_env(); - let min_trades_to_avg = 10; - let mut ob_state = OrderbookState { - market_id: MarketId::unchecked("test"), - subaccount: SubaccountId::unchecked("test"), - asset_infos: vec![], - min_price_tick_size: Default::default(), - min_quantity_tick_size: Default::default(), - need_reconcile: false, - last_balances: vec![], - orders_number: 0, - liquidity_percent: Default::default(), - min_base_order_size: Default::default(), - min_quote_order_size: Default::default(), - min_trades_to_avg, - ready: false, - enabled: true, - }; - BufferManager::init(&mut store, OBSERVATIONS, min_trades_to_avg).unwrap(); - - for _ in 0..min_trades_to_avg { - accumulate_swap_sizes(&mut store, &env, &mut ob_state).unwrap(); - PrecommitObservation::save(&mut store, &env, 1000u128.into(), 500u128.into()).unwrap(); - next_block(&mut env.block); - } - assert!(!ob_state.ready, "Contract should not be ready yet"); - - // last observation to make contract ready - accumulate_swap_sizes(&mut store, &env, &mut ob_state).unwrap(); - - assert!(ob_state.ready, "Contract should be ready"); + assert_eq!(dec_to_f64(obs.price), 2.0_f64); + assert_eq!(dec_to_f64(obs.price_sma), 2.0_f64); } } diff --git a/contracts/pair_concentrated_inj/tests/helper/mod.rs b/contracts/pair_concentrated_inj/tests/helper/mod.rs index 6347abea9..ad24c20b1 100644 --- a/contracts/pair_concentrated_inj/tests/helper/mod.rs +++ b/contracts/pair_concentrated_inj/tests/helper/mod.rs @@ -330,7 +330,6 @@ impl Helper { orderbook_config: OrderbookConfig { market_id, orders_number: 5, - min_trades_to_avg: 1, min_base_order_size: 100, min_quote_order_size: 100, liquidity_percent: Decimal::percent(10), diff --git a/contracts/pair_concentrated_inj/tests/pair_inj_concentrated_integration.rs b/contracts/pair_concentrated_inj/tests/pair_inj_concentrated_integration.rs index bfe321d54..0e30adcdc 100644 --- a/contracts/pair_concentrated_inj/tests/pair_inj_concentrated_integration.rs +++ b/contracts/pair_concentrated_inj/tests/pair_inj_concentrated_integration.rs @@ -886,7 +886,6 @@ fn check_orderbook_integration() { let ob_state = helper.query_ob_config_smart().unwrap(); assert_eq!(ob_state.orders_number, 5); assert_eq!(ob_state.need_reconcile, false); // sudo endpoint was already executed and liq. deployed in OB - assert_eq!(ob_state.ready, true); let ob_config = helper.query_ob_config().unwrap(); let querier_wrapper = helper.app.wrap(); diff --git a/packages/astroport/src/pair_concentrated_inj.rs b/packages/astroport/src/pair_concentrated_inj.rs index 2b9c7bb9d..118122232 100644 --- a/packages/astroport/src/pair_concentrated_inj.rs +++ b/packages/astroport/src/pair_concentrated_inj.rs @@ -17,7 +17,6 @@ pub struct OrderbookConfig { pub market_id: String, /// Number of orders on each side of the orderbook pub orders_number: u8, - pub min_trades_to_avg: u32, /// Minimum order size for base token pub min_base_order_size: u32, /// Minimum order size for quote token @@ -151,11 +150,12 @@ pub struct OrderbookStateResponse { pub last_balances: Vec, /// Order number on each side of the orderbook pub orders_number: u8, - /// Minimum number of trades to accumulate average trade size. - /// Orderbook integration will not be enabled until this number is reached. - pub min_trades_to_avg: u32, - /// Whether the pool is ready to integrate with the orderbook (MIN_TRADES_TO_AVG is reached) - pub ready: bool, + /// Minimum order size for base token + pub min_base_order_size: u32, + /// Minimum order size for quote token + pub min_quote_order_size: u32, + /// Percent of liquidity to be deployed to the orderbook + pub liquidity_percent: Decimal, /// Whether the begin blocker execution is allowed or not. Default: true pub enabled: bool, } From 2dfb84bf30a152b54a967cd44b11ac64eac3a2b8 Mon Sep 17 00:00:00 2001 From: j0nl1 Date: Sun, 4 Feb 2024 01:07:50 +0000 Subject: [PATCH 3/5] test: update params checking in orderbook --- .../pair_concentrated_inj/src/contract.rs | 3 +- .../src/orderbook/state.rs | 208 +++++++++++++++++- .../pair_concentrated_inj/src/queries.rs | 150 +++++++++++++ 3 files changed, 349 insertions(+), 12 deletions(-) diff --git a/contracts/pair_concentrated_inj/src/contract.rs b/contracts/pair_concentrated_inj/src/contract.rs index 337bba6ba..9cf6844c1 100644 --- a/contracts/pair_concentrated_inj/src/contract.rs +++ b/contracts/pair_concentrated_inj/src/contract.rs @@ -910,7 +910,8 @@ fn update_config( vec![attr("action", "stop_changing_amp_gamma")] } ConcentratedObPoolUpdateParams::UpdateOrderbookParams(update_params) => { - let mut attrs = OrderbookState::update_params(deps.storage, update_params)?; + let ob_state = OrderbookState::load(deps.storage)?; + let mut attrs = ob_state.update_params(deps.storage, update_params)?; attrs.push(attr("action", "update_orderbook_params")); attrs } diff --git a/contracts/pair_concentrated_inj/src/orderbook/state.rs b/contracts/pair_concentrated_inj/src/orderbook/state.rs index 61e577d61..8cd70985c 100644 --- a/contracts/pair_concentrated_inj/src/orderbook/state.rs +++ b/contracts/pair_concentrated_inj/src/orderbook/state.rs @@ -118,8 +118,8 @@ impl OrderbookState { ) -> StdResult<()> { Self::validate_orders_number(orderbook_config.orders_number)?; Self::validate_liquidity_percent(orderbook_config.liquidity_percent)?; - Self::validate_min_order_size(orderbook_config.min_base_order_size)?; - Self::validate_min_order_size(orderbook_config.min_quote_order_size)?; + Self::validate_min_base_order_size(orderbook_config.min_base_order_size)?; + Self::validate_min_quote_order_size(orderbook_config.min_quote_order_size)?; let market_ids = calc_market_ids(asset_infos)?; @@ -165,8 +165,13 @@ impl OrderbookState { Ok(()) } - fn validate_min_order_size(order_size: u32) -> StdResult<()> { - validate_param!(order_size, order_size, 1, u32::MAX); + fn validate_min_base_order_size(min_base_order_size: u32) -> StdResult<()> { + validate_param!(min_base_order_size, min_base_order_size, 1, u32::MAX); + Ok(()) + } + + fn validate_min_quote_order_size(min_quote_order_size: u32) -> StdResult<()> { + validate_param!(min_quote_order_size, min_quote_order_size, 1, u32::MAX); Ok(()) } @@ -234,12 +239,13 @@ impl OrderbookState { } pub fn update_params( + self, storage: &mut dyn Storage, - update_params: UpdateOrderBookParams, + params: UpdateOrderBookParams, ) -> StdResult> { let mut attributes: Vec<_> = vec![]; - if let Some(orders_number) = update_params.orders_number { + if let Some(orders_number) = params.orders_number { Self::validate_orders_number(orders_number)?; OB_CONFIG .update(storage, |mut ob_state| -> StdResult { @@ -250,8 +256,8 @@ impl OrderbookState { attributes.push(attr("orders_number", orders_number.to_string())); } - if let Some(min_base_order_size) = update_params.min_base_order_size { - Self::validate_min_order_size(min_base_order_size)?; + if let Some(min_base_order_size) = params.min_base_order_size { + Self::validate_min_base_order_size(min_base_order_size)?; OB_CONFIG .update(storage, |mut ob_state| -> StdResult { ob_state.min_base_order_size = min_base_order_size; @@ -261,8 +267,8 @@ impl OrderbookState { attributes.push(attr("min_base_order_size", min_base_order_size.to_string())); } - if let Some(min_quote_order_size) = update_params.min_quote_order_size { - Self::validate_min_order_size(min_quote_order_size)?; + if let Some(min_quote_order_size) = params.min_quote_order_size { + Self::validate_min_quote_order_size(min_quote_order_size)?; OB_CONFIG .update(storage, |mut ob_state| -> StdResult { ob_state.min_quote_order_size = min_quote_order_size; @@ -275,7 +281,7 @@ impl OrderbookState { )); } - if let Some(liquidity_percent) = update_params.liquidity_percent { + if let Some(liquidity_percent) = params.liquidity_percent { Self::validate_liquidity_percent(liquidity_percent)?; OB_CONFIG .update(storage, |mut ob_state| -> StdResult { @@ -307,3 +313,183 @@ impl From for OrderbookStateResponse { } } } + +#[cfg(test)] +mod tests { + use std::fmt::Display; + + use cosmwasm_std::testing::MockStorage; + + use super::*; + + fn error_params(name: &str, min: T, max: T, val: T) -> StdError + where + T: Display, + { + StdError::generic_err(format!( + "Incorrect orderbook params: must be {min} <= {name} <= {max}, but value is {val}", + name = name, + min = min, + max = max, + val = val + )) + } + + #[test] + fn check_update_params() { + let min_liquidity_percent = Decimal::from_str("0.01").unwrap(); + let max_liquidity_percent = Decimal::percent(100); + let min_orders_number = ORDER_SIZE_LIMITS.start(); + let max_orders_number = ORDER_SIZE_LIMITS.end(); + let min_order_size = 1_u32; + + let mut storage = MockStorage::default(); + let ob_state = OrderbookState { + market_id: MarketId::unchecked( + "0x1c79dac019f73e4060494ab1b4fcba734350656d6fc4d474f6a238c13c6f9ced", + ), + subaccount: SubaccountId::unchecked( + "0xc7dca7c15c364865f77a4fb67ab11dc95502e6fe000000000000000000000001", + ), + asset_infos: vec![], + min_price_tick_size: Default::default(), + min_quantity_tick_size: Default::default(), + need_reconcile: false, + last_balances: vec![], + orders_number: 0, + liquidity_percent: Default::default(), + min_base_order_size: Default::default(), + min_quote_order_size: Default::default(), + enabled: true, + }; + + OB_CONFIG.save(&mut storage, &ob_state).unwrap(); + + let mut params = UpdateOrderBookParams { + orders_number: None, + min_base_order_size: None, + min_quote_order_size: None, + liquidity_percent: Some(Decimal::percent(0)), + }; + + // Should fail since liquidity percent is less than 0.01 + let res = ob_state + .clone() + .update_params(&mut storage, params.clone()) + .unwrap_err(); + + assert_eq!( + res, + error_params( + "liquidity_percent", + min_liquidity_percent, + max_liquidity_percent, + Decimal::percent(0) + ), + ); + + // Should fail if liquidity is bigger than 100% + params.liquidity_percent = Some(Decimal::percent(101)); + + let res = ob_state + .clone() + .update_params(&mut storage, params.clone()) + .unwrap_err(); + + assert_eq!( + res, + error_params( + "liquidity_percent", + min_liquidity_percent, + max_liquidity_percent, + Decimal::percent(101) + ), + ); + + // Should fail if orders_number is less than 1 + params.liquidity_percent = None; + params.orders_number = Some(0); + + let res = ob_state + .clone() + .update_params(&mut storage, params.clone()) + .unwrap_err(); + + assert_eq!( + res, + error_params("orders_number", min_orders_number, max_orders_number, &0), + ); + + // Should fail if orders_number is bigger than ORDER_SIZE_LIMITS.end() + + params.orders_number = Some(ORDER_SIZE_LIMITS.end() + 1); + + let res = ob_state + .clone() + .update_params(&mut storage, params.clone()) + .unwrap_err(); + + assert_eq!( + res, + error_params( + "orders_number", + min_orders_number, + max_orders_number, + &(ORDER_SIZE_LIMITS.end() + 1) + ), + ); + + // Should fail if min_base_order_size is less than 1 + params.orders_number = None; + params.min_base_order_size = Some(0); + + let res = ob_state + .clone() + .update_params(&mut storage, params.clone()) + .unwrap_err(); + + assert_eq!( + res, + error_params("min_base_order_size", min_order_size, u32::MAX, 0), + ); + + // Should fail if min_quote_order_size is less than 1 + params.min_base_order_size = None; + params.min_quote_order_size = Some(0); + + let res = ob_state + .clone() + .update_params(&mut storage, params.clone()) + .unwrap_err(); + + assert_eq!( + res, + error_params("min_quote_order_size", min_order_size, u32::MAX, 0), + ); + + // Should pass if all params are valid + let params = UpdateOrderBookParams { + orders_number: Some(5), + min_base_order_size: Some(22), + min_quote_order_size: Some(33), + liquidity_percent: Some(Decimal::percent(50)), + }; + + let res = ob_state + .update_params(&mut storage, params.clone()) + .unwrap(); + + let ob_state = OB_CONFIG.load(&mut storage).unwrap(); + + assert_eq!(ob_state.orders_number, 5); + assert_eq!(ob_state.min_base_order_size, 22); + assert_eq!(ob_state.min_quote_order_size, 33); + assert_eq!(ob_state.liquidity_percent, Decimal::percent(50)); + + assert_eq!(res.len(), 4); + assert_eq!(res[0], attr("orders_number", "5")); + assert_eq!(res[1], attr("min_base_order_size", "22")); + assert_eq!(res[2], attr("min_quote_order_size", "33")); + assert_eq!(res[3], attr("liquidity_percent", "0.5")); + } +} diff --git a/contracts/pair_concentrated_inj/src/queries.rs b/contracts/pair_concentrated_inj/src/queries.rs index 140019e6b..4b4fda638 100644 --- a/contracts/pair_concentrated_inj/src/queries.rs +++ b/contracts/pair_concentrated_inj/src/queries.rs @@ -363,4 +363,154 @@ mod testing { { T::from_str(&val.to_string()).unwrap() } + + #[test] + fn observations_full_buffer() { + let mut deps = mock_dependencies(); + let mut env = mock_env(); + env.block.time = Timestamp::from_seconds(100_000); + BufferManager::init(&mut deps.storage, OBSERVATIONS, 20).unwrap(); + + let mut buffer = BufferManager::new(&deps.storage, OBSERVATIONS).unwrap(); + + let err = query_observation(deps.as_ref(), env.clone(), OBSERVATIONS, 11000).unwrap_err(); + assert_eq!(err.to_string(), "Generic error: Buffer is empty"); + + let array = (1..=30) + .into_iter() + .map(|i| Observation { + ts: env.block.time.seconds() + i * 1000, + price_sma: Decimal::from_ratio(i, i * i), + price: Default::default(), + }) + .collect_vec(); + buffer.push_many(&array); + buffer.commit(&mut deps.storage).unwrap(); + + env.block.time = env.block.time.plus_seconds(30_000); + + assert_eq!( + OracleObservation { + timestamp: 120_000, + price: f64_to_dec(20.0 / 400.0), + }, + query_observation(deps.as_ref(), env.clone(), OBSERVATIONS, 10000).unwrap() + ); + + assert_eq!( + OracleObservation { + timestamp: 124_411, + price: f64_to_dec(0.04098166666666694), + }, + query_observation(deps.as_ref(), env.clone(), OBSERVATIONS, 5589).unwrap() + ); + + let err = query_observation(deps.as_ref(), env, OBSERVATIONS, 35_000).unwrap_err(); + assert_eq!( + err.to_string(), + "Generic error: Requested observation is too old. Last known observation is at 111000" + ); + } + + #[test] + fn observations_incomplete_buffer() { + let mut deps = mock_dependencies(); + let mut env = mock_env(); + env.block.time = Timestamp::from_seconds(100_000); + BufferManager::init(&mut deps.storage, OBSERVATIONS, 3000).unwrap(); + + let mut buffer = BufferManager::new(&deps.storage, OBSERVATIONS).unwrap(); + + let err = query_observation(deps.as_ref(), env.clone(), OBSERVATIONS, 11000).unwrap_err(); + assert_eq!(err.to_string(), "Generic error: Buffer is empty"); + + let array = (1..=30) + .into_iter() + .map(|i| Observation { + ts: env.block.time.seconds() + i * 1000, + price: Default::default(), + price_sma: Decimal::from_ratio(i, i * i), + }) + .collect_vec(); + buffer.push_many(&array); + buffer.commit(&mut deps.storage).unwrap(); + + env.block.time = env.block.time.plus_seconds(30_000); + + assert_eq!( + OracleObservation { + timestamp: 120_000, + price: f64_to_dec(20.0 / 400.0), + }, + query_observation(deps.as_ref(), env.clone(), OBSERVATIONS, 10000).unwrap() + ); + + assert_eq!( + OracleObservation { + timestamp: 124_411, + price: f64_to_dec(0.04098166666666694), + }, + query_observation(deps.as_ref(), env.clone(), OBSERVATIONS, 5589).unwrap() + ); + } + + #[test] + fn observations_checking_triple_capacity_step_by_step() { + let mut deps = mock_dependencies(); + let mut env = mock_env(); + env.block.time = Timestamp::from_seconds(100_000); + const CAPACITY: u32 = 20; + BufferManager::init(&mut deps.storage, OBSERVATIONS, CAPACITY).unwrap(); + + let mut buffer = BufferManager::new(&deps.storage, OBSERVATIONS).unwrap(); + + let ts = env.block.time.seconds(); + + let array = (1..=CAPACITY * 3) + .into_iter() + .map(|i| Observation { + ts: ts + i as u64 * 1000, + price: Default::default(), + price_sma: Decimal::from_ratio(i * i, i), + }) + .collect_vec(); + + for (k, obs) in array.iter().enumerate() { + env.block.time = env.block.time.plus_seconds(1000); + + buffer.push(&obs); + buffer.commit(&mut deps.storage).unwrap(); + let k1 = k as u32 + 1; + + let from = k1.saturating_sub(CAPACITY) + 1; + let to = k1; + + for i in from..=to { + let shift = (to - i) as u64; + if shift != 0 { + assert_eq!( + OracleObservation { + timestamp: ts + i as u64 * 1000 + 500, + price: f64_to_dec(i as f64 + 0.5), + }, + query_observation( + deps.as_ref(), + env.clone(), + OBSERVATIONS, + shift * 1000 - 500 + ) + .unwrap() + ); + } + assert_eq!( + OracleObservation { + timestamp: ts + i as u64 * 1000, + price: f64_to_dec(i as f64), + }, + query_observation(deps.as_ref(), env.clone(), OBSERVATIONS, shift * 1000) + .unwrap() + ); + } + } + } } From d3c9630cbe1d3012f4d0ecf44d23de922c1803ec Mon Sep 17 00:00:00 2001 From: j0nl1 Date: Mon, 5 Feb 2024 11:39:14 +0000 Subject: [PATCH 4/5] fix: min quantity order size --- .../src/orderbook/sudo.rs | 6 ++-- .../pair_concentrated_inj/tests/helper/mod.rs | 4 +-- .../pair_inj_concentrated_integration.rs | 28 ++++--------------- 3 files changed, 11 insertions(+), 27 deletions(-) diff --git a/contracts/pair_concentrated_inj/src/orderbook/sudo.rs b/contracts/pair_concentrated_inj/src/orderbook/sudo.rs index 9ff65a9a0..1e383a69d 100644 --- a/contracts/pair_concentrated_inj/src/orderbook/sudo.rs +++ b/contracts/pair_concentrated_inj/src/orderbook/sudo.rs @@ -107,8 +107,10 @@ fn begin_blocker( return leave_orderbook(&ob_state, balances, &env); } - let base_trade_size = base_order_size / Decimal256::from_integer(total_order_size); - let quote_trade_size = quote_order_size / Decimal256::from_integer(total_order_size); + let base_trade_size = + (base_order_size / Decimal256::from_integer(total_order_size)).floor(); + let quote_trade_size = + (quote_order_size / Decimal256::from_integer(total_order_size)).floor(); let amp_gamma = config.pool_state.get_amp_gamma(&env); let mut ixs = pools.to_vec(); diff --git a/contracts/pair_concentrated_inj/tests/helper/mod.rs b/contracts/pair_concentrated_inj/tests/helper/mod.rs index ad24c20b1..47e90dd82 100644 --- a/contracts/pair_concentrated_inj/tests/helper/mod.rs +++ b/contracts/pair_concentrated_inj/tests/helper/mod.rs @@ -330,8 +330,8 @@ impl Helper { orderbook_config: OrderbookConfig { market_id, orders_number: 5, - min_base_order_size: 100, - min_quote_order_size: 100, + min_base_order_size: 1000, + min_quote_order_size: 500, liquidity_percent: Decimal::percent(10), }, }) diff --git a/contracts/pair_concentrated_inj/tests/pair_inj_concentrated_integration.rs b/contracts/pair_concentrated_inj/tests/pair_inj_concentrated_integration.rs index 0e30adcdc..887a9ab4c 100644 --- a/contracts/pair_concentrated_inj/tests/pair_inj_concentrated_integration.rs +++ b/contracts/pair_concentrated_inj/tests/pair_inj_concentrated_integration.rs @@ -2,7 +2,6 @@ use std::cell::RefCell; use std::rc::Rc; -use std::str::FromStr; use astroport_pcl_common::consts::{AMP_MAX, AMP_MIN, MA_HALF_TIME_LIMITS}; use cosmwasm_std::{coins, Addr, Coin, Decimal, Decimal256, StdError, Uint128}; @@ -891,14 +890,6 @@ fn check_orderbook_integration() { let querier_wrapper = helper.app.wrap(); let inj_querier = InjectiveQuerier::new(&querier_wrapper); - let pools = helper - .query_pool() - .unwrap() - .assets - .iter() - .map(|asset| asset.amount) - .collect_vec(); - let inj_deposit: u128 = inj_querier .query_subaccount_deposit(&ob_config.subaccount, &"inj".to_string()) .unwrap() @@ -912,27 +903,18 @@ fn check_orderbook_integration() { .total_balance .into(); - /* let expected_inj = - Decimal::from_str(&pools[0].to_string()).unwrap() * Decimal::from_ratio(5u128, 100u128); */ - println!( - "expected_inj: {} total: {} deposited: {}", - Decimal256::from_str(&pools[0].to_string()).unwrap() * Decimal256::from_str("0.1").unwrap(), - pools[0], - inj_deposit - ); - - assert_eq!(astro_deposit, 4979_051501); - + assert_eq!(inj_deposit, 16540_551000000000000000); + assert_eq!(astro_deposit, 32844_980113); let inj_pool = helper.coin_balance(&test_coins[0], &helper.pair_addr); let astro_pool = helper.coin_balance(&test_coins[1], &helper.pair_addr); - assert_eq!(inj_pool, 497542_933893233248565365); - assert_eq!(astro_pool, 995085_325039); + assert_eq!(inj_pool, 483492_363893233248386874); + assert_eq!(astro_pool, 967219_396427); // total liquidity is close to initial provided liquidity let total_inj = inj_deposit + inj_pool; let total_astro = astro_deposit + astro_pool; - assert_eq!(total_inj, 500032_914893233248565365); + assert_eq!(total_inj, 500032_914893233248386874); assert_eq!(total_astro, 100006_4376540); let random_user = generate_inj_address(); From c4f366de37dd0265237eb16eeebf093ea63e11e1 Mon Sep 17 00:00:00 2001 From: j0nl1 Date: Mon, 5 Feb 2024 16:45:54 +0000 Subject: [PATCH 5/5] fix: clippy and fomart --- contracts/pair_concentrated_inj/src/contract.rs | 6 +++--- contracts/pair_concentrated_inj/src/migrate.rs | 2 +- contracts/pair_concentrated_inj/src/orderbook/state.rs | 2 +- .../tests/pair_inj_concentrated_integration.rs | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/contracts/pair_concentrated_inj/src/contract.rs b/contracts/pair_concentrated_inj/src/contract.rs index 9cf6844c1..685357eef 100644 --- a/contracts/pair_concentrated_inj/src/contract.rs +++ b/contracts/pair_concentrated_inj/src/contract.rs @@ -70,7 +70,7 @@ pub fn instantiate( } let orderbook_params: ConcentratedInjObParams = from_json( - &msg.init_params + msg.init_params .ok_or(ContractError::InitParamsNotFound {})?, )?; @@ -518,11 +518,11 @@ where // calculate accrued share let share_ratio = share / (total_share + share); - let balanced_share = vec![ + let balanced_share = [ new_xp[0] * share_ratio, new_xp[1] * share_ratio / config.pool_state.price_state.price_scale, ]; - let assets_diff = vec![ + let assets_diff = [ deposits[0].diff(balanced_share[0]), deposits[1].diff(balanced_share[1]), ]; diff --git a/contracts/pair_concentrated_inj/src/migrate.rs b/contracts/pair_concentrated_inj/src/migrate.rs index c8a21dd97..58cc8cea7 100644 --- a/contracts/pair_concentrated_inj/src/migrate.rs +++ b/contracts/pair_concentrated_inj/src/migrate.rs @@ -28,7 +28,7 @@ pub fn migrate( min_quote_order_size, orders_number, .. - } = from_json(&data)?; + } = from_json(data)?; let mut ob_state = OrderbookState::load(deps.storage)?; ob_state.liquidity_percent = liquidity_percent; diff --git a/contracts/pair_concentrated_inj/src/orderbook/state.rs b/contracts/pair_concentrated_inj/src/orderbook/state.rs index 8cd70985c..3934cc8da 100644 --- a/contracts/pair_concentrated_inj/src/orderbook/state.rs +++ b/contracts/pair_concentrated_inj/src/orderbook/state.rs @@ -22,7 +22,7 @@ use crate::orderbook::utils::{calc_market_ids, get_subaccount}; macro_rules! validate_param { ($name:ident, $val:expr, $min:expr, $max:expr) => { - if $val < $min || $val > $max { + if !($min..$max).contains(&$val) { return Err(StdError::generic_err(format!( "Incorrect orderbook params: must be {min} <= {name} <= {max}, but value is {val}", name = stringify!($name), diff --git a/contracts/pair_concentrated_inj/tests/pair_inj_concentrated_integration.rs b/contracts/pair_concentrated_inj/tests/pair_inj_concentrated_integration.rs index 887a9ab4c..31d647d04 100644 --- a/contracts/pair_concentrated_inj/tests/pair_inj_concentrated_integration.rs +++ b/contracts/pair_concentrated_inj/tests/pair_inj_concentrated_integration.rs @@ -903,13 +903,13 @@ fn check_orderbook_integration() { .total_balance .into(); - assert_eq!(inj_deposit, 16540_551000000000000000); - assert_eq!(astro_deposit, 32844_980113); + assert_eq!(inj_deposit, 16540_316000000000000000); + assert_eq!(astro_deposit, 32839_685906); let inj_pool = helper.coin_balance(&test_coins[0], &helper.pair_addr); let astro_pool = helper.coin_balance(&test_coins[1], &helper.pair_addr); - assert_eq!(inj_pool, 483492_363893233248386874); - assert_eq!(astro_pool, 967219_396427); + assert_eq!(inj_pool, 483492_598893233248386874); + assert_eq!(astro_pool, 967224_690634); // total liquidity is close to initial provided liquidity let total_inj = inj_deposit + inj_pool;