diff --git a/Cargo.lock b/Cargo.lock index 8d70558..ade137e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,6 +34,12 @@ version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "autocfg" version = "1.3.0" @@ -51,16 +57,10 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object 0.36.1", + "object", "rustc-demangle", ] -[[package]] -name = "base16ct" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" - [[package]] name = "base16ct" version = "0.2.0" @@ -69,9 +69,9 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64" -version = "0.13.1" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64ct" @@ -85,6 +85,12 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "bech32" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" + [[package]] name = "bitflags" version = "1.3.2" @@ -127,6 +133,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56953345e39537a3e18bdaeba4cb0c58a78c1f61f361dc0fa7c5c7340ae87c5f" + [[package]] name = "bumpalo" version = "3.16.0" @@ -181,9 +193,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "cbd0f76e066e64fdc5631e3bb46381254deab9ef1158292f27c8c57e3bf3fe59" [[package]] name = "const-oid" @@ -212,14 +224,14 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.5.2" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6aa9f904de106fa16443ad14ec2abe75e94ba003bb61c681c0e43d4c58d2a" +checksum = "dd50718a2b6830ce9eb5d465de5a018a12e71729d66b70807ce97e6dd14f931d" dependencies = [ "digest 0.10.7", - "ecdsa 0.16.9", + "ecdsa", "ed25519-zebra", - "k256 0.13.3", + "k256", "rand_core 0.6.4", "thiserror", ] @@ -235,9 +247,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema" -version = "1.2.8" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0864db0a1e0ba2abd35d1105d0487e779538f616daa0338697315cc1601bf24" +checksum = "7879036156092ad1c22fe0d7316efc5a5eceec2bc3906462a2560215f2a2f929" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -248,9 +260,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.8" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cd0f686ff907612ed63fdff97906b3eafe169d560ebd428e92ef400fb56479f" +checksum = "0bb57855fbfc83327f8445ae0d413b1a05ac0d68c396ab4d122b2abd7bb82cb6" dependencies = [ "proc-macro2", "quote", @@ -259,11 +271,13 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.8" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197ed654c41885e2d76b23bff8db11b0f9745bfdede0909de9daa564edb19187" +checksum = "78c1556156fdf892a55cced6115968b961eaaadd6f724a2c2cb7d1e168e32dd3" dependencies = [ "base64", + "bech32 0.9.1", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -273,15 +287,15 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror", - "uint", ] [[package]] name = "cosmwasm-storage" -version = "1.2.8" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a2997bbb57c24c7ca9f745799c8839c08b7cde0be3f6c43127d25a7f03f2fe" +checksum = "66de2ab9db04757bcedef2b5984fbe536903ada4a8a9766717a4a71197ef34f6" dependencies = [ "cosmwasm-std", "serde", @@ -290,9 +304,10 @@ dependencies = [ [[package]] name = "cosmwasm-testing-util" version = "0.1.0" -source = "git+https://github.com/oraichain/cosmwasm-testing-util.git?rev=aaa09cf#aaa09cfe66da9e32dbc0627be575ff20e34a8e3a" +source = "git+https://github.com/oraichain/cosmwasm-testing-util.git?rev=1b9c412#1b9c4126e8b5e19dc547f992a1a5cd9dde3d1b0d" dependencies = [ - "bech32", + "anyhow", + "bech32 0.9.1", "cosmwasm-crypto", "cosmwasm-schema", "cosmwasm-std", @@ -307,19 +322,20 @@ dependencies = [ [[package]] name = "cosmwasm-vm" -version = "1.2.8" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613a3a96581b476b0b0a2fead0c970f9e8c844b7aa41bc45648ebcaa84d6b8ab" +checksum = "9897b46059d68ade608fbdcec33e02b0de0ca4b32ee787a83b8a3eb31b754c85" dependencies = [ "bitflags 1.3.2", "bytecheck", + "bytes", "clru", "cosmwasm-crypto", "cosmwasm-std", + "crc32fast", + "derivative", "enumset", "hex", - "loupe", - "parity-wasm", "schemars", "serde", "serde_json", @@ -340,56 +356,74 @@ dependencies = [ [[package]] name = "cranelift-bforest" -version = "0.82.3" +version = "0.91.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38faa2a16616c8e78a18d37b4726b98bfd2de192f2fdc8a39ddf568a408a0f75" +checksum = "2a2ab4512dfd3a6f4be184403a195f76e81a8a9f9e6c898e19d2dc3ce20e0115" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.82.3" +version = "0.91.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26f192472a3ba23860afd07d2b0217dc628f21fcc72617aa1336d98e1671f33b" +checksum = "98b022ed2a5913a38839dfbafe6cf135342661293b08049843362df4301261dc" dependencies = [ + "arrayvec", + "bumpalo", "cranelift-bforest", "cranelift-codegen-meta", "cranelift-codegen-shared", + "cranelift-egraph", "cranelift-entity", + "cranelift-isle", "gimli 0.26.2", "log", - "regalloc", + "regalloc2", "smallvec", "target-lexicon", ] [[package]] name = "cranelift-codegen-meta" -version = "0.82.3" +version = "0.91.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32ddb89e9b89d3d9b36a5b7d7ea3261c98235a76ac95ba46826b8ec40b1a24" +checksum = "639307b45434ad112a98f8300c0f0ab085cbefcd767efcdef9ef19d4c0756e74" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.82.3" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "278e52e29c53fcf32431ef08406c295699a70306d05a0715c5b1bf50e33a9ab7" + +[[package]] +name = "cranelift-egraph" +version = "0.91.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01fd0d9f288cc1b42d9333b7a776b17e278fc888c28e6a0f09b5573d45a150bc" +checksum = "624b54323b06e675293939311943ba82d323bb340468ce1889be5da7932c8d73" +dependencies = [ + "cranelift-entity", + "fxhash", + "hashbrown 0.12.3", + "indexmap", + "log", + "smallvec", +] [[package]] name = "cranelift-entity" -version = "0.82.3" +version = "0.91.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3bfe172b83167604601faf9dc60453e0d0a93415b57a9c4d1a7ae6849185cf" +checksum = "9a59bcbca89c3f1b70b93ab3cbba5e5e0cbf3e63dadb23c7525cb142e21a9d4c" [[package]] name = "cranelift-frontend" -version = "0.82.3" +version = "0.91.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a006e3e32d80ce0e4ba7f1f9ddf66066d052a8c884a110b91d05404d6ce26dce" +checksum = "0d70abacb8cfef3dc8ff7e8836e9c1d70f7967dfdac824a4cd5e30223415aca6" dependencies = [ "cranelift-codegen", "log", @@ -397,6 +431,12 @@ dependencies = [ "target-lexicon", ] +[[package]] +name = "cranelift-isle" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "393bc73c451830ff8dbb3a07f61843d6cb41a084f9996319917c0b291ed785bb" + [[package]] name = "crc32fast" version = "1.4.2" @@ -425,6 +465,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.20" @@ -437,18 +486,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -[[package]] -name = "crypto-bigint" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - [[package]] name = "crypto-bigint" version = "0.5.5" @@ -486,69 +523,44 @@ dependencies = [ [[package]] name = "cw-multi-test" -version = "0.16.6" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3a9d62dba95ab262438dfd403bffdca647c6615ef1aa431cdc94c262b09643a" +checksum = "91fc33b1d65c102d72f46548c64dca423c337e528d6747d0c595316aa65f887b" dependencies = [ "anyhow", + "bech32 0.11.0", "cosmwasm-std", - "cw-storage-plus 1.1.0", - "cw-utils 1.0.1", + "cw-storage-plus", + "cw-utils", "derivative", - "itertools 0.10.5", - "k256 0.11.6", + "itertools 0.13.0", "prost", "schemars", "serde", + "sha2 0.10.8", "thiserror", ] [[package]] name = "cw-storage-plus" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b6f91c0b94481a3e9ef1ceb183c37d00764f8751e39b45fc09f4d9b970d469" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", -] - -[[package]] -name = "cw-storage-plus" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f0e92a069d62067f3472c62e30adedb4cab1754725c0f2a682b3128d2bf3c79" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", -] - -[[package]] -name = "cw-utils" -version = "0.16.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6a84c6c1c0acc3616398eba50783934bd6c964bad6974241eaee3460c8f5b26" +checksum = "d5ff29294ee99373e2cd5fd21786a3c0ced99a52fec2ca347d565489c61b723c" dependencies = [ - "cosmwasm-schema", "cosmwasm-std", - "cw2 0.16.0", "schemars", - "semver", "serde", - "thiserror", ] [[package]] name = "cw-utils" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c80e93d1deccb8588db03945016a292c3c631e6325d349ebb35d2db6f4f946f7" +checksum = "1c4a657e5caacc3a0d00ee96ca8618745d050b8f757c709babafb81208d4239c" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw2 1.1.0", + "cw2", "schemars", "semver", "serde", @@ -557,27 +569,15 @@ dependencies = [ [[package]] name = "cw2" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91398113b806f4d2a8d5f8d05684704a20ffd5968bf87e3473e1973710b884ad" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.16.0", - "schemars", - "serde", -] - -[[package]] -name = "cw2" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ac2dc7a55ad64173ca1e0a46697c31b7a5c51342f55a1e84a724da4eb99908" +checksum = "c6c120b24fbbf5c3bedebb97f2cc85fbfa1c3287e09223428e7e597b5293c1fa" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus 1.1.0", + "cw-storage-plus", "schemars", + "semver", "serde", "thiserror", ] @@ -590,7 +590,7 @@ checksum = "011c45920f8200bd5d32d4fe52502506f64f2f75651ab408054d4cfc75ca3a9b" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-utils 1.0.1", + "cw-utils", "schemars", "serde", ] @@ -603,9 +603,9 @@ checksum = "0b3ad456059901a36cfa68b596d85d579c3df2b797dae9950dc34c27e14e995f" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus 1.1.0", - "cw-utils 1.0.1", - "cw2 1.1.0", + "cw-storage-plus", + "cw-utils", + "cw2", "cw20", "schemars", "semver", @@ -647,9 +647,22 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "decimal" -version = "0.1.0" +version = "0.2.0" dependencies = [ "decimal-core", "integer-sqrt", @@ -659,7 +672,7 @@ dependencies = [ [[package]] name = "decimal-core" -version = "0.1.0" +version = "0.2.0" dependencies = [ "proc-macro2", "quote", @@ -667,16 +680,6 @@ dependencies = [ "syn 2.0.66", ] -[[package]] -name = "der" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" -dependencies = [ - "const-oid", - "zeroize", -] - [[package]] name = "der" version = "0.7.9" @@ -761,19 +764,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", -] - -[[package]] -name = "ecdsa" -version = "0.14.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" -dependencies = [ - "der 0.6.1", - "elliptic-curve 0.12.3", - "rfc6979 0.3.1", - "signature 1.6.4", + "memmap2 0.5.10", ] [[package]] @@ -782,12 +773,12 @@ version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der 0.7.9", + "der", "digest 0.10.7", - "elliptic-curve 0.13.8", - "rfc6979 0.4.0", - "signature 2.2.0", - "spki 0.7.3", + "elliptic-curve", + "rfc6979", + "signature", + "spki", ] [[package]] @@ -811,41 +802,21 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" -[[package]] -name = "elliptic-curve" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" -dependencies = [ - "base16ct 0.1.1", - "crypto-bigint 0.4.9", - "der 0.6.1", - "digest 0.10.7", - "ff 0.12.1", - "generic-array", - "group 0.12.1", - "pkcs8 0.9.0", - "rand_core 0.6.4", - "sec1 0.3.0", - "subtle", - "zeroize", -] - [[package]] name = "elliptic-curve" version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ - "base16ct 0.2.0", - "crypto-bigint 0.5.5", + "base16ct", + "crypto-bigint", "digest 0.10.7", - "ff 0.13.0", + "ff", "generic-array", - "group 0.13.0", - "pkcs8 0.10.2", + "group", + "pkcs8", "rand_core 0.6.4", - "sec1 0.7.3", + "sec1", "subtle", "zeroize", ] @@ -891,38 +862,12 @@ dependencies = [ "syn 2.0.66", ] -[[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 = "fallible-iterator" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" -[[package]] -name = "fastrand" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" - -[[package]] -name = "ff" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "ff" version = "0.13.0" @@ -939,6 +884,15 @@ 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" @@ -951,6 +905,15 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1003,45 +966,31 @@ dependencies = [ "web-sys", ] -[[package]] -name = "group" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" -dependencies = [ - "ff 0.12.1", - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "group" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ - "ff 0.13.0", + "ff", "rand_core 0.6.4", "subtle", ] [[package]] name = "hashbrown" -version = "0.11.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ "ahash", ] [[package]] name = "hashbrown" -version = "0.12.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash", -] +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hex" @@ -1058,21 +1007,22 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -1081,7 +1031,6 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", - "serde", ] [[package]] @@ -1095,18 +1044,18 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.5" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" dependencies = [ "either", ] [[package]] name = "itertools" -version = "0.11.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] @@ -1128,28 +1077,16 @@ dependencies = [ [[package]] name = "k256" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" -dependencies = [ - "cfg-if", - "ecdsa 0.14.8", - "elliptic-curve 0.12.3", - "sha2 0.10.8", -] - -[[package]] -name = "k256" -version = "0.13.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ "cfg-if", - "ecdsa 0.16.9", - "elliptic-curve 0.13.8", + "ecdsa", + "elliptic-curve", "once_cell", "sha2 0.10.8", - "signature 2.2.0", + "signature", ] [[package]] @@ -1171,48 +1108,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] -name = "libloading" -version = "0.7.4" +name = "lock_api" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ - "cfg-if", - "winapi", + "autocfg", + "scopeguard", ] -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - [[package]] name = "log" version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" -[[package]] -name = "loupe" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6a72dfa44fe15b5e76b94307eeb2ff995a8c5b283b55008940c02e0c5b634d" -dependencies = [ - "indexmap", - "loupe-derive", - "rustversion", -] - -[[package]] -name = "loupe-derive" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fbfc88337168279f2e9ae06e157cfed4efd3316e14dc96ed074d4f2e6c5952" -dependencies = [ - "quote", - "syn 1.0.109", -] - [[package]] name = "mach" version = "0.3.2" @@ -1246,11 +1156,20 @@ dependencies = [ "libc", ] +[[package]] +name = "memmap2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" -version = "0.6.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" dependencies = [ "autocfg", ] @@ -1279,18 +1198,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "object" -version = "0.28.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" -dependencies = [ - "crc32fast", - "hashbrown 0.11.2", - "indexmap", - "memchr", -] - [[package]] name = "object" version = "0.36.1" @@ -1314,16 +1221,14 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "oraiswap-v3" -version = "0.1.0" +version = "0.2.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cosmwasm-storage", "cosmwasm-testing-util", - "cw-multi-test", - "cw-storage-plus 1.1.0", - "cw-utils 0.16.0", - "cw2 1.1.0", + "cw-storage-plus", + "cw2", "cw20", "cw20-base", "decimal", @@ -1333,24 +1238,31 @@ dependencies = [ [[package]] name = "oraiswap-v3-wasm" -version = "0.1.0" +version = "0.2.0" dependencies = [ "decimal", "derive_more", "js-sys", "paste", "serde", - "serde-wasm-bindgen", + "serde-wasm-bindgen 0.6.5", "traceable_result", "tsify", "wasm-bindgen", ] [[package]] -name = "parity-wasm" -version = "0.42.2" +name = "parking_lot_core" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] [[package]] name = "paste" @@ -1359,20 +1271,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] -name = "pin-project-lite" -version = "0.2.14" +name = "percent-encoding" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] -name = "pkcs8" -version = "0.9.0" +name = "pin-project-lite" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" -dependencies = [ - "der 0.6.1", - "spki 0.6.0", -] +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pkcs8" @@ -1380,8 +1288,8 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.9", - "spki 0.7.3", + "der", + "spki", ] [[package]] @@ -1419,9 +1327,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.9.0" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" dependencies = [ "bytes", "prost-derive", @@ -1429,15 +1337,15 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.9.0" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.11.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.66", ] [[package]] @@ -1511,13 +1419,23 @@ dependencies = [ ] [[package]] -name = "regalloc" -version = "0.0.34" +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "regalloc2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62446b1d3ebf980bdc68837700af1d77b37bc430e524bf95319c6eada2a4cc02" +checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c" dependencies = [ + "fxhash", "log", - "rustc-hash", + "slice-group-by", "smallvec", ] @@ -1567,17 +1485,6 @@ dependencies = [ "bytecheck", ] -[[package]] -name = "rfc6979" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" -dependencies = [ - "crypto-bigint 0.4.9", - "hmac", - "zeroize", -] - [[package]] name = "rfc6979" version = "0.4.0" @@ -1598,6 +1505,7 @@ dependencies = [ "bytecheck", "bytes", "hashbrown 0.12.3", + "indexmap", "ptr_meta", "rend", "rkyv_derive", @@ -1623,12 +1531,6 @@ 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 = "rustc_version" version = "0.4.0" @@ -1638,25 +1540,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustix" -version = "0.38.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" -dependencies = [ - "bitflags 2.6.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustversion" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" - [[package]] name = "ryu" version = "1.0.18" @@ -1701,31 +1584,23 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" [[package]] name = "sec1" -version = "0.3.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ - "base16ct 0.1.1", - "der 0.6.1", + "base16ct", + "der", "generic-array", - "pkcs8 0.9.0", + "pkcs8", "subtle", "zeroize", ] [[package]] -name = "sec1" -version = "0.7.3" +name = "self_cell" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct 0.2.0", - "der 0.7.9", - "generic-array", - "pkcs8 0.10.2", - "subtle", - "zeroize", -] +checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" [[package]] name = "semver" @@ -1753,9 +1628,9 @@ dependencies = [ [[package]] name = "serde-wasm-bindgen" -version = "0.6.5" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" +checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf" dependencies = [ "js-sys", "serde", @@ -1763,12 +1638,14 @@ dependencies = [ ] [[package]] -name = "serde_bytes" -version = "0.11.15" +name = "serde-wasm-bindgen" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" dependencies = [ + "js-sys", "serde", + "wasm-bindgen", ] [[package]] @@ -1840,13 +1717,13 @@ dependencies = [ ] [[package]] -name = "signature" -version = "1.6.4" +name = "shared-buffer" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +checksum = "f6c99835bad52957e7aa241d3975ed17c1e5f8c92026377d117a606f36b84b16" dependencies = [ - "digest 0.10.7", - "rand_core 0.6.4", + "bytes", + "memmap2 0.6.2", ] [[package]] @@ -1866,20 +1743,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" [[package]] -name = "smallvec" -version = "1.13.2" +name = "slice-group-by" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" [[package]] -name = "spki" -version = "0.6.0" +name = "smallvec" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" -dependencies = [ - "base64ct", - "der 0.6.1", -] +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "spki" @@ -1888,7 +1761,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der 0.7.9", + "der", ] [[package]] @@ -1943,18 +1816,6 @@ version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4873307b7c257eddcb50c9bedf158eb669578359fb28428bef438fec8e6ba7c2" -[[package]] -name = "tempfile" -version = "3.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" -dependencies = [ - "cfg-if", - "fastrand", - "rustix", - "windows-sys 0.52.0", -] - [[package]] name = "thiserror" version = "1.0.61" @@ -1993,7 +1854,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "token-bindings" version = "0.11.0" -source = "git+https://github.com/oraichain/token-bindings.git?rev=e85d734#e85d7348b07c3d0403c60435c0a7d291af81f987" +source = "git+https://github.com/oraichain/token-bindings.git?rev=7861576#78615761c7c0eb8ff8ebe64e26f70d20fec5b4e2" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -2004,12 +1865,12 @@ dependencies = [ [[package]] name = "token-bindings-test" version = "0.11.0" -source = "git+https://github.com/oraichain/token-bindings.git?rev=e85d734#e85d7348b07c3d0403c60435c0a7d291af81f987" +source = "git+https://github.com/oraichain/token-bindings.git?rev=7861576#78615761c7c0eb8ff8ebe64e26f70d20fec5b4e2" dependencies = [ "anyhow", "cosmwasm-std", "cw-multi-test", - "cw-storage-plus 0.16.0", + "cw-storage-plus", "itertools 0.11.0", "schemars", "serde", @@ -2020,13 +1881,13 @@ dependencies = [ [[package]] name = "tokenfactory" version = "0.11.0" -source = "git+https://github.com/oraichain/token-bindings.git?rev=e85d734#e85d7348b07c3d0403c60435c0a7d291af81f987" +source = "git+https://github.com/oraichain/token-bindings.git?rev=7861576#78615761c7c0eb8ff8ebe64e26f70d20fec5b4e2" dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cosmwasm-storage", - "cw-storage-plus 0.16.0", - "cw2 1.1.0", + "cw-storage-plus", + "cw2", "schemars", "serde", "thiserror", @@ -2046,7 +1907,6 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2115,12 +1975,38 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "uuid" version = "1.10.0" @@ -2164,6 +2050,29 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-downcast" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dac026d43bcca6e7ce1c0956ba68f59edf6403e8e930a5d891be72c31a44340" +dependencies = [ + "js-sys", + "once_cell", + "wasm-bindgen", + "wasm-bindgen-downcast-macros", +] + +[[package]] +name = "wasm-bindgen-downcast-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5020cfa87c7cecefef118055d44e3c1fc122c7ec25701d528ee458a0b45f38f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.92" @@ -2195,73 +2104,70 @@ checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasmer" -version = "2.3.0" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea8d8361c9d006ea3d7797de7bd6b1492ffd0f91a22430cfda6c1658ad57bedf" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ + "bytes", "cfg-if", + "derivative", "indexmap", "js-sys", - "loupe", "more-asserts", + "rustc-demangle", + "serde", + "serde-wasm-bindgen 0.4.5", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", - "wasmer-artifact", + "wasm-bindgen-downcast", "wasmer-compiler", "wasmer-compiler-cranelift", "wasmer-compiler-singlepass", "wasmer-derive", - "wasmer-engine", - "wasmer-engine-dylib", - "wasmer-engine-universal", "wasmer-types", "wasmer-vm", "winapi", ] -[[package]] -name = "wasmer-artifact" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aaf9428c29c1d8ad2ac0e45889ba8a568a835e33fd058964e5e500f2f7ce325" -dependencies = [ - "enumset", - "loupe", - "thiserror", - "wasmer-compiler", - "wasmer-types", -] - [[package]] name = "wasmer-compiler" -version = "2.3.0" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67a6cd866aed456656db2cfea96c18baabbd33f676578482b85c51e1ee19d2c" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ + "backtrace", + "bytes", + "cfg-if", + "enum-iterator", "enumset", - "loupe", + "lazy_static", + "leb128", + "memmap2 0.5.10", + "more-asserts", + "region", "rkyv", - "serde", - "serde_bytes", + "self_cell", + "shared-buffer", "smallvec", - "target-lexicon", "thiserror", "wasmer-types", + "wasmer-vm", "wasmparser", + "winapi", ] [[package]] name = "wasmer-compiler-cranelift" -version = "2.3.0" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48be2f9f6495f08649e4f8b946a2cbbe119faf5a654aa1457f9504a99d23dae0" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", "cranelift-frontend", "gimli 0.26.2", - "loupe", "more-asserts", "rayon", "smallvec", @@ -2273,16 +2179,16 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "2.3.0" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ca2a35204d8befa85062bc7aac259a8db8070b801b8a783770ba58231d729e" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", "dynasmrt", + "enumset", "gimli 0.26.2", "lazy_static", - "loupe", "more-asserts", "rayon", "smallvec", @@ -2292,9 +2198,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "2.3.0" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e50405cc2a2f74ff574584710a5f2c1d5c93744acce2ca0866084739284b51" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -2302,164 +2208,70 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "wasmer-engine" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f98f010978c244db431b392aeab0661df7ea0822343334f8f2a920763548e45" -dependencies = [ - "backtrace", - "enumset", - "lazy_static", - "loupe", - "memmap2", - "more-asserts", - "rustc-demangle", - "serde", - "serde_bytes", - "target-lexicon", - "thiserror", - "wasmer-artifact", - "wasmer-compiler", - "wasmer-types", - "wasmer-vm", -] - -[[package]] -name = "wasmer-engine-dylib" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0358af9c154724587731175553805648d9acb8f6657880d165e378672b7e53" -dependencies = [ - "cfg-if", - "enum-iterator", - "enumset", - "leb128", - "libloading", - "loupe", - "object 0.28.4", - "rkyv", - "serde", - "tempfile", - "tracing", - "wasmer-artifact", - "wasmer-compiler", - "wasmer-engine", - "wasmer-object", - "wasmer-types", - "wasmer-vm", - "which", -] - -[[package]] -name = "wasmer-engine-universal" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "440dc3d93c9ca47865a4f4edd037ea81bf983b5796b59b3d712d844b32dbef15" -dependencies = [ - "cfg-if", - "enumset", - "leb128", - "loupe", - "region", - "rkyv", - "wasmer-compiler", - "wasmer-engine", - "wasmer-engine-universal-artifact", - "wasmer-types", - "wasmer-vm", - "winapi", -] - -[[package]] -name = "wasmer-engine-universal-artifact" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f1db3f54152657eb6e86c44b66525ff7801dad8328fe677da48dd06af9ad41" -dependencies = [ - "enum-iterator", - "enumset", - "loupe", - "rkyv", - "thiserror", - "wasmer-artifact", - "wasmer-compiler", - "wasmer-types", -] - [[package]] name = "wasmer-middlewares" -version = "2.3.0" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7812438ed2f37203a37007cdb5332b8475cb2b16e15d51299b2647894e9ed3a" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ - "loupe", "wasmer", "wasmer-types", "wasmer-vm", ] -[[package]] -name = "wasmer-object" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d831335ff3a44ecf451303f6f891175c642488036b92ceceb24ac8623a8fa8b" -dependencies = [ - "object 0.28.4", - "thiserror", - "wasmer-compiler", - "wasmer-types", -] - [[package]] name = "wasmer-types" -version = "2.3.0" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39df01ea05dc0a9bab67e054c7cb01521e53b35a7bb90bd02eca564ed0b2667f" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ - "backtrace", + "bytecheck", "enum-iterator", + "enumset", "indexmap", - "loupe", "more-asserts", "rkyv", - "serde", + "target-lexicon", "thiserror", ] [[package]] name = "wasmer-vm" -version = "2.3.0" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d965fa61f4dc4cdb35a54daaf7ecec3563fbb94154a6c35433f879466247dd" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", + "dashmap", + "derivative", "enum-iterator", + "fnv", "indexmap", "lazy_static", "libc", - "loupe", "mach", "memoffset", "more-asserts", "region", - "rkyv", "scopeguard", - "serde", "thiserror", - "wasmer-artifact", "wasmer-types", "winapi", ] [[package]] name = "wasmparser" -version = "0.83.0" +version = "0.95.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a" +checksum = "f2ea896273ea99b15132414be1da01ab0d8836415083298ecaffbe308eaac87a" +dependencies = [ + "indexmap", + "url", +] [[package]] name = "web-sys" @@ -2471,18 +2283,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - [[package]] name = "winapi" version = "0.3.9" @@ -2635,8 +2435,3 @@ name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" - -[[patch.unused]] -name = "curve25519-dalek" -version = "4.1.3" -source = "git+https://github.com/dalek-cryptography/curve25519-dalek.git#5b7082bbc8e0b2106ab0d956064f61fa0f393cdc" diff --git a/Cargo.toml b/Cargo.toml index 6890643..a4c6eed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ members = ["packages/*", "contracts/*", "wasm"] [workspace.package] -version = "0.1.0" +version = "0.2.0" authors = ["Oraichain Labs"] edition = "2021" license = "MIT" @@ -19,10 +19,10 @@ exclude = [ ] [workspace.dependencies] -cosmwasm-std = { version = "=1.2" } -cosmwasm-schema = { version = "=1.2" } -cosmwasm-storage = { version = "=1.2" } -cosmwasm-vm = { version = "=1.2" } +cosmwasm-std = { version = "1.5" } +cosmwasm-schema = { version = "1.5" } +cosmwasm-storage = { version = "1.5" } +cosmwasm-vm = { version = "1.5" } thiserror = "1.0.26" @@ -30,8 +30,6 @@ cw2 = { version = "1.0.1" } cw20 = { version = "1.0.1" } cw20-base = { version = "1.0.1" } cw-storage-plus = { version = "1.0.1" } -cw-multi-test = "0.16.6" -cw-utils = "0.16.0" derive_more = "0.99.17" decimal-core = { path = "./packages/decimal-core" } decimal = { path = "./packages/decimal" } @@ -46,6 +44,3 @@ rpath = false lto = true overflow-checks = true panic = 'abort' - -[patch.crates-io] -curve25519-dalek = { git = 'https://github.com/dalek-cryptography/curve25519-dalek.git' } diff --git a/contracts/oraiswap-v3/Cargo.toml b/contracts/oraiswap-v3/Cargo.toml index dcbea90..58632ed 100644 --- a/contracts/oraiswap-v3/Cargo.toml +++ b/contracts/oraiswap-v3/Cargo.toml @@ -25,12 +25,10 @@ cosmwasm-storage = { workspace = true } cw-storage-plus = { workspace = true } cw2 = { workspace = true } cw20 = { workspace = true } -cw-utils = { workspace = true } thiserror = { workspace = true } decimal = { workspace = true } derive_more = { workspace = true } [dev-dependencies] -cw-multi-test = { workspace = true } cw20-base = { workspace = true, features = ["library"] } -cosmwasm-testing-util = { git = "https://github.com/oraichain/cosmwasm-testing-util.git", rev = "aaa09cf" } +cosmwasm-testing-util = { git = "https://github.com/oraichain/cosmwasm-testing-util.git", rev = "1b9c412" } diff --git a/contracts/oraiswap-v3/src/contract.rs b/contracts/oraiswap-v3/src/contract.rs index bf1ae82..59765a1 100644 --- a/contracts/oraiswap-v3/src/contract.rs +++ b/contracts/oraiswap-v3/src/contract.rs @@ -6,7 +6,7 @@ use crate::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; use crate::state::CONFIG; use crate::{entrypoints::*, Config}; -use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; +use cosmwasm_std::{to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; use cw2::set_contract_version; // version info for migration info @@ -109,6 +109,7 @@ pub fn execute( init_tick, } => create_pool( deps, + info, env, token_0, token_1, @@ -151,44 +152,81 @@ pub fn execute( extension.slippage_limit_lower, extension.slippage_limit_upper, ), + ExecuteMsg::CreateIncentive { + pool_key, + reward_token, + total_reward, + reward_per_sec, + start_timestamp, + } => create_incentive( + deps, + env, + info, + pool_key, + reward_token, + total_reward, + reward_per_sec, + start_timestamp, + ), + ExecuteMsg::ClaimIncentive { index } => claim_incentives(deps, env, info, index), + ExecuteMsg::UpdateIncentive { + pool_key, + incentive_id, + remaining_reward, + start_timestamp, + reward_per_sec, + } => update_incentive( + deps, + env, + info, + pool_key, + incentive_id, + remaining_reward, + start_timestamp, + reward_per_sec, + ), } } #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::Admin {} => to_binary(&query_admin(deps)?), - QueryMsg::ProtocolFee {} => to_binary(&get_protocol_fee(deps)?), - QueryMsg::Position { owner_id, index } => to_binary(&get_position(deps, owner_id, index)?), + QueryMsg::Admin {} => to_json_binary(&query_admin(deps)?), + QueryMsg::ProtocolFee {} => to_json_binary(&get_protocol_fee(deps)?), + QueryMsg::Position { owner_id, index } => { + to_json_binary(&get_position(deps, owner_id, index)?) + } QueryMsg::Positions { owner_id, limit, offset, - } => to_binary(&get_positions(deps, owner_id, limit, offset)?), - QueryMsg::FeeTierExist { fee_tier } => to_binary(&fee_tier_exist(deps, fee_tier)?), + } => to_json_binary(&get_positions(deps, owner_id, limit, offset)?), + QueryMsg::FeeTierExist { fee_tier } => to_json_binary(&fee_tier_exist(deps, fee_tier)?), QueryMsg::Pool { token_0, token_1, fee_tier, - } => to_binary(&get_pool(deps, token_0, token_1, fee_tier)?), - QueryMsg::Pools { limit, start_after } => to_binary(&get_pools(deps, limit, start_after)?), - QueryMsg::Tick { key, index } => to_binary(&get_tick(deps, key, index)?), + } => to_json_binary(&get_pool(deps, token_0, token_1, fee_tier)?), + QueryMsg::Pools { limit, start_after } => { + to_json_binary(&get_pools(deps, limit, start_after)?) + } + QueryMsg::Tick { key, index } => to_json_binary(&get_tick(deps, key, index)?), QueryMsg::IsTickInitialized { key, index } => { - to_binary(&is_tick_initialized(deps, key, index)?) + to_json_binary(&is_tick_initialized(deps, key, index)?) } - QueryMsg::FeeTiers {} => to_binary(&get_fee_tiers(deps)?), + QueryMsg::FeeTiers {} => to_json_binary(&get_fee_tiers(deps)?), QueryMsg::PositionTicks { owner, offset } => { - to_binary(&get_position_ticks(deps, owner, offset)?) + to_json_binary(&get_position_ticks(deps, owner, offset)?) } QueryMsg::UserPositionAmount { owner } => { - to_binary(&get_user_position_amount(deps, owner)?) + to_json_binary(&get_user_position_amount(deps, owner)?) } QueryMsg::TickMap { pool_key, lower_tick_index, upper_tick_index, x_to_y, - } => to_binary(&get_tickmap( + } => to_json_binary(&get_tickmap( deps, pool_key, lower_tick_index, @@ -198,16 +236,16 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { QueryMsg::LiquidityTicks { pool_key, tick_indexes, - } => to_binary(&get_liquidity_ticks(deps, pool_key, tick_indexes)?), + } => to_json_binary(&get_liquidity_ticks(deps, pool_key, tick_indexes)?), QueryMsg::LiquidityTicksAmount { pool_key, lower_tick, upper_tick, - } => to_binary(&get_liquidity_ticks_amount( + } => to_json_binary(&get_liquidity_ticks_amount( deps, pool_key, lower_tick, upper_tick, )?), QueryMsg::PoolsForPair { token_0, token_1 } => { - to_binary(&get_all_pools_for_pair(deps, token_0, token_1)?) + to_json_binary(&get_all_pools_for_pair(deps, token_0, token_1)?) } QueryMsg::Quote { pool_key, @@ -215,7 +253,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { amount, by_amount_in, sqrt_price_limit, - } => to_binary("e( + } => to_json_binary("e( deps, env, pool_key, @@ -225,12 +263,12 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { sqrt_price_limit, )?), QueryMsg::QuoteRoute { amount_in, swaps } => { - to_binary("e_route(deps, env, amount_in, swaps)?) + to_json_binary("e_route(deps, env, amount_in, swaps)?) } QueryMsg::OwnerOf { token_id, include_expired, - } => to_binary(&query_owner_of( + } => to_json_binary(&query_owner_of( deps, env, token_id, @@ -241,7 +279,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { include_expired, start_after, limit, - } => to_binary(&query_all_approvals( + } => to_json_binary(&query_all_approvals( deps, env, owner, @@ -249,11 +287,11 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { start_after, limit, )?), - QueryMsg::NftInfo { token_id } => to_binary(&query_nft_info(deps, token_id)?), + QueryMsg::NftInfo { token_id } => to_json_binary(&query_nft_info(deps, token_id)?), QueryMsg::AllNftInfo { token_id, include_expired, - } => to_binary(&query_all_nft_info( + } => to_json_binary(&query_all_nft_info( deps, env, token_id, @@ -263,43 +301,52 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { owner, start_after, limit, - } => to_binary(&query_tokens(deps, owner, start_after, limit)?), + } => to_json_binary(&query_tokens(deps, owner, start_after, limit)?), QueryMsg::AllTokens { start_after, limit } => { - to_binary(&query_all_tokens(deps, start_after, limit)?) + to_json_binary(&query_all_tokens(deps, start_after, limit)?) + } + QueryMsg::NumTokens {} => to_json_binary(&query_num_tokens(deps)?), + QueryMsg::PositionIncentives { owner_id, index } => { + to_json_binary(&query_position_incentives(deps, env, owner_id, index)?) + } + QueryMsg::PoolsByPoolKeys { pool_keys } => { + to_json_binary(&get_pools_with_pool_keys(deps, pool_keys)?) + } + QueryMsg::AllPosition { limit, start_after } => { + to_json_binary(&query_all_positions(deps, limit, start_after)?) } - QueryMsg::NumTokens {} => to_binary(&query_num_tokens(deps)?), } } #[cfg_attr(not(feature = "library"), entry_point)] pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result { let original_version = - cw_utils::ensure_from_older_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + cw2::ensure_from_older_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - // query all position, then update token id - let positions: Vec<_> = crate::state::POSITIONS - .range_raw(deps.storage, None, None, cosmwasm_std::Order::Ascending) - .collect(); - let mut token_id = 0; - for item in positions { - if let Ok((key, mut position)) = item { - token_id += 1; - position.token_id = token_id; - let account_id = &key[..key.len() - 4]; - let index = u32::from_be_bytes(key[key.len() - 4..].try_into().unwrap()); - // update position and its index - crate::state::POSITIONS.save(deps.storage, &key, &position)?; - crate::state::POSITION_KEYS_BY_TOKEN_ID.save( - deps.storage, - position.token_id, - &(account_id.to_vec(), index), - )?; - } - } + // // query all position, then update token id + // let positions: Vec<_> = crate::state::POSITIONS + // .range_raw(deps.storage, None, None, cosmwasm_std::Order::Ascending) + // .collect(); + // let mut token_id = 0; + // for item in positions { + // if let Ok((key, mut position)) = item { + // token_id += 1; + // position.token_id = token_id; + // let account_id = &key[..key.len() - 4]; + // let index = u32::from_be_bytes(key[key.len() - 4..].try_into().unwrap()); + // // update position and its index + // crate::state::POSITIONS.save(deps.storage, &key, &position)?; + // crate::state::POSITION_KEYS_BY_TOKEN_ID.save( + // deps.storage, + // position.token_id, + // &(account_id.to_vec(), index), + // )?; + // } + // } - // update total token id, first time token count is total token ids - crate::state::TOKEN_COUNT.save(deps.storage, &token_id)?; - crate::state::TOKEN_ID.save(deps.storage, &token_id)?; + // // update total token id, first time token count is total token ids + // crate::state::TOKEN_COUNT.save(deps.storage, &token_id)?; + // crate::state::TOKEN_ID.save(deps.storage, &token_id)?; Ok(Response::new().add_attribute("new_version", original_version.to_string())) } diff --git a/contracts/oraiswap-v3/src/entrypoints/common.rs b/contracts/oraiswap-v3/src/entrypoints/common.rs index b752b18..ac47f1e 100644 --- a/contracts/oraiswap-v3/src/entrypoints/common.rs +++ b/contracts/oraiswap-v3/src/entrypoints/common.rs @@ -230,8 +230,8 @@ pub fn swap_internal( amount: calculate_swap_result.amount_out.into(), }; - asset_0.transfer_from(msgs, &info, contract_address.to_string())?; - asset_1.transfer(msgs, &info)?; + asset_0.transfer_from(msgs, info, contract_address.to_string())?; + asset_1.transfer(msgs, info)?; Ok(calculate_swap_result) } diff --git a/contracts/oraiswap-v3/src/entrypoints/execute.rs b/contracts/oraiswap-v3/src/entrypoints/execute.rs index 63c6ca2..eac2d63 100644 --- a/contracts/oraiswap-v3/src/entrypoints/execute.rs +++ b/contracts/oraiswap-v3/src/entrypoints/execute.rs @@ -1,4 +1,6 @@ use crate::error::ContractError; +use crate::fee_growth::FeeGrowth; +use crate::incentive::IncentiveRecord; use crate::interface::{Asset, AssetInfo, CalculateSwapResult, Cw721ReceiveMsg, SwapHop}; use crate::liquidity::Liquidity; use crate::percentage::Percentage; @@ -11,7 +13,7 @@ use super::{ check_can_send, create_tick, remove_tick_and_flip_bitmap, swap_internal, swap_route_internal, transfer_nft, update_approvals, TimeStampExt, }; -use cosmwasm_std::{attr, Addr, Binary, DepsMut, Env, MessageInfo, Response}; +use cosmwasm_std::{attr, Addr, Attribute, Binary, DepsMut, Env, MessageInfo, Response}; use cw20::Expiration; use decimal::Decimal; @@ -38,9 +40,13 @@ pub fn change_admin( config.admin = new_admin.clone(); CONFIG.save(deps.storage, &config)?; - Ok(Response::new() - .add_attribute("action", "change_admin") - .add_attribute("new_admin", new_admin)) + let event_attributes = vec![ + attr("action", "change_admin"), + attr("old_admin", info.sender.as_str()), + attr("new_admin", new_admin.as_str()), + ]; + + Ok(Response::new().add_attributes(event_attributes)) } /// Allows an fee receiver to withdraw collected fees. @@ -79,9 +85,17 @@ pub fn withdraw_protocol_fee( asset_0.transfer(&mut msgs, &info)?; asset_1.transfer(&mut msgs, &info)?; + let event_attributes = vec![ + attr("action", "withdraw_protocol_fee"), + attr("pool_key", pool_key.to_string()), + attr("token_x", fee_protocol_token_x.to_string()), + attr("token_y", fee_protocol_token_y.to_string()), + attr("receiver", pool.fee_receiver.as_str()), + ]; + Ok(Response::new() .add_messages(msgs) - .add_attribute("action", "withdraw_protocol_fee")) + .add_attributes(event_attributes)) } /// Allows an admin to adjust the protocol fee. @@ -102,10 +116,19 @@ pub fn change_protocol_fee( return Err(ContractError::Unauthorized {}); } + let old_protocol_fee = config.protocol_fee; + config.protocol_fee = protocol_fee; CONFIG.save(deps.storage, &config)?; - Ok(Response::new().add_attribute("action", "change_protocol_fee")) + let event_attributes = vec![ + attr("action", "change_protocol_fee"), + attr("admin", info.sender.as_str()), + attr("old_protocol_fee", old_protocol_fee.get().to_string()), + attr("new_protocol_fee", protocol_fee.get().to_string()), + ]; + + Ok(Response::new().add_attributes(event_attributes)) } /// Allows admin to change current fee receiver. @@ -130,10 +153,19 @@ pub fn change_fee_receiver( let pool_key_db = pool_key.key(); let mut pool = POOLS.load(deps.storage, &pool_key_db)?; + let old_fee_receiver = pool.fee_receiver.clone(); pool.fee_receiver = fee_receiver.to_string(); POOLS.save(deps.storage, &pool_key_db, &pool)?; - Ok(Response::new().add_attribute("action", "change_fee_receiver")) + let event_attributes = vec![ + attr("action", "change_fee_receiver"), + attr("pool_key", pool_key.to_string()), + attr("admin", info.sender.as_str()), + attr("old_fee_receiver", old_fee_receiver.as_str()), + attr("new_fee_receiver", fee_receiver.as_str()), + ]; + + Ok(Response::new().add_attributes(event_attributes)) } /// Opens a position. @@ -181,6 +213,9 @@ pub fn create_position( let pool_key_db = pool_key.key(); let mut pool = POOLS.load(deps.storage, &pool_key_db)?; + // update global incentives + pool.update_global_incentives(env.block.time.seconds())?; + let mut lower_tick = match state::get_tick(deps.storage, &pool_key, lower_tick) { Ok(tick) => tick, _ => create_tick(deps.storage, current_timestamp, &pool_key, lower_tick)?, @@ -229,12 +264,17 @@ pub fn create_position( let event_attributes = vec![ attr("action", "create_position"), + attr("pool_key", pool_key.to_string()), attr("token_id", position.token_id.to_string()), - attr("address", info.sender.as_str()), - attr("liquidity", liquidity_delta.to_string()), + attr("owner", info.sender.as_str()), + attr("position_liquidity", liquidity_delta.get().to_string()), attr("lower_tick", lower_tick.index.to_string()), attr("upper_tick", upper_tick.index.to_string()), - attr("current_sqrt_price", pool.sqrt_price.to_string()), + attr("current_sqrt_price", pool.sqrt_price.get().to_string()), + attr("liquidity_x", x.to_string()), + attr("liquidity_y", y.to_string()), + attr("after_liquidity ", pool.liquidity.get().to_string()), + attr("ater_tick_index", pool.current_tick_index.to_string()), ]; Ok(Response::new() @@ -273,11 +313,18 @@ pub fn swap( by_amount_in: bool, sqrt_price_limit: SqrtPrice, ) -> Result { + // update incentives first + let mut pool = state::get_pool(deps.storage, &pool_key)?; + pool.update_global_incentives(env.block.time.seconds())?; + POOLS.save(deps.storage, &pool_key.key(), &pool)?; + let mut msgs = vec![]; let CalculateSwapResult { amount_in, amount_out, + fee, + pool: after_pool, .. } = swap_internal( deps.storage, @@ -293,11 +340,25 @@ pub fn swap( sqrt_price_limit, )?; + let event_attributes = vec![ + attr("action", "swap"), + attr("pool_key", pool_key.to_string()), + attr("sender", info.sender.as_str()), + attr("amount_in", amount_in.to_string()), + attr("amount_out", amount_out.to_string()), + attr("current_tick", after_pool.current_tick_index.to_string()), + attr( + "current_sqrt_price", + after_pool.sqrt_price.get().to_string(), + ), + attr("liquidity", after_pool.liquidity.get().to_string()), + attr("x_to_y", x_to_y.to_string()), + attr("fee", fee.to_string()), + ]; + Ok(Response::new() .add_messages(msgs) - .add_attribute("action", "swap") - .add_attribute("amount_in", amount_in.to_string()) - .add_attribute("amount_out", amount_out.to_string())) + .add_attributes(event_attributes)) } /// Performs atomic swap involving several pools based on the provided parameters. @@ -329,6 +390,13 @@ pub fn swap_route( slippage: Percentage, swaps: Vec, ) -> Result { + // update incentives first + for hop in &swaps { + let mut pool = state::get_pool(deps.storage, &hop.pool_key)?; + pool.update_global_incentives(env.block.time.seconds())?; + POOLS.save(deps.storage, &hop.pool_key.key(), &pool)?; + } + let mut msgs = vec![]; let amount_out = swap_route_internal( deps.storage, @@ -337,7 +405,7 @@ pub fn swap_route( &info, &mut msgs, amount_in, - swaps, + swaps.clone(), )?; let min_amount_out = calculate_min_amount_out(expected_amount_out, slippage); @@ -349,7 +417,8 @@ pub fn swap_route( Ok(Response::new() .add_messages(msgs) .add_attribute("action", "swap_route") - .add_attribute("amount_out", amount_out.to_string())) + .add_attribute("amount_out", amount_out.to_string()) + .add_attribute("swap_hop", format!("{:?}", swaps))) } /// Transfers a position between users. @@ -373,7 +442,14 @@ pub fn transfer_position( position.approvals = vec![]; state::add_position(deps.storage, &receiver_addr, &position)?; - Ok(Response::new().add_attribute("action", "transfer_position")) + let event_attributes = vec![ + attr("action", "transfer_position"), + attr("sender", info.sender.as_str()), + attr("receiver", receiver), + attr("position_token_id", position.token_id.to_string()), + ]; + + Ok(Response::new().add_attributes(event_attributes)) } /// Allows an authorized user (owner of the position) to claim collected fees. @@ -436,11 +512,81 @@ pub fn claim_fee( asset_0.transfer(&mut msgs, &info)?; asset_1.transfer(&mut msgs, &info)?; + let event_attributes = vec![ + attr("action", "claim_fee"), + attr("owner", info.sender.as_str()), + attr("pool_key", position.pool_key.to_string()), + attr("position_token_id", position.token_id.to_string()), + attr("amount_x", x.to_string()), + attr("amount_y", y.to_string()), + ]; + + let resp: Response = claim_incentives(deps, env, info, index)?; + + Ok(resp.add_messages(msgs).add_attributes(event_attributes)) +} + +/// Allows an authorized user (owner of the position) to claim incentives. +/// +/// # Parameters +/// - `index`: The index of the user position from which fees will be claimed. +/// +/// # Errors +/// - Fails if the position cannot be found. +pub fn claim_incentives( + deps: DepsMut, + env: Env, + info: MessageInfo, + index: u32, +) -> Result { + let mut position = state::get_position(deps.storage, &info.sender, index)?; + + let lower_tick = state::get_tick(deps.storage, &position.pool_key, position.lower_tick_index)?; + let upper_tick = state::get_tick(deps.storage, &position.pool_key, position.upper_tick_index)?; + let pool_key_db = position.pool_key.key(); + let mut pool = POOLS.load(deps.storage, &pool_key_db)?; + + // update global incentive + pool.update_global_incentives(env.block.time.seconds())?; + + let incentives = position + .claim_incentives(&pool, &upper_tick, &lower_tick) + .unwrap_or(vec![]); + + state::update_position(deps.storage, &position)?; + POOLS.save(deps.storage, &pool_key_db, &pool)?; + + let mut msgs = vec![]; + for asset in incentives.clone() { + asset.transfer(&mut msgs, &info)?; + } + + let mut event_attributes: Vec = vec![]; + + if !incentives.is_empty() { + event_attributes.append(&mut vec![ + attr( + "incentives_token_address", + incentives + .iter() + .map(|x| x.info.denom()) + .collect::>() + .join(","), + ), + attr( + "incentives_amount", + incentives + .iter() + .map(|x| x.amount.to_string()) + .collect::>() + .join(","), + ), + ]); + } + Ok(Response::new() .add_messages(msgs) - .add_attribute("action", "claim_fee") - .add_attribute("amount_x", x.to_string()) - .add_attribute("amount_y", y.to_string())) + .add_attributes(event_attributes)) } /// Removes a position. Sends tokens associated with specified position to the owner. @@ -471,7 +617,48 @@ pub fn remove_position( let pool_key_db = position.pool_key.key(); let mut pool = POOLS.load(deps.storage, &pool_key_db)?; - let (amount_x, amount_y, deinitialize_lower_tick, deinitialize_upper_tick) = position.remove( + // update global incentives first + pool.update_global_incentives(env.block.time.seconds())?; + + // calculate pending incentives + let incentives = position.claim_incentives(&pool, &upper_tick, &lower_tick)?; + + let mut event_attributes: Vec = vec![attr("action", "remove_position")]; + + if !incentives.is_empty() { + event_attributes.append(&mut vec![ + // attr("_contract_address", env.contract.address.to_string()), + attr( + "incentives_token_address", + incentives + .iter() + .map(|x| x.info.denom()) + .collect::>() + .join(","), + ), + attr( + "incentives_amount", + incentives + .iter() + .map(|x| x.amount.to_string()) + .collect::>() + .join(","), + ), + ]); + } + + let ( + amount_x, + amount_y, + liquidity_x, + liquidity_y, + fee_x, + fee_y, + after_liquidity, + ater_tick_index, + deinitialize_lower_tick, + deinitialize_upper_tick, + ) = position.remove( &mut pool, current_timestamp, &mut lower_tick, @@ -479,6 +666,11 @@ pub fn remove_position( position.pool_key.fee_tier.tick_spacing, )?; + event_attributes.append(&mut vec![ + attr("fee_x", fee_x.to_string()), + attr("fee_y", fee_y.to_string()), + ]); + POOLS.save(deps.storage, &pool_key_db, &pool)?; if deinitialize_lower_tick { @@ -502,6 +694,7 @@ pub fn remove_position( &upper_tick, )?; } + let position = state::remove_position(deps.storage, &info.sender, index)?; let asset_0 = Asset { @@ -517,15 +710,23 @@ pub fn remove_position( let mut msgs = vec![]; asset_0.transfer(&mut msgs, &info)?; asset_1.transfer(&mut msgs, &info)?; + for asset in incentives { + asset.transfer(&mut msgs, &info)?; + } - let event_attributes = vec![ - attr("action", "remove_position"), - attr("address", info.sender.as_str()), - attr("liquidity", withdrawed_liquidity.to_string()), + event_attributes.append(&mut vec![ + attr("pool_key", position.pool_key.to_string()), + attr("token_id", position.token_id.to_string()), + attr("owner", info.sender.as_str()), + attr("position_liquidity", withdrawed_liquidity.get().to_string()), attr("lower_tick", lower_tick.index.to_string()), attr("upper_tick", upper_tick.index.to_string()), - attr("current_sqrt_price", pool.sqrt_price.to_string()), - ]; + attr("current_sqrt_price", pool.sqrt_price.get().to_string()), + attr("liquidity_x", liquidity_x.to_string()), + attr("liquidity_y", liquidity_y.to_string()), + attr("after_liquidity ", after_liquidity.get().to_string()), + attr("after_tick_index", ater_tick_index.to_string()), + ]); Ok(Response::new() .add_messages(msgs) @@ -552,6 +753,7 @@ pub fn remove_position( #[allow(clippy::too_many_arguments)] pub fn create_pool( deps: DepsMut, + info: MessageInfo, env: Env, token_0: String, token_1: String, @@ -588,7 +790,16 @@ pub fn create_pool( POOLS.save(deps.storage, &db_key, &pool)?; - Ok(Response::new().add_attribute("action", "create_pool")) + let event_attributes = vec![ + attr("action", "create_pool"), + attr("pool_creator", info.sender.as_str()), + attr("block", env.block.height.to_string()), + attr("pool_key", pool_key.to_string()), + attr("init_sqrt_price", init_sqrt_price.get().to_string()), + attr("init_tick", init_tick.to_string()), + ]; + + Ok(Response::new().add_attributes(event_attributes)) } /// Allows admin to add a custom fee tier. @@ -626,7 +837,14 @@ pub fn add_fee_tier( CONFIG.save(deps.storage, &config)?; - Ok(Response::new().add_attribute("action", "add_fee_tier")) + let event_attributes = vec![ + attr("action", "add_fee_tier"), + attr("admin", info.sender.as_str()), + attr("fee", fee_tier.fee.get().to_string()), + attr("tick_spacing", fee_tier.tick_spacing.to_string()), + ]; + + Ok(Response::new().add_attributes(event_attributes)) } /// Removes an existing fee tier. @@ -657,7 +875,14 @@ pub fn remove_fee_tier( CONFIG.save(deps.storage, &config)?; - Ok(Response::new().add_attribute("action", "remove_fee_tier")) + let event_attributes = vec![ + attr("action", "remove_fee_tier"), + attr("admin", info.sender.as_str()), + attr("fee", fee_tier.fee.get().to_string()), + attr("tick_spacing", fee_tier.tick_spacing.to_string()), + ]; + + Ok(Response::new().add_attributes(event_attributes)) } pub fn handle_approve( @@ -672,6 +897,7 @@ pub fn handle_approve( Ok(Response::new().add_attributes(vec![ attr("action", "approve"), + attr("token_id", token_id.to_string()), attr("sender", info.sender), attr("spender", spender), ])) @@ -688,6 +914,7 @@ pub fn handle_revoke( Ok(Response::new().add_attributes(vec![ attr("action", "revoke"), + attr("token_id", token_id.to_string()), attr("sender", info.sender), attr("spender", spender), ])) @@ -749,6 +976,7 @@ pub fn handle_transfer_nft( Ok(Response::new().add_attributes(vec![ attr("action", "transfer_nft"), + attr("token_id", token_id.to_string()), attr("sender", info.sender), attr("recipient", recipient), ])) @@ -793,11 +1021,13 @@ pub fn handle_send_nft( .add_message(send.into_cosmos_msg(contract.to_string())?) .add_attributes(vec![ attr("action", "send_nft"), + attr("token_id", token_id.to_string()), attr("sender", info.sender), attr("recipient", contract), ])) } +#[allow(clippy::too_many_arguments)] pub fn handle_mint( deps: DepsMut, env: Env, @@ -822,3 +1052,109 @@ pub fn handle_mint( slippage_limit_upper, ) } + +// only owner can execute +#[allow(clippy::too_many_arguments)] +pub fn create_incentive( + deps: DepsMut, + env: Env, + info: MessageInfo, + pool_key: PoolKey, + reward_token: AssetInfo, + total_reward: Option, + reward_per_sec: TokenAmount, + start_timestamp: Option, +) -> Result { + let config = CONFIG.load(deps.storage)?; + if info.sender != config.admin { + return Err(ContractError::Unauthorized {}); + } + + let pool_key_db = pool_key.key(); + let mut pool = POOLS.load(deps.storage, &pool_key_db)?; + pool.update_global_incentives(env.block.time.seconds())?; + + let id = pool.incentives.len() as u64; + let remaining = total_reward.unwrap_or(TokenAmount(u128::MAX)); + let incentive = IncentiveRecord { + id, + reward_per_sec, + reward_token: reward_token.clone(), + remaining, + start_timestamp: start_timestamp.unwrap_or(env.block.time.seconds()), + incentive_growth_global: FeeGrowth(0), + last_updated: env.block.time.seconds(), + }; + pool.incentives.push(incentive); + + POOLS.save(deps.storage, &pool_key_db, &pool)?; + + Ok(Response::new().add_attributes(vec![ + ("action", "create_incentive"), + ("pool", &pool_key.to_string()), + ("record_id", &id.to_string()), + ("reward_token", &reward_token.denom()), + ("total_reward", &remaining.to_string()), + ("reward_per_sec", &reward_per_sec.to_string()), + ( + "start_timestamp", + &start_timestamp + .unwrap_or(env.block.time.seconds()) + .to_string(), + ), + ])) +} + +// only owner can execute +#[allow(clippy::too_many_arguments)] +pub fn update_incentive( + deps: DepsMut, + env: Env, + info: MessageInfo, + pool_key: PoolKey, + record_id: u64, + remaining_reward: Option, + start_timestamp: Option, + reward_per_sec: Option, +) -> Result { + let config = CONFIG.load(deps.storage)?; + if info.sender != config.admin { + return Err(ContractError::Unauthorized {}); + } + + let pool_key_db = pool_key.key(); + let mut pool = POOLS.load(deps.storage, &pool_key_db)?; + pool.update_global_incentives(env.block.time.seconds())?; + + if let Some(record) = pool.incentives.iter_mut().find(|i| i.id == record_id) { + if let Some(remaining_reward) = remaining_reward { + record.remaining = remaining_reward; + } + if let Some(start_timestamp) = start_timestamp { + record.start_timestamp = start_timestamp; + } + if let Some(reward_per_sec) = reward_per_sec { + record.reward_per_sec = reward_per_sec; + } + } + + POOLS.save(deps.storage, &pool_key_db, &pool)?; + + Ok(Response::new().add_attributes(vec![ + ("action", "update_incentive"), + ("pool", &pool_key.to_string()), + ("record_id", &record_id.to_string()), + ( + "remaining_reward", + &remaining_reward.unwrap_or_default().to_string(), + ), + ( + "start_timestamp", + &start_timestamp.unwrap_or_default().to_string(), + ), + ( + "reward_per_sec", + &reward_per_sec.unwrap_or_default().to_string(), + ), + ])) +} diff --git a/contracts/oraiswap-v3/src/entrypoints/query.rs b/contracts/oraiswap-v3/src/entrypoints/query.rs index 5b21480..beb7efa 100644 --- a/contracts/oraiswap-v3/src/entrypoints/query.rs +++ b/contracts/oraiswap-v3/src/entrypoints/query.rs @@ -1,15 +1,15 @@ -use cosmwasm_std::{Addr, Deps, Env, Order, StdResult}; +use cosmwasm_std::{Addr, Binary, Deps, Env, Order, StdResult}; use cw_storage_plus::Bound; use crate::{ get_max_chunk, get_min_chunk, interface::{ - AllNftInfoResponse, Approval, ApprovedForAllResponse, NftInfoResponse, NumTokensResponse, - OwnerOfResponse, PoolWithPoolKey, QuoteResult, SwapHop, TokensResponse, + AllNftInfoResponse, Approval, ApprovedForAllResponse, Asset, NftInfoResponse, + NumTokensResponse, OwnerOfResponse, PoolWithPoolKey, QuoteResult, SwapHop, TokensResponse, }, percentage::Percentage, sqrt_price::{get_max_tick, get_min_tick, SqrtPrice}, - state::{self, CONFIG, MAX_LIMIT}, + state::{self, CONFIG, MAX_LIMIT, POSITIONS}, tick_to_position, token_amount::TokenAmount, ContractError, FeeTier, LiquidityTick, Pool, PoolKey, Position, PositionTick, Tick, CHUNK_SIZE, @@ -121,6 +121,19 @@ pub fn get_pools( state::get_pools(deps.storage, limit, start_after) } +pub fn get_pools_with_pool_keys( + deps: Deps, + pool_keys: Vec, +) -> Result, ContractError> { + let mut pools = vec![]; + for pool_key in pool_keys { + if let Ok(pool) = state::get_pool(deps.storage, &pool_key) { + pools.push(PoolWithPoolKey { pool, pool_key }); + } + } + Ok(pools) +} + /// Retrieves listed pools for provided token pair /// - `token_0`: Address of first token /// - `token_1`: Address of second token @@ -517,3 +530,45 @@ pub fn query_num_tokens(deps: Deps) -> Result let count = state::num_tokens(deps.storage)?; Ok(NumTokensResponse { count }) } + +/// Retrieves incentives information of a single position. +/// +/// # Parameters +/// - `owner_id`: An `Addr` identifying the user who owns the position. +/// - `index`: The index of the user position. +/// +/// # Errors +/// - Fails if position cannot be found +pub fn query_position_incentives( + deps: Deps, + env: Env, + owner_id: Addr, + index: u32, +) -> Result, ContractError> { + let mut position = state::get_position(deps.storage, &owner_id, index)?; + let mut pool = state::get_pool(deps.storage, &position.pool_key)?; + let lower_tick = state::get_tick(deps.storage, &position.pool_key, position.lower_tick_index)?; + let upper_tick = state::get_tick(deps.storage, &position.pool_key, position.upper_tick_index)?; + // update global incentive + pool.update_global_incentives(env.block.time.seconds())?; + position.update_incentives(&pool, &upper_tick, &lower_tick)?; + + let incentives = position.claim_incentives(&pool, &upper_tick, &lower_tick)?; + + Ok(incentives) +} + +pub fn query_all_positions( + deps: Deps, + limit: Option, + start_after: Option, +) -> Result, ContractError> { + let limit = limit.unwrap_or(MAX_LIMIT).min(MAX_LIMIT) as usize; + let start = start_after.map(|x| x.to_vec()).map(Bound::ExclusiveRaw); + Ok(POSITIONS + .range(deps.storage, start, None, Order::Ascending) + .take(limit) + .filter_map(Result::ok) + .map(|(_, position)| position) + .collect::>()) +} diff --git a/contracts/oraiswap-v3/src/error.rs b/contracts/oraiswap-v3/src/error.rs index bc0c9f9..d903b89 100644 --- a/contracts/oraiswap-v3/src/error.rs +++ b/contracts/oraiswap-v3/src/error.rs @@ -119,7 +119,7 @@ pub enum ContractError { #[error("Current Sqrt Price < Lower Sqrt Price")] CurrentSqrtPriceLess, - #[error("unauthorized")] + #[error("Unauthorized")] Unauthorized {}, #[error("Cannot set approval that is already expired")] diff --git a/contracts/oraiswap-v3/src/interface.rs b/contracts/oraiswap-v3/src/interface.rs index aec9095..7bc6d59 100644 --- a/contracts/oraiswap-v3/src/interface.rs +++ b/contracts/oraiswap-v3/src/interface.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{ - to_binary, Addr, Api, BankMsg, Binary, Coin, CosmosMsg, MessageInfo, StdResult, Uint128, + to_json_binary, Addr, Api, BankMsg, Binary, Coin, CosmosMsg, MessageInfo, StdResult, Uint128, WasmMsg, }; use cw20::{Cw20ExecuteMsg, Expiration}; @@ -45,6 +45,13 @@ impl AssetInfo { } } } + + pub fn denom(&self) -> String { + match self { + AssetInfo::Token { contract_addr } => contract_addr.to_string(), + AssetInfo::NativeToken { denom } => denom.to_string(), + } + } } #[cw_serde] @@ -65,7 +72,7 @@ impl Asset { msgs.push( WasmMsg::Execute { contract_addr: contract_addr.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Transfer { + msg: to_json_binary(&Cw20ExecuteMsg::Transfer { recipient: info.sender.to_string(), amount: self.amount, })?, @@ -101,7 +108,7 @@ impl Asset { msgs.push( WasmMsg::Execute { contract_addr: contract_addr.to_string(), - msg: to_binary(&Cw20ExecuteMsg::TransferFrom { + msg: to_json_binary(&Cw20ExecuteMsg::TransferFrom { owner: info.sender.to_string(), recipient, amount: self.amount, @@ -220,7 +227,7 @@ impl Cw721ReceiveMsg { /// serializes the message pub fn into_binary(self) -> StdResult { let msg = ReceiverHandleMsg::ReceiveNft(self); - to_binary(&msg) + to_json_binary(&msg) } /// creates a cosmos_msg sending this struct to the named contract diff --git a/contracts/oraiswap-v3/src/math/log.rs b/contracts/oraiswap-v3/src/math/log.rs index e83cf6c..8f4253b 100644 --- a/contracts/oraiswap-v3/src/math/log.rs +++ b/contracts/oraiswap-v3/src/math/log.rs @@ -391,24 +391,6 @@ mod tests { } } - #[test] - fn test_domain_calculate_sqrt_price() { - // Over max tick - // { - // let tick_out_of_range = MAX_TICK + 1; - // let (_, cause, stack) = SqrtPrice::from_tick(tick_out_of_range).unwrap_err().get(); - // assert_eq!("tick over bounds", cause); - // assert_eq!(1, stack.len()); - // } - // // Below min tick - // { - // let tick_out_of_range = -MAX_TICK - 1; - // let (_, cause, stack) = SqrtPrice::from_tick(tick_out_of_range).unwrap_err().get(); - // assert_eq!("tick over bounds", cause); - // assert_eq!(1, stack.len()); - // } - } - #[test] fn test_align_tick_with_spacing() { // zero diff --git a/contracts/oraiswap-v3/src/msg.rs b/contracts/oraiswap-v3/src/msg.rs index a611c0a..ae80bac 100644 --- a/contracts/oraiswap-v3/src/msg.rs +++ b/contracts/oraiswap-v3/src/msg.rs @@ -4,8 +4,8 @@ use cosmwasm_std::{Addr, Binary}; use cw20::Expiration; use crate::interface::{ - AllNftInfoResponse, ApprovedForAllResponse, NftInfoResponse, NumTokensResponse, - OwnerOfResponse, PoolWithPoolKey, PositionTick, QuoteResult, TokensResponse, + AllNftInfoResponse, ApprovedForAllResponse, Asset, AssetInfo, NftInfoResponse, + NumTokensResponse, OwnerOfResponse, PoolWithPoolKey, PositionTick, QuoteResult, TokensResponse, }; #[allow(unused_imports)] use crate::{ @@ -130,6 +130,26 @@ pub enum ExecuteMsg { RevokeAll { operator: Addr, }, + // create incentives for specific pool + CreateIncentive { + pool_key: PoolKey, + reward_token: AssetInfo, + total_reward: Option, + reward_per_sec: TokenAmount, + start_timestamp: Option, + }, + // update for specific pool + UpdateIncentive { + pool_key: PoolKey, + incentive_id: u64, + remaining_reward: Option, + start_timestamp: Option, + reward_per_sec: Option, + }, + // Claim Incentives + ClaimIncentive { + index: u32, + }, } #[cw_serde] @@ -154,6 +174,12 @@ pub enum QueryMsg { offset: Option, }, + #[returns(Vec)] + AllPosition { + limit: Option, + start_after: Option, + }, + #[returns(bool)] FeeTierExist { fee_tier: FeeTier }, @@ -281,4 +307,10 @@ pub enum QueryMsg { start_after: Option, limit: Option, }, + + #[returns(Vec)] + PositionIncentives { owner_id: Addr, index: u32 }, + + #[returns(Vec)] + PoolsByPoolKeys { pool_keys: Vec }, } diff --git a/contracts/oraiswap-v3/src/state.rs b/contracts/oraiswap-v3/src/state.rs index 8109d0b..ce9fd9d 100644 --- a/contracts/oraiswap-v3/src/state.rs +++ b/contracts/oraiswap-v3/src/state.rs @@ -4,6 +4,7 @@ use cw_storage_plus::{Bound, Item, Map}; use crate::{ flip_bit_at_position, get_bit_at_position, get_search_limit, + incentive::IncentiveRecord, interface::PoolWithPoolKey, sqrt_price::{calculate_sqrt_price, SqrtPrice}, tick_to_position, Config, ContractError, Pool, PoolKey, Position, Tick, CHUNK_SIZE, MAX_TICK, @@ -29,6 +30,8 @@ pub const POSITION_KEYS_BY_TOKEN_ID: Map, u32)> = Map::new("positi pub const TOKEN_COUNT: Item = Item::new("num_tokens"); pub const OPERATORS: Map<(&[u8], &[u8]), Expiration> = Map::new("operators"); +pub const INCENTIVE_RECORD: Map = Map::new("incentive_record"); + pub const MAX_LIMIT: u32 = 100; pub fn num_tokens(storage: &dyn Storage) -> StdResult { diff --git a/contracts/oraiswap-v3/src/storage/incentive.rs b/contracts/oraiswap-v3/src/storage/incentive.rs new file mode 100644 index 0000000..6046336 --- /dev/null +++ b/contracts/oraiswap-v3/src/storage/incentive.rs @@ -0,0 +1,380 @@ +use cosmwasm_schema::cw_serde; + +use crate::{ + fee_growth::FeeGrowth, interface::AssetInfo, liquidity::Liquidity, token_amount::TokenAmount, + ContractError, +}; + +#[cw_serde] +pub struct IncentiveRecord { + pub id: u64, + pub reward_per_sec: TokenAmount, + pub reward_token: AssetInfo, + pub remaining: TokenAmount, + pub start_timestamp: u64, + pub incentive_growth_global: FeeGrowth, + pub last_updated: u64, +} + +#[cw_serde] +#[derive(Eq, Copy, Default)] +pub struct TickIncentive { + pub incentive_id: u64, + pub incentive_growth_outside: FeeGrowth, +} + +#[cw_serde] +pub struct PositionIncentives { + pub incentive_id: u64, + pub pending_rewards: TokenAmount, + pub incentive_growth_inside: FeeGrowth, +} + +impl IncentiveRecord { + pub fn update_global_incentive_growth( + &mut self, + pool_liquidity: Liquidity, + current_timestamp: u64, + ) -> Result<(), ContractError> { + if current_timestamp.lt(&self.start_timestamp) || current_timestamp.lt(&self.last_updated) { + return Ok(()); + } + + let pass_time = current_timestamp - self.last_updated; + + let mut total_emit = self.reward_per_sec * TokenAmount(pass_time.into()); + if total_emit > self.remaining { + total_emit = self.remaining; + }; + + let incentive_growth = FeeGrowth::from_fee(pool_liquidity, total_emit); + match incentive_growth { + Ok(value) => { + self.incentive_growth_global += value; + self.remaining -= total_emit; + } + Err(_) => { + // Do nothing if there is an error when converting the amount to FeeGrowth + // Potential errors in calculating incentive growth: + // - overflow + // - liquidity is zero + } + } + self.last_updated = current_timestamp; + + Ok(()) + } +} + +// reference: https://uniswap.org/whitepaper-v3.pdf (6.17) and (6.18) +pub fn calculate_incentive_growth_inside( + tick_lower: i32, + tick_lower_incentive_growth_outside: FeeGrowth, + tick_upper: i32, + tick_upper_incentive_growth_outside: FeeGrowth, + tick_current: i32, + incentive_growth_global: FeeGrowth, +) -> FeeGrowth { + // determine position relative to current tick + let current_above_lower = tick_current >= tick_lower; + let current_below_upper = tick_current < tick_upper; + + // calculate fee growth below + let incentive_growth_below = if current_above_lower { + tick_lower_incentive_growth_outside + } else { + incentive_growth_global.unchecked_sub(tick_lower_incentive_growth_outside) + }; + + // calculate fee growth above + let incentive_growth_above = if current_below_upper { + tick_upper_incentive_growth_outside + } else { + incentive_growth_global.unchecked_sub(tick_upper_incentive_growth_outside) + }; + + // calculate fee growth inside + incentive_growth_global + .unchecked_sub(incentive_growth_below) + .unchecked_sub(incentive_growth_above) +} + +#[cfg(test)] +mod tests { + use decimal::*; + + use super::*; + + #[test] + fn test_update_global_incentive_growth() { + let mut record = IncentiveRecord { + id: 0, + reward_per_sec: TokenAmount(0), + reward_token: AssetInfo::NativeToken { + denom: "orai".to_string(), + }, + remaining: TokenAmount(1000000), + start_timestamp: 1000, + incentive_growth_global: FeeGrowth(0), + last_updated: 1000, + }; + let mut pool_liquidity = Liquidity::new(1000000); + + // case 1: CurrentTimestamp < start_timestamp => no update + record + .update_global_incentive_growth(pool_liquidity, 900) + .unwrap(); + assert_eq!(record.last_updated, 1000); + assert_eq!(record.incentive_growth_global, FeeGrowth(0)); + + // case 2: CurrentTimestamp < last_updated => no update + record.last_updated = 1100; + record + .update_global_incentive_growth(pool_liquidity, 1099) + .unwrap(); + assert_eq!(record.last_updated, 1100); + assert_eq!(record.incentive_growth_global, FeeGrowth(0)); + + // case 3: liquidity = 0 => still success, but don;t update incentive_growth_global + pool_liquidity = Liquidity::new(0); + record.reward_per_sec = TokenAmount(100); + record + .update_global_incentive_growth(pool_liquidity, 1200) + .unwrap(); + assert_eq!(record.last_updated, 1200); + assert_eq!(record.remaining, TokenAmount(1000000)); + assert_eq!(record.incentive_growth_global, FeeGrowth(0)); + + // case 4: overflow => still success, but don;t update incentive_growth_global + pool_liquidity = Liquidity::new(1); + record.reward_per_sec = TokenAmount(1000); + record + .update_global_incentive_growth(pool_liquidity, 1300) + .unwrap(); + assert_eq!(record.last_updated, 1300); + assert_eq!(record.remaining, TokenAmount(1000000)); + assert_eq!(record.incentive_growth_global, FeeGrowth(0)); + + // case 4: happy case + pool_liquidity = Liquidity::new(1000); + record.reward_per_sec = TokenAmount(100); + record + .update_global_incentive_growth(pool_liquidity, 1400) + .unwrap(); + assert_eq!(record.last_updated, 1400); + assert_eq!(record.remaining, TokenAmount(990000)); + assert_eq!( + record.incentive_growth_global, + FeeGrowth(100000000000000000000000000000000000) + ); + + // case 5: total emit > remaining reward + pool_liquidity = Liquidity::new(100000); + record.reward_per_sec = TokenAmount(10000); + record + .update_global_incentive_growth(pool_liquidity, 1500) + .unwrap(); + assert_eq!(record.last_updated, 1500); + assert_eq!(record.remaining, TokenAmount(0)); + assert_eq!( + record.incentive_growth_global, + FeeGrowth(199000000000000000000000000000000000) + ); + + // case 6: no reward remaining + record + .update_global_incentive_growth(pool_liquidity, 1600) + .unwrap(); + assert_eq!(record.last_updated, 1600); + assert_eq!(record.remaining, TokenAmount(0)); + assert_eq!( + record.incentive_growth_global, + FeeGrowth(199000000000000000000000000000000000) + ); + } + + #[test] + fn test_calculate_incentive_growth_inside() { + // <────────────── ──────────────> + // incentive_outside_t0| incentive_growth_inside |incentive_outside_t1 + //<───────────── t0 ────── C ────── t1 ───────────────────> + + // incentive_growth_inside = incentive_growth_global - t0.incentive_outside - t1.incentive_outside + + let incentive_growth_global = FeeGrowth::from_integer(15); + + let tick_lower_index = -2; + let tick_lower_incentive_growth_outside = FeeGrowth::new(0); + + let tick_upper_index = 2; + let tick_upper_incentive_growth_outside = FeeGrowth::from_integer(0); + + // current tick inside range + // lower current upper + // | | | + // -2 0 2 + { + // index and fee global + let tick_current = 0; + let incentive_growth_inside = calculate_incentive_growth_inside( + tick_lower_index, + tick_lower_incentive_growth_outside, + tick_upper_index, + tick_upper_incentive_growth_outside, + tick_current, + incentive_growth_global, + ); + + assert_eq!(incentive_growth_inside, FeeGrowth::from_integer(15)); // x incentive growth inside + } + // ───────incentive_outside_t0──────────> + // |incentive_growth_inside| incentive_outside_t1 + // ─────── c ─────── t0 ──────────────> t1 ───────────> + // + // incentive_growth_inside = t0.incentive_outisde - t1.incentive_outside + // + // current tick below range + // current lower upper + // | | | + // -4 2 2 + { + let tick_current = -4; + let incentive_growth_inside = calculate_incentive_growth_inside( + tick_lower_index, + tick_lower_incentive_growth_outside, + tick_upper_index, + tick_upper_incentive_growth_outside, + tick_current, + incentive_growth_global, + ); + + assert_eq!(incentive_growth_inside, FeeGrowth::new(0)); // incentive growth inside + } + + // <──────────incentive_outside_t0────────── + // incentive_outside_t1 | incentive_growth_inside| + // ────────────── t1 ──────────────── t0 ─────── c ───────────> + + // incentive_growth_inside = t0.incentive_outisde - t1.incentive_outside + + // current tick upper range + // lower upper current + // | | | + // -2 2 4 + { + let tick_current = 4; + let incentive_growth_inside = calculate_incentive_growth_inside( + tick_lower_index, + tick_lower_incentive_growth_outside, + tick_upper_index, + tick_upper_incentive_growth_outside, + tick_current, + incentive_growth_global, + ); + + assert_eq!(incentive_growth_inside, FeeGrowth::new(0)); // incentive growth inside + } + + // current tick upper range + // lower upper current + // | | | + // -2 2 3 + { + let tick_lower_index = -2; + let tick_lower_incentive_growth_outside = FeeGrowth::new(0); + + let tick_upper_index = 2; + let tick_upper_incentive_growth_outside = FeeGrowth::new(1); + + let incentive_growth_global = FeeGrowth::from_integer(5); + + let tick_current = 3; + let incentive_growth_inside = calculate_incentive_growth_inside( + tick_lower_index, + tick_lower_incentive_growth_outside, + tick_upper_index, + tick_upper_incentive_growth_outside, + tick_current, + incentive_growth_global, + ); + + assert_eq!(incentive_growth_inside, FeeGrowth::new(1)); // incentive growth inside + } + + // subtracts upper tick if below + let tick_upper_index = 2; + let tick_upper_incentive_growth_outside = FeeGrowth::from_integer(2); + + // lower current upper + // | | | + // -2 0 2 + { + let tick_current = 0; + let incentive_growth_inside = calculate_incentive_growth_inside( + tick_lower_index, + tick_lower_incentive_growth_outside, + tick_upper_index, + tick_upper_incentive_growth_outside, + tick_current, + incentive_growth_global, + ); + + assert_eq!(incentive_growth_inside, FeeGrowth::from_integer(13)); // incentive growth inside + } + + // subtracts lower tick if above + let tick_upper_index = 2; + let tick_upper_incentive_growth_outside = FeeGrowth::new(0); + + let tick_lower_index = -2; + let tick_lower_incentive_growth_outside = FeeGrowth::from_integer(2); + + // current tick inside range + // lower current upper + // | | | + // -2 0 2 + { + let tick_current = 0; + let incentive_growth_inside = calculate_incentive_growth_inside( + tick_lower_index, + tick_lower_incentive_growth_outside, + tick_upper_index, + tick_upper_incentive_growth_outside, + tick_current, + incentive_growth_global, + ); + + assert_eq!(incentive_growth_inside, FeeGrowth::from_integer(13)); // incentive growth inside + } + } + + #[test] + fn test_domain_calculate_incentive_growth_inside() { + let tick_current = 0; + let incentive_growth_global = FeeGrowth::from_integer(20); + + let tick_lower_index = -20; + let tick_lower_incentive_growth_outside = FeeGrowth::from_integer(20); + + let tick_upper_index = -10; + let tick_upper_incentive_growth_outside = FeeGrowth::from_integer(15); + + let incentive_growth_inside = calculate_incentive_growth_inside( + tick_lower_index, + tick_lower_incentive_growth_outside, + tick_upper_index, + tick_upper_incentive_growth_outside, + tick_current, + incentive_growth_global, + ); + + assert_eq!( + incentive_growth_inside, + FeeGrowth::max_instance() - FeeGrowth::from_integer(5) + FeeGrowth::new(1) + ); + assert_eq!( + incentive_growth_inside, + FeeGrowth::max_instance() - FeeGrowth::from_integer(5) + FeeGrowth::new(1) + ); + } +} diff --git a/contracts/oraiswap-v3/src/storage/mod.rs b/contracts/oraiswap-v3/src/storage/mod.rs index 200c16c..f9b89bb 100644 --- a/contracts/oraiswap-v3/src/storage/mod.rs +++ b/contracts/oraiswap-v3/src/storage/mod.rs @@ -1,5 +1,6 @@ pub mod config; pub mod fee_tier; +pub mod incentive; pub mod pool; pub mod pool_key; pub mod position; diff --git a/contracts/oraiswap-v3/src/storage/pool.rs b/contracts/oraiswap-v3/src/storage/pool.rs index e44faa1..ff32080 100644 --- a/contracts/oraiswap-v3/src/storage/pool.rs +++ b/contracts/oraiswap-v3/src/storage/pool.rs @@ -1,4 +1,5 @@ use super::{FeeTier, Tick}; +use crate::incentive::IncentiveRecord; use crate::math::types::sqrt_price::check_tick_to_sqrt_price_relationship; use crate::{ math::{ @@ -29,6 +30,9 @@ pub struct Pool { pub start_timestamp: u64, pub last_timestamp: u64, pub fee_receiver: String, + + #[serde(default)] + pub incentives: Vec, } #[derive(PartialEq, Eq, Debug, Clone)] @@ -185,6 +189,17 @@ impl Pool { (fee_protocol_token_x, fee_protocol_token_y) } + + pub fn update_global_incentives( + &mut self, + current_timestamp: u64, + ) -> Result<(), ContractError> { + for record in &mut self.incentives { + record.update_global_incentive_growth(self.liquidity, current_timestamp)?; + } + + Ok(()) + } } #[cfg(test)] diff --git a/contracts/oraiswap-v3/src/storage/pool_key.rs b/contracts/oraiswap-v3/src/storage/pool_key.rs index 3d4161b..ff835ce 100644 --- a/contracts/oraiswap-v3/src/storage/pool_key.rs +++ b/contracts/oraiswap-v3/src/storage/pool_key.rs @@ -1,3 +1,5 @@ +use core::fmt; + use crate::{ContractError, FeeTier}; use cosmwasm_schema::cw_serde; use cosmwasm_storage::to_length_prefixed_nested; @@ -65,6 +67,16 @@ impl PoolKey { } } +impl fmt::Display for PoolKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // tokenx-tokeny-fee-tick_spacing + write!( + f, + "{}-{}-{}-{}", + self.token_x, self.token_y, self.fee_tier.fee.0, self.fee_tier.tick_spacing + ) + } +} #[cfg(test)] mod tests { use cosmwasm_std::Addr; @@ -88,5 +100,7 @@ mod tests { pool_key.key(), to_length_prefixed_nested(&[b"token_0", b"token_1", fee_tier.key().as_slice()]) ); + + println!("{:?}", pool_key.to_string()); } } diff --git a/contracts/oraiswap-v3/src/storage/position.rs b/contracts/oraiswap-v3/src/storage/position.rs index 4c8f8a9..19cd4e6 100644 --- a/contracts/oraiswap-v3/src/storage/position.rs +++ b/contracts/oraiswap-v3/src/storage/position.rs @@ -1,6 +1,7 @@ use super::{Pool, PoolKey, Tick}; use crate::{ - interface::Approval, + incentive::{calculate_incentive_growth_inside, PositionIncentives}, + interface::{Approval, Asset}, math::{ clamm::*, types::{ @@ -32,6 +33,8 @@ pub struct Position { pub approvals: Vec, #[serde(default)] pub token_id: u64, + #[serde(default)] + pub incentives: Vec, } impl Position { @@ -80,6 +83,65 @@ impl Position { pool.update_liquidity(liquidity_delta, add, upper_tick.index, lower_tick.index) } + pub fn update_incentives( + &mut self, + pool: &Pool, + upper_tick: &Tick, + lower_tick: &Tick, + ) -> Result<(), ContractError> { + // try update incentives + for record in &pool.incentives { + let tick_lower_incentive_growth_outside = lower_tick + .incentives + .iter() + .find(|i| i.incentive_id == record.id) + .map_or(FeeGrowth::new(0), |incentive| { + incentive.incentive_growth_outside + }); + + let tick_upper_incentive_growth_outside = upper_tick + .incentives + .iter() + .find(|i| i.incentive_id == record.id) + .map_or(FeeGrowth::new(0), |incentive| { + incentive.incentive_growth_outside + }); + + let incentive_growth_inside = calculate_incentive_growth_inside( + lower_tick.index, + tick_lower_incentive_growth_outside, + upper_tick.index, + tick_upper_incentive_growth_outside, + pool.current_tick_index, + record.incentive_growth_global, + ); + + if let Some(incentive) = self + .incentives + .iter_mut() + .find(|i| i.incentive_id == record.id) + { + incentive.pending_rewards += incentive_growth_inside + .unchecked_sub(incentive.incentive_growth_inside) + .to_fee(self.liquidity)?; + incentive.incentive_growth_inside = incentive_growth_inside; + continue; + } + let pending_rewards = incentive_growth_inside.to_fee(self.liquidity)?; + if pending_rewards.is_zero() && incentive_growth_inside.is_zero() { + continue; + } + let incentive = PositionIncentives { + incentive_id: record.id, + pending_rewards, + incentive_growth_inside, + }; + self.incentives.push(incentive); + } + + Ok(()) + } + pub fn update( &mut self, sign: bool, @@ -149,6 +211,39 @@ impl Position { Ok((tokens_owed_x, tokens_owed_y)) } + + pub fn claim_incentives( + &mut self, + pool: &Pool, + upper_tick: &Tick, + lower_tick: &Tick, + ) -> Result, ContractError> { + self.update_incentives(pool, upper_tick, lower_tick)?; + let incentives: Vec = self + .incentives + .iter_mut() + .filter_map(|incentive| { + if incentive.pending_rewards.is_zero() { + return None; + } + if let Some(record) = pool + .incentives + .iter() + .find(|i| i.id == incentive.incentive_id) + { + let reward = incentive.pending_rewards; + incentive.pending_rewards = TokenAmount::new(0); + return Some(Asset { + info: record.reward_token.clone(), + amount: reward.into(), + }); + } + None + }) + .collect(); + + Ok(incentives) + } #[allow(clippy::too_many_arguments)] pub fn create( pool: &mut Pool, @@ -166,6 +261,16 @@ impl Position { return Err(ContractError::PriceLimitReached); } + let incentives: Vec = pool + .incentives + .iter() + .map(|record| PositionIncentives { + incentive_id: record.id, + pending_rewards: TokenAmount::new(0), + incentive_growth_inside: FeeGrowth::new(0), + }) + .collect(); + // init position let mut position = Self { token_id: 0, @@ -179,8 +284,12 @@ impl Position { tokens_owed_x: TokenAmount::new(0), tokens_owed_y: TokenAmount::new(0), approvals: vec![], + incentives, }; + // try update incentives first + position.update_incentives(pool, upper_tick, lower_tick)?; + let (required_x, required_y) = position.modify( pool, upper_tick, @@ -201,7 +310,21 @@ impl Position { lower_tick: &mut Tick, upper_tick: &mut Tick, tick_spacing: u16, - ) -> Result<(TokenAmount, TokenAmount, bool, bool), ContractError> { + ) -> Result< + ( + TokenAmount, + TokenAmount, + TokenAmount, + TokenAmount, + TokenAmount, + TokenAmount, + Liquidity, + i32, + bool, + bool, + ), + ContractError, + > { let liquidity_delta = self.liquidity; let (mut amount_x, mut amount_y) = self.modify( pool, @@ -213,6 +336,8 @@ impl Position { tick_spacing, )?; + let liquidity_x = amount_x; + let liquidity_y = amount_y; amount_x = amount_x.checked_add(self.tokens_owed_x)?; amount_y = amount_y.checked_add(self.tokens_owed_y)?; @@ -222,6 +347,12 @@ impl Position { Ok(( amount_x, amount_y, + liquidity_x, + liquidity_y, + self.tokens_owed_x, + self.tokens_owed_y, + pool.liquidity, + pool.current_tick_index, deinitialize_lower_tick, deinitialize_upper_tick, )) diff --git a/contracts/oraiswap-v3/src/storage/tick.rs b/contracts/oraiswap-v3/src/storage/tick.rs index c8055b9..3fc2f38 100644 --- a/contracts/oraiswap-v3/src/storage/tick.rs +++ b/contracts/oraiswap-v3/src/storage/tick.rs @@ -1,5 +1,6 @@ use super::Pool; use crate::{ + incentive::TickIncentive, math::types::{ fee_growth::FeeGrowth, liquidity::Liquidity, @@ -11,7 +12,7 @@ use cosmwasm_schema::cw_serde; use decimal::*; #[cw_serde] -#[derive(Eq, Copy, Default)] +#[derive(Eq, Default)] pub struct Tick { pub index: i32, pub sign: bool, @@ -21,6 +22,9 @@ pub struct Tick { pub fee_growth_outside_x: FeeGrowth, pub fee_growth_outside_y: FeeGrowth, pub seconds_outside: u64, + + #[serde(default)] + pub incentives: Vec, } pub const MAX_RESULT_SIZE: usize = 16 * 1024 * 8; @@ -60,6 +64,20 @@ impl Tick { pub fn create(index: i32, pool: &Pool, current_timestamp: u64) -> Self { let below_current_tick = index <= pool.current_tick_index; + // ensure update global incentive before + // reference: https://uniswap.org/whitepaper-v3.pdf (6.21) + let incentives: Vec = pool + .incentives + .iter() + .map(|record| TickIncentive { + incentive_id: record.id, + incentive_growth_outside: match below_current_tick { + true => record.incentive_growth_global, + false => FeeGrowth::new(0), + }, + }) + .collect(); + Self { index, sign: true, @@ -76,11 +94,13 @@ impl Tick { true => current_timestamp - pool.start_timestamp, false => 0, }, + incentives, ..Self::default() } } pub fn cross(&mut self, pool: &mut Pool, current_timestamp: u64) -> Result<(), ContractError> { + // reference: https://uniswap.org/whitepaper-v3.pdf (6.20) self.fee_growth_outside_x = pool .fee_growth_global_x .unchecked_sub(self.fee_growth_outside_x); @@ -95,6 +115,29 @@ impl Tick { pool.last_timestamp = current_timestamp; + // ensure update global incentive before + // Iterate through the pool incentives + for record in &pool.incentives { + // Check if the incentive ID exists in the existing set + if let Some(incentive) = self + .incentives + .iter_mut() + .find(|i| i.incentive_id == record.id) + { + // reference: https://uniswap.org/whitepaper-v3.pdf (6.20) + // Update the incentive growth if it exists + incentive.incentive_growth_outside = record + .incentive_growth_global + .unchecked_sub(incentive.incentive_growth_outside); + } else { + // If the incentive ID doesn't exist, add a new incentive + self.incentives.push(TickIncentive { + incentive_id: record.id, + incentive_growth_outside: record.incentive_growth_global, + }); + } + } + // When going to higher tick net_liquidity should be added and for going lower subtracted if (pool.current_tick_index >= self.index) ^ self.sign { pool.liquidity = pool.liquidity.checked_add(self.liquidity_change)?; @@ -141,7 +184,7 @@ impl Tick { } fn calculate_new_liquidity_gross( - self, + &self, sign: bool, liquidity_delta: Liquidity, max_liquidity_per_tick: Liquidity, diff --git a/contracts/oraiswap-v3/src/tests/add_fee_tier.rs b/contracts/oraiswap-v3/src/tests/add_fee_tier.rs index 0b4bb43..86b17c2 100644 --- a/contracts/oraiswap-v3/src/tests/add_fee_tier.rs +++ b/contracts/oraiswap-v3/src/tests/add_fee_tier.rs @@ -1,6 +1,7 @@ use crate::math::types::percentage::Percentage; use crate::msg::QueryMsg; use crate::tests::helper::{macros::*, MockApp}; +use crate::ContractError; use crate::FeeTier; use decimal::Decimal; @@ -30,8 +31,12 @@ fn test_add_fee_tier_not_admin() { let dex = create_dex!(app, Percentage::new(0)); let fee_tier = FeeTier::new(Percentage::new(1), 1).unwrap(); - let result = add_fee_tier!(app, dex, fee_tier, "bob").unwrap_err(); - assert!(result.contains("error executing WasmMsg")); + let error = add_fee_tier!(app, dex, fee_tier, "bob").unwrap_err(); + + assert_eq!( + error.root_cause().to_string(), + ContractError::Unauthorized {}.to_string() + ); } #[test] @@ -53,9 +58,12 @@ fn test_add_fee_tier_tick_spacing_zero() { fee: Percentage::new(1), tick_spacing: 0, }; - let result = add_fee_tier!(app, dex, fee_tier, "alice").unwrap_err(); + let error = add_fee_tier!(app, dex, fee_tier, "alice").unwrap_err(); - assert!(result.contains("error executing WasmMsg")); + assert_eq!( + error.root_cause().to_string(), + ContractError::InvalidTickSpacing {}.to_string() + ); } #[test] @@ -67,9 +75,12 @@ fn test_add_fee_tier_over_upper_bound_tick_spacing() { fee: Percentage::new(1), tick_spacing: 101, }; - let result = add_fee_tier!(app, dex, fee_tier, "alice").unwrap_err(); + let error = add_fee_tier!(app, dex, fee_tier, "alice").unwrap_err(); - assert!(result.contains("error executing WasmMsg")); + assert_eq!( + error.root_cause().to_string(), + ContractError::InvalidTickSpacing {}.to_string() + ); } #[test] @@ -81,7 +92,10 @@ fn test_add_fee_tier_fee_above_limit() { fee: Percentage::new(1000000000000), tick_spacing: 10, }; - let result = add_fee_tier!(app, dex, fee_tier, "alice").unwrap_err(); + let error = add_fee_tier!(app, dex, fee_tier, "alice").unwrap_err(); - assert!(result.contains("error executing WasmMsg")); + assert_eq!( + error.root_cause().to_string(), + ContractError::InvalidFee {}.to_string() + ); } diff --git a/contracts/oraiswap-v3/src/tests/admin.rs b/contracts/oraiswap-v3/src/tests/admin.rs index 2549961..90d7fbc 100644 --- a/contracts/oraiswap-v3/src/tests/admin.rs +++ b/contracts/oraiswap-v3/src/tests/admin.rs @@ -2,6 +2,7 @@ use crate::msg::{ExecuteMsg, QueryMsg}; use crate::percentage::Percentage; use crate::tests::helper::macros::*; use crate::tests::helper::MockApp; +use crate::ContractError; use cosmwasm_std::Addr; use decimal::Decimal; @@ -38,7 +39,7 @@ fn test_change_admin_not_admin() { let execute_msg = ExecuteMsg::ChangeAdmin { new_admin: Addr::unchecked("bob"), }; - let result = app + let error = app .execute( Addr::unchecked("bob"), Addr::unchecked(dex.clone()), @@ -47,5 +48,8 @@ fn test_change_admin_not_admin() { ) .unwrap_err(); - assert!(result.contains("error executing WasmMsg")); + assert_eq!( + error.root_cause().to_string(), + ContractError::Unauthorized {}.to_string() + ); } diff --git a/contracts/oraiswap-v3/src/tests/change_fee_receiver.rs b/contracts/oraiswap-v3/src/tests/change_fee_receiver.rs index 8720be6..49cf2a1 100644 --- a/contracts/oraiswap-v3/src/tests/change_fee_receiver.rs +++ b/contracts/oraiswap-v3/src/tests/change_fee_receiver.rs @@ -2,6 +2,7 @@ use crate::math::types::percentage::Percentage; use crate::math::types::sqrt_price::calculate_sqrt_price; use crate::tests::helper::macros::*; use crate::tests::helper::MockApp; +use crate::ContractError; use crate::{FeeTier, PoolKey}; use cosmwasm_std::Addr; use decimal::Decimal; @@ -31,7 +32,8 @@ fn test_change_fee_reciever() { ); assert!(result.is_ok()); - let pool_key = PoolKey::new(token_x.to_string(), token_y.to_string(), fee_tier.clone()).unwrap(); + let pool_key = + PoolKey::new(token_x.to_string(), token_y.to_string(), fee_tier.clone()).unwrap(); let result = change_fee_receiver!(app, dex, pool_key, "alice", "alice"); assert!(result.is_ok()); @@ -65,7 +67,12 @@ fn test_not_admin_change_fee_reciever() { ); assert!(result.is_ok()); - let pool_key = PoolKey::new(token_x.to_string(), token_y.to_string(), fee_tier.clone()).unwrap(); - let result = change_fee_receiver!(app, dex, pool_key, "bob", "bob").unwrap_err(); - assert!(result.contains("error executing WasmMsg")); + let pool_key = + PoolKey::new(token_x.to_string(), token_y.to_string(), fee_tier.clone()).unwrap(); + let error = change_fee_receiver!(app, dex, pool_key, "bob", "bob").unwrap_err(); + + assert_eq!( + error.root_cause().to_string(), + ContractError::Unauthorized {}.to_string() + ); } diff --git a/contracts/oraiswap-v3/src/tests/change_protocol_fee.rs b/contracts/oraiswap-v3/src/tests/change_protocol_fee.rs index 9ddb4f6..d87e319 100644 --- a/contracts/oraiswap-v3/src/tests/change_protocol_fee.rs +++ b/contracts/oraiswap-v3/src/tests/change_protocol_fee.rs @@ -2,6 +2,7 @@ use crate::msg::{ExecuteMsg, QueryMsg}; use crate::percentage::Percentage; use crate::tests::helper::macros::*; use crate::tests::helper::MockApp; +use crate::ContractError; use cosmwasm_std::Addr; use decimal::Decimal; @@ -37,7 +38,7 @@ fn test_change_protocol_fee_not_admin() { let execute_msg = ExecuteMsg::ChangeProtocolFee { protocol_fee: Percentage::new(1), }; - let result = app + let error = app .execute( Addr::unchecked("bob"), Addr::unchecked(dex.clone()), @@ -46,5 +47,8 @@ fn test_change_protocol_fee_not_admin() { ) .unwrap_err(); - assert!(result.contains("error executing WasmMsg")); + assert_eq!( + error.root_cause().to_string(), + ContractError::Unauthorized {}.to_string() + ); } diff --git a/contracts/oraiswap-v3/src/tests/claim.rs b/contracts/oraiswap-v3/src/tests/claim.rs index a4b5f79..e01f20b 100644 --- a/contracts/oraiswap-v3/src/tests/claim.rs +++ b/contracts/oraiswap-v3/src/tests/claim.rs @@ -1,10 +1,14 @@ +use cosmwasm_std::Timestamp; use decimal::{Decimal, Factories}; use crate::{ + interface::AssetInfo, + liquidity::Liquidity, percentage::Percentage, + sqrt_price::{self, calculate_sqrt_price, SqrtPrice}, tests::helper::{macros::*, MockApp}, token_amount::TokenAmount, - FeeTier, + FeeTier, PoolKey, }; #[test] @@ -48,5 +52,168 @@ fn test_claim_not_owner() { init_basic_position!(app, dex, token_x, token_y); init_basic_swap!(app, dex, token_x, token_y); - claim_fee!(app, dex, 0, "bob").unwrap_err(); + let error = claim_fee!(app, dex, 0, "bob").unwrap_err(); + assert!(error.root_cause().to_string().contains("not found")); +} + +#[test] +fn claim_both_fee_and_incentives() { + let protocol_fee = Percentage::from_scale(6, 3); + let mut app = MockApp::new(&[]); + let dex = create_dex!(app, Percentage::new(0)); + let dex_raw = &dex.to_string(); + + let initial_amount = 10u128.pow(10); + let (token_x, token_y, token_z) = + create_3_tokens!(app, initial_amount, initial_amount, initial_amount); + mint!(app, token_z, dex_raw, initial_amount, "alice").unwrap(); + let (token_a, token_b) = create_tokens!(app, initial_amount, initial_amount); + mint!(app, token_a, dex_raw, initial_amount, "alice").unwrap(); + mint!(app, token_b, dex_raw, initial_amount, "alice").unwrap(); + + let fee_tier = FeeTier::new(protocol_fee, 1).unwrap(); + + add_fee_tier!(app, dex, fee_tier, "alice").unwrap(); + + let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); + create_pool!( + app, + dex, + token_x, + token_y, + fee_tier, + init_sqrt_price, + init_tick, + "alice" + ) + .unwrap(); + + let pool_key = PoolKey::new(token_x.to_string(), token_y.to_string(), fee_tier).unwrap(); + + let reward_token_1 = AssetInfo::Token { + contract_addr: token_z.clone(), + }; + let reward_token_2 = AssetInfo::Token { + contract_addr: token_a.clone(), + }; + let reward_token_3 = AssetInfo::Token { + contract_addr: token_b.clone(), + }; + let total_reward = Some(TokenAmount::from_integer(1000000000)); + let reward_per_sec = TokenAmount(100); + let start_timestamp: Option = None; + let liquidity = Liquidity::from_integer(1000000); + + // create position in range -20 - 20 + approve!(app, token_x, dex, initial_amount, "alice").unwrap(); + approve!(app, token_y, dex, initial_amount, "alice").unwrap(); + create_position!( + app, + dex, + pool_key, + -20, + 20, + liquidity, + SqrtPrice::new(0), + SqrtPrice::max_instance(), + "alice" + ) + .unwrap(); + + let timestamp_init = app.app.block_info().time.seconds(); + create_incentive!( + app, + dex, + pool_key, + reward_token_1.clone(), + total_reward, + reward_per_sec, + start_timestamp, + "alice" + ) + .unwrap(); + create_incentive!( + app, + dex, + pool_key, + reward_token_2.clone(), + total_reward, + reward_per_sec, + start_timestamp, + "alice" + ) + .unwrap(); + create_incentive!( + app, + dex, + pool_key, + reward_token_3.clone(), + total_reward, + reward_per_sec, + start_timestamp, + "alice" + ) + .unwrap(); + + let before_dex_balance_token_x = balance_of!(app, token_x, dex); + let before_dex_balance_token_z = balance_of!(app, token_z, dex); + let before_user_balance_token_x = balance_of!(app, token_x, "alice"); + let before_user_balance_token_z = balance_of!(app, token_z, "alice"); + + // swap to increase fee growth + mint!(app, token_x, "bob", initial_amount, "alice").unwrap(); + approve!(app, token_x, dex, initial_amount, "bob").unwrap(); + swap!( + app, + dex, + pool_key, + true, + TokenAmount(1000), + true, + sqrt_price::get_min_sqrt_price(fee_tier.tick_spacing), + "bob" + ) + .unwrap(); + + // increase time to have incentives + let mut block_info = app.app.block_info(); + let current_timestamp = block_info.time.seconds(); + block_info.time = Timestamp::from_seconds(current_timestamp + 1000); + app.app.set_block(block_info.clone()); + + // claim both + claim_fee!(app, dex, 0, "alice").unwrap(); + + let timestamp_after = app.app.block_info().time.seconds(); + let total_emit = (timestamp_after - timestamp_init) as u128 * reward_per_sec.0; + + let after_dex_balance_token_x = balance_of!(app, token_x, dex); + let after_dex_balance_token_z = balance_of!(app, token_z, dex); + let after_user_balance_token_x = balance_of!(app, token_x, "alice"); + let after_user_balance_token_z = balance_of!(app, token_z, "alice"); + + // incentive assert + assert!(before_dex_balance_token_z.gt(&after_dex_balance_token_z)); + assert!(before_user_balance_token_z.lt(&after_user_balance_token_z)); + assert!((before_user_balance_token_z + before_dex_balance_token_z) + .eq(&(after_user_balance_token_z + after_dex_balance_token_z))); + assert!((after_user_balance_token_z - before_user_balance_token_z).le(&total_emit)); + + // fee claimed assert + let position = get_position!(app, dex, 0, "alice").unwrap(); + let fee_tokens_claimed = 6; + let receive_x_for_dex = 994; + let pool = get_pool!(app, dex, token_x, token_y, fee_tier).unwrap(); + + assert_eq!( + before_user_balance_token_x + fee_tokens_claimed, + after_user_balance_token_x + ); + assert_eq!( + before_dex_balance_token_x + receive_x_for_dex, + after_dex_balance_token_x + ); + assert_eq!(position.fee_growth_inside_x, pool.fee_growth_global_x); + assert_eq!(position.tokens_owed_x, TokenAmount(0)); } diff --git a/contracts/oraiswap-v3/src/tests/get_position_ticks.rs b/contracts/oraiswap-v3/src/tests/get_position_ticks.rs index d6952c8..a5ef4c8 100644 --- a/contracts/oraiswap-v3/src/tests/get_position_ticks.rs +++ b/contracts/oraiswap-v3/src/tests/get_position_ticks.rs @@ -2,6 +2,9 @@ use cosmwasm_std::coin; use cosmwasm_std::Addr; use decimal::{Decimal, Factories}; +use crate::fee_growth::FeeGrowth; +use crate::token_amount::TokenAmount; +use crate::Position; use crate::POSITION_TICK_LIMIT; use crate::{ liquidity::Liquidity, @@ -220,3 +223,112 @@ fn test_get_position_ticks_with_offset() { assert_eq!(result_1[2], result_2[0]); assert_eq!(result_1[3], result_2[1]); } + +#[test] +fn test_query_all_positions() { + let initial_mint = 10u128.pow(10); + let mut app = MockApp::new(&[("alice", &[coin(initial_mint, "orai")])]); + + let dex = app + .create_dex("alice", Percentage::from_scale(1, 2)) + .unwrap(); + + let initial_amount = 10u128.pow(10); + let (token_x, token_y) = create_tokens!(app, initial_amount, initial_amount); + + let fee_tier = FeeTier::new(Percentage::from_scale(1, 2), 1).unwrap(); + + add_fee_tier!(app, dex, fee_tier, "alice").unwrap(); + + let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); + create_pool!( + app, + dex, + token_x, + token_y, + fee_tier, + init_sqrt_price, + init_tick, + "alice" + ) + .unwrap(); + + approve!(app, token_x, dex, 500, "alice").unwrap(); + approve!(app, token_y, dex, 500, "alice").unwrap(); + + let pool_key = PoolKey::new(token_x.to_string(), token_y.to_string(), fee_tier).unwrap(); + create_position!( + app, + dex, + pool_key, + -10, + 10, + Liquidity::new(10), + SqrtPrice::new(0), + SqrtPrice::max_instance(), + "alice" + ) + .unwrap(); + create_position!( + app, + dex, + pool_key, + -100, + 100, + Liquidity::new(10), + SqrtPrice::new(0), + SqrtPrice::max_instance(), + "alice" + ) + .unwrap(); + let positions = app.query_all_positions(dex.as_str(), None, None).unwrap(); + assert_eq!(positions.len(), 2); + assert_eq!( + positions, + [ + Position { + pool_key: PoolKey { + token_x: String::from("contract1"), + token_y: String::from("contract2"), + fee_tier: FeeTier { + fee: Percentage(10000000000), + tick_spacing: 1 + } + }, + liquidity: Liquidity(10), + lower_tick_index: -10, + upper_tick_index: 10, + fee_growth_inside_x: FeeGrowth(0), + fee_growth_inside_y: FeeGrowth(0), + last_block_number: 12353, + tokens_owed_x: TokenAmount(0), + tokens_owed_y: TokenAmount(0), + approvals: vec![], + token_id: 1, + incentives: vec![] + }, + Position { + pool_key: PoolKey { + token_x: String::from("contract1"), + token_y: String::from("contract2"), + fee_tier: FeeTier { + fee: Percentage(10000000000), + tick_spacing: 1 + } + }, + liquidity: Liquidity(10), + lower_tick_index: -100, + upper_tick_index: 100, + fee_growth_inside_x: FeeGrowth(0), + fee_growth_inside_y: FeeGrowth(0), + last_block_number: 12354, + tokens_owed_x: TokenAmount(0), + tokens_owed_y: TokenAmount(0), + token_id: 2, + approvals: vec![], + incentives: vec![] + } + ] + ) +} diff --git a/contracts/oraiswap-v3/src/tests/helper.rs b/contracts/oraiswap-v3/src/tests/helper.rs index a5a8911..1545774 100644 --- a/contracts/oraiswap-v3/src/tests/helper.rs +++ b/contracts/oraiswap-v3/src/tests/helper.rs @@ -1,9 +1,8 @@ -use cosmwasm_std::{Addr, Coin, Event, StdResult}; -use cw_multi_test::{AppResponse, ContractWrapper}; +use cosmwasm_std::{Addr, Binary, Coin, Event, StdResult, Timestamp}; +use cosmwasm_testing_util::{AppResponse, ContractWrapper, MockResult}; use crate::{ - interface::SwapHop, - interface::{PoolWithPoolKey, QuoteResult}, + interface::{Asset, AssetInfo, PoolWithPoolKey, QuoteResult, SwapHop}, liquidity::Liquidity, msg::{self}, percentage::Percentage, @@ -36,7 +35,7 @@ impl MockApp { Self { app, dex_id } } - pub fn create_dex(&mut self, owner: &str, protocol_fee: Percentage) -> Result { + pub fn create_dex(&mut self, owner: &str, protocol_fee: Percentage) -> MockResult { let code_id = self.dex_id; self.instantiate( code_id, @@ -52,7 +51,7 @@ impl MockApp { sender: &str, dex: &str, fee_tier: FeeTier, - ) -> Result { + ) -> MockResult { self.execute( Addr::unchecked(sender), Addr::unchecked(dex), @@ -66,7 +65,7 @@ impl MockApp { sender: &str, dex: &str, fee_tier: FeeTier, - ) -> Result { + ) -> MockResult { self.execute( Addr::unchecked(sender), Addr::unchecked(dex), @@ -84,7 +83,7 @@ impl MockApp { fee_tier: FeeTier, init_sqrt_price: SqrtPrice, init_tick: i32, - ) -> Result { + ) -> MockResult { self.execute( Addr::unchecked(sender), Addr::unchecked(dex), @@ -104,7 +103,7 @@ impl MockApp { sender: &str, dex: &str, pool_key: &PoolKey, - ) -> Result { + ) -> MockResult { self.execute( Addr::unchecked(sender), Addr::unchecked(dex), @@ -121,7 +120,7 @@ impl MockApp { dex: &str, pool_key: &PoolKey, fee_recevier: &str, - ) -> Result { + ) -> MockResult { self.execute( Addr::unchecked(sender), Addr::unchecked(dex), @@ -143,7 +142,7 @@ impl MockApp { liquidity_delta: Liquidity, slippage_limit_lower: SqrtPrice, slippage_limit_upper: SqrtPrice, - ) -> Result { + ) -> MockResult { self.execute( Addr::unchecked(sender), Addr::unchecked(dex), @@ -165,7 +164,7 @@ impl MockApp { dex: &str, index: u32, receiver: &str, - ) -> Result { + ) -> MockResult { self.execute( Addr::unchecked(sender), Addr::unchecked(dex), @@ -182,7 +181,7 @@ impl MockApp { sender: &str, dex: &str, index: u32, - ) -> Result { + ) -> MockResult { self.execute( Addr::unchecked(sender), Addr::unchecked(dex), @@ -199,7 +198,7 @@ impl MockApp { expected_amount_out: TokenAmount, slippage: Percentage, swaps: Vec, - ) -> Result { + ) -> MockResult { self.execute( Addr::unchecked(sender), Addr::unchecked(dex), @@ -222,7 +221,7 @@ impl MockApp { amount: TokenAmount, by_amount_in: bool, sqrt_price_limit: SqrtPrice, - ) -> Result { + ) -> MockResult { self.execute( Addr::unchecked(sender), Addr::unchecked(dex), @@ -237,16 +236,25 @@ impl MockApp { ) } - pub fn claim_fee( + pub fn claim_fee(&mut self, sender: &str, dex: &str, index: u32) -> MockResult { + self.execute( + Addr::unchecked(sender), + Addr::unchecked(dex), + &msg::ExecuteMsg::ClaimFee { index }, + &[], + ) + } + + pub fn claim_incentives( &mut self, sender: &str, dex: &str, index: u32, - ) -> Result { + ) -> MockResult { self.execute( Addr::unchecked(sender), Addr::unchecked(dex), - &msg::ExecuteMsg::ClaimFee { index }, + &msg::ExecuteMsg::ClaimIncentive { index }, &[], ) } @@ -338,6 +346,21 @@ impl MockApp { ) } + pub fn get_position_incentives( + &self, + dex: &str, + owner_id: &str, + index: u32, + ) -> StdResult> { + self.query( + Addr::unchecked(dex), + &msg::QueryMsg::PositionIncentives { + owner_id: Addr::unchecked(owner_id), + index, + }, + ) + } + pub fn get_all_positions(&self, dex: &str, owner_id: &str) -> StdResult> { self.query( Addr::unchecked(dex), @@ -381,12 +404,45 @@ impl MockApp { ) } - pub fn assert_fail(&self, res: Result) { - // new version of cosmwasm does not return detail error - match res.err() { - Some(msg) => assert!(msg.contains("error executing WasmMsg")), - None => panic!("Must return generic error"), - } + pub fn create_incentive( + &mut self, + sender: &str, + dex: &str, + pool_key: &PoolKey, + reward_token: AssetInfo, + total_reward: Option, + reward_per_sec: TokenAmount, + start_timestamp: Option, + ) -> MockResult { + self.execute( + Addr::unchecked(sender), + Addr::unchecked(dex), + &msg::ExecuteMsg::CreateIncentive { + pool_key: pool_key.clone(), + reward_token, + total_reward, + reward_per_sec, + start_timestamp, + }, + &[], + ) + } + pub fn query_all_positions( + &self, + dex: &str, + limit: Option, + start_after: Option, + ) -> StdResult> { + self.query( + Addr::unchecked(dex), + &msg::QueryMsg::AllPosition { limit, start_after }, + ) + } + + pub fn increase_block_time_by_seconds(&mut self, seconds: u64) { + let mut block_info = self.app.app.block_info(); + block_info.time = Timestamp::from_seconds(block_info.time.seconds() + seconds); + self.app.app.set_block(block_info); } } @@ -494,6 +550,21 @@ pub mod macros { } pub(crate) use fee_tier_exist; + macro_rules! create_incentive { + ($app:ident, $dex_address:expr, $pool_key:expr, $reward_token:expr, $total_reward:expr, $reward_per_sec:expr, $start_timestamp:expr, $caller:tt) => {{ + $app.create_incentive( + $caller, + $dex_address.as_str(), + &$pool_key, + $reward_token, + $total_reward, + $reward_per_sec, + $start_timestamp, + ) + }}; + } + pub(crate) use create_incentive; + macro_rules! create_position { ($app:ident, $dex_address:expr, $pool_key:expr, $lower_tick:expr, $upper_tick:expr, $liquidity_delta:expr, $slippage_limit_lower:expr, $slippage_limit_upper:expr, $caller:tt) => {{ $app.create_position( @@ -536,6 +607,13 @@ pub mod macros { } pub(crate) use get_position; + macro_rules! get_position_incentives { + ($app:ident, $dex_address:expr, $index:expr, $owner:tt) => {{ + $app.get_position_incentives($dex_address.as_str(), $owner, $index) + }}; + } + pub(crate) use get_position_incentives; + macro_rules! get_tick { ($app:ident, $dex_address:expr, $key:expr, $index:expr) => {{ $app.get_tick($dex_address.as_str(), &$key, $index) @@ -630,6 +708,13 @@ pub mod macros { } pub(crate) use claim_fee; + macro_rules! claim_incentives { + ($app:ident, $dex_address:expr, $index:expr, $caller:tt) => {{ + $app.claim_incentives($caller, $dex_address.as_str(), $index) + }}; + } + pub(crate) use claim_incentives; + macro_rules! init_slippage_pool_with_liquidity { ($app:ident, $dex_address:ident, $token_x_address:ident, $token_y_address:ident) => {{ let fee_tier = FeeTier { diff --git a/contracts/oraiswap-v3/src/tests/incentive.rs b/contracts/oraiswap-v3/src/tests/incentive.rs new file mode 100644 index 0000000..e88b3e9 --- /dev/null +++ b/contracts/oraiswap-v3/src/tests/incentive.rs @@ -0,0 +1,1580 @@ +use cosmwasm_std::{Addr, Timestamp, Uint128}; +use decimal::*; + +use crate::{ + fee_growth::FeeGrowth, + incentive::{IncentiveRecord, PositionIncentives}, + interface::{Asset, AssetInfo}, + liquidity::Liquidity, + percentage::Percentage, + sqrt_price::{calculate_sqrt_price, SqrtPrice}, + tests::helper::{macros::*, MockApp}, + token_amount::TokenAmount, + ContractError, FeeTier, PoolKey, MAX_SQRT_PRICE, MIN_SQRT_PRICE, +}; + +#[test] +pub fn test_create_incentive() { + let mut app = MockApp::new(&[]); + let dex = create_dex!(app, Percentage::new(0)); + let (token_x, token_y) = create_tokens!(app, 500, 500); + + let fee_tier = FeeTier::new(Percentage::new(0), 1).unwrap(); + + add_fee_tier!(app, dex, fee_tier, "alice").unwrap(); + + let init_tick = 10; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); + create_pool!( + app, + dex, + token_x, + token_y, + fee_tier, + init_sqrt_price, + init_tick, + "alice" + ) + .unwrap(); + + let pool_key = PoolKey::new(token_x.to_string(), token_y.to_string(), fee_tier).unwrap(); + + let reward_token = AssetInfo::NativeToken { + denom: "orai".to_string(), + }; + let total_reward = Some(TokenAmount(1000000000)); + let reward_per_sec = TokenAmount(100); + let start_timestamp: Option = None; + let current_time = app.app.block_info().time.seconds(); + create_incentive!( + app, + dex, + pool_key, + reward_token.clone(), + total_reward, + reward_per_sec, + start_timestamp, + "alice" + ) + .unwrap(); + + let pool = get_pool!(app, dex, token_x, token_y, fee_tier).unwrap(); + assert_eq!( + pool.incentives, + vec![IncentiveRecord { + id: 0, + reward_per_sec, + reward_token: reward_token.clone(), + remaining: total_reward.unwrap(), + start_timestamp: current_time, + incentive_growth_global: FeeGrowth(0), + last_updated: current_time + }] + ); + + // create other incentives + let new_timestamp_time = app.app.block_info().time.seconds(); + create_incentive!( + app, + dex, + pool_key, + reward_token.clone(), + total_reward, + reward_per_sec, + start_timestamp, + "alice" + ) + .unwrap(); + let pool = get_pool!(app, dex, token_x, token_y, fee_tier).unwrap(); + assert_eq!( + pool.incentives, + vec![ + IncentiveRecord { + id: 0, + reward_per_sec, + reward_token: reward_token.clone(), + remaining: total_reward.unwrap(), + start_timestamp: current_time, + incentive_growth_global: FeeGrowth(0), + last_updated: new_timestamp_time + }, + IncentiveRecord { + id: 1, + reward_per_sec, + reward_token: reward_token.clone(), + remaining: total_reward.unwrap(), + start_timestamp: new_timestamp_time, + incentive_growth_global: FeeGrowth(0), + last_updated: new_timestamp_time + } + ] + ); + + // create incentive with no total reward -> fallback to max:u128 + let latest_timestamp_time = app.app.block_info().time.seconds(); + create_incentive!( + app, + dex, + pool_key, + reward_token.clone(), + None, + reward_per_sec, + start_timestamp, + "alice" + ) + .unwrap(); + let pool = get_pool!(app, dex, token_x, token_y, fee_tier).unwrap(); + assert_eq!( + pool.incentives, + vec![ + IncentiveRecord { + id: 0, + reward_per_sec, + reward_token: reward_token.clone(), + remaining: total_reward.unwrap(), + start_timestamp: current_time, + incentive_growth_global: FeeGrowth(0), + last_updated: latest_timestamp_time + }, + IncentiveRecord { + id: 1, + reward_per_sec, + reward_token: reward_token.clone(), + remaining: total_reward.unwrap(), + start_timestamp: new_timestamp_time, + incentive_growth_global: FeeGrowth(0), + last_updated: latest_timestamp_time + }, + IncentiveRecord { + id: 2, + reward_per_sec, + reward_token: reward_token.clone(), + remaining: TokenAmount(u128::MAX), + start_timestamp: latest_timestamp_time, + incentive_growth_global: FeeGrowth(0), + last_updated: latest_timestamp_time + } + ] + ); + + // create fail, unauthorized + let error = create_incentive!( + app, + dex, + pool_key, + reward_token.clone(), + total_reward, + reward_per_sec, + start_timestamp, + "bob" + ) + .unwrap_err(); + assert!(error + .root_cause() + .to_string() + .contains(&ContractError::Unauthorized {}.to_string())); +} + +#[test] +pub fn test_single_incentive_with_single_position() { + let mut app = MockApp::new(&[]); + let dex = create_dex!(app, Percentage::new(0)); + let (token_x, token_y) = create_tokens!(app, 500, 500); + + let fee_tier = FeeTier::new(Percentage::new(0), 1).unwrap(); + + add_fee_tier!(app, dex, fee_tier, "alice").unwrap(); + + let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); + create_pool!( + app, + dex, + token_x, + token_y, + fee_tier, + init_sqrt_price, + init_tick, + "alice" + ) + .unwrap(); + + let pool_key = PoolKey::new(token_x.to_string(), token_y.to_string(), fee_tier).unwrap(); + + let reward_token = AssetInfo::NativeToken { + denom: "orai".to_string(), + }; + let total_reward = Some(TokenAmount(1000000)); + let reward_per_sec = TokenAmount(100); + let start_timestamp: Option = None; + create_incentive!( + app, + dex, + pool_key, + reward_token.clone(), + total_reward, + reward_per_sec, + start_timestamp, + "alice" + ) + .unwrap(); + + // create position + approve!(app, token_x, dex, 5000, "alice").unwrap(); + approve!(app, token_y, dex, 5000, "alice").unwrap(); + + let pool_key = PoolKey::new(token_x.to_string(), token_y.to_string(), fee_tier).unwrap(); + + let block_info = app.app.block_info(); + create_position!( + app, + dex, + pool_key, + -10, + 10, + Liquidity::new(1000), + SqrtPrice::new(0), + SqrtPrice::max_instance(), + "alice" + ) + .unwrap(); + + // No incentive available after creating the position + let position_state = get_position!(app, dex, 0, "alice").unwrap(); + + assert_eq!( + position_state.incentives, + vec![PositionIncentives { + incentive_id: 0, + pending_rewards: TokenAmount(0), + incentive_growth_inside: FeeGrowth(0) + }] + ); + // set block_info to ensure after create position, block time not change + app.app.set_block(block_info); + let incentives = get_position_incentives!(app, dex, 0, "alice").unwrap(); + assert_eq!(incentives, vec![]); + + // try increase block time to 1000s + // => totalReward for position = 100 * 1000 = 100000; + let mut block_info = app.app.block_info(); + block_info.time = Timestamp::from_seconds(block_info.time.seconds() + 1000); + app.app.set_block(block_info); + + // get position + let incentives = get_position_incentives!(app, dex, 0, "alice").unwrap(); + assert_eq!( + incentives, + vec![Asset { + info: reward_token.clone(), + amount: Uint128::from(100000u128) + }] + ); + + // reach limit of total reward + block_info = app.app.block_info(); + block_info.time = Timestamp::from_seconds(block_info.time.seconds() + 1000000); + app.app.set_block(block_info); + let incentives = get_position_incentives!(app, dex, 0, "alice").unwrap(); + assert_eq!( + incentives, + vec![Asset { + info: reward_token.clone(), + amount: Uint128::from(1000000u128) + }] + ); +} + +#[test] +pub fn test_multi_incentives_with_single_position() { + let mut app = MockApp::new(&[]); + let dex = create_dex!(app, Percentage::new(0)); + let (token_x, token_y) = create_tokens!(app, 500, 500); + + let fee_tier = FeeTier::new(Percentage::new(0), 1).unwrap(); + + add_fee_tier!(app, dex, fee_tier, "alice").unwrap(); + + let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); + create_pool!( + app, + dex, + token_x, + token_y, + fee_tier, + init_sqrt_price, + init_tick, + "alice" + ) + .unwrap(); + + let pool_key = PoolKey::new(token_x.to_string(), token_y.to_string(), fee_tier).unwrap(); + + let reward_token = AssetInfo::NativeToken { + denom: "orai".to_string(), + }; + let reward_token_2 = AssetInfo::Token { + contract_addr: Addr::unchecked("usdt"), + }; + let total_reward = Some(TokenAmount(1000000)); + let reward_per_sec = TokenAmount(100); + let start_timestamp: Option = None; + create_incentive!( + app, + dex, + pool_key, + reward_token.clone(), + total_reward, + reward_per_sec, + start_timestamp, + "alice" + ) + .unwrap(); + create_incentive!( + app, + dex, + pool_key, + reward_token_2.clone(), + Some(TokenAmount(1000000000)), + TokenAmount(200), + start_timestamp, + "alice" + ) + .unwrap(); + + // create position + approve!(app, token_x, dex, 5000, "alice").unwrap(); + approve!(app, token_y, dex, 5000, "alice").unwrap(); + + let pool_key = PoolKey::new(token_x.to_string(), token_y.to_string(), fee_tier).unwrap(); + + let block_info = app.app.block_info(); + create_position!( + app, + dex, + pool_key, + -10, + 10, + Liquidity::new(1000), + SqrtPrice::new(0), + SqrtPrice::max_instance(), + "alice" + ) + .unwrap(); + + // No incentive available after creating the position + let position_state = get_position!(app, dex, 0, "alice").unwrap(); + + assert_eq!( + position_state.incentives, + vec![ + PositionIncentives { + incentive_id: 0, + pending_rewards: TokenAmount(0), + incentive_growth_inside: FeeGrowth(0) + }, + PositionIncentives { + incentive_id: 1, + pending_rewards: TokenAmount(0), + incentive_growth_inside: FeeGrowth(0) + } + ] + ); + // set block_info to ensure after create position, block time not change + app.app.set_block(block_info); + let incentives = get_position_incentives!(app, dex, 0, "alice").unwrap(); + assert_eq!(incentives, vec![]); + + // try increase block time to 1000s + // => totalReward for position = 100 * 1000 = 100000; + let mut block_info = app.app.block_info(); + block_info.time = Timestamp::from_seconds(block_info.time.seconds() + 1000); + app.app.set_block(block_info); + + // get position + let incentives = get_position_incentives!(app, dex, 0, "alice").unwrap(); + assert_eq!( + incentives, + vec![ + Asset { + info: reward_token.clone(), + amount: Uint128::from(100000u128) + }, + Asset { + info: reward_token_2.clone(), + amount: Uint128::from(200000u128) + } + ] + ); + + // Reached the limit of the total reward for the first incentive, + // and the calculation for the second incentive is impacted by overflow. + block_info = app.app.block_info(); + let current_timestamp = block_info.time.seconds(); + block_info.time = Timestamp::from_seconds(current_timestamp + 1000000); + app.app.set_block(block_info.clone()); + let incentives = get_position_incentives!(app, dex, 0, "alice").unwrap(); + assert_eq!( + incentives, + vec![Asset { + info: reward_token.clone(), + amount: Uint128::from(1000000u128) + }] + ); + + // success + block_info.time = Timestamp::from_seconds(current_timestamp + 20000); + app.app.set_block(block_info); + let incentives = get_position_incentives!(app, dex, 0, "alice").unwrap(); + assert_eq!( + incentives, + vec![ + Asset { + info: reward_token.clone(), + amount: Uint128::from(1000000u128) + }, + Asset { + info: reward_token_2.clone(), + amount: Uint128::from(4200000u128) + } + ] + ); +} + +#[test] +pub fn test_multi_incentives_with_multi_positions() { + let mut app = MockApp::new(&[]); + let dex = create_dex!(app, Percentage::new(0)); + let (token_x, token_y) = create_tokens!(app, 500, 500); + + let fee_tier = FeeTier::new(Percentage::new(0), 1).unwrap(); + + add_fee_tier!(app, dex, fee_tier, "alice").unwrap(); + + let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); + create_pool!( + app, + dex, + token_x, + token_y, + fee_tier, + init_sqrt_price, + init_tick, + "alice" + ) + .unwrap(); + + let pool_key = PoolKey::new(token_x.to_string(), token_y.to_string(), fee_tier).unwrap(); + + let reward_token = AssetInfo::NativeToken { + denom: "orai".to_string(), + }; + let reward_token_2 = AssetInfo::Token { + contract_addr: Addr::unchecked("usdt"), + }; + let total_reward = Some(TokenAmount(1000000)); + let reward_per_sec = TokenAmount(100); + let start_timestamp: Option = None; + create_incentive!( + app, + dex, + pool_key, + reward_token.clone(), + total_reward, + reward_per_sec, + start_timestamp, + "alice" + ) + .unwrap(); + create_incentive!( + app, + dex, + pool_key, + reward_token_2.clone(), + Some(TokenAmount(1000000000)), + TokenAmount(200), + start_timestamp, + "alice" + ) + .unwrap(); + + // create position + approve!(app, token_x, dex, 5000, "alice").unwrap(); + approve!(app, token_y, dex, 5000, "alice").unwrap(); + + let pool_key = PoolKey::new(token_x.to_string(), token_y.to_string(), fee_tier).unwrap(); + + let block_info = app.app.block_info(); + create_position!( + app, + dex, + pool_key, + -10, + 10, + Liquidity::new(1000), + SqrtPrice::new(0), + SqrtPrice::max_instance(), + "alice" + ) + .unwrap(); + + // No incentive available after creating the position + let position_state = get_position!(app, dex, 0, "alice").unwrap(); + + assert_eq!( + position_state.incentives, + vec![ + PositionIncentives { + incentive_id: 0, + pending_rewards: TokenAmount(0), + incentive_growth_inside: FeeGrowth(0) + }, + PositionIncentives { + incentive_id: 1, + pending_rewards: TokenAmount(0), + incentive_growth_inside: FeeGrowth(0) + } + ] + ); + // set block_info to ensure after create position, block time not change + app.app.set_block(block_info); + let incentives = get_position_incentives!(app, dex, 0, "alice").unwrap(); + assert_eq!(incentives, vec![]); + + // try increase block time to 1000s + // => totalReward for position = 100 * 1000 = 100000; + let mut block_info = app.app.block_info(); + block_info.time = Timestamp::from_seconds(block_info.time.seconds() + 1000); + app.app.set_block(block_info); + + // get position + let incentives = get_position_incentives!(app, dex, 0, "alice").unwrap(); + assert_eq!( + incentives, + vec![ + Asset { + info: reward_token.clone(), + amount: Uint128::from(100000u128) + }, + Asset { + info: reward_token_2.clone(), + amount: Uint128::from(200000u128) + } + ] + ); + + // try create other position, with the same range, but double liquidity + create_position!( + app, + dex, + pool_key, + -10, + 10, + Liquidity::new(2000), + SqrtPrice::new(0), + SqrtPrice::max_instance(), + "alice" + ) + .unwrap(); + + // try increase 1000s + block_info = app.app.block_info(); + let current_timestamp = block_info.time.seconds(); + block_info.time = Timestamp::from_seconds(current_timestamp + 1000); + app.app.set_block(block_info.clone()); + + let incentives = get_position_incentives!(app, dex, 0, "alice").unwrap(); + assert_eq!( + incentives, + vec![ + Asset { + info: reward_token.clone(), + amount: Uint128::from(133500u128) + }, + Asset { + info: reward_token_2.clone(), + amount: Uint128::from(267000u128) + } + ] + ); + let incentives_2 = get_position_incentives!(app, dex, 1, "alice").unwrap(); + assert_eq!( + incentives_2, + vec![ + Asset { + info: reward_token.clone(), + amount: Uint128::from(67000u128) + }, + Asset { + info: reward_token_2.clone(), + amount: Uint128::from(134000u128) + } + ] + ); +} +#[test] +pub fn test_incentive_with_position_cross_out_of_range() { + let protocol_fee = Percentage::from_scale(6, 3); + let mut app = MockApp::new(&[]); + let dex = create_dex!(app, Percentage::new(0)); + let dex_raw = &dex.to_string(); + + let initial_amount = 10u128.pow(10); + let (token_x, token_y, token_z) = + create_3_tokens!(app, initial_amount, initial_amount, initial_amount); + mint!(app, token_z, dex_raw, initial_amount, "alice").unwrap(); + + let fee_tier = FeeTier::new(protocol_fee, 1).unwrap(); + + add_fee_tier!(app, dex, fee_tier, "alice").unwrap(); + + let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); + create_pool!( + app, + dex, + token_x, + token_y, + fee_tier, + init_sqrt_price, + init_tick, + "alice" + ) + .unwrap(); + + let pool_key = PoolKey::new(token_x.to_string(), token_y.to_string(), fee_tier).unwrap(); + + let reward_token = AssetInfo::Token { + contract_addr: token_z.clone(), + }; + let total_reward = Some(TokenAmount(1000000000)); + let reward_per_sec = TokenAmount(100); + let start_timestamp: Option = None; + let liquidity = Liquidity::from_integer(1000000); + create_incentive!( + app, + dex, + pool_key, + reward_token.clone(), + total_reward, + reward_per_sec, + start_timestamp, + "alice" + ) + .unwrap(); + + approve!(app, token_x, dex, initial_amount, "alice").unwrap(); + approve!(app, token_y, dex, initial_amount, "alice").unwrap(); + + // create 2 position + // first_pos: range (-20, 20) + // second_pos: range (10, 50) + create_position!( + app, + dex, + pool_key, + -20, + 20, + liquidity, + SqrtPrice::new(0), + SqrtPrice::max_instance(), + "alice" + ) + .unwrap(); + create_position!( + app, + dex, + pool_key, + 10, + 50, + liquidity, + SqrtPrice::new(0), + SqrtPrice::max_instance(), + "alice" + ) + .unwrap(); + + // increase 1000s, the second position does not have incentive + let mut block_info = app.app.block_info(); + let current_timestamp = block_info.time.seconds(); + block_info.time = Timestamp::from_seconds(current_timestamp + 1000); + app.app.set_block(block_info.clone()); + + let incentives = get_position_incentives!(app, dex, 0, "alice").unwrap(); + assert_eq!(incentives.len(), 1); + println!("incentives: {:?}", incentives); + let incentives_2 = get_position_incentives!(app, dex, 1, "alice").unwrap(); + assert_eq!(incentives_2, vec![]); + + // try swap to cross tick + + let amount = 1000; + let swap_amount = TokenAmount(amount); + + mint!(app, token_y, "bob", amount, "alice").unwrap(); + approve!(app, token_y, dex, amount, "bob").unwrap(); + + let target_sqrt_price = SqrtPrice::new(MAX_SQRT_PRICE); + + let target_sqrt_price = quote!( + app, + dex, + pool_key, + false, + swap_amount, + true, + target_sqrt_price + ) + .unwrap() + .target_sqrt_price; + + swap!( + app, + dex, + pool_key, + false, + swap_amount, + true, + target_sqrt_price, + "bob" + ) + .unwrap(); + let pool = get_pool!(app, dex, token_x, token_y, fee_tier).unwrap(); + assert_eq!(pool.current_tick_index, 14); + + // currently, the both position in range + let incentive_1_before = get_position_incentives!(app, dex, 0, "alice").unwrap()[0].amount; + let incentive_2_before = get_position_incentives!(app, dex, 1, "alice").unwrap()[0].amount; + + // try increase 1000s + let mut block_info = app.app.block_info(); + let current_timestamp = block_info.time.seconds(); + block_info.time = Timestamp::from_seconds(current_timestamp + 1000); + app.app.set_block(block_info.clone()); + + let incentive_1_after = get_position_incentives!(app, dex, 0, "alice").unwrap()[0].amount; + let incentive_2_after = get_position_incentives!(app, dex, 1, "alice").unwrap()[0].amount; + + assert!(incentive_1_before.lt(&incentive_1_after)); + assert!(incentive_2_before.lt(&incentive_2_after)); + let emit = Uint128::new(100 * 1000); + assert!( + (incentive_1_after - incentive_1_before + incentive_2_after - incentive_2_before).le(&emit) + ); + + //try swap to out of range of fist position + let amount = 1000; + let swap_amount = TokenAmount(amount); + + mint!(app, token_y, "bob", amount, "alice").unwrap(); + approve!(app, token_y, dex, amount, "bob").unwrap(); + + let target_sqrt_price = SqrtPrice::new(MAX_SQRT_PRICE); + + let target_sqrt_price = quote!( + app, + dex, + pool_key, + false, + swap_amount, + true, + target_sqrt_price + ) + .unwrap() + .target_sqrt_price; + + swap!( + app, + dex, + pool_key, + false, + swap_amount, + true, + target_sqrt_price, + "bob" + ) + .unwrap(); + + let pool = get_pool!(app, dex, token_x, token_y, fee_tier).unwrap(); + assert_eq!(pool.current_tick_index, 29); + + // currently, the first position is out_of_range, but the second position still in range + let incentive_1_before = get_position_incentives!(app, dex, 0, "alice").unwrap()[0].amount; + let incentive_2_before = get_position_incentives!(app, dex, 1, "alice").unwrap()[0].amount; + + // try increase 1000s + let mut block_info = app.app.block_info(); + let current_timestamp = block_info.time.seconds(); + block_info.time = Timestamp::from_seconds(current_timestamp + 1000); + app.app.set_block(block_info.clone()); + + let incentive_1_after = get_position_incentives!(app, dex, 0, "alice").unwrap()[0].amount; + let incentive_2_after = get_position_incentives!(app, dex, 1, "alice").unwrap()[0].amount; + + assert!(incentive_1_before.eq(&incentive_1_after)); + assert!(incentive_2_before.lt(&incentive_2_after)); + let emit = Uint128::new(100 * 1000); + assert!( + (incentive_1_after - incentive_1_before + incentive_2_after - incentive_2_before).le(&emit) + ); + + // try claim incentives + let before_dex_balance = balance_of!(app, token_z, dex); + let before_user_balance = balance_of!(app, token_z, "alice"); + + claim_incentives!(app, dex, 0, "alice").unwrap(); + claim_incentives!(app, dex, 1, "alice").unwrap(); + + let after_dex_balance = balance_of!(app, token_z, dex); + let after_user_balance = balance_of!(app, token_z, "alice"); + assert!(before_dex_balance.gt(&after_dex_balance)); + assert!(before_user_balance.lt(&after_user_balance)); + assert!( + (before_user_balance + before_dex_balance).eq(&(after_user_balance + after_dex_balance)) + ); +} + +#[test] +pub fn test_remove_position() { + let protocol_fee = Percentage::from_scale(6, 3); + let mut app = MockApp::new(&[]); + let dex = create_dex!(app, Percentage::new(0)); + let dex_raw = &dex.to_string(); + + let initial_amount = 10u128.pow(10); + let (token_x, token_y, token_z) = + create_3_tokens!(app, initial_amount, initial_amount, initial_amount); + mint!(app, token_z, dex_raw, initial_amount, "alice").unwrap(); + + let fee_tier = FeeTier::new(protocol_fee, 1).unwrap(); + + add_fee_tier!(app, dex, fee_tier, "alice").unwrap(); + + let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); + create_pool!( + app, + dex, + token_x, + token_y, + fee_tier, + init_sqrt_price, + init_tick, + "alice" + ) + .unwrap(); + + let pool_key = PoolKey::new(token_x.to_string(), token_y.to_string(), fee_tier).unwrap(); + + let reward_token = AssetInfo::Token { + contract_addr: token_z.clone(), + }; + let total_reward = Some(TokenAmount(1000000000)); + let reward_per_sec = TokenAmount(100); + let start_timestamp: Option = None; + let liquidity = Liquidity::from_integer(1000000); + create_incentive!( + app, + dex, + pool_key, + reward_token.clone(), + total_reward, + reward_per_sec, + start_timestamp, + "alice" + ) + .unwrap(); + + // create position in range + approve!(app, token_x, dex, initial_amount, "alice").unwrap(); + approve!(app, token_y, dex, initial_amount, "alice").unwrap(); + + create_position!( + app, + dex, + pool_key, + -20, + 20, + liquidity, + SqrtPrice::new(0), + SqrtPrice::max_instance(), + "alice" + ) + .unwrap(); + + // increase block time + let mut block_info = app.app.block_info(); + let current_timestamp = block_info.time.seconds(); + block_info.time = Timestamp::from_seconds(current_timestamp + 1000); + app.app.set_block(block_info.clone()); + + let before_dex_balance = balance_of!(app, token_z, dex); + let before_user_balance = balance_of!(app, token_z, "alice"); + + // try remove position + remove_position!(app, dex, 0, "alice").unwrap(); + + let after_dex_balance = balance_of!(app, token_z, dex); + let after_user_balance = balance_of!(app, token_z, "alice"); + + assert!(before_dex_balance.gt(&after_dex_balance)); + assert!(before_user_balance.lt(&after_user_balance)); + assert!( + (before_user_balance + before_dex_balance).eq(&(after_user_balance + after_dex_balance)) + ); +} + +#[test] +pub fn incentive_stress_test() { + let protocol_fee = Percentage::from_scale(6, 3); + let mut app = MockApp::new(&[]); + let dex = create_dex!(app, Percentage::new(0)); + let dex_raw = &dex.to_string(); + + let initial_amount = 10u128.pow(20); + let (token_x, token_y, token_z) = + create_3_tokens!(app, initial_amount, initial_amount, initial_amount); + mint!(app, token_z, dex_raw, initial_amount, "alice").unwrap(); + approve!(app, token_x, dex, initial_amount, "alice").unwrap(); + approve!(app, token_y, dex, initial_amount, "alice").unwrap(); + + let fee_tier = FeeTier::new(protocol_fee, 1).unwrap(); + + add_fee_tier!(app, dex, fee_tier, "alice").unwrap(); + + let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); + create_pool!( + app, + dex, + token_x, + token_y, + fee_tier, + init_sqrt_price, + init_tick, + "alice" + ) + .unwrap(); + + let pool_key = PoolKey::new(token_x.to_string(), token_y.to_string(), fee_tier).unwrap(); + + let reward_token = AssetInfo::Token { + contract_addr: token_z.clone(), + }; + let total_reward = Some(TokenAmount(1000000000)); + let start_timestamp: Option = None; + + let rps: Vec = vec![ + TokenAmount(1), + TokenAmount(5), + TokenAmount(10), + TokenAmount(49), + TokenAmount(99), + TokenAmount(10000), + ]; + + for i in 0..rps.len() { + create_incentive!( + app, + dex, + pool_key, + reward_token.clone(), + total_reward, + rps[i], + start_timestamp, + "alice" + ) + .unwrap(); + } + + // create multi position + let liq = vec![3233322, 54343223, 3223135, 2431323, 1322339, 53283, 123293]; + let ranges = vec![ + -10000, -1000, -500, -90, -40, -20, -5, 4, 12, 23, 35, 42, 63, 120, 1000, 10000, + ]; + for i in 0..1000 { + let liquidity = Liquidity::from_integer(liq[i % liq.len()]); + let tick_index = i % (ranges.len() - 1); + let lower_tick = ranges[tick_index]; + let upper_tick = ranges[tick_index + 1]; + create_position!( + app, + dex, + pool_key, + lower_tick, + upper_tick, + liquidity, + SqrtPrice::new(0), + SqrtPrice::max_instance(), + "alice" + ) + .unwrap(); + } + + // try swap + mint!(app, token_y, "bob", initial_amount, "alice").unwrap(); + approve!(app, token_y, dex, initial_amount, "bob").unwrap(); + mint!(app, token_x, "bob", initial_amount, "alice").unwrap(); + approve!(app, token_x, dex, initial_amount, "bob").unwrap(); + + let swap_amounts: Vec = vec![2323, 233, 321, 5353, 12, 932, 42, 3123, 5438]; + let x_to_y_list = vec![true, false, false, true, true, false, false, true]; + + for i in 0..1000 { + let x_to_y = x_to_y_list[i % x_to_y_list.len()]; + let swap_amount = TokenAmount(swap_amounts[i % swap_amounts.len()]); + let target_sqrt_price = if x_to_y { + SqrtPrice::new(MIN_SQRT_PRICE) + } else { + SqrtPrice::new(MAX_SQRT_PRICE) + }; + + swap!( + app, + dex, + pool_key, + x_to_y, + swap_amount, + true, + target_sqrt_price, + "bob" + ) + .unwrap(); + } + + let before_dex_balance = balance_of!(app, token_z, dex); + let before_user_balance = balance_of!(app, token_z, "alice"); + + // claim all incentives + for _ in 0..1000 { + // try remove position + remove_position!(app, dex, 0, "alice").unwrap(); + } + + let after_dex_balance = balance_of!(app, token_z, dex); + let after_user_balance = balance_of!(app, token_z, "alice"); + + assert!(before_dex_balance.gt(&after_dex_balance)); + assert!(before_user_balance.lt(&after_user_balance)); + assert!( + (before_user_balance + before_dex_balance).eq(&(after_user_balance + after_dex_balance)) + ); +} + +#[test] +pub fn test_claim_incentive_with_single_position() { + let protocol_fee = Percentage::from_scale(6, 3); + let mut app = MockApp::new(&[]); + let dex = create_dex!(app, Percentage::new(0)); + let dex_raw = &dex.to_string(); + + let initial_amount = 10u128.pow(10); + let (token_x, token_y, token_z) = + create_3_tokens!(app, initial_amount, initial_amount, initial_amount); + mint!(app, token_z, dex_raw, initial_amount, "alice").unwrap(); + + let fee_tier = FeeTier::new(protocol_fee, 1).unwrap(); + + add_fee_tier!(app, dex, fee_tier, "alice").unwrap(); + + let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); + create_pool!( + app, + dex, + token_x, + token_y, + fee_tier, + init_sqrt_price, + init_tick, + "alice" + ) + .unwrap(); + + let pool_key = PoolKey::new(token_x.to_string(), token_y.to_string(), fee_tier).unwrap(); + + let reward_token = AssetInfo::Token { + contract_addr: token_z.clone(), + }; + let total_reward = Some(TokenAmount::from_integer(1000000000)); + let reward_per_sec = TokenAmount(100); + let start_timestamp: Option = None; + let liquidity = Liquidity::from_integer(1000000); + + // create position in range + approve!(app, token_x, dex, initial_amount, "alice").unwrap(); + approve!(app, token_y, dex, initial_amount, "alice").unwrap(); + create_position!( + app, + dex, + pool_key, + -20, + 20, + liquidity, + SqrtPrice::new(0), + SqrtPrice::max_instance(), + "alice" + ) + .unwrap(); + + let timestamp_init = app.app.block_info().time.seconds(); + create_incentive!( + app, + dex, + pool_key, + reward_token.clone(), + total_reward, + reward_per_sec, + start_timestamp, + "alice" + ) + .unwrap(); + + let before_dex_balance = balance_of!(app, token_z, dex); + let before_user_balance = balance_of!(app, token_z, "alice"); + + // increase block time + for _ in 0..100 { + let mut block_info = app.app.block_info(); + let current_timestamp = block_info.time.seconds(); + block_info.time = Timestamp::from_seconds(current_timestamp + 1000); + app.app.set_block(block_info.clone()); + + // claim incentives + claim_incentives!(app, dex, 0, "alice").unwrap(); + let position_state = get_position!(app, dex, 0, "alice").unwrap(); + assert_eq!(position_state.incentives[0].pending_rewards, TokenAmount(0)); + } + let timestamp_after = app.app.block_info().time.seconds(); + let total_emit = (timestamp_after - timestamp_init) as u128 * reward_per_sec.0; + + let after_dex_balance = balance_of!(app, token_z, dex); + let after_user_balance = balance_of!(app, token_z, "alice"); + + assert!(before_dex_balance.gt(&after_dex_balance)); + assert!(before_user_balance.lt(&after_user_balance)); + assert!( + (before_user_balance + before_dex_balance).eq(&(after_user_balance + after_dex_balance)) + ); + // total claimed of user must be less than or equal total emit + assert!((after_user_balance - before_user_balance).le(&total_emit)); +} + +#[test] +pub fn test_claim_incentive_with_multi_position() { + let protocol_fee = Percentage::from_scale(6, 3); + let mut app = MockApp::new(&[]); + let dex = create_dex!(app, Percentage::new(0)); + let dex_raw = &dex.to_string(); + + let initial_amount = 10u128.pow(10); + let (token_x, token_y, token_z) = + create_3_tokens!(app, initial_amount, initial_amount, initial_amount); + mint!(app, token_z, dex_raw, initial_amount, "alice").unwrap(); + + let fee_tier = FeeTier::new(protocol_fee, 1).unwrap(); + + add_fee_tier!(app, dex, fee_tier, "alice").unwrap(); + + let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); + create_pool!( + app, + dex, + token_x, + token_y, + fee_tier, + init_sqrt_price, + init_tick, + "alice" + ) + .unwrap(); + + let pool_key = PoolKey::new(token_x.to_string(), token_y.to_string(), fee_tier).unwrap(); + + let reward_token = AssetInfo::Token { + contract_addr: token_z.clone(), + }; + let total_reward = Some(TokenAmount::from_integer(1000000000)); + let reward_per_sec = TokenAmount(100); + let start_timestamp: Option = None; + + // create position in range + approve!(app, token_x, dex, initial_amount, "alice").unwrap(); + approve!(app, token_y, dex, initial_amount, "alice").unwrap(); + let timestamp_init = app.app.block_info().time.seconds(); + create_incentive!( + app, + dex, + pool_key, + reward_token.clone(), + total_reward, + reward_per_sec, + start_timestamp, + "alice" + ) + .unwrap(); + + // create multi position + let liq = vec![3233322, 54343223, 3223135, 2431323, 1322339, 53283, 123293]; + let ranges = vec![ + -10000, -1000, -500, -90, -40, -20, -5, 4, 12, 23, 35, 42, 63, 120, 1000, 10000, + ]; + for i in 0..100 { + let liquidity = Liquidity::from_integer(liq[i % liq.len()]); + let tick_index = i % (ranges.len() - 1); + let lower_tick = ranges[tick_index]; + let upper_tick = ranges[tick_index + 1]; + create_position!( + app, + dex, + pool_key, + lower_tick, + upper_tick, + liquidity, + SqrtPrice::new(0), + SqrtPrice::max_instance(), + "alice" + ) + .unwrap(); + } + + let before_dex_balance = balance_of!(app, token_z, dex); + let before_user_balance = balance_of!(app, token_z, "alice"); + + // increase block time + for _ in 0..100 { + let mut block_info = app.app.block_info(); + let current_timestamp = block_info.time.seconds(); + block_info.time = Timestamp::from_seconds(current_timestamp + 1000); + app.app.set_block(block_info.clone()); + for i in 0..100 { + // claim incentives + claim_incentives!(app, dex, i, "alice").unwrap(); + let position_state = get_position!(app, dex, i, "alice").unwrap(); + assert_eq!(position_state.incentives[0].pending_rewards, TokenAmount(0)); + } + } + + let timestamp_after = app.app.block_info().time.seconds(); + let total_emit = (timestamp_after - timestamp_init) as u128 * reward_per_sec.0; + + let after_dex_balance = balance_of!(app, token_z, dex); + let after_user_balance = balance_of!(app, token_z, "alice"); + + assert!(before_dex_balance.gt(&after_dex_balance)); + assert!(before_user_balance.lt(&after_user_balance)); + assert!( + (before_user_balance + before_dex_balance).eq(&(after_user_balance + after_dex_balance)) + ); + // total claimed of user must be less than or equal total emit + assert!((after_user_balance - before_user_balance).le(&total_emit)); +} + +#[test] +pub fn test_update_incentive_with_tick_move_left_to_right() { + let protocol_fee = Percentage::from_scale(6, 3); + let mut app = MockApp::new(&[]); + let dex = create_dex!(app, Percentage::new(0)); + let dex_raw = &dex.to_string(); + + let initial_amount = 10u128.pow(10); + let (token_x, token_y, token_z) = + create_3_tokens!(app, initial_amount, initial_amount, initial_amount); + mint!(app, token_z, dex_raw, initial_amount, "alice").unwrap(); + + let fee_tier = FeeTier::new(protocol_fee, 1).unwrap(); + + add_fee_tier!(app, dex, fee_tier, "alice").unwrap(); + + let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); + create_pool!( + app, + dex, + token_x, + token_y, + fee_tier, + init_sqrt_price, + init_tick, + "alice" + ) + .unwrap(); + + let pool_key = PoolKey::new(token_x.to_string(), token_y.to_string(), fee_tier).unwrap(); + + let reward_token = AssetInfo::Token { + contract_addr: token_z.clone(), + }; + let total_reward = Some(TokenAmount(1000000000)); + let reward_per_sec = TokenAmount(100); + let start_timestamp: Option = None; + let liquidity = Liquidity::from_integer(1000000); + create_incentive!( + app, + dex, + pool_key, + reward_token.clone(), + total_reward, + reward_per_sec, + start_timestamp, + "alice" + ) + .unwrap(); + + approve!(app, token_x, dex, initial_amount, "alice").unwrap(); + approve!(app, token_y, dex, initial_amount, "alice").unwrap(); + + // create 2 position + // first_pos: range (10, 20) + // second_pos: range (30, 40) + create_position!( + app, + dex, + pool_key, + 10, + 20, + liquidity, + SqrtPrice::new(0), + SqrtPrice::max_instance(), + "alice" + ) + .unwrap(); + create_position!( + app, + dex, + pool_key, + 30, + 40, + liquidity, + SqrtPrice::new(0), + SqrtPrice::max_instance(), + "alice" + ) + .unwrap(); + + // Both positions do not have any incentives due to being out of range + app.increase_block_time_by_seconds(1000); + let incentive = get_position_incentives!(app, dex, 0, "alice").unwrap(); + assert_eq!(incentive, vec![]); + let incentive = get_position_incentives!(app, dex, 1, "alice").unwrap(); + assert_eq!(incentive, vec![]); + + // swap y to x, tick move left -> right + let amount = 100; + let swap_amount = TokenAmount(amount); + mint!(app, token_y, "bob", amount, "alice").unwrap(); + approve!(app, token_y, dex, amount, "bob").unwrap(); + swap!( + app, + dex, + pool_key, + false, + swap_amount, + true, + SqrtPrice::new(MAX_SQRT_PRICE), + "bob" + ) + .unwrap(); + let pool = get_pool!(app, dex, token_x, token_y, fee_tier).unwrap(); + assert_eq!(pool.current_tick_index, 11); + // The first position has an incentive, but the second one does not have any. + app.increase_block_time_by_seconds(1000); + let incentive = get_position_incentives!(app, dex, 0, "alice").unwrap(); + assert_eq!( + incentive, + vec![Asset { + info: reward_token.clone(), + amount: Uint128::new(100500u128) + }] + ); + let incentive = get_position_incentives!(app, dex, 1, "alice").unwrap(); + assert_eq!(incentive, vec![]); + + // swap again + let amount = 700; + let swap_amount = TokenAmount(amount); + mint!(app, token_y, "bob", amount, "alice").unwrap(); + approve!(app, token_y, dex, amount, "bob").unwrap(); + swap!( + app, + dex, + pool_key, + false, + swap_amount, + true, + SqrtPrice::new(MAX_SQRT_PRICE), + "bob" + ) + .unwrap(); + let pool = get_pool!(app, dex, token_x, token_y, fee_tier).unwrap(); + assert_eq!(pool.current_tick_index, 35); + // the second position have incentive, + claim_incentives!(app, dex, 0, "alice").unwrap(); + app.increase_block_time_by_seconds(1000); + let incentive = get_position_incentives!(app, dex, 0, "alice").unwrap(); + assert_eq!(incentive, vec![]); + let incentive = get_position_incentives!(app, dex, 1, "alice").unwrap(); + assert_eq!( + incentive, + vec![Asset { + info: reward_token.clone(), + amount: Uint128::new(101000u128) + }] + ); +} + +#[test] +pub fn test_update_incentive_with_tick_move_right_to_left() { + let protocol_fee = Percentage::from_scale(6, 3); + let mut app = MockApp::new(&[]); + let dex = create_dex!(app, Percentage::new(0)); + let dex_raw = &dex.to_string(); + + let initial_amount = 10u128.pow(10); + let (token_x, token_y, token_z) = + create_3_tokens!(app, initial_amount, initial_amount, initial_amount); + mint!(app, token_z, dex_raw, initial_amount, "alice").unwrap(); + + let fee_tier = FeeTier::new(protocol_fee, 1).unwrap(); + + add_fee_tier!(app, dex, fee_tier, "alice").unwrap(); + + let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); + create_pool!( + app, + dex, + token_x, + token_y, + fee_tier, + init_sqrt_price, + init_tick, + "alice" + ) + .unwrap(); + + let pool_key = PoolKey::new(token_x.to_string(), token_y.to_string(), fee_tier).unwrap(); + + let reward_token = AssetInfo::Token { + contract_addr: token_z.clone(), + }; + let total_reward = Some(TokenAmount(1000000000)); + let reward_per_sec = TokenAmount(100); + let start_timestamp: Option = None; + let liquidity = Liquidity::from_integer(1000000); + create_incentive!( + app, + dex, + pool_key, + reward_token.clone(), + total_reward, + reward_per_sec, + start_timestamp, + "alice" + ) + .unwrap(); + + approve!(app, token_x, dex, initial_amount, "alice").unwrap(); + approve!(app, token_y, dex, initial_amount, "alice").unwrap(); + + // create 2 position + // first_pos: range (-20, -10) + // second_pos: range (-40, -30) + create_position!( + app, + dex, + pool_key, + -20, + -10, + liquidity, + SqrtPrice::new(0), + SqrtPrice::max_instance(), + "alice" + ) + .unwrap(); + create_position!( + app, + dex, + pool_key, + -40, + -30, + liquidity, + SqrtPrice::new(0), + SqrtPrice::max_instance(), + "alice" + ) + .unwrap(); + + // Both positions do not have any incentives due to being out of range + app.increase_block_time_by_seconds(1000); + let incentive = get_position_incentives!(app, dex, 0, "alice").unwrap(); + assert_eq!(incentive, vec![]); + let incentive = get_position_incentives!(app, dex, 1, "alice").unwrap(); + assert_eq!(incentive, vec![]); + + // swap x to y, tick move right -> left + let amount = 100; + let swap_amount = TokenAmount(amount); + mint!(app, token_x, "bob", amount, "alice").unwrap(); + approve!(app, token_x, dex, amount, "bob").unwrap(); + swap!( + app, + dex, + pool_key, + true, + swap_amount, + true, + SqrtPrice::new(MIN_SQRT_PRICE), + "bob" + ) + .unwrap(); + let pool = get_pool!(app, dex, token_x, token_y, fee_tier).unwrap(); + assert_eq!(pool.current_tick_index, -12); + // The first position has an incentive, but the second one does not have any. + app.increase_block_time_by_seconds(1000); + let incentive = get_position_incentives!(app, dex, 0, "alice").unwrap(); + assert_eq!( + incentive, + vec![Asset { + info: reward_token.clone(), + amount: Uint128::new(100500u128) + }] + ); + let incentive = get_position_incentives!(app, dex, 1, "alice").unwrap(); + assert_eq!(incentive, vec![]); + + // swap again + let amount = 700; + let swap_amount = TokenAmount(amount); + mint!(app, token_x, "bob", amount, "alice").unwrap(); + approve!(app, token_x, dex, amount, "bob").unwrap(); + swap!( + app, + dex, + pool_key, + true, + swap_amount, + true, + SqrtPrice::new(MIN_SQRT_PRICE), + "bob" + ) + .unwrap(); + let pool = get_pool!(app, dex, token_x, token_y, fee_tier).unwrap(); + assert_eq!(pool.current_tick_index, -36); + // the second position have incentive, + claim_incentives!(app, dex, 0, "alice").unwrap(); + app.increase_block_time_by_seconds(1000); + let incentive = get_position_incentives!(app, dex, 0, "alice").unwrap(); + assert_eq!(incentive, vec![]); + let incentive = get_position_incentives!(app, dex, 1, "alice").unwrap(); + assert_eq!( + incentive, + vec![Asset { + info: reward_token.clone(), + amount: Uint128::new(101000u128) + }] + ); +} diff --git a/contracts/oraiswap-v3/src/tests/liquidity_gap.rs b/contracts/oraiswap-v3/src/tests/liquidity_gap.rs index 8afb42b..9ca8a08 100644 --- a/contracts/oraiswap-v3/src/tests/liquidity_gap.rs +++ b/contracts/oraiswap-v3/src/tests/liquidity_gap.rs @@ -7,7 +7,7 @@ use crate::{ sqrt_price::{calculate_sqrt_price, SqrtPrice}, tests::helper::{macros::*, MockApp}, token_amount::TokenAmount, - FeeTier, PoolKey, MIN_SQRT_PRICE, + ContractError, FeeTier, PoolKey, MIN_SQRT_PRICE, }; #[test] @@ -137,7 +137,7 @@ fn test_liquidity_gap() { let swap_amount = TokenAmount(1); let target_sqrt_price = SqrtPrice::new(MIN_SQRT_PRICE); - swap!( + let error = swap!( app, dex, pool_key, @@ -148,6 +148,10 @@ fn test_liquidity_gap() { "bob" ) .unwrap_err(); + assert_eq!( + error.root_cause().to_string(), + ContractError::NoGainSwap {}.to_string() + ); } // Should skip gap and then swap diff --git a/contracts/oraiswap-v3/src/tests/migration.rs b/contracts/oraiswap-v3/src/tests/migration.rs index fa0d75e..7bbae61 100644 --- a/contracts/oraiswap-v3/src/tests/migration.rs +++ b/contracts/oraiswap-v3/src/tests/migration.rs @@ -1,107 +1,107 @@ -use cosmwasm_std::{ - testing::{mock_dependencies, mock_env, mock_info}, - Order, -}; -use decimal::Decimal; +// use cosmwasm_std::{ +// testing::{mock_dependencies, mock_env, mock_info}, +// Order, +// }; +// use decimal::Decimal; -use crate::{ - contract::{instantiate, migrate}, - entrypoints::{self}, - liquidity::Liquidity, - msg::{self, InstantiateMsg}, - percentage::Percentage, - sqrt_price::{calculate_sqrt_price, SqrtPrice}, - state::{self}, - FeeTier, PoolKey, -}; +// use crate::{ +// contract::{instantiate, migrate}, +// entrypoints::{self}, +// liquidity::Liquidity, +// msg::{self, InstantiateMsg}, +// percentage::Percentage, +// sqrt_price::{calculate_sqrt_price, SqrtPrice}, +// state::{self}, +// FeeTier, PoolKey, +// }; -#[test] -fn test_migrate_contract() { - // fixture - let mut deps = mock_dependencies(); +// #[test] +// fn test_migrate_contract() { +// // fixture +// let mut deps = mock_dependencies(); - let info = mock_info("alice", &[]); +// let info = mock_info("alice", &[]); - instantiate( - deps.as_mut(), - mock_env(), - info.clone(), - InstantiateMsg { - protocol_fee: Percentage::new(0), - }, - ) - .unwrap(); +// instantiate( +// deps.as_mut(), +// mock_env(), +// info.clone(), +// InstantiateMsg { +// protocol_fee: Percentage::new(0), +// }, +// ) +// .unwrap(); - let fee_tier = FeeTier::new(Percentage::new(0), 1).unwrap(); +// let fee_tier = FeeTier::new(Percentage::new(0), 1).unwrap(); - entrypoints::add_fee_tier(deps.as_mut(), mock_env(), info.clone(), fee_tier).unwrap(); +// entrypoints::add_fee_tier(deps.as_mut(), mock_env(), info.clone(), fee_tier).unwrap(); - let init_tick = 10; - let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); - entrypoints::create_pool( - deps.as_mut(), - mock_env(), - "token_x".to_string(), - "token_y".to_string(), - fee_tier, - init_sqrt_price, - init_tick, - ) - .unwrap(); +// let init_tick = 10; +// let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); +// entrypoints::create_pool( +// deps.as_mut(), +// mock_env(), +// "token_x".to_string(), +// "token_y".to_string(), +// fee_tier, +// init_sqrt_price, +// init_tick, +// ) +// .unwrap(); - let pool_key = PoolKey::new("token_x".to_string(), "token_y".to_string(), fee_tier).unwrap(); - let tick_indexes = [-9780, -42, 0, 9, 276, 32343, 50001]; - for i in 0..tick_indexes.len() - 1 { - entrypoints::create_position( - deps.as_mut(), - mock_env(), - info.clone(), - pool_key.clone(), - tick_indexes[i], - tick_indexes[i + 1], - Liquidity::new(10), - SqrtPrice::new(0), - SqrtPrice::max_instance(), - ) - .unwrap(); - } +// let pool_key = PoolKey::new("token_x".to_string(), "token_y".to_string(), fee_tier).unwrap(); +// let tick_indexes = [-9780, -42, 0, 9, 276, 32343, 50001]; +// for i in 0..tick_indexes.len() - 1 { +// entrypoints::create_position( +// deps.as_mut(), +// mock_env(), +// info.clone(), +// pool_key.clone(), +// tick_indexes[i], +// tick_indexes[i + 1], +// Liquidity::new(10), +// SqrtPrice::new(0), +// SqrtPrice::max_instance(), +// ) +// .unwrap(); +// } - // now can query first NFT info - let nft_info = entrypoints::query_nft_info(deps.as_ref(), 1).unwrap(); - assert_eq!(nft_info.extension.token_id, 1); +// // now can query first NFT info +// let nft_info = entrypoints::query_nft_info(deps.as_ref(), 1).unwrap(); +// assert_eq!(nft_info.extension.token_id, 1); - // we will reset to old storage - state::POSITION_KEYS_BY_TOKEN_ID.clear(deps.as_mut().storage); - state::TOKEN_COUNT.remove(deps.as_mut().storage); - state::TOKEN_ID.remove(deps.as_mut().storage); +// // we will reset to old storage +// state::POSITION_KEYS_BY_TOKEN_ID.clear(deps.as_mut().storage); +// state::TOKEN_COUNT.remove(deps.as_mut().storage); +// state::TOKEN_ID.remove(deps.as_mut().storage); - // and also reset all token_id in positions - let positions: Vec<_> = state::POSITIONS - .range_raw(deps.as_mut().storage, None, None, Order::Ascending) - .collect(); - for item in positions { - if let Ok((key, mut position)) = item { - position.token_id = 0; - // update position and its index - crate::state::POSITIONS - .save(deps.as_mut().storage, &key, &position) - .unwrap(); - } - } +// // and also reset all token_id in positions +// let positions: Vec<_> = state::POSITIONS +// .range_raw(deps.as_mut().storage, None, None, Order::Ascending) +// .collect(); +// for item in positions { +// if let Ok((key, mut position)) = item { +// position.token_id = 0; +// // update position and its index +// crate::state::POSITIONS +// .save(deps.as_mut().storage, &key, &position) +// .unwrap(); +// } +// } - // must return error - entrypoints::query_nft_info(deps.as_ref(), 1).unwrap_err(); - let num_tokens = entrypoints::query_num_tokens(deps.as_ref()).unwrap(); - assert_eq!(num_tokens.count, 0); +// // must return error +// entrypoints::query_nft_info(deps.as_ref(), 1).unwrap_err(); +// let num_tokens = entrypoints::query_num_tokens(deps.as_ref()).unwrap(); +// assert_eq!(num_tokens.count, 0); - // then migrate contract - migrate(deps.as_mut(), mock_env(), msg::MigrateMsg {}).unwrap(); +// // then migrate contract +// migrate(deps.as_mut(), mock_env(), msg::MigrateMsg {}).unwrap(); - // now can query first NFT info again - let nft_info = entrypoints::query_nft_info(deps.as_ref(), 1).unwrap(); - assert_eq!(nft_info.extension.token_id, 1); +// // now can query first NFT info again +// let nft_info = entrypoints::query_nft_info(deps.as_ref(), 1).unwrap(); +// assert_eq!(nft_info.extension.token_id, 1); - // total tokens is total ranges in tick_indexes - let num_tokens = entrypoints::query_num_tokens(deps.as_ref()).unwrap(); - assert_eq!(num_tokens.count as usize, tick_indexes.len() - 1); -} +// // total tokens is total ranges in tick_indexes +// let num_tokens = entrypoints::query_num_tokens(deps.as_ref()).unwrap(); +// assert_eq!(num_tokens.count as usize, tick_indexes.len() - 1); +// } diff --git a/contracts/oraiswap-v3/src/tests/mod.rs b/contracts/oraiswap-v3/src/tests/mod.rs index d781a16..b8514a0 100644 --- a/contracts/oraiswap-v3/src/tests/mod.rs +++ b/contracts/oraiswap-v3/src/tests/mod.rs @@ -10,6 +10,7 @@ mod get_liquidity_ticks; mod get_position_ticks; mod get_tickmap; mod helper; +mod incentive; mod interaction_with_pool_on_removed_fee_tier; mod limits; mod liquidity_gap; diff --git a/contracts/oraiswap-v3/src/tests/nft.rs b/contracts/oraiswap-v3/src/tests/nft.rs index 41fe0f8..9d15c91 100644 --- a/contracts/oraiswap-v3/src/tests/nft.rs +++ b/contracts/oraiswap-v3/src/tests/nft.rs @@ -10,7 +10,7 @@ use crate::{ sqrt_price::{calculate_sqrt_price, SqrtPrice}, tests::helper::{macros::*, MockApp}, token_amount::TokenAmount, - FeeTier, PoolKey, Position, MIN_SQRT_PRICE, + ContractError, FeeTier, PoolKey, Position, MIN_SQRT_PRICE, }; #[test] @@ -275,8 +275,10 @@ fn test_burn_nft() { // Load states let pool_state = get_pool!(app, dex, token_x, token_y, fee_tier).unwrap(); // Check ticks - get_tick!(app, dex, pool_key, lower_tick_index).unwrap_err(); - get_tick!(app, dex, pool_key, upper_tick_index).unwrap_err(); + let error = get_tick!(app, dex, pool_key, lower_tick_index).unwrap_err(); + assert!(error.to_string().contains("not found")); + let error = get_tick!(app, dex, pool_key, upper_tick_index).unwrap_err(); + assert!(error.to_string().contains("not found")); let lower_tick_bit = is_tick_initialized!(app, dex, pool_key, lower_tick_index); let upper_tick_bit = is_tick_initialized!(app, dex, pool_key, upper_tick_index); @@ -1104,16 +1106,21 @@ fn test_only_owner_can_transfer_nft() { ) .unwrap(); - app.execute( - Addr::unchecked("bob"), - dex.clone(), - &msg::ExecuteMsg::TransferNft { - recipient: Addr::unchecked("alice"), - token_id, - }, - &[], - ) - .unwrap_err(); + let error = app + .execute( + Addr::unchecked("bob"), + dex.clone(), + &msg::ExecuteMsg::TransferNft { + recipient: Addr::unchecked("alice"), + token_id, + }, + &[], + ) + .unwrap_err(); + assert_eq!( + error.root_cause().to_string(), + ContractError::Unauthorized {}.to_string() + ); } } @@ -1192,10 +1199,11 @@ fn test_approving_revoking() { wasm_event.attributes, vec![ Attribute { - key: "_contract_addr".to_string(), + key: "_contract_address".to_string(), value: dex.to_string() }, attr("action", "approve"), + attr("token_id", token_id.to_string()), attr("sender", "alice"), attr("spender", "random"), ] diff --git a/contracts/oraiswap-v3/src/tests/position.rs b/contracts/oraiswap-v3/src/tests/position.rs index ca850ac..b1536a6 100644 --- a/contracts/oraiswap-v3/src/tests/position.rs +++ b/contracts/oraiswap-v3/src/tests/position.rs @@ -7,7 +7,7 @@ use crate::{ sqrt_price::{calculate_sqrt_price, SqrtPrice}, tests::helper::{macros::*, MockApp}, token_amount::TokenAmount, - FeeTier, PoolKey, MIN_SQRT_PRICE, + ContractError, FeeTier, PoolKey, MIN_SQRT_PRICE, }; #[test] @@ -20,7 +20,7 @@ fn test_create_position() { add_fee_tier!(app, dex, fee_tier, "alice").unwrap(); - let init_tick = 10; + let init_tick = 0; let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); create_pool!( app, @@ -82,7 +82,7 @@ fn test_position_same_upper_and_lower_tick() { let pool_key = PoolKey::new(token_x.to_string(), token_y.to_string(), fee_tier).unwrap(); - create_position!( + let error = create_position!( app, dex, pool_key, @@ -94,6 +94,11 @@ fn test_position_same_upper_and_lower_tick() { "alice" ) .unwrap_err(); + + assert_eq!( + error.root_cause().to_string(), + ContractError::InvalidTickIndex {}.to_string() + ); } #[test] @@ -224,8 +229,11 @@ fn test_remove_position() { // Load states let pool_state = get_pool!(app, dex, token_x, token_y, fee_tier).unwrap(); // Check ticks - get_tick!(app, dex, pool_key, lower_tick_index).unwrap_err(); - get_tick!(app, dex, pool_key, upper_tick_index).unwrap_err(); + let error = get_tick!(app, dex, pool_key, lower_tick_index).unwrap_err(); + assert!(error.to_string().contains("not found")); + let error = get_tick!(app, dex, pool_key, upper_tick_index).unwrap_err(); + assert!(error.to_string().contains("not found")); + let lower_tick_bit = is_tick_initialized!(app, dex, pool_key, lower_tick_index); let upper_tick_bit = is_tick_initialized!(app, dex, pool_key, upper_tick_index); diff --git a/contracts/oraiswap-v3/src/tests/position_list.rs b/contracts/oraiswap-v3/src/tests/position_list.rs index 9d89ea0..09d58cf 100644 --- a/contracts/oraiswap-v3/src/tests/position_list.rs +++ b/contracts/oraiswap-v3/src/tests/position_list.rs @@ -34,7 +34,8 @@ fn test_remove_position_from_empty_list() { ) .unwrap(); - remove_position!(app, dex, 0, "alice").unwrap_err(); + let error = remove_position!(app, dex, 0, "alice").unwrap_err(); + assert!(error.root_cause().to_string().contains("not found")); } #[test] @@ -369,13 +370,14 @@ fn test_only_owner_can_modify_position_list() { let last_position_index_before = get_all_positions!(app, dex, "alice").len() - 1; let unauthorized_user = "bob"; - remove_position!( + let error = remove_position!( app, dex, last_position_index_before as u32, unauthorized_user ) .unwrap_err(); + assert!(error.root_cause().to_string().contains("not found")); } } @@ -674,7 +676,8 @@ fn test_only_owner_can_transfer_position() { { let transferred_index = 0; - transfer_position!(app, dex, transferred_index, "alice", "bob").unwrap_err(); + let error = transfer_position!(app, dex, transferred_index, "alice", "bob").unwrap_err(); + assert!(error.root_cause().to_string().contains("not found")); } } diff --git a/contracts/oraiswap-v3/src/tests/position_slippage.rs b/contracts/oraiswap-v3/src/tests/position_slippage.rs index 170394e..c7053ea 100644 --- a/contracts/oraiswap-v3/src/tests/position_slippage.rs +++ b/contracts/oraiswap-v3/src/tests/position_slippage.rs @@ -5,7 +5,7 @@ use crate::{ percentage::Percentage, sqrt_price::{calculate_sqrt_price, SqrtPrice}, tests::helper::{macros::*, MockApp}, - FeeTier, PoolKey, + ContractError, FeeTier, PoolKey, }; #[test] @@ -72,7 +72,7 @@ fn test_position_slippage_below_range() { let limit_lower = SqrtPrice::new(1014432353584998786339859); let limit_upper = SqrtPrice::new(1045335831204498605270797); let tick = pool_key.fee_tier.tick_spacing as i32; - create_position!( + let error = create_position!( app, dex, pool_key, @@ -84,6 +84,10 @@ fn test_position_slippage_below_range() { "alice" ) .unwrap_err(); + assert_eq!( + error.root_cause().to_string(), + ContractError::PriceLimitReached {}.to_string() + ); } #[test] @@ -99,7 +103,7 @@ fn test_position_slippage_above_range() { let limit_lower = SqrtPrice::new(955339206774222158009382); let limit_upper = SqrtPrice::new(984442481813945288458906); let tick = pool_key.fee_tier.tick_spacing as i32; - create_position!( + let error = create_position!( app, dex, pool_key, @@ -111,4 +115,8 @@ fn test_position_slippage_above_range() { "alice" ) .unwrap_err(); + assert_eq!( + error.root_cause().to_string(), + ContractError::PriceLimitReached {}.to_string() + ); } diff --git a/contracts/oraiswap-v3/src/tests/protocol_fee.rs b/contracts/oraiswap-v3/src/tests/protocol_fee.rs index 9715aa4..ea443db 100644 --- a/contracts/oraiswap-v3/src/tests/protocol_fee.rs +++ b/contracts/oraiswap-v3/src/tests/protocol_fee.rs @@ -4,7 +4,7 @@ use crate::{ percentage::Percentage, tests::helper::{macros::*, MockApp}, token_amount::TokenAmount, - FeeTier, PoolKey, + ContractError, FeeTier, PoolKey, }; #[test] @@ -60,7 +60,11 @@ fn test_protocol_fee_not_admin() { ) .unwrap(); - withdraw_protocol_fee!(app, dex, pool_key, "bob").unwrap_err(); + let error = withdraw_protocol_fee!(app, dex, pool_key, "bob").unwrap_err(); + assert_eq!( + error.root_cause().to_string(), + ContractError::Unauthorized {}.to_string() + ); } #[test] diff --git a/contracts/oraiswap-v3/src/tests/remove_fee_tier.rs b/contracts/oraiswap-v3/src/tests/remove_fee_tier.rs index 84273fb..a7c622c 100644 --- a/contracts/oraiswap-v3/src/tests/remove_fee_tier.rs +++ b/contracts/oraiswap-v3/src/tests/remove_fee_tier.rs @@ -3,7 +3,7 @@ use decimal::*; use crate::{ percentage::Percentage, tests::helper::{macros::*, MockApp}, - FeeTier, + ContractError, FeeTier, }; #[test] @@ -35,7 +35,12 @@ fn test_remove_not_existing_fee_tier() { add_fee_tier!(app, dex, fee_tier, "alice").unwrap(); let fee_tier = FeeTier::new(Percentage::from_scale(2, 4), 2).unwrap(); - remove_fee_tier!(app, dex, fee_tier, "alice").unwrap_err(); + let error = remove_fee_tier!(app, dex, fee_tier, "alice").unwrap_err(); + + assert_eq!( + error.root_cause().to_string(), + ContractError::FeeTierNotFound {}.to_string() + ); } #[test] @@ -49,5 +54,9 @@ fn test_remove_fee_tier_not_admin() { let fee_tier = FeeTier::new(Percentage::from_scale(2, 4), 2).unwrap(); add_fee_tier!(app, dex, fee_tier, "alice").unwrap(); - remove_fee_tier!(app, dex, fee_tier, "bob").unwrap_err(); + let error = remove_fee_tier!(app, dex, fee_tier, "bob").unwrap_err(); + assert_eq!( + error.root_cause().to_string(), + ContractError::Unauthorized {}.to_string() + ); } diff --git a/contracts/oraiswap-v3/src/tests/slippage.rs b/contracts/oraiswap-v3/src/tests/slippage.rs index 8dbc459..22eb60b 100644 --- a/contracts/oraiswap-v3/src/tests/slippage.rs +++ b/contracts/oraiswap-v3/src/tests/slippage.rs @@ -6,7 +6,7 @@ use crate::{ sqrt_price::{calculate_sqrt_price, SqrtPrice}, tests::helper::{macros::*, MockApp}, token_amount::TokenAmount, - FeeTier, PoolKey, MAX_SQRT_PRICE, + ContractError, FeeTier, PoolKey, MAX_SQRT_PRICE, }; #[test] @@ -66,7 +66,7 @@ fn test_swap_close_to_limit() { let target_sqrt_price = quoted_target_sqrt_price - SqrtPrice::new(1); - swap!( + let error = swap!( app, dex, pool_key, @@ -77,6 +77,11 @@ fn test_swap_close_to_limit() { "alice" ) .unwrap_err(); + + assert_eq!( + error.root_cause().to_string(), + ContractError::PriceLimitReached {}.to_string() + ); } #[test] diff --git a/contracts/oraiswap-v3/src/tests/swap.rs b/contracts/oraiswap-v3/src/tests/swap.rs index 35852da..3edfe52 100644 --- a/contracts/oraiswap-v3/src/tests/swap.rs +++ b/contracts/oraiswap-v3/src/tests/swap.rs @@ -7,7 +7,7 @@ use crate::{ sqrt_price::{calculate_sqrt_price, SqrtPrice}, tests::helper::macros::*, token_amount::TokenAmount, - FeeTier, PoolKey, MAX_SQRT_PRICE, MIN_SQRT_PRICE, + ContractError, FeeTier, PoolKey, MAX_SQRT_PRICE, MIN_SQRT_PRICE, }; use super::helper::MockApp; @@ -301,7 +301,7 @@ fn test_swap_y_to_x() { #[test] fn test_swap_not_enough_liquidity_token_x() { - let protocol_fee = Percentage::from_scale(6, 3); + let protocol_fee: Percentage = Percentage::from_scale(6, 3); let mut app = MockApp::new(&[]); let dex = create_dex!(app, protocol_fee); @@ -373,7 +373,7 @@ fn test_swap_not_enough_liquidity_token_x() { let target_sqrt_price = SqrtPrice::new(MIN_SQRT_PRICE); - swap!( + let error = swap!( app, dex, pool_key, @@ -384,6 +384,11 @@ fn test_swap_not_enough_liquidity_token_x() { "bob" ) .unwrap_err(); + + assert_eq!( + error.root_cause().to_string(), + ContractError::TickLimitReached {}.to_string() + ); } #[test] @@ -460,7 +465,7 @@ fn test_swap_not_enough_liquidity_token_y() { let slippage = SqrtPrice::new(MAX_SQRT_PRICE); - swap!( + let error = swap!( app, dex, pool_key, @@ -471,4 +476,8 @@ fn test_swap_not_enough_liquidity_token_y() { "bob" ) .unwrap_err(); + assert_eq!( + error.root_cause().to_string(), + ContractError::TickLimitReached {}.to_string() + ); }