diff --git a/Cargo.lock b/Cargo.lock index ebecff7..84a17f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "ahash" version = "0.7.8" @@ -13,18 +28,53 @@ dependencies = [ "version_check", ] +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "anyhow" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +[[package]] +name = "async-trait" +version = "0.1.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.49", +] + [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base16ct" version = "0.2.0" @@ -49,6 +99,57 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags 2.5.0", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.49", + "which", +] + +[[package]] +name = "bip32" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e141fb0f8be1c7b45887af94c88b182472b57c96b56773250ae00cd6a14a164" +dependencies = [ + "bs58", + "hmac", + "k256", + "rand_core 0.6.4", + "ripemd", + "sha2 0.10.8", + "subtle", + "zeroize", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + [[package]] name = "block-buffer" version = "0.9.0" @@ -73,6 +174,21 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56953345e39537a3e18bdaeba4cb0c58a78c1f61f361dc0fa7c5c7340ae87c5f" +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "sha2 0.10.8", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "byteorder" version = "1.5.0" @@ -84,6 +200,24 @@ name = "bytes" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +dependencies = [ + "serde", +] + +[[package]] +name = "cc" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] [[package]] name = "cfg-if" @@ -100,12 +234,71 @@ dependencies = [ "num-traits", ] +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "const-oid" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cosmos-sdk-proto" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32560304ab4c365791fd307282f76637213d8083c1a98490c35159cd67852237" +dependencies = [ + "prost 0.12.3", + "prost-types 0.12.3", + "tendermint-proto", +] + +[[package]] +name = "cosmrs" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47126f5364df9387b9d8559dcef62e99010e1d4098f39eb3f7ee4b5c254e40ea" +dependencies = [ + "bip32", + "cosmos-sdk-proto", + "ecdsa", + "eyre", + "k256", + "rand_core 0.6.4", + "serde", + "serde_json", + "signature", + "subtle-encoding", + "tendermint", + "tendermint-rpc", + "thiserror", +] + [[package]] name = "cosmwasm-crypto" version = "1.5.5" @@ -219,6 +412,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "curve25519-dalek-ng" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.6.4", + "subtle-ng", + "zeroize", +] + [[package]] name = "cw-multi-test" version = "0.18.1" @@ -289,6 +495,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + [[package]] name = "derivative" version = "2.2.0" @@ -321,6 +536,17 @@ dependencies = [ "subtle", ] +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.49", +] + [[package]] name = "dyn-clone" version = "1.0.16" @@ -341,6 +567,29 @@ dependencies = [ "spki", ] +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-consensus" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8465edc8ee7436ffea81d21a019b16676ee3db267aa8d5a8d729581ecf998b" +dependencies = [ + "curve25519-dalek-ng", + "hex", + "rand_core 0.6.4", + "sha2 0.9.9", + "zeroize", +] + [[package]] name = "ed25519-zebra" version = "3.1.0" @@ -348,7 +597,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" dependencies = [ "curve25519-dalek", - "hashbrown", + "hashbrown 0.12.3", "hex", "rand_core 0.6.4", "serde", @@ -381,6 +630,41 @@ dependencies = [ "zeroize", ] +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + [[package]] name = "ff" version = "0.13.0" @@ -391,12 +675,98 @@ dependencies = [ "subtle", ] +[[package]] +name = "flex-error" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c606d892c9de11507fa0dcffc116434f94e105d0bbdc4e405b61519464c49d7b" +dependencies = [ + "eyre", + "paste", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "forward_ref" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -415,10 +785,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "group" version = "0.13.0" @@ -430,6 +814,25 @@ dependencies = [ "subtle", ] +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -439,6 +842,18 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + [[package]] name = "hex" version = "0.4.3" @@ -455,43 +870,285 @@ dependencies = [ ] [[package]] -name = "itertools" -version = "0.10.5" +name = "home" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "either", + "windows-sys 0.52.0", ] [[package]] -name = "itertools" -version = "0.11.0" +name = "http" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ - "either", + "bytes", + "fnv", + "itoa", ] [[package]] -name = "itertools" -version = "0.12.1" +name = "http-body" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ - "either", + "bytes", + "http", + "pin-project-lite", ] [[package]] -name = "itoa" -version = "1.0.10" +name = "httparse" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "d0e7a4dd27b9476dc40cb050d3632d3bba3a70ddbff012285f7f8559a1e7e545" [[package]] -name = "k256" -version = "0.13.1" +name = "httpdate" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.49", +] + +[[package]] +name = "idna" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed" +dependencies = [ + "icu_normalizer", + "icu_properties", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ "cfg-if", "ecdsa", @@ -501,12 +1158,117 @@ dependencies = [ "signature", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if", + "windows-targets 0.52.5", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -516,6 +1278,25 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -528,17 +1309,23 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + [[package]] name = "osmosis-std" -version = "0.16.2" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75895e4db1a81ca29118e366365744f64314938327e4eedba8e6e462fb15e94f" +checksum = "ca66dca7e8c9b11b995cd41a44c038134ccca4469894d663d8a9452d6e716241" dependencies = [ "chrono", "cosmwasm-std", - "osmosis-std-derive 0.16.2", - "prost 0.11.9", - "prost-types", + "osmosis-std-derive 0.20.1", + "prost 0.12.3", + "prost-types 0.12.3", "schemars", "serde", "serde-cw-value", @@ -558,373 +1345,1473 @@ dependencies = [ [[package]] name = "osmosis-std-derive" -version = "0.16.2" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f47f0b2f22adb341bb59e5a3a1b464dde033181954bd055b9ae86d6511ba465b" +checksum = "c5ebdfd1bc8ed04db596e110c6baa9b174b04f6ed1ec22c666ddc5cb3fa91bd7" dependencies = [ "itertools 0.10.5", "proc-macro2", - "prost-types", + "prost-types 0.11.9", "quote", "syn 1.0.109", ] [[package]] -name = "pkcs8" -version = "0.10.2" +name = "osmosis-test-tube" +version = "25.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +checksum = "5eb35dcc9adc1b39e23dfae07c9f04a60187fde57a52b7762434ea6548581a1a" dependencies = [ - "der", - "spki", + "base64", + "bindgen", + "cosmrs", + "cosmwasm-std", + "osmosis-std", + "prost 0.12.3", + "serde", + "serde_json", + "test-tube", + "thiserror", ] [[package]] -name = "ppv-lite86" -version = "0.2.17" +name = "paste" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] -name = "proc-macro2" -version = "1.0.78" +name = "peg" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "8a625d12ad770914cbf7eff6f9314c3ef803bfe364a1b20bc36ddf56673e71e5" dependencies = [ - "unicode-ident", + "peg-macros", + "peg-runtime", ] [[package]] -name = "prost" -version = "0.11.9" +name = "peg-macros" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f241d42067ed3ab6a4fece1db720838e1418f36d868585a27931f95d6bc03582" +dependencies = [ + "peg-runtime", + "proc-macro2", + "quote", +] + +[[package]] +name = "peg-runtime" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3aeb8f54c078314c2065ee649a7241f46b9d8e418e1a9581ba0546657d7aa3a" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.49", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "prettyplease" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" +dependencies = [ + "proc-macro2", + "syn 2.0.49", +] + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes", + "prost-derive 0.11.9", +] + +[[package]] +name = "prost" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" +dependencies = [ + "bytes", + "prost-derive 0.12.3", +] + +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools 0.10.5", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "prost-derive" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" +dependencies = [ + "anyhow", + "itertools 0.11.0", + "proc-macro2", + "quote", + "syn 2.0.49", +] + +[[package]] +name = "prost-types" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +dependencies = [ + "prost 0.11.9", +] + +[[package]] +name = "prost-types" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" +dependencies = [ + "prost 0.12.3", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-native-certs", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-rustls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "schemars" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +dependencies = [ + "bitflags 2.5.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" + +[[package]] +name = "serde" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-cw-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75d32da6b8ed758b7d850b6c3c08f1d7df51a4df3cb201296e63e34a78e99d4" +dependencies = [ + "serde", +] + +[[package]] +name = "serde-json-wasm" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e9213a07d53faa0b8dd81e767a54a8188a242fdb9be99ab75ec576a774bfdd7" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_bytes" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.49", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_json" +version = "1.0.113" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.49", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "subtle-encoding" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dcb1ed7b8330c5eed5441052651dd7a12c75e2ed88f2ec024ae1fa3a5e59945" +dependencies = [ + "zeroize", +] + +[[package]] +name = "subtle-ng" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" + +[[package]] +name = "sumtree-orderbook" +version = "0.1.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-multi-test", + "cw-storage-plus", + "cw-utils", + "cw2", + "osmosis-std", + "osmosis-std-derive 0.15.3", + "osmosis-test-tube", + "prost 0.11.9", + "rand", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.49", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tendermint" +version = "0.34.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15ab8f0a25d0d2ad49ac615da054d6a76aa6603ff95f7d18bafdd34450a1a04b" +dependencies = [ + "bytes", + "digest 0.10.7", + "ed25519", + "ed25519-consensus", + "flex-error", + "futures", + "k256", + "num-traits", + "once_cell", + "prost 0.12.3", + "prost-types 0.12.3", + "ripemd", + "serde", + "serde_bytes", + "serde_json", + "serde_repr", + "sha2 0.10.8", + "signature", + "subtle", + "subtle-encoding", + "tendermint-proto", + "time", + "zeroize", +] + +[[package]] +name = "tendermint-config" +version = "0.34.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1a02da769166e2052cd537b1a97c78017632c2d9e19266367b27e73910434fc" +dependencies = [ + "flex-error", + "serde", + "serde_json", + "tendermint", + "toml", + "url", +] + +[[package]] +name = "tendermint-proto" +version = "0.34.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b797dd3d2beaaee91d2f065e7bdf239dc8d80bba4a183a288bc1279dd5a69a1e" +dependencies = [ + "bytes", + "flex-error", + "num-derive", + "num-traits", + "prost 0.12.3", + "prost-types 0.12.3", + "serde", + "serde_bytes", + "subtle-encoding", + "time", +] + +[[package]] +name = "tendermint-rpc" +version = "0.34.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71afae8bb5f6b14ed48d4e1316a643b6c2c3cbad114f510be77b4ed20b7b3e42" +dependencies = [ + "async-trait", + "bytes", + "flex-error", + "futures", + "getrandom", + "peg", + "pin-project", + "rand", + "reqwest", + "semver", + "serde", + "serde_bytes", + "serde_json", + "subtle", + "subtle-encoding", + "tendermint", + "tendermint-config", + "tendermint-proto", + "thiserror", + "time", + "tokio", + "tracing", + "url", + "uuid", + "walkdir", +] + +[[package]] +name = "test-tube" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804bb9bda992b6cda6f883e7973cb999d4da90d21257fb918d6a693407148681" +dependencies = [ + "base64", + "cosmrs", + "cosmwasm-std", + "osmosis-std", + "prost 0.12.3", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "thiserror" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.49", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.49", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ - "bytes", - "prost-derive 0.11.9", + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.49", + "wasm-bindgen-shared", ] [[package]] -name = "prost" -version = "0.12.3" +name = "wasm-bindgen-futures" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ - "bytes", - "prost-derive 0.12.3", + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] -name = "prost-derive" -version = "0.11.9" +name = "wasm-bindgen-macro" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ - "anyhow", - "itertools 0.10.5", - "proc-macro2", "quote", - "syn 1.0.109", + "wasm-bindgen-macro-support", ] [[package]] -name = "prost-derive" -version = "0.12.3" +name = "wasm-bindgen-macro-support" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ - "anyhow", - "itertools 0.11.0", "proc-macro2", "quote", "syn 2.0.49", + "wasm-bindgen-backend", + "wasm-bindgen-shared", ] [[package]] -name = "prost-types" -version = "0.11.9" +name = "wasm-bindgen-shared" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ - "prost 0.11.9", + "js-sys", + "wasm-bindgen", ] [[package]] -name = "quote" -version = "1.0.35" +name = "which" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ - "proc-macro2", + "either", + "home", + "once_cell", + "rustix", ] [[package]] -name = "rand" -version = "0.8.5" +name = "winapi-util" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "libc", - "rand_chacha", - "rand_core 0.6.4", + "windows-sys 0.52.0", ] [[package]] -name = "rand_chacha" -version = "0.3.1" +name = "windows-sys" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", + "windows-targets 0.48.5", ] [[package]] -name = "rand_core" -version = "0.5.1" +name = "windows-sys" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", +] [[package]] -name = "rand_core" -version = "0.6.4" +name = "windows-targets" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "getrandom", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] -name = "rfc6979" -version = "0.4.0" +name = "windows-targets" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "hmac", - "subtle", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] -name = "ryu" -version = "1.0.16" +name = "windows_aarch64_gnullvm" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] -name = "schemars" -version = "0.8.16" +name = "windows_aarch64_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" -dependencies = [ - "dyn-clone", - "schemars_derive", - "serde", - "serde_json", -] +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] -name = "schemars_derive" -version = "0.8.16" +name = "windows_aarch64_msvc" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 1.0.109", -] +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] -name = "sec1" -version = "0.7.3" +name = "windows_aarch64_msvc" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", -] +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] -name = "semver" -version = "1.0.21" +name = "windows_i686_gnu" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] -name = "serde" -version = "1.0.196" +name = "windows_i686_gnu" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" -dependencies = [ - "serde_derive", -] +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] -name = "serde-cw-value" -version = "0.7.0" +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75d32da6b8ed758b7d850b6c3c08f1d7df51a4df3cb201296e63e34a78e99d4" -dependencies = [ - "serde", -] +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] -name = "serde-json-wasm" -version = "0.5.2" +name = "windows_i686_msvc" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e9213a07d53faa0b8dd81e767a54a8188a242fdb9be99ab75ec576a774bfdd7" -dependencies = [ - "serde", -] +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] -name = "serde_derive" -version = "1.0.196" +name = "windows_i686_msvc" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.49", -] +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] -name = "serde_derive_internals" -version = "0.26.0" +name = "windows_x86_64_gnu" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] -name = "serde_json" -version = "1.0.113" +name = "windows_x86_64_gnu" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" -dependencies = [ - "itoa", - "ryu", - "serde", -] +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] -name = "sha2" -version = "0.9.9" +name = "windows_x86_64_gnullvm" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] -name = "sha2" -version = "0.10.8" +name = "windows_x86_64_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.7", -] +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] -name = "signature" -version = "2.2.0" +name = "windows_x86_64_msvc" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest 0.10.7", - "rand_core 0.6.4", -] +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] -name = "spki" -version = "0.7.3" +name = "windows_x86_64_msvc" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "base64ct", - "der", + "cfg-if", + "windows-sys 0.48.0", ] [[package]] -name = "static_assertions" -version = "1.1.0" +name = "write16" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" [[package]] -name = "subtle" -version = "2.5.0" +name = "writeable" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] -name = "sumtree-orderbook" -version = "0.1.0" +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-multi-test", - "cw-storage-plus", - "cw-utils", - "cw2", - "osmosis-std", - "osmosis-std-derive 0.15.3", - "prost 0.11.9", - "rand", - "schemars", "serde", - "thiserror", + "stable_deref_trait", + "yoke-derive", + "zerofrom", ] [[package]] -name = "syn" -version = "1.0.109" +name = "yoke-derive" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" dependencies = [ "proc-macro2", "quote", - "unicode-ident", + "syn 2.0.49", + "synstructure", ] [[package]] -name = "syn" -version = "2.0.49" +name = "zerofrom" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" dependencies = [ "proc-macro2", "quote", - "unicode-ident", + "syn 2.0.49", + "synstructure", ] [[package]] -name = "thiserror" -version = "1.0.57" +name = "zeroize" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" dependencies = [ - "thiserror-impl", + "zeroize_derive", ] [[package]] -name = "thiserror-impl" -version = "1.0.57" +name = "zeroize_derive" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", @@ -932,31 +2819,23 @@ dependencies = [ ] [[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +name = "zerovec" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] [[package]] -name = "zeroize" -version = "1.7.0" +name = "zerovec-derive" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.49", +] diff --git a/contracts/sumtree-orderbook/Cargo.toml b/contracts/sumtree-orderbook/Cargo.toml index 9eed4dc..587ea8f 100644 --- a/contracts/sumtree-orderbook/Cargo.toml +++ b/contracts/sumtree-orderbook/Cargo.toml @@ -31,6 +31,7 @@ rpath = false backtraces = ["cosmwasm-std/backtraces"] # use library feature to disable all instantiate/execute/query exports library = [] +skip-integration-tests = [] [package.metadata.scripts] optimize = """docker run --rm -v "$(pwd)":/code \ @@ -52,7 +53,7 @@ schemars = "0.8.15" serde = { version = "1.0.189", default-features = false, features = ["derive"] } thiserror = { version = "1.0.49" } osmosis-std-derive = "0.15.3" -osmosis-std = "0.16.0" +osmosis-std = "0.25.0" prost = { version = "0.11.2", default-features = false, features = [ "prost-derive", ] } @@ -60,4 +61,5 @@ prost = { version = "0.11.2", default-features = false, features = [ [dev-dependencies] cw-multi-test = "0.18.0" +osmosis-test-tube = { version = "25.0.0", features = ["wasm-sudo"] } rand = "0.8.4" diff --git a/contracts/sumtree-orderbook/src/contract.rs b/contracts/sumtree-orderbook/src/contract.rs index 6cf4dc8..aec8330 100644 --- a/contracts/sumtree-orderbook/src/contract.rs +++ b/contracts/sumtree-orderbook/src/contract.rs @@ -143,6 +143,7 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> ContractResult { } => Ok(to_json_binary(&query::orders_by_owner( deps, owner, start_from, end_at, limit, )?)?), + QueryMsg::OrderbookState {} => Ok(to_json_binary(&query::orderbook_state(deps)?)?), QueryMsg::TicksById { tick_ids } => { Ok(to_json_binary(&query::ticks_by_id(deps, tick_ids)?)?) } diff --git a/contracts/sumtree-orderbook/src/msg.rs b/contracts/sumtree-orderbook/src/msg.rs index 42771ca..058fdff 100644 --- a/contracts/sumtree-orderbook/src/msg.rs +++ b/contracts/sumtree-orderbook/src/msg.rs @@ -109,7 +109,7 @@ pub enum QueryMsg { #[returns(bool)] IsActive {}, - #[returns(Vec)] + #[returns(OrdersResponse)] OrdersByOwner { // The address of the order maker owner: Addr, @@ -129,6 +129,9 @@ pub enum QueryMsg { limit: Option, }, + #[returns(crate::types::Orderbook)] + OrderbookState {}, + #[returns(DenomsResponse)] Denoms {}, diff --git a/contracts/sumtree-orderbook/src/order.rs b/contracts/sumtree-orderbook/src/order.rs index 8f616de..c97ec62 100644 --- a/contracts/sumtree-orderbook/src/order.rs +++ b/contracts/sumtree-orderbook/src/order.rs @@ -106,7 +106,6 @@ pub fn place_limit( .with_placed_at(env.block.time); let quant_dec256 = Decimal256::from_ratio(limit_order.quantity.u128(), Uint256::one()); - // Save the order to the orderbook orders().save(deps.storage, &(tick_id, order_id), &limit_order)?; @@ -166,7 +165,6 @@ pub fn cancel_limit( .effective_total_amount_swapped, )?; - // Ensure the order has not been filled. let tick_state = TICK_STATE.load(deps.storage, tick_id).unwrap_or_default(); let tick_values = tick_state.get_values(order.order_direction); ensure!( @@ -213,10 +211,10 @@ pub fn cancel_limit( ); orders().remove(deps.storage, &(order.tick_id, order.order_id))?; - curr_tick_values.total_amount_of_liquidity = curr_tick_values .total_amount_of_liquidity .checked_sub(Decimal256::from_ratio(order.quantity, Uint256::one()))?; + curr_tick_state.set_values(order.order_direction, curr_tick_values); TICK_STATE.save(deps.storage, order.tick_id, &curr_tick_state)?; subtract_directional_liquidity(deps.storage, order.order_direction, quant_dec256)?; @@ -503,6 +501,11 @@ pub(crate) fn run_market_order_internal( let current_tick_id = maybe_current_tick?; let mut current_tick = TICK_STATE.load(storage, current_tick_id)?; let mut current_tick_values = current_tick.get_values(order.order_direction.opposite()); + + if current_tick_values.total_amount_of_liquidity.is_zero() { + continue; + } + let tick_price = tick_to_price(current_tick_id)?; last_tick_price = tick_price; @@ -634,6 +637,7 @@ pub(crate) fn claim_order( // Sync the tick the order is on to ensure correct ETAS let bid_tick_values = tick_state.get_values(OrderDirection::Bid); let ask_tick_values = tick_state.get_values(OrderDirection::Ask); + sync_tick( storage, tick_id, diff --git a/contracts/sumtree-orderbook/src/query.rs b/contracts/sumtree-orderbook/src/query.rs index b0092c9..6e951a4 100644 --- a/contracts/sumtree-orderbook/src/query.rs +++ b/contracts/sumtree-orderbook/src/query.rs @@ -18,7 +18,7 @@ use crate::{ sudo::ensure_swap_fee, sumtree::tree::{get_prefix_sum, get_root_node}, tick_math::tick_to_price, - types::{FilterOwnerOrders, LimitOrder, MarketOrder, OrderDirection, TickState}, + types::{FilterOwnerOrders, MarketOrder, OrderDirection, Orderbook, TickState}, ContractError, }; @@ -60,8 +60,8 @@ pub(crate) fn spot_price( let price = tick_to_price(next_tick)?; let spot_price = match direction { - OrderDirection::Ask => price.inv().unwrap(), - OrderDirection::Bid => price, + OrderDirection::Ask => price, + OrderDirection::Bid => price.inv().unwrap(), }; Ok(SpotPriceResponse { @@ -205,7 +205,13 @@ pub(crate) fn orders_by_owner( start_from: Option<(i64, u64)>, end_at: Option<(i64, u64)>, limit: Option, -) -> ContractResult> { +) -> ContractResult { + let count = orders() + .idx + .owner + .prefix(owner.clone()) + .keys(deps.storage, None, None, Order::Ascending) + .count(); let orders = get_orders_by_owner( deps.storage, FilterOwnerOrders::all(owner), @@ -213,7 +219,11 @@ pub(crate) fn orders_by_owner( end_at, limit, )?; - Ok(orders) + + Ok(OrdersResponse { + count: count as u64, + orders, + }) } pub(crate) fn denoms(deps: Deps) -> ContractResult { @@ -224,6 +234,11 @@ pub(crate) fn denoms(deps: Deps) -> ContractResult { }) } +pub(crate) fn orderbook_state(deps: Deps) -> ContractResult { + let orderbook = ORDERBOOK.load(deps.storage)?; + Ok(orderbook) +} + pub(crate) fn ticks_by_id(deps: Deps, tick_ids: Vec) -> ContractResult { let mut ticks: Vec = vec![]; for tick_id in tick_ids { diff --git a/contracts/sumtree-orderbook/src/state.rs b/contracts/sumtree-orderbook/src/state.rs index c894841..7cac036 100644 --- a/contracts/sumtree-orderbook/src/state.rs +++ b/contracts/sumtree-orderbook/src/state.rs @@ -83,7 +83,7 @@ pub fn get_orders_by_owner( page_size: Option, ) -> StdResult> { let page_size = page_size.unwrap_or(DEFAULT_PAGE_SIZE) as usize; - let min = min.map(Bound::exclusive); + let min = min.map(Bound::inclusive); let max = max.map(Bound::inclusive); // Define the prefix iterator based on the filter diff --git a/contracts/sumtree-orderbook/src/sumtree/tree.rs b/contracts/sumtree-orderbook/src/sumtree/tree.rs index 03c7704..7be0311 100644 --- a/contracts/sumtree-orderbook/src/sumtree/tree.rs +++ b/contracts/sumtree-orderbook/src/sumtree/tree.rs @@ -64,6 +64,7 @@ pub fn get_prefix_sum( // Since the longest path this function can walk is from the root to a leaf, it runs in O(log(N)) time. Given // how it is able to terminate early using our sumtree's range properties, in many cases it will likely run // in much less. + fn prefix_sum_walk( storage: &dyn Storage, node: &TreeNode, diff --git a/contracts/sumtree-orderbook/src/tests/e2e/cases/mod.rs b/contracts/sumtree-orderbook/src/tests/e2e/cases/mod.rs new file mode 100644 index 0000000..0eb7047 --- /dev/null +++ b/contracts/sumtree-orderbook/src/tests/e2e/cases/mod.rs @@ -0,0 +1,2 @@ +mod test_fuzz; +mod test_orders; diff --git a/contracts/sumtree-orderbook/src/tests/e2e/cases/test_fuzz.rs b/contracts/sumtree-orderbook/src/tests/e2e/cases/test_fuzz.rs new file mode 100644 index 0000000..8b6599d --- /dev/null +++ b/contracts/sumtree-orderbook/src/tests/e2e/cases/test_fuzz.rs @@ -0,0 +1,716 @@ +use std::collections::HashMap; +use std::time::{Duration, SystemTime}; + +use cosmwasm_std::{Coin, Decimal256, Uint128}; +use osmosis_test_tube::{Account, Module, OsmosisTestApp}; +use rand::rngs::StdRng; +use rand::seq::SliceRandom; +use rand::{Rng, SeedableRng}; + +use super::super::utils::*; +use crate::ContractError; +use crate::{ + constants::MIN_TICK, + msg::{DenomsResponse, GetTotalPoolLiquidityResponse, QueryMsg}, + setup, + tests::e2e::modules::cosmwasm_pool::CosmwasmPool, + tests::e2e::test_env::TestEnv, + types::OrderDirection, +}; + +// Tick Price = 100000 +pub(crate) const LARGE_POSITIVE_TICK: i64 = 4500000; +// Tick Price = 0.00001 +pub(crate) const LARGE_NEGATIVE_TICK: i64 = -4500000; + +// Loops over a provided action for the provided duration +// Tracks the number of operations and iterations +// Duration is in seconds +fn run_for_duration( + duration: u64, + count_per_iteration: u64, + action: impl FnOnce(u64) + std::marker::Copy, +) { + let duration = Duration::from_secs(duration); + let now = SystemTime::now(); + let end = now.checked_add(duration).unwrap(); + + let mut oper_count = 0; + let mut iterations = 0; + while SystemTime::now().le(&end) { + action(count_per_iteration); + + oper_count += count_per_iteration; + iterations += 1; + } + println!("operations: {}", oper_count); + println!("iterations: {}", iterations); +} + +#[test] +fn test_order_fuzz_linear_large_orders_small_range() { + let oper_per_iteration = 1000; + run_for_duration(30, oper_per_iteration, |count| { + run_fuzz_linear(count, (-10, 10), 0.2); + }); +} + +#[test] +fn test_order_fuzz_linear_small_orders_large_range() { + let oper_per_iteration = 1000; + run_for_duration(30, oper_per_iteration, |count| { + run_fuzz_linear(count, (LARGE_NEGATIVE_TICK, LARGE_POSITIVE_TICK), 0.2); + }); +} + +// This test takes a VERY long time to run +// #[test] +// fn test_order_fuzz_linear_very_large_orders_large_range() { +// run_fuzz_linear(5000, (LARGE_NEGATIVE_TICK, LARGE_POSITIVE_TICK), 0.2); +// } + +#[test] +fn test_order_fuzz_linear_small_orders_small_range() { + let oper_per_iteration = 100; + run_for_duration(30, oper_per_iteration, |count| { + run_fuzz_linear(count, (-10, 0), 0.1); + }); +} + +#[test] +fn test_order_fuzz_linear_large_cancelled_orders_small_range() { + let oper_per_iteration = 1000; + run_for_duration(30, oper_per_iteration, |count| { + run_fuzz_linear(count, (MIN_TICK, MIN_TICK + 20), 0.8); + }); +} + +#[test] +fn test_order_fuzz_linear_small_cancelled_orders_large_range() { + let oper_per_iteration = 100; + run_for_duration(30, oper_per_iteration, |count| { + run_fuzz_linear(count, (LARGE_NEGATIVE_TICK, LARGE_POSITIVE_TICK), 0.8); + }); +} + +#[test] +fn test_order_fuzz_linear_large_all_cancelled_orders_small_range() { + let oper_per_iteration = 1000; + run_for_duration(30, oper_per_iteration, |count| { + run_fuzz_linear(count, (-10, 10), 1.0); + }); +} + +#[test] +fn test_order_fuzz_linear_single_tick() { + let oper_per_iteration = 1000; + run_for_duration(30, oper_per_iteration, |count| { + run_fuzz_linear(count, (0, 0), 0.2); + }); +} + +#[test] +fn test_order_fuzz_mixed() { + let oper_per_iteration = 1000; + run_for_duration(30, oper_per_iteration, |count| { + run_fuzz_mixed(count, (-20, 20)); + }); +} + +#[test] +fn test_order_fuzz_mixed_single_tick() { + let oper_per_iteration = 1000; + + run_for_duration(30, oper_per_iteration, |count| { + run_fuzz_mixed(count, (0, 0)); + }); +} + +#[test] +fn test_order_fuzz_mixed_large_negative_tick_range() { + let oper_per_iteration = 1000; + + run_for_duration(30, oper_per_iteration, |count| { + run_fuzz_mixed(count, (LARGE_NEGATIVE_TICK, LARGE_NEGATIVE_TICK + 10)); + }); +} + +#[test] +fn test_order_fuzz_mixed_large_positive_tick_range() { + let oper_per_iteration = 1000; + + run_for_duration(30, oper_per_iteration, |count| { + run_fuzz_mixed(count, (LARGE_POSITIVE_TICK - 10, LARGE_POSITIVE_TICK)); + }); +} + +#[test] +fn test_order_fuzz_mixed_min_tick() { + let oper_per_iteration = 1000; + + run_for_duration(30, oper_per_iteration, |count| { + run_fuzz_mixed(count, (MIN_TICK, MIN_TICK + 10)); + }); +} + +#[test] +fn test_order_fuzz_large_tick_range() { + let oper_per_iteration = 1000; + + run_for_duration(30, oper_per_iteration, |count| { + run_fuzz_mixed(count, (MIN_TICK, LARGE_POSITIVE_TICK)); + }); +} + +/// Runs a linear fuzz test with the following steps +/// 1. Place x amount of random limit orders in given tick range and cancel with provided probability +/// 2. For both directions fill the entire amount of liquidity available using market orders +/// 3. Claim orders in random order +/// 4. Assert that the orders were filled correctly +fn run_fuzz_linear(amount_limit_orders: u64, tick_range: (i64, i64), cancel_probability: f64) { + // -- Test Setup -- + let seed: u64 = 123456789; + let mut rng = StdRng::seed_from_u64(seed); + + let app = OsmosisTestApp::new(); + let cp = CosmwasmPool::new(&app); + let mut t = setup!(&app, "quote", "base", 1); + + let mut orders: HashMap = HashMap::new(); + + // -- System Under Test -- + + // -- Step 1: Place Limits -- + + // Places the set amount of orders within the provided tick range + // Orders will be cancelled with a chance equal to the provided cancel_probability + // Tick state is verified after every order is placed (and cancelled) + for i in 0..amount_limit_orders { + let username = format!("user{}", i); + let chosen_tick = place_random_limit(&mut t, &mut rng, &username, tick_range); + let is_cancelled = rng.gen_bool(cancel_probability); + + if is_cancelled { + orders::cancel_limit_and_assert_balance(&t, &username, chosen_tick, i).unwrap(); + } else { + orders.insert(i, (username, chosen_tick)); + } + + assert::tick_invariants(&t); + } + + // -- Step 2: Place Market Orders & Fill Liquidity -- + + // For both directions fill the entire amount of liquidity available using market orders + // For certain cases it is not possible to fill the entire liquidity so a remainder of 1 may occur + for order_direction in [OrderDirection::Bid, OrderDirection::Ask] { + // Determine the amount of liquidity for the given direction + let mut liquidity = t.contract.get_directional_liquidity(order_direction); + + // A counter to track the number of zero amount returns + let mut zero_amount_returns = 0; + // A counter to track the current user ID + let mut user_id = 0; + + // Record starting expected output + // Provide max as previous to ensure we start with a valid expected output + let mut previous_expected_out = + assert::decrementing_market_order_output(&t, u128::MAX, 10000000u128, order_direction); + + // While there is some fillable liquidity we want to place randomised market orders + while liquidity > 1u128 { + let username = format!("user{}{}", order_direction, user_id); + let placed_amount = + place_random_market(&cp, &mut t, &mut rng, &username, order_direction); + + if placed_amount == 0 { + // In the case that the last order cannot be filled we want an exit condition + // If there are 100 consecutive zero amount returns we will break + zero_amount_returns += 1; + if zero_amount_returns == 100 { + break; + } + continue; + } + + // Increment the username of the order placer + user_id += 1; + + // Reset counter as order was placed + zero_amount_returns = 0; + + // Update the liquidity + liquidity = t.contract.get_directional_liquidity(order_direction); + assert::tick_invariants(&t); + assert::has_liquidity(&t); + + previous_expected_out = assert::decrementing_market_order_output( + &t, + previous_expected_out.u128(), + 100u128, + order_direction, + ); + } + println!( + "Placed {} market orders in {} direction", + user_id, order_direction + ); + } + + let GetTotalPoolLiquidityResponse { + total_pool_liquidity, + } = t + .contract + .query(&QueryMsg::GetTotalPoolLiquidity {}) + .unwrap(); + println!("Total remaining pool liquidity: {:?}", total_pool_liquidity); + + // -- Step 3: Claim & Cancel Orders -- + + // Attempt to claim & cancel all limit orders + let (bid_unclaimable_amount, ask_unclaimable_amount) = + clear_remaining_orders(&mut t, &mut rng, &orders); + // At most one order should remain in each direction + assert!( + bid_unclaimable_amount <= 1, + "bid_unclaimable_amount is greater than 1" + ); + assert!( + ask_unclaimable_amount <= 1, + "ask_unclaimable_amount is greater than 1" + ); + + // -- Post Test Assertions -- + assert::clean_ticks(&t); + assert::no_remaining_orders(&t); +} + +#[derive(Debug, Eq, PartialEq, Hash)] +enum MixedFuzzOperation { + PlaceLimit, + PlaceMarket, + CancelLimit, + Claim, +} + +impl MixedFuzzOperation { + /// Chooses a random fuzz operation + fn random(rng: &mut StdRng) -> Self { + let index: u32 = rng.gen_range(0..=100); + + if index < 25 { + Self::PlaceLimit + } else if index < 50 { + Self::PlaceMarket + } else if index < 75 { + Self::CancelLimit + } else { + Self::Claim + } + } + + /// Attempts to run the chosen fuzz operation + /// + /// Returns true if the operation was successful, false otherwise + #[allow(clippy::too_many_arguments)] + fn run( + &self, + t: &mut TestEnv, + cp: &CosmwasmPool, + rng: &mut StdRng, + iteration: u64, + orders: &mut HashMap, + order_count: &mut u64, + tick_bounds: (i64, i64), + ) -> Result { + let username = format!("user{}", iteration); + match self { + MixedFuzzOperation::PlaceLimit => { + // Place the limit order + let tick_id = place_random_limit(t, rng, &username, tick_bounds); + // Record the order for claims/cancels + orders.insert(*order_count, (username, tick_id)); + *order_count += 1; + Ok(true) + } + MixedFuzzOperation::PlaceMarket => { + // Determine the market direction + let maybe_market_direction = get_random_market_direction(t, rng); + // May error if the orderbook has 0 liquidity for both directions + if maybe_market_direction.is_err() { + return Ok(false); + } + let market_direction = maybe_market_direction.unwrap(); + // Determine the maximum amount of the opposite direction that can be bought + let max_amount = t + .contract + .get_directional_liquidity(market_direction.opposite()); + // If nothing can be bought then we skip this operation + if max_amount == 0 { + return Ok(false); + } + + // Place the order + let amount = place_random_market(cp, t, rng, &username, market_direction); + + Ok(amount != 0) + } + MixedFuzzOperation::CancelLimit => { + // If there are no active orders skip the operation + if orders.is_empty() { + return Ok(false); + } + + // Determine the order to be cancelled + let order_ids = orders.keys().collect::>(); + let order_idx = rng.gen_range(0..order_ids.len()); + let order_id = *order_ids[order_idx]; + let (username, tick_id) = orders.get(&order_id).unwrap().clone(); + + // We cannot cancel an order if it is partially filled + let order = t + .contract + .get_order(t.accounts[&username].address(), tick_id, order_id) + .unwrap(); + let amount_claimable = t.contract.get_order_claimable_amount(order.clone()); + + // Determine if the order can be cancelled + if amount_claimable > 0 { + let res = orders::cancel_limit(t, &username, tick_id, order_id).unwrap_err(); + assert::contract_err(ContractError::CancelFilledOrder, res); + return Ok(false); + } + + // Cancel the order + orders::cancel_limit_and_assert_balance(t, &username, tick_id, order_id).unwrap(); + // Remove the order once we know it is cancellable + orders.remove(&order_id).unwrap(); + Ok(true) + } + MixedFuzzOperation::Claim => { + // If there are no active orders skip the operation + if orders.is_empty() { + return Ok(false); + } + + // Determine the order to be claimed + let order_ids = orders.keys().collect::>(); + let order_idx = rng.gen_range(0..order_ids.len()); + let order_id = *order_ids[order_idx]; + let (username, tick_id) = orders.get(&order_id).unwrap().clone(); + + // We cannot claim an order if it has nothing to be claimed + let order = t + .contract + .get_order(t.accounts[&username].address(), tick_id, order_id) + .unwrap(); + let amount_claimable = t.contract.get_order_claimable_amount(order.clone()); + + // Determine if the order can be claimed + if amount_claimable == 0 { + let res = orders::claim(t, &username, tick_id, order_id).unwrap_err(); + assert::contract_err(ContractError::ZeroClaim, res); + return Ok(false); + } + + let claimant = if order.claim_bounty.is_some() { + t.add_account("claimant", vec![Coin::new(1000000000000u128, "uosmo")]); + "claimant" + } else { + username.as_str() + }; + + // Claim the order + orders::claim_and_assert_balance(t, claimant, &username, tick_id, order_id) + .unwrap(); + let order = + t.contract + .get_order(t.accounts[&username].address(), tick_id, order_id); + if order.is_none() { + // Remove the order once we know its claimable + orders.remove(&order_id).unwrap(); + } + Ok(true) + } + } + } +} + +/// Runs a fuzz test that randomly chooses between 4 operations: +/// 1. Place a Limit +/// 2. Place a Market +/// 3. Cancel a Limit +/// 4. Claim a Limit +/// +/// These operations are chosen at random and if they are an invalid operation they are skipped and a new operation is chosen. +/// Orders are placing in a tick range determined by the current tick bounds with the intent that ticks spread over time randomly to the desired tick bounds. +/// Expected errors are handled by skipping the operation and randomly choosing a new operation. Any errors returned are expected to be because of an issue in the orderbook. +fn run_fuzz_mixed(amount_of_orders: u64, tick_bounds: (i64, i64)) { + // -- Test Setup -- + let seed: u64 = 123456789; + let mut rng = StdRng::seed_from_u64(seed); + + let app = OsmosisTestApp::new(); + let cp = CosmwasmPool::new(&app); + let mut t = setup!(&app, "quote", "base", 1); + + // A record of the orders placed to allow for simpler management of cancellations and claims + let mut orders: HashMap = HashMap::new(); + // A count of the orders placed to track the current order ID + let mut order_count = 0; + + // Record how many times each operation is chosen for assertion post test + let mut oper_count: HashMap = HashMap::new(); + oper_count.insert(MixedFuzzOperation::PlaceLimit, 0); + oper_count.insert(MixedFuzzOperation::PlaceMarket, 0); + oper_count.insert(MixedFuzzOperation::CancelLimit, 0); + oper_count.insert(MixedFuzzOperation::Claim, 0); + + // -- System Under Test -- + for i in 0..amount_of_orders { + // Chooses an operation at random + let mut operation = MixedFuzzOperation::random(&mut rng); + + // We add an escape clause in the case that the test ever gets caught in an infinite loop + let mut repeated_failures = 0; + + // Repeat randomising operations until a successful one is chosen + while !operation + .run( + &mut t, + &cp, + &mut rng, + i, + &mut orders, + &mut order_count, + tick_bounds, + ) + .unwrap() + { + operation = MixedFuzzOperation::random(&mut rng); + repeated_failures += 1; + if repeated_failures > 100 { + panic!("Caught in loop"); + } + } + oper_count.entry(operation).and_modify(|c| *c += 1); + + // -- Post operation assertions -- + assert::tick_invariants(&t); + assert::has_liquidity(&t); + } + + // Attempt to claim/cancel all remaining orders + clear_remaining_orders(&mut t, &mut rng, &orders); + + // -- Post test assertions -- + + // Assert every operation ran at least once successfully + assert!( + oper_count.values().all(|c| *c > 0), + "not all operations were used" + ); + assert::no_remaining_orders(&t); + assert::clean_ticks(&t); +} + +/// Places a random limit order in the provided tick range using the provided username +fn place_random_limit( + t: &mut TestEnv, + rng: &mut StdRng, + username: &str, + tick_range: (i64, i64), +) -> i64 { + // Quantities are in magnitudes of u32 + let quantity = Uint128::from(rng.gen::()); + // 50% chance to choose either direction + let order_direction = if rng.gen_bool(0.5) { + OrderDirection::Bid + } else { + OrderDirection::Ask + }; + + // Get the appropriate denom for the chosen direction + let DenomsResponse { + base_denom, + quote_denom, + } = t.contract.get_denoms(); + let expected_denom = if order_direction == OrderDirection::Bid { + "e_denom + } else { + &base_denom + }; + // Select a random tick from the provided range + let tick_id = rng.gen_range(tick_range.0..=tick_range.1); + + // Add the user account with the appropriate amount of the denom + t.add_account( + username, + vec![ + Coin::new(quantity.u128(), expected_denom), + Coin::new(1000000000000000u128, "uosmo"), + ], + ); + + // Give orders an 80% chance of having a randomised bounty (may be 0) + let has_claim_bounty = rng.gen_bool(0.8); + let claim_bounty = if has_claim_bounty { + Some(Decimal256::percent(rng.gen_range(0..=1))) + } else { + None + }; + + // Place the generated limit + orders::place_limit( + t, + tick_id, + order_direction, + quantity, + claim_bounty, + username, + ) + .unwrap(); + + // Return the tick id to record the order + tick_id +} + +/// Places a random market order in the provided tick range using the provided username with at most max value +fn place_random_market( + cp: &CosmwasmPool, + t: &mut TestEnv, + rng: &mut StdRng, + username: &str, + order_direction: OrderDirection, +) -> u128 { + // Get the appropriate denom for the chosen direction + let token_in_denom = if order_direction == OrderDirection::Bid { + "quote" + } else { + "base" + }; + + // Select a random amount of the token in to swap + let max_amount = t.contract.get_max_market_amount(order_direction); + let amount = rng.gen_range(0..=max_amount); + + if amount == 0 { + return 0; + } + + // Calculate the expected amount of token out + let expected_out = t.contract.get_out_given_in(order_direction, amount); + + if let Ok(expected_out) = expected_out { + if expected_out.amount == "0" { + return 0; + } + } else if expected_out.is_err() { + return 0; + } + + // Generate the user account + t.add_account( + username, + vec![ + Coin::new(amount, token_in_denom), + Coin::new(1000000000000000u128, "uosmo"), + ], + ); + + // Places the market order and ensures that funds are transferred correctly + orders::place_market_and_assert_balance(cp, t, order_direction, amount, username).unwrap(); + + // We return the amount placed for recording + amount +} + +/// Determines a random market direction based on the available liquidity for bids and asks. +/// Errors if both directions have no liquidity +/// +/// Chooses a direction if that direction is the only one with liquidity. +fn get_random_market_direction<'a>( + t: &TestEnv, + rng: &mut StdRng, +) -> Result { + let bid_liquidity = t.contract.get_directional_liquidity(OrderDirection::Bid); + let ask_liquidity = t.contract.get_directional_liquidity(OrderDirection::Ask); + if bid_liquidity == 0 && ask_liquidity == 0 { + return Err("No liquidity available to place market order"); + } + + let bid_probability = if bid_liquidity != 0 && ask_liquidity == 0 { + 1.0 + } else if bid_liquidity != 0 { + 0.5 + } else { + 0.0 + }; + + if rng.gen_bool(bid_probability) { + Ok(OrderDirection::Bid) + } else { + Ok(OrderDirection::Ask) + } +} + +/// Attempts to clear the remaining orders by first attempting to claim the order. +/// If the order is claimed but not removed (not fully filled) it is then cancelled. +/// +/// If neither/both succeed and the order is still in state then something is wrong. +fn clear_remaining_orders( + t: &mut TestEnv, + rng: &mut StdRng, + orders: &HashMap, +) -> (u64, u64) { + // Shuffle the order of recorded orders (as liquidity is fully filled (except the possibility of a 1 remainder)) + // every order should be claimable and the order should not matter + let mut orders_vec = orders.iter().collect::>(); + orders_vec.shuffle(rng); + + // We track how many orders were not fully claimable for future assertions + let mut bid_unclaimable_amount = 0; + let mut ask_unclaimable_amount = 0; + + for (order_id, (username, tick_id)) in orders_vec.iter().cloned() { + let maybe_order = t + .contract + .get_order(t.accounts[username].address(), *tick_id, *order_id); + + if maybe_order.is_none() { + continue; + } + + let _ = orders::claim_and_assert_balance(t, username, username, *tick_id, *order_id); + + // Order may be cleared by fully claiming, in which case we want to continue to the next order + if t.contract + .get_order(t.accounts[username.as_str()].address(), *tick_id, *order_id) + .is_none() + { + continue; + } + + let order = maybe_order.unwrap(); + match order.order_direction { + OrderDirection::Bid => bid_unclaimable_amount += 1, + OrderDirection::Ask => ask_unclaimable_amount += 1, + } + + // If cancelling is a success we can continue to the next order + if orders::cancel_limit_and_assert_balance(t, username, *tick_id, *order_id).is_ok() { + continue; + } + + // If an order cannot be claimed or cancelled something has gone wrong + let order = t + .contract + .get_order(t.accounts[username].address(), *tick_id, *order_id); + assert!( + order.is_none(), + "order was not cleaned from state: {order:?}" + ); + } + + (bid_unclaimable_amount, ask_unclaimable_amount) +} diff --git a/contracts/sumtree-orderbook/src/tests/e2e/cases/test_orders.rs b/contracts/sumtree-orderbook/src/tests/e2e/cases/test_orders.rs new file mode 100644 index 0000000..74d8a0d --- /dev/null +++ b/contracts/sumtree-orderbook/src/tests/e2e/cases/test_orders.rs @@ -0,0 +1,273 @@ +use cosmwasm_std::{coin, Decimal256, Uint128}; +use osmosis_test_tube::{Module, OsmosisTestApp}; + +use super::super::utils::*; +use crate::{ + constants::{MAX_TICK, MIN_TICK}, + setup, + tests::e2e::modules::cosmwasm_pool::CosmwasmPool, + types::OrderDirection, +}; + +struct BasicFulfilledOrderTestCase { + name: &'static str, + placed_amount: u128, + filled_amount: u128, + tick_id: i64, + claim_bounty: Option, + order_direction: OrderDirection, + claimer: &'static str, +} + +#[test] +fn test_basic_order() { + let cases = vec![ + BasicFulfilledOrderTestCase { + name: "basic fulfilled ask", + placed_amount: 10u128, + filled_amount: 10u128, + tick_id: 0, + claim_bounty: None, + order_direction: OrderDirection::Ask, + claimer: "user1", + }, + BasicFulfilledOrderTestCase { + name: "basic fulfilled bid", + placed_amount: 10u128, + filled_amount: 10u128, + tick_id: 0, + claim_bounty: None, + order_direction: OrderDirection::Bid, + claimer: "user1", + }, + BasicFulfilledOrderTestCase { + name: "basic partially filled ask", + placed_amount: 10u128, + filled_amount: 5u128, + tick_id: 0, + claim_bounty: None, + order_direction: OrderDirection::Ask, + claimer: "user1", + }, + BasicFulfilledOrderTestCase { + name: "basic partially filled bid", + placed_amount: 10u128, + filled_amount: 5u128, + tick_id: 0, + claim_bounty: None, + order_direction: OrderDirection::Bid, + claimer: "user1", + }, + BasicFulfilledOrderTestCase { + name: "basic fulfilled ask with bounty", + placed_amount: 100u128, + filled_amount: 100u128, + tick_id: 0, + claim_bounty: Some(Decimal256::percent(1)), + order_direction: OrderDirection::Ask, + claimer: "user1", + }, + BasicFulfilledOrderTestCase { + name: "basic fulfilled ask with bounty with external claimant", + placed_amount: 100u128, + filled_amount: 100u128, + tick_id: 0, + claim_bounty: Some(Decimal256::percent(1)), + order_direction: OrderDirection::Ask, + claimer: "user2", + }, + ]; + for case in cases { + let app = OsmosisTestApp::new(); + let cp = CosmwasmPool::new(&app); + let t = setup!(&app, "quote", "base", 0); + let (expected_bid_tick, expected_ask_tick) = if case.order_direction == OrderDirection::Ask + { + (MIN_TICK, case.tick_id) + } else { + (case.tick_id, MAX_TICK) + }; + + // Place limit + orders::place_limit( + &t, + case.tick_id, + case.order_direction, + case.placed_amount, + case.claim_bounty, + "user1", + ) + .unwrap(); + match case.order_direction { + OrderDirection::Ask => { + assert::pool_liquidity(&t, case.placed_amount, 0u8, case.name); + assert::pool_balance(&t, case.placed_amount, 0u8, case.name); + } + OrderDirection::Bid => { + assert::pool_liquidity(&t, 0u8, case.placed_amount, case.name); + assert::pool_balance(&t, 0u8, case.placed_amount, case.name); + } + } + assert::spot_price(&t, expected_bid_tick, expected_ask_tick, case.name); + + // Fill limit order + orders::place_market_and_assert_balance( + &cp, + &t, + case.order_direction.opposite(), + case.filled_amount, + "user2", + ) + .unwrap(); + match case.order_direction { + OrderDirection::Ask => { + assert::pool_liquidity(&t, case.placed_amount - case.filled_amount, 0u8, case.name); + assert::pool_balance( + &t, + case.placed_amount - case.filled_amount, + case.filled_amount, + case.name, + ); + } + OrderDirection::Bid => { + assert::pool_liquidity(&t, 0u8, case.placed_amount - case.filled_amount, case.name); + assert::pool_balance( + &t, + case.filled_amount, + case.placed_amount - case.filled_amount, + case.name, + ); + } + } + assert::spot_price(&t, expected_bid_tick, expected_ask_tick, case.name); + + // Claim limit + orders::claim_and_assert_balance(&t, case.claimer, "user1", 0, 0).unwrap(); + match case.order_direction { + OrderDirection::Ask => { + assert::pool_liquidity(&t, case.placed_amount - case.filled_amount, 0u8, case.name); + assert::pool_balance(&t, case.placed_amount - case.filled_amount, 0u8, case.name); + } + OrderDirection::Bid => { + assert::pool_liquidity(&t, 0u8, case.placed_amount - case.filled_amount, case.name); + assert::pool_balance(&t, 0u8, case.placed_amount - case.filled_amount, case.name); + } + } + assert::spot_price(&t, expected_bid_tick, expected_ask_tick, case.name); + } +} + +#[test] +fn test_cancelled_orders() { + let app = OsmosisTestApp::new(); + let cp = CosmwasmPool::new(&app); + let t = setup!(&app, "quote", "base", 0); + let amount_orders = 3; + + for i in 0..amount_orders { + orders::place_limit( + &t, + 0, + OrderDirection::Ask, + Uint128::from(100u128), + None, + "user1", + ) + .unwrap(); + orders::cancel_limit_and_assert_balance(&t, "user1", 0, i).unwrap(); + } + assert::pool_liquidity(&t, 0u8, 0u8, "cancelled orders"); + assert::pool_balance(&t, 0u8, 0u8, "cancelled orders"); + assert::tick_invariants(&t); + + orders::place_limit( + &t, + 0, + OrderDirection::Ask, + Uint128::from(100u128), + None, + "user1", + ) + .unwrap(); + assert::pool_liquidity(&t, 100u8, 0u8, "cancelled orders"); + assert::pool_balance(&t, 100u8, 0u8, "cancelled orders"); + assert::tick_invariants(&t); + + orders::place_market_and_assert_balance( + &cp, + &t, + OrderDirection::Bid, + Uint128::from(100u128), + "user1", + ) + .unwrap(); + assert::tick_invariants(&t); + assert::pool_liquidity(&t, 0u8, 0u8, "cancelled orders"); + assert::pool_balance(&t, 0u8, 100u8, "cancelled orders"); + assert::tick_invariants(&t); + + orders::claim(&t, "user1", 0, amount_orders).unwrap(); + assert::tick_invariants(&t); +} + +/// This test ensures that as ticks are iterated and filled the price is getting worse (as it starts at the best possible price an works towards the worst) +#[test] +fn test_decrementing_market_value() { + let app = OsmosisTestApp::new(); + let cp = CosmwasmPool::new(&app); + let mut t = setup!(&app, "quote", "base", 0); + + // We want a relatively large market amount to ensure tick moves without a large amount of market orders + let market_amount = 1000u128; + // We want a relatively small limit amount to help move the tick + let limit_amount = 10u128; + // We want a relatively large tick step to ensure price is shifting between ticks + let tick_step = 10000; + let (min_tick, max_tick) = (-4500000, 4500000); + + // Ensure this is true for both directions + for direction in [OrderDirection::Ask, OrderDirection::Bid] { + // Place a limit order at each tick step between the min and max tick + for tick in (min_tick..max_tick).step_by(tick_step as usize) { + // We don't care who places the order as we are only checking expected output + let username = format!("user{}", tick); + t.add_account( + &username, + vec![ + coin(1000000, "base"), + coin(1000000, "quote"), + coin(10000000000, "uosmo"), + ], + ); + orders::place_limit(&t, tick, direction, limit_amount, None, &username).unwrap(); + } + + // Record the current expected output for the first market order + let mut prev_expected_output = assert::decrementing_market_order_output( + &t, + u128::MAX - 1, + market_amount, + direction.opposite(), + ); + + // If this is zero then the setup amounts do not work + assert!(!prev_expected_output.is_zero()); + + let mut iterations = 0; + // Place orders while possible (this should loop at least once due to the expected output from above being non-zero) + while orders::place_market(&cp, &t, direction.opposite(), market_amount, "user1").is_ok() { + iterations += 1; + // Assert the expected output is getting worse + prev_expected_output = assert::decrementing_market_order_output( + &t, + prev_expected_output.u128(), + market_amount, + direction.opposite(), + ); + // Ensure ticks are correct as we iterate + assert::tick_invariants(&t); + } + + assert!(iterations > 0, "no market orders were placed"); + } +} diff --git a/contracts/sumtree-orderbook/src/tests/e2e/mod.rs b/contracts/sumtree-orderbook/src/tests/e2e/mod.rs new file mode 100644 index 0000000..2a95497 --- /dev/null +++ b/contracts/sumtree-orderbook/src/tests/e2e/mod.rs @@ -0,0 +1,6 @@ +#![cfg(all(not(tarpaulin), not(feature = "skip-integration-tests")))] + +mod cases; +mod modules; +mod test_env; +mod utils; diff --git a/contracts/sumtree-orderbook/src/tests/e2e/modules/cosmwasm_pool.rs b/contracts/sumtree-orderbook/src/tests/e2e/modules/cosmwasm_pool.rs new file mode 100644 index 0000000..7c6039b --- /dev/null +++ b/contracts/sumtree-orderbook/src/tests/e2e/modules/cosmwasm_pool.rs @@ -0,0 +1,43 @@ +use osmosis_std::types::osmosis::cosmwasmpool::v1beta1::{ + ContractInfoByPoolIdRequest, ContractInfoByPoolIdResponse, MsgCreateCosmWasmPool, + MsgCreateCosmWasmPoolResponse, +}; +use osmosis_std::types::osmosis::poolmanager::v1beta1::{ + MsgSwapExactAmountIn, MsgSwapExactAmountInResponse, MsgSwapExactAmountOut, + MsgSwapExactAmountOutResponse, +}; +use osmosis_test_tube::{fn_execute, fn_query}; + +use osmosis_test_tube::Module; +use osmosis_test_tube::Runner; + +pub struct CosmwasmPool<'a, R: Runner<'a>> { + runner: &'a R, +} + +impl<'a, R: Runner<'a>> Module<'a, R> for CosmwasmPool<'a, R> { + fn new(runner: &'a R) -> Self { + Self { runner } + } +} + +impl<'a, R> CosmwasmPool<'a, R> +where + R: Runner<'a>, +{ + fn_execute! { + pub create_cosmwasm_pool: MsgCreateCosmWasmPool => MsgCreateCosmWasmPoolResponse + } + + fn_execute! { + pub swap_exact_amount_in: MsgSwapExactAmountIn => MsgSwapExactAmountInResponse + } + + fn_execute! { + pub swap_exact_amount_out: MsgSwapExactAmountOut => MsgSwapExactAmountOutResponse + } + + fn_query! { + pub contract_info_by_pool_id ["/osmosis.cosmwasmpool.v1beta1.Query/ContractInfoByPoolId"]: ContractInfoByPoolIdRequest => ContractInfoByPoolIdResponse + } +} diff --git a/contracts/sumtree-orderbook/src/tests/e2e/modules/mod.rs b/contracts/sumtree-orderbook/src/tests/e2e/modules/mod.rs new file mode 100644 index 0000000..941af34 --- /dev/null +++ b/contracts/sumtree-orderbook/src/tests/e2e/modules/mod.rs @@ -0,0 +1 @@ +pub mod cosmwasm_pool; diff --git a/contracts/sumtree-orderbook/src/tests/e2e/test_env.rs b/contracts/sumtree-orderbook/src/tests/e2e/test_env.rs new file mode 100644 index 0000000..d09185f --- /dev/null +++ b/contracts/sumtree-orderbook/src/tests/e2e/test_env.rs @@ -0,0 +1,440 @@ +use std::{collections::HashMap, path::PathBuf, str::FromStr}; + +use crate::{ + constants::{MAX_TICK, MIN_TICK}, + msg::{ + AuthExecuteMsg, CalcOutAmtGivenInResponse, DenomsResponse, ExecuteMsg, + GetTotalPoolLiquidityResponse, GetUnrealizedCancelsResponse, InstantiateMsg, + OrdersResponse, QueryMsg, SudoMsg, TickIdAndState, TickUnrealizedCancels, TicksResponse, + }, + tests::test_utils::decimal256_from_u128, + tick_math::{amount_to_value, tick_to_price, RoundingDirection}, + types::{LimitOrder, OrderDirection}, +}; + +use cosmwasm_std::{to_json_binary, Addr, Coin, Coins, Decimal, Decimal256, Uint128, Uint256}; +use osmosis_std::types::{ + cosmos::{bank::v1beta1::QueryAllBalancesRequest, base::v1beta1::Coin as ProtoCoin}, + cosmwasm::wasm::v1::MsgExecuteContractResponse, + osmosis::cosmwasmpool::v1beta1::{ + ContractInfoByPoolIdRequest, ContractInfoByPoolIdResponse, MsgCreateCosmWasmPool, + }, +}; +use osmosis_test_tube::{ + osmosis_std::types::osmosis::cosmwasmpool::v1beta1::UploadCosmWasmPoolCodeAndWhiteListProposal, + GovWithAppAccess, +}; + +use osmosis_test_tube::{ + Account, Bank, Module, OsmosisTestApp, RunnerError, RunnerExecuteResult, RunnerResult, + SigningAccount, Wasm, +}; +use serde::de::DeserializeOwned; + +use super::modules::cosmwasm_pool::CosmwasmPool; + +pub struct TestEnv<'a> { + pub app: &'a OsmosisTestApp, + pub creator: SigningAccount, + pub contract: OrderbookContract<'a>, + pub accounts: HashMap, +} + +impl<'a> TestEnv<'a> { + pub(crate) fn add_account(&mut self, username: &str, balance: Vec) { + let account = self.app.init_account(&balance).unwrap(); + self.accounts.insert(username.to_string(), account); + } + + pub(crate) fn assert_contract_balances(&self, expected_balances: &[Coin], label: &str) { + let contract_balances: Vec = self.get_balance(&self.contract.contract_addr); + + assert_eq!( + contract_balances, expected_balances, + "{}: contract balances did not match", + label + ); + } + + pub(crate) fn get_balance(&self, address: &str) -> Vec { + let account_balances: Vec = Bank::new(self.app) + .query_all_balances(&QueryAllBalancesRequest { + address: address.to_string(), + pagination: None, + }) + .unwrap() + .balances + .into_iter() + .map(|coin| Coin::new(coin.amount.parse().unwrap(), coin.denom)) + .collect(); + + account_balances + } +} + +pub struct TestEnvBuilder { + account_balances: HashMap>, + instantiate_msg: Option, +} + +impl TestEnvBuilder { + pub(crate) fn new() -> Self { + Self { + account_balances: HashMap::new(), + instantiate_msg: None, + } + } + + pub(crate) fn with_instantiate_msg(mut self, msg: InstantiateMsg) -> Self { + self.instantiate_msg = Some(msg); + self + } + + pub(crate) fn with_account(mut self, account: &str, balance: Vec) -> Self { + self.account_balances.insert(account.to_string(), balance); + self + } + pub(crate) fn build(self, app: &'_ OsmosisTestApp) -> TestEnv<'_> { + let accounts: HashMap<_, _> = self + .account_balances + .into_iter() + .map(|(account, balance)| { + let balance: Vec<_> = balance + .into_iter() + .chain(vec![Coin::new(1000000000000, "uosmo")]) + .collect(); + + (account, app.init_account(&balance).unwrap()) + }) + .collect(); + + let creator = app + .init_account(&[Coin::new(1000000000000000u128, "uosmo")]) + .unwrap(); + + let instantiate_msg = self.instantiate_msg.expect("instantiate msg not set"); + let instantiate_msg = InstantiateMsg { ..instantiate_msg }; + + let contract = OrderbookContract::deploy(app, &instantiate_msg, &creator).unwrap(); + + TestEnv { + app, + creator, + contract, + accounts, + } + } +} + +pub struct OrderbookContract<'a> { + app: &'a OsmosisTestApp, + pub code_id: u64, + pub pool_id: u64, + pub contract_addr: String, +} + +impl<'a> OrderbookContract<'a> { + pub(crate) fn deploy( + app: &'a OsmosisTestApp, + instantiate_msg: &InstantiateMsg, + signer: &SigningAccount, + ) -> Result { + let cp = CosmwasmPool::new(app); + let gov = GovWithAppAccess::new(app); + + let code_id = 1; // temporary solution + gov.propose_and_execute( + UploadCosmWasmPoolCodeAndWhiteListProposal::TYPE_URL.to_string(), + UploadCosmWasmPoolCodeAndWhiteListProposal { + title: String::from("store test cosmwasm pool code"), + description: String::from("test"), + wasm_byte_code: Self::get_wasm_byte_code(), + }, + signer.address(), + signer, + )?; + + let res = cp.create_cosmwasm_pool( + MsgCreateCosmWasmPool { + code_id, + instantiate_msg: to_json_binary(instantiate_msg).unwrap().to_vec(), + sender: signer.address(), + }, + signer, + )?; + + let pool_id = res.data.pool_id; + + let ContractInfoByPoolIdResponse { + contract_address, + code_id: _, + } = cp.contract_info_by_pool_id(&ContractInfoByPoolIdRequest { pool_id })?; + + let contract = Self { + app, + code_id, + pool_id, + contract_addr: contract_address, + }; + + Ok(contract) + } + + pub(crate) fn execute( + &self, + msg: &ExecuteMsg, + funds: &[Coin], + signer: &SigningAccount, + ) -> RunnerExecuteResult { + let wasm = Wasm::new(self.app); + wasm.execute(&self.contract_addr, msg, funds, signer) + } + + pub(crate) fn query(&self, msg: &QueryMsg) -> RunnerResult + where + Res: ?Sized + DeserializeOwned, + { + let wasm = Wasm::new(self.app); + wasm.query(&self.contract_addr, msg) + } + + pub(crate) fn get_wasm_byte_code() -> Vec { + let manifest_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + std::fs::read( + manifest_path + .join("..") + .join("..") + .join("target") + .join("wasm32-unknown-unknown") + .join("release") + .join("sumtree_orderbook.wasm"), + ) + .unwrap() + } + + // -- Admin Methods -- + pub(crate) fn _set_admin(&self, app: &OsmosisTestApp, admin: Addr) { + app.wasm_sudo( + &self.contract_addr, + SudoMsg::TransferAdmin { new_admin: admin }, + ) + .unwrap(); + } + + pub(crate) fn _set_maker_fee( + &self, + signer: &SigningAccount, + maker_fee: Decimal256, + recipient: &SigningAccount, + ) { + self.execute( + &ExecuteMsg::Auth(AuthExecuteMsg::SetMakerFee { fee: maker_fee }), + &[], + signer, + ) + .unwrap(); + + self.execute( + &ExecuteMsg::Auth(AuthExecuteMsg::SetMakerFeeRecipient { + recipient: Addr::unchecked(recipient.address()), + }), + &[], + signer, + ) + .unwrap(); + } + + // -- Queries -- + + pub(crate) fn get_maker_fee(&self) -> Decimal256 { + let maker_fee: Decimal256 = self.query(&QueryMsg::GetMakerFee {}).unwrap(); + maker_fee + } + + pub(crate) fn get_denoms(&self) -> DenomsResponse { + self.query(&QueryMsg::Denoms {}).unwrap() + } + + // Calculate the expected output for a given input amount/direction using the CosmWasm pool query + pub(crate) fn get_out_given_in( + &self, + direction: OrderDirection, + amount: impl Into, + ) -> RunnerResult { + let (token_in_denom, token_out_denom) = if direction == OrderDirection::Bid { + (self.get_denoms().quote_denom, self.get_denoms().base_denom) + } else { + (self.get_denoms().base_denom, self.get_denoms().quote_denom) + }; + + self.query(&QueryMsg::CalcOutAmountGivenIn { + token_in: Coin::new(amount.into(), token_in_denom), + token_out_denom, + swap_fee: Decimal::zero(), + }) + .map(|r: CalcOutAmtGivenInResponse| r.token_out) + } + + pub(crate) fn get_directional_liquidity(&self, order_direction: OrderDirection) -> u128 { + let GetTotalPoolLiquidityResponse { + total_pool_liquidity, + } = self.query(&QueryMsg::GetTotalPoolLiquidity {}).unwrap(); + + // Determine the amount of liquidity for the given direction + let liquidity = if order_direction == OrderDirection::Bid { + Coins::try_from(total_pool_liquidity.clone()) + .unwrap() + .amount_of("base") + } else { + Coins::try_from(total_pool_liquidity.clone()) + .unwrap() + .amount_of("quote") + }; + + liquidity.u128() + } + + pub(crate) fn get_order( + &self, + sender: String, + tick_id: i64, + order_id: u64, + ) -> Option { + let OrdersResponse { orders, .. } = self + .query(&QueryMsg::OrdersByOwner { + owner: Addr::unchecked(sender), + start_from: Some((tick_id, order_id)), + end_at: None, + limit: Some(1), + }) + .unwrap(); + orders.first().cloned() + } + + pub(crate) fn collect_all_ticks(&self) -> Vec { + let mut ticks = vec![]; + let mut min_tick = MIN_TICK; + while min_tick <= MAX_TICK { + let tick: TicksResponse = self + .query(&QueryMsg::AllTicks { + start_from: Some(min_tick), + end_at: Some(MAX_TICK), + limit: Some(300), + }) + .unwrap(); + if tick.ticks.is_empty() { + break; + } + ticks.extend(tick.ticks.clone()); + // Determine the next tick to start at for the next query loop + min_tick = tick.ticks.iter().max_by_key(|t| t.tick_id).unwrap().tick_id + 1; + } + ticks + } + + pub(crate) fn collect_all_orders(&self) -> Vec { + let ticks = self.collect_all_ticks(); + + let mut all_orders: Vec = vec![]; + for tick in ticks { + let orders: OrdersResponse = self + .query(&QueryMsg::OrdersByTick { + tick_id: tick.tick_id, + start_from: None, + end_at: None, + limit: None, + }) + .unwrap(); + all_orders.extend(orders.orders.clone()); + } + + all_orders + } + + /// Calculates the max amount for a market order that can be placed + /// by iterating over all the ticks, calculating their liquidity and summing + /// + /// The amount caps at `u128::MAX` + pub(crate) fn get_max_market_amount(&self, direction: OrderDirection) -> u128 { + let mut max_amount: Uint128 = Uint128::zero(); + let ticks = self.collect_all_ticks(); + for tick in ticks { + let value = tick.tick_state.get_values(direction.opposite()); + + // If the tick has no liquidity we can skip this tick + if value.total_amount_of_liquidity.is_zero() { + continue; + } + + let price = tick_to_price(tick.tick_id).unwrap(); + let amount_of_liquidity = Uint128::from_str( + &(value + .total_amount_of_liquidity + .min(decimal256_from_u128(u128::MAX))) + .to_string(), + ) + .unwrap(); + let amount_u256 = amount_to_value( + direction.opposite(), + amount_of_liquidity, + price, + RoundingDirection::Down, + ) + .unwrap(); + let amount = + Uint128::from_str(&(amount_u256.min(Uint256::from_u128(u128::MAX))).to_string()) + .unwrap(); + + max_amount = max_amount.saturating_add(amount); + } + max_amount.u128() + } + + /// Calculates how much is available for claim for a given order + /// + /// The amount that is claimable is dependent on a tick sync. + /// To account for this we first fetch the amount of unrealized cancels for the tick + /// and add it to the current ETAS before computing the difference. + pub(crate) fn get_order_claimable_amount(&self, order: LimitOrder) -> u128 { + let TicksResponse { ticks } = self + .query(&QueryMsg::AllTicks { + start_from: Some(order.tick_id), + end_at: None, + limit: Some(1), + }) + .unwrap(); + + // Get current tick values + let tick = ticks.first().unwrap().tick_state.clone(); + let tick_values = tick.get_values(order.order_direction); + + // Get the current unrealized cancels for the tick + let GetUnrealizedCancelsResponse { ticks } = self + .query(&QueryMsg::GetUnrealizedCancels { + tick_ids: vec![order.tick_id], + }) + .unwrap(); + let TickUnrealizedCancels { + unrealized_cancels, .. + } = ticks.first().unwrap(); + let cancelled_amount = match order.order_direction { + OrderDirection::Bid => unrealized_cancels.bid_unrealized_cancels, + OrderDirection::Ask => unrealized_cancels.ask_unrealized_cancels, + }; + + // Add unrealized cancels to the current ETAS + let synced_etas = tick_values + .effective_total_amount_swapped + .checked_add(cancelled_amount) + .unwrap(); + + // Compute the expected amount as if the tick had been synced + let expected_amount_u256 = synced_etas + .saturating_sub(order.etas) + .to_uint_floor() + .min(Uint256::from(order.quantity.u128())); + + let expected_amount = Uint128::try_from(expected_amount_u256).unwrap(); + expected_amount.u128() + } +} diff --git a/contracts/sumtree-orderbook/src/tests/e2e/utils.rs b/contracts/sumtree-orderbook/src/tests/e2e/utils.rs new file mode 100644 index 0000000..5e890d0 --- /dev/null +++ b/contracts/sumtree-orderbook/src/tests/e2e/utils.rs @@ -0,0 +1,744 @@ +/// Sets up the testing environment for the orderbook +#[macro_export] +macro_rules! setup { + ($($app:expr, $quote_denom:expr, $base_denom:expr, $maker_fee:expr),* ) => {{ + // -- Setup -- + // Ensure both denoms are present in the app + $($app.init_account(&[ + cosmwasm_std::Coin::new(1, $quote_denom), + cosmwasm_std::Coin::new(1, $base_denom), + ]) + .unwrap(); + + // Create two user accounts, an account for contract admin and one to be the recipient for the marker fee + let t = $crate::tests::e2e::test_env::TestEnvBuilder::new() + .with_account( + "user1", + vec![ + cosmwasm_std::Coin::new(2_000, $quote_denom), + cosmwasm_std::Coin::new(2_000, $base_denom), + ], + ) + .with_account( + "user2", + vec![ + cosmwasm_std::Coin::new(2_000, $quote_denom), + cosmwasm_std::Coin::new(2_000, $base_denom), + ], + ) + .with_account( + "contract_admin", + vec![], + ) + .with_account( + "maker_fee_recipient", + vec![], + ) + .with_instantiate_msg($crate::msg::InstantiateMsg { + base_denom: $base_denom.to_string(), + quote_denom: $quote_denom.to_string(), + }) + .build(&$app); + + // -- Assert Contract State -- + let $crate::msg::DenomsResponse { + quote_denom, + base_denom, + } = t.contract.query(&$crate::msg::QueryMsg::Denoms {}).unwrap(); + + assert_eq!(quote_denom, "quote"); + assert_eq!(base_denom, "base"); + + let $crate::msg::GetTotalPoolLiquidityResponse { + total_pool_liquidity, + } = t + .contract + .query(&$crate::msg::QueryMsg::GetTotalPoolLiquidity {}) + .unwrap(); + assert_eq!( + total_pool_liquidity, + vec![ + cosmwasm_std::Coin::new(0, "base"), + cosmwasm_std::Coin::new(0, "quote"), + ] + ); + + let is_active: bool = t.contract.query(&$crate::msg::QueryMsg::IsActive {}).unwrap(); + assert!(is_active); + + // NOTE: wasm_sudo does not currently maintain state so these calls will not work + // use osmosis_test_tube::Account; + // t.contract.set_admin($app, cosmwasm_std::Addr::unchecked(&t.accounts["contract_admin"].address())); + // t.contract + // .set_maker_fee(&t.accounts["contract_admin"], Decimal256::percent($maker_fee), &t.accounts["maker_fee_recipient"]); + + t)* + }}; +} + +// -- Assertions -- +// Assertions about current state +pub mod assert { + use std::str::FromStr; + + use crate::{ + msg::{ + DenomsResponse, GetTotalPoolLiquidityResponse, GetUnrealizedCancelsResponse, QueryMsg, + SpotPriceResponse, TickIdAndState, + }, + tests::e2e::test_env::TestEnv, + tick_math::{amount_to_value, tick_to_price, RoundingDirection}, + types::{OrderDirection, Orderbook}, + ContractError, + }; + use cosmwasm_std::{Coin, Coins, Fraction, Uint128}; + use osmosis_test_tube::{cosmrs::proto::prost::Message, RunnerError, RunnerExecuteResult}; + + // -- Contract State Assertions + + /// Asserts that the orderbook's current liquidity matches what is provided + pub(crate) fn pool_liquidity( + t: &TestEnv, + base_liquidity: impl Into, + quote_liquidity: impl Into, + label: &str, + ) { + let DenomsResponse { + quote_denom, + base_denom, + } = t.contract.get_denoms(); + let GetTotalPoolLiquidityResponse { + total_pool_liquidity, + } = t + .contract + .query(&QueryMsg::GetTotalPoolLiquidity {}) + .unwrap(); + assert_eq!( + total_pool_liquidity, + vec![ + Coin::new(base_liquidity.into(), base_denom), + Coin::new(quote_liquidity.into(), quote_denom) + ], + "{}: pool liquidity did not match", + label + ); + } + + /// Asserts that the contract's balance matches what is provided + pub(crate) fn pool_balance( + t: &TestEnv, + base_liquidity: impl Into, + quote_liquidity: impl Into, + label: &str, + ) { + let DenomsResponse { + quote_denom, + base_denom, + } = t.contract.get_denoms(); + t.assert_contract_balances( + [ + Coin::new(base_liquidity.into(), base_denom), + Coin::new(quote_liquidity.into(), quote_denom), + ] + .iter() + .filter(|x| !x.amount.is_zero()) + .cloned() + .collect::>() + .as_slice(), + label, + ); + } + + /// Asserts that the orderbook spot price matches what is provided + pub(crate) fn spot_price(t: &TestEnv, bid_tick: i64, ask_tick: i64, label: &str) { + let bid_price = tick_to_price(bid_tick).unwrap(); + let ask_price = tick_to_price(ask_tick).unwrap(); + let DenomsResponse { + quote_denom, + base_denom, + } = t.contract.get_denoms(); + + for (base_denom, quote_denom, price, direction) in [ + (base_denom.clone(), quote_denom.clone(), bid_price, "ask"), + (quote_denom, base_denom, ask_price.inv().unwrap(), "bid"), + ] { + let SpotPriceResponse { spot_price } = t + .contract + .query(&QueryMsg::SpotPrice { + base_asset_denom: base_denom, + quote_asset_denom: quote_denom, + }) + .unwrap(); + + assert_eq!( + spot_price.to_string(), + price.to_string(), + "{}: {} price did not match", + label, + direction + ); + } + } + + /// Asserts that the contract balance is greater than or equal to what is recorded in the orderbook directional liquidity state + /// If this assertion is ever false then the orderbook is "out of balance" and cannot provide liquidity for future orders + pub(crate) fn has_liquidity(t: &TestEnv) { + let bid_liquidity = t.contract.get_directional_liquidity(OrderDirection::Bid); + let ask_liquidity = t.contract.get_directional_liquidity(OrderDirection::Ask); + + let balance = Coins::try_from(t.get_balance(&t.contract.contract_addr)).unwrap(); + let bid_balance = balance.amount_of(&t.contract.get_denoms().base_denom); + let ask_balance = balance.amount_of(&t.contract.get_denoms().quote_denom); + + assert!( + bid_liquidity <= bid_balance.u128(), + "invalid bid liquidity, expected: {}, got: {}", + bid_balance, + bid_liquidity + ); + assert!( + ask_liquidity <= ask_balance.u128(), + "invalid ask liquidity, expected: {}, got: {}", + ask_balance, + ask_liquidity + ); + } + + /// Determines if the provided tick has fillable liquidity for the given direction + /// + /// Fillable liquidity means that after calculating amount to value the amount of liquidity is still non-zero + fn has_fillable_liqudity(tick: &TickIdAndState, direction: OrderDirection) -> bool { + if tick + .tick_state + .get_values(direction) + .total_amount_of_liquidity + .is_zero() + { + return false; + } + + let price = tick_to_price(tick.tick_id).unwrap(); + let amount_of_liquidity = Uint128::from_str( + &tick + .tick_state + .get_values(direction) + .total_amount_of_liquidity + .to_string(), + ) + .unwrap(); + let fillable_amount = amount_to_value( + direction, + amount_of_liquidity, + price, + RoundingDirection::Down, + ) + .unwrap(); + !fillable_amount.is_zero() + } + + /// Assertions about tick state + /// 1. All ticks have a cumulative value that is greater than or equal to the effective total amount swapped + /// 2. The next ask tick is less than or equal to the minimum tick with an ask amount + /// 3. The next bid tick is greater than or equal to the maximum tick with a bid amount + /// + /// This assertion can be run mid test as it must always be true + pub(crate) fn tick_invariants(t: &TestEnv) { + let Orderbook { + next_ask_tick, + next_bid_tick, + .. + } = t.contract.query(&QueryMsg::OrderbookState {}).unwrap(); + + let ticks = t.contract.collect_all_ticks(); + for direction in [OrderDirection::Bid, OrderDirection::Ask] { + // Assert every tick has a cumulative value that is greater than or equal to the effective total amount swapped + assert!(ticks.iter().all(|t| t + .tick_state + .get_values(direction) + .effective_total_amount_swapped + <= t.tick_state.get_values(direction).cumulative_total_value)); + + // Get all ticks with fillable liquidity for the given direction + let ticks_with_liquidity = ticks.iter().filter(|t| has_fillable_liqudity(t, direction)); + + // Determine the max/min tick with liquidity for the given direction + let boundary_tick_with_liquidity = match direction { + OrderDirection::Bid => ticks_with_liquidity.max_by_key(|tick| tick.tick_id), + OrderDirection::Ask => ticks_with_liquidity.min_by_key(|tick| tick.tick_id), + }; + + // If the given direction has at least one tick and a boundary exists we compare this with what is + // stored in the orderbook to ensure that tick pointers are correctly updated for the current orderbook state + if let Some(boundary_tick) = boundary_tick_with_liquidity { + match direction { + OrderDirection::Bid => { + assert!( + boundary_tick.tick_id <= next_bid_tick, + "BID TICK: got: {}, expected: {}", + next_bid_tick, + boundary_tick.tick_id + ); + } + OrderDirection::Ask => { + assert!( + boundary_tick.tick_id >= next_ask_tick, + "ASK TICK: got: {}, expected: {}", + next_ask_tick, + boundary_tick.tick_id + ); + } + } + } + } + } + + // Asserts that a new market order will return a lower or equal expected amount that a previous market expected output + pub(crate) fn decrementing_market_order_output( + t: &TestEnv, + previous_market_value: u128, + amount_to_run: u128, + direction: OrderDirection, + ) -> Uint128 { + // Calculate the expected output for a market order of the given amount + let maybe_expected_output = t.contract.get_out_given_in(direction, amount_to_run); + + // If the expected output errors we return zero + let expected_output = maybe_expected_output + .map_or(Uint128::zero(), |r| Uint128::from_str(&r.amount).unwrap()); + + // Assert that the expected output is less than or equal to the previous market value + assert!( + expected_output.u128() <= previous_market_value, + "subsequent market orders increased unexpectedly, got: {}, expected: {}", + expected_output, + previous_market_value + ); + + // Return the expected output + expected_output + } + + /// Asserts that there are no remaining orders in the orderbook + pub(crate) fn no_remaining_orders(t: &TestEnv) { + let all_orders = t.contract.collect_all_orders(); + assert_eq!(all_orders.len(), 0); + } + + /// Asserts that all ticks are fully synced + /// + /// **Should be run AFTER a fuzz test** + pub(crate) fn clean_ticks(t: &TestEnv) { + let all_ticks = t.contract.collect_all_ticks(); + for tick in all_ticks { + let GetUnrealizedCancelsResponse { ticks } = t + .contract + .query(&QueryMsg::GetUnrealizedCancels { + tick_ids: vec![tick.tick_id], + }) + .unwrap(); + let unrealized_cancels_state = ticks.first().unwrap(); + for direction in [OrderDirection::Ask, OrderDirection::Bid] { + let values = tick.tick_state.get_values(direction); + assert!( + values.total_amount_of_liquidity.is_zero(), + "tick {} has {} liquidity", + tick.tick_id, + values.total_amount_of_liquidity + ); + + let unrealized_cancels = match direction { + OrderDirection::Ask => { + unrealized_cancels_state + .unrealized_cancels + .ask_unrealized_cancels + } + OrderDirection::Bid => { + unrealized_cancels_state + .unrealized_cancels + .bid_unrealized_cancels + } + }; + + // As a tick may not be fully synced due to the last order being a cancellation rather than a claim + // we check that if the tick was fully synced then ETAS == CTT must be true + // In the case that the tick was already synced then unrealized cancels is 0 and we are doing a direct + // ETAS == CTT comparison + assert_eq!( + values + .effective_total_amount_swapped + .checked_add(unrealized_cancels) + .unwrap(), + values.cumulative_total_value + ); + } + } + } + + // -- Balance Assertions -- + + /// An assertion that records balances before an action and compares the balances after the provided action + /// Comparisons are only done for the vector of addresses provided in the second parameter + pub(crate) fn balance_changes( + t: &TestEnv, + changes: &[(&str, Vec)], + action: impl FnOnce() -> RunnerExecuteResult, + ) -> RunnerExecuteResult { + // Record balances before the action + let pre_balances: Vec<(String, Coins)> = changes + .iter() + .map(|(sender, _)| { + ( + sender.to_string(), + Coins::try_from(t.get_balance(sender)).unwrap(), + ) + }) + .collect(); + + // Run the action + let result = action()?; + + // Check balances after running the action + let post_balances: Vec<(String, Coins)> = changes + .iter() + .map(|(sender, _)| { + ( + sender.to_string(), + Coins::try_from(t.get_balance(sender)).unwrap(), + ) + }) + .collect(); + + // Check all expected balance changes + for (sender, balance_change) in changes.iter().cloned() { + let pre_balance = pre_balances + .iter() + .find(|(s, _)| s == sender) + .unwrap() + .1 + .clone(); + let post_balance = post_balances + .iter() + .find(|(s, _)| s == sender) + .unwrap() + .1 + .clone(); + + for coin in balance_change { + let pre_amount = pre_balance.amount_of(&coin.denom); + let post_amount = post_balance.amount_of(&coin.denom); + let change = post_amount.saturating_sub(pre_amount); + assert_eq!( + change, coin.amount, + "Did not receive expected amount change, expected: {}{}, got: {}{}", + coin.amount, coin.denom, change, coin.denom + ); + } + } + + Ok(result) + } + + pub(crate) fn contract_err(expected: ContractError, actual: RunnerError) { + match actual { + RunnerError::ExecuteError { msg } => { + if !msg.contains(&expected.to_string()) { + panic!( + "assertion failed:\n\n must contain \t: \"{}\",\n actual \t: \"{}\"\n", + expected, msg + ) + } + } + _ => panic!("unexpected error, expect execute error but got: {}", actual), + }; + } +} + +/// Utility functions for interacting with the orderbook +pub mod orders { + use std::str::FromStr; + + use cosmwasm_std::{coins, Coin, Decimal256, Uint128, Uint256}; + + use osmosis_std::types::{ + cosmwasm::wasm::v1::MsgExecuteContractResponse, + osmosis::poolmanager::v1beta1::{ + MsgSwapExactAmountIn, MsgSwapExactAmountInResponse, SwapAmountInRoute, + }, + }; + use osmosis_test_tube::{Account, OsmosisTestApp, RunnerExecuteResult}; + + use crate::{ + msg::{DenomsResponse, ExecuteMsg, QueryMsg}, + tests::e2e::{modules::cosmwasm_pool::CosmwasmPool, test_env::TestEnv}, + tick_math::{amount_to_value, tick_to_price, RoundingDirection}, + types::{LimitOrder, OrderDirection}, + }; + + use super::assert; + + pub(crate) fn place_limit( + t: &TestEnv, + tick_id: i64, + order_direction: OrderDirection, + quantity: impl Into, + claim_bounty: Option, + sender: &str, + ) -> RunnerExecuteResult { + let DenomsResponse { + quote_denom, + base_denom, + } = t.contract.query(&QueryMsg::Denoms {}).unwrap(); + + let denom = if order_direction == OrderDirection::Bid { + quote_denom + } else { + base_denom + }; + + let quantity_u128: Uint128 = quantity.into(); + + t.contract.execute( + &ExecuteMsg::PlaceLimit { + tick_id, + order_direction, + quantity: quantity_u128, + claim_bounty, + }, + &coins(quantity_u128.u128(), denom), + &t.accounts[sender], + ) + } + + pub(crate) fn place_market( + cp: &CosmwasmPool, + t: &TestEnv, + order_direction: OrderDirection, + quantity: impl Into, + sender: &str, + ) -> RunnerExecuteResult { + let pool_id = t.contract.pool_id; + let quantity_u128: Uint128 = quantity.into(); + let DenomsResponse { + base_denom, + quote_denom, + } = t.contract.query(&QueryMsg::Denoms {}).unwrap(); + + // Determine denom ordering based on order direction + let (token_in_denom, token_out_denom) = if order_direction == OrderDirection::Bid { + (quote_denom.clone(), base_denom.clone()) + } else { + (base_denom.clone(), quote_denom.clone()) + }; + + cp.swap_exact_amount_in( + MsgSwapExactAmountIn { + sender: t.accounts[sender].address(), + routes: vec![SwapAmountInRoute { + pool_id, + token_out_denom, + }], + token_in: Some(Coin::new(quantity_u128.u128(), token_in_denom).into()), + token_out_min_amount: Uint128::one().to_string(), + }, + &t.accounts[sender], + ) + } + + /// Places a market order and asserts that the sender's balance changes correctly + /// + /// Note: this check has some circularity to it as the expected out depends on the `CalcOutAmtGivenInResponse` + pub(crate) fn place_market_and_assert_balance( + cp: &CosmwasmPool, + t: &TestEnv, + order_direction: OrderDirection, + quantity: impl Into + Clone, + sender: &str, + ) -> RunnerExecuteResult { + let quantity_u128: Uint128 = quantity.clone().into(); + + // DEV NOTE: is there a way to remove circular dependency for output expectancy? + let token_out = t + .contract + .get_out_given_in(order_direction, quantity_u128.u128()) + .unwrap(); + + assert::balance_changes( + t, + // Users receives expected amount out in token out denom + &[( + &t.accounts[sender].address(), + vec![Coin::new( + Uint128::from_str(&token_out.amount.to_string()) + .unwrap() + .u128(), + token_out.denom, + )], + )], + || place_market(cp, t, order_direction, quantity, sender), + ) + } + + pub(crate) fn claim( + t: &TestEnv, + sender: &str, + tick_id: i64, + order_id: u64, + ) -> RunnerExecuteResult { + t.contract.execute( + &ExecuteMsg::ClaimLimit { order_id, tick_id }, + &[], + &t.accounts[sender], + ) + } + + /// Claims a given order using the provided sender account name + /// + /// Asserts that the sender and order owner's balances change correctly + pub(crate) fn claim_and_assert_balance( + t: &TestEnv, + sender: &str, + owner: &str, + tick_id: i64, + order_id: u64, + ) -> RunnerExecuteResult { + let order: LimitOrder = t + .contract + .get_order(t.accounts[owner].address(), tick_id, order_id) + .unwrap(); + + // Get how much is expected out given the current tick state (accounts for unrealized cancels) + let expected_amount = t.contract.get_order_claimable_amount(order.clone()); + + // Convert the expected amount to the price of the order + let price = tick_to_price(order.tick_id).unwrap(); + let mut expected_received_u256 = amount_to_value( + order.order_direction, + Uint128::from(expected_amount), + price, + RoundingDirection::Down, + ) + .unwrap(); + // Create immutable expected received for calculating claim and maker fees + let immut_expected_received_u256 = expected_received_u256; + + // Calculate the bounty amount if there is one + let mut bounty_amount_256 = Uint256::zero(); + if let Some(bounty) = order.claim_bounty { + if order.owner != t.accounts[sender].address() { + bounty_amount_256 = + Decimal256::from_ratio(immut_expected_received_u256, Uint256::one()) + .checked_mul(bounty) + .unwrap() + .to_uint_floor(); + // Subtract the bounty from the expected received + expected_received_u256 = expected_received_u256 + .checked_sub(bounty_amount_256) + .unwrap(); + } + } + + // Calculate the maker fee + // May be zero + let maker_fee = t.contract.get_maker_fee(); + let maker_fee_amount_u256 = + Decimal256::from_ratio(immut_expected_received_u256, Uint256::one()) + .checked_mul(maker_fee) + .unwrap() + .to_uint_floor(); + let maker_fee_amount = Uint128::try_from(maker_fee_amount_u256).unwrap(); + + // Subtract the maker fee from the expected received + expected_received_u256 = expected_received_u256 + .checked_sub(maker_fee_amount_u256) + .unwrap(); + + let bounty_amount = Uint128::try_from(bounty_amount_256).unwrap(); + let expected_received = Uint128::try_from(expected_received_u256).unwrap(); + + let DenomsResponse { + base_denom, + quote_denom, + } = t.contract.get_denoms(); + let expected_denom = if order.order_direction == OrderDirection::Bid { + base_denom + } else { + quote_denom + }; + + assert::balance_changes( + t, + [ + // Assert owner receives amount - maker fee - claim bounty + ( + order.owner.as_str(), + vec![Coin::new(expected_received.u128(), expected_denom.clone())], + ), + // Assert sender receives bounty (will be 0 if the sender is the owner) + ( + &t.accounts[sender].address(), + vec![Coin::new(bounty_amount.u128(), expected_denom.clone())], + ), + // Assert maker fee recipient receives maker fee + ( + &t.accounts["maker_fee_recipient"].address(), + vec![Coin::new(maker_fee_amount.u128(), expected_denom)], + ), + ] + .iter() + // Remove any 0 checks + .filter(|x| x.1.iter().all(|y| !y.amount.is_zero())) + .cloned() + .collect::)>>() + .as_slice(), + || claim(t, sender, tick_id, order_id), + ) + } + + pub(crate) fn cancel_limit( + t: &TestEnv, + sender: &str, + tick_id: i64, + order_id: u64, + ) -> RunnerExecuteResult { + t.contract.execute( + &ExecuteMsg::CancelLimit { order_id, tick_id }, + &[], + &t.accounts[sender], + ) + } + + /// Cancels a limit order and asserts that the owner receives back the remaining order quantity (may be partially filled) + pub(crate) fn cancel_limit_and_assert_balance( + t: &TestEnv, + sender: &str, + tick_id: i64, + order_id: u64, + ) -> RunnerExecuteResult { + let order = t + .contract + .get_order(t.accounts[sender].address(), tick_id, order_id) + .unwrap(); + + let order_direction = order.order_direction; + let quantity = order.quantity; + let DenomsResponse { + base_denom, + quote_denom, + } = t.contract.get_denoms(); + let token_in_denom = if order_direction == OrderDirection::Bid { + quote_denom + } else { + base_denom + }; + + assert::balance_changes( + t, + // Assert owner receives back the remaining order quantity + &[( + &t.accounts[sender].address(), + vec![Coin::new(quantity.u128(), token_in_denom)], + )], + || cancel_limit(t, sender, tick_id, order_id), + ) + } +} diff --git a/contracts/sumtree-orderbook/src/tests/mod.rs b/contracts/sumtree-orderbook/src/tests/mod.rs index 4dada36..770bb97 100644 --- a/contracts/sumtree-orderbook/src/tests/mod.rs +++ b/contracts/sumtree-orderbook/src/tests/mod.rs @@ -10,3 +10,5 @@ pub mod test_sudo; pub mod test_tick; pub mod test_tick_math; mod test_utils; + +pub mod e2e; diff --git a/contracts/sumtree-orderbook/src/tests/test_order.rs b/contracts/sumtree-orderbook/src/tests/test_order.rs index dfa3dfc..7430c82 100644 --- a/contracts/sumtree-orderbook/src/tests/test_order.rs +++ b/contracts/sumtree-orderbook/src/tests/test_order.rs @@ -2,7 +2,7 @@ use std::str::FromStr; use crate::{ constants::{MAX_TICK, MIN_TICK}, error::ContractError, order::*, orderbook::*, state::*, sumtree::{ - node::{NodeType, TreeNode}, test::test_node::print_tree, tree::{get_or_init_root_node, get_root_node} + node::{NodeType, TreeNode}, tree::get_root_node }, tests::{mock_querier::mock_dependencies_custom, test_utils::{decimal256_from_u128, place_multiple_limit_orders}}, types::{ @@ -17,7 +17,6 @@ use cosmwasm_std::{ Decimal256, }; use cw_utils::PaymentError; - use super::{test_constants::{DEFAULT_OWNER, DEFAULT_SENDER, BASE_DENOM, QUOTE_DENOM, LARGE_POSITIVE_TICK, LARGE_NEGATIVE_TICK}, test_utils::{ format_test_name, generate_limit_orders, OrderOperation, }}; @@ -655,21 +654,23 @@ fn test_run_market_order() { orders: generate_limit_orders( &[-1500000], // 1000 units of liquidity total - 10, + 12, default_quantity, OrderDirection::Ask, ), // Bidding 1000 units of input into tick -1500000, which corresponds to $0.85, - // implies 1000*0.85 = 850 units of output. - expected_output: Uint256::from_u128(850), - expected_tick_etas: vec![(-1500000, decimal256_from_u128(Uint128::new(850)))], + // implies 1000 / 0.85 = 1176 units of output. As a sanity check, a bid is a + // buy on the other asset, so we should expect >1000 output due to the price + // being below $1. + expected_output: Uint256::from_u128(1176), + expected_tick_etas: vec![(-1500000, decimal256_from_u128(Uint128::new(1176)))], expected_tick_pointers: vec![(OrderDirection::Ask, -1500000)], expected_error: None, }, RunMarketOrderTestCase { name: "happy path bid at positive tick", placed_order: MarketOrder::new( - Uint128::new(1000), + Uint128::new(100_000), OrderDirection::Bid, Addr::unchecked(DEFAULT_SENDER), ), @@ -680,15 +681,15 @@ fn test_run_market_order() { // Two orders with sufficient total liquidity to process the // full market order 2, - Uint128::new(25_000_000), + Uint128::new(1), OrderDirection::Ask, ), - // Bidding 1000 units of input into tick 40,000,000, which corresponds to a + // Bidding 100,000 units of input into tick 40,000,000, which corresponds to a // price of $50000 (from tick math test cases). // - // This implies 1000*50000 = 50,000,000 units of output. - expected_output: Uint256::from_u128(50_000_000), - expected_tick_etas: vec![(40000000, decimal256_from_u128(Uint128::new(50_000_000)))], + // This implies 100,000 / 50,000 = 2 units of output. + expected_output: Uint256::from_u128(2), + expected_tick_etas: vec![(40000000, decimal256_from_u128(Uint128::new(2)))], expected_tick_pointers: vec![(OrderDirection::Ask, 40000000)], expected_error: None, }, @@ -706,23 +707,23 @@ fn test_run_market_order() { // Four limit orders with sufficient total liquidity to process the // full market order 4, - Uint128::new(3), + Uint128::new(20_250), OrderDirection::Ask, ), // Bidding 1000 units of input into tick -17765433, which corresponds to a // price of $0.012345670000000000 (from tick math test cases). // - // This implies 1000*0.012345670000000000 = 12.34567 units of output, + // This implies 1000 / 0.012345670000000000 = 81,000 units of output, // truncated to 12 units. - expected_output: Uint256::from_u128(12), - expected_tick_etas: vec![(-17765433, decimal256_from_u128(Uint128::new(12)))], + expected_output: Uint256::from_u128(81_000), + expected_tick_etas: vec![(-17765433, decimal256_from_u128(Uint128::new(81_000)))], expected_tick_pointers: vec![(OrderDirection::Ask, -17765433)], expected_error: None, }, RunMarketOrderTestCase { name: "bid across multiple ticks", placed_order: MarketOrder::new( - Uint128::new(589 + 1), + Uint128::new(510 + 3), OrderDirection::Bid, Addr::unchecked(DEFAULT_SENDER), ), @@ -730,26 +731,26 @@ fn test_run_market_order() { // Orders to fill against orders: generate_limit_orders( &[-1500000, 1500000], - // 500 units of liquidity on each tick - 5, + // 600 units of liquidity on each tick + 6, default_quantity, OrderDirection::Ask, ), // Bidding 1000 units of input into tick -1500000, which corresponds to $0.85, - // implies 1000*0.85 = 850 units of output, but there is only 500 on the tick. + // implies 1000 / 0.85 = 1176 units of output, but there is only 600 on the tick. // - // So 500 gets filled at -1500000, corresponding to ~589 of the input (500/0.85). - // The remaining 1 unit is filled at tick 1500000 (price $2.5), which - // corresponds to the remaining liquidity. + // So 600 gets filled at -1500000, corresponding to ~510 of the input (600 * 0.85). + // The remaining 3 units of input is filled at tick 1500000 (price $2.5), which + // corresponds to the remaining liquidity (3 / 2.5 = 1.2 -> truncated to 1). // - // Thus, the total expected output is 502. + // Thus, the total expected output is 600 + 1. // // Note: this case does not cover rounding for input consumption since it overfills // the tick. - expected_output: Uint256::from_u128(502), + expected_output: Uint256::from_u128(601), expected_tick_etas: vec![ - (-1500000, decimal256_from_u128(Uint128::new(500))), - (1500000, decimal256_from_u128(Uint128::new(2))), + (-1500000, decimal256_from_u128(Uint128::new(600))), + (1500000, decimal256_from_u128(Uint128::new(1))), ], expected_tick_pointers: vec![(OrderDirection::Ask, 1500000)], expected_error: None, @@ -757,7 +758,7 @@ fn test_run_market_order() { RunMarketOrderTestCase { name: "happy path ask at positive tick", placed_order: MarketOrder::new( - Uint128::new(100000), + Uint128::new(100), OrderDirection::Ask, Addr::unchecked(DEFAULT_SENDER), ), @@ -767,16 +768,16 @@ fn test_run_market_order() { &[40000000], // Two orders with sufficient total liquidity to process the // full market order - 2, - Uint128::new(1), + 5, + Uint128::new(1_000_000), OrderDirection::Bid, ), - // Asking 100,000 units of input into tick 40,000,000, which corresponds to a - // price of $1/50000 (from tick math test cases). + // Asking 100 units of input into tick 40,000,000, which corresponds to a + // price of $50,000 (from tick math test cases). // - // This implies 100,000/50000 = 2 units of output. - expected_output: Uint256::from_u128(2), - expected_tick_etas: vec![(40000000, decimal256_from_u128(Uint128::new(2)))], + // This implies 100 * 50,000 = 5,000,000 units of output. + expected_output: Uint256::from_u128(5_000_000), + expected_tick_etas: vec![(40000000, decimal256_from_u128(Uint128::new(5_000_000)))], expected_tick_pointers: vec![(OrderDirection::Bid, 40000000)], expected_error: None, }, @@ -794,16 +795,16 @@ fn test_run_market_order() { // Two orders with sufficient total liquidity to process the // full market order 2, - Uint128::new(50_000), + Uint128::new(10), OrderDirection::Bid, ), // The order asks with 1000 units of input into tick -17765433, which corresponds // to a price of $0.012345670000000000 (from tick math test cases). // - // This implies 1000 / 0.012345670000000000 = 81,000.059 units of output, - // which gets truncated to 81,000 units. - expected_output: Uint256::from_u128(81_000), - expected_tick_etas: vec![(-17765433, decimal256_from_u128(Uint128::new(81_000)))], + // This implies 1000 * 0.012345670000000000 = 12.34567 units of output, + // which gets truncated to 12 units. + expected_output: Uint256::from_u128(12), + expected_tick_etas: vec![(-17765433, decimal256_from_u128(Uint128::new(12)))], expected_tick_pointers: vec![(OrderDirection::Bid, -17765433)], expected_error: None, }, @@ -1052,7 +1053,8 @@ fn test_run_market_order_moving_tick() { )), // Fill all limits on tick 0 and 50% of tick 1, leaving tick 0 empty and forcing positive movement OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(15u128), + // We provide 16 here as rounding on the second tick causes this to fill 5 on the second tick + Uint128::from(16u128), OrderDirection::Bid, Addr::unchecked("buyer"), )), @@ -1293,7 +1295,8 @@ fn test_run_market_order_moving_tick() { )), // Fill entire first tick and 50% of next tick to force negative movement OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(15u128), + // We provide 16 here as rounding on the second tick causes this to fill 5 on the second tick + Uint128::from(16u128), OrderDirection::Ask, Addr::unchecked("buyer"), )), @@ -1370,7 +1373,8 @@ fn test_run_market_order_moving_tick() { )), // Fill entire first tick and 50% of next tick to force negative movement OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(15u128), + // We provide 16 here as rounding on the second tick causes this to fill 5 on the second tick + Uint128::from(16u128), OrderDirection::Ask, Addr::unchecked("buyer"), )), @@ -1467,7 +1471,8 @@ fn test_run_market_order_moving_tick() { )), // Fill entire first tick and 50% of second tick to force positive movement OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(15u128), + // We provide 16 here as rounding on the second tick causes this to fill 5 on the second tick + Uint128::from(16u128), OrderDirection::Bid, Addr::unchecked("buyer"), )), @@ -1847,8 +1852,8 @@ fn test_claim_order() { None, )), OrderOperation::RunMarket(MarketOrder::new( - // Tick price is 2, 2*5 = 10 - Uint128::from(5u128), + // Tick price is 2, 2*10 = 20 + Uint128::from(20u128), OrderDirection::Bid, Addr::unchecked("buyer"), )), @@ -1859,7 +1864,7 @@ fn test_claim_order() { MsgSend256 { from_address: "cosmos2contract".to_string(), to_address: sender.to_string(), - amount: vec![coin_u256(Uint256::from(5u128), QUOTE_DENOM)], + amount: vec![coin_u256(Uint256::from(20u128), QUOTE_DENOM)], }, REPLY_ID_CLAIM, )), @@ -1881,8 +1886,9 @@ fn test_claim_order() { None, )), OrderOperation::RunMarket(MarketOrder::new( - // Tick price is 2, 2*2 = 4 - Uint128::from(2u128), + // Filling 4/10 of the Ask order + // Tick price is 2, 2*4 = 8 + Uint128::from(8u128), OrderDirection::Bid, Addr::unchecked("buyer"), )), @@ -1894,7 +1900,7 @@ fn test_claim_order() { MsgSend256 { from_address: "cosmos2contract".to_string(), to_address: sender.to_string(), - amount: vec![coin_u256(Uint256::from(2u128), QUOTE_DENOM)], + amount: vec![coin_u256(Uint256::from(8u128), QUOTE_DENOM)], }, REPLY_ID_CLAIM, )), @@ -1924,15 +1930,18 @@ fn test_claim_order() { None, )), OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(2u128), + // Filling 6/10 of the Ask order + // Tick price is 2, 2*6 = 12 + Uint128::from(12u128), OrderDirection::Bid, Addr::unchecked("buyer"), )), // Claim the first partial fill OrderOperation::Claim((LARGE_POSITIVE_TICK, 0)), OrderOperation::RunMarket(MarketOrder::new( - // Tick price is 2, 2*3 = 6 - Uint128::from(3u128), + // Filling 4/10 of the Ask order (full fill) + // Tick price is 2, 2*4 = 8 + Uint128::from(8u128), OrderDirection::Bid, Addr::unchecked("buyer"), )), @@ -1944,7 +1953,7 @@ fn test_claim_order() { MsgSend256 { from_address: "cosmos2contract".to_string(), to_address: sender.to_string(), - amount: vec![coin_u256(Uint256::from(3u128), QUOTE_DENOM)], + amount: vec![coin_u256(Uint256::from(8u128), QUOTE_DENOM)], }, REPLY_ID_CLAIM, )), @@ -1967,7 +1976,9 @@ fn test_claim_order() { None, )), OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(200u128), + // Full filling ask order + // Tick price is 0.5, 0.5*100 = 50 + Uint128::from(50u128), OrderDirection::Bid, Addr::unchecked("buyer"), )), @@ -1979,7 +1990,7 @@ fn test_claim_order() { MsgSend256 { from_address: "cosmos2contract".to_string(), to_address: sender.to_string(), - amount: vec![coin_u256(Uint256::from(200u128), QUOTE_DENOM)], + amount: vec![coin_u256(Uint256::from(50u128), QUOTE_DENOM)], }, REPLY_ID_CLAIM, )), @@ -2001,7 +2012,9 @@ fn test_claim_order() { None, )), OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(100u128), + // Filling 50/100 of the Ask order + // Tick price is 0.5, 0.5*50 = 25 + Uint128::from(25u128), OrderDirection::Bid, Addr::unchecked("buyer"), )), @@ -2013,7 +2026,7 @@ fn test_claim_order() { MsgSend256 { from_address: "cosmos2contract".to_string(), to_address: sender.to_string(), - amount: vec![coin_u256(Uint256::from(100u128), QUOTE_DENOM)], + amount: vec![coin_u256(Uint256::from(25u128), QUOTE_DENOM)], }, REPLY_ID_CLAIM, )), @@ -2043,14 +2056,18 @@ fn test_claim_order() { None, )), OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(100u128), + // Filling 50/100 of the Ask order + // Tick price is 0.5, 0.5*50 = 25 + Uint128::from(25u128), OrderDirection::Bid, Addr::unchecked("buyer"), )), // Claim the first partial fill OrderOperation::Claim((LARGE_NEGATIVE_TICK, 0)), OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(100u128), + // Filling 50/100 of the Ask order (full fill) + // Tick price is 0.5, 0.5*50 = 25 + Uint128::from(25u128), OrderDirection::Bid, Addr::unchecked("buyer"), )), @@ -2062,7 +2079,7 @@ fn test_claim_order() { MsgSend256 { from_address: "cosmos2contract".to_string(), to_address: sender.to_string(), - amount: vec![coin_u256(Uint256::from(100u128), QUOTE_DENOM)], + amount: vec![coin_u256(Uint256::from(25u128), QUOTE_DENOM)], }, REPLY_ID_CLAIM, )), @@ -2094,13 +2111,13 @@ fn test_claim_order() { )), OrderOperation::Cancel((valid_tick_id, 0)), OrderOperation::RunMarket(MarketOrder::new( + // Filling 100/100 of the Ask order Uint128::from(100u128), OrderDirection::Bid, Addr::unchecked("buyer"), )), ], order_id: 1, - tick_id: valid_tick_id, expected_bank_msg: Some(SubMsg::reply_on_error( MsgSend256 { @@ -2115,7 +2132,7 @@ fn test_claim_order() { expected_error: None, }, ClaimOrderTestCase { - name: "ASK: valid basic full claim at MIN_TICK", + name: "ASK: valid basic partial claim at MIN_TICK", sender: sender.clone(), operations: vec![ OrderOperation::PlaceLimit(LimitOrder::new( @@ -2123,7 +2140,7 @@ fn test_claim_order() { 0, OrderDirection::Ask, sender.clone(), - Uint128::from(10u128), + Uint128::from(3_000_000_000_000u128), Decimal256::zero(), None, )), @@ -2131,32 +2148,23 @@ fn test_claim_order() { // Tick price is 0.000000000001, so 3_333_333_333_333 * 0.000000000001 = 3.33333333333 // We expect this to get truncated to 3, as order outputs should always be rounding // in favor of the orderbook. - Uint128::from(3_000_000_000_000u128), + Uint128::from(3u128), OrderDirection::Bid, Addr::unchecked("buyer"), )), ], order_id: 0, - tick_id: MIN_TICK, expected_bank_msg: Some(SubMsg::reply_on_error( MsgSend256 { from_address: "cosmos2contract".to_string(), to_address: sender.to_string(), - amount: vec![coin_u256(Uint256::from(3_000_000_000_000u128), QUOTE_DENOM)], + amount: vec![coin_u256(Uint256::from(3u128), QUOTE_DENOM)], }, REPLY_ID_CLAIM, )), expected_bounty_msg: None, - expected_order_state: Some(LimitOrder::new( - MIN_TICK, - 0, - OrderDirection::Ask, - sender.clone(), - Uint128::from(7u128), - decimal256_from_u128(3u128), - None, - ).with_placed_quantity(10u128)), + expected_order_state: None, expected_error: None, }, // A tick id of 0 operates on a tick price of 1 @@ -2180,7 +2188,6 @@ fn test_claim_order() { )), ], order_id: 0, - tick_id: valid_tick_id, expected_bank_msg: Some(SubMsg::reply_on_error( MsgSend256 { @@ -2291,8 +2298,9 @@ fn test_claim_order() { None, )), OrderOperation::RunMarket(MarketOrder::new( - // Tick price is 2, 2*5 = 10 - Uint128::from(20u128), + // Filling 10/10 of the Bid order + // Tick price is 2, 10/2 = 5 + Uint128::from(5u128), OrderDirection::Ask, Addr::unchecked("buyer"), )), @@ -2304,7 +2312,7 @@ fn test_claim_order() { MsgSend256 { from_address: "cosmos2contract".to_string(), to_address: sender.to_string(), - amount: vec![coin_u256(Uint256::from(20u128), BASE_DENOM)], + amount: vec![coin_u256(Uint256::from(5u128), BASE_DENOM)], }, REPLY_ID_CLAIM, )), @@ -2326,19 +2334,20 @@ fn test_claim_order() { None, )), OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(10u128), + // Filling 4/10 of the Bid order + // Tick price is 2, 4/2 = 2 + Uint128::from(2u128), OrderDirection::Ask, Addr::unchecked("buyer"), )), ], order_id: 0, - tick_id: LARGE_POSITIVE_TICK, expected_bank_msg: Some(SubMsg::reply_on_error( MsgSend256 { from_address: "cosmos2contract".to_string(), to_address: sender.to_string(), - amount: vec![coin_u256(Uint256::from(10u128), BASE_DENOM)], + amount: vec![coin_u256(Uint256::from(2u128), BASE_DENOM)], }, REPLY_ID_CLAIM, )), @@ -2348,8 +2357,8 @@ fn test_claim_order() { 0, OrderDirection::Bid, sender.clone(), - Uint128::from(5u128), - decimal256_from_u128(5u128), + Uint128::from(6u128), + decimal256_from_u128(4u128), None, ).with_placed_quantity(10u128)), expected_error: None, @@ -2368,26 +2377,29 @@ fn test_claim_order() { None, )), OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(10u128), + // Filling 4/10 of the Bid Order + // Tick price is 2, 4/2 = 2 + Uint128::from(2u128), OrderDirection::Ask, Addr::unchecked("buyer"), )), // Claim the first partial fill OrderOperation::Claim((LARGE_POSITIVE_TICK, 0)), OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(10u128), + // Filling 6/10 of the Bid order (full fill) + // Tick price is 2, 6/2 = 3 + Uint128::from(3u128), OrderDirection::Ask, Addr::unchecked("buyer"), )), ], order_id: 0, - tick_id: LARGE_POSITIVE_TICK, expected_bank_msg: Some(SubMsg::reply_on_error( MsgSend256 { from_address: "cosmos2contract".to_string(), to_address: sender.to_string(), - amount: vec![coin_u256(Uint256::from(10u128), BASE_DENOM)], + amount: vec![coin_u256(Uint256::from(3u128), BASE_DENOM)], }, REPLY_ID_CLAIM, )), @@ -2410,19 +2422,20 @@ fn test_claim_order() { None, )), OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(50u128), + // Full filling the bid order + // Tick price is 0.5 so 100/0.5 = 200 + Uint128::from(200u128), OrderDirection::Ask, Addr::unchecked("buyer"), )), ], order_id: 0, - tick_id: LARGE_NEGATIVE_TICK, expected_bank_msg: Some(SubMsg::reply_on_error( MsgSend256 { from_address: "cosmos2contract".to_string(), to_address: sender.to_string(), - amount: vec![coin_u256(Uint256::from(50u128), BASE_DENOM)], + amount: vec![coin_u256(Uint256::from(200u128), BASE_DENOM)], }, REPLY_ID_CLAIM, )), @@ -2444,19 +2457,20 @@ fn test_claim_order() { None, )), OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(25u128), + // Filling 25/100 of the Bid order + // Tick price is 0.5 so 25/0.5 = 50 + Uint128::from(50u128), OrderDirection::Ask, Addr::unchecked("buyer"), )), ], order_id: 0, - tick_id: LARGE_NEGATIVE_TICK, expected_bank_msg: Some(SubMsg::reply_on_error( MsgSend256 { from_address: "cosmos2contract".to_string(), to_address: sender.to_string(), - amount: vec![coin_u256(Uint256::from(25u128), BASE_DENOM)], + amount: vec![coin_u256(Uint256::from(50u128), BASE_DENOM)], }, REPLY_ID_CLAIM, )), @@ -2466,8 +2480,8 @@ fn test_claim_order() { 0, OrderDirection::Bid, sender.clone(), - Uint128::from(50u128), - decimal256_from_u128(50u128), + Uint128::from(75u128), + decimal256_from_u128(25u128), None, ).with_placed_quantity(100u128)), expected_error: None, @@ -2486,26 +2500,29 @@ fn test_claim_order() { None, )), OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(25u128), + // Filling 50/100 of the Bid order + // Tick price is 0.5 so 50/0.5 = 100 + Uint128::from(100u128), OrderDirection::Ask, Addr::unchecked("buyer"), )), // Claim the first partial fill OrderOperation::Claim((LARGE_NEGATIVE_TICK, 0)), OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(25u128), + // Filling 50/100 of the Bid order (full fill) + // Tick price is 0.5 so 50/0.5 = 100 + Uint128::from(100u128), OrderDirection::Ask, Addr::unchecked("buyer"), )), ], order_id: 0, - tick_id: LARGE_NEGATIVE_TICK, expected_bank_msg: Some(SubMsg::reply_on_error( MsgSend256 { from_address: "cosmos2contract".to_string(), to_address: sender.to_string(), - amount: vec![coin_u256(Uint256::from(25u128), BASE_DENOM)], + amount: vec![coin_u256(Uint256::from(100u128), BASE_DENOM)], }, REPLY_ID_CLAIM, )), @@ -2543,7 +2560,6 @@ fn test_claim_order() { )), ], order_id: 1, - tick_id: valid_tick_id, expected_bank_msg: Some(SubMsg::reply_on_error( MsgSend256 { @@ -2772,7 +2788,7 @@ fn test_claim_order() { OrderOperation::PlaceLimit(LimitOrder::new( LARGE_NEGATIVE_TICK, 0, - OrderDirection::Bid, + OrderDirection::Ask, sender.clone(), Uint128::from(1u128), Decimal256::zero(), @@ -2781,7 +2797,7 @@ fn test_claim_order() { OrderOperation::PlaceLimit(LimitOrder::new( LARGE_NEGATIVE_TICK, 1, - OrderDirection::Bid, + OrderDirection::Ask, sender.clone(), Uint128::from(1u128), Decimal256::zero(), @@ -2789,7 +2805,7 @@ fn test_claim_order() { )), OrderOperation::RunMarket(MarketOrder::new( Uint128::from(1u128), - OrderDirection::Ask, + OrderDirection::Bid, sender.clone(), )), ], @@ -2813,7 +2829,6 @@ fn test_claim_order() { BASE_DENOM.to_string(), ) .unwrap(); - // Run setup operations for operation in test.operations { operation @@ -3621,7 +3636,7 @@ fn test_batch_claim_order() { owner.clone(), )), OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(50u128), + Uint128::from(51u128), OrderDirection::Bid, owner.clone(), )), @@ -3649,7 +3664,7 @@ fn test_batch_claim_order() { MsgSend256 { from_address: "cosmos2contract".to_string(), to_address: owner.to_string(), - amount: vec![coin_u256(49u128, QUOTE_DENOM)], + amount: vec![coin_u256(50u128, QUOTE_DENOM)], }, REPLY_ID_CLAIM, ), @@ -3697,7 +3712,7 @@ fn test_batch_claim_order() { owner.clone(), )), OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(50u128), + Uint128::from(51u128), OrderDirection::Bid, owner.clone(), )), @@ -3717,7 +3732,7 @@ fn test_batch_claim_order() { MsgSend256 { from_address: "cosmos2contract".to_string(), to_address: owner.to_string(), - amount: vec![coin_u256(49u128, QUOTE_DENOM)], + amount: vec![coin_u256(50u128, QUOTE_DENOM)], }, REPLY_ID_CLAIM, ), @@ -3941,15 +3956,15 @@ fn test_directional_liquidity() { Decimal256::zero(), None, )), - // Filling Ask at 0.5 price = 100 units of opposite denom + // Partial filling Bid at 2 price = 50 units of opposite denom OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(200u128), + Uint128::from(50u128), OrderDirection::Ask, sender.clone(), )), - // Filling Bid at 0.5 price = 100 units of opposite denom + // Filling Ask at 2 price = 200 units of opposite denom OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(50u128), + Uint128::from(200u128), OrderDirection::Bid, sender.clone(), )), @@ -3980,21 +3995,21 @@ fn test_directional_liquidity() { Decimal256::zero(), None, )), - // Filling Ask at 0.5 price = 200 units of opposite denom + // Filling Bid at 0.5 price = 400 units of opposite denom OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(100u128), + Uint128::from(400u128), OrderDirection::Ask, sender.clone(), )), - // Filling Bid at 0.5 price = 25 units of opposite denom + // Partial flling Ask at 0.5 price = 25 units of opposite denom OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(50u128), + Uint128::from(25u128), OrderDirection::Bid, sender, )), ], expected_liquidity: ( - (OrderDirection::Ask, decimal256_from_u128(75u128)), + (OrderDirection::Ask, decimal256_from_u128(50u128)), (OrderDirection::Bid, decimal256_from_u128(0u128)), ), }, @@ -4223,10 +4238,8 @@ fn test_maker_fee() { assert_eq!(maker_fee_msg, None, "{}", format_test_name(test.name)); } } - } - #[test] fn test_cancelled_orders() { let mut deps = mock_dependencies_custom(); diff --git a/contracts/sumtree-orderbook/src/tests/test_query.rs b/contracts/sumtree-orderbook/src/tests/test_query.rs index 5faba40..09806be 100644 --- a/contracts/sumtree-orderbook/src/tests/test_query.rs +++ b/contracts/sumtree-orderbook/src/tests/test_query.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use cosmwasm_std::{ coin, testing::{mock_env, mock_info}, - Addr, Coin, Decimal, Decimal256, Uint128, + Addr, Coin, Decimal, Decimal256, Fraction, Uint128, }; use crate::{ @@ -137,14 +137,14 @@ fn test_query_spot_price() { None, )), OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(2u128), + Uint128::from(3u128), OrderDirection::Bid, sender.clone(), )), ], base_denom: QUOTE_DENOM.to_string(), quote_denom: BASE_DENOM.to_string(), - expected_price: Decimal::percent(200), + expected_price: Decimal::percent(200).inv().unwrap(), expected_error: None, }, SpotPriceTestCase { @@ -160,7 +160,7 @@ fn test_query_spot_price() { ))], base_denom: QUOTE_DENOM.to_string(), quote_denom: BASE_DENOM.to_string(), - expected_price: Decimal::from_ratio(340282300000000000000u128, 1u128), + expected_price: Decimal::from_ratio(1u128, 340282300000000000000u128), expected_error: None, }, SpotPriceTestCase { @@ -176,7 +176,7 @@ fn test_query_spot_price() { ))], base_denom: QUOTE_DENOM.to_string(), quote_denom: BASE_DENOM.to_string(), - expected_price: Decimal::from_str("0.000000000001").unwrap(), + expected_price: Decimal::from_str("1000000000000").unwrap(), expected_error: None, }, SpotPriceTestCase { @@ -280,14 +280,14 @@ fn test_query_spot_price() { None, )), OrderOperation::RunMarket(MarketOrder::new( - Uint128::from(2u128), + Uint128::from(3u128), OrderDirection::Ask, sender.clone(), )), ], base_denom: BASE_DENOM.to_string(), quote_denom: QUOTE_DENOM.to_string(), - expected_price: Decimal::percent(200), + expected_price: Decimal::percent(50), expected_error: None, }, SpotPriceTestCase { @@ -310,7 +310,7 @@ fn test_query_spot_price() { ], base_denom: BASE_DENOM.to_string(), quote_denom: QUOTE_DENOM.to_string(), - expected_price: Decimal::percent(50), + expected_price: Decimal::percent(200), expected_error: None, }, SpotPriceTestCase { @@ -328,7 +328,7 @@ fn test_query_spot_price() { quote_denom: QUOTE_DENOM.to_string(), // At max tick the price is 2.9387365e-21 which is outside the range of the `Decimal` type // As such the returned price is zero - expected_price: Decimal::zero(), + expected_price: Decimal::from_str("340282300000000000000").unwrap(), expected_error: None, }, SpotPriceTestCase { @@ -344,7 +344,7 @@ fn test_query_spot_price() { ))], base_denom: BASE_DENOM.to_string(), quote_denom: QUOTE_DENOM.to_string(), - expected_price: Decimal::from_ratio(1000000000000u128, 1u128), + expected_price: Decimal::from_ratio(1u128, 1000000000000u128), expected_error: None, }, SpotPriceTestCase { @@ -387,7 +387,6 @@ fn test_query_spot_price() { let mut deps = mock_dependencies_custom(); let env = mock_env(); let info = mock_info(sender.as_str(), &[]); - create_orderbook( deps.as_mut(), QUOTE_DENOM.to_string(), @@ -517,8 +516,8 @@ fn test_calc_out_amount_given_in() { token_in: coin(150, QUOTE_DENOM), token_out_denom: BASE_DENOM, swap_fee: EXPECTED_SWAP_FEE, - // Output: 100*1 (tick: 0) + 50*2 (tick: LARGE_POSITIVE_TICK) = 200 - expected_output: coin_u256(200u128, BASE_DENOM), + // Output: 100*1 (tick: 0) + 50/2 (tick: LARGE_POSITIVE_TICK) = 125 + expected_output: coin_u256(125u128, BASE_DENOM), expected_error: None, }, CalcOutAmountGivenInTestCase { @@ -572,7 +571,7 @@ fn test_calc_out_amount_given_in() { 0, OrderDirection::Bid, sender.clone(), - Uint128::from(25u128), + Uint128::from(100u128), Decimal256::percent(0), None, )), @@ -598,8 +597,8 @@ fn test_calc_out_amount_given_in() { token_in: coin(150, BASE_DENOM), token_out_denom: QUOTE_DENOM, swap_fee: EXPECTED_SWAP_FEE, - // Output: 25 at 0.5 tick price + 100 at 1 tick price = 125 - expected_output: coin_u256(125u128, QUOTE_DENOM), + // Output: 50/0.5 at LARGE_POSITIVE_TICK tick price + 100 at 1 tick price = 200 + expected_output: coin_u256(200u128, QUOTE_DENOM), expected_error: None, }, CalcOutAmountGivenInTestCase { @@ -1247,6 +1246,7 @@ struct OrdersByOwnerTestCase { name: &'static str, pre_operations: Vec, expected_output: Vec, + expected_count: u64, owner: Addr, start_from: Option<(i64, u64)>, end_at: Option<(i64, u64)>, @@ -1264,6 +1264,7 @@ fn test_orders_by_owner() { name: "no orders", pre_operations: vec![], expected_output: vec![], + expected_count: 0, owner: Addr::unchecked("sender"), start_from: None, end_at: None, @@ -1290,6 +1291,7 @@ fn test_orders_by_owner() { Decimal256::zero(), None, )], + expected_count: 1, owner: Addr::unchecked("sender"), start_from: None, end_at: None, @@ -1327,6 +1329,7 @@ fn test_orders_by_owner() { Decimal256::zero(), None, )], + expected_count: 2, owner: Addr::unchecked("sender"), start_from: None, end_at: None, @@ -1364,8 +1367,9 @@ fn test_orders_by_owner() { Decimal256::zero(), None, )], + expected_count: 2, owner: Addr::unchecked("sender"), - start_from: Some((0, 0)), + start_from: Some((0, 1)), end_at: None, limit: None, expected_error: None, @@ -1401,6 +1405,7 @@ fn test_orders_by_owner() { Decimal256::zero(), None, )], + expected_count: 2, owner: Addr::unchecked("sender"), start_from: None, end_at: Some((0, 0)), @@ -1477,6 +1482,7 @@ fn test_orders_by_owner() { None, ), ], + expected_count: 3, owner: Addr::unchecked("sender"), start_from: None, end_at: None, @@ -1531,7 +1537,7 @@ fn test_orders_by_owner() { ); }); assert_eq!( - res, + res.orders, test.expected_output .iter() .map(|o| o.clone().with_placed_at(env.block.time)) @@ -1539,6 +1545,11 @@ fn test_orders_by_owner() { "{}: output did not match", test.name ); + assert_eq!( + res.count, test.expected_count, + "{}: count did not match", + test.name + ); } } diff --git a/contracts/sumtree-orderbook/src/tests/test_state.rs b/contracts/sumtree-orderbook/src/tests/test_state.rs index ff4a71d..2c3d4cc 100644 --- a/contracts/sumtree-orderbook/src/tests/test_state.rs +++ b/contracts/sumtree-orderbook/src/tests/test_state.rs @@ -242,7 +242,7 @@ fn test_get_orders_by_owner_with_pagination() { start_after = Some( owner_orders .last() - .map(|order| (order.tick_id, order.order_id)) + .map(|order| (order.tick_id, order.order_id + 1)) .unwrap(), ); } diff --git a/contracts/sumtree-orderbook/src/tests/test_utils.rs b/contracts/sumtree-orderbook/src/tests/test_utils.rs index 157ec14..6df60e7 100644 --- a/contracts/sumtree-orderbook/src/tests/test_utils.rs +++ b/contracts/sumtree-orderbook/src/tests/test_utils.rs @@ -5,7 +5,7 @@ use cosmwasm_std::{ use crate::{ constants::{MAX_TICK, MIN_TICK}, error::ContractResult, - order::{cancel_limit, claim_order, place_limit, run_market_order}, + order::{cancel_limit, claim_limit, place_limit, run_market_order}, state::orders, types::{LimitOrder, MarketOrder, OrderDirection}, }; @@ -33,8 +33,7 @@ impl OrderOperation { OrderDirection::Bid => MAX_TICK, OrderDirection::Ask => MIN_TICK, }; - run_market_order(deps.storage, env.contract.address, &mut order, tick_bound) - .unwrap(); + run_market_order(deps.storage, env.contract.address, &mut order, tick_bound)?; Ok(()) } OrderOperation::PlaceLimitMulti(( @@ -49,7 +48,7 @@ impl OrderOperation { quantity_per_order, direction, ); - place_multiple_limit_orders(&mut deps, env, info.sender.as_str(), orders).unwrap(); + place_multiple_limit_orders(&mut deps, env, info.sender.as_str(), orders)?; Ok(()) } OrderOperation::PlaceLimit(limit_order) => { @@ -73,13 +72,7 @@ impl OrderOperation { Ok(()) } OrderOperation::Claim((tick_id, order_id)) => { - claim_order( - deps.storage, - info.sender.clone(), - env.contract.address, - tick_id, - order_id, - )?; + claim_limit(deps, env, info, tick_id, order_id)?; Ok(()) } OrderOperation::Cancel((tick_id, order_id)) => { diff --git a/contracts/sumtree-orderbook/src/tick.rs b/contracts/sumtree-orderbook/src/tick.rs index e5b92a1..89df3f7 100644 --- a/contracts/sumtree-orderbook/src/tick.rs +++ b/contracts/sumtree-orderbook/src/tick.rs @@ -91,8 +91,8 @@ pub fn sync_tick( } // Write updated tick values to state - tick_state.set_values(OrderDirection::Bid, bid_values); - tick_state.set_values(OrderDirection::Ask, ask_values); + tick_state.set_values(OrderDirection::Bid, bid_values.clone()); + tick_state.set_values(OrderDirection::Ask, ask_values.clone()); TICK_STATE.save(storage, tick_id, &tick_state)?; Ok(()) diff --git a/contracts/sumtree-orderbook/src/tick_math.rs b/contracts/sumtree-orderbook/src/tick_math.rs index 15f05e4..06db154 100644 --- a/contracts/sumtree-orderbook/src/tick_math.rs +++ b/contracts/sumtree-orderbook/src/tick_math.rs @@ -146,7 +146,7 @@ pub fn amount_to_value( return Ok(Uint256::zero()); } match order { - OrderDirection::Bid => multiply_by_price(amount, price, rounding_direction), - OrderDirection::Ask => divide_by_price(amount, price, rounding_direction), + OrderDirection::Bid => divide_by_price(amount, price, rounding_direction), + OrderDirection::Ask => multiply_by_price(amount, price, rounding_direction), } }