From 69613f7bed9cd013aa88de5612ef5947736ce11c Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Thu, 21 Aug 2025 13:33:17 +0300 Subject: [PATCH 01/25] build --- Cargo.lock | 1277 +++++++++++++++++++++++++++++++++++++++------ Cargo.toml | 25 +- src/contract.rs | 50 +- src/error.rs | 4 +- src/helpers.rs | 8 +- src/killswitch.rs | 25 +- src/msg.rs | 8 +- src/state.rs | 12 +- src/threshold.rs | 12 +- 9 files changed, 1191 insertions(+), 230 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8286339..32da867 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,35 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "anyhow" @@ -8,6 +37,147 @@ version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" +[[package]] +name = "ark-bls12-381" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df4dcc01ff89867cd86b0da835f23c3f02738353aaee7dde7495af71363b8d5" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" +dependencies = [ + "ahash 0.8.12", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "educe", + "fnv", + "hashbrown 0.15.5", + "itertools 0.13.0", + "num-bigint", + "num-integer", + "num-traits", + "rayon", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "arrayvec", + "digest 0.10.7", + "educe", + "itertools 0.13.0", + "num-bigint", + "num-traits", + "paste", + "rayon", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" +dependencies = [ + "quote", + "syn 2.0.106", +] + +[[package]] +name = "ark-ff-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "ark-poly" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" +dependencies = [ + "ahash 0.8.12", + "ark-ff", + "ark-serialize", + "ark-std", + "educe", + "fnv", + "hashbrown 0.15.5", +] + +[[package]] +name = "ark-serialize" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "arrayvec", + "digest 0.10.7", + "num-bigint", + "rayon", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "ark-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" +dependencies = [ + "num-traits", + "rand", + "rayon", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + [[package]] name = "base16ct" version = "0.2.0" @@ -20,11 +190,17 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" -version = "1.5.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "bech32" @@ -62,6 +238,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56953345e39537a3e18bdaeba4cb0c58a78c1f61f361dc0fa7c5c7340ae87c5f" +[[package]] +name = "bnum" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e31ea183f6ee62ac8b8a8cf7feddd766317adfb13ff469de57ce033efd6a790" + [[package]] name = "byteorder" version = "1.4.3" @@ -86,84 +268,200 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "cosmwasm-core" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f86359c565ec8dc6f1b39e51fff1f63f712767d77529ff46d0d466ea37cd34" + [[package]] name = "cosmwasm-crypto" -version = "1.5.4" +version = "1.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b4c3f9c4616d6413d4b5fc4c270a4cc32a374b9be08671e80e1a019f805d8f" +checksum = "a9c82e56962f0f18c9a292aa59940e03a82ce15ef79b93679d5838bb8143f0df" dependencies = [ + "digest 0.10.7", + "ed25519-zebra 3.1.0", + "k256", + "rand_core 0.6.4", + "thiserror 1.0.60", +] + +[[package]] +name = "cosmwasm-crypto" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e9d0e376c224afcb401809f50209b7787390041e8898684ccbd3ecac9be299d" +dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-serialize", + "cosmwasm-core", + "curve25519-dalek 4.1.3", "digest 0.10.7", "ecdsa", - "ed25519-zebra", + "ed25519-zebra 4.1.0", "k256", + "num-bigint", + "num-traits", + "p256", "rand_core 0.6.4", - "thiserror", + "rayon", + "sha2 0.10.9", + "thiserror 1.0.60", +] + +[[package]] +name = "cosmwasm-derive" +version = "1.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b804ff15a0e059c88f85ae0e868cf8c7aba9d61221e46f1ad7250f270628c7" +dependencies = [ + "syn 1.0.109", ] [[package]] name = "cosmwasm-derive" -version = "1.5.4" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c586ced10c3b00e809ee664a895025a024f60d65d34fe4c09daed4a4db68a3f3" +checksum = "58e3ed1b8f1b9df2a30c03b6c25177acdb15f5de0cfcd6ad708f0fbd9d9c7c91" dependencies = [ - "syn 1.0.107", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "cosmwasm-schema" +version = "1.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5526ea839acb47bbf8fff031ed9aad86e74d43f77b089255417328c3664367d5" +dependencies = [ + "cosmwasm-schema-derive 1.5.11", + "schemars 0.8.22", + "serde", + "serde_json", + "thiserror 1.0.60", ] [[package]] name = "cosmwasm-schema" -version = "1.5.4" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8467874827d384c131955ff6f4d47d02e72a956a08eb3c0ff24f8c903a5517b4" +checksum = "2e476f2415bf6c582d3a6743de056d6a217933b753aa4994cdb9c6a89d09a97c" dependencies = [ - "cosmwasm-schema-derive", - "schemars", + "cosmwasm-schema-derive 3.0.1", + "cw-schema", + "schemars 0.8.22", "serde", "serde_json", - "thiserror", + "thiserror 1.0.60", ] [[package]] name = "cosmwasm-schema-derive" -version = "1.5.4" +version = "1.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6db85d98ac80922aef465e564d5b21fa9cfac5058cb62df7f116c3682337393" +checksum = "10f41b99f41f840765d02ae858956bb52af910755976312082e90493c67db512" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", +] + +[[package]] +name = "cosmwasm-schema-derive" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e652d98c68f04e4f7e97b16009a94ccf550341b091213c15bbe3eb2d14139709" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", ] [[package]] name = "cosmwasm-std" -version = "1.5.4" +version = "1.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712fe58f39d55c812f7b2c84e097cdede3a39d520f89b6dc3153837e31741927" +checksum = "763340055b84e5482ed90fec8194ff7d59112267a09bbf5819c9e3edca8c052e" dependencies = [ - "base64", + "base64 0.21.7", "bech32 0.9.1", - "bnum", - "cosmwasm-crypto", - "cosmwasm-derive", + "bnum 0.10.0", + "cosmwasm-crypto 1.5.11", + "cosmwasm-derive 1.5.11", "derivative", "forward_ref", "hex", - "schemars", + "schemars 0.8.22", "serde", "serde-json-wasm", - "sha2 0.10.8", + "sha2 0.10.9", "static_assertions", - "thiserror", + "thiserror 1.0.60", +] + +[[package]] +name = "cosmwasm-std" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c145f354e0dac3734bf62872ab8969e03bbd36b6c67e189cc7c15a8a47d19aab" +dependencies = [ + "base64 0.22.1", + "bech32 0.11.0", + "bnum 0.11.0", + "cosmwasm-core", + "cosmwasm-crypto 3.0.1", + "cosmwasm-derive 3.0.1", + "cw-schema", + "derive_more", + "hex", + "rand_core 0.6.4", + "rmp-serde", + "schemars 0.8.22", + "serde", + "serde_json", + "sha2 0.10.9", + "static_assertions", + "thiserror 1.0.60", ] [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crypto-bigint" version = "0.5.5" @@ -199,39 +497,95 @@ dependencies = [ "zeroize", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "cw-controllers" -version = "1.1.2" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57de8d3761e46be863e3ac1eba8c8a976362a48c6abf240df1e26c3e421ee9e8" +checksum = "5b8b9a060524f8c5bff8c736e890fb52a16533e7107c6c41e1cae6077c177516" dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus", - "cw-utils", - "schemars", + "cosmwasm-schema 3.0.1", + "cosmwasm-std 3.0.1", + "cw-storage-plus 3.0.0", + "cw-utils 3.0.0", + "schemars 0.8.22", "serde", - "thiserror", + "thiserror 2.0.16", ] [[package]] name = "cw-multi-test" -version = "1.1.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ffa9e3bae206540c084198e5be5aea2ecb1f2597f79dc09263b528ea0604788" +checksum = "cc392a5cb7e778e3f90adbf7faa43c4db7f35b6623224b08886d796718edb875" dependencies = [ "anyhow", - "bech32 0.11.0", - "cosmwasm-std", - "cw-storage-plus", - "cw-utils", + "bech32 0.9.1", + "cosmwasm-std 1.5.11", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", "derivative", "itertools 0.12.1", "prost", - "schemars", + "schemars 0.8.22", + "serde", + "sha2 0.10.9", + "thiserror 1.0.60", +] + +[[package]] +name = "cw-schema" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79a9a8e4486ac59db0e97a84304bc0fb869b543af8a08c53f971238a13e25079" +dependencies = [ + "cw-schema-derive", + "indexmap", + "schemars 1.0.4", "serde", - "sha2 0.10.8", - "thiserror", + "serde_with", + "siphasher", + "typeid", +] + +[[package]] +name = "cw-schema-derive" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31d3e652162f453ced896b72bad35fe6746ad2cf770e125f9a27f20838250111" +dependencies = [ + "heck", + "itertools 0.13.0", + "owo-colors", + "proc-macro2", + "quote", + "syn 2.0.106", ] [[package]] @@ -240,8 +594,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5ff29294ee99373e2cd5fd21786a3c0ced99a52fec2ca347d565489c61b723c" dependencies = [ - "cosmwasm-std", - "schemars", + "cosmwasm-std 1.5.11", + "schemars 0.8.22", + "serde", +] + +[[package]] +name = "cw-storage-plus" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffe14a1c713486ec9f07059afcce49a9cd68657aaf2e2a736cd3bbf487de8e2" +dependencies = [ + "cosmwasm-std 3.0.1", + "schemars 0.8.22", "serde", ] @@ -249,17 +614,18 @@ dependencies = [ name = "cw-streamswap" version = "0.1.5" dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", + "cosmwasm-schema 3.0.1", + "cosmwasm-std 3.0.1", "cw-controllers", "cw-multi-test", - "cw-storage-plus", - "cw-utils", - "cw2", - "schemars", + "cw-storage-plus 3.0.0", + "cw-utils 3.0.0", + "cw2 3.0.0", + "ed25519-zebra 4.1.0", + "schemars 1.0.4", "semver", "serde", - "thiserror", + "thiserror 2.0.16", ] [[package]] @@ -268,13 +634,26 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c4a657e5caacc3a0d00ee96ca8618745d050b8f757c709babafb81208d4239c" dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw2", - "schemars", + "cosmwasm-schema 1.5.11", + "cosmwasm-std 1.5.11", + "cw2 1.1.2", + "schemars 0.8.22", "semver", "serde", - "thiserror", + "thiserror 1.0.60", +] + +[[package]] +name = "cw-utils" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8667e96f2c65cf7f4c6c66bfd6ee46909c40827bc1caea0409234e34f03cf061" +dependencies = [ + "cosmwasm-schema 3.0.1", + "cosmwasm-std 3.0.1", + "schemars 0.8.22", + "serde", + "thiserror 2.0.16", ] [[package]] @@ -283,13 +662,63 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6c120b24fbbf5c3bedebb97f2cc85fbfa1c3287e09223428e7e597b5293c1fa" dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus", - "schemars", + "cosmwasm-schema 1.5.11", + "cosmwasm-std 1.5.11", + "cw-storage-plus 1.2.0", + "schemars 0.8.22", "semver", "serde", - "thiserror", + "thiserror 1.0.60", +] + +[[package]] +name = "cw2" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50582114ab739724c25a27cea1785351f2b5fea7968214302e0023ee2c0e8b39" +dependencies = [ + "cosmwasm-schema 3.0.1", + "cosmwasm-std 3.0.1", + "cw-storage-plus 3.0.0", + "schemars 0.8.22", + "semver", + "serde", + "thiserror 2.0.16", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.106", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.106", ] [[package]] @@ -299,6 +728,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", + "pem-rfc7468", "zeroize", ] @@ -310,7 +740,28 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", + "unicode-xid", ] [[package]] @@ -336,9 +787,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.9" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "ecdsa" @@ -354,21 +805,61 @@ dependencies = [ "spki", ] +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "serde", + "signature", +] + [[package]] name = "ed25519-zebra" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403ef3e961ab98f0ba902771d29f842058578bb1ce7e3c59dad5a6a93e784c69" +checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 3.2.0", + "hashbrown 0.12.3", "hex", "rand_core 0.6.4", "serde", "sha2 0.9.9", - "thiserror", "zeroize", ] +[[package]] +name = "ed25519-zebra" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0017d969298eec91e3db7a2985a8cab4df6341d86e6f3a6f5878b13fb7846bc9" +dependencies = [ + "curve25519-dalek 4.1.3", + "ed25519", + "hashbrown 0.15.5", + "pkcs8", + "rand_core 0.6.4", + "serde", + "sha2 0.10.9", + "subtle", + "zeroize", +] + +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "either" version = "1.8.0" @@ -394,6 +885,32 @@ dependencies = [ "zeroize", ] +[[package]] +name = "enum-ordinalize" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "ff" version = "0.13.0" @@ -404,6 +921,24 @@ dependencies = [ "subtle", ] +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "forward_ref" version = "1.0.0" @@ -421,17 +956,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.8" @@ -440,7 +964,7 @@ checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] @@ -455,34 +979,99 @@ dependencies = [ ] [[package]] -name = "hex" -version = "0.4.3" +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown 0.15.5", +] [[package]] -name = "hmac" -version = "0.12.1" +name = "is-terminal" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ - "digest 0.10.7", + "hermit-abi", + "libc", + "windows-sys", ] +[[package]] +name = "is_ci" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" + [[package]] name = "itertools" -version = "0.10.5" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] [[package]] name = "itertools" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] @@ -495,23 +1084,57 @@ checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" [[package]] name = "k256" -version = "0.13.1" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if", "ecdsa", "elliptic-curve", "once_cell", - "sha2 0.10.8", + "sha2 0.10.9", "signature", ] [[package]] name = "libc" -version = "0.2.135" +version = "0.2.175" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] [[package]] name = "once_cell" @@ -521,9 +1144,46 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "owo-colors" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48dd4f4a2c8405440fd0462561f0e5806bd0f77e86f51c761481bdd4018b545e" +dependencies = [ + "supports-color 2.1.0", + "supports-color 3.0.2", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.9", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] [[package]] name = "pkcs8" @@ -535,20 +1195,38 @@ dependencies = [ "spki", ] +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] [[package]] name = "prost" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" dependencies = [ "bytes", "prost-derive", @@ -556,15 +1234,15 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.106", ] [[package]] @@ -576,14 +1254,31 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_chacha", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + [[package]] name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] [[package]] name = "rand_core" @@ -591,7 +1286,47 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.8", + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "ref-cast" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", ] [[package]] @@ -604,6 +1339,37 @@ dependencies = [ "subtle", ] +[[package]] +name = "rmp" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "ryu" version = "1.0.11" @@ -612,26 +1378,51 @@ checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] name = "schemars" -version = "0.8.19" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6e7ed6919cb46507fb01ff1654309219f62b4d603822501b0b80d42f6f21ef" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" dependencies = [ "dyn-clone", - "schemars_derive", + "schemars_derive 0.8.22", "serde", "serde_json", ] +[[package]] +name = "schemars" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +dependencies = [ + "dyn-clone", + "ref-cast", + "schemars_derive 1.0.4", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.106", +] + [[package]] name = "schemars_derive" -version = "0.8.19" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "185f2b7aa7e02d418e453790dde16890256bbd2bcd04b7dc5348811052b53f49" +checksum = "33d020396d1d138dc19f1165df7545479dcd58d93810dc5d646a16e55abefa80" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.61", + "syn 2.0.106", ] [[package]] @@ -650,15 +1441,15 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.200" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] @@ -674,37 +1465,61 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.200" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.106", ] [[package]] name = "serde_derive_internals" -version = "0.29.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.106", ] [[package]] name = "serde_json" -version = "1.0.87" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] +[[package]] +name = "serde_with" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" +dependencies = [ + "serde", + "serde_derive", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "sha2" version = "0.9.9" @@ -720,9 +1535,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -739,6 +1554,12 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "spki" version = "0.7.3" @@ -755,17 +1576,42 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "subtle" -version = "2.4.1" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "supports-color" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6398cde53adc3c4557306a96ce67b302968513830a77a95b2b17305d9719a89" +dependencies = [ + "is-terminal", + "is_ci", +] + +[[package]] +name = "supports-color" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "c64fc7232dd8d2e4ac5ce4ef302b1d81e0b80d055b9d77c7c4f51f6aa4c867d6" +dependencies = [ + "is_ci", +] [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -774,9 +1620,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.61" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -789,7 +1635,16 @@ version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.60", +] + +[[package]] +name = "thiserror" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +dependencies = [ + "thiserror-impl 2.0.16", ] [[package]] @@ -800,9 +1655,26 @@ checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.106", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", ] +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "typenum" version = "1.15.0" @@ -815,6 +1687,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "version_check" version = "0.9.4" @@ -823,18 +1701,119 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +name = "windows-sys" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] diff --git a/Cargo.toml b/Cargo.toml index 3a0013b..09e5e9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,6 @@ overflow-checks = true crate-type = ["cdylib", "rlib"] [features] -# for more explicit tests, cargo test --features=backtraces -backtraces = ["cosmwasm-std/backtraces"] # use library feature to disable all instantiate/execute/query exports library = [] @@ -31,16 +29,21 @@ optimize = """docker run --rm -v "$(pwd)":/code \ """ [dependencies] -cosmwasm-std = "1.5.4" -cosmwasm-schema = "1.5.4" -cw-controllers = "1.1.2" -cw-storage-plus = "1.2.0" -cw-utils = "1.0.3" -cw2 = "1.1.2 " -schemars = "0.8.11" +cosmwasm-std = "3.0.1" +cosmwasm-schema = "3.0.1" +cw-controllers = "3.0.0" +cw-storage-plus = "3.0.0" +cw-utils = "3.0.0" +cw2 = "3.0.0" +schemars = "1.0.4" serde = { version = "1.0.152", default-features = false, features = ["derive"] } -thiserror = { version = "1.0.38" } +thiserror = { version = "2.0.1" } semver = "1.0.16" +ed25519-zebra = { version = "4.1.0", features = ["alloc"] } + + [dev-dependencies] -cw-multi-test = "1.1.0" +cw-multi-test = { version = "0.20.1", features = ["cosmwasm_1_2"] } + + diff --git a/src/contract.rs b/src/contract.rs index 0f7fd21..7620691 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -14,12 +14,12 @@ use crate::{killswitch, ContractError}; use cosmwasm_std::{ attr, entry_point, to_json_binary, Addr, BankMsg, Binary, Coin, CosmosMsg, Decimal, Decimal256, Deps, DepsMut, Env, Fraction, MessageInfo, Order, Response, StdError, StdResult, Timestamp, - Uint128, Uint256, Uint64, + Uint256, Uint64, }; use cw2::{get_contract_version, set_contract_version}; use semver::Version; -use crate::helpers::{check_name_and_url, from_semver, get_decimals, to_uint256}; +use crate::helpers::{check_name_and_url, from_semver, get_decimals}; use cw_storage_plus::Bound; use cw_utils::{maybe_addr, must_pay}; @@ -307,7 +307,7 @@ pub fn execute_create_stream( .find(|p| p.denom == config.stream_creation_denom) .ok_or(ContractError::NoFundsSent {})?; - if to_uint256(total_funds.amount) != to_uint256(config.stream_creation_fee) + out_supply { + if total_funds.amount != Uint256::from(config.stream_creation_fee) + out_supply { return Err(ContractError::StreamOutSupplyFundsRequired {}); } // check for extra funds sent in msg @@ -321,7 +321,7 @@ pub fn execute_create_stream( .find(|p| p.denom == out_denom) .ok_or(ContractError::NoFundsSent {})?; - if to_uint256(funds.amount) != out_supply { + if funds.amount != out_supply { return Err(ContractError::StreamOutSupplyFundsRequired {}); } @@ -330,7 +330,7 @@ pub fn execute_create_stream( .iter() .find(|p| p.denom == config.stream_creation_denom) .ok_or(ContractError::NoFundsSent {})?; - if creation_fee.amount != config.stream_creation_fee { + if creation_fee.amount != Uint256::from(config.stream_creation_fee) { return Err(ContractError::StreamCreationFeeRequired {}); } @@ -630,7 +630,7 @@ pub fn update_position( position.pending_purchase = decimals; // floors the decimal points - purchased_uint128 = purchased * Uint256::one(); + purchased_uint128 = purchased.atomics(); position.purchased = position.purchased.checked_add(purchased_uint128)?; } @@ -663,7 +663,7 @@ pub fn execute_subscribe( } let in_amount = must_pay(&info, &stream.in_denom)?; - let in_amount_uint256 = to_uint256(in_amount); + let in_amount_uint256 = in_amount; let new_shares; let operator = maybe_addr(deps.api, operator)?; @@ -753,7 +753,7 @@ pub fn execute_subscribe_pending( return Err(ContractError::StreamKillswitchActive {}); } let in_amount = must_pay(&info, &stream.in_denom)?; - let in_amount_uint256 = to_uint256(in_amount); + let in_amount_uint256 = in_amount; let new_shares = stream.compute_shares_amount(in_amount_uint256, false); let operator = maybe_addr(deps.api, operator)?; @@ -909,8 +909,6 @@ pub fn execute_withdraw( attr("spent", position.spent.to_string()), attr("tos_version", position.tos_version), ]; - // TODO: This might be a problem if the withdraw amount is too large but unlikely - let withdraw_amount: Uint128 = Uint128::try_from(withdraw_amount)?; // send funds to withdraw address or to the sender let res = Response::new() @@ -987,8 +985,6 @@ pub fn execute_withdraw_pending( attr("tos_version", position.tos_version), ]; - let withdraw_amount: Uint128 = Uint128::try_from(withdraw_amount)?; - // send funds to withdraw address or to the sender let res = Response::new() .add_message(CosmosMsg::Bank(BankMsg::Send { @@ -1045,17 +1041,16 @@ pub fn execute_finalize_stream( //Stream's swap fee collected at fixed rate from accumulated spent_in of positions(ie stream.spent_in) let swap_fee = Decimal256::from_ratio(stream.spent_in, Uint256::one()) .checked_mul(stream.stream_exit_fee_percent)? - * Uint256::one(); + .atomics(); let creator_revenue = stream.spent_in.checked_sub(swap_fee)?; - let creator_revenue_u128: Uint128 = Uint128::try_from(creator_revenue)?; //Creator's revenue claimed at finalize let revenue_msg = CosmosMsg::Bank(BankMsg::Send { to_address: treasury.to_string(), amount: vec![Coin { denom: stream.in_denom.clone(), - amount: creator_revenue_u128, + amount: creator_revenue, }], }); //Exact fee for stream creation charged at creation but claimed at finalize @@ -1063,16 +1058,15 @@ pub fn execute_finalize_stream( to_address: config.fee_collector.to_string(), amount: vec![Coin { denom: stream.stream_creation_denom, - amount: stream.stream_creation_fee, + amount: Uint256::from(stream.stream_creation_fee), }], }); - let swap_fee_128: Uint128 = Uint128::try_from(swap_fee)?; let swap_fee_msg = CosmosMsg::Bank(BankMsg::Send { to_address: config.fee_collector.to_string(), amount: vec![Coin { denom: stream.in_denom, - amount: swap_fee_128, + amount: swap_fee, }], }); @@ -1084,12 +1078,11 @@ pub fn execute_finalize_stream( // In case the stream is ended without any shares in it. We need to refund the remaining out tokens although that is unlikely to happen if stream.out_remaining > Uint256::zero() { - let remaining_out: Uint128 = Uint128::try_from(stream.out_remaining)?; let remaining_msg = CosmosMsg::Bank(BankMsg::Send { to_address: treasury.to_string(), amount: vec![Coin { denom: stream.out_denom, - amount: remaining_out, + amount: stream.out_remaining, }], }); messages.push(remaining_msg); @@ -1164,15 +1157,13 @@ pub fn execute_exit_stream( // Swap fee = fixed_rate*position.spent_in this calculation is only for execution reply attributes let swap_fee = Decimal256::from_ratio(position.spent, Uint256::one()) .checked_mul(stream.stream_exit_fee_percent)? - * Uint256::one(); - - let purchased = Uint128::try_from(position.purchased)?; + .atomics(); let send_msg = CosmosMsg::Bank(BankMsg::Send { to_address: operator_target.to_string(), amount: vec![Coin { denom: stream.out_denom.to_string(), - amount: purchased, + amount: position.purchased, }], }); @@ -1202,12 +1193,11 @@ pub fn execute_exit_stream( attr("swap_fee_paid", swap_fee.to_string()), ]; if !position.in_balance.is_zero() { - let unspent: Uint128 = Uint128::try_from(position.in_balance)?; let unspent_msg = CosmosMsg::Bank(BankMsg::Send { to_address: operator_target.to_string(), amount: vec![Coin { denom: stream.in_denom, - amount: unspent, + amount: position.in_balance, }], }); @@ -1230,7 +1220,7 @@ pub fn execute_update_config( min_stream_duration: Option, min_duration_until_start_time: Option, stream_creation_denom: Option, - stream_creation_fee: Option, + stream_creation_fee: Option, fee_collector: Option, accepted_in_denom: Option, exit_fee_percent: Option, @@ -1299,8 +1289,8 @@ pub fn execute_migrate_position( operator: old_position.operator, tos_version: "".to_string(), pending_purchase: old_position.pending_purchase, - purchased: Uint256::from_u128(old_position.purchased.into()), - spent: Uint256::from_u128(old_position.spent.into()), + purchased: Uint256::new(old_position.purchased.into()), + spent: Uint256::new(old_position.spent.into()), }; OLD_POSITIONS.remove(deps.storage, (stream_id, info.sender.clone())); POSITIONS.save(deps.storage, (stream_id, &info.sender.clone()), &position)?; @@ -1318,7 +1308,7 @@ fn check_access( position_owner: &Addr, position_operator: &Option, ) -> Result<(), ContractError> { - if position_owner.as_ref() != info.sender + if position_owner != info.sender && position_operator .as_ref() .map_or(true, |o| o != info.sender) diff --git a/src/error.rs b/src/error.rs index 4d423aa..972b3a1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,7 +6,7 @@ use cw_utils::PaymentError; use std::convert::Infallible; use thiserror::Error; -#[derive(Error, Debug, PartialEq)] +#[derive(Error, Debug)] pub enum ContractError { #[error("{0}")] Std(#[from] StdError), @@ -157,7 +157,7 @@ pub enum ContractError { #[error("Invalid terms and services")] InvalidToSVersion {}, - + #[error("Treasury cancel period : Active")] TreasuryCancelPeriodActive {}, diff --git a/src/helpers.rs b/src/helpers.rs index 2ada12e..597b109 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,5 +1,5 @@ use crate::ContractError; -use cosmwasm_std::{Decimal256, StdError, Uint128, Uint256}; +use cosmwasm_std::{Decimal256, StdError}; use std::str::FromStr; /// Stream validation related constants @@ -57,9 +57,5 @@ pub fn check_name_and_url(name: &String, url: &Option) -> Result<(), Con } pub fn from_semver(err: semver::Error) -> ContractError { - ContractError::from(StdError::generic_err(format!("Semver: {}", err))) -} - -pub fn to_uint256(value: Uint128) -> Uint256 { - Uint256::from(value.u128()) + ContractError::from(StdError::msg(format!("Semver: {}", err))) } diff --git a/src/killswitch.rs b/src/killswitch.rs index cc68163..af78b71 100644 --- a/src/killswitch.rs +++ b/src/killswitch.rs @@ -4,7 +4,7 @@ use crate::threshold::{ThresholdError, ThresholdState}; use crate::ContractError; use cosmwasm_std::{ attr, BankMsg, Coin, CosmosMsg, DepsMut, Env, MessageInfo, Response, StdResult, Timestamp, - Uint128, Uint256, + Uint256, }; use cw_utils::maybe_addr; @@ -80,14 +80,13 @@ pub fn execute_withdraw_paused( attr("operator_target", operator_target.clone()), attr("withdraw_amount", withdraw_amount), ]; - let withdraw_amount_u128: Uint128 = withdraw_amount.to_string().parse().unwrap(); // send funds to withdraw address or to the sender let res = Response::new() .add_message(CosmosMsg::Bank(BankMsg::Send { to_address: operator_target.to_string(), amount: vec![Coin { denom: stream.in_denom, - amount: withdraw_amount_u128, + amount: withdraw_amount, }], })) .add_attributes(attributes); @@ -153,14 +152,13 @@ pub fn execute_exit_cancelled( attr("operator_target", operator_target.clone()), attr("total_balance", total_balance), ]; - let total_balance_u128: Uint128 = total_balance.to_string().parse().unwrap(); // send funds to withdraw address or to the sender let res = Response::new() .add_message(CosmosMsg::Bank(BankMsg::Send { to_address: operator_target.to_string(), amount: vec![Coin { denom: stream.in_denom, - amount: total_balance_u128, + amount: total_balance, }], })) .add_attributes(attributes); @@ -268,15 +266,13 @@ pub fn execute_cancel_stream( stream.status = Status::Cancelled; STREAMS.save(deps.storage, stream_id, &stream)?; - let out_supply_u128: Uint128 = stream.out_supply.to_string().parse().unwrap(); - //Refund all out tokens to stream creator(treasury) let messages: Vec = vec![ CosmosMsg::Bank(BankMsg::Send { to_address: stream.treasury.to_string(), amount: vec![Coin { denom: stream.out_denom, - amount: out_supply_u128, + amount: stream.out_supply, }], }), //Refund stream creation fee to stream creator @@ -284,7 +280,7 @@ pub fn execute_cancel_stream( to_address: stream.treasury.to_string(), amount: vec![Coin { denom: stream.stream_creation_denom, - amount: stream.stream_creation_fee, + amount: Uint256::from(stream.stream_creation_fee), }], }), ]; @@ -340,12 +336,11 @@ pub fn execute_cancel_stream_with_threshold( STREAMS.save(deps.storage, stream_id, &stream)?; //Refund all out tokens to stream creator(treasury) - let out_supply_u128: Uint128 = stream.out_supply.to_string().parse().unwrap(); let messages: Vec = vec![CosmosMsg::Bank(BankMsg::Send { to_address: stream.treasury.to_string(), amount: vec![Coin { denom: stream.out_denom, - amount: out_supply_u128, + amount: stream.out_supply, }], })]; @@ -387,12 +382,11 @@ pub fn execute_treasury_cancel_stream( STREAMS.remove(deps.storage, stream_id); //Refund all out tokens to stream creator(treasury) - let out_supply_u128: Uint128 = stream.out_supply.to_string().parse().unwrap(); let messages: Vec = vec![CosmosMsg::Bank(BankMsg::Send { to_address: stream.treasury.to_string(), amount: vec![Coin { denom: stream.out_denom, - amount: out_supply_u128, + amount: stream.out_supply, }], })]; @@ -480,14 +474,13 @@ pub fn sudo_cancel_stream( } stream.status = Status::Cancelled; STREAMS.save(deps.storage, stream_id, &stream)?; - let out_supply_u128: Uint128 = stream.out_supply.to_string().parse().unwrap(); //Refund all out tokens to stream creator(treasury) let messages: Vec = vec![ CosmosMsg::Bank(BankMsg::Send { to_address: stream.treasury.to_string(), amount: vec![Coin { denom: stream.out_denom, - amount: out_supply_u128, + amount: stream.out_supply, }], }), //Refund stream creation fee to stream creator @@ -495,7 +488,7 @@ pub fn sudo_cancel_stream( to_address: stream.treasury.to_string(), amount: vec![Coin { denom: stream.stream_creation_denom, - amount: stream.stream_creation_fee, + amount: Uint256::from(stream.stream_creation_fee), }], }), ]; diff --git a/src/msg.rs b/src/msg.rs index 71f03c9..53f9442 100644 --- a/src/msg.rs +++ b/src/msg.rs @@ -11,7 +11,7 @@ pub struct InstantiateMsg { /// Accepted stream creation fee denom pub stream_creation_denom: String, /// Stream creation fee amount - pub stream_creation_fee: Uint128, + pub stream_creation_fee: Uint256, /// in/buy token exit fee in percent pub exit_fee_percent: Decimal256, /// Address of the fee collector @@ -135,7 +135,7 @@ pub enum ExecuteMsg { min_stream_duration: Option, min_duration_until_start_time: Option, stream_creation_denom: Option, - stream_creation_fee: Option, + stream_creation_fee: Option, fee_collector: Option, accepted_in_denom: Option, exit_fee_percent: Option, @@ -201,7 +201,7 @@ pub struct ConfigResponse { /// Denom used as fee for creating a stream. pub stream_creation_denom: String, /// Creation fee amount. - pub stream_creation_fee: Uint128, + pub stream_creation_fee: Uint256, /// This percentage represents the fee that will be collected from the investors. pub exit_fee_percent: Decimal256, /// Address of the fee collector. @@ -251,7 +251,7 @@ pub struct StreamResponse { /// Exit fee percent. pub exit_fee_percent: Decimal256, /// Creation fee amount. - pub stream_creation_fee: Uint128, + pub stream_creation_fee: Uint256, } #[cw_serde] diff --git a/src/state.rs b/src/state.rs index 62d91e0..0b500a9 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,6 +1,6 @@ use crate::ContractError; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, Decimal256, Storage, Timestamp, Uint128, Uint256, Uint64}; +use cosmwasm_std::{Addr, Decimal256, Storage, Timestamp, Uint256, Uint64}; use cw_storage_plus::{Item, Map}; use std::ops::Mul; @@ -15,7 +15,7 @@ pub struct Config { /// Accepted stream creation fee denom pub stream_creation_denom: String, /// Stream creation fee amount - pub stream_creation_fee: Uint128, + pub stream_creation_fee: Uint256, /// in/buy token exit fee in percent pub exit_fee_percent: Decimal256, /// Address of the fee collector @@ -67,7 +67,7 @@ pub struct Stream { /// Stream creation fee denom. Saved under here to avoid any changes in config to efect existing streams. pub stream_creation_denom: String, /// Stream creation fee amount. Saved under here to avoid any changes in config to efect existing streams. - pub stream_creation_fee: Uint128, + pub stream_creation_fee: Uint256, /// Stream swap fee in percent. Saved under here to avoid any changes in config to efect existing streams. pub stream_exit_fee_percent: Decimal256, // Tos version @@ -110,7 +110,7 @@ impl Stream { end_time: Timestamp, last_updated: Timestamp, stream_creation_denom: String, - stream_creation_fee: Uint128, + stream_creation_fee: Uint256, stream_exit_fee_percent: Decimal256, tos_version: String, ) -> Self { @@ -254,7 +254,7 @@ impl TreasuryCancelStreamPeriod { mod tests { use super::*; - use cosmwasm_std::{Addr, Uint128}; + use cosmwasm_std::Addr; // Test compute_shares_amount #[test] @@ -278,7 +278,7 @@ mod tests { status: Status::Waiting, pause_date: None, stream_creation_denom: "fee_denom".to_string(), - stream_creation_fee: Uint128::from(150000000000000000000u128), + stream_creation_fee: Uint256::from(150000000000000000000u128), stream_exit_fee_percent: Decimal256::percent(1), tos_version: "".to_string(), }; diff --git a/src/threshold.rs b/src/threshold.rs index e77d356..be0faa5 100644 --- a/src/threshold.rs +++ b/src/threshold.rs @@ -6,7 +6,7 @@ use crate::state::Stream; pub type Threshold = Uint256; -#[derive(Error, Debug, PartialEq)] +#[derive(Error, Debug)] pub enum ThresholdError { #[error(transparent)] Std(#[from] StdError), @@ -25,15 +25,15 @@ pub enum ThresholdError { } pub const THRESHOLDS_STATE_KEY: &str = "thresholds"; -pub struct ThresholdState<'a>(Map<'a, u64, Threshold>); +pub struct ThresholdState(Map); -impl<'a> Default for ThresholdState<'a> { +impl Default for ThresholdState { fn default() -> Self { Self::new() } } -impl<'a> ThresholdState<'a> { +impl ThresholdState { pub fn new() -> Self { ThresholdState(Map::new(THRESHOLDS_STATE_KEY)) } @@ -116,7 +116,7 @@ mod tests { use super::*; use crate::state::Stream; use cosmwasm_std::testing::MockStorage; - use cosmwasm_std::{Addr, Decimal256, Timestamp, Uint128}; + use cosmwasm_std::{Addr, Decimal256, Timestamp}; #[test] fn test_thresholds_state() { @@ -140,7 +140,7 @@ mod tests { spent_in: Uint256::zero(), status: crate::state::Status::Active, stream_creation_denom: "uusd".to_string(), - stream_creation_fee: Uint128::new(0), + stream_creation_fee: Uint256::from(0 as u128), stream_exit_fee_percent: Decimal256::from_str("0.042").unwrap(), treasury: Addr::unchecked("treasury"), tos_version: "".to_string(), From d14402d0b515894944d4b56874a4b9f1259da8a3 Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Thu, 21 Aug 2025 16:21:16 +0300 Subject: [PATCH 02/25] add partial eq --- Cargo.toml | 3 +- src/error.rs | 15 +- src/tests.rs | 8271 +++++++++++++++++++++++++------------------------- 3 files changed, 4144 insertions(+), 4145 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 09e5e9f..0ffb261 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ crate-type = ["cdylib", "rlib"] [features] # use library feature to disable all instantiate/execute/query exports library = [] +backtrace = [] [package.metadata.scripts] optimize = """docker run --rm -v "$(pwd)":/code \ @@ -29,7 +30,7 @@ optimize = """docker run --rm -v "$(pwd)":/code \ """ [dependencies] -cosmwasm-std = "3.0.1" +cosmwasm-std = { version = "3.0.1", default-features = false, features = ["std", "stargate"] } cosmwasm-schema = "3.0.1" cw-controllers = "3.0.0" cw-storage-plus = "3.0.0" diff --git a/src/error.rs b/src/error.rs index 972b3a1..26955f5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -8,10 +8,10 @@ use thiserror::Error; #[derive(Error, Debug)] pub enum ContractError { - #[error("{0}")] + #[error(transparent)] Std(#[from] StdError), - #[error("{0}")] + #[error(transparent)] Overflow(#[from] OverflowError), #[error("{0}")] @@ -167,3 +167,14 @@ pub enum ContractError { #[error("Treasury cancel period : Not set")] TreasuryCancelPeriodNotSet {}, } + +impl PartialEq for ContractError { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + // StdError variants cannot be compared due to cosmwasm changes + (ContractError::Std(_), ContractError::Std(_)) => false, + // For all other variants, use discriminant comparison (type only, no data comparison) + _ => std::mem::discriminant(self) == std::mem::discriminant(other), + } + } +} diff --git a/src/tests.rs b/src/tests.rs index e8f111a..4cca12a 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -11,16 +11,24 @@ mod test_module { use crate::state::{Status, Stream}; use crate::threshold::ThresholdError; use crate::ContractError; - use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + use cosmwasm_std::testing::{mock_dependencies, mock_env}; use cosmwasm_std::StdError::{self}; use cosmwasm_std::{ - attr, coin, Addr, Attribute, BankMsg, Coin, CosmosMsg, Decimal256, Response, SubMsg, - Timestamp, Uint128, Uint256, Uint64, + attr, coin, Addr, Attribute, BankMsg, Coin, CosmosMsg, Decimal256, MessageInfo, Response, + SubMsg, Timestamp, Uint128, Uint256, Uint64, }; use cw_utils::PaymentError; + use std::borrow::Borrow; use std::ops::Sub; use std::str::FromStr; + fn mock_info(sender: &str, funds: &[Coin]) -> MessageInfo { + MessageInfo { + sender: Addr::unchecked(sender), + funds: funds.to_vec(), + } + } + #[test] fn test_compute_shares_amount() { let mut stream = Stream::new( @@ -34,7 +42,7 @@ mod test_module { Timestamp::from_seconds(100), Timestamp::from_seconds(0), "fee".to_string(), - Uint128::from(100u128), + Uint256::from(100u128), Decimal256::percent(10), "v1".to_string(), ); @@ -69,23 +77,23 @@ mod test_module { min_stream_seconds: Uint64::new(1000), min_seconds_until_start_time: Uint64::new(1000), stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), + stream_creation_fee: Uint256::from(100u128), exit_fee_percent: Decimal256::percent(101), fee_collector: "collector".to_string(), protocol_admin: "protocol_admin".to_string(), accepted_in_denom: "in".to_string(), tos_version: "v1".to_string(), }; - let res = + let err = instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap_err(); - assert_eq!(res, ContractError::InvalidExitFeePercent {}); + assert_eq!(err, ContractError::InvalidExitFeePercent {}); // Invalid stream creation fee let msg = crate::msg::InstantiateMsg { min_stream_seconds: Uint64::new(1000), min_seconds_until_start_time: Uint64::new(1000), stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::zero(), + stream_creation_fee: Uint256::zero(), exit_fee_percent: Decimal256::percent(1), fee_collector: "collector".to_string(), protocol_admin: "protocol_admin".to_string(), @@ -100,7 +108,7 @@ mod test_module { min_stream_seconds: Uint64::new(1000), min_seconds_until_start_time: Uint64::new(1000), stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), + stream_creation_fee: Uint256::from(100u128), exit_fee_percent: Decimal256::percent(1), fee_collector: "collector".to_string(), protocol_admin: "protocol_admin".to_string(), @@ -136,7 +144,7 @@ mod test_module { None, "v1".to_string(), ); - assert_eq!(res, Err(ContractError::InDenomIsNotAccepted {})); + assert_eq!(res.unwrap_err(), ContractError::InDenomIsNotAccepted {}); // end < start case let treasury = "treasury"; let name = "name"; @@ -164,7 +172,7 @@ mod test_module { None, "v1".to_string(), ); - assert_eq!(res, Err(ContractError::StreamInvalidEndTime {})); + assert_eq!(res.unwrap_err(), ContractError::StreamInvalidEndTime {}); // min_stream_duration is not sufficient let end_time = Timestamp::from_seconds(1000); @@ -187,7 +195,7 @@ mod test_module { None, "v1".to_string(), ); - assert_eq!(res, Err(ContractError::StreamDurationTooShort {})); + assert_eq!(res.unwrap_err(), ContractError::StreamDurationTooShort {}); // start cannot be before current time let end_time = Timestamp::from_seconds(1000); @@ -210,7 +218,7 @@ mod test_module { None, "v1".to_string(), ); - assert_eq!(res, Err(ContractError::StreamInvalidStartTime {})); + assert_eq!(res.unwrap_err(), ContractError::StreamInvalidStartTime {}); // stream starts too soon case let end_time = Timestamp::from_seconds(100000); @@ -233,7 +241,7 @@ mod test_module { None, "v1".to_string(), ); - assert_eq!(res, Err(ContractError::StreamStartsTooSoon {})); + assert_eq!(res.unwrap_err(), ContractError::StreamStartsTooSoon {}); // Same in and out denom case let end_time = Timestamp::from_seconds(100000); @@ -256,7 +264,7 @@ mod test_module { None, "v1".to_string(), ); - assert_eq!(res, Err(ContractError::SameDenomOnEachSide {})); + assert_eq!(res.unwrap_err(), ContractError::SameDenomOnEachSide {}); // 0 out supply case let end_time = Timestamp::from_seconds(100000); @@ -279,7 +287,7 @@ mod test_module { None, "v1".to_string(), ); - assert_eq!(res, Err(ContractError::ZeroOutSupply {})); + assert_eq!(res.unwrap_err(), ContractError::ZeroOutSupply {}); // threshold zero case let start_time = Timestamp::from_seconds(3000); @@ -291,7 +299,7 @@ mod test_module { &[ Coin { denom: "fee".to_string(), - amount: Uint128::new(100), + amount: Uint256::from(100u128), }, Coin { denom: out_denom.to_string(), @@ -341,93 +349,12 @@ mod test_module { None, "v1".to_string(), ); - assert_eq!(res, Err(ContractError::NoFundsSent {})); + assert_eq!(res.unwrap_err().to_string(), "NoFundsSent"); // wrong supply amount case let mut env = mock_env(); env.block.time = Timestamp::from_seconds(1); - let info = mock_info("creator1", &[Coin::new(1_000_000, "out_denom")]); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - name.to_string(), - Some(url.to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start_time, - end_time, - None, - "v1".to_string(), - ); - assert_eq!(res, Err(ContractError::StreamOutSupplyFundsRequired {})); - - // wrong creation fee case - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(1); - let info = mock_info( - "creator1", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), "out_denom"), - Coin::new(99, "fee"), - ], - ); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - name.to_string(), - Some(url.to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start_time, - end_time, - None, - "v1".to_string(), - ); - assert_eq!(res, Err(ContractError::StreamCreationFeeRequired {})); - - // no creation fee case - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(1); - let info = mock_info( - "creator1", - &[Coin::new( - out_supply.to_string().parse().unwrap(), - "out_denom", - )], - ); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - name.to_string(), - Some(url.to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start_time, - end_time, - None, - "v1".to_string(), - ); - assert_eq!(res, Err(ContractError::NoFundsSent {})); - - // mismatch creation fee case - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(1); - let info = mock_info( - "creator1", - &[Coin::new( - out_supply.to_string().parse().unwrap(), - "out_denom", - )], - ); + let info = mock_info("creator1", &[Coin::new(1_000_000u128, "out_denom")]); let res = execute_create_stream( deps.as_mut(), env, @@ -443,4049 +370,4109 @@ mod test_module { None, "v1".to_string(), ); - assert_eq!(res, Err(ContractError::NoFundsSent {})); - - // same denom case, insufficient total - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(1); - let info = mock_info("creator1", &[Coin::new(1, "fee")]); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - name.to_string(), - Some(url.to_string()), - in_denom.to_string(), - "fee".to_string(), - out_supply, - start_time, - end_time, - None, - "v1".to_string(), - ); - assert_eq!(res, Err(ContractError::StreamOutSupplyFundsRequired {})); - - // same denom case, sufficient total - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(1); - let info = mock_info( - "creator1", - &[Coin::new( - out_supply - .strict_add(Uint256::from(100u128)) - .to_string() - .parse() - .unwrap(), - "fee", - )], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - name.to_string(), - Some(url.to_string()), - in_denom.to_string(), - "fee".to_string(), - out_supply, - start_time, - end_time, - None, - "v1".to_string(), - ) - .unwrap(); - - // same tokens extra funds sent - let info = mock_info( - "creator1", - &[ - coin( - out_supply - .strict_add(Uint256::from(100u128)) - .to_string() - .parse() - .unwrap(), - "fee", - ), - coin(15, "random"), - ], - ); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(1); - let err = execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - name.to_string(), - Some(url.to_string()), - in_denom.to_string(), - "fee".to_string(), - out_supply, - start_time, - end_time, - None, - "v1".to_string(), - ) - .unwrap_err(); - assert_eq!(err, ContractError::InvalidFunds {}); - - // different tokens extra funds sent - let info = mock_info( - "creator1", - &[ - coin(out_supply.to_string().parse().unwrap(), "different_denom"), - coin(Uint128::new(100).u128(), "fee"), - coin(15, "random"), - ], - ); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(1); - let err = execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - name.to_string(), - Some(url.to_string()), - in_denom.to_string(), - "different_denom".to_string(), - out_supply, - start_time, - end_time, - None, - "v1".to_string(), - ) - .unwrap_err(); - assert_eq!(err, ContractError::InvalidFunds {}); - - // failed name checks - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(1); - let info = mock_info( - "creator1", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), "out_denom"), - Coin::new(100, "fee"), - ], - ); - let res = execute_create_stream( - deps.as_mut(), - env.clone(), - info.clone(), - treasury.to_string(), - "n".to_string(), - Some(url.to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start_time, - end_time, - None, - "v1".to_string(), - ) - .unwrap_err(); - assert_eq!(res, ContractError::StreamNameTooShort {}); - - let res = execute_create_stream( - deps.as_mut(), - env.clone(), - info.clone(), - treasury.to_string(), - "12345678901234567890123456789012345678901234567890123456789012345".to_string(), - Some(url.to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start_time, - end_time, - None, - "v1".to_string(), - ) - .unwrap_err(); - assert_eq!(res, ContractError::StreamNameTooLong {}); - - let res = execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "abc~ß".to_string(), - Some(url.to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start_time, - end_time, - None, - "v1".to_string(), - ) - .unwrap_err(); - assert_eq!(res, ContractError::InvalidStreamName {}); - - //failed url checks - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(1); - let info = mock_info( - "creator1", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), "out_denom"), - Coin::new(100, "fee"), - ], + assert_eq!( + res.unwrap_err(), + ContractError::StreamOutSupplyFundsRequired {} ); - let res = execute_create_stream( - deps.as_mut(), - env.clone(), - info.clone(), - treasury.to_string(), - "name".to_string(), - Some("https://a.b".to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start_time, - end_time, - None, - "v1".to_string(), - ) - .unwrap_err(); - assert_eq!(res, ContractError::StreamUrlTooShort {}); - - let res = execute_create_stream( - deps.as_mut(), - env.clone(), - info.clone(), - treasury.to_string(), - "name".to_string(), - Some("https://abcdefghijklmnopqrstuvw.xyz/abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz/abcdefghijklmnopqrstuvwxyzabcdefghijklmn".to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start_time, - end_time, - None, - "v1".to_string(), - ) - .unwrap_err(); - assert_eq!(res, ContractError::StreamUrlTooLong {}); - - let res = execute_create_stream( - deps.as_mut(), - env.clone(), - info.clone(), - treasury.to_string(), - "name".to_string(), - Some("https://abc defghijklmnopqrstuvw.xyz/".to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start_time, - end_time, - None, - "v1".to_string(), - ) - .unwrap_err(); - assert_eq!(res, ContractError::InvalidStreamUrl {}); - - let res = execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "name".to_string(), - Some("https://abcdefghijklmnopqrstuvw.xyz/".to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start_time, - end_time, - None, - "random".to_string(), - ) - .unwrap_err(); - assert_eq!(res, ContractError::InvalidToSVersion {}); - - // happy path - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(1); - let info = mock_info( - "creator1", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), "out_denom"), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - name.to_string(), - Some(url.to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start_time, - end_time, - None, - "v1".to_string(), - ) - .unwrap(); - - // query stream with id - let env = mock_env(); - let stream = query_stream(deps.as_ref(), env, 1).unwrap(); - assert_eq!(stream.id, 1); - } - - #[test] - fn test_subscribe() { - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(2000); - let end = Timestamp::from_seconds(1_000_000); - let out_supply = Uint256::from(1_000_000u128); - let out_denom = "out_denom"; - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(100); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(1000), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(1); - let info = mock_info( - "creator1", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - "in".to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - - // stream ended - let mut env = mock_env(); - env.block.time = end.plus_seconds(1000000); - let info = mock_info("creator1", &[]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(res, ContractError::StreamEnded {}); - - // no funds - let mut env = mock_env(); - env.block.time = start.plus_seconds(100); - let info = mock_info("creator1", &[]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(res, PaymentError::NoFunds {}.into()); - - // incorrect denom - let mut env = mock_env(); - env.block.time = start.plus_seconds(100); - let info = mock_info("creator1", &[Coin::new(100, "wrong_denom")]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let res = execute(deps.as_mut(), env.clone(), info, msg).unwrap_err(); - assert_eq!(res, PaymentError::MissingDenom("in".to_string()).into()); - - let stream = query_stream(deps.as_ref(), env, 1).unwrap(); - assert_eq!(stream.status, Status::Waiting); - - // incorrect toc - let mut env = mock_env(); - env.block.time = start.plus_seconds(100); - let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "random".to_string(), - }; - let res = execute(deps.as_mut(), env.clone(), info, msg).unwrap_err(); - assert_eq!(res, ContractError::InvalidToSVersion {}); - - // first subscribe - let mut env = mock_env(); - env.block.time = start.plus_seconds(100); - let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg); - - // dist index updated - let env = mock_env(); - let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); - // position index not updated, in_supply updated - assert_eq!(stream.dist_index, Decimal256::zero()); - //see that the status is updated - assert_eq!(stream.status, Status::Active); - assert_eq!(stream.in_supply, Uint256::from(1000000u128)); - let position = query_position(deps.as_ref(), env, 1, "creator1".to_string()).unwrap(); - assert_eq!(position.index, Decimal256::zero()); - assert_eq!(position.in_balance, Uint256::from(1000000u128)); - // unauthorized subscription increase - let mut env = mock_env(); - env.block.time = start.plus_seconds(200); - let info = mock_info("random", &[Coin::new(1_000_000, "in")]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: Some("creator1".to_string()), - operator: None, - tos_version: "v1".to_string(), - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(res, ContractError::Unauthorized {}); - - // subscription increase - let mut env = mock_env(); - env.block.time = start.plus_seconds(200); - let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env.clone(), info, msg); - // dist index updated - let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); - assert_eq!(stream.dist_index, Decimal256::from_str("0.0001").unwrap()); - // dist index updated, position reduced and increased - let position = query_position(deps.as_ref(), env, 1, "creator1".to_string()).unwrap(); - assert_eq!(position.index, Decimal256::from_str("0.0001").unwrap()); - assert_eq!(position.in_balance, Uint256::from(1999900u128)); - } - - #[test] - fn test_subscribe_pending() { - // instantiate - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(5_000); - let end = Timestamp::from_seconds(10_000); - let out_supply = Uint256::from(1_000_000u128); - let out_denom = "out_denom"; - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(100); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(0), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(200); - let info = mock_info( - "creator1", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - "in".to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - - // first subscribe - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(300); - - let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(res.attributes[0].key, "action"); - assert_eq!(res.attributes[0].value, "subscribe_pending"); - // query stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(350); - let stream = query_stream(deps.as_ref(), env, 1).unwrap(); - assert_eq!(stream.status, Status::Waiting); - assert_eq!(stream.in_supply, Uint256::from(1000000u128)); - assert_eq!(stream.shares, Uint256::from(1000000u128)); - - // second subscribe still waiting - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(500); - let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(res.attributes[0].key, "action"); - assert_eq!(res.attributes[0].value, "subscribe_pending"); - - // query stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(450); - let stream = query_stream(deps.as_ref(), env, 1).unwrap(); - assert_eq!(stream.status, Status::Waiting); - assert_eq!(stream.in_supply, Uint256::from(2000000u128)); - - // Before stream start time 2 subscriptions have been made and the stream is pending - // After stream start time plus 1000 seconds one subscription is made and the stream is active - // Creator 1 has 2 subscriptions and 2_000_000 in balance - // Creator 2 has 1 subscription and 1_000_000 in balance - // At 6000 seconds the stream is active and the balance to be distributed is ~2000000 - // At 6000 seconds creator 1 shold spent 2000000*1000/5000= 400000 - // At 6000 seconds creator 1 should get all 2000000 tokens - // At 6000 seconds creator 2 should get 0 tokens - // At 7500 seconds the stream is active and the balance to be distributed is 300000 - // At 7500 seconds creator 1 should get 300000*2000000/3250000 = 184615 - // At 7500 seconds creator 2 should get 300000*1250000/3250000 = 115384 - - // subscription after start time - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(6000); - let info = mock_info("creator2", &[Coin::new(1_000_000, "in")]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(res.attributes[0].key, "action"); - // diffirent action because stream is active - assert_eq!(res.attributes[0].value, "subscribe"); - - // update creator 1 position - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(6000); - let update_msg = crate::msg::ExecuteMsg::UpdatePosition { - stream_id: 1, - operator_target: None, - }; - let info = mock_info("creator1", &[]); - let _res = execute(deps.as_mut(), env.clone(), info, update_msg).unwrap(); - let position = query_position(deps.as_ref(), env, 1, "creator1".to_string()).unwrap(); - assert_eq!(position.spent, Uint256::from(400000u128)); - - // query stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(6000); - let stream = query_stream(deps.as_ref(), env, 1).unwrap(); - assert_eq!(stream.status, Status::Active); - assert_eq!(stream.in_supply, Uint256::from(3000000u128 - 400000u128)); - assert_eq!(stream.spent_in, Uint256::from(400000u128)); - - // update creator 1 position at 3500 - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(7500); - let update_msg = crate::msg::ExecuteMsg::UpdatePosition { - stream_id: 1, - operator_target: None, - }; - let info = mock_info("creator1", &[]); - let _res = execute(deps.as_mut(), env.clone(), info, update_msg).unwrap(); - - // query position - let res = query_position(deps.as_ref(), env, 1, "creator1".to_string()).unwrap(); - assert_eq!(res.purchased, Uint256::from(184615u128 + 200000u128)); - assert_eq!(res.spent, Uint256::from(2000000u128 / 2u128)); - - // update creator 2 position at 3500 - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(3500); - let update_msg = crate::msg::ExecuteMsg::UpdatePosition { - stream_id: 1, - operator_target: None, - }; - let info = mock_info("creator2", &[]); - let _res = execute(deps.as_mut(), env.clone(), info, update_msg).unwrap(); - - // query position - let res = query_position(deps.as_ref(), env, 1, "creator2".to_string()).unwrap(); - assert_eq!(res.purchased, Uint256::from(115384u128)); - // spent = in_supply * (now-last_updated) / (end-last_updated) - assert_eq!(res.spent, Uint256::from(1000000u128 * 1500u128 / 4000u128)); - // query stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(3500); - let stream = query_stream(deps.as_ref(), env, 1).unwrap(); - assert_eq!(stream.status, Status::Active); - // in supply = 3000000 - (positions.spent summed) - assert_eq!(stream.in_supply, Uint256::from(1625000u128)); - } - - #[test] - pub fn test_withdraw_pending() { - // // instantiate - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(2000); - let end = Timestamp::from_seconds(1_000_000); - let out_supply = Uint256::from(1_000_000u128); - let out_denom = "out_denom"; - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(100); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(0), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(200); - let info = mock_info( - "creator1", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - "in".to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - - // first subscribe before start time - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(300); - let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - // update creator 1 position no distrubution is excepted - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(350); - let update_msg = crate::msg::ExecuteMsg::UpdatePosition { - stream_id: 1, - operator_target: None, - }; - let info = mock_info("creator1", &[]); - let res = execute(deps.as_mut(), env.clone(), info, update_msg).unwrap(); - - let expected_attributes = vec![ - Attribute::new("action", "update_position"), - Attribute::new("stream_id", "1"), - Attribute::new("in_balance", "1000000"), - Attribute::new("shares", "1000000"), - Attribute::new("index", "0"), - Attribute::new("last_updated", start.to_string()), - Attribute::new("pending_purchase", "0"), - Attribute::new("purchased", "0"), - Attribute::new("spent", "0"), - ]; - assert_eq!(res.attributes, expected_attributes); - - // query stream before withdraw - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(400); - let stream = query_stream(deps.as_ref(), env, 1).unwrap(); - - assert_eq!(stream.id, 1); - assert_eq!(stream.dist_index, Decimal256::zero()); - assert_eq!(stream.last_updated, Timestamp::from_seconds(2000)); - assert_eq!(stream.in_supply, Uint256::from(1_000_000u128)); - assert_eq!(stream.spent_in, Uint256::zero()); - assert_eq!(stream.shares, Uint256::from(1_000_000u128)); - - // withdraw before start time - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(400); - let info = mock_info("creator1", &[]); - let msg = crate::msg::ExecuteMsg::Withdraw { - stream_id: 1, - cap: Some(Uint256::from(500_000u128)), - operator_target: None, - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(res.attributes[0].value, "withdraw_pending"); - assert_eq!(res.attributes[1].key, "stream_id"); - assert_eq!(res.attributes[1].value, "1"); - assert_eq!(res.attributes[3].key, "withdraw_amount"); - assert_eq!(res.attributes[3].value, "500000"); - assert_eq!( - res.messages[0].msg, - CosmosMsg::Bank(BankMsg::Send { - to_address: "creator1".to_string(), - amount: vec![Coin::new(500000, "in")] - }) - ); - // query stream after withdraw - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(400); - let stream = query_stream(deps.as_ref(), env, 1).unwrap(); - assert_eq!(stream.id, 1); - assert_eq!(stream.dist_index, Decimal256::zero()); - assert_eq!(stream.last_updated, Timestamp::from_seconds(2000)); - assert_eq!(stream.in_supply, Uint256::from(500_000u128)); - assert_eq!(stream.spent_in, Uint256::zero()); - assert_eq!(stream.shares, Uint256::from(500_000u128)); - - // withdraw after start time - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(3000); - let info = mock_info("creator1", &[]); - let msg = crate::msg::ExecuteMsg::Withdraw { - stream_id: 1, - cap: Some(Uint256::from(400_000u128)), - operator_target: None, - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(res.attributes[0].value, "withdraw"); - assert_eq!(res.attributes[1].key, "stream_id"); - assert_eq!(res.attributes[1].value, "1"); - assert_eq!(res.attributes[3].key, "withdraw_amount"); - assert_eq!(res.attributes[3].value, "400000"); - assert_eq!( - res.messages[0].msg, - CosmosMsg::Bank(BankMsg::Send { - to_address: "creator1".to_string(), - amount: vec![Coin::new(400000, "in")] - }) - ); - // query stream after withdraw - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(3000); - let _stream = query_stream(deps.as_ref(), env, 1).unwrap(); - } - - #[test] - fn test_operator() { - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(1_590_797_419); - let end = Timestamp::from_seconds(5_571_797_419); - let out_supply = Uint256::from(1_000_000u128); - let out_denom = "out_denom"; - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(100); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(1), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let env = mock_env(); - let info = mock_info( - "creator", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - "in".to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - - //random cannot make the first subscription on behalf of user - let mut env = mock_env(); - let info = mock_info("random", &[Coin::new(1_000_000, "in")]); - env.block.time = start.plus_seconds(100); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: Some("creator1".to_string()), - operator: None, - tos_version: "v1".to_string(), - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(res, ContractError::Unauthorized {}); - - //random cannot make the first subscription on behalf of user even if defined as operator in message - let mut env = mock_env(); - let info = mock_info("random", &[Coin::new(1_000_000, "in")]); - env.block.time = start.plus_seconds(100); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: Some("creator1".to_string()), - operator: Some("random".to_string()), - tos_version: "v1".to_string(), - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(res, ContractError::Unauthorized {}); - - // first subscription - let mut env = mock_env(); - env.block.time = start.plus_seconds(100); - let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg); - - // only owner can update - let mut env = mock_env(); - let info = mock_info("creator2", &[]); - env.block.time = start.plus_seconds(100); - let res = - execute_update_position(deps.as_mut(), env, info, 1, Some("creator1".to_string())) - .unwrap_err(); - assert_eq!(res, ContractError::Unauthorized {}); - - // update creator 1 position no distrubution is excepted - let mut env = mock_env(); - env.block.time = start.plus_seconds(100); - let update_msg = crate::msg::ExecuteMsg::UpdatePosition { - stream_id: 1, - operator_target: None, - }; - let info = mock_info("creator1", &[]); - let res = execute(deps.as_mut(), env.clone(), info, update_msg).unwrap(); - - let expected_attributes = vec![ - Attribute::new("action", "update_position"), - Attribute::new("stream_id", "1"), - Attribute::new("in_balance", "1000000"), - Attribute::new("shares", "1000000"), - Attribute::new("index", "0"), - Attribute::new("last_updated", "1590797519.000000000"), - Attribute::new("pending_purchase", "0"), - Attribute::new("purchased", "0"), - Attribute::new("spent", "0"), - ]; - assert_eq!(res.attributes, expected_attributes); - - // Optional: Add query to verify position state matches response - let position = - query_position(deps.as_ref(), env.clone(), 1, "creator1".to_string()).unwrap(); - assert_eq!(position.in_balance, Uint256::from(1000000u128)); - assert_eq!(position.shares, Uint256::from(1000000u128)); - assert_eq!(position.index, Decimal256::zero()); - assert_eq!(position.last_updated, env.block.time); - assert_eq!(position.pending_purchase, Decimal256::zero()); - assert_eq!(position.purchased, Uint256::zero()); - assert_eq!(position.spent, Uint256::zero()); - assert_eq!(position.tos_version, "v1"); - - // random cannot update - let info = mock_info("random", &[]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(100); - let res = - execute_update_position(deps.as_mut(), env, info, 1, Some("creator1".to_string())) - .unwrap_err(); - assert_eq!(res, ContractError::Unauthorized {}); - - // random cannot withdraw - let _info = mock_info("random", &[]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(100); - let _msg = crate::msg::ExecuteMsg::Withdraw { - stream_id: 1, - cap: None, - operator_target: Some("creator1".to_string()), - }; - assert_eq!(res, ContractError::Unauthorized {}); - - //owner can update operator - let info = mock_info("creator1", &[]); - let mut env = mock_env(); - let owner = "creator1".to_string(); - let stream_id = 1; - env.block.time = start.plus_seconds(100); - execute_update_operator( - deps.as_mut(), - env.clone(), - info, - 1, - Some("operator1".to_string()), - ) - .unwrap(); - let position = query_position(deps.as_ref(), env, stream_id, owner).unwrap(); - assert_eq!(position.operator.unwrap().as_str(), "operator1".to_string()); - - //operator can increase subscription on behalf of owner - let info = mock_info("operator1", &[Coin::new(1_000_000, "in")]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(100); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: Some("creator1".to_string()), - operator: None, - tos_version: "v1".to_string(), - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - let expected_attributes = vec![ - Attribute::new("action", "subscribe"), - Attribute::new("stream_id", "1"), - Attribute::new("in_balance", "2000000"), - Attribute::new("shares", "2000000"), - Attribute::new("index", "0"), - Attribute::new("last_updated", "1590797519.000000000"), - Attribute::new("pending_purchase", "0"), - Attribute::new("purchased", "0"), - Attribute::new("spent", "0"), - Attribute::new("tos_version", "v1"), - Attribute::new("stream_new_in_supply", "2000000"), - Attribute::new("stream_previous_in_supply", "2000000"), - Attribute::new("stream_new_shares", "2000000"), - Attribute::new("stream_previous_shares", "2000000"), - Attribute::new("stream_status", "Active"), - ]; - assert_eq!(res.attributes, expected_attributes); - - // random cannot update operator - let info = mock_info("random", &[]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(100); - let res = - execute_update_operator(deps.as_mut(), env, info, 1, Some("operator1".to_string())) - .unwrap_err(); - assert!(matches!(res, ContractError::Std(StdError::NotFound { .. }))); - - // operator can't update operator - let info = mock_info("operator1", &[]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(100); - let res = - execute_update_operator(deps.as_mut(), env, info, 1, Some("operator2".to_string())) - .unwrap_err(); - assert!(matches!(res, ContractError::Std(StdError::NotFound { .. }))); - - // operator can update position - let info = mock_info("operator1", &[]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(100); - let res = - execute_update_position(deps.as_mut(), env, info, 1, Some("creator1".to_string())) - .unwrap(); - let expected_attributes = vec![ - Attribute::new("action", "update_position"), - Attribute::new("stream_id", "1"), - Attribute::new("in_balance", "2000000"), - Attribute::new("shares", "2000000"), - Attribute::new("index", "0"), - Attribute::new("last_updated", "1590797519.000000000"), - Attribute::new("pending_purchase", "0"), - Attribute::new("purchased", "0"), - Attribute::new("spent", "0"), - ]; - assert_eq!(res.attributes, expected_attributes); - - // operator can withdraw - let _info = mock_info("operator1", &[]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(100); - let _msg = crate::msg::ExecuteMsg::Withdraw { - stream_id: 1, - cap: Some(5u128.into()), - operator_target: Some("creator1".to_string()), - }; - - // random cannot exit - let info = mock_info("random", &[]); - let mut env = mock_env(); - env.block.time = end.plus_seconds(100); - execute_update_stream(deps.as_mut(), env.clone(), 1).unwrap(); - let res = execute_exit_stream(deps.as_mut(), env, info, 1, Some("creator1".to_string())) - .unwrap_err(); - assert_eq!(res, ContractError::Unauthorized {}); - - let mut env = mock_env(); - env.block.time = end.plus_seconds(100); - execute_update_stream(deps.as_mut(), env, 1).unwrap(); - - // operator can exit - let info = mock_info("operator1", &[]); - let mut env = mock_env(); - env.block.time = end.plus_seconds(100); - let res = - execute_exit_stream(deps.as_mut(), env, info, 1, Some("creator1".to_string())).unwrap(); - match res.messages.first().unwrap().msg.clone() { - CosmosMsg::Bank(BankMsg::Send { - to_address, - amount: _, - }) => { - assert_eq!(to_address, "creator1"); - } - _ => panic!("unexpected message"), - } - } - - #[test] - fn test_update_stream() { - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(1_000_000); - let end = Timestamp::from_seconds(5_000_000); - let out_supply = Uint256::from(1_000_000u128); - let out_denom = "out_denom"; - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(100); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(1000), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(1); - let info = mock_info( - "creator", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - "in".to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - - //update stream without subscription this means no new distribution so returned index should be 0 - let mut env = mock_env(); - env.block.time = start.plus_seconds(100); - let res = execute_update_stream(deps.as_mut(), env, 1).unwrap(); - assert_eq!( - res, - Response::default().add_attributes(vec![ - attr("action", "update_stream"), - attr("stream_id", "1"), - attr("new_distribution_amount", "0"), - attr("dist_index", "0"), - attr("last_updated", "1000100.000000000"), - attr("start_time", "1000000.000000000"), - attr("end_time", "5000000.000000000"), - attr("in_denom", "in"), - attr("out_denom", "out_denom"), - attr("in_supply", "0"), - attr("out_supply", "1000000"), - attr("out_remaining", "1000000"), - attr("spent_in", "0"), - attr("shares", "0"), - attr("current_streamed_price", "0"), - attr("status", "Waiting"), - attr("tos_version", "v1") - ]) - ); - //first subscription - //On first subscription index is not incresed because no distrubution prior to that(Execute_subscibe also includes update_stream) - let mut env = mock_env(); - env.block.time = start.plus_seconds(100); - let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: Some("creator1".to_string()), - operator: None, - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg); - - //Query stream - let mut env = mock_env(); - env.block.time = start.plus_seconds(200); - let res = query_stream(deps.as_ref(), env, 1).unwrap(); - assert_eq!(res.dist_index, Decimal256::zero()); - - //Update stream again, this time with subscriber - let mut env = mock_env(); - env.block.time = start.plus_seconds(300); - execute_update_stream(deps.as_mut(), env, 1).unwrap(); - - //Query stream - let mut env = mock_env(); - env.block.time = start.plus_seconds(300); - let res = query_stream(deps.as_ref(), env, 1).unwrap(); - assert_eq!(res.dist_index, Decimal256::from_str("0.00005").unwrap()) - } - #[test] - fn test_update_position() { - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(1_000_000); - let end = Timestamp::from_seconds(5_000_000); - let out_supply = Uint256::from(1_000_000u128); - let out_denom = "out_denom"; - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(100); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(1000), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(1); - let info = mock_info( - "creator", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - "in".to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - - // first subscription - let mut env = mock_env(); - env.block.time = start.plus_seconds(100); - let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg); - - // non owner operator cannot update position - let mut env = mock_env(); - env.block.time = start.plus_seconds(3_000_000); - let info = mock_info("random", &[]); - let err = - execute_update_position(deps.as_mut(), env, info, 1, Some("creator1".to_string())) - .unwrap_err(); - assert_eq!(err, ContractError::Unauthorized {}); - - // update position - let mut env = mock_env(); - env.block.time = start.plus_seconds(3_000_000); - let info = mock_info("creator1", &[]); - execute_update_position(deps.as_mut(), env.clone(), info, 1, None).unwrap(); - - let position = - query_position(deps.as_ref(), env.clone(), 1, "creator1".to_string()).unwrap(); - assert_eq!( - position.index, - Decimal256::from_str("0.749993000000000000").unwrap() - ); - assert_eq!(position.purchased, Uint256::from(749_993u128)); - assert_eq!(position.spent, Uint256::from(749_993u128)); - assert_eq!(position.in_balance, Uint256::from(250_007u128)); - let stream = query_stream(deps.as_ref(), env, 1).unwrap(); - assert_eq!( - stream.dist_index, - Decimal256::from_str("0.749993000000000000").unwrap() - ); - - // can update position after stream ends - let mut env = mock_env(); - env.block.time = end.plus_seconds(1); - let info = mock_info("creator1", &[]); - execute_update_position(deps.as_mut(), env.clone(), info, 1, None).unwrap(); - let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); - assert_eq!(stream.dist_index, Decimal256::from_str("1").unwrap()); - assert_eq!(stream.in_supply, Uint256::zero()); - let position = query_position(deps.as_ref(), env, 1, "creator1".to_string()).unwrap(); - assert_eq!(position.index, Decimal256::from_str("1").unwrap()); - assert_eq!(position.spent, Uint256::from(1_000_000u128)); - assert_eq!(position.in_balance, Uint256::zero()); - - assert_eq!(stream.out_supply, Uint256::from(1_000_000u128)); - assert_eq!(position.purchased, stream.out_supply); - } - - // this is for testing the leftover amount with bigger values - #[test] - fn test_rounding_leftover() { - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(1_000_000); - let end = Timestamp::from_seconds(5_000_000); - let out_supply = Uint256::from(1_000_000_000_000u128); - let out_denom = "out_denom"; - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(100); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(1000), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(1); - let info = mock_info( - "creator", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - "in".to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - - // first subscription - let mut env = mock_env(); - env.block.time = start.plus_seconds(100); - let info = mock_info("creator1", &[Coin::new(1_000_000_000, "in")]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - execute(deps.as_mut(), env, info, msg).unwrap(); - - // second subscription - let mut env = mock_env(); - env.block.time = start.plus_seconds(100_000); - let info = mock_info("creator2", &[Coin::new(3_000_000_000, "in")]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - execute(deps.as_mut(), env, info, msg).unwrap(); - - // update position creator1 - let mut env = mock_env(); - env.block.time = start.plus_seconds(3_000_000); - let info = mock_info("creator1", &[]); - execute_update_position(deps.as_mut(), env.clone(), info, 1, None).unwrap(); - - let position = - query_position(deps.as_ref(), env.clone(), 1, "creator1".to_string()).unwrap(); - assert_eq!( - position.index, - Decimal256::from_str("202.813614449380587585").unwrap() - ); - assert_eq!(position.purchased, Uint256::from(202_813_614_449u128)); - assert_eq!(position.spent, Uint256::from(749_993_750u128)); - assert_eq!(position.in_balance, Uint256::from(250_006_250u128)); - let stream = query_stream(deps.as_ref(), env, 1).unwrap(); - assert_eq!( - stream.dist_index, - Decimal256::from_str("202.813614449380587585").unwrap() - ); - - // update position creator2 - let mut env = mock_env(); - env.block.time = start.plus_seconds(3_575_000); - let info = mock_info("creator2", &[]); - execute_update_position(deps.as_mut(), env.clone(), info, 1, None).unwrap(); - - let position = - query_position(deps.as_ref(), env.clone(), 1, "creator2".to_string()).unwrap(); - assert_eq!( - position.index, - Decimal256::from_str("238.074595237060799266").unwrap() - ); - assert_eq!(position.purchased, Uint256::from(655672748445u128)); - assert_eq!(position.spent, Uint256::from(2673076923u128)); - assert_eq!(position.in_balance, Uint256::from(326923077u128)); - let stream = query_stream(deps.as_ref(), env, 1).unwrap(); - assert_eq!( - stream.dist_index, - Decimal256::from_str("238.074595237060799266").unwrap() - ); - - // update position after stream ends - let mut env = mock_env(); - env.block.time = end.plus_seconds(1); - let info = mock_info("creator1", &[]); - execute_update_position(deps.as_mut(), env.clone(), info, 1, None).unwrap(); - let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); - assert_eq!( - stream.dist_index, - Decimal256::from_str("264.137059297637397644").unwrap() - ); - assert_eq!(stream.in_supply, Uint256::zero()); - let position1 = query_position(deps.as_ref(), env, 1, "creator1".to_string()).unwrap(); - assert_eq!( - position1.index, - Decimal256::from_str("264.137059297637397644").unwrap() - ); - assert_eq!(position1.spent, Uint256::from(1_000_000_000u128)); - assert_eq!(position1.in_balance, Uint256::zero()); - - // update position after stream ends - let mut env = mock_env(); - env.block.time = end.plus_seconds(1); - let info = mock_info("creator2", &[]); - execute_update_position(deps.as_mut(), env.clone(), info, 1, None).unwrap(); - let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); - assert_eq!( - stream.dist_index, - Decimal256::from_str("264.137059297637397644").unwrap() - ); - assert_eq!(stream.in_supply, Uint256::zero()); - let position2 = query_position(deps.as_ref(), env, 1, "creator2".to_string()).unwrap(); - assert_eq!( - position2.index, - Decimal256::from_str("264.137059297637397644").unwrap() - ); - assert_eq!(position2.spent, Uint256::from(3_000_000_000u128)); - assert_eq!(position2.in_balance, Uint256::zero()); - - assert_eq!(stream.out_remaining, Uint256::zero()); - assert_eq!( - position1 - .purchased - .checked_add(position2.purchased) - .unwrap(), - // 1 difference due to rounding - stream.out_supply.sub(Uint256::from(1u128)) - ); - } - - #[test] - fn test_withdraw() { - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(1_000_000); - let end = Timestamp::from_seconds(5_000_000); - let out_supply = Uint256::from(1_000_000_000_000u128); - let out_denom = "out_denom"; - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(0), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let info = mock_info( - "creator1", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - "in".to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - - // first subscription - let mut env = mock_env(); - env.block.time = start.plus_seconds(0); - let funds = Coin::new(2_000_000_000_000, "in"); - let info = mock_info("creator1", &[funds.clone()]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - // withdraw with cap - let mut env = mock_env(); - env.block.time = start.plus_seconds(5000); - let info = mock_info("creator1", &[]); - // withdraw amount zero - let cap = Uint256::zero(); - let msg = crate::msg::ExecuteMsg::Withdraw { - stream_id: 1, - cap: Some(cap), - operator_target: None, - }; - let res = execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap_err(); - assert_eq!(res, ContractError::InvalidWithdrawAmount {}); - // withdraw amount too high - let cap = Uint256::from(2_250_000_000_000u128); - let msg = crate::msg::ExecuteMsg::Withdraw { - stream_id: 1, - cap: Some(cap), - operator_target: None, - }; - let res = execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap_err(); - assert_eq!( - res, - ContractError::WithdrawAmountExceedsBalance(Uint256::from(2250000000000u128)) - ); - //withdraw with valid cap - let cap = Uint256::from(25_000_000u128); - let msg = crate::msg::ExecuteMsg::Withdraw { - stream_id: 1, - cap: Some(cap), - operator_target: None, - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - let position = - query_position(deps.as_ref(), mock_env(), 1, "creator1".to_string()).unwrap(); - assert_eq!(position.in_balance, Uint256::from(1_997_475_000_000u128)); - assert_eq!(position.spent, Uint256::from(2_500_000_000u128)); - assert_eq!(position.purchased, Uint256::from(1_250_000_000u128)); - // first fund amount should be equal to in_balance + spent + cap - assert_eq!( - position.in_balance + position.spent + cap, - Uint256::from_str(funds.amount.to_string().as_str()).unwrap() - ); - - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_000); - let info = mock_info("creator1", &[]); - let msg = crate::msg::ExecuteMsg::Withdraw { - stream_id: 1, - cap: None, - operator_target: None, - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - let position = - query_position(deps.as_ref(), mock_env(), 1, "creator1".to_string()).unwrap(); - assert_eq!(position.in_balance, Uint256::zero()); - assert_eq!(position.spent, Uint256::from(499_993_773_466u128)); - assert_eq!(position.purchased, Uint256::from(249_999_999_998u128)); - assert_eq!(position.shares, Uint256::zero()); - let msg = res.messages.first().unwrap(); - assert_eq!( - msg.msg, - CosmosMsg::Bank(BankMsg::Send { - to_address: "creator1".to_string(), - amount: vec![Coin::new(1_499_981_226_534, "in")] - }) - ); - - // can't withdraw after stream ends - let mut env = mock_env(); - env.block.time = end.plus_seconds(1); - let info = mock_info("creator1", &[]); - let msg = crate::msg::ExecuteMsg::Withdraw { - stream_id: 1, - cap: None, - operator_target: None, - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(res, ContractError::StreamEnded {}); - } - - #[test] - fn test_finalize_stream() { - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(1_000_000); - let end = Timestamp::from_seconds(5_000_000); - let out_supply = Uint256::from(1_000_000_000_000u128); - let out_denom = "out_denom"; - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(0), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let info = mock_info( - "creator1", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - "in".to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - - // first subscription - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_000); - let funds = Coin::new(2_000_000_000_000, "in"); - let info = mock_info("creator1", &[funds]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - // only treasury can finalize - let mut env = mock_env(); - env.block.time = end.plus_seconds(1); - let info = mock_info("random", &[]); - let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None).unwrap_err(); - assert_eq!(res, ContractError::Unauthorized {}); - - // can't finalize before stream ends - let mut env = mock_env(); - env.block.time = start.plus_seconds(1); - let info = mock_info(treasury.as_str(), &[]); - let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None).unwrap_err(); - assert_eq!(res, ContractError::StreamNotEnded {}); - - // happy path - let mut env = mock_env(); - env.block.time = end.plus_seconds(1); - let info = mock_info(treasury.as_str(), &[]); - execute_update_stream(deps.as_mut(), env.clone(), 1).unwrap(); - - let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None).unwrap(); - assert_eq!( - vec![ - Attribute::new("action", "finalize_stream"), - Attribute::new("stream_id", "1"), - Attribute::new("stream_dist_index", "0.5"), - Attribute::new("stream_shares", "2000000000000"), - Attribute::new("stream_last_updated", "5000001.000000000"), - Attribute::new("stream_in_supply", "0"), - Attribute::new("stream_out_remaining", "0"), - Attribute::new("stream_status", "Finalized"), - Attribute::new("stream_exit_fee_percent", "0.01"), - Attribute::new("treasury", "treasury"), - Attribute::new("creators_revenue", "1980000000000"), - Attribute::new("refunded_out_remaining", "0"), - Attribute::new("total_sold", "1000000000000"), - Attribute::new("fee_collector", "collector"), - Attribute::new("swap_fee", "20000000000"), - Attribute::new("creation_fee", "100"), - ], - res.attributes, - ); - assert_eq!( - res.messages, - vec![ - SubMsg::new(BankMsg::Send { - to_address: "treasury".to_string(), - amount: vec![Coin { - denom: "in".to_string(), - amount: Uint128::new(1_980_000_000_000), - }], - }), - SubMsg::new(BankMsg::Send { - to_address: "collector".to_string(), - amount: vec![Coin { - denom: "fee".to_string(), - amount: Uint128::new(100), - }], - }), - SubMsg::new(BankMsg::Send { - to_address: "collector".to_string(), - amount: vec![Coin { - denom: "in".to_string(), - amount: Uint128::new(20_000_000_000), - }], - }), - ], - ); - } - - #[test] - fn test_recurring_finalize_stream_calls() { - let malicious_treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(10); - let end = Timestamp::from_seconds(110); - let out_supply = Uint256::from(1000u128); - let out_denom = "myToken"; - let in_denom = "uosmo"; - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(100), - min_seconds_until_start_time: Uint64::new(0), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: in_denom.to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - // Create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let info = mock_info( - malicious_treasury.as_str(), - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - malicious_treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - // First subscription - let mut env = mock_env(); - env.block.time = start.plus_seconds(1); - let funds = Coin::new(200, in_denom.to_string()); - let info = mock_info("user1", &[funds]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - // Update - let mut env = mock_env(); - env.block.time = end.plus_seconds(1); - let info = mock_info(malicious_treasury.as_str(), &[]); - execute_update_stream(deps.as_mut(), env.clone(), 1).unwrap(); - // First call - let res = - execute_finalize_stream(deps.as_mut(), env.clone(), info.clone(), 1, None).unwrap(); - assert_eq!( - res.messages, - vec![ - SubMsg::new(BankMsg::Send { - to_address: malicious_treasury.to_string(), - amount: vec![Coin { - denom: in_denom.to_string(), - amount: Uint128::new(198), - }], - }), - SubMsg::new(BankMsg::Send { - to_address: "collector".to_string(), - amount: vec![Coin { - denom: "fee".to_string(), - amount: Uint128::new(100), - }], - }), - SubMsg::new(BankMsg::Send { - to_address: "collector".to_string(), - amount: vec![Coin { - denom: in_denom.to_string(), - amount: Uint128::new(2), - }], - }), - ], - ); - // Check stream status - let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); - assert_eq!(stream.status, Status::Finalized); - // Sequential calls, anyone could force this sequential calls - let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None).unwrap_err(); - assert_eq!(res, ContractError::StreamAlreadyFinalized {}); - } - - #[test] - fn test_exit_stream() { - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(1_000_000); - let end = Timestamp::from_seconds(5_000_000); - let out_supply = Uint256::from(1_000_000_000_000u128); - let out_denom = "out_denom"; - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(0), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let info = mock_info( - "creator1", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - "in".to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - - // first subscription - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_000); - let funds = Coin::new(2_000_000_000_000, "in"); - let info = mock_info("creator1", &[funds]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - // can't exit before stream ends - let mut env = mock_env(); - env.block.time = start.plus_seconds(2_000_000); - let info = mock_info("creator1", &[]); - let res = execute_exit_stream(deps.as_mut(), env, info, 1, None).unwrap_err(); - assert_eq!(res, ContractError::StreamNotEnded {}); - - //failed exit from random address - let mut env = mock_env(); - env.block.time = end.plus_seconds(3_000_000); - let info = mock_info("random", &[]); - let res = execute_exit_stream( - deps.as_mut(), - env.clone(), - info, - 1, - Some("creator1".to_string()), - ) - .unwrap_err(); - assert_eq!(res, ContractError::Unauthorized {}); - // can exit - let info = mock_info("creator1", &[]); - let res = execute_exit_stream(deps.as_mut(), env, info, 1, None).unwrap(); - assert_eq!( - res.messages, - vec![SubMsg::new(CosmosMsg::Bank(BankMsg::Send { - to_address: "creator1".to_string(), - amount: vec![Coin::new( - Uint128::new(1_000_000_000_000).u128(), - "out_denom" - )] - }))] - ); - - // position deleted - let mut env = mock_env(); - env.block.time = end.plus_seconds(4_000_000); - let info = mock_info("creator1", &[]); - let res = execute_exit_stream(deps.as_mut(), env, info, 1, None).unwrap_err(); - assert!(matches!(res, ContractError::Std(StdError::NotFound { .. }))); - } - - #[test] - fn test_withdraw_all_before_exit_case() { - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(1_000_000); - let end = Timestamp::from_seconds(5_000_000); - let out_supply = Uint256::from(1_000_000_000_000u128); - let out_denom = "out_denom"; - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(0), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let info = mock_info( - "creator1", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - "in".to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - - // first subscription - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_000); - let funds = Coin::new(2_000_000_000_000, "in"); - let info = mock_info("creator1", &[funds]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - // second subscription - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_000); - let funds = Coin::new(1_000_000_000_000, "in"); - let info = mock_info("creator2", &[funds]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - // first withdraw - let info = mock_info("creator1", &[]); - let mut env = mock_env(); - env.block.time = end.minus_seconds(1_000_000); - let msg = crate::msg::ExecuteMsg::Withdraw { - stream_id: 1, - cap: None, - operator_target: None, - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - // second withdraw - let info = mock_info("creator2", &[]); - let mut env = mock_env(); - env.block.time = end.minus_seconds(1_000_000); - let msg = crate::msg::ExecuteMsg::Withdraw { - stream_id: 1, - cap: None, - operator_target: None, - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - // can exit - let mut env = mock_env(); - env.block.time = end.plus_seconds(1_000_000); - execute_update_stream(deps.as_mut(), env, 1).unwrap(); - - let mut env = mock_env(); - env.block.time = end.plus_seconds(1_000_001); - let info = mock_info("creator1", &[]); - execute_exit_stream(deps.as_mut(), env, info, 1, None).unwrap(); - - let mut env = mock_env(); - env.block.time = end.plus_seconds(1_000_002); - let info = mock_info("creator2", &[]); - execute_exit_stream(deps.as_mut(), env, info, 1, None).unwrap(); - } - - #[test] - fn test_price_feed() { - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(1_000_000); - let end = Timestamp::from_seconds(5_000_000); - let out_supply = Uint256::from(1_000_000u128); - let out_denom = "out_denom"; - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(0), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let info = mock_info( - "creator1", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - "in".to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - - // first subscription - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_000); - let funds = Coin::new(3_000, "in"); - let info = mock_info("creator1", &[funds]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - //check current streamed price before update - let mut env = mock_env(); - env.block.time = start.plus_seconds(2_000_000); - let res = query_last_streamed_price(deps.as_ref(), env, 1).unwrap(); - assert_eq!(res.current_streamed_price, Decimal256::zero()); - - //check current streamed price after update - let mut env = mock_env(); - env.block.time = start.plus_seconds(2_000_000); - execute_update_stream(deps.as_mut(), env, 1).unwrap(); - let res = query_last_streamed_price(deps.as_ref(), mock_env(), 1).unwrap(); - //approx 1000/333333 - assert_eq!( - res.current_streamed_price, - Decimal256::from_str("0.002997002997002997").unwrap() - ); - // second subscription - let mut env = mock_env(); - env.block.time = start.plus_seconds(2_000_000); - let funds = Coin::new(1_000, "in"); - let info = mock_info("creator2", &[funds]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - //check current streamed price before update - let mut env = mock_env(); - env.block.time = start.plus_seconds(3_000_000); - let res = query_last_streamed_price(deps.as_ref(), env, 1).unwrap(); - assert_eq!( - res.current_streamed_price, - Decimal256::from_str("0.002997002997002997").unwrap() - ); - - //check current streamed price after update - let mut env = mock_env(); - env.block.time = start.plus_seconds(3_000_000); - execute_update_stream(deps.as_mut(), env, 1).unwrap(); - let res = query_last_streamed_price(deps.as_ref(), mock_env(), 1).unwrap(); - //approx 2000/333333 - assert_eq!( - res.current_streamed_price, - Decimal256::from_str("0.0045000045000045").unwrap() - ); - - //check average streamed price - let mut env = mock_env(); - env.block.time = start.plus_seconds(3_000_000); - let res = query_average_price(deps.as_ref(), env, 1).unwrap(); - //approx 2500/333333 - assert_eq!( - res.average_price, - Decimal256::from_str("0.003748503748503748").unwrap() - ); - - //withdraw creator 1 - let info = mock_info("creator1", &[]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(3_500_000); - let msg = crate::msg::ExecuteMsg::Withdraw { - stream_id: 1, - cap: None, - operator_target: None, - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - let res = query_last_streamed_price(deps.as_ref(), mock_env(), 1).unwrap(); - assert_eq!( - res.current_streamed_price, - Decimal256::from_str("0.004499991000017999").unwrap() - ); - - //test price after withdraw - let mut env = mock_env(); - env.block.time = start.plus_seconds(3_750_000); - execute_update_stream(deps.as_mut(), env, 1).unwrap(); - let res = query_last_streamed_price(deps.as_ref(), mock_env(), 1).unwrap(); - //approx 2500/333333 - assert_eq!( - res.current_streamed_price, - Decimal256::from_str("0.001500006000024000").unwrap() - ); - } - - #[test] - fn test_update_protocol_admin() { - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(0), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // random cannot update - let env = mock_env(); - let msg = UpdateProtocolAdmin { - new_protocol_admin: "new_protocol_admin".to_string(), - }; - let info = mock_info("random", &[]); - let err = execute(deps.as_mut(), env.clone(), info, msg.clone()).unwrap_err(); - assert_eq!(err, ContractError::Unauthorized {}); - - // protocol admin can update - let info = mock_info("protocol_admin", &[]); - execute(deps.as_mut(), env, info, msg).unwrap(); - let query = query_config(deps.as_ref()).unwrap(); - assert_eq!(query.protocol_admin, "new_protocol_admin".to_string()); - } - #[test] - fn test_execute_update_config() { - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(0), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - //query config - let config_response = query_config(deps.as_ref()).unwrap(); - //check config - assert_eq!(config_response.min_stream_seconds, Uint64::new(1000)); - assert_eq!(config_response.min_seconds_until_start_time, Uint64::new(0)); - assert_eq!(config_response.stream_creation_denom, "fee".to_string()); - assert_eq!(config_response.stream_creation_fee, Uint128::new(100)); - assert_eq!(config_response.fee_collector, "collector".to_string()); - assert_eq!(config_response.protocol_admin, "protocol_admin".to_string()); - assert_eq!(config_response.accepted_in_denom, "in".to_string()); - - // random user cant update config - let mut env = mock_env(); - let info = mock_info("random", &[]); - env.block.time = Timestamp::from_seconds(0); - let msg = crate::msg::ExecuteMsg::UpdateConfig { - min_stream_duration: Some(Uint64::new(2000)), - min_duration_until_start_time: Some(Uint64::new(2000)), - stream_creation_denom: Some("fee2".to_string()), - stream_creation_fee: Some(Uint128::new(200)), - fee_collector: Some("collector2".to_string()), - accepted_in_denom: Some("new_denom".to_string()), - exit_fee_percent: Some(Decimal256::percent(2)), - tos_version: None, - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(res, ContractError::Unauthorized {}); - - // wrong fee amount - let mut env = mock_env(); - let info = mock_info("protocol_admin", &[]); - env.block.time = Timestamp::from_seconds(0); - let msg = crate::msg::ExecuteMsg::UpdateConfig { - min_stream_duration: Some(Uint64::new(2000)), - min_duration_until_start_time: Some(Uint64::new(2000)), - stream_creation_denom: Some("fee2".to_string()), - stream_creation_fee: Some(Uint128::new(0)), - fee_collector: Some("collector2".to_string()), - accepted_in_denom: Some("new_denom".to_string()), - exit_fee_percent: Some(Decimal256::percent(2)), - tos_version: None, - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(res, ContractError::InvalidStreamCreationFee {}); - - // wrong exit fee percent - let mut env = mock_env(); - let info = mock_info("protocol_admin", &[]); - env.block.time = Timestamp::from_seconds(0); - let msg = crate::msg::ExecuteMsg::UpdateConfig { - min_stream_duration: Some(Uint64::new(2000)), - min_duration_until_start_time: Some(Uint64::new(2000)), - stream_creation_denom: Some("fee2".to_string()), - stream_creation_fee: Some(Uint128::new(200)), - fee_collector: Some("collector2".to_string()), - accepted_in_denom: Some("new_denom".to_string()), - exit_fee_percent: Some(Decimal256::percent(101)), - tos_version: None, - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(res, ContractError::InvalidExitFeePercent {}); - - // protocol admin can update config - let mut env = mock_env(); - let info = mock_info("protocol_admin", &[]); - env.block.time = Timestamp::from_seconds(0); - let msg = crate::msg::ExecuteMsg::UpdateConfig { - min_stream_duration: Some(Uint64::new(2000)), - min_duration_until_start_time: Some(Uint64::new(2000)), - stream_creation_denom: Some("fee2".to_string()), - stream_creation_fee: Some(Uint128::new(200)), - fee_collector: Some("collector2".to_string()), - accepted_in_denom: Some("new_denom".to_string()), - exit_fee_percent: Some(Decimal256::percent(2)), - tos_version: None, - }; - execute(deps.as_mut(), env, info, msg).unwrap(); - - //query config - let config_response = query_config(deps.as_ref()).unwrap(); - //check config - assert_eq!(config_response.min_stream_seconds, Uint64::new(2000)); - assert_eq!( - config_response.min_seconds_until_start_time, - Uint64::new(2000) - ); - assert_eq!(config_response.stream_creation_denom, "fee2".to_string()); - assert_eq!(config_response.stream_creation_fee, Uint128::new(200)); - assert_eq!(config_response.fee_collector, "collector2".to_string()); - assert_eq!(config_response.protocol_admin, "protocol_admin".to_string()); - assert_eq!(config_response.accepted_in_denom, "new_denom".to_string()); - assert_eq!(config_response.exit_fee_percent, Decimal256::percent(2)); - - // create stream - let out_supply = Uint256::from(1000u128); - let out_denom = "out"; - let start = Timestamp::from_seconds(10000); - let end = Timestamp::from_seconds(1000000); - let treasury = "treasury"; - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let info = mock_info( - "creator1", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(200, "fee2"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - "new_denom".to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - - // update config during stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(100000); - let info = mock_info("protocol_admin", &[]); - let msg = crate::msg::ExecuteMsg::UpdateConfig { - min_stream_duration: Some(Uint64::new(3000)), - min_duration_until_start_time: Some(Uint64::new(4000)), - stream_creation_denom: Some("fee3".to_string()), - stream_creation_fee: Some(Uint128::new(300)), - fee_collector: Some("collector3".to_string()), - accepted_in_denom: Some("new_denom2".to_string()), - exit_fee_percent: Some(Decimal256::percent(5)), - tos_version: None, - }; - execute(deps.as_mut(), env, info, msg).unwrap(); - //query config - let config_response = query_config(deps.as_ref()).unwrap(); - //check config - assert_eq!(config_response.min_stream_seconds, Uint64::new(3000)); - assert_eq!( - config_response.min_seconds_until_start_time, - Uint64::new(4000) - ); - assert_eq!(config_response.stream_creation_denom, "fee3".to_string()); - assert_eq!(config_response.stream_creation_fee, Uint128::new(300)); - assert_eq!(config_response.fee_collector, "collector3".to_string()); - assert_eq!(config_response.protocol_admin, "protocol_admin".to_string()); - assert_eq!(config_response.accepted_in_denom, "new_denom2".to_string()); - assert_eq!(config_response.exit_fee_percent, Decimal256::percent(5)); - - // check stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(100000); - let stream_response = query_stream(deps.as_ref(), env, 1).unwrap(); - assert_eq!(stream_response.exit_fee_percent, Decimal256::percent(2)); - assert_eq!(stream_response.stream_creation_fee, Uint128::new(200)); - } - - #[cfg(test)] - mod killswitch { - use super::*; - use crate::contract::{list_positions, list_streams}; - use crate::killswitch::{ - execute_cancel_stream, execute_exit_cancelled, execute_resume_stream, - sudo_cancel_stream, sudo_pause_stream, - }; - use cosmwasm_std::CosmosMsg::Bank; - use cosmwasm_std::{ReplyOn, SubMsg}; - - #[test] - fn test_pause_protocol_admin() { - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(1_000_000); - let end = Timestamp::from_seconds(5_000_000); - let out_supply = Uint256::from(1_000_000_000_000u128); - let out_denom = "out_denom"; - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(0), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let info = mock_info( - "creator1", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - "in".to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - - // non protocol admin can't pause - let info = mock_info("non_protocol_admin", &[]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(100); - - let res = execute_pause_stream(deps.as_mut(), env, info, 1); - assert_eq!(res, Err(ContractError::Unauthorized {})); - - // first subscription - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_000); - let funds = Coin::new(3_000, "in"); - let info = mock_info("position1", &[funds]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - //can't pause before start time - let info = mock_info("protocol_admin", &[]); - let mut env = mock_env(); - env.block.time = start.minus_seconds(500_000); - let res = execute_pause_stream(deps.as_mut(), env, info, 1).unwrap_err(); - assert_eq!(res, ContractError::StreamNotStarted {}); - - // can't pause after end time - let info = mock_info("protocol_admin", &[]); - let mut env = mock_env(); - env.block.time = end.plus_seconds(500_000); - let res = execute_pause_stream(deps.as_mut(), env, info, 1).unwrap_err(); - assert_eq!(res, ContractError::StreamEnded {}); - - // protocol admin can pause - let info = mock_info("protocol_admin", &[]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_001); - execute_pause_stream(deps.as_mut(), env, info, 1).unwrap(); - - // can't paused if already paused - let info = mock_info("protocol_admin", &[]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_005); - let res = execute_pause_stream(deps.as_mut(), env, info, 1).unwrap_err(); - assert_eq!(res, ContractError::StreamKillswitchActive {}); - - // can't subscribe new - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_002); - let funds = Coin::new(3_000, "in"); - let info = mock_info("position2", &[funds]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(res, ContractError::StreamKillswitchActive {}); - - // can't subscribe more - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_002); - let funds = Coin::new(3_000, "in"); - let info = mock_info("position1", &[funds]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(res, ContractError::StreamKillswitchActive {}); - - // can't withdraw - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_002); - let info = mock_info("position1", &[]); - let msg = crate::msg::ExecuteMsg::Withdraw { - stream_id: 1, - cap: None, - operator_target: None, - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(res, ContractError::StreamKillswitchActive {}); - - // can't update stream - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_002); - let res = execute_update_stream(deps.as_mut(), env, 1); - assert_eq!(res, Err(ContractError::StreamPaused {})); - - // can't update position - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_002); - let info = mock_info("position1", &[]); - let res = execute_update_position(deps.as_mut(), env, info, 1, None); - assert_eq!(res, Err(ContractError::StreamPaused {})); - - // can't finalize stream - let mut env = mock_env(); - env.block.time = end.plus_seconds(1_000_002); - let info = mock_info("treasury", &[]); - let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None); - assert_eq!(res, Err(ContractError::StreamKillswitchActive {})); - - // can't exit - let mut env = mock_env(); - env.block.time = end.plus_seconds(1_000_002); - let info = mock_info("position1", &[]); - let res = execute_exit_stream(deps.as_mut(), env, info, 1, None); - assert_eq!(res, Err(ContractError::StreamKillswitchActive {})); - } - - #[test] - fn test_resume_protocol_admin() { - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(1_000_000); - let end = Timestamp::from_seconds(5_000_000); - let out_supply = Uint256::from(1_000_000_000_000u128); - let out_denom = "out_denom"; - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(0), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let info = mock_info( - "creator1", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - "in".to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - - // first subscription - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_000); - let funds = Coin::new(3_000, "in"); - let info = mock_info("position1", &[funds]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - // can't resume if not paused - let info = mock_info("protocol_admin", &[]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_003); - let res = execute_resume_stream(deps.as_mut(), env, info, 1).unwrap_err(); - assert_eq!(res, ContractError::StreamNotPaused {}); - - // protocol admin can pause - let info = mock_info("protocol_admin", &[]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_001); - execute_pause_stream(deps.as_mut(), env, info, 1).unwrap(); - - // can't subscribe new - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_002); - let funds = Coin::new(3_000, "in"); - let info = mock_info("position2", &[funds]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(res, ContractError::StreamKillswitchActive {}); - - // non protocol admin can't resume - let info = mock_info("non_protocol_admin", &[]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_003); - let res = execute_resume_stream(deps.as_mut(), env, info, 1).unwrap_err(); - assert_eq!(res, ContractError::Unauthorized {}); - - // protocol admin can resume - let info = mock_info("protocol_admin", &[]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_003); - execute_resume_stream(deps.as_mut(), env, info, 1).unwrap(); - - // can subscribe new after resume - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_004); - let funds = Coin::new(3_000, "in"); - let info = mock_info("position2", &[funds]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(res.attributes[0].key, "action"); - assert_eq!(res.attributes[1].key, "stream_id"); - assert_eq!(res.attributes[2].key, "in_balance"); - assert_eq!(res.attributes[3].key, "shares"); - assert_eq!(res.attributes[4].key, "index"); - assert_eq!(res.attributes[5].key, "last_updated"); - assert_eq!(res.attributes[6].key, "pending_purchase"); - assert_eq!(res.attributes[7].key, "purchased"); - assert_eq!(res.attributes[8].key, "spent"); - assert_eq!(res.attributes[9].key, "tos_version"); - assert_eq!(res.attributes[10].key, "stream_new_in_supply"); - assert_eq!(res.attributes[11].key, "stream_previous_in_supply"); - assert_eq!(res.attributes[12].key, "stream_new_shares"); - assert_eq!(res.attributes[13].key, "stream_previous_shares"); - assert_eq!(res.attributes[14].key, "stream_status"); - // validate values - assert_eq!(res.attributes[0].value, "subscribe"); - assert_eq!(res.attributes[1].value, "1"); - assert_eq!(res.attributes[2].value, "3000"); - assert_eq!(res.attributes[3].value, "3000"); - assert_eq!(res.attributes[4].value, "222.222"); - assert_eq!(res.attributes[5].value, "2000004.000000000"); - assert_eq!(res.attributes[6].value, "0"); - assert_eq!(res.attributes[7].value, "0"); - assert_eq!(res.attributes[8].value, "0"); - assert_eq!(res.attributes[9].value, "v1"); - assert_eq!(res.attributes[10].value, "6000"); - assert_eq!(res.attributes[11].value, "6000"); - assert_eq!(res.attributes[12].value, "6000"); - assert_eq!(res.attributes[13].value, "6000"); - assert_eq!(res.attributes[14].value, "Active"); - - // protocol admin can pause - let info = mock_info("protocol_admin", &[]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_005); - execute_pause_stream(deps.as_mut(), env, info, 1).unwrap(); - - // cancel the stream - let info = mock_info("protocol_admin", &[]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_006); - execute_cancel_stream(deps.as_mut(), env, info, 1).unwrap(); - - // can't resume if cancelled - let info = mock_info("protocol_admin", &[]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_007); - let res = execute_resume_stream(deps.as_mut(), env, info, 1).unwrap_err(); - assert_eq!(res, ContractError::StreamIsCancelled {}); - } - - #[test] - fn test_cancel_protocol_admin() { - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(1_000_000); - let end = Timestamp::from_seconds(5_000_000); - let out_supply = Uint256::from(1_000_000_000_000u128); - let out_denom = "out_denom"; - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(0), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let info = mock_info( - "creator1", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - "in".to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - - // subscription - let mut env = mock_env(); - env.block.time = start.plus_seconds(0); - let funds = Coin::new(2_000_000_000_000, "in"); - let info = mock_info("creator1", &[funds]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: Some("operator".to_string()), - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - // non protocol admin can't cancel - let info = mock_info("non_protocol_admin", &[]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_000); - let err = execute_cancel_stream(deps.as_mut(), env, info, 1).unwrap_err(); - assert_eq!(err, ContractError::Unauthorized {}); - - // cant cancel without pause - let info = mock_info("protocol_admin", &[]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_000); - let err = execute_cancel_stream(deps.as_mut(), env, info, 1).unwrap_err(); - assert_eq!(err, ContractError::StreamNotPaused {}); - - // pause - let mut env = mock_env(); - env.block.time = start.plus_seconds(2_000_000); - let info = mock_info("protocol_admin", &[]); - execute_pause_stream(deps.as_mut(), env, info, 1).unwrap(); - - //cancel - let info = mock_info("protocol_admin", &[]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(2_500_000); - let response = execute_cancel_stream(deps.as_mut(), env, info, 1).unwrap(); - //out_tokens and the creation fee are sent back to the treasury upon cancellation - assert_eq!( - response.messages, - [ - SubMsg { - id: 0, - msg: Bank(BankMsg::Send { - to_address: "treasury".to_string(), - amount: Vec::from([Coin { - denom: "out_denom".to_string(), - amount: Uint128::new(1000000000000) - }]) - }), - gas_limit: None, - reply_on: ReplyOn::Never - }, - SubMsg { - id: 0, - msg: Bank(BankMsg::Send { - to_address: "treasury".to_string(), - amount: Vec::from([Coin { - denom: "fee".to_string(), - amount: Uint128::new(100) - }]) - }), - gas_limit: None, - reply_on: ReplyOn::Never - } - ] - ); - - // can't cancel cancelled stream - let info = mock_info("protocol_admin", &[]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(2_500_000); - let response = execute_cancel_stream(deps.as_mut(), env, info, 1).unwrap_err(); - assert_eq!(response, ContractError::StreamIsCancelled {}); - } - - #[test] - fn test_withdraw_pause() { - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(1_000_000); - let end = Timestamp::from_seconds(5_000_000); - let out_supply = Uint256::from(1_000_000_000_000u128); - let out_denom = "out_denom"; - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(0), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let info = mock_info( - "creator1", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - "in".to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - - // subscription - let mut env = mock_env(); - env.block.time = start.plus_seconds(0); - let funds = Coin::new(2_000_000_000_000, "in"); - let info = mock_info("creator1", &[funds.clone()]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: Some("operator".to_string()), - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - // withdraw with cap - let mut env = mock_env(); - env.block.time = start.plus_seconds(5000); - let info = mock_info("creator1", &[]); - let cap = Uint256::from(25_000_000u128); - let msg = crate::msg::ExecuteMsg::Withdraw { - stream_id: 1, - cap: Some(cap), - operator_target: None, - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - let position = - query_position(deps.as_ref(), mock_env(), 1, "creator1".to_string()).unwrap(); - assert_eq!(position.in_balance, Uint256::from(1_997_475_000_000u128)); - assert_eq!(position.spent, Uint256::from(2_500_000_000u128)); - assert_eq!(position.purchased, Uint256::from(1_250_000_000u128)); - // first fund amount should be equal to in_balance + spent + cap - assert_eq!( - position.in_balance + position.spent + cap, - Uint256::from_str(funds.amount.to_string().as_str()).unwrap() - ); - - // can't withdraw pause - let mut env = mock_env(); - env.block.time = start.plus_seconds(6000); - let info = mock_info("creator1", &[]); - let err = execute_withdraw_paused(deps.as_mut(), env, info, 1, None, None).unwrap_err(); - assert_eq!(err, ContractError::StreamNotPaused {}); - - // pause - let mut env = mock_env(); - env.block.time = start.plus_seconds(6000); - let info = mock_info("protocol_admin", &[]); - execute_pause_stream(deps.as_mut(), env, info, 1).unwrap(); - - let mut env = mock_env(); - env.block.time = start.plus_seconds(6500); - let stream1_old = query_stream(deps.as_ref(), env, 1).unwrap(); - //Unauthorized check - let info = mock_info("random", &[]); - let mut env = mock_env(); - env.block.time = start.plus_seconds(7000); - let res = execute_withdraw_paused( - deps.as_mut(), - env, - info, - 1, - None, - Some("creator1".to_string()), - ) - .unwrap_err(); - - assert_eq!(res, ContractError::Unauthorized {}); - //Cap exceeds in balance check - let mut env = mock_env(); - env.block.time = start.plus_seconds(7000); - let info = mock_info("creator1", &[]); - let res = execute_withdraw_paused( - deps.as_mut(), - env, - info, - 1, - Some(Uint256::from(2_000_000_000_000u128 + 1u128)), - None, - ) - .unwrap_err(); - assert_eq!( - res, - ContractError::WithdrawAmountExceedsBalance(Uint256::from(2_000_000_000_001u128)) - ); - // Withdraw cap is zero - let mut env = mock_env(); - env.block.time = start.plus_seconds(7000); - let info = mock_info("creator1", &[]); - let res = - execute_withdraw_paused(deps.as_mut(), env, info, 1, Some(Uint256::zero()), None) - .unwrap_err(); - assert_eq!(res, ContractError::InvalidWithdrawAmount {}); - - //withdraw with cap - let mut env = mock_env(); - env.block.time = start.plus_seconds(7000); - let info = mock_info("creator1", &[]); - let cap = Uint256::from(25_000_000u128); - execute_withdraw_paused(deps.as_mut(), env, info, 1, Some(cap), None).unwrap(); - - // withdraw after pause - let mut env = mock_env(); - env.block.time = start.plus_seconds(7000); - let info = mock_info("creator1", &[]); - let res = execute_withdraw_paused(deps.as_mut(), env, info, 1, None, None).unwrap(); - assert_eq!( - res.messages, - vec![SubMsg { - id: 0, - msg: BankMsg::Send { - to_address: "creator1".to_string(), - amount: vec![Coin { - denom: "in".to_string(), - amount: Uint128::new(1996950006258), - }], - } - .into(), - gas_limit: None, - reply_on: ReplyOn::Never, - }] - ); - - // stream not updated - let mut env = mock_env(); - env.block.time = start.plus_seconds(8000); - let stream1_new = query_stream(deps.as_ref(), env, 1).unwrap(); - // dist_index not updated - assert_eq!(stream1_old.dist_index, stream1_new.dist_index); - assert_eq!(stream1_new.in_supply, Uint256::zero()); - assert_eq!(stream1_new.shares, Uint256::zero()); - - // position updated - let mut env = mock_env(); - env.block.time = start.plus_seconds(8001); - let position = - query_position(deps.as_ref(), mock_env(), 1, "creator1".to_string()).unwrap(); - // in_balance updated - assert_eq!(position.in_balance, Uint256::zero()); - assert_eq!(position.spent, Uint256::from(2_999_993_742u128)); - assert_eq!(position.purchased, Uint256::from(1_499_999_998u128)); - assert_eq!(position.shares, Uint256::zero()); - } - - #[test] - fn test_resume() { - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(1_000_000); - let end = Timestamp::from_seconds(5_000_000); - let out_supply = Uint256::from(1_000_000_000_000u128); - let out_denom = "out_denom"; - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(0), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let info = mock_info( - "creator1", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - "in".to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - - // first subscription - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_000); - let funds = Coin::new(3_000, "in"); - let info = mock_info("position1", &[funds]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - //cant resume if not paused - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_000); - let res = sudo_resume_stream(deps.as_mut(), env, 1).unwrap_err(); - assert_eq!(res, ContractError::StreamNotPaused {}); - - // pause - let info = mock_info("protocol_admin", &[]); - let mut env = mock_env(); - let pause_date = start.plus_seconds(2_000_000); - env.block.time = pause_date; - execute_pause_stream(deps.as_mut(), env, info, 1).unwrap(); - - // resume - let mut env = mock_env(); - let resume_date = start.plus_seconds(3_000_000); - env.block.time = resume_date; - sudo_resume_stream(deps.as_mut(), env, 1).unwrap(); - - // new end date is correct - let new_end_date = end.plus_nanos(resume_date.nanos() - pause_date.nanos()); - let stream = query_stream(deps.as_ref(), mock_env(), 1).unwrap(); - assert_eq!(stream.end_time, new_end_date); - } - - #[test] - fn test_sudo_pause_stream() { - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(1_000_000); - let end = Timestamp::from_seconds(5_000_000); - let out_supply = Uint256::from(1_000_000_000_000u128); - let out_denom = "out_denom"; - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(0), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let info = mock_info( - "creator1", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - "in".to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(500_000); - let res = sudo_pause_stream(deps.as_mut(), env, 1).unwrap_err(); - assert_eq!(res, ContractError::StreamNotStarted {}); - - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(6_000_000); - let res = sudo_pause_stream(deps.as_mut(), env, 1).unwrap_err(); - assert_eq!(res, ContractError::StreamEnded {}); - - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(3_000_000); - let res = sudo_pause_stream(deps.as_mut(), env, 1).unwrap(); - assert_eq!( - res, - Response::new() - .add_attribute("action", "sudo_pause_stream") - .add_attribute("stream_id", "1") - .add_attribute("is_paused", "true") - .add_attribute("pause_date", "3000000.000000000") - ); - - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(4_000_000); - let res = sudo_pause_stream(deps.as_mut(), env, 1).unwrap_err(); - assert_eq!(res, ContractError::StreamKillswitchActive {}); - } - - #[test] - fn test_range_queries() { - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(2000); - let end = Timestamp::from_seconds(1_000_000); - let out_supply = Uint256::from(1_000_000u128); - let out_denom = "out_denom"; - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(100); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(1000), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(1); - let info = mock_info( - "creator1", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - //first stream - execute_create_stream( - deps.as_mut(), - env.clone(), - info.clone(), - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - "in".to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - //second stream - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - "in".to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - - let res = list_streams(deps.as_ref(), None, None).unwrap(); - assert_eq!(res.streams.len(), 2); - - // first subscription to first stream - let mut env = mock_env(); - env.block.time = start.plus_seconds(100); - let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - // second subscription to first stream - let mut env = mock_env(); - env.block.time = start.plus_seconds(100); - let info = mock_info("creator2", &[Coin::new(1_000_000, "in")]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: None, - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - let res = list_positions(deps.as_ref(), 1, None, None).unwrap(); - assert_eq!(res.positions.len(), 2); - } - - #[test] - fn test_exit_cancel() { - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(1_000_000); - let end = Timestamp::from_seconds(5_000_000); - let out_supply = Uint256::from(1_000_000_000_000u128); - let out_denom = "out_denom"; - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(0), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let info = mock_info( - "creator1", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - "in".to_string(), - out_denom.to_string(), - out_supply, - start, - end, - None, - "v1".to_string(), - ) - .unwrap(); - - // subscription - let mut env = mock_env(); - env.block.time = start.plus_seconds(0); - let funds = Coin::new(2_000_000_000_000, "in"); - let info = mock_info("creator1", &[funds]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: Some("operator".to_string()), - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - // cant cancel without pause - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_000); - let err = sudo_cancel_stream(deps.as_mut(), env, 1).unwrap_err(); - assert_eq!(err, ContractError::StreamNotPaused {}); - - // pause - let mut env = mock_env(); - env.block.time = start.plus_seconds(2_000_000); - let info = mock_info("protocol_admin", &[]); - execute_pause_stream(deps.as_mut(), env, info, 1).unwrap(); - - //can't exit before cancel - let mut env = mock_env(); - env.block.time = start.plus_seconds(2_250_000); - let info = mock_info("creator1", &[]); - let res = execute_exit_cancelled(deps.as_mut(), env, info, 1, None).unwrap_err(); - assert_eq!(res, ContractError::StreamNotCancelled {}); - - //cancel - let mut env = mock_env(); - env.block.time = start.plus_seconds(2_500_000); - let response = sudo_cancel_stream(deps.as_mut(), env, 1).unwrap(); - //out_tokens and the creation fee are sent back to the treasury upon cancellation - assert_eq!( - response.messages, - [ - SubMsg { - id: 0, - msg: Bank(BankMsg::Send { - to_address: "treasury".to_string(), - amount: Vec::from([Coin { - denom: "out_denom".to_string(), - amount: Uint128::new(1000000000000) - }]) - }), - gas_limit: None, - reply_on: ReplyOn::Never - }, - SubMsg { - id: 0, - msg: Bank(BankMsg::Send { - to_address: "treasury".to_string(), - amount: Vec::from([Coin { - denom: "fee".to_string(), - amount: Uint128::new(100) - }]) - }), - gas_limit: None, - reply_on: ReplyOn::Never - } - ] - ); - - //random operator can't exit - let mut env = mock_env(); - env.block.time = start.plus_seconds(2_250_000); - let info = mock_info("random", &[]); - let res = - execute_exit_cancelled(deps.as_mut(), env, info, 1, Some("creator1".to_string())) - .unwrap_err(); - assert_eq!(res, ContractError::Unauthorized {}); - - // exit - let mut env = mock_env(); - env.block.time = start.plus_seconds(3_000_000); - let info = mock_info("creator1", &[]); - let res = execute_exit_cancelled(deps.as_mut(), env, info, 1, None).unwrap(); - let msg = res.messages.first().unwrap(); - assert_eq!( - msg.msg, - Bank(BankMsg::Send { - to_address: "creator1".to_string(), - amount: vec![Coin::new(2000000000000, "in")] - }) - ); - } - #[test] - fn test_treasury_cancel_stream() { - use crate::killswitch::execute_treasury_cancel_stream; - - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(1_000_000); - let end = Timestamp::from_seconds(5_000_000); - let out_supply = Uint256::from(500u128); - let out_denom = "out_denom"; - let in_denom = "in_denom"; - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(100_000), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: in_denom.to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let info = mock_info( - "creator", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start, - end, - Some(1_000u128.into()), - "v1".to_string(), - ) - .unwrap(); - - // Cancel period should be ended Now+min_seconds_until_start_time - - // Cancel stream with wrong address - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(100); - - let error = - execute_treasury_cancel_stream(deps.as_mut(), env, mock_info("random", &[]), 1) - .unwrap_err(); - assert_eq!(error, ContractError::Unauthorized {}); - - // Try subscribing to the stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(100); - let funds = Coin::new(250, "in_denom"); - let info = mock_info("subscriber", &[funds]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: Some("operator".to_string()), - tos_version: "v1".to_string(), - }; - let err = execute(deps.as_mut(), env.clone(), info, msg).unwrap_err(); - assert_eq!(err, ContractError::TreasuryCancelPeriodActive {}); - - // Try canceling the stream outside the cancel period - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(100_000 + 1); - let error = execute_treasury_cancel_stream( - deps.as_mut(), - env.clone(), - mock_info("treasury", &[]), - 1, - ) - .unwrap_err(); - assert_eq!(error, ContractError::TreasuryCancelPeriodEnded {}); - - // Query the stream - let _stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); - - // Cancel the stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(100_000); - let response = execute_treasury_cancel_stream( - deps.as_mut(), - env.clone(), - mock_info("treasury", &[]), - 1, - ) - .unwrap(); - assert_eq!( - response.messages, - [SubMsg { - id: 0, - msg: Bank(BankMsg::Send { - to_address: "treasury".to_string(), - amount: vec![Coin { - denom: "out_denom".to_string(), - amount: Uint128::new(500) - }] - }), - gas_limit: None, - reply_on: ReplyOn::Never - }] - ); - - // Query the stream again should return error because we removed the stream - let _stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap_err(); - } - } - mod threshold { - use crate::{ - killswitch::{execute_cancel_stream_with_threshold, execute_exit_cancelled}, - threshold::ThresholdError, - }; - - // Create a stream with a threshold - // Subscribe to the stream - use super::*; - - #[test] - fn test_threshold_reached() { - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(1_000_000); - let end = Timestamp::from_seconds(5_000_000); - let out_supply = Uint256::from(500u128); - let out_denom = "out_denom"; - let in_denom = "in_denom"; - - // threshold = 500*0.5 / 1-0.01 =252.5 - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(0), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: in_denom.to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let info = mock_info( - "creator", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start, - end, - Some(Uint256::from(250u128)), - "v1".to_string(), - ) - .unwrap(); - - // subscription - let mut env = mock_env(); - env.block.time = start; - let funds = Coin::new(252, "in_denom"); - let info = mock_info("subscriber", &[funds]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: Some("operator".to_string()), - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - // Threshold should be reached - let mut env = mock_env(); - env.block.time = end.plus_seconds(1); - - // Exit should be possible - // Since there is only one subscriber all out denom should be sent to subscriber - // In calculations we are always rounding down that one token will be left in the stream - // Asuming token is 6 decimals - // This amount could be considered as insignificant - let info = mock_info("subscriber", &[]); - let res = execute_exit_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap(); - assert_eq!( - res.messages, - vec![SubMsg::new(BankMsg::Send { - to_address: "subscriber".to_string(), - amount: vec![Coin::new(499, "out_denom")], - })], - ); - - // Creator finalizes the stream - let info = mock_info("treasury", &[]); - let res = execute_finalize_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap(); - // Creator's revenue - assert_eq!( - res.messages[0].msg, - cosmwasm_std::CosmosMsg::Bank(BankMsg::Send { - to_address: "treasury".to_string(), - amount: vec![Coin::new(250, "in_denom")], - }) - ); - assert_eq!( - res.messages[1].msg, - cosmwasm_std::CosmosMsg::Bank(BankMsg::Send { - to_address: "collector".to_string(), - amount: vec![Coin::new(100, "fee")], - }) - ); - assert_eq!( - res.messages[2].msg, - cosmwasm_std::CosmosMsg::Bank(BankMsg::Send { - to_address: "collector".to_string(), - amount: vec![Coin::new(2, "in_denom")], - }) - ) - } - - #[test] - fn test_threshold_not_reached() { - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(1_000_000); - let end = Timestamp::from_seconds(5_000_000); - let out_supply = Uint256::from(500u128); - let out_denom = "out_denom"; - let in_denom = "in_denom"; - - // threshold = 500*0.5 / 1-0.01 =252.5 - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.height = 0; - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(0), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: in_denom.to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let info = mock_info( - "creator", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start, - end, - Some(500u128.into()), - "v1".to_string(), - ) - .unwrap(); - - // Subscription 1 - let mut env = mock_env(); - env.block.time = start; - let funds = Coin::new(250, "in_denom"); - let info = mock_info("subscriber", &[funds]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: Some("operator".to_string()), - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); - - // Subscription 2 - let funds = Coin::new(1, "in_denom"); - let info = mock_info("subscriber2", &[funds]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: Some("operator".to_string()), - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); - - // Set time to the end of the stream - let mut env = mock_env(); - env.block.time = end.plus_seconds(1); - - // Exit should not be possible - let info = mock_info("subscriber", &[]); - let res = execute_exit_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap_err(); - assert_eq!( - res, - ContractError::ThresholdError(ThresholdError::ThresholdNotReached {}) - ); - - // Finalize should not be possible - let info = mock_info("treasury", &[]); - let res = - execute_finalize_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap_err(); - assert_eq!( - res, - ContractError::ThresholdError(ThresholdError::ThresholdNotReached {}) - ); - - // Subscriber one executes exit cancelled before creator cancels stream - let info = mock_info("subscriber", &[]); - let res = execute_exit_cancelled(deps.as_mut(), env.clone(), info, 1, None).unwrap(); - assert_eq!( - res.messages, - vec![SubMsg::new(BankMsg::Send { - to_address: "subscriber".to_string(), - amount: vec![Coin::new(250, "in_denom")], - })] - ); - // Creator threshold cancels the stream - let info = mock_info("treasury", &[]); - let res = - execute_cancel_stream_with_threshold(deps.as_mut(), env.clone(), info, 1).unwrap(); - assert_eq!( - res.messages, - vec![ - // Out denom refunded - SubMsg::new(BankMsg::Send { - to_address: "treasury".to_string(), - amount: vec![Coin::new(500, "out_denom")], - }), - ] - ); - // Creator can not finalize the stream - let info = mock_info("treasury", &[]); - let res = - execute_finalize_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap_err(); - assert_eq!(res, ContractError::StreamKillswitchActive {}); - - // Creator can not cancel the stream again - let info = mock_info("treasury", &[]); - let res = execute_cancel_stream_with_threshold(deps.as_mut(), env.clone(), info, 1) - .unwrap_err(); - assert_eq!(res, ContractError::StreamKillswitchActive {}); - - // Subscriber 2 executes exit cancelled after creator cancels stream - let info = mock_info("subscriber2", &[]); - let res = execute_exit_cancelled(deps.as_mut(), env.clone(), info, 1, None).unwrap(); - assert_eq!( - // In denom refunded - res.messages, - vec![SubMsg::new(BankMsg::Send { - to_address: "subscriber2".to_string(), - amount: vec![Coin::new(1, "in_denom")], - })] - ); - } - - #[test] - fn test_threshold_cancel() { - let treasury = Addr::unchecked("treasury"); - let start = Timestamp::from_seconds(1_000_000); - let end = Timestamp::from_seconds(5_000_000); - let out_supply = Uint256::from(500u128); - let out_denom = "out_denom"; - let in_denom = "in_denom"; - - // threshold = 500*0.5 / 1-0.01 =252.5 - - // instantiate - let mut deps = mock_dependencies(); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(0), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint128::new(100), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: in_denom.to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - // create stream - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let info = mock_info( - "creator", - &[ - Coin::new(out_supply.to_string().parse().unwrap(), out_denom), - Coin::new(100, "fee"), - ], - ); - execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - "test".to_string(), - Some("https://sample.url".to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start, - end, - Some(1_000u128.into()), - "v1".to_string(), - ) - .unwrap(); - - // Subscription 1 - let mut env = mock_env(); - env.block.time = start; - let funds = Coin::new(250, "in_denom"); - let info = mock_info("subscriber", &[funds]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: Some("operator".to_string()), - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); - - // Subscription 2 - let funds = Coin::new(500, "in_denom"); - let info = mock_info("subscriber2", &[funds]); - let msg = crate::msg::ExecuteMsg::Subscribe { - stream_id: 1, - operator_target: None, - operator: Some("operator".to_string()), - tos_version: "v1".to_string(), - }; - let _res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); - // Can not cancel stream before it ends - let mut env = mock_env(); - env.block.time = start.plus_seconds(1_000_000); - let res = execute_cancel_stream_with_threshold( - deps.as_mut(), - env, - mock_info("treasury", &[]), - 1, - ) - .unwrap_err(); - assert_eq!(res, ContractError::StreamNotEnded {}); - - // Set block to the end of the stream - let mut env = mock_env(); - env.block.time = end.plus_seconds(1); - - // Non creator can't cancel stream - let res = execute_cancel_stream_with_threshold( - deps.as_mut(), - env.clone(), - mock_info("random", &[]), - 1, - ) - .unwrap_err(); - assert_eq!(res, ContractError::Unauthorized {}); - - // Creator can cancel stream - let _res = execute_cancel_stream_with_threshold( - deps.as_mut(), - env.clone(), - mock_info("treasury", &[]), - 1, - ) - .unwrap(); - // Query stream should return stream with is_cancelled = true - let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); - assert_eq!(stream.status, Status::Cancelled); - } + // // wrong creation fee case + // let mut env = mock_env(); + // env.block.time = Timestamp::from_seconds(1); + // let info = mock_info( + // "creator1", + // &[Coin::new(out_supply, "out_denom"), Coin::new(99u128, "fee")], + // ); + // let res = execute_create_stream( + // deps.as_mut(), + // env, + // info, + // treasury.to_string(), + // name.to_string(), + // Some(url.to_string()), + // in_denom.to_string(), + // out_denom.to_string(), + // out_supply, + // start_time, + // end_time, + // None, + // "v1".to_string(), + // ); + // assert_eq!(res.unwrap_err().to_string(), "StreamCreationFeeRequired"); + + // // no creation fee case + // let mut env = mock_env(); + // env.block.time = Timestamp::from_seconds(1); + // let info = mock_info("creator1", &[Coin::new(out_supply, "out_denom")]); + // let res = execute_create_stream( + // deps.as_mut(), + // env, + // info, + // treasury.to_string(), + // name.to_string(), + // Some(url.to_string()), + // in_denom.to_string(), + // out_denom.to_string(), + // out_supply, + // start_time, + // end_time, + // None, + // "v1".to_string(), + // ); + // assert_eq!(res.unwrap_err().to_string(), "NoFundsSent"); + + // // mismatch creation fee case + // let mut env = mock_env(); + // env.block.time = Timestamp::from_seconds(1); + // let info = mock_info("creator1", &[Coin::new(out_supply, "out_denom")]); + // let res = execute_create_stream( + // deps.as_mut(), + // env, + // info, + // treasury.to_string(), + // name.to_string(), + // Some(url.to_string()), + // in_denom.to_string(), + // out_denom.to_string(), + // out_supply, + // start_time, + // end_time, + // None, + // "v1".to_string(), + // ); + // assert_eq!(res.unwrap_err().to_string(), "NoFundsSent"); + + // // same denom case, insufficient total + // let mut env = mock_env(); + // env.block.time = Timestamp::from_seconds(1); + // let info = mock_info("creator1", &[Coin::new(1u128, "fee")]); + // let res = execute_create_stream( + // deps.as_mut(), + // env, + // info, + // treasury.to_string(), + // name.to_string(), + // Some(url.to_string()), + // in_denom.to_string(), + // "fee".to_string(), + // out_supply, + // start_time, + // end_time, + // None, + // "v1".to_string(), + // ); + // assert_eq!(res.unwrap_err().to_string(), "StreamOutSupplyFundsRequired"); + + // // same denom case, sufficient total + // let mut env = mock_env(); + // env.block.time = Timestamp::from_seconds(1); + // let info = mock_info( + // "creator1", + // &[Coin::new( + // out_supply.strict_add(Uint256::from(100u128)), + // "fee", + // )], + // ); + // execute_create_stream( + // deps.as_mut(), + // env, + // info, + // treasury.to_string(), + // name.to_string(), + // Some(url.to_string()), + // in_denom.to_string(), + // "fee".to_string(), + // out_supply, + // start_time, + // end_time, + // None, + // "v1".to_string(), + // ) + // .unwrap(); + + // // same tokens extra funds sent + // let info = mock_info( + // "creator1", + // &[ + // Coin::new(out_supply.strict_add(Uint256::from(100u128)), "fee"), + // Coin::new(15u128, "random"), + // ], + // ); + // let mut env = mock_env(); + // env.block.time = Timestamp::from_seconds(1); + // let err = execute_create_stream( + // deps.as_mut(), + // env, + // info, + // treasury.to_string(), + // name.to_string(), + // Some(url.to_string()), + // in_denom.to_string(), + // "fee".to_string(), + // out_supply, + // start_time, + // end_time, + // None, + // "v1".to_string(), + // ) + // .unwrap_err(); + // assert_eq!(err.to_string(), "InvalidFunds"); + + // // different tokens extra funds sent + // let info = mock_info( + // "creator1", + // &[ + // Coin::new(out_supply, "different_denom"), + // Coin::new(100u128, "fee"), + // Coin::new(15u128, "random"), + // ], + // ); + // let mut env = mock_env(); + // env.block.time = Timestamp::from_seconds(1); + // let err = execute_create_stream( + // deps.as_mut(), + // env, + // info, + // treasury.to_string(), + // name.to_string(), + // Some(url.to_string()), + // in_denom.to_string(), + // "different_denom".to_string(), + // out_supply, + // start_time, + // end_time, + // None, + // "v1".to_string(), + // ) + // .unwrap_err(); + // assert_eq!(err.to_string(), "InvalidFunds"); + + // // failed name checks + // let mut env = mock_env(); + // env.block.time = Timestamp::from_seconds(1); + // let info = mock_info( + // "creator1", + // &[ + // Coin::new(out_supply, "out_denom"), + // Coin::new(100u128, "fee"), + // ], + // ); + // let res = execute_create_stream( + // deps.as_mut(), + // env.clone(), + // info.clone(), + // treasury.to_string(), + // "n".to_string(), + // Some(url.to_string()), + // in_denom.to_string(), + // out_denom.to_string(), + // out_supply, + // start_time, + // end_time, + // None, + // "v1".to_string(), + // ) + // .unwrap_err(); + // assert_eq!(res.to_string(), "StreamNameTooShort"); + + // let res = execute_create_stream( + // deps.as_mut(), + // env.clone(), + // info.clone(), + // treasury.to_string(), + // "12345678901234567890123456789012345678901234567890123456789012345".to_string(), + // Some(url.to_string()), + // in_denom.to_string(), + // out_denom.to_string(), + // out_supply, + // start_time, + // end_time, + // None, + // "v1".to_string(), + // ) + // .unwrap_err(); + // assert_eq!(res.to_string(), "StreamNameTooLong"); + + // let res = execute_create_stream( + // deps.as_mut(), + // env, + // info, + // treasury.to_string(), + // "abc~ß".to_string(), + // Some(url.to_string()), + // in_denom.to_string(), + // out_denom.to_string(), + // out_supply, + // start_time, + // end_time, + // None, + // "v1".to_string(), + // ) + // .unwrap_err(); + // assert_eq!(res.to_string(), "InvalidStreamName"); + + // //failed url checks + // let mut env = mock_env(); + // env.block.time = Timestamp::from_seconds(1); + // let info = mock_info( + // "creator1", + // &[ + // Coin::new(out_supply, "out_denom"), + // Coin::new(100u128, "fee"), + // ], + // ); + // let res = execute_create_stream( + // deps.as_mut(), + // env.clone(), + // info.clone(), + // treasury.to_string(), + // "name".to_string(), + // Some("https://a.b".to_string()), + // in_denom.to_string(), + // out_denom.to_string(), + // out_supply, + // start_time, + // end_time, + // None, + // "v1".to_string(), + // ) + // .unwrap_err(); + // assert_eq!(res.to_string(), "StreamUrlTooShort"); + + // let res = execute_create_stream( + // deps.as_mut(), + // env.clone(), + // info.clone(), + // treasury.to_string(), + // "name".to_string(), + // Some("https://abcdefghijklmnopqrstuvw.xyz/abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz/abcdefghijklmnopqrstuvwxyzabcdefghijklmn".to_string()), + // in_denom.to_string(), + // out_denom.to_string(), + // out_supply, + // start_time, + // end_time, + // None, + // "v1".to_string(), + // ) + // .unwrap_err(); + // assert_eq!(res.to_string(), "StreamUrlTooLong"); + + // let res = execute_create_stream( + // deps.as_mut(), + // env.clone(), + // info.clone(), + // treasury.to_string(), + // "name".to_string(), + // Some("https://abc defghijklmnopqrstuvw.xyz/".to_string()), + // in_denom.to_string(), + // out_denom.to_string(), + // out_supply, + // start_time, + // end_time, + // None, + // "v1".to_string(), + // ) + // .unwrap_err(); + + // assert_eq!(res.to_string(), "InvalidStreamUrl"); + + // let res = execute_create_stream( + // deps.as_mut(), + // env, + // info, + // treasury.to_string(), + // "name".to_string(), + // Some("https://abcdefghijklmnopqrstuvw.xyz/".to_string()), + // in_denom.to_string(), + // out_denom.to_string(), + // out_supply, + // start_time, + // end_time, + // None, + // "random".to_string(), + // ) + // .unwrap_err(); + // assert_eq!(res.to_string(), "InvalidToSVersion"); + + // // happy path + // let mut env = mock_env(); + // env.block.time = Timestamp::from_seconds(1); + // let info = mock_info( + // "creator1", + // &[ + // Coin::new(out_supply, "out_denom"), + // Coin::new(100u128, "fee"), + // ], + // ); + // execute_create_stream( + // deps.as_mut(), + // env, + // info, + // treasury.to_string(), + // name.to_string(), + // Some(url.to_string()), + // in_denom.to_string(), + // out_denom.to_string(), + // out_supply, + // start_time, + // end_time, + // None, + // "v1".to_string(), + // ) + // .unwrap(); + + // // query stream with id + // let env = mock_env(); + // let stream = query_stream(deps.as_ref(), env, 1).unwrap(); + // assert_eq!(stream.id, 1); } } +// #[test] +// fn test_subscribe() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(2000); +// let end = Timestamp::from_seconds(1_000_000); +// let out_supply = Uint256::from(1_000_000u128); +// let out_denom = "out_denom"; + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(100); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(1000), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: "in".to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(1); +// let info = mock_info( +// "creator1", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// "in".to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); + +// // stream ended +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(1000000); +// let info = mock_info("creator1", &[]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); +// assert_eq!(res, ContractError::StreamEnded {}); + +// // no funds +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(100); +// let info = mock_info("creator1", &[]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); +// assert_eq!(res, PaymentError::NoFunds {}.into()); + +// // incorrect denom +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(100); +// let info = mock_info("creator1", &[Coin::new(100, "wrong_denom")]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let res = execute(deps.as_mut(), env.clone(), info, msg).unwrap_err(); +// assert_eq!(res, PaymentError::MissingDenom("in".to_string()).into()); + +// let stream = query_stream(deps.as_ref(), env, 1).unwrap(); +// assert_eq!(stream.status, Status::Waiting); + +// // incorrect toc +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(100); +// let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "random".to_string(), +// }; +// let res = execute(deps.as_mut(), env.clone(), info, msg).unwrap_err(); +// assert_eq!(res, ContractError::InvalidToSVersion {}); + +// // first subscribe +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(100); +// let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg); + +// // dist index updated +// let env = mock_env(); +// let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); +// // position index not updated, in_supply updated +// assert_eq!(stream.dist_index, Decimal256::zero()); +// //see that the status is updated +// assert_eq!(stream.status, Status::Active); +// assert_eq!(stream.in_supply, Uint256::from(1000000u128)); +// let position = query_position(deps.as_ref(), env, 1, "creator1".to_string()).unwrap(); +// assert_eq!(position.index, Decimal256::zero()); +// assert_eq!(position.in_balance, Uint256::from(1000000u128)); +// // unauthorized subscription increase +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(200); +// let info = mock_info("random", &[Coin::new(1_000_000, "in")]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: Some("creator1".to_string()), +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); +// assert_eq!(res, ContractError::Unauthorized {}); + +// // subscription increase +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(200); +// let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env.clone(), info, msg); +// // dist index updated +// let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); +// assert_eq!(stream.dist_index, Decimal256::from_str("0.0001").unwrap()); +// // dist index updated, position reduced and increased +// let position = query_position(deps.as_ref(), env, 1, "creator1".to_string()).unwrap(); +// assert_eq!(position.index, Decimal256::from_str("0.0001").unwrap()); +// assert_eq!(position.in_balance, Uint256::from(1999900u128)); +// } + +// #[test] +// fn test_subscribe_pending() { +// // instantiate +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(5_000); +// let end = Timestamp::from_seconds(10_000); +// let out_supply = Uint256::from(1_000_000u128); +// let out_denom = "out_denom"; + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(100); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: "in".to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(200); +// let info = mock_info( +// "creator1", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// "in".to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); + +// // first subscribe +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(300); + +// let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let res = execute(deps.as_mut(), env, info, msg).unwrap(); +// assert_eq!(res.attributes[0].key, "action"); +// assert_eq!(res.attributes[0].value, "subscribe_pending"); +// // query stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(350); +// let stream = query_stream(deps.as_ref(), env, 1).unwrap(); +// assert_eq!(stream.status, Status::Waiting); +// assert_eq!(stream.in_supply, Uint256::from(1000000u128)); +// assert_eq!(stream.shares, Uint256::from(1000000u128)); + +// // second subscribe still waiting +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(500); +// let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let res = execute(deps.as_mut(), env, info, msg).unwrap(); +// assert_eq!(res.attributes[0].key, "action"); +// assert_eq!(res.attributes[0].value, "subscribe_pending"); + +// // query stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(450); +// let stream = query_stream(deps.as_ref(), env, 1).unwrap(); +// assert_eq!(stream.status, Status::Waiting); +// assert_eq!(stream.in_supply, Uint256::from(2000000u128)); + +// // Before stream start time 2 subscriptions have been made and the stream is pending +// // After stream start time plus 1000 seconds one subscription is made and the stream is active +// // Creator 1 has 2 subscriptions and 2_000_000 in balance +// // Creator 2 has 1 subscription and 1_000_000 in balance +// // At 6000 seconds the stream is active and the balance to be distributed is ~2000000 +// // At 6000 seconds creator 1 shold spent 2000000*1000/5000= 400000 +// // At 6000 seconds creator 1 should get all 2000000 tokens +// // At 6000 seconds creator 2 should get 0 tokens +// // At 7500 seconds the stream is active and the balance to be distributed is 300000 +// // At 7500 seconds creator 1 should get 300000*2000000/3250000 = 184615 +// // At 7500 seconds creator 2 should get 300000*1250000/3250000 = 115384 + +// // subscription after start time +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(6000); +// let info = mock_info("creator2", &[Coin::new(1_000_000, "in")]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let res = execute(deps.as_mut(), env, info, msg).unwrap(); +// assert_eq!(res.attributes[0].key, "action"); +// // diffirent action because stream is active +// assert_eq!(res.attributes[0].value, "subscribe"); + +// // update creator 1 position +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(6000); +// let update_msg = crate::msg::ExecuteMsg::UpdatePosition { +// stream_id: 1, +// operator_target: None, +// }; +// let info = mock_info("creator1", &[]); +// let _res = execute(deps.as_mut(), env.clone(), info, update_msg).unwrap(); +// let position = query_position(deps.as_ref(), env, 1, "creator1".to_string()).unwrap(); +// assert_eq!(position.spent, Uint256::from(400000u128)); + +// // query stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(6000); +// let stream = query_stream(deps.as_ref(), env, 1).unwrap(); +// assert_eq!(stream.status, Status::Active); +// assert_eq!(stream.in_supply, Uint256::from(3000000u128 - 400000u128)); +// assert_eq!(stream.spent_in, Uint256::from(400000u128)); + +// // update creator 1 position at 3500 +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(7500); +// let update_msg = crate::msg::ExecuteMsg::UpdatePosition { +// stream_id: 1, +// operator_target: None, +// }; +// let info = mock_info("creator1", &[]); +// let _res = execute(deps.as_mut(), env.clone(), info, update_msg).unwrap(); + +// // query position +// let res = query_position(deps.as_ref(), env, 1, "creator1".to_string()).unwrap(); +// assert_eq!(res.purchased, Uint256::from(184615u128 + 200000u128)); +// assert_eq!(res.spent, Uint256::from(2000000u128 / 2u128)); + +// // update creator 2 position at 3500 +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(3500); +// let update_msg = crate::msg::ExecuteMsg::UpdatePosition { +// stream_id: 1, +// operator_target: None, +// }; +// let info = mock_info("creator2", &[]); +// let _res = execute(deps.as_mut(), env.clone(), info, update_msg).unwrap(); + +// // query position +// let res = query_position(deps.as_ref(), env, 1, "creator2".to_string()).unwrap(); +// assert_eq!(res.purchased, Uint256::from(115384u128)); +// // spent = in_supply * (now-last_updated) / (end-last_updated) +// assert_eq!(res.spent, Uint256::from(1000000u128 * 1500u128 / 4000u128)); +// // query stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(3500); +// let stream = query_stream(deps.as_ref(), env, 1).unwrap(); +// assert_eq!(stream.status, Status::Active); +// // in supply = 3000000 - (positions.spent summed) +// assert_eq!(stream.in_supply, Uint256::from(1625000u128)); +// } + +// #[test] +// pub fn test_withdraw_pending() { +// // // instantiate +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(2000); +// let end = Timestamp::from_seconds(1_000_000); +// let out_supply = Uint256::from(1_000_000u128); +// let out_denom = "out_denom"; + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(100); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: "in".to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(200); +// let info = mock_info( +// "creator1", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// "in".to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); + +// // first subscribe before start time +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(300); +// let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + +// // update creator 1 position no distrubution is excepted +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(350); +// let update_msg = crate::msg::ExecuteMsg::UpdatePosition { +// stream_id: 1, +// operator_target: None, +// }; +// let info = mock_info("creator1", &[]); +// let res = execute(deps.as_mut(), env.clone(), info, update_msg).unwrap(); + +// let expected_attributes = vec![ +// Attribute::new("action", "update_position"), +// Attribute::new("stream_id", "1"), +// Attribute::new("in_balance", "1000000"), +// Attribute::new("shares", "1000000"), +// Attribute::new("index", "0"), +// Attribute::new("last_updated", start.to_string()), +// Attribute::new("pending_purchase", "0"), +// Attribute::new("purchased", "0"), +// Attribute::new("spent", "0"), +// ]; +// assert_eq!(res.attributes, expected_attributes); + +// // query stream before withdraw +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(400); +// let stream = query_stream(deps.as_ref(), env, 1).unwrap(); + +// assert_eq!(stream.id, 1); +// assert_eq!(stream.dist_index, Decimal256::zero()); +// assert_eq!(stream.last_updated, Timestamp::from_seconds(2000)); +// assert_eq!(stream.in_supply, Uint256::from(1_000_000u128)); +// assert_eq!(stream.spent_in, Uint256::zero()); +// assert_eq!(stream.shares, Uint256::from(1_000_000u128)); + +// // withdraw before start time +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(400); +// let info = mock_info("creator1", &[]); +// let msg = crate::msg::ExecuteMsg::Withdraw { +// stream_id: 1, +// cap: Some(Uint256::from(500_000u128)), +// operator_target: None, +// }; +// let res = execute(deps.as_mut(), env, info, msg).unwrap(); +// assert_eq!(res.attributes[0].value, "withdraw_pending"); +// assert_eq!(res.attributes[1].key, "stream_id"); +// assert_eq!(res.attributes[1].value, "1"); +// assert_eq!(res.attributes[3].key, "withdraw_amount"); +// assert_eq!(res.attributes[3].value, "500000"); +// assert_eq!( +// res.messages[0].msg, +// CosmosMsg::Bank(BankMsg::Send { +// to_address: "creator1".to_string(), +// amount: vec![Coin::new(500000, "in")] +// }) +// ); +// // query stream after withdraw +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(400); +// let stream = query_stream(deps.as_ref(), env, 1).unwrap(); +// assert_eq!(stream.id, 1); +// assert_eq!(stream.dist_index, Decimal256::zero()); +// assert_eq!(stream.last_updated, Timestamp::from_seconds(2000)); +// assert_eq!(stream.in_supply, Uint256::from(500_000u128)); +// assert_eq!(stream.spent_in, Uint256::zero()); +// assert_eq!(stream.shares, Uint256::from(500_000u128)); + +// // withdraw after start time +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(3000); +// let info = mock_info("creator1", &[]); +// let msg = crate::msg::ExecuteMsg::Withdraw { +// stream_id: 1, +// cap: Some(Uint256::from(400_000u128)), +// operator_target: None, +// }; +// let res = execute(deps.as_mut(), env, info, msg).unwrap(); +// assert_eq!(res.attributes[0].value, "withdraw"); +// assert_eq!(res.attributes[1].key, "stream_id"); +// assert_eq!(res.attributes[1].value, "1"); +// assert_eq!(res.attributes[3].key, "withdraw_amount"); +// assert_eq!(res.attributes[3].value, "400000"); +// assert_eq!( +// res.messages[0].msg, +// CosmosMsg::Bank(BankMsg::Send { +// to_address: "creator1".to_string(), +// amount: vec![Coin::new(400000, "in")] +// }) +// ); +// // query stream after withdraw +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(3000); +// let _stream = query_stream(deps.as_ref(), env, 1).unwrap(); +// } + +// #[test] +// fn test_operator() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_590_797_419); +// let end = Timestamp::from_seconds(5_571_797_419); +// let out_supply = Uint256::from(1_000_000u128); +// let out_denom = "out_denom"; + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(100); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(1), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: "in".to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let env = mock_env(); +// let info = mock_info( +// "creator", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// "in".to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); + +// //random cannot make the first subscription on behalf of user +// let mut env = mock_env(); +// let info = mock_info("random", &[Coin::new(1_000_000, "in")]); +// env.block.time = start.plus_seconds(100); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: Some("creator1".to_string()), +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); +// assert_eq!(res, ContractError::Unauthorized {}); + +// //random cannot make the first subscription on behalf of user even if defined as operator in message +// let mut env = mock_env(); +// let info = mock_info("random", &[Coin::new(1_000_000, "in")]); +// env.block.time = start.plus_seconds(100); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: Some("creator1".to_string()), +// operator: Some("random".to_string()), +// tos_version: "v1".to_string(), +// }; +// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); +// assert_eq!(res, ContractError::Unauthorized {}); + +// // first subscription +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(100); +// let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg); + +// // only owner can update +// let mut env = mock_env(); +// let info = mock_info("creator2", &[]); +// env.block.time = start.plus_seconds(100); +// let res = +// execute_update_position(deps.as_mut(), env, info, 1, Some("creator1".to_string())) +// .unwrap_err(); +// assert_eq!(res, ContractError::Unauthorized {}); + +// // update creator 1 position no distrubution is excepted +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(100); +// let update_msg = crate::msg::ExecuteMsg::UpdatePosition { +// stream_id: 1, +// operator_target: None, +// }; +// let info = mock_info("creator1", &[]); +// let res = execute(deps.as_mut(), env.clone(), info, update_msg).unwrap(); + +// let expected_attributes = vec![ +// Attribute::new("action", "update_position"), +// Attribute::new("stream_id", "1"), +// Attribute::new("in_balance", "1000000"), +// Attribute::new("shares", "1000000"), +// Attribute::new("index", "0"), +// Attribute::new("last_updated", "1590797519.000000000"), +// Attribute::new("pending_purchase", "0"), +// Attribute::new("purchased", "0"), +// Attribute::new("spent", "0"), +// ]; +// assert_eq!(res.attributes, expected_attributes); + +// // Optional: Add query to verify position state matches response +// let position = +// query_position(deps.as_ref(), env.clone(), 1, "creator1".to_string()).unwrap(); +// assert_eq!(position.in_balance, Uint256::from(1000000u128)); +// assert_eq!(position.shares, Uint256::from(1000000u128)); +// assert_eq!(position.index, Decimal256::zero()); +// assert_eq!(position.last_updated, env.block.time); +// assert_eq!(position.pending_purchase, Decimal256::zero()); +// assert_eq!(position.purchased, Uint256::zero()); +// assert_eq!(position.spent, Uint256::zero()); +// assert_eq!(position.tos_version, "v1"); + +// // random cannot update +// let info = mock_info("random", &[]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(100); +// let res = +// execute_update_position(deps.as_mut(), env, info, 1, Some("creator1".to_string())) +// .unwrap_err(); +// assert_eq!(res, ContractError::Unauthorized {}); + +// // random cannot withdraw +// let _info = mock_info("random", &[]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(100); +// let _msg = crate::msg::ExecuteMsg::Withdraw { +// stream_id: 1, +// cap: None, +// operator_target: Some("creator1".to_string()), +// }; +// assert_eq!(res, ContractError::Unauthorized {}); + +// //owner can update operator +// let info = mock_info("creator1", &[]); +// let mut env = mock_env(); +// let owner = "creator1".to_string(); +// let stream_id = 1; +// env.block.time = start.plus_seconds(100); +// execute_update_operator( +// deps.as_mut(), +// env.clone(), +// info, +// 1, +// Some("operator1".to_string()), +// ) +// .unwrap(); +// let position = query_position(deps.as_ref(), env, stream_id, owner).unwrap(); +// assert_eq!(position.operator.unwrap().as_str(), "operator1".to_string()); + +// //operator can increase subscription on behalf of owner +// let info = mock_info("operator1", &[Coin::new(1_000_000, "in")]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(100); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: Some("creator1".to_string()), +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let res = execute(deps.as_mut(), env, info, msg).unwrap(); +// let expected_attributes = vec![ +// Attribute::new("action", "subscribe"), +// Attribute::new("stream_id", "1"), +// Attribute::new("in_balance", "2000000"), +// Attribute::new("shares", "2000000"), +// Attribute::new("index", "0"), +// Attribute::new("last_updated", "1590797519.000000000"), +// Attribute::new("pending_purchase", "0"), +// Attribute::new("purchased", "0"), +// Attribute::new("spent", "0"), +// Attribute::new("tos_version", "v1"), +// Attribute::new("stream_new_in_supply", "2000000"), +// Attribute::new("stream_previous_in_supply", "2000000"), +// Attribute::new("stream_new_shares", "2000000"), +// Attribute::new("stream_previous_shares", "2000000"), +// Attribute::new("stream_status", "Active"), +// ]; +// assert_eq!(res.attributes, expected_attributes); + +// // random cannot update operator +// let info = mock_info("random", &[]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(100); +// let res = +// execute_update_operator(deps.as_mut(), env, info, 1, Some("operator1".to_string())) +// .unwrap_err(); +// assert!(matches!(res, ContractError::Std(StdError::NotFound { .. }))); + +// // operator can't update operator +// let info = mock_info("operator1", &[]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(100); +// let res = +// execute_update_operator(deps.as_mut(), env, info, 1, Some("operator2".to_string())) +// .unwrap_err(); +// assert!(matches!(res, ContractError::Std(StdError::NotFound { .. }))); + +// // operator can update position +// let info = mock_info("operator1", &[]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(100); +// let res = +// execute_update_position(deps.as_mut(), env, info, 1, Some("creator1".to_string())) +// .unwrap(); +// let expected_attributes = vec![ +// Attribute::new("action", "update_position"), +// Attribute::new("stream_id", "1"), +// Attribute::new("in_balance", "2000000"), +// Attribute::new("shares", "2000000"), +// Attribute::new("index", "0"), +// Attribute::new("last_updated", "1590797519.000000000"), +// Attribute::new("pending_purchase", "0"), +// Attribute::new("purchased", "0"), +// Attribute::new("spent", "0"), +// ]; +// assert_eq!(res.attributes, expected_attributes); + +// // operator can withdraw +// let _info = mock_info("operator1", &[]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(100); +// let _msg = crate::msg::ExecuteMsg::Withdraw { +// stream_id: 1, +// cap: Some(5u128.into()), +// operator_target: Some("creator1".to_string()), +// }; + +// // random cannot exit +// let info = mock_info("random", &[]); +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(100); +// execute_update_stream(deps.as_mut(), env.clone(), 1).unwrap(); +// let res = execute_exit_stream(deps.as_mut(), env, info, 1, Some("creator1".to_string())) +// .unwrap_err(); +// assert_eq!(res, ContractError::Unauthorized {}); + +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(100); +// execute_update_stream(deps.as_mut(), env, 1).unwrap(); + +// // operator can exit +// let info = mock_info("operator1", &[]); +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(100); +// let res = +// execute_exit_stream(deps.as_mut(), env, info, 1, Some("creator1".to_string())).unwrap(); +// match res.messages.first().unwrap().msg.clone() { +// CosmosMsg::Bank(BankMsg::Send { +// to_address, +// amount: _, +// }) => { +// assert_eq!(to_address, "creator1"); +// } +// _ => panic!("unexpected message"), +// } +// } + +// #[test] +// fn test_update_stream() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_000_000); +// let end = Timestamp::from_seconds(5_000_000); +// let out_supply = Uint256::from(1_000_000u128); +// let out_denom = "out_denom"; + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(100); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(1000), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: "in".to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(1); +// let info = mock_info( +// "creator", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// "in".to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); + +// //update stream without subscription this means no new distribution so returned index should be 0 +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(100); +// let res = execute_update_stream(deps.as_mut(), env, 1).unwrap(); +// assert_eq!( +// res, +// Response::default().add_attributes(vec![ +// attr("action", "update_stream"), +// attr("stream_id", "1"), +// attr("new_distribution_amount", "0"), +// attr("dist_index", "0"), +// attr("last_updated", "1000100.000000000"), +// attr("start_time", "1000000.000000000"), +// attr("end_time", "5000000.000000000"), +// attr("in_denom", "in"), +// attr("out_denom", "out_denom"), +// attr("in_supply", "0"), +// attr("out_supply", "1000000"), +// attr("out_remaining", "1000000"), +// attr("spent_in", "0"), +// attr("shares", "0"), +// attr("current_streamed_price", "0"), +// attr("status", "Waiting"), +// attr("tos_version", "v1") +// ]) +// ); +// //first subscription +// //On first subscription index is not incresed because no distrubution prior to that(Execute_subscibe also includes update_stream) +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(100); +// let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: Some("creator1".to_string()), +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg); + +// //Query stream +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(200); +// let res = query_stream(deps.as_ref(), env, 1).unwrap(); +// assert_eq!(res.dist_index, Decimal256::zero()); + +// //Update stream again, this time with subscriber +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(300); +// execute_update_stream(deps.as_mut(), env, 1).unwrap(); + +// //Query stream +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(300); +// let res = query_stream(deps.as_ref(), env, 1).unwrap(); +// assert_eq!(res.dist_index, Decimal256::from_str("0.00005").unwrap()) +// } +// #[test] +// fn test_update_position() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_000_000); +// let end = Timestamp::from_seconds(5_000_000); +// let out_supply = Uint256::from(1_000_000u128); +// let out_denom = "out_denom"; + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(100); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(1000), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: "in".to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(1); +// let info = mock_info( +// "creator", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// "in".to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); + +// // first subscription +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(100); +// let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg); + +// // non owner operator cannot update position +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(3_000_000); +// let info = mock_info("random", &[]); +// let err = +// execute_update_position(deps.as_mut(), env, info, 1, Some("creator1".to_string())) +// .unwrap_err(); +// assert_eq!(err, ContractError::Unauthorized {}); + +// // update position +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(3_000_000); +// let info = mock_info("creator1", &[]); +// execute_update_position(deps.as_mut(), env.clone(), info, 1, None).unwrap(); + +// let position = +// query_position(deps.as_ref(), env.clone(), 1, "creator1".to_string()).unwrap(); +// assert_eq!( +// position.index, +// Decimal256::from_str("0.749993000000000000").unwrap() +// ); +// assert_eq!(position.purchased, Uint256::from(749_993u128)); +// assert_eq!(position.spent, Uint256::from(749_993u128)); +// assert_eq!(position.in_balance, Uint256::from(250_007u128)); +// let stream = query_stream(deps.as_ref(), env, 1).unwrap(); +// assert_eq!( +// stream.dist_index, +// Decimal256::from_str("0.749993000000000000").unwrap() +// ); + +// // can update position after stream ends +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(1); +// let info = mock_info("creator1", &[]); +// execute_update_position(deps.as_mut(), env.clone(), info, 1, None).unwrap(); +// let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); +// assert_eq!(stream.dist_index, Decimal256::from_str("1").unwrap()); +// assert_eq!(stream.in_supply, Uint256::zero()); +// let position = query_position(deps.as_ref(), env, 1, "creator1".to_string()).unwrap(); +// assert_eq!(position.index, Decimal256::from_str("1").unwrap()); +// assert_eq!(position.spent, Uint256::from(1_000_000u128)); +// assert_eq!(position.in_balance, Uint256::zero()); + +// assert_eq!(stream.out_supply, Uint256::from(1_000_000u128)); +// assert_eq!(position.purchased, stream.out_supply); +// } + +// // this is for testing the leftover amount with bigger values +// #[test] +// fn test_rounding_leftover() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_000_000); +// let end = Timestamp::from_seconds(5_000_000); +// let out_supply = Uint256::from(1_000_000_000_000u128); +// let out_denom = "out_denom"; + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(100); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(1000), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: "in".to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(1); +// let info = mock_info( +// "creator", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// "in".to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); + +// // first subscription +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(100); +// let info = mock_info("creator1", &[Coin::new(1_000_000_000, "in")]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// execute(deps.as_mut(), env, info, msg).unwrap(); + +// // second subscription +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(100_000); +// let info = mock_info("creator2", &[Coin::new(3_000_000_000, "in")]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// execute(deps.as_mut(), env, info, msg).unwrap(); + +// // update position creator1 +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(3_000_000); +// let info = mock_info("creator1", &[]); +// execute_update_position(deps.as_mut(), env.clone(), info, 1, None).unwrap(); + +// let position = +// query_position(deps.as_ref(), env.clone(), 1, "creator1".to_string()).unwrap(); +// assert_eq!( +// position.index, +// Decimal256::from_str("202.813614449380587585").unwrap() +// ); +// assert_eq!(position.purchased, Uint256::from(202_813_614_449u128)); +// assert_eq!(position.spent, Uint256::from(749_993_750u128)); +// assert_eq!(position.in_balance, Uint256::from(250_006_250u128)); +// let stream = query_stream(deps.as_ref(), env, 1).unwrap(); +// assert_eq!( +// stream.dist_index, +// Decimal256::from_str("202.813614449380587585").unwrap() +// ); + +// // update position creator2 +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(3_575_000); +// let info = mock_info("creator2", &[]); +// execute_update_position(deps.as_mut(), env.clone(), info, 1, None).unwrap(); + +// let position = +// query_position(deps.as_ref(), env.clone(), 1, "creator2".to_string()).unwrap(); +// assert_eq!( +// position.index, +// Decimal256::from_str("238.074595237060799266").unwrap() +// ); +// assert_eq!(position.purchased, Uint256::from(655672748445u128)); +// assert_eq!(position.spent, Uint256::from(2673076923u128)); +// assert_eq!(position.in_balance, Uint256::from(326923077u128)); +// let stream = query_stream(deps.as_ref(), env, 1).unwrap(); +// assert_eq!( +// stream.dist_index, +// Decimal256::from_str("238.074595237060799266").unwrap() +// ); + +// // update position after stream ends +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(1); +// let info = mock_info("creator1", &[]); +// execute_update_position(deps.as_mut(), env.clone(), info, 1, None).unwrap(); +// let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); +// assert_eq!( +// stream.dist_index, +// Decimal256::from_str("264.137059297637397644").unwrap() +// ); +// assert_eq!(stream.in_supply, Uint256::zero()); +// let position1 = query_position(deps.as_ref(), env, 1, "creator1".to_string()).unwrap(); +// assert_eq!( +// position1.index, +// Decimal256::from_str("264.137059297637397644").unwrap() +// ); +// assert_eq!(position1.spent, Uint256::from(1_000_000_000u128)); +// assert_eq!(position1.in_balance, Uint256::zero()); + +// // update position after stream ends +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(1); +// let info = mock_info("creator2", &[]); +// execute_update_position(deps.as_mut(), env.clone(), info, 1, None).unwrap(); +// let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); +// assert_eq!( +// stream.dist_index, +// Decimal256::from_str("264.137059297637397644").unwrap() +// ); +// assert_eq!(stream.in_supply, Uint256::zero()); +// let position2 = query_position(deps.as_ref(), env, 1, "creator2".to_string()).unwrap(); +// assert_eq!( +// position2.index, +// Decimal256::from_str("264.137059297637397644").unwrap() +// ); +// assert_eq!(position2.spent, Uint256::from(3_000_000_000u128)); +// assert_eq!(position2.in_balance, Uint256::zero()); + +// assert_eq!(stream.out_remaining, Uint256::zero()); +// assert_eq!( +// position1 +// .purchased +// .checked_add(position2.purchased) +// .unwrap(), +// // 1 difference due to rounding +// stream.out_supply.sub(Uint256::from(1u128)) +// ); +// } + +// #[test] +// fn test_withdraw() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_000_000); +// let end = Timestamp::from_seconds(5_000_000); +// let out_supply = Uint256::from(1_000_000_000_000u128); +// let out_denom = "out_denom"; + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: "in".to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let info = mock_info( +// "creator1", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// "in".to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); + +// // first subscription +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(0); +// let funds = Coin::new(2_000_000_000_000, "in"); +// let info = mock_info("creator1", &[funds.clone()]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + +// // withdraw with cap +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(5000); +// let info = mock_info("creator1", &[]); +// // withdraw amount zero +// let cap = Uint256::zero(); +// let msg = crate::msg::ExecuteMsg::Withdraw { +// stream_id: 1, +// cap: Some(cap), +// operator_target: None, +// }; +// let res = execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap_err(); +// assert_eq!(res, ContractError::InvalidWithdrawAmount {}); +// // withdraw amount too high +// let cap = Uint256::from(2_250_000_000_000u128); +// let msg = crate::msg::ExecuteMsg::Withdraw { +// stream_id: 1, +// cap: Some(cap), +// operator_target: None, +// }; +// let res = execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap_err(); +// assert_eq!( +// res, +// ContractError::WithdrawAmountExceedsBalance(Uint256::from(2250000000000u128)) +// ); +// //withdraw with valid cap +// let cap = Uint256::from(25_000_000u128); +// let msg = crate::msg::ExecuteMsg::Withdraw { +// stream_id: 1, +// cap: Some(cap), +// operator_target: None, +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); +// let position = +// query_position(deps.as_ref(), mock_env(), 1, "creator1".to_string()).unwrap(); +// assert_eq!(position.in_balance, Uint256::from(1_997_475_000_000u128)); +// assert_eq!(position.spent, Uint256::from(2_500_000_000u128)); +// assert_eq!(position.purchased, Uint256::from(1_250_000_000u128)); +// // first fund amount should be equal to in_balance + spent + cap +// assert_eq!( +// position.in_balance + position.spent + cap, +// Uint256::from_str(funds.amount.to_string().as_str()).unwrap() +// ); + +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_000); +// let info = mock_info("creator1", &[]); +// let msg = crate::msg::ExecuteMsg::Withdraw { +// stream_id: 1, +// cap: None, +// operator_target: None, +// }; +// let res = execute(deps.as_mut(), env, info, msg).unwrap(); +// let position = +// query_position(deps.as_ref(), mock_env(), 1, "creator1".to_string()).unwrap(); +// assert_eq!(position.in_balance, Uint256::zero()); +// assert_eq!(position.spent, Uint256::from(499_993_773_466u128)); +// assert_eq!(position.purchased, Uint256::from(249_999_999_998u128)); +// assert_eq!(position.shares, Uint256::zero()); +// let msg = res.messages.first().unwrap(); +// assert_eq!( +// msg.msg, +// CosmosMsg::Bank(BankMsg::Send { +// to_address: "creator1".to_string(), +// amount: vec![Coin::new(1_499_981_226_534, "in")] +// }) +// ); + +// // can't withdraw after stream ends +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(1); +// let info = mock_info("creator1", &[]); +// let msg = crate::msg::ExecuteMsg::Withdraw { +// stream_id: 1, +// cap: None, +// operator_target: None, +// }; +// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); +// assert_eq!(res, ContractError::StreamEnded {}); +// } + +// #[test] +// fn test_finalize_stream() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_000_000); +// let end = Timestamp::from_seconds(5_000_000); +// let out_supply = Uint256::from(1_000_000_000_000u128); +// let out_denom = "out_denom"; + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: "in".to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let info = mock_info( +// "creator1", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// "in".to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); + +// // first subscription +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_000); +// let funds = Coin::new(2_000_000_000_000, "in"); +// let info = mock_info("creator1", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + +// // only treasury can finalize +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(1); +// let info = mock_info("random", &[]); +// let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None).unwrap_err(); +// assert_eq!(res, ContractError::Unauthorized {}); + +// // can't finalize before stream ends +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1); +// let info = mock_info(treasury.as_str(), &[]); +// let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None).unwrap_err(); +// assert_eq!(res, ContractError::StreamNotEnded {}); + +// // happy path +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(1); +// let info = mock_info(treasury.as_str(), &[]); +// execute_update_stream(deps.as_mut(), env.clone(), 1).unwrap(); + +// let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None).unwrap(); +// assert_eq!( +// vec![ +// Attribute::new("action", "finalize_stream"), +// Attribute::new("stream_id", "1"), +// Attribute::new("stream_dist_index", "0.5"), +// Attribute::new("stream_shares", "2000000000000"), +// Attribute::new("stream_last_updated", "5000001.000000000"), +// Attribute::new("stream_in_supply", "0"), +// Attribute::new("stream_out_remaining", "0"), +// Attribute::new("stream_status", "Finalized"), +// Attribute::new("stream_exit_fee_percent", "0.01"), +// Attribute::new("treasury", "treasury"), +// Attribute::new("creators_revenue", "1980000000000"), +// Attribute::new("refunded_out_remaining", "0"), +// Attribute::new("total_sold", "1000000000000"), +// Attribute::new("fee_collector", "collector"), +// Attribute::new("swap_fee", "20000000000"), +// Attribute::new("creation_fee", "100"), +// ], +// res.attributes, +// ); +// assert_eq!( +// res.messages, +// vec![ +// SubMsg::new(BankMsg::Send { +// to_address: "treasury".to_string(), +// amount: vec![Coin { +// denom: "in".to_string(), +// amount: Uint128::new(1_980_000_000_000), +// }], +// }), +// SubMsg::new(BankMsg::Send { +// to_address: "collector".to_string(), +// amount: vec![Coin { +// denom: "fee".to_string(), +// amount: Uint128::new(100), +// }], +// }), +// SubMsg::new(BankMsg::Send { +// to_address: "collector".to_string(), +// amount: vec![Coin { +// denom: "in".to_string(), +// amount: Uint128::new(20_000_000_000), +// }], +// }), +// ], +// ); +// } + +// #[test] +// fn test_recurring_finalize_stream_calls() { +// let malicious_treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(10); +// let end = Timestamp::from_seconds(110); +// let out_supply = Uint256::from(1000u128); +// let out_denom = "myToken"; +// let in_denom = "uosmo"; +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(100), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: in_denom.to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); +// // Create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let info = mock_info( +// malicious_treasury.as_str(), +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// malicious_treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// in_denom.to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); +// // First subscription +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1); +// let funds = Coin::new(200, in_denom.to_string()); +// let info = mock_info("user1", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); +// // Update +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(1); +// let info = mock_info(malicious_treasury.as_str(), &[]); +// execute_update_stream(deps.as_mut(), env.clone(), 1).unwrap(); +// // First call +// let res = +// execute_finalize_stream(deps.as_mut(), env.clone(), info.clone(), 1, None).unwrap(); +// assert_eq!( +// res.messages, +// vec![ +// SubMsg::new(BankMsg::Send { +// to_address: malicious_treasury.to_string(), +// amount: vec![Coin { +// denom: in_denom.to_string(), +// amount: Uint128::new(198), +// }], +// }), +// SubMsg::new(BankMsg::Send { +// to_address: "collector".to_string(), +// amount: vec![Coin { +// denom: "fee".to_string(), +// amount: Uint128::new(100), +// }], +// }), +// SubMsg::new(BankMsg::Send { +// to_address: "collector".to_string(), +// amount: vec![Coin { +// denom: in_denom.to_string(), +// amount: Uint128::new(2), +// }], +// }), +// ], +// ); +// // Check stream status +// let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); +// assert_eq!(stream.status, Status::Finalized); +// // Sequential calls, anyone could force this sequential calls +// let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None).unwrap_err(); +// assert_eq!(res, ContractError::StreamAlreadyFinalized {}); +// } + +// #[test] +// fn test_exit_stream() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_000_000); +// let end = Timestamp::from_seconds(5_000_000); +// let out_supply = Uint256::from(1_000_000_000_000u128); +// let out_denom = "out_denom"; + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: "in".to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let info = mock_info( +// "creator1", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// "in".to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); + +// // first subscription +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_000); +// let funds = Coin::new(2_000_000_000_000, "in"); +// let info = mock_info("creator1", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + +// // can't exit before stream ends +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(2_000_000); +// let info = mock_info("creator1", &[]); +// let res = execute_exit_stream(deps.as_mut(), env, info, 1, None).unwrap_err(); +// assert_eq!(res, ContractError::StreamNotEnded {}); + +// //failed exit from random address +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(3_000_000); +// let info = mock_info("random", &[]); +// let res = execute_exit_stream( +// deps.as_mut(), +// env.clone(), +// info, +// 1, +// Some("creator1".to_string()), +// ) +// .unwrap_err(); +// assert_eq!(res, ContractError::Unauthorized {}); +// // can exit +// let info = mock_info("creator1", &[]); +// let res = execute_exit_stream(deps.as_mut(), env, info, 1, None).unwrap(); +// assert_eq!( +// res.messages, +// vec![SubMsg::new(CosmosMsg::Bank(BankMsg::Send { +// to_address: "creator1".to_string(), +// amount: vec![Coin::new( +// Uint128::new(1_000_000_000_000).u128(), +// "out_denom" +// )] +// }))] +// ); + +// // position deleted +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(4_000_000); +// let info = mock_info("creator1", &[]); +// let res = execute_exit_stream(deps.as_mut(), env, info, 1, None).unwrap_err(); +// assert!(matches!(res, ContractError::Std(StdError::NotFound { .. }))); +// } + +// #[test] +// fn test_withdraw_all_before_exit_case() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_000_000); +// let end = Timestamp::from_seconds(5_000_000); +// let out_supply = Uint256::from(1_000_000_000_000u128); +// let out_denom = "out_denom"; + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: "in".to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let info = mock_info( +// "creator1", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// "in".to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); + +// // first subscription +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_000); +// let funds = Coin::new(2_000_000_000_000, "in"); +// let info = mock_info("creator1", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + +// // second subscription +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_000); +// let funds = Coin::new(1_000_000_000_000, "in"); +// let info = mock_info("creator2", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + +// // first withdraw +// let info = mock_info("creator1", &[]); +// let mut env = mock_env(); +// env.block.time = end.minus_seconds(1_000_000); +// let msg = crate::msg::ExecuteMsg::Withdraw { +// stream_id: 1, +// cap: None, +// operator_target: None, +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + +// // second withdraw +// let info = mock_info("creator2", &[]); +// let mut env = mock_env(); +// env.block.time = end.minus_seconds(1_000_000); +// let msg = crate::msg::ExecuteMsg::Withdraw { +// stream_id: 1, +// cap: None, +// operator_target: None, +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + +// // can exit +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(1_000_000); +// execute_update_stream(deps.as_mut(), env, 1).unwrap(); + +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(1_000_001); +// let info = mock_info("creator1", &[]); +// execute_exit_stream(deps.as_mut(), env, info, 1, None).unwrap(); + +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(1_000_002); +// let info = mock_info("creator2", &[]); +// execute_exit_stream(deps.as_mut(), env, info, 1, None).unwrap(); +// } + +// #[test] +// fn test_price_feed() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_000_000); +// let end = Timestamp::from_seconds(5_000_000); +// let out_supply = Uint256::from(1_000_000u128); +// let out_denom = "out_denom"; + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: "in".to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let info = mock_info( +// "creator1", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// "in".to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); + +// // first subscription +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_000); +// let funds = Coin::new(3_000, "in"); +// let info = mock_info("creator1", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + +// //check current streamed price before update +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(2_000_000); +// let res = query_last_streamed_price(deps.as_ref(), env, 1).unwrap(); +// assert_eq!(res.current_streamed_price, Decimal256::zero()); + +// //check current streamed price after update +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(2_000_000); +// execute_update_stream(deps.as_mut(), env, 1).unwrap(); +// let res = query_last_streamed_price(deps.as_ref(), mock_env(), 1).unwrap(); +// //approx 1000/333333 +// assert_eq!( +// res.current_streamed_price, +// Decimal256::from_str("0.002997002997002997").unwrap() +// ); +// // second subscription +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(2_000_000); +// let funds = Coin::new(1_000, "in"); +// let info = mock_info("creator2", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + +// //check current streamed price before update +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(3_000_000); +// let res = query_last_streamed_price(deps.as_ref(), env, 1).unwrap(); +// assert_eq!( +// res.current_streamed_price, +// Decimal256::from_str("0.002997002997002997").unwrap() +// ); + +// //check current streamed price after update +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(3_000_000); +// execute_update_stream(deps.as_mut(), env, 1).unwrap(); +// let res = query_last_streamed_price(deps.as_ref(), mock_env(), 1).unwrap(); +// //approx 2000/333333 +// assert_eq!( +// res.current_streamed_price, +// Decimal256::from_str("0.0045000045000045").unwrap() +// ); + +// //check average streamed price +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(3_000_000); +// let res = query_average_price(deps.as_ref(), env, 1).unwrap(); +// //approx 2500/333333 +// assert_eq!( +// res.average_price, +// Decimal256::from_str("0.003748503748503748").unwrap() +// ); + +// //withdraw creator 1 +// let info = mock_info("creator1", &[]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(3_500_000); +// let msg = crate::msg::ExecuteMsg::Withdraw { +// stream_id: 1, +// cap: None, +// operator_target: None, +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); +// let res = query_last_streamed_price(deps.as_ref(), mock_env(), 1).unwrap(); +// assert_eq!( +// res.current_streamed_price, +// Decimal256::from_str("0.004499991000017999").unwrap() +// ); + +// //test price after withdraw +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(3_750_000); +// execute_update_stream(deps.as_mut(), env, 1).unwrap(); +// let res = query_last_streamed_price(deps.as_ref(), mock_env(), 1).unwrap(); +// //approx 2500/333333 +// assert_eq!( +// res.current_streamed_price, +// Decimal256::from_str("0.001500006000024000").unwrap() +// ); +// } + +// #[test] +// fn test_update_protocol_admin() { +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: "in".to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // random cannot update +// let env = mock_env(); +// let msg = UpdateProtocolAdmin { +// new_protocol_admin: "new_protocol_admin".to_string(), +// }; +// let info = mock_info("random", &[]); +// let err = execute(deps.as_mut(), env.clone(), info, msg.clone()).unwrap_err(); +// assert_eq!(err, ContractError::Unauthorized {}); + +// // protocol admin can update +// let info = mock_info("protocol_admin", &[]); +// execute(deps.as_mut(), env, info, msg).unwrap(); +// let query = query_config(deps.as_ref()).unwrap(); +// assert_eq!(query.protocol_admin, "new_protocol_admin".to_string()); +// } +// #[test] +// fn test_execute_update_config() { +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: "in".to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// //query config +// let config_response = query_config(deps.as_ref()).unwrap(); +// //check config +// assert_eq!(config_response.min_stream_seconds, Uint64::new(1000)); +// assert_eq!(config_response.min_seconds_until_start_time, Uint64::new(0)); +// assert_eq!(config_response.stream_creation_denom, "fee".to_string()); +// assert_eq!(config_response.stream_creation_fee, Uint128::new(100)); +// assert_eq!(config_response.fee_collector, "collector".to_string()); +// assert_eq!(config_response.protocol_admin, "protocol_admin".to_string()); +// assert_eq!(config_response.accepted_in_denom, "in".to_string()); + +// // random user cant update config +// let mut env = mock_env(); +// let info = mock_info("random", &[]); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::ExecuteMsg::UpdateConfig { +// min_stream_duration: Some(Uint64::new(2000)), +// min_duration_until_start_time: Some(Uint64::new(2000)), +// stream_creation_denom: Some("fee2".to_string()), +// stream_creation_fee: Some(Uint128::new(200)), +// fee_collector: Some("collector2".to_string()), +// accepted_in_denom: Some("new_denom".to_string()), +// exit_fee_percent: Some(Decimal256::percent(2)), +// tos_version: None, +// }; +// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); +// assert_eq!(res, ContractError::Unauthorized {}); + +// // wrong fee amount +// let mut env = mock_env(); +// let info = mock_info("protocol_admin", &[]); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::ExecuteMsg::UpdateConfig { +// min_stream_duration: Some(Uint64::new(2000)), +// min_duration_until_start_time: Some(Uint64::new(2000)), +// stream_creation_denom: Some("fee2".to_string()), +// stream_creation_fee: Some(Uint128::new(0)), +// fee_collector: Some("collector2".to_string()), +// accepted_in_denom: Some("new_denom".to_string()), +// exit_fee_percent: Some(Decimal256::percent(2)), +// tos_version: None, +// }; +// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); +// assert_eq!(res, ContractError::InvalidStreamCreationFee {}); + +// // wrong exit fee percent +// let mut env = mock_env(); +// let info = mock_info("protocol_admin", &[]); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::ExecuteMsg::UpdateConfig { +// min_stream_duration: Some(Uint64::new(2000)), +// min_duration_until_start_time: Some(Uint64::new(2000)), +// stream_creation_denom: Some("fee2".to_string()), +// stream_creation_fee: Some(Uint128::new(200)), +// fee_collector: Some("collector2".to_string()), +// accepted_in_denom: Some("new_denom".to_string()), +// exit_fee_percent: Some(Decimal256::percent(101)), +// tos_version: None, +// }; +// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); +// assert_eq!(res, ContractError::InvalidExitFeePercent {}); + +// // protocol admin can update config +// let mut env = mock_env(); +// let info = mock_info("protocol_admin", &[]); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::ExecuteMsg::UpdateConfig { +// min_stream_duration: Some(Uint64::new(2000)), +// min_duration_until_start_time: Some(Uint64::new(2000)), +// stream_creation_denom: Some("fee2".to_string()), +// stream_creation_fee: Some(Uint128::new(200)), +// fee_collector: Some("collector2".to_string()), +// accepted_in_denom: Some("new_denom".to_string()), +// exit_fee_percent: Some(Decimal256::percent(2)), +// tos_version: None, +// }; +// execute(deps.as_mut(), env, info, msg).unwrap(); + +// //query config +// let config_response = query_config(deps.as_ref()).unwrap(); +// //check config +// assert_eq!(config_response.min_stream_seconds, Uint64::new(2000)); +// assert_eq!( +// config_response.min_seconds_until_start_time, +// Uint64::new(2000) +// ); +// assert_eq!(config_response.stream_creation_denom, "fee2".to_string()); +// assert_eq!(config_response.stream_creation_fee, Uint128::new(200)); +// assert_eq!(config_response.fee_collector, "collector2".to_string()); +// assert_eq!(config_response.protocol_admin, "protocol_admin".to_string()); +// assert_eq!(config_response.accepted_in_denom, "new_denom".to_string()); +// assert_eq!(config_response.exit_fee_percent, Decimal256::percent(2)); + +// // create stream +// let out_supply = Uint256::from(1000u128); +// let out_denom = "out"; +// let start = Timestamp::from_seconds(10000); +// let end = Timestamp::from_seconds(1000000); +// let treasury = "treasury"; +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let info = mock_info( +// "creator1", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(200, "fee2"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// "new_denom".to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); + +// // update config during stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(100000); +// let info = mock_info("protocol_admin", &[]); +// let msg = crate::msg::ExecuteMsg::UpdateConfig { +// min_stream_duration: Some(Uint64::new(3000)), +// min_duration_until_start_time: Some(Uint64::new(4000)), +// stream_creation_denom: Some("fee3".to_string()), +// stream_creation_fee: Some(Uint128::new(300)), +// fee_collector: Some("collector3".to_string()), +// accepted_in_denom: Some("new_denom2".to_string()), +// exit_fee_percent: Some(Decimal256::percent(5)), +// tos_version: None, +// }; +// execute(deps.as_mut(), env, info, msg).unwrap(); +// //query config +// let config_response = query_config(deps.as_ref()).unwrap(); +// //check config +// assert_eq!(config_response.min_stream_seconds, Uint64::new(3000)); +// assert_eq!( +// config_response.min_seconds_until_start_time, +// Uint64::new(4000) +// ); +// assert_eq!(config_response.stream_creation_denom, "fee3".to_string()); +// assert_eq!(config_response.stream_creation_fee, Uint128::new(300)); +// assert_eq!(config_response.fee_collector, "collector3".to_string()); +// assert_eq!(config_response.protocol_admin, "protocol_admin".to_string()); +// assert_eq!(config_response.accepted_in_denom, "new_denom2".to_string()); +// assert_eq!(config_response.exit_fee_percent, Decimal256::percent(5)); + +// // check stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(100000); +// let stream_response = query_stream(deps.as_ref(), env, 1).unwrap(); +// assert_eq!(stream_response.exit_fee_percent, Decimal256::percent(2)); +// assert_eq!(stream_response.stream_creation_fee, Uint128::new(200)); +// } + +// #[cfg(test)] +// mod killswitch { +// use super::*; +// use crate::contract::{list_positions, list_streams}; +// use crate::killswitch::{ +// execute_cancel_stream, execute_exit_cancelled, execute_resume_stream, +// sudo_cancel_stream, sudo_pause_stream, +// }; +// use cosmwasm_std::CosmosMsg::Bank; +// use cosmwasm_std::{Binary, ReplyOn, SubMsg}; + +// #[test] +// fn test_pause_protocol_admin() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_000_000); +// let end = Timestamp::from_seconds(5_000_000); +// let out_supply = Uint256::from(1_000_000_000_000u128); +// let out_denom = "out_denom"; + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: "in".to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let info = mock_info( +// "creator1", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// "in".to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); + +// // non protocol admin can't pause +// let info = mock_info("non_protocol_admin", &[]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(100); + +// let res = execute_pause_stream(deps.as_mut(), env, info, 1); +// assert_eq!(res, Err(ContractError::Unauthorized {})); + +// // first subscription +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_000); +// let funds = Coin::new(3_000, "in"); +// let info = mock_info("position1", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + +// //can't pause before start time +// let info = mock_info("protocol_admin", &[]); +// let mut env = mock_env(); +// env.block.time = start.minus_seconds(500_000); +// let res = execute_pause_stream(deps.as_mut(), env, info, 1).unwrap_err(); +// assert_eq!(res, ContractError::StreamNotStarted {}); + +// // can't pause after end time +// let info = mock_info("protocol_admin", &[]); +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(500_000); +// let res = execute_pause_stream(deps.as_mut(), env, info, 1).unwrap_err(); +// assert_eq!(res, ContractError::StreamEnded {}); + +// // protocol admin can pause +// let info = mock_info("protocol_admin", &[]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_001); +// execute_pause_stream(deps.as_mut(), env, info, 1).unwrap(); + +// // can't paused if already paused +// let info = mock_info("protocol_admin", &[]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_005); +// let res = execute_pause_stream(deps.as_mut(), env, info, 1).unwrap_err(); +// assert_eq!(res, ContractError::StreamKillswitchActive {}); + +// // can't subscribe new +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_002); +// let funds = Coin::new(3_000, "in"); +// let info = mock_info("position2", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); +// assert_eq!(res, ContractError::StreamKillswitchActive {}); + +// // can't subscribe more +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_002); +// let funds = Coin::new(3_000, "in"); +// let info = mock_info("position1", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); +// assert_eq!(res, ContractError::StreamKillswitchActive {}); + +// // can't withdraw +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_002); +// let info = mock_info("position1", &[]); +// let msg = crate::msg::ExecuteMsg::Withdraw { +// stream_id: 1, +// cap: None, +// operator_target: None, +// }; +// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); +// assert_eq!(res, ContractError::StreamKillswitchActive {}); + +// // can't update stream +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_002); +// let res = execute_update_stream(deps.as_mut(), env, 1); +// assert_eq!(res, Err(ContractError::StreamPaused {})); + +// // can't update position +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_002); +// let info = mock_info("position1", &[]); +// let res = execute_update_position(deps.as_mut(), env, info, 1, None); +// assert_eq!(res, Err(ContractError::StreamPaused {})); + +// // can't finalize stream +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(1_000_002); +// let info = mock_info("treasury", &[]); +// let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None); +// assert_eq!(res, Err(ContractError::StreamKillswitchActive {})); + +// // can't exit +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(1_000_002); +// let info = mock_info("position1", &[]); +// let res = execute_exit_stream(deps.as_mut(), env, info, 1, None); +// assert_eq!(res, Err(ContractError::StreamKillswitchActive {})); +// } + +// #[test] +// fn test_resume_protocol_admin() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_000_000); +// let end = Timestamp::from_seconds(5_000_000); +// let out_supply = Uint256::from(1_000_000_000_000u128); +// let out_denom = "out_denom"; + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: "in".to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let info = mock_info( +// "creator1", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// "in".to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); + +// // first subscription +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_000); +// let funds = Coin::new(3_000, "in"); +// let info = mock_info("position1", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + +// // can't resume if not paused +// let info = mock_info("protocol_admin", &[]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_003); +// let res = execute_resume_stream(deps.as_mut(), env, info, 1).unwrap_err(); +// assert_eq!(res, ContractError::StreamNotPaused {}); + +// // protocol admin can pause +// let info = mock_info("protocol_admin", &[]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_001); +// execute_pause_stream(deps.as_mut(), env, info, 1).unwrap(); + +// // can't subscribe new +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_002); +// let funds = Coin::new(3_000, "in"); +// let info = mock_info("position2", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); +// assert_eq!(res, ContractError::StreamKillswitchActive {}); + +// // non protocol admin can't resume +// let info = mock_info("non_protocol_admin", &[]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_003); +// let res = execute_resume_stream(deps.as_mut(), env, info, 1).unwrap_err(); +// assert_eq!(res, ContractError::Unauthorized {}); + +// // protocol admin can resume +// let info = mock_info("protocol_admin", &[]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_003); +// execute_resume_stream(deps.as_mut(), env, info, 1).unwrap(); + +// // can subscribe new after resume +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_004); +// let funds = Coin::new(3_000, "in"); +// let info = mock_info("position2", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let res = execute(deps.as_mut(), env, info, msg).unwrap(); +// assert_eq!(res.attributes[0].key, "action"); +// assert_eq!(res.attributes[1].key, "stream_id"); +// assert_eq!(res.attributes[2].key, "in_balance"); +// assert_eq!(res.attributes[3].key, "shares"); +// assert_eq!(res.attributes[4].key, "index"); +// assert_eq!(res.attributes[5].key, "last_updated"); +// assert_eq!(res.attributes[6].key, "pending_purchase"); +// assert_eq!(res.attributes[7].key, "purchased"); +// assert_eq!(res.attributes[8].key, "spent"); +// assert_eq!(res.attributes[9].key, "tos_version"); +// assert_eq!(res.attributes[10].key, "stream_new_in_supply"); +// assert_eq!(res.attributes[11].key, "stream_previous_in_supply"); +// assert_eq!(res.attributes[12].key, "stream_new_shares"); +// assert_eq!(res.attributes[13].key, "stream_previous_shares"); +// assert_eq!(res.attributes[14].key, "stream_status"); +// // validate values +// assert_eq!(res.attributes[0].value, "subscribe"); +// assert_eq!(res.attributes[1].value, "1"); +// assert_eq!(res.attributes[2].value, "3000"); +// assert_eq!(res.attributes[3].value, "3000"); +// assert_eq!(res.attributes[4].value, "222.222"); +// assert_eq!(res.attributes[5].value, "2000004.000000000"); +// assert_eq!(res.attributes[6].value, "0"); +// assert_eq!(res.attributes[7].value, "0"); +// assert_eq!(res.attributes[8].value, "0"); +// assert_eq!(res.attributes[9].value, "v1"); +// assert_eq!(res.attributes[10].value, "6000"); +// assert_eq!(res.attributes[11].value, "6000"); +// assert_eq!(res.attributes[12].value, "6000"); +// assert_eq!(res.attributes[13].value, "6000"); +// assert_eq!(res.attributes[14].value, "Active"); + +// // protocol admin can pause +// let info = mock_info("protocol_admin", &[]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_005); +// execute_pause_stream(deps.as_mut(), env, info, 1).unwrap(); + +// // cancel the stream +// let info = mock_info("protocol_admin", &[]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_006); +// execute_cancel_stream(deps.as_mut(), env, info, 1).unwrap(); + +// // can't resume if cancelled +// let info = mock_info("protocol_admin", &[]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_007); +// let res = execute_resume_stream(deps.as_mut(), env, info, 1).unwrap_err(); +// assert_eq!(res, ContractError::StreamIsCancelled {}); +// } + +// #[test] +// fn test_cancel_protocol_admin() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_000_000); +// let end = Timestamp::from_seconds(5_000_000); +// let out_supply = Uint256::from(1_000_000_000_000u128); +// let out_denom = "out_denom"; + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: "in".to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let info = mock_info( +// "creator1", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// "in".to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); + +// // subscription +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(0); +// let funds = Coin::new(2_000_000_000_000, "in"); +// let info = mock_info("creator1", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: Some("operator".to_string()), +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + +// // non protocol admin can't cancel +// let info = mock_info("non_protocol_admin", &[]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_000); +// let err = execute_cancel_stream(deps.as_mut(), env, info, 1).unwrap_err(); +// assert_eq!(err, ContractError::Unauthorized {}); + +// // cant cancel without pause +// let info = mock_info("protocol_admin", &[]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_000); +// let err = execute_cancel_stream(deps.as_mut(), env, info, 1).unwrap_err(); +// assert_eq!(err, ContractError::StreamNotPaused {}); + +// // pause +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(2_000_000); +// let info = mock_info("protocol_admin", &[]); +// execute_pause_stream(deps.as_mut(), env, info, 1).unwrap(); + +// //cancel +// let info = mock_info("protocol_admin", &[]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(2_500_000); +// let response = execute_cancel_stream(deps.as_mut(), env, info, 1).unwrap(); +// //out_tokens and the creation fee are sent back to the treasury upon cancellation +// assert_eq!( +// response.messages, +// [ +// SubMsg { +// payload: Binary::from(b""), +// id: 0, +// msg: Bank(BankMsg::Send { +// to_address: "treasury".to_string(), +// amount: Vec::from([Coin { +// denom: "out_denom".to_string(), +// amount: Uint256::from(1000000000000) +// }]) +// }), +// gas_limit: None, +// reply_on: ReplyOn::Never +// }, +// SubMsg { +// payload: Binary::from(b""), +// id: 0, +// msg: Bank(BankMsg::Send { +// to_address: "treasury".to_string(), +// amount: Vec::from([Coin { +// denom: "fee".to_string(), +// amount: Uint256::from(100) +// }]) +// }), +// gas_limit: None, +// reply_on: ReplyOn::Never +// } +// ] +// ); + +// // can't cancel cancelled stream +// let info = mock_info("protocol_admin", &[]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(2_500_000); +// let response = execute_cancel_stream(deps.as_mut(), env, info, 1).unwrap_err(); +// assert_eq!(response, ContractError::StreamIsCancelled {}); +// } + +// #[test] +// fn test_withdraw_pause() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_000_000); +// let end = Timestamp::from_seconds(5_000_000); +// let out_supply = Uint256::from(1_000_000_000_000u128); +// let out_denom = "out_denom"; + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: "in".to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let info = mock_info( +// "creator1", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// "in".to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); + +// // subscription +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(0); +// let funds = Coin::new(2_000_000_000_000, "in"); +// let info = mock_info("creator1", &[funds.clone()]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: Some("operator".to_string()), +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + +// // withdraw with cap +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(5000); +// let info = mock_info("creator1", &[]); +// let cap = Uint256::from(25_000_000u128); +// let msg = crate::msg::ExecuteMsg::Withdraw { +// stream_id: 1, +// cap: Some(cap), +// operator_target: None, +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + +// let position = +// query_position(deps.as_ref(), mock_env(), 1, "creator1".to_string()).unwrap(); +// assert_eq!(position.in_balance, Uint256::from(1_997_475_000_000u128)); +// assert_eq!(position.spent, Uint256::from(2_500_000_000u128)); +// assert_eq!(position.purchased, Uint256::from(1_250_000_000u128)); +// // first fund amount should be equal to in_balance + spent + cap +// assert_eq!( +// position.in_balance + position.spent + cap, +// Uint256::from_str(funds.amount.to_string().as_str()).unwrap() +// ); + +// // can't withdraw pause +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(6000); +// let info = mock_info("creator1", &[]); +// let err = execute_withdraw_paused(deps.as_mut(), env, info, 1, None, None).unwrap_err(); +// assert_eq!(err, ContractError::StreamNotPaused {}); + +// // pause +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(6000); +// let info = mock_info("protocol_admin", &[]); +// execute_pause_stream(deps.as_mut(), env, info, 1).unwrap(); + +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(6500); +// let stream1_old = query_stream(deps.as_ref(), env, 1).unwrap(); +// //Unauthorized check +// let info = mock_info("random", &[]); +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(7000); +// let res = execute_withdraw_paused( +// deps.as_mut(), +// env, +// info, +// 1, +// None, +// Some("creator1".to_string()), +// ) +// .unwrap_err(); + +// assert_eq!(res, ContractError::Unauthorized {}); +// //Cap exceeds in balance check +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(7000); +// let info = mock_info("creator1", &[]); +// let res = execute_withdraw_paused( +// deps.as_mut(), +// env, +// info, +// 1, +// Some(Uint256::from(2_000_000_000_000u128 + 1u128)), +// None, +// ) +// .unwrap_err(); +// assert_eq!( +// res, +// ContractError::WithdrawAmountExceedsBalance(Uint256::from(2_000_000_000_001u128)) +// ); +// // Withdraw cap is zero +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(7000); +// let info = mock_info("creator1", &[]); +// let res = +// execute_withdraw_paused(deps.as_mut(), env, info, 1, Some(Uint256::zero()), None) +// .unwrap_err(); +// assert_eq!(res, ContractError::InvalidWithdrawAmount {}); + +// //withdraw with cap +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(7000); +// let info = mock_info("creator1", &[]); +// let cap = Uint256::from(25_000_000u128); +// execute_withdraw_paused(deps.as_mut(), env, info, 1, Some(cap), None).unwrap(); + +// // withdraw after pause +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(7000); +// let info = mock_info("creator1", &[]); +// let res = execute_withdraw_paused(deps.as_mut(), env, info, 1, None, None).unwrap(); +// assert_eq!( +// res.messages, +// vec![SubMsg { +// id: 0, +// msg: BankMsg::Send { +// to_address: "creator1".to_string(), +// amount: vec![Coin { +// denom: "in".to_string(), +// amount: Uint128::new(1996950006258), +// }], +// } +// .into(), +// gas_limit: None, +// reply_on: ReplyOn::Never, +// }] +// ); + +// // stream not updated +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(8000); +// let stream1_new = query_stream(deps.as_ref(), env, 1).unwrap(); +// // dist_index not updated +// assert_eq!(stream1_old.dist_index, stream1_new.dist_index); +// assert_eq!(stream1_new.in_supply, Uint256::zero()); +// assert_eq!(stream1_new.shares, Uint256::zero()); + +// // position updated +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(8001); +// let position = +// query_position(deps.as_ref(), mock_env(), 1, "creator1".to_string()).unwrap(); +// // in_balance updated +// assert_eq!(position.in_balance, Uint256::zero()); +// assert_eq!(position.spent, Uint256::from(2_999_993_742u128)); +// assert_eq!(position.purchased, Uint256::from(1_499_999_998u128)); +// assert_eq!(position.shares, Uint256::zero()); +// } + +// #[test] +// fn test_resume() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_000_000); +// let end = Timestamp::from_seconds(5_000_000); +// let out_supply = Uint256::from(1_000_000_000_000u128); +// let out_denom = "out_denom"; + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: "in".to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let info = mock_info( +// "creator1", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// "in".to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); + +// // first subscription +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_000); +// let funds = Coin::new(3_000, "in"); +// let info = mock_info("position1", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + +// //cant resume if not paused +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_000); +// let res = sudo_resume_stream(deps.as_mut(), env, 1).unwrap_err(); +// assert_eq!(res, ContractError::StreamNotPaused {}); + +// // pause +// let info = mock_info("protocol_admin", &[]); +// let mut env = mock_env(); +// let pause_date = start.plus_seconds(2_000_000); +// env.block.time = pause_date; +// execute_pause_stream(deps.as_mut(), env, info, 1).unwrap(); + +// // resume +// let mut env = mock_env(); +// let resume_date = start.plus_seconds(3_000_000); +// env.block.time = resume_date; +// sudo_resume_stream(deps.as_mut(), env, 1).unwrap(); + +// // new end date is correct +// let new_end_date = end.plus_nanos(resume_date.nanos() - pause_date.nanos()); +// let stream = query_stream(deps.as_ref(), mock_env(), 1).unwrap(); +// assert_eq!(stream.end_time, new_end_date); +// } + +// #[test] +// fn test_sudo_pause_stream() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_000_000); +// let end = Timestamp::from_seconds(5_000_000); +// let out_supply = Uint256::from(1_000_000_000_000u128); +// let out_denom = "out_denom"; + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: "in".to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let info = mock_info( +// "creator1", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// "in".to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); + +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(500_000); +// let res = sudo_pause_stream(deps.as_mut(), env, 1).unwrap_err(); +// assert_eq!(res, ContractError::StreamNotStarted {}); + +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(6_000_000); +// let res = sudo_pause_stream(deps.as_mut(), env, 1).unwrap_err(); +// assert_eq!(res, ContractError::StreamEnded {}); + +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(3_000_000); +// let res = sudo_pause_stream(deps.as_mut(), env, 1).unwrap(); +// assert_eq!( +// res, +// Response::new() +// .add_attribute("action", "sudo_pause_stream") +// .add_attribute("stream_id", "1") +// .add_attribute("is_paused", "true") +// .add_attribute("pause_date", "3000000.000000000") +// ); + +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(4_000_000); +// let res = sudo_pause_stream(deps.as_mut(), env, 1).unwrap_err(); +// assert_eq!(res, ContractError::StreamKillswitchActive {}); +// } + +// #[test] +// fn test_range_queries() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(2000); +// let end = Timestamp::from_seconds(1_000_000); +// let out_supply = Uint256::from(1_000_000u128); +// let out_denom = "out_denom"; + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(100); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(1000), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: "in".to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(1); +// let info = mock_info( +// "creator1", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// //first stream +// execute_create_stream( +// deps.as_mut(), +// env.clone(), +// info.clone(), +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// "in".to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); +// //second stream +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// "in".to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); + +// let res = list_streams(deps.as_ref(), None, None).unwrap(); +// assert_eq!(res.streams.len(), 2); + +// // first subscription to first stream +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(100); +// let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + +// // second subscription to first stream +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(100); +// let info = mock_info("creator2", &[Coin::new(1_000_000, "in")]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: None, +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + +// let res = list_positions(deps.as_ref(), 1, None, None).unwrap(); +// assert_eq!(res.positions.len(), 2); +// } + +// #[test] +// fn test_exit_cancel() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_000_000); +// let end = Timestamp::from_seconds(5_000_000); +// let out_supply = Uint256::from(1_000_000_000_000u128); +// let out_denom = "out_denom"; + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: "in".to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let info = mock_info( +// "creator1", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// "in".to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// None, +// "v1".to_string(), +// ) +// .unwrap(); + +// // subscription +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(0); +// let funds = Coin::new(2_000_000_000_000, "in"); +// let info = mock_info("creator1", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: Some("operator".to_string()), +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + +// // cant cancel without pause +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_000); +// let err = sudo_cancel_stream(deps.as_mut(), env, 1).unwrap_err(); +// assert_eq!(err, ContractError::StreamNotPaused {}); + +// // pause +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(2_000_000); +// let info = mock_info("protocol_admin", &[]); +// execute_pause_stream(deps.as_mut(), env, info, 1).unwrap(); + +// //can't exit before cancel +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(2_250_000); +// let info = mock_info("creator1", &[]); +// let res = execute_exit_cancelled(deps.as_mut(), env, info, 1, None).unwrap_err(); +// assert_eq!(res, ContractError::StreamNotCancelled {}); + +// //cancel +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(2_500_000); +// let response = sudo_cancel_stream(deps.as_mut(), env, 1).unwrap(); +// //out_tokens and the creation fee are sent back to the treasury upon cancellation +// assert_eq!( +// response.messages, +// [ +// SubMsg { +// id: 0, +// msg: Bank(BankMsg::Send { +// to_address: "treasury".to_string(), +// amount: Vec::from([Coin { +// denom: "out_denom".to_string(), +// amount: Uint128::new(1000000000000) +// }]) +// }), +// gas_limit: None, +// reply_on: ReplyOn::Never +// }, +// SubMsg { +// id: 0, +// msg: Bank(BankMsg::Send { +// to_address: "treasury".to_string(), +// amount: Vec::from([Coin { +// denom: "fee".to_string(), +// amount: Uint128::new(100) +// }]) +// }), +// gas_limit: None, +// reply_on: ReplyOn::Never +// } +// ] +// ); + +// //random operator can't exit +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(2_250_000); +// let info = mock_info("random", &[]); +// let res = +// execute_exit_cancelled(deps.as_mut(), env, info, 1, Some("creator1".to_string())) +// .unwrap_err(); +// assert_eq!(res, ContractError::Unauthorized {}); + +// // exit +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(3_000_000); +// let info = mock_info("creator1", &[]); +// let res = execute_exit_cancelled(deps.as_mut(), env, info, 1, None).unwrap(); +// let msg = res.messages.first().unwrap(); +// assert_eq!( +// msg.msg, +// Bank(BankMsg::Send { +// to_address: "creator1".to_string(), +// amount: vec![Coin::new(2000000000000, "in")] +// }) +// ); +// } +// #[test] +// fn test_treasury_cancel_stream() { +// use crate::killswitch::execute_treasury_cancel_stream; + +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_000_000); +// let end = Timestamp::from_seconds(5_000_000); +// let out_supply = Uint256::from(500u128); +// let out_denom = "out_denom"; +// let in_denom = "in_denom"; + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(100_000), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: in_denom.to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let info = mock_info( +// "creator", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// in_denom.to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// Some(1_000u128.into()), +// "v1".to_string(), +// ) +// .unwrap(); + +// // Cancel period should be ended Now+min_seconds_until_start_time + +// // Cancel stream with wrong address +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(100); + +// let error = +// execute_treasury_cancel_stream(deps.as_mut(), env, mock_info("random", &[]), 1) +// .unwrap_err(); +// assert_eq!(error, ContractError::Unauthorized {}); + +// // Try subscribing to the stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(100); +// let funds = Coin::new(250, "in_denom"); +// let info = mock_info("subscriber", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: Some("operator".to_string()), +// tos_version: "v1".to_string(), +// }; +// let err = execute(deps.as_mut(), env.clone(), info, msg).unwrap_err(); +// assert_eq!(err, ContractError::TreasuryCancelPeriodActive {}); + +// // Try canceling the stream outside the cancel period +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(100_000 + 1); +// let error = execute_treasury_cancel_stream( +// deps.as_mut(), +// env.clone(), +// mock_info("treasury", &[]), +// 1, +// ) +// .unwrap_err(); +// assert_eq!(error, ContractError::TreasuryCancelPeriodEnded {}); + +// // Query the stream +// let _stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); + +// // Cancel the stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(100_000); +// let response = execute_treasury_cancel_stream( +// deps.as_mut(), +// env.clone(), +// mock_info("treasury", &[]), +// 1, +// ) +// .unwrap(); +// assert_eq!( +// response.messages, +// [SubMsg { +// id: 0, +// msg: Bank(BankMsg::Send { +// to_address: "treasury".to_string(), +// amount: vec![Coin { +// denom: "out_denom".to_string(), +// amount: Uint128::new(500) +// }] +// }), +// gas_limit: None, +// reply_on: ReplyOn::Never +// }] +// ); + +// // Query the stream again should return error because we removed the stream +// let _stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap_err(); +// } +// } +// mod threshold { +// use crate::{ +// killswitch::{execute_cancel_stream_with_threshold, execute_exit_cancelled}, +// threshold::ThresholdError, +// }; + +// // Create a stream with a threshold +// // Subscribe to the stream +// use super::*; + +// #[test] +// fn test_threshold_reached() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_000_000); +// let end = Timestamp::from_seconds(5_000_000); +// let out_supply = Uint256::from(500u128); +// let out_denom = "out_denom"; +// let in_denom = "in_denom"; + +// // threshold = 500*0.5 / 1-0.01 =252.5 + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: in_denom.to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let info = mock_info( +// "creator", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// in_denom.to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// Some(Uint256::from(250u128)), +// "v1".to_string(), +// ) +// .unwrap(); + +// // subscription +// let mut env = mock_env(); +// env.block.time = start; +// let funds = Coin::new(252, "in_denom"); +// let info = mock_info("subscriber", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: Some("operator".to_string()), +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + +// // Threshold should be reached +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(1); + +// // Exit should be possible +// // Since there is only one subscriber all out denom should be sent to subscriber +// // In calculations we are always rounding down that one token will be left in the stream +// // Asuming token is 6 decimals +// // This amount could be considered as insignificant +// let info = mock_info("subscriber", &[]); +// let res = execute_exit_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap(); +// assert_eq!( +// res.messages, +// vec![SubMsg::new(BankMsg::Send { +// to_address: "subscriber".to_string(), +// amount: vec![Coin::new(499, "out_denom")], +// })], +// ); + +// // Creator finalizes the stream +// let info = mock_info("treasury", &[]); +// let res = execute_finalize_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap(); +// // Creator's revenue +// assert_eq!( +// res.messages[0].msg, +// cosmwasm_std::CosmosMsg::Bank(BankMsg::Send { +// to_address: "treasury".to_string(), +// amount: vec![Coin::new(250, "in_denom")], +// }) +// ); +// assert_eq!( +// res.messages[1].msg, +// cosmwasm_std::CosmosMsg::Bank(BankMsg::Send { +// to_address: "collector".to_string(), +// amount: vec![Coin::new(100, "fee")], +// }) +// ); +// assert_eq!( +// res.messages[2].msg, +// cosmwasm_std::CosmosMsg::Bank(BankMsg::Send { +// to_address: "collector".to_string(), +// amount: vec![Coin::new(2, "in_denom")], +// }) +// ) +// } + +// #[test] +// fn test_threshold_not_reached() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_000_000); +// let end = Timestamp::from_seconds(5_000_000); +// let out_supply = Uint256::from(500u128); +// let out_denom = "out_denom"; +// let in_denom = "in_denom"; + +// // threshold = 500*0.5 / 1-0.01 =252.5 + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.height = 0; +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: in_denom.to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let info = mock_info( +// "creator", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// in_denom.to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// Some(500u128.into()), +// "v1".to_string(), +// ) +// .unwrap(); + +// // Subscription 1 +// let mut env = mock_env(); +// env.block.time = start; +// let funds = Coin::new(250, "in_denom"); +// let info = mock_info("subscriber", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: Some("operator".to_string()), +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); + +// // Subscription 2 +// let funds = Coin::new(1, "in_denom"); +// let info = mock_info("subscriber2", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: Some("operator".to_string()), +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); + +// // Set time to the end of the stream +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(1); + +// // Exit should not be possible +// let info = mock_info("subscriber", &[]); +// let res = execute_exit_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap_err(); +// assert_eq!( +// res, +// ContractError::ThresholdError(ThresholdError::ThresholdNotReached {}) +// ); + +// // Finalize should not be possible +// let info = mock_info("treasury", &[]); +// let res = +// execute_finalize_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap_err(); +// assert_eq!( +// res, +// ContractError::ThresholdError(ThresholdError::ThresholdNotReached {}) +// ); + +// // Subscriber one executes exit cancelled before creator cancels stream +// let info = mock_info("subscriber", &[]); +// let res = execute_exit_cancelled(deps.as_mut(), env.clone(), info, 1, None).unwrap(); +// assert_eq!( +// res.messages, +// vec![SubMsg::new(BankMsg::Send { +// to_address: "subscriber".to_string(), +// amount: vec![Coin::new(250, "in_denom")], +// })] +// ); +// // Creator threshold cancels the stream +// let info = mock_info("treasury", &[]); +// let res = +// execute_cancel_stream_with_threshold(deps.as_mut(), env.clone(), info, 1).unwrap(); +// assert_eq!( +// res.messages, +// vec![ +// // Out denom refunded +// SubMsg::new(BankMsg::Send { +// to_address: "treasury".to_string(), +// amount: vec![Coin::new(500, "out_denom")], +// }), +// ] +// ); +// // Creator can not finalize the stream +// let info = mock_info("treasury", &[]); +// let res = +// execute_finalize_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap_err(); +// assert_eq!(res, ContractError::StreamKillswitchActive {}); + +// // Creator can not cancel the stream again +// let info = mock_info("treasury", &[]); +// let res = execute_cancel_stream_with_threshold(deps.as_mut(), env.clone(), info, 1) +// .unwrap_err(); +// assert_eq!(res, ContractError::StreamKillswitchActive {}); + +// // Subscriber 2 executes exit cancelled after creator cancels stream +// let info = mock_info("subscriber2", &[]); +// let res = execute_exit_cancelled(deps.as_mut(), env.clone(), info, 1, None).unwrap(); +// assert_eq!( +// // In denom refunded +// res.messages, +// vec![SubMsg::new(BankMsg::Send { +// to_address: "subscriber2".to_string(), +// amount: vec![Coin::new(1, "in_denom")], +// })] +// ); +// } + +// #[test] +// fn test_threshold_cancel() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_000_000); +// let end = Timestamp::from_seconds(5_000_000); +// let out_supply = Uint256::from(500u128); +// let out_denom = "out_denom"; +// let in_denom = "in_denom"; + +// // threshold = 500*0.5 / 1-0.01 =252.5 + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: in_denom.to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let info = mock_info( +// "creator", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// in_denom.to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// Some(1_000u128.into()), +// "v1".to_string(), +// ) +// .unwrap(); + +// // Subscription 1 +// let mut env = mock_env(); +// env.block.time = start; +// let funds = Coin::new(250, "in_denom"); +// let info = mock_info("subscriber", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: Some("operator".to_string()), +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); + +// // Subscription 2 +// let funds = Coin::new(500, "in_denom"); +// let info = mock_info("subscriber2", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: Some("operator".to_string()), +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); +// // Can not cancel stream before it ends +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_000); +// let res = execute_cancel_stream_with_threshold( +// deps.as_mut(), +// env, +// mock_info("treasury", &[]), +// 1, +// ) +// .unwrap_err(); +// assert_eq!(res, ContractError::StreamNotEnded {}); + +// // Set block to the end of the stream +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(1); + +// // Non creator can't cancel stream +// let res = execute_cancel_stream_with_threshold( +// deps.as_mut(), +// env.clone(), +// mock_info("random", &[]), +// 1, +// ) +// .unwrap_err(); +// assert_eq!(res, ContractError::Unauthorized {}); + +// // Creator can cancel stream +// let _res = execute_cancel_stream_with_threshold( +// deps.as_mut(), +// env.clone(), +// mock_info("treasury", &[]), +// 1, +// ) +// .unwrap(); +// // Query stream should return stream with is_cancelled = true +// let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); +// assert_eq!(stream.status, Status::Cancelled); +// } +// } +// } From 0b040a5210aba3f57a22cda42af6ff29854724cd Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Thu, 21 Aug 2025 16:48:00 +0300 Subject: [PATCH 03/25] initiate test refactor --- src/tests.rs | 95 ++++++-- tests/create_stream.rs | 536 +++++++++++++++++++++++++++++++++++++++++ tests/helpers.rs | 175 ++++++++++++++ 3 files changed, 792 insertions(+), 14 deletions(-) create mode 100644 tests/create_stream.rs create mode 100644 tests/helpers.rs diff --git a/src/tests.rs b/src/tests.rs index 4cca12a..251f009 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -22,6 +22,76 @@ mod test_module { use std::ops::Sub; use std::str::FromStr; + // Test helpers: centralize defaults and builders for readability and reuse + mod test_helpers { + use cosmwasm_std::{testing::mock_env, Addr, Coin, MessageInfo, Uint256, Uint64}; + + pub const DEFAULT_FEE_COLLECTOR: &str = "collector"; + pub const DEFAULT_PROTOCOL_ADMIN: &str = "protocol_admin"; + pub const DEFAULT_ACCEPTED_IN_DENOM: &str = "in"; + pub const DEFAULT_STREAM_CREATION_DENOM: &str = "fee"; + + pub fn mock_info(sender: &str, funds: &[Coin]) -> MessageInfo { + MessageInfo { + sender: Addr::unchecked(sender), + funds: funds.to_vec(), + } + } + + pub fn env_now() -> cosmwasm_std::Env { + mock_env() + } + + pub struct InstantiateBuilder { + pub min_stream_seconds: Uint64, + pub min_seconds_until_start_time: Uint64, + pub stream_creation_denom: String, + pub stream_creation_fee: Uint256, + pub exit_fee_percent: cosmwasm_std::Decimal256, + pub fee_collector: String, + pub protocol_admin: String, + pub accepted_in_denom: String, + pub tos_version: String, + } + + impl Default for InstantiateBuilder { + fn default() -> Self { + Self { + min_stream_seconds: Uint64::new(1000), + min_seconds_until_start_time: Uint64::new(1000), + stream_creation_denom: DEFAULT_STREAM_CREATION_DENOM.to_string(), + stream_creation_fee: Uint256::from(100u128), + exit_fee_percent: cosmwasm_std::Decimal256::percent(1), + fee_collector: DEFAULT_FEE_COLLECTOR.to_string(), + protocol_admin: DEFAULT_PROTOCOL_ADMIN.to_string(), + accepted_in_denom: DEFAULT_ACCEPTED_IN_DENOM.to_string(), + tos_version: "v1".to_string(), + } + } + } + + impl InstantiateBuilder { + pub fn exit_fee_percent(mut self, pct: cosmwasm_std::Decimal256) -> Self { + self.exit_fee_percent = pct; + self + } + + pub fn build(self) -> crate::msg::InstantiateMsg { + crate::msg::InstantiateMsg { + min_stream_seconds: self.min_stream_seconds, + min_seconds_until_start_time: self.min_seconds_until_start_time, + stream_creation_denom: self.stream_creation_denom, + stream_creation_fee: self.stream_creation_fee, + exit_fee_percent: self.exit_fee_percent, + fee_collector: self.fee_collector, + protocol_admin: self.protocol_admin, + accepted_in_denom: self.accepted_in_denom, + tos_version: self.tos_version, + } + } + } + } + fn mock_info(sender: &str, funds: &[Coin]) -> MessageInfo { MessageInfo { sender: Addr::unchecked(sender), @@ -72,20 +142,17 @@ mod test_module { #[test] fn test_create_stream() { let mut deps = mock_dependencies(); - // Invalid exit fee - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(1000), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint256::from(100u128), - exit_fee_percent: Decimal256::percent(101), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - let err = - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap_err(); + // Invalid exit fee (refactored to use helpers) + let msg = test_helpers::InstantiateBuilder::default() + .exit_fee_percent(Decimal256::percent(101)) + .build(); + let err = instantiate( + deps.as_mut(), + test_helpers::env_now(), + test_helpers::mock_info("creator", &[]), + msg, + ) + .unwrap_err(); assert_eq!(err, ContractError::InvalidExitFeePercent {}); // Invalid stream creation fee diff --git a/tests/create_stream.rs b/tests/create_stream.rs new file mode 100644 index 0000000..468d85a --- /dev/null +++ b/tests/create_stream.rs @@ -0,0 +1,536 @@ +use cosmwasm_std::testing::mock_dependencies; +use cosmwasm_std::Timestamp; +use cosmwasm_std::{Coin, Decimal256, Uint256}; +use cw_streamswap::{ + contract::{execute_create_stream, instantiate}, + ContractError, +}; +mod helpers; + +#[test] +fn instantiate_invalid_exit_fee_percent() { + let mut deps = mock_dependencies(); + let msg = helpers::InstantiateBuilder::default() + .exit_fee_percent(Decimal256::percent(101)) + .build(); + let err = instantiate( + deps.as_mut(), + helpers::env_now(), + helpers::mock_info("creator", &[]), + msg, + ) + .unwrap_err(); + assert_eq!(err, ContractError::InvalidExitFeePercent {}); +} + +#[test] +fn instantiate_invalid_stream_creation_fee() { + let mut deps = mock_dependencies(); + let msg = helpers::InstantiateBuilder::default() + .stream_creation_fee(Uint256::zero()) + .build(); + let err = instantiate( + deps.as_mut(), + helpers::env_now(), + helpers::mock_info("creator", &[]), + msg, + ) + .unwrap_err(); + assert_eq!(err, ContractError::InvalidStreamCreationFee {}); +} + +#[test] +fn create_stream_in_denom_not_accepted() { + let mut deps = mock_dependencies(); + // set up config + helpers::instantiate_defaults(deps.as_mut()); + + // prepare stream args with wrong in_denom + let b = helpers::CreateStreamBuilder::default().in_denom("random"); + let env = helpers::env_at(0); + let info = helpers::mock_info("creator", &[]); + let res = execute_create_stream( + deps.as_mut(), + env, + info, + b.treasury, + b.name, + b.url, + b.in_denom, + b.out_denom, + b.out_supply, + b.start_time, + b.end_time, + b.threshold, + b.tos_version, + ); + assert_eq!(res.unwrap_err(), ContractError::InDenomIsNotAccepted {}); +} + +#[test] +fn create_stream_end_before_start() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(10_000)) + .end_time(Timestamp::from_seconds(5_000)); + let env = helpers::env_at(0); + let info = helpers::mock_info("creator", &[]); + let res = execute_create_stream( + deps.as_mut(), + env, + info, + b.treasury, + b.name, + b.url, + b.in_denom, + b.out_denom, + b.out_supply, + b.start_time, + b.end_time, + b.threshold, + b.tos_version, + ); + assert_eq!(res.unwrap_err(), ContractError::StreamInvalidEndTime {}); +} + +#[test] +fn create_stream_start_in_past() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(5_000)) + .end_time(Timestamp::from_seconds(10_000)); + let env = helpers::env_at(20_000); + let info = helpers::mock_info("creator", &[]); + let res = execute_create_stream( + deps.as_mut(), + env, + info, + b.treasury, + b.name, + b.url, + b.in_denom, + b.out_denom, + b.out_supply, + b.start_time, + b.end_time, + b.threshold, + b.tos_version, + ); + assert_eq!(res.unwrap_err(), ContractError::StreamInvalidStartTime {}); +} + +#[test] +fn create_stream_duration_too_short() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // default min_stream_seconds is 1000; set duration < 1000 + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(5_000)) + .end_time(Timestamp::from_seconds(5_500)); + let env = helpers::env_at(0); + let info = helpers::mock_info("creator", &[]); + let res = execute_create_stream( + deps.as_mut(), + env, + info, + b.treasury, + b.name, + b.url, + b.in_denom, + b.out_denom, + b.out_supply, + b.start_time, + b.end_time, + b.threshold, + b.tos_version, + ); + assert_eq!(res.unwrap_err(), ContractError::StreamDurationTooShort {}); +} + +#[test] +fn create_stream_starts_too_soon() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // default min_seconds_until_start_time is 1000; set start - now < 1000 + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(5_500)) + .end_time(Timestamp::from_seconds(7_000)); + let env = helpers::env_at(5_000); + let info = helpers::mock_info("creator", &[]); + let res = execute_create_stream( + deps.as_mut(), + env, + info, + b.treasury, + b.name, + b.url, + b.in_denom, + b.out_denom, + b.out_supply, + b.start_time, + b.end_time, + b.threshold, + b.tos_version, + ); + assert_eq!(res.unwrap_err(), ContractError::StreamStartsTooSoon {}); +} + +#[test] +fn create_stream_same_denom_each_side() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + let b = helpers::CreateStreamBuilder::default() + .in_denom(helpers::DEFAULT_ACCEPTED_IN_DENOM) + .out_denom(helpers::DEFAULT_ACCEPTED_IN_DENOM); + let env = helpers::env_at(0); + let info = helpers::mock_info("creator", &[]); + let res = execute_create_stream( + deps.as_mut(), + env, + info, + b.treasury, + b.name, + b.url, + b.in_denom, + b.out_denom, + b.out_supply, + b.start_time, + b.end_time, + b.threshold, + b.tos_version, + ); + assert_eq!(res.unwrap_err(), ContractError::SameDenomOnEachSide {}); +} + +#[test] +fn create_stream_zero_out_supply() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + let b = helpers::CreateStreamBuilder::default().out_supply(Uint256::zero()); + let env = helpers::env_at(0); + let info = helpers::mock_info("creator", &[]); + let res = execute_create_stream( + deps.as_mut(), + env, + info, + b.treasury, + b.name, + b.url, + b.in_denom, + b.out_denom, + b.out_supply, + b.start_time, + b.end_time, + b.threshold, + b.tos_version, + ); + assert_eq!(res.unwrap_err(), ContractError::ZeroOutSupply {}); +} + +#[test] +fn create_stream_invalid_tos_version() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + let b = helpers::CreateStreamBuilder::default().tos_version("v2"); + let env = helpers::env_at(0); + let info = helpers::mock_info("creator", &[]); + let res = execute_create_stream( + deps.as_mut(), + env, + info, + b.treasury, + b.name, + b.url, + b.in_denom, + b.out_denom, + b.out_supply, + b.start_time, + b.end_time, + b.threshold, + b.tos_version, + ); + assert_eq!(res.unwrap_err(), ContractError::InvalidToSVersion {}); +} + +#[test] +fn create_stream_else_branch_missing_out_funds() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // out_denom != fee, but provide no funds + let b = helpers::CreateStreamBuilder::default().out_denom("token"); + let env = helpers::env_at(0); + let info = helpers::mock_info("creator", &[]); + let res = execute_create_stream( + deps.as_mut(), + env, + info, + b.treasury, + b.name, + b.url, + b.in_denom, + b.out_denom, + b.out_supply, + b.start_time, + b.end_time, + b.threshold, + b.tos_version, + ); + assert_eq!(res.unwrap_err(), ContractError::NoFundsSent {}); +} + +#[test] +fn create_stream_else_branch_wrong_out_amount() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + let b = helpers::CreateStreamBuilder::default().out_denom("token"); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "token".to_string(), + amount: b.out_supply + Uint256::from(1u128), + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + let res = execute_create_stream( + deps.as_mut(), + env, + info, + b.treasury, + b.name, + b.url, + b.in_denom, + b.out_denom, + b.out_supply, + b.start_time, + b.end_time, + b.threshold, + b.tos_version, + ); + assert_eq!( + res.unwrap_err(), + ContractError::StreamOutSupplyFundsRequired {} + ); +} + +#[test] +fn create_stream_else_branch_missing_creation_fee() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + let b = helpers::CreateStreamBuilder::default().out_denom("token"); + let env = helpers::env_at(0); + let funds = vec![Coin { + denom: "token".to_string(), + amount: b.out_supply, + }]; + let info = helpers::mock_info("creator", &funds); + let res = execute_create_stream( + deps.as_mut(), + env, + info, + b.treasury, + b.name, + b.url, + b.in_denom, + b.out_denom, + b.out_supply, + b.start_time, + b.end_time, + b.threshold, + b.tos_version, + ); + assert_eq!(res.unwrap_err(), ContractError::NoFundsSent {}); +} + +#[test] +fn create_stream_else_branch_wrong_creation_fee_amount() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + let b = helpers::CreateStreamBuilder::default().out_denom("token"); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "token".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(99u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + let res = execute_create_stream( + deps.as_mut(), + env, + info, + b.treasury, + b.name, + b.url, + b.in_denom, + b.out_denom, + b.out_supply, + b.start_time, + b.end_time, + b.threshold, + b.tos_version, + ); + assert_eq!( + res.unwrap_err(), + ContractError::StreamCreationFeeRequired {} + ); +} + +#[test] +fn create_stream_else_branch_invalid_extra_funds() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + let b = helpers::CreateStreamBuilder::default().out_denom("token"); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "token".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + Coin { + denom: "random".to_string(), + amount: Uint256::from(1u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + let res = execute_create_stream( + deps.as_mut(), + env, + info, + b.treasury, + b.name, + b.url, + b.in_denom, + b.out_denom, + b.out_supply, + b.start_time, + b.end_time, + b.threshold, + b.tos_version, + ); + assert_eq!(res.unwrap_err(), ContractError::InvalidFunds {}); +} + +#[test] +fn create_stream_fee_branch_missing_funds() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // out_denom equals fee denom; expect NoFundsSent without funds + let b = + helpers::CreateStreamBuilder::default().out_denom(helpers::DEFAULT_STREAM_CREATION_DENOM); + let env = helpers::env_at(0); + let info = helpers::mock_info("creator", &[]); + let res = execute_create_stream( + deps.as_mut(), + env, + info, + b.treasury, + b.name, + b.url, + b.in_denom, + b.out_denom, + b.out_supply, + b.start_time, + b.end_time, + b.threshold, + b.tos_version, + ); + assert_eq!(res.unwrap_err(), ContractError::NoFundsSent {}); +} + +#[test] +fn create_stream_fee_branch_wrong_total_amount() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + let b = + helpers::CreateStreamBuilder::default().out_denom(helpers::DEFAULT_STREAM_CREATION_DENOM); + let env = helpers::env_at(0); + let funds = vec![Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: b.out_supply + Uint256::from(99u128), + }]; + let info = helpers::mock_info("creator", &funds); + let res = execute_create_stream( + deps.as_mut(), + env, + info, + b.treasury, + b.name, + b.url, + b.in_denom, + b.out_denom, + b.out_supply, + b.start_time, + b.end_time, + b.threshold, + b.tos_version, + ); + assert_eq!( + res.unwrap_err(), + ContractError::StreamOutSupplyFundsRequired {} + ); +} + +#[test] +fn create_stream_fee_branch_invalid_extra_funds() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + let b = + helpers::CreateStreamBuilder::default().out_denom(helpers::DEFAULT_STREAM_CREATION_DENOM); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: b.out_supply + Uint256::from(100u128), + }, + Coin { + denom: "random".to_string(), + amount: Uint256::from(1u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + let res = execute_create_stream( + deps.as_mut(), + env, + info, + b.treasury, + b.name, + b.url, + b.in_denom, + b.out_denom, + b.out_supply, + b.start_time, + b.end_time, + b.threshold, + b.tos_version, + ); + assert_eq!(res.unwrap_err(), ContractError::InvalidFunds {}); +} diff --git a/tests/helpers.rs b/tests/helpers.rs new file mode 100644 index 0000000..31553c7 --- /dev/null +++ b/tests/helpers.rs @@ -0,0 +1,175 @@ +use cosmwasm_std::testing::{mock_env, MOCK_CONTRACT_ADDR}; +use cosmwasm_std::{Addr, Coin, Decimal256, DepsMut, MessageInfo, Timestamp, Uint256, Uint64}; + +use cw_streamswap::{contract::instantiate, msg::InstantiateMsg}; + +fn valid_addr() -> String { + MOCK_CONTRACT_ADDR.to_string() +} + +pub const DEFAULT_ACCEPTED_IN_DENOM: &str = "in"; +pub const DEFAULT_STREAM_CREATION_DENOM: &str = "fee"; + +pub fn mock_info(sender: &str, funds: &[Coin]) -> MessageInfo { + MessageInfo { + sender: Addr::unchecked(sender), + funds: funds.to_vec(), + } +} + +pub fn env_now() -> cosmwasm_std::Env { + mock_env() +} + +pub fn env_at(seconds: u64) -> cosmwasm_std::Env { + let mut env = mock_env(); + env.block.time = Timestamp::from_seconds(seconds); + env +} + +pub struct InstantiateBuilder { + pub min_stream_seconds: Uint64, + pub min_seconds_until_start_time: Uint64, + pub stream_creation_denom: String, + pub stream_creation_fee: Uint256, + pub exit_fee_percent: Decimal256, + pub fee_collector: String, + pub protocol_admin: String, + pub accepted_in_denom: String, + pub tos_version: String, +} + +impl Default for InstantiateBuilder { + fn default() -> Self { + Self { + min_stream_seconds: Uint64::new(1000), + min_seconds_until_start_time: Uint64::new(1000), + stream_creation_denom: DEFAULT_STREAM_CREATION_DENOM.to_string(), + stream_creation_fee: Uint256::from(100u128), + exit_fee_percent: Decimal256::percent(1), + fee_collector: valid_addr(), + protocol_admin: valid_addr(), + accepted_in_denom: DEFAULT_ACCEPTED_IN_DENOM.to_string(), + tos_version: "v1".to_string(), + } + } +} + +impl InstantiateBuilder { + pub fn exit_fee_percent(mut self, pct: Decimal256) -> Self { + self.exit_fee_percent = pct; + self + } + + pub fn stream_creation_fee(mut self, fee: Uint256) -> Self { + self.stream_creation_fee = fee; + self + } + + pub fn build(self) -> InstantiateMsg { + InstantiateMsg { + min_stream_seconds: self.min_stream_seconds, + min_seconds_until_start_time: self.min_seconds_until_start_time, + stream_creation_denom: self.stream_creation_denom, + stream_creation_fee: self.stream_creation_fee, + exit_fee_percent: self.exit_fee_percent, + fee_collector: self.fee_collector, + protocol_admin: self.protocol_admin, + accepted_in_denom: self.accepted_in_denom, + tos_version: self.tos_version, + } + } +} + +pub fn instantiate_defaults(deps: DepsMut) { + let msg = InstantiateBuilder::default().build(); + instantiate(deps, env_now(), mock_info("creator", &[]), msg).unwrap(); +} + +pub struct CreateStreamBuilder { + pub treasury: String, + pub name: String, + pub url: Option, + pub in_denom: String, + pub out_denom: String, + pub out_supply: Uint256, + pub start_time: Timestamp, + pub end_time: Timestamp, + pub threshold: Option, + pub tos_version: String, +} + +impl Default for CreateStreamBuilder { + fn default() -> Self { + Self { + treasury: valid_addr(), + name: "name".to_string(), + url: Some("https://sample.url".to_string()), + in_denom: DEFAULT_ACCEPTED_IN_DENOM.to_string(), + out_denom: "out_denom".to_string(), + out_supply: Uint256::from(1u128), + start_time: Timestamp::from_seconds(5_000), + end_time: Timestamp::from_seconds(10_000), + threshold: None, + tos_version: "v1".to_string(), + } + } +} + +impl CreateStreamBuilder { + pub fn in_denom(mut self, denom: &str) -> Self { + self.in_denom = denom.to_string(); + self + } + pub fn out_denom(mut self, denom: &str) -> Self { + self.out_denom = denom.to_string(); + self + } + pub fn out_supply(mut self, supply: Uint256) -> Self { + self.out_supply = supply; + self + } + pub fn start_time(mut self, t: Timestamp) -> Self { + self.start_time = t; + self + } + pub fn end_time(mut self, t: Timestamp) -> Self { + self.end_time = t; + self + } + pub fn name(mut self, n: &str) -> Self { + self.name = n.to_string(); + self + } + pub fn url(mut self, u: Option<&str>) -> Self { + self.url = u.map(|s| s.to_string()); + self + } + pub fn tos_version(mut self, v: &str) -> Self { + self.tos_version = v.to_string(); + self + } +} + +#[allow(dead_code)] +pub fn valid_funds_for(builder: &CreateStreamBuilder) -> Vec { + if builder.out_denom == DEFAULT_STREAM_CREATION_DENOM { + // require out_denom funds equal to out_supply + creation_fee + let total = builder.out_supply + Uint256::from(100u128); + vec![Coin { + denom: builder.out_denom.clone(), + amount: total, + }] + } else { + vec![ + Coin { + denom: builder.out_denom.clone(), + amount: builder.out_supply, + }, + Coin { + denom: DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ] + } +} From 7b966d8ca3443d4715ca6ae6a53f7cbd7be2b299 Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Sun, 24 Aug 2025 20:32:40 +0300 Subject: [PATCH 04/25] add create stream test --- src/lib.rs | 2 - tests/create_stream.rs | 420 ++++++++++++++++++----------------------- tests/helpers.rs | 19 ++ 3 files changed, 198 insertions(+), 243 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index cf9d3a1..df90bfa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,4 @@ mod killswitch; mod migrate_v0_1_4; pub mod msg; pub mod state; -#[cfg(test)] -mod tests; pub mod threshold; diff --git a/tests/create_stream.rs b/tests/create_stream.rs index 468d85a..0764edc 100644 --- a/tests/create_stream.rs +++ b/tests/create_stream.rs @@ -2,7 +2,7 @@ use cosmwasm_std::testing::mock_dependencies; use cosmwasm_std::Timestamp; use cosmwasm_std::{Coin, Decimal256, Uint256}; use cw_streamswap::{ - contract::{execute_create_stream, instantiate}, + contract::{execute, instantiate}, ContractError, }; mod helpers; @@ -49,21 +49,7 @@ fn create_stream_in_denom_not_accepted() { let b = helpers::CreateStreamBuilder::default().in_denom("random"); let env = helpers::env_at(0); let info = helpers::mock_info("creator", &[]); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - b.treasury, - b.name, - b.url, - b.in_denom, - b.out_denom, - b.out_supply, - b.start_time, - b.end_time, - b.threshold, - b.tos_version, - ); + let res = execute(deps.as_mut(), env, info, b.build()); assert_eq!(res.unwrap_err(), ContractError::InDenomIsNotAccepted {}); } @@ -77,21 +63,7 @@ fn create_stream_end_before_start() { .end_time(Timestamp::from_seconds(5_000)); let env = helpers::env_at(0); let info = helpers::mock_info("creator", &[]); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - b.treasury, - b.name, - b.url, - b.in_denom, - b.out_denom, - b.out_supply, - b.start_time, - b.end_time, - b.threshold, - b.tos_version, - ); + let res = execute(deps.as_mut(), env, info, b.build()); assert_eq!(res.unwrap_err(), ContractError::StreamInvalidEndTime {}); } @@ -105,21 +77,7 @@ fn create_stream_start_in_past() { .end_time(Timestamp::from_seconds(10_000)); let env = helpers::env_at(20_000); let info = helpers::mock_info("creator", &[]); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - b.treasury, - b.name, - b.url, - b.in_denom, - b.out_denom, - b.out_supply, - b.start_time, - b.end_time, - b.threshold, - b.tos_version, - ); + let res = execute(deps.as_mut(), env, info, b.build()); assert_eq!(res.unwrap_err(), ContractError::StreamInvalidStartTime {}); } @@ -134,21 +92,7 @@ fn create_stream_duration_too_short() { .end_time(Timestamp::from_seconds(5_500)); let env = helpers::env_at(0); let info = helpers::mock_info("creator", &[]); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - b.treasury, - b.name, - b.url, - b.in_denom, - b.out_denom, - b.out_supply, - b.start_time, - b.end_time, - b.threshold, - b.tos_version, - ); + let res = execute(deps.as_mut(), env, info, b.build()); assert_eq!(res.unwrap_err(), ContractError::StreamDurationTooShort {}); } @@ -163,21 +107,7 @@ fn create_stream_starts_too_soon() { .end_time(Timestamp::from_seconds(7_000)); let env = helpers::env_at(5_000); let info = helpers::mock_info("creator", &[]); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - b.treasury, - b.name, - b.url, - b.in_denom, - b.out_denom, - b.out_supply, - b.start_time, - b.end_time, - b.threshold, - b.tos_version, - ); + let res = execute(deps.as_mut(), env, info, b.build()); assert_eq!(res.unwrap_err(), ContractError::StreamStartsTooSoon {}); } @@ -191,21 +121,7 @@ fn create_stream_same_denom_each_side() { .out_denom(helpers::DEFAULT_ACCEPTED_IN_DENOM); let env = helpers::env_at(0); let info = helpers::mock_info("creator", &[]); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - b.treasury, - b.name, - b.url, - b.in_denom, - b.out_denom, - b.out_supply, - b.start_time, - b.end_time, - b.threshold, - b.tos_version, - ); + let res = execute(deps.as_mut(), env, info, b.build()); assert_eq!(res.unwrap_err(), ContractError::SameDenomOnEachSide {}); } @@ -217,21 +133,7 @@ fn create_stream_zero_out_supply() { let b = helpers::CreateStreamBuilder::default().out_supply(Uint256::zero()); let env = helpers::env_at(0); let info = helpers::mock_info("creator", &[]); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - b.treasury, - b.name, - b.url, - b.in_denom, - b.out_denom, - b.out_supply, - b.start_time, - b.end_time, - b.threshold, - b.tos_version, - ); + let res = execute(deps.as_mut(), env, info, b.build()); assert_eq!(res.unwrap_err(), ContractError::ZeroOutSupply {}); } @@ -243,21 +145,7 @@ fn create_stream_invalid_tos_version() { let b = helpers::CreateStreamBuilder::default().tos_version("v2"); let env = helpers::env_at(0); let info = helpers::mock_info("creator", &[]); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - b.treasury, - b.name, - b.url, - b.in_denom, - b.out_denom, - b.out_supply, - b.start_time, - b.end_time, - b.threshold, - b.tos_version, - ); + let res = execute(deps.as_mut(), env, info, b.build()); assert_eq!(res.unwrap_err(), ContractError::InvalidToSVersion {}); } @@ -270,21 +158,7 @@ fn create_stream_else_branch_missing_out_funds() { let b = helpers::CreateStreamBuilder::default().out_denom("token"); let env = helpers::env_at(0); let info = helpers::mock_info("creator", &[]); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - b.treasury, - b.name, - b.url, - b.in_denom, - b.out_denom, - b.out_supply, - b.start_time, - b.end_time, - b.threshold, - b.tos_version, - ); + let res = execute(deps.as_mut(), env, info, b.build()); assert_eq!(res.unwrap_err(), ContractError::NoFundsSent {}); } @@ -306,21 +180,7 @@ fn create_stream_else_branch_wrong_out_amount() { }, ]; let info = helpers::mock_info("creator", &funds); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - b.treasury, - b.name, - b.url, - b.in_denom, - b.out_denom, - b.out_supply, - b.start_time, - b.end_time, - b.threshold, - b.tos_version, - ); + let res = execute(deps.as_mut(), env, info, b.build()); assert_eq!( res.unwrap_err(), ContractError::StreamOutSupplyFundsRequired {} @@ -339,21 +199,7 @@ fn create_stream_else_branch_missing_creation_fee() { amount: b.out_supply, }]; let info = helpers::mock_info("creator", &funds); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - b.treasury, - b.name, - b.url, - b.in_denom, - b.out_denom, - b.out_supply, - b.start_time, - b.end_time, - b.threshold, - b.tos_version, - ); + let res = execute(deps.as_mut(), env, info, b.build()); assert_eq!(res.unwrap_err(), ContractError::NoFundsSent {}); } @@ -375,21 +221,7 @@ fn create_stream_else_branch_wrong_creation_fee_amount() { }, ]; let info = helpers::mock_info("creator", &funds); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - b.treasury, - b.name, - b.url, - b.in_denom, - b.out_denom, - b.out_supply, - b.start_time, - b.end_time, - b.threshold, - b.tos_version, - ); + let res = execute(deps.as_mut(), env, info, b.build()); assert_eq!( res.unwrap_err(), ContractError::StreamCreationFeeRequired {} @@ -418,21 +250,7 @@ fn create_stream_else_branch_invalid_extra_funds() { }, ]; let info = helpers::mock_info("creator", &funds); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - b.treasury, - b.name, - b.url, - b.in_denom, - b.out_denom, - b.out_supply, - b.start_time, - b.end_time, - b.threshold, - b.tos_version, - ); + let res = execute(deps.as_mut(), env, info, b.build()); assert_eq!(res.unwrap_err(), ContractError::InvalidFunds {}); } @@ -446,21 +264,7 @@ fn create_stream_fee_branch_missing_funds() { helpers::CreateStreamBuilder::default().out_denom(helpers::DEFAULT_STREAM_CREATION_DENOM); let env = helpers::env_at(0); let info = helpers::mock_info("creator", &[]); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - b.treasury, - b.name, - b.url, - b.in_denom, - b.out_denom, - b.out_supply, - b.start_time, - b.end_time, - b.threshold, - b.tos_version, - ); + let res = execute(deps.as_mut(), env, info, b.build()); assert_eq!(res.unwrap_err(), ContractError::NoFundsSent {}); } @@ -477,21 +281,7 @@ fn create_stream_fee_branch_wrong_total_amount() { amount: b.out_supply + Uint256::from(99u128), }]; let info = helpers::mock_info("creator", &funds); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - b.treasury, - b.name, - b.url, - b.in_denom, - b.out_denom, - b.out_supply, - b.start_time, - b.end_time, - b.threshold, - b.tos_version, - ); + let res = execute(deps.as_mut(), env, info, b.build()); assert_eq!( res.unwrap_err(), ContractError::StreamOutSupplyFundsRequired {} @@ -517,20 +307,168 @@ fn create_stream_fee_branch_invalid_extra_funds() { }, ]; let info = helpers::mock_info("creator", &funds); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - b.treasury, - b.name, - b.url, - b.in_denom, - b.out_denom, - b.out_supply, - b.start_time, - b.end_time, - b.threshold, - b.tos_version, - ); + let res = execute(deps.as_mut(), env, info, b.build()); assert_eq!(res.unwrap_err(), ContractError::InvalidFunds {}); } + +#[test] +fn create_stream_threshold_zero() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + let b = helpers::CreateStreamBuilder::default().threshold(Some(Uint256::zero())); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + ]; + let info = helpers::mock_info("creator", &funds); + let res = execute(deps.as_mut(), env, info, b.build()); + assert_eq!( + res.unwrap_err(), + ContractError::ThresholdError(cw_streamswap::threshold::ThresholdError::ThresholdZero {}) + ); +} + +#[test] +fn create_stream_name_validation() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Test name too short (less than 2 characters) + let b = helpers::CreateStreamBuilder::default().name("n"); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + let res = execute(deps.as_mut(), env.clone(), info, b.build()); + assert_eq!(res.unwrap_err(), ContractError::StreamNameTooShort {}); + + // Test name too long (more than 64 characters) + let long_name = "12345678901234567890123456789012345678901234567890123456789012345"; + let b = helpers::CreateStreamBuilder::default().name(long_name); + let info = helpers::mock_info("creator", &funds); + let res = execute(deps.as_mut(), env.clone(), info, b.build()); + assert_eq!(res.unwrap_err(), ContractError::StreamNameTooLong {}); + + // Test invalid characters in name + let b = helpers::CreateStreamBuilder::default().name("abc~ß"); + let info = helpers::mock_info("creator", &funds); + let res = execute(deps.as_mut(), env, info, b.build()); + assert_eq!(res.unwrap_err(), ContractError::InvalidStreamName {}); +} + +#[test] +fn create_stream_url_validation() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Test URL too short (less than 10 characters) + let b = helpers::CreateStreamBuilder::default().url(Some("https://a.b")); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + let res = execute(deps.as_mut(), env.clone(), info, b.build()); + assert_eq!(res.unwrap_err(), ContractError::StreamUrlTooShort {}); + + // Test URL too long (more than 128 characters) + let long_url = "https://abcdefghijklmnopqrstuvw.xyz/abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz/abcdefghijklmnopqrstuvwxyzabcdefghijklmn"; + let b = helpers::CreateStreamBuilder::default().url(Some(long_url)); + let info = helpers::mock_info("creator", &funds); + let res = execute(deps.as_mut(), env.clone(), info, b.build()); + assert_eq!(res.unwrap_err(), ContractError::StreamUrlTooLong {}); + + // Test invalid URL format (contains spaces) + let b = + helpers::CreateStreamBuilder::default().url(Some("https://abc defghijklmnopqrstuvw.xyz/")); + let info = helpers::mock_info("creator", &funds); + let res = execute(deps.as_mut(), env, info, b.build()); + assert_eq!(res.unwrap_err(), ContractError::InvalidStreamUrl {}); +} + +#[test] +fn create_stream_happy_path() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + let b = helpers::CreateStreamBuilder::default(); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + let res = execute(deps.as_mut(), env, info, b.build()); + + // Should succeed + assert!(res.is_ok()); + + // Verify the response has the expected attributes + let response = res.unwrap(); + assert_eq!(response.attributes[0].key, "action"); + assert_eq!(response.attributes[0].value, "create_stream"); + assert_eq!(response.attributes[1].key, "stream_id"); + assert_eq!(response.attributes[1].value, "1"); +} + +#[test] +fn create_stream_successful_with_threshold() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + let b = helpers::CreateStreamBuilder::default().threshold(Some(Uint256::from(1000u128))); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + let res = execute(deps.as_mut(), env, info, b.build()); + + // Should succeed + assert!(res.is_ok()); + + // Verify the response has the expected attributes + let response = res.unwrap(); + assert_eq!(response.attributes[0].key, "action"); + assert_eq!(response.attributes[0].value, "create_stream"); + assert_eq!(response.attributes[1].key, "stream_id"); + assert_eq!(response.attributes[1].value, "1"); + + // Stream created successfully with threshold +} diff --git a/tests/helpers.rs b/tests/helpers.rs index 31553c7..72d5207 100644 --- a/tests/helpers.rs +++ b/tests/helpers.rs @@ -149,6 +149,25 @@ impl CreateStreamBuilder { self.tos_version = v.to_string(); self } + pub fn threshold(mut self, t: Option) -> Self { + self.threshold = t; + self + } + + pub fn build(self) -> cw_streamswap::msg::ExecuteMsg { + cw_streamswap::msg::ExecuteMsg::CreateStream { + treasury: self.treasury, + name: self.name, + url: self.url, + in_denom: self.in_denom, + out_denom: self.out_denom, + out_supply: self.out_supply, + start_time: self.start_time, + end_time: self.end_time, + threshold: self.threshold, + tos_version: self.tos_version, + } + } } #[allow(dead_code)] From ebcdd6337326cbe105e5d383148f579b0b776da3 Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Sun, 24 Aug 2025 21:47:40 +0300 Subject: [PATCH 05/25] address generation --- Cargo.lock | 456 +++++++++-------------------------------- Cargo.toml | 2 +- tests/create_stream.rs | 2 - tests/helpers.rs | 16 +- tests/subscribe.rs | 224 ++++++++++++++++++++ 5 files changed, 331 insertions(+), 369 deletions(-) create mode 100644 tests/subscribe.rs diff --git a/Cargo.lock b/Cargo.lock index 32da867..cb2be13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,17 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - [[package]] name = "ahash" version = "0.8.12" @@ -55,14 +44,14 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" dependencies = [ - "ahash 0.8.12", + "ahash", "ark-ff", "ark-poly", "ark-serialize", "ark-std", "educe", "fnv", - "hashbrown 0.15.5", + "hashbrown", "itertools 0.13.0", "num-bigint", "num-integer", @@ -82,7 +71,7 @@ dependencies = [ "ark-serialize", "ark-std", "arrayvec", - "digest 0.10.7", + "digest", "educe", "itertools 0.13.0", "num-bigint", @@ -99,7 +88,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -112,7 +101,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -121,13 +110,13 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" dependencies = [ - "ahash 0.8.12", + "ahash", "ark-ff", "ark-serialize", "ark-std", "educe", "fnv", - "hashbrown 0.15.5", + "hashbrown", ] [[package]] @@ -139,7 +128,7 @@ dependencies = [ "ark-serialize-derive", "ark-std", "arrayvec", - "digest 0.10.7", + "digest", "num-bigint", "rayon", ] @@ -152,7 +141,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -184,12 +173,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.1" @@ -202,27 +185,12 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" -[[package]] -name = "bech32" -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 = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - [[package]] name = "block-buffer" version = "0.10.3" @@ -232,12 +200,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "bnum" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56953345e39537a3e18bdaeba4cb0c58a78c1f61f361dc0fa7c5c7340ae87c5f" - [[package]] name = "bnum" version = "0.11.0" @@ -274,19 +236,6 @@ version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09f86359c565ec8dc6f1b39e51fff1f63f712767d77529ff46d0d466ea37cd34" -[[package]] -name = "cosmwasm-crypto" -version = "1.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c82e56962f0f18c9a292aa59940e03a82ce15ef79b93679d5838bb8143f0df" -dependencies = [ - "digest 0.10.7", - "ed25519-zebra 3.1.0", - "k256", - "rand_core 0.6.4", - "thiserror 1.0.60", -] - [[package]] name = "cosmwasm-crypto" version = "3.0.1" @@ -298,29 +247,20 @@ dependencies = [ "ark-ff", "ark-serialize", "cosmwasm-core", - "curve25519-dalek 4.1.3", - "digest 0.10.7", + "curve25519-dalek", + "digest", "ecdsa", - "ed25519-zebra 4.1.0", + "ed25519-zebra", "k256", "num-bigint", "num-traits", "p256", - "rand_core 0.6.4", + "rand_core", "rayon", - "sha2 0.10.9", + "sha2", "thiserror 1.0.60", ] -[[package]] -name = "cosmwasm-derive" -version = "1.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b804ff15a0e059c88f85ae0e868cf8c7aba9d61221e46f1ad7250f270628c7" -dependencies = [ - "syn 1.0.109", -] - [[package]] name = "cosmwasm-derive" version = "3.0.1" @@ -329,20 +269,7 @@ checksum = "58e3ed1b8f1b9df2a30c03b6c25177acdb15f5de0cfcd6ad708f0fbd9d9c7c91" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", -] - -[[package]] -name = "cosmwasm-schema" -version = "1.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5526ea839acb47bbf8fff031ed9aad86e74d43f77b089255417328c3664367d5" -dependencies = [ - "cosmwasm-schema-derive 1.5.11", - "schemars 0.8.22", - "serde", - "serde_json", - "thiserror 1.0.60", + "syn", ] [[package]] @@ -351,7 +278,7 @@ version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e476f2415bf6c582d3a6743de056d6a217933b753aa4994cdb9c6a89d09a97c" dependencies = [ - "cosmwasm-schema-derive 3.0.1", + "cosmwasm-schema-derive", "cw-schema", "schemars 0.8.22", "serde", @@ -359,17 +286,6 @@ dependencies = [ "thiserror 1.0.60", ] -[[package]] -name = "cosmwasm-schema-derive" -version = "1.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f41b99f41f840765d02ae858956bb52af910755976312082e90493c67db512" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "cosmwasm-schema-derive" version = "3.0.1" @@ -378,29 +294,7 @@ checksum = "e652d98c68f04e4f7e97b16009a94ccf550341b091213c15bbe3eb2d14139709" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", -] - -[[package]] -name = "cosmwasm-std" -version = "1.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763340055b84e5482ed90fec8194ff7d59112267a09bbf5819c9e3edca8c052e" -dependencies = [ - "base64 0.21.7", - "bech32 0.9.1", - "bnum 0.10.0", - "cosmwasm-crypto 1.5.11", - "cosmwasm-derive 1.5.11", - "derivative", - "forward_ref", - "hex", - "schemars 0.8.22", - "serde", - "serde-json-wasm", - "sha2 0.10.9", - "static_assertions", - "thiserror 1.0.60", + "syn", ] [[package]] @@ -409,21 +303,21 @@ version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c145f354e0dac3734bf62872ab8969e03bbd36b6c67e189cc7c15a8a47d19aab" dependencies = [ - "base64 0.22.1", - "bech32 0.11.0", - "bnum 0.11.0", + "base64", + "bech32", + "bnum", "cosmwasm-core", - "cosmwasm-crypto 3.0.1", - "cosmwasm-derive 3.0.1", + "cosmwasm-crypto", + "cosmwasm-derive", "cw-schema", "derive_more", "hex", - "rand_core 0.6.4", + "rand_core", "rmp-serde", "schemars 0.8.22", "serde", "serde_json", - "sha2 0.10.9", + "sha2", "static_assertions", "thiserror 1.0.60", ] @@ -469,7 +363,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", - "rand_core 0.6.4", + "rand_core", "subtle", "zeroize", ] @@ -484,19 +378,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "curve25519-dalek" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle", - "zeroize", -] - [[package]] name = "curve25519-dalek" version = "4.1.3" @@ -506,7 +387,7 @@ dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", - "digest 0.10.7", + "digest", "fiat-crypto", "rustc_version", "subtle", @@ -521,7 +402,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -530,10 +411,10 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8b9a060524f8c5bff8c736e890fb52a16533e7107c6c41e1cae6077c177516" dependencies = [ - "cosmwasm-schema 3.0.1", - "cosmwasm-std 3.0.1", - "cw-storage-plus 3.0.0", - "cw-utils 3.0.0", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", + "cw-utils", "schemars 0.8.22", "serde", "thiserror 2.0.16", @@ -541,22 +422,20 @@ dependencies = [ [[package]] name = "cw-multi-test" -version = "0.20.1" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc392a5cb7e778e3f90adbf7faa43c4db7f35b6623224b08886d796718edb875" +checksum = "cf9875e88f5b67dbaf729da99a7de4acd593d18d6d8ee83c8006e09dd865745e" dependencies = [ - "anyhow", - "bech32 0.9.1", - "cosmwasm-std 1.5.11", - "cw-storage-plus 1.2.0", - "cw-utils 1.0.3", - "derivative", - "itertools 0.12.1", + "bech32", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", + "cw-utils", + "itertools 0.14.0", "prost", "schemars 0.8.22", "serde", - "sha2 0.10.9", - "thiserror 1.0.60", + "sha2", ] [[package]] @@ -585,18 +464,7 @@ dependencies = [ "owo-colors", "proc-macro2", "quote", - "syn 2.0.106", -] - -[[package]] -name = "cw-storage-plus" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5ff29294ee99373e2cd5fd21786a3c0ced99a52fec2ca347d565489c61b723c" -dependencies = [ - "cosmwasm-std 1.5.11", - "schemars 0.8.22", - "serde", + "syn", ] [[package]] @@ -605,7 +473,7 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffe14a1c713486ec9f07059afcce49a9cd68657aaf2e2a736cd3bbf487de8e2" dependencies = [ - "cosmwasm-std 3.0.1", + "cosmwasm-std", "schemars 0.8.22", "serde", ] @@ -614,72 +482,42 @@ dependencies = [ name = "cw-streamswap" version = "0.1.5" dependencies = [ - "cosmwasm-schema 3.0.1", - "cosmwasm-std 3.0.1", + "cosmwasm-schema", + "cosmwasm-std", "cw-controllers", "cw-multi-test", - "cw-storage-plus 3.0.0", - "cw-utils 3.0.0", - "cw2 3.0.0", - "ed25519-zebra 4.1.0", + "cw-storage-plus", + "cw-utils", + "cw2", + "ed25519-zebra", "schemars 1.0.4", "semver", "serde", "thiserror 2.0.16", ] -[[package]] -name = "cw-utils" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c4a657e5caacc3a0d00ee96ca8618745d050b8f757c709babafb81208d4239c" -dependencies = [ - "cosmwasm-schema 1.5.11", - "cosmwasm-std 1.5.11", - "cw2 1.1.2", - "schemars 0.8.22", - "semver", - "serde", - "thiserror 1.0.60", -] - [[package]] name = "cw-utils" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8667e96f2c65cf7f4c6c66bfd6ee46909c40827bc1caea0409234e34f03cf061" dependencies = [ - "cosmwasm-schema 3.0.1", - "cosmwasm-std 3.0.1", + "cosmwasm-schema", + "cosmwasm-std", "schemars 0.8.22", "serde", "thiserror 2.0.16", ] -[[package]] -name = "cw2" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c120b24fbbf5c3bedebb97f2cc85fbfa1c3287e09223428e7e597b5293c1fa" -dependencies = [ - "cosmwasm-schema 1.5.11", - "cosmwasm-std 1.5.11", - "cw-storage-plus 1.2.0", - "schemars 0.8.22", - "semver", - "serde", - "thiserror 1.0.60", -] - [[package]] name = "cw2" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50582114ab739724c25a27cea1785351f2b5fea7968214302e0023ee2c0e8b39" dependencies = [ - "cosmwasm-schema 3.0.1", - "cosmwasm-std 3.0.1", - "cw-storage-plus 3.0.0", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", "schemars 0.8.22", "semver", "serde", @@ -707,7 +545,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.106", + "syn", ] [[package]] @@ -718,7 +556,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -732,17 +570,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "derive_more" version = "2.0.1" @@ -760,26 +587,17 @@ checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", "unicode-xid", ] -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.3", + "block-buffer", "const-oid", "crypto-common", "subtle", @@ -798,11 +616,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", - "digest 0.10.7", + "digest", "elliptic-curve", "rfc6979", "signature", - "spki", ] [[package]] @@ -816,34 +633,19 @@ dependencies = [ "signature", ] -[[package]] -name = "ed25519-zebra" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" -dependencies = [ - "curve25519-dalek 3.2.0", - "hashbrown 0.12.3", - "hex", - "rand_core 0.6.4", - "serde", - "sha2 0.9.9", - "zeroize", -] - [[package]] name = "ed25519-zebra" version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0017d969298eec91e3db7a2985a8cab4df6341d86e6f3a6f5878b13fb7846bc9" dependencies = [ - "curve25519-dalek 4.1.3", + "curve25519-dalek", "ed25519", - "hashbrown 0.15.5", + "hashbrown", "pkcs8", - "rand_core 0.6.4", + "rand_core", "serde", - "sha2 0.10.9", + "sha2", "subtle", "zeroize", ] @@ -857,7 +659,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -874,12 +676,11 @@ checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", - "digest 0.10.7", + "digest", "ff", "generic-array", "group", - "pkcs8", - "rand_core 0.6.4", + "rand_core", "sec1", "subtle", "zeroize", @@ -902,7 +703,7 @@ checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -917,7 +718,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ - "rand_core 0.6.4", + "rand_core", "subtle", ] @@ -939,12 +740,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" -[[package]] -name = "forward_ref" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" - [[package]] name = "generic-array" version = "0.14.6" @@ -974,19 +769,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", - "rand_core 0.6.4", + "rand_core", "subtle", ] -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] - [[package]] name = "hashbrown" version = "0.15.5" @@ -1022,7 +808,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.7", + "digest", ] [[package]] @@ -1038,7 +824,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", - "hashbrown 0.15.5", + "hashbrown", ] [[package]] @@ -1060,18 +846,18 @@ checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" [[package]] name = "itertools" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] [[package]] name = "itertools" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ "either", ] @@ -1091,9 +877,7 @@ dependencies = [ "cfg-if", "ecdsa", "elliptic-curve", - "once_cell", - "sha2 0.10.9", - "signature", + "sha2", ] [[package]] @@ -1142,12 +926,6 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - [[package]] name = "owo-colors" version = "4.2.2" @@ -1167,7 +945,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2 0.10.9", + "sha2", ] [[package]] @@ -1224,9 +1002,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.12.6" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" dependencies = [ "bytes", "prost-derive", @@ -1234,15 +1012,15 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.12.6" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -1261,7 +1039,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "rand_chacha", - "rand_core 0.6.4", + "rand_core", ] [[package]] @@ -1271,15 +1049,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", + "rand_core", ] -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" - [[package]] name = "rand_core" version = "0.6.4" @@ -1326,7 +1098,7 @@ checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -1410,7 +1182,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.106", + "syn", ] [[package]] @@ -1422,7 +1194,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.106", + "syn", ] [[package]] @@ -1434,7 +1206,6 @@ dependencies = [ "base16ct", "der", "generic-array", - "pkcs8", "subtle", "zeroize", ] @@ -1454,15 +1225,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-json-wasm" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e9213a07d53faa0b8dd81e767a54a8188a242fdb9be99ab75ec576a774bfdd7" -dependencies = [ - "serde", -] - [[package]] name = "serde_derive" version = "1.0.219" @@ -1471,7 +1233,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -1482,7 +1244,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -1517,20 +1279,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.106", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", + "syn", ] [[package]] @@ -1541,7 +1290,7 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.7", + "digest", ] [[package]] @@ -1550,8 +1299,8 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest 0.10.7", - "rand_core 0.6.4", + "digest", + "rand_core", ] [[package]] @@ -1607,17 +1356,6 @@ dependencies = [ "is_ci", ] -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.106" @@ -1655,7 +1393,7 @@ checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -1666,7 +1404,7 @@ checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -1795,7 +1533,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -1815,5 +1553,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] diff --git a/Cargo.toml b/Cargo.toml index 0ffb261..1c944b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,6 @@ ed25519-zebra = { version = "4.1.0", features = ["alloc"] } [dev-dependencies] -cw-multi-test = { version = "0.20.1", features = ["cosmwasm_1_2"] } +cw-multi-test = { version = "3.0.1", features = ["cosmwasm_1_2"] } diff --git a/tests/create_stream.rs b/tests/create_stream.rs index 0764edc..2ef716a 100644 --- a/tests/create_stream.rs +++ b/tests/create_stream.rs @@ -469,6 +469,4 @@ fn create_stream_successful_with_threshold() { assert_eq!(response.attributes[0].value, "create_stream"); assert_eq!(response.attributes[1].key, "stream_id"); assert_eq!(response.attributes[1].value, "1"); - - // Stream created successfully with threshold } diff --git a/tests/helpers.rs b/tests/helpers.rs index 72d5207..6f6394d 100644 --- a/tests/helpers.rs +++ b/tests/helpers.rs @@ -1,10 +1,12 @@ -use cosmwasm_std::testing::{mock_env, MOCK_CONTRACT_ADDR}; +use cosmwasm_std::testing::mock_env; use cosmwasm_std::{Addr, Coin, Decimal256, DepsMut, MessageInfo, Timestamp, Uint256, Uint64}; +use cw_multi_test::MockApiBech32; use cw_streamswap::{contract::instantiate, msg::InstantiateMsg}; -fn valid_addr() -> String { - MOCK_CONTRACT_ADDR.to_string() +fn valid_addr(name: &str) -> Addr { + let api = MockApiBech32::new("cosmwasm"); + api.addr_make(name) } pub const DEFAULT_ACCEPTED_IN_DENOM: &str = "in"; @@ -12,7 +14,7 @@ pub const DEFAULT_STREAM_CREATION_DENOM: &str = "fee"; pub fn mock_info(sender: &str, funds: &[Coin]) -> MessageInfo { MessageInfo { - sender: Addr::unchecked(sender), + sender: valid_addr(sender), funds: funds.to_vec(), } } @@ -47,8 +49,8 @@ impl Default for InstantiateBuilder { stream_creation_denom: DEFAULT_STREAM_CREATION_DENOM.to_string(), stream_creation_fee: Uint256::from(100u128), exit_fee_percent: Decimal256::percent(1), - fee_collector: valid_addr(), - protocol_admin: valid_addr(), + fee_collector: valid_addr("collector").to_string(), + protocol_admin: valid_addr("protocol_admin").to_string(), accepted_in_denom: DEFAULT_ACCEPTED_IN_DENOM.to_string(), tos_version: "v1".to_string(), } @@ -102,7 +104,7 @@ pub struct CreateStreamBuilder { impl Default for CreateStreamBuilder { fn default() -> Self { Self { - treasury: valid_addr(), + treasury: valid_addr("treasury").to_string(), name: "name".to_string(), url: Some("https://sample.url".to_string()), in_denom: DEFAULT_ACCEPTED_IN_DENOM.to_string(), diff --git a/tests/subscribe.rs b/tests/subscribe.rs new file mode 100644 index 0000000..3a44422 --- /dev/null +++ b/tests/subscribe.rs @@ -0,0 +1,224 @@ +use cosmwasm_std::testing::mock_dependencies; +use cosmwasm_std::Timestamp; +use cosmwasm_std::{Coin, Decimal256, Uint256}; +use cw_streamswap::{contract::execute, ContractError}; +use cw_utils::PaymentError; +mod helpers; + +#[test] +fn subscribe_stream_ended() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream first + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(2000)) + .end_time(Timestamp::from_seconds(3000)); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // Try to subscribe after stream has ended + let env = helpers::env_at(4000); // After end time + let info = helpers::mock_info("user1", &[Coin::new(1000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + + let res = execute(deps.as_mut(), env, info, msg); + assert_eq!(res.unwrap_err(), ContractError::StreamEnded {}); +} + +#[test] +fn subscribe_no_funds() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream first + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(2000)) + .end_time(Timestamp::from_seconds(10000)); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // Try to subscribe without funds + let env = helpers::env_at(2500); // During stream + let info = helpers::mock_info("user1", &[]); // No funds + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + + let res = execute(deps.as_mut(), env, info, msg); + assert_eq!(res.unwrap_err(), PaymentError::NoFunds {}.into()); +} + +#[test] +fn subscribe_incorrect_denom() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream first + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(2000)) + .end_time(Timestamp::from_seconds(10000)); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // Try to subscribe with wrong denom + let env = helpers::env_at(2500); // During stream + let info = helpers::mock_info("user1", &[Coin::new(1000u128, "wrong_denom")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + + let res = execute(deps.as_mut(), env, info, msg); + assert_eq!( + res.unwrap_err(), + PaymentError::MissingDenom("in".to_string()).into() + ); +} + +#[test] +fn subscribe_incorrect_tos_version() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream first + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(2000)) + .end_time(Timestamp::from_seconds(10000)); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // Try to subscribe with incorrect ToS version + let env = helpers::env_at(2500); // During stream + let info = helpers::mock_info("user1", &[Coin::new(1000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "random".to_string(), // Invalid ToS version + }; + + let res = execute(deps.as_mut(), env, info, msg); + assert_eq!(res.unwrap_err(), ContractError::InvalidToSVersion {}); +} + +#[test] +fn subscribe_first_subscription() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream first + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(2000)) + .end_time(Timestamp::from_seconds(10000)); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // First subscription + let env = helpers::env_at(2500); // During stream + let info = helpers::mock_info("user1", &[Coin::new(1000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + + let res = execute(deps.as_mut(), env, info.clone(), msg); + assert!(res.is_ok()); + + // Verify the response attributes + let response = res.unwrap(); + assert_eq!(response.attributes[0].key, "action"); + assert_eq!(response.attributes[0].value, "subscribe"); + + // Query stream to verify status changed to Active + let query_env = helpers::env_at(2500); + let stream = + cw_streamswap::contract::query_stream(deps.as_ref(), query_env.clone(), 1).unwrap(); + assert_eq!(stream.status, cw_streamswap::state::Status::Active); + assert_eq!(stream.in_supply, Uint256::from(1000u128)); + + // Verify distribution index is still 0 on first subscription (no distribution yet) + assert_eq!(stream.dist_index, Decimal256::zero()); + + // Query position to verify user details + let position = cw_streamswap::contract::query_position( + deps.as_ref(), + query_env, + 1, + info.sender.to_string(), + ) + .unwrap(); + assert_eq!(position.index, Decimal256::zero()); + assert_eq!(position.in_balance, Uint256::from(1000u128)); + assert_eq!(position.shares, Uint256::from(1000u128)); + assert_eq!(position.pending_purchase, Decimal256::zero()); + assert_eq!(position.purchased, Uint256::zero()); + assert_eq!(position.spent, Uint256::zero()); +} From e529d9671b6fb429cc547ffa4442b662a821bfcd Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Sun, 24 Aug 2025 22:01:02 +0300 Subject: [PATCH 06/25] subscription testing --- tests/helpers.rs | 2 +- tests/subscribe.rs | 75 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/tests/helpers.rs b/tests/helpers.rs index 6f6394d..65e7038 100644 --- a/tests/helpers.rs +++ b/tests/helpers.rs @@ -109,7 +109,7 @@ impl Default for CreateStreamBuilder { url: Some("https://sample.url".to_string()), in_denom: DEFAULT_ACCEPTED_IN_DENOM.to_string(), out_denom: "out_denom".to_string(), - out_supply: Uint256::from(1u128), + out_supply: Uint256::from(1_000_000u128), start_time: Timestamp::from_seconds(5_000), end_time: Timestamp::from_seconds(10_000), threshold: None, diff --git a/tests/subscribe.rs b/tests/subscribe.rs index 3a44422..a404900 100644 --- a/tests/subscribe.rs +++ b/tests/subscribe.rs @@ -222,3 +222,78 @@ fn subscribe_first_subscription() { assert_eq!(position.purchased, Uint256::zero()); assert_eq!(position.spent, Uint256::zero()); } + +#[test] +fn subscribe_increase_subscription() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream first + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(2000)) + .end_time(Timestamp::from_seconds(10000)); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // First subscription + let env = helpers::env_at(2500); // During stream + let info = helpers::mock_info("user1", &[Coin::new(1000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + + let res = execute(deps.as_mut(), env, info, msg); + assert!(res.is_ok()); + + // Query stream after first subscription + let query_env = helpers::env_at(2500); + let stream = + cw_streamswap::contract::query_stream(deps.as_ref(), query_env.clone(), 1).unwrap(); + assert_eq!(stream.dist_index, Decimal256::zero()); + assert_eq!(stream.in_supply, Uint256::from(1000u128)); + + // Second subscription (increase) by the same user + let env = helpers::env_at(3000); // Later during stream + let info = helpers::mock_info("user1", &[Coin::new(500u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + + let res = execute(deps.as_mut(), env, info.clone(), msg); + assert!(res.is_ok()); + + // Query stream after second subscription + let query_env = helpers::env_at(3000); + let stream = + cw_streamswap::contract::query_stream(deps.as_ref(), query_env.clone(), 1).unwrap(); + + // Distribution index should now be updated (non-zero) + assert!(stream.dist_index > Decimal256::zero()); + + // Query position to verify user's updated details + let position = cw_streamswap::contract::query_position( + deps.as_ref(), + query_env, + 1, + info.sender.to_string(), + ) + .unwrap(); + assert!(position.in_balance < Uint256::from(1500u128)); // Total balance minus spent between subscriptions +} From 260ba13d934ca47dcf7e177d744b7b63608160ba Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Mon, 25 Aug 2025 01:18:07 +0300 Subject: [PATCH 07/25] subscribe testing --- tests/subscribe.rs | 285 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 285 insertions(+) diff --git a/tests/subscribe.rs b/tests/subscribe.rs index a404900..972c6be 100644 --- a/tests/subscribe.rs +++ b/tests/subscribe.rs @@ -297,3 +297,288 @@ fn subscribe_increase_subscription() { .unwrap(); assert!(position.in_balance < Uint256::from(1500u128)); // Total balance minus spent between subscriptions } + +#[test] +fn subscribe_pending_stream() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream that hasn't started yet + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(5000)) // Start in the future + .end_time(Timestamp::from_seconds(10000)); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // Subscribe before stream starts (pending subscription) + // Note: Treasury cancel period is active from time 0 to 1000 (min_seconds_until_start_time) + // So we need to subscribe after time 1000 but before start time 5000 + let env = helpers::env_at(2000); // After cancel period, before start time + let info = helpers::mock_info("user1", &[Coin::new(1000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + + let res = execute(deps.as_mut(), env, info.clone(), msg); + assert!(res.is_ok()); + + let response = res.unwrap(); + assert_eq!(response.attributes[0].key, "action"); + assert_eq!(response.attributes[0].value, "subscribe_pending"); + + // Query stream to verify it's still in Waiting status + let query_env = helpers::env_at(2000); + let stream = + cw_streamswap::contract::query_stream(deps.as_ref(), query_env.clone(), 1).unwrap(); + assert_eq!(stream.status, cw_streamswap::state::Status::Waiting); + assert_eq!(stream.in_supply, Uint256::from(1000u128)); + assert_eq!(stream.shares, Uint256::from(1000u128)); + + // Query position to verify user details + let position = cw_streamswap::contract::query_position( + deps.as_ref(), + query_env, + 1, + info.sender.to_string(), + ) + .unwrap(); + assert_eq!(position.in_balance, Uint256::from(1000u128)); + assert_eq!(position.shares, Uint256::from(1000u128)); + assert_eq!(position.index, Decimal256::zero()); +} + +#[test] +fn subscribe_pending_treasury_cancel_period() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream that hasn't started yet + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(5000)) // Start in the future + .end_time(Timestamp::from_seconds(10000)); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // Try to subscribe during treasury cancel period (time 0 to 1000) + // This should fail with TreasuryCancelPeriodActive + let env = helpers::env_at(500); // During cancel period + let info = helpers::mock_info("user1", &[Coin::new(1000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + + let res = execute(deps.as_mut(), env, info, msg); + assert!(res.is_err()); + assert_eq!( + res.unwrap_err(), + cw_streamswap::ContractError::TreasuryCancelPeriodActive {} + ); + + // Verify the stream still has no subscriptions + let query_env = helpers::env_at(500); + let stream = cw_streamswap::contract::query_stream(deps.as_ref(), query_env, 1).unwrap(); + assert_eq!(stream.in_supply, Uint256::zero()); + assert_eq!(stream.shares, Uint256::zero()); +} + +#[test] +fn subscribe_pending_multiple() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream that hasn't started yet + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(5000)) // Start in the future + .end_time(Timestamp::from_seconds(10000)); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // First subscription after cancel period but before start time + let env = helpers::env_at(2000); // After cancel period (0-1000), before start (5000) + let info1 = helpers::mock_info("user1", &[Coin::new(1000u128, "in")]); + let msg1 = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + + let res1 = execute(deps.as_mut(), env, info1.clone(), msg1); + assert!(res1.is_ok()); + let response1 = res1.unwrap(); + assert_eq!(response1.attributes[0].key, "action"); + assert_eq!(response1.attributes[0].value, "subscribe_pending"); + + // Second subscription by different user while still waiting + let env = helpers::env_at(3000); // Still before start time + let info2 = helpers::mock_info("user2", &[Coin::new(500u128, "in")]); + let msg2 = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + + let res2 = execute(deps.as_mut(), env, info2.clone(), msg2); + assert!(res2.is_ok()); + let response2 = res2.unwrap(); + assert_eq!(response2.attributes[0].key, "action"); + assert_eq!(response2.attributes[0].value, "subscribe_pending"); + + // Query stream to verify both subscriptions are recorded + let query_env = helpers::env_at(3000); + let stream = + cw_streamswap::contract::query_stream(deps.as_ref(), query_env.clone(), 1).unwrap(); + assert_eq!(stream.status, cw_streamswap::state::Status::Waiting); + assert_eq!(stream.in_supply, Uint256::from(1500u128)); // 1000 + 500 + assert_eq!(stream.shares, Uint256::from(1500u128)); // 1000 + 500 + + // Query both positions to verify they're created correctly + let position1 = cw_streamswap::contract::query_position( + deps.as_ref(), + query_env.clone(), + 1, + info1.sender.to_string(), + ) + .unwrap(); + assert_eq!(position1.in_balance, Uint256::from(1000u128)); + assert_eq!(position1.shares, Uint256::from(1000u128)); + assert_eq!(position1.index, Decimal256::zero()); + + let position2 = cw_streamswap::contract::query_position( + deps.as_ref(), + query_env, + 1, + info2.sender.to_string(), + ) + .unwrap(); + assert_eq!(position2.in_balance, Uint256::from(500u128)); + assert_eq!(position2.shares, Uint256::from(500u128)); + assert_eq!(position2.index, Decimal256::zero()); +} + +#[test] +fn subscribe_pending_to_active_transition() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream that hasn't started yet + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(5000)) // Start in the future + .end_time(Timestamp::from_seconds(10000)); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // Subscribe before stream starts (pending subscription) + let env = helpers::env_at(2000); // After cancel period, before start time + let info = helpers::mock_info("user1", &[Coin::new(1000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + + let res = execute(deps.as_mut(), env, info.clone(), msg); + assert!(res.is_ok()); + + // Verify stream is in Waiting status after pending subscription + let query_env = helpers::env_at(2000); + let stream = cw_streamswap::contract::query_stream(deps.as_ref(), query_env, 1).unwrap(); + assert_eq!(stream.status, cw_streamswap::state::Status::Waiting); + + // Now subscribe after start time - this should trigger status change to Active + let env = helpers::env_at(5000); // After start time (5000) + let info2 = helpers::mock_info("user2", &[Coin::new(500u128, "in")]); + let msg2 = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + + let res2 = execute(deps.as_mut(), env, info2.clone(), msg2); + assert!(res2.is_ok()); + let response2 = res2.unwrap(); + assert_eq!(response2.attributes[0].key, "action"); + assert_eq!(response2.attributes[0].value, "subscribe"); // Should be "subscribe", not "subscribe_pending" + + // Verify stream status has changed to Active + let query_env = helpers::env_at(5000); + let stream = + cw_streamswap::contract::query_stream(deps.as_ref(), query_env.clone(), 1).unwrap(); + assert_eq!(stream.status, cw_streamswap::state::Status::Active); + assert_eq!(stream.in_supply, Uint256::from(1500u128)); // 1000 + 500 + assert_eq!(stream.shares, Uint256::from(1500u128)); // 1000 + 500 + + // Verify both positions exist + let position1 = cw_streamswap::contract::query_position( + deps.as_ref(), + query_env.clone(), + 1, + info.sender.to_string(), + ) + .unwrap(); + assert_eq!(position1.in_balance, Uint256::from(1000u128)); + assert_eq!(position1.shares, Uint256::from(1000u128)); + + let position2 = cw_streamswap::contract::query_position( + deps.as_ref(), + query_env, + 1, + info2.sender.to_string(), + ) + .unwrap(); + assert_eq!(position2.in_balance, Uint256::from(500u128)); + assert_eq!(position2.shares, Uint256::from(500u128)); +} From 773a1606f11527bacc68b5ce644efd17ac3aa906 Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Mon, 25 Aug 2025 01:47:38 +0300 Subject: [PATCH 08/25] subscribe pending --- src/contract.rs | 2 +- tests/subscribe.rs | 156 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 133 insertions(+), 25 deletions(-) diff --git a/src/contract.rs b/src/contract.rs index 7620691..b3bd0c9 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -630,7 +630,7 @@ pub fn update_position( position.pending_purchase = decimals; // floors the decimal points - purchased_uint128 = purchased.atomics(); + purchased_uint128 = purchased.to_uint_floor(); position.purchased = position.purchased.checked_add(purchased_uint128)?; } diff --git a/tests/subscribe.rs b/tests/subscribe.rs index 972c6be..5d0da23 100644 --- a/tests/subscribe.rs +++ b/tests/subscribe.rs @@ -306,7 +306,8 @@ fn subscribe_pending_stream() { // Create a stream that hasn't started yet let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(5000)) // Start in the future - .end_time(Timestamp::from_seconds(10000)); + .end_time(Timestamp::from_seconds(10000)) + .out_supply(Uint256::from(1_000_000u128)); let env = helpers::env_at(0); let funds = vec![ Coin { @@ -410,7 +411,7 @@ fn subscribe_pending_treasury_cancel_period() { } #[test] -fn subscribe_pending_multiple() { +fn subscribe_pending_multiple_with_transition() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); @@ -432,9 +433,9 @@ fn subscribe_pending_multiple() { let info = helpers::mock_info("creator", &funds); execute(deps.as_mut(), env, info, b.build()).unwrap(); - // First subscription after cancel period but before start time - let env = helpers::env_at(2000); // After cancel period (0-1000), before start (5000) - let info1 = helpers::mock_info("user1", &[Coin::new(1000u128, "in")]); + // First subscription before start time (pending) + let env = helpers::env_at(2000); // After cancel period, before start time + let info1 = helpers::mock_info("subscriber1", &[Coin::new(1000000u128, "in")]); let msg1 = cw_streamswap::msg::ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, @@ -448,9 +449,41 @@ fn subscribe_pending_multiple() { assert_eq!(response1.attributes[0].key, "action"); assert_eq!(response1.attributes[0].value, "subscribe_pending"); - // Second subscription by different user while still waiting + // Query stream after first subscription + let query_env = helpers::env_at(2000); + let stream = cw_streamswap::contract::query_stream(deps.as_ref(), query_env, 1).unwrap(); + assert_eq!(stream.status, cw_streamswap::state::Status::Waiting); + assert_eq!(stream.in_supply, Uint256::from(1000000u128)); + assert_eq!(stream.shares, Uint256::from(1000000u128)); + + // Second subscription still waiting (by same user) let env = helpers::env_at(3000); // Still before start time - let info2 = helpers::mock_info("user2", &[Coin::new(500u128, "in")]); + let info1_second = helpers::mock_info("subscriber1", &[Coin::new(1000000u128, "in")]); + let msg1_second = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + + let res1_second = execute(deps.as_mut(), env, info1_second.clone(), msg1_second); + assert!(res1_second.is_ok()); + let response1_second = res1_second.unwrap(); + assert_eq!(response1_second.attributes[0].key, "action"); + assert_eq!(response1_second.attributes[0].value, "subscribe_pending"); + + // Query stream after second subscription + let query_env = helpers::env_at(3000); + let stream = cw_streamswap::contract::query_stream(deps.as_ref(), query_env, 1).unwrap(); + assert_eq!(stream.status, cw_streamswap::state::Status::Waiting); + assert_eq!(stream.in_supply, Uint256::from(2000000u128)); // 1M + 1M + + // Before stream start time: 2 subscriptions made, stream is pending + // Subscriber1 has 2 subscriptions and 2_000_000 in balance + + // Third subscription after start time (stream becomes active) + let env = helpers::env_at(6000); // After start time (5000) + let info2 = helpers::mock_info("subscriber2", &[Coin::new(1000000u128, "in")]); let msg2 = cw_streamswap::msg::ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, @@ -462,38 +495,113 @@ fn subscribe_pending_multiple() { assert!(res2.is_ok()); let response2 = res2.unwrap(); assert_eq!(response2.attributes[0].key, "action"); - assert_eq!(response2.attributes[0].value, "subscribe_pending"); + // Different action because stream is now active + assert_eq!(response2.attributes[0].value, "subscribe"); - // Query stream to verify both subscriptions are recorded - let query_env = helpers::env_at(3000); + // Query stream after third subscription + let query_env = helpers::env_at(6000); let stream = cw_streamswap::contract::query_stream(deps.as_ref(), query_env.clone(), 1).unwrap(); - assert_eq!(stream.status, cw_streamswap::state::Status::Waiting); - assert_eq!(stream.in_supply, Uint256::from(1500u128)); // 1000 + 500 - assert_eq!(stream.shares, Uint256::from(1500u128)); // 1000 + 500 + assert_eq!(stream.status, cw_streamswap::state::Status::Active); + // update_stream ran at t=6000 before adding creator2's 1,000,000, spending 400,000 from 2,000,000 + // then +1,000,000 added → 1,600,000 + 1,000,000 = 2,600,000 + assert_eq!(stream.in_supply, Uint256::from(3_000_000u128 - 400_000u128)); + assert_eq!(stream.spent_in, Uint256::from(400_000u128)); - // Query both positions to verify they're created correctly + // Update creator1 position to calculate spent/purchased amounts + let update_msg = cw_streamswap::msg::ExecuteMsg::UpdatePosition { + stream_id: 1, + operator_target: None, + }; + let update_info = helpers::mock_info("subscriber1", &[]); + let update_env = helpers::env_at(6000); + let update_res = execute(deps.as_mut(), update_env, update_info.clone(), update_msg); + assert!(update_res.is_ok()); + + // Query subscriber1 position after update let position1 = cw_streamswap::contract::query_position( deps.as_ref(), query_env.clone(), 1, - info1.sender.to_string(), + update_info.sender.to_string(), ) .unwrap(); - assert_eq!(position1.in_balance, Uint256::from(1000u128)); - assert_eq!(position1.shares, Uint256::from(1000u128)); - assert_eq!(position1.index, Decimal256::zero()); - let position2 = cw_streamswap::contract::query_position( + // At 6000 seconds, subscriber1 should have spent 400,000 + assert_eq!(position1.spent, Uint256::from(400_000u128)); + + // Query stream to see updated state + let stream_after_update = + cw_streamswap::contract::query_stream(deps.as_ref(), query_env.clone(), 1).unwrap(); + assert_eq!( + stream_after_update.status, + cw_streamswap::state::Status::Active + ); + assert!(stream_after_update.spent_in > Uint256::zero()); + + // Update subscriber1 position at 7500 + let update_msg_7500 = cw_streamswap::msg::ExecuteMsg::UpdatePosition { + stream_id: 1, + operator_target: None, + }; + let update_info_7500 = helpers::mock_info("subscriber1", &[]); + let update_env_7500 = helpers::env_at(7500); + let update_res_7500 = execute( + deps.as_mut(), + update_env_7500, + update_info_7500.clone(), + update_msg_7500, + ); + assert!(update_res_7500.is_ok()); + + // Query position at 7500 for subscriber1 + let pos1_7500 = cw_streamswap::contract::query_position( deps.as_ref(), - query_env, + helpers::env_at(7500), 1, - info2.sender.to_string(), + update_info_7500.sender.to_string(), ) .unwrap(); - assert_eq!(position2.in_balance, Uint256::from(500u128)); - assert_eq!(position2.shares, Uint256::from(500u128)); - assert_eq!(position2.index, Decimal256::zero()); + assert_eq!( + pos1_7500.purchased, + Uint256::from(184_615u128 + 200_000u128) + ); + assert_eq!(pos1_7500.spent, Uint256::from(2_000_000u128 / 2u128)); + + // Update creator2 position at 3500 + let update_msg2 = cw_streamswap::msg::ExecuteMsg::UpdatePosition { + stream_id: 1, + operator_target: None, + }; + let update_info2 = helpers::mock_info("subscriber2", &[]); + let update_env2 = helpers::env_at(3500); + let update_res2 = execute( + deps.as_mut(), + update_env2, + update_info2.clone(), + update_msg2, + ); + assert!(update_res2.is_ok()); + + // Query position for creator2 at 3500 + let pos2_3500 = cw_streamswap::contract::query_position( + deps.as_ref(), + helpers::env_at(3500), + 1, + update_info2.sender.to_string(), + ) + .unwrap(); + assert_eq!(pos2_3500.purchased, Uint256::from(115_384u128)); + assert_eq!( + pos2_3500.spent, + Uint256::from(1_000_000u128 * 1_500u128 / 4_000u128) + ); + + // Query stream at 3500 + let stream_3500 = + cw_streamswap::contract::query_stream(deps.as_ref(), helpers::env_at(3500), 1).unwrap(); + assert_eq!(stream_3500.status, cw_streamswap::state::Status::Active); + assert_eq!(stream_3500.in_supply, Uint256::from(1_625_000u128)); } #[test] From d2c1c307814e93beebedf02ac4f21f46f2ea541d Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Mon, 25 Aug 2025 01:58:44 +0300 Subject: [PATCH 09/25] withdraw tests --- tests/withdraw.rs | 192 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 tests/withdraw.rs diff --git a/tests/withdraw.rs b/tests/withdraw.rs new file mode 100644 index 0000000..8923007 --- /dev/null +++ b/tests/withdraw.rs @@ -0,0 +1,192 @@ +use cosmwasm_std::testing::mock_dependencies; +use cosmwasm_std::{BankMsg, Coin, Decimal256, Uint256}; +use cosmwasm_std::{CosmosMsg, Timestamp}; +use cw_streamswap::contract::execute; +mod helpers; + +#[test] +fn withdraw_pending_basic_flow() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream that hasn't started yet + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(2000)) // Start in the future + .end_time(Timestamp::from_seconds(1_000_000)); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // First subscribe before start time (pending) + let env = helpers::env_at(2000 - 1); // Before start time + let info = helpers::mock_info("subscriber1", &[Coin::new(1_000_000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + let res = execute(deps.as_mut(), env, info, msg); + assert!(res.is_ok()); + + // Update subscriber1 position - no distribution expected + let env = helpers::env_at(2000 - 1); + let update_msg = cw_streamswap::msg::ExecuteMsg::UpdatePosition { + stream_id: 1, + operator_target: None, + }; + let update_info = helpers::mock_info("subscriber1", &[]); + let res = execute(deps.as_mut(), env.clone(), update_info, update_msg); + assert!(res.is_ok()); + + let response = res.unwrap(); + assert_eq!(response.attributes[0].key, "action"); + assert_eq!(response.attributes[0].value, "update_position"); + assert_eq!(response.attributes[1].key, "stream_id"); + assert_eq!(response.attributes[1].value, "1"); + assert_eq!(response.attributes[2].key, "in_balance"); + assert_eq!(response.attributes[2].value, "1000000"); + assert_eq!(response.attributes[3].key, "shares"); + assert_eq!(response.attributes[3].value, "1000000"); + assert_eq!(response.attributes[4].key, "index"); + assert_eq!(response.attributes[4].value, "0"); + assert_eq!(response.attributes[5].key, "last_updated"); + assert_eq!(response.attributes[5].value, "2000.000000000"); // start_time + assert_eq!(response.attributes[6].key, "pending_purchase"); + assert_eq!(response.attributes[6].value, "0"); + assert_eq!(response.attributes[7].key, "purchased"); + assert_eq!(response.attributes[7].value, "0"); + assert_eq!(response.attributes[8].key, "spent"); + assert_eq!(response.attributes[8].value, "0"); + + // Query stream before withdraw + let query_env = helpers::env_at(400); + let stream = cw_streamswap::contract::query_stream(deps.as_ref(), query_env, 1).unwrap(); + assert_eq!(stream.id, 1); + assert_eq!(stream.dist_index, Decimal256::zero()); + assert_eq!(stream.last_updated, Timestamp::from_seconds(2000)); + assert_eq!(stream.in_supply, Uint256::from(1_000_000u128)); + assert_eq!(stream.spent_in, Uint256::zero()); + assert_eq!(stream.shares, Uint256::from(1_000_000u128)); + + // Withdraw before start time (pending withdraw) + let env = helpers::env_at(2000 - 1); + let info = helpers::mock_info("subscriber1", &[]); + let msg = cw_streamswap::msg::ExecuteMsg::Withdraw { + stream_id: 1, + cap: Some(Uint256::from(500_000u128)), + operator_target: None, + }; + let res = execute(deps.as_mut(), env, info.clone(), msg); + assert!(res.is_ok()); + + let response = res.unwrap(); + assert_eq!(response.attributes[0].key, "action"); + assert_eq!(response.attributes[0].value, "withdraw_pending"); + assert_eq!(response.attributes[1].key, "stream_id"); + assert_eq!(response.attributes[1].value, "1"); + assert_eq!(response.attributes[3].key, "withdraw_amount"); + assert_eq!(response.attributes[3].value, "500000"); + + // Check bank message for withdrawal + assert_eq!(response.messages.len(), 1); + let bank_msg = &response.messages[0].msg; + assert_eq!( + bank_msg, + &CosmosMsg::Bank(BankMsg::Send { + to_address: info.sender.to_string(), + amount: vec![Coin::new(500000u128, "in")], + }) + ); + + // Query stream after withdraw + let query_env = helpers::env_at(2000 - 1); + let stream = cw_streamswap::contract::query_stream(deps.as_ref(), query_env, 1).unwrap(); + assert_eq!(stream.id, 1); + assert_eq!(stream.dist_index, Decimal256::zero()); + assert_eq!(stream.last_updated, Timestamp::from_seconds(2000)); + assert_eq!(stream.in_supply, Uint256::from(500_000u128)); // 1M - 500K + assert_eq!(stream.spent_in, Uint256::zero()); + assert_eq!(stream.shares, Uint256::from(500_000u128)); // 1M - 500K +} +#[test] +fn withdraw_after_start_time() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream that hasn't started yet + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(2000)) // Start in the future + .end_time(Timestamp::from_seconds(1_000_000)); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // Subscribe before start time + let env = helpers::env_at(2000 - 1); + let info = helpers::mock_info("subscriber1", &[Coin::new(1_000_000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + let res = execute(deps.as_mut(), env, info, msg); + assert!(res.is_ok()); + + // Withdraw after start time (active withdraw) + let env = helpers::env_at(3000); // After start time (2000) + let info = helpers::mock_info("subscriber1", &[]); + let msg = cw_streamswap::msg::ExecuteMsg::Withdraw { + stream_id: 1, + cap: Some(Uint256::from(400_000u128)), + operator_target: None, + }; + let res = execute(deps.as_mut(), env, info.clone(), msg); + assert!(res.is_ok()); + + let response = res.unwrap(); + assert_eq!(response.attributes[0].key, "action"); + assert_eq!(response.attributes[0].value, "withdraw"); + assert_eq!(response.attributes[1].key, "stream_id"); + assert_eq!(response.attributes[1].value, "1"); + assert_eq!(response.attributes[3].key, "withdraw_amount"); + assert_eq!(response.attributes[3].value, "400000"); + + // Check bank message for withdrawal + assert_eq!(response.messages.len(), 1); + let bank_msg = &response.messages[0].msg; + assert_eq!( + bank_msg, + &CosmosMsg::Bank(BankMsg::Send { + to_address: info.sender.to_string(), + amount: vec![Coin::new(400000u128, "in")], + }) + ); + + // Query stream after withdraw + let query_env = helpers::env_at(3000); + let stream = cw_streamswap::contract::query_stream(deps.as_ref(), query_env, 1).unwrap(); + // Stream should still exist and be active + assert_eq!(stream.id, 1); +} From 443174a04ce635227db7afd55a7779a892658165 Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Mon, 25 Aug 2025 02:11:47 +0300 Subject: [PATCH 10/25] update position tests --- tests/update_position.rs | 185 +++++++++++++++++++++++++++++++++++++++ tests/update_stream.rs | 143 ++++++++++++++++++++++++++++++ 2 files changed, 328 insertions(+) create mode 100644 tests/update_position.rs create mode 100644 tests/update_stream.rs diff --git a/tests/update_position.rs b/tests/update_position.rs new file mode 100644 index 0000000..0d136d3 --- /dev/null +++ b/tests/update_position.rs @@ -0,0 +1,185 @@ +use cosmwasm_std::testing::mock_dependencies; +use cosmwasm_std::{Coin, Decimal256, Timestamp, Uint256}; +use cw_streamswap::ContractError; +use cw_streamswap::{self, contract::execute}; +use std::str::FromStr; + +mod helpers; + +#[test] +fn update_position_unauthorized_operator() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(1_000_000)) + .end_time(Timestamp::from_seconds(5_000_000)); + let env = helpers::env_at(1); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // First subscription + let env = helpers::env_at(1_000_000 + 100); + let info = helpers::mock_info("creator1", &[Coin::new(1_000_000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + + // Non-owner cannot update position (should fail because random user has no position) + let env = helpers::env_at(1_000_000 + 3_000_000); + let info = helpers::mock_info("random", &[]); + let msg = cw_streamswap::msg::ExecuteMsg::UpdatePosition { + stream_id: 1, + operator_target: Some(helpers::mock_info("creator1", &[]).sender.to_string()), + }; + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert_eq!(err, ContractError::Unauthorized {}); +} + +#[test] +fn update_position_basic_flow() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(1_000_000)) + .end_time(Timestamp::from_seconds(5_000_000)); + let env = helpers::env_at(1); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // First subscription + let env = helpers::env_at(1_000_000 + 100); + let info = helpers::mock_info("creator1", &[Coin::new(1_000_000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + + // Update position + let env = helpers::env_at(1_000_000 + 3_000_000); + let info = helpers::mock_info("creator1", &[]); + let msg = cw_streamswap::msg::ExecuteMsg::UpdatePosition { + stream_id: 1, + operator_target: None, + }; + let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + + // Query position and verify values + let query_env = helpers::env_at(1_000_000 + 3_000_000); + let position = cw_streamswap::contract::query_position( + deps.as_ref(), + query_env.clone(), + 1, + helpers::mock_info("creator1", &[]).sender.to_string(), + ) + .unwrap(); + assert_eq!( + position.index, + Decimal256::from_str("0.749993000000000000").unwrap() + ); + assert_eq!(position.purchased, Uint256::from(749_993u128)); + assert_eq!(position.spent, Uint256::from(749_993u128)); + assert_eq!(position.in_balance, Uint256::from(250_007u128)); + + // Query stream and verify dist_index + let stream = cw_streamswap::contract::query_stream(deps.as_ref(), query_env, 1).unwrap(); + assert_eq!( + stream.dist_index, + Decimal256::from_str("0.749993000000000000").unwrap() + ); +} + +#[test] +fn update_position_after_stream_ends() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(1_000_000)) + .end_time(Timestamp::from_seconds(5_000_000)); + let env = helpers::env_at(1); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // First subscription + let env = helpers::env_at(1_000_000 + 100); + let info = helpers::mock_info("creator1", &[Coin::new(1_000_000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + + // Update position after stream ends + let env = helpers::env_at(5_000_000 + 1); + let info = helpers::mock_info("creator1", &[]); + let msg = cw_streamswap::msg::ExecuteMsg::UpdatePosition { + stream_id: 1, + operator_target: None, + }; + let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + + // Query stream and verify final state + let query_env = helpers::env_at(5_000_000 + 1); + let stream = + cw_streamswap::contract::query_stream(deps.as_ref(), query_env.clone(), 1).unwrap(); + assert_eq!(stream.dist_index, Decimal256::from_str("1").unwrap()); + assert_eq!(stream.in_supply, Uint256::zero()); + + // Query position and verify final state + let position = cw_streamswap::contract::query_position( + deps.as_ref(), + query_env, + 1, + helpers::mock_info("creator1", &[]).sender.to_string(), + ) + .unwrap(); + assert_eq!(position.index, Decimal256::from_str("1").unwrap()); + assert_eq!(position.spent, Uint256::from(1_000_000u128)); + assert_eq!(position.in_balance, Uint256::zero()); + assert_eq!(stream.out_supply, Uint256::from(1_000_000u128)); + assert_eq!(position.purchased, stream.out_supply); +} diff --git a/tests/update_stream.rs b/tests/update_stream.rs new file mode 100644 index 0000000..2c9dcf2 --- /dev/null +++ b/tests/update_stream.rs @@ -0,0 +1,143 @@ +use cosmwasm_std::testing::mock_dependencies; +use cosmwasm_std::{Coin, Decimal256, Timestamp, Uint256}; +use cw_streamswap::{self, contract::execute}; +use std::str::FromStr; + +mod helpers; + +#[test] +fn update_stream_no_subscriptions() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream that starts in the future + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(1_000_000)) + .end_time(Timestamp::from_seconds(5_000_000)); + let env = helpers::env_at(1); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // Update stream without subscriptions - no distribution should occur + let env = helpers::env_at(1_000_000 + 100); // After start time + let res = cw_streamswap::contract::execute_update_stream(deps.as_mut(), env, 1).unwrap(); + + // Verify no distribution occurred - match original test exactly + assert_eq!(res.attributes[0].key, "action"); + assert_eq!(res.attributes[0].value, "update_stream"); + assert_eq!(res.attributes[1].key, "stream_id"); + assert_eq!(res.attributes[1].value, "1"); + assert_eq!(res.attributes[2].key, "new_distribution_amount"); + assert_eq!(res.attributes[2].value, "0"); + assert_eq!(res.attributes[3].key, "dist_index"); + assert_eq!(res.attributes[3].value, "0"); + assert_eq!(res.attributes[4].key, "last_updated"); + assert_eq!(res.attributes[5].key, "start_time"); + assert_eq!(res.attributes[6].key, "end_time"); + assert_eq!(res.attributes[7].key, "in_denom"); + assert_eq!(res.attributes[8].key, "out_denom"); + assert_eq!(res.attributes[9].key, "in_supply"); + assert_eq!(res.attributes[10].key, "out_supply"); + assert_eq!(res.attributes[11].key, "out_remaining"); + assert_eq!(res.attributes[12].key, "spent_in"); + assert_eq!(res.attributes[13].key, "shares"); + assert_eq!(res.attributes[14].key, "current_streamed_price"); + assert_eq!(res.attributes[15].key, "status"); + assert_eq!(res.attributes[15].value, "Waiting"); + assert_eq!(res.attributes[16].key, "tos_version"); + assert_eq!(res.attributes[16].value, "v1"); +} + +#[test] +fn update_stream_first_subscription() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(1_000_000)) + .end_time(Timestamp::from_seconds(5_000_000)); + let env = helpers::env_at(1); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // First subscription - dist_index should remain 0 (no prior distribution) + let env = helpers::env_at(1_000_000 + 100); + let info = helpers::mock_info("creator1", &[Coin::new(1_000_000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + + // Query stream after first subscription - dist_index should still be 0 (no prior distribution) + let env = helpers::env_at(1_000_000 + 200); + let stream = cw_streamswap::contract::query_stream(deps.as_ref(), env, 1).unwrap(); + assert_eq!(stream.dist_index, Decimal256::zero()); +} + +#[test] +fn update_stream_with_subscribers() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(1_000_000)) + .end_time(Timestamp::from_seconds(5_000_000)); + let env = helpers::env_at(1); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // First subscription + let env = helpers::env_at(1_000_000 + 100); + let info = helpers::mock_info("creator1", &[Coin::new(1_000_000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + + // Update stream again with existing subscribers - dist_index should increase + let env = helpers::env_at(1_000_000 + 300); + let _res = cw_streamswap::contract::execute_update_stream(deps.as_mut(), env, 1).unwrap(); + + // Query stream - dist_index should now be non-zero + let env = helpers::env_at(1_000_000 + 300); + let stream = cw_streamswap::contract::query_stream(deps.as_ref(), env, 1).unwrap(); + assert_eq!(stream.dist_index, Decimal256::from_str("0.00005").unwrap()); +} From 1a8a15f24b73379110e6e27aed29b746958679f3 Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Mon, 25 Aug 2025 02:25:28 +0300 Subject: [PATCH 11/25] rounding error --- tests/rounding.rs | 196 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 tests/rounding.rs diff --git a/tests/rounding.rs b/tests/rounding.rs new file mode 100644 index 0000000..a5c22dc --- /dev/null +++ b/tests/rounding.rs @@ -0,0 +1,196 @@ +use cosmwasm_std::testing::mock_dependencies; +use cosmwasm_std::{Coin, Decimal256, Timestamp, Uint256}; +use cw_streamswap::contract::execute; +use cw_streamswap::msg::ExecuteMsg; +use std::str::FromStr; + +mod helpers; + +#[test] +fn test_rounding_leftover() { + let mut deps = mock_dependencies(); + + // Setup: Instantiate the contract + helpers::instantiate_defaults(deps.as_mut()); + + // Setup: Create a stream with specific parameters + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(1_000_000_000_000u128); + let out_denom = "out_denom"; + + let b = helpers::CreateStreamBuilder::default() + .start_time(start) + .end_time(end) + .out_supply(out_supply) + .out_denom(out_denom); + + let env = helpers::env_at(1); + let funds = vec![ + Coin { + denom: out_denom.to_string(), + amount: out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // Setup: First subscription + let env = helpers::env_at(1_000_000 + 100); + let info = helpers::mock_info("creator1", &[Coin::new(1_000_000_000u128, "in")]); + let msg = ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // Setup: Second subscription + let env = helpers::env_at(1_000_000 + 100_000); + let info = helpers::mock_info("creator2", &[Coin::new(3_000_000_000u128, "in")]); + let msg = ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // Test 1: Update position creator1 during stream + let env = helpers::env_at(1_000_000 + 3_000_000); + let info = helpers::mock_info("creator1", &[]); + let msg = ExecuteMsg::UpdatePosition { + stream_id: 1, + operator_target: None, + }; + execute(deps.as_mut(), env.clone(), info, msg).unwrap(); + + // Verify position creator1 + let position = cw_streamswap::contract::query_position( + deps.as_ref(), + env.clone(), + 1, + helpers::mock_info("creator1", &[]).sender.to_string(), + ) + .unwrap(); + assert_eq!( + position.index, + Decimal256::from_str("202.813614449380587585").unwrap() + ); + assert_eq!(position.purchased, Uint256::from(202_813_614_449u128)); + assert_eq!(position.spent, Uint256::from(749_993_750u128)); + assert_eq!(position.in_balance, Uint256::from(250_006_250u128)); + + let stream = cw_streamswap::contract::query_stream(deps.as_ref(), env, 1).unwrap(); + assert_eq!( + stream.dist_index, + Decimal256::from_str("202.813614449380587585").unwrap() + ); + + // Test 2: Update position creator2 during stream + let env = helpers::env_at(1_000_000 + 3_575_000); + let info = helpers::mock_info("creator2", &[]); + let msg = ExecuteMsg::UpdatePosition { + stream_id: 1, + operator_target: None, + }; + execute(deps.as_mut(), env.clone(), info, msg).unwrap(); + + // Verify position creator2 + let position = cw_streamswap::contract::query_position( + deps.as_ref(), + env.clone(), + 1, + helpers::mock_info("creator2", &[]).sender.to_string(), + ) + .unwrap(); + assert_eq!( + position.index, + Decimal256::from_str("238.074595237060799266").unwrap() + ); + assert_eq!(position.purchased, Uint256::from(655672748445u128)); + assert_eq!(position.spent, Uint256::from(2673076923u128)); + assert_eq!(position.in_balance, Uint256::from(326923077u128)); + + let stream = cw_streamswap::contract::query_stream(deps.as_ref(), env, 1).unwrap(); + assert_eq!( + stream.dist_index, + Decimal256::from_str("238.074595237060799266").unwrap() + ); + + // Test 3: Update position creator1 after stream ends + let env = helpers::env_at(5_000_000 + 1); + let info = helpers::mock_info("creator1", &[]); + let msg = ExecuteMsg::UpdatePosition { + stream_id: 1, + operator_target: None, + }; + execute(deps.as_mut(), env.clone(), info, msg).unwrap(); + + let stream = cw_streamswap::contract::query_stream(deps.as_ref(), env.clone(), 1).unwrap(); + assert_eq!( + stream.dist_index, + Decimal256::from_str("264.137059297637397644").unwrap() + ); + assert_eq!(stream.in_supply, Uint256::zero()); + + let position1 = cw_streamswap::contract::query_position( + deps.as_ref(), + env, + 1, + helpers::mock_info("creator1", &[]).sender.to_string(), + ) + .unwrap(); + assert_eq!( + position1.index, + Decimal256::from_str("264.137059297637397644").unwrap() + ); + assert_eq!(position1.spent, Uint256::from(1_000_000_000u128)); + assert_eq!(position1.in_balance, Uint256::zero()); + + // Test 4: Update position creator2 after stream ends + let env = helpers::env_at(5_000_000 + 1); + let info = helpers::mock_info("creator2", &[]); + let msg = ExecuteMsg::UpdatePosition { + stream_id: 1, + operator_target: None, + }; + execute(deps.as_mut(), env.clone(), info, msg).unwrap(); + + let stream = cw_streamswap::contract::query_stream(deps.as_ref(), env.clone(), 1).unwrap(); + assert_eq!( + stream.dist_index, + Decimal256::from_str("264.137059297637397644").unwrap() + ); + assert_eq!(stream.in_supply, Uint256::zero()); + + let position2 = cw_streamswap::contract::query_position( + deps.as_ref(), + env, + 1, + helpers::mock_info("creator2", &[]).sender.to_string(), + ) + .unwrap(); + assert_eq!( + position2.index, + Decimal256::from_str("264.137059297637397644").unwrap() + ); + assert_eq!(position2.spent, Uint256::from(3_000_000_000u128)); + assert_eq!(position2.in_balance, Uint256::zero()); + + // Test 5: Verify rounding behavior + assert_eq!(stream.out_remaining, Uint256::zero()); + assert_eq!( + position1 + .purchased + .checked_add(position2.purchased) + .unwrap(), + // 1 difference due to rounding + stream.out_supply.checked_sub(Uint256::from(1u128)).unwrap() + ); +} From 5b167d25a8ecd7390230574af741d8f15fd30fbe Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Mon, 25 Aug 2025 02:37:42 +0300 Subject: [PATCH 12/25] add more withdraw cases --- tests/withdraw.rs | 253 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 253 insertions(+) diff --git a/tests/withdraw.rs b/tests/withdraw.rs index 8923007..9e88353 100644 --- a/tests/withdraw.rs +++ b/tests/withdraw.rs @@ -2,6 +2,7 @@ use cosmwasm_std::testing::mock_dependencies; use cosmwasm_std::{BankMsg, Coin, Decimal256, Uint256}; use cosmwasm_std::{CosmosMsg, Timestamp}; use cw_streamswap::contract::execute; +use std::str::FromStr; mod helpers; #[test] @@ -190,3 +191,255 @@ fn withdraw_after_start_time() { // Stream should still exist and be active assert_eq!(stream.id, 1); } + +#[test] +fn withdraw_invalid_amounts() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(1_000_000)) + .end_time(Timestamp::from_seconds(5_000_000)); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // Subscribe to the stream + let env = helpers::env_at(1_000_000); + let funds = Coin::new(2_000_000_000_000u128, "in"); + let info = helpers::mock_info("creator1", &[funds.clone()]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + + // Test 1: Withdraw with cap = 0 (should fail) + let env = helpers::env_at(1_000_000 + 5000); + let info = helpers::mock_info("creator1", &[]); + let cap = Uint256::zero(); + let msg = cw_streamswap::msg::ExecuteMsg::Withdraw { + stream_id: 1, + cap: Some(cap), + operator_target: None, + }; + let res = execute(deps.as_mut(), env.clone(), info.clone(), msg); + assert_eq!( + res.unwrap_err(), + cw_streamswap::ContractError::InvalidWithdrawAmount {} + ); + + // Test 2: Withdraw with cap > available balance (should fail) + let cap = Uint256::from(2_250_000_000_000u128); + let msg = cw_streamswap::msg::ExecuteMsg::Withdraw { + stream_id: 1, + cap: Some(cap), + operator_target: None, + }; + let res = execute(deps.as_mut(), env.clone(), info.clone(), msg); + assert_eq!( + res.unwrap_err(), + cw_streamswap::ContractError::WithdrawAmountExceedsBalance(Uint256::from( + 2250000000000u128 + )) + ); +} + +#[test] +fn withdraw_with_valid_cap() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(1_000_000)) + .end_time(Timestamp::from_seconds(5_000_000)); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // Subscribe to the stream + let env = helpers::env_at(1_000_000); + let funds = Coin::new(2_000_000_000_000u128, "in"); + let info = helpers::mock_info("creator1", &[funds.clone()]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + + // Withdraw with valid cap + let env = helpers::env_at(1_000_000 + 5000); + let info = helpers::mock_info("creator1", &[]); + let cap = Uint256::from(25_000_000u128); + let msg = cw_streamswap::msg::ExecuteMsg::Withdraw { + stream_id: 1, + cap: Some(cap), + operator_target: None, + }; + let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + + // Verify position state after withdrawal + let position = cw_streamswap::contract::query_position( + deps.as_ref(), + helpers::env_at(1_000_000 + 5000), + 1, + helpers::mock_info("creator1", &[]).sender.to_string(), + ) + .unwrap(); + + // Verify position state after withdrawal (exact values from original test) + assert_eq!(position.in_balance, Uint256::from(1_997_475_000_000u128)); + assert_eq!(position.spent, Uint256::from(2_500_000_000u128)); + assert_eq!(position.purchased, Uint256::from(1250u128)); + + // Verify total funds consistency: in_balance + spent + cap = original_funds + let total_after_withdrawal = position.in_balance + position.spent + cap; + let original_funds = Uint256::from_str(funds.amount.to_string().as_str()).unwrap(); + assert_eq!(total_after_withdrawal, original_funds); +} + +#[test] +fn withdraw_full_balance() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(1_000_000)) + .end_time(Timestamp::from_seconds(5_000_000)); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // Subscribe to the stream + let env = helpers::env_at(1_000_000); + let funds = Coin::new(2_000_000_000_000u128, "in"); + let info = helpers::mock_info("creator1", &[funds.clone()]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + + // Withdraw full balance (cap = None) + let env = helpers::env_at(1_000_000 + 1_000_000); + let info = helpers::mock_info("creator1", &[]); + let msg = cw_streamswap::msg::ExecuteMsg::Withdraw { + stream_id: 1, + cap: None, + operator_target: None, + }; + let res = execute(deps.as_mut(), env, info, msg).unwrap(); + + // Verify final position state + let position = cw_streamswap::contract::query_position( + deps.as_ref(), + helpers::env_at(1_000_000 + 1_000_000), + 1, + helpers::mock_info("creator1", &[]).sender.to_string(), + ) + .unwrap(); + + assert_eq!(position.in_balance, Uint256::zero()); + assert_eq!(position.spent, Uint256::from(500000000000u128)); + assert_eq!(position.purchased, Uint256::from(250000u128)); + assert_eq!(position.shares, Uint256::zero()); + + // Verify bank message for full withdrawal + let msg = res.messages.first().unwrap(); + assert_eq!( + msg.msg, + CosmosMsg::Bank(BankMsg::Send { + to_address: helpers::mock_info("creator1", &[]).sender.to_string(), + amount: vec![Coin::new(1500000000000u128, "in")] + }) + ); +} + +#[test] +fn withdraw_after_stream_ends() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(1_000_000)) + .end_time(Timestamp::from_seconds(5_000_000)); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // Subscribe to the stream + let env = helpers::env_at(1_000_000); + let funds = Coin::new(2_000_000_000_000u128, "in"); + let info = helpers::mock_info("creator1", &[funds.clone()]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + + // Attempt withdrawal after stream ends (should fail) + let env = helpers::env_at(5_000_000 + 1); // After end time + let info = helpers::mock_info("creator1", &[]); + let msg = cw_streamswap::msg::ExecuteMsg::Withdraw { + stream_id: 1, + cap: None, + operator_target: None, + }; + let res = execute(deps.as_mut(), env, info, msg); + assert_eq!( + res.unwrap_err(), + cw_streamswap::ContractError::StreamEnded {} + ); +} From 1d3e0a486252a69e4e0cb840f1516d3cb5bd4fdc Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Mon, 25 Aug 2025 03:23:03 +0300 Subject: [PATCH 13/25] optimize --- Cargo.toml | 2 +- src/contract.rs | 1 + src/msg.rs | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1c944b3..ca82733 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ optimize = """docker run --rm -v "$(pwd)":/code \ """ [dependencies] -cosmwasm-std = { version = "3.0.1", default-features = false, features = ["std", "stargate"] } +cosmwasm-std = { version = "3.0.1", default-features = false, features = ["std", "stargate", "exports"] } cosmwasm-schema = "3.0.1" cw-controllers = "3.0.0" cw-storage-plus = "3.0.0" diff --git a/src/contract.rs b/src/contract.rs index b3bd0c9..3e4bdda 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -1378,6 +1378,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { } } } + pub fn query_config(deps: Deps) -> StdResult { let cfg = CONFIG.load(deps.storage)?; Ok(ConfigResponse { diff --git a/src/msg.rs b/src/msg.rs index 53f9442..9ad92c7 100644 --- a/src/msg.rs +++ b/src/msg.rs @@ -1,6 +1,6 @@ use crate::state::Status; use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Addr, Decimal256, Timestamp, Uint128, Uint256, Uint64}; +use cosmwasm_std::{Addr, Decimal256, Timestamp, Uint256, Uint64}; #[cw_serde] pub struct InstantiateMsg { @@ -186,7 +186,7 @@ pub enum QueryMsg { /// Returns currently streaming price of a sale. #[returns(LatestStreamedPriceResponse)] LastStreamedPrice { stream_id: u64 }, - #[returns(Uint128)] + #[returns(Option)] Threshold { stream_id: u64 }, } From c463d2640764be95a6f87d7c2a251ee963af8340 Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Mon, 25 Aug 2025 03:44:17 +0300 Subject: [PATCH 14/25] finalize tests --- src/contract.rs | 2 +- tests/finalize.rs | 265 ++++++++++++++++++++++++++++++++++++++++++++++ tests/helpers.rs | 6 +- 3 files changed, 271 insertions(+), 2 deletions(-) create mode 100644 tests/finalize.rs diff --git a/src/contract.rs b/src/contract.rs index 3e4bdda..ab75616 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -1041,7 +1041,7 @@ pub fn execute_finalize_stream( //Stream's swap fee collected at fixed rate from accumulated spent_in of positions(ie stream.spent_in) let swap_fee = Decimal256::from_ratio(stream.spent_in, Uint256::one()) .checked_mul(stream.stream_exit_fee_percent)? - .atomics(); + .to_uint_ceil(); let creator_revenue = stream.spent_in.checked_sub(swap_fee)?; diff --git a/tests/finalize.rs b/tests/finalize.rs new file mode 100644 index 0000000..aa23bd4 --- /dev/null +++ b/tests/finalize.rs @@ -0,0 +1,265 @@ +use cosmwasm_std::testing::mock_dependencies; +use cosmwasm_std::{BankMsg, Coin, CosmosMsg, Timestamp, Uint256}; +use cw_streamswap::contract::{execute, execute_finalize_stream}; +use cw_streamswap::msg::ExecuteMsg; +use cw_streamswap::ContractError; + +mod helpers; + +#[test] +fn finalize_unauthorized_access() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(1_000_000)) + .end_time(Timestamp::from_seconds(5_000_000)); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // Subscribe to the stream + let env = helpers::env_at(1_000_000 + 1_000_000); + let funds = Coin::new(2_000_000_000_000u128, "in"); + let info = helpers::mock_info("creator1", &[funds]); + let msg = ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + + // Random user attempts to finalize (should fail with Unauthorized) + let env = helpers::env_at(5_000_000 + 1); // After stream ends + let info = helpers::mock_info("random", &[]); + let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None); + assert_eq!(res.unwrap_err(), ContractError::Unauthorized {}); +} + +#[test] +fn finalize_timing_validation() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(1_000_000)) + .end_time(Timestamp::from_seconds(5_000_000)); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // Subscribe to the stream + let env = helpers::env_at(1_000_000 + 1_000_000); + let funds = Coin::new(2_000_000_000_000u128, "in"); + let info = helpers::mock_info("creator1", &[funds]); + let msg = ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + + // Test 1: Can't finalize before stream ends + let env = helpers::env_at(1_000_000 + 1); // During stream + let info = helpers::mock_info("creator", &[]); // Creator is the treasury + let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None); + assert_eq!(res.unwrap_err(), ContractError::StreamNotEnded {}); + + // Test 2: Can finalize after stream ends + let env = helpers::env_at(5_000_000 + 1); // After stream ends + let info = helpers::mock_info("creator", &[]); // Creator is the treasury + let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None); + println!("res: {:?}", res); + assert!(res.is_ok()); // Should succeed +} + +#[test] +fn finalize_happy_path() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(1_000_000)) + .end_time(Timestamp::from_seconds(5_000_000)); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // Subscribe to the stream + let env = helpers::env_at(1_000_000 + 1_000_000); + let funds = Coin::new(2_000_000_000_000u128, "in"); + let info = helpers::mock_info("creator1", &[funds]); + let msg = ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + + // Update stream before finalization + let env = helpers::env_at(5_000_000 + 1); + helpers::mock_info("creator", &[]); // Creator is the treasury + // Note: execute_update_stream function call would go here if available + + // Happy path finalization + let info = helpers::mock_info("creator", &[]); // Creator is the treasury + let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None).unwrap(); + + // Verify finalization attributes + assert_eq!(res.attributes[0].key, "action"); + assert_eq!(res.attributes[0].value, "finalize_stream"); + assert_eq!(res.attributes[1].key, "stream_id"); + assert_eq!(res.attributes[1].value, "1"); + assert_eq!(res.attributes[2].key, "stream_dist_index"); + assert_eq!(res.attributes[2].value, "0.0000005"); + assert_eq!(res.attributes[3].key, "stream_shares"); + assert_eq!(res.attributes[3].value, "2000000000000"); + assert_eq!(res.attributes[4].key, "stream_last_updated"); + assert_eq!(res.attributes[4].value, "5000001.000000000"); + assert_eq!(res.attributes[5].key, "stream_in_supply"); + assert_eq!(res.attributes[5].value, "0"); + assert_eq!(res.attributes[6].key, "stream_out_remaining"); + assert_eq!(res.attributes[6].value, "0"); + assert_eq!(res.attributes[7].key, "stream_status"); + assert_eq!(res.attributes[7].value, "Finalized"); + assert_eq!(res.attributes[8].key, "stream_exit_fee_percent"); + assert_eq!(res.attributes[8].value, "0.01"); + assert_eq!(res.attributes[9].key, "treasury"); + assert_eq!( + res.attributes[9].value, + helpers::mock_info("creator", &[]).sender.to_string() + ); + assert_eq!(res.attributes[10].key, "creators_revenue"); + assert_eq!(res.attributes[10].value, "1980000000000"); + assert_eq!(res.attributes[11].key, "refunded_out_remaining"); + assert_eq!(res.attributes[11].value, "0"); + assert_eq!(res.attributes[12].key, "total_sold"); + assert_eq!(res.attributes[12].value, "1000000"); + assert_eq!(res.attributes[13].key, "fee_collector"); + assert_eq!( + res.attributes[13].value, + helpers::mock_info("collector", &[]).sender.to_string() + ); + assert_eq!(res.attributes[14].key, "swap_fee"); + assert_eq!(res.attributes[14].value, "20000000000"); + assert_eq!(res.attributes[15].key, "creation_fee"); + assert_eq!(res.attributes[15].value, "100"); + + // Verify bank messages + assert_eq!(res.messages.len(), 3); + + // Message 1: Treasury payment + let msg1 = &res.messages[0].msg; + assert_eq!( + msg1, + &CosmosMsg::Bank(BankMsg::Send { + to_address: helpers::mock_info("creator", &[]).sender.to_string(), + amount: vec![Coin::new(1_980_000_000_000u128, "in")], + }) + ); + + // Message 2: Creation fee to collector + let msg2 = &res.messages[1].msg; + assert_eq!( + msg2, + &CosmosMsg::Bank(BankMsg::Send { + to_address: helpers::mock_info("collector", &[]).sender.to_string(), + amount: vec![Coin::new(100u128, "fee")], + }) + ); + + // Message 3: Swap fee to collector + let msg3 = &res.messages[2].msg; + assert_eq!( + msg3, + &CosmosMsg::Bank(BankMsg::Send { + to_address: helpers::mock_info("collector", &[]).sender.to_string(), + amount: vec![Coin::new(20_000_000_000u128, "in")], + }) + ); +} + +#[test] +fn finalize_duplicate_prevention() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Create a stream + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(1_000_000)) + .end_time(Timestamp::from_seconds(5_000_000)); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = helpers::mock_info("creator", &funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // Subscribe to the stream + let env = helpers::env_at(1_000_000 + 1_000_000); // After start time + let funds = Coin::new(2_000_000_000_000u128, "in"); + let info = helpers::mock_info("creator1", &[funds]); + let msg = ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + + // First finalization (should succeed) + let env = helpers::env_at(5_000_000 + 1); // After stream ends + let info = helpers::mock_info("creator", &[]); // Creator is the treasury + let res = execute_finalize_stream(deps.as_mut(), env.clone(), info.clone(), 1, None).unwrap(); + + // Verify first finalization succeeded by checking response attributes + assert_eq!(res.attributes[0].key, "action"); + assert_eq!(res.attributes[0].value, "finalize_stream"); + + // Second finalization (should fail with StreamAlreadyFinalized) + let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None); + assert_eq!(res.unwrap_err(), ContractError::StreamAlreadyFinalized {}); +} diff --git a/tests/helpers.rs b/tests/helpers.rs index 65e7038..7813e96 100644 --- a/tests/helpers.rs +++ b/tests/helpers.rs @@ -104,7 +104,7 @@ pub struct CreateStreamBuilder { impl Default for CreateStreamBuilder { fn default() -> Self { Self { - treasury: valid_addr("treasury").to_string(), + treasury: valid_addr("creator").to_string(), name: "name".to_string(), url: Some("https://sample.url".to_string()), in_denom: DEFAULT_ACCEPTED_IN_DENOM.to_string(), @@ -155,6 +155,10 @@ impl CreateStreamBuilder { self.threshold = t; self } + pub fn treasury(mut self, t: &str) -> Self { + self.treasury = t.to_string(); + self + } pub fn build(self) -> cw_streamswap::msg::ExecuteMsg { cw_streamswap::msg::ExecuteMsg::CreateStream { From 3c74a872aecc22ac179e1decd2060fd80afb67c5 Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Tue, 26 Aug 2025 01:30:45 +0300 Subject: [PATCH 15/25] actor model for tests --- tests/create_stream.rs | 20 ++----- tests/finalize.rs | 93 +++++++++++++++++------------- tests/rounding.rs | 36 +++++++----- tests/subscribe.rs | 121 +++++++++++++++++++++++++++------------ tests/update_position.rs | 55 ++++++++++++------ tests/update_stream.rs | 36 ++++++++---- tests/withdraw.rs | 118 ++++++++++++++++++++++++++++---------- 7 files changed, 317 insertions(+), 162 deletions(-) diff --git a/tests/create_stream.rs b/tests/create_stream.rs index 2ef716a..cb7b32a 100644 --- a/tests/create_stream.rs +++ b/tests/create_stream.rs @@ -10,32 +10,24 @@ mod helpers; #[test] fn instantiate_invalid_exit_fee_percent() { let mut deps = mock_dependencies(); + // Define actors + let treasury = helpers::mock_info("creator", &[]); let msg = helpers::InstantiateBuilder::default() .exit_fee_percent(Decimal256::percent(101)) .build(); - let err = instantiate( - deps.as_mut(), - helpers::env_now(), - helpers::mock_info("creator", &[]), - msg, - ) - .unwrap_err(); + let err = instantiate(deps.as_mut(), helpers::env_now(), treasury.clone(), msg).unwrap_err(); assert_eq!(err, ContractError::InvalidExitFeePercent {}); } #[test] fn instantiate_invalid_stream_creation_fee() { let mut deps = mock_dependencies(); + // Define actors + let treasury = helpers::mock_info("creator", &[]); let msg = helpers::InstantiateBuilder::default() .stream_creation_fee(Uint256::zero()) .build(); - let err = instantiate( - deps.as_mut(), - helpers::env_now(), - helpers::mock_info("creator", &[]), - msg, - ) - .unwrap_err(); + let err = instantiate(deps.as_mut(), helpers::env_now(), treasury.clone(), msg).unwrap_err(); assert_eq!(err, ContractError::InvalidStreamCreationFee {}); } diff --git a/tests/finalize.rs b/tests/finalize.rs index aa23bd4..c8d44e5 100644 --- a/tests/finalize.rs +++ b/tests/finalize.rs @@ -11,6 +11,11 @@ fn finalize_unauthorized_access() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define actors + let treasury = helpers::mock_info("creator", &[]); + let subscriber1 = helpers::mock_info("creator1", &[]); + let unauthorized = helpers::mock_info("random", &[]); + // Create a stream let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(1_000_000)) @@ -26,25 +31,26 @@ fn finalize_unauthorized_access() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds.clone(); + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // Subscribe to the stream let env = helpers::env_at(1_000_000 + 1_000_000); let funds = Coin::new(2_000_000_000_000u128, "in"); - let info = helpers::mock_info("creator1", &[funds]); + let mut subscriber1_funded = subscriber1.clone(); + subscriber1_funded.funds = vec![funds]; let msg = ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, operator: None, tos_version: "v1".to_string(), }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + let _res = execute(deps.as_mut(), env, subscriber1_funded, msg).unwrap(); // Random user attempts to finalize (should fail with Unauthorized) let env = helpers::env_at(5_000_000 + 1); // After stream ends - let info = helpers::mock_info("random", &[]); - let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None); + let res = execute_finalize_stream(deps.as_mut(), env, unauthorized.clone(), 1, None); assert_eq!(res.unwrap_err(), ContractError::Unauthorized {}); } @@ -53,6 +59,10 @@ fn finalize_timing_validation() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define actors + let treasury = helpers::mock_info("creator", &[]); + let subscriber1 = helpers::mock_info("creator1", &[]); + // Create a stream let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(1_000_000)) @@ -68,32 +78,31 @@ fn finalize_timing_validation() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds.clone(); + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // Subscribe to the stream let env = helpers::env_at(1_000_000 + 1_000_000); let funds = Coin::new(2_000_000_000_000u128, "in"); - let info = helpers::mock_info("creator1", &[funds]); + let mut subscriber1_funded = subscriber1.clone(); + subscriber1_funded.funds = vec![funds]; let msg = ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, operator: None, tos_version: "v1".to_string(), }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + let _res = execute(deps.as_mut(), env, subscriber1_funded, msg).unwrap(); // Test 1: Can't finalize before stream ends let env = helpers::env_at(1_000_000 + 1); // During stream - let info = helpers::mock_info("creator", &[]); // Creator is the treasury - let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None); + let res = execute_finalize_stream(deps.as_mut(), env, treasury.clone(), 1, None); assert_eq!(res.unwrap_err(), ContractError::StreamNotEnded {}); // Test 2: Can finalize after stream ends let env = helpers::env_at(5_000_000 + 1); // After stream ends - let info = helpers::mock_info("creator", &[]); // Creator is the treasury - let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None); - println!("res: {:?}", res); + let res = execute_finalize_stream(deps.as_mut(), env, treasury.clone(), 1, None); assert!(res.is_ok()); // Should succeed } @@ -102,6 +111,11 @@ fn finalize_happy_path() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define actors + let treasury = helpers::mock_info("creator", &[]); + let subscriber1 = helpers::mock_info("creator1", &[]); + let collector = helpers::mock_info("collector", &[]); + // Create a stream let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(1_000_000)) @@ -117,20 +131,22 @@ fn finalize_happy_path() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds.clone(); + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // Subscribe to the stream let env = helpers::env_at(1_000_000 + 1_000_000); let funds = Coin::new(2_000_000_000_000u128, "in"); - let info = helpers::mock_info("creator1", &[funds]); + let mut subscriber1_funded = subscriber1.clone(); + subscriber1_funded.funds = vec![funds]; let msg = ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, operator: None, tos_version: "v1".to_string(), }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + let _res = execute(deps.as_mut(), env, subscriber1_funded, msg).unwrap(); // Update stream before finalization let env = helpers::env_at(5_000_000 + 1); @@ -138,8 +154,7 @@ fn finalize_happy_path() { // Note: execute_update_stream function call would go here if available // Happy path finalization - let info = helpers::mock_info("creator", &[]); // Creator is the treasury - let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None).unwrap(); + let res = execute_finalize_stream(deps.as_mut(), env, treasury.clone(), 1, None).unwrap(); // Verify finalization attributes assert_eq!(res.attributes[0].key, "action"); @@ -161,10 +176,7 @@ fn finalize_happy_path() { assert_eq!(res.attributes[8].key, "stream_exit_fee_percent"); assert_eq!(res.attributes[8].value, "0.01"); assert_eq!(res.attributes[9].key, "treasury"); - assert_eq!( - res.attributes[9].value, - helpers::mock_info("creator", &[]).sender.to_string() - ); + assert_eq!(res.attributes[9].value, treasury.sender.to_string()); assert_eq!(res.attributes[10].key, "creators_revenue"); assert_eq!(res.attributes[10].value, "1980000000000"); assert_eq!(res.attributes[11].key, "refunded_out_remaining"); @@ -172,10 +184,7 @@ fn finalize_happy_path() { assert_eq!(res.attributes[12].key, "total_sold"); assert_eq!(res.attributes[12].value, "1000000"); assert_eq!(res.attributes[13].key, "fee_collector"); - assert_eq!( - res.attributes[13].value, - helpers::mock_info("collector", &[]).sender.to_string() - ); + assert_eq!(res.attributes[13].value, collector.sender.to_string()); assert_eq!(res.attributes[14].key, "swap_fee"); assert_eq!(res.attributes[14].value, "20000000000"); assert_eq!(res.attributes[15].key, "creation_fee"); @@ -189,7 +198,7 @@ fn finalize_happy_path() { assert_eq!( msg1, &CosmosMsg::Bank(BankMsg::Send { - to_address: helpers::mock_info("creator", &[]).sender.to_string(), + to_address: treasury.sender.to_string(), amount: vec![Coin::new(1_980_000_000_000u128, "in")], }) ); @@ -199,7 +208,7 @@ fn finalize_happy_path() { assert_eq!( msg2, &CosmosMsg::Bank(BankMsg::Send { - to_address: helpers::mock_info("collector", &[]).sender.to_string(), + to_address: collector.sender.to_string(), amount: vec![Coin::new(100u128, "fee")], }) ); @@ -209,7 +218,7 @@ fn finalize_happy_path() { assert_eq!( msg3, &CosmosMsg::Bank(BankMsg::Send { - to_address: helpers::mock_info("collector", &[]).sender.to_string(), + to_address: collector.sender.to_string(), amount: vec![Coin::new(20_000_000_000u128, "in")], }) ); @@ -220,6 +229,10 @@ fn finalize_duplicate_prevention() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define actors + let treasury = helpers::mock_info("creator", &[]); + let subscriber1 = helpers::mock_info("creator1", &[]); + // Create a stream let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(1_000_000)) @@ -235,31 +248,33 @@ fn finalize_duplicate_prevention() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds.clone(); + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // Subscribe to the stream let env = helpers::env_at(1_000_000 + 1_000_000); // After start time let funds = Coin::new(2_000_000_000_000u128, "in"); - let info = helpers::mock_info("creator1", &[funds]); + let mut subscriber1_funded = subscriber1.clone(); + subscriber1_funded.funds = vec![funds]; let msg = ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, operator: None, tos_version: "v1".to_string(), }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + let _res = execute(deps.as_mut(), env, subscriber1_funded, msg).unwrap(); // First finalization (should succeed) let env = helpers::env_at(5_000_000 + 1); // After stream ends - let info = helpers::mock_info("creator", &[]); // Creator is the treasury - let res = execute_finalize_stream(deps.as_mut(), env.clone(), info.clone(), 1, None).unwrap(); + let res = + execute_finalize_stream(deps.as_mut(), env.clone(), treasury.clone(), 1, None).unwrap(); // Verify first finalization succeeded by checking response attributes assert_eq!(res.attributes[0].key, "action"); assert_eq!(res.attributes[0].value, "finalize_stream"); // Second finalization (should fail with StreamAlreadyFinalized) - let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None); + let res = execute_finalize_stream(deps.as_mut(), env, treasury, 1, None); assert_eq!(res.unwrap_err(), ContractError::StreamAlreadyFinalized {}); } diff --git a/tests/rounding.rs b/tests/rounding.rs index a5c22dc..4b41809 100644 --- a/tests/rounding.rs +++ b/tests/rounding.rs @@ -13,6 +13,11 @@ fn test_rounding_leftover() { // Setup: Instantiate the contract helpers::instantiate_defaults(deps.as_mut()); + // Define actors + let treasury = helpers::mock_info("creator", &[]); + let subscriber1 = helpers::mock_info("creator1", &[]); + let subscriber2 = helpers::mock_info("creator2", &[]); + // Setup: Create a stream with specific parameters let start = Timestamp::from_seconds(1_000_000); let end = Timestamp::from_seconds(5_000_000); @@ -36,34 +41,37 @@ fn test_rounding_leftover() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds; + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // Setup: First subscription let env = helpers::env_at(1_000_000 + 100); - let info = helpers::mock_info("creator1", &[Coin::new(1_000_000_000u128, "in")]); + let mut s1 = subscriber1.clone(); + s1.funds = vec![Coin::new(1_000_000_000u128, "in")]; let msg = ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, operator: None, tos_version: "v1".to_string(), }; - execute(deps.as_mut(), env, info, msg).unwrap(); + execute(deps.as_mut(), env, s1, msg).unwrap(); // Setup: Second subscription let env = helpers::env_at(1_000_000 + 100_000); - let info = helpers::mock_info("creator2", &[Coin::new(3_000_000_000u128, "in")]); + let mut s2 = subscriber2.clone(); + s2.funds = vec![Coin::new(3_000_000_000u128, "in")]; let msg = ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, operator: None, tos_version: "v1".to_string(), }; - execute(deps.as_mut(), env, info, msg).unwrap(); + execute(deps.as_mut(), env, s2, msg).unwrap(); // Test 1: Update position creator1 during stream let env = helpers::env_at(1_000_000 + 3_000_000); - let info = helpers::mock_info("creator1", &[]); + let info = subscriber1.clone(); let msg = ExecuteMsg::UpdatePosition { stream_id: 1, operator_target: None, @@ -75,7 +83,7 @@ fn test_rounding_leftover() { deps.as_ref(), env.clone(), 1, - helpers::mock_info("creator1", &[]).sender.to_string(), + subscriber1.sender.to_string(), ) .unwrap(); assert_eq!( @@ -94,7 +102,7 @@ fn test_rounding_leftover() { // Test 2: Update position creator2 during stream let env = helpers::env_at(1_000_000 + 3_575_000); - let info = helpers::mock_info("creator2", &[]); + let info = subscriber2.clone(); let msg = ExecuteMsg::UpdatePosition { stream_id: 1, operator_target: None, @@ -106,7 +114,7 @@ fn test_rounding_leftover() { deps.as_ref(), env.clone(), 1, - helpers::mock_info("creator2", &[]).sender.to_string(), + subscriber2.sender.to_string(), ) .unwrap(); assert_eq!( @@ -125,7 +133,7 @@ fn test_rounding_leftover() { // Test 3: Update position creator1 after stream ends let env = helpers::env_at(5_000_000 + 1); - let info = helpers::mock_info("creator1", &[]); + let info = subscriber1.clone(); let msg = ExecuteMsg::UpdatePosition { stream_id: 1, operator_target: None, @@ -143,7 +151,7 @@ fn test_rounding_leftover() { deps.as_ref(), env, 1, - helpers::mock_info("creator1", &[]).sender.to_string(), + subscriber1.sender.to_string(), ) .unwrap(); assert_eq!( @@ -155,7 +163,7 @@ fn test_rounding_leftover() { // Test 4: Update position creator2 after stream ends let env = helpers::env_at(5_000_000 + 1); - let info = helpers::mock_info("creator2", &[]); + let info = subscriber2.clone(); let msg = ExecuteMsg::UpdatePosition { stream_id: 1, operator_target: None, @@ -173,7 +181,7 @@ fn test_rounding_leftover() { deps.as_ref(), env, 1, - helpers::mock_info("creator2", &[]).sender.to_string(), + subscriber2.sender.to_string(), ) .unwrap(); assert_eq!( diff --git a/tests/subscribe.rs b/tests/subscribe.rs index 5d0da23..3b96e59 100644 --- a/tests/subscribe.rs +++ b/tests/subscribe.rs @@ -10,6 +10,10 @@ fn subscribe_stream_ended() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define actors + let treasury = helpers::mock_info("creator", &[]); + let user1 = helpers::mock_info("user1", &[]); + // Create a stream first let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(2000)) @@ -25,12 +29,14 @@ fn subscribe_stream_ended() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds; + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // Try to subscribe after stream has ended let env = helpers::env_at(4000); // After end time - let info = helpers::mock_info("user1", &[Coin::new(1000u128, "in")]); + let mut user1_funded = user1.clone(); + user1_funded.funds = vec![Coin::new(1000u128, "in")]; let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, @@ -38,7 +44,7 @@ fn subscribe_stream_ended() { tos_version: "v1".to_string(), }; - let res = execute(deps.as_mut(), env, info, msg); + let res = execute(deps.as_mut(), env, user1_funded, msg); assert_eq!(res.unwrap_err(), ContractError::StreamEnded {}); } @@ -47,6 +53,10 @@ fn subscribe_no_funds() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define actors + let treasury = helpers::mock_info("creator", &[]); + let user1 = helpers::mock_info("user1", &[]); + // Create a stream first let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(2000)) @@ -62,12 +72,13 @@ fn subscribe_no_funds() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds; + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // Try to subscribe without funds let env = helpers::env_at(2500); // During stream - let info = helpers::mock_info("user1", &[]); // No funds + let info = user1.clone(); // No funds let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, @@ -84,6 +95,10 @@ fn subscribe_incorrect_denom() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define actors + let treasury = helpers::mock_info("creator", &[]); + let user1 = helpers::mock_info("user1", &[]); + // Create a stream first let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(2000)) @@ -99,12 +114,14 @@ fn subscribe_incorrect_denom() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds; + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // Try to subscribe with wrong denom let env = helpers::env_at(2500); // During stream - let info = helpers::mock_info("user1", &[Coin::new(1000u128, "wrong_denom")]); + let mut user1_funded = user1.clone(); + user1_funded.funds = vec![Coin::new(1000u128, "wrong_denom")]; let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, @@ -112,7 +129,7 @@ fn subscribe_incorrect_denom() { tos_version: "v1".to_string(), }; - let res = execute(deps.as_mut(), env, info, msg); + let res = execute(deps.as_mut(), env, user1_funded, msg); assert_eq!( res.unwrap_err(), PaymentError::MissingDenom("in".to_string()).into() @@ -124,6 +141,10 @@ fn subscribe_incorrect_tos_version() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define actors + let treasury = helpers::mock_info("creator", &[]); + let user1 = helpers::mock_info("user1", &[]); + // Create a stream first let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(2000)) @@ -139,12 +160,14 @@ fn subscribe_incorrect_tos_version() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds; + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // Try to subscribe with incorrect ToS version let env = helpers::env_at(2500); // During stream - let info = helpers::mock_info("user1", &[Coin::new(1000u128, "in")]); + let mut user1_funded = user1.clone(); + user1_funded.funds = vec![Coin::new(1000u128, "in")]; let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, @@ -152,7 +175,7 @@ fn subscribe_incorrect_tos_version() { tos_version: "random".to_string(), // Invalid ToS version }; - let res = execute(deps.as_mut(), env, info, msg); + let res = execute(deps.as_mut(), env, user1_funded, msg); assert_eq!(res.unwrap_err(), ContractError::InvalidToSVersion {}); } @@ -161,6 +184,10 @@ fn subscribe_first_subscription() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define actors + let treasury = helpers::mock_info("creator", &[]); + let user1 = helpers::mock_info("user1", &[]); + // Create a stream first let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(2000)) @@ -176,12 +203,14 @@ fn subscribe_first_subscription() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds; + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // First subscription let env = helpers::env_at(2500); // During stream - let info = helpers::mock_info("user1", &[Coin::new(1000u128, "in")]); + let mut user1_funded = user1.clone(); + user1_funded.funds = vec![Coin::new(1000u128, "in")]; let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, @@ -189,7 +218,7 @@ fn subscribe_first_subscription() { tos_version: "v1".to_string(), }; - let res = execute(deps.as_mut(), env, info.clone(), msg); + let res = execute(deps.as_mut(), env, user1_funded.clone(), msg); assert!(res.is_ok()); // Verify the response attributes @@ -212,7 +241,7 @@ fn subscribe_first_subscription() { deps.as_ref(), query_env, 1, - info.sender.to_string(), + user1_funded.sender.to_string(), ) .unwrap(); assert_eq!(position.index, Decimal256::zero()); @@ -228,6 +257,10 @@ fn subscribe_increase_subscription() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define actors + let treasury = helpers::mock_info("creator", &[]); + let user1 = helpers::mock_info("user1", &[]); + // Create a stream first let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(2000)) @@ -243,12 +276,14 @@ fn subscribe_increase_subscription() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds; + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // First subscription let env = helpers::env_at(2500); // During stream - let info = helpers::mock_info("user1", &[Coin::new(1000u128, "in")]); + let mut user1_funded = user1.clone(); + user1_funded.funds = vec![Coin::new(1000u128, "in")]; let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, @@ -256,7 +291,7 @@ fn subscribe_increase_subscription() { tos_version: "v1".to_string(), }; - let res = execute(deps.as_mut(), env, info, msg); + let res = execute(deps.as_mut(), env, user1_funded.clone(), msg); assert!(res.is_ok()); // Query stream after first subscription @@ -268,7 +303,8 @@ fn subscribe_increase_subscription() { // Second subscription (increase) by the same user let env = helpers::env_at(3000); // Later during stream - let info = helpers::mock_info("user1", &[Coin::new(500u128, "in")]); + let mut user1_funded = user1.clone(); + user1_funded.funds = vec![Coin::new(500u128, "in")]; let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, @@ -276,7 +312,7 @@ fn subscribe_increase_subscription() { tos_version: "v1".to_string(), }; - let res = execute(deps.as_mut(), env, info.clone(), msg); + let res = execute(deps.as_mut(), env, user1_funded.clone(), msg); assert!(res.is_ok()); // Query stream after second subscription @@ -292,7 +328,7 @@ fn subscribe_increase_subscription() { deps.as_ref(), query_env, 1, - info.sender.to_string(), + user1_funded.sender.to_string(), ) .unwrap(); assert!(position.in_balance < Uint256::from(1500u128)); // Total balance minus spent between subscriptions @@ -303,6 +339,10 @@ fn subscribe_pending_stream() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define actors + let treasury = helpers::mock_info("creator", &[]); + let user1 = helpers::mock_info("user1", &[]); + // Create a stream that hasn't started yet let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(5000)) // Start in the future @@ -319,14 +359,16 @@ fn subscribe_pending_stream() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds; + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // Subscribe before stream starts (pending subscription) // Note: Treasury cancel period is active from time 0 to 1000 (min_seconds_until_start_time) // So we need to subscribe after time 1000 but before start time 5000 let env = helpers::env_at(2000); // After cancel period, before start time - let info = helpers::mock_info("user1", &[Coin::new(1000u128, "in")]); + let mut user1_funded = user1.clone(); + user1_funded.funds = vec![Coin::new(1000u128, "in")]; let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, @@ -334,7 +376,7 @@ fn subscribe_pending_stream() { tos_version: "v1".to_string(), }; - let res = execute(deps.as_mut(), env, info.clone(), msg); + let res = execute(deps.as_mut(), env, user1_funded.clone(), msg); assert!(res.is_ok()); let response = res.unwrap(); @@ -354,7 +396,7 @@ fn subscribe_pending_stream() { deps.as_ref(), query_env, 1, - info.sender.to_string(), + user1_funded.sender.to_string(), ) .unwrap(); assert_eq!(position.in_balance, Uint256::from(1000u128)); @@ -367,6 +409,10 @@ fn subscribe_pending_treasury_cancel_period() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define actors + let treasury = helpers::mock_info("creator", &[]); + let user1 = helpers::mock_info("user1", &[]); + // Create a stream that hasn't started yet let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(5000)) // Start in the future @@ -382,13 +428,15 @@ fn subscribe_pending_treasury_cancel_period() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds; + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // Try to subscribe during treasury cancel period (time 0 to 1000) // This should fail with TreasuryCancelPeriodActive let env = helpers::env_at(500); // During cancel period - let info = helpers::mock_info("user1", &[Coin::new(1000u128, "in")]); + let mut user1_funded = user1.clone(); + user1_funded.funds = vec![Coin::new(1000u128, "in")]; let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, @@ -396,7 +444,7 @@ fn subscribe_pending_treasury_cancel_period() { tos_version: "v1".to_string(), }; - let res = execute(deps.as_mut(), env, info, msg); + let res = execute(deps.as_mut(), env, user1_funded, msg); assert!(res.is_err()); assert_eq!( res.unwrap_err(), @@ -562,6 +610,7 @@ fn subscribe_pending_multiple_with_transition() { update_info_7500.sender.to_string(), ) .unwrap(); + // 184_615 + 200_000 = 384_615 assert_eq!( pos1_7500.purchased, Uint256::from(184_615u128 + 200_000u128) diff --git a/tests/update_position.rs b/tests/update_position.rs index 0d136d3..ea0dad1 100644 --- a/tests/update_position.rs +++ b/tests/update_position.rs @@ -11,6 +11,11 @@ fn update_position_unauthorized_operator() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define actors + let treasury = helpers::mock_info("creator", &[]); + let subscriber1 = helpers::mock_info("creator1", &[]); + let unauthorized = helpers::mock_info("random", &[]); + // Create a stream let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(1_000_000)) @@ -26,26 +31,28 @@ fn update_position_unauthorized_operator() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds; + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // First subscription let env = helpers::env_at(1_000_000 + 100); - let info = helpers::mock_info("creator1", &[Coin::new(1_000_000u128, "in")]); + let mut subscriber1_funded = subscriber1.clone(); + subscriber1_funded.funds = vec![Coin::new(1_000_000u128, "in")]; let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, operator: None, tos_version: "v1".to_string(), }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + let _res = execute(deps.as_mut(), env, subscriber1_funded, msg).unwrap(); // Non-owner cannot update position (should fail because random user has no position) let env = helpers::env_at(1_000_000 + 3_000_000); - let info = helpers::mock_info("random", &[]); + let info = unauthorized.clone(); let msg = cw_streamswap::msg::ExecuteMsg::UpdatePosition { stream_id: 1, - operator_target: Some(helpers::mock_info("creator1", &[]).sender.to_string()), + operator_target: Some(subscriber1.sender.to_string()), }; let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); assert_eq!(err, ContractError::Unauthorized {}); @@ -56,6 +63,10 @@ fn update_position_basic_flow() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define actors + let treasury = helpers::mock_info("creator", &[]); + let subscriber1 = helpers::mock_info("creator1", &[]); + // Create a stream let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(1_000_000)) @@ -71,23 +82,25 @@ fn update_position_basic_flow() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds; + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // First subscription let env = helpers::env_at(1_000_000 + 100); - let info = helpers::mock_info("creator1", &[Coin::new(1_000_000u128, "in")]); + let mut subscriber1_funded = subscriber1.clone(); + subscriber1_funded.funds = vec![Coin::new(1_000_000u128, "in")]; let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, operator: None, tos_version: "v1".to_string(), }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + let _res = execute(deps.as_mut(), env, subscriber1_funded, msg).unwrap(); // Update position let env = helpers::env_at(1_000_000 + 3_000_000); - let info = helpers::mock_info("creator1", &[]); + let info = subscriber1.clone(); let msg = cw_streamswap::msg::ExecuteMsg::UpdatePosition { stream_id: 1, operator_target: None, @@ -100,7 +113,7 @@ fn update_position_basic_flow() { deps.as_ref(), query_env.clone(), 1, - helpers::mock_info("creator1", &[]).sender.to_string(), + subscriber1.sender.to_string(), ) .unwrap(); assert_eq!( @@ -124,6 +137,10 @@ fn update_position_after_stream_ends() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define actors + let treasury = helpers::mock_info("creator", &[]); + let subscriber1 = helpers::mock_info("creator1", &[]); + // Create a stream let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(1_000_000)) @@ -139,23 +156,25 @@ fn update_position_after_stream_ends() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds; + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // First subscription let env = helpers::env_at(1_000_000 + 100); - let info = helpers::mock_info("creator1", &[Coin::new(1_000_000u128, "in")]); + let mut subscriber1_funded = subscriber1.clone(); + subscriber1_funded.funds = vec![Coin::new(1_000_000u128, "in")]; let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, operator: None, tos_version: "v1".to_string(), }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + let _res = execute(deps.as_mut(), env, subscriber1_funded, msg).unwrap(); // Update position after stream ends let env = helpers::env_at(5_000_000 + 1); - let info = helpers::mock_info("creator1", &[]); + let info = subscriber1.clone(); let msg = cw_streamswap::msg::ExecuteMsg::UpdatePosition { stream_id: 1, operator_target: None, @@ -174,7 +193,7 @@ fn update_position_after_stream_ends() { deps.as_ref(), query_env, 1, - helpers::mock_info("creator1", &[]).sender.to_string(), + subscriber1.sender.to_string(), ) .unwrap(); assert_eq!(position.index, Decimal256::from_str("1").unwrap()); diff --git a/tests/update_stream.rs b/tests/update_stream.rs index 2c9dcf2..f678d2a 100644 --- a/tests/update_stream.rs +++ b/tests/update_stream.rs @@ -10,6 +10,9 @@ fn update_stream_no_subscriptions() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define actors + let treasury = helpers::mock_info("creator", &[]); + // Create a stream that starts in the future let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(1_000_000)) @@ -25,8 +28,9 @@ fn update_stream_no_subscriptions() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds; + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // Update stream without subscriptions - no distribution should occur let env = helpers::env_at(1_000_000 + 100); // After start time @@ -63,6 +67,10 @@ fn update_stream_first_subscription() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define actors + let treasury = helpers::mock_info("creator", &[]); + let subscriber1 = helpers::mock_info("creator1", &[]); + // Create a stream let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(1_000_000)) @@ -78,19 +86,21 @@ fn update_stream_first_subscription() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds; + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // First subscription - dist_index should remain 0 (no prior distribution) let env = helpers::env_at(1_000_000 + 100); - let info = helpers::mock_info("creator1", &[Coin::new(1_000_000u128, "in")]); + let mut subscriber1_funded = subscriber1.clone(); + subscriber1_funded.funds = vec![Coin::new(1_000_000u128, "in")]; let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, operator: None, tos_version: "v1".to_string(), }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + let _res = execute(deps.as_mut(), env, subscriber1_funded, msg).unwrap(); // Query stream after first subscription - dist_index should still be 0 (no prior distribution) let env = helpers::env_at(1_000_000 + 200); @@ -103,6 +113,10 @@ fn update_stream_with_subscribers() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define actors + let treasury = helpers::mock_info("creator", &[]); + let subscriber1 = helpers::mock_info("creator1", &[]); + // Create a stream let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(1_000_000)) @@ -118,19 +132,21 @@ fn update_stream_with_subscribers() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds; + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // First subscription let env = helpers::env_at(1_000_000 + 100); - let info = helpers::mock_info("creator1", &[Coin::new(1_000_000u128, "in")]); + let mut subscriber1_funded = subscriber1.clone(); + subscriber1_funded.funds = vec![Coin::new(1_000_000u128, "in")]; let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, operator: None, tos_version: "v1".to_string(), }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + let _res = execute(deps.as_mut(), env, subscriber1_funded, msg).unwrap(); // Update stream again with existing subscribers - dist_index should increase let env = helpers::env_at(1_000_000 + 300); diff --git a/tests/withdraw.rs b/tests/withdraw.rs index 9e88353..947f37f 100644 --- a/tests/withdraw.rs +++ b/tests/withdraw.rs @@ -10,6 +10,11 @@ fn withdraw_pending_basic_flow() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define actors (MessageInfo with empty funds) + let treasury = helpers::mock_info("creator", &[]); + let subscriber1 = helpers::mock_info("subscriber1", &[]); + let _unauthorized = helpers::mock_info("unauthorized", &[]); + // Create a stream that hasn't started yet let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(2000)) // Start in the future @@ -25,19 +30,21 @@ fn withdraw_pending_basic_flow() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds; + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // First subscribe before start time (pending) let env = helpers::env_at(2000 - 1); // Before start time - let info = helpers::mock_info("subscriber1", &[Coin::new(1_000_000u128, "in")]); + let mut subscriber1_funded = subscriber1.clone(); + subscriber1_funded.funds = vec![Coin::new(1_000_000u128, "in")]; let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, operator: None, tos_version: "v1".to_string(), }; - let res = execute(deps.as_mut(), env, info, msg); + let res = execute(deps.as_mut(), env, subscriber1_funded, msg); assert!(res.is_ok()); // Update subscriber1 position - no distribution expected @@ -46,7 +53,7 @@ fn withdraw_pending_basic_flow() { stream_id: 1, operator_target: None, }; - let update_info = helpers::mock_info("subscriber1", &[]); + let update_info = subscriber1.clone(); let res = execute(deps.as_mut(), env.clone(), update_info, update_msg); assert!(res.is_ok()); @@ -82,7 +89,7 @@ fn withdraw_pending_basic_flow() { // Withdraw before start time (pending withdraw) let env = helpers::env_at(2000 - 1); - let info = helpers::mock_info("subscriber1", &[]); + let info = subscriber1.clone(); let msg = cw_streamswap::msg::ExecuteMsg::Withdraw { stream_id: 1, cap: Some(Uint256::from(500_000u128)), @@ -105,7 +112,7 @@ fn withdraw_pending_basic_flow() { assert_eq!( bank_msg, &CosmosMsg::Bank(BankMsg::Send { - to_address: info.sender.to_string(), + to_address: subscriber1.sender.to_string(), amount: vec![Coin::new(500000u128, "in")], }) ); @@ -125,6 +132,11 @@ fn withdraw_after_start_time() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define actors (MessageInfo with empty funds) + let treasury = helpers::mock_info("creator", &[]); + let subscriber1 = helpers::mock_info("subscriber1", &[]); + let _unauthorized = helpers::mock_info("unauthorized", &[]); + // Create a stream that hasn't started yet let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(2000)) // Start in the future @@ -140,24 +152,26 @@ fn withdraw_after_start_time() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds; + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // Subscribe before start time let env = helpers::env_at(2000 - 1); - let info = helpers::mock_info("subscriber1", &[Coin::new(1_000_000u128, "in")]); + let mut subscriber1_funded = subscriber1.clone(); + subscriber1_funded.funds = vec![Coin::new(1_000_000u128, "in")]; let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, operator: None, tos_version: "v1".to_string(), }; - let res = execute(deps.as_mut(), env, info, msg); + let res = execute(deps.as_mut(), env, subscriber1_funded, msg); assert!(res.is_ok()); // Withdraw after start time (active withdraw) let env = helpers::env_at(3000); // After start time (2000) - let info = helpers::mock_info("subscriber1", &[]); + let info = subscriber1.clone(); let msg = cw_streamswap::msg::ExecuteMsg::Withdraw { stream_id: 1, cap: Some(Uint256::from(400_000u128)), @@ -197,6 +211,11 @@ fn withdraw_invalid_amounts() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define all actors upfront + let treasury = helpers::mock_info("creator", &[]); + let subscriber1 = helpers::mock_info("subscriber1", &[]); + let _unauthorized = helpers::mock_info("unauthorized", &[]); + // Create a stream let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(1_000_000)) @@ -212,24 +231,26 @@ fn withdraw_invalid_amounts() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds.clone(); + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // Subscribe to the stream let env = helpers::env_at(1_000_000); let funds = Coin::new(2_000_000_000_000u128, "in"); - let info = helpers::mock_info("creator1", &[funds.clone()]); + let mut subscriber1_funded = subscriber1.clone(); + subscriber1_funded.funds = vec![funds]; let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, operator: None, tos_version: "v1".to_string(), }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + let _res = execute(deps.as_mut(), env, subscriber1_funded, msg).unwrap(); // Test 1: Withdraw with cap = 0 (should fail) let env = helpers::env_at(1_000_000 + 5000); - let info = helpers::mock_info("creator1", &[]); + let info = subscriber1.clone(); let cap = Uint256::zero(); let msg = cw_streamswap::msg::ExecuteMsg::Withdraw { stream_id: 1, @@ -263,6 +284,11 @@ fn withdraw_with_valid_cap() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define all actors upfront + let treasury = helpers::mock_info("creator", &[]); + let subscriber1 = helpers::mock_info("subscriber1", &[]); + let _unauthorized = helpers::mock_info("unauthorized", &[]); + // Create a stream let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(1_000_000)) @@ -278,24 +304,26 @@ fn withdraw_with_valid_cap() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); - execute(deps.as_mut(), env, info, b.build()).unwrap(); + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds; + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); // Subscribe to the stream let env = helpers::env_at(1_000_000); let funds = Coin::new(2_000_000_000_000u128, "in"); - let info = helpers::mock_info("creator1", &[funds.clone()]); + let mut subscriber1_funded = subscriber1.clone(); + subscriber1_funded.funds = vec![funds.clone()]; let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, operator: None, tos_version: "v1".to_string(), }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + let _res = execute(deps.as_mut(), env, subscriber1_funded, msg).unwrap(); // Withdraw with valid cap let env = helpers::env_at(1_000_000 + 5000); - let info = helpers::mock_info("creator1", &[]); + let info = subscriber1.clone(); let cap = Uint256::from(25_000_000u128); let msg = cw_streamswap::msg::ExecuteMsg::Withdraw { stream_id: 1, @@ -309,7 +337,7 @@ fn withdraw_with_valid_cap() { deps.as_ref(), helpers::env_at(1_000_000 + 5000), 1, - helpers::mock_info("creator1", &[]).sender.to_string(), + subscriber1.sender.to_string(), ) .unwrap(); @@ -329,6 +357,11 @@ fn withdraw_full_balance() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define all actors upfront + let treasury = helpers::mock_info("creator", &[]); + let subscriber = helpers::mock_info("subscriber1", &[]); + let _unauthorized = helpers::mock_info("unauthorized", &[]); + // Create a stream let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(1_000_000)) @@ -344,13 +377,19 @@ fn withdraw_full_balance() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); + let info = cosmwasm_std::MessageInfo { + sender: treasury.sender.clone(), + funds, + }; execute(deps.as_mut(), env, info, b.build()).unwrap(); // Subscribe to the stream let env = helpers::env_at(1_000_000); let funds = Coin::new(2_000_000_000_000u128, "in"); - let info = helpers::mock_info("creator1", &[funds.clone()]); + let info = cosmwasm_std::MessageInfo { + sender: subscriber.sender.clone(), + funds: vec![funds.clone()], + }; let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, @@ -361,7 +400,10 @@ fn withdraw_full_balance() { // Withdraw full balance (cap = None) let env = helpers::env_at(1_000_000 + 1_000_000); - let info = helpers::mock_info("creator1", &[]); + let info = cosmwasm_std::MessageInfo { + sender: subscriber.sender.clone(), + funds: vec![], + }; let msg = cw_streamswap::msg::ExecuteMsg::Withdraw { stream_id: 1, cap: None, @@ -374,7 +416,7 @@ fn withdraw_full_balance() { deps.as_ref(), helpers::env_at(1_000_000 + 1_000_000), 1, - helpers::mock_info("creator1", &[]).sender.to_string(), + subscriber.sender.to_string(), ) .unwrap(); @@ -388,7 +430,7 @@ fn withdraw_full_balance() { assert_eq!( msg.msg, CosmosMsg::Bank(BankMsg::Send { - to_address: helpers::mock_info("creator1", &[]).sender.to_string(), + to_address: subscriber.sender.to_string(), amount: vec![Coin::new(1500000000000u128, "in")] }) ); @@ -399,6 +441,11 @@ fn withdraw_after_stream_ends() { let mut deps = mock_dependencies(); helpers::instantiate_defaults(deps.as_mut()); + // Define all actors upfront + let treasury = helpers::mock_info("creator", &[]); + let subscriber = helpers::mock_info("subscriber1", &[]); + let _unauthorized = helpers::mock_info("unauthorized", &[]); + // Create a stream let b = helpers::CreateStreamBuilder::default() .start_time(Timestamp::from_seconds(1_000_000)) @@ -414,13 +461,19 @@ fn withdraw_after_stream_ends() { amount: Uint256::from(100u128), }, ]; - let info = helpers::mock_info("creator", &funds); + let info = cosmwasm_std::MessageInfo { + sender: treasury.sender.clone(), + funds, + }; execute(deps.as_mut(), env, info, b.build()).unwrap(); // Subscribe to the stream let env = helpers::env_at(1_000_000); let funds = Coin::new(2_000_000_000_000u128, "in"); - let info = helpers::mock_info("creator1", &[funds.clone()]); + let info = cosmwasm_std::MessageInfo { + sender: subscriber.sender.clone(), + funds: vec![funds.clone()], + }; let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { stream_id: 1, operator_target: None, @@ -431,7 +484,10 @@ fn withdraw_after_stream_ends() { // Attempt withdrawal after stream ends (should fail) let env = helpers::env_at(5_000_000 + 1); // After end time - let info = helpers::mock_info("creator1", &[]); + let info = cosmwasm_std::MessageInfo { + sender: subscriber.sender.clone(), + funds: vec![], + }; let msg = cw_streamswap::msg::ExecuteMsg::Withdraw { stream_id: 1, cap: None, From 9758e6e1b8dcb83e3d458e3c52677370a0c91a0c Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Tue, 26 Aug 2025 02:26:12 +0300 Subject: [PATCH 16/25] operator tests --- tests/operator.rs | 225 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 tests/operator.rs diff --git a/tests/operator.rs b/tests/operator.rs new file mode 100644 index 0000000..3b47128 --- /dev/null +++ b/tests/operator.rs @@ -0,0 +1,225 @@ +use cosmwasm_std::testing::mock_dependencies; +use cosmwasm_std::{BankMsg, Coin, CosmosMsg, Timestamp, Uint256}; +use cw_streamswap::contract::{execute, execute_finalize_stream}; +use cw_streamswap::ContractError; + +mod helpers; + +#[test] +fn operator_unauthorized_subscribe_on_behalf() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + // Define actors + let treasury = helpers::mock_info("creator", &[]); + let owner = helpers::mock_info("creator1", &[]); + let random = helpers::mock_info("random", &[]); + // Create stream + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(1_000_000u128); + let b = helpers::CreateStreamBuilder::default() + .start_time(start) + .end_time(end) + .out_supply(out_supply) + .out_denom("out_denom"); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds; + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); + let env = helpers::env_at(start.seconds() + 100); + let mut random_funded = random.clone(); + random_funded.funds = vec![Coin::new(1_000_000u128, "in")]; + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: Some(owner.sender.to_string()), + operator: None, + tos_version: "v1".to_string(), + }; + let err = execute(deps.as_mut(), env, random_funded, msg).unwrap_err(); + assert_eq!(err, ContractError::Unauthorized {}); +} + +#[test] +fn operator_only_owner_can_update() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + let treasury = helpers::mock_info("creator", &[]); + let owner = helpers::mock_info("creator1", &[]); + let other = helpers::mock_info("creator2", &[]); + + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(1_000_000u128); + let b = helpers::CreateStreamBuilder::default() + .start_time(start) + .end_time(end) + .out_supply(out_supply) + .out_denom("out_denom"); + let env0 = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds; + execute(deps.as_mut(), env0, treasury_funded, b.build()).unwrap(); + // Owner subscribes first + let env = helpers::env_at(start.seconds() + 100); + let mut owner_funded = owner.clone(); + owner_funded.funds = vec![Coin::new(1_000_000u128, "in")]; + execute( + deps.as_mut(), + env, + owner_funded, + cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }, + ) + .unwrap(); + // Other cannot update owner's position + let env = helpers::env_at(start.seconds() + 100); + let msg = cw_streamswap::msg::ExecuteMsg::UpdatePosition { + stream_id: 1, + operator_target: Some(owner.sender.to_string()), + }; + let err = execute(deps.as_mut(), env, other, msg).unwrap_err(); + assert_eq!(err, ContractError::Unauthorized {}); +} + +#[test] +fn operator_set_and_use_operator() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + let treasury = helpers::mock_info("creator", &[]); + let owner = helpers::mock_info("creator1", &[]); + let operator = helpers::mock_info("operator1", &[]); + let random = helpers::mock_info("random", &[]); + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(1_000_000u128); + let b = helpers::CreateStreamBuilder::default() + .start_time(start) + .end_time(end) + .out_supply(out_supply) + .out_denom("out_denom"); + let env0 = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds; + execute(deps.as_mut(), env0, treasury_funded, b.build()).unwrap(); + // Owner subscribes + let env = helpers::env_at(start.seconds() + 100); + let mut owner_funded = owner.clone(); + owner_funded.funds = vec![Coin::new(1_000_000u128, "in")]; + execute( + deps.as_mut(), + env, + owner_funded, + cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }, + ) + .unwrap(); + // Owner sets operator + let env = helpers::env_at(start.seconds() + 100); + execute( + deps.as_mut(), + env, + owner.clone(), + cw_streamswap::msg::ExecuteMsg::UpdateOperator { + stream_id: 1, + new_operator: Some(operator.sender.to_string()), + }, + ) + .unwrap(); + // Operator subscribes on behalf of owner + let env = helpers::env_at(start.seconds() + 100); + let mut operator_funded = operator.clone(); + operator_funded.funds = vec![Coin::new(1_000_000u128, "in")]; + let res = execute( + deps.as_mut(), + env, + operator_funded, + cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: Some(owner.sender.to_string()), + operator: None, + tos_version: "v1".to_string(), + }, + ) + .unwrap(); + assert_eq!(res.attributes[0].value, "subscribe"); + // Operator update position + let env = helpers::env_at(start.seconds() + 100); + let res = execute( + deps.as_mut(), + env, + operator.clone(), + cw_streamswap::msg::ExecuteMsg::UpdatePosition { + stream_id: 1, + operator_target: Some(owner.sender.to_string()), + }, + ) + .unwrap(); + assert_eq!(res.attributes[0].value, "update_position"); + // Finalize and check exit auth + let env = helpers::env_at(end.seconds() + 1); + execute_finalize_stream(deps.as_mut(), env.clone(), treasury.clone(), 1, None).unwrap(); + // Random cannot exit + let err = cw_streamswap::contract::execute_exit_stream( + deps.as_mut(), + env.clone(), + random.clone(), + 1, + Some(owner.sender.to_string()), + ) + .unwrap_err(); + assert_eq!(err, ContractError::Unauthorized {}); + // Operator can exit to owner + let res = cw_streamswap::contract::execute_exit_stream( + deps.as_mut(), + env, + operator, + 1, + Some(owner.sender.to_string()), + ) + .unwrap(); + match res.messages.first().unwrap().msg.clone() { + CosmosMsg::Bank(BankMsg::Send { to_address, .. }) => { + assert_eq!(to_address, owner.sender.to_string()) + } + _ => panic!("unexpected message"), + } +} From 7c7a026b559e54d5302693bcfa130ec95d94ec65 Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Tue, 26 Aug 2025 02:45:32 +0300 Subject: [PATCH 17/25] exit stream --- tests/exit.rs | 274 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 274 insertions(+) create mode 100644 tests/exit.rs diff --git a/tests/exit.rs b/tests/exit.rs new file mode 100644 index 0000000..7f1852b --- /dev/null +++ b/tests/exit.rs @@ -0,0 +1,274 @@ +use cosmwasm_std::testing::mock_dependencies; +use cosmwasm_std::{Addr, BankMsg, Coin, CosmosMsg, Timestamp, Uint256}; +use cw_streamswap::contract::{execute, execute_exit_stream, execute_update_stream}; +use cw_streamswap::ContractError; + +mod helpers; + +#[test] +fn exit_cannot_before_stream_ends() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Define actors (addresses) and a helper to build MessageInfo + let treasury: Addr = helpers::mock_info("creator", &[]).sender; + let subscriber1: Addr = helpers::mock_info("subscriber1", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Create stream + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(1_000_000_000_000u128); + let b = helpers::CreateStreamBuilder::default() + .start_time(start) + .end_time(end) + .out_supply(out_supply) + .out_denom("out_denom"); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = mk_info(&treasury, funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // First subscription + let env = helpers::env_at(start.seconds() + 1_000_000); + let info = mk_info(&subscriber1, vec![Coin::new(2_000_000_000_000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // Try to exit during stream + let env = helpers::env_at(start.seconds() + 2_000_000); + let info = mk_info(&subscriber1, vec![]); + let err = execute_exit_stream(deps.as_mut(), env, info, 1, None).unwrap_err(); + assert_eq!(err, ContractError::StreamNotEnded {}); +} + +#[test] +fn exit_unauthorized_on_behalf_fails() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Define actors (addresses) and a helper to build MessageInfo + let treasury: Addr = helpers::mock_info("creator", &[]).sender; + let subscriber1: Addr = helpers::mock_info("subscriber1", &[]).sender; + let unauthorized: Addr = helpers::mock_info("unauthorized", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Create stream + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(1_000_000_000_000u128); + let b = helpers::CreateStreamBuilder::default() + .start_time(start) + .end_time(end) + .out_supply(out_supply) + .out_denom("out_denom"); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = mk_info(&treasury, funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // First subscription + let env = helpers::env_at(start.seconds() + 1_000_000); + let info = mk_info(&subscriber1, vec![Coin::new(2_000_000_000_000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // After end, random cannot exit on behalf + let env = helpers::env_at(end.seconds() + 3_000_000); + let info = mk_info(&unauthorized, vec![]); + let err = execute_exit_stream(deps.as_mut(), env, info, 1, Some(subscriber1.to_string())) + .unwrap_err(); + assert_eq!(err, ContractError::Unauthorized {}); +} + +#[test] +fn exit_owner_success_and_position_deleted() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Define actors (addresses) and a helper to build MessageInfo + let treasury: Addr = helpers::mock_info("creator", &[]).sender; + let subscriber1: Addr = helpers::mock_info("subscriber1", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Create stream + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(1_000_000_000_000u128); + let b = helpers::CreateStreamBuilder::default() + .start_time(start) + .end_time(end) + .out_supply(out_supply) + .out_denom("out_denom"); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = mk_info(&treasury, funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // First subscription + let env = helpers::env_at(start.seconds() + 1_000_000); + let info = mk_info(&subscriber1, vec![Coin::new(2_000_000_000_000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // Exit after end + let env = helpers::env_at(end.seconds() + 3_000_000); + let info = mk_info(&subscriber1, vec![]); + let res = execute_exit_stream(deps.as_mut(), env, info, 1, None).unwrap(); + assert_eq!( + res.messages, + vec![cosmwasm_std::SubMsg::new(CosmosMsg::Bank(BankMsg::Send { + to_address: subscriber1.to_string(), + amount: vec![Coin::new(1_000_000_000_000u128, "out_denom")], + }))] + ); + + // Position deleted + let env = helpers::env_at(end.seconds() + 4_000_000); + let err = + cw_streamswap::contract::query_position(deps.as_ref(), env, 1, subscriber1.to_string()) + .unwrap_err(); + // We expect a not found error; comparing by string to avoid importing StdError variants here + assert!(err.to_string().contains("not found")); +} + +#[test] +fn exit_withdraw_all_before_exit_two_users() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Define actors (addresses) and a helper to build MessageInfo + let treasury: Addr = helpers::mock_info("creator", &[]).sender; + let subscriber1: Addr = helpers::mock_info("subscriber1", &[]).sender; + let subscriber2: Addr = helpers::mock_info("subscriber2", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Create stream + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(1_000_000_000_000u128); + let b = helpers::CreateStreamBuilder::default() + .start_time(start) + .end_time(end) + .out_supply(out_supply) + .out_denom("out_denom"); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = mk_info(&treasury, funds); + execute(deps.as_mut(), env, info, b.build()).unwrap(); + + // Subscriptions + let env = helpers::env_at(start.seconds() + 1_000_000); + let info = mk_info(&subscriber1, vec![Coin::new(2_000_000_000_000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + let env = helpers::env_at(start.seconds() + 1_000_000); + let info = mk_info(&subscriber2, vec![Coin::new(1_000_000_000_000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // Withdraw all before end for both + let env = helpers::env_at(end.seconds() - 1_000_000); + let info = mk_info(&subscriber1, vec![]); + let msg = cw_streamswap::msg::ExecuteMsg::Withdraw { + stream_id: 1, + cap: None, + operator_target: None, + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + let env = helpers::env_at(end.seconds() - 1_000_000); + let info = mk_info(&subscriber2, vec![]); + let msg = cw_streamswap::msg::ExecuteMsg::Withdraw { + stream_id: 1, + cap: None, + operator_target: None, + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // Ensure stream updated and both can exit after end + let env = helpers::env_at(end.seconds() + 1_000_000); + execute_update_stream(deps.as_mut(), env, 1).unwrap(); + + let env = helpers::env_at(end.seconds() + 1_000_001); + let info = mk_info(&subscriber1, vec![]); + execute_exit_stream(deps.as_mut(), env, info, 1, None).unwrap(); + + let env = helpers::env_at(end.seconds() + 1_000_002); + let info = mk_info(&subscriber2, vec![]); + execute_exit_stream(deps.as_mut(), env, info, 1, None).unwrap(); +} From 80f6054ec978430a4e1fdbbd2e9ec76e3dfbaf17 Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Tue, 26 Aug 2025 03:00:09 +0300 Subject: [PATCH 18/25] operational tests --- tests/price_feed.rs | 440 ++++++++++++++++++++++++++++++++++++++++ tests/protocol_admin.rs | 323 +++++++++++++++++++++++++++++ 2 files changed, 763 insertions(+) create mode 100644 tests/price_feed.rs create mode 100644 tests/protocol_admin.rs diff --git a/tests/price_feed.rs b/tests/price_feed.rs new file mode 100644 index 0000000..0669816 --- /dev/null +++ b/tests/price_feed.rs @@ -0,0 +1,440 @@ +use cosmwasm_std::testing::mock_dependencies; +use cosmwasm_std::{Addr, Coin, Decimal256, Timestamp, Uint256}; +use cw_streamswap::contract::{execute, execute_create_stream, execute_update_stream}; +use std::str::FromStr; + +mod helpers; + +#[test] +fn price_feed_initial_price_before_update() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Define actors (addresses) and a helper to build MessageInfo + let treasury: Addr = helpers::mock_info("creator", &[]).sender; + let subscriber1: Addr = helpers::mock_info("creator1", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Create stream + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(1_000_000u128); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = mk_info(&treasury, funds); + execute_create_stream( + deps.as_mut(), + env, + info, + treasury.to_string(), + "test".to_string(), + Some("https://sample.url".to_string()), + "in".to_string(), + "out_denom".to_string(), + out_supply, + start, + end, + None, + "v1".to_string(), + ) + .unwrap(); + + // First subscription + let env = helpers::env_at(start.seconds() + 1_000_000); + let info = mk_info(&subscriber1, vec![Coin::new(3_000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // Check current streamed price before update + let env = helpers::env_at(start.seconds() + 2_000_000); + let res = cw_streamswap::contract::query_last_streamed_price(deps.as_ref(), env, 1).unwrap(); + assert_eq!(res.current_streamed_price, Decimal256::zero()); +} + +#[test] +fn price_feed_price_after_first_update() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Define actors (addresses) and a helper to build MessageInfo + let treasury: Addr = helpers::mock_info("creator", &[]).sender; + let subscriber1: Addr = helpers::mock_info("creator1", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Create stream + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(1_000_000u128); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = mk_info(&treasury, funds); + execute_create_stream( + deps.as_mut(), + env, + info, + treasury.to_string(), + "test".to_string(), + Some("https://sample.url".to_string()), + "in".to_string(), + "out_denom".to_string(), + out_supply, + start, + end, + None, + "v1".to_string(), + ) + .unwrap(); + + // First subscription + let env = helpers::env_at(start.seconds() + 1_000_000); + let info = mk_info(&subscriber1, vec![Coin::new(3_000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // Check current streamed price after update + let env = helpers::env_at(start.seconds() + 2_000_000); + execute_update_stream(deps.as_mut(), env, 1).unwrap(); + let res = cw_streamswap::contract::query_last_streamed_price( + deps.as_ref(), + helpers::env_at(start.seconds() + 2_000_000), + 1, + ) + .unwrap(); + // approx 1000/333333 + assert_eq!( + res.current_streamed_price, + Decimal256::from_str("0.002997002997002997").unwrap() + ); +} + +#[test] +fn price_feed_price_with_two_subscribers() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Define actors (addresses) and a helper to build MessageInfo + let treasury: Addr = helpers::mock_info("creator", &[]).sender; + let subscriber1: Addr = helpers::mock_info("creator1", &[]).sender; + let subscriber2: Addr = helpers::mock_info("creator2", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Create stream + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(1_000_000u128); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = mk_info(&treasury, funds); + execute_create_stream( + deps.as_mut(), + env, + info, + treasury.to_string(), + "test".to_string(), + Some("https://sample.url".to_string()), + "in".to_string(), + "out_denom".to_string(), + out_supply, + start, + end, + None, + "v1".to_string(), + ) + .unwrap(); + + // First subscription + let env = helpers::env_at(start.seconds() + 1_000_000); + let info = mk_info(&subscriber1, vec![Coin::new(3_000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // Update stream after first subscription + let env = helpers::env_at(start.seconds() + 2_000_000); + execute_update_stream(deps.as_mut(), env, 1).unwrap(); + + // Second subscription + let env = helpers::env_at(start.seconds() + 2_000_000); + let info = mk_info(&subscriber2, vec![Coin::new(1_000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // Check current streamed price before update + let env = helpers::env_at(start.seconds() + 3_000_000); + let res = cw_streamswap::contract::query_last_streamed_price(deps.as_ref(), env, 1).unwrap(); + assert_eq!( + res.current_streamed_price, + Decimal256::from_str("0.002997002997002997").unwrap() + ); + + // Check current streamed price after update + let env = helpers::env_at(start.seconds() + 3_000_000); + execute_update_stream(deps.as_mut(), env, 1).unwrap(); + let res = cw_streamswap::contract::query_last_streamed_price( + deps.as_ref(), + helpers::env_at(start.seconds() + 3_000_000), + 1, + ) + .unwrap(); + // approx 2000/333333 + assert_eq!( + res.current_streamed_price, + Decimal256::from_str("0.0045000045000045").unwrap() + ); +} + +#[test] +fn price_feed_average_price() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Define actors (addresses) and a helper to build MessageInfo + let treasury: Addr = helpers::mock_info("creator", &[]).sender; + let subscriber1: Addr = helpers::mock_info("creator1", &[]).sender; + let subscriber2: Addr = helpers::mock_info("creator2", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Create stream + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(1_000_000u128); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = mk_info(&treasury, funds); + execute_create_stream( + deps.as_mut(), + env, + info, + treasury.to_string(), + "test".to_string(), + Some("https://sample.url".to_string()), + "in".to_string(), + "out_denom".to_string(), + out_supply, + start, + end, + None, + "v1".to_string(), + ) + .unwrap(); + + // First subscription + let env = helpers::env_at(start.seconds() + 1_000_000); + let info = mk_info(&subscriber1, vec![Coin::new(3_000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // Update stream after first subscription + let env = helpers::env_at(start.seconds() + 2_000_000); + execute_update_stream(deps.as_mut(), env, 1).unwrap(); + + // Second subscription + let env = helpers::env_at(start.seconds() + 2_000_000); + let info = mk_info(&subscriber2, vec![Coin::new(1_000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // Update stream after second subscription + let env = helpers::env_at(start.seconds() + 3_000_000); + execute_update_stream(deps.as_mut(), env, 1).unwrap(); + + // Check average streamed price + let env = helpers::env_at(start.seconds() + 3_000_000); + let res = cw_streamswap::contract::query_average_price(deps.as_ref(), env, 1).unwrap(); + // approx 2500/333333 + assert_eq!( + res.average_price, + Decimal256::from_str("0.003748503748503748").unwrap() + ); +} + +#[test] +fn price_feed_price_after_withdraw() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Define actors (addresses) and a helper to build MessageInfo + let treasury: Addr = helpers::mock_info("creator", &[]).sender; + let subscriber1: Addr = helpers::mock_info("creator1", &[]).sender; + let subscriber2: Addr = helpers::mock_info("creator2", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Create stream + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(1_000_000u128); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let info = mk_info(&treasury, funds); + execute_create_stream( + deps.as_mut(), + env, + info, + treasury.to_string(), + "test".to_string(), + Some("https://sample.url".to_string()), + "in".to_string(), + "out_denom".to_string(), + out_supply, + start, + end, + None, + "v1".to_string(), + ) + .unwrap(); + + // First subscription + let env = helpers::env_at(start.seconds() + 1_000_000); + let info = mk_info(&subscriber1, vec![Coin::new(3_000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // Update stream after first subscription + let env = helpers::env_at(start.seconds() + 2_000_000); + execute_update_stream(deps.as_mut(), env, 1).unwrap(); + + // Second subscription + let env = helpers::env_at(start.seconds() + 2_000_000); + let info = mk_info(&subscriber2, vec![Coin::new(1_000u128, "in")]); + let msg = cw_streamswap::msg::ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // Update stream after second subscription + let env = helpers::env_at(start.seconds() + 3_000_000); + execute_update_stream(deps.as_mut(), env, 1).unwrap(); + + // Withdraw creator 1 + let env = helpers::env_at(start.seconds() + 3_500_000); + let info = mk_info(&subscriber1, vec![]); + let msg = cw_streamswap::msg::ExecuteMsg::Withdraw { + stream_id: 1, + cap: None, + operator_target: None, + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + let res = cw_streamswap::contract::query_last_streamed_price( + deps.as_ref(), + helpers::env_at(start.seconds() + 3_500_000), + 1, + ) + .unwrap(); + assert_eq!( + res.current_streamed_price, + Decimal256::from_str("0.004499991000017999").unwrap() + ); + + // Test price after withdraw and update + let env = helpers::env_at(start.seconds() + 3_750_000); + execute_update_stream(deps.as_mut(), env, 1).unwrap(); + let res = cw_streamswap::contract::query_last_streamed_price( + deps.as_ref(), + helpers::env_at(start.seconds() + 3_750_000), + 1, + ) + .unwrap(); + // approx 2500/333333 + assert_eq!( + res.current_streamed_price, + Decimal256::from_str("0.001500006000024000").unwrap() + ); +} diff --git a/tests/protocol_admin.rs b/tests/protocol_admin.rs new file mode 100644 index 0000000..570847d --- /dev/null +++ b/tests/protocol_admin.rs @@ -0,0 +1,323 @@ +use cosmwasm_std::testing::mock_dependencies; +use cosmwasm_std::{Addr, Coin, Decimal256, Timestamp, Uint256, Uint64}; +use cw_streamswap::contract::execute; +use cw_streamswap::msg::ExecuteMsg; +use cw_streamswap::ContractError; + +mod helpers; + +#[test] +fn protocol_admin_unauthorized_update() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Define actors (addresses) and a helper to build MessageInfo + let random: Addr = helpers::mock_info("random", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Random cannot update protocol admin + let env = helpers::env_at(0); + let msg = ExecuteMsg::UpdateProtocolAdmin { + new_protocol_admin: helpers::mock_info("new_protocol_admin", &[]) + .sender + .to_string(), + }; + let info = mk_info(&random, vec![]); + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert_eq!(err, ContractError::Unauthorized {}); +} + +#[test] +fn protocol_admin_successful_update() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Define actors (addresses) and a helper to build MessageInfo + let protocol_admin: Addr = helpers::mock_info("protocol_admin", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Protocol admin can update + let env = helpers::env_at(0); + let msg = ExecuteMsg::UpdateProtocolAdmin { + new_protocol_admin: helpers::mock_info("new_protocol_admin", &[]) + .sender + .to_string(), + }; + let info = mk_info(&protocol_admin, vec![]); + execute(deps.as_mut(), env, info, msg).unwrap(); + + let query = cw_streamswap::contract::query_config(deps.as_ref()).unwrap(); + assert_eq!( + query.protocol_admin, + helpers::mock_info("new_protocol_admin", &[]) + .sender + .to_string() + ); +} + +#[test] +fn config_initial_values() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Query config and check initial values + let config_response = cw_streamswap::contract::query_config(deps.as_ref()).unwrap(); + assert_eq!(config_response.min_stream_seconds, Uint64::new(1000)); + assert_eq!( + config_response.min_seconds_until_start_time, + Uint64::new(1000) + ); + assert_eq!(config_response.stream_creation_denom, "fee".to_string()); + assert_eq!(config_response.stream_creation_fee, Uint256::from(100u128)); + assert_eq!( + config_response.fee_collector, + helpers::mock_info("collector", &[]).sender.to_string() + ); + assert_eq!( + config_response.protocol_admin, + helpers::mock_info("protocol_admin", &[]).sender.to_string() + ); + assert_eq!(config_response.accepted_in_denom, "in".to_string()); +} + +#[test] +fn config_unauthorized_update() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Define actors (addresses) and a helper to build MessageInfo + let random: Addr = helpers::mock_info("random", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Random user can't update config + let env = helpers::env_at(0); + let msg = ExecuteMsg::UpdateConfig { + min_stream_duration: Some(Uint64::new(2000)), + min_duration_until_start_time: Some(Uint64::new(2000)), + stream_creation_denom: Some("fee2".to_string()), + stream_creation_fee: Some(Uint256::from(200u128)), + fee_collector: Some("collector2".to_string()), + accepted_in_denom: Some("new_denom".to_string()), + exit_fee_percent: Some(Decimal256::percent(2)), + tos_version: None, + }; + let info = mk_info(&random, vec![]); + let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert_eq!(res, ContractError::Unauthorized {}); +} + +#[test] +fn config_invalid_stream_creation_fee() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Define actors (addresses) and a helper to build MessageInfo + let protocol_admin: Addr = helpers::mock_info("protocol_admin", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Wrong fee amount (zero) + let env = helpers::env_at(0); + let msg = ExecuteMsg::UpdateConfig { + min_stream_duration: Some(Uint64::new(2000)), + min_duration_until_start_time: Some(Uint64::new(2000)), + stream_creation_denom: Some("fee2".to_string()), + stream_creation_fee: Some(Uint256::from(0u128)), + fee_collector: Some("collector2".to_string()), + accepted_in_denom: Some("new_denom".to_string()), + exit_fee_percent: Some(Decimal256::percent(2)), + tos_version: None, + }; + let info = mk_info(&protocol_admin, vec![]); + let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert_eq!(res, ContractError::InvalidStreamCreationFee {}); +} + +#[test] +fn config_invalid_exit_fee_percent() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Define actors (addresses) and a helper to build MessageInfo + let protocol_admin: Addr = helpers::mock_info("protocol_admin", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Wrong exit fee percent (101%) + let env = helpers::env_at(0); + let msg = ExecuteMsg::UpdateConfig { + min_stream_duration: Some(Uint64::new(2000)), + min_duration_until_start_time: Some(Uint64::new(2000)), + stream_creation_denom: Some("fee2".to_string()), + stream_creation_fee: Some(Uint256::from(200u128)), + fee_collector: Some("collector2".to_string()), + accepted_in_denom: Some("new_denom".to_string()), + exit_fee_percent: Some(Decimal256::percent(101)), + tos_version: None, + }; + let info = mk_info(&protocol_admin, vec![]); + let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert_eq!(res, ContractError::InvalidExitFeePercent {}); +} + +#[test] +fn config_successful_update() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Define actors (addresses) and a helper to build MessageInfo + let protocol_admin: Addr = helpers::mock_info("protocol_admin", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Protocol admin can update config + let env = helpers::env_at(0); + let msg = ExecuteMsg::UpdateConfig { + min_stream_duration: Some(Uint64::new(2000)), + min_duration_until_start_time: Some(Uint64::new(2000)), + stream_creation_denom: Some("fee2".to_string()), + stream_creation_fee: Some(Uint256::from(200u128)), + fee_collector: Some(helpers::mock_info("collector2", &[]).sender.to_string()), + accepted_in_denom: Some("new_denom".to_string()), + exit_fee_percent: Some(Decimal256::percent(2)), + tos_version: None, + }; + let info = mk_info(&protocol_admin, vec![]); + execute(deps.as_mut(), env, info, msg).unwrap(); + + // Query config and check updated values + let config_response = cw_streamswap::contract::query_config(deps.as_ref()).unwrap(); + assert_eq!(config_response.min_stream_seconds, Uint64::new(2000)); + assert_eq!( + config_response.min_seconds_until_start_time, + Uint64::new(2000) + ); + assert_eq!(config_response.stream_creation_denom, "fee2".to_string()); + assert_eq!(config_response.stream_creation_fee, Uint256::from(200u128)); + assert_eq!( + config_response.fee_collector, + helpers::mock_info("collector2", &[]).sender.to_string() + ); + assert_eq!( + config_response.protocol_admin, + helpers::mock_info("protocol_admin", &[]).sender.to_string() + ); + assert_eq!(config_response.accepted_in_denom, "new_denom".to_string()); + assert_eq!(config_response.exit_fee_percent, Decimal256::percent(2)); +} + +#[test] +fn config_update_during_stream() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Define actors (addresses) and a helper to build MessageInfo + let protocol_admin: Addr = helpers::mock_info("protocol_admin", &[]).sender; + let creator: Addr = helpers::mock_info("creator1", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // First update config + let env = helpers::env_at(0); + let msg = ExecuteMsg::UpdateConfig { + min_stream_duration: Some(Uint64::new(2000)), + min_duration_until_start_time: Some(Uint64::new(2000)), + stream_creation_denom: Some("fee2".to_string()), + stream_creation_fee: Some(Uint256::from(200u128)), + fee_collector: Some(helpers::mock_info("collector2", &[]).sender.to_string()), + accepted_in_denom: Some("new_denom".to_string()), + exit_fee_percent: Some(Decimal256::percent(2)), + tos_version: None, + }; + let info = mk_info(&protocol_admin, vec![]); + execute(deps.as_mut(), env, info, msg).unwrap(); + + // Create stream with updated config + let out_supply = Uint256::from(1000u128); + let out_denom = "out"; + let start = Timestamp::from_seconds(10000); + let end = Timestamp::from_seconds(1000000); + let treasury = helpers::mock_info("treasury", &[]).sender.to_string(); + let env = helpers::env_at(0); + let info = mk_info( + &creator, + vec![ + Coin::new(out_supply.to_string().parse::().unwrap(), out_denom), + Coin::new(200u128, "fee2"), + ], + ); + cw_streamswap::contract::execute_create_stream( + deps.as_mut(), + env, + info, + treasury, + "test".to_string(), + Some("https://sample.url".to_string()), + "new_denom".to_string(), + out_denom.to_string(), + out_supply, + start, + end, + None, + "v1".to_string(), + ) + .unwrap(); + + // Update config during stream + let env = helpers::env_at(100000); + let msg = ExecuteMsg::UpdateConfig { + min_stream_duration: Some(Uint64::new(3000)), + min_duration_until_start_time: Some(Uint64::new(4000)), + stream_creation_denom: Some("fee3".to_string()), + stream_creation_fee: Some(Uint256::from(300u128)), + fee_collector: Some(helpers::mock_info("collector3", &[]).sender.to_string()), + accepted_in_denom: Some("new_denom2".to_string()), + exit_fee_percent: Some(Decimal256::percent(5)), + tos_version: None, + }; + let info = mk_info(&protocol_admin, vec![]); + execute(deps.as_mut(), env, info, msg).unwrap(); + + // Query config and check updated values + let config_response = cw_streamswap::contract::query_config(deps.as_ref()).unwrap(); + assert_eq!(config_response.min_stream_seconds, Uint64::new(3000)); + assert_eq!( + config_response.min_seconds_until_start_time, + Uint64::new(4000) + ); + assert_eq!(config_response.stream_creation_denom, "fee3".to_string()); + assert_eq!(config_response.stream_creation_fee, Uint256::from(300u128)); + assert_eq!( + config_response.fee_collector, + helpers::mock_info("collector3", &[]).sender.to_string() + ); + assert_eq!( + config_response.protocol_admin, + helpers::mock_info("protocol_admin", &[]).sender.to_string() + ); + assert_eq!(config_response.accepted_in_denom, "new_denom2".to_string()); + assert_eq!(config_response.exit_fee_percent, Decimal256::percent(5)); + + // Check stream still has old values + let env = helpers::env_at(100000); + let stream_response = cw_streamswap::contract::query_stream(deps.as_ref(), env, 1).unwrap(); + assert_eq!(stream_response.exit_fee_percent, Decimal256::percent(2)); + assert_eq!(stream_response.stream_creation_fee, Uint256::from(200u128)); +} From 7935b1dec85f5152c029a4c29c471d6951a5eb38 Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Tue, 26 Aug 2025 23:48:12 +0300 Subject: [PATCH 19/25] killswich --- tests/killswitch.rs | 1214 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1214 insertions(+) create mode 100644 tests/killswitch.rs diff --git a/tests/killswitch.rs b/tests/killswitch.rs new file mode 100644 index 0000000..8d61aef --- /dev/null +++ b/tests/killswitch.rs @@ -0,0 +1,1214 @@ +use cosmwasm_std::testing::mock_dependencies; +use cosmwasm_std::{Addr, BankMsg, Coin, CosmosMsg, Decimal256, Timestamp, Uint256, Uint64}; + +use cw_streamswap::contract::{ + execute, execute_create_stream, execute_finalize_stream, execute_update_position, + execute_update_stream, list_positions, list_streams, query_position, query_stream, sudo, +}; +use cw_streamswap::msg::{ExecuteMsg, InstantiateMsg, SudoMsg}; +use cw_streamswap::ContractError; + +mod helpers; + +#[test] +fn killswitch_pause_protocol_admin() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Actors and mk_info + let treasury: Addr = helpers::mock_info("treasury", &[]).sender; + let creator1: Addr = helpers::mock_info("creator1", &[]).sender; + let protocol_admin: Addr = helpers::mock_info("protocol_admin", &[]).sender; + let non_protocol_admin: Addr = helpers::mock_info("non_protocol_admin", &[]).sender; + let position1: Addr = helpers::mock_info("position1", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Create stream + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(1_000_000_000_000u128); + let out_denom = "out_denom"; + let env = helpers::env_at(0); + let info = mk_info( + &creator1, + vec![ + Coin::new(out_supply.to_string().parse::().unwrap(), out_denom), + Coin::new(100u128, helpers::DEFAULT_STREAM_CREATION_DENOM), + ], + ); + execute_create_stream( + deps.as_mut(), + env, + info, + treasury.to_string(), + "test".to_string(), + Some("https://sample.url".to_string()), + helpers::DEFAULT_ACCEPTED_IN_DENOM.to_string(), + out_denom.to_string(), + out_supply, + start, + end, + None, + "v1".to_string(), + ) + .unwrap(); + + // first subscription (ensure position exists) + let env = helpers::env_at(start.plus_seconds(1_000_000).seconds()); + let info = mk_info( + &position1, + vec![Coin::new(3_000u128, helpers::DEFAULT_ACCEPTED_IN_DENOM)], + ); + let msg = ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // non protocol admin can't pause + let env = helpers::env_at(start.seconds() + 100); + let err = execute( + deps.as_mut(), + env, + mk_info(&non_protocol_admin, vec![]), + ExecuteMsg::PauseStream { stream_id: 1 }, + ) + .unwrap_err(); + assert_eq!(err, ContractError::Unauthorized {}); + + // can't pause before start time + let env = helpers::env_at(start.minus_seconds(500_000).seconds()); + let err = execute( + deps.as_mut(), + env, + mk_info(&protocol_admin, vec![]), + ExecuteMsg::PauseStream { stream_id: 1 }, + ) + .unwrap_err(); + assert_eq!(err, ContractError::StreamNotStarted {}); + + // can't pause after end time + let env = helpers::env_at(end.plus_seconds(500_000).seconds()); + let err = execute( + deps.as_mut(), + env, + mk_info(&protocol_admin, vec![]), + ExecuteMsg::PauseStream { stream_id: 1 }, + ) + .unwrap_err(); + assert_eq!(err, ContractError::StreamEnded {}); + + // protocol admin can pause + let env = helpers::env_at(start.plus_seconds(1_000_001).seconds()); + execute( + deps.as_mut(), + env, + mk_info(&protocol_admin, vec![]), + ExecuteMsg::PauseStream { stream_id: 1 }, + ) + .unwrap(); + + // can't pause if already paused + let env = helpers::env_at(start.plus_seconds(1_000_005).seconds()); + let err = execute( + deps.as_mut(), + env, + mk_info(&protocol_admin, vec![]), + ExecuteMsg::PauseStream { stream_id: 1 }, + ) + .unwrap_err(); + assert_eq!(err, ContractError::StreamKillswitchActive {}); + + // can't subscribe new + let env = helpers::env_at(start.plus_seconds(1_000_002).seconds()); + let info = mk_info( + &Addr::clone(&helpers::mock_info("position2", &[]).sender), + vec![Coin::new(3_000u128, helpers::DEFAULT_ACCEPTED_IN_DENOM)], + ); + let msg = ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + let err = execute(deps.as_mut(), env.clone(), info, msg).unwrap_err(); + assert_eq!(err, ContractError::StreamKillswitchActive {}); + + // can't subscribe more + let info = mk_info( + &position1, + vec![Coin::new(3_000u128, helpers::DEFAULT_ACCEPTED_IN_DENOM)], + ); + let msg = ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + let err = execute(deps.as_mut(), env.clone(), info, msg).unwrap_err(); + assert_eq!(err, ContractError::StreamKillswitchActive {}); + + // can't withdraw + let info = mk_info(&position1, vec![]); + let msg = ExecuteMsg::Withdraw { + stream_id: 1, + cap: None, + operator_target: None, + }; + let err = execute(deps.as_mut(), env.clone(), info, msg).unwrap_err(); + assert_eq!(err, ContractError::StreamKillswitchActive {}); + + // can't update stream + let err = execute_update_stream(deps.as_mut(), env.clone(), 1).unwrap_err(); + assert_eq!(err, ContractError::StreamPaused {}); + + // can't update position + let info = mk_info(&position1, vec![]); + let err = execute_update_position(deps.as_mut(), env, info, 1, None).unwrap_err(); + assert_eq!(err, ContractError::StreamPaused {}); + + // can't finalize + let env = helpers::env_at(end.plus_seconds(1_000_002).seconds()); + let info = mk_info(&treasury, vec![]); + let err = execute_finalize_stream(deps.as_mut(), env, info, 1, None).unwrap_err(); + assert_eq!(err, ContractError::StreamKillswitchActive {}); + + // can't exit + let env = helpers::env_at(end.plus_seconds(1_000_002).seconds()); + let info = mk_info(&position1, vec![]); + let msg = ExecuteMsg::ExitStream { + stream_id: 1, + operator_target: None, + }; + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert_eq!(err, ContractError::StreamKillswitchActive {}); +} + +#[test] +fn killswitch_resume_protocol_admin() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Actors + let treasury: Addr = helpers::mock_info("treasury", &[]).sender; + let creator1: Addr = helpers::mock_info("creator1", &[]).sender; + let protocol_admin: Addr = helpers::mock_info("protocol_admin", &[]).sender; + let non_protocol_admin: Addr = helpers::mock_info("non_protocol_admin", &[]).sender; + let position1: Addr = helpers::mock_info("position1", &[]).sender; + let position2: Addr = helpers::mock_info("position2", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Create stream + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(1_000_000_000_000u128); + let out_denom = "out_denom"; + let env = helpers::env_at(0); + let info = mk_info( + &creator1, + vec![ + Coin::new(out_supply.to_string().parse::().unwrap(), out_denom), + Coin::new(100u128, helpers::DEFAULT_STREAM_CREATION_DENOM), + ], + ); + execute_create_stream( + deps.as_mut(), + env, + info, + treasury.to_string(), + "test".to_string(), + Some("https://sample.url".to_string()), + helpers::DEFAULT_ACCEPTED_IN_DENOM.to_string(), + out_denom.to_string(), + out_supply, + start, + end, + None, + "v1".to_string(), + ) + .unwrap(); + + // first subscription + let env = helpers::env_at(start.plus_seconds(1_000_000).seconds()); + let info = mk_info( + &position1, + vec![Coin::new(3_000u128, helpers::DEFAULT_ACCEPTED_IN_DENOM)], + ); + let msg = ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // can't resume if not paused + let env = helpers::env_at(start.plus_seconds(1_000_003).seconds()); + let err = execute( + deps.as_mut(), + env, + mk_info(&protocol_admin, vec![]), + ExecuteMsg::ResumeStream { stream_id: 1 }, + ) + .unwrap_err(); + assert_eq!(err, ContractError::StreamNotPaused {}); + + // pause + let env = helpers::env_at(start.plus_seconds(1_000_001).seconds()); + execute( + deps.as_mut(), + env, + mk_info(&protocol_admin, vec![]), + ExecuteMsg::PauseStream { stream_id: 1 }, + ) + .unwrap(); + + // can't subscribe new during pause + let env = helpers::env_at(start.plus_seconds(1_000_002).seconds()); + let info = mk_info( + &position2, + vec![Coin::new(3_000u128, helpers::DEFAULT_ACCEPTED_IN_DENOM)], + ); + let msg = ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert_eq!(err, ContractError::StreamKillswitchActive {}); + + // non protocol admin can't resume + let env = helpers::env_at(start.plus_seconds(1_000_003).seconds()); + let err = execute( + deps.as_mut(), + env, + mk_info(&non_protocol_admin, vec![]), + ExecuteMsg::ResumeStream { stream_id: 1 }, + ) + .unwrap_err(); + assert_eq!(err, ContractError::Unauthorized {}); + + // protocol admin can resume + let env = helpers::env_at(start.plus_seconds(1_000_003).seconds()); + execute( + deps.as_mut(), + env, + mk_info(&protocol_admin, vec![]), + ExecuteMsg::ResumeStream { stream_id: 1 }, + ) + .unwrap(); + + // can subscribe after resume + let env = helpers::env_at(start.plus_seconds(1_000_004).seconds()); + let info = mk_info( + &position2, + vec![Coin::new(3_000u128, helpers::DEFAULT_ACCEPTED_IN_DENOM)], + ); + let msg = ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + let res = execute(deps.as_mut(), env, info, msg).unwrap(); + assert_eq!(res.attributes[0].key, "action"); + assert_eq!(res.attributes[1].key, "stream_id"); + assert_eq!(res.attributes[2].key, "in_balance"); + assert_eq!(res.attributes[3].key, "shares"); + assert_eq!(res.attributes[4].key, "index"); + assert_eq!(res.attributes[5].key, "last_updated"); + assert_eq!(res.attributes[6].key, "pending_purchase"); + assert_eq!(res.attributes[7].key, "purchased"); + assert_eq!(res.attributes[8].key, "spent"); + assert_eq!(res.attributes[9].key, "tos_version"); + assert_eq!(res.attributes[10].key, "stream_new_in_supply"); + assert_eq!(res.attributes[11].key, "stream_previous_in_supply"); + assert_eq!(res.attributes[12].key, "stream_new_shares"); + assert_eq!(res.attributes[13].key, "stream_previous_shares"); + assert_eq!(res.attributes[14].key, "stream_status"); + // values (must match the original test) + assert_eq!(res.attributes[0].value, "subscribe"); + assert_eq!(res.attributes[1].value, "1"); + assert_eq!(res.attributes[2].value, "3000"); + assert_eq!(res.attributes[3].value, "3000"); + assert_eq!(res.attributes[4].value, "222.222"); + assert_eq!(res.attributes[5].value, "2000004.000000000"); + assert_eq!(res.attributes[6].value, "0"); + assert_eq!(res.attributes[7].value, "0"); + assert_eq!(res.attributes[8].value, "0"); + assert_eq!(res.attributes[9].value, "v1"); + assert_eq!(res.attributes[10].value, "6000"); + assert_eq!(res.attributes[11].value, "6000"); + assert_eq!(res.attributes[12].value, "6000"); + assert_eq!(res.attributes[13].value, "6000"); + assert_eq!(res.attributes[14].value, "Active"); + + // protocol admin can pause then cancel then resume should fail with cancelled + let env = helpers::env_at(start.plus_seconds(1_000_005).seconds()); + execute( + deps.as_mut(), + env, + mk_info(&protocol_admin, vec![]), + ExecuteMsg::PauseStream { stream_id: 1 }, + ) + .unwrap(); + + let env = helpers::env_at(start.plus_seconds(1_000_006).seconds()); + execute( + deps.as_mut(), + env, + mk_info(&protocol_admin, vec![]), + ExecuteMsg::CancelStream { stream_id: 1 }, + ) + .unwrap(); + + let env = helpers::env_at(start.plus_seconds(1_000_007).seconds()); + let err = execute( + deps.as_mut(), + env, + mk_info(&protocol_admin, vec![]), + ExecuteMsg::ResumeStream { stream_id: 1 }, + ) + .unwrap_err(); + assert_eq!(err, ContractError::StreamIsCancelled {}); +} + +#[test] +fn killswitch_cancel_protocol_admin() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Actors + let treasury: Addr = helpers::mock_info("treasury", &[]).sender; + let creator1: Addr = helpers::mock_info("creator1", &[]).sender; + let protocol_admin: Addr = helpers::mock_info("protocol_admin", &[]).sender; + let non_protocol_admin: Addr = helpers::mock_info("non_protocol_admin", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Create stream + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(1_000_000_000_000u128); + let out_denom = "out_denom"; + let env = helpers::env_at(0); + let info = mk_info( + &creator1, + vec![ + Coin::new(out_supply.to_string().parse::().unwrap(), out_denom), + Coin::new(100u128, helpers::DEFAULT_STREAM_CREATION_DENOM), + ], + ); + execute_create_stream( + deps.as_mut(), + env, + info, + treasury.to_string(), + "test".to_string(), + Some("https://sample.url".to_string()), + helpers::DEFAULT_ACCEPTED_IN_DENOM.to_string(), + out_denom.to_string(), + out_supply, + start, + end, + None, + "v1".to_string(), + ) + .unwrap(); + + // Subscribe + let env = helpers::env_at(start.seconds()); + let info = mk_info( + &creator1, + vec![Coin::new( + 2_000_000_000_000u128, + helpers::DEFAULT_ACCEPTED_IN_DENOM, + )], + ); + let msg = ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: Some(helpers::mock_info("operator", &[]).sender.to_string()), + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // non protocol admin can't cancel + let env = helpers::env_at(start.plus_seconds(1_000_000).seconds()); + let err = execute( + deps.as_mut(), + env, + mk_info(&non_protocol_admin, vec![]), + ExecuteMsg::CancelStream { stream_id: 1 }, + ) + .unwrap_err(); + assert_eq!(err, ContractError::Unauthorized {}); + + // can't cancel without pause + let env = helpers::env_at(start.plus_seconds(1_000_000).seconds()); + let err = execute( + deps.as_mut(), + env, + mk_info(&protocol_admin, vec![]), + ExecuteMsg::CancelStream { stream_id: 1 }, + ) + .unwrap_err(); + assert_eq!(err, ContractError::StreamNotPaused {}); + + // pause then cancel + let env = helpers::env_at(start.plus_seconds(2_000_000).seconds()); + execute( + deps.as_mut(), + env, + mk_info(&protocol_admin, vec![]), + ExecuteMsg::PauseStream { stream_id: 1 }, + ) + .unwrap(); + + let env = helpers::env_at(start.plus_seconds(2_500_000).seconds()); + let res = execute( + deps.as_mut(), + env, + mk_info(&protocol_admin, vec![]), + ExecuteMsg::CancelStream { stream_id: 1 }, + ) + .unwrap(); + + // out tokens and creation fee refunded to treasury + assert_eq!(res.messages.len(), 2); + if let cosmwasm_std::SubMsg { + msg: CosmosMsg::Bank(BankMsg::Send { to_address, amount }), + .. + } = &res.messages[0] + { + assert_eq!(to_address, &treasury.to_string()); + assert_eq!(amount.len(), 1); + assert_eq!(amount[0].denom, out_denom.to_string()); + assert_eq!(amount[0].amount, out_supply); + } else { + panic!("unexpected message variant"); + } + if let cosmwasm_std::SubMsg { + msg: CosmosMsg::Bank(BankMsg::Send { to_address, amount }), + .. + } = &res.messages[1] + { + assert_eq!(to_address, &treasury.to_string()); + assert_eq!(amount.len(), 1); + assert_eq!( + amount[0].denom, + helpers::DEFAULT_STREAM_CREATION_DENOM.to_string() + ); + assert_eq!(amount[0].amount, Uint256::from(100u128)); + } else { + panic!("unexpected message variant"); + } + + // can't cancel cancelled + let env = helpers::env_at(start.plus_seconds(2_500_000).seconds()); + let err = execute( + deps.as_mut(), + env, + mk_info(&protocol_admin, vec![]), + ExecuteMsg::CancelStream { stream_id: 1 }, + ) + .unwrap_err(); + assert_eq!(err, ContractError::StreamIsCancelled {}); +} +#[test] +fn killswitch_withdraw_paused_flow() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Actors + let treasury: Addr = helpers::mock_info("treasury", &[]).sender; + let creator1: Addr = helpers::mock_info("creator1", &[]).sender; + let protocol_admin: Addr = helpers::mock_info("protocol_admin", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Create stream + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(1_000_000_000_000u128); + let out_denom = "out_denom"; + let env = helpers::env_at(0); + let info = mk_info( + &creator1, + vec![ + Coin::new(out_supply.to_string().parse::().unwrap(), out_denom), + Coin::new(100u128, helpers::DEFAULT_STREAM_CREATION_DENOM), + ], + ); + execute_create_stream( + deps.as_mut(), + env, + info, + treasury.to_string(), + "test".to_string(), + Some("https://sample.url".to_string()), + helpers::DEFAULT_ACCEPTED_IN_DENOM.to_string(), + out_denom.to_string(), + out_supply, + start, + end, + None, + "v1".to_string(), + ) + .unwrap(); + + // subscription + let env = helpers::env_at(start.seconds()); + let funds = Coin::new(2_000_000_000_000u128, helpers::DEFAULT_ACCEPTED_IN_DENOM); + let info = mk_info(&creator1, vec![funds.clone()]); + let msg = ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: Some(helpers::mock_info("operator", &[]).sender.to_string()), + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // normal withdraw (not paused) with cap succeeds before pause + let env = helpers::env_at(start.plus_seconds(5_000).seconds()); + let cap = Uint256::from(25_000_000u128); + execute( + deps.as_mut(), + env.clone(), + mk_info(&creator1, vec![]), + ExecuteMsg::Withdraw { + stream_id: 1, + cap: Some(cap), + operator_target: None, + }, + ) + .unwrap(); + + let position = query_position(deps.as_ref(), env.clone(), 1, creator1.to_string()).unwrap(); + assert_eq!(position.in_balance, Uint256::from(1_997_475_000_000u128)); + assert_eq!(position.spent, Uint256::from(2_500_000_000u128)); + assert_eq!(position.purchased, Uint256::from(1_250_000_000u128)); + // first fund amount should be equal to in_balance + spent + cap + assert_eq!(position.in_balance + position.spent + cap, funds.amount); + + // pause + let env = helpers::env_at(start.plus_seconds(6_000).seconds()); + execute( + deps.as_mut(), + env, + mk_info(&protocol_admin, vec![]), + ExecuteMsg::PauseStream { stream_id: 1 }, + ) + .unwrap(); + + // withdraw paused with cap + let env = helpers::env_at(start.plus_seconds(7_000).seconds()); + execute( + deps.as_mut(), + env, + mk_info(&creator1, vec![]), + ExecuteMsg::WithdrawPaused { + stream_id: 1, + cap: Some(Uint256::from(25_000_000u128)), + operator_target: None, + }, + ) + .unwrap(); + + // withdraw after pause (remaining) + let env = helpers::env_at(start.plus_seconds(7_000).seconds()); + let _res = execute( + deps.as_mut(), + env, + mk_info(&creator1, vec![]), + ExecuteMsg::WithdrawPaused { + stream_id: 1, + cap: None, + operator_target: None, + }, + ) + .unwrap(); + + // stream not updated; check state + let env = helpers::env_at(start.plus_seconds(8_000).seconds()); + let stream_new = query_stream(deps.as_ref(), env, 1).unwrap(); + assert_eq!(stream_new.in_supply, Uint256::zero()); + assert_eq!(stream_new.shares, Uint256::zero()); + + // position updated + let position = query_position( + deps.as_ref(), + helpers::env_at(start.plus_seconds(8_001).seconds()), + 1, + creator1.to_string(), + ) + .unwrap(); + assert_eq!(position.in_balance, Uint256::zero()); + assert_eq!(position.spent, Uint256::from(2_999_993_742u128)); + assert_eq!(position.purchased, Uint256::from(1_499_999_998u128)); + assert_eq!(position.shares, Uint256::zero()); +} + +#[test] +fn killswitch_sudo_resume_updates_end_time() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Actors + let treasury: Addr = helpers::mock_info("treasury", &[]).sender; + let creator1: Addr = helpers::mock_info("creator1", &[]).sender; + let protocol_admin: Addr = helpers::mock_info("protocol_admin", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Create stream + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(1_000_000_000_000u128); + let out_denom = "out_denom"; + let env = helpers::env_at(0); + let info = mk_info( + &creator1, + vec![ + Coin::new(out_supply.to_string().parse::().unwrap(), out_denom), + Coin::new(100u128, helpers::DEFAULT_STREAM_CREATION_DENOM), + ], + ); + execute_create_stream( + deps.as_mut(), + env, + info, + treasury.to_string(), + "test".to_string(), + Some("https://sample.url".to_string()), + helpers::DEFAULT_ACCEPTED_IN_DENOM.to_string(), + out_denom.to_string(), + out_supply, + start, + end, + None, + "v1".to_string(), + ) + .unwrap(); + + // first subscription + let env = helpers::env_at(start.plus_seconds(1_000_000).seconds()); + let info = mk_info( + &Addr::clone(&helpers::mock_info("position1", &[]).sender), + vec![Coin::new(3_000u128, helpers::DEFAULT_ACCEPTED_IN_DENOM)], + ); + let msg = ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // pause + let pause_date = start.plus_seconds(2_000_000); + let env = helpers::env_at(pause_date.seconds()); + execute( + deps.as_mut(), + env, + mk_info(&protocol_admin, vec![]), + ExecuteMsg::PauseStream { stream_id: 1 }, + ) + .unwrap(); + + // resume via sudo + let resume_date = start.plus_seconds(3_000_000); + let env = helpers::env_at(resume_date.seconds()); + sudo(deps.as_mut(), env, SudoMsg::ResumeStream { stream_id: 1 }).unwrap(); + + // new end date is correct + let new_end_date = end.plus_nanos(resume_date.nanos() - pause_date.nanos()); + let stream = query_stream(deps.as_ref(), helpers::env_at(0), 1).unwrap(); + assert_eq!(stream.end_time, new_end_date); +} + +#[test] +fn killswitch_sudo_pause_stream() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Actors + let treasury: Addr = helpers::mock_info("treasury", &[]).sender; + let creator1: Addr = helpers::mock_info("creator1", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Create stream + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(1_000_000_000_000u128); + let out_denom = "out_denom"; + let env = helpers::env_at(0); + let info = mk_info( + &creator1, + vec![ + Coin::new(out_supply.to_string().parse::().unwrap(), out_denom), + Coin::new(100u128, helpers::DEFAULT_STREAM_CREATION_DENOM), + ], + ); + execute_create_stream( + deps.as_mut(), + env, + info, + treasury.to_string(), + "test".to_string(), + Some("https://sample.url".to_string()), + helpers::DEFAULT_ACCEPTED_IN_DENOM.to_string(), + out_denom.to_string(), + out_supply, + start, + end, + None, + "v1".to_string(), + ) + .unwrap(); + + // not started + let env = helpers::env_at(500_000); + let err = sudo(deps.as_mut(), env, SudoMsg::PauseStream { stream_id: 1 }).unwrap_err(); + assert_eq!(err, ContractError::StreamNotStarted {}); + + // ended + let env = helpers::env_at(6_000_000); + let err = sudo(deps.as_mut(), env, SudoMsg::PauseStream { stream_id: 1 }).unwrap_err(); + assert_eq!(err, ContractError::StreamEnded {}); + + // success + let env = helpers::env_at(3_000_000); + let res = sudo(deps.as_mut(), env, SudoMsg::PauseStream { stream_id: 1 }).unwrap(); + assert_eq!( + res, + cosmwasm_std::Response::new() + .add_attribute("action", "sudo_pause_stream") + .add_attribute("stream_id", "1") + .add_attribute("is_paused", "true") + .add_attribute("pause_date", "3000000.000000000") + ); + + // double pause + let env = helpers::env_at(4_000_000); + let err = sudo(deps.as_mut(), env, SudoMsg::PauseStream { stream_id: 1 }).unwrap_err(); + assert_eq!(err, ContractError::StreamKillswitchActive {}); +} + +#[test] +fn killswitch_range_queries() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Actors + let treasury: Addr = helpers::mock_info("treasury", &[]).sender; + let creator1: Addr = helpers::mock_info("creator1", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + let start = Timestamp::from_seconds(2_000); + let end = Timestamp::from_seconds(1_000_000); + let out_supply = Uint256::from(1_000_000u128); + let out_denom = "out_denom"; + + // first stream + let env = helpers::env_at(1); + let info = mk_info( + &creator1, + vec![ + Coin::new(out_supply.to_string().parse::().unwrap(), out_denom), + Coin::new(100u128, helpers::DEFAULT_STREAM_CREATION_DENOM), + ], + ); + execute_create_stream( + deps.as_mut(), + env.clone(), + info.clone(), + treasury.to_string(), + "test".to_string(), + Some("https://sample.url".to_string()), + helpers::DEFAULT_ACCEPTED_IN_DENOM.to_string(), + out_denom.to_string(), + out_supply, + start, + end, + None, + "v1".to_string(), + ) + .unwrap(); + + // second stream + execute_create_stream( + deps.as_mut(), + env, + info, + treasury.to_string(), + "test".to_string(), + Some("https://sample.url".to_string()), + helpers::DEFAULT_ACCEPTED_IN_DENOM.to_string(), + out_denom.to_string(), + out_supply, + start, + end, + None, + "v1".to_string(), + ) + .unwrap(); + + let res = list_streams(deps.as_ref(), None, None).unwrap(); + assert_eq!(res.streams.len(), 2); + + // subscriptions to first stream + let env = helpers::env_at(start.plus_seconds(100).seconds()); + let info = mk_info( + &creator1, + vec![Coin::new(1_000_000u128, helpers::DEFAULT_ACCEPTED_IN_DENOM)], + ); + let msg = ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + let env = helpers::env_at(start.plus_seconds(100).seconds()); + let info = mk_info( + &Addr::clone(&helpers::mock_info("creator2", &[]).sender), + vec![Coin::new(1_000_000u128, helpers::DEFAULT_ACCEPTED_IN_DENOM)], + ); + let msg = ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + let res = list_positions(deps.as_ref(), 1, None, None).unwrap(); + assert_eq!(res.positions.len(), 2); +} + +#[test] +fn killswitch_exit_cancel() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Actors + let treasury: Addr = helpers::mock_info("treasury", &[]).sender; + let creator1: Addr = helpers::mock_info("creator1", &[]).sender; + let protocol_admin: Addr = helpers::mock_info("protocol_admin", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Create stream + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(1_000_000_000_000u128); + let out_denom = "out_denom"; + let env = helpers::env_at(0); + let info = mk_info( + &creator1, + vec![ + Coin::new(out_supply.to_string().parse::().unwrap(), out_denom), + Coin::new(100u128, helpers::DEFAULT_STREAM_CREATION_DENOM), + ], + ); + execute_create_stream( + deps.as_mut(), + env, + info, + treasury.to_string(), + "test".to_string(), + Some("https://sample.url".to_string()), + helpers::DEFAULT_ACCEPTED_IN_DENOM.to_string(), + out_denom.to_string(), + out_supply, + start, + end, + None, + "v1".to_string(), + ) + .unwrap(); + + // subscription + let env = helpers::env_at(start.seconds()); + let info = mk_info( + &creator1, + vec![Coin::new( + 2_000_000_000_000u128, + helpers::DEFAULT_ACCEPTED_IN_DENOM, + )], + ); + let msg = ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: Some(helpers::mock_info("operator", &[]).sender.to_string()), + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // cant cancel without pause (sudo) + let env = helpers::env_at(start.plus_seconds(1_000_000).seconds()); + let err = sudo(deps.as_mut(), env, SudoMsg::CancelStream { stream_id: 1 }).unwrap_err(); + assert_eq!(err, ContractError::StreamNotPaused {}); + + // pause + let env = helpers::env_at(start.plus_seconds(2_000_000).seconds()); + execute( + deps.as_mut(), + env, + mk_info(&protocol_admin, vec![]), + ExecuteMsg::PauseStream { stream_id: 1 }, + ) + .unwrap(); + + // can't exit before cancel (ExitCancelled) + let env = helpers::env_at(start.plus_seconds(2_250_000).seconds()); + let err = execute( + deps.as_mut(), + env, + mk_info(&creator1, vec![]), + ExecuteMsg::ExitCancelled { + stream_id: 1, + operator_target: None, + }, + ) + .unwrap_err(); + assert_eq!(err, ContractError::StreamNotCancelled {}); + + // cancel + let env = helpers::env_at(start.plus_seconds(2_500_000).seconds()); + let res = sudo(deps.as_mut(), env, SudoMsg::CancelStream { stream_id: 1 }).unwrap(); + assert_eq!(res.messages.len(), 2); + // first message + if let cosmwasm_std::SubMsg { + msg: CosmosMsg::Bank(BankMsg::Send { to_address, amount }), + .. + } = &res.messages[0] + { + assert_eq!(to_address, &treasury.to_string()); + assert_eq!(amount.len(), 1); + assert_eq!(amount[0].denom, out_denom.to_string()); + assert_eq!(amount[0].amount, out_supply); + } else { + panic!("unexpected message variant"); + } + // second message + if let cosmwasm_std::SubMsg { + msg: CosmosMsg::Bank(BankMsg::Send { to_address, amount }), + .. + } = &res.messages[1] + { + assert_eq!(to_address, &treasury.to_string()); + assert_eq!(amount.len(), 1); + assert_eq!( + amount[0].denom, + helpers::DEFAULT_STREAM_CREATION_DENOM.to_string() + ); + assert_eq!(amount[0].amount, Uint256::from(100u128)); + } else { + panic!("unexpected message variant"); + } + + // random operator can't exit on behalf + let env = helpers::env_at(start.plus_seconds(2_250_000).seconds()); + let err = execute( + deps.as_mut(), + env, + mk_info( + &Addr::clone(&helpers::mock_info("random", &[]).sender), + vec![], + ), + ExecuteMsg::ExitCancelled { + stream_id: 1, + operator_target: Some(creator1.to_string()), + }, + ) + .unwrap_err(); + assert_eq!(err, ContractError::Unauthorized {}); + + // exit after cancellation + let env = helpers::env_at(start.plus_seconds(3_000_000).seconds()); + let res = execute( + deps.as_mut(), + env, + mk_info(&creator1, vec![]), + ExecuteMsg::ExitCancelled { + stream_id: 1, + operator_target: None, + }, + ) + .unwrap(); + assert_eq!(res.messages.len(), 1); + if let cosmwasm_std::SubMsg { + msg: CosmosMsg::Bank(BankMsg::Send { to_address, amount }), + .. + } = &res.messages[0] + { + assert_eq!(to_address, &creator1.to_string()); + assert_eq!( + amount, + &vec![Coin::new( + 2_000_000_000_000u128, + helpers::DEFAULT_ACCEPTED_IN_DENOM + )] + ); + } else { + panic!("unexpected message variant"); + } +} + +#[test] +fn killswitch_treasury_cancel_stream_window() { + let mut deps = mock_dependencies(); + + // instantiate with custom min_seconds_until_start_time = 100_000 + let msg = InstantiateMsg { + min_stream_seconds: Uint64::new(1000), + min_seconds_until_start_time: Uint64::new(100_000), + stream_creation_denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + stream_creation_fee: Uint256::from(100u128), + exit_fee_percent: Decimal256::percent(1), + fee_collector: helpers::mock_info("collector", &[]).sender.to_string(), + protocol_admin: helpers::mock_info("protocol_admin", &[]).sender.to_string(), + accepted_in_denom: "in_denom".to_string(), + tos_version: "v1".to_string(), + }; + cw_streamswap::contract::instantiate( + deps.as_mut(), + helpers::env_now(), + helpers::mock_info("creator", &[]), + msg, + ) + .unwrap(); + + // Actors + let treasury: Addr = helpers::mock_info("treasury", &[]).sender; + let creator: Addr = helpers::mock_info("creator", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Create stream + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(500u128); + let out_denom = "out_denom"; + let in_denom = "in_denom"; + let env = helpers::env_at(0); + let info = mk_info( + &creator, + vec![ + Coin::new(out_supply.to_string().parse::().unwrap(), out_denom), + Coin::new(100u128, helpers::DEFAULT_STREAM_CREATION_DENOM), + ], + ); + execute_create_stream( + deps.as_mut(), + env, + info, + treasury.to_string(), + "test".to_string(), + Some("https://sample.url".to_string()), + in_denom.to_string(), + out_denom.to_string(), + out_supply, + start, + end, + Some(Uint256::from(1_000u128)), + "v1".to_string(), + ) + .unwrap(); + + // Cancel period should be active until now+100_000 + + // Cancel stream with wrong address + let env = helpers::env_at(100); + let err = execute( + deps.as_mut(), + env, + helpers::mock_info("random", &[]), + ExecuteMsg::TreasuryCancelStream { stream_id: 1 }, + ) + .unwrap_err(); + assert_eq!(err, ContractError::Unauthorized {}); + + // Try subscribing to the stream during cancel period + let env = helpers::env_at(100); + let info = mk_info( + &Addr::clone(&helpers::mock_info("subscriber", &[]).sender), + vec![Coin::new(250u128, in_denom)], + ); + let msg = ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: Some(helpers::mock_info("operator", &[]).sender.to_string()), + tos_version: "v1".to_string(), + }; + let err = execute(deps.as_mut(), env.clone(), info, msg).unwrap_err(); + assert_eq!(err, ContractError::TreasuryCancelPeriodActive {}); + + // Try canceling outside the cancel period + let env = helpers::env_at(100_000 + 1); + let err = execute( + deps.as_mut(), + env.clone(), + helpers::mock_info("treasury", &[]), + ExecuteMsg::TreasuryCancelStream { stream_id: 1 }, + ) + .unwrap_err(); + assert_eq!(err, ContractError::TreasuryCancelPeriodEnded {}); + + // Query the stream + let _ = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); + + // Cancel at exact end of period + let env = helpers::env_at(100_000); + let res = execute( + deps.as_mut(), + env.clone(), + helpers::mock_info("treasury", &[]), + ExecuteMsg::TreasuryCancelStream { stream_id: 1 }, + ) + .unwrap(); + assert_eq!(res.messages.len(), 1); + if let cosmwasm_std::SubMsg { + msg: CosmosMsg::Bank(BankMsg::Send { to_address, amount }), + .. + } = &res.messages[0] + { + assert_eq!(to_address, &treasury.to_string()); + assert_eq!(amount.len(), 1); + assert_eq!(amount[0].denom, out_denom.to_string()); + assert_eq!(amount[0].amount, out_supply); + } else { + panic!("unexpected message variant"); + } + + // Querying the stream again should error since it's removed + let _err = query_stream(deps.as_ref(), env, 1).unwrap_err(); +} From ddaf30255839f1dfd1b2f32fc2eebcccffe02168 Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Wed, 27 Aug 2025 00:40:54 +0300 Subject: [PATCH 20/25] threshold tests --- tests/threshold.rs | 904 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 904 insertions(+) create mode 100644 tests/threshold.rs diff --git a/tests/threshold.rs b/tests/threshold.rs new file mode 100644 index 0000000..4ea9531 --- /dev/null +++ b/tests/threshold.rs @@ -0,0 +1,904 @@ +use cosmwasm_std::testing::mock_dependencies; +use cosmwasm_std::{Addr, BankMsg, Coin, CosmosMsg, Decimal256, Timestamp, Uint256, Uint64}; + +use cw_streamswap::contract::{ + execute, execute_create_stream, execute_exit_stream, execute_finalize_stream, query_stream, +}; +use cw_streamswap::msg::{ExecuteMsg, InstantiateMsg}; +use cw_streamswap::{threshold::ThresholdError, ContractError}; + +mod helpers; +//mod threshold { +// use crate::{ +// killswitch::{execute_cancel_stream_with_threshold, execute_exit_cancelled}, +// threshold::ThresholdError, +// }; + +// // Create a stream with a threshold +// // Subscribe to the stream +// use super::*; + +// #[test] +// fn test_threshold_reached() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_000_000); +// let end = Timestamp::from_seconds(5_000_000); +// let out_supply = Uint256::from(500u128); +// let out_denom = "out_denom"; +// let in_denom = "in_denom"; + +// // threshold = 500*0.5 / 1-0.01 =252.5 + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: in_denom.to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let info = mock_info( +// "creator", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// in_denom.to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// Some(Uint256::from(250u128)), +// "v1".to_string(), +// ) +// .unwrap(); + +// // subscription +// let mut env = mock_env(); +// env.block.time = start; +// let funds = Coin::new(252, "in_denom"); +// let info = mock_info("subscriber", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: Some("operator".to_string()), +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + +// // Threshold should be reached +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(1); + +// // Exit should be possible +// // Since there is only one subscriber all out denom should be sent to subscriber +// // In calculations we are always rounding down that one token will be left in the stream +// // Asuming token is 6 decimals +// // This amount could be considered as insignificant +// let info = mock_info("subscriber", &[]); +// let res = execute_exit_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap(); +// assert_eq!( +// res.messages, +// vec![SubMsg::new(BankMsg::Send { +// to_address: "subscriber".to_string(), +// amount: vec![Coin::new(499, "out_denom")], +// })], +// ); + +// // Creator finalizes the stream +// let info = mock_info("treasury", &[]); +// let res = execute_finalize_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap(); +// // Creator's revenue +// assert_eq!( +// res.messages[0].msg, +// cosmwasm_std::CosmosMsg::Bank(BankMsg::Send { +// to_address: "treasury".to_string(), +// amount: vec![Coin::new(250, "in_denom")], +// }) +// ); +// assert_eq!( +// res.messages[1].msg, +// cosmwasm_std::CosmosMsg::Bank(BankMsg::Send { +// to_address: "collector".to_string(), +// amount: vec![Coin::new(100, "fee")], +// }) +// ); +// assert_eq!( +// res.messages[2].msg, +// cosmwasm_std::CosmosMsg::Bank(BankMsg::Send { +// to_address: "collector".to_string(), +// amount: vec![Coin::new(2, "in_denom")], +// }) +// ) +// } + +// #[test] +// fn test_threshold_not_reached() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_000_000); +// let end = Timestamp::from_seconds(5_000_000); +// let out_supply = Uint256::from(500u128); +// let out_denom = "out_denom"; +// let in_denom = "in_denom"; + +// // threshold = 500*0.5 / 1-0.01 =252.5 + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.height = 0; +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: in_denom.to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let info = mock_info( +// "creator", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// in_denom.to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// Some(500u128.into()), +// "v1".to_string(), +// ) +// .unwrap(); + +// // Subscription 1 +// let mut env = mock_env(); +// env.block.time = start; +// let funds = Coin::new(250, "in_denom"); +// let info = mock_info("subscriber", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: Some("operator".to_string()), +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); + +// // Subscription 2 +// let funds = Coin::new(1, "in_denom"); +// let info = mock_info("subscriber2", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: Some("operator".to_string()), +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); + +// // Set time to the end of the stream +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(1); + +// // Exit should not be possible +// let info = mock_info("subscriber", &[]); +// let res = execute_exit_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap_err(); +// assert_eq!( +// res, +// ContractError::ThresholdError(ThresholdError::ThresholdNotReached {}) +// ); + +// // Finalize should not be possible +// let info = mock_info("treasury", &[]); +// let res = +// execute_finalize_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap_err(); +// assert_eq!( +// res, +// ContractError::ThresholdError(ThresholdError::ThresholdNotReached {}) +// ); + +// // Subscriber one executes exit cancelled before creator cancels stream +// let info = mock_info("subscriber", &[]); +// let res = execute_exit_cancelled(deps.as_mut(), env.clone(), info, 1, None).unwrap(); +// assert_eq!( +// res.messages, +// vec![SubMsg::new(BankMsg::Send { +// to_address: "subscriber".to_string(), +// amount: vec![Coin::new(250, "in_denom")], +// })] +// ); +// // Creator threshold cancels the stream +// let info = mock_info("treasury", &[]); +// let res = +// execute_cancel_stream_with_threshold(deps.as_mut(), env.clone(), info, 1).unwrap(); +// assert_eq!( +// res.messages, +// vec![ +// // Out denom refunded +// SubMsg::new(BankMsg::Send { +// to_address: "treasury".to_string(), +// amount: vec![Coin::new(500, "out_denom")], +// }), +// ] +// ); +// // Creator can not finalize the stream +// let info = mock_info("treasury", &[]); +// let res = +// execute_finalize_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap_err(); +// assert_eq!(res, ContractError::StreamKillswitchActive {}); + +// // Creator can not cancel the stream again +// let info = mock_info("treasury", &[]); +// let res = execute_cancel_stream_with_threshold(deps.as_mut(), env.clone(), info, 1) +// .unwrap_err(); +// assert_eq!(res, ContractError::StreamKillswitchActive {}); + +// // Subscriber 2 executes exit cancelled after creator cancels stream +// let info = mock_info("subscriber2", &[]); +// let res = execute_exit_cancelled(deps.as_mut(), env.clone(), info, 1, None).unwrap(); +// assert_eq!( +// // In denom refunded +// res.messages, +// vec![SubMsg::new(BankMsg::Send { +// to_address: "subscriber2".to_string(), +// amount: vec![Coin::new(1, "in_denom")], +// })] +// ); +// } + +// #[test] +// fn test_threshold_cancel() { +// let treasury = Addr::unchecked("treasury"); +// let start = Timestamp::from_seconds(1_000_000); +// let end = Timestamp::from_seconds(5_000_000); +// let out_supply = Uint256::from(500u128); +// let out_denom = "out_denom"; +// let in_denom = "in_denom"; + +// // threshold = 500*0.5 / 1-0.01 =252.5 + +// // instantiate +// let mut deps = mock_dependencies(); +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let msg = crate::msg::InstantiateMsg { +// min_stream_seconds: Uint64::new(1000), +// min_seconds_until_start_time: Uint64::new(0), +// stream_creation_denom: "fee".to_string(), +// stream_creation_fee: Uint128::new(100), +// exit_fee_percent: Decimal256::percent(1), +// fee_collector: "collector".to_string(), +// protocol_admin: "protocol_admin".to_string(), +// accepted_in_denom: in_denom.to_string(), +// tos_version: "v1".to_string(), +// }; +// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + +// // create stream +// let mut env = mock_env(); +// env.block.time = Timestamp::from_seconds(0); +// let info = mock_info( +// "creator", +// &[ +// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), +// Coin::new(100, "fee"), +// ], +// ); +// execute_create_stream( +// deps.as_mut(), +// env, +// info, +// treasury.to_string(), +// "test".to_string(), +// Some("https://sample.url".to_string()), +// in_denom.to_string(), +// out_denom.to_string(), +// out_supply, +// start, +// end, +// Some(1_000u128.into()), +// "v1".to_string(), +// ) +// .unwrap(); + +// // Subscription 1 +// let mut env = mock_env(); +// env.block.time = start; +// let funds = Coin::new(250, "in_denom"); +// let info = mock_info("subscriber", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: Some("operator".to_string()), +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); + +// // Subscription 2 +// let funds = Coin::new(500, "in_denom"); +// let info = mock_info("subscriber2", &[funds]); +// let msg = crate::msg::ExecuteMsg::Subscribe { +// stream_id: 1, +// operator_target: None, +// operator: Some("operator".to_string()), +// tos_version: "v1".to_string(), +// }; +// let _res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); +// // Can not cancel stream before it ends +// let mut env = mock_env(); +// env.block.time = start.plus_seconds(1_000_000); +// let res = execute_cancel_stream_with_threshold( +// deps.as_mut(), +// env, +// mock_info("treasury", &[]), +// 1, +// ) +// .unwrap_err(); +// assert_eq!(res, ContractError::StreamNotEnded {}); + +// // Set block to the end of the stream +// let mut env = mock_env(); +// env.block.time = end.plus_seconds(1); + +// // Non creator can't cancel stream +// let res = execute_cancel_stream_with_threshold( +// deps.as_mut(), +// env.clone(), +// mock_info("random", &[]), +// 1, +// ) +// .unwrap_err(); +// assert_eq!(res, ContractError::Unauthorized {}); + +// // Creator can cancel stream +// let _res = execute_cancel_stream_with_threshold( +// deps.as_mut(), +// env.clone(), +// mock_info("treasury", &[]), +// 1, +// ) +// .unwrap(); +// // Query stream should return stream with is_cancelled = true +// let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); +// assert_eq!(stream.status, Status::Cancelled); +// } +// } +// } + +#[test] +fn threshold_reached() { + let mut deps = mock_dependencies(); + + // Instantiate with specific params + let msg = InstantiateMsg { + min_stream_seconds: Uint64::new(1000), + min_seconds_until_start_time: Uint64::new(0), + stream_creation_denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + stream_creation_fee: Uint256::from(100u128), + exit_fee_percent: Decimal256::percent(1), + fee_collector: helpers::mock_info("collector", &[]).sender.to_string(), + protocol_admin: helpers::mock_info("protocol_admin", &[]).sender.to_string(), + accepted_in_denom: "in_denom".to_string(), + tos_version: "v1".to_string(), + }; + cw_streamswap::contract::instantiate( + deps.as_mut(), + helpers::env_now(), + helpers::mock_info("creator", &[]), + msg, + ) + .unwrap(); + + // Actors and mk_info + let treasury: Addr = helpers::mock_info("treasury", &[]).sender; + let creator: Addr = helpers::mock_info("creator", &[]).sender; + let subscriber: Addr = helpers::mock_info("subscriber", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Create stream with threshold 250 + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(500u128); + let out_denom = "out_denom"; + let in_denom = "in_denom"; + let env = helpers::env_at(0); + let info = mk_info( + &creator, + vec![ + Coin::new(out_supply.to_string().parse::().unwrap(), out_denom), + Coin::new(100u128, helpers::DEFAULT_STREAM_CREATION_DENOM), + ], + ); + execute_create_stream( + deps.as_mut(), + env, + info, + treasury.to_string(), + "test".to_string(), + Some("https://sample.url".to_string()), + in_denom.to_string(), + out_denom.to_string(), + out_supply, + start, + end, + Some(Uint256::from(250u128)), + "v1".to_string(), + ) + .unwrap(); + + // subscription 252 in_denom + let env = helpers::env_at(start.seconds()); + let info = mk_info(&subscriber, vec![Coin::new(252u128, in_denom)]); + let msg = ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: Some(helpers::mock_info("operator", &[]).sender.to_string()), + tos_version: "v1".to_string(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // Exit after end: expect 499 out_denom to subscriber (1 unit remains due to rounding) + let env = helpers::env_at(end.plus_seconds(1).seconds()); + let res = execute_exit_stream( + deps.as_mut(), + env.clone(), + mk_info(&subscriber, vec![]), + 1, + None, + ) + .unwrap(); + assert_eq!(res.messages.len(), 1); + if let cosmwasm_std::SubMsg { + msg: CosmosMsg::Bank(BankMsg::Send { to_address, amount }), + .. + } = &res.messages[0] + { + assert_eq!(to_address, &subscriber.to_string()); + assert_eq!(amount, &vec![Coin::new(499u128, out_denom)]); + } else { + panic!("unexpected message variant"); + } + + // Compute expected treasury dynamically from spent_in and exit fee (no extra update) + let stream_state = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); + + // Finalize stream + let res = + execute_finalize_stream(deps.as_mut(), env, mk_info(&treasury, vec![]), 1, None).unwrap(); + assert_eq!(res.messages.len(), 3); + // 0: revenue to treasury (capture amount for consistency checks) + let treasury_amount = if let cosmwasm_std::SubMsg { + msg: CosmosMsg::Bank(BankMsg::Send { to_address, amount }), + .. + } = &res.messages[0] + { + assert_eq!(to_address, &treasury.to_string()); + assert_eq!(amount.len(), 1); + amount[0].amount.clone() + } else { + panic!("unexpected message variant"); + }; + // 1: creation fee to collector + if let cosmwasm_std::SubMsg { + msg: CosmosMsg::Bank(BankMsg::Send { to_address, amount }), + .. + } = &res.messages[1] + { + assert_eq!( + to_address, + &helpers::mock_info("collector", &[]).sender.to_string() + ); + assert_eq!( + amount, + &vec![Coin::new(100u128, helpers::DEFAULT_STREAM_CREATION_DENOM)] + ); + } else { + panic!("unexpected message variant"); + } + // 2: protocol fee to collector (check consistency with spent_in) + let fee_amount = if let cosmwasm_std::SubMsg { + msg: CosmosMsg::Bank(BankMsg::Send { to_address, amount }), + .. + } = &res.messages[2] + { + assert_eq!( + to_address, + &helpers::mock_info("collector", &[]).sender.to_string() + ); + assert_eq!(amount.len(), 1); + assert_eq!(amount[0].denom, in_denom); + amount[0].amount.clone() + } else { + panic!("unexpected message variant"); + }; + + // Consistency: treasury + fee == spent_in + assert_eq!(treasury_amount + fee_amount, stream_state.spent_in); + // Verify fee is approximately 1% of spent_in (allow for rounding) + let expected_fee = stream_state.spent_in.multiply_ratio(1u128, 100u128); + assert!(fee_amount >= expected_fee && fee_amount <= expected_fee + Uint256::from(1u128)); +} + +#[test] +fn threshold_not_reached() { + let mut deps = mock_dependencies(); + + // Instantiate + let msg = InstantiateMsg { + min_stream_seconds: Uint64::new(1000), + min_seconds_until_start_time: Uint64::new(0), + stream_creation_denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + stream_creation_fee: Uint256::from(100u128), + exit_fee_percent: Decimal256::percent(1), + fee_collector: helpers::mock_info("collector", &[]).sender.to_string(), + protocol_admin: helpers::mock_info("protocol_admin", &[]).sender.to_string(), + accepted_in_denom: "in_denom".to_string(), + tos_version: "v1".to_string(), + }; + cw_streamswap::contract::instantiate( + deps.as_mut(), + helpers::env_now(), + helpers::mock_info("creator", &[]), + msg, + ) + .unwrap(); + + // Actors + let treasury: Addr = helpers::mock_info("treasury", &[]).sender; + let creator: Addr = helpers::mock_info("creator", &[]).sender; + let subscriber: Addr = helpers::mock_info("subscriber", &[]).sender; + let subscriber2: Addr = helpers::mock_info("subscriber2", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Create stream with threshold 500 + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(500u128); + let out_denom = "out_denom"; + let in_denom = "in_denom"; + let env = helpers::env_at(0); + let info = mk_info( + &creator, + vec![ + Coin::new(out_supply.to_string().parse::().unwrap(), out_denom), + Coin::new(100u128, helpers::DEFAULT_STREAM_CREATION_DENOM), + ], + ); + execute_create_stream( + deps.as_mut(), + env, + info, + treasury.to_string(), + "test".to_string(), + Some("https://sample.url".to_string()), + in_denom.to_string(), + out_denom.to_string(), + out_supply, + start, + end, + Some(Uint256::from(500u128)), + "v1".to_string(), + ) + .unwrap(); + + // Subscriptions 250 and 1 + let env = helpers::env_at(start.seconds()); + execute( + deps.as_mut(), + env.clone(), + mk_info(&subscriber, vec![Coin::new(250u128, in_denom)]), + ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: Some(helpers::mock_info("operator", &[]).sender.to_string()), + tos_version: "v1".to_string(), + }, + ) + .unwrap(); + + execute( + deps.as_mut(), + env.clone(), + mk_info(&subscriber2, vec![Coin::new(1u128, in_denom)]), + ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: Some(helpers::mock_info("operator", &[]).sender.to_string()), + tos_version: "v1".to_string(), + }, + ) + .unwrap(); + + let env = helpers::env_at(end.plus_seconds(1).seconds()); + + // Exit should not be possible + let err = execute_exit_stream( + deps.as_mut(), + env.clone(), + mk_info(&subscriber, vec![]), + 1, + None, + ) + .unwrap_err(); + assert_eq!( + err, + ContractError::ThresholdError(ThresholdError::ThresholdNotReached {}) + ); + + // Finalize should not be possible + let err = execute_finalize_stream( + deps.as_mut(), + env.clone(), + mk_info(&treasury, vec![]), + 1, + None, + ) + .unwrap_err(); + assert_eq!( + err, + ContractError::ThresholdError(ThresholdError::ThresholdNotReached {}) + ); + + // Subscriber one executes exit cancelled before creator cancels + let res = execute( + deps.as_mut(), + env.clone(), + mk_info(&subscriber, vec![]), + ExecuteMsg::ExitCancelled { + stream_id: 1, + operator_target: None, + }, + ) + .unwrap(); + assert_eq!(res.messages.len(), 1); + if let cosmwasm_std::SubMsg { + msg: CosmosMsg::Bank(BankMsg::Send { to_address, amount }), + .. + } = &res.messages[0] + { + assert_eq!(to_address, &subscriber.to_string()); + assert_eq!(amount, &vec![Coin::new(250u128, in_denom)]); + } else { + panic!("unexpected message variant"); + } + + // Creator threshold cancels the stream + let res = execute( + deps.as_mut(), + env.clone(), + mk_info(&treasury, vec![]), + ExecuteMsg::CancelStreamWithThreshold { stream_id: 1 }, + ) + .unwrap(); + assert_eq!(res.messages.len(), 1); + if let cosmwasm_std::SubMsg { + msg: CosmosMsg::Bank(BankMsg::Send { to_address, amount }), + .. + } = &res.messages[0] + { + assert_eq!(to_address, &treasury.to_string()); + assert_eq!(amount, &vec![Coin::new(500u128, out_denom)]); + } else { + panic!("unexpected message variant"); + } + + // Creator cannot finalize afterwards + let err = execute_finalize_stream( + deps.as_mut(), + env.clone(), + mk_info(&treasury, vec![]), + 1, + None, + ) + .unwrap_err(); + assert_eq!(err, ContractError::StreamKillswitchActive {}); + + // Creator cannot cancel again + let err = execute( + deps.as_mut(), + env.clone(), + mk_info(&treasury, vec![]), + ExecuteMsg::CancelStreamWithThreshold { stream_id: 1 }, + ) + .unwrap_err(); + assert_eq!(err, ContractError::StreamKillswitchActive {}); + + // Subscriber 2 executes exit cancelled + let res = execute( + deps.as_mut(), + env.clone(), + mk_info(&subscriber2, vec![]), + ExecuteMsg::ExitCancelled { + stream_id: 1, + operator_target: None, + }, + ) + .unwrap(); + assert_eq!(res.messages.len(), 1); + if let cosmwasm_std::SubMsg { + msg: CosmosMsg::Bank(BankMsg::Send { to_address, amount }), + .. + } = &res.messages[0] + { + assert_eq!(to_address, &subscriber2.to_string()); + assert_eq!(amount, &vec![Coin::new(1u128, in_denom)]); + } else { + panic!("unexpected message variant"); + } +} + +#[test] +fn threshold_cancel() { + let mut deps = mock_dependencies(); + + // Instantiate + let msg = InstantiateMsg { + min_stream_seconds: Uint64::new(1000), + min_seconds_until_start_time: Uint64::new(0), + stream_creation_denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + stream_creation_fee: Uint256::from(100u128), + exit_fee_percent: Decimal256::percent(1), + fee_collector: helpers::mock_info("collector", &[]).sender.to_string(), + protocol_admin: helpers::mock_info("protocol_admin", &[]).sender.to_string(), + accepted_in_denom: "in_denom".to_string(), + tos_version: "v1".to_string(), + }; + cw_streamswap::contract::instantiate( + deps.as_mut(), + helpers::env_now(), + helpers::mock_info("creator", &[]), + msg, + ) + .unwrap(); + + // Actors + let treasury: Addr = helpers::mock_info("treasury", &[]).sender; + let creator: Addr = helpers::mock_info("creator", &[]).sender; + let subscriber: Addr = helpers::mock_info("subscriber", &[]).sender; + let subscriber2: Addr = helpers::mock_info("subscriber2", &[]).sender; + let mk_info = |sender: &Addr, funds: Vec| cosmwasm_std::MessageInfo { + sender: sender.clone(), + funds, + }; + + // Create stream with threshold 1000 + let start = Timestamp::from_seconds(1_000_000); + let end = Timestamp::from_seconds(5_000_000); + let out_supply = Uint256::from(500u128); + let out_denom = "out_denom"; + let in_denom = "in_denom"; + let env = helpers::env_at(0); + let info = mk_info( + &creator, + vec![ + Coin::new(out_supply.to_string().parse::().unwrap(), out_denom), + Coin::new(100u128, helpers::DEFAULT_STREAM_CREATION_DENOM), + ], + ); + execute_create_stream( + deps.as_mut(), + env, + info, + treasury.to_string(), + "test".to_string(), + Some("https://sample.url".to_string()), + in_denom.to_string(), + out_denom.to_string(), + out_supply, + start, + end, + Some(Uint256::from(1_000u128)), + "v1".to_string(), + ) + .unwrap(); + + // Subscription 1: 250 + let env = helpers::env_at(start.seconds()); + execute( + deps.as_mut(), + env.clone(), + mk_info(&subscriber, vec![Coin::new(250u128, in_denom)]), + ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: Some(helpers::mock_info("operator", &[]).sender.to_string()), + tos_version: "v1".to_string(), + }, + ) + .unwrap(); + + // Subscription 2: 500 + execute( + deps.as_mut(), + env.clone(), + mk_info(&subscriber2, vec![Coin::new(500u128, in_denom)]), + ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: Some(helpers::mock_info("operator", &[]).sender.to_string()), + tos_version: "v1".to_string(), + }, + ) + .unwrap(); + + // Before end cannot cancel + let env_before = helpers::env_at(start.plus_seconds(1_000_000).seconds()); + let err = execute( + deps.as_mut(), + env_before, + mk_info(&treasury, vec![]), + ExecuteMsg::CancelStreamWithThreshold { stream_id: 1 }, + ) + .unwrap_err(); + assert_eq!(err, ContractError::StreamNotEnded {}); + + // At end+1 + let env = helpers::env_at(end.plus_seconds(1).seconds()); + + // Non-creator unauthorized + let err = execute( + deps.as_mut(), + env.clone(), + mk_info( + &Addr::clone(&helpers::mock_info("random", &[]).sender), + vec![], + ), + ExecuteMsg::CancelStreamWithThreshold { stream_id: 1 }, + ) + .unwrap_err(); + assert_eq!(err, ContractError::Unauthorized {}); + + // Creator can cancel stream + execute( + deps.as_mut(), + env.clone(), + mk_info(&treasury, vec![]), + ExecuteMsg::CancelStreamWithThreshold { stream_id: 1 }, + ) + .unwrap(); + + // Stream status is Cancelled + let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); + assert_eq!(stream.status, cw_streamswap::state::Status::Cancelled); +} From 48901e02057971d58590c4325a41d7078e678fb7 Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Wed, 27 Aug 2025 01:07:16 +0300 Subject: [PATCH 21/25] linter --- src/contract.rs | 88 ++-------- src/error.rs | 3 - src/helpers.rs | 8 +- src/killswitch.rs | 16 +- src/lib.rs | 2 +- src/migrate_v0_1_4.rs | 26 --- src/msg.rs | 6 - src/state.rs | 1 - src/threshold.rs | 2 +- tests/helpers.rs | 5 + tests/threshold.rs | 396 +----------------------------------------- 11 files changed, 30 insertions(+), 523 deletions(-) delete mode 100644 src/migrate_v0_1_4.rs diff --git a/src/contract.rs b/src/contract.rs index ab75616..d13ef39 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -1,9 +1,8 @@ use crate::killswitch::execute_cancel_stream_with_threshold; -use crate::migrate_v0_1_4::OLD_POSITIONS; + use crate::msg::{ AveragePriceResponse, ConfigResponse, ExecuteMsg, InstantiateMsg, LatestStreamedPriceResponse, - MigrateMsg, PositionResponse, PositionsResponse, QueryMsg, StreamResponse, StreamsResponse, - SudoMsg, + PositionResponse, PositionsResponse, QueryMsg, StreamResponse, StreamsResponse, SudoMsg, }; use crate::state::{ next_stream_id, Config, Position, Status, Stream, TreasuryCancelStreamPeriod, CONFIG, @@ -11,15 +10,16 @@ use crate::state::{ }; use crate::threshold::ThresholdState; use crate::{killswitch, ContractError}; +#[allow(unused_imports)] +use cosmwasm_std::entry_point; use cosmwasm_std::{ - attr, entry_point, to_json_binary, Addr, BankMsg, Binary, Coin, CosmosMsg, Decimal, Decimal256, - Deps, DepsMut, Env, Fraction, MessageInfo, Order, Response, StdError, StdResult, Timestamp, - Uint256, Uint64, + attr, to_json_binary, Addr, BankMsg, Binary, Coin, CosmosMsg, Decimal, Decimal256, Deps, + DepsMut, Env, Fraction, MessageInfo, Order, Response, StdError, StdResult, Timestamp, Uint256, + Uint64, }; -use cw2::{get_contract_version, set_contract_version}; -use semver::Version; +use cw2::set_contract_version; -use crate::helpers::{check_name_and_url, from_semver, get_decimals}; +use crate::helpers::{check_name_and_url, get_decimals}; use cw_storage_plus::Bound; use cw_utils::{maybe_addr, must_pay}; @@ -248,9 +248,6 @@ pub fn execute( exit_fee_percent, tos_version, ), - ExecuteMsg::MigratePosition { stream_id } => { - execute_migrate_position(deps, env, info, stream_id) - } } } #[allow(clippy::too_many_arguments)] @@ -307,7 +304,7 @@ pub fn execute_create_stream( .find(|p| p.denom == config.stream_creation_denom) .ok_or(ContractError::NoFundsSent {})?; - if total_funds.amount != Uint256::from(config.stream_creation_fee) + out_supply { + if total_funds.amount != config.stream_creation_fee + out_supply { return Err(ContractError::StreamOutSupplyFundsRequired {}); } // check for extra funds sent in msg @@ -330,7 +327,7 @@ pub fn execute_create_stream( .iter() .find(|p| p.denom == config.stream_creation_denom) .ok_or(ContractError::NoFundsSent {})?; - if creation_fee.amount != Uint256::from(config.stream_creation_fee) { + if creation_fee.amount != config.stream_creation_fee { return Err(ContractError::StreamCreationFeeRequired {}); } @@ -349,7 +346,7 @@ pub fn execute_create_stream( TreasuryCancelStreamPeriod::new(config.min_seconds_until_start_time.u64(), env.block.time); let id = next_stream_id(deps.storage)?; - TREASURY_STREAM_CANCEL_PERIOD.save(deps.storage, id.clone(), &treasury_cancel_period)?; + TREASURY_STREAM_CANCEL_PERIOD.save(deps.storage, id, &treasury_cancel_period)?; let stream = Stream::new( name.clone(), @@ -1058,7 +1055,7 @@ pub fn execute_finalize_stream( to_address: config.fee_collector.to_string(), amount: vec![Coin { denom: stream.stream_creation_denom, - amount: Uint256::from(stream.stream_creation_fee), + amount: stream.stream_creation_fee, }], }); @@ -1272,46 +1269,13 @@ pub fn execute_update_config( Ok(Response::default().add_attributes(attributes)) } -pub fn execute_migrate_position( - deps: DepsMut, - _env: Env, - info: MessageInfo, - stream_id: u64, -) -> Result { - // Old positions state is loaded and saved in new format - let old_position = OLD_POSITIONS.load(deps.storage, (stream_id, info.sender.clone()))?; - let position: Position = Position { - owner: old_position.owner, - in_balance: Uint256::from_uint128(old_position.in_balance), - shares: Uint256::from_uint128(old_position.shares), - index: old_position.index, - last_updated: old_position.last_updated, - operator: old_position.operator, - tos_version: "".to_string(), - pending_purchase: old_position.pending_purchase, - purchased: Uint256::new(old_position.purchased.into()), - spent: Uint256::new(old_position.spent.into()), - }; - OLD_POSITIONS.remove(deps.storage, (stream_id, info.sender.clone())); - POSITIONS.save(deps.storage, (stream_id, &info.sender.clone()), &position)?; - - let attributes = vec![ - attr("action", "migrate_position"), - attr("stream_id", stream_id.to_string()), - attr("owner", info.sender.clone()), - ]; - Ok(Response::default().add_attributes(attributes)) -} fn check_access( info: &MessageInfo, position_owner: &Addr, position_operator: &Option, ) -> Result<(), ContractError> { - if position_owner != info.sender - && position_operator - .as_ref() - .map_or(true, |o| o != info.sender) + if position_owner != info.sender && position_operator.as_ref().is_none_or(|o| o != info.sender) { return Err(ContractError::Unauthorized {}); } @@ -1327,30 +1291,6 @@ pub fn sudo(deps: DepsMut, env: Env, msg: SudoMsg) -> Result Result { - const OLDER_CONTRACT_VERSION: &str = "0.1.4"; - let contract_info = get_contract_version(deps.storage)?; - let storage_contract_name: String = contract_info.contract; - let storage_contract_version: Version = contract_info.version.parse().map_err(from_semver)?; - - if !(storage_contract_name == CONTRACT_NAME) { - return Err(ContractError::CannotMigrate { - previous_contract: storage_contract_name, - }); - } - - if storage_contract_version.to_string() != OLDER_CONTRACT_VERSION { - return Err(ContractError::CannotMigrate { - previous_contract: storage_contract_name, - }); - } - // Set the new contract version - set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - - Ok(Response::default()) -} - #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { diff --git a/src/error.rs b/src/error.rs index 26955f5..406fd1a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -29,9 +29,6 @@ pub enum ContractError { #[error("{0}")] ConversionOverflowError(#[from] ConversionOverflowError), - #[error("Cannot migrate from different contract type: {previous_contract}")] - CannotMigrate { previous_contract: String }, - #[error("No rewards accrued")] NoDistribution {}, diff --git a/src/helpers.rs b/src/helpers.rs index 597b109..45268a7 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,5 +1,5 @@ use crate::ContractError; -use cosmwasm_std::{Decimal256, StdError}; +use cosmwasm_std::Decimal256; use std::str::FromStr; /// Stream validation related constants @@ -26,7 +26,7 @@ pub fn get_decimals(value: Decimal256) -> Result { } } -pub fn check_name_and_url(name: &String, url: &Option) -> Result<(), ContractError> { +pub fn check_name_and_url(name: &str, url: &Option) -> Result<(), ContractError> { if name.len() < MIN_NAME_LENGTH { return Err(ContractError::StreamNameTooShort {}); } @@ -55,7 +55,3 @@ pub fn check_name_and_url(name: &String, url: &Option) -> Result<(), Con } Ok(()) } - -pub fn from_semver(err: semver::Error) -> ContractError { - ContractError::from(StdError::msg(format!("Semver: {}", err))) -} diff --git a/src/killswitch.rs b/src/killswitch.rs index af78b71..1a31e05 100644 --- a/src/killswitch.rs +++ b/src/killswitch.rs @@ -31,11 +31,7 @@ pub fn execute_withdraw_paused( let operator_target = maybe_addr(deps.api, operator_target)?.unwrap_or_else(|| info.sender.clone()); let mut position = POSITIONS.load(deps.storage, (stream_id, &operator_target))?; - if position.owner != info.sender - && position - .operator - .as_ref() - .map_or(true, |o| o != info.sender) + if position.owner != info.sender && position.operator.as_ref().is_none_or(|o| o != info.sender) { return Err(ContractError::Unauthorized {}); } @@ -133,11 +129,7 @@ pub fn execute_exit_cancelled( let operator_target = maybe_addr(deps.api, operator_target)?.unwrap_or_else(|| info.sender.clone()); let position = POSITIONS.load(deps.storage, (stream_id, &operator_target))?; - if position.owner != info.sender - && position - .operator - .as_ref() - .map_or(true, |o| o != info.sender) + if position.owner != info.sender && position.operator.as_ref().is_none_or(|o| o != info.sender) { return Err(ContractError::Unauthorized {}); } @@ -280,7 +272,7 @@ pub fn execute_cancel_stream( to_address: stream.treasury.to_string(), amount: vec![Coin { denom: stream.stream_creation_denom, - amount: Uint256::from(stream.stream_creation_fee), + amount: stream.stream_creation_fee, }], }), ]; @@ -488,7 +480,7 @@ pub fn sudo_cancel_stream( to_address: stream.treasury.to_string(), amount: vec![Coin { denom: stream.stream_creation_denom, - amount: Uint256::from(stream.stream_creation_fee), + amount: stream.stream_creation_fee, }], }), ]; diff --git a/src/lib.rs b/src/lib.rs index df90bfa..499a315 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ pub mod contract; mod error; mod helpers; mod killswitch; -mod migrate_v0_1_4; + pub mod msg; pub mod state; pub mod threshold; diff --git a/src/migrate_v0_1_4.rs b/src/migrate_v0_1_4.rs deleted file mode 100644 index ecde491..0000000 --- a/src/migrate_v0_1_4.rs +++ /dev/null @@ -1,26 +0,0 @@ -use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, Decimal256, Timestamp, Uint128}; -use cw_storage_plus::Map; - -use crate::state::StreamId; - -#[cw_serde] -pub struct PositionV0_1_0 { - /// creator of the position. - pub owner: Addr, - /// current amount of tokens in buy pool - pub in_balance: Uint128, - pub shares: Uint128, - // index is used to calculate the distribution a position has - pub index: Decimal256, - pub last_updated: Timestamp, - // total amount of `token_out` purchased in tokens at latest calculation - pub purchased: Uint128, - // pending purchased accumulates purchases after decimal truncation - pub pending_purchase: Decimal256, - // total amount of `token_in` spent tokens at latest calculation - pub spent: Uint128, - // operator can update position - pub operator: Option, -} -pub const OLD_POSITIONS: Map<(StreamId, Addr), PositionV0_1_0> = Map::new("positions"); diff --git a/src/msg.rs b/src/msg.rs index 9ad92c7..5c4e746 100644 --- a/src/msg.rs +++ b/src/msg.rs @@ -150,9 +150,6 @@ pub enum ExecuteMsg { TreasuryCancelStream { stream_id: u64, }, - MigratePosition { - stream_id: u64, - }, } #[cw_serde] @@ -303,6 +300,3 @@ pub enum SudoMsg { CancelStream { stream_id: u64 }, ResumeStream { stream_id: u64 }, } - -#[cw_serde] -pub struct MigrateMsg {} diff --git a/src/state.rs b/src/state.rs index 0b500a9..e58871f 100644 --- a/src/state.rs +++ b/src/state.rs @@ -251,7 +251,6 @@ impl TreasuryCancelStreamPeriod { } // Testing module #[cfg(test)] - mod tests { use super::*; use cosmwasm_std::Addr; diff --git a/src/threshold.rs b/src/threshold.rs index be0faa5..ccef695 100644 --- a/src/threshold.rs +++ b/src/threshold.rs @@ -140,7 +140,7 @@ mod tests { spent_in: Uint256::zero(), status: crate::state::Status::Active, stream_creation_denom: "uusd".to_string(), - stream_creation_fee: Uint256::from(0 as u128), + stream_creation_fee: Uint256::from(0_u128), stream_exit_fee_percent: Decimal256::from_str("0.042").unwrap(), treasury: Addr::unchecked("treasury"), tos_version: "".to_string(), diff --git a/tests/helpers.rs b/tests/helpers.rs index 7813e96..68f25f1 100644 --- a/tests/helpers.rs +++ b/tests/helpers.rs @@ -29,6 +29,7 @@ pub fn env_at(seconds: u64) -> cosmwasm_std::Env { env } +#[allow(dead_code)] pub struct InstantiateBuilder { pub min_stream_seconds: Uint64, pub min_seconds_until_start_time: Uint64, @@ -57,6 +58,7 @@ impl Default for InstantiateBuilder { } } +#[allow(dead_code)] impl InstantiateBuilder { pub fn exit_fee_percent(mut self, pct: Decimal256) -> Self { self.exit_fee_percent = pct; @@ -83,11 +85,13 @@ impl InstantiateBuilder { } } +#[allow(dead_code)] pub fn instantiate_defaults(deps: DepsMut) { let msg = InstantiateBuilder::default().build(); instantiate(deps, env_now(), mock_info("creator", &[]), msg).unwrap(); } +#[allow(dead_code)] pub struct CreateStreamBuilder { pub treasury: String, pub name: String, @@ -118,6 +122,7 @@ impl Default for CreateStreamBuilder { } } +#[allow(dead_code)] impl CreateStreamBuilder { pub fn in_denom(mut self, denom: &str) -> Self { self.in_denom = denom.to_string(); diff --git a/tests/threshold.rs b/tests/threshold.rs index 4ea9531..07fa27e 100644 --- a/tests/threshold.rs +++ b/tests/threshold.rs @@ -8,397 +8,6 @@ use cw_streamswap::msg::{ExecuteMsg, InstantiateMsg}; use cw_streamswap::{threshold::ThresholdError, ContractError}; mod helpers; -//mod threshold { -// use crate::{ -// killswitch::{execute_cancel_stream_with_threshold, execute_exit_cancelled}, -// threshold::ThresholdError, -// }; - -// // Create a stream with a threshold -// // Subscribe to the stream -// use super::*; - -// #[test] -// fn test_threshold_reached() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_000_000); -// let end = Timestamp::from_seconds(5_000_000); -// let out_supply = Uint256::from(500u128); -// let out_denom = "out_denom"; -// let in_denom = "in_denom"; - -// // threshold = 500*0.5 / 1-0.01 =252.5 - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: in_denom.to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let info = mock_info( -// "creator", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// in_denom.to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// Some(Uint256::from(250u128)), -// "v1".to_string(), -// ) -// .unwrap(); - -// // subscription -// let mut env = mock_env(); -// env.block.time = start; -// let funds = Coin::new(252, "in_denom"); -// let info = mock_info("subscriber", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: Some("operator".to_string()), -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - -// // Threshold should be reached -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(1); - -// // Exit should be possible -// // Since there is only one subscriber all out denom should be sent to subscriber -// // In calculations we are always rounding down that one token will be left in the stream -// // Asuming token is 6 decimals -// // This amount could be considered as insignificant -// let info = mock_info("subscriber", &[]); -// let res = execute_exit_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap(); -// assert_eq!( -// res.messages, -// vec![SubMsg::new(BankMsg::Send { -// to_address: "subscriber".to_string(), -// amount: vec![Coin::new(499, "out_denom")], -// })], -// ); - -// // Creator finalizes the stream -// let info = mock_info("treasury", &[]); -// let res = execute_finalize_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap(); -// // Creator's revenue -// assert_eq!( -// res.messages[0].msg, -// cosmwasm_std::CosmosMsg::Bank(BankMsg::Send { -// to_address: "treasury".to_string(), -// amount: vec![Coin::new(250, "in_denom")], -// }) -// ); -// assert_eq!( -// res.messages[1].msg, -// cosmwasm_std::CosmosMsg::Bank(BankMsg::Send { -// to_address: "collector".to_string(), -// amount: vec![Coin::new(100, "fee")], -// }) -// ); -// assert_eq!( -// res.messages[2].msg, -// cosmwasm_std::CosmosMsg::Bank(BankMsg::Send { -// to_address: "collector".to_string(), -// amount: vec![Coin::new(2, "in_denom")], -// }) -// ) -// } - -// #[test] -// fn test_threshold_not_reached() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_000_000); -// let end = Timestamp::from_seconds(5_000_000); -// let out_supply = Uint256::from(500u128); -// let out_denom = "out_denom"; -// let in_denom = "in_denom"; - -// // threshold = 500*0.5 / 1-0.01 =252.5 - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.height = 0; -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: in_denom.to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let info = mock_info( -// "creator", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// in_denom.to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// Some(500u128.into()), -// "v1".to_string(), -// ) -// .unwrap(); - -// // Subscription 1 -// let mut env = mock_env(); -// env.block.time = start; -// let funds = Coin::new(250, "in_denom"); -// let info = mock_info("subscriber", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: Some("operator".to_string()), -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); - -// // Subscription 2 -// let funds = Coin::new(1, "in_denom"); -// let info = mock_info("subscriber2", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: Some("operator".to_string()), -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); - -// // Set time to the end of the stream -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(1); - -// // Exit should not be possible -// let info = mock_info("subscriber", &[]); -// let res = execute_exit_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap_err(); -// assert_eq!( -// res, -// ContractError::ThresholdError(ThresholdError::ThresholdNotReached {}) -// ); - -// // Finalize should not be possible -// let info = mock_info("treasury", &[]); -// let res = -// execute_finalize_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap_err(); -// assert_eq!( -// res, -// ContractError::ThresholdError(ThresholdError::ThresholdNotReached {}) -// ); - -// // Subscriber one executes exit cancelled before creator cancels stream -// let info = mock_info("subscriber", &[]); -// let res = execute_exit_cancelled(deps.as_mut(), env.clone(), info, 1, None).unwrap(); -// assert_eq!( -// res.messages, -// vec![SubMsg::new(BankMsg::Send { -// to_address: "subscriber".to_string(), -// amount: vec![Coin::new(250, "in_denom")], -// })] -// ); -// // Creator threshold cancels the stream -// let info = mock_info("treasury", &[]); -// let res = -// execute_cancel_stream_with_threshold(deps.as_mut(), env.clone(), info, 1).unwrap(); -// assert_eq!( -// res.messages, -// vec![ -// // Out denom refunded -// SubMsg::new(BankMsg::Send { -// to_address: "treasury".to_string(), -// amount: vec![Coin::new(500, "out_denom")], -// }), -// ] -// ); -// // Creator can not finalize the stream -// let info = mock_info("treasury", &[]); -// let res = -// execute_finalize_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap_err(); -// assert_eq!(res, ContractError::StreamKillswitchActive {}); - -// // Creator can not cancel the stream again -// let info = mock_info("treasury", &[]); -// let res = execute_cancel_stream_with_threshold(deps.as_mut(), env.clone(), info, 1) -// .unwrap_err(); -// assert_eq!(res, ContractError::StreamKillswitchActive {}); - -// // Subscriber 2 executes exit cancelled after creator cancels stream -// let info = mock_info("subscriber2", &[]); -// let res = execute_exit_cancelled(deps.as_mut(), env.clone(), info, 1, None).unwrap(); -// assert_eq!( -// // In denom refunded -// res.messages, -// vec![SubMsg::new(BankMsg::Send { -// to_address: "subscriber2".to_string(), -// amount: vec![Coin::new(1, "in_denom")], -// })] -// ); -// } - -// #[test] -// fn test_threshold_cancel() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_000_000); -// let end = Timestamp::from_seconds(5_000_000); -// let out_supply = Uint256::from(500u128); -// let out_denom = "out_denom"; -// let in_denom = "in_denom"; - -// // threshold = 500*0.5 / 1-0.01 =252.5 - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: in_denom.to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let info = mock_info( -// "creator", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// in_denom.to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// Some(1_000u128.into()), -// "v1".to_string(), -// ) -// .unwrap(); - -// // Subscription 1 -// let mut env = mock_env(); -// env.block.time = start; -// let funds = Coin::new(250, "in_denom"); -// let info = mock_info("subscriber", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: Some("operator".to_string()), -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); - -// // Subscription 2 -// let funds = Coin::new(500, "in_denom"); -// let info = mock_info("subscriber2", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: Some("operator".to_string()), -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); -// // Can not cancel stream before it ends -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_000); -// let res = execute_cancel_stream_with_threshold( -// deps.as_mut(), -// env, -// mock_info("treasury", &[]), -// 1, -// ) -// .unwrap_err(); -// assert_eq!(res, ContractError::StreamNotEnded {}); - -// // Set block to the end of the stream -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(1); - -// // Non creator can't cancel stream -// let res = execute_cancel_stream_with_threshold( -// deps.as_mut(), -// env.clone(), -// mock_info("random", &[]), -// 1, -// ) -// .unwrap_err(); -// assert_eq!(res, ContractError::Unauthorized {}); - -// // Creator can cancel stream -// let _res = execute_cancel_stream_with_threshold( -// deps.as_mut(), -// env.clone(), -// mock_info("treasury", &[]), -// 1, -// ) -// .unwrap(); -// // Query stream should return stream with is_cancelled = true -// let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); -// assert_eq!(stream.status, Status::Cancelled); -// } -// } -// } #[test] fn threshold_reached() { @@ -512,7 +121,7 @@ fn threshold_reached() { { assert_eq!(to_address, &treasury.to_string()); assert_eq!(amount.len(), 1); - amount[0].amount.clone() + amount[0].amount } else { panic!("unexpected message variant"); }; @@ -545,7 +154,7 @@ fn threshold_reached() { ); assert_eq!(amount.len(), 1); assert_eq!(amount[0].denom, in_denom); - amount[0].amount.clone() + amount[0].amount } else { panic!("unexpected message variant"); }; @@ -718,6 +327,7 @@ fn threshold_not_reached() { } = &res.messages[0] { assert_eq!(to_address, &treasury.to_string()); + // Refund assert_eq!(amount, &vec![Coin::new(500u128, out_denom)]); } else { panic!("unexpected message variant"); From 67264bf35e9dc42ccad0a2be90c0ca912d9ab0bc Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Wed, 27 Aug 2025 02:41:10 +0300 Subject: [PATCH 22/25] tidy --- src/tests.rs | 4545 -------------------------------------------------- 1 file changed, 4545 deletions(-) delete mode 100644 src/tests.rs diff --git a/src/tests.rs b/src/tests.rs deleted file mode 100644 index 251f009..0000000 --- a/src/tests.rs +++ /dev/null @@ -1,4545 +0,0 @@ -#[cfg(test)] -mod test_module { - use crate::contract::execute; - use crate::contract::{ - execute_create_stream, execute_exit_stream, execute_finalize_stream, - execute_update_operator, execute_update_position, execute_update_stream, instantiate, - query_average_price, query_config, query_last_streamed_price, query_position, query_stream, - }; - use crate::killswitch::{execute_pause_stream, execute_withdraw_paused, sudo_resume_stream}; - use crate::msg::ExecuteMsg::UpdateProtocolAdmin; - use crate::state::{Status, Stream}; - use crate::threshold::ThresholdError; - use crate::ContractError; - use cosmwasm_std::testing::{mock_dependencies, mock_env}; - use cosmwasm_std::StdError::{self}; - use cosmwasm_std::{ - attr, coin, Addr, Attribute, BankMsg, Coin, CosmosMsg, Decimal256, MessageInfo, Response, - SubMsg, Timestamp, Uint128, Uint256, Uint64, - }; - use cw_utils::PaymentError; - use std::borrow::Borrow; - use std::ops::Sub; - use std::str::FromStr; - - // Test helpers: centralize defaults and builders for readability and reuse - mod test_helpers { - use cosmwasm_std::{testing::mock_env, Addr, Coin, MessageInfo, Uint256, Uint64}; - - pub const DEFAULT_FEE_COLLECTOR: &str = "collector"; - pub const DEFAULT_PROTOCOL_ADMIN: &str = "protocol_admin"; - pub const DEFAULT_ACCEPTED_IN_DENOM: &str = "in"; - pub const DEFAULT_STREAM_CREATION_DENOM: &str = "fee"; - - pub fn mock_info(sender: &str, funds: &[Coin]) -> MessageInfo { - MessageInfo { - sender: Addr::unchecked(sender), - funds: funds.to_vec(), - } - } - - pub fn env_now() -> cosmwasm_std::Env { - mock_env() - } - - pub struct InstantiateBuilder { - pub min_stream_seconds: Uint64, - pub min_seconds_until_start_time: Uint64, - pub stream_creation_denom: String, - pub stream_creation_fee: Uint256, - pub exit_fee_percent: cosmwasm_std::Decimal256, - pub fee_collector: String, - pub protocol_admin: String, - pub accepted_in_denom: String, - pub tos_version: String, - } - - impl Default for InstantiateBuilder { - fn default() -> Self { - Self { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(1000), - stream_creation_denom: DEFAULT_STREAM_CREATION_DENOM.to_string(), - stream_creation_fee: Uint256::from(100u128), - exit_fee_percent: cosmwasm_std::Decimal256::percent(1), - fee_collector: DEFAULT_FEE_COLLECTOR.to_string(), - protocol_admin: DEFAULT_PROTOCOL_ADMIN.to_string(), - accepted_in_denom: DEFAULT_ACCEPTED_IN_DENOM.to_string(), - tos_version: "v1".to_string(), - } - } - } - - impl InstantiateBuilder { - pub fn exit_fee_percent(mut self, pct: cosmwasm_std::Decimal256) -> Self { - self.exit_fee_percent = pct; - self - } - - pub fn build(self) -> crate::msg::InstantiateMsg { - crate::msg::InstantiateMsg { - min_stream_seconds: self.min_stream_seconds, - min_seconds_until_start_time: self.min_seconds_until_start_time, - stream_creation_denom: self.stream_creation_denom, - stream_creation_fee: self.stream_creation_fee, - exit_fee_percent: self.exit_fee_percent, - fee_collector: self.fee_collector, - protocol_admin: self.protocol_admin, - accepted_in_denom: self.accepted_in_denom, - tos_version: self.tos_version, - } - } - } - } - - fn mock_info(sender: &str, funds: &[Coin]) -> MessageInfo { - MessageInfo { - sender: Addr::unchecked(sender), - funds: funds.to_vec(), - } - } - - #[test] - fn test_compute_shares_amount() { - let mut stream = Stream::new( - "test".to_string(), - Addr::unchecked("treasury"), - Some("url".to_string()), - "out_denom".to_string(), - Uint256::from(100u128), - "in_denom".to_string(), - Timestamp::from_seconds(0), - Timestamp::from_seconds(100), - Timestamp::from_seconds(0), - "fee".to_string(), - Uint256::from(100u128), - Decimal256::percent(10), - "v1".to_string(), - ); - - // add new shares - let shares = stream.compute_shares_amount(Uint256::from(100u128), false); - assert_eq!(shares, Uint256::from(100u128)); - stream.in_supply = Uint256::from(100u128); - stream.shares = shares; - - // add new shares - stream.shares += stream.compute_shares_amount(Uint256::from(100u128), false); - stream.in_supply += Uint256::from(100u128); - assert_eq!(stream.shares, Uint256::from(200u128)); - - // add new shares - stream.shares += stream.compute_shares_amount(Uint256::from(250u128), false); - assert_eq!(stream.shares, Uint256::from(450u128)); - stream.in_supply += Uint256::from(250u128); - - // remove shares - stream.shares -= stream.compute_shares_amount(Uint256::from(100u128), true); - assert_eq!(stream.shares, Uint256::from(350u128)); - stream.in_supply -= Uint256::from(100u128); - } - - #[test] - fn test_create_stream() { - let mut deps = mock_dependencies(); - // Invalid exit fee (refactored to use helpers) - let msg = test_helpers::InstantiateBuilder::default() - .exit_fee_percent(Decimal256::percent(101)) - .build(); - let err = instantiate( - deps.as_mut(), - test_helpers::env_now(), - test_helpers::mock_info("creator", &[]), - msg, - ) - .unwrap_err(); - assert_eq!(err, ContractError::InvalidExitFeePercent {}); - - // Invalid stream creation fee - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(1000), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint256::zero(), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - let res = - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap_err(); - assert_eq!(res, ContractError::InvalidStreamCreationFee {}); - - let msg = crate::msg::InstantiateMsg { - min_stream_seconds: Uint64::new(1000), - min_seconds_until_start_time: Uint64::new(1000), - stream_creation_denom: "fee".to_string(), - stream_creation_fee: Uint256::from(100u128), - exit_fee_percent: Decimal256::percent(1), - fee_collector: "collector".to_string(), - protocol_admin: "protocol_admin".to_string(), - accepted_in_denom: "in".to_string(), - tos_version: "v1".to_string(), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - - let treasury = "treasury"; - let name = "name"; - let url = "https://sample.url"; - let start_time = Timestamp::from_seconds(3000); - let end_time = Timestamp::from_seconds(100000); - let out_supply = Uint256::from(50_000_000u128); - let out_denom = "out_denom"; - let in_denom = "random"; - - let info = mock_info("creator", &[]); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - name.to_string(), - Some(url.to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start_time, - end_time, - None, - "v1".to_string(), - ); - assert_eq!(res.unwrap_err(), ContractError::InDenomIsNotAccepted {}); - // end < start case - let treasury = "treasury"; - let name = "name"; - let url = "https://sample.url"; - let start_time = Timestamp::from_seconds(1000); - let end_time = Timestamp::from_seconds(10); - let out_supply = Uint256::from(50_000_000u128); - let out_denom = "out_denom"; - let in_denom = "in"; - - let info = mock_info("creator", &[]); - let env = mock_env(); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - name.to_string(), - Some(url.to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start_time, - end_time, - None, - "v1".to_string(), - ); - assert_eq!(res.unwrap_err(), ContractError::StreamInvalidEndTime {}); - - // min_stream_duration is not sufficient - let end_time = Timestamp::from_seconds(1000); - let start_time = Timestamp::from_seconds(500); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(0); - let info = mock_info("creator1", &[]); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - name.to_string(), - Some(url.to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start_time, - end_time, - None, - "v1".to_string(), - ); - assert_eq!(res.unwrap_err(), ContractError::StreamDurationTooShort {}); - - // start cannot be before current time - let end_time = Timestamp::from_seconds(1000); - let start_time = Timestamp::from_seconds(500); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(600); - let info = mock_info("creator1", &[]); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - name.to_string(), - Some(url.to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start_time, - end_time, - None, - "v1".to_string(), - ); - assert_eq!(res.unwrap_err(), ContractError::StreamInvalidStartTime {}); - - // stream starts too soon case - let end_time = Timestamp::from_seconds(100000); - let start_time = Timestamp::from_seconds(1400); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(401); - let info = mock_info("creator1", &[]); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - name.to_string(), - Some(url.to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start_time, - end_time, - None, - "v1".to_string(), - ); - assert_eq!(res.unwrap_err(), ContractError::StreamStartsTooSoon {}); - - // Same in and out denom case - let end_time = Timestamp::from_seconds(100000); - let start_time = Timestamp::from_seconds(3000); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(1); - let info = mock_info("creator1", &[]); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - name.to_string(), - Some(url.to_string()), - "in".to_string(), - "in".to_string(), - out_supply, - start_time, - end_time, - None, - "v1".to_string(), - ); - assert_eq!(res.unwrap_err(), ContractError::SameDenomOnEachSide {}); - - // 0 out supply case - let end_time = Timestamp::from_seconds(100000); - let start_time = Timestamp::from_seconds(3000); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(1); - let info = mock_info("creator1", &[]); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - name.to_string(), - Some(url.to_string()), - in_denom.to_string(), - out_denom.to_string(), - Uint256::zero(), - start_time, - end_time, - None, - "v1".to_string(), - ); - assert_eq!(res.unwrap_err(), ContractError::ZeroOutSupply {}); - - // threshold zero case - let start_time = Timestamp::from_seconds(3000); - let end_time = Timestamp::from_seconds(100000); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(1); - let info = mock_info( - "creator1", - &[ - Coin { - denom: "fee".to_string(), - amount: Uint256::from(100u128), - }, - Coin { - denom: out_denom.to_string(), - amount: out_supply.to_string().parse().unwrap(), - }, - ], - ); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - name.to_string(), - Some(url.to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start_time, - end_time, - Some(Uint256::zero()), - "v1".to_string(), - ) - .unwrap_err(); - assert_eq!( - res, - ContractError::ThresholdError(ThresholdError::ThresholdZero {}) - ); - - // no funds fee case - let end_time = Timestamp::from_seconds(100000); - let start_time = Timestamp::from_seconds(3000); - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(1); - let info = mock_info("creator1", &[]); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - name.to_string(), - Some(url.to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start_time, - end_time, - None, - "v1".to_string(), - ); - assert_eq!(res.unwrap_err().to_string(), "NoFundsSent"); - - // wrong supply amount case - let mut env = mock_env(); - env.block.time = Timestamp::from_seconds(1); - let info = mock_info("creator1", &[Coin::new(1_000_000u128, "out_denom")]); - let res = execute_create_stream( - deps.as_mut(), - env, - info, - treasury.to_string(), - name.to_string(), - Some(url.to_string()), - in_denom.to_string(), - out_denom.to_string(), - out_supply, - start_time, - end_time, - None, - "v1".to_string(), - ); - assert_eq!( - res.unwrap_err(), - ContractError::StreamOutSupplyFundsRequired {} - ); - - // // wrong creation fee case - // let mut env = mock_env(); - // env.block.time = Timestamp::from_seconds(1); - // let info = mock_info( - // "creator1", - // &[Coin::new(out_supply, "out_denom"), Coin::new(99u128, "fee")], - // ); - // let res = execute_create_stream( - // deps.as_mut(), - // env, - // info, - // treasury.to_string(), - // name.to_string(), - // Some(url.to_string()), - // in_denom.to_string(), - // out_denom.to_string(), - // out_supply, - // start_time, - // end_time, - // None, - // "v1".to_string(), - // ); - // assert_eq!(res.unwrap_err().to_string(), "StreamCreationFeeRequired"); - - // // no creation fee case - // let mut env = mock_env(); - // env.block.time = Timestamp::from_seconds(1); - // let info = mock_info("creator1", &[Coin::new(out_supply, "out_denom")]); - // let res = execute_create_stream( - // deps.as_mut(), - // env, - // info, - // treasury.to_string(), - // name.to_string(), - // Some(url.to_string()), - // in_denom.to_string(), - // out_denom.to_string(), - // out_supply, - // start_time, - // end_time, - // None, - // "v1".to_string(), - // ); - // assert_eq!(res.unwrap_err().to_string(), "NoFundsSent"); - - // // mismatch creation fee case - // let mut env = mock_env(); - // env.block.time = Timestamp::from_seconds(1); - // let info = mock_info("creator1", &[Coin::new(out_supply, "out_denom")]); - // let res = execute_create_stream( - // deps.as_mut(), - // env, - // info, - // treasury.to_string(), - // name.to_string(), - // Some(url.to_string()), - // in_denom.to_string(), - // out_denom.to_string(), - // out_supply, - // start_time, - // end_time, - // None, - // "v1".to_string(), - // ); - // assert_eq!(res.unwrap_err().to_string(), "NoFundsSent"); - - // // same denom case, insufficient total - // let mut env = mock_env(); - // env.block.time = Timestamp::from_seconds(1); - // let info = mock_info("creator1", &[Coin::new(1u128, "fee")]); - // let res = execute_create_stream( - // deps.as_mut(), - // env, - // info, - // treasury.to_string(), - // name.to_string(), - // Some(url.to_string()), - // in_denom.to_string(), - // "fee".to_string(), - // out_supply, - // start_time, - // end_time, - // None, - // "v1".to_string(), - // ); - // assert_eq!(res.unwrap_err().to_string(), "StreamOutSupplyFundsRequired"); - - // // same denom case, sufficient total - // let mut env = mock_env(); - // env.block.time = Timestamp::from_seconds(1); - // let info = mock_info( - // "creator1", - // &[Coin::new( - // out_supply.strict_add(Uint256::from(100u128)), - // "fee", - // )], - // ); - // execute_create_stream( - // deps.as_mut(), - // env, - // info, - // treasury.to_string(), - // name.to_string(), - // Some(url.to_string()), - // in_denom.to_string(), - // "fee".to_string(), - // out_supply, - // start_time, - // end_time, - // None, - // "v1".to_string(), - // ) - // .unwrap(); - - // // same tokens extra funds sent - // let info = mock_info( - // "creator1", - // &[ - // Coin::new(out_supply.strict_add(Uint256::from(100u128)), "fee"), - // Coin::new(15u128, "random"), - // ], - // ); - // let mut env = mock_env(); - // env.block.time = Timestamp::from_seconds(1); - // let err = execute_create_stream( - // deps.as_mut(), - // env, - // info, - // treasury.to_string(), - // name.to_string(), - // Some(url.to_string()), - // in_denom.to_string(), - // "fee".to_string(), - // out_supply, - // start_time, - // end_time, - // None, - // "v1".to_string(), - // ) - // .unwrap_err(); - // assert_eq!(err.to_string(), "InvalidFunds"); - - // // different tokens extra funds sent - // let info = mock_info( - // "creator1", - // &[ - // Coin::new(out_supply, "different_denom"), - // Coin::new(100u128, "fee"), - // Coin::new(15u128, "random"), - // ], - // ); - // let mut env = mock_env(); - // env.block.time = Timestamp::from_seconds(1); - // let err = execute_create_stream( - // deps.as_mut(), - // env, - // info, - // treasury.to_string(), - // name.to_string(), - // Some(url.to_string()), - // in_denom.to_string(), - // "different_denom".to_string(), - // out_supply, - // start_time, - // end_time, - // None, - // "v1".to_string(), - // ) - // .unwrap_err(); - // assert_eq!(err.to_string(), "InvalidFunds"); - - // // failed name checks - // let mut env = mock_env(); - // env.block.time = Timestamp::from_seconds(1); - // let info = mock_info( - // "creator1", - // &[ - // Coin::new(out_supply, "out_denom"), - // Coin::new(100u128, "fee"), - // ], - // ); - // let res = execute_create_stream( - // deps.as_mut(), - // env.clone(), - // info.clone(), - // treasury.to_string(), - // "n".to_string(), - // Some(url.to_string()), - // in_denom.to_string(), - // out_denom.to_string(), - // out_supply, - // start_time, - // end_time, - // None, - // "v1".to_string(), - // ) - // .unwrap_err(); - // assert_eq!(res.to_string(), "StreamNameTooShort"); - - // let res = execute_create_stream( - // deps.as_mut(), - // env.clone(), - // info.clone(), - // treasury.to_string(), - // "12345678901234567890123456789012345678901234567890123456789012345".to_string(), - // Some(url.to_string()), - // in_denom.to_string(), - // out_denom.to_string(), - // out_supply, - // start_time, - // end_time, - // None, - // "v1".to_string(), - // ) - // .unwrap_err(); - // assert_eq!(res.to_string(), "StreamNameTooLong"); - - // let res = execute_create_stream( - // deps.as_mut(), - // env, - // info, - // treasury.to_string(), - // "abc~ß".to_string(), - // Some(url.to_string()), - // in_denom.to_string(), - // out_denom.to_string(), - // out_supply, - // start_time, - // end_time, - // None, - // "v1".to_string(), - // ) - // .unwrap_err(); - // assert_eq!(res.to_string(), "InvalidStreamName"); - - // //failed url checks - // let mut env = mock_env(); - // env.block.time = Timestamp::from_seconds(1); - // let info = mock_info( - // "creator1", - // &[ - // Coin::new(out_supply, "out_denom"), - // Coin::new(100u128, "fee"), - // ], - // ); - // let res = execute_create_stream( - // deps.as_mut(), - // env.clone(), - // info.clone(), - // treasury.to_string(), - // "name".to_string(), - // Some("https://a.b".to_string()), - // in_denom.to_string(), - // out_denom.to_string(), - // out_supply, - // start_time, - // end_time, - // None, - // "v1".to_string(), - // ) - // .unwrap_err(); - // assert_eq!(res.to_string(), "StreamUrlTooShort"); - - // let res = execute_create_stream( - // deps.as_mut(), - // env.clone(), - // info.clone(), - // treasury.to_string(), - // "name".to_string(), - // Some("https://abcdefghijklmnopqrstuvw.xyz/abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz/abcdefghijklmnopqrstuvwxyzabcdefghijklmn".to_string()), - // in_denom.to_string(), - // out_denom.to_string(), - // out_supply, - // start_time, - // end_time, - // None, - // "v1".to_string(), - // ) - // .unwrap_err(); - // assert_eq!(res.to_string(), "StreamUrlTooLong"); - - // let res = execute_create_stream( - // deps.as_mut(), - // env.clone(), - // info.clone(), - // treasury.to_string(), - // "name".to_string(), - // Some("https://abc defghijklmnopqrstuvw.xyz/".to_string()), - // in_denom.to_string(), - // out_denom.to_string(), - // out_supply, - // start_time, - // end_time, - // None, - // "v1".to_string(), - // ) - // .unwrap_err(); - - // assert_eq!(res.to_string(), "InvalidStreamUrl"); - - // let res = execute_create_stream( - // deps.as_mut(), - // env, - // info, - // treasury.to_string(), - // "name".to_string(), - // Some("https://abcdefghijklmnopqrstuvw.xyz/".to_string()), - // in_denom.to_string(), - // out_denom.to_string(), - // out_supply, - // start_time, - // end_time, - // None, - // "random".to_string(), - // ) - // .unwrap_err(); - // assert_eq!(res.to_string(), "InvalidToSVersion"); - - // // happy path - // let mut env = mock_env(); - // env.block.time = Timestamp::from_seconds(1); - // let info = mock_info( - // "creator1", - // &[ - // Coin::new(out_supply, "out_denom"), - // Coin::new(100u128, "fee"), - // ], - // ); - // execute_create_stream( - // deps.as_mut(), - // env, - // info, - // treasury.to_string(), - // name.to_string(), - // Some(url.to_string()), - // in_denom.to_string(), - // out_denom.to_string(), - // out_supply, - // start_time, - // end_time, - // None, - // "v1".to_string(), - // ) - // .unwrap(); - - // // query stream with id - // let env = mock_env(); - // let stream = query_stream(deps.as_ref(), env, 1).unwrap(); - // assert_eq!(stream.id, 1); - } -} -// #[test] -// fn test_subscribe() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(2000); -// let end = Timestamp::from_seconds(1_000_000); -// let out_supply = Uint256::from(1_000_000u128); -// let out_denom = "out_denom"; - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(100); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(1000), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: "in".to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(1); -// let info = mock_info( -// "creator1", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// "in".to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); - -// // stream ended -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(1000000); -// let info = mock_info("creator1", &[]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); -// assert_eq!(res, ContractError::StreamEnded {}); - -// // no funds -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(100); -// let info = mock_info("creator1", &[]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); -// assert_eq!(res, PaymentError::NoFunds {}.into()); - -// // incorrect denom -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(100); -// let info = mock_info("creator1", &[Coin::new(100, "wrong_denom")]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let res = execute(deps.as_mut(), env.clone(), info, msg).unwrap_err(); -// assert_eq!(res, PaymentError::MissingDenom("in".to_string()).into()); - -// let stream = query_stream(deps.as_ref(), env, 1).unwrap(); -// assert_eq!(stream.status, Status::Waiting); - -// // incorrect toc -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(100); -// let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "random".to_string(), -// }; -// let res = execute(deps.as_mut(), env.clone(), info, msg).unwrap_err(); -// assert_eq!(res, ContractError::InvalidToSVersion {}); - -// // first subscribe -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(100); -// let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg); - -// // dist index updated -// let env = mock_env(); -// let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); -// // position index not updated, in_supply updated -// assert_eq!(stream.dist_index, Decimal256::zero()); -// //see that the status is updated -// assert_eq!(stream.status, Status::Active); -// assert_eq!(stream.in_supply, Uint256::from(1000000u128)); -// let position = query_position(deps.as_ref(), env, 1, "creator1".to_string()).unwrap(); -// assert_eq!(position.index, Decimal256::zero()); -// assert_eq!(position.in_balance, Uint256::from(1000000u128)); -// // unauthorized subscription increase -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(200); -// let info = mock_info("random", &[Coin::new(1_000_000, "in")]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: Some("creator1".to_string()), -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); -// assert_eq!(res, ContractError::Unauthorized {}); - -// // subscription increase -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(200); -// let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env.clone(), info, msg); -// // dist index updated -// let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); -// assert_eq!(stream.dist_index, Decimal256::from_str("0.0001").unwrap()); -// // dist index updated, position reduced and increased -// let position = query_position(deps.as_ref(), env, 1, "creator1".to_string()).unwrap(); -// assert_eq!(position.index, Decimal256::from_str("0.0001").unwrap()); -// assert_eq!(position.in_balance, Uint256::from(1999900u128)); -// } - -// #[test] -// fn test_subscribe_pending() { -// // instantiate -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(5_000); -// let end = Timestamp::from_seconds(10_000); -// let out_supply = Uint256::from(1_000_000u128); -// let out_denom = "out_denom"; - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(100); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: "in".to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(200); -// let info = mock_info( -// "creator1", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// "in".to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); - -// // first subscribe -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(300); - -// let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let res = execute(deps.as_mut(), env, info, msg).unwrap(); -// assert_eq!(res.attributes[0].key, "action"); -// assert_eq!(res.attributes[0].value, "subscribe_pending"); -// // query stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(350); -// let stream = query_stream(deps.as_ref(), env, 1).unwrap(); -// assert_eq!(stream.status, Status::Waiting); -// assert_eq!(stream.in_supply, Uint256::from(1000000u128)); -// assert_eq!(stream.shares, Uint256::from(1000000u128)); - -// // second subscribe still waiting -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(500); -// let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let res = execute(deps.as_mut(), env, info, msg).unwrap(); -// assert_eq!(res.attributes[0].key, "action"); -// assert_eq!(res.attributes[0].value, "subscribe_pending"); - -// // query stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(450); -// let stream = query_stream(deps.as_ref(), env, 1).unwrap(); -// assert_eq!(stream.status, Status::Waiting); -// assert_eq!(stream.in_supply, Uint256::from(2000000u128)); - -// // Before stream start time 2 subscriptions have been made and the stream is pending -// // After stream start time plus 1000 seconds one subscription is made and the stream is active -// // Creator 1 has 2 subscriptions and 2_000_000 in balance -// // Creator 2 has 1 subscription and 1_000_000 in balance -// // At 6000 seconds the stream is active and the balance to be distributed is ~2000000 -// // At 6000 seconds creator 1 shold spent 2000000*1000/5000= 400000 -// // At 6000 seconds creator 1 should get all 2000000 tokens -// // At 6000 seconds creator 2 should get 0 tokens -// // At 7500 seconds the stream is active and the balance to be distributed is 300000 -// // At 7500 seconds creator 1 should get 300000*2000000/3250000 = 184615 -// // At 7500 seconds creator 2 should get 300000*1250000/3250000 = 115384 - -// // subscription after start time -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(6000); -// let info = mock_info("creator2", &[Coin::new(1_000_000, "in")]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let res = execute(deps.as_mut(), env, info, msg).unwrap(); -// assert_eq!(res.attributes[0].key, "action"); -// // diffirent action because stream is active -// assert_eq!(res.attributes[0].value, "subscribe"); - -// // update creator 1 position -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(6000); -// let update_msg = crate::msg::ExecuteMsg::UpdatePosition { -// stream_id: 1, -// operator_target: None, -// }; -// let info = mock_info("creator1", &[]); -// let _res = execute(deps.as_mut(), env.clone(), info, update_msg).unwrap(); -// let position = query_position(deps.as_ref(), env, 1, "creator1".to_string()).unwrap(); -// assert_eq!(position.spent, Uint256::from(400000u128)); - -// // query stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(6000); -// let stream = query_stream(deps.as_ref(), env, 1).unwrap(); -// assert_eq!(stream.status, Status::Active); -// assert_eq!(stream.in_supply, Uint256::from(3000000u128 - 400000u128)); -// assert_eq!(stream.spent_in, Uint256::from(400000u128)); - -// // update creator 1 position at 3500 -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(7500); -// let update_msg = crate::msg::ExecuteMsg::UpdatePosition { -// stream_id: 1, -// operator_target: None, -// }; -// let info = mock_info("creator1", &[]); -// let _res = execute(deps.as_mut(), env.clone(), info, update_msg).unwrap(); - -// // query position -// let res = query_position(deps.as_ref(), env, 1, "creator1".to_string()).unwrap(); -// assert_eq!(res.purchased, Uint256::from(184615u128 + 200000u128)); -// assert_eq!(res.spent, Uint256::from(2000000u128 / 2u128)); - -// // update creator 2 position at 3500 -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(3500); -// let update_msg = crate::msg::ExecuteMsg::UpdatePosition { -// stream_id: 1, -// operator_target: None, -// }; -// let info = mock_info("creator2", &[]); -// let _res = execute(deps.as_mut(), env.clone(), info, update_msg).unwrap(); - -// // query position -// let res = query_position(deps.as_ref(), env, 1, "creator2".to_string()).unwrap(); -// assert_eq!(res.purchased, Uint256::from(115384u128)); -// // spent = in_supply * (now-last_updated) / (end-last_updated) -// assert_eq!(res.spent, Uint256::from(1000000u128 * 1500u128 / 4000u128)); -// // query stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(3500); -// let stream = query_stream(deps.as_ref(), env, 1).unwrap(); -// assert_eq!(stream.status, Status::Active); -// // in supply = 3000000 - (positions.spent summed) -// assert_eq!(stream.in_supply, Uint256::from(1625000u128)); -// } - -// #[test] -// pub fn test_withdraw_pending() { -// // // instantiate -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(2000); -// let end = Timestamp::from_seconds(1_000_000); -// let out_supply = Uint256::from(1_000_000u128); -// let out_denom = "out_denom"; - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(100); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: "in".to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(200); -// let info = mock_info( -// "creator1", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// "in".to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); - -// // first subscribe before start time -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(300); -// let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - -// // update creator 1 position no distrubution is excepted -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(350); -// let update_msg = crate::msg::ExecuteMsg::UpdatePosition { -// stream_id: 1, -// operator_target: None, -// }; -// let info = mock_info("creator1", &[]); -// let res = execute(deps.as_mut(), env.clone(), info, update_msg).unwrap(); - -// let expected_attributes = vec![ -// Attribute::new("action", "update_position"), -// Attribute::new("stream_id", "1"), -// Attribute::new("in_balance", "1000000"), -// Attribute::new("shares", "1000000"), -// Attribute::new("index", "0"), -// Attribute::new("last_updated", start.to_string()), -// Attribute::new("pending_purchase", "0"), -// Attribute::new("purchased", "0"), -// Attribute::new("spent", "0"), -// ]; -// assert_eq!(res.attributes, expected_attributes); - -// // query stream before withdraw -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(400); -// let stream = query_stream(deps.as_ref(), env, 1).unwrap(); - -// assert_eq!(stream.id, 1); -// assert_eq!(stream.dist_index, Decimal256::zero()); -// assert_eq!(stream.last_updated, Timestamp::from_seconds(2000)); -// assert_eq!(stream.in_supply, Uint256::from(1_000_000u128)); -// assert_eq!(stream.spent_in, Uint256::zero()); -// assert_eq!(stream.shares, Uint256::from(1_000_000u128)); - -// // withdraw before start time -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(400); -// let info = mock_info("creator1", &[]); -// let msg = crate::msg::ExecuteMsg::Withdraw { -// stream_id: 1, -// cap: Some(Uint256::from(500_000u128)), -// operator_target: None, -// }; -// let res = execute(deps.as_mut(), env, info, msg).unwrap(); -// assert_eq!(res.attributes[0].value, "withdraw_pending"); -// assert_eq!(res.attributes[1].key, "stream_id"); -// assert_eq!(res.attributes[1].value, "1"); -// assert_eq!(res.attributes[3].key, "withdraw_amount"); -// assert_eq!(res.attributes[3].value, "500000"); -// assert_eq!( -// res.messages[0].msg, -// CosmosMsg::Bank(BankMsg::Send { -// to_address: "creator1".to_string(), -// amount: vec![Coin::new(500000, "in")] -// }) -// ); -// // query stream after withdraw -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(400); -// let stream = query_stream(deps.as_ref(), env, 1).unwrap(); -// assert_eq!(stream.id, 1); -// assert_eq!(stream.dist_index, Decimal256::zero()); -// assert_eq!(stream.last_updated, Timestamp::from_seconds(2000)); -// assert_eq!(stream.in_supply, Uint256::from(500_000u128)); -// assert_eq!(stream.spent_in, Uint256::zero()); -// assert_eq!(stream.shares, Uint256::from(500_000u128)); - -// // withdraw after start time -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(3000); -// let info = mock_info("creator1", &[]); -// let msg = crate::msg::ExecuteMsg::Withdraw { -// stream_id: 1, -// cap: Some(Uint256::from(400_000u128)), -// operator_target: None, -// }; -// let res = execute(deps.as_mut(), env, info, msg).unwrap(); -// assert_eq!(res.attributes[0].value, "withdraw"); -// assert_eq!(res.attributes[1].key, "stream_id"); -// assert_eq!(res.attributes[1].value, "1"); -// assert_eq!(res.attributes[3].key, "withdraw_amount"); -// assert_eq!(res.attributes[3].value, "400000"); -// assert_eq!( -// res.messages[0].msg, -// CosmosMsg::Bank(BankMsg::Send { -// to_address: "creator1".to_string(), -// amount: vec![Coin::new(400000, "in")] -// }) -// ); -// // query stream after withdraw -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(3000); -// let _stream = query_stream(deps.as_ref(), env, 1).unwrap(); -// } - -// #[test] -// fn test_operator() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_590_797_419); -// let end = Timestamp::from_seconds(5_571_797_419); -// let out_supply = Uint256::from(1_000_000u128); -// let out_denom = "out_denom"; - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(100); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(1), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: "in".to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let env = mock_env(); -// let info = mock_info( -// "creator", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// "in".to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); - -// //random cannot make the first subscription on behalf of user -// let mut env = mock_env(); -// let info = mock_info("random", &[Coin::new(1_000_000, "in")]); -// env.block.time = start.plus_seconds(100); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: Some("creator1".to_string()), -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); -// assert_eq!(res, ContractError::Unauthorized {}); - -// //random cannot make the first subscription on behalf of user even if defined as operator in message -// let mut env = mock_env(); -// let info = mock_info("random", &[Coin::new(1_000_000, "in")]); -// env.block.time = start.plus_seconds(100); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: Some("creator1".to_string()), -// operator: Some("random".to_string()), -// tos_version: "v1".to_string(), -// }; -// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); -// assert_eq!(res, ContractError::Unauthorized {}); - -// // first subscription -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(100); -// let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg); - -// // only owner can update -// let mut env = mock_env(); -// let info = mock_info("creator2", &[]); -// env.block.time = start.plus_seconds(100); -// let res = -// execute_update_position(deps.as_mut(), env, info, 1, Some("creator1".to_string())) -// .unwrap_err(); -// assert_eq!(res, ContractError::Unauthorized {}); - -// // update creator 1 position no distrubution is excepted -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(100); -// let update_msg = crate::msg::ExecuteMsg::UpdatePosition { -// stream_id: 1, -// operator_target: None, -// }; -// let info = mock_info("creator1", &[]); -// let res = execute(deps.as_mut(), env.clone(), info, update_msg).unwrap(); - -// let expected_attributes = vec![ -// Attribute::new("action", "update_position"), -// Attribute::new("stream_id", "1"), -// Attribute::new("in_balance", "1000000"), -// Attribute::new("shares", "1000000"), -// Attribute::new("index", "0"), -// Attribute::new("last_updated", "1590797519.000000000"), -// Attribute::new("pending_purchase", "0"), -// Attribute::new("purchased", "0"), -// Attribute::new("spent", "0"), -// ]; -// assert_eq!(res.attributes, expected_attributes); - -// // Optional: Add query to verify position state matches response -// let position = -// query_position(deps.as_ref(), env.clone(), 1, "creator1".to_string()).unwrap(); -// assert_eq!(position.in_balance, Uint256::from(1000000u128)); -// assert_eq!(position.shares, Uint256::from(1000000u128)); -// assert_eq!(position.index, Decimal256::zero()); -// assert_eq!(position.last_updated, env.block.time); -// assert_eq!(position.pending_purchase, Decimal256::zero()); -// assert_eq!(position.purchased, Uint256::zero()); -// assert_eq!(position.spent, Uint256::zero()); -// assert_eq!(position.tos_version, "v1"); - -// // random cannot update -// let info = mock_info("random", &[]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(100); -// let res = -// execute_update_position(deps.as_mut(), env, info, 1, Some("creator1".to_string())) -// .unwrap_err(); -// assert_eq!(res, ContractError::Unauthorized {}); - -// // random cannot withdraw -// let _info = mock_info("random", &[]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(100); -// let _msg = crate::msg::ExecuteMsg::Withdraw { -// stream_id: 1, -// cap: None, -// operator_target: Some("creator1".to_string()), -// }; -// assert_eq!(res, ContractError::Unauthorized {}); - -// //owner can update operator -// let info = mock_info("creator1", &[]); -// let mut env = mock_env(); -// let owner = "creator1".to_string(); -// let stream_id = 1; -// env.block.time = start.plus_seconds(100); -// execute_update_operator( -// deps.as_mut(), -// env.clone(), -// info, -// 1, -// Some("operator1".to_string()), -// ) -// .unwrap(); -// let position = query_position(deps.as_ref(), env, stream_id, owner).unwrap(); -// assert_eq!(position.operator.unwrap().as_str(), "operator1".to_string()); - -// //operator can increase subscription on behalf of owner -// let info = mock_info("operator1", &[Coin::new(1_000_000, "in")]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(100); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: Some("creator1".to_string()), -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let res = execute(deps.as_mut(), env, info, msg).unwrap(); -// let expected_attributes = vec![ -// Attribute::new("action", "subscribe"), -// Attribute::new("stream_id", "1"), -// Attribute::new("in_balance", "2000000"), -// Attribute::new("shares", "2000000"), -// Attribute::new("index", "0"), -// Attribute::new("last_updated", "1590797519.000000000"), -// Attribute::new("pending_purchase", "0"), -// Attribute::new("purchased", "0"), -// Attribute::new("spent", "0"), -// Attribute::new("tos_version", "v1"), -// Attribute::new("stream_new_in_supply", "2000000"), -// Attribute::new("stream_previous_in_supply", "2000000"), -// Attribute::new("stream_new_shares", "2000000"), -// Attribute::new("stream_previous_shares", "2000000"), -// Attribute::new("stream_status", "Active"), -// ]; -// assert_eq!(res.attributes, expected_attributes); - -// // random cannot update operator -// let info = mock_info("random", &[]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(100); -// let res = -// execute_update_operator(deps.as_mut(), env, info, 1, Some("operator1".to_string())) -// .unwrap_err(); -// assert!(matches!(res, ContractError::Std(StdError::NotFound { .. }))); - -// // operator can't update operator -// let info = mock_info("operator1", &[]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(100); -// let res = -// execute_update_operator(deps.as_mut(), env, info, 1, Some("operator2".to_string())) -// .unwrap_err(); -// assert!(matches!(res, ContractError::Std(StdError::NotFound { .. }))); - -// // operator can update position -// let info = mock_info("operator1", &[]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(100); -// let res = -// execute_update_position(deps.as_mut(), env, info, 1, Some("creator1".to_string())) -// .unwrap(); -// let expected_attributes = vec![ -// Attribute::new("action", "update_position"), -// Attribute::new("stream_id", "1"), -// Attribute::new("in_balance", "2000000"), -// Attribute::new("shares", "2000000"), -// Attribute::new("index", "0"), -// Attribute::new("last_updated", "1590797519.000000000"), -// Attribute::new("pending_purchase", "0"), -// Attribute::new("purchased", "0"), -// Attribute::new("spent", "0"), -// ]; -// assert_eq!(res.attributes, expected_attributes); - -// // operator can withdraw -// let _info = mock_info("operator1", &[]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(100); -// let _msg = crate::msg::ExecuteMsg::Withdraw { -// stream_id: 1, -// cap: Some(5u128.into()), -// operator_target: Some("creator1".to_string()), -// }; - -// // random cannot exit -// let info = mock_info("random", &[]); -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(100); -// execute_update_stream(deps.as_mut(), env.clone(), 1).unwrap(); -// let res = execute_exit_stream(deps.as_mut(), env, info, 1, Some("creator1".to_string())) -// .unwrap_err(); -// assert_eq!(res, ContractError::Unauthorized {}); - -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(100); -// execute_update_stream(deps.as_mut(), env, 1).unwrap(); - -// // operator can exit -// let info = mock_info("operator1", &[]); -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(100); -// let res = -// execute_exit_stream(deps.as_mut(), env, info, 1, Some("creator1".to_string())).unwrap(); -// match res.messages.first().unwrap().msg.clone() { -// CosmosMsg::Bank(BankMsg::Send { -// to_address, -// amount: _, -// }) => { -// assert_eq!(to_address, "creator1"); -// } -// _ => panic!("unexpected message"), -// } -// } - -// #[test] -// fn test_update_stream() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_000_000); -// let end = Timestamp::from_seconds(5_000_000); -// let out_supply = Uint256::from(1_000_000u128); -// let out_denom = "out_denom"; - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(100); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(1000), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: "in".to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(1); -// let info = mock_info( -// "creator", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// "in".to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); - -// //update stream without subscription this means no new distribution so returned index should be 0 -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(100); -// let res = execute_update_stream(deps.as_mut(), env, 1).unwrap(); -// assert_eq!( -// res, -// Response::default().add_attributes(vec![ -// attr("action", "update_stream"), -// attr("stream_id", "1"), -// attr("new_distribution_amount", "0"), -// attr("dist_index", "0"), -// attr("last_updated", "1000100.000000000"), -// attr("start_time", "1000000.000000000"), -// attr("end_time", "5000000.000000000"), -// attr("in_denom", "in"), -// attr("out_denom", "out_denom"), -// attr("in_supply", "0"), -// attr("out_supply", "1000000"), -// attr("out_remaining", "1000000"), -// attr("spent_in", "0"), -// attr("shares", "0"), -// attr("current_streamed_price", "0"), -// attr("status", "Waiting"), -// attr("tos_version", "v1") -// ]) -// ); -// //first subscription -// //On first subscription index is not incresed because no distrubution prior to that(Execute_subscibe also includes update_stream) -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(100); -// let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: Some("creator1".to_string()), -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg); - -// //Query stream -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(200); -// let res = query_stream(deps.as_ref(), env, 1).unwrap(); -// assert_eq!(res.dist_index, Decimal256::zero()); - -// //Update stream again, this time with subscriber -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(300); -// execute_update_stream(deps.as_mut(), env, 1).unwrap(); - -// //Query stream -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(300); -// let res = query_stream(deps.as_ref(), env, 1).unwrap(); -// assert_eq!(res.dist_index, Decimal256::from_str("0.00005").unwrap()) -// } -// #[test] -// fn test_update_position() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_000_000); -// let end = Timestamp::from_seconds(5_000_000); -// let out_supply = Uint256::from(1_000_000u128); -// let out_denom = "out_denom"; - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(100); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(1000), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: "in".to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(1); -// let info = mock_info( -// "creator", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// "in".to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); - -// // first subscription -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(100); -// let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg); - -// // non owner operator cannot update position -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(3_000_000); -// let info = mock_info("random", &[]); -// let err = -// execute_update_position(deps.as_mut(), env, info, 1, Some("creator1".to_string())) -// .unwrap_err(); -// assert_eq!(err, ContractError::Unauthorized {}); - -// // update position -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(3_000_000); -// let info = mock_info("creator1", &[]); -// execute_update_position(deps.as_mut(), env.clone(), info, 1, None).unwrap(); - -// let position = -// query_position(deps.as_ref(), env.clone(), 1, "creator1".to_string()).unwrap(); -// assert_eq!( -// position.index, -// Decimal256::from_str("0.749993000000000000").unwrap() -// ); -// assert_eq!(position.purchased, Uint256::from(749_993u128)); -// assert_eq!(position.spent, Uint256::from(749_993u128)); -// assert_eq!(position.in_balance, Uint256::from(250_007u128)); -// let stream = query_stream(deps.as_ref(), env, 1).unwrap(); -// assert_eq!( -// stream.dist_index, -// Decimal256::from_str("0.749993000000000000").unwrap() -// ); - -// // can update position after stream ends -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(1); -// let info = mock_info("creator1", &[]); -// execute_update_position(deps.as_mut(), env.clone(), info, 1, None).unwrap(); -// let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); -// assert_eq!(stream.dist_index, Decimal256::from_str("1").unwrap()); -// assert_eq!(stream.in_supply, Uint256::zero()); -// let position = query_position(deps.as_ref(), env, 1, "creator1".to_string()).unwrap(); -// assert_eq!(position.index, Decimal256::from_str("1").unwrap()); -// assert_eq!(position.spent, Uint256::from(1_000_000u128)); -// assert_eq!(position.in_balance, Uint256::zero()); - -// assert_eq!(stream.out_supply, Uint256::from(1_000_000u128)); -// assert_eq!(position.purchased, stream.out_supply); -// } - -// // this is for testing the leftover amount with bigger values -// #[test] -// fn test_rounding_leftover() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_000_000); -// let end = Timestamp::from_seconds(5_000_000); -// let out_supply = Uint256::from(1_000_000_000_000u128); -// let out_denom = "out_denom"; - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(100); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(1000), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: "in".to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(1); -// let info = mock_info( -// "creator", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// "in".to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); - -// // first subscription -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(100); -// let info = mock_info("creator1", &[Coin::new(1_000_000_000, "in")]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// execute(deps.as_mut(), env, info, msg).unwrap(); - -// // second subscription -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(100_000); -// let info = mock_info("creator2", &[Coin::new(3_000_000_000, "in")]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// execute(deps.as_mut(), env, info, msg).unwrap(); - -// // update position creator1 -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(3_000_000); -// let info = mock_info("creator1", &[]); -// execute_update_position(deps.as_mut(), env.clone(), info, 1, None).unwrap(); - -// let position = -// query_position(deps.as_ref(), env.clone(), 1, "creator1".to_string()).unwrap(); -// assert_eq!( -// position.index, -// Decimal256::from_str("202.813614449380587585").unwrap() -// ); -// assert_eq!(position.purchased, Uint256::from(202_813_614_449u128)); -// assert_eq!(position.spent, Uint256::from(749_993_750u128)); -// assert_eq!(position.in_balance, Uint256::from(250_006_250u128)); -// let stream = query_stream(deps.as_ref(), env, 1).unwrap(); -// assert_eq!( -// stream.dist_index, -// Decimal256::from_str("202.813614449380587585").unwrap() -// ); - -// // update position creator2 -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(3_575_000); -// let info = mock_info("creator2", &[]); -// execute_update_position(deps.as_mut(), env.clone(), info, 1, None).unwrap(); - -// let position = -// query_position(deps.as_ref(), env.clone(), 1, "creator2".to_string()).unwrap(); -// assert_eq!( -// position.index, -// Decimal256::from_str("238.074595237060799266").unwrap() -// ); -// assert_eq!(position.purchased, Uint256::from(655672748445u128)); -// assert_eq!(position.spent, Uint256::from(2673076923u128)); -// assert_eq!(position.in_balance, Uint256::from(326923077u128)); -// let stream = query_stream(deps.as_ref(), env, 1).unwrap(); -// assert_eq!( -// stream.dist_index, -// Decimal256::from_str("238.074595237060799266").unwrap() -// ); - -// // update position after stream ends -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(1); -// let info = mock_info("creator1", &[]); -// execute_update_position(deps.as_mut(), env.clone(), info, 1, None).unwrap(); -// let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); -// assert_eq!( -// stream.dist_index, -// Decimal256::from_str("264.137059297637397644").unwrap() -// ); -// assert_eq!(stream.in_supply, Uint256::zero()); -// let position1 = query_position(deps.as_ref(), env, 1, "creator1".to_string()).unwrap(); -// assert_eq!( -// position1.index, -// Decimal256::from_str("264.137059297637397644").unwrap() -// ); -// assert_eq!(position1.spent, Uint256::from(1_000_000_000u128)); -// assert_eq!(position1.in_balance, Uint256::zero()); - -// // update position after stream ends -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(1); -// let info = mock_info("creator2", &[]); -// execute_update_position(deps.as_mut(), env.clone(), info, 1, None).unwrap(); -// let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); -// assert_eq!( -// stream.dist_index, -// Decimal256::from_str("264.137059297637397644").unwrap() -// ); -// assert_eq!(stream.in_supply, Uint256::zero()); -// let position2 = query_position(deps.as_ref(), env, 1, "creator2".to_string()).unwrap(); -// assert_eq!( -// position2.index, -// Decimal256::from_str("264.137059297637397644").unwrap() -// ); -// assert_eq!(position2.spent, Uint256::from(3_000_000_000u128)); -// assert_eq!(position2.in_balance, Uint256::zero()); - -// assert_eq!(stream.out_remaining, Uint256::zero()); -// assert_eq!( -// position1 -// .purchased -// .checked_add(position2.purchased) -// .unwrap(), -// // 1 difference due to rounding -// stream.out_supply.sub(Uint256::from(1u128)) -// ); -// } - -// #[test] -// fn test_withdraw() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_000_000); -// let end = Timestamp::from_seconds(5_000_000); -// let out_supply = Uint256::from(1_000_000_000_000u128); -// let out_denom = "out_denom"; - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: "in".to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let info = mock_info( -// "creator1", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// "in".to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); - -// // first subscription -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(0); -// let funds = Coin::new(2_000_000_000_000, "in"); -// let info = mock_info("creator1", &[funds.clone()]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - -// // withdraw with cap -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(5000); -// let info = mock_info("creator1", &[]); -// // withdraw amount zero -// let cap = Uint256::zero(); -// let msg = crate::msg::ExecuteMsg::Withdraw { -// stream_id: 1, -// cap: Some(cap), -// operator_target: None, -// }; -// let res = execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap_err(); -// assert_eq!(res, ContractError::InvalidWithdrawAmount {}); -// // withdraw amount too high -// let cap = Uint256::from(2_250_000_000_000u128); -// let msg = crate::msg::ExecuteMsg::Withdraw { -// stream_id: 1, -// cap: Some(cap), -// operator_target: None, -// }; -// let res = execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap_err(); -// assert_eq!( -// res, -// ContractError::WithdrawAmountExceedsBalance(Uint256::from(2250000000000u128)) -// ); -// //withdraw with valid cap -// let cap = Uint256::from(25_000_000u128); -// let msg = crate::msg::ExecuteMsg::Withdraw { -// stream_id: 1, -// cap: Some(cap), -// operator_target: None, -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); -// let position = -// query_position(deps.as_ref(), mock_env(), 1, "creator1".to_string()).unwrap(); -// assert_eq!(position.in_balance, Uint256::from(1_997_475_000_000u128)); -// assert_eq!(position.spent, Uint256::from(2_500_000_000u128)); -// assert_eq!(position.purchased, Uint256::from(1_250_000_000u128)); -// // first fund amount should be equal to in_balance + spent + cap -// assert_eq!( -// position.in_balance + position.spent + cap, -// Uint256::from_str(funds.amount.to_string().as_str()).unwrap() -// ); - -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_000); -// let info = mock_info("creator1", &[]); -// let msg = crate::msg::ExecuteMsg::Withdraw { -// stream_id: 1, -// cap: None, -// operator_target: None, -// }; -// let res = execute(deps.as_mut(), env, info, msg).unwrap(); -// let position = -// query_position(deps.as_ref(), mock_env(), 1, "creator1".to_string()).unwrap(); -// assert_eq!(position.in_balance, Uint256::zero()); -// assert_eq!(position.spent, Uint256::from(499_993_773_466u128)); -// assert_eq!(position.purchased, Uint256::from(249_999_999_998u128)); -// assert_eq!(position.shares, Uint256::zero()); -// let msg = res.messages.first().unwrap(); -// assert_eq!( -// msg.msg, -// CosmosMsg::Bank(BankMsg::Send { -// to_address: "creator1".to_string(), -// amount: vec![Coin::new(1_499_981_226_534, "in")] -// }) -// ); - -// // can't withdraw after stream ends -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(1); -// let info = mock_info("creator1", &[]); -// let msg = crate::msg::ExecuteMsg::Withdraw { -// stream_id: 1, -// cap: None, -// operator_target: None, -// }; -// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); -// assert_eq!(res, ContractError::StreamEnded {}); -// } - -// #[test] -// fn test_finalize_stream() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_000_000); -// let end = Timestamp::from_seconds(5_000_000); -// let out_supply = Uint256::from(1_000_000_000_000u128); -// let out_denom = "out_denom"; - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: "in".to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let info = mock_info( -// "creator1", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// "in".to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); - -// // first subscription -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_000); -// let funds = Coin::new(2_000_000_000_000, "in"); -// let info = mock_info("creator1", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - -// // only treasury can finalize -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(1); -// let info = mock_info("random", &[]); -// let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None).unwrap_err(); -// assert_eq!(res, ContractError::Unauthorized {}); - -// // can't finalize before stream ends -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1); -// let info = mock_info(treasury.as_str(), &[]); -// let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None).unwrap_err(); -// assert_eq!(res, ContractError::StreamNotEnded {}); - -// // happy path -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(1); -// let info = mock_info(treasury.as_str(), &[]); -// execute_update_stream(deps.as_mut(), env.clone(), 1).unwrap(); - -// let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None).unwrap(); -// assert_eq!( -// vec![ -// Attribute::new("action", "finalize_stream"), -// Attribute::new("stream_id", "1"), -// Attribute::new("stream_dist_index", "0.5"), -// Attribute::new("stream_shares", "2000000000000"), -// Attribute::new("stream_last_updated", "5000001.000000000"), -// Attribute::new("stream_in_supply", "0"), -// Attribute::new("stream_out_remaining", "0"), -// Attribute::new("stream_status", "Finalized"), -// Attribute::new("stream_exit_fee_percent", "0.01"), -// Attribute::new("treasury", "treasury"), -// Attribute::new("creators_revenue", "1980000000000"), -// Attribute::new("refunded_out_remaining", "0"), -// Attribute::new("total_sold", "1000000000000"), -// Attribute::new("fee_collector", "collector"), -// Attribute::new("swap_fee", "20000000000"), -// Attribute::new("creation_fee", "100"), -// ], -// res.attributes, -// ); -// assert_eq!( -// res.messages, -// vec![ -// SubMsg::new(BankMsg::Send { -// to_address: "treasury".to_string(), -// amount: vec![Coin { -// denom: "in".to_string(), -// amount: Uint128::new(1_980_000_000_000), -// }], -// }), -// SubMsg::new(BankMsg::Send { -// to_address: "collector".to_string(), -// amount: vec![Coin { -// denom: "fee".to_string(), -// amount: Uint128::new(100), -// }], -// }), -// SubMsg::new(BankMsg::Send { -// to_address: "collector".to_string(), -// amount: vec![Coin { -// denom: "in".to_string(), -// amount: Uint128::new(20_000_000_000), -// }], -// }), -// ], -// ); -// } - -// #[test] -// fn test_recurring_finalize_stream_calls() { -// let malicious_treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(10); -// let end = Timestamp::from_seconds(110); -// let out_supply = Uint256::from(1000u128); -// let out_denom = "myToken"; -// let in_denom = "uosmo"; -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(100), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: in_denom.to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); -// // Create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let info = mock_info( -// malicious_treasury.as_str(), -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// malicious_treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// in_denom.to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); -// // First subscription -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1); -// let funds = Coin::new(200, in_denom.to_string()); -// let info = mock_info("user1", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); -// // Update -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(1); -// let info = mock_info(malicious_treasury.as_str(), &[]); -// execute_update_stream(deps.as_mut(), env.clone(), 1).unwrap(); -// // First call -// let res = -// execute_finalize_stream(deps.as_mut(), env.clone(), info.clone(), 1, None).unwrap(); -// assert_eq!( -// res.messages, -// vec![ -// SubMsg::new(BankMsg::Send { -// to_address: malicious_treasury.to_string(), -// amount: vec![Coin { -// denom: in_denom.to_string(), -// amount: Uint128::new(198), -// }], -// }), -// SubMsg::new(BankMsg::Send { -// to_address: "collector".to_string(), -// amount: vec![Coin { -// denom: "fee".to_string(), -// amount: Uint128::new(100), -// }], -// }), -// SubMsg::new(BankMsg::Send { -// to_address: "collector".to_string(), -// amount: vec![Coin { -// denom: in_denom.to_string(), -// amount: Uint128::new(2), -// }], -// }), -// ], -// ); -// // Check stream status -// let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); -// assert_eq!(stream.status, Status::Finalized); -// // Sequential calls, anyone could force this sequential calls -// let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None).unwrap_err(); -// assert_eq!(res, ContractError::StreamAlreadyFinalized {}); -// } - -// #[test] -// fn test_exit_stream() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_000_000); -// let end = Timestamp::from_seconds(5_000_000); -// let out_supply = Uint256::from(1_000_000_000_000u128); -// let out_denom = "out_denom"; - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: "in".to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let info = mock_info( -// "creator1", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// "in".to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); - -// // first subscription -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_000); -// let funds = Coin::new(2_000_000_000_000, "in"); -// let info = mock_info("creator1", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - -// // can't exit before stream ends -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(2_000_000); -// let info = mock_info("creator1", &[]); -// let res = execute_exit_stream(deps.as_mut(), env, info, 1, None).unwrap_err(); -// assert_eq!(res, ContractError::StreamNotEnded {}); - -// //failed exit from random address -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(3_000_000); -// let info = mock_info("random", &[]); -// let res = execute_exit_stream( -// deps.as_mut(), -// env.clone(), -// info, -// 1, -// Some("creator1".to_string()), -// ) -// .unwrap_err(); -// assert_eq!(res, ContractError::Unauthorized {}); -// // can exit -// let info = mock_info("creator1", &[]); -// let res = execute_exit_stream(deps.as_mut(), env, info, 1, None).unwrap(); -// assert_eq!( -// res.messages, -// vec![SubMsg::new(CosmosMsg::Bank(BankMsg::Send { -// to_address: "creator1".to_string(), -// amount: vec![Coin::new( -// Uint128::new(1_000_000_000_000).u128(), -// "out_denom" -// )] -// }))] -// ); - -// // position deleted -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(4_000_000); -// let info = mock_info("creator1", &[]); -// let res = execute_exit_stream(deps.as_mut(), env, info, 1, None).unwrap_err(); -// assert!(matches!(res, ContractError::Std(StdError::NotFound { .. }))); -// } - -// #[test] -// fn test_withdraw_all_before_exit_case() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_000_000); -// let end = Timestamp::from_seconds(5_000_000); -// let out_supply = Uint256::from(1_000_000_000_000u128); -// let out_denom = "out_denom"; - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: "in".to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let info = mock_info( -// "creator1", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// "in".to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); - -// // first subscription -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_000); -// let funds = Coin::new(2_000_000_000_000, "in"); -// let info = mock_info("creator1", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - -// // second subscription -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_000); -// let funds = Coin::new(1_000_000_000_000, "in"); -// let info = mock_info("creator2", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - -// // first withdraw -// let info = mock_info("creator1", &[]); -// let mut env = mock_env(); -// env.block.time = end.minus_seconds(1_000_000); -// let msg = crate::msg::ExecuteMsg::Withdraw { -// stream_id: 1, -// cap: None, -// operator_target: None, -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - -// // second withdraw -// let info = mock_info("creator2", &[]); -// let mut env = mock_env(); -// env.block.time = end.minus_seconds(1_000_000); -// let msg = crate::msg::ExecuteMsg::Withdraw { -// stream_id: 1, -// cap: None, -// operator_target: None, -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - -// // can exit -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(1_000_000); -// execute_update_stream(deps.as_mut(), env, 1).unwrap(); - -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(1_000_001); -// let info = mock_info("creator1", &[]); -// execute_exit_stream(deps.as_mut(), env, info, 1, None).unwrap(); - -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(1_000_002); -// let info = mock_info("creator2", &[]); -// execute_exit_stream(deps.as_mut(), env, info, 1, None).unwrap(); -// } - -// #[test] -// fn test_price_feed() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_000_000); -// let end = Timestamp::from_seconds(5_000_000); -// let out_supply = Uint256::from(1_000_000u128); -// let out_denom = "out_denom"; - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: "in".to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let info = mock_info( -// "creator1", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// "in".to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); - -// // first subscription -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_000); -// let funds = Coin::new(3_000, "in"); -// let info = mock_info("creator1", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - -// //check current streamed price before update -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(2_000_000); -// let res = query_last_streamed_price(deps.as_ref(), env, 1).unwrap(); -// assert_eq!(res.current_streamed_price, Decimal256::zero()); - -// //check current streamed price after update -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(2_000_000); -// execute_update_stream(deps.as_mut(), env, 1).unwrap(); -// let res = query_last_streamed_price(deps.as_ref(), mock_env(), 1).unwrap(); -// //approx 1000/333333 -// assert_eq!( -// res.current_streamed_price, -// Decimal256::from_str("0.002997002997002997").unwrap() -// ); -// // second subscription -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(2_000_000); -// let funds = Coin::new(1_000, "in"); -// let info = mock_info("creator2", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - -// //check current streamed price before update -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(3_000_000); -// let res = query_last_streamed_price(deps.as_ref(), env, 1).unwrap(); -// assert_eq!( -// res.current_streamed_price, -// Decimal256::from_str("0.002997002997002997").unwrap() -// ); - -// //check current streamed price after update -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(3_000_000); -// execute_update_stream(deps.as_mut(), env, 1).unwrap(); -// let res = query_last_streamed_price(deps.as_ref(), mock_env(), 1).unwrap(); -// //approx 2000/333333 -// assert_eq!( -// res.current_streamed_price, -// Decimal256::from_str("0.0045000045000045").unwrap() -// ); - -// //check average streamed price -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(3_000_000); -// let res = query_average_price(deps.as_ref(), env, 1).unwrap(); -// //approx 2500/333333 -// assert_eq!( -// res.average_price, -// Decimal256::from_str("0.003748503748503748").unwrap() -// ); - -// //withdraw creator 1 -// let info = mock_info("creator1", &[]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(3_500_000); -// let msg = crate::msg::ExecuteMsg::Withdraw { -// stream_id: 1, -// cap: None, -// operator_target: None, -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); -// let res = query_last_streamed_price(deps.as_ref(), mock_env(), 1).unwrap(); -// assert_eq!( -// res.current_streamed_price, -// Decimal256::from_str("0.004499991000017999").unwrap() -// ); - -// //test price after withdraw -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(3_750_000); -// execute_update_stream(deps.as_mut(), env, 1).unwrap(); -// let res = query_last_streamed_price(deps.as_ref(), mock_env(), 1).unwrap(); -// //approx 2500/333333 -// assert_eq!( -// res.current_streamed_price, -// Decimal256::from_str("0.001500006000024000").unwrap() -// ); -// } - -// #[test] -// fn test_update_protocol_admin() { -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: "in".to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // random cannot update -// let env = mock_env(); -// let msg = UpdateProtocolAdmin { -// new_protocol_admin: "new_protocol_admin".to_string(), -// }; -// let info = mock_info("random", &[]); -// let err = execute(deps.as_mut(), env.clone(), info, msg.clone()).unwrap_err(); -// assert_eq!(err, ContractError::Unauthorized {}); - -// // protocol admin can update -// let info = mock_info("protocol_admin", &[]); -// execute(deps.as_mut(), env, info, msg).unwrap(); -// let query = query_config(deps.as_ref()).unwrap(); -// assert_eq!(query.protocol_admin, "new_protocol_admin".to_string()); -// } -// #[test] -// fn test_execute_update_config() { -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: "in".to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// //query config -// let config_response = query_config(deps.as_ref()).unwrap(); -// //check config -// assert_eq!(config_response.min_stream_seconds, Uint64::new(1000)); -// assert_eq!(config_response.min_seconds_until_start_time, Uint64::new(0)); -// assert_eq!(config_response.stream_creation_denom, "fee".to_string()); -// assert_eq!(config_response.stream_creation_fee, Uint128::new(100)); -// assert_eq!(config_response.fee_collector, "collector".to_string()); -// assert_eq!(config_response.protocol_admin, "protocol_admin".to_string()); -// assert_eq!(config_response.accepted_in_denom, "in".to_string()); - -// // random user cant update config -// let mut env = mock_env(); -// let info = mock_info("random", &[]); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::ExecuteMsg::UpdateConfig { -// min_stream_duration: Some(Uint64::new(2000)), -// min_duration_until_start_time: Some(Uint64::new(2000)), -// stream_creation_denom: Some("fee2".to_string()), -// stream_creation_fee: Some(Uint128::new(200)), -// fee_collector: Some("collector2".to_string()), -// accepted_in_denom: Some("new_denom".to_string()), -// exit_fee_percent: Some(Decimal256::percent(2)), -// tos_version: None, -// }; -// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); -// assert_eq!(res, ContractError::Unauthorized {}); - -// // wrong fee amount -// let mut env = mock_env(); -// let info = mock_info("protocol_admin", &[]); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::ExecuteMsg::UpdateConfig { -// min_stream_duration: Some(Uint64::new(2000)), -// min_duration_until_start_time: Some(Uint64::new(2000)), -// stream_creation_denom: Some("fee2".to_string()), -// stream_creation_fee: Some(Uint128::new(0)), -// fee_collector: Some("collector2".to_string()), -// accepted_in_denom: Some("new_denom".to_string()), -// exit_fee_percent: Some(Decimal256::percent(2)), -// tos_version: None, -// }; -// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); -// assert_eq!(res, ContractError::InvalidStreamCreationFee {}); - -// // wrong exit fee percent -// let mut env = mock_env(); -// let info = mock_info("protocol_admin", &[]); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::ExecuteMsg::UpdateConfig { -// min_stream_duration: Some(Uint64::new(2000)), -// min_duration_until_start_time: Some(Uint64::new(2000)), -// stream_creation_denom: Some("fee2".to_string()), -// stream_creation_fee: Some(Uint128::new(200)), -// fee_collector: Some("collector2".to_string()), -// accepted_in_denom: Some("new_denom".to_string()), -// exit_fee_percent: Some(Decimal256::percent(101)), -// tos_version: None, -// }; -// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); -// assert_eq!(res, ContractError::InvalidExitFeePercent {}); - -// // protocol admin can update config -// let mut env = mock_env(); -// let info = mock_info("protocol_admin", &[]); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::ExecuteMsg::UpdateConfig { -// min_stream_duration: Some(Uint64::new(2000)), -// min_duration_until_start_time: Some(Uint64::new(2000)), -// stream_creation_denom: Some("fee2".to_string()), -// stream_creation_fee: Some(Uint128::new(200)), -// fee_collector: Some("collector2".to_string()), -// accepted_in_denom: Some("new_denom".to_string()), -// exit_fee_percent: Some(Decimal256::percent(2)), -// tos_version: None, -// }; -// execute(deps.as_mut(), env, info, msg).unwrap(); - -// //query config -// let config_response = query_config(deps.as_ref()).unwrap(); -// //check config -// assert_eq!(config_response.min_stream_seconds, Uint64::new(2000)); -// assert_eq!( -// config_response.min_seconds_until_start_time, -// Uint64::new(2000) -// ); -// assert_eq!(config_response.stream_creation_denom, "fee2".to_string()); -// assert_eq!(config_response.stream_creation_fee, Uint128::new(200)); -// assert_eq!(config_response.fee_collector, "collector2".to_string()); -// assert_eq!(config_response.protocol_admin, "protocol_admin".to_string()); -// assert_eq!(config_response.accepted_in_denom, "new_denom".to_string()); -// assert_eq!(config_response.exit_fee_percent, Decimal256::percent(2)); - -// // create stream -// let out_supply = Uint256::from(1000u128); -// let out_denom = "out"; -// let start = Timestamp::from_seconds(10000); -// let end = Timestamp::from_seconds(1000000); -// let treasury = "treasury"; -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let info = mock_info( -// "creator1", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(200, "fee2"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// "new_denom".to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); - -// // update config during stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(100000); -// let info = mock_info("protocol_admin", &[]); -// let msg = crate::msg::ExecuteMsg::UpdateConfig { -// min_stream_duration: Some(Uint64::new(3000)), -// min_duration_until_start_time: Some(Uint64::new(4000)), -// stream_creation_denom: Some("fee3".to_string()), -// stream_creation_fee: Some(Uint128::new(300)), -// fee_collector: Some("collector3".to_string()), -// accepted_in_denom: Some("new_denom2".to_string()), -// exit_fee_percent: Some(Decimal256::percent(5)), -// tos_version: None, -// }; -// execute(deps.as_mut(), env, info, msg).unwrap(); -// //query config -// let config_response = query_config(deps.as_ref()).unwrap(); -// //check config -// assert_eq!(config_response.min_stream_seconds, Uint64::new(3000)); -// assert_eq!( -// config_response.min_seconds_until_start_time, -// Uint64::new(4000) -// ); -// assert_eq!(config_response.stream_creation_denom, "fee3".to_string()); -// assert_eq!(config_response.stream_creation_fee, Uint128::new(300)); -// assert_eq!(config_response.fee_collector, "collector3".to_string()); -// assert_eq!(config_response.protocol_admin, "protocol_admin".to_string()); -// assert_eq!(config_response.accepted_in_denom, "new_denom2".to_string()); -// assert_eq!(config_response.exit_fee_percent, Decimal256::percent(5)); - -// // check stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(100000); -// let stream_response = query_stream(deps.as_ref(), env, 1).unwrap(); -// assert_eq!(stream_response.exit_fee_percent, Decimal256::percent(2)); -// assert_eq!(stream_response.stream_creation_fee, Uint128::new(200)); -// } - -// #[cfg(test)] -// mod killswitch { -// use super::*; -// use crate::contract::{list_positions, list_streams}; -// use crate::killswitch::{ -// execute_cancel_stream, execute_exit_cancelled, execute_resume_stream, -// sudo_cancel_stream, sudo_pause_stream, -// }; -// use cosmwasm_std::CosmosMsg::Bank; -// use cosmwasm_std::{Binary, ReplyOn, SubMsg}; - -// #[test] -// fn test_pause_protocol_admin() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_000_000); -// let end = Timestamp::from_seconds(5_000_000); -// let out_supply = Uint256::from(1_000_000_000_000u128); -// let out_denom = "out_denom"; - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: "in".to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let info = mock_info( -// "creator1", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// "in".to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); - -// // non protocol admin can't pause -// let info = mock_info("non_protocol_admin", &[]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(100); - -// let res = execute_pause_stream(deps.as_mut(), env, info, 1); -// assert_eq!(res, Err(ContractError::Unauthorized {})); - -// // first subscription -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_000); -// let funds = Coin::new(3_000, "in"); -// let info = mock_info("position1", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - -// //can't pause before start time -// let info = mock_info("protocol_admin", &[]); -// let mut env = mock_env(); -// env.block.time = start.minus_seconds(500_000); -// let res = execute_pause_stream(deps.as_mut(), env, info, 1).unwrap_err(); -// assert_eq!(res, ContractError::StreamNotStarted {}); - -// // can't pause after end time -// let info = mock_info("protocol_admin", &[]); -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(500_000); -// let res = execute_pause_stream(deps.as_mut(), env, info, 1).unwrap_err(); -// assert_eq!(res, ContractError::StreamEnded {}); - -// // protocol admin can pause -// let info = mock_info("protocol_admin", &[]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_001); -// execute_pause_stream(deps.as_mut(), env, info, 1).unwrap(); - -// // can't paused if already paused -// let info = mock_info("protocol_admin", &[]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_005); -// let res = execute_pause_stream(deps.as_mut(), env, info, 1).unwrap_err(); -// assert_eq!(res, ContractError::StreamKillswitchActive {}); - -// // can't subscribe new -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_002); -// let funds = Coin::new(3_000, "in"); -// let info = mock_info("position2", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); -// assert_eq!(res, ContractError::StreamKillswitchActive {}); - -// // can't subscribe more -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_002); -// let funds = Coin::new(3_000, "in"); -// let info = mock_info("position1", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); -// assert_eq!(res, ContractError::StreamKillswitchActive {}); - -// // can't withdraw -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_002); -// let info = mock_info("position1", &[]); -// let msg = crate::msg::ExecuteMsg::Withdraw { -// stream_id: 1, -// cap: None, -// operator_target: None, -// }; -// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); -// assert_eq!(res, ContractError::StreamKillswitchActive {}); - -// // can't update stream -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_002); -// let res = execute_update_stream(deps.as_mut(), env, 1); -// assert_eq!(res, Err(ContractError::StreamPaused {})); - -// // can't update position -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_002); -// let info = mock_info("position1", &[]); -// let res = execute_update_position(deps.as_mut(), env, info, 1, None); -// assert_eq!(res, Err(ContractError::StreamPaused {})); - -// // can't finalize stream -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(1_000_002); -// let info = mock_info("treasury", &[]); -// let res = execute_finalize_stream(deps.as_mut(), env, info, 1, None); -// assert_eq!(res, Err(ContractError::StreamKillswitchActive {})); - -// // can't exit -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(1_000_002); -// let info = mock_info("position1", &[]); -// let res = execute_exit_stream(deps.as_mut(), env, info, 1, None); -// assert_eq!(res, Err(ContractError::StreamKillswitchActive {})); -// } - -// #[test] -// fn test_resume_protocol_admin() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_000_000); -// let end = Timestamp::from_seconds(5_000_000); -// let out_supply = Uint256::from(1_000_000_000_000u128); -// let out_denom = "out_denom"; - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: "in".to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let info = mock_info( -// "creator1", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// "in".to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); - -// // first subscription -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_000); -// let funds = Coin::new(3_000, "in"); -// let info = mock_info("position1", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - -// // can't resume if not paused -// let info = mock_info("protocol_admin", &[]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_003); -// let res = execute_resume_stream(deps.as_mut(), env, info, 1).unwrap_err(); -// assert_eq!(res, ContractError::StreamNotPaused {}); - -// // protocol admin can pause -// let info = mock_info("protocol_admin", &[]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_001); -// execute_pause_stream(deps.as_mut(), env, info, 1).unwrap(); - -// // can't subscribe new -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_002); -// let funds = Coin::new(3_000, "in"); -// let info = mock_info("position2", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); -// assert_eq!(res, ContractError::StreamKillswitchActive {}); - -// // non protocol admin can't resume -// let info = mock_info("non_protocol_admin", &[]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_003); -// let res = execute_resume_stream(deps.as_mut(), env, info, 1).unwrap_err(); -// assert_eq!(res, ContractError::Unauthorized {}); - -// // protocol admin can resume -// let info = mock_info("protocol_admin", &[]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_003); -// execute_resume_stream(deps.as_mut(), env, info, 1).unwrap(); - -// // can subscribe new after resume -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_004); -// let funds = Coin::new(3_000, "in"); -// let info = mock_info("position2", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let res = execute(deps.as_mut(), env, info, msg).unwrap(); -// assert_eq!(res.attributes[0].key, "action"); -// assert_eq!(res.attributes[1].key, "stream_id"); -// assert_eq!(res.attributes[2].key, "in_balance"); -// assert_eq!(res.attributes[3].key, "shares"); -// assert_eq!(res.attributes[4].key, "index"); -// assert_eq!(res.attributes[5].key, "last_updated"); -// assert_eq!(res.attributes[6].key, "pending_purchase"); -// assert_eq!(res.attributes[7].key, "purchased"); -// assert_eq!(res.attributes[8].key, "spent"); -// assert_eq!(res.attributes[9].key, "tos_version"); -// assert_eq!(res.attributes[10].key, "stream_new_in_supply"); -// assert_eq!(res.attributes[11].key, "stream_previous_in_supply"); -// assert_eq!(res.attributes[12].key, "stream_new_shares"); -// assert_eq!(res.attributes[13].key, "stream_previous_shares"); -// assert_eq!(res.attributes[14].key, "stream_status"); -// // validate values -// assert_eq!(res.attributes[0].value, "subscribe"); -// assert_eq!(res.attributes[1].value, "1"); -// assert_eq!(res.attributes[2].value, "3000"); -// assert_eq!(res.attributes[3].value, "3000"); -// assert_eq!(res.attributes[4].value, "222.222"); -// assert_eq!(res.attributes[5].value, "2000004.000000000"); -// assert_eq!(res.attributes[6].value, "0"); -// assert_eq!(res.attributes[7].value, "0"); -// assert_eq!(res.attributes[8].value, "0"); -// assert_eq!(res.attributes[9].value, "v1"); -// assert_eq!(res.attributes[10].value, "6000"); -// assert_eq!(res.attributes[11].value, "6000"); -// assert_eq!(res.attributes[12].value, "6000"); -// assert_eq!(res.attributes[13].value, "6000"); -// assert_eq!(res.attributes[14].value, "Active"); - -// // protocol admin can pause -// let info = mock_info("protocol_admin", &[]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_005); -// execute_pause_stream(deps.as_mut(), env, info, 1).unwrap(); - -// // cancel the stream -// let info = mock_info("protocol_admin", &[]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_006); -// execute_cancel_stream(deps.as_mut(), env, info, 1).unwrap(); - -// // can't resume if cancelled -// let info = mock_info("protocol_admin", &[]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_007); -// let res = execute_resume_stream(deps.as_mut(), env, info, 1).unwrap_err(); -// assert_eq!(res, ContractError::StreamIsCancelled {}); -// } - -// #[test] -// fn test_cancel_protocol_admin() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_000_000); -// let end = Timestamp::from_seconds(5_000_000); -// let out_supply = Uint256::from(1_000_000_000_000u128); -// let out_denom = "out_denom"; - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: "in".to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let info = mock_info( -// "creator1", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// "in".to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); - -// // subscription -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(0); -// let funds = Coin::new(2_000_000_000_000, "in"); -// let info = mock_info("creator1", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: Some("operator".to_string()), -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - -// // non protocol admin can't cancel -// let info = mock_info("non_protocol_admin", &[]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_000); -// let err = execute_cancel_stream(deps.as_mut(), env, info, 1).unwrap_err(); -// assert_eq!(err, ContractError::Unauthorized {}); - -// // cant cancel without pause -// let info = mock_info("protocol_admin", &[]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_000); -// let err = execute_cancel_stream(deps.as_mut(), env, info, 1).unwrap_err(); -// assert_eq!(err, ContractError::StreamNotPaused {}); - -// // pause -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(2_000_000); -// let info = mock_info("protocol_admin", &[]); -// execute_pause_stream(deps.as_mut(), env, info, 1).unwrap(); - -// //cancel -// let info = mock_info("protocol_admin", &[]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(2_500_000); -// let response = execute_cancel_stream(deps.as_mut(), env, info, 1).unwrap(); -// //out_tokens and the creation fee are sent back to the treasury upon cancellation -// assert_eq!( -// response.messages, -// [ -// SubMsg { -// payload: Binary::from(b""), -// id: 0, -// msg: Bank(BankMsg::Send { -// to_address: "treasury".to_string(), -// amount: Vec::from([Coin { -// denom: "out_denom".to_string(), -// amount: Uint256::from(1000000000000) -// }]) -// }), -// gas_limit: None, -// reply_on: ReplyOn::Never -// }, -// SubMsg { -// payload: Binary::from(b""), -// id: 0, -// msg: Bank(BankMsg::Send { -// to_address: "treasury".to_string(), -// amount: Vec::from([Coin { -// denom: "fee".to_string(), -// amount: Uint256::from(100) -// }]) -// }), -// gas_limit: None, -// reply_on: ReplyOn::Never -// } -// ] -// ); - -// // can't cancel cancelled stream -// let info = mock_info("protocol_admin", &[]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(2_500_000); -// let response = execute_cancel_stream(deps.as_mut(), env, info, 1).unwrap_err(); -// assert_eq!(response, ContractError::StreamIsCancelled {}); -// } - -// #[test] -// fn test_withdraw_pause() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_000_000); -// let end = Timestamp::from_seconds(5_000_000); -// let out_supply = Uint256::from(1_000_000_000_000u128); -// let out_denom = "out_denom"; - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: "in".to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let info = mock_info( -// "creator1", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// "in".to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); - -// // subscription -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(0); -// let funds = Coin::new(2_000_000_000_000, "in"); -// let info = mock_info("creator1", &[funds.clone()]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: Some("operator".to_string()), -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - -// // withdraw with cap -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(5000); -// let info = mock_info("creator1", &[]); -// let cap = Uint256::from(25_000_000u128); -// let msg = crate::msg::ExecuteMsg::Withdraw { -// stream_id: 1, -// cap: Some(cap), -// operator_target: None, -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - -// let position = -// query_position(deps.as_ref(), mock_env(), 1, "creator1".to_string()).unwrap(); -// assert_eq!(position.in_balance, Uint256::from(1_997_475_000_000u128)); -// assert_eq!(position.spent, Uint256::from(2_500_000_000u128)); -// assert_eq!(position.purchased, Uint256::from(1_250_000_000u128)); -// // first fund amount should be equal to in_balance + spent + cap -// assert_eq!( -// position.in_balance + position.spent + cap, -// Uint256::from_str(funds.amount.to_string().as_str()).unwrap() -// ); - -// // can't withdraw pause -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(6000); -// let info = mock_info("creator1", &[]); -// let err = execute_withdraw_paused(deps.as_mut(), env, info, 1, None, None).unwrap_err(); -// assert_eq!(err, ContractError::StreamNotPaused {}); - -// // pause -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(6000); -// let info = mock_info("protocol_admin", &[]); -// execute_pause_stream(deps.as_mut(), env, info, 1).unwrap(); - -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(6500); -// let stream1_old = query_stream(deps.as_ref(), env, 1).unwrap(); -// //Unauthorized check -// let info = mock_info("random", &[]); -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(7000); -// let res = execute_withdraw_paused( -// deps.as_mut(), -// env, -// info, -// 1, -// None, -// Some("creator1".to_string()), -// ) -// .unwrap_err(); - -// assert_eq!(res, ContractError::Unauthorized {}); -// //Cap exceeds in balance check -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(7000); -// let info = mock_info("creator1", &[]); -// let res = execute_withdraw_paused( -// deps.as_mut(), -// env, -// info, -// 1, -// Some(Uint256::from(2_000_000_000_000u128 + 1u128)), -// None, -// ) -// .unwrap_err(); -// assert_eq!( -// res, -// ContractError::WithdrawAmountExceedsBalance(Uint256::from(2_000_000_000_001u128)) -// ); -// // Withdraw cap is zero -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(7000); -// let info = mock_info("creator1", &[]); -// let res = -// execute_withdraw_paused(deps.as_mut(), env, info, 1, Some(Uint256::zero()), None) -// .unwrap_err(); -// assert_eq!(res, ContractError::InvalidWithdrawAmount {}); - -// //withdraw with cap -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(7000); -// let info = mock_info("creator1", &[]); -// let cap = Uint256::from(25_000_000u128); -// execute_withdraw_paused(deps.as_mut(), env, info, 1, Some(cap), None).unwrap(); - -// // withdraw after pause -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(7000); -// let info = mock_info("creator1", &[]); -// let res = execute_withdraw_paused(deps.as_mut(), env, info, 1, None, None).unwrap(); -// assert_eq!( -// res.messages, -// vec![SubMsg { -// id: 0, -// msg: BankMsg::Send { -// to_address: "creator1".to_string(), -// amount: vec![Coin { -// denom: "in".to_string(), -// amount: Uint128::new(1996950006258), -// }], -// } -// .into(), -// gas_limit: None, -// reply_on: ReplyOn::Never, -// }] -// ); - -// // stream not updated -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(8000); -// let stream1_new = query_stream(deps.as_ref(), env, 1).unwrap(); -// // dist_index not updated -// assert_eq!(stream1_old.dist_index, stream1_new.dist_index); -// assert_eq!(stream1_new.in_supply, Uint256::zero()); -// assert_eq!(stream1_new.shares, Uint256::zero()); - -// // position updated -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(8001); -// let position = -// query_position(deps.as_ref(), mock_env(), 1, "creator1".to_string()).unwrap(); -// // in_balance updated -// assert_eq!(position.in_balance, Uint256::zero()); -// assert_eq!(position.spent, Uint256::from(2_999_993_742u128)); -// assert_eq!(position.purchased, Uint256::from(1_499_999_998u128)); -// assert_eq!(position.shares, Uint256::zero()); -// } - -// #[test] -// fn test_resume() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_000_000); -// let end = Timestamp::from_seconds(5_000_000); -// let out_supply = Uint256::from(1_000_000_000_000u128); -// let out_denom = "out_denom"; - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: "in".to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let info = mock_info( -// "creator1", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// "in".to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); - -// // first subscription -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_000); -// let funds = Coin::new(3_000, "in"); -// let info = mock_info("position1", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - -// //cant resume if not paused -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_000); -// let res = sudo_resume_stream(deps.as_mut(), env, 1).unwrap_err(); -// assert_eq!(res, ContractError::StreamNotPaused {}); - -// // pause -// let info = mock_info("protocol_admin", &[]); -// let mut env = mock_env(); -// let pause_date = start.plus_seconds(2_000_000); -// env.block.time = pause_date; -// execute_pause_stream(deps.as_mut(), env, info, 1).unwrap(); - -// // resume -// let mut env = mock_env(); -// let resume_date = start.plus_seconds(3_000_000); -// env.block.time = resume_date; -// sudo_resume_stream(deps.as_mut(), env, 1).unwrap(); - -// // new end date is correct -// let new_end_date = end.plus_nanos(resume_date.nanos() - pause_date.nanos()); -// let stream = query_stream(deps.as_ref(), mock_env(), 1).unwrap(); -// assert_eq!(stream.end_time, new_end_date); -// } - -// #[test] -// fn test_sudo_pause_stream() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_000_000); -// let end = Timestamp::from_seconds(5_000_000); -// let out_supply = Uint256::from(1_000_000_000_000u128); -// let out_denom = "out_denom"; - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: "in".to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let info = mock_info( -// "creator1", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// "in".to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); - -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(500_000); -// let res = sudo_pause_stream(deps.as_mut(), env, 1).unwrap_err(); -// assert_eq!(res, ContractError::StreamNotStarted {}); - -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(6_000_000); -// let res = sudo_pause_stream(deps.as_mut(), env, 1).unwrap_err(); -// assert_eq!(res, ContractError::StreamEnded {}); - -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(3_000_000); -// let res = sudo_pause_stream(deps.as_mut(), env, 1).unwrap(); -// assert_eq!( -// res, -// Response::new() -// .add_attribute("action", "sudo_pause_stream") -// .add_attribute("stream_id", "1") -// .add_attribute("is_paused", "true") -// .add_attribute("pause_date", "3000000.000000000") -// ); - -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(4_000_000); -// let res = sudo_pause_stream(deps.as_mut(), env, 1).unwrap_err(); -// assert_eq!(res, ContractError::StreamKillswitchActive {}); -// } - -// #[test] -// fn test_range_queries() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(2000); -// let end = Timestamp::from_seconds(1_000_000); -// let out_supply = Uint256::from(1_000_000u128); -// let out_denom = "out_denom"; - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(100); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(1000), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: "in".to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(1); -// let info = mock_info( -// "creator1", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// //first stream -// execute_create_stream( -// deps.as_mut(), -// env.clone(), -// info.clone(), -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// "in".to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); -// //second stream -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// "in".to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); - -// let res = list_streams(deps.as_ref(), None, None).unwrap(); -// assert_eq!(res.streams.len(), 2); - -// // first subscription to first stream -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(100); -// let info = mock_info("creator1", &[Coin::new(1_000_000, "in")]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - -// // second subscription to first stream -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(100); -// let info = mock_info("creator2", &[Coin::new(1_000_000, "in")]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: None, -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - -// let res = list_positions(deps.as_ref(), 1, None, None).unwrap(); -// assert_eq!(res.positions.len(), 2); -// } - -// #[test] -// fn test_exit_cancel() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_000_000); -// let end = Timestamp::from_seconds(5_000_000); -// let out_supply = Uint256::from(1_000_000_000_000u128); -// let out_denom = "out_denom"; - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: "in".to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let info = mock_info( -// "creator1", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// "in".to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// None, -// "v1".to_string(), -// ) -// .unwrap(); - -// // subscription -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(0); -// let funds = Coin::new(2_000_000_000_000, "in"); -// let info = mock_info("creator1", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: Some("operator".to_string()), -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - -// // cant cancel without pause -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_000); -// let err = sudo_cancel_stream(deps.as_mut(), env, 1).unwrap_err(); -// assert_eq!(err, ContractError::StreamNotPaused {}); - -// // pause -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(2_000_000); -// let info = mock_info("protocol_admin", &[]); -// execute_pause_stream(deps.as_mut(), env, info, 1).unwrap(); - -// //can't exit before cancel -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(2_250_000); -// let info = mock_info("creator1", &[]); -// let res = execute_exit_cancelled(deps.as_mut(), env, info, 1, None).unwrap_err(); -// assert_eq!(res, ContractError::StreamNotCancelled {}); - -// //cancel -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(2_500_000); -// let response = sudo_cancel_stream(deps.as_mut(), env, 1).unwrap(); -// //out_tokens and the creation fee are sent back to the treasury upon cancellation -// assert_eq!( -// response.messages, -// [ -// SubMsg { -// id: 0, -// msg: Bank(BankMsg::Send { -// to_address: "treasury".to_string(), -// amount: Vec::from([Coin { -// denom: "out_denom".to_string(), -// amount: Uint128::new(1000000000000) -// }]) -// }), -// gas_limit: None, -// reply_on: ReplyOn::Never -// }, -// SubMsg { -// id: 0, -// msg: Bank(BankMsg::Send { -// to_address: "treasury".to_string(), -// amount: Vec::from([Coin { -// denom: "fee".to_string(), -// amount: Uint128::new(100) -// }]) -// }), -// gas_limit: None, -// reply_on: ReplyOn::Never -// } -// ] -// ); - -// //random operator can't exit -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(2_250_000); -// let info = mock_info("random", &[]); -// let res = -// execute_exit_cancelled(deps.as_mut(), env, info, 1, Some("creator1".to_string())) -// .unwrap_err(); -// assert_eq!(res, ContractError::Unauthorized {}); - -// // exit -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(3_000_000); -// let info = mock_info("creator1", &[]); -// let res = execute_exit_cancelled(deps.as_mut(), env, info, 1, None).unwrap(); -// let msg = res.messages.first().unwrap(); -// assert_eq!( -// msg.msg, -// Bank(BankMsg::Send { -// to_address: "creator1".to_string(), -// amount: vec![Coin::new(2000000000000, "in")] -// }) -// ); -// } -// #[test] -// fn test_treasury_cancel_stream() { -// use crate::killswitch::execute_treasury_cancel_stream; - -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_000_000); -// let end = Timestamp::from_seconds(5_000_000); -// let out_supply = Uint256::from(500u128); -// let out_denom = "out_denom"; -// let in_denom = "in_denom"; - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(100_000), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: in_denom.to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let info = mock_info( -// "creator", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// in_denom.to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// Some(1_000u128.into()), -// "v1".to_string(), -// ) -// .unwrap(); - -// // Cancel period should be ended Now+min_seconds_until_start_time - -// // Cancel stream with wrong address -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(100); - -// let error = -// execute_treasury_cancel_stream(deps.as_mut(), env, mock_info("random", &[]), 1) -// .unwrap_err(); -// assert_eq!(error, ContractError::Unauthorized {}); - -// // Try subscribing to the stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(100); -// let funds = Coin::new(250, "in_denom"); -// let info = mock_info("subscriber", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: Some("operator".to_string()), -// tos_version: "v1".to_string(), -// }; -// let err = execute(deps.as_mut(), env.clone(), info, msg).unwrap_err(); -// assert_eq!(err, ContractError::TreasuryCancelPeriodActive {}); - -// // Try canceling the stream outside the cancel period -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(100_000 + 1); -// let error = execute_treasury_cancel_stream( -// deps.as_mut(), -// env.clone(), -// mock_info("treasury", &[]), -// 1, -// ) -// .unwrap_err(); -// assert_eq!(error, ContractError::TreasuryCancelPeriodEnded {}); - -// // Query the stream -// let _stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); - -// // Cancel the stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(100_000); -// let response = execute_treasury_cancel_stream( -// deps.as_mut(), -// env.clone(), -// mock_info("treasury", &[]), -// 1, -// ) -// .unwrap(); -// assert_eq!( -// response.messages, -// [SubMsg { -// id: 0, -// msg: Bank(BankMsg::Send { -// to_address: "treasury".to_string(), -// amount: vec![Coin { -// denom: "out_denom".to_string(), -// amount: Uint128::new(500) -// }] -// }), -// gas_limit: None, -// reply_on: ReplyOn::Never -// }] -// ); - -// // Query the stream again should return error because we removed the stream -// let _stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap_err(); -// } -// } -// mod threshold { -// use crate::{ -// killswitch::{execute_cancel_stream_with_threshold, execute_exit_cancelled}, -// threshold::ThresholdError, -// }; - -// // Create a stream with a threshold -// // Subscribe to the stream -// use super::*; - -// #[test] -// fn test_threshold_reached() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_000_000); -// let end = Timestamp::from_seconds(5_000_000); -// let out_supply = Uint256::from(500u128); -// let out_denom = "out_denom"; -// let in_denom = "in_denom"; - -// // threshold = 500*0.5 / 1-0.01 =252.5 - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: in_denom.to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let info = mock_info( -// "creator", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// in_denom.to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// Some(Uint256::from(250u128)), -// "v1".to_string(), -// ) -// .unwrap(); - -// // subscription -// let mut env = mock_env(); -// env.block.time = start; -// let funds = Coin::new(252, "in_denom"); -// let info = mock_info("subscriber", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: Some("operator".to_string()), -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - -// // Threshold should be reached -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(1); - -// // Exit should be possible -// // Since there is only one subscriber all out denom should be sent to subscriber -// // In calculations we are always rounding down that one token will be left in the stream -// // Asuming token is 6 decimals -// // This amount could be considered as insignificant -// let info = mock_info("subscriber", &[]); -// let res = execute_exit_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap(); -// assert_eq!( -// res.messages, -// vec![SubMsg::new(BankMsg::Send { -// to_address: "subscriber".to_string(), -// amount: vec![Coin::new(499, "out_denom")], -// })], -// ); - -// // Creator finalizes the stream -// let info = mock_info("treasury", &[]); -// let res = execute_finalize_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap(); -// // Creator's revenue -// assert_eq!( -// res.messages[0].msg, -// cosmwasm_std::CosmosMsg::Bank(BankMsg::Send { -// to_address: "treasury".to_string(), -// amount: vec![Coin::new(250, "in_denom")], -// }) -// ); -// assert_eq!( -// res.messages[1].msg, -// cosmwasm_std::CosmosMsg::Bank(BankMsg::Send { -// to_address: "collector".to_string(), -// amount: vec![Coin::new(100, "fee")], -// }) -// ); -// assert_eq!( -// res.messages[2].msg, -// cosmwasm_std::CosmosMsg::Bank(BankMsg::Send { -// to_address: "collector".to_string(), -// amount: vec![Coin::new(2, "in_denom")], -// }) -// ) -// } - -// #[test] -// fn test_threshold_not_reached() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_000_000); -// let end = Timestamp::from_seconds(5_000_000); -// let out_supply = Uint256::from(500u128); -// let out_denom = "out_denom"; -// let in_denom = "in_denom"; - -// // threshold = 500*0.5 / 1-0.01 =252.5 - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.height = 0; -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: in_denom.to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let info = mock_info( -// "creator", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// in_denom.to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// Some(500u128.into()), -// "v1".to_string(), -// ) -// .unwrap(); - -// // Subscription 1 -// let mut env = mock_env(); -// env.block.time = start; -// let funds = Coin::new(250, "in_denom"); -// let info = mock_info("subscriber", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: Some("operator".to_string()), -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); - -// // Subscription 2 -// let funds = Coin::new(1, "in_denom"); -// let info = mock_info("subscriber2", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: Some("operator".to_string()), -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); - -// // Set time to the end of the stream -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(1); - -// // Exit should not be possible -// let info = mock_info("subscriber", &[]); -// let res = execute_exit_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap_err(); -// assert_eq!( -// res, -// ContractError::ThresholdError(ThresholdError::ThresholdNotReached {}) -// ); - -// // Finalize should not be possible -// let info = mock_info("treasury", &[]); -// let res = -// execute_finalize_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap_err(); -// assert_eq!( -// res, -// ContractError::ThresholdError(ThresholdError::ThresholdNotReached {}) -// ); - -// // Subscriber one executes exit cancelled before creator cancels stream -// let info = mock_info("subscriber", &[]); -// let res = execute_exit_cancelled(deps.as_mut(), env.clone(), info, 1, None).unwrap(); -// assert_eq!( -// res.messages, -// vec![SubMsg::new(BankMsg::Send { -// to_address: "subscriber".to_string(), -// amount: vec![Coin::new(250, "in_denom")], -// })] -// ); -// // Creator threshold cancels the stream -// let info = mock_info("treasury", &[]); -// let res = -// execute_cancel_stream_with_threshold(deps.as_mut(), env.clone(), info, 1).unwrap(); -// assert_eq!( -// res.messages, -// vec![ -// // Out denom refunded -// SubMsg::new(BankMsg::Send { -// to_address: "treasury".to_string(), -// amount: vec![Coin::new(500, "out_denom")], -// }), -// ] -// ); -// // Creator can not finalize the stream -// let info = mock_info("treasury", &[]); -// let res = -// execute_finalize_stream(deps.as_mut(), env.clone(), info, 1, None).unwrap_err(); -// assert_eq!(res, ContractError::StreamKillswitchActive {}); - -// // Creator can not cancel the stream again -// let info = mock_info("treasury", &[]); -// let res = execute_cancel_stream_with_threshold(deps.as_mut(), env.clone(), info, 1) -// .unwrap_err(); -// assert_eq!(res, ContractError::StreamKillswitchActive {}); - -// // Subscriber 2 executes exit cancelled after creator cancels stream -// let info = mock_info("subscriber2", &[]); -// let res = execute_exit_cancelled(deps.as_mut(), env.clone(), info, 1, None).unwrap(); -// assert_eq!( -// // In denom refunded -// res.messages, -// vec![SubMsg::new(BankMsg::Send { -// to_address: "subscriber2".to_string(), -// amount: vec![Coin::new(1, "in_denom")], -// })] -// ); -// } - -// #[test] -// fn test_threshold_cancel() { -// let treasury = Addr::unchecked("treasury"); -// let start = Timestamp::from_seconds(1_000_000); -// let end = Timestamp::from_seconds(5_000_000); -// let out_supply = Uint256::from(500u128); -// let out_denom = "out_denom"; -// let in_denom = "in_denom"; - -// // threshold = 500*0.5 / 1-0.01 =252.5 - -// // instantiate -// let mut deps = mock_dependencies(); -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let msg = crate::msg::InstantiateMsg { -// min_stream_seconds: Uint64::new(1000), -// min_seconds_until_start_time: Uint64::new(0), -// stream_creation_denom: "fee".to_string(), -// stream_creation_fee: Uint128::new(100), -// exit_fee_percent: Decimal256::percent(1), -// fee_collector: "collector".to_string(), -// protocol_admin: "protocol_admin".to_string(), -// accepted_in_denom: in_denom.to_string(), -// tos_version: "v1".to_string(), -// }; -// instantiate(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); - -// // create stream -// let mut env = mock_env(); -// env.block.time = Timestamp::from_seconds(0); -// let info = mock_info( -// "creator", -// &[ -// Coin::new(out_supply.to_string().parse().unwrap(), out_denom), -// Coin::new(100, "fee"), -// ], -// ); -// execute_create_stream( -// deps.as_mut(), -// env, -// info, -// treasury.to_string(), -// "test".to_string(), -// Some("https://sample.url".to_string()), -// in_denom.to_string(), -// out_denom.to_string(), -// out_supply, -// start, -// end, -// Some(1_000u128.into()), -// "v1".to_string(), -// ) -// .unwrap(); - -// // Subscription 1 -// let mut env = mock_env(); -// env.block.time = start; -// let funds = Coin::new(250, "in_denom"); -// let info = mock_info("subscriber", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: Some("operator".to_string()), -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); - -// // Subscription 2 -// let funds = Coin::new(500, "in_denom"); -// let info = mock_info("subscriber2", &[funds]); -// let msg = crate::msg::ExecuteMsg::Subscribe { -// stream_id: 1, -// operator_target: None, -// operator: Some("operator".to_string()), -// tos_version: "v1".to_string(), -// }; -// let _res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); -// // Can not cancel stream before it ends -// let mut env = mock_env(); -// env.block.time = start.plus_seconds(1_000_000); -// let res = execute_cancel_stream_with_threshold( -// deps.as_mut(), -// env, -// mock_info("treasury", &[]), -// 1, -// ) -// .unwrap_err(); -// assert_eq!(res, ContractError::StreamNotEnded {}); - -// // Set block to the end of the stream -// let mut env = mock_env(); -// env.block.time = end.plus_seconds(1); - -// // Non creator can't cancel stream -// let res = execute_cancel_stream_with_threshold( -// deps.as_mut(), -// env.clone(), -// mock_info("random", &[]), -// 1, -// ) -// .unwrap_err(); -// assert_eq!(res, ContractError::Unauthorized {}); - -// // Creator can cancel stream -// let _res = execute_cancel_stream_with_threshold( -// deps.as_mut(), -// env.clone(), -// mock_info("treasury", &[]), -// 1, -// ) -// .unwrap(); -// // Query stream should return stream with is_cancelled = true -// let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); -// assert_eq!(stream.status, Status::Cancelled); -// } -// } -// } From 2c0f86e8bf33ebee4b1236e9c51ae64fea3ac162 Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Wed, 27 Aug 2025 02:46:44 +0300 Subject: [PATCH 23/25] bump cargo version --- Cargo.lock | 2 +- Cargo.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cb2be13..1cced8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -480,7 +480,7 @@ dependencies = [ [[package]] name = "cw-streamswap" -version = "0.1.5" +version = "0.1.6" dependencies = [ "cosmwasm-schema", "cosmwasm-std", diff --git a/Cargo.toml b/Cargo.toml index ca82733..ad1045f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cw-streamswap" -version = "0.1.5" -authors = ["Orkun Külçe "] +version = "0.1.6" +authors = ["Adnan Deniz Corlu "] edition = "2021" exclude = [ # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. From 41e7dbc70c2f03f80237710406ea635d33d40f25 Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Wed, 27 Aug 2025 02:57:03 +0300 Subject: [PATCH 24/25] fix exit stream swap fee calculation for attributes --- src/contract.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/contract.rs b/src/contract.rs index d13ef39..e9276e8 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -1154,7 +1154,7 @@ pub fn execute_exit_stream( // Swap fee = fixed_rate*position.spent_in this calculation is only for execution reply attributes let swap_fee = Decimal256::from_ratio(position.spent, Uint256::one()) .checked_mul(stream.stream_exit_fee_percent)? - .atomics(); + .to_uint_ceil(); let send_msg = CosmosMsg::Bank(BankMsg::Send { to_address: operator_target.to_string(), From 5bf03b8f6794b91cfec13fe910a3fd9a18832bae Mon Sep 17 00:00:00 2001 From: Ninjatosba Date: Fri, 29 Aug 2025 12:46:15 +0300 Subject: [PATCH 25/25] fix finalize on waiting state --- src/contract.rs | 2 +- tests/finalize.rs | 65 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/src/contract.rs b/src/contract.rs index e9276e8..3e22e50 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -1022,7 +1022,7 @@ pub fn execute_finalize_stream( update_stream(env.block.time, &mut stream)?; } - if stream.status == Status::Active { + if stream.status == Status::Active || stream.status == Status::Waiting { stream.status = Status::Finalized } // If threshold is set and not reached, finalize will fail diff --git a/tests/finalize.rs b/tests/finalize.rs index c8d44e5..d4f9374 100644 --- a/tests/finalize.rs +++ b/tests/finalize.rs @@ -1,7 +1,8 @@ use cosmwasm_std::testing::mock_dependencies; use cosmwasm_std::{BankMsg, Coin, CosmosMsg, Timestamp, Uint256}; -use cw_streamswap::contract::{execute, execute_finalize_stream}; +use cw_streamswap::contract::{execute, execute_finalize_stream, query_stream}; use cw_streamswap::msg::ExecuteMsg; +use cw_streamswap::state::Status; use cw_streamswap::ContractError; mod helpers; @@ -154,7 +155,8 @@ fn finalize_happy_path() { // Note: execute_update_stream function call would go here if available // Happy path finalization - let res = execute_finalize_stream(deps.as_mut(), env, treasury.clone(), 1, None).unwrap(); + let res = + execute_finalize_stream(deps.as_mut(), env.clone(), treasury.clone(), 1, None).unwrap(); // Verify finalization attributes assert_eq!(res.attributes[0].key, "action"); @@ -222,6 +224,10 @@ fn finalize_happy_path() { amount: vec![Coin::new(20_000_000_000u128, "in")], }) ); + + // Query stream + let stream = query_stream(deps.as_ref(), env, 1).unwrap(); + assert_eq!(stream.status, Status::Finalized); } #[test] @@ -278,3 +284,58 @@ fn finalize_duplicate_prevention() { let res = execute_finalize_stream(deps.as_mut(), env, treasury, 1, None); assert_eq!(res.unwrap_err(), ContractError::StreamAlreadyFinalized {}); } + +#[test] +fn finalize_stream_in_waiting_state() { + let mut deps = mock_dependencies(); + helpers::instantiate_defaults(deps.as_mut()); + + // Define actors + let treasury = helpers::mock_info("creator", &[]); + let subscriber1 = helpers::mock_info("creator1", &[]); + + // Create a stream + let b = helpers::CreateStreamBuilder::default() + .start_time(Timestamp::from_seconds(1_000_000)) + .end_time(Timestamp::from_seconds(5_000_000)); + let env = helpers::env_at(0); + let funds = vec![ + Coin { + denom: "out_denom".to_string(), + amount: b.out_supply, + }, + Coin { + denom: helpers::DEFAULT_STREAM_CREATION_DENOM.to_string(), + amount: Uint256::from(100u128), + }, + ]; + let mut treasury_funded = treasury.clone(); + treasury_funded.funds = funds.clone(); + execute(deps.as_mut(), env, treasury_funded, b.build()).unwrap(); + + // Subscribe to the stream during waiting state + let env = helpers::env_at(500_000); + let funds = Coin::new(2_000_000_000_000u128, "in"); + let mut subscriber1_funded = subscriber1.clone(); + subscriber1_funded.funds = vec![funds]; + let msg = ExecuteMsg::Subscribe { + stream_id: 1, + operator_target: None, + operator: None, + tos_version: "v1".to_string(), + }; + let _res = execute(deps.as_mut(), env.clone(), subscriber1_funded, msg).unwrap(); + + // Query stream + let stream = query_stream(deps.as_ref(), env.clone(), 1).unwrap(); + assert_eq!(stream.status, Status::Waiting); + + // Finalize stream + let env = helpers::env_at(5_000_000 + 1); + let _res = + execute_finalize_stream(deps.as_mut(), env.clone(), treasury.clone(), 1, None).unwrap(); + + // Verify stream is finalized + let stream = query_stream(deps.as_ref(), env, 1).unwrap(); + assert_eq!(stream.status, Status::Finalized); +}