diff --git a/.github/workflows/ci-contracts-upload-binaries.yml b/.github/workflows/ci-contracts-upload-binaries.yml index 4346726435f..d4fe57e01c7 100644 --- a/.github/workflows/ci-contracts-upload-binaries.yml +++ b/.github/workflows/ci-contracts-upload-binaries.yml @@ -31,7 +31,6 @@ jobs: - name: Install Rust stable uses: actions-rs/toolchain@v1 with: - toolchain: 1.77 target: wasm32-unknown-unknown override: true diff --git a/.github/workflows/publish-nym-contracts.yml b/.github/workflows/publish-nym-contracts.yml index ed1141ddf1f..e6354f33ae1 100644 --- a/.github/workflows/publish-nym-contracts.yml +++ b/.github/workflows/publish-nym-contracts.yml @@ -2,19 +2,18 @@ name: publish-nym-contracts on: workflow_dispatch: release: - types: [created] + types: [ created ] jobs: build: if: ${{ (startsWith(github.ref, 'refs/tags/nym-contracts-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }} - runs-on: [self-hosted, custom-ubuntu-20.04] + runs-on: [ self-hosted, custom-ubuntu-20.04 ] steps: - uses: actions/checkout@v4 - name: Install Rust stable uses: actions-rs/toolchain@v1 with: - toolchain: 1.77 target: wasm32-unknown-unknown override: true diff --git a/Cargo.lock b/Cargo.lock index 0bff8583a22..302296283e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,17 +89,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom 0.2.15", - "once_cell", - "version_check", -] - [[package]] name = "ahash" version = "0.8.11" @@ -229,9 +218,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.96" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "arbitrary" @@ -260,6 +249,127 @@ dependencies = [ "password-hash", ] +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", + "num-traits", + "rayon", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rayon", + "rustc_version 0.4.1", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", + "rayon", +] + [[package]] name = "arrayref" version = "0.3.9" @@ -662,6 +772,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bech32" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" + [[package]] name = "binascii" version = "0.1.4" @@ -702,7 +818,7 @@ checksum = "33415e24172c1b7d6066f6d999545375ab8e1d95421d6784bdfff9496f292387" dependencies = [ "bitcoin_hashes", "rand 0.8.5", - "rand_core 0.5.1", + "rand_core 0.6.4", "serde", "unicode-normalization", "zeroize", @@ -774,9 +890,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.6.0" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1230237285e3e10cde447185e8975408ae24deaa67205ce684805c25bc0c7937" +checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e" dependencies = [ "arrayref", "arrayvec", @@ -784,7 +900,6 @@ dependencies = [ "cfg-if", "constant_time_eq", "digest 0.10.7", - "memmap2", ] [[package]] @@ -817,9 +932,9 @@ dependencies = [ [[package]] name = "bls12_381" version = "0.8.0" -source = "git+https://github.com/jstuczyn/bls12_381?branch=temp/experimental-serdect#22cd0a16b674af1629110a2dc8b6cf6c73ea4cd9" +source = "git+https://github.com/jstuczyn/bls12_381?branch=temp/experimental-serdect-updated#9bf520059cb28323fc51469cae86868ef4fa6fbd" dependencies = [ - "digest 0.9.0", + "digest 0.10.7", "ff", "group", "pairing", @@ -832,9 +947,9 @@ dependencies = [ [[package]] name = "bnum" -version = "0.8.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab9008b6bb9fc80b5277f2fe481c09e828743d9151203e804583eb4c9e15b31d" +checksum = "3e31ea183f6ee62ac8b8a8cf7feddd766317adfb13ff469de57ce033efd6a790" [[package]] name = "brotli" @@ -1117,9 +1232,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.45" +version = "4.5.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e3040c8291884ddf39445dc033c70abc2bc44a42f0a3a00571a0f483a83f0cd" +checksum = "375f9d8255adeeedd51053574fd8d4ba875ea5fa558e86617b07f09f1680c8b6" dependencies = [ "clap", ] @@ -1208,7 +1323,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2895653b4d9f1538a83970077cb01dfc77a4810524e51a110944688e916b18e" dependencies = [ "prost 0.11.9", - "prost-types 0.11.9", + "prost-types", "tonic", "tracing-core", ] @@ -1225,7 +1340,7 @@ dependencies = [ "futures", "hdrhistogram", "humantime 2.1.0", - "prost-types 0.11.9", + "prost-types", "serde", "serde_json", "thread_local", @@ -1312,17 +1427,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "cosmos-sdk-proto" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32560304ab4c365791fd307282f76637213d8083c1a98490c35159cd67852237" -dependencies = [ - "prost 0.12.6", - "prost-types 0.12.6", - "tendermint-proto 0.34.1", -] - [[package]] name = "cosmos-sdk-proto" version = "0.26.1" @@ -1330,27 +1434,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "462e1f6a8e005acc8835d32d60cbd7973ed65ea2a8d8473830e675f050956427" dependencies = [ "prost 0.13.5", - "tendermint-proto 0.40.1", -] - -[[package]] -name = "cosmrs" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47126f5364df9387b9d8559dcef62e99010e1d4098f39eb3f7ee4b5c254e40ea" -dependencies = [ - "bip32", - "cosmos-sdk-proto 0.20.0", - "ecdsa", - "eyre", - "k256", - "rand_core 0.6.4", - "serde", - "serde_json", - "signature", - "subtle-encoding", - "tendermint 0.34.1", - "thiserror 1.0.69", + "tendermint-proto", ] [[package]] @@ -1360,7 +1444,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1394c263335da09e8ba8c4b2c675d804e3e0deb44cce0866a5f838d3ddd43d02" dependencies = [ "bip32", - "cosmos-sdk-proto 0.26.1", + "cosmos-sdk-proto", "ecdsa", "eyre", "k256", @@ -1369,39 +1453,57 @@ dependencies = [ "serde_json", "signature", "subtle-encoding", - "tendermint 0.40.1", + "tendermint", "tendermint-rpc", "thiserror 1.0.69", ] +[[package]] +name = "cosmwasm-core" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de32156e4fd80c59be39ed6f4ebb596d59b0a4eaf01d6f146e27628ec7e8f8c1" + [[package]] name = "cosmwasm-crypto" -version = "1.5.2" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6aa9f904de106fa16443ad14ec2abe75e94ba003bb61c681c0e43d4c58d2a" +checksum = "38fe1e6107ae3c9ba5e1f14178dd8bd52210535030d07f0609cf0d754c1f7de2" dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-serialize", + "cosmwasm-core", + "curve25519-dalek", "digest 0.10.7", "ecdsa", "ed25519-zebra", "k256", + "num-traits", + "p256", "rand_core 0.6.4", + "rayon", + "sha2 0.10.8", "thiserror 1.0.69", ] [[package]] name = "cosmwasm-derive" -version = "1.5.10" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343f5ce9f8f83dc2b3c03cb51ac2d39315ac4defd200d544e5a190726e01d2d5" +checksum = "484926c9dc8b90c59a717946c86bb272182003cbaabb378560086648d4056656" dependencies = [ - "syn 1.0.109", + "proc-macro2", + "quote", + "syn 2.0.98", ] [[package]] name = "cosmwasm-schema" -version = "1.4.3" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ae2e971fb831d0c4fa3c8c3d2291cdbdd73786a73d65196dbf983d9b2468af" +checksum = "d2a25988c48703d1450a5ac5e7cd3ad82ec8a7552f3dde8f9b8927e682bd02c7" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -1412,45 +1514,39 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.4.3" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cadc57fd0825b85bc2f9b972c17da718b9efb4bc17e5935cc2d6036324f853d" +checksum = "bbd08fac60d30e341d9365033f519c0a36fdf38bde6ec196179e653d2723aebd" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.98", ] [[package]] name = "cosmwasm-std" -version = "1.4.3" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e98e19fae6c3f468412f731274b0f9434602722009d6a77432d39c7c4bb09202" +checksum = "c92be4747d9abe3a96a5a78af34d29947992b3f67f602987ff8a87142ce9c413" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", + "bech32", "bnum", + "cosmwasm-core", "cosmwasm-crypto", "cosmwasm-derive", - "derivative", - "forward_ref", + "derive_more", "hex", + "rand_core 0.6.4", + "rmp-serde", "schemars", "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror 1.0.69", ] -[[package]] -name = "cosmwasm-storage" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8935079712688b8d8d4c036378b38b49d13692621c6fcba96700fadfd5126a18" -dependencies = [ - "cosmwasm-std", - "serde", -] - [[package]] name = "cpufeatures" version = "0.2.17" @@ -1710,19 +1806,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[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 2.6.1", - "zeroize", -] - [[package]] name = "curve25519-dalek" version = "4.1.3" @@ -1766,9 +1849,9 @@ dependencies = [ [[package]] name = "cw-controllers" -version = "1.1.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d8edce4b78785f36413f67387e4be7d0cb7d032b5d4164bcc024f9c3f3f2ea" +checksum = "50c1804013d21060b994dea28a080f9eab78a3bcb6b617f05e7634b0600bf7b1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1781,9 +1864,9 @@ dependencies = [ [[package]] name = "cw-storage-plus" -version = "1.2.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5ff29294ee99373e2cd5fd21786a3c0ced99a52fec2ca347d565489c61b723c" +checksum = "f13360e9007f51998d42b1bc6b7fa0141f74feae61ed5fd1e5b0a89eec7b5de1" dependencies = [ "cosmwasm-std", "schemars", @@ -1792,24 +1875,22 @@ dependencies = [ [[package]] name = "cw-utils" -version = "1.0.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c80e93d1deccb8588db03945016a292c3c631e6325d349ebb35d2db6f4f946f7" +checksum = "07dfee7f12f802431a856984a32bce1cb7da1e6c006b5409e3981035ce562dec" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw2", "schemars", - "semver 1.0.25", "serde", "thiserror 1.0.69", ] [[package]] name = "cw2" -version = "1.1.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c120b24fbbf5c3bedebb97f2cc85fbfa1c3287e09223428e7e597b5293c1fa" +checksum = "b04852cd38f044c0751259d5f78255d07590d136b8a86d4e09efdd7666bd6d27" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1822,9 +1903,9 @@ dependencies = [ [[package]] name = "cw20" -version = "1.1.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "526e39bb20534e25a1cd0386727f0038f4da294e5e535729ba3ef54055246abd" +checksum = "a42212b6bf29bbdda693743697c621894723f35d3db0d5df930be22903d0e27c" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1835,9 +1916,9 @@ dependencies = [ [[package]] name = "cw3" -version = "1.1.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2967fbd073d4b626dd9e7148e05a84a3bebd9794e71342e12351110ffbb12395" +checksum = "d5e53c2057526c65d9c88be8b2a564729ebad7a3d87ee97b97665a71446f913a" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1850,9 +1931,9 @@ dependencies = [ [[package]] name = "cw4" -version = "1.1.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24754ff6e45f2a1c60adc409d9b2eb87666012c44021329141ffaab3388fccd2" +checksum = "d33f5c8a6b6cd1bd24e212d7f44967697bfa3c4f9cc3f9a8e1c58f5fe5db032d" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -2155,7 +2236,6 @@ version = "0.1.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cosmwasm-storage", "cw-storage-plus", "nym-coconut-dkg-common", "nym-contracts-common", @@ -2179,6 +2259,15 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "feeef44e73baff3a26d371801df019877a9866a8c493d315ab00177843314f35" +[[package]] +name = "easy-addr" +version = "0.1.0" +dependencies = [ + "cosmwasm-std", + "quote", + "syn 1.0.109", +] + [[package]] name = "ecdsa" version = "0.16.9" @@ -2244,7 +2333,7 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ - "curve25519-dalek 4.1.3", + "curve25519-dalek", "ed25519", "rand_core 0.6.4", "serde", @@ -2255,16 +2344,16 @@ dependencies = [ [[package]] name = "ed25519-zebra" -version = "3.1.0" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +checksum = "7d9ce6874da5d4415896cd45ffbc4d1cfc0c4f9c079427bd870742c30f2f65a9" dependencies = [ - "curve25519-dalek 3.2.0", - "hashbrown 0.12.3", + "curve25519-dalek", + "ed25519", + "hashbrown 0.14.5", "hex", "rand_core 0.6.4", - "serde", - "sha2 0.9.9", + "sha2 0.10.8", "zeroize", ] @@ -2279,9 +2368,9 @@ dependencies = [ [[package]] name = "elliptic-curve" -version = "0.13.7" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9775b22bc152ad86a0cf23f0f348b884b26add12bf741e7ffc4d4ab2ab4d205" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", @@ -2456,7 +2545,7 @@ dependencies = [ "rand 0.8.5", "rand_pcg", "rand_seeder", - "reqwest 0.12.4", + "reqwest 0.12.12", "rocket", "rocket_cors", "rocket_okapi", @@ -2620,12 +2709,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "forward_ref" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" - [[package]] name = "forwarded-header-value" version = "0.1.1" @@ -3026,8 +3109,14 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.7.8", + "ahash", ] [[package]] @@ -3036,7 +3125,7 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash 0.8.11", + "ahash", "allocator-api2", ] @@ -3157,9 +3246,9 @@ checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" [[package]] name = "hickory-proto" -version = "0.24.4" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92652067c9ce6f66ce53cc38d1169daa36e6e7eb7dd3b63b5103bd9d97117248" +checksum = "2ad3d6d98c648ed628df039541a5577bee1a7c83e9e16fe3dbedeea4cdfeb971" dependencies = [ "async-trait", "bytes", @@ -3188,9 +3277,9 @@ dependencies = [ [[package]] name = "hickory-resolver" -version = "0.24.4" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbb117a1ca520e111743ab2f6688eddee69db4e0ea242545a604dce8a66fd22e" +checksum = "dcf287bde7b776e85d7188e6e5db7cf410a2f9531fe82817eb87feed034c8d14" dependencies = [ "cfg-if", "futures-util", @@ -3449,19 +3538,20 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.26.0" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", "http 1.2.0", "hyper 1.6.0", "hyper-util", - "rustls 0.22.4", + "rustls 0.23.23", "rustls-pki-types", "tokio", - "tokio-rustls 0.25.0", + "tokio-rustls 0.26.1", "tower-service", + "webpki-roots 0.26.8", ] [[package]] @@ -3701,7 +3791,6 @@ dependencies = [ "base85rs", "cosmwasm-schema", "cosmwasm-std", - "cosmwasm-storage", ] [[package]] @@ -3870,7 +3959,7 @@ dependencies = [ "socket2", "widestring", "windows-sys 0.48.0", - "winreg 0.50.0", + "winreg", ] [[package]] @@ -3958,15 +4047,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.13.0" @@ -4012,9 +4092,9 @@ dependencies = [ [[package]] name = "k256" -version = "0.13.2" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f01b677d82ef7a676aa37e099defd83a28e15687112cafdd112d60236b6115b" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if", "ecdsa", @@ -4354,15 +4434,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "memmap2" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" -dependencies = [ - "libc", -] - [[package]] name = "memoffset" version = "0.9.1" @@ -4708,6 +4779,16 @@ dependencies = [ "winapi", ] +[[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-bigint-dig" version = "0.8.4" @@ -4731,17 +4812,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" -[[package]] -name = "num-derive" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "num-integer" version = "0.1.46" @@ -4859,12 +4929,12 @@ dependencies = [ "pin-project", "rand 0.8.5", "rand_chacha 0.3.1", - "reqwest 0.12.4", + "reqwest 0.12.12", "schemars", "semver 1.0.25", "serde", "serde_json", - "sha2 0.9.9", + "sha2 0.10.8", "sqlx", "tempfile", "thiserror 2.0.11", @@ -4887,7 +4957,7 @@ name = "nym-api-requests" version = "0.1.0" dependencies = [ "bs58", - "cosmrs 0.21.1", + "cosmrs", "cosmwasm-std", "ecdsa", "getset", @@ -4907,7 +4977,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.8", - "tendermint 0.40.1", + "tendermint", "thiserror 2.0.11", "time", "ts-rs", @@ -5035,7 +5105,7 @@ name = "nym-bity-integration" version = "0.1.0" dependencies = [ "anyhow", - "cosmrs 0.21.1", + "cosmrs", "eyre", "k256", "nym-cli-commands", @@ -5082,7 +5152,7 @@ dependencies = [ "clap", "colored", "comfy-table", - "cosmrs 0.21.1", + "cosmrs", "cosmwasm-std", "csv", "cw-utils", @@ -5249,7 +5319,7 @@ name = "nym-client-core-gateways-storage" version = "0.1.0" dependencies = [ "async-trait", - "cosmrs 0.21.1", + "cosmrs", "log", "nym-crypto", "nym-gateway-requests", @@ -5317,7 +5387,7 @@ dependencies = [ "bls12_381", "bs58", "criterion", - "digest 0.9.0", + "digest 0.10.7", "doc-comment", "ff", "getrandom 0.2.15", @@ -5329,21 +5399,11 @@ dependencies = [ "rand_chacha 0.3.1", "serde", "serde_derive", - "sha2 0.9.9", + "sha2 0.10.8", "thiserror 2.0.11", "zeroize", ] -[[package]] -name = "nym-coconut-bandwidth-contract-common" -version = "0.1.0" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw2", - "nym-multisig-contract-common", -] - [[package]] name = "nym-coconut-dkg-common" version = "0.1.0" @@ -5366,7 +5426,7 @@ dependencies = [ "bs58", "cfg-if", "criterion", - "digest 0.9.0", + "digest 0.10.7", "ff", "group", "itertools 0.13.0", @@ -5375,7 +5435,7 @@ dependencies = [ "rand 0.8.5", "rayon", "serde", - "sha2 0.9.9", + "sha2 0.10.8", "subtle 2.6.1", "thiserror 2.0.11", "zeroize", @@ -5460,7 +5520,7 @@ dependencies = [ "nym-network-defaults", "nym-validator-client", "rand 0.8.5", - "reqwest 0.12.4", + "reqwest 0.12.12", "serde", "serde_json", "sqlx", @@ -5491,7 +5551,7 @@ dependencies = [ "nym-http-api-client", "nym-http-api-common", "nym-serde-helpers", - "reqwest 0.12.4", + "reqwest 0.12.12", "schemars", "serde", "serde_json", @@ -5568,7 +5628,7 @@ version = "0.1.0" dependencies = [ "bincode", "bls12_381", - "cosmrs 0.21.1", + "cosmrs", "log", "nym-api-requests", "nym-credentials-interface", @@ -5648,7 +5708,7 @@ dependencies = [ "rand_core 0.6.4", "serde", "serde_derive", - "sha2 0.9.9", + "sha2 0.10.8", "thiserror 2.0.11", "zeroize", ] @@ -5687,7 +5747,7 @@ dependencies = [ name = "nym-exit-policy" version = "0.1.0" dependencies = [ - "reqwest 0.12.4", + "reqwest 0.12.12", "serde", "serde_json", "thiserror 2.0.11", @@ -5712,7 +5772,7 @@ name = "nym-explorer-client" version = "0.1.0" dependencies = [ "nym-explorer-api-requests", - "reqwest 0.12.4", + "reqwest 0.12.12", "serde", "thiserror 2.0.11", "tokio", @@ -5917,7 +5977,7 @@ dependencies = [ "http 1.2.0", "nym-bin-common", "once_cell", - "reqwest 0.12.4", + "reqwest 0.12.12", "serde", "serde_json", "thiserror 2.0.11", @@ -6028,7 +6088,7 @@ dependencies = [ "nym-wireguard", "nym-wireguard-types", "rand 0.8.5", - "reqwest 0.12.4", + "reqwest 0.12.12", "serde", "serde_json", "tap", @@ -6178,7 +6238,7 @@ dependencies = [ "petgraph", "rand 0.8.5", "rand_chacha 0.3.1", - "reqwest 0.12.4", + "reqwest 0.12.12", "serde", "serde_json", "tokio", @@ -6225,7 +6285,7 @@ dependencies = [ "publicsuffix", "rand 0.8.5", "regex", - "reqwest 0.12.4", + "reqwest 0.12.12", "serde", "serde_json", "sqlx", @@ -6389,7 +6449,7 @@ dependencies = [ "nym-task", "nym-validator-client", "regex", - "reqwest 0.12.4", + "reqwest 0.12.12", "serde", "serde_json", "serde_json_path", @@ -6418,7 +6478,7 @@ dependencies = [ "chrono", "nym-crypto", "nym-http-api-client", - "reqwest 0.12.4", + "reqwest 0.12.12", "serde", "serde_json", "tracing", @@ -6524,6 +6584,19 @@ dependencies = [ "pem", ] +[[package]] +name = "nym-pool-contract-common" +version = "0.1.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-controllers", + "schemars", + "serde", + "thiserror 2.0.11", + "time", +] + [[package]] name = "nym-sdk" version = "0.1.0" @@ -6564,7 +6637,7 @@ dependencies = [ "parking_lot", "pretty_env_logger", "rand 0.8.5", - "reqwest 0.12.4", + "reqwest 0.12.12", "serde", "tap", "thiserror 2.0.11", @@ -6670,7 +6743,7 @@ dependencies = [ "nym-validator-client", "pin-project", "rand 0.8.5", - "reqwest 0.12.4", + "reqwest 0.12.12", "schemars", "serde", "tap", @@ -6955,7 +7028,7 @@ dependencies = [ "nym-sphinx-routing", "nym-sphinx-types", "rand 0.8.5", - "reqwest 0.12.4", + "reqwest 0.12.12", "serde", "serde_json", "thiserror 2.0.11", @@ -6982,7 +7055,7 @@ name = "nym-types" version = "1.0.0" dependencies = [ "base64 0.22.1", - "cosmrs 0.21.1", + "cosmrs", "cosmwasm-std", "eyre", "hmac", @@ -6993,7 +7066,7 @@ dependencies = [ "nym-mixnet-contract-common", "nym-validator-client", "nym-vesting-contract-common", - "reqwest 0.12.4", + "reqwest 0.12.12", "schemars", "serde", "serde_json", @@ -7016,7 +7089,7 @@ dependencies = [ "bip32", "bip39", "colored", - "cosmrs 0.21.1", + "cosmrs", "cosmwasm-std", "cw-controllers", "cw-utils", @@ -7028,7 +7101,6 @@ dependencies = [ "futures", "itertools 0.13.0", "nym-api-requests", - "nym-coconut-bandwidth-contract-common", "nym-coconut-dkg-common", "nym-compact-ecash", "nym-config", @@ -7042,7 +7114,7 @@ dependencies = [ "nym-serde-helpers", "nym-vesting-contract-common", "prost 0.13.5", - "reqwest 0.12.4", + "reqwest 0.12.12", "serde", "serde_json", "sha2 0.9.9", @@ -7069,7 +7141,6 @@ dependencies = [ "humantime 2.1.0", "humantime-serde", "nym-bin-common", - "nym-coconut-bandwidth-contract-common", "nym-coconut-dkg-common", "nym-compact-ecash", "nym-config", @@ -7161,7 +7232,7 @@ dependencies = [ name = "nym-wallet-types" version = "1.0.0" dependencies = [ - "cosmrs 0.15.0", + "cosmrs", "cosmwasm-std", "hex-literal", "nym-config", @@ -7237,7 +7308,7 @@ dependencies = [ "nym-bin-common", "nym-config", "nym-task", - "reqwest 0.12.4", + "reqwest 0.12.12", "serde", "serde_json", "sha2 0.10.8", @@ -7265,7 +7336,7 @@ dependencies = [ "nym-task", "nym-validator-client", "nyxd-scraper", - "reqwest 0.12.4", + "reqwest 0.12.12", "rocket", "schemars", "serde", @@ -7289,14 +7360,14 @@ version = "0.1.0" dependencies = [ "async-trait", "const_format", - "cosmrs 0.21.1", + "cosmrs", "eyre", "futures", "humantime 2.1.0", "serde", "sha2 0.10.8", "sqlx", - "tendermint 0.40.1", + "tendermint", "tendermint-rpc", "thiserror 2.0.11", "time", @@ -7481,6 +7552,18 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[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.8", +] + [[package]] name = "pairing" version = "0.23.0" @@ -7913,6 +7996,15 @@ dependencies = [ "log", ] +[[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-macro-error-attr2" version = "2.0.0" @@ -7982,16 +8074,6 @@ dependencies = [ "prost-derive 0.11.9", ] -[[package]] -name = "prost" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" -dependencies = [ - "bytes", - "prost-derive 0.12.6", -] - [[package]] name = "prost" version = "0.13.5" @@ -8015,19 +8097,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "prost-derive" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" -dependencies = [ - "anyhow", - "itertools 0.12.1", - "proc-macro2", - "quote", - "syn 2.0.98", -] - [[package]] name = "prost-derive" version = "0.13.5" @@ -8050,15 +8119,6 @@ dependencies = [ "prost 0.11.9", ] -[[package]] -name = "prost-types" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" -dependencies = [ - "prost 0.12.6", -] - [[package]] name = "protobuf" version = "2.28.0" @@ -8067,9 +8127,9 @@ checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" [[package]] name = "psl" -version = "2.1.86" +version = "2.1.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e02ed846877ce4044391085ca68b470b0d379cd18a9be0666161764d35448" +checksum = "49531bbe7afe7b7253f4b18bfee08909be85491f866ab19ae5df6c17d7909363" dependencies = [ "psl-types", ] @@ -8102,6 +8162,58 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" +[[package]] +name = "quinn" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.23.23", + "socket2", + "thiserror 2.0.11", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" +dependencies = [ + "bytes", + "getrandom 0.2.15", + "rand 0.8.5", + "ring", + "rustc-hash", + "rustls 0.23.23", + "rustls-pki-types", + "slab", + "thiserror 2.0.11", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e46f3055866785f6b92bc6164b76be02ca8f2eb4b002c0354b28cf4c119e5944" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "quote" version = "1.0.38" @@ -8136,7 +8248,7 @@ checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.1", - "zerocopy 0.8.20", + "zerocopy 0.8.18", ] [[package]] @@ -8159,12 +8271,6 @@ dependencies = [ "rand_core 0.9.1", ] -[[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" @@ -8181,7 +8287,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a88e0da7a2c97baa202165137c158d0a2e824ac465d13d81046727b34cb247d3" dependencies = [ "getrandom 0.3.1", - "zerocopy 0.8.20", + "zerocopy 0.8.18", ] [[package]] @@ -8209,7 +8315,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf2890aaef0aa82719a50e808de264f9484b74b442e1a3a0e5ee38243ac40bdb" dependencies = [ - "rand_core 0.5.1", + "rand_core 0.6.4", ] [[package]] @@ -8354,14 +8460,14 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg 0.50.0", + "winreg", ] [[package]] name = "reqwest" -version = "0.12.4" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ "async-compression", "base64 0.22.1", @@ -8372,7 +8478,7 @@ dependencies = [ "http-body 1.0.1", "http-body-util", "hyper 1.6.0", - "hyper-rustls 0.26.0", + "hyper-rustls 0.27.5", "hyper-util", "ipnet", "js-sys", @@ -8381,17 +8487,19 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.22.4", + "quinn", + "rustls 0.23.23", "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.2", "tokio", - "tokio-rustls 0.25.0", + "tokio-rustls 0.26.1", "tokio-socks", "tokio-util", + "tower 0.5.2", "tower-service", "url", "wasm-bindgen", @@ -8399,7 +8507,7 @@ dependencies = [ "wasm-streams", "web-sys", "webpki-roots 0.26.8", - "winreg 0.52.0", + "windows-registry", ] [[package]] @@ -8494,6 +8602,28 @@ dependencies = [ "digest 0.10.7", ] +[[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 = "rocket" version = "0.5.0" @@ -8769,6 +8899,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls" +version = "0.23.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki 0.102.8", + "subtle 2.6.1", + "zeroize", +] + [[package]] name = "rustls-native-certs" version = "0.6.3" @@ -8817,6 +8961,9 @@ name = "rustls-pki-types" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +dependencies = [ + "web-time", +] [[package]] name = "rustls-webpki" @@ -9030,18 +9177,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.218" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde-json-wasm" -version = "0.5.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15bee9b04dd165c3f4e142628982ddde884c2022a89e8ddf99c4829bf2c3a58" +checksum = "f05da0d153dd4595bdffd5099dc0e9ce425b205ee648eb93437ff7302af8c9a5" dependencies = [ "serde", ] @@ -9079,9 +9226,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.218" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", @@ -9112,9 +9259,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.139" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" dependencies = [ "itoa", "memchr", @@ -9468,7 +9615,7 @@ dependencies = [ "byteorder", "chacha", "ctr", - "curve25519-dalek 4.1.3", + "curve25519-dalek", "digest 0.10.7", "hkdf", "hmac", @@ -9528,7 +9675,7 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6" dependencies = [ - "ahash 0.8.11", + "ahash", "atoi", "byteorder", "bytes", @@ -9906,6 +10053,9 @@ name = "sync_wrapper" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] [[package]] name = "synstructure" @@ -9978,9 +10128,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.17.1" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" +checksum = "a40f762a77d2afa88c2d919489e390a12bdd261ed568e60cfa7e48d4e20f0d33" dependencies = [ "cfg-if", "fastrand 2.3.0", @@ -9990,37 +10140,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "tendermint" -version = "0.34.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15ab8f0a25d0d2ad49ac615da054d6a76aa6603ff95f7d18bafdd34450a1a04b" -dependencies = [ - "bytes", - "digest 0.10.7", - "ed25519", - "ed25519-consensus", - "flex-error", - "futures", - "k256", - "num-traits", - "once_cell", - "prost 0.12.6", - "prost-types 0.12.6", - "ripemd", - "serde", - "serde_bytes", - "serde_json", - "serde_repr", - "sha2 0.10.8", - "signature", - "subtle 2.6.1", - "subtle-encoding", - "tendermint-proto 0.34.1", - "time", - "zeroize", -] - [[package]] name = "tendermint" version = "0.40.1" @@ -10046,7 +10165,7 @@ dependencies = [ "signature", "subtle 2.6.1", "subtle-encoding", - "tendermint-proto 0.40.1", + "tendermint-proto", "time", "zeroize", ] @@ -10060,29 +10179,11 @@ dependencies = [ "flex-error", "serde", "serde_json", - "tendermint 0.40.1", + "tendermint", "toml 0.8.20", "url", ] -[[package]] -name = "tendermint-proto" -version = "0.34.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b797dd3d2beaaee91d2f065e7bdf239dc8d80bba4a183a288bc1279dd5a69a1e" -dependencies = [ - "bytes", - "flex-error", - "num-derive", - "num-traits", - "prost 0.12.6", - "prost-types 0.12.6", - "serde", - "serde_bytes", - "subtle-encoding", - "time", -] - [[package]] name = "tendermint-proto" version = "0.40.1" @@ -10120,9 +10221,9 @@ dependencies = [ "serde_json", "subtle 2.6.1", "subtle-encoding", - "tendermint 0.40.1", + "tendermint", "tendermint-config", - "tendermint-proto 0.40.1", + "tendermint-proto", "thiserror 1.0.69", "time", "tokio", @@ -10428,6 +10529,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +dependencies = [ + "rustls 0.23.23", + "tokio", +] + [[package]] name = "tokio-socks" version = "0.5.2" @@ -10906,9 +11017,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.18.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ubyte" @@ -10959,9 +11070,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.17" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "unicode-normalization" @@ -11262,9 +11373,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.13.2" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c1f41ffb7cf259f1ecc2876861a17e7142e63ead296f671f81f6ae85903e0d6" +checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0" dependencies = [ "getrandom 0.3.1", "serde", @@ -11744,6 +11855,17 @@ dependencies = [ "syn 2.0.98", ] +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result 0.2.0", + "windows-strings", + "windows-targets 0.52.6", +] + [[package]] name = "windows-result" version = "0.1.2" @@ -11988,9 +12110,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.3" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" +checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" dependencies = [ "memchr", ] @@ -12005,16 +12127,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "winreg" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "wit-bindgen-rt" version = "0.33.0" @@ -12051,7 +12163,7 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ - "curve25519-dalek 4.1.3", + "curve25519-dalek", "rand_core 0.6.4", "serde", "zeroize", @@ -12113,11 +12225,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.20" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde3bb8c68a8f3f1ed4ac9221aad6b10cece3e60a8e2ea54a6a2dec806d0084c" +checksum = "79386d31a42a4996e3336b0919ddb90f81112af416270cff95b5f5af22b839c2" dependencies = [ - "zerocopy-derive 0.8.20", + "zerocopy-derive 0.8.18", ] [[package]] @@ -12133,9 +12245,9 @@ dependencies = [ [[package]] name = "zerocopy-derive" -version = "0.8.20" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eea57037071898bf96a6da35fd626f4f27e9cee3ead2a6c703cf09d472b2e700" +checksum = "76331675d372f91bf8d17e13afbd5fe639200b73d01f0fc748bb059f9cca2db7" dependencies = [ "proc-macro2", "quote", @@ -12165,9 +12277,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.6.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] @@ -12238,7 +12350,7 @@ dependencies = [ "nym-crypto", "nym-http-api-client", "rand 0.8.5", - "reqwest 0.12.4", + "reqwest 0.12.12", "serde", "thiserror 2.0.11", "tokio", @@ -12266,27 +12378,27 @@ dependencies = [ [[package]] name = "zstd" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "7.2.1" +version = "7.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +checksum = "f3051792fbdc2e1e143244dc28c60f73d8470e93f3f9cbd0ead44da5ed802722" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.13+zstd.1.5.6" +version = "2.0.14+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +checksum = "8fb060d4926e4ac3a3ad15d864e99ceb5f343c6b34f5bd6d81ae6ed417311be5" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index a750d7dbc7f..0216b14609b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,13 +32,12 @@ members = [ "common/client-libs/validator-client", "common/commands", "common/config", - "common/cosmwasm-smart-contracts/coconut-bandwidth-contract", "common/cosmwasm-smart-contracts/coconut-dkg", - "common/cosmwasm-smart-contracts/contracts-common", + "common/cosmwasm-smart-contracts/contracts-common", "common/cosmwasm-smart-contracts/easy_addr", "common/cosmwasm-smart-contracts/ecash-contract", "common/cosmwasm-smart-contracts/group-contract", "common/cosmwasm-smart-contracts/mixnet-contract", - "common/cosmwasm-smart-contracts/multisig-contract", + "common/cosmwasm-smart-contracts/multisig-contract", "common/cosmwasm-smart-contracts/nym-pool-contract", "common/cosmwasm-smart-contracts/vesting-contract", "common/country-group", "common/credential-storage", @@ -370,25 +369,24 @@ prometheus = { version = "0.13.0" } # unfortunately until https://github.com/zkcrypto/bls12_381/issues/10 is resolved, we have to rely on the fork # as we need to be able to serialize Gt so that we could create the lookup table for baby-step-giant-step algorithm # plus to make our live easier we need serde support from https://github.com/zkcrypto/bls12_381/pull/125 -bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", default-features = false, branch = "temp/experimental-serdect" } +bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", default-features = false, branch = "temp/experimental-serdect-updated" } group = { version = "0.13.0", default-features = false } ff = { version = "0.13.0", default-features = false } subtle = "2.5.0" # cosmwasm-related -cosmwasm-schema = "=1.4.3" -cosmwasm-std = "=1.4.3" -# use 0.5.0 as that's the version used by cosmwasm-std 1.4.3 +cosmwasm-schema = "=2.2.1" +cosmwasm-std = "=2.2.1" +# use 1.0.1 as that's the version used by cosmwasm-std 2.2.1 # (and ideally we don't want to pull the same dependency twice) -serde-json-wasm = "=0.5.0" -cosmwasm-storage = "=1.4.3" +serde-json-wasm = "=1.0.1" # same version as used by cosmwasm -cw-utils = "=1.0.1" -cw-storage-plus = "=1.2.0" -cw2 = { version = "=1.1.2" } -cw3 = { version = "=1.1.2" } -cw4 = { version = "=1.1.2" } -cw-controllers = { version = "=1.1.0" } +cw-utils = "=2.0.0" +cw-storage-plus = "=2.0.0" +cw2 = { version = "=2.0.0" } +cw3 = { version = "=2.0.0" } +cw4 = { version = "=2.0.0" } +cw-controllers = { version = "=2.0.0" } # cosmrs-related bip32 = { version = "0.5.3", default-features = false } diff --git a/common/client-libs/validator-client/Cargo.toml b/common/client-libs/validator-client/Cargo.toml index 5cd86e2dba4..dad0efdb54c 100644 --- a/common/client-libs/validator-client/Cargo.toml +++ b/common/client-libs/validator-client/Cargo.toml @@ -16,7 +16,6 @@ nym-coconut-dkg-common = { path = "../../cosmwasm-smart-contracts/coconut-dkg" } nym-contracts-common = { path = "../../cosmwasm-smart-contracts/contracts-common" } nym-mixnet-contract-common = { path = "../../cosmwasm-smart-contracts/mixnet-contract" } nym-vesting-contract-common = { path = "../../cosmwasm-smart-contracts/vesting-contract" } -nym-coconut-bandwidth-contract-common = { path = "../../cosmwasm-smart-contracts/coconut-bandwidth-contract" } nym-ecash-contract-common = { path = "../../cosmwasm-smart-contracts/ecash-contract" } nym-multisig-contract-common = { path = "../../cosmwasm-smart-contracts/multisig-contract" } nym-group-contract-common = { path = "../../cosmwasm-smart-contracts/group-contract" } diff --git a/common/client-libs/validator-client/src/nyxd/coin.rs b/common/client-libs/validator-client/src/nyxd/coin.rs index 8050273a51d..efd53d12f5d 100644 --- a/common/client-libs/validator-client/src/nyxd/coin.rs +++ b/common/client-libs/validator-client/src/nyxd/coin.rs @@ -48,7 +48,7 @@ impl Div for &Coin { panic!("attempted to divide by zero!") }; - let implicit_gas_limit = gas_price_inv * Uint128::new(self.amount); + let implicit_gas_limit = Uint128::new(self.amount).mul_floor(gas_price_inv); if implicit_gas_limit.u128() >= u64::MAX as u128 { u64::MAX } else { @@ -169,13 +169,7 @@ impl CoinConverter for CosmosCoin { type Target = CosmWasmCoin; fn convert_coin(&self) -> Self::Target { - CosmWasmCoin::new( - self.amount - .to_string() - .parse() - .expect("cosmos coin had an invalid amount assigned"), - self.denom.to_string(), - ) + CosmWasmCoin::new(self.amount, self.denom.to_string()) } } diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/multisig_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/multisig_signing_client.rs index ae28cf0ef37..715a38d4579 100644 --- a/common/client-libs/validator-client/src/nyxd/contract_traits/multisig_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/multisig_signing_client.rs @@ -7,10 +7,10 @@ use crate::nyxd::error::NyxdError; use crate::nyxd::{Coin, Fee, SigningCosmWasmClient}; use crate::signing::signer::OfflineSigner; use async_trait::async_trait; -use cosmwasm_std::{to_binary, CosmosMsg, WasmMsg}; +use cosmwasm_std::{CosmosMsg, Empty}; use cw3::Vote; use cw4::{MemberChangedHookMsg, MemberDiff}; -use nym_coconut_bandwidth_contract_common::msg::ExecuteMsg as CoconutBandwidthExecuteMsg; +use cw_utils::Expiration; use nym_multisig_contract_common::msg::ExecuteMsg as MultisigExecuteMsg; #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] @@ -24,35 +24,23 @@ pub trait MultisigSigningClient: NymContractsProvider { funds: Vec, ) -> Result; - async fn propose_release_funds( + async fn propose( &self, title: String, - blinded_serial_number: String, - voucher_value: Coin, + description: String, + msgs: Vec>, + latest: Option, fee: Option, ) -> Result { - let ecash_contract_address = self - .ecash_contract_address() - .ok_or_else(|| NyxdError::unavailable_contract_address("coconut bandwidth contract"))?; - - let release_funds_req = CoconutBandwidthExecuteMsg::ReleaseFunds { - funds: voucher_value.into(), - }; - let release_funds_msg = CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: ecash_contract_address.to_string(), - msg: to_binary(&release_funds_req)?, - funds: vec![], - }); - let req = MultisigExecuteMsg::Propose { - title, - description: blinded_serial_number, - msgs: vec![release_funds_msg], - latest: None, - }; self.execute_multisig_contract( fee, - req, - "Multisig::Propose::Execute::ReleaseFunds".to_string(), + MultisigExecuteMsg::Propose { + title, + description, + msgs, + latest, + }, + "Multisig::Propose".to_string(), vec![], ) .await @@ -161,7 +149,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue}; + use crate::nyxd::contract_traits::tests::IgnoreValue; // it's enough that this compiles and clippy is happy about it #[allow(dead_code)] @@ -171,9 +159,12 @@ mod tests { ) { match msg { MultisigExecuteMsg::Propose { - title, description, .. + title, + description, + msgs, + latest, } => client - .propose_release_funds(title, description, mock_coin(), None) + .propose(title, description, msgs, latest, None) .ignore(), MultisigExecuteMsg::Vote { proposal_id, vote } => { client.vote(proposal_id, vote, None).ignore() diff --git a/common/client-libs/validator-client/src/nyxd/fee/gas_price.rs b/common/client-libs/validator-client/src/nyxd/fee/gas_price.rs index 3679cc7500b..ab70ad5618e 100644 --- a/common/client-libs/validator-client/src/nyxd/fee/gas_price.rs +++ b/common/client-libs/validator-client/src/nyxd/fee/gas_price.rs @@ -27,7 +27,7 @@ impl Mul for &GasPrice { fn mul(self, gas_limit: Gas) -> Self::Output { let limit_uint128 = Uint128::from(gas_limit); - let mut amount = self.amount * limit_uint128; + let mut amount = limit_uint128.mul_floor(self.amount); let gas_price_numerator = self.amount.numerator(); let gas_price_denominator = self.amount.denominator(); @@ -35,7 +35,7 @@ impl Mul for &GasPrice { // gas price is a fraction of the smallest fee token unit, so we must ensure that // for any multiplication, we have rounded up // - // I don't really like the this solution as it has a theoretical chance of + // I don't really like this solution as it has a theoretical chance of // overflowing (internally cosmwasm uses U256 to avoid that) // however, realistically that is impossible to happen as the resultant value // would have to be way higher than our token limit of 10^15 (1 billion of tokens * 1 million for denomination) diff --git a/common/commands/src/validator/mixnet/delegators/delegate_to_multiple_mixnodes.rs b/common/commands/src/validator/mixnet/delegators/delegate_to_multiple_mixnodes.rs index 77d59d6bb3b..96bf95be04d 100644 --- a/common/commands/src/validator/mixnet/delegators/delegate_to_multiple_mixnodes.rs +++ b/common/commands/src/validator/mixnet/delegators/delegate_to_multiple_mixnodes.rs @@ -155,7 +155,7 @@ async fn fetch_delegation_data( match event.event.kind { // If a pending undelegate tx is found, remove it from delegation map PendingEpochEventKind::Undelegate { owner, node_id, .. } => { - if owner == address.as_ref() + if owner.as_str() == address.as_ref() && existing_delegation_map.contains_key(&node_id.to_string()) { existing_delegation_map.remove(&node_id.to_string()); @@ -169,7 +169,7 @@ async fn fetch_delegation_data( amount, .. } => { - if owner == address.as_ref() { + if owner.as_str() == address.as_ref() { let mut amount = Coin::from(amount); if let Some(pending_record) = pending_delegation_map.get(&node_id.to_string()) { amount.amount += pending_record.amount; diff --git a/common/commands/src/validator/vesting/create_vesting_schedule.rs b/common/commands/src/validator/vesting/create_vesting_schedule.rs index 151350bf2cf..35ee669dabe 100644 --- a/common/commands/src/validator/vesting/create_vesting_schedule.rs +++ b/common/commands/src/validator/vesting/create_vesting_schedule.rs @@ -54,7 +54,7 @@ pub async fn create(args: Args, client: SigningClient, network_details: &NymNetw let denom = network_details.chain_details.mix_denom.base.to_string(); - let coin = Coin::new(args.amount.into(), &denom); + let coin = Coin::new(args.amount, &denom); let res = client .create_periodic_vesting_account( diff --git a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/Cargo.toml b/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/Cargo.toml deleted file mode 100644 index c60406bc57f..00000000000 --- a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "nym-coconut-bandwidth-contract-common" -version = "0.1.0" -edition = "2021" -license.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -cosmwasm-std = { workspace = true } -cosmwasm-schema = { workspace = true } -cw2 = { workspace = true, optional = true } -nym-multisig-contract-common = { path = "../multisig-contract" } - -[features] -schema = ["cw2"] diff --git a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/deposit.rs b/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/deposit.rs deleted file mode 100644 index c0e3a4acd3a..00000000000 --- a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/deposit.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use cosmwasm_schema::cw_serde; - -#[cw_serde] -pub struct DepositData { - deposit_info: String, - identity_key: String, - encryption_key: String, -} - -impl DepositData { - pub fn new(deposit_info: String, identity_key: String, encryption_key: String) -> Self { - DepositData { - deposit_info, - identity_key, - encryption_key, - } - } - - pub fn deposit_info(&self) -> &str { - &self.deposit_info - } - - pub fn identity_key(&self) -> &str { - &self.identity_key - } - - pub fn encryption_key(&self) -> &str { - &self.encryption_key - } -} diff --git a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/event_attributes.rs b/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/event_attributes.rs deleted file mode 100644 index 80f5daf68ab..00000000000 --- a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/event_attributes.rs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -pub const BANDWIDTH_PROPOSAL_ID: &str = "proposal_id"; diff --git a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/events.rs b/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/events.rs deleted file mode 100644 index 5204f6a97e0..00000000000 --- a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/events.rs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2021 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -// event types -pub const DEPOSITED_FUNDS_EVENT_TYPE: &str = "deposited-funds"; - -// attributes that are used in multiple places -pub const DEPOSIT_VALUE: &str = "deposit-value"; -pub const DEPOSIT_INFO: &str = "deposit-info"; -pub const DEPOSIT_IDENTITY_KEY: &str = "deposit-identity-key"; -pub const DEPOSIT_ENCRYPTION_KEY: &str = "deposit-encryption-key"; diff --git a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/lib.rs b/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/lib.rs deleted file mode 100644 index 56cb54e81d3..00000000000 --- a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod deposit; -pub mod event_attributes; -pub mod events; -pub mod msg; -pub mod spend_credential; diff --git a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/msg.rs b/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/msg.rs deleted file mode 100644 index 8387ac4ec55..00000000000 --- a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/msg.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2021 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::{deposit::DepositData, spend_credential::SpendCredentialData}; -use cosmwasm_schema::cw_serde; -use cosmwasm_std::Coin; - -#[cfg(feature = "schema")] -use crate::spend_credential::{PagedSpendCredentialResponse, SpendCredentialResponse}; -#[cfg(feature = "schema")] -use cosmwasm_schema::QueryResponses; - -#[cw_serde] -pub struct InstantiateMsg { - pub multisig_addr: String, - pub pool_addr: String, - pub mix_denom: String, -} - -#[cw_serde] -pub enum ExecuteMsg { - DepositFunds { data: DepositData }, - SpendCredential { data: SpendCredentialData }, - ReleaseFunds { funds: Coin }, -} - -#[cw_serde] -#[cfg_attr(feature = "schema", derive(QueryResponses))] -pub enum QueryMsg { - #[cfg_attr(feature = "schema", returns(SpendCredentialResponse))] - GetSpentCredential { blinded_serial_number: String }, - - #[cfg_attr(feature = "schema", returns(PagedSpendCredentialResponse))] - GetAllSpentCredentials { - limit: Option, - start_after: Option, - }, -} - -#[cw_serde] -pub struct MigrateMsg {} diff --git a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/spend_credential.rs b/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/spend_credential.rs deleted file mode 100644 index 3ab4e72150a..00000000000 --- a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/spend_credential.rs +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use cosmwasm_schema::cw_serde; -use cosmwasm_std::{from_binary, to_binary, Addr, Coin, CosmosMsg, StdResult, WasmMsg}; -use nym_multisig_contract_common::msg::ExecuteMsg as MultisigExecuteMsg; - -use crate::msg::ExecuteMsg; - -#[cw_serde] -pub struct SpendCredentialData { - funds: Coin, - blinded_serial_number: String, - gateway_cosmos_address: String, -} - -impl SpendCredentialData { - pub fn new(funds: Coin, blinded_serial_number: String, gateway_cosmos_address: String) -> Self { - SpendCredentialData { - funds, - blinded_serial_number, - gateway_cosmos_address, - } - } - - pub fn funds(&self) -> &Coin { - &self.funds - } - - pub fn blinded_serial_number(&self) -> &str { - &self.blinded_serial_number - } - - pub fn gateway_cosmos_address(&self) -> &str { - &self.gateway_cosmos_address - } -} - -#[cw_serde] -#[derive(Copy)] -pub enum SpendCredentialStatus { - #[serde(alias = "InProgress")] - InProgress, - #[serde(alias = "Spent")] - Spent, -} - -#[cw_serde] -pub struct SpendCredential { - funds: Coin, - blinded_serial_number: String, - gateway_cosmos_address: Addr, - status: SpendCredentialStatus, -} - -impl SpendCredential { - pub fn new(funds: Coin, blinded_serial_number: String, gateway_cosmos_address: Addr) -> Self { - SpendCredential { - funds, - blinded_serial_number, - gateway_cosmos_address, - status: SpendCredentialStatus::InProgress, - } - } - - pub fn blinded_serial_number(&self) -> &str { - &self.blinded_serial_number - } - - pub fn status(&self) -> SpendCredentialStatus { - self.status - } - - pub fn mark_as_spent(&mut self) { - self.status = SpendCredentialStatus::Spent; - } -} - -#[cw_serde] -pub struct PagedSpendCredentialResponse { - pub spend_credentials: Vec, - pub per_page: usize, - - /// Field indicating paging information for the following queries if the caller wishes to get further entries. - pub start_next_after: Option, -} - -impl PagedSpendCredentialResponse { - pub fn new( - spend_credentials: Vec, - per_page: usize, - start_next_after: Option, - ) -> Self { - PagedSpendCredentialResponse { - spend_credentials, - per_page, - start_next_after, - } - } -} - -#[cw_serde] -pub struct SpendCredentialResponse { - pub spend_credential: Option, -} - -impl SpendCredentialResponse { - pub fn new(spend_credential: Option) -> Self { - SpendCredentialResponse { spend_credential } - } -} - -pub fn to_cosmos_msg( - funds: Coin, - blinded_serial_number: String, - coconut_bandwidth_addr: String, - multisig_addr: String, -) -> StdResult { - let release_funds_req = ExecuteMsg::ReleaseFunds { funds }; - let release_funds_msg = CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: coconut_bandwidth_addr, - msg: to_binary(&release_funds_req)?, - funds: vec![], - }); - let req = MultisigExecuteMsg::Propose { - title: String::from("Release funds, as ordered by Coconut Bandwidth Contract"), - description: blinded_serial_number, - msgs: vec![release_funds_msg], - latest: None, - }; - let msg = CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: multisig_addr, - msg: to_binary(&req)?, - funds: vec![], - }); - - Ok(msg) -} - -pub fn funds_from_cosmos_msgs(msgs: Vec) -> Option { - if let Some(CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: _, - msg, - funds: _, - })) = msgs.first() - { - if let Ok(ExecuteMsg::ReleaseFunds { funds }) = from_binary::(msg) { - return Some(funds); - } - } - None -} diff --git a/common/cosmwasm-smart-contracts/coconut-dkg/src/verification_key.rs b/common/cosmwasm-smart-contracts/coconut-dkg/src/verification_key.rs index e3a66a78a46..2f83d176cc3 100644 --- a/common/cosmwasm-smart-contracts/coconut-dkg/src/verification_key.rs +++ b/common/cosmwasm-smart-contracts/coconut-dkg/src/verification_key.rs @@ -4,7 +4,7 @@ use crate::msg::ExecuteMsg; use crate::types::{EpochId, NodeIndex}; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{from_binary, to_binary, Addr, CosmosMsg, StdResult, Timestamp, WasmMsg}; +use cosmwasm_std::{from_json, to_json_binary, Addr, CosmosMsg, StdResult, Timestamp, WasmMsg}; use cw_utils::Expiration; use nym_multisig_contract_common::msg::ExecuteMsg as MultisigExecuteMsg; @@ -49,7 +49,7 @@ pub fn to_cosmos_msg( }; let verify_vk_share_msg = CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: coconut_dkg_addr, - msg: to_binary(&verify_vk_share_req)?, + msg: to_json_binary(&verify_vk_share_req)?, funds: vec![], }); let req = MultisigExecuteMsg::Propose { @@ -60,7 +60,7 @@ pub fn to_cosmos_msg( }; let msg = CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: multisig_addr, - msg: to_binary(&req)?, + msg: to_json_binary(&req)?, funds: vec![], }); @@ -82,7 +82,7 @@ pub fn owner_from_cosmos_msgs(msgs: &[CosmosMsg]) -> Option { })) = msgs.first() { if let Ok(ExecuteMsg::VerifyVerificationKeyShare { owner, .. }) = - from_binary::(msg) + from_json::(msg) { return Some(owner); } diff --git a/common/cosmwasm-smart-contracts/contracts-common/src/signing/mod.rs b/common/cosmwasm-smart-contracts/contracts-common/src/signing/mod.rs index 228e188b844..3d0b28a247a 100644 --- a/common/cosmwasm-smart-contracts/contracts-common/src/signing/mod.rs +++ b/common/cosmwasm-smart-contracts/contracts-common/src/signing/mod.rs @@ -1,7 +1,7 @@ // Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use cosmwasm_std::{from_slice, to_vec, Addr, Coin, MessageInfo, StdResult}; +use cosmwasm_std::{from_json, to_json_vec, Addr, Coin, MessageInfo, StdResult}; use schemars::JsonSchema; use serde::de::DeserializeOwned; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; @@ -164,7 +164,7 @@ where where T: Serialize, { - to_vec(self) + to_json_vec(self) } pub fn to_sha256_plaintext_digest(&self) -> StdResult> @@ -195,7 +195,7 @@ where where T: DeserializeOwned, { - from_slice(bytes) + from_json(bytes) } pub fn try_from_string(raw: &str) -> StdResult> diff --git a/common/cosmwasm-smart-contracts/contracts-common/src/types.rs b/common/cosmwasm-smart-contracts/contracts-common/src/types.rs index 456fd45a767..e0f5845fa08 100644 --- a/common/cosmwasm-smart-contracts/contracts-common/src/types.rs +++ b/common/cosmwasm-smart-contracts/contracts-common/src/types.rs @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 use cosmwasm_schema::cw_serde; -use cosmwasm_std::Decimal; use cosmwasm_std::OverflowError; use cosmwasm_std::Uint128; +use cosmwasm_std::{Decimal, Fraction}; use serde::de::Error; use serde::{Deserialize, Deserializer}; use std::fmt::{self, Display, Formatter}; @@ -17,7 +17,7 @@ pub type IdentityKey = String; pub type IdentityKeyRef<'a> = &'a str; pub fn truncate_decimal(amount: Decimal) -> Uint128 { - amount * Uint128::new(1) + Uint128::new(1).mul_floor(amount) } #[derive(Error, Debug)] @@ -113,11 +113,17 @@ impl Mul for Decimal { } } -impl Mul for Percent { - type Output = Uint128; +impl Fraction for Percent { + fn numerator(&self) -> Uint128 { + self.0.numerator() + } - fn mul(self, rhs: Uint128) -> Self::Output { - self.0 * rhs + fn denominator(&self) -> Uint128 { + self.0.denominator() + } + + fn inv(&self) -> Option { + Percent::new(self.0.inv()?).ok() } } diff --git a/common/cosmwasm-smart-contracts/easy_addr/Cargo.toml b/common/cosmwasm-smart-contracts/easy_addr/Cargo.toml new file mode 100644 index 00000000000..992378fbe06 --- /dev/null +++ b/common/cosmwasm-smart-contracts/easy_addr/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "easy-addr" +version = "0.1.0" +edition = "2021" +publish = false +license.workspace = true + +[lib] +proc-macro = true + +[dependencies] +cosmwasm-std = { workspace = true } +quote = { workspace = true } +syn = { workspace = true, features = ["full", "printing", "extra-traits"] } \ No newline at end of file diff --git a/common/cosmwasm-smart-contracts/easy_addr/src/lib.rs b/common/cosmwasm-smart-contracts/easy_addr/src/lib.rs new file mode 100644 index 00000000000..87d634a61ec --- /dev/null +++ b/common/cosmwasm-smart-contracts/easy_addr/src/lib.rs @@ -0,0 +1,12 @@ +use cosmwasm_std::testing::MockApi; + +use proc_macro::TokenStream; +use quote::quote; +use syn::parse_macro_input; + +#[proc_macro] +pub fn addr(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as syn::LitStr).value(); + let addr = MockApi::default().addr_make(input.as_str()).to_string(); + TokenStream::from(quote! {#addr}) +} diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs index e21ad52d696..deeb416887b 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs @@ -241,10 +241,10 @@ mod tests { #[test] fn gateway_bond_partial_ord() { - let _150foos = Coin::new(150, "foo"); - let _140foos = Coin::new(140, "foo"); - let _50foos = Coin::new(50, "foo"); - let _0foos = Coin::new(0, "foo"); + let _150foos = Coin::new(150u32, "foo"); + let _140foos = Coin::new(140u32, "foo"); + let _50foos = Coin::new(50u32, "foo"); + let _0foos = Coin::new(0u32, "foo"); let gate1 = GatewayBond { pledge_amount: _150foos.clone(), diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/helpers.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/helpers.rs index 092f35e80ca..125359d9f5e 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/helpers.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/helpers.rs @@ -34,8 +34,10 @@ where { fn into_base_decimal(self) -> StdResult { let atomics = self.into(); - Decimal::from_atomics(atomics, 0).map_err(|_| StdError::GenericErr { - msg: format!("Decimal range exceeded for {atomics} with 0 decimal places."), + Decimal::from_atomics(atomics, 0).map_err(|_| { + StdError::generic_err(format!( + "Decimal range exceeded for {atomics} with 0 decimal places." + )) }) } } diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/nym_node.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/nym_node.rs index 85e28c7d757..cecbd4392ff 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/nym_node.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/nym_node.rs @@ -77,6 +77,8 @@ impl<'a> PrimaryKey<'a> for Role { impl KeyDeserialize for Role { type Output = Role; + const KEY_ELEMS: u16 = 1; + fn from_vec(value: Vec) -> StdResult { let u8_key: ::Output = ::from_vec(value)?; Role::try_from(u8_key).map_err(|err| StdError::generic_err(err.to_string())) diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/rewarding/simulator/mod.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/rewarding/simulator/mod.rs index 46326a17129..9ad2a3d02a0 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/rewarding/simulator/mod.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/rewarding/simulator/mod.rs @@ -242,7 +242,7 @@ mod tests { #[allow(clippy::unwrap_used)] fn base_simulator(initial_pledge: u128) -> Simulator { let profit_margin = Percent::from_percentage_value(10).unwrap(); - let interval_operating_cost = Coin::new(40_000_000, "unym"); + let interval_operating_cost = Coin::new(40_000_000u64, "unym"); let epochs_in_interval = 720u32; let interval_pool_emission = Percent::from_percentage_value(2).unwrap(); @@ -347,7 +347,7 @@ mod tests { fn single_delegation_at_genesis() { let mut simulator = base_simulator(10000_000000); simulator - .delegate("alice", Coin::new(18000_000000, "unym"), 0) + .delegate("alice", Coin::new(18000_000000u64, "unym"), 0) .unwrap(); let node_params = NodeRewardingParameters::new( @@ -393,7 +393,7 @@ mod tests { compare_decimals(rewards1.operator, expected_operator1, None); simulator - .delegate("alice", Coin::new(18000_000000, "unym"), 0) + .delegate("alice", Coin::new(18000_000000u64, "unym"), 0) .unwrap(); let rewards2 = simulator.simulate_epoch_single_node(node_params).unwrap(); @@ -439,10 +439,10 @@ mod tests { // add 2 delegations at genesis (because it makes things easier and as shown with previous tests // delegating at different times still work) simulator - .delegate("alice", Coin::new(18000_000000, "unym"), 0) + .delegate("alice", Coin::new(18000_000000u64, "unym"), 0) .unwrap(); simulator - .delegate("bob", Coin::new(4000_000000, "unym"), 0) + .delegate("bob", Coin::new(4000_000000u64, "unym"), 0) .unwrap(); // "normal", sanity check rewarding @@ -484,10 +484,10 @@ mod tests { // add 2 delegations at genesis (because it makes things easier and as shown with previous tests // delegating at different times still work) simulator - .delegate("alice", Coin::new(18000_000000, "unym"), 0) + .delegate("alice", Coin::new(18000_000000u64, "unym"), 0) .unwrap(); simulator - .delegate("bob", Coin::new(4000_000000, "unym"), 0) + .delegate("bob", Coin::new(4000_000000u64, "unym"), 0) .unwrap(); // "normal", sanity check rewarding @@ -553,12 +553,12 @@ mod tests { for epoch in 0..720 { if epoch == 0 { simulator - .delegate("a", Coin::new(18000_000000, "unym"), 0) + .delegate("a", Coin::new(18000_000000u64, "unym"), 0) .unwrap() } if epoch == 42 { simulator - .delegate("b", Coin::new(2000_000000, "unym"), 0) + .delegate("b", Coin::new(2000_000000u64, "unym"), 0) .unwrap() } if epoch == 89 { @@ -566,7 +566,7 @@ mod tests { } if epoch == 123 { simulator - .delegate("c", Coin::new(6666_000000, "unym"), 0) + .delegate("c", Coin::new(6666_000000u64, "unym"), 0) .unwrap() } if epoch == 167 { @@ -574,7 +574,7 @@ mod tests { } if epoch == 245 { simulator - .delegate("d", Coin::new(2050_000000, "unym"), 0) + .delegate("d", Coin::new(2050_000000u64, "unym"), 0) .unwrap() } if epoch == 264 { @@ -597,7 +597,7 @@ mod tests { } if epoch == 545 { simulator - .delegate("e", Coin::new(5000_000000, "unym"), 0) + .delegate("e", Coin::new(5000_000000u64, "unym"), 0) .unwrap() } @@ -666,132 +666,132 @@ mod tests { let n0 = simulator .bond( - Coin::new(11_000_000_000000, "unym"), + Coin::new(11_000_000_000000u64, "unym"), NodeCostParams { profit_margin_percent: Percent::from_percentage_value(10).unwrap(), - interval_operating_cost: Coin::new(40_000_000, "unym"), + interval_operating_cost: Coin::new(40_000_000u64, "unym"), }, ) .unwrap(); simulator - .delegate("delegator", Coin::new(1_000_000_000000, "unym"), n0) + .delegate("delegator", Coin::new(1_000_000_000000u64, "unym"), n0) .unwrap(); let n1 = simulator .bond( - Coin::new(1_000_000_000000, "unym"), + Coin::new(1_000_000_000000u64, "unym"), NodeCostParams { profit_margin_percent: Percent::from_percentage_value(10).unwrap(), - interval_operating_cost: Coin::new(40_000_000, "unym"), + interval_operating_cost: Coin::new(40_000_000u64, "unym"), }, ) .unwrap(); simulator - .delegate("delegator", Coin::new(11_000_000_000000, "unym"), n1) + .delegate("delegator", Coin::new(11_000_000_000000u64, "unym"), n1) .unwrap(); let n2 = simulator .bond( - Coin::new(1_000_000_000000, "unym"), + Coin::new(1_000_000_000000u64, "unym"), NodeCostParams { profit_margin_percent: Percent::from_percentage_value(10).unwrap(), - interval_operating_cost: Coin::new(40_000_000, "unym"), + interval_operating_cost: Coin::new(40_000_000u64, "unym"), }, ) .unwrap(); simulator - .delegate("delegator", Coin::new(9_000_000_000000, "unym"), n2) + .delegate("delegator", Coin::new(9_000_000_000000u64, "unym"), n2) .unwrap(); let n3 = simulator .bond( - Coin::new(1_000_000_000000, "unym"), + Coin::new(1_000_000_000000u64, "unym"), NodeCostParams { profit_margin_percent: Percent::from_percentage_value(0).unwrap(), - interval_operating_cost: Coin::new(500_000_000, "unym"), + interval_operating_cost: Coin::new(500_000_000u64, "unym"), }, ) .unwrap(); simulator - .delegate("delegator", Coin::new(7_000_000_000000, "unym"), n3) + .delegate("delegator", Coin::new(7_000_000_000000u64, "unym"), n3) .unwrap(); let n4 = simulator .bond( - Coin::new(1000_000000, "unym"), + Coin::new(1000_000000u64, "unym"), NodeCostParams { profit_margin_percent: Percent::from_percentage_value(10).unwrap(), - interval_operating_cost: Coin::new(40_000_000, "unym"), + interval_operating_cost: Coin::new(40_000_000u64, "unym"), }, ) .unwrap(); simulator - .delegate("delegator", Coin::new(7_999_000_000000, "unym"), n4) + .delegate("delegator", Coin::new(7_999_000_000000u64, "unym"), n4) .unwrap(); let n5 = simulator .bond( - Coin::new(1_000_000_000000, "unym"), + Coin::new(1_000_000_000000u64, "unym"), NodeCostParams { profit_margin_percent: Percent::from_percentage_value(10).unwrap(), - interval_operating_cost: Coin::new(40_000_000, "unym"), + interval_operating_cost: Coin::new(40_000_000u64, "unym"), }, ) .unwrap(); simulator - .delegate("delegator", Coin::new(7_000_000_000000, "unym"), n5) + .delegate("delegator", Coin::new(7_000_000_000000u64, "unym"), n5) .unwrap(); let n6 = simulator .bond( - Coin::new(11_000_000_000000, "unym"), + Coin::new(11_000_000_000000u64, "unym"), NodeCostParams { profit_margin_percent: Percent::from_percentage_value(10).unwrap(), - interval_operating_cost: Coin::new(40_000_000, "unym"), + interval_operating_cost: Coin::new(40_000_000u64, "unym"), }, ) .unwrap(); simulator - .delegate("delegator", Coin::new(1_000_000_000000, "unym"), n6) + .delegate("delegator", Coin::new(1_000_000_000000u64, "unym"), n6) .unwrap(); let n7 = simulator .bond( - Coin::new(1_000_000_000000, "unym"), + Coin::new(1_000_000_000000u64, "unym"), NodeCostParams { profit_margin_percent: Percent::from_percentage_value(10).unwrap(), - interval_operating_cost: Coin::new(40_000_000, "unym"), + interval_operating_cost: Coin::new(40_000_000u64, "unym"), }, ) .unwrap(); simulator - .delegate("delegator", Coin::new(9_000_000_000000, "unym"), n7) + .delegate("delegator", Coin::new(9_000_000_000000u64, "unym"), n7) .unwrap(); let n8 = simulator .bond( - Coin::new(1_000_000_000000, "unym"), + Coin::new(1_000_000_000000u64, "unym"), NodeCostParams { profit_margin_percent: Percent::from_percentage_value(0).unwrap(), - interval_operating_cost: Coin::new(500_000_000, "unym"), + interval_operating_cost: Coin::new(500_000_000u64, "unym"), }, ) .unwrap(); simulator - .delegate("delegator", Coin::new(7_000_000_000000, "unym"), n8) + .delegate("delegator", Coin::new(7_000_000_000000u64, "unym"), n8) .unwrap(); let n9 = simulator .bond( - Coin::new(1_000_000_000000, "unym"), + Coin::new(1_000_000_000000u64, "unym"), NodeCostParams { profit_margin_percent: Percent::from_percentage_value(10).unwrap(), - interval_operating_cost: Coin::new(40_000_000, "unym"), + interval_operating_cost: Coin::new(40_000_000u64, "unym"), }, ) .unwrap(); simulator - .delegate("delegator", Coin::new(7_000_000_000000, "unym"), n9) + .delegate("delegator", Coin::new(7_000_000_000000u64, "unym"), n9) .unwrap(); let uptime_1 = Percent::from_percentage_value(100).unwrap(); diff --git a/common/cosmwasm-smart-contracts/nym-pool-contract/Cargo.toml b/common/cosmwasm-smart-contracts/nym-pool-contract/Cargo.toml new file mode 100644 index 00000000000..38d7659ece7 --- /dev/null +++ b/common/cosmwasm-smart-contracts/nym-pool-contract/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "nym-pool-contract-common" +version = "0.1.0" +description = "Common library for the Nym Pool contract" +authors.workspace = true +repository.workspace = true +homepage.workspace = true +documentation.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true +readme.workspace = true + +[dependencies] +thiserror = { workspace = true } +serde = { workspace = true } +schemars = { workspace = true } + +cosmwasm-std = { workspace = true } +cosmwasm-schema = { workspace = true } +cw-controllers = { workspace = true } + +[dev-dependencies] +time = { workspace = true, features = ["macros"] } + +[features] +schema = [] diff --git a/common/cosmwasm-smart-contracts/nym-pool-contract/src/constants.rs b/common/cosmwasm-smart-contracts/nym-pool-contract/src/constants.rs new file mode 100644 index 00000000000..a71397ab2b8 --- /dev/null +++ b/common/cosmwasm-smart-contracts/nym-pool-contract/src/constants.rs @@ -0,0 +1,11 @@ +// Copyright 2025 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +pub mod storage_keys { + pub const CONTRACT_ADMIN: &str = "contract-admin"; + pub const POOL_DENOMINATION: &str = "pool_denom"; + pub const GRANTERS: &str = "granters"; + pub const GRANTS: &str = "grants"; + pub const TOTAL_LOCKED: &str = "total_locked"; + pub const LOCKED_GRANTEES: &str = "locked_grantees"; +} diff --git a/common/cosmwasm-smart-contracts/nym-pool-contract/src/error.rs b/common/cosmwasm-smart-contracts/nym-pool-contract/src/error.rs new file mode 100644 index 00000000000..35ad7ec745e --- /dev/null +++ b/common/cosmwasm-smart-contracts/nym-pool-contract/src/error.rs @@ -0,0 +1,98 @@ +// Copyright 2025 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmwasm_std::{Coin, Uint128}; +use cw_controllers::AdminError; +use thiserror::Error; + +#[derive(Error, Debug, PartialEq)] +pub enum NymPoolContractError { + #[error("could not perform contract migration: {comment}")] + FailedMigration { comment: String }, + + #[error(transparent)] + Admin(#[from] AdminError), + + #[error(transparent)] + StdErr(#[from] cosmwasm_std::StdError), + + #[error("this sender is not authorised to revoke this grant. its neither the admin or the original (and still whitelisted) granter")] + UnauthorizedGrantRevocation, + + #[error("the specified address is already a whitelisted granter")] + AlreadyAGranter, + + #[error("{addr} is not a permitted granter")] + InvalidGranter { addr: String }, + + #[error("invalid coin denomination. got {got}, but expected {expected}")] + InvalidDenom { expected: String, got: String }, + + #[error("there already exists an active grant for {grantee}. it was granted by {granter} at block height {created_at_height}")] + GrantAlreadyExist { + granter: String, + grantee: String, + created_at_height: u64, + }, + + #[error("could not find any active grants for {grantee}")] + GrantNotFound { grantee: String }, + + #[error("the provided timestamp value ({timestamp}) is set in the past. the current block timestamp is {current_block_timestamp}")] + TimestampInThePast { + timestamp: u64, + current_block_timestamp: u64, + }, + + #[error("there are not enough tokens to process this request. {available} are available, but {required} is needed.")] + InsufficientTokens { available: Coin, required: Coin }, + + #[error("the period length can't be zero")] + ZeroAllowancePeriod, + + #[error("the provided coin value is zero")] + ZeroAmount, + + #[error("the periodic spend limit of {periodic} was set to be higher than the total spend limit {total_limit}")] + PeriodicGrantOverSpendLimit { periodic: Coin, total_limit: Coin }, + + #[error("the accumulation spend limit of {accumulation} was set to be lower than the periodic grant amount of {periodic_grant}")] + AccumulationBelowGrantAmount { + accumulation: Coin, + periodic_grant: Coin, + }, + + #[error("the accumulation spend limit of {accumulation} was set to be higher than the total spend limit of {total_limit}")] + AccumulationOverSpendLimit { + accumulation: Coin, + total_limit: Coin, + }, + + #[error("the specified delayed allowance would never be available. it would become active at {available_timestamp} yet it expires at {expiration_timestamp}")] + UnattainableDelayedAllowance { + expiration_timestamp: u64, + available_timestamp: u64, + }, + + #[error("could not unlock {requested} tokens from {grantee}. it only has {locked} locked")] + InsufficientLockedTokens { + grantee: String, + locked: Uint128, + requested: Uint128, + }, + + #[error("attempted to spend more tokens than permitted by the current allowance")] + SpendingAboveAllowance, + + #[error("attempted to send an empty allowance usage request")] + EmptyUsageRequest, + + #[error("the associated grant has already expired")] + GrantExpired, + + #[error("the associated grant hasn't expired yet")] + GrantNotExpired, + + #[error("this grant is not available yet. it will become usable at {available_at_timestamp}")] + GrantNotYetAvailable { available_at_timestamp: u64 }, +} diff --git a/common/cosmwasm-smart-contracts/nym-pool-contract/src/lib.rs b/common/cosmwasm-smart-contracts/nym-pool-contract/src/lib.rs new file mode 100644 index 00000000000..faa1b98b99a --- /dev/null +++ b/common/cosmwasm-smart-contracts/nym-pool-contract/src/lib.rs @@ -0,0 +1,12 @@ +// Copyright 2025 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +pub mod constants; +pub mod error; +pub mod msg; +pub mod types; +mod utils; + +pub use error::*; +pub use msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; +pub use types::*; diff --git a/common/cosmwasm-smart-contracts/nym-pool-contract/src/msg.rs b/common/cosmwasm-smart-contracts/nym-pool-contract/src/msg.rs new file mode 100644 index 00000000000..734cb46d288 --- /dev/null +++ b/common/cosmwasm-smart-contracts/nym-pool-contract/src/msg.rs @@ -0,0 +1,125 @@ +// Copyright 2025 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::{Allowance, TransferRecipient}; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::Coin; +use std::collections::HashMap; + +#[cfg(feature = "schema")] +use crate::types::{ + AvailableTokensResponse, GrantResponse, GranterResponse, GrantersPagedResponse, + GrantsPagedResponse, LockedTokensPagedResponse, LockedTokensResponse, + TotalLockedTokensResponse, +}; + +#[cw_serde] +pub struct InstantiateMsg { + pub pool_denomination: String, + + /// Initial map of grants to be created at instantiation + pub grants: HashMap, +} + +#[cw_serde] +pub enum ExecuteMsg { + /// Change the admin + UpdateAdmin { + admin: String, + // flag to determine whether old admin should be removed from the granter set + // and new one should be included instead + // the reason it's provided as an option is to make it possible to skip this field + // when creating transaction directly with nyxd + update_granter_set: Option, + }, + + /// Attempt to grant new allowance to the specified grantee + GrantAllowance { + grantee: String, + allowance: Box, + }, + + /// Attempt to revoke previously granted allowance + RevokeAllowance { grantee: String }, + + /// Attempt to use allowance + UseAllowance { recipients: Vec }, + + /// Attempt to withdraw the specified amount into the grantee's account + WithdrawAllowance { amount: Coin }, + + /// Attempt to lock part of existing allowance for future use + LockAllowance { amount: Coin }, + + /// Attempt to unlock previously locked allowance + UnlockAllowance { amount: Coin }, + + /// Attempt to use part of the locked allowance + UseLockedAllowance { recipients: Vec }, + + /// Attempt to withdraw the specified amount of locked tokens into the grantee's account + WithdrawLockedAllowance { amount: Coin }, + + /// Attempt to add a new account to the permitted set of grant granters + AddNewGranter { granter: String }, + + /// Revoke the provided account from the permitted set of granters + RevokeGranter { granter: String }, + + /// Attempt to remove expired grant from the storage and unlock (if any) locked tokens + RemoveExpiredGrant { grantee: String }, +} + +#[cw_serde] +#[cfg_attr(feature = "schema", derive(cosmwasm_schema::QueryResponses))] +pub enum QueryMsg { + #[cfg_attr(feature = "schema", returns(cw_controllers::AdminResponse))] + Admin {}, + + #[cfg_attr(feature = "schema", returns(AvailableTokensResponse))] + GetAvailableTokens {}, + + #[cfg_attr(feature = "schema", returns(TotalLockedTokensResponse))] + GetTotalLockedTokens {}, + + #[cfg_attr(feature = "schema", returns(LockedTokensResponse))] + GetLockedTokens { grantee: String }, + + #[cfg_attr(feature = "schema", returns(GrantResponse))] + GetGrant { grantee: String }, + + #[cfg_attr(feature = "schema", returns(GranterResponse))] + GetGranter { granter: String }, + + #[cfg_attr(feature = "schema", returns(LockedTokensPagedResponse))] + GetLockedTokensPaged { + /// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default. + limit: Option, + + /// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response. + start_after: Option, + }, + + #[cfg_attr(feature = "schema", returns(GrantersPagedResponse))] + GetGrantersPaged { + /// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default. + limit: Option, + + /// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response. + start_after: Option, + }, + + #[cfg_attr(feature = "schema", returns(GrantsPagedResponse))] + GetGrantsPaged { + /// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default. + limit: Option, + + /// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response. + start_after: Option, + }, +} + +#[cw_serde] +pub struct MigrateMsg { + // +} diff --git a/common/cosmwasm-smart-contracts/nym-pool-contract/src/types.rs b/common/cosmwasm-smart-contracts/nym-pool-contract/src/types.rs new file mode 100644 index 00000000000..8bffe7dc6a3 --- /dev/null +++ b/common/cosmwasm-smart-contracts/nym-pool-contract/src/types.rs @@ -0,0 +1,1279 @@ +// Copyright 2025 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, Coin}; + +pub type GranterAddress = Addr; +pub type GranteeAddress = Addr; + +pub use grants::*; +pub use query_responses::*; + +#[cw_serde] +pub struct TransferRecipient { + pub recipient: String, + pub amount: Coin, +} + +pub mod grants { + use crate::utils::ensure_unix_timestamp_not_in_the_past; + use crate::{GranteeAddress, GranterAddress, NymPoolContractError}; + use cosmwasm_schema::cw_serde; + use cosmwasm_std::{Addr, Coin, Env, Timestamp, Uint128}; + use std::cmp::min; + + #[cw_serde] + pub struct GranterInformation { + // realistically this is always going to be the contract admin, + // but let's keep this metadata regardless just in case it ever changes, + // such as we create a granter controlled by validator multisig or governance + pub created_by: Addr, + pub created_at_height: u64, + } + + #[cw_serde] + pub struct Grant { + pub granter: GranterAddress, + pub grantee: GranteeAddress, + pub granted_at_height: u64, + pub allowance: Allowance, + } + + #[cw_serde] + pub enum Allowance { + Basic(BasicAllowance), + ClassicPeriodic(ClassicPeriodicAllowance), + CumulativePeriodic(CumulativePeriodicAllowance), + Delayed(DelayedAllowance), + } + + impl From for Allowance { + fn from(value: BasicAllowance) -> Self { + Allowance::Basic(value) + } + } + + impl From for Allowance { + fn from(value: ClassicPeriodicAllowance) -> Self { + Allowance::ClassicPeriodic(value) + } + } + + impl From for Allowance { + fn from(value: CumulativePeriodicAllowance) -> Self { + Allowance::CumulativePeriodic(value) + } + } + + impl From for Allowance { + fn from(value: DelayedAllowance) -> Self { + Allowance::Delayed(value) + } + } + + impl Allowance { + pub fn expired(&self, env: &Env) -> bool { + self.basic().expired(env) + } + + pub fn basic(&self) -> &BasicAllowance { + match self { + Allowance::Basic(allowance) => allowance, + Allowance::ClassicPeriodic(allowance) => &allowance.basic, + Allowance::CumulativePeriodic(allowance) => &allowance.basic, + Allowance::Delayed(allowance) => &allowance.basic, + } + } + + pub fn basic_mut(&mut self) -> &mut BasicAllowance { + match self { + Allowance::Basic(ref mut allowance) => allowance, + Allowance::ClassicPeriodic(ref mut allowance) => &mut allowance.basic, + Allowance::CumulativePeriodic(ref mut allowance) => &mut allowance.basic, + Allowance::Delayed(ref mut allowance) => &mut allowance.basic, + } + } + + pub fn expiration(&self) -> Option { + let expiration_unix = match self { + Allowance::Basic(allowance) => allowance.expiration_unix_timestamp, + Allowance::ClassicPeriodic(allowance) => allowance.basic.expiration_unix_timestamp, + Allowance::CumulativePeriodic(allowance) => { + allowance.basic.expiration_unix_timestamp + } + Allowance::Delayed(allowance) => allowance.basic.expiration_unix_timestamp, + }; + + expiration_unix.map(Timestamp::from_seconds) + } + + /// Perform validation of a new grant that's to be created + pub fn validate_new(&self, env: &Env, denom: &str) -> Result<(), NymPoolContractError> { + // 1. perform validation on the inner, basic, allowance + self.basic().validate(env, denom)?; + + // 2. perform additional validation specific to each variant + match self { + // we already validated basic allowance + Allowance::Basic(_) => Ok(()), + Allowance::ClassicPeriodic(allowance) => allowance.validate_new_inner(denom), + Allowance::CumulativePeriodic(allowance) => allowance.validate_new_inner(denom), + Allowance::Delayed(allowance) => allowance.validate_new_inner(env), + } + } + + /// Updates initial state of this allowance settings things such as period reset timestamps. + pub fn set_initial_state(&mut self, env: &Env) { + match self { + // nothing to do for the basic allowance + Allowance::Basic(_) => {} + Allowance::ClassicPeriodic(allowance) => allowance.set_initial_state(env), + Allowance::CumulativePeriodic(allowance) => allowance.set_initial_state(env), + // nothing to do for the delayed allowance + Allowance::Delayed(_) => {} + } + } + + pub fn try_update_state(&mut self, env: &Env) { + match self { + // nothing to do for the basic allowance + Allowance::Basic(_) => {} + Allowance::ClassicPeriodic(allowance) => allowance.try_update_state(env), + Allowance::CumulativePeriodic(allowance) => allowance.try_update_state(env), + // nothing to do for the delayed allowance + Allowance::Delayed(_) => {} + } + } + + pub fn within_spendable_limits(&self, amount: &Coin) -> bool { + match self { + Allowance::Basic(allowance) => allowance.within_spendable_limits(amount), + Allowance::ClassicPeriodic(allowance) => allowance.within_spendable_limits(amount), + Allowance::CumulativePeriodic(allowance) => { + allowance.within_spendable_limits(amount) + } + Allowance::Delayed(allowance) => allowance.within_spendable_limits(amount), + } + } + + // check whether given the current allowance state, the provided amount could be spent + // note: it's responsibility of the caller to call `try_update_state` before the call. + pub fn ensure_can_spend( + &self, + env: &Env, + amount: &Coin, + ) -> Result<(), NymPoolContractError> { + match self { + Allowance::Basic(allowance) => allowance.ensure_can_spend(env, amount), + Allowance::ClassicPeriodic(allowance) => allowance.ensure_can_spend(env, amount), + Allowance::CumulativePeriodic(allowance) => allowance.ensure_can_spend(env, amount), + Allowance::Delayed(allowance) => allowance.ensure_can_spend(env, amount), + } + } + + pub fn try_spend(&mut self, env: &Env, amount: &Coin) -> Result<(), NymPoolContractError> { + self.try_update_state(env); + + match self { + Allowance::Basic(allowance) => allowance.try_spend(env, amount), + Allowance::ClassicPeriodic(allowance) => allowance.try_spend(env, amount), + Allowance::CumulativePeriodic(allowance) => allowance.try_spend(env, amount), + Allowance::Delayed(allowance) => allowance.try_spend(env, amount), + } + } + + pub fn increase_spend_limit(&mut self, amount: Uint128) { + if let Some(ref mut limit) = self.basic_mut().spend_limit { + limit.amount += amount + } + } + + pub fn is_used_up(&self) -> bool { + let Some(ref limit) = self.basic().spend_limit else { + return false; + }; + limit.amount.is_zero() + } + } + + /// BasicAllowance is an allowance with a one-time grant of coins + /// that optionally expires. The grantee can use up to SpendLimit to cover fees. + #[cw_serde] + pub struct BasicAllowance { + /// spend_limit specifies the maximum amount of coins that can be spent + /// by this allowance and will be updated as coins are spent. If it is + /// empty, there is no spend limit and any amount of coins can be spent. + pub spend_limit: Option, + + /// expiration specifies an optional time when this allowance expires + pub expiration_unix_timestamp: Option, + } + + impl BasicAllowance { + pub fn unlimited() -> BasicAllowance { + BasicAllowance { + spend_limit: None, + expiration_unix_timestamp: None, + } + } + + pub fn validate(&self, env: &Env, denom: &str) -> Result<(), NymPoolContractError> { + // expiration shouldn't be in the past. + if let Some(expiration) = self.expiration_unix_timestamp { + ensure_unix_timestamp_not_in_the_past(expiration, env)?; + } + + // if spend limit is set, it must use the same denomination as the underlying pool + if let Some(ref spend_limit) = self.spend_limit { + if spend_limit.denom != denom { + return Err(NymPoolContractError::InvalidDenom { + expected: denom.to_string(), + got: spend_limit.denom.to_string(), + }); + } + + if spend_limit.amount.is_zero() { + return Err(NymPoolContractError::ZeroAmount); + } + } + + Ok(()) + } + + pub fn expired(&self, env: &Env) -> bool { + let Some(expiration) = self.expiration_unix_timestamp else { + return false; + }; + let current_unix_timestamp = env.block.time.seconds(); + + expiration < current_unix_timestamp + } + + fn within_spendable_limits(&self, amount: &Coin) -> bool { + let Some(ref spend_limit) = self.spend_limit else { + // if there's no spend limit then whatever the amount is, it's spendable + return true; + }; + + spend_limit.amount >= amount.amount + } + + fn ensure_can_spend(&self, env: &Env, amount: &Coin) -> Result<(), NymPoolContractError> { + if self.expired(env) { + return Err(NymPoolContractError::GrantExpired); + } + if !self.within_spendable_limits(amount) { + return Err(NymPoolContractError::SpendingAboveAllowance); + } + Ok(()) + } + + fn try_spend(&mut self, env: &Env, amount: &Coin) -> Result<(), NymPoolContractError> { + self.ensure_can_spend(env, amount)?; + + if let Some(ref mut spend_limit) = self.spend_limit { + spend_limit.amount -= amount.amount; + } + Ok(()) + } + } + + /// ClassicPeriodicAllowance extends BasicAllowance to allow for both a maximum cap, + /// as well as a limit per time period. + #[cw_serde] + pub struct ClassicPeriodicAllowance { + /// basic specifies a struct of `BasicAllowance` + pub basic: BasicAllowance, + + /// period_duration_secs specifies the time duration in which period_spend_limit coins can + /// be spent before that allowance is reset + pub period_duration_secs: u64, + + /// period_spend_limit specifies the maximum number of coins that can be spent + /// in the period + pub period_spend_limit: Coin, + + /// period_can_spend is the number of coins left to be spent before the period_reset time + // set by the contract during initialisation of the grant + #[serde(default)] + pub period_can_spend: Option, + + /// period_reset is the time at which this period resets and a new one begins, + /// it is calculated from the start time of the first transaction after the + /// last period ended + // set by the contract during initialisation of the grant + #[serde(default)] + pub period_reset_unix_timestamp: u64, + } + + impl ClassicPeriodicAllowance { + pub(super) fn validate_new_inner(&self, denom: &str) -> Result<(), NymPoolContractError> { + // period duration shouldn't be zero + if self.period_duration_secs == 0 { + return Err(NymPoolContractError::ZeroAllowancePeriod); + } + + // the denom for period spend limit must match the expected value + if self.period_spend_limit.denom != denom { + return Err(NymPoolContractError::InvalidDenom { + expected: denom.to_string(), + got: self.period_spend_limit.denom.to_string(), + }); + } + + if self.period_spend_limit.amount.is_zero() { + return Err(NymPoolContractError::ZeroAmount); + } + + // if the basic spend limit is set, the period spend limit cannot be larger than it + if let Some(ref basic_limit) = self.basic.spend_limit { + if basic_limit.amount < self.period_spend_limit.amount { + return Err(NymPoolContractError::PeriodicGrantOverSpendLimit { + periodic: self.period_spend_limit.clone(), + total_limit: basic_limit.clone(), + }); + } + } + + Ok(()) + } + + /// The value that can be spent in the period is the lesser of the basic spend limit + /// and the period spend limit + /// + /// ```go + /// if _, isNeg := a.Basic.SpendLimit.SafeSub(a.PeriodSpendLimit...); isNeg && !a.Basic.SpendLimit.Empty() { + /// a.PeriodCanSpend = a.Basic.SpendLimit + /// } else { + /// a.PeriodCanSpend = a.PeriodSpendLimit + /// } + /// ``` + fn determine_period_can_spend(&self) -> Coin { + let Some(ref basic_limit) = self.basic.spend_limit else { + // if there's no spend limit, there's nothing to compare against + return self.period_spend_limit.clone(); + }; + + if basic_limit.amount < self.period_spend_limit.amount { + basic_limit.clone() + } else { + self.period_spend_limit.clone() + } + } + + pub(super) fn set_initial_state(&mut self, env: &Env) { + self.try_update_state(env); + } + + /// try_update_state will check if the period_reset_unix_timestamp has been hit. If not, it is a no-op. + /// If we hit the reset period, it will top up the period_can_spend amount to + /// min(period_spend_limit, basic.spend_limit) so it is never more than the maximum allowed. + /// It will also update the period_reset_unix_timestamp. + /// + /// If we are within one period, it will update from the + /// last period_reset (eg. if you always do one tx per day, it will always reset the same time) + /// If we are more than one period out (eg. no activity in a week), reset is one period from the execution of this method + pub fn try_update_state(&mut self, env: &Env) { + if env.block.time.seconds() < self.period_reset_unix_timestamp { + // we haven't yet reached the reset time + return; + } + self.period_can_spend = Some(self.determine_period_can_spend()); + + // If we are within the period, step from expiration (eg. if you always do one tx per day, + // it will always reset the same time) + // If we are more then one period out (eg. no activity in a week), + // reset is one period from this time + self.period_reset_unix_timestamp += self.period_duration_secs; + if env.block.time.seconds() > self.period_duration_secs { + self.period_reset_unix_timestamp = + env.block.time.seconds() + self.period_duration_secs; + } + } + + fn within_spendable_limits(&self, amount: &Coin) -> bool { + let Some(ref available) = self.period_can_spend else { + return false; + }; + available.amount >= amount.amount + } + + fn ensure_can_spend(&self, env: &Env, amount: &Coin) -> Result<(), NymPoolContractError> { + if self.basic.expired(env) { + return Err(NymPoolContractError::GrantExpired); + } + if !self.within_spendable_limits(amount) { + return Err(NymPoolContractError::SpendingAboveAllowance); + } + Ok(()) + } + + fn try_spend(&mut self, env: &Env, amount: &Coin) -> Result<(), NymPoolContractError> { + self.ensure_can_spend(env, amount)?; + + // deduct from both the current period and the max amount + if let Some(ref mut spend_limit) = self.basic.spend_limit { + spend_limit.amount -= amount.amount; + } + + // SAFETY: initial `period_can_spend` value is always unconditionally set by the contract during + // grant creation + #[allow(clippy::unwrap_used)] + let period_can_spend = self.period_can_spend.as_mut().unwrap(); + period_can_spend.amount -= amount.amount; + + Ok(()) + } + } + + #[cw_serde] + pub struct CumulativePeriodicAllowance { + /// basic specifies a struct of `BasicAllowance` + pub basic: BasicAllowance, + + /// period_duration_secs specifies the time duration in which spendable coins can + /// be spent before that allowance is incremented + pub period_duration_secs: u64, + + /// period_grant specifies the maximum number of coins that is granted per period + pub period_grant: Coin, + + /// accumulation_limit is the maximum value the grants and accumulate to + pub accumulation_limit: Option, + + /// spendable is the number of coins left to be spent before additional grant is applied + // set by the contract during initialisation of the grant + #[serde(default)] + pub spendable: Option, + + /// last_grant_applied is the time at which last transaction associated with this allowance + /// has been sent and `spendable` value has been adjusted + // set by the contract during initialisation of the grant + #[serde(default)] + pub last_grant_applied_unix_timestamp: u64, + } + + impl CumulativePeriodicAllowance { + pub(super) fn validate_new_inner(&self, denom: &str) -> Result<(), NymPoolContractError> { + // period duration shouldn't be zero + if self.period_duration_secs == 0 { + return Err(NymPoolContractError::ZeroAllowancePeriod); + } + + // the denom for period grant must match the expected value + if self.period_grant.denom != denom { + return Err(NymPoolContractError::InvalidDenom { + expected: denom.to_string(), + got: self.period_grant.denom.to_string(), + }); + } + + if self.period_grant.amount.is_zero() { + return Err(NymPoolContractError::ZeroAmount); + } + + // the period grant must not be larger than the total spend limit, if set + if let Some(ref basic_limit) = self.basic.spend_limit { + if basic_limit.amount < self.period_grant.amount { + return Err(NymPoolContractError::PeriodicGrantOverSpendLimit { + periodic: self.period_grant.clone(), + total_limit: basic_limit.clone(), + }); + } + } + + if let Some(ref accumulation_limit) = self.accumulation_limit { + // if set, the accumulation limit must not be smaller than the period grant + if accumulation_limit.amount < self.period_grant.amount { + return Err(NymPoolContractError::AccumulationBelowGrantAmount { + accumulation: accumulation_limit.clone(), + periodic_grant: self.period_grant.clone(), + }); + } + + // if set, the denom for accumulation limit must match the expected value + if accumulation_limit.denom != denom { + return Err(NymPoolContractError::InvalidDenom { + expected: denom.to_string(), + got: accumulation_limit.denom.to_string(), + }); + } + + // if set, the accumulation limit must not be larger than the total spend limit + if let Some(ref basic_limit) = self.basic.spend_limit { + if basic_limit.amount < accumulation_limit.amount { + return Err(NymPoolContractError::AccumulationOverSpendLimit { + accumulation: accumulation_limit.clone(), + total_limit: basic_limit.clone(), + }); + } + } + } + + Ok(()) + } + + pub(super) fn set_initial_state(&mut self, env: &Env) { + self.last_grant_applied_unix_timestamp = env.block.time.seconds(); + + // initially we can spend equivalent of a single grant + self.spendable = Some(self.period_grant.clone()) + } + + #[inline] + fn missed_periods(&self, env: &Env) -> u64 { + (env.block.time.seconds() - self.last_grant_applied_unix_timestamp) + % self.period_duration_secs + } + + /// The value that can be spent is the last of the basic spend limit, the accumulation limit + /// and number of missed periods multiplied by the period grant + fn determine_spendable(&self, env: &Env) -> Coin { + // SAFETY: initial `spendable` value is always unconditionally set by the contract during + // grant creation + #[allow(clippy::unwrap_used)] + let spendable = self.spendable.as_ref().unwrap(); + + let missed_periods = self.missed_periods(env); + let mut max_spendable = spendable.clone(); + max_spendable.amount += Uint128::new(missed_periods as u128) * self.period_grant.amount; + + match (&self.basic.spend_limit, &self.accumulation_limit) { + (Some(spend_limit), Some(accumulation_limit)) => { + let limit = min(spend_limit.amount, accumulation_limit.amount); + let amount = min(limit, max_spendable.amount); + Coin::new(amount, max_spendable.denom) + } + (None, Some(accumulation_limit)) => { + let amount = min(accumulation_limit.amount, max_spendable.amount); + Coin::new(amount, max_spendable.denom) + } + (Some(spend_limit), None) => { + let amount = min(spend_limit.amount, max_spendable.amount); + Coin::new(amount, max_spendable.denom) + } + (None, None) => max_spendable, + } + } + + /// try_update_state will check if we've rolled over into the next grant period. If not, it is a no-op. + /// If we hit the next period, it will top up the spendable amount to + /// min(accumulation_limit, basic.spend_limit, spendable + period_grant * num_missed_periods) so it is never more than the maximum allowed. + /// It will also update the last_grant_applied_unix_timestamp. + pub fn try_update_state(&mut self, env: &Env) { + let missed_periods = self.missed_periods(env); + + if missed_periods == 0 { + // we haven't yet reached the next grant time + return; + } + + self.spendable = Some(self.determine_spendable(env)) + } + + fn within_spendable_limits(&self, amount: &Coin) -> bool { + let Some(ref available) = self.spendable else { + return false; + }; + available.amount >= amount.amount + } + + fn ensure_can_spend(&self, env: &Env, amount: &Coin) -> Result<(), NymPoolContractError> { + if self.basic.expired(env) { + return Err(NymPoolContractError::GrantExpired); + } + if !self.within_spendable_limits(amount) { + return Err(NymPoolContractError::SpendingAboveAllowance); + } + Ok(()) + } + + fn try_spend(&mut self, env: &Env, amount: &Coin) -> Result<(), NymPoolContractError> { + self.ensure_can_spend(env, amount)?; + + // deduct from both the current period and the max amount + if let Some(ref mut spend_limit) = self.basic.spend_limit { + spend_limit.amount -= amount.amount; + } + + // SAFETY: initial `spendable` value is always unconditionally set by the contract during + // grant creation + #[allow(clippy::unwrap_used)] + let spendable = self.spendable.as_mut().unwrap(); + spendable.amount -= amount.amount; + + Ok(()) + } + } + + /// Create a grant to allow somebody to withdraw from the pool only after the specified time. + /// For example, we could create a grant for mixnet rewarding/testing/etc + /// However, if the required work has not been completed, the grant could be revoked before it's withdrawn + #[cw_serde] + pub struct DelayedAllowance { + /// basic specifies a struct of `BasicAllowance` + pub basic: BasicAllowance, + + /// available_at specifies when this allowance is going to become usable + pub available_at_unix_timestamp: u64, + } + + impl DelayedAllowance { + pub(super) fn validate_new_inner(&self, env: &Env) -> Result<(), NymPoolContractError> { + // available at must be set in the future + ensure_unix_timestamp_not_in_the_past(self.available_at_unix_timestamp, env)?; + + // and it must become available before the underlying allowance expires + if let Some(expiration) = self.basic.expiration_unix_timestamp { + if expiration < self.available_at_unix_timestamp { + return Err(NymPoolContractError::UnattainableDelayedAllowance { + expiration_timestamp: expiration, + available_timestamp: self.available_at_unix_timestamp, + }); + } + } + + Ok(()) + } + + fn within_spendable_limits(&self, amount: &Coin) -> bool { + self.basic.within_spendable_limits(amount) + } + + fn ensure_can_spend(&self, env: &Env, amount: &Coin) -> Result<(), NymPoolContractError> { + if self.basic.expired(env) { + return Err(NymPoolContractError::GrantExpired); + } + if !self.within_spendable_limits(amount) { + return Err(NymPoolContractError::SpendingAboveAllowance); + } + if self.available_at_unix_timestamp < env.block.time.seconds() { + return Err(NymPoolContractError::GrantNotYetAvailable { + available_at_timestamp: self.available_at_unix_timestamp, + }); + } + + Ok(()) + } + + fn try_spend(&mut self, env: &Env, amount: &Coin) -> Result<(), NymPoolContractError> { + self.ensure_can_spend(env, amount)?; + + if let Some(ref mut spend_limit) = self.basic.spend_limit { + spend_limit.amount -= amount.amount; + } + + Ok(()) + } + } +} + +pub mod query_responses { + use crate::{Grant, GranteeAddress, GranterAddress, GranterInformation}; + use cosmwasm_schema::cw_serde; + use cosmwasm_std::Coin; + + #[cw_serde] + pub struct AvailableTokensResponse { + pub available: Coin, + } + + #[cw_serde] + pub struct TotalLockedTokensResponse { + pub locked: Coin, + } + + #[cw_serde] + pub struct LockedTokensResponse { + pub grantee: GranteeAddress, + + pub locked: Option, + } + + #[cw_serde] + pub struct GrantInformation { + pub grant: Grant, + pub expired: bool, + } + + #[cw_serde] + pub struct GrantResponse { + pub grantee: GranteeAddress, + pub grant: Option, + } + + #[cw_serde] + pub struct GranterResponse { + pub granter: GranterAddress, + pub information: Option, + } + + #[cw_serde] + pub struct GrantsPagedResponse { + pub grants: Vec, + pub start_next_after: Option, + } + + #[cw_serde] + pub struct GranterDetails { + pub granter: GranterAddress, + pub information: GranterInformation, + } + + impl From<(GranterAddress, GranterInformation)> for GranterDetails { + fn from((granter, information): (GranterAddress, GranterInformation)) -> Self { + GranterDetails { + granter, + information, + } + } + } + + #[cw_serde] + pub struct GrantersPagedResponse { + pub granters: Vec, + pub start_next_after: Option, + } + + #[cw_serde] + pub struct LockedTokens { + pub grantee: GranteeAddress, + pub locked: Coin, + } + + #[cw_serde] + pub struct LockedTokensPagedResponse { + pub locked: Vec, + pub start_next_after: Option, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use cosmwasm_std::{coin, Uint128}; + + const TEST_DENOM: &str = "unym"; + + fn mock_basic_allowance() -> BasicAllowance { + BasicAllowance { + spend_limit: Some(coin(100000, TEST_DENOM)), + expiration_unix_timestamp: Some(1643652000), + } + } + + fn mock_classic_periodic_allowance() -> ClassicPeriodicAllowance { + ClassicPeriodicAllowance { + basic: mock_basic_allowance(), + period_duration_secs: 10, + period_spend_limit: coin(1000, TEST_DENOM), + period_can_spend: None, + period_reset_unix_timestamp: 0, + } + } + + fn mock_cumulative_periodic_allowance() -> CumulativePeriodicAllowance { + CumulativePeriodicAllowance { + basic: mock_basic_allowance(), + period_duration_secs: 10, + period_grant: coin(1000, TEST_DENOM), + accumulation_limit: Some(coin(10000, TEST_DENOM)), + spendable: None, + last_grant_applied_unix_timestamp: 0, + } + } + + fn mock_delayed_allowance() -> DelayedAllowance { + DelayedAllowance { + basic: mock_basic_allowance(), + available_at_unix_timestamp: 1643650000, + } + } + + #[test] + fn increasing_spend_limit() { + // no-op if there's no limit + let mut basic = mock_basic_allowance(); + basic.spend_limit = None; + let mut basic = Allowance::Basic(basic); + + let mut classic = mock_classic_periodic_allowance(); + classic.basic.spend_limit = None; + let mut classic = Allowance::ClassicPeriodic(classic); + + let mut cumulative = mock_cumulative_periodic_allowance(); + cumulative.basic.spend_limit = None; + let mut cumulative = Allowance::CumulativePeriodic(cumulative); + + let mut delayed = mock_delayed_allowance(); + delayed.basic.spend_limit = None; + let mut delayed = Allowance::Delayed(delayed); + + let basic_og = basic.clone(); + let classic_og = classic.clone(); + let cumulative_og = cumulative.clone(); + let delayed_og = delayed.clone(); + + basic.increase_spend_limit(Uint128::new(100)); + classic.increase_spend_limit(Uint128::new(100)); + cumulative.increase_spend_limit(Uint128::new(100)); + delayed.increase_spend_limit(Uint128::new(100)); + + assert_eq!(basic, basic_og); + assert_eq!(classic, classic_og); + assert_eq!(cumulative, cumulative_og); + assert_eq!(delayed, delayed_og); + + // adds to spend limit otherwise + let limit = coin(1000, TEST_DENOM); + let mut basic = mock_basic_allowance(); + basic.spend_limit = Some(limit.clone()); + let mut basic = Allowance::Basic(basic); + + let mut classic = mock_classic_periodic_allowance(); + classic.basic.spend_limit = Some(limit.clone()); + let mut classic = Allowance::ClassicPeriodic(classic); + + let mut cumulative = mock_cumulative_periodic_allowance(); + cumulative.basic.spend_limit = Some(limit.clone()); + let mut cumulative = Allowance::CumulativePeriodic(cumulative); + + let mut delayed = mock_delayed_allowance(); + delayed.basic.spend_limit = Some(limit.clone()); + let mut delayed = Allowance::Delayed(delayed); + + basic.increase_spend_limit(Uint128::new(100)); + classic.increase_spend_limit(Uint128::new(100)); + cumulative.increase_spend_limit(Uint128::new(100)); + delayed.increase_spend_limit(Uint128::new(100)); + + assert_eq!( + basic.basic().spend_limit.as_ref().unwrap().amount, + limit.amount + Uint128::new(100) + ); + assert_eq!( + classic.basic().spend_limit.as_ref().unwrap().amount, + limit.amount + Uint128::new(100) + ); + assert_eq!( + cumulative.basic().spend_limit.as_ref().unwrap().amount, + limit.amount + Uint128::new(100) + ); + assert_eq!( + delayed.basic().spend_limit.as_ref().unwrap().amount, + limit.amount + Uint128::new(100) + ); + } + + #[cfg(test)] + mod validating_new_allowances { + use super::*; + + #[cfg(test)] + mod basic_allowance { + use super::*; + use cosmwasm_std::testing::mock_env; + use cosmwasm_std::Timestamp; + + #[test] + fn doesnt_allow_expirations_in_the_past() { + let mut allowance = mock_basic_allowance(); + + let mut env = mock_env(); + + // allowance expiration is in the past + env.block.time = + Timestamp::from_seconds(allowance.expiration_unix_timestamp.unwrap() + 1); + assert!(allowance.validate(&env, TEST_DENOM).is_err()); + + // allowance expiration is equal to the current block time + env.block.time = + Timestamp::from_seconds(allowance.expiration_unix_timestamp.unwrap()); + assert!(allowance.validate(&env, TEST_DENOM).is_ok()); + + // allowance expiration is in the future + env.block.time = + Timestamp::from_seconds(allowance.expiration_unix_timestamp.unwrap() - 1); + assert!(allowance.validate(&env, TEST_DENOM).is_ok()); + + // no explicit expiration + allowance.expiration_unix_timestamp = None; + assert!(allowance.validate(&env, TEST_DENOM).is_ok()); + } + + #[test] + fn spend_limit_must_match_expected_denom() { + let mut allowance = mock_basic_allowance(); + + let env = mock_env(); + + // mismatched denom + assert!(allowance.validate(&env, "baddenom").is_err()); + + // matched denom + assert!(allowance.validate(&env, TEST_DENOM).is_ok()); + + // no spend limit + allowance.spend_limit = None; + assert!(allowance.validate(&env, TEST_DENOM).is_ok()); + } + + #[test] + fn spend_limit_must_be_non_zero() { + let mut allowance = mock_basic_allowance(); + + let env = mock_env(); + + // zero amount + allowance.spend_limit = Some(coin(0, TEST_DENOM)); + assert!(allowance.validate(&env, TEST_DENOM).is_err()); + + // non-zero amount + allowance.spend_limit = Some(coin(69, TEST_DENOM)); + assert!(allowance.validate(&env, TEST_DENOM).is_ok()); + } + } + + #[cfg(test)] + mod classic_periodic_allowance { + use super::*; + use crate::NymPoolContractError; + + #[test] + fn period_duration_must_be_nonzero() { + let mut allowance = mock_classic_periodic_allowance(); + + allowance.period_duration_secs = 0; + assert_eq!( + allowance.validate_new_inner(TEST_DENOM).unwrap_err(), + NymPoolContractError::ZeroAllowancePeriod + ); + + allowance.period_duration_secs = 1; + assert!(allowance.validate_new_inner(TEST_DENOM).is_ok()); + } + + #[test] + fn spend_limit_must_match_expected_denom() { + let allowance = mock_classic_periodic_allowance(); + + // mismatched denom + assert!(allowance.validate_new_inner("baddenom").is_err()); + + // matched denom + assert!(allowance.validate_new_inner(TEST_DENOM).is_ok()); + } + + #[test] + fn spend_limit_must_be_non_zero() { + let mut allowance = mock_classic_periodic_allowance(); + + // zero amount + allowance.period_spend_limit = coin(0, TEST_DENOM); + assert!(allowance.validate_new_inner(TEST_DENOM).is_err()); + + // non-zero amount + allowance.period_spend_limit = coin(69, TEST_DENOM); + assert!(allowance.validate_new_inner(TEST_DENOM).is_ok()); + } + + #[test] + fn period_spend_limit_must_be_smaller_than_total_limit() { + let mut allowance = mock_classic_periodic_allowance(); + + let total_limit = coin(1000, TEST_DENOM); + allowance.basic.spend_limit = Some(total_limit); + + // above total spend limit + allowance.period_spend_limit = coin(1001, TEST_DENOM); + assert!(matches!( + allowance.validate_new_inner(TEST_DENOM).unwrap_err(), + NymPoolContractError::PeriodicGrantOverSpendLimit { .. } + )); + + // below total spend limit + allowance.period_spend_limit = coin(999, TEST_DENOM); + assert!(allowance.validate_new_inner(TEST_DENOM).is_ok()); + + // equal to total spend limit + allowance.period_spend_limit = coin(1000, TEST_DENOM); + assert!(allowance.validate_new_inner(TEST_DENOM).is_ok()); + + // no total spend limit + allowance.basic.spend_limit = None; + assert!(allowance.validate_new_inner(TEST_DENOM).is_ok()); + } + } + + #[cfg(test)] + mod cumulative_periodic_allowance { + use super::*; + use crate::NymPoolContractError; + + #[test] + fn period_duration_must_be_nonzero() { + let mut allowance = mock_cumulative_periodic_allowance(); + + allowance.period_duration_secs = 0; + assert_eq!( + allowance.validate_new_inner(TEST_DENOM).unwrap_err(), + NymPoolContractError::ZeroAllowancePeriod + ); + + allowance.period_duration_secs = 1; + assert!(allowance.validate_new_inner(TEST_DENOM).is_ok()); + } + + #[test] + fn grant_must_match_expected_denom() { + let allowance = mock_cumulative_periodic_allowance(); + + // mismatched denom + assert!(allowance.validate_new_inner("baddenom").is_err()); + + // matched denom + assert!(allowance.validate_new_inner(TEST_DENOM).is_ok()); + } + + #[test] + fn grant_must_be_non_zero() { + let mut allowance = mock_cumulative_periodic_allowance(); + + // zero amount + allowance.period_grant = coin(0, TEST_DENOM); + assert!(allowance.validate_new_inner(TEST_DENOM).is_err()); + + // non-zero amount + allowance.period_grant = coin(69, TEST_DENOM); + assert!(allowance.validate_new_inner(TEST_DENOM).is_ok()); + } + + #[test] + fn grant_amount_must_be_smaller_than_total_limit() { + let mut allowance = mock_cumulative_periodic_allowance(); + + let total_limit = coin(1000, TEST_DENOM); + allowance.basic.spend_limit = Some(total_limit); + allowance.accumulation_limit = None; + + // above total spend limit + allowance.period_grant = coin(1001, TEST_DENOM); + assert!(matches!( + allowance.validate_new_inner(TEST_DENOM).unwrap_err(), + NymPoolContractError::PeriodicGrantOverSpendLimit { .. } + )); + + // below total spend limit + allowance.period_grant = coin(999, TEST_DENOM); + assert!(allowance.validate_new_inner(TEST_DENOM).is_ok()); + + // equal to total spend limit + allowance.period_grant = coin(1000, TEST_DENOM); + assert!(allowance.validate_new_inner(TEST_DENOM).is_ok()); + + // no total spend limit + allowance.basic.spend_limit = None; + assert!(allowance.validate_new_inner(TEST_DENOM).is_ok()); + } + + #[test] + fn accumulation_limit_must_be_smaller_than_total_limit() { + let mut allowance = mock_cumulative_periodic_allowance(); + + let total_limit = coin(1000, TEST_DENOM); + allowance.basic.spend_limit = Some(total_limit.clone()); + allowance.period_grant = coin(500, TEST_DENOM); + + // above total spend limit + allowance.accumulation_limit = Some(coin(1001, TEST_DENOM)); + assert!(matches!( + allowance.validate_new_inner(TEST_DENOM).unwrap_err(), + NymPoolContractError::AccumulationOverSpendLimit { .. } + )); + + // below total spend limit + allowance.accumulation_limit = Some(coin(999, TEST_DENOM)); + assert!(allowance.validate_new_inner(TEST_DENOM).is_ok()); + + // equal to total spend limit + allowance.accumulation_limit = Some(coin(1000, TEST_DENOM)); + assert!(allowance.validate_new_inner(TEST_DENOM).is_ok()); + + // no total spend limit + allowance.basic.spend_limit = None; + assert!(allowance.validate_new_inner(TEST_DENOM).is_ok()); + + // no accumulation limit + allowance.accumulation_limit = None; + assert!(allowance.validate_new_inner(TEST_DENOM).is_ok()); + + // no accumulation limit but with spend limit + allowance.basic.spend_limit = Some(total_limit); + assert!(allowance.validate_new_inner(TEST_DENOM).is_ok()); + } + + #[test] + fn accumulation_limit_must_not_be_smaller_than_grant_amount() { + let mut allowance = mock_cumulative_periodic_allowance(); + + let total_limit = coin(1000, TEST_DENOM); + allowance.basic.spend_limit = Some(total_limit); + allowance.period_grant = coin(500, TEST_DENOM); + + // below grant amount + allowance.accumulation_limit = Some(coin(499, TEST_DENOM)); + assert!(matches!( + allowance.validate_new_inner(TEST_DENOM).unwrap_err(), + NymPoolContractError::AccumulationBelowGrantAmount { .. } + )); + + // above grant amount + allowance.accumulation_limit = Some(coin(501, TEST_DENOM)); + assert!(allowance.validate_new_inner(TEST_DENOM).is_ok()); + + // equal to grant amount + allowance.accumulation_limit = Some(coin(500, TEST_DENOM)); + assert!(allowance.validate_new_inner(TEST_DENOM).is_ok()); + + // no accumulation limit + allowance.accumulation_limit = None; + assert!(allowance.validate_new_inner(TEST_DENOM).is_ok()); + } + + #[test] + fn accumulation_limit_must_match_expected_denom() { + let mut allowance = mock_cumulative_periodic_allowance(); + allowance.accumulation_limit = Some(coin(1000, "baddenom")); + + // mismatched denom + assert!(allowance.validate_new_inner(TEST_DENOM).is_err()); + + // matched denom + allowance.accumulation_limit = Some(coin(1000, TEST_DENOM)); + assert!(allowance.validate_new_inner(TEST_DENOM).is_ok()); + } + } + + #[cfg(test)] + mod delayed_allowance { + use super::*; + use cosmwasm_std::testing::mock_env; + use cosmwasm_std::Timestamp; + + #[test] + fn doesnt_allow_availability_in_the_past() { + let allowance = mock_delayed_allowance(); + let mut env = mock_env(); + + // availability is in the past + env.block.time = Timestamp::from_seconds(allowance.available_at_unix_timestamp + 1); + assert!(allowance.validate_new_inner(&env).is_err()); + + // availability is equal to the current block time + env.block.time = Timestamp::from_seconds(allowance.available_at_unix_timestamp); + assert!(allowance.validate_new_inner(&env).is_ok()); + + // availability is in the future + env.block.time = Timestamp::from_seconds(allowance.available_at_unix_timestamp - 1); + assert!(allowance.validate_new_inner(&env).is_ok()); + } + + #[test] + fn must_have_available_before_allowance_expiration() { + let mut allowance = mock_delayed_allowance(); + let mut env = mock_env(); + env.block.time = Timestamp::from_seconds(100); + allowance.basic.expiration_unix_timestamp = Some(1000); + + // after expiration + allowance.available_at_unix_timestamp = 1001; + assert!(allowance.validate_new_inner(&env).is_err()); + + // equal to expiration + allowance.available_at_unix_timestamp = 1000; + assert!(allowance.validate_new_inner(&env).is_ok()); + + // before expiration + allowance.available_at_unix_timestamp = 999; + assert!(allowance.validate_new_inner(&env).is_ok()); + + // with no explicit expiration + allowance.basic.expiration_unix_timestamp = None; + assert!(allowance.validate_new_inner(&env).is_ok()); + } + } + } + + #[cfg(test)] + mod setting_initial_state { + use super::*; + use cosmwasm_std::testing::mock_env; + + #[test] + fn basic_allowance() { + let mut basic = Allowance::Basic(mock_basic_allowance()); + + let og = basic.clone(); + + // this is a no-op + let env = mock_env(); + basic.set_initial_state(&env); + assert_eq!(basic, og); + } + + #[test] + fn classic_periodic_allowance() { + let mut inner = mock_classic_periodic_allowance(); + let mut cumulative = Allowance::ClassicPeriodic(inner.clone()); + + let env = mock_env(); + + let mut expected = inner.clone(); + + // sets the spendable amount to min(basic_limit, period_limit) + expected.period_can_spend = Some(expected.period_spend_limit.clone()); + + // set period reset to current block time + period duration + expected.period_reset_unix_timestamp = + env.block.time.seconds() + expected.period_duration_secs; + + inner.set_initial_state(&env); + assert_eq!(inner, expected); + + cumulative.set_initial_state(&env); + assert_eq!(cumulative, Allowance::ClassicPeriodic(inner)); + } + + #[test] + fn cumulative_periodic_allowance() { + let mut inner = mock_cumulative_periodic_allowance(); + let mut cumulative = Allowance::CumulativePeriodic(inner.clone()); + + let env = mock_env(); + + // sets the last applied grant to current time and spendable to a single grant value + let mut expected = inner.clone(); + expected.last_grant_applied_unix_timestamp = env.block.time.seconds(); + expected.spendable = Some(expected.period_grant.clone()); + + inner.set_initial_state(&env); + assert_eq!(inner, expected); + + cumulative.set_initial_state(&env); + assert_eq!(cumulative, Allowance::CumulativePeriodic(inner)); + } + + #[test] + fn delayed_allowance() { + let mut delayed = Allowance::Delayed(mock_delayed_allowance()); + + let og = delayed.clone(); + + // this is a no-op + let env = mock_env(); + delayed.set_initial_state(&env); + assert_eq!(delayed, og); + } + } +} diff --git a/common/cosmwasm-smart-contracts/nym-pool-contract/src/utils.rs b/common/cosmwasm-smart-contracts/nym-pool-contract/src/utils.rs new file mode 100644 index 00000000000..0b6f5ba1f4c --- /dev/null +++ b/common/cosmwasm-smart-contracts/nym-pool-contract/src/utils.rs @@ -0,0 +1,77 @@ +// Copyright 2025 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::NymPoolContractError; +use cosmwasm_std::Env; + +pub fn ensure_unix_timestamp_not_in_the_past( + unix_timestamp: u64, + env: &Env, +) -> Result<(), NymPoolContractError> { + if unix_timestamp < env.block.time.seconds() { + return Err(NymPoolContractError::TimestampInThePast { + timestamp: unix_timestamp, + current_block_timestamp: env.block.time.seconds(), + }); + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use cosmwasm_std::testing::mock_env; + use cosmwasm_std::Timestamp; + use time::macros::datetime; + + #[test] + fn ensuring_unix_timestamp_not_in_the_past() { + let unix_epoch = 0; + + let date_in_the_past = datetime!(1984-01-02 3:45 UTC); + let sane_block_time = datetime!(2025-01-28 12:15 UTC); + + let before_block = datetime!(2025-01-28 12:00 UTC); + let after_block = datetime!(2025-01-28 12:30 UTC); + + let mut env = mock_env(); + env.block.time = Timestamp::from_seconds(sane_block_time.unix_timestamp() as u64); + + let res = ensure_unix_timestamp_not_in_the_past(unix_epoch, &env).unwrap_err(); + assert_eq!( + NymPoolContractError::TimestampInThePast { + timestamp: unix_epoch, + current_block_timestamp: env.block.time.seconds(), + }, + res + ); + + let res = + ensure_unix_timestamp_not_in_the_past(date_in_the_past.unix_timestamp() as u64, &env) + .unwrap_err(); + assert_eq!( + NymPoolContractError::TimestampInThePast { + timestamp: date_in_the_past.unix_timestamp() as u64, + current_block_timestamp: env.block.time.seconds(), + }, + res + ); + + let res = ensure_unix_timestamp_not_in_the_past(before_block.unix_timestamp() as u64, &env) + .unwrap_err(); + assert_eq!( + NymPoolContractError::TimestampInThePast { + timestamp: before_block.unix_timestamp() as u64, + current_block_timestamp: env.block.time.seconds(), + }, + res + ); + + let res = + ensure_unix_timestamp_not_in_the_past(sane_block_time.unix_timestamp() as u64, &env); + assert!(res.is_ok()); + + let res = ensure_unix_timestamp_not_in_the_past(after_block.unix_timestamp() as u64, &env); + assert!(res.is_ok()); + } +} diff --git a/common/credential-verification/src/ecash/state.rs b/common/credential-verification/src/ecash/state.rs index 4c718c3c30d..2c937fd6923 100644 --- a/common/credential-verification/src/ecash/state.rs +++ b/common/credential-verification/src/ecash/state.rs @@ -3,7 +3,7 @@ use crate::ecash::error::EcashTicketError; use crate::Error; -use cosmwasm_std::{from_binary, CosmosMsg, WasmMsg}; +use cosmwasm_std::{from_json, CosmosMsg, WasmMsg}; use nym_credentials_interface::VerificationKeyAuth; use nym_ecash_contract_common::msg::ExecuteMsg; use nym_gateway_storage::GatewayStorage; @@ -72,7 +72,7 @@ impl SharedState { let CosmosMsg::Wasm(WasmMsg::Execute { msg, .. }) = msg else { return false; }; - let Ok(ExecuteMsg::RedeemTickets { gw, .. }) = from_binary(msg) else { + let Ok(ExecuteMsg::RedeemTickets { gw, .. }) = from_json(msg) else { return false; }; diff --git a/common/dkg/Cargo.toml b/common/dkg/Cargo.toml index 799e38a7023..58e899d7b37 100644 --- a/common/dkg/Cargo.toml +++ b/common/dkg/Cargo.toml @@ -21,7 +21,7 @@ lazy_static = { workspace = true } rand = { workspace = true } rand_chacha = { workspace = true } rand_core = { workspace = true } -sha2 = "0.9" +sha2 = { workspace = true } serde = { workspace = true } serde_derive = { workspace = true } thiserror = { workspace = true } diff --git a/common/dkg/src/utils.rs b/common/dkg/src/utils.rs index 28fb99ad5d4..e654a83f8c1 100644 --- a/common/dkg/src/utils.rs +++ b/common/dkg/src/utils.rs @@ -54,12 +54,12 @@ pub(crate) fn hash_to_scalar>(msg: M, domain: &[u8]) -> Scalar { pub(crate) fn hash_to_scalars>(msg: M, domain: &[u8], n: usize) -> Vec { let mut output = vec![Scalar::zero(); n]; - Scalar::hash_to_field::>(msg.as_ref(), domain, &mut output); + Scalar::hash_to_field::, _>([msg], domain, &mut output); output } pub(crate) fn hash_g2>(msg: M, domain: &[u8]) -> G2Projective { - >>::hash_to_curve(msg, domain) + >>::hash_to_curve([msg], domain) } pub(crate) fn combine_scalar_chunks(chunks: &[Scalar]) -> Scalar { @@ -112,3 +112,97 @@ pub(crate) fn deserialize_g2(b: &[u8]) -> Option { G2Projective::from_bytes(&encoding).into() } } + +#[cfg(test)] +mod tests { + use super::*; + use bls12_381::G2Affine; + + #[test] + fn test_hash_to_scalar() { + let msg1 = "foo"; + let expected1 = Scalar::from_bytes(&[ + 253, 57, 224, 227, 175, 195, 226, 82, 46, 175, 33, 126, 171, 239, 255, 92, 108, 168, 6, + 79, 90, 11, 235, 236, 221, 10, 85, 133, 42, 81, 95, 30, + ]) + .unwrap(); + + let msg2 = "bar"; + let expected2 = Scalar::from_bytes(&[ + 48, 83, 69, 52, 42, 18, 135, 244, 211, 190, 160, 196, 118, 154, 24, 126, 0, 125, 72, + 201, 170, 225, 123, 201, 52, 120, 171, 132, 235, 182, 20, 26, + ]) + .unwrap(); + + let msg3 = [ + 33, 135, 76, 234, 71, 35, 247, 216, 39, 242, 42, 88, 152, 29, 74, 135, 9, 29, 216, 123, + 250, 87, 108, 29, 245, 126, 109, 102, 84, 71, 158, 224, 145, 243, 49, 121, 244, 27, + 115, 121, 25, 66, 216, 67, 97, 101, 140, 160, 77, 239, 114, 215, 152, 48, 15, 231, 101, + 60, 42, 92, 128, 131, 161, 43, + ]; + let expected3 = Scalar::from_bytes(&[ + 128, 189, 8, 43, 186, 55, 52, 61, 171, 196, 159, 177, 162, 100, 27, 143, 85, 83, 218, + 171, 91, 220, 155, 25, 7, 38, 2, 36, 4, 93, 136, 4, + ]) + .unwrap(); + + assert_eq!( + hash_to_scalar(msg1, b"NYMECASH-V01-CS02-with-expander-SHA256"), + expected1 + ); + assert_eq!( + hash_to_scalar(msg2, b"NYMECASH-V01-CS02-with-expander-SHA256"), + expected2 + ); + assert_eq!( + hash_to_scalar(msg3, b"NYMECASH-V01-CS02-with-expander-SHA256"), + expected3 + ); + } + + #[test] + fn test_hash_g2() { + let msg1 = "foo"; + let expected1 = G2Affine::from_compressed(&[ + 175, 187, 62, 7, 29, 17, 42, 93, 28, 93, 234, 253, 101, 166, 158, 187, 153, 82, 93, 18, + 11, 233, 36, 107, 51, 117, 30, 127, 32, 254, 210, 77, 133, 12, 253, 255, 84, 128, 36, + 214, 234, 103, 50, 21, 26, 78, 112, 49, 20, 69, 19, 109, 7, 78, 33, 227, 196, 180, 168, + 219, 73, 251, 192, 221, 41, 138, 160, 131, 191, 186, 156, 117, 179, 179, 191, 235, 171, + 26, 219, 148, 170, 179, 11, 38, 137, 14, 95, 115, 171, 186, 163, 82, 158, 6, 239, 88, + ]) + .unwrap() + .into(); + + let msg2 = "bar"; + let expected2 = G2Affine::from_compressed(&[ + 183, 25, 90, 187, 34, 184, 30, 182, 215, 242, 158, 83, 116, 34, 210, 96, 188, 79, 83, + 255, 100, 122, 90, 188, 196, 93, 164, 253, 20, 106, 205, 33, 48, 140, 60, 149, 66, 246, + 121, 244, 146, 66, 170, 60, 113, 95, 102, 237, 25, 231, 8, 42, 121, 124, 180, 140, 34, + 104, 173, 251, 89, 189, 28, 196, 49, 66, 101, 38, 68, 44, 40, 235, 21, 35, 204, 123, + 218, 238, 216, 92, 134, 217, 212, 246, 176, 77, 187, 0, 245, 134, 132, 73, 31, 44, 137, + 197, + ]) + .unwrap() + .into(); + let msg3 = [ + 33, 135, 76, 234, 71, 35, 247, 216, 39, 242, 42, 88, 152, 29, 74, 135, 9, 29, 216, 123, + 250, 87, 108, 29, 245, 126, 109, 102, 84, 71, 158, 224, 145, 243, 49, 121, 244, 27, + 115, 121, 25, 66, 216, 67, 97, 101, 140, 160, 77, 239, 114, 215, 152, 48, 15, 231, 101, + 60, 42, 92, 128, 131, 161, 43, + ]; + let expected3 = G2Affine::from_compressed(&[ + 151, 185, 8, 123, 223, 150, 192, 192, 115, 10, 3, 129, 49, 179, 31, 108, 0, 17, 46, + 231, 184, 164, 247, 228, 22, 142, 87, 70, 120, 111, 154, 15, 245, 110, 32, 84, 53, 117, + 239, 93, 89, 119, 32, 17, 39, 250, 198, 137, 6, 95, 137, 202, 54, 244, 238, 190, 11, + 217, 237, 95, 72, 59, 140, 56, 3, 42, 61, 195, 192, 101, 46, 204, 207, 75, 70, 176, + 207, 48, 24, 195, 248, 234, 178, 168, 54, 109, 19, 189, 51, 52, 120, 69, 248, 226, 102, + 91, + ]) + .unwrap() + .into(); + + assert_eq!(hash_g2(msg1, b"DUMMY_TEST_DOMAIN"), expected1); + assert_eq!(hash_g2(msg2, b"DUMMY_TEST_DOMAIN"), expected2); + assert_eq!(hash_g2(msg3, b"DUMMY_TEST_DOMAIN"), expected3); + } +} diff --git a/common/nym_offline_compact_ecash/Cargo.toml b/common/nym_offline_compact_ecash/Cargo.toml index 9b29d818fe9..718787b457c 100644 --- a/common/nym_offline_compact_ecash/Cargo.toml +++ b/common/nym_offline_compact_ecash/Cargo.toml @@ -15,10 +15,10 @@ bls12_381 = { workspace = true, features = ["alloc", "pairings", "experimental", bincode.workspace = true cfg-if.workspace = true itertools = { workspace = true } -digest = "0.9" +digest = { workspace = true } rand = { workspace = true } thiserror = { workspace = true } -sha2 = "0.9" +sha2 = { workspace = true } bs58 = { workspace = true } serde = { workspace = true, features = ["derive"] } rayon = { workspace = true, optional = true } diff --git a/common/nym_offline_compact_ecash/src/utils.rs b/common/nym_offline_compact_ecash/src/utils.rs index 57a648fff24..9042975dec4 100644 --- a/common/nym_offline_compact_ecash/src/utils.rs +++ b/common/nym_offline_compact_ecash/src/utils.rs @@ -113,17 +113,13 @@ const G1_HASH_DOMAIN: &[u8] = b"NYMECASH-V01-CS02-with-BLS12381G1_XMD:SHA-256_SS const SCALAR_HASH_DOMAIN: &[u8] = b"NYMECASH-V01-CS02-with-expander-SHA256"; pub fn hash_g1>(msg: M) -> G1Projective { - >>::hash_to_curve(msg, G1_HASH_DOMAIN) + >>::hash_to_curve([msg], G1_HASH_DOMAIN) } pub fn hash_to_scalar>(msg: M) -> Scalar { let mut output = vec![Scalar::zero()]; - Scalar::hash_to_field::>( - msg.as_ref(), - SCALAR_HASH_DOMAIN, - &mut output, - ); + Scalar::hash_to_field::, _>([msg], SCALAR_HASH_DOMAIN, &mut output); output[0] } @@ -273,9 +269,8 @@ pub fn check_vk_pairing( #[cfg(test)] mod tests { - use rand::RngCore; - use super::*; + use rand::RngCore; #[test] fn polynomial_evaluation() { @@ -401,4 +396,75 @@ mod tests { assert_eq!(hash_to_scalar(msg2), hash_to_scalar(msg2)); assert_ne!(hash_to_scalar(msg1), hash_to_scalar(msg2)); } + + #[test] + fn test_hash_to_scalar() { + let msg1 = "foo"; + let expected1 = Scalar::from_bytes(&[ + 253, 57, 224, 227, 175, 195, 226, 82, 46, 175, 33, 126, 171, 239, 255, 92, 108, 168, 6, + 79, 90, 11, 235, 236, 221, 10, 85, 133, 42, 81, 95, 30, + ]) + .unwrap(); + + let msg2 = "bar"; + let expected2 = Scalar::from_bytes(&[ + 48, 83, 69, 52, 42, 18, 135, 244, 211, 190, 160, 196, 118, 154, 24, 126, 0, 125, 72, + 201, 170, 225, 123, 201, 52, 120, 171, 132, 235, 182, 20, 26, + ]) + .unwrap(); + + let msg3 = [ + 33, 135, 76, 234, 71, 35, 247, 216, 39, 242, 42, 88, 152, 29, 74, 135, 9, 29, 216, 123, + 250, 87, 108, 29, 245, 126, 109, 102, 84, 71, 158, 224, 145, 243, 49, 121, 244, 27, + 115, 121, 25, 66, 216, 67, 97, 101, 140, 160, 77, 239, 114, 215, 152, 48, 15, 231, 101, + 60, 42, 92, 128, 131, 161, 43, + ]; + let expected3 = Scalar::from_bytes(&[ + 128, 189, 8, 43, 186, 55, 52, 61, 171, 196, 159, 177, 162, 100, 27, 143, 85, 83, 218, + 171, 91, 220, 155, 25, 7, 38, 2, 36, 4, 93, 136, 4, + ]) + .unwrap(); + + assert_eq!(hash_to_scalar(msg1), expected1); + assert_eq!(hash_to_scalar(msg2), expected2); + assert_eq!(hash_to_scalar(msg3), expected3); + } + + #[test] + fn test_hash_to_g1() { + let msg1 = "foo"; + let expected1 = G1Affine::from_compressed(&[ + 161, 109, 186, 0, 192, 221, 83, 87, 71, 31, 120, 201, 185, 35, 62, 239, 46, 120, 117, + 150, 191, 227, 128, 161, 78, 201, 207, 167, 86, 181, 229, 115, 2, 6, 178, 16, 251, 118, + 219, 115, 184, 96, 2, 10, 31, 63, 150, 70, + ]) + .unwrap() + .into(); + + let msg2 = "bar"; + let expected2 = G1Affine::from_compressed(&[ + 135, 102, 204, 42, 221, 49, 209, 192, 250, 87, 59, 255, 197, 93, 37, 113, 38, 2, 154, + 233, 68, 234, 206, 182, 121, 212, 166, 210, 74, 155, 190, 33, 203, 237, 176, 60, 249, + 241, 53, 170, 18, 168, 49, 35, 1, 151, 205, 174, + ]) + .unwrap() + .into(); + let msg3 = [ + 33, 135, 76, 234, 71, 35, 247, 216, 39, 242, 42, 88, 152, 29, 74, 135, 9, 29, 216, 123, + 250, 87, 108, 29, 245, 126, 109, 102, 84, 71, 158, 224, 145, 243, 49, 121, 244, 27, + 115, 121, 25, 66, 216, 67, 97, 101, 140, 160, 77, 239, 114, 215, 152, 48, 15, 231, 101, + 60, 42, 92, 128, 131, 161, 43, + ]; + let expected3 = G1Affine::from_compressed(&[ + 184, 200, 211, 115, 47, 45, 39, 185, 105, 9, 222, 247, 132, 241, 121, 130, 238, 224, + 155, 109, 105, 201, 137, 154, 132, 149, 214, 233, 136, 69, 77, 132, 174, 30, 46, 123, + 20, 92, 219, 18, 45, 29, 208, 127, 158, 145, 130, 41, + ]) + .unwrap() + .into(); + + assert_eq!(hash_g1(msg1), expected1); + assert_eq!(hash_g1(msg2), expected2); + assert_eq!(hash_g1(msg3), expected3); + } } diff --git a/common/nymcoconut/Cargo.toml b/common/nymcoconut/Cargo.toml index c4266b3459f..59e516da93b 100644 --- a/common/nymcoconut/Cargo.toml +++ b/common/nymcoconut/Cargo.toml @@ -10,13 +10,13 @@ license.workspace = true [dependencies] bls12_381 = { workspace = true, default-features = false, features = ["pairings", "alloc", "experimental"] } itertools = { workspace = true } -digest = "0.9" +digest = { workspace = true } rand = { workspace = true } thiserror = { workspace = true } serde = { workspace = true } serde_derive = { workspace = true } bs58 = { workspace = true } -sha2 = "0.9" +sha2 = { workspace = true } zeroize = { workspace = true, optional = true } nym-dkg = { path = "../dkg" } @@ -45,4 +45,4 @@ default = [] [target.'cfg(target_env = "wasm32-unknown-unknown")'.dependencies] -getrandom = { version="0.2", features=["js"] } +getrandom = { version = "0.2", features = ["js"] } diff --git a/common/nymcoconut/src/utils.rs b/common/nymcoconut/src/utils.rs index d12707408c2..89db776ff80 100644 --- a/common/nymcoconut/src/utils.rs +++ b/common/nymcoconut/src/utils.rs @@ -123,17 +123,13 @@ const G1_HASH_DOMAIN: &[u8] = b"QUUX-V01-CS02-with-BLS12381G1_XMD:SHA-256_SSWU_R const SCALAR_HASH_DOMAIN: &[u8] = b"QUUX-V01-CS02-with-expander"; pub fn hash_g1>(msg: M) -> G1Projective { - >>::hash_to_curve(msg, G1_HASH_DOMAIN) + >>::hash_to_curve([msg], G1_HASH_DOMAIN) } pub fn hash_to_scalar>(msg: M) -> Scalar { let mut output = vec![Scalar::zero()]; - Scalar::hash_to_field::>( - msg.as_ref(), - SCALAR_HASH_DOMAIN, - &mut output, - ); + Scalar::hash_to_field::, _>([msg], SCALAR_HASH_DOMAIN, &mut output); output[0] } diff --git a/contracts/Cargo.lock b/contracts/Cargo.lock index 7a2e25bfb62..dea0392315c 100644 --- a/contracts/Cargo.lock +++ b/contracts/Cargo.lock @@ -4,13 +4,14 @@ version = 4 [[package]] name = "ahash" -version = "0.7.8" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ - "getrandom", + "cfg-if", "once_cell", "version_check", + "zerocopy", ] [[package]] @@ -22,6 +23,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "anyhow" version = "1.0.95" @@ -29,10 +36,131 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] -name = "base16ct" -version = "0.1.1" +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", + "num-traits", + "rayon", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rayon", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", + "rayon", +] + +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "base16ct" @@ -52,6 +180,12 @@ 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.6.0" @@ -59,13 +193,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] -name = "block-buffer" -version = "0.9.0" +name = "bech32" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] +checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" [[package]] name = "block-buffer" @@ -78,15 +209,9 @@ dependencies = [ [[package]] name = "bnum" -version = "0.8.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab9008b6bb9fc80b5277f2fe481c09e828743d9151203e804583eb4c9e15b31d" - -[[package]] -name = "bs58" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" +checksum = "3e31ea183f6ee62ac8b8a8cf7feddd766317adfb13ff469de57ce033efd6a790" [[package]] name = "bs58" @@ -105,15 +230,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "camino" -version = "1.1.9" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" dependencies = [ "serde", ] @@ -138,7 +263,7 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 1.0.64", ] [[package]] @@ -147,34 +272,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "coconut-test" -version = "0.1.0" -dependencies = [ - "bs58 0.4.0", - "cosmwasm-std", - "cosmwasm-storage", - "cw-controllers", - "cw-multi-test", - "cw-storage-plus", - "cw-utils", - "cw3", - "cw3-flex-multisig", - "cw4", - "cw4-group", - "nym-coconut-bandwidth", - "nym-coconut-bandwidth-contract-common", - "nym-coconut-dkg", - "nym-coconut-dkg-common", - "nym-group-contract-common", - "nym-multisig-contract-common", - "rand_chacha", - "schemars", - "serde", - "subtle-encoding", - "thiserror 1.0.69", -] - [[package]] name = "const-oid" version = "0.9.6" @@ -183,9 +280,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const_panic" -version = "0.2.12" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2459fc9262a1aa204eb4b5764ad4f189caec88aea9634389c0a25f8be7f6265e" +checksum = "6051f239ecec86fde3410901ab7860d458d160371533842974fc61f96d15879b" [[package]] name = "convert_case" @@ -197,102 +294,128 @@ dependencies = [ ] [[package]] -name = "cosmwasm-crypto" -version = "1.4.3" +name = "cosmwasm-core" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533b66e502ecab30fec23d03eb54ab1d3ce120645a00493459f8510b7a9736f" -dependencies = [ - "digest 0.10.7", +checksum = "de32156e4fd80c59be39ed6f4ebb596d59b0a4eaf01d6f146e27628ec7e8f8c1" + +[[package]] +name = "cosmwasm-crypto" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38fe1e6107ae3c9ba5e1f14178dd8bd52210535030d07f0609cf0d754c1f7de2" +dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-serialize", + "cosmwasm-core", + "curve25519-dalek", + "digest", + "ecdsa", "ed25519-zebra", - "k256 0.13.4", - "rand_core 0.6.4", - "thiserror 1.0.69", + "k256", + "num-traits", + "p256", + "rand_core", + "rayon", + "sha2", + "thiserror 1.0.64", ] [[package]] name = "cosmwasm-derive" -version = "1.4.3" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a2c11de93c565a993b2b687469789146808970b6cfabcd0d44a8915f96da67" +checksum = "484926c9dc8b90c59a717946c86bb272182003cbaabb378560086648d4056656" dependencies = [ - "syn 1.0.109", + "proc-macro2", + "quote", + "syn 2.0.98", ] [[package]] name = "cosmwasm-schema" -version = "1.4.3" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ae2e971fb831d0c4fa3c8c3d2291cdbdd73786a73d65196dbf983d9b2468af" +checksum = "d2a25988c48703d1450a5ac5e7cd3ad82ec8a7552f3dde8f9b8927e682bd02c7" dependencies = [ "cosmwasm-schema-derive", "schemars", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 1.0.64", ] [[package]] name = "cosmwasm-schema-derive" -version = "1.4.3" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cadc57fd0825b85bc2f9b972c17da718b9efb4bc17e5935cc2d6036324f853d" +checksum = "bbd08fac60d30e341d9365033f519c0a36fdf38bde6ec196179e653d2723aebd" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.98", ] [[package]] name = "cosmwasm-std" -version = "1.4.3" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e98e19fae6c3f468412f731274b0f9434602722009d6a77432d39c7c4bb09202" +checksum = "c92be4747d9abe3a96a5a78af34d29947992b3f67f602987ff8a87142ce9c413" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", + "bech32", "bnum", + "cosmwasm-core", "cosmwasm-crypto", "cosmwasm-derive", - "derivative", - "forward_ref", + "derive_more", "hex", + "rand_core", + "rmp-serde", "schemars", "serde", "serde-json-wasm", - "sha2 0.10.8", - "thiserror 1.0.69", + "sha2", + "static_assertions", + "thiserror 1.0.64", ] [[package]] -name = "cosmwasm-storage" -version = "1.4.3" +name = "cpufeatures" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8935079712688b8d8d4c036378b38b49d13692621c6fcba96700fadfd5126a18" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ - "cosmwasm-std", - "serde", + "libc", ] [[package]] -name = "cpufeatures" -version = "0.2.17" +name = "crossbeam-deque" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ - "libc", + "crossbeam-epoch", + "crossbeam-utils", ] [[package]] -name = "crypto-bigint" -version = "0.4.9" +name = "crossbeam-epoch" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", + "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" @@ -300,7 +423,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", ] @@ -315,19 +438,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" @@ -337,7 +447,7 @@ dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", - "digest 0.10.7", + "digest", "fiat-crypto", "rustc_version", "subtle", @@ -357,9 +467,9 @@ dependencies = [ [[package]] name = "cw-controllers" -version = "1.1.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d8edce4b78785f36413f67387e4be7d0cb7d032b5d4164bcc024f9c3f3f2ea" +checksum = "50c1804013d21060b994dea28a080f9eab78a3bcb6b617f05e7634b0600bf7b1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -367,33 +477,34 @@ dependencies = [ "cw-utils", "schemars", "serde", - "thiserror 1.0.69", + "thiserror 1.0.64", ] [[package]] name = "cw-multi-test" -version = "0.16.5" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "127c7bb95853b8e828bdab97065c81cb5ddc20f7339180b61b2300565aaa99d1" +checksum = "8b00c3a218ed6abc6f1dd9be8146d4f23d8f41cf4e6d53cdfa71bab4514e1fc3" dependencies = [ "anyhow", + "bech32", + "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "cw-utils", - "derivative", - "itertools", - "k256 0.11.6", + "itertools 0.14.0", "prost", "schemars", "serde", - "thiserror 1.0.69", + "sha2", + "thiserror 2.0.11", ] [[package]] name = "cw-storage-plus" -version = "1.2.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5ff29294ee99373e2cd5fd21786a3c0ced99a52fec2ca347d565489c61b723c" +checksum = "f13360e9007f51998d42b1bc6b7fa0141f74feae61ed5fd1e5b0a89eec7b5de1" dependencies = [ "cosmwasm-std", "schemars", @@ -402,24 +513,22 @@ dependencies = [ [[package]] name = "cw-utils" -version = "1.0.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c80e93d1deccb8588db03945016a292c3c631e6325d349ebb35d2db6f4f946f7" +checksum = "07dfee7f12f802431a856984a32bce1cb7da1e6c006b5409e3981035ce562dec" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw2", "schemars", - "semver", "serde", - "thiserror 1.0.69", + "thiserror 1.0.64", ] [[package]] name = "cw2" -version = "1.1.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c120b24fbbf5c3bedebb97f2cc85fbfa1c3287e09223428e7e597b5293c1fa" +checksum = "b04852cd38f044c0751259d5f78255d07590d136b8a86d4e09efdd7666bd6d27" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -427,14 +536,14 @@ dependencies = [ "schemars", "semver", "serde", - "thiserror 1.0.69", + "thiserror 1.0.64", ] [[package]] name = "cw20" -version = "1.1.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "526e39bb20534e25a1cd0386727f0038f4da294e5e535729ba3ef54055246abd" +checksum = "a42212b6bf29bbdda693743697c621894723f35d3db0d5df930be22903d0e27c" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -445,9 +554,9 @@ dependencies = [ [[package]] name = "cw20-base" -version = "1.1.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ad79e86ea3707229bf78df94e08732e8f713207b4a77b2699755596725e7d9" +checksum = "d6de8c32e100f1fca306972d86b617234a5e6b00594ea2b48716fd6804d4d95d" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -457,14 +566,14 @@ dependencies = [ "schemars", "semver", "serde", - "thiserror 1.0.69", + "thiserror 1.0.64", ] [[package]] name = "cw3" -version = "1.1.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2967fbd073d4b626dd9e7148e05a84a3bebd9794e71342e12351110ffbb12395" +checksum = "d5e53c2057526c65d9c88be8b2a564729ebad7a3d87ee97b97665a71446f913a" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -472,14 +581,14 @@ dependencies = [ "cw20", "schemars", "serde", - "thiserror 1.0.69", + "thiserror 1.0.64", ] [[package]] name = "cw3-fixed-multisig" -version = "1.1.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58d7480e78644625c38bb082bfe9a27b01ca58bfcb785690ef43363fd40acf51" +checksum = "9a8233125653e61e898eaade6c6fdb3bd9c48aceb2ad97e84eada2c9bf5bff46" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -489,12 +598,12 @@ dependencies = [ "cw3", "schemars", "serde", - "thiserror 1.0.69", + "thiserror 1.0.64", ] [[package]] name = "cw3-flex-multisig" -version = "1.0.0" +version = "2.0.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -508,6 +617,7 @@ dependencies = [ "cw3-fixed-multisig", "cw4", "cw4-group", + "easy-addr", "nym-contracts-common", "nym-group-contract-common", "nym-multisig-contract-common", @@ -515,9 +625,9 @@ dependencies = [ [[package]] name = "cw4" -version = "1.1.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24754ff6e45f2a1c60adc409d9b2eb87666012c44021329141ffaab3388fccd2" +checksum = "d33f5c8a6b6cd1bd24e212d7f44967697bfa3c4f9cc3f9a8e1c58f5fe5db032d" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -528,7 +638,7 @@ dependencies = [ [[package]] name = "cw4-group" -version = "1.0.0" +version = "2.0.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -537,21 +647,12 @@ dependencies = [ "cw-utils", "cw2", "cw4", + "easy-addr", "nym-contracts-common", "nym-group-contract-common", "schemars", "serde", - "thiserror 1.0.69", -] - -[[package]] -name = "der" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" -dependencies = [ - "const-oid", - "zeroize", + "thiserror 2.0.11", ] [[package]] @@ -585,12 +686,24 @@ dependencies = [ ] [[package]] -name = "digest" -version = "0.9.0" +name = "derive_more" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" dependencies = [ - "generic-array", + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", + "unicode-xid", ] [[package]] @@ -599,7 +712,7 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.4", + "block-buffer", "const-oid", "crypto-common", "subtle", @@ -607,20 +720,17 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.18" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feeef44e73baff3a26d371801df019877a9866a8c493d315ab00177843314f35" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] -name = "ecdsa" -version = "0.14.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +name = "easy-addr" +version = "0.1.0" dependencies = [ - "der 0.6.1", - "elliptic-curve 0.12.3", - "rfc6979 0.3.1", - "signature 1.6.4", + "cosmwasm-std", + "quote", + "syn 1.0.109", ] [[package]] @@ -629,12 +739,11 @@ version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der 0.7.9", - "digest 0.10.7", - "elliptic-curve 0.13.8", - "rfc6979 0.4.0", - "signature 2.2.0", - "spki 0.7.3", + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", ] [[package]] @@ -643,8 +752,8 @@ version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ - "pkcs8 0.10.2", - "signature 2.2.0", + "pkcs8", + "signature", ] [[package]] @@ -653,55 +762,35 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ - "curve25519-dalek 4.1.3", + "curve25519-dalek", "ed25519", - "rand_core 0.6.4", + "rand_core", "serde", - "sha2 0.10.8", + "sha2", "subtle", "zeroize", ] [[package]] name = "ed25519-zebra" -version = "3.1.0" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +checksum = "7d9ce6874da5d4415896cd45ffbc4d1cfc0c4f9c079427bd870742c30f2f65a9" dependencies = [ - "curve25519-dalek 3.2.0", - "hashbrown 0.12.3", + "curve25519-dalek", + "ed25519", + "hashbrown 0.14.5", "hex", - "rand_core 0.6.4", - "serde", - "sha2 0.9.9", + "rand_core", + "sha2", "zeroize", ] [[package]] name = "either" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" - -[[package]] -name = "elliptic-curve" -version = "0.12.3" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" -dependencies = [ - "base16ct 0.1.1", - "crypto-bigint 0.4.9", - "der 0.6.1", - "digest 0.10.7", - "ff 0.12.1", - "generic-array", - "group 0.12.1", - "pkcs8 0.9.0", - "rand_core 0.6.4", - "sec1 0.3.0", - "subtle", - "zeroize", -] +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "elliptic-curve" @@ -709,34 +798,23 @@ version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ - "base16ct 0.2.0", - "crypto-bigint 0.5.5", - "digest 0.10.7", - "ff 0.13.0", + "base16ct", + "crypto-bigint", + "digest", + "ff", "generic-array", - "group 0.13.0", - "pkcs8 0.10.2", - "rand_core 0.6.4", - "sec1 0.7.3", + "group", + "rand_core", + "sec1", "subtle", "zeroize", ] [[package]] name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "ff" -version = "0.12.1" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "ff" @@ -744,21 +822,15 @@ 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", ] [[package]] name = "fiat-crypto" -version = "0.2.9" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - -[[package]] -name = "forward_ref" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" +checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f" [[package]] name = "generic-array" @@ -773,9 +845,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "libc", @@ -784,33 +856,32 @@ dependencies = [ [[package]] name = "group" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ - "ff 0.12.1", - "rand_core 0.6.4", + "ff", + "rand_core", "subtle", ] [[package]] -name = "group" -version = "0.13.0" +name = "hashbrown" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ff 0.13.0", - "rand_core 0.6.4", - "subtle", + "ahash", ] [[package]] name = "hashbrown" -version = "0.12.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", + "allocator-api2", ] [[package]] @@ -831,7 +902,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.7", + "digest", ] [[package]] @@ -870,35 +941,39 @@ dependencies = [ ] [[package]] -name = "itoa" -version = "1.0.14" +name = "itertools" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] [[package]] -name = "k256" -version = "0.11.6" +name = "itertools" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ - "cfg-if", - "ecdsa 0.14.8", - "elliptic-curve 0.12.3", - "sha2 0.10.8", + "either", ] +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + [[package]] name = "k256" -version = "0.13.4" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" dependencies = [ "cfg-if", - "ecdsa 0.16.9", - "elliptic-curve 0.13.8", - "once_cell", - "sha2 0.10.8", - "signature 2.2.0", + "ecdsa", + "elliptic-curve", + "sha2", ] [[package]] @@ -930,22 +1005,21 @@ checksum = "00af7901ba50898c9e545c24d5c580c96a982298134e8037d8978b6594782c07" [[package]] name = "libc" -version = "0.2.169" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "mixnet-vesting-integration-tests" version = "0.1.0" dependencies = [ "cosmwasm-std", - "cosmwasm-storage", "cw-multi-test", "nym-contracts-common", "nym-crypto", @@ -956,6 +1030,16 @@ dependencies = [ "rand_chacha", ] +[[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-conv" version = "0.1.0" @@ -963,37 +1047,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] -name = "num_threads" -version = "0.1.7" +name = "num-integer" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "libc", + "num-traits", ] [[package]] -name = "nym-coconut-bandwidth" -version = "0.1.0" +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cosmwasm-storage", - "cw-controllers", - "cw-storage-plus", - "nym-coconut-bandwidth-contract-common", - "nym-multisig-contract-common", - "serde", - "thiserror 1.0.69", + "autocfg", ] [[package]] -name = "nym-coconut-bandwidth-contract-common" -version = "0.1.0" +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw2", - "nym-multisig-contract-common", + "libc", ] [[package]] @@ -1002,18 +1079,17 @@ version = "0.1.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cosmwasm-storage", "cw-controllers", "cw-multi-test", "cw-storage-plus", "cw2", "cw4", "cw4-group", + "easy-addr", "nym-coconut-dkg-common", "nym-contracts-common", "nym-group-contract-common", - "serde", - "thiserror 1.0.69", + "thiserror 2.0.11", ] [[package]] @@ -1033,7 +1109,7 @@ dependencies = [ name = "nym-contracts-common" version = "0.5.0" dependencies = [ - "bs58 0.5.1", + "bs58", "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", @@ -1047,7 +1123,7 @@ dependencies = [ name = "nym-crypto" version = "0.4.0" dependencies = [ - "bs58 0.5.1", + "bs58", "ed25519-dalek", "nym-pemstore", "nym-sphinx-types", @@ -1063,11 +1139,11 @@ name = "nym-ecash" version = "0.1.0" dependencies = [ "anyhow", - "bs58 0.4.0", + "bs58", "cosmwasm-schema", "cosmwasm-std", - "cosmwasm-storage", "cw-controllers", + "cw-multi-test", "cw-storage-plus", "cw-utils", "cw2", @@ -1083,14 +1159,14 @@ dependencies = [ "semver", "serde", "sylvia", - "thiserror 1.0.69", + "thiserror 2.0.11", ] [[package]] name = "nym-ecash-contract-common" version = "0.1.0" dependencies = [ - "bs58 0.5.1", + "bs58", "cosmwasm-schema", "cosmwasm-std", "cw-controllers", @@ -1116,14 +1192,14 @@ name = "nym-mixnet-contract" version = "1.5.1" dependencies = [ "anyhow", - "bs58 0.4.0", + "bs58", "cosmwasm-derive", "cosmwasm-schema", "cosmwasm-std", - "cosmwasm-storage", "cw-controllers", "cw-storage-plus", "cw2", + "easy-addr", "nym-contracts-common", "nym-crypto", "nym-mixnet-contract-common", @@ -1132,7 +1208,7 @@ dependencies = [ "rand_chacha", "semver", "serde", - "thiserror 1.0.69", + "thiserror 2.0.11", "time", ] @@ -1140,7 +1216,7 @@ dependencies = [ name = "nym-mixnet-contract-common" version = "0.6.0" dependencies = [ - "bs58 0.5.1", + "bs58", "cosmwasm-schema", "cosmwasm-std", "cw-controllers", @@ -1187,6 +1263,35 @@ dependencies = [ "pem", ] +[[package]] +name = "nym-pool-contract" +version = "0.1.0" +dependencies = [ + "anyhow", + "cosmwasm-std", + "cw-controllers", + "cw-multi-test", + "cw-storage-plus", + "cw2", + "nym-contracts-common", + "nym-pool-contract-common", + "rand", + "rand_chacha", + "serde", +] + +[[package]] +name = "nym-pool-contract-common" +version = "0.1.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-controllers", + "schemars", + "serde", + "thiserror 2.0.11", +] + [[package]] name = "nym-sphinx-types" version = "0.2.0" @@ -1212,7 +1317,7 @@ dependencies = [ "rand_chacha", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.11", ] [[package]] @@ -1230,15 +1335,27 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.3" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] -name = "opaque-debug" -version = "0.3.1" +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "paste" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pem" @@ -1251,24 +1368,14 @@ dependencies = [ "regex", ] -[[package]] -name = "pkcs8" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" -dependencies = [ - "der 0.6.1", - "spki 0.6.0", -] - [[package]] name = "pkcs8" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.9", - "spki 0.7.3", + "der", + "spki", ] [[package]] @@ -1279,20 +1386,25 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" dependencies = [ - "zerocopy", + "elliptic-curve", ] [[package]] name = "proc-macro-crate" -version = "1.3.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "once_cell", "toml_edit", ] @@ -1331,9 +1443,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.9.0" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" dependencies = [ "bytes", "prost-derive", @@ -1341,15 +1453,15 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.9.0" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools", + "itertools 0.14.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.98", ] [[package]] @@ -1369,7 +1481,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core 0.6.4", + "rand_core", ] [[package]] @@ -1379,15 +1491,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" @@ -1397,6 +1503,26 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "regex" version = "1.11.1" @@ -1428,45 +1554,56 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rfc6979" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "crypto-bigint 0.4.9", "hmac", - "zeroize", + "subtle", ] [[package]] -name = "rfc6979" -version = "0.4.0" +name = "rmp" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" dependencies = [ - "hmac", - "subtle", + "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" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ "semver", ] [[package]] name = "rustversion" -version = "1.0.19" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" [[package]] name = "ryu" -version = "1.0.19" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "schemars" @@ -1492,30 +1629,15 @@ dependencies = [ "syn 2.0.98", ] -[[package]] -name = "sec1" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" -dependencies = [ - "base16ct 0.1.1", - "der 0.6.1", - "generic-array", - "pkcs8 0.9.0", - "subtle", - "zeroize", -] - [[package]] name = "sec1" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ - "base16ct 0.2.0", - "der 0.7.9", + "base16ct", + "der", "generic-array", - "pkcs8 0.10.2", "subtle", "zeroize", ] @@ -1549,9 +1671,9 @@ dependencies = [ [[package]] name = "serde-json-wasm" -version = "0.5.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15bee9b04dd165c3f4e142628982ddde884c2022a89e8ddf99c4829bf2c3a58" +checksum = "f05da0d153dd4595bdffd5099dc0e9ce425b205ee648eb93437ff7302af8c9a5" dependencies = [ "serde", ] @@ -1580,9 +1702,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.138" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", "memchr", @@ -1601,19 +1723,6 @@ dependencies = [ "syn 2.0.98", ] -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "sha2" version = "0.10.8" @@ -1622,17 +1731,7 @@ checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.7", -] - -[[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" -dependencies = [ - "digest 0.10.7", - "rand_core 0.6.4", + "digest", ] [[package]] @@ -1641,35 +1740,31 @@ 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]] name = "spki" -version = "0.6.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der 0.6.1", + "der", ] [[package]] -name = "spki" -version = "0.7.3" +name = "static_assertions" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der 0.7.9", -] +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "subtle" -version = "2.6.1" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "subtle-encoding" @@ -1682,15 +1777,15 @@ dependencies = [ [[package]] name = "sylvia" -version = "0.8.1" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f33388920659b494dab887f3bb40ebb071c602750597575034bea7c63ab12800" +checksum = "6af3b8ce8e43dee3c919b23c720b1da1c3020ebebf421d0d1927ce9bfa51a15d" dependencies = [ "anyhow", "cosmwasm-schema", "cosmwasm-std", "cw-multi-test", - "derivative", + "cw-utils", "konst", "schemars", "serde", @@ -1701,16 +1796,17 @@ dependencies = [ [[package]] name = "sylvia-derive" -version = "0.8.1" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8031f53dbfda341acd7bd321e10d0d684b673324145026e23705da4b6d5c4919" +checksum = "00fd9377878e7c47e9a04f6ee79663c808914584cc7bb3a9f2371b3eef91455a" dependencies = [ "convert_case", + "itertools 0.13.0", "proc-macro-crate", "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.98", ] [[package]] @@ -1737,11 +1833,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.69" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ - "thiserror-impl 1.0.69", + "thiserror-impl 1.0.64", ] [[package]] @@ -1755,9 +1851,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "1.0.69" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", @@ -1810,9 +1906,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] @@ -1831,9 +1927,9 @@ checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.19.15" +version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap", "toml_datetime", @@ -1842,15 +1938,15 @@ dependencies = [ [[package]] name = "typenum" -version = "1.18.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "typewit" -version = "1.11.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb77c29baba9e4d3a6182d51fa75e3215c7fd1dab8f4ea9d107c716878e55fc0" +checksum = "c6fb9ae6a3cafaf0a5d14c2302ca525f9ae8e07a0f0e6949de88d882c37a6e24" dependencies = [ "typewit_proc_macros", ] @@ -1863,15 +1959,21 @@ checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6" [[package]] name = "unicode-ident" -version = "1.0.17" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-segmentation" -version = "1.12.0" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-xid" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "vergen" @@ -1890,9 +1992,9 @@ dependencies = [ [[package]] name = "version_check" -version = "0.9.5" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasi" @@ -1902,9 +2004,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winnow" -version = "0.5.40" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" dependencies = [ "memchr", ] @@ -1915,8 +2017,8 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ - "curve25519-dalek 4.1.3", - "rand_core 0.6.4", + "curve25519-dalek", + "rand_core", "serde", "zeroize", ] @@ -1927,7 +2029,6 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "byteorder", "zerocopy-derive", ] @@ -1944,9 +2045,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.8.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" dependencies = [ "zeroize_derive", ] diff --git a/contracts/Cargo.toml b/contracts/Cargo.toml index b5e53222d68..a5cae830ad4 100644 --- a/contracts/Cargo.toml +++ b/contracts/Cargo.toml @@ -1,12 +1,11 @@ [workspace] resolver = "2" members = [ - # "coconut-bandwidth", "coconut-dkg", - "coconut-test", "ecash", "mixnet", "mixnet-vesting-integration-tests", + "nym-pool", "multisig/cw3-flex-multisig", "multisig/cw4-group", "vesting", @@ -33,24 +32,26 @@ overflow-checks = true [workspace.dependencies] anyhow = "1.0.86" -bs58 = "0.4.0" -cosmwasm-crypto = "=1.4.3" -cosmwasm-derive = "=1.4.3" -cosmwasm-schema = "=1.4.3" -cosmwasm-std = "=1.4.3" -cosmwasm-storage = "=1.4.3" -cw-controllers = "=1.1.0" -cw-multi-test = "=0.16.5" -cw-storage-plus = "=1.2.0" -cw-utils = "=1.0.1" -cw2 = "=1.1.2" -cw3 = "=1.1.2" -cw3-fixed-multisig = "=1.1.2" -cw4 = "=1.1.2" -cw20 = "=1.1.2" +bs58 = "0.5.1" +cosmwasm-crypto = "=2.2.1" +cosmwasm-derive = "=2.2.1" +cosmwasm-schema = "=2.2.1" +cosmwasm-std = "=2.2.1" +cw-controllers = "=2.0.0" +cw-multi-test = "=2.3.1" +cw-storage-plus = "=2.0.0" +cw-utils = "=2.0.0" +cw2 = "=2.0.0" +cw3 = "=2.0.0" +cw3-fixed-multisig = "=2.0.0" +cw4 = "=2.0.0" +cw20 = "=2.0.0" +cw20-base = "2.0.0" +rand = "0.8.5" +rand_chacha = "0.3.1" semver = "1.0.21" serde = "1.0.196" -sylvia = "0.8.0" +sylvia = "1.3.3" schemars = "0.8.16" -thiserror = "1.0.48" +thiserror = "2.0.11" diff --git a/contracts/Makefile b/contracts/Makefile index 62b4abcf181..9c826771c76 100644 --- a/contracts/Makefile +++ b/contracts/Makefile @@ -1,8 +1,5 @@ schema: coconut-dkg-schema mixnet-schema vesting-schema multisig-schema group-schema ecash-schema -#coconut-bandwidth-schema: -# $(MAKE) -C coconut-bandwidth generate-schema - coconut-dkg-schema: $(MAKE) -C coconut-dkg generate-schema diff --git a/contracts/coconut-bandwidth/.cargo/config b/contracts/coconut-bandwidth/.cargo/config deleted file mode 100644 index 2fb2c1afdbc..00000000000 --- a/contracts/coconut-bandwidth/.cargo/config +++ /dev/null @@ -1,4 +0,0 @@ -[alias] -wasm = "build --release --lib --target wasm32-unknown-unknown" -unit-test = "test --lib" -schema = "run --bin schema --features=schema-gen" \ No newline at end of file diff --git a/contracts/coconut-bandwidth/Cargo.toml b/contracts/coconut-bandwidth/Cargo.toml deleted file mode 100644 index b810e741059..00000000000 --- a/contracts/coconut-bandwidth/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "nym-coconut-bandwidth" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[[bin]] -name = "schema" -required-features = ["schema-gen"] - -[lib] -crate-type = ["cdylib", "rlib"] - -[dependencies] -nym-coconut-bandwidth-contract-common = { path = "../../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" } -nym-multisig-contract-common = { path = "../../common/cosmwasm-smart-contracts/multisig-contract" } - -cosmwasm-std = { workspace = true } -cosmwasm-schema = { workspace = true, optional = true } -cosmwasm-storage = { workspace = true } -cw-storage-plus = { workspace = true } -cw-controllers = { workspace = true } - -serde = { version = "1.0.103", default-features = false, features = ["derive"] } -thiserror = { workspace = true } - -[features] -schema-gen = ["nym-coconut-bandwidth-contract-common/schema", "cosmwasm-schema"] diff --git a/contracts/coconut-bandwidth/Makefile b/contracts/coconut-bandwidth/Makefile deleted file mode 100644 index f138e2c879e..00000000000 --- a/contracts/coconut-bandwidth/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -generate-schema: - cargo schema \ No newline at end of file diff --git a/contracts/coconut-bandwidth/schema/nym-coconut-bandwidth.json b/contracts/coconut-bandwidth/schema/nym-coconut-bandwidth.json deleted file mode 100644 index 1b210e85f9f..00000000000 --- a/contracts/coconut-bandwidth/schema/nym-coconut-bandwidth.json +++ /dev/null @@ -1,382 +0,0 @@ -{ - "contract_name": "nym-coconut-bandwidth", - "contract_version": "0.1.0", - "idl_version": "1.0.0", - "instantiate": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "InstantiateMsg", - "type": "object", - "required": [ - "mix_denom", - "multisig_addr", - "pool_addr" - ], - "properties": { - "mix_denom": { - "type": "string" - }, - "multisig_addr": { - "type": "string" - }, - "pool_addr": { - "type": "string" - } - }, - "additionalProperties": false - }, - "execute": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ExecuteMsg", - "oneOf": [ - { - "type": "object", - "required": [ - "deposit_funds" - ], - "properties": { - "deposit_funds": { - "type": "object", - "required": [ - "data" - ], - "properties": { - "data": { - "$ref": "#/definitions/DepositData" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "spend_credential" - ], - "properties": { - "spend_credential": { - "type": "object", - "required": [ - "data" - ], - "properties": { - "data": { - "$ref": "#/definitions/SpendCredentialData" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "release_funds" - ], - "properties": { - "release_funds": { - "type": "object", - "required": [ - "funds" - ], - "properties": { - "funds": { - "$ref": "#/definitions/Coin" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - } - ], - "definitions": { - "Coin": { - "type": "object", - "required": [ - "amount", - "denom" - ], - "properties": { - "amount": { - "$ref": "#/definitions/Uint128" - }, - "denom": { - "type": "string" - } - } - }, - "DepositData": { - "type": "object", - "required": [ - "deposit_info", - "encryption_key", - "identity_key" - ], - "properties": { - "deposit_info": { - "type": "string" - }, - "encryption_key": { - "type": "string" - }, - "identity_key": { - "type": "string" - } - }, - "additionalProperties": false - }, - "SpendCredentialData": { - "type": "object", - "required": [ - "blinded_serial_number", - "funds", - "gateway_cosmos_address" - ], - "properties": { - "blinded_serial_number": { - "type": "string" - }, - "funds": { - "$ref": "#/definitions/Coin" - }, - "gateway_cosmos_address": { - "type": "string" - } - }, - "additionalProperties": false - }, - "Uint128": { - "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", - "type": "string" - } - } - }, - "query": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "QueryMsg", - "oneOf": [ - { - "type": "object", - "required": [ - "get_spent_credential" - ], - "properties": { - "get_spent_credential": { - "type": "object", - "required": [ - "blinded_serial_number" - ], - "properties": { - "blinded_serial_number": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "get_all_spent_credentials" - ], - "properties": { - "get_all_spent_credentials": { - "type": "object", - "properties": { - "limit": { - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "start_after": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "migrate": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "MigrateMsg", - "type": "object", - "additionalProperties": false - }, - "sudo": null, - "responses": { - "get_all_spent_credentials": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "PagedSpendCredentialResponse", - "type": "object", - "required": [ - "per_page", - "spend_credentials" - ], - "properties": { - "per_page": { - "type": "integer", - "format": "uint", - "minimum": 0.0 - }, - "spend_credentials": { - "type": "array", - "items": { - "$ref": "#/definitions/SpendCredential" - } - }, - "start_next_after": { - "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "Coin": { - "type": "object", - "required": [ - "amount", - "denom" - ], - "properties": { - "amount": { - "$ref": "#/definitions/Uint128" - }, - "denom": { - "type": "string" - } - } - }, - "SpendCredential": { - "type": "object", - "required": [ - "blinded_serial_number", - "funds", - "gateway_cosmos_address", - "status" - ], - "properties": { - "blinded_serial_number": { - "type": "string" - }, - "funds": { - "$ref": "#/definitions/Coin" - }, - "gateway_cosmos_address": { - "$ref": "#/definitions/Addr" - }, - "status": { - "$ref": "#/definitions/SpendCredentialStatus" - } - }, - "additionalProperties": false - }, - "SpendCredentialStatus": { - "type": "string", - "enum": [ - "in_progress", - "spent" - ] - }, - "Uint128": { - "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", - "type": "string" - } - } - }, - "get_spent_credential": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "SpendCredentialResponse", - "type": "object", - "properties": { - "spend_credential": { - "anyOf": [ - { - "$ref": "#/definitions/SpendCredential" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "Coin": { - "type": "object", - "required": [ - "amount", - "denom" - ], - "properties": { - "amount": { - "$ref": "#/definitions/Uint128" - }, - "denom": { - "type": "string" - } - } - }, - "SpendCredential": { - "type": "object", - "required": [ - "blinded_serial_number", - "funds", - "gateway_cosmos_address", - "status" - ], - "properties": { - "blinded_serial_number": { - "type": "string" - }, - "funds": { - "$ref": "#/definitions/Coin" - }, - "gateway_cosmos_address": { - "$ref": "#/definitions/Addr" - }, - "status": { - "$ref": "#/definitions/SpendCredentialStatus" - } - }, - "additionalProperties": false - }, - "SpendCredentialStatus": { - "type": "string", - "enum": [ - "in_progress", - "spent" - ] - }, - "Uint128": { - "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", - "type": "string" - } - } - } - } -} diff --git a/contracts/coconut-bandwidth/schema/raw/execute.json b/contracts/coconut-bandwidth/schema/raw/execute.json deleted file mode 100644 index 027b423f7a6..00000000000 --- a/contracts/coconut-bandwidth/schema/raw/execute.json +++ /dev/null @@ -1,130 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ExecuteMsg", - "oneOf": [ - { - "type": "object", - "required": [ - "deposit_funds" - ], - "properties": { - "deposit_funds": { - "type": "object", - "required": [ - "data" - ], - "properties": { - "data": { - "$ref": "#/definitions/DepositData" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "spend_credential" - ], - "properties": { - "spend_credential": { - "type": "object", - "required": [ - "data" - ], - "properties": { - "data": { - "$ref": "#/definitions/SpendCredentialData" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "release_funds" - ], - "properties": { - "release_funds": { - "type": "object", - "required": [ - "funds" - ], - "properties": { - "funds": { - "$ref": "#/definitions/Coin" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - } - ], - "definitions": { - "Coin": { - "type": "object", - "required": [ - "amount", - "denom" - ], - "properties": { - "amount": { - "$ref": "#/definitions/Uint128" - }, - "denom": { - "type": "string" - } - } - }, - "DepositData": { - "type": "object", - "required": [ - "deposit_info", - "encryption_key", - "identity_key" - ], - "properties": { - "deposit_info": { - "type": "string" - }, - "encryption_key": { - "type": "string" - }, - "identity_key": { - "type": "string" - } - }, - "additionalProperties": false - }, - "SpendCredentialData": { - "type": "object", - "required": [ - "blinded_serial_number", - "funds", - "gateway_cosmos_address" - ], - "properties": { - "blinded_serial_number": { - "type": "string" - }, - "funds": { - "$ref": "#/definitions/Coin" - }, - "gateway_cosmos_address": { - "type": "string" - } - }, - "additionalProperties": false - }, - "Uint128": { - "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", - "type": "string" - } - } -} diff --git a/contracts/coconut-bandwidth/schema/raw/instantiate.json b/contracts/coconut-bandwidth/schema/raw/instantiate.json deleted file mode 100644 index 172dc76d79a..00000000000 --- a/contracts/coconut-bandwidth/schema/raw/instantiate.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "InstantiateMsg", - "type": "object", - "required": [ - "mix_denom", - "multisig_addr", - "pool_addr" - ], - "properties": { - "mix_denom": { - "type": "string" - }, - "multisig_addr": { - "type": "string" - }, - "pool_addr": { - "type": "string" - } - }, - "additionalProperties": false -} diff --git a/contracts/coconut-bandwidth/schema/raw/migrate.json b/contracts/coconut-bandwidth/schema/raw/migrate.json deleted file mode 100644 index 7fbe8c5708e..00000000000 --- a/contracts/coconut-bandwidth/schema/raw/migrate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "MigrateMsg", - "type": "object", - "additionalProperties": false -} diff --git a/contracts/coconut-bandwidth/schema/raw/query.json b/contracts/coconut-bandwidth/schema/raw/query.json deleted file mode 100644 index afdfae331d5..00000000000 --- a/contracts/coconut-bandwidth/schema/raw/query.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "QueryMsg", - "oneOf": [ - { - "type": "object", - "required": [ - "get_spent_credential" - ], - "properties": { - "get_spent_credential": { - "type": "object", - "required": [ - "blinded_serial_number" - ], - "properties": { - "blinded_serial_number": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "get_all_spent_credentials" - ], - "properties": { - "get_all_spent_credentials": { - "type": "object", - "properties": { - "limit": { - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "start_after": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] -} diff --git a/contracts/coconut-bandwidth/schema/raw/response_to_get_all_spent_credentials.json b/contracts/coconut-bandwidth/schema/raw/response_to_get_all_spent_credentials.json deleted file mode 100644 index 733da870817..00000000000 --- a/contracts/coconut-bandwidth/schema/raw/response_to_get_all_spent_credentials.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "PagedSpendCredentialResponse", - "type": "object", - "required": [ - "per_page", - "spend_credentials" - ], - "properties": { - "per_page": { - "type": "integer", - "format": "uint", - "minimum": 0.0 - }, - "spend_credentials": { - "type": "array", - "items": { - "$ref": "#/definitions/SpendCredential" - } - }, - "start_next_after": { - "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "Coin": { - "type": "object", - "required": [ - "amount", - "denom" - ], - "properties": { - "amount": { - "$ref": "#/definitions/Uint128" - }, - "denom": { - "type": "string" - } - } - }, - "SpendCredential": { - "type": "object", - "required": [ - "blinded_serial_number", - "funds", - "gateway_cosmos_address", - "status" - ], - "properties": { - "blinded_serial_number": { - "type": "string" - }, - "funds": { - "$ref": "#/definitions/Coin" - }, - "gateway_cosmos_address": { - "$ref": "#/definitions/Addr" - }, - "status": { - "$ref": "#/definitions/SpendCredentialStatus" - } - }, - "additionalProperties": false - }, - "SpendCredentialStatus": { - "type": "string", - "enum": [ - "in_progress", - "spent" - ] - }, - "Uint128": { - "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", - "type": "string" - } - } -} diff --git a/contracts/coconut-bandwidth/schema/raw/response_to_get_spent_credential.json b/contracts/coconut-bandwidth/schema/raw/response_to_get_spent_credential.json deleted file mode 100644 index 9af4def85b0..00000000000 --- a/contracts/coconut-bandwidth/schema/raw/response_to_get_spent_credential.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "SpendCredentialResponse", - "type": "object", - "properties": { - "spend_credential": { - "anyOf": [ - { - "$ref": "#/definitions/SpendCredential" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "Coin": { - "type": "object", - "required": [ - "amount", - "denom" - ], - "properties": { - "amount": { - "$ref": "#/definitions/Uint128" - }, - "denom": { - "type": "string" - } - } - }, - "SpendCredential": { - "type": "object", - "required": [ - "blinded_serial_number", - "funds", - "gateway_cosmos_address", - "status" - ], - "properties": { - "blinded_serial_number": { - "type": "string" - }, - "funds": { - "$ref": "#/definitions/Coin" - }, - "gateway_cosmos_address": { - "$ref": "#/definitions/Addr" - }, - "status": { - "$ref": "#/definitions/SpendCredentialStatus" - } - }, - "additionalProperties": false - }, - "SpendCredentialStatus": { - "type": "string", - "enum": [ - "in_progress", - "spent" - ] - }, - "Uint128": { - "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", - "type": "string" - } - } -} diff --git a/contracts/coconut-bandwidth/src/bin/schema.rs b/contracts/coconut-bandwidth/src/bin/schema.rs deleted file mode 100644 index 4956eb33f40..00000000000 --- a/contracts/coconut-bandwidth/src/bin/schema.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2023 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use cosmwasm_schema::write_api; -use nym_coconut_bandwidth_contract_common::msg::{ - ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, -}; - -fn main() { - write_api! { - instantiate: InstantiateMsg, - query: QueryMsg, - execute: ExecuteMsg, - migrate: MigrateMsg, - } -} diff --git a/contracts/coconut-bandwidth/src/contract.rs b/contracts/coconut-bandwidth/src/contract.rs deleted file mode 100644 index 6ed901a443c..00000000000 --- a/contracts/coconut-bandwidth/src/contract.rs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use cosmwasm_std::{ - entry_point, to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, -}; - -use nym_coconut_bandwidth_contract_common::msg::{ - ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, -}; - -use crate::error::ContractError; -use crate::queries::{query_all_spent_credentials_paged, query_spent_credential}; -use crate::state::{Config, ADMIN, CONFIG}; -use crate::transactions; - -/// Instantiate the contract. -/// -/// `deps` contains Storage, API and Querier -/// `msg` is the contract initialization message, sort of like a constructor call. -#[entry_point] -pub fn instantiate( - mut deps: DepsMut<'_>, - _env: Env, - _info: MessageInfo, - msg: InstantiateMsg, -) -> Result { - let multisig_addr = deps.api.addr_validate(&msg.multisig_addr)?; - let pool_addr = deps.api.addr_validate(&msg.pool_addr)?; - let mix_denom = msg.mix_denom; - - ADMIN.set(deps.branch(), Some(multisig_addr.clone()))?; - - let cfg = Config { - multisig_addr, - pool_addr, - mix_denom, - }; - CONFIG.save(deps.storage, &cfg)?; - - Ok(Response::default()) -} - -/// Handle an incoming message -#[entry_point] -pub fn execute( - deps: DepsMut<'_>, - env: Env, - info: MessageInfo, - msg: ExecuteMsg, -) -> Result { - match msg { - ExecuteMsg::DepositFunds { data } => transactions::deposit_funds(deps, env, info, data), - ExecuteMsg::SpendCredential { data } => { - transactions::spend_credential(deps, env, info, data) - } - ExecuteMsg::ReleaseFunds { funds } => transactions::release_funds(deps, env, info, funds), - } -} - -#[entry_point] -pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { - match msg { - QueryMsg::GetAllSpentCredentials { limit, start_after } => to_binary( - &query_all_spent_credentials_paged(deps, start_after, limit)?, - ), - QueryMsg::GetSpentCredential { - blinded_serial_number, - } => to_binary(&query_spent_credential(deps, blinded_serial_number)?), - } -} - -#[entry_point] -pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result { - Ok(Default::default()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::support::tests::fixtures::TEST_MIX_DENOM; - use crate::support::tests::helpers::*; - use cosmwasm_std::coins; - use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; - - #[test] - fn initialize_contract() { - let mut deps = mock_dependencies(); - let env = mock_env(); - let msg = InstantiateMsg { - multisig_addr: String::from(MULTISIG_CONTRACT), - pool_addr: String::from(POOL_CONTRACT), - mix_denom: TEST_MIX_DENOM.to_string(), - }; - let info = mock_info("creator", &[]); - - let res = instantiate(deps.as_mut(), env.clone(), info, msg).unwrap(); - assert_eq!(0, res.messages.len()); - - // Contract balance should be 0 - assert_eq!( - coins(0, TEST_MIX_DENOM), - vec![deps - .as_ref() - .querier - .query_balance(env.contract.address, TEST_MIX_DENOM) - .unwrap()] - ); - } -} diff --git a/contracts/coconut-bandwidth/src/error.rs b/contracts/coconut-bandwidth/src/error.rs deleted file mode 100644 index 9f4aed6224e..00000000000 --- a/contracts/coconut-bandwidth/src/error.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2021 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use cosmwasm_std::StdError; -use cw_controllers::AdminError; -use thiserror::Error; - -/// Custom errors for contract failure conditions. -/// -/// Add any other custom errors you like here. -/// Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details. -#[derive(Error, Debug, PartialEq)] -pub enum ContractError { - #[error(transparent)] - Std(#[from] StdError), - - #[error("Received multiple coin types")] - MultipleDenoms, - - #[error("No coin was sent for voucher")] - NoCoin, - - #[error("Wrong coin denomination, you must send {mix_denom}")] - WrongDenom { mix_denom: String }, - - #[error("There aren't enough funds in the contract")] - NotEnoughFunds, - - #[error("Credential already spent or in process of spending")] - DuplicateBlindedSerialNumber, - - #[error(transparent)] - Admin(#[from] AdminError), -} diff --git a/contracts/coconut-bandwidth/src/lib.rs b/contracts/coconut-bandwidth/src/lib.rs deleted file mode 100644 index 516cf72b43b..00000000000 --- a/contracts/coconut-bandwidth/src/lib.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2021 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -pub mod contract; -pub mod error; -mod queries; -mod state; -mod storage; -mod support; -mod transactions; diff --git a/contracts/coconut-bandwidth/src/queries.rs b/contracts/coconut-bandwidth/src/queries.rs deleted file mode 100644 index 534696752f9..00000000000 --- a/contracts/coconut-bandwidth/src/queries.rs +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use cosmwasm_std::{Deps, Order, StdResult}; -use cw_storage_plus::Bound; -use nym_coconut_bandwidth_contract_common::spend_credential::{ - PagedSpendCredentialResponse, SpendCredential, SpendCredentialResponse, -}; - -use crate::storage::{self, SPEND_CREDENTIAL_PAGE_DEFAULT_LIMIT, SPEND_CREDENTIAL_PAGE_MAX_LIMIT}; - -pub(crate) fn query_all_spent_credentials_paged( - deps: Deps<'_>, - start_after: Option, - limit: Option, -) -> StdResult { - let limit = limit - .unwrap_or(SPEND_CREDENTIAL_PAGE_DEFAULT_LIMIT) - .min(SPEND_CREDENTIAL_PAGE_MAX_LIMIT) as usize; - - let start = start_after.as_deref().map(Bound::exclusive); - - let nodes = storage::spent_credentials() - .range(deps.storage, start, None, Order::Ascending) - .take(limit) - .map(|res| res.map(|item| item.1)) - .collect::>>()?; - - let start_next_after = nodes - .last() - .map(|spend_credential| spend_credential.blinded_serial_number().to_string()); - - Ok(PagedSpendCredentialResponse::new( - nodes, - limit, - start_next_after, - )) -} - -pub(crate) fn query_spent_credential( - deps: Deps<'_>, - blinded_serial_number: String, -) -> StdResult { - let spend_credential = - storage::spent_credentials().may_load(deps.storage, &blinded_serial_number)?; - Ok(SpendCredentialResponse::new(spend_credential)) -} - -#[cfg(test)] -pub(crate) mod tests { - use super::*; - use crate::support::tests::fixtures::spend_credential_data_fixture; - use crate::support::tests::helpers::init_contract; - use crate::transactions::spend_credential; - use cosmwasm_std::testing::{mock_env, mock_info}; - - #[test] - fn spent_credentials_empty_on_init() { - let deps = init_contract(); - let response = - query_all_spent_credentials_paged(deps.as_ref(), None, Option::from(2)).unwrap(); - assert_eq!(0, response.spend_credentials.len()); - } - - #[test] - fn spent_credentials_paged_retrieval_obeys_limits() { - let mut deps = init_contract(); - let env = mock_env(); - let info = mock_info("requester", &[]); - let limit = 2; - for n in 0..1000 { - let data = spend_credential_data_fixture(&format!("blinded_serial_number{}", n)); - spend_credential(deps.as_mut(), env.clone(), info.clone(), data).unwrap(); - } - - let page1 = - query_all_spent_credentials_paged(deps.as_ref(), None, Option::from(limit)).unwrap(); - assert_eq!(limit, page1.spend_credentials.len() as u32); - } - - #[test] - fn spent_credentials_paged_retrieval_has_default_limit() { - let mut deps = init_contract(); - let env = mock_env(); - let info = mock_info("requester", &[]); - for n in 0..1000 { - let data = spend_credential_data_fixture(&format!("blinded_serial_number{}", n)); - spend_credential(deps.as_mut(), env.clone(), info.clone(), data).unwrap(); - } - - // query without explicitly setting a limit - let page1 = query_all_spent_credentials_paged(deps.as_ref(), None, None).unwrap(); - - assert_eq!( - SPEND_CREDENTIAL_PAGE_DEFAULT_LIMIT, - page1.spend_credentials.len() as u32 - ); - } - - #[test] - fn spent_credentials_paged_retrieval_has_max_limit() { - let mut deps = init_contract(); - let env = mock_env(); - let info = mock_info("requester", &[]); - for n in 0..1000 { - let data = spend_credential_data_fixture(&format!("blinded_serial_number{}", n)); - spend_credential(deps.as_mut(), env.clone(), info.clone(), data).unwrap(); - } - - // query with a crazily high limit in an attempt to use too many resources - let crazy_limit = 1000 * SPEND_CREDENTIAL_PAGE_MAX_LIMIT; - let page1 = - query_all_spent_credentials_paged(deps.as_ref(), None, Option::from(crazy_limit)) - .unwrap(); - - // we default to a decent sized upper bound instead - let expected_limit = SPEND_CREDENTIAL_PAGE_MAX_LIMIT; - assert_eq!(expected_limit, page1.spend_credentials.len() as u32); - } - - #[test] - fn spent_credentials_pagination_works() { - let mut deps = init_contract(); - let env = mock_env(); - let info = mock_info("requester", &[]); - - let data = spend_credential_data_fixture("blinded_serial_number1"); - spend_credential(deps.as_mut(), env.clone(), info.clone(), data).unwrap(); - - let per_page = 2; - let page1 = - query_all_spent_credentials_paged(deps.as_ref(), None, Option::from(per_page)).unwrap(); - - // page should have 1 result on it - assert_eq!(1, page1.spend_credentials.len()); - - // save another - let data = spend_credential_data_fixture("blinded_serial_number2"); - spend_credential(deps.as_mut(), env.clone(), info.clone(), data).unwrap(); - - // page1 should have 2 results on it - let page1 = - query_all_spent_credentials_paged(deps.as_ref(), None, Option::from(per_page)).unwrap(); - assert_eq!(2, page1.spend_credentials.len()); - - let data = spend_credential_data_fixture("blinded_serial_number3"); - spend_credential(deps.as_mut(), env.clone(), info.clone(), data).unwrap(); - - // page1 still has 2 results - let page1 = - query_all_spent_credentials_paged(deps.as_ref(), None, Option::from(per_page)).unwrap(); - assert_eq!(2, page1.spend_credentials.len()); - - // retrieving the next page should start after the last key on this page - let start_after = page1.start_next_after.unwrap(); - let page2 = query_all_spent_credentials_paged( - deps.as_ref(), - Option::from(start_after.clone()), - Option::from(per_page), - ) - .unwrap(); - - assert_eq!(1, page2.spend_credentials.len()); - - let data = spend_credential_data_fixture("blinded_serial_number4"); - spend_credential(deps.as_mut(), env, info, data).unwrap(); - - let page2 = query_all_spent_credentials_paged( - deps.as_ref(), - Option::from(start_after), - Option::from(per_page), - ) - .unwrap(); - - // now we have 2 pages, with 2 results on the second page - assert_eq!(2, page2.spend_credentials.len()); - } -} diff --git a/contracts/coconut-bandwidth/src/state.rs b/contracts/coconut-bandwidth/src/state.rs deleted file mode 100644 index 855d9fa07a5..00000000000 --- a/contracts/coconut-bandwidth/src/state.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use cosmwasm_std::Addr; -use cw_controllers::Admin; -use cw_storage_plus::Item; -use serde::{Deserialize, Serialize}; - -pub const ADMIN: Admin = Admin::new("admin"); - -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] -pub struct Config { - pub multisig_addr: Addr, - pub pool_addr: Addr, - pub mix_denom: String, -} - -pub const CONFIG: Item = Item::new("config"); diff --git a/contracts/coconut-bandwidth/src/storage.rs b/contracts/coconut-bandwidth/src/storage.rs deleted file mode 100644 index aa671bbbcba..00000000000 --- a/contracts/coconut-bandwidth/src/storage.rs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use cw_storage_plus::{Index, IndexList, IndexedMap, UniqueIndex}; -use nym_coconut_bandwidth_contract_common::spend_credential::SpendCredential; - -// storage prefixes -const SPEND_CREDENTIAL_PK_NAMESPACE: &str = "sc"; -const SPEND_CREDENTIAL_BLINDED_SERIAL_NO_IDX_NAMESPACE: &str = "scn"; - -// paged retrieval limits for all queries and transactions -pub(crate) const SPEND_CREDENTIAL_PAGE_MAX_LIMIT: u32 = 75; -pub(crate) const SPEND_CREDENTIAL_PAGE_DEFAULT_LIMIT: u32 = 50; - -pub(crate) struct SpendCredentialIndex<'a> { - pub(crate) blinded_serial_number: UniqueIndex<'a, String, SpendCredential>, -} - -// IndexList is just boilerplate code for fetching a struct's indexes -// note that from my understanding this will be converted into a macro at some point in the future -impl IndexList for SpendCredentialIndex<'_> { - fn get_indexes(&'_ self) -> Box> + '_> { - let v: Vec<&dyn Index> = vec![&self.blinded_serial_number]; - Box::new(v.into_iter()) - } -} - -// spent_credentials() is the storage access function. -pub(crate) fn spent_credentials<'a>( -) -> IndexedMap<'a, &'a str, SpendCredential, SpendCredentialIndex<'a>> { - let indexes = SpendCredentialIndex { - blinded_serial_number: UniqueIndex::new( - |d| d.blinded_serial_number().to_string(), - SPEND_CREDENTIAL_BLINDED_SERIAL_NO_IDX_NAMESPACE, - ), - }; - IndexedMap::new(SPEND_CREDENTIAL_PK_NAMESPACE, indexes) -} - -// currently not used outside tests -#[cfg(test)] -mod tests { - use super::super::storage; - use crate::storage::SpendCredential; - use crate::support::tests::fixtures; - use crate::support::tests::fixtures::TEST_MIX_DENOM; - use cosmwasm_std::testing::MockStorage; - use cosmwasm_std::Addr; - use cosmwasm_std::Coin; - use nym_coconut_bandwidth_contract_common::spend_credential::SpendCredentialStatus; - - #[test] - fn spend_credential_single_read_retrieval() { - let mut storage = MockStorage::new(); - let blind_serial_number1 = "number1"; - let blind_serial_number2 = "number2"; - let spend1 = fixtures::spend_credential_fixture(blind_serial_number1); - let spend2 = fixtures::spend_credential_fixture(blind_serial_number2); - storage::spent_credentials() - .save(&mut storage, blind_serial_number1, &spend1) - .unwrap(); - storage::spent_credentials() - .save(&mut storage, blind_serial_number2, &spend2) - .unwrap(); - - let res1 = storage::spent_credentials() - .load(&storage, blind_serial_number1) - .unwrap(); - let res2 = storage::spent_credentials() - .load(&storage, blind_serial_number2) - .unwrap(); - assert_eq!(spend1, res1); - assert_eq!(spend2, res2); - } - - #[test] - fn mark_as_spent_credential() { - let mut mock_storage = MockStorage::new(); - let funds = Coin::new(100, TEST_MIX_DENOM); - let blind_serial_number = "blind_serial_number"; - let gateway_cosmos_address: Addr = Addr::unchecked("gateway_cosmos_address"); - - let res = storage::spent_credentials() - .may_load(&mock_storage, blind_serial_number) - .unwrap(); - assert!(res.is_none()); - - let mut spend_credential = SpendCredential::new( - funds, - blind_serial_number.to_string(), - gateway_cosmos_address, - ); - spend_credential.mark_as_spent(); - - storage::spent_credentials() - .save(&mut mock_storage, blind_serial_number, &spend_credential) - .unwrap(); - - let ret = storage::spent_credentials() - .load(&mock_storage, blind_serial_number) - .unwrap(); - - assert_eq!(ret, spend_credential); - assert_eq!(ret.status(), SpendCredentialStatus::Spent); - } -} diff --git a/contracts/coconut-bandwidth/src/support/mod.rs b/contracts/coconut-bandwidth/src/support/mod.rs deleted file mode 100644 index 2f17cb1c46b..00000000000 --- a/contracts/coconut-bandwidth/src/support/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2021 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -#[cfg(test)] -pub mod tests; diff --git a/contracts/coconut-bandwidth/src/support/tests/fixtures.rs b/contracts/coconut-bandwidth/src/support/tests/fixtures.rs deleted file mode 100644 index 1b5ac62dbef..00000000000 --- a/contracts/coconut-bandwidth/src/support/tests/fixtures.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use cosmwasm_std::{Addr, Coin}; -use nym_coconut_bandwidth_contract_common::spend_credential::{ - SpendCredential, SpendCredentialData, -}; - -pub const TEST_MIX_DENOM: &str = "unym"; - -pub fn spend_credential_fixture(blinded_serial_number: &str) -> SpendCredential { - SpendCredential::new( - Coin::new(100, TEST_MIX_DENOM), - blinded_serial_number.to_string(), - Addr::unchecked("gateway_owner_addr"), - ) -} - -pub fn spend_credential_data_fixture(blinded_serial_number: &str) -> SpendCredentialData { - SpendCredentialData::new( - Coin::new(100, TEST_MIX_DENOM), - blinded_serial_number.to_string(), - "gateway_owner_addr".to_string(), - ) -} diff --git a/contracts/coconut-bandwidth/src/support/tests/helpers.rs b/contracts/coconut-bandwidth/src/support/tests/helpers.rs deleted file mode 100644 index bf9bccdcf62..00000000000 --- a/contracts/coconut-bandwidth/src/support/tests/helpers.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2021 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -pub const MULTISIG_CONTRACT: &str = "multisig contract address"; -pub const POOL_CONTRACT: &str = "mix pool contract address"; - -use crate::contract::instantiate; -use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier}; -use cosmwasm_std::{Empty, MemoryStorage, OwnedDeps}; -use nym_coconut_bandwidth_contract_common::msg::InstantiateMsg; - -use super::fixtures::TEST_MIX_DENOM; - -pub fn init_contract() -> OwnedDeps> { - let mut deps = mock_dependencies(); - let msg = InstantiateMsg { - multisig_addr: String::from(MULTISIG_CONTRACT), - pool_addr: String::from(POOL_CONTRACT), - mix_denom: TEST_MIX_DENOM.to_string(), - }; - let env = mock_env(); - let info = mock_info("creator", &[]); - instantiate(deps.as_mut(), env, info, msg).unwrap(); - deps -} diff --git a/contracts/coconut-bandwidth/src/support/tests/mod.rs b/contracts/coconut-bandwidth/src/support/tests/mod.rs deleted file mode 100644 index 1e96465ed58..00000000000 --- a/contracts/coconut-bandwidth/src/support/tests/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -pub mod fixtures; -pub mod helpers; diff --git a/contracts/coconut-bandwidth/src/transactions.rs b/contracts/coconut-bandwidth/src/transactions.rs deleted file mode 100644 index 8a55ec832fb..00000000000 --- a/contracts/coconut-bandwidth/src/transactions.rs +++ /dev/null @@ -1,369 +0,0 @@ -// Copyright 2021 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use cosmwasm_std::{BankMsg, Coin, DepsMut, Env, Event, MessageInfo, Response}; -use nym_coconut_bandwidth_contract_common::spend_credential::{ - to_cosmos_msg, SpendCredential, SpendCredentialData, -}; - -use crate::error::ContractError; -use crate::state::{ADMIN, CONFIG}; -use crate::storage; - -use nym_coconut_bandwidth_contract_common::deposit::DepositData; -use nym_coconut_bandwidth_contract_common::events::{ - DEPOSITED_FUNDS_EVENT_TYPE, DEPOSIT_ENCRYPTION_KEY, DEPOSIT_IDENTITY_KEY, DEPOSIT_INFO, - DEPOSIT_VALUE, -}; - -pub(crate) fn deposit_funds( - deps: DepsMut<'_>, - _env: Env, - info: MessageInfo, - data: DepositData, -) -> Result { - if info.funds.is_empty() { - return Err(ContractError::NoCoin); - } - if info.funds.len() > 1 { - return Err(ContractError::MultipleDenoms); - } - let mix_denom = CONFIG.load(deps.storage)?.mix_denom; - if info.funds[0].denom != mix_denom { - return Err(ContractError::WrongDenom { mix_denom }); - } - - let voucher_value = info.funds.last().unwrap(); - let event = Event::new(DEPOSITED_FUNDS_EVENT_TYPE) - .add_attribute(DEPOSIT_VALUE, voucher_value.amount) - .add_attribute(DEPOSIT_INFO, data.deposit_info()) - .add_attribute(DEPOSIT_IDENTITY_KEY, data.identity_key()) - .add_attribute(DEPOSIT_ENCRYPTION_KEY, data.encryption_key()); - - Ok(Response::new().add_event(event)) -} - -pub(crate) fn spend_credential( - deps: DepsMut<'_>, - env: Env, - _info: MessageInfo, - data: SpendCredentialData, -) -> Result { - let mix_denom = CONFIG.load(deps.storage)?.mix_denom; - if data.funds().denom != mix_denom { - return Err(ContractError::WrongDenom { mix_denom }); - } - if storage::spent_credentials().has(deps.storage, data.blinded_serial_number()) { - return Err(ContractError::DuplicateBlindedSerialNumber); - } - let cfg = CONFIG.load(deps.storage)?; - - let gateway_cosmos_address = deps.api.addr_validate(data.gateway_cosmos_address())?; - storage::spent_credentials().save( - deps.storage, - data.blinded_serial_number(), - &SpendCredential::new( - data.funds().to_owned(), - data.blinded_serial_number().to_owned(), - gateway_cosmos_address, - ), - )?; - - let msg = to_cosmos_msg( - data.funds().clone(), - data.blinded_serial_number().to_string(), - env.contract.address.into_string(), - cfg.multisig_addr.into_string(), - )?; - - Ok(Response::new().add_message(msg)) -} - -pub(crate) fn release_funds( - deps: DepsMut<'_>, - env: Env, - info: MessageInfo, - funds: Coin, -) -> Result { - let mix_denom = CONFIG.load(deps.storage)?.mix_denom; - if funds.denom != mix_denom { - return Err(ContractError::WrongDenom { mix_denom }); - } - let current_balance = deps - .querier - .query_balance(env.contract.address, mix_denom)?; - if funds.amount > current_balance.amount { - return Err(ContractError::NotEnoughFunds); - } - ADMIN.assert_admin(deps.as_ref(), &info.sender)?; - - let cfg = CONFIG.load(deps.storage)?; - - let return_tokens = BankMsg::Send { - to_address: cfg.pool_addr.into(), - amount: vec![funds], - }; - let response = Response::new().add_message(return_tokens); - - Ok(response) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::support::tests::fixtures::spend_credential_data_fixture; - use crate::support::tests::helpers::{self, MULTISIG_CONTRACT, POOL_CONTRACT}; - use cosmwasm_std::testing::{mock_env, mock_info}; - use cosmwasm_std::{from_binary, CosmosMsg, WasmMsg}; - use cw_controllers::AdminError; - use nym_coconut_bandwidth_contract_common::msg::ExecuteMsg; - use nym_multisig_contract_common::msg::ExecuteMsg as MultisigExecuteMsg; - - #[test] - fn invalid_deposit() { - let mut deps = helpers::init_contract(); - let env = mock_env(); - let info = mock_info("requester", &[]); - - let deposit_info = String::from("Deposit info"); - let verification_key = String::from("Verification key"); - let encryption_key = String::from("Encryption key"); - let data = DepositData::new(deposit_info, verification_key, encryption_key); - - assert_eq!( - deposit_funds(deps.as_mut(), env.clone(), info, data.clone()), - Err(ContractError::NoCoin) - ); - - let coin = Coin::new(1000000, crate::support::tests::fixtures::TEST_MIX_DENOM); - let second_coin = Coin::new(1000000, "some_denom"); - - let info = mock_info("requester", &[coin, second_coin.clone()]); - assert_eq!( - deposit_funds(deps.as_mut(), env.clone(), info, data.clone()), - Err(ContractError::MultipleDenoms) - ); - - let info = mock_info("requester", &[second_coin]); - assert_eq!( - deposit_funds(deps.as_mut(), env, info, data), - Err(ContractError::WrongDenom { - mix_denom: crate::support::tests::fixtures::TEST_MIX_DENOM.to_string() - }) - ); - } - - #[test] - fn valid_deposit() { - let mut deps = helpers::init_contract(); - let env = mock_env(); - - let deposit_info = String::from("Deposit info"); - let verification_key = String::from("Verification key"); - let encryption_key = String::from("Encryption key"); - let deposit_value = 424242; - let data = DepositData::new( - deposit_info.clone(), - verification_key.clone(), - encryption_key.clone(), - ); - let coin = Coin::new( - deposit_value, - crate::support::tests::fixtures::TEST_MIX_DENOM, - ); - let info = mock_info("requester", &[coin]); - - let tx = deposit_funds(deps.as_mut(), env, info, data).unwrap(); - - let events: Vec<_> = tx - .events - .iter() - .filter(|event| event.ty == DEPOSITED_FUNDS_EVENT_TYPE) - .collect(); - assert_eq!(events.len(), 1); - - let event = events[0]; - assert_eq!(event.attributes.len(), 4); - - let deposit_attr = event - .attributes - .iter() - .find(|attr| attr.key == DEPOSIT_VALUE) - .unwrap(); - assert_eq!(deposit_attr.value, deposit_value.to_string()); - - let info_attr = event - .attributes - .iter() - .find(|attr| attr.key == DEPOSIT_INFO) - .unwrap(); - assert_eq!(info_attr.value, deposit_info); - - let verification_key_attr = event - .attributes - .iter() - .find(|attr| attr.key == DEPOSIT_IDENTITY_KEY) - .unwrap(); - assert_eq!(verification_key_attr.value, verification_key); - - let encryption_key_attr = event - .attributes - .iter() - .find(|attr| attr.key == DEPOSIT_ENCRYPTION_KEY) - .unwrap(); - assert_eq!(encryption_key_attr.value, encryption_key); - } - - #[test] - fn invalid_release() { - let mut deps = helpers::init_contract(); - let env = mock_env(); - let invalid_admin = "invalid admin"; - let funds = Coin::new(1, crate::support::tests::fixtures::TEST_MIX_DENOM); - - let err = release_funds( - deps.as_mut(), - env.clone(), - mock_info(invalid_admin, &[]), - Coin::new(1, "invalid denom"), - ) - .unwrap_err(); - assert_eq!( - err, - ContractError::WrongDenom { - mix_denom: crate::support::tests::fixtures::TEST_MIX_DENOM.to_string() - } - ); - - let err = release_funds( - deps.as_mut(), - env.clone(), - mock_info(invalid_admin, &[]), - funds.clone(), - ) - .unwrap_err(); - assert_eq!(err, ContractError::NotEnoughFunds); - - deps.querier - .update_balance(env.contract.address.clone(), vec![funds.clone()]); - let err = - release_funds(deps.as_mut(), env, mock_info(invalid_admin, &[]), funds).unwrap_err(); - assert_eq!(err, ContractError::Admin(AdminError::NotAdmin {})); - } - - #[test] - fn valid_release() { - let mut deps = helpers::init_contract(); - let env = mock_env(); - let coin = Coin::new(1, crate::support::tests::fixtures::TEST_MIX_DENOM); - - deps.querier - .update_balance(env.contract.address.clone(), vec![coin.clone()]); - let res = release_funds( - deps.as_mut(), - env, - mock_info(MULTISIG_CONTRACT, &[]), - coin.clone(), - ) - .unwrap(); - assert_eq!( - res.messages[0].msg, - CosmosMsg::Bank(BankMsg::Send { - to_address: String::from(POOL_CONTRACT), - amount: vec![coin] - }) - ); - } - #[test] - fn valid_spend() { - let mut deps = helpers::init_contract(); - let env = mock_env(); - let info = mock_info("requester", &[]); - let data = spend_credential_data_fixture("blinded_serial_number"); - let res = spend_credential(deps.as_mut(), env.clone(), info, data.clone()).unwrap(); - if let CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr, - msg, - funds, - }) = &res.messages[0].msg - { - assert_eq!(contract_addr, MULTISIG_CONTRACT); - assert!(funds.is_empty()); - let multisig_msg: MultisigExecuteMsg = from_binary(msg).unwrap(); - if let MultisigExecuteMsg::Propose { - title: _, - description, - msgs, - latest, - } = multisig_msg - { - assert_eq!(description, data.blinded_serial_number().to_string()); - assert!(latest.is_none()); - if let CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr, - msg, - funds, - }) = &msgs[0] - { - assert_eq!(*contract_addr, env.contract.address.into_string()); - assert!(funds.is_empty()); - let release_funds_req: ExecuteMsg = from_binary(msg).unwrap(); - if let ExecuteMsg::ReleaseFunds { funds } = release_funds_req { - assert_eq!(funds, *data.funds()); - } else { - panic!("Could not extract release funds message from proposal"); - } - } - } else { - panic!("Could not extract proposal from binary blob"); - } - } else { - panic!("Wasm execute message not found"); - } - } - - #[test] - fn invalid_spend_attempts() { - let mut deps = helpers::init_contract(); - let env = mock_env(); - let info = mock_info("requester", &[]); - - let invalid_data = SpendCredentialData::new( - Coin::new(1, "invalid_denom".to_string()), - String::new(), - String::new(), - ); - let ret = spend_credential(deps.as_mut(), env.clone(), info.clone(), invalid_data); - assert_eq!( - ret.unwrap_err(), - ContractError::WrongDenom { - mix_denom: crate::support::tests::fixtures::TEST_MIX_DENOM.to_string() - } - ); - - let invalid_data = SpendCredentialData::new( - Coin::new(1, crate::support::tests::fixtures::TEST_MIX_DENOM), - String::new(), - "Blinded Serial Number".to_string(), - ); - let ret = spend_credential(deps.as_mut(), env.clone(), info.clone(), invalid_data); - assert_eq!( - ret.unwrap_err().to_string(), - "Generic error: Invalid input: address not normalized".to_string() - ); - - let invalid_data = spend_credential_data_fixture("blined_serial_number"); - spend_credential( - deps.as_mut(), - env.clone(), - info.clone(), - invalid_data.clone(), - ) - .unwrap(); - let ret = spend_credential(deps.as_mut(), env, info, invalid_data); - assert_eq!( - ret.unwrap_err(), - ContractError::DuplicateBlindedSerialNumber - ); - } -} diff --git a/contracts/coconut-dkg/Cargo.toml b/contracts/coconut-dkg/Cargo.toml index 836c58104aa..e65539ea417 100644 --- a/contracts/coconut-dkg/Cargo.toml +++ b/contracts/coconut-dkg/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "nym-coconut-dkg" version = "0.1.0" -edition = "2021" +edition = { workspace = true } +authors = { workspace = true } +license = { workspace = true } +repository = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -18,15 +21,15 @@ nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts cosmwasm-schema = { workspace = true, optional = true } cosmwasm-std = { workspace = true } -cosmwasm-storage = { workspace = true } + cw-storage-plus = { workspace = true } cw-controllers = { workspace = true } cw2 = { workspace = true } cw4 = { workspace = true } -serde = { version = "1.0.103", default-features = false, features = ["derive"] } thiserror = { workspace = true } [dev-dependencies] +easy-addr = { path = "../../common/cosmwasm-smart-contracts/easy_addr" } cw-multi-test = { workspace = true } cw4-group = { path = "../multisig/cw4-group" } nym-group-contract-common = { path = "../../common/cosmwasm-smart-contracts/group-contract" } diff --git a/contracts/coconut-dkg/schema/nym-coconut-dkg.json b/contracts/coconut-dkg/schema/nym-coconut-dkg.json index b33212190b5..456631e5016 100644 --- a/contracts/coconut-dkg/schema/nym-coconut-dkg.json +++ b/contracts/coconut-dkg/schema/nym-coconut-dkg.json @@ -1357,9 +1357,7 @@ }, "dealing_submission_status": { "type": "object", - "additionalProperties": { - "$ref": "#/definitions/DealingStatus" - } + "additionalProperties": false }, "epoch_id": { "type": "integer", @@ -1397,9 +1395,7 @@ "properties": { "chunk_submission_status": { "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ChunkSubmissionStatus" - } + "additionalProperties": false }, "fully_submitted": { "type": "boolean" @@ -1744,9 +1740,7 @@ "properties": { "chunk_submission_status": { "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ChunkSubmissionStatus" - } + "additionalProperties": false }, "fully_submitted": { "type": "boolean" @@ -1841,9 +1835,7 @@ }, "submitted_chunks": { "type": "object", - "additionalProperties": { - "$ref": "#/definitions/SubmittedChunk" - } + "additionalProperties": false } }, "additionalProperties": false diff --git a/contracts/coconut-dkg/schema/raw/response_to_get_dealer_dealings_status.json b/contracts/coconut-dkg/schema/raw/response_to_get_dealer_dealings_status.json index 0422d9506b4..265b14c35e7 100644 --- a/contracts/coconut-dkg/schema/raw/response_to_get_dealer_dealings_status.json +++ b/contracts/coconut-dkg/schema/raw/response_to_get_dealer_dealings_status.json @@ -17,9 +17,7 @@ }, "dealing_submission_status": { "type": "object", - "additionalProperties": { - "$ref": "#/definitions/DealingStatus" - } + "additionalProperties": false }, "epoch_id": { "type": "integer", @@ -57,9 +55,7 @@ "properties": { "chunk_submission_status": { "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ChunkSubmissionStatus" - } + "additionalProperties": false }, "fully_submitted": { "type": "boolean" diff --git a/contracts/coconut-dkg/schema/raw/response_to_get_dealing_status.json b/contracts/coconut-dkg/schema/raw/response_to_get_dealing_status.json index c3a428dd14d..a9ab7880fc9 100644 --- a/contracts/coconut-dkg/schema/raw/response_to_get_dealing_status.json +++ b/contracts/coconut-dkg/schema/raw/response_to_get_dealing_status.json @@ -56,9 +56,7 @@ "properties": { "chunk_submission_status": { "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ChunkSubmissionStatus" - } + "additionalProperties": false }, "fully_submitted": { "type": "boolean" diff --git a/contracts/coconut-dkg/schema/raw/response_to_get_dealings_metadata.json b/contracts/coconut-dkg/schema/raw/response_to_get_dealings_metadata.json index 1ef4bc443c7..2350f4102e8 100644 --- a/contracts/coconut-dkg/schema/raw/response_to_get_dealings_metadata.json +++ b/contracts/coconut-dkg/schema/raw/response_to_get_dealings_metadata.json @@ -80,9 +80,7 @@ }, "submitted_chunks": { "type": "object", - "additionalProperties": { - "$ref": "#/definitions/SubmittedChunk" - } + "additionalProperties": false } }, "additionalProperties": false diff --git a/contracts/coconut-dkg/src/contract.rs b/contracts/coconut-dkg/src/contract.rs index acc1917b8da..1324abf2778 100644 --- a/contracts/coconut-dkg/src/contract.rs +++ b/contracts/coconut-dkg/src/contract.rs @@ -26,7 +26,7 @@ use crate::verification_key_shares::queries::{query_vk_share, query_vk_shares_pa use crate::verification_key_shares::transactions::try_commit_verification_key_share; use crate::verification_key_shares::transactions::try_verify_verification_key_share; use cosmwasm_std::{ - entry_point, to_binary, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, + entry_point, to_json_binary, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, }; use cw4::Cw4Contract; use nym_coconut_dkg_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; @@ -129,50 +129,52 @@ pub fn execute( #[entry_point] pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result { let response = match msg { - QueryMsg::GetState {} => to_binary(&query_state(deps.storage)?)?, - QueryMsg::GetCurrentEpochState {} => to_binary(&query_current_epoch(deps.storage)?)?, - QueryMsg::CanAdvanceState {} => to_binary(&query_can_advance_state(deps.storage, env)?)?, + QueryMsg::GetState {} => to_json_binary(&query_state(deps.storage)?)?, + QueryMsg::GetCurrentEpochState {} => to_json_binary(&query_current_epoch(deps.storage)?)?, + QueryMsg::CanAdvanceState {} => { + to_json_binary(&query_can_advance_state(deps.storage, env)?)? + } QueryMsg::GetCurrentEpochThreshold {} => { - to_binary(&query_current_epoch_threshold(deps.storage)?)? + to_json_binary(&query_current_epoch_threshold(deps.storage)?)? } QueryMsg::GetEpochThreshold { epoch_id } => { - to_binary(&query_epoch_threshold(deps.storage, epoch_id)?)? + to_json_binary(&query_epoch_threshold(deps.storage, epoch_id)?)? } QueryMsg::GetRegisteredDealer { dealer_address, epoch_id, - } => to_binary(&query_registered_dealer_details( + } => to_json_binary(&query_registered_dealer_details( deps, dealer_address, epoch_id, )?)?, QueryMsg::GetDealerDetails { dealer_address } => { - to_binary(&query_dealer_details(deps, dealer_address)?)? + to_json_binary(&query_dealer_details(deps, dealer_address)?)? } QueryMsg::GetCurrentDealers { limit, start_after } => { - to_binary(&query_current_dealers_paged(deps, start_after, limit)?)? + to_json_binary(&query_current_dealers_paged(deps, start_after, limit)?)? } QueryMsg::GetDealerIndices { limit, start_after } => { - to_binary(&query_dealers_indices_paged(deps, start_after, limit)?)? + to_json_binary(&query_dealers_indices_paged(deps, start_after, limit)?)? } QueryMsg::GetDealingsMetadata { epoch_id, dealer, dealing_index, - } => to_binary(&query_dealing_metadata( + } => to_json_binary(&query_dealing_metadata( deps, epoch_id, dealer, dealing_index, )?)?, QueryMsg::GetDealerDealingsStatus { epoch_id, dealer } => { - to_binary(&query_dealer_dealings_status(deps, epoch_id, dealer)?)? + to_json_binary(&query_dealer_dealings_status(deps, epoch_id, dealer)?)? } QueryMsg::GetDealingStatus { epoch_id, dealer, dealing_index, - } => to_binary(&query_dealing_status( + } => to_json_binary(&query_dealing_status( deps, epoch_id, dealer, @@ -183,7 +185,7 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result to_binary(&query_dealing_chunk_status( + } => to_json_binary(&query_dealing_chunk_status( deps, epoch_id, dealer, @@ -195,7 +197,7 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result to_binary(&query_dealing_chunk( + } => to_json_binary(&query_dealing_chunk( deps, epoch_id, dealer, @@ -203,14 +205,16 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result { - to_binary(&query_vk_share(deps, owner, epoch_id)?)? + to_json_binary(&query_vk_share(deps, owner, epoch_id)?)? } QueryMsg::GetVerificationKeys { epoch_id, limit, start_after, - } => to_binary(&query_vk_shares_paged(deps, epoch_id, start_after, limit)?)?, - QueryMsg::GetCW2ContractVersion {} => to_binary(&cw2::get_contract_version(deps.storage)?)?, + } => to_json_binary(&query_vk_shares_paged(deps, epoch_id, start_after, limit)?)?, + QueryMsg::GetCW2ContractVersion {} => { + to_json_binary(&cw2::get_contract_version(deps.storage)?)? + } }; Ok(response) @@ -236,7 +240,7 @@ mod tests { use super::*; use crate::support::tests::fixtures::TEST_MIX_DENOM; use crate::support::tests::helpers::{ADMIN_ADDRESS, MULTISIG_CONTRACT}; - use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + use cosmwasm_std::testing::{message_info, mock_dependencies, mock_env, MockApi}; use cosmwasm_std::{coins, Addr}; use cw4::Member; use cw_multi_test::{App, AppBuilder, AppResponse, ContractWrapper, Executor}; @@ -311,13 +315,13 @@ mod tests { let mut deps = mock_dependencies(); let env = mock_env(); let msg = InstantiateMsg { - group_addr: "group_addr".to_string(), - multisig_addr: "multisig_addr".to_string(), + group_addr: deps.api.addr_make("group_addr").to_string(), + multisig_addr: deps.api.addr_make("multisig_addr").to_string(), time_configuration: None, mix_denom: "nym".to_string(), key_size: 5, }; - let info = mock_info("creator", &[]); + let info = message_info(&deps.api.addr_make("creator"), &[]); let res = instantiate(deps.as_mut(), env, info, msg); assert!(res.is_ok()) @@ -326,9 +330,11 @@ mod tests { #[test] fn execute_add_dealer() { let init_funds = coins(100, TEST_MIX_DENOM); + + let api = MockApi::default(); const MEMBER_SIZE: usize = 100; let members: [Addr; MEMBER_SIZE] = - std::array::from_fn(|idx| Addr::unchecked(format!("member{}", idx))); + std::array::from_fn(|idx| api.addr_make(&format!("member{}", idx))); let mut app = AppBuilder::new().build(|router, _, storage| { router @@ -378,7 +384,7 @@ mod tests { assert_eq!(ContractError::AlreadyADealer, err.downcast().unwrap()); } - let unauthorized_member = Addr::unchecked("not_a_member"); + let unauthorized_member = MockApi::default().addr_make("not_a_member"); let err = app .execute_contract( unauthorized_member, diff --git a/contracts/coconut-dkg/src/dealers/queries.rs b/contracts/coconut-dkg/src/dealers/queries.rs index 9abf90c218e..b092d8c140b 100644 --- a/contracts/coconut-dkg/src/dealers/queries.rs +++ b/contracts/coconut-dkg/src/dealers/queries.rs @@ -131,21 +131,30 @@ pub(crate) mod tests { use crate::dealers::storage::{DEALERS_PAGE_DEFAULT_LIMIT, DEALERS_PAGE_MAX_LIMIT}; use crate::support::tests::fixtures::dealer_details_fixture; use crate::support::tests::helpers::{init_contract, insert_dealer}; - use cosmwasm_std::DepsMut; - - fn fill_dealers(mut deps: DepsMut<'_>, epoch_id: EpochId, size: usize) { + use cosmwasm_std::testing::{MockApi, MockQuerier}; + use cosmwasm_std::{Empty, MemoryStorage, OwnedDeps}; + + fn fill_dealers( + deps: &mut OwnedDeps>, + epoch_id: EpochId, + size: usize, + ) { for assigned_index in 0..size { - let dealer_details = dealer_details_fixture(assigned_index as u64); - insert_dealer(deps.branch(), epoch_id, &dealer_details); + let dealer_details = dealer_details_fixture(&deps.api, assigned_index as u64); + insert_dealer(deps.as_mut(), epoch_id, &dealer_details); } } - fn remove_dealers(deps: DepsMut<'_>, epoch_id: EpochId, size: usize) { + fn remove_dealers( + deps: &mut OwnedDeps>, + epoch_id: EpochId, + size: usize, + ) { for assigned_index in 0..size { - let dealer_details = dealer_details_fixture(assigned_index as u64); - DEALERS_INDICES.remove(deps.storage, &dealer_details.address); + let dealer_details = dealer_details_fixture(&deps.api, assigned_index as u64); + DEALERS_INDICES.remove(deps.as_mut().storage, &dealer_details.address); - EPOCH_DEALERS_MAP.remove(deps.storage, (epoch_id, &dealer_details.address)); + EPOCH_DEALERS_MAP.remove(deps.as_mut().storage, (epoch_id, &dealer_details.address)); } } @@ -162,26 +171,26 @@ pub(crate) mod tests { let mut deps = init_contract(); let limit = 2; - fill_dealers(deps.as_mut(), 0, 1000); + fill_dealers(&mut deps, 0, 1000); let page1 = query_current_dealers_paged(deps.as_ref(), None, Option::from(limit)).unwrap(); assert_eq!(limit, page1.dealers.len() as u32); - remove_dealers(deps.as_mut(), 0, 1000); + remove_dealers(&mut deps, 0, 1000); } #[test] fn dealers_paged_retrieval_has_default_limit() { let mut deps = init_contract(); - fill_dealers(deps.as_mut(), 0, 1000); + fill_dealers(&mut deps, 0, 1000); // query without explicitly setting a limit let page1 = query_current_dealers_paged(deps.as_ref(), None, None).unwrap(); assert_eq!(DEALERS_PAGE_DEFAULT_LIMIT, page1.dealers.len() as u32); - remove_dealers(deps.as_mut(), 0, 1000); + remove_dealers(&mut deps, 0, 1000); } #[test] @@ -191,7 +200,7 @@ pub(crate) mod tests { // query with a crazily high limit in an attempt to use too many resources let crazy_limit = 1000 * DEALERS_PAGE_MAX_LIMIT; - fill_dealers(deps.as_mut(), 0, 1000); + fill_dealers(&mut deps, 0, 1000); let page1 = query_current_dealers_paged(deps.as_ref(), None, Option::from(crazy_limit)).unwrap(); @@ -200,7 +209,7 @@ pub(crate) mod tests { let expected_limit = DEALERS_PAGE_MAX_LIMIT; assert_eq!(expected_limit, page1.dealers.len() as u32); - remove_dealers(deps.as_mut(), 0, 1000); + remove_dealers(&mut deps, 0, 1000); } #[test] @@ -209,22 +218,22 @@ pub(crate) mod tests { let per_page = 2; - fill_dealers(deps.as_mut(), 0, 1); + fill_dealers(&mut deps, 0, 1); let page1 = query_current_dealers_paged(deps.as_ref(), None, Option::from(per_page)).unwrap(); // page should have 1 result on it assert_eq!(1, page1.dealers.len()); - remove_dealers(deps.as_mut(), 0, 1); + remove_dealers(&mut deps, 0, 1); - fill_dealers(deps.as_mut(), 0, 2); + fill_dealers(&mut deps, 0, 2); // page1 should have 2 results on it let page1 = query_current_dealers_paged(deps.as_ref(), None, Option::from(per_page)).unwrap(); assert_eq!(2, page1.dealers.len()); - remove_dealers(deps.as_mut(), 0, 2); + remove_dealers(&mut deps, 0, 2); - fill_dealers(deps.as_mut(), 0, 3); + fill_dealers(&mut deps, 0, 3); // page1 still has 2 results let page1 = query_current_dealers_paged(deps.as_ref(), None, Option::from(per_page)).unwrap(); @@ -240,9 +249,9 @@ pub(crate) mod tests { .unwrap(); assert_eq!(1, page2.dealers.len()); - remove_dealers(deps.as_mut(), 0, 3); + remove_dealers(&mut deps, 0, 3); - fill_dealers(deps.as_mut(), 0, 4); + fill_dealers(&mut deps, 0, 4); let page1 = query_current_dealers_paged(deps.as_ref(), None, Option::from(per_page)).unwrap(); let start_after = page1.start_next_after.unwrap(); @@ -255,6 +264,6 @@ pub(crate) mod tests { // now we have 2 pages, with 2 results on the second page assert_eq!(2, page2.dealers.len()); - remove_dealers(deps.as_mut(), 0, 4); + remove_dealers(&mut deps, 0, 4); } } diff --git a/contracts/coconut-dkg/src/dealers/transactions.rs b/contracts/coconut-dkg/src/dealers/transactions.rs index e534d072bc0..a7fee911085 100644 --- a/contracts/coconut-dkg/src/dealers/transactions.rs +++ b/contracts/coconut-dkg/src/dealers/transactions.rs @@ -88,7 +88,7 @@ pub(crate) mod tests { use crate::epoch_state::transactions::{try_advance_epoch_state, try_initiate_dkg}; use crate::support::tests::helpers; use crate::support::tests::helpers::{add_fixture_dealer, ADMIN_ADDRESS}; - use cosmwasm_std::testing::{mock_env, mock_info}; + use cosmwasm_std::testing::{message_info, mock_env}; use cosmwasm_std::Addr; use nym_coconut_dkg_common::types::TimeConfiguration; @@ -96,10 +96,15 @@ pub(crate) mod tests { fn invalid_state() { let mut deps = helpers::init_contract(); let mut env = mock_env(); - try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap(); + try_initiate_dkg( + deps.as_mut(), + env.clone(), + message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]), + ) + .unwrap(); - let owner = Addr::unchecked("owner"); - let info = mock_info(owner.as_str(), &[]); + let owner = deps.api.addr_make("owner"); + let info = message_info(&owner, &[]); let bte_key_with_proof = String::from("bte_key_with_proof"); let identity = String::from("identity"); let announce_address = String::from("localhost:8000"); diff --git a/contracts/coconut-dkg/src/dealings/queries.rs b/contracts/coconut-dkg/src/dealings/queries.rs index 0bedba014bf..d4de607c6d1 100644 --- a/contracts/coconut-dkg/src/dealings/queries.rs +++ b/contracts/coconut-dkg/src/dealings/queries.rs @@ -168,29 +168,26 @@ pub(crate) mod tests { let mut deps = init_contract(); let bad_address = "FOOMP".to_string(); + let good_address = deps.api.addr_make("foo"); assert!(query_dealing_chunk(deps.as_ref(), 0, bad_address, 0, 0).is_err()); - let empty = query_dealing_chunk(deps.as_ref(), 0, "foo".to_string(), 0, 0).unwrap(); + let empty = query_dealing_chunk(deps.as_ref(), 0, good_address.to_string(), 0, 0).unwrap(); assert_eq!(empty.epoch_id, 0); assert_eq!(empty.dealing_index, 0); assert_eq!(empty.chunk_index, 0); - assert_eq!(empty.dealer, Addr::unchecked("foo")); + assert_eq!(empty.dealer, good_address); assert!(empty.chunk.is_none()); // insert the dealing chunk let dealing = partial_dealing_fixture(); - StoredDealing::save( - deps.as_mut().storage, - 0, - &Addr::unchecked("foo"), - dealing.clone(), - ); - - let retrieved = query_dealing_chunk(deps.as_ref(), 0, "foo".to_string(), 0, 0).unwrap(); + StoredDealing::save(deps.as_mut().storage, 0, &good_address, dealing.clone()); + + let retrieved = + query_dealing_chunk(deps.as_ref(), 0, good_address.to_string(), 0, 0).unwrap(); assert_eq!(retrieved.epoch_id, 0); assert_eq!(retrieved.dealing_index, dealing.dealing_index); assert_eq!(retrieved.chunk_index, dealing.chunk_index); - assert_eq!(retrieved.dealer, Addr::unchecked("foo")); + assert_eq!(retrieved.dealer, good_address); assert_eq!(retrieved.chunk.unwrap(), dealing.data); } @@ -201,10 +198,12 @@ pub(crate) mod tests { let bad_address = "FOOMP".to_string(); assert!(query_dealing_status(deps.as_ref(), 0, bad_address, 0).is_err()); - let empty = query_dealing_status(deps.as_ref(), 0, "foo".to_string(), 0).unwrap(); + let empty = + query_dealing_status(deps.as_ref(), 0, deps.api.addr_make("foo").to_string(), 0) + .unwrap(); assert_eq!(empty.epoch_id, 0); assert_eq!(empty.dealing_index, 0); - assert_eq!(empty.dealer, Addr::unchecked("foo")); + assert_eq!(empty.dealer, deps.api.addr_make("foo")); assert!(!empty.status.fully_submitted); assert!(!empty.status.has_metadata); assert!(empty.status.chunk_submission_status.is_empty()); diff --git a/contracts/coconut-dkg/src/dealings/storage.rs b/contracts/coconut-dkg/src/dealings/storage.rs index 6e2a4aaa90b..27ef0fd3fdc 100644 --- a/contracts/coconut-dkg/src/dealings/storage.rs +++ b/contracts/coconut-dkg/src/dealings/storage.rs @@ -77,17 +77,10 @@ impl StoredDealing { #[cfg(test)] fn prefix( prefix: (EpochId, Dealer, DealingIndex), - ) -> cw_storage_plus::Prefix { + ) -> cw_storage_plus::Prefix { use cw_storage_plus::Prefixer; - cw_storage_plus::Prefix::with_deserialization_functions( - Self::NAMESPACE, - &prefix.prefix(), - &[], - // explicitly panic to make sure we're never attempting to call an unexpected deserializer on our data - |_, _, kv| Self::deserialize_dealing_record(kv), - |_, _, _| panic!("attempted to call custom de_fn_v"), - ) + cw_storage_plus::Prefix::new(Self::NAMESPACE, &prefix.prefix()) } // prefix-range related should we need it @@ -98,15 +91,23 @@ impl StoredDealing { start: Option>, ) -> impl Iterator> + 'a { let dealing_index = prefix.2; - Self::prefix(prefix) - .range(storage, start, None, cosmwasm_std::Order::Ascending) - .map(move |maybe_record| { - maybe_record.map(|(chunk_index, data)| PartialContractDealing { - dealing_index, - chunk_index, - data, - }) + let prefix = Self::prefix(prefix); + + cw_storage_plus::range_with_prefix( + storage, + &prefix, + start.map(|b| b.to_raw_bound()), + None, + cosmwasm_std::Order::Ascending, + ) + .map(Self::deserialize_dealing_record) + .map(move |maybe_record| { + maybe_record.map(|(chunk_index, data)| PartialContractDealing { + dealing_index, + chunk_index, + data, }) + }) } fn storage_key( @@ -186,22 +187,19 @@ impl StoredDealing { type StorageKey<'a> = (EpochId, Dealer<'a>, (DealingIndex, ChunkIndex)); - let empty_prefix: cw_storage_plus::Prefix< - StorageKey, - PartialContractDealingData, - StorageKey, - > = cw_storage_plus::Prefix::with_deserialization_functions( - Self::NAMESPACE, - &[], - &[], - |_, _, kv| StorageKey::from_vec(kv.0).map(|kt| (kt, ContractSafeBytes(kv.1))), - |_, _, _| unimplemented!(), - ); + let empty_prefix: cw_storage_plus::Prefix = + cw_storage_plus::Prefix::new(Self::NAMESPACE, &[]); - empty_prefix - .range(storage, None, None, cosmwasm_std::Order::Ascending) - .collect::>() - .unwrap() + cw_storage_plus::range_with_prefix( + storage, + &empty_prefix, + None, + None, + cosmwasm_std::Order::Ascending, + ) + .map(|kv| StorageKey::from_vec(kv.0).map(|kt| (kt, ContractSafeBytes(kv.1)))) + .collect::>() + .unwrap() } } diff --git a/contracts/coconut-dkg/src/dealings/transactions.rs b/contracts/coconut-dkg/src/dealings/transactions.rs index 694ce7f3f7b..73107c352f7 100644 --- a/contracts/coconut-dkg/src/dealings/transactions.rs +++ b/contracts/coconut-dkg/src/dealings/transactions.rs @@ -210,7 +210,7 @@ pub(crate) mod tests { use crate::support::tests::fixtures::{dealing_metadata_fixture, partial_dealing_fixture}; use crate::support::tests::helpers; use crate::support::tests::helpers::{add_current_dealer, re_register_dealer, ADMIN_ADDRESS}; - use cosmwasm_std::testing::{mock_env, mock_info}; + use cosmwasm_std::testing::{message_info, mock_env}; use nym_coconut_dkg_common::dealer::DealerDetails; use nym_coconut_dkg_common::types::{ContractSafeBytes, TimeConfiguration}; @@ -218,10 +218,15 @@ pub(crate) mod tests { fn invalid_commit_dealing_chunk() { let mut deps = helpers::init_contract(); let mut env = mock_env(); - try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap(); + try_initiate_dkg( + deps.as_mut(), + env.clone(), + message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]), + ) + .unwrap(); - let owner = Addr::unchecked("owner1"); - let info = mock_info(owner.as_str(), &[]); + let owner = deps.api.addr_make("owner1"); + let info = message_info(&owner, &[]); let chunk = partial_dealing_fixture(); // no dealing metadata diff --git a/contracts/coconut-dkg/src/epoch_state/queries.rs b/contracts/coconut-dkg/src/epoch_state/queries.rs index 4e05cae8219..15ba780aec4 100644 --- a/contracts/coconut-dkg/src/epoch_state/queries.rs +++ b/contracts/coconut-dkg/src/epoch_state/queries.rs @@ -57,7 +57,8 @@ pub(crate) mod test { use super::*; use crate::epoch_state::transactions::try_initiate_dkg; use crate::support::tests::helpers::{init_contract, ADMIN_ADDRESS}; - use cosmwasm_std::testing::{mock_env, mock_info}; + use cosmwasm_std::testing::{message_info, mock_env}; + use cosmwasm_std::Addr; use nym_coconut_dkg_common::types::TimeConfiguration; #[test] @@ -68,7 +69,12 @@ pub(crate) mod test { assert_eq!(epoch.deadline, None); let env = mock_env(); - try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap(); + try_initiate_dkg( + deps.as_mut(), + env.clone(), + message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]), + ) + .unwrap(); let epoch = query_current_epoch(deps.as_mut().storage).unwrap(); assert_eq!( diff --git a/contracts/coconut-dkg/src/epoch_state/storage.rs b/contracts/coconut-dkg/src/epoch_state/storage.rs index 0ab1d9ab9c3..faed18e4311 100644 --- a/contracts/coconut-dkg/src/epoch_state/storage.rs +++ b/contracts/coconut-dkg/src/epoch_state/storage.rs @@ -4,7 +4,7 @@ use cw_storage_plus::{Item, Map}; use nym_coconut_dkg_common::types::{Epoch, EpochId}; -pub(crate) const CURRENT_EPOCH: Item<'_, Epoch> = Item::new("current_epoch"); +pub(crate) const CURRENT_EPOCH: Item = Item::new("current_epoch"); pub const THRESHOLD: Item = Item::new("threshold"); diff --git a/contracts/coconut-dkg/src/epoch_state/transactions/advance_epoch_state.rs b/contracts/coconut-dkg/src/epoch_state/transactions/advance_epoch_state.rs index 6eda1951808..cfabdc700da 100644 --- a/contracts/coconut-dkg/src/epoch_state/transactions/advance_epoch_state.rs +++ b/contracts/coconut-dkg/src/epoch_state/transactions/advance_epoch_state.rs @@ -95,8 +95,8 @@ mod tests { use crate::error::ContractError::EarlyEpochStateAdvancement; use crate::state::storage::STATE; use crate::support::tests::helpers::{init_contract, ADMIN_ADDRESS}; - use cosmwasm_std::testing::{mock_env, mock_info}; - use cosmwasm_std::{StdResult, Storage}; + use cosmwasm_std::testing::{message_info, mock_env}; + use cosmwasm_std::{Addr, StdResult, Storage}; use nym_coconut_dkg_common::types::TimeConfiguration; #[test] @@ -359,7 +359,12 @@ mod tests { ContractError::WaitingInitialisation ); - try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap(); + try_initiate_dkg( + deps.as_mut(), + env.clone(), + message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]), + ) + .unwrap(); let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap(); assert_eq!( @@ -584,7 +589,12 @@ mod tests { fn verify_threshold() { let mut deps = init_contract(); let mut env = mock_env(); - try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap(); + try_initiate_dkg( + deps.as_mut(), + env.clone(), + message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]), + ) + .unwrap(); assert!(THRESHOLD.may_load(deps.as_mut().storage).unwrap().is_none()); diff --git a/contracts/coconut-dkg/src/epoch_state/transactions/mod.rs b/contracts/coconut-dkg/src/epoch_state/transactions/mod.rs index 8922abc1ca8..73d548cdebd 100644 --- a/contracts/coconut-dkg/src/epoch_state/transactions/mod.rs +++ b/contracts/coconut-dkg/src/epoch_state/transactions/mod.rs @@ -90,7 +90,8 @@ pub(crate) fn try_trigger_resharing( pub(crate) mod tests { use super::*; use crate::support::tests::helpers::{init_contract, ADMIN_ADDRESS}; - use cosmwasm_std::testing::{mock_env, mock_info}; + use cosmwasm_std::testing::{message_info, mock_env}; + use cosmwasm_std::Addr; use cw_controllers::AdminError; #[test] @@ -101,17 +102,26 @@ pub(crate) mod tests { let initial_epoch_info = CURRENT_EPOCH.load(&deps.storage).unwrap(); assert!(initial_epoch_info.deadline.is_none()); + let not_admin = deps.api.addr_make("not an admin"); // can only be executed by the admin - let res = try_initiate_dkg(deps.as_mut(), env.clone(), mock_info("not an admin", &[])) + let res = try_initiate_dkg(deps.as_mut(), env.clone(), message_info(¬_admin, &[])) .unwrap_err(); assert_eq!(ContractError::Admin(AdminError::NotAdmin {}), res); - let res = try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])); + let res = try_initiate_dkg( + deps.as_mut(), + env.clone(), + message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]), + ); assert!(res.is_ok()); // can't be initialised more than once - let res = try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])) - .unwrap_err(); + let res = try_initiate_dkg( + deps.as_mut(), + env.clone(), + message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]), + ) + .unwrap_err(); assert_eq!(ContractError::AlreadyInitialised, res); // sets the correct epoch data diff --git a/contracts/coconut-dkg/src/support/tests/fixtures.rs b/contracts/coconut-dkg/src/support/tests/fixtures.rs index 85995024499..04dde903587 100644 --- a/contracts/coconut-dkg/src/support/tests/fixtures.rs +++ b/contracts/coconut-dkg/src/support/tests/fixtures.rs @@ -1,6 +1,7 @@ // Copyright 2022-2024 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use cosmwasm_std::testing::MockApi; use cosmwasm_std::Addr; use nym_coconut_dkg_common::dealer::DealerDetails; use nym_coconut_dkg_common::dealing::{DealingChunkInfo, PartialContractDealing}; @@ -40,9 +41,9 @@ pub fn dealing_metadata_fixture() -> Vec { }] } -pub fn dealer_details_fixture(assigned_index: u64) -> DealerDetails { +pub fn dealer_details_fixture(api: &MockApi, assigned_index: u64) -> DealerDetails { DealerDetails { - address: Addr::unchecked(format!("owner{}", assigned_index)), + address: api.addr_make(&format!("owner{}", assigned_index)), bte_public_key_with_proof: "".to_string(), ed25519_identity: "".to_string(), announce_address: "".to_string(), diff --git a/contracts/coconut-dkg/src/support/tests/helpers.rs b/contracts/coconut-dkg/src/support/tests/helpers.rs index ce0e370d557..d5678885771 100644 --- a/contracts/coconut-dkg/src/support/tests/helpers.rs +++ b/contracts/coconut-dkg/src/support/tests/helpers.rs @@ -4,12 +4,13 @@ use crate::contract::instantiate; use crate::dealers::storage::{DEALERS_INDICES, EPOCH_DEALERS_MAP}; use crate::epoch_state::storage::CURRENT_EPOCH; -use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier}; +use cosmwasm_std::testing::{message_info, mock_dependencies, mock_env, MockApi, MockQuerier}; use cosmwasm_std::{ - from_binary, to_binary, Addr, ContractResult, DepsMut, Empty, MemoryStorage, OwnedDeps, + from_json, to_json_binary, Addr, ContractResult, DepsMut, Empty, MemoryStorage, OwnedDeps, QuerierResult, SystemResult, WasmQuery, }; use cw4::{Cw4QueryMsg, Member, MemberListResponse, MemberResponse}; +use easy_addr::addr; use nym_coconut_dkg_common::dealer::DealerRegistrationDetails; use nym_coconut_dkg_common::dealing::DEFAULT_DEALINGS; use nym_coconut_dkg_common::msg::InstantiateMsg; @@ -18,9 +19,9 @@ use std::sync::Mutex; use super::fixtures::TEST_MIX_DENOM; -pub const ADMIN_ADDRESS: &str = "admin address"; -pub const GROUP_CONTRACT: &str = "group contract address"; -pub const MULTISIG_CONTRACT: &str = "multisig contract address"; +pub const ADMIN_ADDRESS: &str = addr!("admin address"); +pub const GROUP_CONTRACT: &str = addr!("group contract address"); +pub const MULTISIG_CONTRACT: &str = addr!("multisig contract address"); // wtf, why is this a thing? pub(crate) static GROUP_MEMBERS: Mutex> = Mutex::new(Vec::new()); @@ -79,7 +80,7 @@ fn querier_handler(query: &WasmQuery) -> QuerierResult { if contract_addr != GROUP_CONTRACT { panic!("Not supported"); } - match from_binary(msg) { + match from_json(msg) { Ok(Cw4QueryMsg::Member { addr, at_height }) => { let weight = GROUP_MEMBERS.lock().unwrap().iter().find_map(|(m, h)| { if m.addr == addr { @@ -93,7 +94,7 @@ fn querier_handler(query: &WasmQuery) -> QuerierResult { None } }); - to_binary(&MemberResponse { weight }).unwrap() + to_json_binary(&MemberResponse { weight }).unwrap() } Ok(Cw4QueryMsg::ListMembers { .. }) => { let members = GROUP_MEMBERS @@ -102,7 +103,7 @@ fn querier_handler(query: &WasmQuery) -> QuerierResult { .iter() .map(|m| m.0.clone()) .collect(); - to_binary(&MemberListResponse { members }).unwrap() + to_json_binary(&MemberListResponse { members }).unwrap() } _ => panic!("Not supported"), } @@ -123,7 +124,7 @@ pub fn init_contract() -> OwnedDeps> key_size: DEFAULT_DEALINGS as u32, }; let env = mock_env(); - let info = mock_info(ADMIN_ADDRESS, &[]); + let info = message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]); instantiate(deps.as_mut(), env, info, msg).unwrap(); deps } diff --git a/contracts/coconut-dkg/src/verification_key_shares/queries.rs b/contracts/coconut-dkg/src/verification_key_shares/queries.rs index 77abfd8d6eb..e0254eebdd4 100644 --- a/contracts/coconut-dkg/src/verification_key_shares/queries.rs +++ b/contracts/coconut-dkg/src/verification_key_shares/queries.rs @@ -158,8 +158,17 @@ pub(crate) mod tests { fn vk_shares_pagination_works() { let mut deps = init_contract(); - let owner = format!("owner{}", 1); - let vk_share = vk_share_fixture(&owner, 0); + let mut owners = [ + deps.api.addr_make("owner1"), + deps.api.addr_make("owner2"), + deps.api.addr_make("owner3"), + deps.api.addr_make("owner4"), + ]; + // sort them due to how values are saved + owners.sort(); + + let owner = owners[0].clone(); + let vk_share = vk_share_fixture(owner.as_ref(), 0); let sender = Addr::unchecked(owner); vk_shares() .save(&mut deps.storage, (&sender, 0), &vk_share) @@ -172,8 +181,8 @@ pub(crate) mod tests { assert_eq!(1, page1.shares.len()); // save another - let owner = format!("owner{}", 2); - let vk_share = vk_share_fixture(&owner, 0); + let owner = owners[1].clone(); + let vk_share = vk_share_fixture(owner.as_str(), 0); let sender = Addr::unchecked(owner); vk_shares() .save(&mut deps.storage, (&sender, 0), &vk_share) @@ -183,8 +192,8 @@ pub(crate) mod tests { let page1 = query_vk_shares_paged(deps.as_ref(), 0, None, Option::from(per_page)).unwrap(); assert_eq!(2, page1.shares.len()); - let owner = format!("owner{}", 3); - let vk_share = vk_share_fixture(&owner, 0); + let owner = owners[2].clone(); + let vk_share = vk_share_fixture(owner.as_str(), 0); let sender = Addr::unchecked(owner); vk_shares() .save(&mut deps.storage, (&sender, 0), &vk_share) @@ -206,8 +215,8 @@ pub(crate) mod tests { assert_eq!(1, page2.shares.len()); - let owner = format!("owner{}", 4); - let vk_share = vk_share_fixture(&owner, 0); + let owner = owners[3].clone(); + let vk_share = vk_share_fixture(owner.as_str(), 0); let sender = Addr::unchecked(owner); vk_shares() .save(&mut deps.storage, (&sender, 0), &vk_share) diff --git a/contracts/coconut-dkg/src/verification_key_shares/storage.rs b/contracts/coconut-dkg/src/verification_key_shares/storage.rs index e9f92dfbdc8..091787805e0 100644 --- a/contracts/coconut-dkg/src/verification_key_shares/storage.rs +++ b/contracts/coconut-dkg/src/verification_key_shares/storage.rs @@ -23,7 +23,7 @@ impl IndexList for VkShareIndex<'_> { } } -pub(crate) fn vk_shares<'a>() -> IndexedMap<'a, VKShareKey<'a>, ContractVKShare, VkShareIndex<'a>> { +pub(crate) fn vk_shares<'a>() -> IndexedMap, ContractVKShare, VkShareIndex<'a>> { let indexes = VkShareIndex { epoch_id: MultiIndex::new( |_pk, d| d.epoch_id, diff --git a/contracts/coconut-dkg/src/verification_key_shares/transactions.rs b/contracts/coconut-dkg/src/verification_key_shares/transactions.rs index f8f4534a60c..cff0c882323 100644 --- a/contracts/coconut-dkg/src/verification_key_shares/transactions.rs +++ b/contracts/coconut-dkg/src/verification_key_shares/transactions.rs @@ -106,7 +106,7 @@ mod tests { use crate::support::tests::helpers::{ add_current_dealer, add_fixture_dealer, ADMIN_ADDRESS, MULTISIG_CONTRACT, }; - use cosmwasm_std::testing::{mock_env, mock_info}; + use cosmwasm_std::testing::{message_info, mock_env}; use cosmwasm_std::Addr; use cw_controllers::AdminError; use nym_coconut_dkg_common::dealer::DealerDetails; @@ -116,9 +116,14 @@ mod tests { fn current_epoch_id() { let mut deps = helpers::init_contract(); let mut env = mock_env(); - try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap(); + try_initiate_dkg( + deps.as_mut(), + env.clone(), + message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]), + ) + .unwrap(); - let info = mock_info("requester", &[]); + let info = message_info(&deps.api.addr_make("requester"), &[]); let share = "share".to_string(); add_fixture_dealer(deps.as_mut()); @@ -132,7 +137,7 @@ mod tests { .time .plus_seconds(TimeConfiguration::default().dealing_exchange_time_secs); try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap(); - let dealer = Addr::unchecked("requester"); + let dealer = deps.api.addr_make("requester"); let announce_address = String::from("localhost"); let dealer_details = DealerDetails { address: dealer.clone(), @@ -163,9 +168,14 @@ mod tests { fn commit_vk_share() { let mut deps = helpers::init_contract(); let mut env = mock_env(); - try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap(); + try_initiate_dkg( + deps.as_mut(), + env.clone(), + message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]), + ) + .unwrap(); - let info = mock_info("requester", &[]); + let info = message_info(&deps.api.addr_make("requester"), &[]); let share = "share".to_string(); let ret = try_commit_verification_key_share( @@ -205,7 +215,7 @@ mod tests { .unwrap_err(); assert_eq!(ret, ContractError::NotADealer { epoch_id: 0 }); - let dealer = Addr::unchecked("requester"); + let dealer = deps.api.addr_make("requester"); let dealer_details = DealerDetails { address: dealer.clone(), bte_public_key_with_proof: String::new(), @@ -238,11 +248,16 @@ mod tests { fn invalid_verify_vk_share() { let mut deps = helpers::init_contract(); let mut env = mock_env(); - try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap(); + try_initiate_dkg( + deps.as_mut(), + env.clone(), + message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]), + ) + .unwrap(); - let info = mock_info("requester", &[]); - let owner = "owner".to_string(); - let multisig_info = mock_info(MULTISIG_CONTRACT, &[]); + let info = message_info(&deps.api.addr_make("requester"), &[]); + let owner = deps.api.addr_make("owner").to_string(); + let multisig_info = message_info(&Addr::unchecked(MULTISIG_CONTRACT), &[]); let ret = try_verify_verification_key_share(deps.as_mut(), info.clone(), owner.clone(), false) @@ -297,12 +312,17 @@ mod tests { fn verify_vk_share() { let mut deps = helpers::init_contract(); let mut env = mock_env(); - try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap(); + try_initiate_dkg( + deps.as_mut(), + env.clone(), + message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]), + ) + .unwrap(); - let owner = "owner".to_string(); - let info = mock_info(owner.as_ref(), &[]); + let owner = deps.api.addr_make("owner"); + let info = message_info(&owner, &[]); let share = "share".to_string(); - let multisig_info = mock_info(MULTISIG_CONTRACT, &[]); + let multisig_info = message_info(&Addr::unchecked(MULTISIG_CONTRACT), &[]); add_fixture_dealer(deps.as_mut()); env.block.time = env @@ -338,6 +358,7 @@ mod tests { .plus_seconds(TimeConfiguration::default().verification_key_validation_time_secs); try_advance_epoch_state(deps.as_mut(), env).unwrap(); - try_verify_verification_key_share(deps.as_mut(), multisig_info, owner, false).unwrap(); + try_verify_verification_key_share(deps.as_mut(), multisig_info, owner.to_string(), false) + .unwrap(); } } diff --git a/contracts/coconut-test/Cargo.toml b/contracts/coconut-test/Cargo.toml deleted file mode 100644 index 8b174175df4..00000000000 --- a/contracts/coconut-test/Cargo.toml +++ /dev/null @@ -1,39 +0,0 @@ -[package] -name = "coconut-test" -version = "0.1.0" -edition = "2021" -publish = false - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -nym-coconut-bandwidth-contract-common = { path = "../../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" } -nym-coconut-dkg-common = { path = "../../common/cosmwasm-smart-contracts/coconut-dkg" } -nym-multisig-contract-common = { path = "../../common/cosmwasm-smart-contracts/multisig-contract" } -nym-group-contract-common = { path = "../../common/cosmwasm-smart-contracts/group-contract" } - -cosmwasm-std = { workspace = true } -cosmwasm-storage = { workspace = true } -cw3 = { workspace = true } -cw4 = { workspace = true } -cw-storage-plus = { workspace = true } -cw-controllers = { workspace = true } -cw-utils = { workspace = true } - -subtle-encoding = { version = "0.5", features = ["bech32-preview"] } -bs58 = "0.4.0" -schemars = "0.8" -serde = { version = "1.0.103", default-features = false, features = ["derive"] } -thiserror = { workspace = true } - -nym-coconut-bandwidth = { path = "../coconut-bandwidth" } -nym-coconut-dkg = { path = "../coconut-dkg" } -cw-multi-test = { workspace = true } -cw3-flex-multisig = { path = "../multisig/cw3-flex-multisig" } -cw4-group = { path = "../multisig/cw4-group" } - -rand_chacha = "0.3" - -[[test]] -name = "coconut-test" -path = "src/tests.rs" diff --git a/contracts/coconut-test/src/deposit_and_release.rs b/contracts/coconut-test/src/deposit_and_release.rs deleted file mode 100644 index 4468a79ebc7..00000000000 --- a/contracts/coconut-test/src/deposit_and_release.rs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::helpers::*; -use cosmwasm_std::{coins, Addr}; -use cw_controllers::AdminError; -use cw_multi_test::Executor; -use nym_coconut_bandwidth::error::ContractError; -use nym_coconut_bandwidth_contract_common::{ - deposit::DepositData, - msg::{ExecuteMsg, InstantiateMsg}, -}; - -const TEST_MIX_DENOM: &str = "unym"; - -#[test] -fn deposit_and_release() { - let init_funds = coins(10, TEST_MIX_DENOM); - let deposit_funds = coins(1, TEST_MIX_DENOM); - let release_funds = coins(2, TEST_MIX_DENOM); - let mut app = mock_app(&init_funds); - let multisig_addr = String::from(MULTISIG_CONTRACT); - let pool_addr = String::from(POOL_CONTRACT); - let random_addr = String::from(RANDOM_ADDRESS); - - let code_id = app.store_code(contract_bandwidth()); - let msg = InstantiateMsg { - multisig_addr: multisig_addr.clone(), - pool_addr: pool_addr.clone(), - mix_denom: TEST_MIX_DENOM.to_string(), - }; - let contract_addr = app - .instantiate_contract( - code_id, - Addr::unchecked(OWNER), - &msg, - &[], - "bandwidth", - None, - ) - .unwrap(); - - let msg = ExecuteMsg::DepositFunds { - data: DepositData::new( - String::from("info"), - String::from("id"), - String::from("enc"), - ), - }; - app.execute_contract( - Addr::unchecked(OWNER), - contract_addr.clone(), - &msg, - &deposit_funds, - ) - .unwrap(); - - // try to release more then it's in the contract - let msg = ExecuteMsg::ReleaseFunds { - funds: release_funds[0].clone(), - }; - let err = app - .execute_contract( - Addr::unchecked(multisig_addr.clone()), - contract_addr.clone(), - &msg, - &[], - ) - .unwrap_err(); - assert_eq!(ContractError::NotEnoughFunds, err.downcast().unwrap()); - - // try to call release from non-admin - let msg = ExecuteMsg::ReleaseFunds { - funds: deposit_funds[0].clone(), - }; - let err = app - .execute_contract( - Addr::unchecked(random_addr), - contract_addr.clone(), - &msg, - &[], - ) - .unwrap_err(); - assert_eq!( - ContractError::Admin(AdminError::NotAdmin {}), - err.downcast().unwrap() - ); - - let msg = ExecuteMsg::ReleaseFunds { - funds: deposit_funds[0].clone(), - }; - app.execute_contract(Addr::unchecked(multisig_addr), contract_addr, &msg, &[]) - .unwrap(); - let pool_bal = app.wrap().query_balance(pool_addr, TEST_MIX_DENOM).unwrap(); - assert_eq!(pool_bal, deposit_funds[0]); -} diff --git a/contracts/coconut-test/src/helpers.rs b/contracts/coconut-test/src/helpers.rs deleted file mode 100644 index 229cd55128a..00000000000 --- a/contracts/coconut-test/src/helpers.rs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use cosmwasm_std::{Addr, Coin, Empty}; -use cw_multi_test::{App, AppBuilder, Contract, ContractWrapper}; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -pub const OWNER: &str = "admin0001"; -pub const MEMBER1: &str = "member1"; -pub const MULTISIG_CONTRACT: &str = "multisig contract address"; -pub const POOL_CONTRACT: &str = "mix pool contract address"; -pub const RANDOM_ADDRESS: &str = "random address"; - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub struct MigrateMsg { - pub coconut_bandwidth_address: String, - pub coconut_dkg_address: String, -} - -pub fn mock_app(init_funds: &[Coin]) -> App { - AppBuilder::new().build(|router, _, storage| { - router - .bank - .init_balance(storage, &Addr::unchecked(OWNER), init_funds.to_vec()) - .unwrap(); - router - .bank - .init_balance(storage, &Addr::unchecked(MEMBER1), init_funds.to_vec()) - .unwrap(); - }) -} -pub fn contract_dkg() -> Box> { - let contract = ContractWrapper::new( - nym_coconut_dkg::contract::execute, - nym_coconut_dkg::contract::instantiate, - nym_coconut_dkg::contract::query, - ); - Box::new(contract) -} - -pub fn contract_bandwidth() -> Box> { - let contract = ContractWrapper::new( - nym_coconut_bandwidth::contract::execute, - nym_coconut_bandwidth::contract::instantiate, - nym_coconut_bandwidth::contract::query, - ); - Box::new(contract) -} - -pub fn contract_multisig() -> Box> { - let contract = ContractWrapper::new( - cw3_flex_multisig::contract::execute, - cw3_flex_multisig::contract::instantiate, - cw3_flex_multisig::contract::query, - ) - .with_migrate(cw3_flex_multisig::contract::migrate); - Box::new(contract) -} - -pub fn contract_group() -> Box> { - let contract = ContractWrapper::new( - cw4_group::contract::execute, - cw4_group::contract::instantiate, - cw4_group::contract::query, - ); - Box::new(contract) -} diff --git a/contracts/coconut-test/src/spend_credential_creates_proposal.rs b/contracts/coconut-test/src/spend_credential_creates_proposal.rs deleted file mode 100644 index 05eb30ba9ef..00000000000 --- a/contracts/coconut-test/src/spend_credential_creates_proposal.rs +++ /dev/null @@ -1,165 +0,0 @@ -use crate::helpers::*; -use cosmwasm_std::{coins, Addr, Coin, Decimal}; -use cw_multi_test::Executor; -use cw_utils::{Duration, Threshold}; -use nym_coconut_bandwidth::error::ContractError; -use nym_coconut_bandwidth_contract_common::{ - msg::{ - ExecuteMsg as CoconutBandwidthExecuteMsg, InstantiateMsg as CoconutBandwidthInstantiateMsg, - }, - spend_credential::SpendCredentialData, -}; -use nym_group_contract_common::msg::InstantiateMsg as GroupInstantiateMsg; -use nym_multisig_contract_common::msg::InstantiateMsg as MultisigInstantiateMsg; - -pub const TEST_COIN_DENOM: &str = "unym"; -pub const TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str = - "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; -pub const TEST_COCONUT_DKG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; - -#[test] -fn spend_credential_creates_proposal() { - let init_funds = coins(10, TEST_COIN_DENOM); - let mut app = mock_app(&init_funds); - let pool_addr = String::from(POOL_CONTRACT); - - let group_code_id = app.store_code(contract_group()); - let msg = GroupInstantiateMsg { - admin: Some(OWNER.to_string()), - members: vec![], - }; - let group_contract_addr = app - .instantiate_contract( - group_code_id, - Addr::unchecked(OWNER), - &msg, - &[], - "group", - None, - ) - .unwrap(); - - let multisig_code_id = app.store_code(contract_multisig()); - let msg = MultisigInstantiateMsg { - group_addr: group_contract_addr.into_string(), - threshold: Threshold::AbsolutePercentage { - percentage: Decimal::from_ratio(2u128, 3u128), - }, - executor: None, - proposal_deposit: None, - max_voting_period: Duration::Height(1000), - coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(), - coconut_dkg_contract_address: TEST_COCONUT_DKG_CONTRACT_ADDRESS.to_string(), - }; - let multisig_contract_addr = app - .instantiate_contract( - multisig_code_id, - Addr::unchecked(OWNER), - &msg, - &[], - "multisig", - Some(OWNER.to_string()), - ) - .unwrap(); - - let coconut_bandwidth_code_id = app.store_code(contract_bandwidth()); - let msg = CoconutBandwidthInstantiateMsg { - multisig_addr: multisig_contract_addr.to_string(), - pool_addr, - mix_denom: TEST_COIN_DENOM.to_string(), - }; - let coconut_bandwidth_contract_addr = app - .instantiate_contract( - coconut_bandwidth_code_id, - Addr::unchecked(OWNER), - &msg, - &[], - "coconut bandwidth", - None, - ) - .unwrap(); - - let msg = MigrateMsg { - coconut_bandwidth_address: coconut_bandwidth_contract_addr.to_string(), - coconut_dkg_address: "dkg-address".to_string(), - }; - app.migrate_contract( - Addr::unchecked(OWNER), - multisig_contract_addr, - &msg, - multisig_code_id, - ) - .unwrap(); - - let msg = CoconutBandwidthExecuteMsg::SpendCredential { - data: SpendCredentialData::new( - Coin::new(1, TEST_COIN_DENOM), - String::from("blinded_serial_number"), - String::from("gateway_cosmos_address"), - ), - }; - let res = app - .execute_contract( - Addr::unchecked(OWNER), - coconut_bandwidth_contract_addr.clone(), - &msg, - &[], - ) - .unwrap(); - let proposal_id = res - .events - .into_iter() - .find(|e| &e.ty == "wasm") - .unwrap() - .attributes - .into_iter() - .find(|attr| &attr.key == "proposal_id") - .unwrap() - .value - .parse::() - .unwrap(); - assert_eq!(1, proposal_id); - - // Trying with the same blinded serial number will detect the double spend attempt - let err = app - .execute_contract( - Addr::unchecked(OWNER), - coconut_bandwidth_contract_addr.clone(), - &msg, - &[], - ) - .unwrap_err(); - assert_eq!( - ContractError::DuplicateBlindedSerialNumber, - err.downcast().unwrap() - ); - - let msg = CoconutBandwidthExecuteMsg::SpendCredential { - data: SpendCredentialData::new( - Coin::new(1, TEST_COIN_DENOM), - String::from("blinded_serial_number2"), - String::from("gateway_cosmos_address"), - ), - }; - let res = app - .execute_contract( - Addr::unchecked(OWNER), - coconut_bandwidth_contract_addr, - &msg, - &[], - ) - .unwrap(); - let proposal_id = res - .events - .into_iter() - .find(|e| &e.ty == "wasm") - .unwrap() - .attributes - .into_iter() - .find(|attr| &attr.key == "proposal_id") - .unwrap() - .value - .parse::() - .unwrap(); - assert_eq!(2, proposal_id); -} diff --git a/contracts/coconut-test/src/submit_vk_creates_proposal.rs b/contracts/coconut-test/src/submit_vk_creates_proposal.rs deleted file mode 100644 index d863f90cfe6..00000000000 --- a/contracts/coconut-test/src/submit_vk_creates_proposal.rs +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::helpers::{ - contract_dkg, contract_group, contract_multisig, mock_app, MigrateMsg, MEMBER1, OWNER, -}; -use crate::spend_credential_creates_proposal::{ - TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS, TEST_COCONUT_DKG_CONTRACT_ADDRESS, TEST_COIN_DENOM, -}; -use cosmwasm_std::{coins, Addr, Decimal}; -use cw4::Member; -use cw_multi_test::Executor; -use cw_utils::{Duration, Threshold}; -use nym_coconut_dkg_common::msg::ExecuteMsg::{ - AdvanceEpochState, CommitVerificationKeyShare, InitiateDkg, RegisterDealer, -}; -use nym_coconut_dkg_common::msg::InstantiateMsg as DkgInstantiateMsg; -use nym_coconut_dkg_common::msg::QueryMsg::GetVerificationKeys; -use nym_coconut_dkg_common::verification_key::PagedVKSharesResponse; -use nym_group_contract_common::msg::InstantiateMsg as GroupInstantiateMsg; -use nym_multisig_contract_common::msg::ExecuteMsg::{Execute, Vote}; -use nym_multisig_contract_common::msg::InstantiateMsg as MultisigInstantiateMsg; - -#[test] -fn dkg_proposal() { - let init_funds = coins(10000000000, TEST_COIN_DENOM); - let mut app = mock_app(&init_funds); - let member1 = Member { - addr: MEMBER1.to_string(), - weight: 10, - }; - - let group_code_id = app.store_code(contract_group()); - let msg = GroupInstantiateMsg { - admin: Some(OWNER.to_string()), - members: vec![member1], - }; - let group_contract_addr = app - .instantiate_contract( - group_code_id, - Addr::unchecked(OWNER), - &msg, - &[], - "group", - None, - ) - .unwrap(); - - let multisig_code_id = app.store_code(contract_multisig()); - let msg = MultisigInstantiateMsg { - group_addr: group_contract_addr.to_string(), - threshold: Threshold::AbsolutePercentage { - percentage: Decimal::from_ratio(1u128, 1u128), - }, - executor: None, - proposal_deposit: None, - max_voting_period: Duration::Time(1000), - coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(), - coconut_dkg_contract_address: TEST_COCONUT_DKG_CONTRACT_ADDRESS.to_string(), - }; - let multisig_contract_addr = app - .instantiate_contract( - multisig_code_id, - Addr::unchecked(OWNER), - &msg, - &[], - "multisig", - Some(OWNER.to_string()), - ) - .unwrap(); - - let coconut_dkg_code_id = app.store_code(contract_dkg()); - let msg = DkgInstantiateMsg { - group_addr: group_contract_addr.to_string(), - multisig_addr: multisig_contract_addr.to_string(), - time_configuration: None, - mix_denom: TEST_COIN_DENOM.to_string(), - key_size: 5, - }; - let coconut_dkg_contract_addr = app - .instantiate_contract( - coconut_dkg_code_id, - Addr::unchecked(OWNER), - &msg, - &[], - "coconut dkg", - None, - ) - .unwrap(); - - let msg = MigrateMsg { - coconut_bandwidth_address: coconut_dkg_contract_addr.to_string(), - coconut_dkg_address: coconut_dkg_contract_addr.to_string(), - }; - app.migrate_contract( - Addr::unchecked(OWNER), - multisig_contract_addr.clone(), - &msg, - multisig_code_id, - ) - .unwrap(); - - app.execute_contract( - Addr::unchecked(OWNER), - coconut_dkg_contract_addr.clone(), - &InitiateDkg {}, - &[], - ) - .unwrap(); - - app.execute_contract( - Addr::unchecked(MEMBER1), - coconut_dkg_contract_addr.clone(), - &RegisterDealer { - bte_key_with_proof: "bte_key_with_proof".to_string(), - identity_key: "identity".to_string(), - announce_address: "127.0.0.1:8000".to_string(), - resharing: false, - }, - &[], - ) - .unwrap(); - - for _ in 0..2 { - app.update_block(|block| block.time = block.time.plus_seconds(1000)); - app.execute_contract( - Addr::unchecked(OWNER), - coconut_dkg_contract_addr.clone(), - &AdvanceEpochState {}, - &[], - ) - .unwrap(); - } - - // Proposal needs to be later then the member became part of the group - app.update_block(|block| block.height += 1); - - let msg = CommitVerificationKeyShare { - share: "share".to_string(), - resharing: false, - }; - let res = app - .execute_contract( - Addr::unchecked(MEMBER1), - coconut_dkg_contract_addr.clone(), - &msg, - &[], - ) - .unwrap(); - - let proposal_id = res - .events - .into_iter() - .find(|e| &e.ty == "wasm") - .unwrap() - .attributes - .into_iter() - .find(|attr| &attr.key == "proposal_id") - .unwrap() - .value - .parse::() - .unwrap(); - - let mut res: PagedVKSharesResponse = app - .wrap() - .query_wasm_smart( - coconut_dkg_contract_addr.clone(), - &GetVerificationKeys { - epoch_id: 0, - limit: None, - start_after: None, - }, - ) - .unwrap(); - let share = res.shares.pop().unwrap(); - assert_eq!(share.share, "share".to_string()); - assert_eq!(share.announce_address, "127.0.0.1:8000".to_string()); - assert_eq!(share.node_index, 1); - assert_eq!(share.owner, Addr::unchecked(MEMBER1)); - assert!(!share.verified); - - app.execute_contract( - Addr::unchecked(MEMBER1), - multisig_contract_addr.clone(), - &Vote { - proposal_id, - vote: cw3::Vote::Yes, - }, - &[], - ) - .unwrap(); - - for _ in 0..2 { - app.update_block(|block| block.time = block.time.plus_seconds(1000)); - app.execute_contract( - Addr::unchecked(OWNER), - coconut_dkg_contract_addr.clone(), - &AdvanceEpochState {}, - &[], - ) - .unwrap(); - } - - app.execute_contract( - Addr::unchecked(MEMBER1), - multisig_contract_addr, - &Execute { proposal_id }, - &[], - ) - .unwrap(); - - let res: PagedVKSharesResponse = app - .wrap() - .query_wasm_smart( - coconut_dkg_contract_addr, - &GetVerificationKeys { - epoch_id: 0, - limit: None, - start_after: None, - }, - ) - .unwrap(); - assert!(res.shares[0].verified); -} diff --git a/contracts/coconut-test/src/test_wrapper.rs b/contracts/coconut-test/src/test_wrapper.rs deleted file mode 100644 index e9336ad19cf..00000000000 --- a/contracts/coconut-test/src/test_wrapper.rs +++ /dev/null @@ -1,433 +0,0 @@ -// Copyright 2024 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::helpers::{contract_bandwidth, contract_dkg, contract_group, contract_multisig}; -use cosmwasm_std::{coins, Addr}; -use cw3::{Cw3Contract, ProposalListResponse, Status, Vote}; -use cw4::{Cw4Contract, Member}; -use cw_multi_test::{App, AppBuilder, Executor}; -use cw_utils::{Duration, Threshold}; -use nym_coconut_bandwidth_contract_common::msg::InstantiateMsg as BandwidthInstantiateMsg; -use nym_coconut_dkg_common::dealing::{chunk_dealing, DealingChunkInfo, MAX_DEALING_CHUNK_SIZE}; -use nym_coconut_dkg_common::msg::ExecuteMsg as DkgExecuteMsg; -use nym_coconut_dkg_common::msg::InstantiateMsg as DkgInstantiateMsg; -use nym_coconut_dkg_common::msg::QueryMsg as DkgQueryMsg; -use nym_coconut_dkg_common::types::{Epoch, State}; -use nym_group_contract_common::msg::InstantiateMsg as GroupInstantiateMsg; -use nym_multisig_contract_common::msg::InstantiateMsg as MultisigInstantiateMsg; -use nym_multisig_contract_common::msg::MigrateMsg as MultisigMigrateMsg; -use rand_chacha::rand_core::{RngCore, SeedableRng}; -use rand_chacha::ChaCha20Rng; -use subtle_encoding::bech32; - -pub const PREFIX: &str = "n"; -pub const TEST_DENOM: &str = "unym"; - -pub const BANDWIDTH_POOL: &str = "pool"; - -pub const BLOCK_TIME_SECS: u64 = 6; - -pub fn test_rng() -> ChaCha20Rng { - let dummy_seed = [42u8; 32]; - ChaCha20Rng::from_seed(dummy_seed) -} - -pub fn random_address(rng: &mut ChaCha20Rng) -> Addr { - let mut bytes = [0u8; 32]; - rng.fill_bytes(&mut bytes); - - Addr::unchecked(bech32::encode(PREFIX, bytes)) -} - -#[allow(dead_code)] -pub struct TestSetup { - pub app: App, - pub rng: ChaCha20Rng, - pub global_admin: Addr, - - pub multisig_contract: Cw3Contract, - pub group_contract: Cw4Contract, - pub dkg_contract: Addr, - pub bandwidth_contract: Addr, -} - -#[allow(dead_code)] -impl TestSetup { - pub fn new() -> Self { - let mut rng = test_rng(); - - let global_admin = random_address(&mut rng); - let mut app = AppBuilder::new().build(|router, _, storage| { - router - .bank - .init_balance(storage, &global_admin, coins(1000000000000000, TEST_DENOM)) - .unwrap(); - }); - - let group_id = app.store_code(contract_group()); - let multisig_id = app.store_code(contract_multisig()); - let bandwidth_id = app.store_code(contract_bandwidth()); - let dkg_id = app.store_code(contract_dkg()); - - // 1. init group contract - let group_contract = app - .instantiate_contract( - group_id, - global_admin.clone(), - &GroupInstantiateMsg { - admin: Some(global_admin.to_string()), - members: vec![], - }, - &[], - "group contract", - Some(global_admin.to_string()), - ) - .unwrap(); - - // 2. init multisig contract - let multisig_contract = app - .instantiate_contract( - multisig_id, - global_admin.clone(), - &MultisigInstantiateMsg { - group_addr: group_contract.to_string(), - coconut_bandwidth_contract_address: group_contract.to_string(), - coconut_dkg_contract_address: group_contract.to_string(), - threshold: Threshold::AbsolutePercentage { - percentage: "0.67".parse().unwrap(), - }, - max_voting_period: Duration::Time(3600), - executor: None, - proposal_deposit: None, - }, - &[], - "multisig contract", - Some(global_admin.to_string()), - ) - .unwrap(); - - // 3. init bandwidth contract - let bandwidth_contract = app - .instantiate_contract( - bandwidth_id, - global_admin.clone(), - &BandwidthInstantiateMsg { - multisig_addr: multisig_contract.to_string(), - pool_addr: BANDWIDTH_POOL.to_string(), - mix_denom: TEST_DENOM.to_string(), - }, - &[], - "bandwidth contract", - Some(global_admin.to_string()), - ) - .unwrap(); - - // 4. init dkg contract - let dkg_contract = app - .instantiate_contract( - dkg_id, - global_admin.clone(), - &DkgInstantiateMsg { - group_addr: group_contract.to_string(), - multisig_addr: multisig_contract.to_string(), - time_configuration: None, - mix_denom: TEST_DENOM.to_string(), - key_size: 5, - }, - &[], - "dkg contract", - Some(global_admin.to_string()), - ) - .unwrap(); - - // 5.migrate multisig contract with addresses of bandwidth and dkg contracts - app.migrate_contract( - global_admin.clone(), - multisig_contract.clone(), - &MultisigMigrateMsg { - coconut_bandwidth_address: bandwidth_contract.to_string(), - coconut_dkg_address: dkg_contract.to_string(), - }, - multisig_id, - ) - .unwrap(); - - TestSetup { - app, - rng, - global_admin, - multisig_contract: Cw3Contract(multisig_contract), - group_contract: Cw4Contract(group_contract), - dkg_contract, - bandwidth_contract, - } - } - - pub fn random_address(&mut self) -> Addr { - random_address(&mut self.rng) - } - - pub fn next_block(&mut self) { - self.app.update_block(|block| { - block.height += 1; - block.time = block.time.plus_seconds(BLOCK_TIME_SECS); - }) - } - - // if we ever want to expand those tests, those queries should be moved to contract specific structs - // (kinda like what cw4 has) - pub fn dkg_state(&self) -> State { - self.app - .wrap() - .query_wasm_smart(&self.dkg_contract, &DkgQueryMsg::GetState {}) - .unwrap() - } - - pub fn epoch(&self) -> Epoch { - self.app - .wrap() - .query_wasm_smart(&self.dkg_contract, &DkgQueryMsg::GetCurrentEpochState {}) - .unwrap() - } - - // TODO: this will not go beyond first page - pub fn all_proposals(&self) -> ProposalListResponse { - self.app - .wrap() - .query_wasm_smart( - &self.multisig_contract.0, - &nym_multisig_contract_common::msg::QueryMsg::ListProposals { - start_after: None, - limit: None, - }, - ) - .unwrap() - } - - pub fn admin(&self) -> Addr { - self.global_admin.clone() - } - - pub fn remove_group_member(&mut self, addr: &Addr) { - self.app - .execute_contract( - self.admin(), - self.group_contract.addr(), - &nym_group_contract_common::msg::ExecuteMsg::UpdateMembers { - remove: vec![addr.to_string()], - add: vec![], - }, - &[], - ) - .unwrap(); - } - - pub fn add_mock_group_member(&mut self, weight: Option) -> Addr { - let member_addr = self.random_address(); - let weight = weight.unwrap_or(10); - - self.app - .execute_contract( - self.admin(), - self.group_contract.addr(), - &nym_group_contract_common::msg::ExecuteMsg::UpdateMembers { - remove: vec![], - add: vec![Member { - addr: member_addr.to_string(), - weight, - }], - }, - &[], - ) - .unwrap(); - - member_addr - } - - pub fn begin_dkg(&mut self) { - self.app - .execute_contract( - self.admin(), - self.dkg_contract.clone(), - &DkgExecuteMsg::InitiateDkg {}, - &[], - ) - .unwrap(); - } - - pub fn skip_to_dkg_state_end(&mut self) { - let epoch = self.epoch(); - - self.app.update_block(|block| { - block.height += 42; - if let Some(finish_timestamp) = epoch.deadline { - block.time = finish_timestamp.plus_seconds(BLOCK_TIME_SECS); - } else { - block.time = block.time.plus_seconds(BLOCK_TIME_SECS) - } - }); - } - - pub fn advance_dkg_epoch(&mut self) { - self.skip_to_dkg_state_end(); - self.unchecked_advance_dkg_epoch(); - } - - pub fn unchecked_advance_dkg_epoch(&mut self) { - self.app - .execute_contract( - self.admin(), - self.dkg_contract.clone(), - &DkgExecuteMsg::AdvanceEpochState {}, - &[], - ) - .unwrap(); - } - - pub fn submit_dummy_dkg_keys(&mut self, member: &Addr, resharing: bool) { - let mut bte_key_with_proof = [0u8; 32]; - self.rng.fill_bytes(&mut bte_key_with_proof); - let bte_key_with_proof = bs58::encode(&bte_key_with_proof).into_string(); - - let mut identity_key = [0u8; 32]; - self.rng.fill_bytes(&mut identity_key); - let identity_key = bs58::encode(&identity_key).into_string(); - - let mut announce_address = [0u8; 16]; - self.rng.fill_bytes(&mut announce_address); - let announce_address = bs58::encode(&announce_address).into_string(); - - self.app - .execute_contract( - member.clone(), - self.dkg_contract.clone(), - &DkgExecuteMsg::RegisterDealer { - bte_key_with_proof, - identity_key, - announce_address, - resharing, - }, - &[], - ) - .unwrap(); - } - - pub fn submit_dummy_dealings(&mut self, member: &Addr, resharing: bool) { - let dealings = self.dkg_state().key_size; - - for dealing_index in 0..dealings { - let mut dealing_bytes = vec![0u8; 5000]; - self.rng.fill_bytes(&mut dealing_bytes); - - let chunks = DealingChunkInfo::construct(dealing_bytes.len(), MAX_DEALING_CHUNK_SIZE); - self.app - .execute_contract( - member.clone(), - self.dkg_contract.clone(), - &DkgExecuteMsg::CommitDealingsMetadata { - dealing_index, - chunks, - resharing, - }, - &[], - ) - .unwrap(); - self.next_block(); - - let chunks = chunk_dealing(dealing_index, dealing_bytes, MAX_DEALING_CHUNK_SIZE); - for (_, chunk) in chunks { - self.app - .execute_contract( - member.clone(), - self.dkg_contract.clone(), - &DkgExecuteMsg::CommitDealingsChunk { chunk }, - &[], - ) - .unwrap(); - self.next_block(); - } - } - } - - pub fn submit_dummy_vk_key(&mut self, member: &Addr, resharing: bool) { - let mut derived_vk = vec![0u8; 256]; - self.rng.fill_bytes(&mut derived_vk); - - let share = bs58::encode(&derived_vk).into_string(); - - self.app - .execute_contract( - member.clone(), - self.dkg_contract.clone(), - &DkgExecuteMsg::CommitVerificationKeyShare { share, resharing }, - &[], - ) - .unwrap(); - } - - pub fn validate_dummy_keys(&mut self, member: &Addr) { - for proposal in self.all_proposals().proposals { - if proposal.status == Status::Open { - self.app - .execute_contract( - member.clone(), - self.multisig_contract.addr(), - &nym_multisig_contract_common::msg::ExecuteMsg::Vote { - proposal_id: proposal.id, - vote: Vote::Yes, - }, - &[], - ) - .unwrap(); - } - } - } - - pub fn finalize_dummy_dkg(&mut self) { - for proposal in self.all_proposals().proposals { - assert_eq!(proposal.status, Status::Passed); - - self.app - .execute_contract( - self.admin(), - self.multisig_contract.addr(), - &nym_multisig_contract_common::msg::ExecuteMsg::Execute { - proposal_id: proposal.id, - }, - &[], - ) - .unwrap(); - } - } - - pub fn full_dummy_dkg(&mut self, members: Vec, resharing: bool) { - for member in &members { - self.submit_dummy_dkg_keys(member, resharing); - self.next_block(); - } - self.advance_dkg_epoch(); - - for member in &members { - self.submit_dummy_dealings(member, resharing); - self.next_block(); - } - self.advance_dkg_epoch(); - - for member in &members { - self.submit_dummy_vk_key(member, resharing); - self.next_block(); - } - self.advance_dkg_epoch(); - - for member in &members { - self.validate_dummy_keys(member); - self.next_block(); - } - self.advance_dkg_epoch(); - - self.finalize_dummy_dkg(); - self.next_block(); - - self.advance_dkg_epoch(); - } -} diff --git a/contracts/coconut-test/src/tests.rs b/contracts/coconut-test/src/tests.rs deleted file mode 100644 index 04c6d327d21..00000000000 --- a/contracts/coconut-test/src/tests.rs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -mod deposit_and_release; -mod helpers; -mod spend_credential_creates_proposal; -mod submit_vk_creates_proposal; -mod test_wrapper; diff --git a/contracts/ecash/Cargo.toml b/contracts/ecash/Cargo.toml index 95142f3f3f0..a80b0fb2412 100644 --- a/contracts/ecash/Cargo.toml +++ b/contracts/ecash/Cargo.toml @@ -23,7 +23,7 @@ sylvia = { workspace = true } cw-storage-plus = { workspace = true } thiserror = { workspace = true } cw-controllers = { workspace = true } -cosmwasm-storage = { workspace = true } + cw2 = { workspace = true } cw3 = { workspace = true } cw4 = { workspace = true } @@ -40,6 +40,7 @@ anyhow = { workspace = true } sylvia = { workspace = true, features = ["mt"] } nym-crypto = { path = "../../common/crypto", features = ["rand", "asymmetric"] } rand_chacha = "0.3" +cw-multi-test = { workspace = true } [features] schema-gen = ["nym-ecash-contract-common/schema"] diff --git a/contracts/ecash/schema/nym-ecash.json b/contracts/ecash/schema/nym-ecash.json index 33fa704a08d..ce67c4b0494 100644 --- a/contracts/ecash/schema/nym-ecash.json +++ b/contracts/ecash/schema/nym-ecash.json @@ -41,7 +41,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", @@ -230,7 +231,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", @@ -658,6 +660,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", diff --git a/contracts/ecash/schema/raw/execute.json b/contracts/ecash/schema/raw/execute.json index 9a7e3e054a9..e16868cf1ad 100644 --- a/contracts/ecash/schema/raw/execute.json +++ b/contracts/ecash/schema/raw/execute.json @@ -179,7 +179,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", diff --git a/contracts/ecash/schema/raw/instantiate.json b/contracts/ecash/schema/raw/instantiate.json index cddf0ba4737..540a22ab4e3 100644 --- a/contracts/ecash/schema/raw/instantiate.json +++ b/contracts/ecash/schema/raw/instantiate.json @@ -37,7 +37,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", diff --git a/contracts/ecash/schema/raw/response_to_get_required_deposit_amount.json b/contracts/ecash/schema/raw/response_to_get_required_deposit_amount.json index 6e18ef9a9bc..b1c4c6c716c 100644 --- a/contracts/ecash/schema/raw/response_to_get_required_deposit_amount.json +++ b/contracts/ecash/schema/raw/response_to_get_required_deposit_amount.json @@ -14,6 +14,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", diff --git a/contracts/ecash/src/contract/helpers.rs b/contracts/ecash/src/contract/helpers.rs index 7b18f39eb2f..578e967da8d 100644 --- a/contracts/ecash/src/contract/helpers.rs +++ b/contracts/ecash/src/contract/helpers.rs @@ -6,19 +6,19 @@ use crate::helpers::{ create_batch_redemption_proposal, create_blacklist_proposal, Config, ProposalId, }; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{to_binary, Addr, Coin, Decimal, Deps, Storage, SubMsg, Uint128}; +use cosmwasm_std::{to_json_binary, Addr, Coin, Decimal, Deps, Storage, SubMsg, Uint128}; use cw3::ProposalResponse; use nym_ecash_contract_common::EcashContractError; use nym_multisig_contract_common::msg::QueryMsg as MultisigQueryMsg; use nym_network_defaults::TICKETBOOK_SIZE; -use sylvia::types::ExecCtx; +use sylvia::ctx::ExecCtx; #[cw_serde] pub(crate) struct Invariants { pub(crate) ticket_book_size: u64, } -impl NymEcashContract<'_> { +impl NymEcashContract { pub(crate) fn get_ticketbook_size( &self, storage: &dyn Storage, @@ -47,7 +47,7 @@ impl NymEcashContract<'_> { let book_ratio = Decimal::from_ratio(tickets, ticketbook_size); // return = ticketbook_price * (tickets / ticketbook_size) - let return_amount = book_ratio * deposit_amount; + let return_amount = deposit_amount.mul_floor(book_ratio); Ok(Coin { denom: config.deposit_amount.denom.clone(), @@ -111,7 +111,7 @@ impl NymEcashContract<'_> { let proposal_response: ProposalResponse = deps.querier.query( &cosmwasm_std::QueryRequest::Wasm(cosmwasm_std::WasmQuery::Smart { contract_addr: multisig_addr.to_string(), - msg: to_binary(&msg)?, + msg: to_json_binary(&msg)?, }), )?; Ok(proposal_response) diff --git a/contracts/ecash/src/contract/mod.rs b/contracts/ecash/src/contract/mod.rs index e8387514799..934b2bdc074 100644 --- a/contracts/ecash/src/contract/mod.rs +++ b/contracts/ecash/src/contract/mod.rs @@ -25,7 +25,7 @@ use nym_ecash_contract_common::events::{ }; use nym_ecash_contract_common::EcashContractError; use nym_network_defaults::TICKETBOOK_SIZE; -use sylvia::types::{ExecCtx, InstantiateCtx, MigrateCtx, QueryCtx, ReplyCtx}; +use sylvia::ctx::{ExecCtx, InstantiateCtx, MigrateCtx, QueryCtx}; use sylvia::{contract, entry_points}; mod helpers; @@ -34,22 +34,22 @@ mod queued_migrations; #[cfg(test)] mod test; -pub struct NymEcashContract<'a> { - pub(crate) contract_admin: Admin<'a>, - pub(crate) multisig: Admin<'a>, - pub(crate) config: Item<'a, Config>, - pub(crate) pool_counters: Item<'a, PoolCounters>, - pub(crate) expected_invariants: Item<'a, Invariants>, +pub struct NymEcashContract { + pub(crate) contract_admin: Admin, + pub(crate) multisig: Admin, + pub(crate) config: Item, + pub(crate) pool_counters: Item, + pub(crate) expected_invariants: Item, - pub(crate) blacklist: Map<'a, BlacklistKey, Blacklisting>, + pub(crate) blacklist: Map, - pub(crate) deposits: DepositStorage<'a>, + pub(crate) deposits: DepositStorage, } #[entry_points] #[contract] -#[error(EcashContractError)] -impl NymEcashContract<'_> { +#[sv::error(EcashContractError)] +impl NymEcashContract { #[allow(clippy::new_without_default)] pub const fn new() -> Self { Self { @@ -63,7 +63,7 @@ impl NymEcashContract<'_> { } } - #[msg(instantiate)] + #[sv::msg(instantiate)] pub fn instantiate( &self, mut ctx: InstantiateCtx, @@ -119,7 +119,7 @@ impl NymEcashContract<'_> { /*================== ======QUERIES======= ==================*/ - #[msg(query)] + #[sv::msg(query)] pub fn get_blacklist_paged( &self, ctx: QueryCtx, @@ -150,7 +150,7 @@ impl NymEcashContract<'_> { )) } - #[msg(query)] + #[sv::msg(query)] pub fn get_blacklisted_account( &self, ctx: QueryCtx, @@ -160,14 +160,14 @@ impl NymEcashContract<'_> { Ok(BlacklistedAccountResponse::new(account)) } - #[msg(query)] + #[sv::msg(query)] pub fn get_required_deposit_amount(&self, ctx: QueryCtx) -> StdResult { let deposit_amount = self.config.load(ctx.deps.storage)?.deposit_amount; Ok(deposit_amount) } - #[msg(query)] + #[sv::msg(query)] pub fn get_deposit( &self, ctx: QueryCtx, @@ -179,7 +179,7 @@ impl NymEcashContract<'_> { }) } - #[msg(query)] + #[sv::msg(query)] pub fn get_latest_deposit( &self, ctx: QueryCtx, @@ -198,7 +198,7 @@ impl NymEcashContract<'_> { }) } - #[msg(query)] + #[sv::msg(query)] pub fn get_deposits_paged( &self, ctx: QueryCtx, @@ -230,7 +230,7 @@ impl NymEcashContract<'_> { ======EXECUTIONS======= =====================*/ - #[msg(exec)] + #[sv::msg(exec)] pub fn deposit_ticket_book_funds( &self, ctx: ExecCtx, @@ -266,7 +266,7 @@ impl NymEcashContract<'_> { .set_data(deposit_id.to_be_bytes())) } - #[msg(exec)] + #[sv::msg(exec)] pub fn request_redemption( &self, ctx: ExecCtx, @@ -285,7 +285,7 @@ impl NymEcashContract<'_> { Ok(Response::new().add_submessage(msg)) } - #[msg(exec)] + #[sv::msg(exec)] pub fn redeem_tickets( &self, ctx: ExecCtx, @@ -319,7 +319,7 @@ impl NymEcashContract<'_> { })) } - #[msg(exec)] + #[sv::msg(exec)] pub fn update_admin( &self, ctx: ExecCtx, @@ -333,7 +333,7 @@ impl NymEcashContract<'_> { .execute_update_admin(ctx.deps, ctx.info, Some(new_admin))?) } - #[msg(exec)] + #[sv::msg(exec)] pub fn update_deposit_value( &self, ctx: ExecCtx, @@ -352,7 +352,7 @@ impl NymEcashContract<'_> { Ok(Response::new().add_attribute("updated_deposit", deposit_str)) } - #[msg(exec)] + #[sv::msg(exec)] pub fn propose_to_blacklist( &self, ctx: ExecCtx, @@ -381,7 +381,7 @@ impl NymEcashContract<'_> { // } } - #[msg(exec)] + #[sv::msg(exec)] pub fn add_to_blacklist( &self, ctx: ExecCtx, @@ -405,8 +405,13 @@ impl NymEcashContract<'_> { /*===================== =========REPLY========= =====================*/ - #[msg(reply)] - pub fn reply(&self, ctx: ReplyCtx, msg: Reply) -> Result { + #[sv::msg(reply)] + #[allow(deprecated)] + pub fn reply( + &self, + ctx: sylvia::types::ReplyCtx, + msg: Reply, + ) -> Result { match msg.id { n if n == BLACKLIST_PROPOSAL_REPLY_ID => self.handle_blacklist_proposal_reply(ctx, msg), n if n == REDEMPTION_PROPOSAL_REPLY_ID => { @@ -416,9 +421,10 @@ impl NymEcashContract<'_> { } } + #[allow(deprecated)] fn handle_blacklist_proposal_reply( &self, - ctx: ReplyCtx, + ctx: sylvia::types::ReplyCtx, msg: Reply, ) -> Result { let proposal_id = msg.multisig_proposal_id()?; @@ -435,9 +441,10 @@ impl NymEcashContract<'_> { Ok(Response::new().add_attribute(PROPOSAL_ID_ATTRIBUTE_NAME, proposal_id.to_string())) } + #[allow(deprecated)] fn handle_redemption_proposal_reply( &self, - _ctx: ReplyCtx, + _ctx: sylvia::types::ReplyCtx, msg: Reply, ) -> Result { let proposal_id = msg.multisig_proposal_id()?; @@ -451,7 +458,7 @@ impl NymEcashContract<'_> { /*===================== =======MIGRATION======= =====================*/ - #[msg(migrate)] + #[sv::msg(migrate)] pub fn migrate(&self, ctx: MigrateCtx) -> Result { set_build_information!(ctx.deps.storage)?; cw2::ensure_from_older_version(ctx.deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; diff --git a/contracts/ecash/src/contract/test.rs b/contracts/ecash/src/contract/test.rs index 0a90c0b5421..09ece6dd4b7 100644 --- a/contracts/ecash/src/contract/test.rs +++ b/contracts/ecash/src/contract/test.rs @@ -2,15 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 use crate::contract::NymEcashContract; -use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier}; +use cosmwasm_std::testing::{message_info, mock_dependencies, mock_env, MockApi, MockQuerier}; use cosmwasm_std::{coin, Addr, Empty, Env, MemoryStorage, OwnedDeps}; -use sylvia::types::{InstantiateCtx, QueryCtx}; +use sylvia::ctx::{InstantiateCtx, QueryCtx}; pub const TEST_DENOM: &str = "unym"; #[allow(dead_code)] pub struct TestSetup { - pub contract: NymEcashContract<'static>, + pub contract: NymEcashContract, pub deps: OwnedDeps>, pub env: Env, @@ -23,12 +23,13 @@ impl TestSetup { pub fn init() -> TestSetup { let mut deps = mock_dependencies(); let env = mock_env(); - let admin = mock_info("admin", &[]); - let init_ctx = InstantiateCtx::from((deps.as_mut(), env.clone(), admin)); - let multisig_contract = "multisig"; - let group_contract = "group"; - let holding = "holding"; + let admin = message_info(&deps.api.addr_make("admin"), &[]); + let multisig_contract = deps.api.addr_make("multisig"); + let group_contract = deps.api.addr_make("group"); + let holding = deps.api.addr_make("holding"); + + let init_ctx = InstantiateCtx::from((deps.as_mut(), env.clone(), admin)); let contract = NymEcashContract::new(); contract diff --git a/contracts/ecash/src/deposit.rs b/contracts/ecash/src/deposit.rs index 355593ad19b..11d13831b82 100644 --- a/contracts/ecash/src/deposit.rs +++ b/contracts/ecash/src/deposit.rs @@ -2,16 +2,17 @@ // SPDX-License-Identifier: Apache-2.0 use cosmwasm_std::{Order, StdResult, Storage}; -use cw_storage_plus::{Bound, Item, Key, Path, Prefix, PrimaryKey}; +use cw_storage_plus::{range_with_prefix, Bound, Item, Key, Path, Prefix, PrimaryKey}; use nym_ecash_contract_common::deposit::DepositId; use nym_ecash_contract_common::{deposit::Deposit, EcashContractError}; +use std::ops::Deref; -pub(crate) struct DepositStorage<'a> { - pub(crate) deposit_id_counter: Item<'a, DepositId>, +pub(crate) struct DepositStorage { + pub(crate) deposit_id_counter: Item, pub(crate) deposits: StoredDeposits, } -impl<'a> DepositStorage<'a> { +impl DepositStorage { pub const fn new() -> Self { DepositStorage { deposit_id_counter: Item::new("deposit_ids"), @@ -65,14 +66,23 @@ impl<'a> DepositStorage<'a> { Ok(Some(Deposit::try_from_bytes(&deposit_bytes)?)) } - pub fn range( + pub fn range<'a>( &'a self, store: &'a dyn Storage, min: Option>, max: Option>, order: Order, ) -> impl Iterator> + 'a { - self.deposits.no_prefix().range(store, min, max, order) + let prefix = self.deposits.no_prefix(); + let mapped = range_with_prefix( + store, + prefix.deref(), + min.map(|b| b.to_raw_bound()), + max.map(|b| b.to_raw_bound()), + order, + ) + .map(StoredDeposits::deserialize_deposit_record); + Box::new(mapped) } } @@ -89,15 +99,8 @@ impl StoredDeposits { Ok((id, Deposit::try_from_bytes(&deposit_bytes)?)) } - fn no_prefix(&self) -> Prefix { - cw_storage_plus::Prefix::with_deserialization_functions( - Self::NAMESPACE, - &[], - &[], - // explicitly panic to make sure we're never attempting to call an unexpected deserializer on our data - |_, _, kv| Self::deserialize_deposit_record(kv), - |_, _, _| panic!("attempted to call custom de_fn_v"), - ) + fn no_prefix(&self) -> Prefix { + Prefix::new(Self::NAMESPACE, &[]) } fn storage_key(deposit_id: u32) -> Path> { diff --git a/contracts/ecash/src/helpers.rs b/contracts/ecash/src/helpers.rs index b19b1e646da..82f5767eccd 100644 --- a/contracts/ecash/src/helpers.rs +++ b/contracts/ecash/src/helpers.rs @@ -3,7 +3,8 @@ use crate::constants::{BLACKLIST_PROPOSAL_REPLY_ID, REDEMPTION_PROPOSAL_REPLY_ID}; use cosmwasm_std::{ - to_binary, Addr, Coin, CosmosMsg, Reply, StdError, StdResult, SubMsg, SubMsgResult, WasmMsg, + to_json_binary, Addr, Coin, CosmosMsg, Reply, StdError, StdResult, SubMsg, SubMsgResult, + WasmMsg, }; use cw4::Cw4Contract; use nym_contracts_common::events::try_find_attribute; @@ -46,7 +47,7 @@ pub(crate) fn create_batch_redemption_proposal( let release_funds_req = ExecuteMsg::RedeemTickets { n, gw }; let release_funds_msg = CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: ecash_bandwidth_address, - msg: to_binary(&release_funds_req)?, + msg: to_json_binary(&release_funds_req)?, funds: vec![], }); let req = MultisigExecuteMsg::Propose { @@ -57,7 +58,7 @@ pub(crate) fn create_batch_redemption_proposal( }; let msg = CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: multisig_addr, - msg: to_binary(&req)?, + msg: to_json_binary(&req)?, funds: vec![], }); @@ -76,7 +77,7 @@ pub(crate) fn create_blacklist_proposal( }; let blacklist_req_msg = CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: ecash_bandwidth_address, - msg: to_binary(&blacklist_req)?, + msg: to_json_binary(&blacklist_req)?, funds: vec![], }); let req = MultisigExecuteMsg::Propose { @@ -87,7 +88,7 @@ pub(crate) fn create_blacklist_proposal( }; let msg = CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: multisig_addr, - msg: to_binary(&req)?, + msg: to_json_binary(&req)?, funds: vec![], }); diff --git a/contracts/ecash/src/multitest.rs b/contracts/ecash/src/multitest.rs index de56796089c..74b3d2c4439 100644 --- a/contracts/ecash/src/multitest.rs +++ b/contracts/ecash/src/multitest.rs @@ -1,16 +1,18 @@ // Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use cosmwasm_std::{coin, Addr, Coin}; +use cosmwasm_std::testing::MockApi; +use cosmwasm_std::{coin, Coin}; +use cw_multi_test::IntoBech32; use cw_utils::PaymentError; use nym_ecash_contract_common::EcashContractError; use sylvia::{cw_multi_test::App as MtApp, multitest::App}; -use crate::contract::multitest_utils::CodeId; +use crate::contract::sv::mt::{CodeId, NymEcashContractProxy}; #[test] fn invalid_deposit() { - let owner = "owner"; + let owner = "owner".into_bech32(); let denom = "unym"; let mtapp = MtApp::new(|router, _, storage| { @@ -18,10 +20,10 @@ fn invalid_deposit() { .bank .init_balance( storage, - &Addr::unchecked(owner), + &owner, vec![ - Coin::new(10000000, denom), - Coin::new(10000000, "some_denom"), + Coin::new(10000000u32, denom), + Coin::new(10000000u32, "some_denom"), ], ) .unwrap() @@ -32,12 +34,12 @@ fn invalid_deposit() { let contract = code_id .instantiate( - "holding_acount".to_string(), - "multisig_addr".to_string(), - "group_addr".to_string(), + MockApi::default().addr_make("holding_acount").to_string(), + MockApi::default().addr_make("multisig_addr").to_string(), + MockApi::default().addr_make("group_addr").to_string(), coin(75000000, denom), ) - .call(owner) + .call(&owner) .unwrap(); let verification_key = "Verification key"; @@ -45,19 +47,19 @@ fn invalid_deposit() { assert_eq!( contract .deposit_ticket_book_funds(verification_key.to_string(),) - .call(owner) + .call(&owner) .unwrap_err(), EcashContractError::InvalidDeposit(PaymentError::NoFunds {}) ); - let coin = Coin::new(1000000, denom.to_string()); - let second_coin = Coin::new(1000000, "some_denom"); + let coin = Coin::new(1000000u32, denom.to_string()); + let second_coin = Coin::new(1000000u32, "some_denom"); assert_eq!( contract .deposit_ticket_book_funds(verification_key.to_string(),) .with_funds(&[coin, second_coin.clone()]) - .call(owner) + .call(&owner) .unwrap_err(), EcashContractError::InvalidDeposit(PaymentError::MultipleDenoms {}) ); @@ -66,7 +68,7 @@ fn invalid_deposit() { contract .deposit_ticket_book_funds(verification_key.to_string(),) .with_funds(&[second_coin]) - .call(owner) + .call(&owner) .unwrap_err(), EcashContractError::InvalidDeposit(PaymentError::MissingDenom(denom.to_string())) ); diff --git a/contracts/ecash/src/support/tests.rs b/contracts/ecash/src/support/tests.rs index 11d5f628a54..fb3f3aa5790 100644 --- a/contracts/ecash/src/support/tests.rs +++ b/contracts/ecash/src/support/tests.rs @@ -3,18 +3,18 @@ use crate::contract::NymEcashContract; use crate::helpers::Config; -use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier}; +use cosmwasm_std::testing::{message_info, mock_dependencies, mock_env, MockApi, MockQuerier}; use cosmwasm_std::{coin, Addr, Deps, Empty, Env, MemoryStorage, MessageInfo, OwnedDeps}; use rand_chacha::rand_core::SeedableRng; use rand_chacha::ChaCha20Rng; -use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx}; +use sylvia::ctx::{ExecCtx, InstantiateCtx, QueryCtx}; pub fn test_rng() -> ChaCha20Rng { let dummy_seed = [42u8; 32]; ChaCha20Rng::from_seed(dummy_seed) } -const CONTRACT: NymEcashContract<'static> = NymEcashContract::new(); +const CONTRACT: NymEcashContract = NymEcashContract::new(); const DENOM: &str = "unym"; @@ -33,18 +33,15 @@ impl TestSetupSimple { pub fn new() -> Self { let mut deps = mock_dependencies(); let env = mock_env(); - let owner = Addr::unchecked("owner"); - - let init_ctx = InstantiateCtx { - deps: deps.as_mut(), - env: env.clone(), - info: mock_info(owner.as_str(), &[]), - }; + let owner = deps.api.addr_make("owner"); let rng = test_rng(); - let holding_account = Addr::unchecked("holding_account"); - let multisig_contract = Addr::unchecked("multisig_contract"); - let group_contract = Addr::unchecked("group_contract"); + let holding_account = deps.api.addr_make("holding_account"); + let multisig_contract = deps.api.addr_make("multisig_contract"); + let group_contract = deps.api.addr_make("group_contract"); + + let init_ctx = + InstantiateCtx::from((deps.as_mut(), env.clone(), message_info(&owner, &[]))); CONTRACT .instantiate( @@ -73,23 +70,17 @@ impl TestSetupSimple { .get(self.deps.as_ref()) .unwrap() .unwrap(); - mock_info(admin.as_str(), &[]) + message_info(&admin, &[]) } pub fn execute_ctx(&mut self, sender: MessageInfo) -> ExecCtx { - ExecCtx { - env: self.env.clone(), - deps: self.deps.as_mut(), - info: sender, - } + let env = self.env.clone(); + ExecCtx::from((self.deps.as_mut(), env, sender)) } #[allow(dead_code)] pub fn query_ctx(&self) -> QueryCtx { - QueryCtx { - env: self.env.clone(), - deps: self.deps.as_ref(), - } + QueryCtx::from((self.deps.as_ref(), self.env.clone())) } pub fn contract(&self) -> NymEcashContract { diff --git a/contracts/mixnet-vesting-integration-tests/Cargo.toml b/contracts/mixnet-vesting-integration-tests/Cargo.toml index a60f4337587..c3fc07974ff 100644 --- a/contracts/mixnet-vesting-integration-tests/Cargo.toml +++ b/contracts/mixnet-vesting-integration-tests/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "mixnet-vesting-integration-tests" version = "0.1.0" -edition = "2021" +edition = { workspace = true } +authors = { workspace = true } +license = { workspace = true } +repository = { workspace = true } publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -10,7 +13,6 @@ publish = false # cosmwasm dependencies cosmwasm-std = { workspace = true } -cosmwasm-storage = { workspace = true } cw-multi-test = { workspace = true } # contracts dependencies diff --git a/contracts/mixnet/Cargo.toml b/contracts/mixnet/Cargo.toml index c3dc4b89d84..917ad82e88a 100644 --- a/contracts/mixnet/Cargo.toml +++ b/contracts/mixnet/Cargo.toml @@ -32,7 +32,7 @@ nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts cosmwasm-schema = { workspace = true, optional = true } cosmwasm-std = { workspace = true } -cosmwasm-storage = { workspace = true } + cosmwasm-derive = { workspace = true } cw-controllers = { workspace = true } cw2 = { workspace = true } @@ -49,6 +49,7 @@ anyhow.workspace = true rand_chacha = "0.3" rand = "0.8.5" nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] } +easy-addr = { path = "../../common/cosmwasm-smart-contracts/easy_addr" } [features] default = [] diff --git a/contracts/mixnet/schema/nym-mixnet-contract.json b/contracts/mixnet/schema/nym-mixnet-contract.json index 2cf7b7675ff..3b80f44e3a6 100644 --- a/contracts/mixnet/schema/nym-mixnet-contract.json +++ b/contracts/mixnet/schema/nym-mixnet-contract.json @@ -109,7 +109,8 @@ "format": "uint64", "minimum": 0.0 } - } + }, + "additionalProperties": false }, "InitialRewardingParams": { "type": "object", @@ -1303,7 +1304,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "ConfigScoreParamsUpdate": { "type": "object", @@ -3558,7 +3560,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -3675,7 +3678,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -3794,7 +3798,8 @@ } ] } - } + }, + "additionalProperties": false }, "MixNodeDetails": { "description": "Full details associated with given mixnode.", @@ -3948,7 +3953,8 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false }, "Percent": { "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", @@ -4093,7 +4099,8 @@ "format": "uint64", "minimum": 0.0 } - } + }, + "additionalProperties": false }, "Interval": { "type": "object", @@ -4132,7 +4139,8 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false } } }, @@ -4290,7 +4298,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -4418,7 +4427,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -4687,7 +4697,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -4776,7 +4787,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -4832,7 +4844,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Gateway": { "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", @@ -4992,7 +5005,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Gateway": { "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", @@ -5154,7 +5168,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "MixNode": { "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", @@ -5269,7 +5284,8 @@ } ] } - } + }, + "additionalProperties": false }, "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", @@ -5329,7 +5345,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -5448,7 +5465,8 @@ } ] } - } + }, + "additionalProperties": false }, "MixNodeDetails": { "description": "Full details associated with given mixnode.", @@ -5602,7 +5620,8 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false }, "Percent": { "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", @@ -5664,7 +5683,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -5783,7 +5803,8 @@ } ] } - } + }, + "additionalProperties": false }, "MixNodeDetails": { "description": "Full details associated with given mixnode.", @@ -5937,7 +5958,8 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false }, "Percent": { "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", @@ -5995,7 +6017,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -6151,7 +6174,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -6266,7 +6290,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -6494,7 +6519,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "NymNode": { "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", @@ -6630,7 +6656,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -6927,7 +6954,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -7337,7 +7365,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -7638,7 +7667,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Gateway": { "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", @@ -7796,7 +7826,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -7915,7 +7946,8 @@ } ] } - } + }, + "additionalProperties": false }, "MixNodeDetails": { "description": "Full details associated with given mixnode.", @@ -8069,7 +8101,8 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false }, "Percent": { "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", @@ -8133,7 +8166,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -8453,7 +8487,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -8538,7 +8573,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "PendingEpochEventData": { "description": "Details of a particular pending epoch event.", @@ -8964,7 +9000,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "PendingEpochEvent": { "description": "A request made at some point in the current epoch that's going to get resolved once the epoch rolls over.", @@ -9373,7 +9410,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -9744,7 +9782,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -10160,7 +10199,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -10239,7 +10279,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -10723,7 +10764,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "ConfigScoreParams": { "type": "object", @@ -10990,7 +11032,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "ConfigScoreParams": { "type": "object", diff --git a/contracts/mixnet/schema/raw/execute.json b/contracts/mixnet/schema/raw/execute.json index 367c6483b97..8884a731581 100644 --- a/contracts/mixnet/schema/raw/execute.json +++ b/contracts/mixnet/schema/raw/execute.json @@ -1022,7 +1022,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "ConfigScoreParamsUpdate": { "type": "object", diff --git a/contracts/mixnet/schema/raw/instantiate.json b/contracts/mixnet/schema/raw/instantiate.json index 7ab76571c79..0eca5410beb 100644 --- a/contracts/mixnet/schema/raw/instantiate.json +++ b/contracts/mixnet/schema/raw/instantiate.json @@ -105,7 +105,8 @@ "format": "uint64", "minimum": 0.0 } - } + }, + "additionalProperties": false }, "InitialRewardingParams": { "type": "object", diff --git a/contracts/mixnet/schema/raw/response_to_get_all_delegations.json b/contracts/mixnet/schema/raw/response_to_get_all_delegations.json index 71878b1827b..06e9bd10027 100644 --- a/contracts/mixnet/schema/raw/response_to_get_all_delegations.json +++ b/contracts/mixnet/schema/raw/response_to_get_all_delegations.json @@ -53,7 +53,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", diff --git a/contracts/mixnet/schema/raw/response_to_get_bonded_mixnode_details_by_identity.json b/contracts/mixnet/schema/raw/response_to_get_bonded_mixnode_details_by_identity.json index 239f0fc353f..85cb97db67c 100644 --- a/contracts/mixnet/schema/raw/response_to_get_bonded_mixnode_details_by_identity.json +++ b/contracts/mixnet/schema/raw/response_to_get_bonded_mixnode_details_by_identity.json @@ -42,7 +42,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -161,7 +162,8 @@ } ] } - } + }, + "additionalProperties": false }, "MixNodeDetails": { "description": "Full details associated with given mixnode.", @@ -315,7 +317,8 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false }, "Percent": { "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", diff --git a/contracts/mixnet/schema/raw/response_to_get_current_interval_details.json b/contracts/mixnet/schema/raw/response_to_get_current_interval_details.json index 1a90ceb1eb9..8eace712abc 100644 --- a/contracts/mixnet/schema/raw/response_to_get_current_interval_details.json +++ b/contracts/mixnet/schema/raw/response_to_get_current_interval_details.json @@ -52,7 +52,8 @@ "format": "uint64", "minimum": 0.0 } - } + }, + "additionalProperties": false }, "Interval": { "type": "object", @@ -91,7 +92,8 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false } } } diff --git a/contracts/mixnet/schema/raw/response_to_get_delegation_details.json b/contracts/mixnet/schema/raw/response_to_get_delegation_details.json index f8807370a5b..931eba686b7 100644 --- a/contracts/mixnet/schema/raw/response_to_get_delegation_details.json +++ b/contracts/mixnet/schema/raw/response_to_get_delegation_details.json @@ -47,7 +47,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", diff --git a/contracts/mixnet/schema/raw/response_to_get_delegator_delegations.json b/contracts/mixnet/schema/raw/response_to_get_delegator_delegations.json index e3556f1db2c..e7e389eb914 100644 --- a/contracts/mixnet/schema/raw/response_to_get_delegator_delegations.json +++ b/contracts/mixnet/schema/raw/response_to_get_delegator_delegations.json @@ -53,7 +53,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", diff --git a/contracts/mixnet/schema/raw/response_to_get_estimated_current_epoch_delegator_reward.json b/contracts/mixnet/schema/raw/response_to_get_estimated_current_epoch_delegator_reward.json index 94ca8beb784..cabfbe90ee9 100644 --- a/contracts/mixnet/schema/raw/response_to_get_estimated_current_epoch_delegator_reward.json +++ b/contracts/mixnet/schema/raw/response_to_get_estimated_current_epoch_delegator_reward.json @@ -75,7 +75,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", diff --git a/contracts/mixnet/schema/raw/response_to_get_estimated_current_epoch_operator_reward.json b/contracts/mixnet/schema/raw/response_to_get_estimated_current_epoch_operator_reward.json index 94ca8beb784..cabfbe90ee9 100644 --- a/contracts/mixnet/schema/raw/response_to_get_estimated_current_epoch_operator_reward.json +++ b/contracts/mixnet/schema/raw/response_to_get_estimated_current_epoch_operator_reward.json @@ -75,7 +75,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", diff --git a/contracts/mixnet/schema/raw/response_to_get_gateway_bond.json b/contracts/mixnet/schema/raw/response_to_get_gateway_bond.json index dd7f99b4a46..34f5b01622a 100644 --- a/contracts/mixnet/schema/raw/response_to_get_gateway_bond.json +++ b/contracts/mixnet/schema/raw/response_to_get_gateway_bond.json @@ -42,7 +42,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Gateway": { "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", diff --git a/contracts/mixnet/schema/raw/response_to_get_gateways.json b/contracts/mixnet/schema/raw/response_to_get_gateways.json index 0ec50135b66..77f2582f39c 100644 --- a/contracts/mixnet/schema/raw/response_to_get_gateways.json +++ b/contracts/mixnet/schema/raw/response_to_get_gateways.json @@ -48,7 +48,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Gateway": { "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", diff --git a/contracts/mixnet/schema/raw/response_to_get_mix_node_bonds.json b/contracts/mixnet/schema/raw/response_to_get_mix_node_bonds.json index ceb99ca8cba..7c5d6daec93 100644 --- a/contracts/mixnet/schema/raw/response_to_get_mix_node_bonds.json +++ b/contracts/mixnet/schema/raw/response_to_get_mix_node_bonds.json @@ -50,7 +50,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "MixNode": { "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", @@ -165,7 +166,8 @@ } ] } - } + }, + "additionalProperties": false }, "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", diff --git a/contracts/mixnet/schema/raw/response_to_get_mix_nodes_detailed.json b/contracts/mixnet/schema/raw/response_to_get_mix_nodes_detailed.json index 8db9d629b62..d57b9984375 100644 --- a/contracts/mixnet/schema/raw/response_to_get_mix_nodes_detailed.json +++ b/contracts/mixnet/schema/raw/response_to_get_mix_nodes_detailed.json @@ -50,7 +50,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -169,7 +170,8 @@ } ] } - } + }, + "additionalProperties": false }, "MixNodeDetails": { "description": "Full details associated with given mixnode.", @@ -323,7 +325,8 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false }, "Percent": { "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", diff --git a/contracts/mixnet/schema/raw/response_to_get_mixnode_details.json b/contracts/mixnet/schema/raw/response_to_get_mixnode_details.json index bee2b305229..1b84fa5e686 100644 --- a/contracts/mixnet/schema/raw/response_to_get_mixnode_details.json +++ b/contracts/mixnet/schema/raw/response_to_get_mixnode_details.json @@ -44,7 +44,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -163,7 +164,8 @@ } ] } - } + }, + "additionalProperties": false }, "MixNodeDetails": { "description": "Full details associated with given mixnode.", @@ -317,7 +319,8 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false }, "Percent": { "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", diff --git a/contracts/mixnet/schema/raw/response_to_get_mixnode_rewarding_details.json b/contracts/mixnet/schema/raw/response_to_get_mixnode_rewarding_details.json index dcb9cb67cda..e3d6f27522b 100644 --- a/contracts/mixnet/schema/raw/response_to_get_mixnode_rewarding_details.json +++ b/contracts/mixnet/schema/raw/response_to_get_mixnode_rewarding_details.json @@ -40,7 +40,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", diff --git a/contracts/mixnet/schema/raw/response_to_get_node_delegations.json b/contracts/mixnet/schema/raw/response_to_get_node_delegations.json index c8ff4df2211..150956b58a2 100644 --- a/contracts/mixnet/schema/raw/response_to_get_node_delegations.json +++ b/contracts/mixnet/schema/raw/response_to_get_node_delegations.json @@ -41,7 +41,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", diff --git a/contracts/mixnet/schema/raw/response_to_get_node_rewarding_details.json b/contracts/mixnet/schema/raw/response_to_get_node_rewarding_details.json index e9b3a6e5450..22277ed1ed1 100644 --- a/contracts/mixnet/schema/raw/response_to_get_node_rewarding_details.json +++ b/contracts/mixnet/schema/raw/response_to_get_node_rewarding_details.json @@ -40,7 +40,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", diff --git a/contracts/mixnet/schema/raw/response_to_get_nym_node_bonds_paged.json b/contracts/mixnet/schema/raw/response_to_get_nym_node_bonds_paged.json index 6be0563f7a5..6c5c6c92d5b 100644 --- a/contracts/mixnet/schema/raw/response_to_get_nym_node_bonds_paged.json +++ b/contracts/mixnet/schema/raw/response_to_get_nym_node_bonds_paged.json @@ -42,7 +42,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "NymNode": { "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", diff --git a/contracts/mixnet/schema/raw/response_to_get_nym_node_details.json b/contracts/mixnet/schema/raw/response_to_get_nym_node_details.json index 4707dce8573..f21138a2e57 100644 --- a/contracts/mixnet/schema/raw/response_to_get_nym_node_details.json +++ b/contracts/mixnet/schema/raw/response_to_get_nym_node_details.json @@ -44,7 +44,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", diff --git a/contracts/mixnet/schema/raw/response_to_get_nym_node_details_by_identity_key.json b/contracts/mixnet/schema/raw/response_to_get_nym_node_details_by_identity_key.json index aea64e0746c..6c1aaf80d0a 100644 --- a/contracts/mixnet/schema/raw/response_to_get_nym_node_details_by_identity_key.json +++ b/contracts/mixnet/schema/raw/response_to_get_nym_node_details_by_identity_key.json @@ -42,7 +42,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", diff --git a/contracts/mixnet/schema/raw/response_to_get_nym_nodes_detailed_paged.json b/contracts/mixnet/schema/raw/response_to_get_nym_nodes_detailed_paged.json index 0b96dd856c3..9d5747da7ab 100644 --- a/contracts/mixnet/schema/raw/response_to_get_nym_nodes_detailed_paged.json +++ b/contracts/mixnet/schema/raw/response_to_get_nym_nodes_detailed_paged.json @@ -42,7 +42,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", diff --git a/contracts/mixnet/schema/raw/response_to_get_owned_gateway.json b/contracts/mixnet/schema/raw/response_to_get_owned_gateway.json index 50f2bbd4ada..a9518f395d8 100644 --- a/contracts/mixnet/schema/raw/response_to_get_owned_gateway.json +++ b/contracts/mixnet/schema/raw/response_to_get_owned_gateway.json @@ -46,7 +46,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Gateway": { "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", diff --git a/contracts/mixnet/schema/raw/response_to_get_owned_mixnode.json b/contracts/mixnet/schema/raw/response_to_get_owned_mixnode.json index 94499e2fd06..968b1fe4ab7 100644 --- a/contracts/mixnet/schema/raw/response_to_get_owned_mixnode.json +++ b/contracts/mixnet/schema/raw/response_to_get_owned_mixnode.json @@ -46,7 +46,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -165,7 +166,8 @@ } ] } - } + }, + "additionalProperties": false }, "MixNodeDetails": { "description": "Full details associated with given mixnode.", @@ -319,7 +321,8 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false }, "Percent": { "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", diff --git a/contracts/mixnet/schema/raw/response_to_get_owned_nym_node.json b/contracts/mixnet/schema/raw/response_to_get_owned_nym_node.json index ed1ccc194d5..0053ea72888 100644 --- a/contracts/mixnet/schema/raw/response_to_get_owned_nym_node.json +++ b/contracts/mixnet/schema/raw/response_to_get_owned_nym_node.json @@ -46,7 +46,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", diff --git a/contracts/mixnet/schema/raw/response_to_get_pending_delegator_reward.json b/contracts/mixnet/schema/raw/response_to_get_pending_delegator_reward.json index 4ae2297c32e..2f0263ea6a1 100644 --- a/contracts/mixnet/schema/raw/response_to_get_pending_delegator_reward.json +++ b/contracts/mixnet/schema/raw/response_to_get_pending_delegator_reward.json @@ -65,7 +65,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", diff --git a/contracts/mixnet/schema/raw/response_to_get_pending_epoch_event.json b/contracts/mixnet/schema/raw/response_to_get_pending_epoch_event.json index 67b89db6720..b5c412c4d96 100644 --- a/contracts/mixnet/schema/raw/response_to_get_pending_epoch_event.json +++ b/contracts/mixnet/schema/raw/response_to_get_pending_epoch_event.json @@ -71,7 +71,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "PendingEpochEventData": { "description": "Details of a particular pending epoch event.", diff --git a/contracts/mixnet/schema/raw/response_to_get_pending_epoch_events.json b/contracts/mixnet/schema/raw/response_to_get_pending_epoch_events.json index 54a6a8e52fb..9ef324a268d 100644 --- a/contracts/mixnet/schema/raw/response_to_get_pending_epoch_events.json +++ b/contracts/mixnet/schema/raw/response_to_get_pending_epoch_events.json @@ -79,7 +79,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "PendingEpochEvent": { "description": "A request made at some point in the current epoch that's going to get resolved once the epoch rolls over.", diff --git a/contracts/mixnet/schema/raw/response_to_get_pending_interval_event.json b/contracts/mixnet/schema/raw/response_to_get_pending_interval_event.json index f6926bf188b..f4fbfd8ec36 100644 --- a/contracts/mixnet/schema/raw/response_to_get_pending_interval_event.json +++ b/contracts/mixnet/schema/raw/response_to_get_pending_interval_event.json @@ -37,7 +37,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", diff --git a/contracts/mixnet/schema/raw/response_to_get_pending_interval_events.json b/contracts/mixnet/schema/raw/response_to_get_pending_interval_events.json index 188e99023a4..ed8a751b2d8 100644 --- a/contracts/mixnet/schema/raw/response_to_get_pending_interval_events.json +++ b/contracts/mixnet/schema/raw/response_to_get_pending_interval_events.json @@ -45,7 +45,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", diff --git a/contracts/mixnet/schema/raw/response_to_get_pending_node_operator_reward.json b/contracts/mixnet/schema/raw/response_to_get_pending_node_operator_reward.json index 4ae2297c32e..2f0263ea6a1 100644 --- a/contracts/mixnet/schema/raw/response_to_get_pending_node_operator_reward.json +++ b/contracts/mixnet/schema/raw/response_to_get_pending_node_operator_reward.json @@ -65,7 +65,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", diff --git a/contracts/mixnet/schema/raw/response_to_get_pending_operator_reward.json b/contracts/mixnet/schema/raw/response_to_get_pending_operator_reward.json index 4ae2297c32e..2f0263ea6a1 100644 --- a/contracts/mixnet/schema/raw/response_to_get_pending_operator_reward.json +++ b/contracts/mixnet/schema/raw/response_to_get_pending_operator_reward.json @@ -65,7 +65,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", diff --git a/contracts/mixnet/schema/raw/response_to_get_state.json b/contracts/mixnet/schema/raw/response_to_get_state.json index 8d688360f71..c4a10bdb80a 100644 --- a/contracts/mixnet/schema/raw/response_to_get_state.json +++ b/contracts/mixnet/schema/raw/response_to_get_state.json @@ -71,7 +71,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "ConfigScoreParams": { "type": "object", diff --git a/contracts/mixnet/schema/raw/response_to_get_state_params.json b/contracts/mixnet/schema/raw/response_to_get_state_params.json index 47da7893a61..0599dfafd3e 100644 --- a/contracts/mixnet/schema/raw/response_to_get_state_params.json +++ b/contracts/mixnet/schema/raw/response_to_get_state_params.json @@ -49,7 +49,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "ConfigScoreParams": { "type": "object", diff --git a/contracts/mixnet/src/compat/helpers.rs b/contracts/mixnet/src/compat/helpers.rs index 46b845a05d4..aa6f3a223ff 100644 --- a/contracts/mixnet/src/compat/helpers.rs +++ b/contracts/mixnet/src/compat/helpers.rs @@ -155,7 +155,7 @@ mod tests { #[test] fn for_legacy_mixnode() -> anyhow::Result<()> { let mut test = TestSetup::new(); - let node_id = test.add_legacy_mixnode("owner", None); + let node_id = test.add_legacy_mixnode(&test.make_addr("owner"), None); let details = test.mixnode_by_id(node_id).unwrap(); // node must not be in the process of unbonding @@ -171,7 +171,7 @@ mod tests { #[test] fn for_nymnode() -> anyhow::Result<()> { let mut test = TestSetup::new(); - let node_id = test.add_dummy_nymnode("owner", None); + let node_id = test.add_dummy_nymnode(&test.make_addr("owner"), None); let details = test.nymnode_by_id(node_id).unwrap(); // node must not be in the process of unbonding @@ -193,7 +193,7 @@ mod tests { #[test] fn for_legacy_mixnode() -> anyhow::Result<()> { let mut test = TestSetup::new(); - let node_id = test.add_legacy_mixnode("owner", None); + let node_id = test.add_legacy_mixnode(&test.make_addr("owner"), None); let details = test.mixnode_by_id(node_id).unwrap(); assert!(ensure_can_modify_cost_params(test.deps().storage, &details).is_ok()); @@ -221,7 +221,7 @@ mod tests { #[test] fn for_nymnode() -> anyhow::Result<()> { let mut test = TestSetup::new(); - let node_id = test.add_dummy_nymnode("owner", None); + let node_id = test.add_dummy_nymnode(&test.make_addr("owner"), None); let details = test.nymnode_by_id(node_id).unwrap(); assert!(ensure_can_modify_cost_params(test.deps().storage, &details).is_ok()); @@ -252,12 +252,12 @@ mod tests { use super::*; use crate::compat::transactions::{try_decrease_pledge, try_increase_pledge}; use crate::support::tests::test_helpers::TestSetup; - use cosmwasm_std::testing::mock_info; + use cosmwasm_std::testing::message_info; #[test] fn for_legacy_mixnode() -> anyhow::Result<()> { let mut test = TestSetup::new(); - let node_id = test.add_legacy_mixnode("owner", None); + let node_id = test.add_legacy_mixnode(&test.make_addr("owner"), None); let details = test.mixnode_by_id(node_id).unwrap(); assert!(ensure_can_increase_pledge(test.deps().storage, &details).is_ok()); @@ -281,9 +281,13 @@ mod tests { // node can't have any pending pledge changes: // - increase - let node_id = test.add_legacy_mixnode("owner2", Some(100_000_000_000u128.into())); + let node_id = test + .add_legacy_mixnode(&test.make_addr("owner2"), Some(100_000_000_000u128.into())); let pledge_change = test.coin(100000); - test.execute_fn(try_increase_pledge, mock_info("owner2", &[pledge_change]))?; + test.execute_fn( + try_increase_pledge, + message_info(&test.make_addr("owner2"), &[pledge_change]), + )?; let details = test.mixnode_by_id(node_id).unwrap(); let res = ensure_can_increase_pledge(test.deps().storage, &details).unwrap_err(); assert_eq!( @@ -294,13 +298,14 @@ mod tests { ); // - decrease - let node_id = test.add_legacy_mixnode("owner3", Some(100_000_000_000u128.into())); + let owner = test.make_addr("owner3"); + let node_id = test.add_legacy_mixnode(&owner, Some(100_000_000_000u128.into())); let pledge_change = test.coin(100000); let env = test.env(); try_decrease_pledge( test.deps_mut(), env, - mock_info("owner3", &[]), + message_info(&owner, &[]), pledge_change, )?; let details = test.mixnode_by_id(node_id).unwrap(); @@ -317,7 +322,7 @@ mod tests { #[test] fn for_nymnode() -> anyhow::Result<()> { let mut test = TestSetup::new(); - let node_id = test.add_dummy_nymnode("owner", None); + let node_id = test.add_dummy_nymnode(&test.make_addr("owner"), None); let details = test.nymnode_by_id(node_id).unwrap(); assert!(ensure_can_increase_pledge(test.deps().storage, &details).is_ok()); @@ -341,9 +346,13 @@ mod tests { // node can't have any pending pledge changes: // - increase - let node_id = test.add_dummy_nymnode("owner2", Some(100_000_000_000u128.into())); + let node_id = + test.add_dummy_nymnode(&test.make_addr("owner2"), Some(100_000_000_000u128.into())); let pledge_change = test.coin(100000); - test.execute_fn(try_increase_pledge, mock_info("owner2", &[pledge_change]))?; + test.execute_fn( + try_increase_pledge, + message_info(&test.make_addr("owner2"), &[pledge_change]), + )?; let details = test.nymnode_by_id(node_id).unwrap(); let res = ensure_can_increase_pledge(test.deps().storage, &details).unwrap_err(); assert_eq!( @@ -354,13 +363,14 @@ mod tests { ); // - decrease - let node_id = test.add_dummy_nymnode("owner3", Some(100_000_000_000u128.into())); + let owner = test.make_addr("owner3"); + let node_id = test.add_dummy_nymnode(&owner, Some(100_000_000_000u128.into())); let pledge_change = test.coin(100000); let env = test.env(); try_decrease_pledge( test.deps_mut(), env, - mock_info("owner3", &[]), + message_info(&owner, &[]), pledge_change, )?; let details = test.nymnode_by_id(node_id).unwrap(); @@ -381,12 +391,13 @@ mod tests { use crate::compat::transactions::{try_decrease_pledge, try_increase_pledge}; use crate::support::tests::test_helpers::TestSetup; use cosmwasm_std::coin; - use cosmwasm_std::testing::mock_info; + use cosmwasm_std::testing::message_info; #[test] fn for_legacy_mixnode() -> anyhow::Result<()> { let mut test = TestSetup::new(); - let node_id = test.add_legacy_mixnode("owner", Some(100_000_000_000u128.into())); + let node_id = + test.add_legacy_mixnode(&test.make_addr("owner"), Some(100_000_000_000u128.into())); let valid_decrease = test.coin(100); let details = test.mixnode_by_id(node_id).unwrap(); @@ -415,9 +426,13 @@ mod tests { // node can't have any pending pledge changes: // - increase - let node_id = test.add_legacy_mixnode("owner2", Some(100_000_000_000u128.into())); + let node_id = test + .add_legacy_mixnode(&test.make_addr("owner2"), Some(100_000_000_000u128.into())); let pledge_change = test.coin(100000); - test.execute_fn(try_increase_pledge, mock_info("owner2", &[pledge_change]))?; + test.execute_fn( + try_increase_pledge, + message_info(&test.make_addr("owner2"), &[pledge_change]), + )?; let details = test.mixnode_by_id(node_id).unwrap(); let res = ensure_can_decrease_pledge(test.deps().storage, &details, &valid_decrease) .unwrap_err(); @@ -429,13 +444,14 @@ mod tests { ); // - decrease - let node_id = test.add_legacy_mixnode("owner3", Some(100_000_000_000u128.into())); + let owner = test.make_addr("owner3"); + let node_id = test.add_legacy_mixnode(&owner, Some(100_000_000_000u128.into())); let pledge_change = test.coin(100000); let env = test.env(); try_decrease_pledge( test.deps_mut(), env, - mock_info("owner3", &[]), + message_info(&owner, &[]), pledge_change, )?; let details = test.mixnode_by_id(node_id).unwrap(); @@ -449,7 +465,8 @@ mod tests { ); // denom must match - let node_id = test.add_legacy_mixnode("owner4", Some(100_000_000_000u128.into())); + let node_id = test + .add_legacy_mixnode(&test.make_addr("owner4"), Some(100_000_000_000u128.into())); let details = test.mixnode_by_id(node_id).unwrap(); let bad_decrease = coin(123, "weird-denom"); let res = ensure_can_decrease_pledge(test.deps().storage, &details, &bad_decrease) @@ -457,7 +474,8 @@ mod tests { assert!(matches!(res, MixnetContractError::WrongDenom { .. })); // value must be non-zero - let node_id = test.add_legacy_mixnode("owner5", Some(100_000_000_000u128.into())); + let node_id = test + .add_legacy_mixnode(&test.make_addr("owner5"), Some(100_000_000_000u128.into())); let details = test.mixnode_by_id(node_id).unwrap(); let bad_decrease = test.coin(0); let res = ensure_can_decrease_pledge(test.deps().storage, &details, &bad_decrease) @@ -465,7 +483,8 @@ mod tests { assert_eq!(res, MixnetContractError::ZeroCoinAmount); // new pledge must be bigger than minimum - let node_id = test.add_legacy_mixnode("owner6", Some(100_000_100u128.into())); + let node_id = + test.add_legacy_mixnode(&test.make_addr("owner6"), Some(100_000_100u128.into())); let details = test.mixnode_by_id(node_id).unwrap(); let bad_decrease = test.coin(101); let res = ensure_can_decrease_pledge(test.deps().storage, &details, &bad_decrease) @@ -481,7 +500,8 @@ mod tests { #[test] fn for_nymnode() -> anyhow::Result<()> { let mut test = TestSetup::new(); - let node_id = test.add_dummy_nymnode("owner", Some(100_000_000_000u128.into())); + let node_id = + test.add_dummy_nymnode(&test.make_addr("owner"), Some(100_000_000_000u128.into())); let valid_decrease = test.coin(100); let details = test.nymnode_by_id(node_id).unwrap(); @@ -510,9 +530,13 @@ mod tests { // node can't have any pending pledge changes: // - increase - let node_id = test.add_dummy_nymnode("owner2", Some(100_000_000_000u128.into())); + let node_id = + test.add_dummy_nymnode(&test.make_addr("owner2"), Some(100_000_000_000u128.into())); let pledge_change = test.coin(100000); - test.execute_fn(try_increase_pledge, mock_info("owner2", &[pledge_change]))?; + test.execute_fn( + try_increase_pledge, + message_info(&test.make_addr("owner2"), &[pledge_change]), + )?; let details = test.nymnode_by_id(node_id).unwrap(); let res = ensure_can_decrease_pledge(test.deps().storage, &details, &valid_decrease) .unwrap_err(); @@ -524,13 +548,14 @@ mod tests { ); // - decrease - let node_id = test.add_dummy_nymnode("owner3", Some(100_000_000_000u128.into())); + let owner = test.make_addr("owner3"); + let node_id = test.add_dummy_nymnode(&owner, Some(100_000_000_000u128.into())); let pledge_change = test.coin(100000); let env = test.env(); try_decrease_pledge( test.deps_mut(), env, - mock_info("owner3", &[]), + message_info(&owner, &[]), pledge_change, )?; let details = test.nymnode_by_id(node_id).unwrap(); @@ -544,7 +569,8 @@ mod tests { ); // denom must match - let node_id = test.add_dummy_nymnode("owner4", Some(100_000_000_000u128.into())); + let node_id = + test.add_dummy_nymnode(&test.make_addr("owner4"), Some(100_000_000_000u128.into())); let details = test.nymnode_by_id(node_id).unwrap(); let bad_decrease = coin(123, "weird-denom"); let res = ensure_can_decrease_pledge(test.deps().storage, &details, &bad_decrease) @@ -552,7 +578,8 @@ mod tests { assert!(matches!(res, MixnetContractError::WrongDenom { .. })); // value must be non-zero - let node_id = test.add_dummy_nymnode("owner5", Some(100_000_000_000u128.into())); + let node_id = + test.add_dummy_nymnode(&test.make_addr("owner5"), Some(100_000_000_000u128.into())); let details = test.nymnode_by_id(node_id).unwrap(); let bad_decrease = test.coin(0); let res = ensure_can_decrease_pledge(test.deps().storage, &details, &bad_decrease) @@ -560,7 +587,8 @@ mod tests { assert_eq!(res, MixnetContractError::ZeroCoinAmount); // new pledge must be bigger than minimum - let node_id = test.add_dummy_nymnode("owner6", Some(100_000_100u128.into())); + let node_id = + test.add_dummy_nymnode(&test.make_addr("owner6"), Some(100_000_100u128.into())); let details = test.nymnode_by_id(node_id).unwrap(); let bad_decrease = test.coin(101); let res = ensure_can_decrease_pledge(test.deps().storage, &details, &bad_decrease) diff --git a/contracts/mixnet/src/compat/transactions.rs b/contracts/mixnet/src/compat/transactions.rs index 64630b564e7..a9941f5b7f5 100644 --- a/contracts/mixnet/src/compat/transactions.rs +++ b/contracts/mixnet/src/compat/transactions.rs @@ -102,19 +102,18 @@ mod tests { mod increasing_pledge { use super::*; use crate::support::tests::test_helpers::TestSetup; - use cosmwasm_std::testing::mock_info; - use cosmwasm_std::Addr; + use cosmwasm_std::testing::message_info; #[test] fn when_there_are_no_nodes() -> anyhow::Result<()> { let mut test = TestSetup::new(); - let sender = mock_info("owner", &[test.coin(100000)]); + let sender = message_info(&test.make_addr("owner"), &[test.coin(100000)]); let err = test.execute_fn(try_increase_pledge, sender).unwrap_err(); assert_eq!( MixnetContractError::NoAssociatedNodeBond { - owner: Addr::unchecked("owner"), + owner: test.make_addr("owner"), }, err ); @@ -126,8 +125,9 @@ mod tests { fn for_legacy_mixnode() -> anyhow::Result<()> { let mut test = TestSetup::new(); - let node_id = test.add_legacy_mixnode("owner", Some(100_000_000u128.into())); - let sender = mock_info("owner", &[test.coin(100_000)]); + let node_id = + test.add_legacy_mixnode(&test.make_addr("owner"), Some(100_000_000u128.into())); + let sender = message_info(&test.make_addr("owner"), &[test.coin(100_000)]); test.assert_simple_execution(try_increase_pledge, sender); let after = test.mixnode_by_id(node_id).unwrap(); @@ -149,14 +149,14 @@ mod tests { fn for_legacy_gateway() -> anyhow::Result<()> { let mut test = TestSetup::new(); - test.add_legacy_gateway("owner", None); - let sender = mock_info("owner", &[test.coin(100000)]); + test.add_legacy_gateway(&test.make_addr("owner"), None); + let sender = message_info(&test.make_addr("owner"), &[test.coin(100000)]); let err = test.execute_fn(try_increase_pledge, sender).unwrap_err(); // it's illegal to increase pledge for legacy gateways assert_eq!( MixnetContractError::NoAssociatedNodeBond { - owner: Addr::unchecked("owner"), + owner: test.make_addr("owner"), }, err ); @@ -168,8 +168,9 @@ mod tests { fn for_nymnode() -> anyhow::Result<()> { let mut test = TestSetup::new(); - let node_id = test.add_dummy_nymnode("owner", Some(100_000_000u128.into())); - let sender = mock_info("owner", &[test.coin(100_000)]); + let node_id = + test.add_dummy_nymnode(&test.make_addr("owner"), Some(100_000_000u128.into())); + let sender = message_info(&test.make_addr("owner"), &[test.coin(100_000)]); test.assert_simple_execution(try_increase_pledge, sender); let after = test.nymnode_by_id(node_id).unwrap(); @@ -192,21 +193,19 @@ mod tests { mod decreasing_pledge { use super::*; use crate::support::tests::test_helpers::TestSetup; - use cosmwasm_std::testing::mock_info; - use cosmwasm_std::Addr; #[test] fn when_there_are_no_nodes() -> anyhow::Result<()> { let mut test = TestSetup::new(); - let sender = mock_info("owner", &[]); + let sender = test.make_sender("owner"); let env = test.env(); let decrease_by = test.coin(1000); let err = try_decrease_pledge(test.deps_mut(), env, sender, decrease_by).unwrap_err(); assert_eq!( MixnetContractError::NoAssociatedNodeBond { - owner: Addr::unchecked("owner"), + owner: test.make_addr("owner"), }, err ); @@ -218,8 +217,9 @@ mod tests { fn for_legacy_mixnode() -> anyhow::Result<()> { let mut test = TestSetup::new(); - let node_id = test.add_legacy_mixnode("owner", Some(120_000_000u128.into())); - let sender = mock_info("owner", &[]); + let node_id = + test.add_legacy_mixnode(&test.make_addr("owner"), Some(120_000_000u128.into())); + let sender = test.make_sender("owner"); let env = test.env(); let decrease_by = test.coin(1000); try_decrease_pledge(test.deps_mut(), env, sender, decrease_by)?; @@ -243,8 +243,8 @@ mod tests { fn for_legacy_gateway() -> anyhow::Result<()> { let mut test = TestSetup::new(); - test.add_legacy_gateway("owner", None); - let sender = mock_info("owner", &[]); + test.add_legacy_gateway(&test.make_addr("owner"), None); + let sender = test.make_sender("owner"); let env = test.env(); let decrease_by = test.coin(1000); let err = try_decrease_pledge(test.deps_mut(), env, sender, decrease_by).unwrap_err(); @@ -252,7 +252,7 @@ mod tests { // it's illegal to decrease pledge for legacy gateways assert_eq!( MixnetContractError::NoAssociatedNodeBond { - owner: Addr::unchecked("owner"), + owner: test.make_addr("owner"), }, err ); @@ -264,8 +264,9 @@ mod tests { fn for_nymnode() -> anyhow::Result<()> { let mut test = TestSetup::new(); - let node_id = test.add_dummy_nymnode("owner", Some(120_000_000u128.into())); - let sender = mock_info("owner", &[]); + let node_id = + test.add_dummy_nymnode(&test.make_addr("owner"), Some(120_000_000u128.into())); + let sender = test.make_sender("owner"); let env = test.env(); let decrease_by = test.coin(1000); @@ -292,8 +293,8 @@ mod tests { use super::*; use crate::support::tests::fixtures::TEST_COIN_DENOM; use crate::support::tests::test_helpers::TestSetup; - use cosmwasm_std::testing::mock_info; - use cosmwasm_std::{Addr, Uint128}; + use cosmwasm_std::testing::message_info; + use cosmwasm_std::Uint128; use mixnet_contract_common::{OperatingCostRange, ProfitMarginRange}; use nym_contracts_common::Percent; @@ -317,8 +318,8 @@ mod tests { test.update_profit_margin_range(range); // below lower - test.add_dummy_nymnode("owner1", None); - let sender = mock_info("owner1", &[]); + test.add_dummy_nymnode(&test.make_addr("owner1"), None); + let sender = message_info(&test.make_addr("owner1"), &[]); let env = test.env(); let mut update = new_dummy_params(); update.profit_margin_percent = Percent::from_percentage_value(9)?; @@ -330,8 +331,8 @@ mod tests { )); // zero - test.add_dummy_nymnode("owner2", None); - let sender = mock_info("owner2", &[]); + test.add_dummy_nymnode(&test.make_addr("owner2"), None); + let sender = message_info(&test.make_addr("owner2"), &[]); let env = test.env(); let mut update = new_dummy_params(); update.profit_margin_percent = Percent::zero(); @@ -343,8 +344,8 @@ mod tests { )); // exactly at lower - test.add_dummy_nymnode("owner3", None); - let sender = mock_info("owner3", &[]); + test.add_dummy_nymnode(&test.make_addr("owner3"), None); + let sender = message_info(&test.make_addr("owner3"), &[]); let env = test.env(); let mut update = new_dummy_params(); update.profit_margin_percent = minimum; @@ -352,8 +353,8 @@ mod tests { assert!(res.is_ok()); // above upper - test.add_dummy_nymnode("owner4", None); - let sender = mock_info("owner4", &[]); + test.add_dummy_nymnode(&test.make_addr("owner4"), None); + let sender = message_info(&test.make_addr("owner4"), &[]); let env = test.env(); let mut update = new_dummy_params(); update.profit_margin_percent = Percent::from_percentage_value(81)?; @@ -365,8 +366,8 @@ mod tests { )); // a hundred - test.add_dummy_nymnode("owner5", None); - let sender = mock_info("owner5", &[]); + test.add_dummy_nymnode(&test.make_addr("owner5"), None); + let sender = message_info(&test.make_addr("owner5"), &[]); let env = test.env(); let mut update = new_dummy_params(); update.profit_margin_percent = Percent::hundred(); @@ -378,8 +379,8 @@ mod tests { )); // exactly at upper - test.add_dummy_nymnode("owner6", None); - let sender = mock_info("owner6", &[]); + test.add_dummy_nymnode(&test.make_addr("owner6"), None); + let sender = message_info(&test.make_addr("owner6"), &[]); let env = test.env(); let mut update = new_dummy_params(); update.profit_margin_percent = maximum; @@ -399,8 +400,8 @@ mod tests { test.update_operating_cost_range(range); // below lower - test.add_dummy_nymnode("owner1", None); - let sender = mock_info("owner1", &[]); + test.add_dummy_nymnode(&test.make_addr("owner1"), None); + let sender = message_info(&test.make_addr("owner1"), &[]); let env = test.env(); let mut update = new_dummy_params(); update.interval_operating_cost = test.coin(999); @@ -412,8 +413,8 @@ mod tests { )); // zero - test.add_dummy_nymnode("owner2", None); - let sender = mock_info("owner2", &[]); + test.add_dummy_nymnode(&test.make_addr("owner2"), None); + let sender = message_info(&test.make_addr("owner2"), &[]); let env = test.env(); let mut update = new_dummy_params(); update.interval_operating_cost = test.coin(0); @@ -425,8 +426,8 @@ mod tests { )); // exactly at lower - test.add_dummy_nymnode("owner3", None); - let sender = mock_info("owner3", &[]); + test.add_dummy_nymnode(&test.make_addr("owner3"), None); + let sender = message_info(&test.make_addr("owner3"), &[]); let env = test.env(); let mut update = new_dummy_params(); update.interval_operating_cost = test.coin(minimum.u128()); @@ -434,8 +435,8 @@ mod tests { assert!(res.is_ok()); // above upper - test.add_dummy_nymnode("owner4", None); - let sender = mock_info("owner4", &[]); + test.add_dummy_nymnode(&test.make_addr("owner4"), None); + let sender = message_info(&test.make_addr("owner4"), &[]); let env = test.env(); let mut update = new_dummy_params(); update.interval_operating_cost = test.coin(100_000_001); @@ -447,8 +448,8 @@ mod tests { )); // max - test.add_dummy_nymnode("owner5", None); - let sender = mock_info("owner5", &[]); + test.add_dummy_nymnode(&test.make_addr("owner5"), None); + let sender = message_info(&test.make_addr("owner5"), &[]); let env = test.env(); let mut update = new_dummy_params(); update.interval_operating_cost = test.coin(u128::MAX); @@ -460,8 +461,8 @@ mod tests { )); // exactly at upper - test.add_dummy_nymnode("owner6", None); - let sender = mock_info("owner6", &[]); + test.add_dummy_nymnode(&test.make_addr("owner6"), None); + let sender = message_info(&test.make_addr("owner6"), &[]); let env = test.env(); let mut update = new_dummy_params(); update.interval_operating_cost = test.coin(100_000_000); @@ -475,14 +476,14 @@ mod tests { fn when_there_are_no_nodes() -> anyhow::Result<()> { let mut test = TestSetup::new(); - let sender = mock_info("owner", &[]); + let sender = test.make_sender("owner"); let env = test.env(); let err = try_update_cost_params(test.deps_mut(), env, sender, new_dummy_params()) .unwrap_err(); assert_eq!( MixnetContractError::NoAssociatedNodeBond { - owner: Addr::unchecked("owner"), + owner: test.make_addr("owner"), }, err ); @@ -494,8 +495,8 @@ mod tests { fn for_legacy_mixnode() -> anyhow::Result<()> { let mut test = TestSetup::new(); - let node_id = test.add_legacy_mixnode("owner", None); - let sender = mock_info("owner", &[]); + let node_id = test.add_legacy_mixnode(&test.make_addr("owner"), None); + let sender = test.make_sender("owner"); let env = test.env(); let update = new_dummy_params(); @@ -517,9 +518,9 @@ mod tests { fn for_legacy_gateway() -> anyhow::Result<()> { let mut test = TestSetup::new(); - test.add_legacy_gateway("owner", None); + test.add_legacy_gateway(&test.make_addr("owner"), None); - let sender = mock_info("owner", &[]); + let sender = test.make_sender("owner"); let env = test.env(); let err = try_update_cost_params(test.deps_mut(), env, sender, new_dummy_params()) .unwrap_err(); @@ -527,7 +528,7 @@ mod tests { // it's illegal to update cost parameters for legacy gateways assert_eq!( MixnetContractError::NoAssociatedNodeBond { - owner: Addr::unchecked("owner"), + owner: test.make_addr("owner"), }, err ); @@ -538,8 +539,8 @@ mod tests { fn for_nymnode() -> anyhow::Result<()> { let mut test = TestSetup::new(); - let node_id = test.add_dummy_nymnode("owner", None); - let sender = mock_info("owner", &[]); + let node_id = test.add_dummy_nymnode(&test.make_addr("owner"), None); + let sender = test.make_sender("owner"); let env = test.env(); let update = new_dummy_params(); @@ -562,19 +563,18 @@ mod tests { mod withdrawing_operator_reward { use super::*; use crate::support::tests::test_helpers::{ExtractBankMsg, TestSetup}; - use cosmwasm_std::testing::mock_info; - use cosmwasm_std::Addr; + use cosmwasm_std::testing::message_info; #[test] fn when_there_are_no_nodes() -> anyhow::Result<()> { let mut test = TestSetup::new(); - let sender = mock_info("owner", &[]); + let sender = test.make_sender("owner"); let err = try_withdraw_operator_reward(test.deps_mut(), sender).unwrap_err(); assert_eq!( MixnetContractError::NoAssociatedNodeBond { - owner: Addr::unchecked("owner"), + owner: test.make_addr("owner"), }, err ); @@ -588,15 +588,15 @@ mod tests { let active_params = test.active_node_params(100.0); // no rewards - test.add_legacy_mixnode("owner1", None); - let sender = mock_info("owner1", &[]); + test.add_legacy_mixnode(&test.make_addr("owner1"), None); + let sender = message_info(&test.make_addr("owner1"), &[]); let res = try_withdraw_operator_reward(test.deps_mut(), sender)?; let maybe_bank = res.unwrap_bank_msg(); assert!(maybe_bank.is_none()); - let node_id = test.add_legacy_mixnode("owner2", None); - let sender = mock_info("owner2", &[]); + let node_id = test.add_legacy_mixnode(&test.make_addr("owner2"), None); + let sender = message_info(&test.make_addr("owner2"), &[]); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![node_id]); test.start_epoch_transition(); @@ -613,15 +613,15 @@ mod tests { fn for_legacy_gateway() -> anyhow::Result<()> { let mut test = TestSetup::new(); - test.add_legacy_gateway("owner", None); + test.add_legacy_gateway(&test.make_addr("owner"), None); - let sender = mock_info("owner", &[]); + let sender = test.make_sender("owner"); let err = try_withdraw_operator_reward(test.deps_mut(), sender).unwrap_err(); // no rewards for legacy gateways... assert_eq!( MixnetContractError::NoAssociatedNodeBond { - owner: Addr::unchecked("owner"), + owner: test.make_addr("owner"), }, err ); @@ -634,15 +634,15 @@ mod tests { let active_params = test.active_node_params(100.0); // no rewards - test.add_dummy_nymnode("owner1", None); - let sender = mock_info("owner1", &[]); + test.add_dummy_nymnode(&test.make_addr("owner1"), None); + let sender = message_info(&test.make_addr("owner1"), &[]); let res = try_withdraw_operator_reward(test.deps_mut(), sender)?; let maybe_bank = res.unwrap_bank_msg(); assert!(maybe_bank.is_none()); - let node_id = test.add_dummy_nymnode("owner2", None); - let sender = mock_info("owner2", &[]); + let node_id = test.add_dummy_nymnode(&test.make_addr("owner2"), None); + let sender = message_info(&test.make_addr("owner2"), &[]); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![node_id]); test.start_epoch_transition(); diff --git a/contracts/mixnet/src/contract.rs b/contracts/mixnet/src/contract.rs index cbd263ef789..db9e3f44f49 100644 --- a/contracts/mixnet/src/contract.rs +++ b/contracts/mixnet/src/contract.rs @@ -7,7 +7,8 @@ use crate::mixnet_contract_settings::storage as mixnet_params_storage; use crate::nodes::storage as nymnodes_storage; use crate::rewards::storage::RewardingStorage; use cosmwasm_std::{ - entry_point, to_binary, Addr, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, + entry_point, to_json_binary, Addr, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse, + Response, }; use mixnet_contract_common::error::MixnetContractError; use mixnet_contract_common::{ @@ -328,56 +329,58 @@ pub fn query( ) -> Result { let query_res = match msg { QueryMsg::GetContractVersion {} => { - to_binary(&crate::mixnet_contract_settings::queries::query_contract_version()) + to_json_binary(&crate::mixnet_contract_settings::queries::query_contract_version()) } - QueryMsg::GetCW2ContractVersion {} => to_binary(&cw2::get_contract_version(deps.storage)?), - QueryMsg::GetRewardingValidatorAddress {} => to_binary( + QueryMsg::GetCW2ContractVersion {} => { + to_json_binary(&cw2::get_contract_version(deps.storage)?) + } + QueryMsg::GetRewardingValidatorAddress {} => to_json_binary( &crate::mixnet_contract_settings::queries::query_rewarding_validator_address(deps)?, ), - QueryMsg::GetStateParams {} => to_binary( + QueryMsg::GetStateParams {} => to_json_binary( &crate::mixnet_contract_settings::queries::query_contract_settings_params(deps)?, ), QueryMsg::GetState {} => { - to_binary(&crate::mixnet_contract_settings::queries::query_contract_state(deps)?) + to_json_binary(&crate::mixnet_contract_settings::queries::query_contract_state(deps)?) } - QueryMsg::GetCurrentNymNodeVersion {} => to_binary( + QueryMsg::GetCurrentNymNodeVersion {} => to_json_binary( &crate::mixnet_contract_settings::queries::query_current_nym_node_version(deps)?, ), - QueryMsg::GetNymNodeVersionHistory { limit, start_after } => to_binary( + QueryMsg::GetNymNodeVersionHistory { limit, start_after } => to_json_binary( &crate::mixnet_contract_settings::queries::query_nym_node_version_history_paged( deps, start_after, limit, )?, ), - QueryMsg::Admin {} => to_binary(&crate::mixnet_contract_settings::queries::query_admin( - deps, - )?), + QueryMsg::Admin {} => to_json_binary( + &crate::mixnet_contract_settings::queries::query_admin(deps)?, + ), QueryMsg::GetRewardingParams {} => { - to_binary(&crate::rewards::queries::query_rewarding_params(deps)?) + to_json_binary(&crate::rewards::queries::query_rewarding_params(deps)?) } QueryMsg::GetEpochStatus {} => { - to_binary(&crate::interval::queries::query_epoch_status(deps)?) + to_json_binary(&crate::interval::queries::query_epoch_status(deps)?) } - QueryMsg::GetCurrentIntervalDetails {} => to_binary( + QueryMsg::GetCurrentIntervalDetails {} => to_json_binary( &crate::interval::queries::query_current_interval_details(deps, env)?, ), // mixnode-related: - QueryMsg::GetMixNodeBonds { start_after, limit } => to_binary( + QueryMsg::GetMixNodeBonds { start_after, limit } => to_json_binary( &crate::mixnodes::queries::query_mixnode_bonds_paged(deps, start_after, limit)?, ), - QueryMsg::GetMixNodesDetailed { start_after, limit } => to_binary( + QueryMsg::GetMixNodesDetailed { start_after, limit } => to_json_binary( &crate::mixnodes::queries::query_mixnodes_details_paged(deps, start_after, limit)?, ), - QueryMsg::GetUnbondedMixNodes { limit, start_after } => to_binary( + QueryMsg::GetUnbondedMixNodes { limit, start_after } => to_json_binary( &crate::mixnodes::queries::query_unbonded_mixnodes_paged(deps, start_after, limit)?, ), QueryMsg::GetUnbondedMixNodesByOwner { owner, limit, start_after, - } => to_binary( + } => to_json_binary( &crate::mixnodes::queries::query_unbonded_mixnodes_by_owner_paged( deps, owner, @@ -389,7 +392,7 @@ pub fn query( identity_key, limit, start_after, - } => to_binary( + } => to_json_binary( &crate::mixnodes::queries::query_unbonded_mixnodes_by_identity_paged( deps, identity_key, @@ -397,57 +400,57 @@ pub fn query( limit, )?, ), - QueryMsg::GetOwnedMixnode { address } => to_binary( + QueryMsg::GetOwnedMixnode { address } => to_json_binary( &crate::mixnodes::queries::query_owned_mixnode(deps, address)?, ), - QueryMsg::GetMixnodeDetails { mix_id } => to_binary( + QueryMsg::GetMixnodeDetails { mix_id } => to_json_binary( &crate::mixnodes::queries::query_mixnode_details(deps, mix_id)?, ), - QueryMsg::GetMixnodeRewardingDetails { mix_id } => to_binary( + QueryMsg::GetMixnodeRewardingDetails { mix_id } => to_json_binary( &crate::mixnodes::queries::query_mixnode_rewarding_details(deps, mix_id)?, ), - QueryMsg::GetStakeSaturation { mix_id } => to_binary( + QueryMsg::GetStakeSaturation { mix_id } => to_json_binary( &crate::mixnodes::queries::query_stake_saturation(deps, mix_id)?, ), - QueryMsg::GetUnbondedMixNodeInformation { mix_id } => to_binary( + QueryMsg::GetUnbondedMixNodeInformation { mix_id } => to_json_binary( &crate::mixnodes::queries::query_unbonded_mixnode(deps, mix_id)?, ), - QueryMsg::GetBondedMixnodeDetailsByIdentity { mix_identity } => to_binary( + QueryMsg::GetBondedMixnodeDetailsByIdentity { mix_identity } => to_json_binary( &crate::mixnodes::queries::query_mixnode_details_by_identity(deps, mix_identity)?, ), // gateway-related: - QueryMsg::GetGateways { limit, start_after } => to_binary( + QueryMsg::GetGateways { limit, start_after } => to_json_binary( &crate::gateways::queries::query_gateways_paged(deps, start_after, limit)?, ), - QueryMsg::GetGatewayBond { identity } => to_binary( + QueryMsg::GetGatewayBond { identity } => to_json_binary( &crate::gateways::queries::query_gateway_bond(deps, identity)?, ), - QueryMsg::GetOwnedGateway { address } => to_binary( + QueryMsg::GetOwnedGateway { address } => to_json_binary( &crate::gateways::queries::query_owned_gateway(deps, address)?, ), - QueryMsg::GetPreassignedGatewayIds { limit, start_after } => to_binary( + QueryMsg::GetPreassignedGatewayIds { limit, start_after } => to_json_binary( &crate::gateways::queries::query_preassigned_ids_paged(deps, start_after, limit)?, ), // nym-node-related: - QueryMsg::GetNymNodeBondsPaged { start_after, limit } => to_binary( + QueryMsg::GetNymNodeBondsPaged { start_after, limit } => to_json_binary( &crate::nodes::queries::query_nymnode_bonds_paged(deps, start_after, limit)?, ), - QueryMsg::GetNymNodesDetailedPaged { limit, start_after } => to_binary( + QueryMsg::GetNymNodesDetailedPaged { limit, start_after } => to_json_binary( &crate::nodes::queries::query_nymnodes_details_paged(deps, start_after, limit)?, ), - QueryMsg::GetUnbondedNymNode { node_id } => to_binary( + QueryMsg::GetUnbondedNymNode { node_id } => to_json_binary( &crate::nodes::queries::query_unbonded_nymnode(deps, node_id)?, ), - QueryMsg::GetUnbondedNymNodesPaged { limit, start_after } => to_binary( + QueryMsg::GetUnbondedNymNodesPaged { limit, start_after } => to_json_binary( &crate::nodes::queries::query_unbonded_nymnodes_paged(deps, limit, start_after)?, ), QueryMsg::GetUnbondedNymNodesByOwnerPaged { owner, limit, start_after, - } => to_binary( + } => to_json_binary( &crate::nodes::queries::query_unbonded_nymnodes_by_owner_paged( deps, owner, @@ -459,7 +462,7 @@ pub fn query( identity_key, limit, start_after, - } => to_binary( + } => to_json_binary( &crate::nodes::queries::query_unbonded_nymnodes_by_identity_paged( deps, identity_key, @@ -468,25 +471,25 @@ pub fn query( )?, ), QueryMsg::GetOwnedNymNode { address } => { - to_binary(&crate::nodes::queries::query_owned_nymnode(deps, address)?) + to_json_binary(&crate::nodes::queries::query_owned_nymnode(deps, address)?) } - QueryMsg::GetNymNodeDetails { node_id } => to_binary( + QueryMsg::GetNymNodeDetails { node_id } => to_json_binary( &crate::nodes::queries::query_nymnode_details(deps, node_id)?, ), - QueryMsg::GetNymNodeDetailsByIdentityKey { node_identity } => to_binary( + QueryMsg::GetNymNodeDetailsByIdentityKey { node_identity } => to_json_binary( &crate::nodes::queries::query_nymnode_details_by_identity(deps, node_identity)?, ), - QueryMsg::GetNodeRewardingDetails { node_id } => to_binary( + QueryMsg::GetNodeRewardingDetails { node_id } => to_json_binary( &crate::nodes::queries::query_nymnode_rewarding_details(deps, node_id)?, ), - QueryMsg::GetNodeStakeSaturation { node_id } => to_binary( + QueryMsg::GetNodeStakeSaturation { node_id } => to_json_binary( &crate::nodes::queries::query_stake_saturation(deps, node_id)?, ), QueryMsg::GetRoleAssignment { role } => { - to_binary(&crate::nodes::queries::query_epoch_assignment(deps, role)?) + to_json_binary(&crate::nodes::queries::query_epoch_assignment(deps, role)?) } QueryMsg::GetRewardedSetMetadata {} => { - to_binary(&crate::nodes::queries::query_rewarded_set_metadata(deps)?) + to_json_binary(&crate::nodes::queries::query_rewarded_set_metadata(deps)?) } // delegation-related: @@ -494,7 +497,7 @@ pub fn query( node_id, start_after, limit, - } => to_binary(&crate::delegations::queries::query_node_delegations_paged( + } => to_json_binary(&crate::delegations::queries::query_node_delegations_paged( deps, node_id, start_after, @@ -504,7 +507,7 @@ pub fn query( delegator, start_after, limit, - } => to_binary( + } => to_json_binary( &crate::delegations::queries::query_delegator_delegations_paged( deps, delegator, @@ -516,32 +519,32 @@ pub fn query( node_id, delegator, proxy, - } => to_binary(&crate::delegations::queries::query_node_delegation( + } => to_json_binary(&crate::delegations::queries::query_node_delegation( deps, node_id, delegator, proxy, )?), - QueryMsg::GetAllDelegations { start_after, limit } => to_binary( + QueryMsg::GetAllDelegations { start_after, limit } => to_json_binary( &crate::delegations::queries::query_all_delegations_paged(deps, start_after, limit)?, ), // rewards related - QueryMsg::GetPendingOperatorReward { address } => to_binary( + QueryMsg::GetPendingOperatorReward { address } => to_json_binary( &crate::rewards::queries::query_pending_operator_reward(deps, address)?, ), - QueryMsg::GetPendingNodeOperatorReward { node_id } => to_binary( + QueryMsg::GetPendingNodeOperatorReward { node_id } => to_json_binary( &crate::rewards::queries::query_pending_mixnode_operator_reward(deps, node_id)?, ), QueryMsg::GetPendingDelegatorReward { address, node_id, proxy, - } => to_binary(&crate::rewards::queries::query_pending_delegator_reward( + } => to_json_binary(&crate::rewards::queries::query_pending_delegator_reward( deps, address, node_id, proxy, )?), QueryMsg::GetEstimatedCurrentEpochOperatorReward { node_id, estimated_performance, estimated_work, - } => to_binary( + } => to_json_binary( &crate::rewards::queries::query_estimated_current_epoch_operator_reward( deps, node_id, @@ -554,7 +557,7 @@ pub fn query( node_id, estimated_performance, estimated_work, - } => to_binary( + } => to_json_binary( &crate::rewards::queries::query_estimated_current_epoch_delegator_reward( deps, address, @@ -566,14 +569,14 @@ pub fn query( // interval-related QueryMsg::GetPendingEpochEvents { limit, start_after } => { - to_binary(&crate::interval::queries::query_pending_epoch_events_paged( + to_json_binary(&crate::interval::queries::query_pending_epoch_events_paged( deps, env, start_after, limit, )?) } - QueryMsg::GetPendingIntervalEvents { limit, start_after } => to_binary( + QueryMsg::GetPendingIntervalEvents { limit, start_after } => to_json_binary( &crate::interval::queries::query_pending_interval_events_paged( deps, env, @@ -581,18 +584,18 @@ pub fn query( limit, )?, ), - QueryMsg::GetPendingEpochEvent { event_id } => to_binary( + QueryMsg::GetPendingEpochEvent { event_id } => to_json_binary( &crate::interval::queries::query_pending_epoch_event(deps, event_id)?, ), - QueryMsg::GetPendingIntervalEvent { event_id } => to_binary( + QueryMsg::GetPendingIntervalEvent { event_id } => to_json_binary( &crate::interval::queries::query_pending_interval_event(deps, event_id)?, ), - QueryMsg::GetNumberOfPendingEvents {} => to_binary( + QueryMsg::GetNumberOfPendingEvents {} => to_json_binary( &crate::interval::queries::query_number_of_pending_events(deps)?, ), // signing-related - QueryMsg::GetSigningNonce { address } => to_binary( + QueryMsg::GetSigningNonce { address } => to_json_binary( &crate::signing::queries::query_current_signing_nonce(deps, address)?, ), }; @@ -632,7 +635,7 @@ pub fn migrate( mod tests { use super::*; use crate::rewards::storage as rewards_storage; - use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + use cosmwasm_std::testing::{message_info, mock_dependencies, mock_env}; use cosmwasm_std::{Decimal, Uint128}; use mixnet_contract_common::reward_params::{ IntervalRewardParams, RewardedSetParams, RewardingParams, @@ -648,8 +651,8 @@ mod tests { let env = mock_env(); let init_msg = InstantiateMsg { - rewarding_validator_address: "foomp123".to_string(), - vesting_contract_address: "bar456".to_string(), + rewarding_validator_address: deps.api.addr_make("foomp123").to_string(), + vesting_contract_address: deps.api.addr_make("bar456").to_string(), rewarding_denom: "uatom".to_string(), epochs_in_interval: 1234, epoch_duration: Duration::from_secs(4321), @@ -680,15 +683,15 @@ mod tests { }, }; - let sender = mock_info("sender", &[]); + let sender = message_info(&deps.api.addr_make("sender"), &[]); let res = instantiate(deps.as_mut(), env, sender, init_msg); assert!(res.is_ok()); #[allow(deprecated)] let expected_state = ContractState { - owner: Some(Addr::unchecked("sender")), - rewarding_validator_address: Addr::unchecked("foomp123"), - vesting_contract_address: Addr::unchecked("bar456"), + owner: Some(deps.api.addr_make("sender")), + rewarding_validator_address: deps.api.addr_make("foomp123"), + vesting_contract_address: deps.api.addr_make("bar456"), rewarding_denom: "uatom".into(), params: ContractStateParams { delegations_params: DelegationsParams { diff --git a/contracts/mixnet/src/delegations/helpers.rs b/contracts/mixnet/src/delegations/helpers.rs index 20ae4e85820..01a0ecf2a57 100644 --- a/contracts/mixnet/src/delegations/helpers.rs +++ b/contracts/mixnet/src/delegations/helpers.rs @@ -33,11 +33,13 @@ mod tests { let mut test = TestSetup::new(); let active_params = test.active_node_params(100.0); - let mix_id = - test.add_rewarded_set_nymnode_id("mix-owner", Some(Uint128::new(100_000_000_000))); - let delegator = "delegator"; + let mix_id = test.add_rewarded_set_nymnode_id( + &test.make_addr("mix-owner"), + Some(Uint128::new(100_000_000_000)), + ); + let delegator = test.make_addr("delegator"); let og_amount = Uint128::new(200_000_000); - test.add_immediate_delegation(delegator, og_amount, mix_id); + test.add_immediate_delegation(&delegator, og_amount, mix_id); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![mix_id]); @@ -46,7 +48,7 @@ mod tests { let dist2 = test.reward_with_distribution_ignore_state(mix_id, active_params); let mix_rewarding = test.mix_rewarding(mix_id); - let delegation = test.delegation(mix_id, delegator, &None); + let delegation = test.delegation(mix_id, &delegator, &None); let expected_amount = og_amount + truncate_reward_amount(dist1.delegates + dist2.delegates); diff --git a/contracts/mixnet/src/delegations/queries.rs b/contracts/mixnet/src/delegations/queries.rs index fb46e8056f9..df2c9befa7a 100644 --- a/contracts/mixnet/src/delegations/queries.rs +++ b/contracts/mixnet/src/delegations/queries.rs @@ -139,9 +139,9 @@ mod tests { fn add_dummy_mixes_with_delegations(test: &mut TestSetup, delegators: usize, mixes: usize) { for i in 0..mixes { - let mix_id = test.add_legacy_mixnode(&format!("mix-owner{}", i), None); + let mix_id = test.add_legacy_mixnode(&test.make_addr(format!("mix-owner{}", i)), None); for delegator in 0..delegators { - let name = &format!("delegator{}", delegator); + let name = &test.make_addr(format!("delegator{}", delegator)); test.add_immediate_delegation(name, 100_000_000u32, mix_id) } } @@ -151,11 +151,12 @@ mod tests { mod mixnode_delegations { use super::*; use crate::support::tests::test_helpers; + use crate::support::tests::test_helpers::sorted_addresses; #[test] fn obeys_limits() { let mut test = TestSetup::new(); - let mix_id = test.add_legacy_mixnode("mix-owner", None); + let mix_id = test.add_legacy_mixnode(&test.make_addr("mix-owner"), None); let env = test.env(); test_helpers::add_dummy_delegations(test.deps_mut(), env, mix_id, 200); @@ -170,7 +171,7 @@ mod tests { #[test] fn has_default_limit() { let mut test = TestSetup::new(); - let mix_id = test.add_legacy_mixnode("mix-owner", None); + let mix_id = test.add_legacy_mixnode(&test.make_addr("mix-owner"), None); let env = test.env(); test_helpers::add_dummy_delegations(test.deps_mut(), env, mix_id, 500); @@ -187,7 +188,7 @@ mod tests { #[test] fn has_max_limit() { let mut test = TestSetup::new(); - let mix_id = test.add_legacy_mixnode("mix-owner", None); + let mix_id = test.add_legacy_mixnode(&test.make_addr("mix-owner"), None); let env = test.env(); test_helpers::add_dummy_delegations(test.deps_mut(), env, mix_id, 5000); @@ -207,8 +208,10 @@ mod tests { fn pagination_works() { let mut test = TestSetup::new(); - let mix_id = test.add_legacy_mixnode("mix-owner", None); - test.add_immediate_delegation("addr1", 1000u32, mix_id); + let delegators = sorted_addresses(4); + + let mix_id = test.add_legacy_mixnode(&test.make_addr("mix-owner"), None); + test.add_immediate_delegation(&delegators[0], 1000u32, mix_id); let per_page = 2; let page1 = @@ -218,14 +221,14 @@ mod tests { assert_eq!(1, page1.delegations.len()); // save another - test.add_immediate_delegation("addr2", 1000u32, mix_id); + test.add_immediate_delegation(&delegators[1], 1000u32, mix_id); // page1 should have 2 results on it let page1 = query_node_delegations_paged(test.deps(), mix_id, None, Some(per_page)).unwrap(); assert_eq!(2, page1.delegations.len()); - test.add_immediate_delegation("addr3", 1000u32, mix_id); + test.add_immediate_delegation(&delegators[2], 1000u32, mix_id); // page1 still has the same 2 results let another_page1 = @@ -246,7 +249,7 @@ mod tests { assert_eq!(1, page2.delegations.len()); // save another one - test.add_immediate_delegation("addr4", 1000u32, mix_id); + test.add_immediate_delegation(&delegators[3], 1000u32, mix_id); let page2 = query_node_delegations_paged( test.deps(), @@ -263,21 +266,21 @@ mod tests { #[test] fn all_retrieved_delegations_are_towards_specified_mixnode() { let mut test = TestSetup::new(); - let mix_id1 = test.add_legacy_mixnode("mix-owner1", None); - let mix_id2 = test.add_legacy_mixnode("mix-owner2", None); - let mix_id3 = test.add_legacy_mixnode("mix-owner3", None); - let mix_id4 = test.add_legacy_mixnode("mix-owner4", None); + let mix_id1 = test.add_legacy_mixnode(&test.make_addr("mix-owner1"), None); + let mix_id2 = test.add_legacy_mixnode(&test.make_addr("mix-owner2"), None); + let mix_id3 = test.add_legacy_mixnode(&test.make_addr("mix-owner3"), None); + let mix_id4 = test.add_legacy_mixnode(&test.make_addr("mix-owner4"), None); let env = test.env(); // add other "out of order" delegations manually - test.add_immediate_delegation("random-delegator1", 1000u32, mix_id2); - test.add_immediate_delegation("random-delegator2", 1000u32, mix_id2); + test.add_immediate_delegation(&test.make_addr("random-delegator1"), 1000u32, mix_id2); + test.add_immediate_delegation(&test.make_addr("random-delegator2"), 1000u32, mix_id2); test_helpers::add_dummy_delegations(test.deps_mut(), env.clone(), mix_id1, 10); test_helpers::add_dummy_delegations(test.deps_mut(), env.clone(), mix_id2, 10); test_helpers::add_dummy_delegations(test.deps_mut(), env.clone(), mix_id3, 10); - test.add_immediate_delegation("random-delegator3", 1000u32, mix_id2); + test.add_immediate_delegation(&test.make_addr("random-delegator3"), 1000u32, mix_id2); test_helpers::add_dummy_delegations(test.deps_mut(), env, mix_id4, 10); - test.add_immediate_delegation("random-delegator4", 1000u32, mix_id2); + test.add_immediate_delegation(&test.make_addr("random-delegator4"), 1000u32, mix_id2); let res1 = query_node_delegations_paged(test.deps(), mix_id1, None, None).unwrap(); assert_eq!(res1.delegations.len(), 10); @@ -299,7 +302,6 @@ mod tests { mod delegator_delegations { use super::*; - use cosmwasm_std::Addr; #[test] fn obeys_limits() { @@ -312,7 +314,7 @@ mod tests { let page1 = query_delegator_delegations_paged( test.deps(), - "delegator1".into(), + test.make_addr("delegator1").to_string(), None, Some(limit), ) @@ -326,9 +328,13 @@ mod tests { add_dummy_mixes_with_delegations(&mut test, 10, 500); // query without explicitly setting a limit - let page1 = - query_delegator_delegations_paged(test.deps(), "delegator1".into(), None, None) - .unwrap(); + let page1 = query_delegator_delegations_paged( + test.deps(), + test.make_addr("delegator1").to_string(), + None, + None, + ) + .unwrap(); assert_eq!( DELEGATION_PAGE_DEFAULT_RETRIEVAL_LIMIT, @@ -345,7 +351,7 @@ mod tests { let crazy_limit = 10000; let page1 = query_delegator_delegations_paged( test.deps(), - "delegator1".into(), + test.make_addr("delegator1").to_string(), None, Some(crazy_limit), ) @@ -362,27 +368,27 @@ mod tests { let mut test = TestSetup::new(); // note that mix_ids are monotonically increasing - let mix_id1 = test.add_legacy_mixnode("mix-owner1", None); - let mix_id2 = test.add_legacy_mixnode("mix-owner2", None); - let mix_id3 = test.add_legacy_mixnode("mix-owner3", None); - let mix_id4 = test.add_legacy_mixnode("mix-owner4", None); - let mix_id5 = test.add_legacy_mixnode("mix-owner5", None); + let mix_id1 = test.add_legacy_mixnode(&test.make_addr("mix-owner1"), None); + let mix_id2 = test.add_legacy_mixnode(&test.make_addr("mix-owner2"), None); + let mix_id3 = test.add_legacy_mixnode(&test.make_addr("mix-owner3"), None); + let mix_id4 = test.add_legacy_mixnode(&test.make_addr("mix-owner4"), None); + let mix_id5 = test.add_legacy_mixnode(&test.make_addr("mix-owner5"), None); // add few delegations from unrelated delegators for mix_id in [mix_id1, mix_id2, mix_id3, mix_id4, mix_id5] { - test.add_immediate_delegation("random1", 1000u32, mix_id); - test.add_immediate_delegation("random2", 1000u32, mix_id); - test.add_immediate_delegation("random1", 1000u32, mix_id); + test.add_immediate_delegation(&test.make_addr("random1"), 1000u32, mix_id); + test.add_immediate_delegation(&test.make_addr("random2"), 1000u32, mix_id); + test.add_immediate_delegation(&test.make_addr("random1"), 1000u32, mix_id); } - let delegator = "delegator"; + let delegator = test.make_addr("delegator"); - test.add_immediate_delegation(delegator, 1000u32, mix_id1); + test.add_immediate_delegation(&delegator, 1000u32, mix_id1); let per_page = 2; let page1 = query_delegator_delegations_paged( test.deps(), - delegator.into(), + delegator.to_string(), None, Some(per_page), ) @@ -392,24 +398,24 @@ mod tests { assert_eq!(1, page1.delegations.len()); // save another - test.add_immediate_delegation(delegator, 1000u32, mix_id2); + test.add_immediate_delegation(&delegator, 1000u32, mix_id2); // page1 should have 2 results on it let page1 = query_delegator_delegations_paged( test.deps(), - delegator.into(), + delegator.to_string(), None, Some(per_page), ) .unwrap(); assert_eq!(2, page1.delegations.len()); - test.add_immediate_delegation(delegator, 1000u32, mix_id3); + test.add_immediate_delegation(&delegator, 1000u32, mix_id3); // page1 still has the same 2 results let another_page1 = query_delegator_delegations_paged( test.deps(), - delegator.into(), + delegator.to_string(), None, Some(per_page), ) @@ -421,7 +427,7 @@ mod tests { let start_after = page1.start_next_after.unwrap(); let page2 = query_delegator_delegations_paged( test.deps(), - delegator.into(), + delegator.to_string(), Some(start_after.clone()), Some(per_page), ) @@ -430,11 +436,11 @@ mod tests { assert_eq!(1, page2.delegations.len()); // save another one - test.add_immediate_delegation(delegator, 1000u32, mix_id4); + test.add_immediate_delegation(&delegator, 1000u32, mix_id4); let page2 = query_delegator_delegations_paged( test.deps(), - delegator.into(), + delegator.to_string(), Some(start_after), Some(per_page), ) @@ -452,28 +458,37 @@ mod tests { add_dummy_mixes_with_delegations(&mut test, 50, 100); // make few queries - let res1 = - query_delegator_delegations_paged(test.deps(), "delegator2".into(), None, None) - .unwrap(); + let res1 = query_delegator_delegations_paged( + test.deps(), + test.make_addr("delegator2").into(), + None, + None, + ) + .unwrap(); assert_eq!(res1.delegations.len(), 100); assert!(res1 .delegations .into_iter() - .all(|d| d.owner == Addr::unchecked("delegator2"))); + .all(|d| d.owner == test.make_addr("delegator2"))); - let res2 = - query_delegator_delegations_paged(test.deps(), "delegator35".into(), None, None) - .unwrap(); + let res2 = query_delegator_delegations_paged( + test.deps(), + test.make_addr("delegator35").into(), + None, + None, + ) + .unwrap(); assert_eq!(res2.delegations.len(), 100); assert!(res2 .delegations .into_iter() - .all(|d| d.owner == Addr::unchecked("delegator35"))); + .all(|d| d.owner == test.make_addr("delegator35"))); } } mod all_delegations { use super::*; + use crate::support::tests::test_helpers::sorted_addresses; #[test] fn obeys_limits() { @@ -523,11 +538,12 @@ mod tests { // note that mix_ids are monotonically increasing and are the first chunk of all // delegation storage keys, - let mix_id1 = test.add_legacy_mixnode("mix-owner1", None); - let mix_id2 = test.add_legacy_mixnode("mix-owner2", None); + let mix_id1 = test.add_legacy_mixnode(&test.make_addr("mix-owner1"), None); + let mix_id2 = test.add_legacy_mixnode(&test.make_addr("mix-owner2"), None); - let delegator1 = "delegator1"; - let delegator2 = "delegator2"; + let delegators = sorted_addresses(2); + let delegator1 = &delegators[0]; + let delegator2 = &delegators[1]; test.add_immediate_delegation(delegator1, 1000u32, mix_id1); @@ -537,8 +553,7 @@ mod tests { // page should have 1 result on it assert_eq!(1, page1.delegations.len()); assert!( - page1.delegations[0].owner.as_str() == delegator1 - && page1.delegations[0].node_id == mix_id1 + page1.delegations[0].owner == delegator1 && page1.delegations[0].node_id == mix_id1 ); test.add_immediate_delegation(delegator1, 1000u32, mix_id2); @@ -548,12 +563,10 @@ mod tests { // page1 should have 2 results on it assert_eq!(2, page1.delegations.len()); assert!( - page1.delegations[0].owner.as_str() == delegator1 - && page1.delegations[0].node_id == mix_id1 + page1.delegations[0].owner == delegator1 && page1.delegations[0].node_id == mix_id1 ); assert!( - page1.delegations[1].owner.as_str() == delegator1 - && page1.delegations[1].node_id == mix_id2 + page1.delegations[1].owner == delegator1 && page1.delegations[1].node_id == mix_id2 ); test.add_immediate_delegation(delegator2, 1000u32, mix_id1); @@ -563,11 +576,11 @@ mod tests { query_all_delegations_paged(test.deps(), None, Some(per_page)).unwrap(); assert_eq!(2, another_page1.delegations.len()); assert!( - another_page1.delegations[0].owner.as_str() == delegator1 + another_page1.delegations[0].owner == delegator1 && another_page1.delegations[0].node_id == mix_id1 ); assert!( - another_page1.delegations[1].owner.as_str() == delegator2 + another_page1.delegations[1].owner == delegator2 && another_page1.delegations[1].node_id == mix_id1 ); @@ -579,8 +592,7 @@ mod tests { assert_eq!(1, page2.delegations.len()); assert!( - page2.delegations[0].owner.as_str() == delegator1 - && page2.delegations[0].node_id == mix_id2 + page2.delegations[0].owner == delegator1 && page2.delegations[0].node_id == mix_id2 ); // save another one @@ -592,12 +604,10 @@ mod tests { // now we have 2 pages, with 2 results on the second page assert_eq!(2, page2.delegations.len()); assert!( - page2.delegations[0].owner.as_str() == delegator1 - && page2.delegations[0].node_id == mix_id2 + page2.delegations[0].owner == delegator1 && page2.delegations[0].node_id == mix_id2 ); assert!( - page2.delegations[1].owner.as_str() == delegator2 - && page2.delegations[1].node_id == mix_id2 + page2.delegations[1].owner == delegator2 && page2.delegations[1].node_id == mix_id2 ); } } @@ -614,10 +624,11 @@ mod tests { #[test] fn when_delegation_doesnt_exist() { let mut test = TestSetup::new(); - let mix_id = test.add_legacy_mixnode("mix-owner", None); - let owner = "owner"; + let mix_id = test.add_legacy_mixnode(&test.make_addr("mix-owner"), None); + let owner = test.make_addr("owner"); - let res = query_node_delegation(test.deps(), mix_id, owner.into(), None).unwrap(); + let res = + query_node_delegation(test.deps(), mix_id, owner.to_string(), None).unwrap(); assert!(res.delegation.is_none()); assert!(res.mixnode_still_bonded); assert!(res.node_still_bonded); @@ -627,14 +638,15 @@ mod tests { #[test] fn when_delegation_exists_but_mixnode_has_unbonded() { let mut test = TestSetup::new(); - let mix_id = test.add_legacy_mixnode("mix-owner", None); - let owner = "owner"; + let mix_id = test.add_legacy_mixnode(&test.make_addr("mix-owner"), None); + let owner = test.make_addr("owner"); - test.add_immediate_delegation(owner, 1000u32, mix_id); + test.add_immediate_delegation(&owner, 1000u32, mix_id); test.immediately_unbond_mixnode(mix_id); - let res = query_node_delegation(test.deps(), mix_id, owner.into(), None).unwrap(); - assert_eq!(res.delegation.as_ref().unwrap().owner.as_str(), owner); + let res = + query_node_delegation(test.deps(), mix_id, owner.to_string(), None).unwrap(); + assert_eq!(res.delegation.as_ref().unwrap().owner, owner); assert_eq!(res.delegation.as_ref().unwrap().amount.amount.u128(), 1000); assert!(!res.mixnode_still_bonded); assert!(!res.node_still_bonded); @@ -644,14 +656,15 @@ mod tests { #[test] fn when_delegation_exists_but_mixnode_is_unbonding() { let mut test = TestSetup::new(); - let mix_id = test.add_legacy_mixnode("mix-owner", None); - let owner = "owner"; + let mix_id = test.add_legacy_mixnode(&test.make_addr("mix-owner"), None); + let owner = test.make_addr("owner"); - test.add_immediate_delegation(owner, 1000u32, mix_id); + test.add_immediate_delegation(&owner, 1000u32, mix_id); test.start_unbonding_mixnode(mix_id); - let res = query_node_delegation(test.deps(), mix_id, owner.into(), None).unwrap(); - assert_eq!(res.delegation.as_ref().unwrap().owner.as_str(), owner); + let res = + query_node_delegation(test.deps(), mix_id, owner.to_string(), None).unwrap(); + assert_eq!(res.delegation.as_ref().unwrap().owner, owner); assert_eq!(res.delegation.as_ref().unwrap().amount.amount.u128(), 1000); assert!(!res.mixnode_still_bonded); assert!(!res.node_still_bonded); @@ -661,13 +674,14 @@ mod tests { #[test] fn when_delegation_exists_with_fully_bonded_node() { let mut test = TestSetup::new(); - let mix_id = test.add_legacy_mixnode("mix-owner", None); - let owner = "owner"; + let mix_id = test.add_legacy_mixnode(&test.make_addr("mix-owner"), None); + let owner = test.make_addr("owner"); - test.add_immediate_delegation(owner, 1000u32, mix_id); + test.add_immediate_delegation(&owner, 1000u32, mix_id); - let res = query_node_delegation(test.deps(), mix_id, owner.into(), None).unwrap(); - assert_eq!(res.delegation.as_ref().unwrap().owner.as_str(), owner); + let res = + query_node_delegation(test.deps(), mix_id, owner.to_string(), None).unwrap(); + assert_eq!(res.delegation.as_ref().unwrap().owner, owner); assert_eq!(res.delegation.as_ref().unwrap().amount.amount.u128(), 1000); assert!(res.mixnode_still_bonded); assert!(res.node_still_bonded); @@ -681,10 +695,11 @@ mod tests { #[test] fn when_delegation_doesnt_exist() { let mut test = TestSetup::new(); - let node_id = test.add_dummy_nymnode("bond-owner", None); - let owner = "owner"; + let node_id = test.add_dummy_nymnode(&test.make_addr("bond-owner"), None); + let owner = test.make_addr("owner"); - let res = query_node_delegation(test.deps(), node_id, owner.into(), None).unwrap(); + let res = + query_node_delegation(test.deps(), node_id, owner.to_string(), None).unwrap(); assert!(res.delegation.is_none()); assert!(res.node_still_bonded); } @@ -692,14 +707,15 @@ mod tests { #[test] fn when_delegation_exists_but_mixnode_has_unbonded() { let mut test = TestSetup::new(); - let node_id = test.add_dummy_nymnode("bond-owner", None); - let owner = "owner"; + let node_id = test.add_dummy_nymnode(&test.make_addr("bond-owner"), None); + let owner = test.make_addr("owner"); - test.add_immediate_delegation(owner, 1000u32, node_id); + test.add_immediate_delegation(&owner, 1000u32, node_id); test.immediately_unbond_nymnode(node_id); - let res = query_node_delegation(test.deps(), node_id, owner.into(), None).unwrap(); - assert_eq!(res.delegation.as_ref().unwrap().owner.as_str(), owner); + let res = + query_node_delegation(test.deps(), node_id, owner.to_string(), None).unwrap(); + assert_eq!(res.delegation.as_ref().unwrap().owner, owner); assert_eq!(res.delegation.as_ref().unwrap().amount.amount.u128(), 1000); assert!(!res.node_still_bonded); } @@ -707,14 +723,15 @@ mod tests { #[test] fn when_delegation_exists_but_mixnode_is_unbonding() { let mut test = TestSetup::new(); - let node_id = test.add_dummy_nymnode("bond-owner", None); - let owner = "owner"; + let node_id = test.add_dummy_nymnode(&test.make_addr("bond-owner"), None); + let owner = test.make_addr("owner"); - test.add_immediate_delegation(owner, 1000u32, node_id); + test.add_immediate_delegation(&owner, 1000u32, node_id); test.start_unbonding_nymnode(node_id); - let res = query_node_delegation(test.deps(), node_id, owner.into(), None).unwrap(); - assert_eq!(res.delegation.as_ref().unwrap().owner.as_str(), owner); + let res = + query_node_delegation(test.deps(), node_id, owner.to_string(), None).unwrap(); + assert_eq!(res.delegation.as_ref().unwrap().owner, owner); assert_eq!(res.delegation.as_ref().unwrap().amount.amount.u128(), 1000); assert!(!res.node_still_bonded); } @@ -722,13 +739,14 @@ mod tests { #[test] fn when_delegation_exists_with_fully_bonded_node() { let mut test = TestSetup::new(); - let node_id = test.add_dummy_nymnode("bond-owner", None); - let owner = "owner"; + let node_id = test.add_dummy_nymnode(&test.make_addr("bond-owner"), None); + let owner = test.make_addr("owner"); - test.add_immediate_delegation(owner, 1000u32, node_id); + test.add_immediate_delegation(&owner, 1000u32, node_id); - let res = query_node_delegation(test.deps(), node_id, owner.into(), None).unwrap(); - assert_eq!(res.delegation.as_ref().unwrap().owner.as_str(), owner); + let res = + query_node_delegation(test.deps(), node_id, owner.to_string(), None).unwrap(); + assert_eq!(res.delegation.as_ref().unwrap().owner, owner); assert_eq!(res.delegation.as_ref().unwrap().amount.amount.u128(), 1000); assert!(res.node_still_bonded); } diff --git a/contracts/mixnet/src/delegations/storage.rs b/contracts/mixnet/src/delegations/storage.rs index 25cd6844c9a..1d96e983fb9 100644 --- a/contracts/mixnet/src/delegations/storage.rs +++ b/contracts/mixnet/src/delegations/storage.rs @@ -24,7 +24,7 @@ impl IndexList for DelegationIndex<'_> { } } -pub(crate) fn delegations<'a>() -> IndexedMap<'a, PrimaryKey, Delegation, DelegationIndex<'a>> { +pub(crate) fn delegations<'a>() -> IndexedMap> { let indexes = DelegationIndex { owner: MultiIndex::new( |_pk, d| d.owner.clone(), diff --git a/contracts/mixnet/src/delegations/transactions.rs b/contracts/mixnet/src/delegations/transactions.rs index f91caa71f09..4611f83c204 100644 --- a/contracts/mixnet/src/delegations/transactions.rs +++ b/contracts/mixnet/src/delegations/transactions.rs @@ -87,7 +87,7 @@ mod tests { use crate::rewards::storage as rewards_storage; use crate::support::tests::fixtures::TEST_COIN_DENOM; use crate::support::tests::test_helpers::TestSetup; - use cosmwasm_std::testing::mock_info; + use cosmwasm_std::testing::message_info; use cosmwasm_std::{coin, Addr, Decimal}; use mixnet_contract_common::nym_node::Role; use mixnet_contract_common::{EpochState, EpochStatus}; @@ -115,9 +115,9 @@ mod tests { let env = test.env(); - let owner = "delegator"; - let mix_id = test.add_legacy_mixnode("mix-owner", None); - let sender = mock_info(owner, &[coin(50_000_000, TEST_COIN_DENOM)]); + let owner = &test.make_addr("delegator"); + let mix_id = test.add_legacy_mixnode(&test.make_addr("mix-owner"), None); + let sender = message_info(owner, &[coin(50_000_000, TEST_COIN_DENOM)]); let res = try_delegate_to_node(test.deps_mut(), env.clone(), sender, mix_id); assert!(matches!( @@ -131,8 +131,8 @@ mod tests { fn can_only_be_done_towards_an_existing_mixnode() { let mut test = TestSetup::new(); let env = test.env(); - let owner = "delegator"; - let sender = mock_info(owner, &[coin(100_000_000, TEST_COIN_DENOM)]); + let owner = &test.make_addr("delegator"); + let sender = message_info(owner, &[coin(100_000_000, TEST_COIN_DENOM)]); let res = try_delegate_to_node(test.deps_mut(), env, sender, 42); assert_eq!( @@ -146,11 +146,11 @@ mod tests { let mut test = TestSetup::new(); let env = test.env(); - let owner = "delegator"; - let mix_id = test.add_legacy_mixnode("mix-owner", None); - let sender1 = mock_info(owner, &[coin(0, TEST_COIN_DENOM)]); - let sender2 = mock_info(owner, &[]); - let sender3 = mock_info(owner, &[coin(1000, "some-weird-coin")]); + let owner = &test.make_addr("delegator"); + let mix_id = test.add_legacy_mixnode(&test.make_addr("mix-owner"), None); + let sender1 = message_info(owner, &[coin(0, TEST_COIN_DENOM)]); + let sender2 = message_info(owner, &[]); + let sender3 = message_info(owner, &[coin(1000, "some-weird-coin")]); let res = try_delegate_to_node(test.deps_mut(), env.clone(), sender1, mix_id); assert_eq!(res, Err(MixnetContractError::EmptyDelegation)); @@ -171,10 +171,10 @@ mod tests { let mut test = TestSetup::new(); let env = test.env(); - let owner = "delegator"; - let mix_id = test.add_legacy_mixnode("mix-owner", None); - let sender1 = mock_info(owner, &[coin(100_000_000, TEST_COIN_DENOM)]); - let sender2 = mock_info(owner, &[coin(150_000_000, TEST_COIN_DENOM)]); + let owner = &test.make_addr("delegator"); + let mix_id = test.add_legacy_mixnode(&test.make_addr("mix-owner"), None); + let sender1 = message_info(owner, &[coin(100_000_000, TEST_COIN_DENOM)]); + let sender2 = message_info(owner, &[coin(150_000_000, TEST_COIN_DENOM)]); let min_delegation = coin(150_000_000, TEST_COIN_DENOM); let mut contract_state = mixnet_params_storage::CONTRACT_STATE @@ -203,13 +203,15 @@ mod tests { fn can_only_be_done_towards_fully_bonded_mixnode() { let mut test = TestSetup::new(); let env = test.env(); - let owner = "delegator"; - let sender = mock_info(owner, &[coin(100_000_000, TEST_COIN_DENOM)]); + let owner = &test.make_addr("delegator"); + let sender = message_info(owner, &[coin(100_000_000, TEST_COIN_DENOM)]); - let mix_id_unbonding = test.add_legacy_mixnode("mix-owner-unbonding", None); - let mix_id_unbonded = test.add_legacy_mixnode("mix-owner-unbonded", None); + let mix_id_unbonding = + test.add_legacy_mixnode(&test.make_addr("mix-owner-unbonding"), None); + let mix_id_unbonded = + test.add_legacy_mixnode(&test.make_addr("mix-owner-unbonded"), None); let mix_id_unbonded_leftover = - test.add_legacy_mixnode("mix-owner-unbonded-leftover", None); + test.add_legacy_mixnode(&test.make_addr("mix-owner-unbonded-leftover"), None); // manually adjust delegation info as to indicate the rewarding information shouldnt get removed let mut rewarding_details = rewards_storage::MIXNODE_REWARDING @@ -225,26 +227,15 @@ mod tests { ) .unwrap(); - try_remove_mixnode( - test.deps_mut(), - env.clone(), - mock_info("mix-owner-unbonded", &[]), - ) - .unwrap(); - try_remove_mixnode( - test.deps_mut(), - env.clone(), - mock_info("mix-owner-unbonded-leftover", &[]), - ) - .unwrap(); + let mix_sender = test.make_sender("mix-owner-unbonded"); + try_remove_mixnode(test.deps_mut(), env.clone(), mix_sender).unwrap(); + + let mix_sender = test.make_sender("mix-owner-unbonded-leftover"); + try_remove_mixnode(test.deps_mut(), env.clone(), mix_sender).unwrap(); test.execute_all_pending_events(); - try_remove_mixnode( - test.deps_mut(), - env.clone(), - mock_info("mix-owner-unbonding", &[]), - ) - .unwrap(); + let mix_sender = test.make_sender("mix-owner-unbonding"); + try_remove_mixnode(test.deps_mut(), env.clone(), mix_sender).unwrap(); let res = try_delegate_to_node( test.deps_mut(), @@ -286,10 +277,10 @@ mod tests { let mut test = TestSetup::new(); let env = test.env(); - let owner = "delegator"; - let mix_id = test.add_legacy_mixnode("mix-owner", None); - let sender1 = mock_info(owner, &[coin(100_000_000, TEST_COIN_DENOM)]); - let sender2 = mock_info(owner, &[coin(50_000_000, TEST_COIN_DENOM)]); + let owner = &test.make_addr("delegator"); + let mix_id = test.add_legacy_mixnode(&test.make_addr("mix-owner"), None); + let sender1 = message_info(owner, &[coin(100_000_000, TEST_COIN_DENOM)]); + let sender2 = message_info(owner, &[coin(50_000_000, TEST_COIN_DENOM)]); let res = try_delegate_to_node(test.deps_mut(), env.clone(), sender1, mix_id); assert!(res.is_ok()); @@ -304,12 +295,12 @@ mod tests { let mut test = TestSetup::new(); let env = test.env(); - let owner = "delegator"; - let mix_id = test.add_legacy_mixnode("mix-owner", None); + let owner = &test.make_addr("delegator"); + let mix_id = test.add_legacy_mixnode(&test.make_addr("mix-owner"), None); let amount1 = coin(100_000_000, TEST_COIN_DENOM); - let sender1 = mock_info(owner, &[amount1.clone()]); + let sender1 = message_info(owner, &[amount1.clone()]); try_delegate_to_node(test.deps_mut(), env.clone(), sender1, mix_id).unwrap(); @@ -329,7 +320,7 @@ mod tests { use crate::support::tests::fixtures::TEST_COIN_DENOM; use crate::support::tests::test_helpers::TestSetup; use cosmwasm_std::coin; - use cosmwasm_std::testing::mock_info; + use cosmwasm_std::testing::message_info; use mixnet_contract_common::nym_node::Role; use mixnet_contract_common::{EpochState, EpochStatus}; @@ -348,8 +339,8 @@ mod tests { for bad_state in bad_states { let mut test = TestSetup::new(); - let mix_id = test.add_legacy_mixnode("owner", None); - test.add_immediate_delegation("foomp", 1000u32, mix_id); + let mix_id = test.add_legacy_mixnode(&test.make_addr("owner"), None); + test.add_immediate_delegation(&test.make_addr("foomp"), 1000u32, mix_id); let mut status = EpochStatus::new(test.rewarding_validator().sender); status.state = bad_state; @@ -357,12 +348,9 @@ mod tests { .unwrap(); let env = test.env(); - let res = try_remove_delegation_from_node( - test.deps_mut(), - env.clone(), - mock_info("sender", &[]), - mix_id, - ); + let sender = test.make_sender("sender"); + let res = + try_remove_delegation_from_node(test.deps_mut(), env.clone(), sender, mix_id); assert!(matches!( res, Err(MixnetContractError::EpochAdvancementInProgress { .. }) @@ -374,9 +362,9 @@ mod tests { fn cannot_be_performed_if_delegation_never_existed() { let mut test = TestSetup::new(); let env = test.env(); - let owner = "delegator"; - let sender = mock_info(owner, &[]); - let node_id = test.add_legacy_mixnode("mix-owner", None); + let owner = &test.make_addr("delegator"); + let sender = message_info(owner, &[]); + let node_id = test.add_legacy_mixnode(&test.make_addr("mix-owner"), None); let res = try_remove_delegation_from_node(test.deps_mut(), env, sender, node_id); assert_eq!( @@ -394,10 +382,10 @@ mod tests { let mut test = TestSetup::new(); let env = test.env(); - let owner = "delegator"; - let node_id = test.add_legacy_mixnode("mix-owner", None); - let sender1 = mock_info(owner, &[coin(100_000_000, TEST_COIN_DENOM)]); - let sender2 = mock_info(owner, &[]); + let owner = &test.make_addr("delegator"); + let node_id = test.add_legacy_mixnode(&test.make_addr("mix-owner"), None); + let sender1 = message_info(owner, &[coin(100_000_000, TEST_COIN_DENOM)]); + let sender2 = message_info(owner, &[]); try_delegate_to_node(test.deps_mut(), env.clone(), sender1, node_id).unwrap(); @@ -417,32 +405,25 @@ mod tests { let mut test = TestSetup::new(); let env = test.env(); - let owner = "delegator"; - let sender = mock_info(owner, &[]); + let owner = &test.make_addr("delegator"); + let sender = message_info(owner, &[]); - let normal_mix_id = test.add_legacy_mixnode("mix-owner", None); - let mix_id_unbonding = test.add_legacy_mixnode("mix-owner-unbonding", None); + let normal_mix_id = test.add_legacy_mixnode(&test.make_addr("mix-owner"), None); + let mix_id_unbonding = + test.add_legacy_mixnode(&test.make_addr("mix-owner-unbonding"), None); let mix_id_unbonded_leftover = - test.add_legacy_mixnode("mix-owner-unbonded-leftover", None); + test.add_legacy_mixnode(&test.make_addr("mix-owner-unbonded-leftover"), None); test.add_immediate_delegation(owner, 10000u32, normal_mix_id); test.add_immediate_delegation(owner, 10000u32, mix_id_unbonding); test.add_immediate_delegation(owner, 10000u32, mix_id_unbonded_leftover); - try_remove_mixnode( - test.deps_mut(), - env.clone(), - mock_info("mix-owner-unbonded-leftover", &[]), - ) - .unwrap(); + let mix_sender = test.make_sender("mix-owner-unbonded-leftover"); + try_remove_mixnode(test.deps_mut(), env.clone(), mix_sender).unwrap(); test.execute_all_pending_events(); - try_remove_mixnode( - test.deps_mut(), - env.clone(), - mock_info("mix-owner-unbonding", &[]), - ) - .unwrap(); + let mix_sender = test.make_sender("mix-owner-unbonding"); + try_remove_mixnode(test.deps_mut(), env.clone(), mix_sender).unwrap(); let res = try_remove_delegation_from_node( test.deps_mut(), diff --git a/contracts/mixnet/src/gateways/queries.rs b/contracts/mixnet/src/gateways/queries.rs index d6eae594877..e3dee948cdb 100644 --- a/contracts/mixnet/src/gateways/queries.rs +++ b/contracts/mixnet/src/gateways/queries.rs @@ -85,8 +85,8 @@ pub(crate) mod tests { use super::*; use crate::support::tests; use crate::support::tests::test_helpers; - use crate::support::tests::test_helpers::TestSetup; - use cosmwasm_std::testing::mock_info; + use crate::support::tests::test_helpers::{sorted_addresses, TestSetup}; + use cosmwasm_std::testing::message_info; #[test] fn gateways_empty_on_init() { @@ -138,15 +138,16 @@ pub(crate) mod tests { fn gateway_pagination_works() { let stake = tests::fixtures::good_gateway_pledge(); let mut test = TestSetup::new(); + let owners = sorted_addresses(4); // prepare 4 gateways that are sorted by the generated identities // (because we query them in an ascended manner) let mut gateways = (0..4) - .map(|i| test.gateway_with_signature(format!("sender{}", i), None).0) + .map(|i| test.gateway_with_signature(&owners[i], None).0) .collect::>(); gateways.sort_by(|g1, g2| g1.identity_key.cmp(&g2.identity_key)); - let info = mock_info("sender0", &stake); + let info = message_info(&owners[0], &stake); test.save_legacy_gateway(gateways[0].clone(), &info); let per_page = 2; @@ -156,14 +157,14 @@ pub(crate) mod tests { assert_eq!(1, page1.nodes.len()); // save another - let info = mock_info("sender1", &stake); + let info = message_info(&owners[1], &stake); test.save_legacy_gateway(gateways[1].clone(), &info); // page1 should have 2 results on it let page1 = query_gateways_paged(test.deps(), None, Option::from(per_page)).unwrap(); assert_eq!(2, page1.nodes.len()); - let info = mock_info("sender2", &stake); + let info = message_info(&owners[2], &stake); test.save_legacy_gateway(gateways[2].clone(), &info); // page1 still has 2 results @@ -182,7 +183,7 @@ pub(crate) mod tests { assert_eq!(1, page2.nodes.len()); // save another one - let info = mock_info("sender3", &stake); + let info = message_info(&owners[3], &stake); test.save_legacy_gateway(gateways[3].clone(), &info); let page2 = query_gateways_paged( @@ -200,27 +201,33 @@ pub(crate) mod tests { fn query_for_gateway_owner_works() { let mut test = TestSetup::new(); + let bob = test.make_addr("bob"); + let fred = test.make_addr("fred"); + // "fred" does not own a mixnode if there are no mixnodes - let res = query_owned_gateway(test.deps(), "fred".to_string()).unwrap(); + let res = query_owned_gateway(test.deps(), fred.to_string()).unwrap(); assert!(res.gateway.is_none()); // gateway was added to "bob", "fred" still does not own one - test.add_legacy_gateway("bob", None); + test.add_legacy_gateway(&bob, None); - let res = query_owned_gateway(test.deps(), "fred".to_string()).unwrap(); + let res = query_owned_gateway(test.deps(), fred.to_string()).unwrap(); assert!(res.gateway.is_none()); // "fred" now owns a gateway! - test.add_legacy_gateway("fred", None); + test.add_legacy_gateway(&fred, None); - let res = query_owned_gateway(test.deps(), "fred".to_string()).unwrap(); + let res = query_owned_gateway(test.deps(), fred.to_string()).unwrap(); assert!(res.gateway.is_some()); // but after unbonding it, he doesn't own one anymore - crate::gateways::transactions::try_remove_gateway(test.deps_mut(), mock_info("fred", &[])) - .unwrap(); + crate::gateways::transactions::try_remove_gateway( + test.deps_mut(), + message_info(&fred, &[]), + ) + .unwrap(); - let res = query_owned_gateway(test.deps(), "fred".to_string()).unwrap(); + let res = query_owned_gateway(test.deps(), fred.to_string()).unwrap(); assert!(res.gateway.is_none()); } } diff --git a/contracts/mixnet/src/gateways/storage.rs b/contracts/mixnet/src/gateways/storage.rs index 9a530fec8d4..b3b60826e2e 100644 --- a/contracts/mixnet/src/gateways/storage.rs +++ b/contracts/mixnet/src/gateways/storage.rs @@ -13,7 +13,7 @@ pub(crate) const PREASSIGNED_LEGACY_IDS: Map = Map::new(LEGACY_GATEWAY_ID_NAMESPACE); pub(crate) struct GatewayBondIndex<'a> { - pub(crate) owner: UniqueIndex<'a, Addr, GatewayBond>, + pub(crate) owner: UniqueIndex<'a, Addr, GatewayBond, ()>, } // IndexList is just boilerplate code for fetching a struct's indexes @@ -26,8 +26,7 @@ impl IndexList for GatewayBondIndex<'_> { } // gateways() is the storage access function. -pub(crate) fn gateways<'a>() -> IndexedMap<'a, IdentityKeyRef<'a>, GatewayBond, GatewayBondIndex<'a>> -{ +pub(crate) fn gateways<'a>() -> IndexedMap, GatewayBond, GatewayBondIndex<'a>> { let indexes = GatewayBondIndex { owner: UniqueIndex::new(|d| d.owner.clone(), GATEWAYS_OWNER_IDX_NAMESPACE), }; diff --git a/contracts/mixnet/src/gateways/transactions.rs b/contracts/mixnet/src/gateways/transactions.rs index ef8f282f3e5..ba325c23a99 100644 --- a/contracts/mixnet/src/gateways/transactions.rs +++ b/contracts/mixnet/src/gateways/transactions.rs @@ -151,10 +151,9 @@ pub mod tests { use crate::mixnet_contract_settings::storage::minimum_node_pledge; use crate::nodes::helpers::{get_node_details_by_identity, must_get_node_bond_by_owner}; use crate::signing::storage as signing_storage; - use crate::support::tests; use crate::support::tests::fixtures::{good_gateway_pledge, good_mixnode_pledge}; use crate::support::tests::test_helpers::TestSetup; - use cosmwasm_std::testing::mock_info; + use cosmwasm_std::testing::message_info; use cosmwasm_std::{Addr, BankMsg, Uint128}; use mixnet_contract_common::ExecuteMsg; @@ -163,12 +162,12 @@ pub mod tests { let mut test = TestSetup::new(); // if we fail validation (by say not sending enough funds - let sender = "alice"; - let minimum_pledge = minimum_node_pledge(test.deps().storage).unwrap(); + let sender = &test.make_addr("alice"); + let minimum_pledge = minimum_node_pledge(test.deps().storage)?; let mut insufficient_pledge = minimum_pledge.clone(); insufficient_pledge.amount -= Uint128::new(1000); - let info = mock_info(sender, &[insufficient_pledge.clone()]); + let info = message_info(sender, &[insufficient_pledge.clone()]); let (gateway, sig) = test.gateway_with_signature(sender, Some(vec![insufficient_pledge.clone()])); @@ -191,7 +190,7 @@ pub mod tests { ); // if the signature provided is invalid, the bonding also fails - let info = mock_info(sender, &[minimum_pledge]); + let info = message_info(sender, &[minimum_pledge]); // if there was already a gateway bonded by particular user test.add_legacy_gateway(sender, None); @@ -201,11 +200,11 @@ pub mod tests { assert_eq!(Err(MixnetContractError::AlreadyOwnsGateway), result); // the same holds if the user already owns a mixnode - let sender2 = "mixnode-owner"; + let sender2 = &test.make_addr("mixnode-owner"); let mix_id = test.add_legacy_mixnode(sender2, None); - let info = mock_info(sender2, &good_gateway_pledge()); + let info = message_info(sender2, &good_gateway_pledge()); let (gateway, sig) = test.gateway_with_signature(sender2, None); let result = try_add_gateway( @@ -225,8 +224,7 @@ pub mod tests { // and the node has been added as a nym-node let nym_node = - get_node_details_by_identity(test.deps().storage, gateway.identity_key.clone()) - .unwrap() + get_node_details_by_identity(test.deps().storage, gateway.identity_key.clone())? .unwrap(); assert_eq!(nym_node.bond_information.owner, info.sender); @@ -235,8 +233,7 @@ pub mod tests { assert!(maybe_legacy.is_none()); // make sure we got assigned the next id (note: we have already bonded a mixnode and a gateway before in this test) - let bond = - must_get_node_bond_by_owner(test.deps().storage, &Addr::unchecked(sender2)).unwrap(); + let bond = must_get_node_bond_by_owner(test.deps().storage, &Addr::unchecked(sender2))?; assert_eq!(3, bond.node_id); Ok(()) @@ -247,9 +244,9 @@ pub mod tests { let mut test = TestSetup::new(); let env = test.env(); - let sender = "alice"; + let sender = &test.make_addr("alice"); let pledge = good_mixnode_pledge(); - let info = mock_info(sender, pledge.as_ref()); + let info = message_info(sender, pledge.as_ref()); let (gateway, signature) = test.gateway_with_signature(sender, Some(pledge.clone())); @@ -269,7 +266,7 @@ pub mod tests { let mut different_pledge = pledge.clone(); different_pledge[0].amount += Uint128::new(12345); - let info = mock_info(sender, different_pledge.as_ref()); + let info = message_info(sender, different_pledge.as_ref()); let res = try_add_gateway( test.deps_mut(), env.clone(), @@ -279,7 +276,7 @@ pub mod tests { ); assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature)); - let other_sender = mock_info("another-sender", pledge.as_ref()); + let other_sender = message_info(&test.make_addr("another-sender"), pledge.as_ref()); let res = try_add_gateway( test.deps_mut(), env.clone(), @@ -290,7 +287,7 @@ pub mod tests { assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature)); // trying to reuse the same signature for another bonding fails (because nonce doesn't match!) - let info = mock_info(sender, pledge.as_ref()); + let info = message_info(sender, pledge.as_ref()); let current_nonce = signing_storage::get_signing_nonce(test.deps().storage, Addr::unchecked(sender)) .unwrap(); @@ -321,7 +318,7 @@ pub mod tests { let env = test.env(); // try unbond when no nodes exist yet - let info = mock_info("anyone", &[]); + let info = message_info(&test.make_addr("anyone"), &[]); let msg = ExecuteMsg::UnbondGateway {}; let result = execute(test.deps_mut(), env.clone(), info, msg); @@ -329,21 +326,21 @@ pub mod tests { assert_eq!( result, Err(MixnetContractError::NoAssociatedGatewayBond { - owner: Addr::unchecked("anyone") + owner: test.make_addr("anyone") }) ); // let's add a node owned by bob - test.add_legacy_gateway("bob", None); + test.add_legacy_gateway(&test.make_addr("bob"), None); // attempt to unbond fred's node, which doesn't exist - let info = mock_info("fred", &[]); + let info = message_info(&test.make_addr("fred"), &[]); let msg = ExecuteMsg::UnbondGateway {}; let result = execute(test.deps_mut(), env.clone(), info, msg); assert_eq!( result, Err(MixnetContractError::NoAssociatedGatewayBond { - owner: Addr::unchecked("fred") + owner: test.make_addr("fred") }) ); @@ -354,10 +351,10 @@ pub mod tests { assert_eq!(1, nodes.len()); let first_node = &nodes[0]; - assert_eq!(&Addr::unchecked("bob"), first_node.owner()); + assert_eq!(&test.make_addr("bob"), first_node.owner()); // add a node owned by fred - let (fred_identity, _) = test.add_legacy_gateway("fred", None); + let (fred_identity, _) = test.add_legacy_gateway(&test.make_addr("fred"), None); // let's make sure we now have 2 nodes: let nodes = queries::query_gateways_paged(test.deps(), None, None) @@ -366,7 +363,7 @@ pub mod tests { assert_eq!(2, nodes.len()); // unbond fred's node - let info = mock_info("fred", &[]); + let info = message_info(&test.make_addr("fred"), &[]); let msg = ExecuteMsg::UnbondGateway {}; let remove_fred = execute(test.deps_mut(), env, info.clone(), msg).unwrap(); @@ -381,8 +378,8 @@ pub mod tests { Response::new() .add_message(expected_message) .add_event(new_gateway_unbonding_event( - &Addr::unchecked("fred"), - &tests::fixtures::good_gateway_pledge()[0], + &test.make_addr("fred"), + &good_gateway_pledge()[0], &fred_identity, )); @@ -393,15 +390,15 @@ pub mod tests { .unwrap() .nodes; assert_eq!(1, nodes.len()); - assert_eq!(&Addr::unchecked("bob"), nodes[0].owner()); + assert_eq!(&test.make_addr("bob"), nodes[0].owner()); } #[test] fn update_gateway_config() { let mut test = TestSetup::new(); - let owner = "alice"; - let info = mock_info(owner, &[]); + let owner = &test.make_addr("alice"); + let info = message_info(owner, &[]); let update = GatewayConfigUpdate { host: "1.1.1.1:1234".to_string(), mix_port: 1234, diff --git a/contracts/mixnet/src/interval/pending_events.rs b/contracts/mixnet/src/interval/pending_events.rs index 75d78c17b65..954d8d66ed4 100644 --- a/contracts/mixnet/src/interval/pending_events.rs +++ b/contracts/mixnet/src/interval/pending_events.rs @@ -652,18 +652,17 @@ mod tests { use crate::support::tests::fixtures::TEST_COIN_DENOM; use crate::support::tests::test_helpers::get_bank_send_msg; use cosmwasm_std::coin; - use cosmwasm_std::testing::mock_info; use mixnet_contract_common::rewarding::helpers::truncate_reward_amount; #[test] fn returns_the_tokens_if_mixnode_has_unbonded() { let mut test = TestSetup::new(); - let mix_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let mix_id = test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner"), None); let delegation = 120_000_000u128; let delegation_coin = coin(delegation, TEST_COIN_DENOM); - let owner1 = "delegator1"; - let owner2 = "delegator2"; + let owner1 = &test.make_addr("delegator1"); + let owner2 = &test.make_addr("delegator2"); // add pre-existing delegation test.add_immediate_delegation(owner1, delegation, mix_id); @@ -682,8 +681,7 @@ mod tests { .unwrap(); // delegation wasn't increased - let storage_key = - Delegation::generate_storage_key(mix_id, &Addr::unchecked(owner1), None); + let storage_key = Delegation::generate_storage_key(mix_id, owner1, None); let amount = delegations_storage::delegations() .load(test.deps().storage, storage_key) .unwrap() @@ -692,7 +690,7 @@ mod tests { // and all tokens are returned back to the delegator let (receiver, sent_amount) = get_bank_send_msg(&res_increase).unwrap(); - assert_eq!(receiver, owner1); + assert_eq!(receiver, owner1.to_string()); assert_eq!(sent_amount[0], delegation_coin); // for a fresh delegation, nothing was added to the storage either @@ -714,25 +712,26 @@ mod tests { // and all tokens are returned back to the delegator let (receiver, sent_amount) = get_bank_send_msg(&res_fresh).unwrap(); - assert_eq!(receiver, owner2); + assert_eq!(receiver, owner2.to_string()); assert_eq!(sent_amount[0], delegation_coin); } #[test] fn returns_the_tokens_is_mixnode_is_unbonding() { let mut test = TestSetup::new(); - let mix_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let mix_id = test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner"), None); let delegation = 120_000_000u128; let delegation_coin = coin(delegation, TEST_COIN_DENOM); - let owner1 = "delegator1"; - let owner2 = "delegator2"; + let owner1 = &test.make_addr("delegator1"); + let owner2 = &test.make_addr("delegator2"); // add pre-existing delegation test.add_immediate_delegation(owner1, delegation, mix_id); let env = test.env(); - try_remove_mixnode(test.deps_mut(), env.clone(), mock_info("mix-owner", &[])).unwrap(); + let sender = test.make_sender("mix-owner"); + try_remove_mixnode(test.deps_mut(), env.clone(), sender).unwrap(); let res_increase = delegate( test.deps_mut(), @@ -745,8 +744,7 @@ mod tests { .unwrap(); // delegation wasn't increased - let storage_key = - Delegation::generate_storage_key(mix_id, &Addr::unchecked(owner1), None); + let storage_key = Delegation::generate_storage_key(mix_id, owner1, None); let amount = delegations_storage::delegations() .load(test.deps().storage, storage_key) .unwrap() @@ -755,7 +753,7 @@ mod tests { // and all tokens are returned back to the delegator let (receiver, sent_amount) = get_bank_send_msg(&res_increase).unwrap(); - assert_eq!(receiver, owner1); + assert_eq!(receiver, owner1.to_string()); assert_eq!(sent_amount[0], delegation_coin); // for a fresh delegation, nothing was added to the storage either @@ -768,8 +766,7 @@ mod tests { delegation_coin.clone(), ) .unwrap(); - let storage_key = - Delegation::generate_storage_key(mix_id, &Addr::unchecked(owner2), None); + let storage_key = Delegation::generate_storage_key(mix_id, owner2, None); assert!(delegations_storage::delegations() .may_load(test.deps().storage, storage_key) .unwrap() @@ -777,21 +774,23 @@ mod tests { // and all tokens are returned back to the delegator let (receiver, sent_amount) = get_bank_send_msg(&res_fresh).unwrap(); - assert_eq!(receiver, owner2); + assert_eq!(receiver, owner2.to_string()); assert_eq!(sent_amount[0], delegation_coin); } #[test] fn if_delegation_already_exists_a_fresh_one_with_sum_of_both_is_created() { let mut test = TestSetup::new(); - let mix_id = - test.add_rewarded_legacy_mixnode("mix-owner", Some(100_000_000_000u128.into())); + let mix_id = test.add_rewarded_legacy_mixnode( + &test.make_addr("mix-owner"), + Some(100_000_000_000u128.into()), + ); let delegation_og = 120_000_000u128; let delegation_new = 543_000_000u128; let delegation_coin_new = coin(delegation_new, TEST_COIN_DENOM); - let owner = "delegator"; + let owner = &test.make_addr("delegator"); test.add_immediate_delegation(owner, delegation_og, mix_id); let env = test.env(); @@ -814,8 +813,7 @@ mod tests { let rewarding = rewards_storage::MIXNODE_REWARDING .load(test.deps().storage, mix_id) .unwrap(); - let storage_key = - Delegation::generate_storage_key(mix_id, &Addr::unchecked(owner), None); + let storage_key = Delegation::generate_storage_key(mix_id, owner, None); let delegation = delegations_storage::delegations() .load(test.deps().storage, storage_key) .unwrap(); @@ -829,8 +827,10 @@ mod tests { #[test] fn if_delegation_already_exists_with_unclaimed_rewards_fresh_one_is_created() { let mut test = TestSetup::new(); - let mix_id = - test.add_rewarded_legacy_mixnode("mix-owner", Some(100_000_000_000u128.into())); + let mix_id = test.add_rewarded_legacy_mixnode( + &test.make_addr("mix-owner"), + Some(100_000_000_000u128.into()), + ); let delegation_og = 120_000_000u128; let delegation_new = 543_000_000u128; @@ -846,7 +846,7 @@ mod tests { test.skip_to_next_epoch_end(); test.reward_with_distribution_ignore_state(mix_id, active_params); - let owner = "delegator"; + let owner = &test.make_addr("delegator"); test.add_immediate_delegation(owner, delegation_og, mix_id); test.skip_to_next_epoch_end(); @@ -854,8 +854,7 @@ mod tests { test.skip_to_next_epoch_end(); let dist2 = test.reward_with_distribution_ignore_state(mix_id, active_params); - let storage_key = - Delegation::generate_storage_key(mix_id, &Addr::unchecked(owner), None); + let storage_key = Delegation::generate_storage_key(mix_id, owner, None); let delegation_pre = delegations_storage::delegations() .load(test.deps().storage, storage_key.clone()) .unwrap(); @@ -903,8 +902,10 @@ mod tests { #[test] fn appropriately_updates_state_for_fresh_delegation() { let mut test = TestSetup::new(); - let mix_id = - test.add_rewarded_legacy_mixnode("mix-owner", Some(100_000_000_000u128.into())); + let mix_id = test.add_rewarded_legacy_mixnode( + &test.make_addr("mix-owner"), + Some(100_000_000_000u128.into()), + ); let owner = "delegator"; let delegation = 120_000_000u128; @@ -964,7 +965,7 @@ mod tests { #[test] fn doesnt_return_any_tokens_if_it_doesnt_exist() { let mut test = TestSetup::new(); - let mix_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let mix_id = test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner"), None); let owner = Addr::unchecked("delegator"); @@ -975,15 +976,15 @@ mod tests { #[test] fn errors_out_if_mix_rewarding_doesnt_exist() { let mut test = TestSetup::new(); - let mix_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let mix_id = test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner"), None); - let owner = Addr::unchecked("delegator"); - test.add_immediate_delegation(owner.as_str(), 100_000_000u32, mix_id); + let owner = &test.make_addr("delegator"); + test.add_immediate_delegation(owner, 100_000_000u32, mix_id); // this should never happen in actual code, but if we manually messed something up, // lets make sure this throws an error rewards_storage::MIXNODE_REWARDING.remove(test.deps_mut().storage, mix_id); - let res = undelegate(test.deps_mut(), 123, owner, mix_id); + let res = undelegate(test.deps_mut(), 123, owner.clone(), mix_id); assert!(matches!( res, Err(MixnetContractError::InconsistentState { .. }) @@ -993,10 +994,12 @@ mod tests { #[test] fn returns_all_delegated_tokens_with_earned_rewards() { let mut test = TestSetup::new(); - let mix_id = - test.add_rewarded_legacy_mixnode("mix-owner", Some(100_000_000_000u128.into())); + let mix_id = test.add_rewarded_legacy_mixnode( + &test.make_addr("mix-owner"), + Some(100_000_000_000u128.into()), + ); - let owner = "delegator"; + let owner = &test.make_addr("delegator"); let delegation = 120_000_000u128; let active_params = test.active_node_params(100.0); @@ -1022,7 +1025,7 @@ mod tests { let res = undelegate(test.deps_mut(), 123, Addr::unchecked(owner), mix_id).unwrap(); let (receiver, sent_amount) = get_bank_send_msg(&res).unwrap(); - assert_eq!(receiver, owner); + assert_eq!(receiver, owner.to_string()); assert_eq!(sent_amount[0].amount.u128(), expected_return); // make sure delegation no longer exists @@ -1047,7 +1050,7 @@ mod tests { use crate::mixnodes::storage as mixnodes_storage; use crate::rewards::storage as rewards_storage; use crate::support::tests::test_helpers::{get_bank_send_msg, TestSetup}; - use cosmwasm_std::testing::mock_info; + use cosmwasm_std::testing::message_info; use cosmwasm_std::{Addr, Uint128}; use mixnet_contract_common::error::MixnetContractError; use mixnet_contract_common::mixnode::{PendingMixNodeChanges, UnbondedMixnode}; @@ -1073,11 +1076,12 @@ mod tests { let change = test.coins(1234); // increase - let owner = "mix-owner1"; + let owner = &test.make_addr("mix-owner1"); let pledge = Uint128::new(250_000_000); let mix_id = test.add_rewarded_legacy_mixnode(owner, Some(pledge)); - try_increase_pledge(test.deps_mut(), env.clone(), mock_info(owner, &change)).unwrap(); + try_increase_pledge(test.deps_mut(), env.clone(), message_info(owner, &change)) + .unwrap(); let res = unbond_mixnode(test.deps_mut(), &env, 123, mix_id); assert!(matches!( @@ -1086,14 +1090,14 @@ mod tests { )); // decrease - let owner = "mix-owner2"; + let owner = &test.make_addr("mix-owner2"); let pledge = Uint128::new(250_000_000); let mix_id = test.add_rewarded_legacy_mixnode(owner, Some(pledge)); try_decrease_pledge( test.deps_mut(), env.clone(), - mock_info(owner, &[]), + message_info(owner, &[]), change[0].clone(), ) .unwrap(); @@ -1105,7 +1109,7 @@ mod tests { )); // artificial - let owner = "mix-owner3"; + let owner = &test.make_addr("mix-owner3"); let pledge = Uint128::new(250_000_000); let mix_id = test.add_rewarded_legacy_mixnode(owner, Some(pledge)); @@ -1128,7 +1132,7 @@ mod tests { fn returns_original_pledge_alongside_any_earned_rewards() { let mut test = TestSetup::new(); - let owner = "mix-owner"; + let owner = &test.make_addr("mix-owner"); let pledge = Uint128::new(250_000_000); let mix_id = test.add_rewarded_legacy_mixnode(owner, Some(pledge)); let mix_details = mixnodes_storage::mixnode_bonds() @@ -1149,7 +1153,7 @@ mod tests { let env = test.env(); let res = unbond_mixnode(test.deps_mut(), &env, 123, mix_id).unwrap(); let (receiver, sent_amount) = get_bank_send_msg(&res).unwrap(); - assert_eq!(receiver, owner); + assert_eq!(receiver, owner.to_string()); assert_eq!(sent_amount[0].amount, expected_return); assert!(rewards_storage::MIXNODE_REWARDING @@ -1201,7 +1205,7 @@ mod tests { let mut test = TestSetup::new(); let change = test.coin(1234); - let owner = "mix-owner"; + let owner = &test.make_addr("mix-owner"); let pledge = Uint128::new(250_000_000); let mix_id = test.add_rewarded_legacy_mixnode(owner, Some(pledge)); @@ -1215,7 +1219,7 @@ mod tests { #[test] fn updates_stored_bond_information_and_rewarding_details() { let mut test = TestSetup::new(); - let mix_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let mix_id = test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner"), None); test.set_pending_pledge_change(mix_id, None); let old_details = get_mixnode_details_by_id(test.deps().storage, mix_id) @@ -1249,21 +1253,43 @@ mod tests { let pledge3 = Uint128::new(200_000_000); let active_params = test.active_node_params(100.0); - let mix_id_repledge = test.add_rewarded_legacy_mixnode("mix-owner1", Some(pledge1)); + let mix_id_repledge = + test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner1"), Some(pledge1)); test.set_pending_pledge_change(mix_id_repledge, None); let increase = test.coin(pledge2.u128()); increase_mixnode_pledge(test.deps_mut(), 123, mix_id_repledge, increase).unwrap(); - let mix_id_full_pledge = test.add_rewarded_legacy_mixnode("mix-owner2", Some(pledge3)); + let mix_id_full_pledge = + test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner2"), Some(pledge3)); - test.add_immediate_delegation("alice", 123_456_789u128, mix_id_repledge); - test.add_immediate_delegation("bob", 500_000_000u128, mix_id_repledge); - test.add_immediate_delegation("carol", 111_111_111u128, mix_id_repledge); + test.add_immediate_delegation( + &test.make_addr("alice"), + 123_456_789u128, + mix_id_repledge, + ); + test.add_immediate_delegation(&test.make_addr("bob"), 500_000_000u128, mix_id_repledge); + test.add_immediate_delegation( + &test.make_addr("carol"), + 111_111_111u128, + mix_id_repledge, + ); - test.add_immediate_delegation("alice", 123_456_789u128, mix_id_full_pledge); - test.add_immediate_delegation("bob", 500_000_000u128, mix_id_full_pledge); - test.add_immediate_delegation("carol", 111_111_111u128, mix_id_full_pledge); + test.add_immediate_delegation( + &test.make_addr("alice"), + 123_456_789u128, + mix_id_full_pledge, + ); + test.add_immediate_delegation( + &test.make_addr("bob"), + 500_000_000u128, + mix_id_full_pledge, + ); + test.add_immediate_delegation( + &test.make_addr("carol"), + 111_111_111u128, + mix_id_full_pledge, + ); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![mix_id_repledge, mix_id_full_pledge]); @@ -1282,12 +1308,25 @@ mod tests { let pledge2 = Uint128::new(50_000_000_000); let active_params = test.active_node_params(100.0); - let mix_id_repledge = test.add_rewarded_legacy_mixnode("mix-owner1", Some(pledge1)); + let mix_id_repledge = + test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner1"), Some(pledge1)); test.set_pending_pledge_change(mix_id_repledge, None); - test.add_immediate_delegation("alice", 123_456_789_000u128, mix_id_repledge); - test.add_immediate_delegation("bob", 500_000_000_000u128, mix_id_repledge); - test.add_immediate_delegation("carol", 111_111_111_000u128, mix_id_repledge); + test.add_immediate_delegation( + &test.make_addr("alice"), + 123_456_789_000u128, + mix_id_repledge, + ); + test.add_immediate_delegation( + &test.make_addr("bob"), + 500_000_000_000u128, + mix_id_repledge, + ); + test.add_immediate_delegation( + &test.make_addr("carol"), + 111_111_111_000u128, + mix_id_repledge, + ); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![mix_id_repledge]); @@ -1298,11 +1337,24 @@ mod tests { increase_mixnode_pledge(test.deps_mut(), 123, mix_id_repledge, increase).unwrap(); let pledge3 = Uint128::new(200_000_000_000) + truncate_reward_amount(dist.operator); - let mix_id_full_pledge = test.add_rewarded_legacy_mixnode("mix-owner2", Some(pledge3)); + let mix_id_full_pledge = + test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner2"), Some(pledge3)); - test.add_immediate_delegation("alice", 123_456_789_000u128, mix_id_full_pledge); - test.add_immediate_delegation("bob", 500_000_000_000u128, mix_id_full_pledge); - test.add_immediate_delegation("carol", 111_111_111_000u128, mix_id_full_pledge); + test.add_immediate_delegation( + &test.make_addr("alice"), + 123_456_789_000u128, + mix_id_full_pledge, + ); + test.add_immediate_delegation( + &test.make_addr("bob"), + 500_000_000_000u128, + mix_id_full_pledge, + ); + test.add_immediate_delegation( + &test.make_addr("carol"), + 111_111_111_000u128, + mix_id_full_pledge, + ); let lost_operator = dist.operator - Decimal::from_atomics(truncate_reward_amount(dist.operator), 0).unwrap(); @@ -1322,7 +1374,7 @@ mod tests { .unwrap(); test.add_immediate_delegation( - "dave", + &test.make_addr("dave"), truncate_reward_amount(dist.delegates).u128(), mix_id_full_pledge, ); @@ -1349,12 +1401,25 @@ mod tests { let pledge2 = Uint128::new(50_000_000_000); let active_params = test.active_node_params(100.0); - let mix_id_repledge = test.add_rewarded_legacy_mixnode("mix-owner1", Some(pledge1)); + let mix_id_repledge = + test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner1"), Some(pledge1)); test.set_pending_pledge_change(mix_id_repledge, None); - test.add_immediate_delegation("alice", 123_456_789_000u128, mix_id_repledge); - test.add_immediate_delegation("bob", 500_000_000_000u128, mix_id_repledge); - test.add_immediate_delegation("carol", 111_111_111_000u128, mix_id_repledge); + test.add_immediate_delegation( + &test.make_addr("alice"), + 123_456_789_000u128, + mix_id_repledge, + ); + test.add_immediate_delegation( + &test.make_addr("bob"), + 500_000_000_000u128, + mix_id_repledge, + ); + test.add_immediate_delegation( + &test.make_addr("carol"), + 111_111_111_000u128, + mix_id_repledge, + ); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![mix_id_repledge]); @@ -1376,11 +1441,24 @@ mod tests { let pledge3 = Uint128::new(200_000_000_000) + truncate_reward_amount(cumulative_op_reward); - let mix_id_full_pledge = test.add_rewarded_legacy_mixnode("mix-owner2", Some(pledge3)); + let mix_id_full_pledge = + test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner2"), Some(pledge3)); - test.add_immediate_delegation("alice", 123_456_789_000u128, mix_id_full_pledge); - test.add_immediate_delegation("bob", 500_000_000_000u128, mix_id_full_pledge); - test.add_immediate_delegation("carol", 111_111_111_000u128, mix_id_full_pledge); + test.add_immediate_delegation( + &test.make_addr("alice"), + 123_456_789_000u128, + mix_id_full_pledge, + ); + test.add_immediate_delegation( + &test.make_addr("bob"), + 500_000_000_000u128, + mix_id_full_pledge, + ); + test.add_immediate_delegation( + &test.make_addr("carol"), + 111_111_111_000u128, + mix_id_full_pledge, + ); let lost_operator = cumulative_op_reward - Decimal::from_atomics(truncate_reward_amount(cumulative_op_reward), 0).unwrap(); @@ -1400,7 +1478,7 @@ mod tests { .unwrap(); test.add_immediate_delegation( - "dave", + &test.make_addr("dave"), truncate_reward_amount(cumulative_del_reward).u128(), mix_id_full_pledge, ); @@ -1423,7 +1501,7 @@ mod tests { #[test] fn updates_the_pending_pledge_changes_field() { let mut test = TestSetup::new(); - let mix_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let mix_id = test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner"), None); test.set_pending_pledge_change(mix_id, None); let amount = test.coin(12345); @@ -1459,7 +1537,7 @@ mod tests { let mut test = TestSetup::new(); let change = test.coin(1234); - let owner = "mix-owner"; + let owner = &test.make_addr("mix-owner"); let pledge = Uint128::new(250_000_000); let mix_id = test.add_rewarded_legacy_mixnode(owner, Some(pledge)); @@ -1473,7 +1551,7 @@ mod tests { #[test] fn updates_stored_bond_information_and_rewarding_details() { let mut test = TestSetup::new(); - let mix_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let mix_id = test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner"), None); test.set_pending_pledge_change(mix_id, None); let old_details = get_mixnode_details_by_id(test.deps().storage, mix_id) @@ -1502,7 +1580,7 @@ mod tests { #[test] fn returns_tokens_back_to_the_owner() { let mut test = TestSetup::new(); - let owner = "mix-owner"; + let owner = &test.make_addr("mix-owner"); let mix_id = test.add_rewarded_legacy_mixnode(owner, None); test.set_pending_pledge_change(mix_id, None); @@ -1528,21 +1606,43 @@ mod tests { let pledge3 = Uint128::new(150_000_000); let active_params = test.active_node_params(100.0); - let mix_id_repledge = test.add_rewarded_legacy_mixnode("mix-owner1", Some(pledge1)); + let mix_id_repledge = + test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner1"), Some(pledge1)); test.set_pending_pledge_change(mix_id_repledge, None); let decrease = test.coin(pledge_change.u128()); decrease_mixnode_pledge(test.deps_mut(), 123, mix_id_repledge, decrease).unwrap(); - let mix_id_full_pledge = test.add_rewarded_legacy_mixnode("mix-owner2", Some(pledge3)); + let mix_id_full_pledge = + test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner2"), Some(pledge3)); - test.add_immediate_delegation("alice", 123_456_789u128, mix_id_repledge); - test.add_immediate_delegation("bob", 500_000_000u128, mix_id_repledge); - test.add_immediate_delegation("carol", 111_111_111u128, mix_id_repledge); + test.add_immediate_delegation( + &test.make_addr("alice"), + 123_456_789u128, + mix_id_repledge, + ); + test.add_immediate_delegation(&test.make_addr("bob"), 500_000_000u128, mix_id_repledge); + test.add_immediate_delegation( + &test.make_addr("carol"), + 111_111_111u128, + mix_id_repledge, + ); - test.add_immediate_delegation("alice", 123_456_789u128, mix_id_full_pledge); - test.add_immediate_delegation("bob", 500_000_000u128, mix_id_full_pledge); - test.add_immediate_delegation("carol", 111_111_111u128, mix_id_full_pledge); + test.add_immediate_delegation( + &test.make_addr("alice"), + 123_456_789u128, + mix_id_full_pledge, + ); + test.add_immediate_delegation( + &test.make_addr("bob"), + 500_000_000u128, + mix_id_full_pledge, + ); + test.add_immediate_delegation( + &test.make_addr("carol"), + 111_111_111u128, + mix_id_full_pledge, + ); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![mix_id_repledge, mix_id_full_pledge]); @@ -1561,12 +1661,25 @@ mod tests { let pledge_change = Uint128::new(50_000_000_000); let active_params = test.active_node_params(100.0); - let mix_id_repledge = test.add_rewarded_legacy_mixnode("mix-owner1", Some(pledge1)); + let mix_id_repledge = + test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner1"), Some(pledge1)); test.set_pending_pledge_change(mix_id_repledge, None); - test.add_immediate_delegation("alice", 123_456_789_000u128, mix_id_repledge); - test.add_immediate_delegation("bob", 500_000_000_000u128, mix_id_repledge); - test.add_immediate_delegation("carol", 111_111_111_000u128, mix_id_repledge); + test.add_immediate_delegation( + &test.make_addr("alice"), + 123_456_789_000u128, + mix_id_repledge, + ); + test.add_immediate_delegation( + &test.make_addr("bob"), + 500_000_000_000u128, + mix_id_repledge, + ); + test.add_immediate_delegation( + &test.make_addr("carol"), + 111_111_111_000u128, + mix_id_repledge, + ); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![mix_id_repledge]); @@ -1577,11 +1690,24 @@ mod tests { decrease_mixnode_pledge(test.deps_mut(), 123, mix_id_repledge, decrease).unwrap(); let pledge3 = Uint128::new(150_000_000_000) + truncate_reward_amount(dist.operator); - let mix_id_full_pledge = test.add_rewarded_legacy_mixnode("mix-owner2", Some(pledge3)); + let mix_id_full_pledge = + test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner2"), Some(pledge3)); - test.add_immediate_delegation("alice", 123_456_789_000u128, mix_id_full_pledge); - test.add_immediate_delegation("bob", 500_000_000_000u128, mix_id_full_pledge); - test.add_immediate_delegation("carol", 111_111_111_000u128, mix_id_full_pledge); + test.add_immediate_delegation( + &test.make_addr("alice"), + 123_456_789_000u128, + mix_id_full_pledge, + ); + test.add_immediate_delegation( + &test.make_addr("bob"), + 500_000_000_000u128, + mix_id_full_pledge, + ); + test.add_immediate_delegation( + &test.make_addr("carol"), + 111_111_111_000u128, + mix_id_full_pledge, + ); let lost_operator = dist.operator - Decimal::from_atomics(truncate_reward_amount(dist.operator), 0).unwrap(); @@ -1601,7 +1727,7 @@ mod tests { .unwrap(); test.add_immediate_delegation( - "dave", + &test.make_addr("dave"), truncate_reward_amount(dist.delegates).u128(), mix_id_full_pledge, ); @@ -1628,12 +1754,25 @@ mod tests { let pledge_change = Uint128::new(50_000_000_000); let active_params = test.active_node_params(100.0); - let mix_id_repledge = test.add_rewarded_legacy_mixnode("mix-owner1", Some(pledge1)); + let mix_id_repledge = + test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner1"), Some(pledge1)); test.set_pending_pledge_change(mix_id_repledge, None); - test.add_immediate_delegation("alice", 123_456_789_000u128, mix_id_repledge); - test.add_immediate_delegation("bob", 500_000_000_000u128, mix_id_repledge); - test.add_immediate_delegation("carol", 111_111_111_000u128, mix_id_repledge); + test.add_immediate_delegation( + &test.make_addr("alice"), + 123_456_789_000u128, + mix_id_repledge, + ); + test.add_immediate_delegation( + &test.make_addr("bob"), + 500_000_000_000u128, + mix_id_repledge, + ); + test.add_immediate_delegation( + &test.make_addr("carol"), + 111_111_111_000u128, + mix_id_repledge, + ); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![mix_id_repledge]); @@ -1655,11 +1794,24 @@ mod tests { let pledge3 = Uint128::new(150_000_000_000) + truncate_reward_amount(cumulative_op_reward); - let mix_id_full_pledge = test.add_rewarded_legacy_mixnode("mix-owner2", Some(pledge3)); + let mix_id_full_pledge = + test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner2"), Some(pledge3)); - test.add_immediate_delegation("alice", 123_456_789_000u128, mix_id_full_pledge); - test.add_immediate_delegation("bob", 500_000_000_000u128, mix_id_full_pledge); - test.add_immediate_delegation("carol", 111_111_111_000u128, mix_id_full_pledge); + test.add_immediate_delegation( + &test.make_addr("alice"), + 123_456_789_000u128, + mix_id_full_pledge, + ); + test.add_immediate_delegation( + &test.make_addr("bob"), + 500_000_000_000u128, + mix_id_full_pledge, + ); + test.add_immediate_delegation( + &test.make_addr("carol"), + 111_111_111_000u128, + mix_id_full_pledge, + ); let lost_operator = cumulative_op_reward - Decimal::from_atomics(truncate_reward_amount(cumulative_op_reward), 0).unwrap(); @@ -1679,7 +1831,7 @@ mod tests { .unwrap(); test.add_immediate_delegation( - "dave", + &test.make_addr("dave"), truncate_reward_amount(cumulative_del_reward).u128(), mix_id_full_pledge, ); @@ -1702,7 +1854,7 @@ mod tests { #[test] fn updates_the_pending_pledge_changes_field() { let mut test = TestSetup::new(); - let mix_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let mix_id = test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner"), None); test.set_pending_pledge_change(mix_id, None); let amount = test.coin(12345); @@ -1750,7 +1902,7 @@ mod tests { #[test] fn doesnt_do_anything_if_mixnode_has_unbonded() { let mut test = TestSetup::new(); - let mix_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let mix_id = test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner"), None); let env = test.env(); unbond_mixnode(test.deps_mut(), &env, 123, mix_id).unwrap(); @@ -1767,7 +1919,7 @@ mod tests { #[test] fn for_bonded_mixnode_updates_saved_value() { let mut test = TestSetup::new(); - let mix_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let mix_id = test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner"), None); let before = test.mix_rewarding(mix_id).cost_params; diff --git a/contracts/mixnet/src/interval/storage.rs b/contracts/mixnet/src/interval/storage.rs index 2a359cd43f8..52b2b8be392 100644 --- a/contracts/mixnet/src/interval/storage.rs +++ b/contracts/mixnet/src/interval/storage.rs @@ -15,8 +15,8 @@ use mixnet_contract_common::{ EpochEventId, EpochStatus, Interval, IntervalEventId, PendingIntervalEventKind, }; -pub(crate) const CURRENT_EPOCH_STATUS: Item<'_, EpochStatus> = Item::new(CURRENT_EPOCH_STATUS_KEY); -pub(crate) const CURRENT_INTERVAL: Item<'_, Interval> = Item::new(CURRENT_INTERVAL_KEY); +pub(crate) const CURRENT_EPOCH_STATUS: Item = Item::new(CURRENT_EPOCH_STATUS_KEY); +pub(crate) const CURRENT_INTERVAL: Item = Item::new(CURRENT_INTERVAL_KEY); pub(crate) const EPOCH_EVENT_ID_COUNTER: Item = Item::new(EPOCH_EVENT_ID_COUNTER_KEY); pub(crate) const INTERVAL_EVENT_ID_COUNTER: Item = diff --git a/contracts/mixnet/src/interval/transactions.rs b/contracts/mixnet/src/interval/transactions.rs index c60e4440c08..e891c1f0bb2 100644 --- a/contracts/mixnet/src/interval/transactions.rs +++ b/contracts/mixnet/src/interval/transactions.rs @@ -336,7 +336,6 @@ mod tests { use crate::rewards::storage as rewards_storage; use crate::support::tests::fixtures; use crate::support::tests::test_helpers::TestSetup; - use cosmwasm_std::Addr; use mixnet_contract_common::pending_events::PendingEpochEventKind; use mixnet_contract_common::NodeId; @@ -346,7 +345,7 @@ mod tests { let env = test.env(); for i in 0..n { let dummy_action = - PendingEpochEventKind::new_undelegate(Addr::unchecked("foomp"), i as NodeId); + PendingEpochEventKind::new_undelegate(test.make_addr("foomp"), i as NodeId); storage::push_new_epoch_event(test.deps_mut().storage, &env, dummy_action).unwrap(); } } @@ -464,10 +463,10 @@ mod tests { let mut test = TestSetup::new(); let env = test.env(); - let legit_mix = test.add_legacy_mixnode("mix-owner", None); - let delegator = Addr::unchecked("delegator"); + let legit_mix = test.add_legacy_mixnode(&test.make_addr("mix-owner"), None); + let delegator = test.make_addr("delegator"); let amount = 123_456_789u128; - test.add_immediate_delegation(delegator.as_str(), amount, legit_mix); + test.add_immediate_delegation(&delegator, amount, legit_mix); let mut expected_events = Vec::new(); let mut expected_messages: Vec> = Vec::new(); @@ -476,18 +475,18 @@ mod tests { // we expect to receive BankMsg with tokens being returned, // and event regarding delegation let non_existent_delegation = PendingEpochEventKind::new_delegate( - Addr::unchecked("foomp"), + test.make_addr("foomp"), 123, coin(123, TEST_COIN_DENOM), ); storage::push_new_epoch_event(test.deps_mut().storage, &env, non_existent_delegation) .unwrap(); expected_events.push(new_delegation_on_unbonded_node_event( - &Addr::unchecked("foomp"), + &test.make_addr("foomp"), 123, )); expected_messages.push(SubMsg::new(BankMsg::Send { - to_address: "foomp".to_string(), + to_address: test.make_addr("foomp").to_string(), amount: coins(123, TEST_COIN_DENOM), })); @@ -692,7 +691,7 @@ mod tests { let mut expected_events = Vec::new(); let expected_messages: Vec> = Vec::new(); - let legit_mix = test.add_legacy_mixnode("mix-owner", None); + let legit_mix = test.add_legacy_mixnode(&test.make_addr("mix-owner"), None); let new_costs = NodeCostParams { profit_margin_percent: Percent::from_percentage_value(12).unwrap(), interval_operating_cost: coin(123_000, TEST_COIN_DENOM), @@ -815,7 +814,7 @@ mod tests { #[cfg(test)] mod beginning_epoch_transition { use super::*; - use cosmwasm_std::testing::mock_info; + use cosmwasm_std::testing::message_info; #[test] fn returns_error_if_epoch_is_in_progress() { @@ -871,7 +870,7 @@ mod tests { test.skip_to_current_epoch_end(); - let random = mock_info("alice", &[]); + let random = message_info(&test.make_addr("alice"), &[]); let owner = test.owner(); let res = try_begin_epoch_transition(test.deps_mut(), env.clone(), random); @@ -948,7 +947,7 @@ mod tests { mod reconciling_epoch_events { use super::*; use crate::support::tests::fixtures::TEST_COIN_DENOM; - use cosmwasm_std::testing::mock_info; + use cosmwasm_std::testing::message_info; use cosmwasm_std::{coin, coins, BankMsg, Empty, SubMsg}; use mixnet_contract_common::events::{ new_delegation_on_unbonded_node_event, new_rewarding_params_update_event, @@ -1113,7 +1112,7 @@ mod tests { test.skip_to_current_epoch_end(); test.set_epoch_reconciliation_state(); - let random = mock_info("alice", &[]); + let random = message_info(&test.make_addr("alice"), &[]); let owner = test.owner(); let res = try_reconcile_epoch_events(test.deps_mut(), env.clone(), random, None); @@ -1285,18 +1284,18 @@ mod tests { // epoch event let non_existent_delegation = PendingEpochEventKind::new_delegate( - Addr::unchecked("foomp"), + test.make_addr("foomp"), 123, coin(123, TEST_COIN_DENOM), ); storage::push_new_epoch_event(test.deps_mut().storage, &env, non_existent_delegation) .unwrap(); expected_events.push(new_delegation_on_unbonded_node_event( - &Addr::unchecked("foomp"), + &test.make_addr("foomp"), 123, )); expected_messages.push(SubMsg::new(BankMsg::Send { - to_address: "foomp".to_string(), + to_address: test.make_addr("foomp").to_string(), amount: coins(123, TEST_COIN_DENOM), })); expected_events.push(new_pending_epoch_events_execution_event(1)); @@ -1348,14 +1347,14 @@ mod tests { #[cfg(test)] mod assigning_roles { use super::*; - use cosmwasm_std::testing::mock_info; + use cosmwasm_std::testing::message_info; use cosmwasm_std::Uint128; fn setup_test() -> TestSetup { let mut test = TestSetup::new(); for i in 0..10 { - test.add_dummy_nymnode(&format!("node-owner-{i}"), None); + test.add_dummy_nymnode(&test.make_addr(format!("node-owner-{i}")), None); } test.skip_to_current_epoch_end(); @@ -1377,9 +1376,9 @@ mod tests { for bad_state in bad_states { let mut test = TestSetup::new(); - test.add_legacy_mixnode("1", Some(Uint128::new(100000000))); - test.add_legacy_mixnode("2", Some(Uint128::new(100000000))); - test.add_legacy_mixnode("3", Some(Uint128::new(100000000))); + test.add_legacy_mixnode(&test.make_addr("1"), Some(Uint128::new(100000000))); + test.add_legacy_mixnode(&test.make_addr("2"), Some(Uint128::new(100000000))); + test.add_legacy_mixnode(&test.make_addr("3"), Some(Uint128::new(100000000))); test.skip_to_current_epoch_end(); @@ -1478,10 +1477,10 @@ mod tests { #[test] fn can_only_be_performed_by_specified_rewarding_validator() { let mut test = TestSetup::new(); - test.add_dummy_nymnode("1", Some(Uint128::new(100000000))); - test.add_dummy_nymnode("2", Some(Uint128::new(100000000))); - test.add_dummy_nymnode("3", Some(Uint128::new(100000000))); - let some_sender = mock_info("foomper", &[]); + test.add_dummy_nymnode(&test.make_addr("1"), Some(Uint128::new(100000000))); + test.add_dummy_nymnode(&test.make_addr("2"), Some(Uint128::new(100000000))); + test.add_dummy_nymnode(&test.make_addr("3"), Some(Uint128::new(100000000))); + let some_sender = message_info(&test.make_addr("foomper"), &[]); test.skip_to_current_epoch_end(); test.set_epoch_role_assignment_state(); @@ -1788,7 +1787,7 @@ mod tests { #[cfg(test)] mod updating_interval_config { use super::*; - use cosmwasm_std::testing::mock_info; + use cosmwasm_std::testing::message_info; use cosmwasm_std::Decimal; use cw_controllers::AdminError::NotAdmin; use std::time::Duration; @@ -1847,7 +1846,7 @@ mod tests { let rewarding_validator = test.rewarding_validator(); let owner = test.owner(); - let random = mock_info("random-guy", &[]); + let random = message_info(&test.make_addr("random-guy"), &[]); let env = test.env(); let res = try_update_interval_config( diff --git a/contracts/mixnet/src/mixnet_contract_settings/storage.rs b/contracts/mixnet/src/mixnet_contract_settings/storage.rs index 992d15ab8ab..edf511e3cdf 100644 --- a/contracts/mixnet/src/mixnet_contract_settings/storage.rs +++ b/contracts/mixnet/src/mixnet_contract_settings/storage.rs @@ -16,15 +16,15 @@ use mixnet_contract_common::{ }; use std::str::FromStr; -pub(crate) const CONTRACT_STATE: Item<'_, ContractState> = Item::new(CONTRACT_STATE_KEY); +pub(crate) const CONTRACT_STATE: Item = Item::new(CONTRACT_STATE_KEY); pub(crate) const ADMIN: Admin = Admin::new(ADMIN_STORAGE_KEY); -pub(crate) struct NymNodeVersionHistory<'a> { - pub(crate) id_counter: Item<'a, u32>, - pub(crate) version_history: Map<'a, u32, HistoricalNymNodeVersion>, +pub(crate) struct NymNodeVersionHistory { + pub(crate) id_counter: Item, + pub(crate) version_history: Map, } -impl NymNodeVersionHistory<'_> { +impl NymNodeVersionHistory { #[allow(clippy::new_without_default)] pub const fn new() -> Self { Self { diff --git a/contracts/mixnet/src/mixnet_contract_settings/transactions.rs b/contracts/mixnet/src/mixnet_contract_settings/transactions.rs index f48f7b401a5..0175e4a2790 100644 --- a/contracts/mixnet/src/mixnet_contract_settings/transactions.rs +++ b/contracts/mixnet/src/mixnet_contract_settings/transactions.rs @@ -130,8 +130,8 @@ pub mod tests { use crate::mixnet_contract_settings::queries::query_rewarding_validator_address; use crate::mixnet_contract_settings::storage::rewarding_denom; use crate::support::tests::test_helpers; - use cosmwasm_std::testing::mock_info; - use cosmwasm_std::{Addr, Coin, Uint128}; + use cosmwasm_std::testing::{message_info, MockApi}; + use cosmwasm_std::{Coin, Uint128}; use cw_controllers::AdminError::NotAdmin; use mixnet_contract_common::OperatorsParamsUpdate; @@ -139,26 +139,26 @@ pub mod tests { fn update_contract_rewarding_validator_address() { let mut deps = test_helpers::init_contract(); - let info = mock_info("not-the-creator", &[]); + let info = message_info(&deps.api.addr_make("not-the-creator"), &[]); let res = try_update_rewarding_validator_address( deps.as_mut(), info, - "not-the-creator".to_string(), + MockApi::default().addr_make("not-the-creator").to_string(), ); assert_eq!(res, Err(MixnetContractError::Admin(NotAdmin {}))); - let info = mock_info("creator", &[]); + let info = message_info(&deps.api.addr_make("creator"), &[]); let res = try_update_rewarding_validator_address( deps.as_mut(), info, - "new-good-address".to_string(), + MockApi::default().addr_make("new-good-address").to_string(), ); assert_eq!( res, Ok( Response::default().add_event(new_rewarding_validator_address_update_event( - Addr::unchecked("rewarder"), - Addr::unchecked("new-good-address") + MockApi::default().addr_make("rewarder"), + MockApi::default().addr_make("new-good-address") )) ) ); @@ -166,11 +166,11 @@ pub mod tests { let state = storage::CONTRACT_STATE.load(&deps.storage).unwrap(); assert_eq!( state.rewarding_validator_address, - Addr::unchecked("new-good-address") + MockApi::default().addr_make("new-good-address") ); assert_eq!( - state.rewarding_validator_address, + state.rewarding_validator_address.as_str(), query_rewarding_validator_address(deps.as_ref()).unwrap() ); } @@ -194,12 +194,12 @@ pub mod tests { }; // cannot be updated from non-owner account - let info = mock_info("not-the-creator", &[]); + let info = message_info(&deps.api.addr_make("not-the-creator"), &[]); let res = try_update_contract_settings(deps.as_mut(), info, pledge_update.clone()); assert_eq!(res, Err(MixnetContractError::Admin(NotAdmin {}))); // but works fine from the creator account - let info = mock_info("creator", &[]); + let info = message_info(&deps.api.addr_make("creator"), &[]); let res = try_update_contract_settings(deps.as_mut(), info, pledge_update.clone()); assert_eq!( res, @@ -218,21 +218,21 @@ pub mod tests { ); // // error is thrown if rewarded set is smaller than the active set - // let info = mock_info("creator", &[]); + // let info = message_info("creator", &[]); // let mut new_params = current_state.params.clone(); // new_params.mixnode_rewarded_set_size = new_params.mixnode_active_set_size - 1; // let res = try_update_contract_settings(deps.as_mut(), info, new_params); // assert_eq!(Err(MixnetContractError::InvalidActiveSetSize), res); // // // error is thrown for 0 size rewarded set - // let info = mock_info("creator", &[]); + // let info = message_info("creator", &[]); // let mut new_params = current_state.params.clone(); // new_params.mixnode_rewarded_set_size = 0; // let res = try_update_contract_settings(deps.as_mut(), info, new_params); // assert_eq!(Err(MixnetContractError::ZeroRewardedSet), res); // // // error is thrown for 0 size active set - // let info = mock_info("creator", &[]); + // let info = message_info("creator", &[]); // let mut new_params = current_state.params; // new_params.mixnode_active_set_size = 0; // let res = try_update_contract_settings(deps.as_mut(), info, new_params); @@ -249,8 +249,8 @@ pub mod tests { fn is_restricted_to_the_admin() -> anyhow::Result<()> { let mut test = TestSetup::new(); - let not_admin = mock_info("not-admin", &[]); - let admin = mock_info(test.admin().as_ref(), &[]); + let not_admin = message_info(&test.make_addr("not-admin"), &[]); + let admin = message_info(&test.admin(), &[]); let env = test.env(); let res = try_update_current_nym_node_semver( diff --git a/contracts/mixnet/src/mixnodes/helpers.rs b/contracts/mixnet/src/mixnodes/helpers.rs index ef78ffb741a..2134f801dc1 100644 --- a/contracts/mixnet/src/mixnodes/helpers.rs +++ b/contracts/mixnet/src/mixnodes/helpers.rs @@ -139,6 +139,7 @@ pub(crate) mod tests { use super::*; use crate::support::tests::test_helpers::TestSetup; use cosmwasm_std::Uint128; + use easy_addr::addr; pub(crate) struct DummyMixnode { pub mix_id: NodeId, @@ -146,31 +147,34 @@ pub(crate) mod tests { pub identity: IdentityKey, } - pub(crate) const OWNER_EXISTS: &str = "mix-owner-existing"; - pub(crate) const OWNER_UNBONDING: &str = "mix-owner-unbonding"; - pub(crate) const OWNER_UNBONDED: &str = "mix-owner-unbonded"; - pub(crate) const OWNER_UNBONDED_LEFTOVER: &str = "mix-owner-unbonded-leftover"; + pub(crate) const OWNER_EXISTS: &str = addr!("mix-owner-existing"); + pub(crate) const OWNER_UNBONDING: &str = addr!("mix-owner-unbonding"); + pub(crate) const OWNER_UNBONDED: &str = addr!("mix-owner-unbonded"); + pub(crate) const OWNER_UNBONDED_LEFTOVER: &str = addr!("mix-owner-unbonded-leftover"); // create a mixnode that is bonded, unbonded, in the process of unbonding and unbonded with leftover mix rewarding details pub(crate) fn setup_mix_combinations( test: &mut TestSetup, stake: Option, ) -> Vec { - let (mix_id, keypair) = test.add_legacy_mixnode_with_keypair(OWNER_EXISTS, stake); + let (mix_id, keypair) = + test.add_legacy_mixnode_with_keypair(&Addr::unchecked(OWNER_EXISTS), stake); let mix_exists = DummyMixnode { mix_id, owner: Addr::unchecked(OWNER_EXISTS), identity: keypair.public_key().to_base58_string(), }; - let (mix_id, keypair) = test.add_legacy_mixnode_with_keypair(OWNER_UNBONDING, stake); + let (mix_id, keypair) = + test.add_legacy_mixnode_with_keypair(&Addr::unchecked(OWNER_UNBONDING), stake); let mix_unbonding = DummyMixnode { mix_id, owner: Addr::unchecked(OWNER_UNBONDING), identity: keypair.public_key().to_base58_string(), }; - let (mix_id, keypair) = test.add_legacy_mixnode_with_keypair(OWNER_UNBONDED, stake); + let (mix_id, keypair) = + test.add_legacy_mixnode_with_keypair(&Addr::unchecked(OWNER_UNBONDED), stake); let mix_unbonded = DummyMixnode { mix_id, owner: Addr::unchecked(OWNER_UNBONDED), @@ -178,7 +182,7 @@ pub(crate) mod tests { }; let (mix_id, keypair) = - test.add_legacy_mixnode_with_keypair(OWNER_UNBONDED_LEFTOVER, stake); + test.add_legacy_mixnode_with_keypair(&Addr::unchecked(OWNER_UNBONDED_LEFTOVER), stake); let mix_unbonded_leftover = DummyMixnode { mix_id, owner: Addr::unchecked(OWNER_UNBONDED_LEFTOVER), @@ -348,8 +352,8 @@ pub(crate) mod tests { fn cleaning_post_unbond_storage() { let mut test = TestSetup::new(); - let mix_id = test.add_legacy_mixnode("mix-owner", None); - let mix_id_leftover = test.add_legacy_mixnode("mix-owner-leftover", None); + let mix_id = test.add_legacy_mixnode(&test.make_addr("mix-owner"), None); + let mix_id_leftover = test.add_legacy_mixnode(&test.make_addr("mix-owner-leftover"), None); // manually adjust delegation info as to indicate the rewarding information shouldnt get removed let mut rewarding_details = test.mix_rewarding(mix_id_leftover); diff --git a/contracts/mixnet/src/mixnodes/queries.rs b/contracts/mixnet/src/mixnodes/queries.rs index 8f127ed9f5f..bac59ab591f 100644 --- a/contracts/mixnet/src/mixnodes/queries.rs +++ b/contracts/mixnet/src/mixnodes/queries.rs @@ -257,11 +257,12 @@ pub(crate) mod tests { use crate::support::tests::test_helpers::TestSetup; use crate::support::tests::{fixtures, test_helpers}; use cosmwasm_std::testing::mock_env; - use cosmwasm_std::Decimal; + use cosmwasm_std::{Addr, Decimal}; #[cfg(test)] mod mixnode_bonds { use super::*; + use crate::support::tests::test_helpers::sorted_addresses; #[test] fn obeys_limits() { @@ -302,10 +303,16 @@ pub(crate) mod tests { #[test] fn pagination_works() { + let owners = sorted_addresses(4); + let addr1 = &owners[0]; + let addr2 = &owners[1]; + let addr3 = &owners[2]; + let addr4 = &owners[3]; + // as we add mixnodes, we're always inserting them in ascending manner due to monotonically increasing id let mut test = TestSetup::new(); - test.add_legacy_mixnode("addr1", None); + test.add_legacy_mixnode(addr1, None); let per_page = 2; let page1 = query_mixnode_bonds_paged(test.deps(), None, Some(per_page)).unwrap(); @@ -314,13 +321,13 @@ pub(crate) mod tests { assert_eq!(1, page1.nodes.len()); // save another - test.add_legacy_mixnode("addr2", None); + test.add_legacy_mixnode(addr2, None); // page1 should have 2 results on it let page1 = query_mixnode_bonds_paged(test.deps(), None, Some(per_page)).unwrap(); assert_eq!(2, page1.nodes.len()); - test.add_legacy_mixnode("addr3", None); + test.add_legacy_mixnode(addr3, None); // page1 still has the same 2 results let another_page1 = @@ -336,7 +343,7 @@ pub(crate) mod tests { assert_eq!(1, page2.nodes.len()); // save another one - test.add_legacy_mixnode("addr4", None); + test.add_legacy_mixnode(addr4, None); let page2 = query_mixnode_bonds_paged(test.deps(), Some(start_after), Some(per_page)).unwrap(); @@ -395,7 +402,7 @@ pub(crate) mod tests { // as we add mixnodes, we're always inserting them in ascending manner due to monotonically increasing id let mut test = TestSetup::new(); - test.add_legacy_mixnode("addr1", None); + test.add_legacy_mixnode(&test.make_addr("addr1"), None); let per_page = 2; let page1 = query_mixnodes_details_paged(test.deps(), None, Some(per_page)).unwrap(); @@ -404,13 +411,13 @@ pub(crate) mod tests { assert_eq!(1, page1.nodes.len()); // save another - test.add_legacy_mixnode("addr2", None); + test.add_legacy_mixnode(&test.make_addr("addr2"), None); // page1 should have 2 results on it let page1 = query_mixnodes_details_paged(test.deps(), None, Some(per_page)).unwrap(); assert_eq!(2, page1.nodes.len()); - test.add_legacy_mixnode("addr3", None); + test.add_legacy_mixnode(&test.make_addr("addr3"), None); // page1 still has the same 2 results let another_page1 = @@ -427,7 +434,7 @@ pub(crate) mod tests { assert_eq!(1, page2.nodes.len()); // save another one - test.add_legacy_mixnode("addr4", None); + test.add_legacy_mixnode(&test.make_addr("addr4"), None); let page2 = query_mixnodes_details_paged(test.deps(), Some(start_after), Some(per_page)) @@ -446,25 +453,23 @@ pub(crate) mod tests { #[test] fn obeys_limits() { - let mut deps = test_helpers::init_contract(); - let _env = mock_env(); - let rng = test_helpers::test_rng(); + let mut test = TestSetup::new(); + let rng = test.rng.clone(); let limit = 2; - test_helpers::add_dummy_unbonded_mixnodes(rng, deps.as_mut(), 1000); - let page1 = query_unbonded_mixnodes_paged(deps.as_ref(), None, Some(limit)).unwrap(); + test_helpers::add_dummy_unbonded_mixnodes(rng, test.deps_mut(), 1000); + let page1 = query_unbonded_mixnodes_paged(test.deps(), None, Some(limit)).unwrap(); assert_eq!(limit, page1.nodes.len() as u32); } #[test] fn has_default_limit() { - let mut deps = test_helpers::init_contract(); - let _env = mock_env(); - let rng = test_helpers::test_rng(); - test_helpers::add_dummy_unbonded_mixnodes(rng, deps.as_mut(), 1000); + let mut test = TestSetup::new(); + let rng = test.rng.clone(); + test_helpers::add_dummy_unbonded_mixnodes(rng, test.deps_mut(), 1000); // query without explicitly setting a limit - let page1 = query_unbonded_mixnodes_paged(deps.as_ref(), None, None).unwrap(); + let page1 = query_unbonded_mixnodes_paged(test.deps(), None, None).unwrap(); assert_eq!( UNBONDED_MIXNODES_DEFAULT_RETRIEVAL_LIMIT, @@ -474,15 +479,14 @@ pub(crate) mod tests { #[test] fn has_max_limit() { - let mut deps = test_helpers::init_contract(); - let _env = mock_env(); - let rng = test_helpers::test_rng(); - test_helpers::add_dummy_unbonded_mixnodes(rng, deps.as_mut(), 1000); + let mut test = TestSetup::new(); + let rng = test.rng.clone(); + test_helpers::add_dummy_unbonded_mixnodes(rng, test.deps_mut(), 1000); // query with a crazily high limit in an attempt to use too many resources let crazy_limit = 1000; let page1 = - query_unbonded_mixnodes_paged(deps.as_ref(), None, Some(crazy_limit)).unwrap(); + query_unbonded_mixnodes_paged(test.deps(), None, Some(crazy_limit)).unwrap(); // we default to a decent sized upper bound instead assert_eq!( @@ -556,17 +560,18 @@ pub(crate) mod tests { #[cfg(test)] mod unbonded_mixnodes_by_owner { use super::*; + use crate::support::tests::test_helpers::sorted_addresses; use cosmwasm_std::Addr; use mixnet_contract_common::mixnode::UnbondedMixnode; - fn add_unbonded_with_owner(storage: &mut dyn Storage, id: NodeId, owner: &str) { + fn add_unbonded_with_owner(storage: &mut dyn Storage, id: NodeId, owner: &Addr) { storage::unbonded_mixnodes() .save( storage, id, &UnbondedMixnode { identity_key: format!("dummy{}", id), - owner: Addr::unchecked(owner), + owner: owner.clone(), proxy: None, unbonding_height: 123, }, @@ -576,15 +581,19 @@ pub(crate) mod tests { #[test] fn obeys_limits() { - let mut deps = test_helpers::init_contract(); - let _env = mock_env(); - let rng = test_helpers::test_rng(); + let mut test = TestSetup::new(); + let rng = test.rng.clone(); let limit = 2; - let owner = "owner"; + let owner = test.make_addr("owner"); - test_helpers::add_dummy_unbonded_mixnodes_with_owner(rng, deps.as_mut(), owner, 1000); + test_helpers::add_dummy_unbonded_mixnodes_with_owner( + rng, + test.deps_mut(), + &owner, + 1000, + ); let page1 = query_unbonded_mixnodes_by_owner_paged( - deps.as_ref(), + test.deps(), owner.into(), None, Some(limit), @@ -595,16 +604,20 @@ pub(crate) mod tests { #[test] fn has_default_limit() { - let mut deps = test_helpers::init_contract(); - let _env = mock_env(); - let rng = test_helpers::test_rng(); - let owner = "owner"; + let mut test = TestSetup::new(); + let rng = test.rng.clone(); + let owner = test.make_addr("owner"); - test_helpers::add_dummy_unbonded_mixnodes_with_owner(rng, deps.as_mut(), owner, 1000); + test_helpers::add_dummy_unbonded_mixnodes_with_owner( + rng, + test.deps_mut(), + &owner, + 1000, + ); // query without explicitly setting a limit let page1 = - query_unbonded_mixnodes_by_owner_paged(deps.as_ref(), owner.into(), None, None) + query_unbonded_mixnodes_by_owner_paged(test.deps(), owner.into(), None, None) .unwrap(); assert_eq!( @@ -615,17 +628,21 @@ pub(crate) mod tests { #[test] fn has_max_limit() { - let mut deps = test_helpers::init_contract(); - let _env = mock_env(); - let rng = test_helpers::test_rng(); - let owner = "owner"; + let mut test = TestSetup::new(); + let rng = test.rng.clone(); + let owner = test.make_addr("owner"); - test_helpers::add_dummy_unbonded_mixnodes_with_owner(rng, deps.as_mut(), owner, 1000); + test_helpers::add_dummy_unbonded_mixnodes_with_owner( + rng, + test.deps_mut(), + &owner, + 1000, + ); // query with a crazily high limit in an attempt to use too many resources let crazy_limit = 1000; let page1 = query_unbonded_mixnodes_by_owner_paged( - deps.as_ref(), + test.deps(), owner.into(), None, Some(crazy_limit), @@ -642,14 +659,15 @@ pub(crate) mod tests { #[test] fn pagination_works() { // as we add mixnodes, we're always inserting them in ascending manner due to monotonically increasing id - let mut deps = test_helpers::init_contract(); - let owner = "owner"; - add_unbonded_with_owner(deps.as_mut().storage, 1, owner); + let mut test = TestSetup::new(); + let owner = test.make_addr("owner"); + + add_unbonded_with_owner(test.storage_mut(), 1, &owner); let per_page = 2; let page1 = query_unbonded_mixnodes_by_owner_paged( - deps.as_ref(), - owner.into(), + test.deps(), + owner.to_string(), None, Some(per_page), ) @@ -659,24 +677,24 @@ pub(crate) mod tests { assert_eq!(1, page1.nodes.len()); // save another - add_unbonded_with_owner(deps.as_mut().storage, 2, owner); + add_unbonded_with_owner(test.storage_mut(), 2, &owner); // page1 should have 2 results on it let page1 = query_unbonded_mixnodes_by_owner_paged( - deps.as_ref(), - owner.into(), + test.deps(), + owner.to_string(), None, Some(per_page), ) .unwrap(); assert_eq!(2, page1.nodes.len()); - add_unbonded_with_owner(deps.as_mut().storage, 3, owner); + add_unbonded_with_owner(test.storage_mut(), 3, &owner); // page1 still has the same 2 results let another_page1 = query_unbonded_mixnodes_by_owner_paged( - deps.as_ref(), - owner.into(), + test.deps(), + owner.to_string(), None, Some(per_page), ) @@ -687,8 +705,8 @@ pub(crate) mod tests { // retrieving the next page should start after the last key on this page let start_after = page1.start_next_after.unwrap(); let page2 = query_unbonded_mixnodes_by_owner_paged( - deps.as_ref(), - owner.into(), + test.deps(), + owner.to_string(), Some(start_after), Some(per_page), ) @@ -697,10 +715,10 @@ pub(crate) mod tests { assert_eq!(1, page2.nodes.len()); // save another one - add_unbonded_with_owner(deps.as_mut().storage, 4, owner); + add_unbonded_with_owner(test.storage_mut(), 4, &owner); let page2 = query_unbonded_mixnodes_by_owner_paged( - deps.as_ref(), - owner.into(), + test.deps(), + owner.to_string(), Some(start_after), Some(per_page), ) @@ -713,10 +731,11 @@ pub(crate) mod tests { #[test] fn only_retrieves_nodes_with_specific_owner() { let mut deps = test_helpers::init_contract(); - let owner1 = "owner1"; - let owner2 = "owner2"; - let owner3 = "owner3"; - let owner4 = "owner4"; + let owners = sorted_addresses(4); + let owner1 = &owners[0]; + let owner2 = &owners[1]; + let owner3 = &owners[2]; + let owner4 = &owners[3]; add_unbonded_with_owner(deps.as_mut().storage, 1, owner1); add_unbonded_with_owner(deps.as_mut().storage, 2, owner1); @@ -772,7 +791,7 @@ pub(crate) mod tests { let res5 = query_unbonded_mixnodes_by_owner_paged( deps.as_ref(), - "doesnt-exist".into(), + deps.api.addr_make("doesnt-exist").into(), None, None, ) @@ -808,20 +827,19 @@ pub(crate) mod tests { #[test] fn obeys_limits() { - let mut deps = test_helpers::init_contract(); - let _env = mock_env(); - let rng = test_helpers::test_rng(); + let mut test = TestSetup::new(); + let rng = test.rng.clone(); let limit = 2; let identity = "foomp123"; test_helpers::add_dummy_unbonded_mixnodes_with_identity( rng, - deps.as_mut(), + test.deps_mut(), identity, 1000, ); let page1 = query_unbonded_mixnodes_by_identity_paged( - deps.as_ref(), + test.deps(), identity.into(), None, Some(limit), @@ -832,25 +850,20 @@ pub(crate) mod tests { #[test] fn has_default_limit() { - let mut deps = test_helpers::init_contract(); - let _env = mock_env(); - let rng = test_helpers::test_rng(); + let mut test = TestSetup::new(); + let rng = test.rng.clone(); let identity = "foomp123"; test_helpers::add_dummy_unbonded_mixnodes_with_identity( rng, - deps.as_mut(), + test.deps_mut(), identity, 1000, ); // query without explicitly setting a limit - let page1 = query_unbonded_mixnodes_by_identity_paged( - deps.as_ref(), - identity.into(), - None, - None, - ) - .unwrap(); + let page1 = + query_unbonded_mixnodes_by_identity_paged(test.deps(), identity.into(), None, None) + .unwrap(); assert_eq!( UNBONDED_MIXNODES_DEFAULT_RETRIEVAL_LIMIT, @@ -860,13 +873,12 @@ pub(crate) mod tests { #[test] fn has_max_limit() { - let mut deps = test_helpers::init_contract(); - let _env = mock_env(); - let rng = test_helpers::test_rng(); + let mut test = TestSetup::new(); + let rng = test.rng.clone(); let identity = "foomp123"; test_helpers::add_dummy_unbonded_mixnodes_with_identity( rng, - deps.as_mut(), + test.deps_mut(), identity, 1000, ); @@ -874,7 +886,7 @@ pub(crate) mod tests { // query with a crazily high limit in an attempt to use too many resources let crazy_limit = 1000; let page1 = query_unbonded_mixnodes_by_identity_paged( - deps.as_ref(), + test.deps(), identity.into(), None, Some(crazy_limit), @@ -1038,7 +1050,7 @@ pub(crate) mod tests { let res5 = query_unbonded_mixnodes_by_owner_paged( deps.as_ref(), - "doesnt-exist".into(), + deps.api.addr_make("doesnt-exist").into(), None, None, ) @@ -1055,23 +1067,23 @@ pub(crate) mod tests { fn query_for_owned_mixnode() { let mut test = TestSetup::new(); - let address = "mix-owner".to_string(); + let address = test.make_addr("mix-owner").to_string(); // when it doesnt exist let res = query_owned_mixnode(test.deps(), address.clone()).unwrap(); assert!(res.mixnode_details.is_none()); - assert_eq!(address, res.address); + assert_eq!(address, res.address.as_str()); // when it [fully] exists - let id = test.add_legacy_mixnode(&address, None); + let id = test.add_legacy_mixnode(&Addr::unchecked(&address), None); let res = query_owned_mixnode(test.deps(), address.clone()).unwrap(); let details = res.mixnode_details.unwrap(); - assert_eq!(address, details.bond_information.owner); + assert_eq!(address, details.bond_information.owner.as_str()); assert_eq!( good_mixnode_pledge()[0], details.bond_information.original_pledge ); - assert_eq!(address, res.address); + assert_eq!(address, res.address.as_str()); // when it partially exists, i.e. case when the operator unbonded, but there are still some pending delegates // TODO: perhaps this should work slightly differently, to return the underlying mixnode rewarding? @@ -1087,7 +1099,7 @@ pub(crate) mod tests { pending_events::unbond_mixnode(test.deps_mut(), &mock_env(), 123, id).unwrap(); let res = query_owned_mixnode(test.deps(), address.clone()).unwrap(); assert!(res.mixnode_details.is_none()); - assert_eq!(address, res.address); + assert_eq!(address, res.address.as_str()); } #[test] @@ -1100,7 +1112,7 @@ pub(crate) mod tests { assert_eq!(42, res.mix_id); // it exists - let mix_id = test.add_legacy_mixnode("foomp", None); + let mix_id = test.add_legacy_mixnode(&test.make_addr("foomp"), None); let res = query_mixnode_details(test.deps(), mix_id).unwrap(); let details = res.mixnode_details.unwrap(); assert_eq!(mix_id, details.bond_information.mix_id); @@ -1122,8 +1134,8 @@ pub(crate) mod tests { assert!(res.is_none()); // it exists - let mix_id = test.add_legacy_mixnode("owner", None); - // this was already tested to be working : ) + let mix_id = test.add_legacy_mixnode(&test.make_addr("owner"), None); + // this was already tested to be working let expected = query_mixnode_details(test.deps(), mix_id) .unwrap() .mixnode_details @@ -1145,7 +1157,7 @@ pub(crate) mod tests { assert!(res.rewarding_details.is_none()); assert_eq!(42, res.mix_id); - let mix_id = test.add_legacy_mixnode("foomp", None); + let mix_id = test.add_legacy_mixnode(&test.make_addr("foomp"), None); let res = query_mixnode_rewarding_details(test.deps(), mix_id).unwrap(); let details = res.rewarding_details.unwrap(); assert_eq!(fixtures::node_cost_params_fixture(), details.cost_params); @@ -1156,7 +1168,7 @@ pub(crate) mod tests { fn query_for_unbonded_mixnode() { let mut test = TestSetup::new(); - let sender = "mix-owner"; + let sender = test.make_addr("mix-owner"); // no node under this id let res = query_unbonded_mixnode(test.deps(), 42).unwrap(); @@ -1164,7 +1176,7 @@ pub(crate) mod tests { assert_eq!(42, res.mix_id); // add and unbond the mixnode - let mix_id = test.add_legacy_mixnode(sender, None); + let mix_id = test.add_legacy_mixnode(&sender, None); pending_events::unbond_mixnode(test.deps_mut(), &mock_env(), 123, mix_id).unwrap(); let res = query_unbonded_mixnode(test.deps(), mix_id).unwrap(); @@ -1187,7 +1199,7 @@ pub(crate) mod tests { .unwrap(); let saturation_point = rewarding_params.interval.stake_saturation_point; - let mix_id = test.add_legacy_mixnode("foomp", None); + let mix_id = test.add_legacy_mixnode(&test.make_addr("foomp"), None); // below saturation point // there's only the base pledge without any delegation diff --git a/contracts/mixnet/src/mixnodes/storage.rs b/contracts/mixnet/src/mixnodes/storage.rs index 8344926b7e1..22a6e8249d6 100644 --- a/contracts/mixnet/src/mixnodes/storage.rs +++ b/contracts/mixnet/src/mixnodes/storage.rs @@ -32,7 +32,7 @@ impl IndexList for UnbondedMixnodeIndex<'_> { } pub(crate) fn unbonded_mixnodes<'a>( -) -> IndexedMap<'a, NodeId, UnbondedMixnode, UnbondedMixnodeIndex<'a>> { +) -> IndexedMap> { let indexes = UnbondedMixnodeIndex { owner: MultiIndex::new( |_pk, d| d.owner.clone(), @@ -49,11 +49,11 @@ pub(crate) fn unbonded_mixnodes<'a>( } pub(crate) struct MixnodeBondIndex<'a> { - pub(crate) owner: UniqueIndex<'a, Addr, MixNodeBond>, + pub(crate) owner: UniqueIndex<'a, Addr, MixNodeBond, ()>, - pub(crate) identity_key: UniqueIndex<'a, IdentityKey, MixNodeBond>, + pub(crate) identity_key: UniqueIndex<'a, IdentityKey, MixNodeBond, ()>, - pub(crate) sphinx_key: UniqueIndex<'a, SphinxKey, MixNodeBond>, + pub(crate) sphinx_key: UniqueIndex<'a, SphinxKey, MixNodeBond, ()>, } // IndexList is just boilerplate code for fetching a struct's indexes @@ -67,7 +67,7 @@ impl IndexList for MixnodeBondIndex<'_> { } // mixnode_bonds() is the storage access function. -pub(crate) fn mixnode_bonds<'a>() -> IndexedMap<'a, NodeId, MixNodeBond, MixnodeBondIndex<'a>> { +pub(crate) fn mixnode_bonds<'a>() -> IndexedMap> { let indexes = MixnodeBondIndex { owner: UniqueIndex::new(|d| d.owner.clone(), MIXNODES_OWNER_IDX_NAMESPACE), identity_key: UniqueIndex::new( diff --git a/contracts/mixnet/src/mixnodes/transactions.rs b/contracts/mixnet/src/mixnodes/transactions.rs index 7541de10b4a..3daffbfb151 100644 --- a/contracts/mixnet/src/mixnodes/transactions.rs +++ b/contracts/mixnet/src/mixnodes/transactions.rs @@ -266,7 +266,7 @@ pub mod tests { use crate::support::tests::fixtures::{good_mixnode_pledge, TEST_COIN_DENOM}; use crate::support::tests::test_helpers::TestSetup; use crate::support::tests::{fixtures, test_helpers}; - use cosmwasm_std::testing::mock_info; + use cosmwasm_std::testing::message_info; use cosmwasm_std::{Addr, Order, StdResult, Uint128}; use mixnet_contract_common::mixnode::PendingMixNodeChanges; use mixnet_contract_common::nym_node::Role; @@ -277,15 +277,15 @@ pub mod tests { let mut test = TestSetup::new(); let env = test.env(); - let sender = "alice"; - let minimum_pledge = minimum_node_pledge(test.deps().storage).unwrap(); + let sender = test.make_addr("alice"); + let minimum_pledge = minimum_node_pledge(test.deps().storage)?; let mut insufficient_pledge = minimum_pledge.clone(); insufficient_pledge.amount -= Uint128::new(1000); // if we don't send enough funds - let info = mock_info(sender, &[insufficient_pledge.clone()]); + let info = message_info(&sender, &[insufficient_pledge.clone()]); let (mixnode, sig, _) = - test.mixnode_with_signature(sender, Some(vec![insufficient_pledge.clone()])); + test.mixnode_with_signature(&sender, Some(vec![insufficient_pledge.clone()])); let cost_params = fixtures::node_cost_params_fixture(); // we are informed that we didn't send enough funds @@ -306,10 +306,10 @@ pub mod tests { ); // if the signature provided is invalid, the bonding also fails - let info = mock_info(sender, &[minimum_pledge]); + let info = message_info(&sender, &[minimum_pledge]); // if there was already a mixnode bonded by particular user - test.add_legacy_mixnode(sender, None); + test.add_legacy_mixnode(&sender, None); // it fails let result = try_add_mixnode( @@ -323,12 +323,12 @@ pub mod tests { assert_eq!(Err(MixnetContractError::AlreadyOwnsMixnode), result); // the same holds if the user already owns a gateway - let sender2 = "gateway-owner"; + let sender2 = test.make_addr("gateway-owner"); - test.add_legacy_gateway(sender2, None); + test.add_legacy_gateway(&sender2, None); - let info = mock_info(sender2, &tests::fixtures::good_mixnode_pledge()); - let (mixnode, sig, _) = test.mixnode_with_signature(sender2, None); + let info = message_info(&sender2, &good_mixnode_pledge()); + let (mixnode, sig, _) = test.mixnode_with_signature(&sender2, None); let result = try_add_mixnode( test.deps_mut(), @@ -342,7 +342,7 @@ pub mod tests { // but after he unbonds it, it's all fine again let msg = ExecuteMsg::UnbondGateway {}; - execute(test.deps_mut(), env.clone(), info.clone(), msg).unwrap(); + execute(test.deps_mut(), env.clone(), info.clone(), msg)?; let result = try_add_mixnode( test.deps_mut(), @@ -356,8 +356,7 @@ pub mod tests { // and the node has been added as a nym-node let nym_node = - get_node_details_by_identity(test.deps().storage, mixnode.identity_key.clone()) - .unwrap() + get_node_details_by_identity(test.deps().storage, mixnode.identity_key.clone())? .unwrap(); assert_eq!(nym_node.bond_information.owner, info.sender); @@ -366,8 +365,7 @@ pub mod tests { assert!(maybe_legacy.is_none()); // make sure we got assigned the next id (note: we have already bonded a mixnode and a gateway before in this test) - let bond = - must_get_node_bond_by_owner(test.deps().storage, &Addr::unchecked(sender2)).unwrap(); + let bond = must_get_node_bond_by_owner(test.deps().storage, &Addr::unchecked(sender2))?; assert_eq!(3, bond.node_id); Ok(()) @@ -378,11 +376,11 @@ pub mod tests { let mut test = TestSetup::new(); let env = test.env(); - let sender = "alice"; + let sender = test.make_addr("alice"); let pledge = good_mixnode_pledge(); - let info = mock_info(sender, pledge.as_ref()); + let info = message_info(&sender, pledge.as_ref()); - let (mixnode, signature, _) = test.mixnode_with_signature(sender, Some(pledge.clone())); + let (mixnode, signature, _) = test.mixnode_with_signature(&sender, Some(pledge.clone())); // the above using cost params fixture let cost_params = fixtures::node_cost_params_fixture(); @@ -403,7 +401,7 @@ pub mod tests { let mut different_pledge = pledge.clone(); different_pledge[0].amount += Uint128::new(12345); - let info = mock_info(sender, different_pledge.as_ref()); + let info = message_info(&sender, different_pledge.as_ref()); let res = try_add_mixnode( test.deps_mut(), env.clone(), @@ -414,7 +412,7 @@ pub mod tests { ); assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature)); - let other_sender = mock_info("another-sender", pledge.as_ref()); + let other_sender = message_info(&test.make_addr("another-sender"), pledge.as_ref()); let res = try_add_mixnode( test.deps_mut(), env.clone(), @@ -426,10 +424,9 @@ pub mod tests { assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature)); // trying to reuse the same signature for another bonding fails (because nonce doesn't match!) - let info = mock_info(sender, pledge.as_ref()); + let info = message_info(&sender, pledge.as_ref()); let current_nonce = - signing_storage::get_signing_nonce(test.deps().storage, Addr::unchecked(sender)) - .unwrap(); + signing_storage::get_signing_nonce(test.deps().storage, sender.clone()).unwrap(); assert_eq!(0, current_nonce); let res = try_add_mixnode( test.deps_mut(), @@ -441,8 +438,7 @@ pub mod tests { ); assert!(res.is_ok()); let updated_nonce = - signing_storage::get_signing_nonce(test.deps().storage, Addr::unchecked(sender)) - .unwrap(); + signing_storage::get_signing_nonce(test.deps().storage, sender.clone()).unwrap(); assert_eq!(1, updated_nonce); test.immediately_unbond_node(1); @@ -466,10 +462,10 @@ pub mod tests { for bad_state in bad_states { let mut test = TestSetup::new(); let env = test.env(); - let owner = "alice"; - let info = mock_info(owner, &[]); + let owner = test.make_addr("alice"); + let info = message_info(&owner, &[]); - test.add_legacy_mixnode(owner, None); + test.add_legacy_mixnode(&owner, None); let mut status = EpochStatus::new(test.rewarding_validator().sender); status.state = bad_state; @@ -488,19 +484,19 @@ pub mod tests { let mut test = TestSetup::new(); let env = test.env(); - let owner = "alice"; - let info = mock_info(owner, &[]); + let owner = test.make_addr("alice"); + let info = message_info(&owner, &[]); // trying to remove your mixnode fails if you never had one in the first place let res = try_remove_mixnode(test.deps_mut(), env.clone(), info.clone()); assert_eq!( res, Err(MixnetContractError::NoAssociatedMixNodeBond { - owner: Addr::unchecked(owner) + owner: owner.clone(), }) ); - let mix_id = test.add_legacy_mixnode(owner, None); + let mix_id = test.add_legacy_mixnode(&owner, None); // "normal" unbonding succeeds and unbonding event is pushed to the pending epoch events let res = try_remove_mixnode(test.deps_mut(), env.clone(), info.clone()); @@ -528,10 +524,10 @@ pub mod tests { let env = test.env(); // prior increase - let owner = "mix-owner1"; - test.add_legacy_mixnode(owner, None); + let owner = test.make_addr("mix-owner1"); + test.add_legacy_mixnode(&owner, None); - let sender = mock_info(owner, &[test.coin(1000)]); + let sender = message_info(&owner, &[test.coin(1000)]); try_increase_pledge(test.deps_mut(), env.clone(), sender.clone()).unwrap(); let res = try_remove_mixnode(test.deps_mut(), env.clone(), sender); @@ -543,13 +539,13 @@ pub mod tests { ); // prior decrease - let owner = "mix-owner2"; - let node_id = test.add_legacy_mixnode(owner, Some(Uint128::new(10000000000))); + let owner = test.make_addr("mix-owner2"); + let node_id = test.add_legacy_mixnode(&owner, Some(Uint128::new(10000000000))); let details = test.mixnode_by_id(node_id).unwrap(); let amount = test.coin(1000); try_decrease_mixnode_pledge(test.deps_mut(), env.clone(), amount, details).unwrap(); - let sender = mock_info(owner, &[test.coin(1000)]); + let sender = message_info(&owner, &[test.coin(1000)]); let res = try_remove_mixnode(test.deps_mut(), env.clone(), sender); assert_eq!( res, @@ -559,8 +555,8 @@ pub mod tests { ); // artificial event - let owner = "mix-owner3"; - let mix_id = test.add_legacy_mixnode(owner, None); + let owner = test.make_addr("mix-owner3"); + let mix_id = test.add_legacy_mixnode(&owner, None); let pending_change = PendingMixNodeChanges { pledge_change: Some(1234), cost_params_change: None, @@ -569,7 +565,7 @@ pub mod tests { .save(test.deps_mut().storage, mix_id, &pending_change) .unwrap(); - let sender = mock_info(owner, &[test.coin(1000)]); + let sender = message_info(&owner, &[test.coin(1000)]); let res = try_remove_mixnode(test.deps_mut(), env, sender); assert_eq!( res, @@ -584,8 +580,8 @@ pub mod tests { let mut test = TestSetup::new(); let env = test.env(); - let owner = "alice"; - let info = mock_info(owner, &[]); + let owner = test.make_addr("alice"); + let info = message_info(&owner, &[]); let update = MixNodeConfigUpdate { host: "1.1.1.1:1234".to_string(), mix_port: 1234, @@ -599,11 +595,11 @@ pub mod tests { assert_eq!( res, Err(MixnetContractError::NoAssociatedMixNodeBond { - owner: Addr::unchecked(owner) + owner: owner.clone() }) ); - let mix_id = test.add_legacy_mixnode(owner, None); + let mix_id = test.add_legacy_mixnode(&owner, None); // "normal" update succeeds let res = try_update_mixnode_config(test.deps_mut(), info.clone(), update.clone()); @@ -639,15 +635,15 @@ pub mod tests { let update = NodeCostParams { profit_margin_percent: Percent::from_percentage_value(42).unwrap(), - interval_operating_cost: Coin::new(12345678, TEST_COIN_DENOM), + interval_operating_cost: Coin::new(12345678u32, TEST_COIN_DENOM), }; for bad_state in bad_states { let mut test = TestSetup::new(); let env = test.env(); - let owner = "alice"; + let owner = test.make_addr("alice"); - let node_id = test.add_legacy_mixnode(owner, None); + let node_id = test.add_legacy_mixnode(&owner, None); let details = test.mixnode_by_id(node_id).unwrap(); let mut status = EpochStatus::new(test.rewarding_validator().sender); @@ -672,14 +668,14 @@ pub mod tests { let mut test = TestSetup::new(); let env = test.env(); - let owner = "alice"; - let info = mock_info(owner, &[]); + let owner = test.make_addr("alice"); + let info = message_info(&owner, &[]); let update = NodeCostParams { profit_margin_percent: Percent::from_percentage_value(42).unwrap(), - interval_operating_cost: Coin::new(12345678, TEST_COIN_DENOM), + interval_operating_cost: Coin::new(12345678u32, TEST_COIN_DENOM), }; - let node_id = test.add_legacy_mixnode(owner, None); + let node_id = test.add_legacy_mixnode(&owner, None); let details = test.mixnode_by_id(node_id).unwrap(); // "normal" update succeeds @@ -750,13 +746,21 @@ pub mod tests { .public_key() .to_base58_string(); - let sig1 = - test.mixnode_bonding_signature(keypair1.private_key(), "alice", mixnode1.clone(), None); - let sig2 = - test.mixnode_bonding_signature(keypair2.private_key(), "bob", mixnode2.clone(), None); + let sig1 = test.mixnode_bonding_signature( + keypair1.private_key(), + &test.make_addr("alice"), + mixnode1.clone(), + None, + ); + let sig2 = test.mixnode_bonding_signature( + keypair2.private_key(), + &test.make_addr("bob"), + mixnode2.clone(), + None, + ); - let info_alice = mock_info("alice", &tests::fixtures::good_mixnode_pledge()); - let info_bob = mock_info("bob", &tests::fixtures::good_mixnode_pledge()); + let info_alice = message_info(&test.make_addr("alice"), &good_mixnode_pledge()); + let info_bob = message_info(&test.make_addr("bob"), &good_mixnode_pledge()); assert!(try_add_mixnode( test.deps_mut(), @@ -795,14 +799,14 @@ pub mod tests { for bad_state in bad_states { let mut test = TestSetup::new(); let env = test.env(); - let owner = "mix-owner"; + let owner = test.make_addr("mix-owner"); let mut status = EpochStatus::new(test.rewarding_validator().sender); status.state = bad_state; interval_storage::save_current_epoch_status(test.deps_mut().storage, &status) .unwrap(); - let node_id = test.add_legacy_mixnode(owner, None); + let node_id = test.add_legacy_mixnode(&owner, None); let details = test.mixnode_by_id(node_id).unwrap(); let increase = test.coins(1000); let res = try_increase_mixnode_pledge(test.deps_mut(), env, increase, details); @@ -843,9 +847,9 @@ pub mod tests { fn is_not_allowed_if_no_tokens_were_sent() { let mut test = TestSetup::new(); let env = test.env(); - let owner = "mix-owner"; + let owner = test.make_addr("mix-owner"); - let node_id = test.add_legacy_mixnode(owner, None); + let node_id = test.add_legacy_mixnode(&owner, None); let details = test.mixnode_by_id(node_id).unwrap(); let sender_empty = Vec::new(); @@ -875,8 +879,8 @@ pub mod tests { let env = test.env(); // prior increase - let owner = "mix-owner1"; - let node_id = test.add_legacy_mixnode(owner, None); + let owner = test.make_addr("mix-owner1"); + let node_id = test.add_legacy_mixnode(&owner, None); let details = test.mixnode_by_id(node_id).unwrap(); let sender = test.coins(1000); try_increase_mixnode_pledge( @@ -897,8 +901,8 @@ pub mod tests { ); // prior decrease - let owner = "mix-owner2"; - let node_id = test.add_legacy_mixnode(owner, Some(Uint128::new(10000000000))); + let owner = test.make_addr("mix-owner2"); + let node_id = test.add_legacy_mixnode(&owner, Some(Uint128::new(10000000000))); let details = test.mixnode_by_id(node_id).unwrap(); let amount = test.coin(1000); @@ -920,8 +924,8 @@ pub mod tests { fn with_valid_information_creates_pending_event() { let mut test = TestSetup::new(); let env = test.env(); - let owner = "mix-owner"; - let mix_id = test.add_legacy_mixnode(owner, None); + let owner = test.make_addr("mix-owner"); + let mix_id = test.add_legacy_mixnode(&owner, None); let details = test.mixnode_by_id(mix_id).unwrap(); let events = test.pending_epoch_events(); @@ -963,10 +967,10 @@ pub mod tests { for bad_state in bad_states { let mut test = TestSetup::new(); let env = test.env(); - let owner = "mix-owner"; + let owner = test.make_addr("mix-owner"); let decrease = test.coin(1000); - let node_id = test.add_legacy_mixnode(owner, Some(Uint128::new(100_000_000_000))); + let node_id = test.add_legacy_mixnode(&owner, Some(Uint128::new(100_000_000_000))); let details = test.mixnode_by_id(node_id).unwrap(); let mut status = EpochStatus::new(test.rewarding_validator().sender); @@ -1012,12 +1016,12 @@ pub mod tests { fn is_not_allowed_if_it_would_result_going_below_minimum_pledge() { let mut test = TestSetup::new(); let env = test.env(); - let owner = "mix-owner"; + let owner = test.make_addr("mix-owner"); let minimum_pledge = minimum_node_pledge(test.deps().storage).unwrap(); let pledge_amount = minimum_pledge.amount + Uint128::new(100); let pledged = test.coin(pledge_amount.u128()); - let node_id = test.add_legacy_mixnode(owner, Some(pledge_amount)); + let node_id = test.add_legacy_mixnode(&owner, Some(pledge_amount)); let details = test.mixnode_by_id(node_id).unwrap(); let invalid_decrease = test.coin(150); @@ -1051,8 +1055,8 @@ pub mod tests { let stake = Uint128::new(100_000_000_000); let decrease = test.coin(0); - let owner = "mix-owner"; - let node_id = test.add_legacy_mixnode(owner, Some(stake)); + let owner = test.make_addr("mix-owner"); + let node_id = test.add_legacy_mixnode(&owner, Some(stake)); let details = test.mixnode_by_id(node_id).unwrap(); let res = try_decrease_mixnode_pledge(test.deps_mut(), env, decrease, details); @@ -1067,8 +1071,8 @@ pub mod tests { let decrease = test.coin(1000); // prior increase - let owner = "mix-owner1"; - let node_id = test.add_legacy_mixnode(owner, Some(stake)); + let owner = test.make_addr("mix-owner1"); + let node_id = test.add_legacy_mixnode(&owner, Some(stake)); let details = test.mixnode_by_id(node_id).unwrap(); let sender = test.coins(1000); @@ -1090,8 +1094,8 @@ pub mod tests { ); // prior decrease - let owner = "mix-owner2"; - let node_id = test.add_legacy_mixnode(owner, Some(stake)); + let owner = test.make_addr("mix-owner2"); + let node_id = test.add_legacy_mixnode(&owner, Some(stake)); let details = test.mixnode_by_id(node_id).unwrap(); let amount = test.coin(1000); try_decrease_mixnode_pledge(test.deps_mut(), env.clone(), amount, details).unwrap(); @@ -1111,8 +1115,8 @@ pub mod tests { ); // artificial event - let owner = "mix-owner3"; - let mix_id = test.add_legacy_mixnode(owner, Some(stake)); + let owner = test.make_addr("mix-owner3"); + let mix_id = test.add_legacy_mixnode(&owner, Some(stake)); let pending_change = PendingMixNodeChanges { pledge_change: Some(1234), cost_params_change: None, @@ -1140,8 +1144,8 @@ pub mod tests { let stake = Uint128::new(100_000_000_000); let decrease = test.coin(1000); - let owner = "mix-owner"; - let mix_id = test.add_legacy_mixnode(owner, Some(stake)); + let owner = test.make_addr("mix-owner"); + let mix_id = test.add_legacy_mixnode(&owner, Some(stake)); let details = test.mixnode_by_id(mix_id).unwrap(); let events = test.pending_epoch_events(); diff --git a/contracts/mixnet/src/nodes/storage/mod.rs b/contracts/mixnet/src/nodes/storage/mod.rs index 63584010a66..41e7aea0335 100644 --- a/contracts/mixnet/src/nodes/storage/mod.rs +++ b/contracts/mixnet/src/nodes/storage/mod.rs @@ -70,9 +70,9 @@ pub mod rewarded_set { } pub(crate) struct NymNodeBondIndex<'a> { - pub(crate) owner: UniqueIndex<'a, Addr, NymNodeBond>, + pub(crate) owner: UniqueIndex<'a, Addr, NymNodeBond, ()>, - pub(crate) identity_key: UniqueIndex<'a, IdentityKey, NymNodeBond>, + pub(crate) identity_key: UniqueIndex<'a, IdentityKey, NymNodeBond, ()>, } impl IndexList for NymNodeBondIndex<'_> { @@ -83,7 +83,7 @@ impl IndexList for NymNodeBondIndex<'_> { } // nym_nodes() is the storage access function. -pub(crate) fn nym_nodes<'a>() -> IndexedMap<'a, NodeId, NymNodeBond, NymNodeBondIndex<'a>> { +pub(crate) fn nym_nodes<'a>() -> IndexedMap> { let indexes = NymNodeBondIndex { owner: UniqueIndex::new(|d| d.owner.clone(), NYMNODE_OWNER_IDX_NAMESPACE), identity_key: UniqueIndex::new( @@ -111,7 +111,7 @@ impl IndexList for UnbondedNymNodeIndex<'_> { } pub(crate) fn unbonded_nym_nodes<'a>( -) -> IndexedMap<'a, NodeId, UnbondedNymNode, UnbondedNymNodeIndex<'a>> { +) -> IndexedMap> { let indexes = UnbondedNymNodeIndex { owner: MultiIndex::new( |_pk, d| d.owner.clone(), diff --git a/contracts/mixnet/src/rewards/helpers.rs b/contracts/mixnet/src/rewards/helpers.rs index fba530e3340..d5e334387f4 100644 --- a/contracts/mixnet/src/rewards/helpers.rs +++ b/contracts/mixnet/src/rewards/helpers.rs @@ -258,7 +258,7 @@ mod tests { let pledge = Uint128::new(250_000_000); let pledge_dec = 250_000_000u32.into_base_decimal().unwrap(); - let mix_id = test.add_legacy_mixnode("mix-owner", Some(pledge)); + let mix_id = test.add_legacy_mixnode(&test.make_addr("mix-owner"), Some(pledge)); let active_params = test.active_node_params(100.0); // no rewards @@ -297,12 +297,12 @@ mod tests { let delegation_amount = Uint128::new(2_500_000_000); let delegation_dec = 2_500_000_000_u32.into_base_decimal().unwrap(); - let mix_id = test.add_legacy_mixnode("mix-owner", None); - let delegator = "delegator"; - test.add_immediate_delegation(delegator, delegation_amount, mix_id); + let mix_id = test.add_legacy_mixnode(&test.make_addr("mix-owner"), None); + let delegator = test.make_addr("delegator"); + test.add_immediate_delegation(&delegator, delegation_amount, mix_id); // no rewards - let delegation = test.delegation(mix_id, delegator, &None); + let delegation = test.delegation(mix_id, &delegator, &None); let mix_rewarding = test.mix_rewarding(mix_id); let res = withdraw_delegator_reward(test.deps_mut().storage, delegation, mix_rewarding).unwrap(); @@ -318,7 +318,7 @@ mod tests { test.skip_to_next_epoch_end(); let dist3 = test.reward_with_distribution_ignore_state(mix_id, active_params); - let delegation_pre = test.delegation(mix_id, delegator, &None); + let delegation_pre = test.delegation(mix_id, &delegator, &None); let mix_rewarding = test.mix_rewarding(mix_id); let res = withdraw_delegator_reward( test.deps_mut().storage, @@ -331,7 +331,7 @@ mod tests { let updated = test.mix_rewarding(mix_id); assert_decimals(updated.delegates, delegation_dec); - let delegation_post = test.delegation(mix_id, delegator, &None); + let delegation_post = test.delegation(mix_id, &delegator, &None); assert_ne!( delegation_pre.cumulative_reward_ratio, delegation_post.cumulative_reward_ratio diff --git a/contracts/mixnet/src/rewards/queries.rs b/contracts/mixnet/src/rewards/queries.rs index 64f55bcec95..f126950877e 100644 --- a/contracts/mixnet/src/rewards/queries.rs +++ b/contracts/mixnet/src/rewards/queries.rs @@ -270,14 +270,14 @@ mod tests { use super::*; use crate::mixnodes::transactions::try_remove_mixnode; use crate::support::tests::fixtures::TEST_COIN_DENOM; - use cosmwasm_std::testing::mock_info; + use cosmwasm_std::testing::message_info; #[test] fn for_non_existent_node() { let test = TestSetup::new(); - let owner = "mix-owner"; + let owner = test.make_addr("mix-owner"); - let res = query_pending_operator_reward(test.deps(), owner.into()).unwrap(); + let res = query_pending_operator_reward(test.deps(), owner.to_string()).unwrap(); let res2 = query_pending_mixnode_operator_reward(test.deps(), 42).unwrap(); assert_eq!(res, res2); @@ -290,12 +290,12 @@ mod tests { #[test] fn for_unrewarded_node() { let mut test = TestSetup::new(); - let owner = "mix-owner"; + let owner = test.make_addr("mix-owner"); let initial_stake = Uint128::new(1_000_000_000_000); - let mix_id = test.add_rewarded_legacy_mixnode(owner, Some(initial_stake)); + let mix_id = test.add_rewarded_legacy_mixnode(&owner, Some(initial_stake)); - let res = query_pending_operator_reward(test.deps(), owner.into()).unwrap(); + let res = query_pending_operator_reward(test.deps(), owner.to_string()).unwrap(); let res2 = query_pending_mixnode_operator_reward(test.deps(), mix_id).unwrap(); assert_eq!(res, res2); @@ -310,9 +310,9 @@ mod tests { #[test] fn for_node_with_pending_reward() { let mut test = TestSetup::new(); - let owner = "mix-owner"; + let owner = test.make_addr("mix-owner"); let initial_stake = Uint128::new(1_000_000_000_000); - let mix_id = test.add_rewarded_legacy_mixnode(owner, Some(initial_stake)); + let mix_id = test.add_rewarded_legacy_mixnode(&owner, Some(initial_stake)); let active_params = test.active_node_params(100.); test.skip_to_next_epoch_end(); @@ -322,7 +322,7 @@ mod tests { let dist = test.reward_with_distribution_ignore_state(mix_id, active_params); total_earned += dist.operator; - let res = query_pending_operator_reward(test.deps(), owner.into()).unwrap(); + let res = query_pending_operator_reward(test.deps(), owner.to_string()).unwrap(); let res2 = query_pending_mixnode_operator_reward(test.deps(), mix_id).unwrap(); assert_eq!(res, res2); @@ -339,7 +339,7 @@ mod tests { let dist = test.reward_with_distribution_ignore_state(mix_id, active_params); total_earned += dist.operator; - let res = query_pending_operator_reward(test.deps(), owner.into()).unwrap(); + let res = query_pending_operator_reward(test.deps(), owner.to_string()).unwrap(); let res2 = query_pending_mixnode_operator_reward(test.deps(), mix_id).unwrap(); assert_eq!(res, res2); @@ -355,9 +355,9 @@ mod tests { #[test] fn for_node_that_is_unbonding() { let mut test = TestSetup::new(); - let owner = "mix-owner"; + let owner = test.make_addr("mix-owner"); let initial_stake = Uint128::new(1_000_000_000_000); - let mix_id = test.add_rewarded_legacy_mixnode(owner, Some(initial_stake)); + let mix_id = test.add_rewarded_legacy_mixnode(&owner, Some(initial_stake)); let active_params = test.active_node_params(100.); test.skip_to_next_epoch_end(); @@ -367,11 +367,11 @@ mod tests { let dist = test.reward_with_distribution_ignore_state(mix_id, active_params); total_earned += dist.operator; - let sender = mock_info(owner, &[]); + let sender = message_info(&owner, &[]); let env = test.env(); try_remove_mixnode(test.deps_mut(), env, sender).unwrap(); - let res = query_pending_operator_reward(test.deps(), owner.into()).unwrap(); + let res = query_pending_operator_reward(test.deps(), owner.to_string()).unwrap(); let res2 = query_pending_mixnode_operator_reward(test.deps(), mix_id).unwrap(); assert_eq!(res, res2); @@ -385,9 +385,9 @@ mod tests { #[test] fn for_node_that_has_unbonded() { let mut test = TestSetup::new(); - let owner = "mix-owner"; + let owner = test.make_addr("mix-owner"); let initial_stake = Uint128::new(1_000_000_000_000); - let mix_id = test.add_rewarded_legacy_mixnode(owner, Some(initial_stake)); + let mix_id = test.add_rewarded_legacy_mixnode(&owner, Some(initial_stake)); let active_params = test.active_node_params(100.); test.skip_to_next_epoch_end(); @@ -395,12 +395,12 @@ mod tests { test.reward_with_distribution_ignore_state(mix_id, active_params); - let sender = mock_info(owner, &[]); + let sender = message_info(&owner, &[]); let env = test.env(); try_remove_mixnode(test.deps_mut(), env, sender).unwrap(); test.execute_all_pending_events(); - let res = query_pending_operator_reward(test.deps(), owner.into()).unwrap(); + let res = query_pending_operator_reward(test.deps(), owner.to_string()).unwrap(); let res2 = query_pending_mixnode_operator_reward(test.deps(), mix_id).unwrap(); assert_eq!(res, res2); @@ -420,12 +420,12 @@ mod tests { use crate::rewards::transactions::try_withdraw_delegator_reward; use crate::support::tests::fixtures::TEST_COIN_DENOM; use crate::support::tests::test_helpers::get_bank_send_msg; - use cosmwasm_std::testing::mock_info; + use cosmwasm_std::testing::message_info; #[test] fn for_non_existent_delegation() { let test = TestSetup::new(); - let delegator = "delegator"; + let delegator = test.make_addr("delegator"); let res = query_pending_delegator_reward(test.deps(), delegator.into(), 42, None).unwrap(); @@ -439,15 +439,16 @@ mod tests { #[test] fn for_unrewarded_delegator() { let mut test = TestSetup::new(); - let owner = "delegator"; + let owner = test.make_addr("delegator"); let initial_stake = Uint128::new(100_000_000); - let mix_id = test - .add_rewarded_legacy_mixnode("mix-owner", Some(Uint128::new(1_000_000_000_000))); - test.add_immediate_delegation(owner, initial_stake, mix_id); + let mix_owner = test.make_addr("mix-owner"); + let mix_id = + test.add_rewarded_legacy_mixnode(&mix_owner, Some(Uint128::new(1_000_000_000_000))); + test.add_immediate_delegation(&owner, initial_stake, mix_id); - let res = - query_pending_delegator_reward(test.deps(), owner.into(), mix_id, None).unwrap(); + let res = query_pending_delegator_reward(test.deps(), owner.to_string(), mix_id, None) + .unwrap(); let expected_actual = coin(0, TEST_COIN_DENOM); @@ -460,13 +461,15 @@ mod tests { #[test] fn for_delegator_with_pending_reward() { let mut test = TestSetup::new(); - let owner = "delegator"; + let owner = test.make_addr("delegator"); let active_params = test.active_node_params(100.); let initial_stake = Uint128::new(100_000_000); - let mix_id = test - .add_rewarded_legacy_mixnode("mix-owner", Some(Uint128::new(1_000_000_000_000))); - test.add_immediate_delegation(owner, initial_stake, mix_id); + let mix_owner = test.make_addr("mix-owner"); + + let mix_id = + test.add_rewarded_legacy_mixnode(&mix_owner, Some(Uint128::new(1_000_000_000_000))); + test.add_immediate_delegation(&owner, initial_stake, mix_id); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![mix_id]); @@ -475,8 +478,8 @@ mod tests { let dist = test.reward_with_distribution_ignore_state(mix_id, active_params); total_earned += dist.delegates; - let res = - query_pending_delegator_reward(test.deps(), owner.into(), mix_id, None).unwrap(); + let res = query_pending_delegator_reward(test.deps(), owner.to_string(), mix_id, None) + .unwrap(); let expected_actual = truncate_reward(total_earned, TEST_COIN_DENOM); @@ -491,8 +494,9 @@ mod tests { let dist = test.reward_with_distribution_ignore_state(mix_id, active_params); total_earned += dist.delegates; - let res = query_pending_delegator_reward(test.deps(), owner.into(), mix_id, None) - .unwrap(); + let res = + query_pending_delegator_reward(test.deps(), owner.to_string(), mix_id, None) + .unwrap(); let expected_actual = truncate_reward(total_earned, TEST_COIN_DENOM); @@ -506,13 +510,15 @@ mod tests { #[test] fn for_node_that_is_unbonding() { let mut test = TestSetup::new(); - let owner = "delegator"; + let owner = test.make_addr("delegator"); let active_params = test.active_node_params(100.); let initial_stake = Uint128::new(100_000_000); - let mix_id = test - .add_rewarded_legacy_mixnode("mix-owner", Some(Uint128::new(1_000_000_000_000))); - test.add_immediate_delegation(owner, initial_stake, mix_id); + let mix_owner = test.make_addr("mix-owner"); + + let mix_id = + test.add_rewarded_legacy_mixnode(&mix_owner, Some(Uint128::new(1_000_000_000_000))); + test.add_immediate_delegation(&owner, initial_stake, mix_id); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![mix_id]); @@ -521,12 +527,12 @@ mod tests { let dist = test.reward_with_distribution_ignore_state(mix_id, active_params); total_earned += dist.delegates; - let sender = mock_info("mix-owner", &[]); + let sender = message_info(&mix_owner, &[]); let env = test.env(); try_remove_mixnode(test.deps_mut(), env, sender).unwrap(); - let res = - query_pending_delegator_reward(test.deps(), owner.into(), mix_id, None).unwrap(); + let res = query_pending_delegator_reward(test.deps(), owner.to_string(), mix_id, None) + .unwrap(); let expected_actual = truncate_reward(total_earned, TEST_COIN_DENOM); assert_eq!(res.amount_earned.unwrap(), expected_actual); @@ -538,13 +544,15 @@ mod tests { #[test] fn for_node_that_has_unbonded() { let mut test = TestSetup::new(); - let owner = "delegator"; + let owner = test.make_addr("delegator"); let active_params = test.active_node_params(100.); let initial_stake = Uint128::new(100_000_000); - let mix_id = test - .add_rewarded_legacy_mixnode("mix-owner", Some(Uint128::new(1_000_000_000_000))); - test.add_immediate_delegation(owner, initial_stake, mix_id); + let mix_owner = test.make_addr("mix-owner"); + + let mix_id = + test.add_rewarded_legacy_mixnode(&mix_owner, Some(Uint128::new(1_000_000_000_000))); + test.add_immediate_delegation(&owner, initial_stake, mix_id); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![mix_id]); @@ -553,13 +561,13 @@ mod tests { let dist = test.reward_with_distribution_ignore_state(mix_id, active_params); total_earned += dist.delegates; - let sender = mock_info("mix-owner", &[]); + let sender = message_info(&mix_owner, &[]); let env = test.env(); try_remove_mixnode(test.deps_mut(), env, sender).unwrap(); test.execute_all_pending_events(); - let res = - query_pending_delegator_reward(test.deps(), owner.into(), mix_id, None).unwrap(); + let res = query_pending_delegator_reward(test.deps(), owner.to_string(), mix_id, None) + .unwrap(); let expected_actual = truncate_reward(total_earned, TEST_COIN_DENOM); assert_eq!(res.amount_earned.unwrap(), expected_actual); @@ -573,16 +581,17 @@ mod tests { // we've already tested withdraw reward to calculate values correctly // even if there are multiple delegators joined at different times when the reward has to be split let mut test = TestSetup::new(); - let del1 = "delegator1"; - let del2 = "delegator2"; - let del3 = "delegator3"; - let del4 = "delegator4"; + let del1 = test.make_addr("delegator1"); + let del2 = test.make_addr("delegator2"); + let del3 = test.make_addr("delegator3"); + let del4 = test.make_addr("delegator4"); let active_params = test.active_node_params(100.); + let mix_owner = test.make_addr("mix-owner"); - let mix_id = test - .add_rewarded_legacy_mixnode("mix-owner", Some(Uint128::new(1_000_000_000_000))); - test.add_immediate_delegation(del1, 123_456_789u32, mix_id); - test.add_immediate_delegation(del2, 150_000_000u32, mix_id); + let mix_id = + test.add_rewarded_legacy_mixnode(&mix_owner, Some(Uint128::new(1_000_000_000_000))); + test.add_immediate_delegation(&del1, 123_456_789u32, mix_id); + test.add_immediate_delegation(&del2, 150_000_000u32, mix_id); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![mix_id]); @@ -592,7 +601,7 @@ mod tests { test.skip_to_next_epoch_end(); test.reward_with_distribution_ignore_state(mix_id, active_params); - test.add_immediate_delegation(del3, 500_000_000u32, mix_id); + test.add_immediate_delegation(&del3, 500_000_000u32, mix_id); test.skip_to_next_epoch_end(); let params = test.active_node_params(85.0); test.reward_with_distribution_ignore_state(mix_id, params); @@ -600,18 +609,18 @@ mod tests { let params = test.active_node_params(5.0); test.reward_with_distribution_ignore_state(mix_id, params); - test.add_immediate_delegation(del4, 5_000_000u32, mix_id); + test.add_immediate_delegation(&del4, 5_000_000u32, mix_id); test.skip_to_next_epoch_end(); test.reward_with_distribution_ignore_state(mix_id, active_params); - test.add_immediate_delegation(del2, 250_000_000u32, mix_id); + test.add_immediate_delegation(&del2, 250_000_000u32, mix_id); test.skip_to_next_epoch_end(); let params = test.active_node_params(98.0); test.reward_with_distribution_ignore_state(mix_id, params); test.skip_to_next_epoch_end(); test.reward_with_distribution_ignore_state(mix_id, active_params); - test.remove_immediate_delegation(del3, mix_id); + test.remove_immediate_delegation(&del3, mix_id); test.skip_to_next_epoch_end(); let params = test.active_node_params(98.0); test.reward_with_distribution_ignore_state(mix_id, params); @@ -619,22 +628,26 @@ mod tests { test.reward_with_distribution_ignore_state(mix_id, active_params); let pending1 = - query_pending_delegator_reward(test.deps(), del1.into(), mix_id, None).unwrap(); + query_pending_delegator_reward(test.deps(), del1.to_string(), mix_id, None) + .unwrap(); let pending2 = - query_pending_delegator_reward(test.deps(), del2.into(), mix_id, None).unwrap(); + query_pending_delegator_reward(test.deps(), del2.to_string(), mix_id, None) + .unwrap(); let pending3 = - query_pending_delegator_reward(test.deps(), del3.into(), mix_id, None).unwrap(); + query_pending_delegator_reward(test.deps(), del3.to_string(), mix_id, None) + .unwrap(); let pending4 = - query_pending_delegator_reward(test.deps(), del4.into(), mix_id, None).unwrap(); + query_pending_delegator_reward(test.deps(), del4.to_string(), mix_id, None) + .unwrap(); let actual1_res = - try_withdraw_delegator_reward(test.deps_mut(), mock_info(del1, &[]), mix_id) + try_withdraw_delegator_reward(test.deps_mut(), message_info(&del1, &[]), mix_id) .unwrap(); let (_, actual1) = get_bank_send_msg(&actual1_res).unwrap(); assert_eq!(pending1.amount_earned.unwrap(), actual1[0]); let actual2_res = - try_withdraw_delegator_reward(test.deps_mut(), mock_info(del2, &[]), mix_id) + try_withdraw_delegator_reward(test.deps_mut(), message_info(&del2, &[]), mix_id) .unwrap(); let (_, actual2) = get_bank_send_msg(&actual2_res).unwrap(); assert_eq!(pending2.amount_earned.unwrap(), actual2[0]); @@ -643,7 +656,7 @@ mod tests { assert!(pending3.amount_earned.is_none()); let actual4_res = - try_withdraw_delegator_reward(test.deps_mut(), mock_info(del4, &[]), mix_id) + try_withdraw_delegator_reward(test.deps_mut(), message_info(&del4, &[]), mix_id) .unwrap(); let (_, actual4) = get_bank_send_msg(&actual4_res).unwrap(); assert_eq!(pending4.amount_earned.unwrap(), actual4[0]); @@ -655,7 +668,7 @@ mod tests { use super::*; use crate::mixnodes::transactions::try_remove_mixnode; use crate::support::tests::fixtures::TEST_COIN_DENOM; - use cosmwasm_std::testing::mock_info; + use cosmwasm_std::testing::message_info; fn expected_current_operator( test: &TestSetup, @@ -689,15 +702,15 @@ mod tests { fn when_node_is_unbonding() { let mut test = TestSetup::new(); let initial_stake = Uint128::new(1_000_000_000_000); - let owner = "mix-owner"; - let mix_id = test.add_rewarded_legacy_mixnode(owner, Some(initial_stake)); + let owner = test.make_addr("mix-owner"); + let mix_id = test.add_rewarded_legacy_mixnode(&owner, Some(initial_stake)); let active_params = test.active_node_params(100.); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![mix_id]); test.reward_with_distribution_ignore_state(mix_id, active_params); - let sender = mock_info(owner, &[]); + let sender = message_info(&owner, &[]); let env = test.env(); try_remove_mixnode(test.deps_mut(), env, sender).unwrap(); @@ -717,15 +730,15 @@ mod tests { fn when_node_has_already_unbonded() { let mut test = TestSetup::new(); let initial_stake = Uint128::new(1_000_000_000_000); - let owner = "mix-owner"; - let mix_id = test.add_rewarded_legacy_mixnode(owner, Some(initial_stake)); + let owner = test.make_addr("mix-owner"); + let mix_id = test.add_rewarded_legacy_mixnode(&owner, Some(initial_stake)); let active_params = test.active_node_params(100.); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![mix_id]); test.reward_with_distribution_ignore_state(mix_id, active_params); - let sender = mock_info(owner, &[]); + let sender = message_info(&owner, &[]); let env = test.env(); try_remove_mixnode(test.deps_mut(), env, sender).unwrap(); test.execute_all_pending_events(); @@ -744,7 +757,8 @@ mod tests { fn when_node_is_not_in_the_rewarded_set() { let mut test = TestSetup::new(); let initial_stake = Uint128::new(1_000_000_000_000); - let mix_id = test.add_rewarded_legacy_mixnode("mix-owner", Some(initial_stake)); + let mix_owner = test.make_addr("mix-owner"); + let mix_id = test.add_rewarded_legacy_mixnode(&mix_owner, Some(initial_stake)); let active_params = test.active_node_params(100.); test.skip_to_next_epoch_end(); @@ -768,7 +782,8 @@ mod tests { fn when_estimated_performance_is_zero() { let mut test = TestSetup::new(); let initial_stake = Uint128::new(1_000_000_000_000); - let mix_id = test.add_rewarded_legacy_mixnode("mix-owner", Some(initial_stake)); + let mix_owner = test.make_addr("mix-owner"); + let mix_id = test.add_rewarded_legacy_mixnode(&mix_owner, Some(initial_stake)); let active_params = test.active_node_params(100.); test.skip_to_next_epoch_end(); @@ -791,7 +806,8 @@ mod tests { fn with_correct_parameters_matches_actual_distribution() { let mut test = TestSetup::new(); let initial_stake = Uint128::new(1_000_000_000_000); - let mix_id = test.add_rewarded_legacy_mixnode("mix-owner", Some(initial_stake)); + let mix_owner = test.make_addr("mix-owner"); + let mix_id = test.add_rewarded_legacy_mixnode(&mix_owner, Some(initial_stake)); let active_params = test.active_node_params(100.); test.skip_to_next_epoch_end(); @@ -828,12 +844,13 @@ mod tests { use super::*; use crate::mixnodes::transactions::try_remove_mixnode; use crate::support::tests::fixtures::TEST_COIN_DENOM; - use cosmwasm_std::testing::mock_info; + use cosmwasm_std::testing::message_info; + use cosmwasm_std::Addr; fn expected_current_delegator( test: &TestSetup, mix_id: NodeId, - owner: &str, + owner: &Addr, ) -> EstimatedCurrentEpochRewardResponse { let mix_rewarding = test.mix_rewarding(mix_id); let delegation = test.delegation(mix_id, owner, &None); @@ -857,16 +874,18 @@ mod tests { #[test] fn when_delegation_doesnt_exist() { let mut test = TestSetup::new(); - let mix_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let mix_owner = test.make_addr("mix-owner"); + let mix_id = test.add_rewarded_legacy_mixnode(&mix_owner, None); let active_params = test.active_node_params(100.); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![mix_id]); test.reward_with_distribution_ignore_state(mix_id, active_params); + let addr = test.make_addr("foomper"); let res = query_estimated_current_epoch_delegator_reward( test.deps(), - "foomper".into(), + addr.to_string(), mix_id, test_helpers::performance(100.0), None, @@ -879,75 +898,78 @@ mod tests { #[test] fn when_node_is_unbonding() { let mut test = TestSetup::new(); - let mix_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let mix_owner = test.make_addr("mix-owner"); + let mix_id = test.add_rewarded_legacy_mixnode(&mix_owner, None); let active_params = test.active_node_params(100.); let initial_stake = Uint128::new(1_000_000_000); - let owner = "delegator"; - test.add_immediate_delegation(owner, initial_stake, mix_id); + let owner = test.make_addr("delegator"); + test.add_immediate_delegation(&owner, initial_stake, mix_id); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![mix_id]); test.reward_with_distribution_ignore_state(mix_id, active_params); - let sender = mock_info("mix-owner", &[]); + let sender = message_info(&mix_owner, &[]); let env = test.env(); try_remove_mixnode(test.deps_mut(), env, sender).unwrap(); let res = query_estimated_current_epoch_delegator_reward( test.deps(), - owner.into(), + owner.to_string(), mix_id, test_helpers::performance(100.0), None, ) .unwrap(); - let expected = expected_current_delegator(&test, mix_id, owner); + let expected = expected_current_delegator(&test, mix_id, &owner); assert_eq!(res, expected) } #[test] fn when_node_has_already_unbonded() { let mut test = TestSetup::new(); - let mix_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let mix_owner = test.make_addr("mix-owner"); + let mix_id = test.add_rewarded_legacy_mixnode(&mix_owner, None); let active_params = test.active_node_params(100.); let initial_stake = Uint128::new(1_000_000_000); - let owner = "delegator"; - test.add_immediate_delegation(owner, initial_stake, mix_id); + let owner = test.make_addr("delegator"); + test.add_immediate_delegation(&owner, initial_stake, mix_id); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![mix_id]); test.reward_with_distribution_ignore_state(mix_id, active_params); - let sender = mock_info("mix-owner", &[]); + let sender = message_info(&mix_owner, &[]); let env = test.env(); try_remove_mixnode(test.deps_mut(), env, sender).unwrap(); test.execute_all_pending_events(); let res = query_estimated_current_epoch_delegator_reward( test.deps(), - owner.into(), + owner.to_string(), mix_id, test_helpers::performance(100.0), None, ) .unwrap(); - let expected = expected_current_delegator(&test, mix_id, owner); + let expected = expected_current_delegator(&test, mix_id, &owner); assert_eq!(res, expected) } #[test] fn when_node_is_not_in_the_rewarded_set() { let mut test = TestSetup::new(); - let mix_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let mix_owner = test.make_addr("mix-owner"); + let mix_id = test.add_rewarded_legacy_mixnode(&mix_owner, None); let active_params = test.active_node_params(100.); let initial_stake = Uint128::new(1_000_000_000); - let owner = "delegator"; - test.add_immediate_delegation(owner, initial_stake, mix_id); + let owner = test.make_addr("delegator"); + test.add_immediate_delegation(&owner, initial_stake, mix_id); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![mix_id]); @@ -956,26 +978,27 @@ mod tests { let res = query_estimated_current_epoch_delegator_reward( test.deps(), - owner.into(), + owner.to_string(), mix_id, test_helpers::performance(100.0), None, ) .unwrap(); - let expected = expected_current_delegator(&test, mix_id, owner); + let expected = expected_current_delegator(&test, mix_id, &owner); assert_eq!(res, expected) } #[test] fn when_estimated_performance_is_zero() { let mut test = TestSetup::new(); - let mix_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let mix_owner = test.make_addr("mix-owner"); + let mix_id = test.add_rewarded_legacy_mixnode(&mix_owner, None); let active_params = test.active_node_params(100.); let initial_stake = Uint128::new(1_000_000_000); - let owner = "delegator"; - test.add_immediate_delegation(owner, initial_stake, mix_id); + let owner = test.make_addr("delegator"); + test.add_immediate_delegation(&owner, initial_stake, mix_id); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![mix_id]); @@ -983,25 +1006,26 @@ mod tests { let res = query_estimated_current_epoch_delegator_reward( test.deps(), - owner.into(), + owner.to_string(), mix_id, test_helpers::performance(0.0), None, ) .unwrap(); - let expected = expected_current_delegator(&test, mix_id, owner); + let expected = expected_current_delegator(&test, mix_id, &owner); assert_eq!(res, expected) } #[test] fn with_correct_parameters_matches_actual_distribution_for_single_delegator() { let mut test = TestSetup::new(); - let mix_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let mix_owner = test.make_addr("mix-owner"); + let mix_id = test.add_rewarded_legacy_mixnode(&mix_owner, None); let initial_stake = Uint128::new(1_000_000_000); - let owner = "delegator"; - test.add_immediate_delegation(owner, initial_stake, mix_id); + let owner = test.make_addr("delegator"); + test.add_immediate_delegation(&owner, initial_stake, mix_id); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![mix_id]); @@ -1009,7 +1033,7 @@ mod tests { let mix_rewarding = test.mix_rewarding(mix_id); let res = query_estimated_current_epoch_delegator_reward( test.deps(), - owner.into(), + owner.to_string(), mix_id, test_helpers::performance(95.0), None, @@ -1037,7 +1061,8 @@ mod tests { #[test] fn with_correct_parameters_matches_actual_distribution_for_three_delegators() { let mut test = TestSetup::new(); - let mix_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let mix_owner = test.make_addr("mix-owner"); + let mix_id = test.add_rewarded_legacy_mixnode(&mix_owner, None); let initial_stake1 = Uint128::new(1_000_000_000); let initial_stake2 = Uint128::new(45_000_000_000); @@ -1046,25 +1071,25 @@ mod tests { let initial_stake1_dec = Decimal::from_atomics(initial_stake1, 0).unwrap(); let initial_stake2_dec = Decimal::from_atomics(initial_stake2, 0).unwrap(); let initial_stake3_dec = Decimal::from_atomics(initial_stake3, 0).unwrap(); - let del1 = "delegator1"; - let del2 = "delegator2"; - let del3 = "delegator3"; - test.add_immediate_delegation(del1, initial_stake1, mix_id); - test.add_immediate_delegation(del2, initial_stake2, mix_id); + let del1 = test.make_addr("delegator1"); + let del2 = test.make_addr("delegator2"); + let del3 = test.make_addr("delegator3"); + test.add_immediate_delegation(&del1, initial_stake1, mix_id); + test.add_immediate_delegation(&del2, initial_stake2, mix_id); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![mix_id]); let params = test.active_node_params(95.0); test.reward_with_distribution_ignore_state(mix_id, params); - test.add_immediate_delegation(del3, initial_stake3, mix_id); + test.add_immediate_delegation(&del3, initial_stake3, mix_id); test.skip_to_next_epoch_end(); let params = test.active_node_params(85.0); test.reward_with_distribution_ignore_state(mix_id, params); let mix_rewarding = test.mix_rewarding(mix_id); - let ress = [del1, del2, del3] + let ress = [&del1, &del2, &del3] .iter() .map(|owner| { query_estimated_current_epoch_delegator_reward( @@ -1079,15 +1104,15 @@ mod tests { .collect::>(); // as verified by other tests those values are correct - let est1 = query_pending_delegator_reward(test.deps(), del1.into(), mix_id, None) + let est1 = query_pending_delegator_reward(test.deps(), del1.to_string(), mix_id, None) .unwrap() .amount_earned_detailed .unwrap(); - let est2 = query_pending_delegator_reward(test.deps(), del2.into(), mix_id, None) + let est2 = query_pending_delegator_reward(test.deps(), del2.to_string(), mix_id, None) .unwrap() .amount_earned_detailed .unwrap(); - let est3 = query_pending_delegator_reward(test.deps(), del3.into(), mix_id, None) + let est3 = query_pending_delegator_reward(test.deps(), del3.to_string(), mix_id, None) .unwrap() .amount_earned_detailed .unwrap(); diff --git a/contracts/mixnet/src/rewards/storage.rs b/contracts/mixnet/src/rewards/storage.rs index 37574f99ee5..21cb1238202 100644 --- a/contracts/mixnet/src/rewards/storage.rs +++ b/contracts/mixnet/src/rewards/storage.rs @@ -16,8 +16,8 @@ use mixnet_contract_common::NodeId; // LEGACY CONSTANTS: // current parameters used for rewarding purposes -pub(crate) const REWARDING_PARAMS: Item<'_, RewardingParams> = Item::new(REWARDING_PARAMS_KEY); -pub(crate) const PENDING_REWARD_POOL_CHANGE: Item<'_, RewardPoolChange> = +pub(crate) const REWARDING_PARAMS: Item = Item::new(REWARDING_PARAMS_KEY); +pub(crate) const PENDING_REWARD_POOL_CHANGE: Item = Item::new(PENDING_REWARD_POOL_KEY); pub const MIXNODE_REWARDING: Map = Map::new(MIXNODES_REWARDING_PK_NAMESPACE); @@ -25,23 +25,23 @@ pub const MIXNODE_REWARDING: Map = Map::new(MIXNODES_REWA // we're using the same underlying key to allow seamless delegation migration pub const NYMNODE_REWARDING: Map = MIXNODE_REWARDING; -pub struct RewardingStorage<'a> { +pub struct RewardingStorage { /// Global parameters used for reward calculation, such as the current reward pool, the active set size, etc. - pub global_rewarding_params: Item<'a, RewardingParams>, + pub global_rewarding_params: Item, /// All the changes to the rewarding pool that should get applied upon the **interval** finishing. - pub pending_reward_pool_change: Item<'a, RewardPoolChange>, + pub pending_reward_pool_change: Item, /// Information associated with all nym-nodes (and legacy-mixnodes) required for reward calculation // important note: this is using **EXACTLY** the same underlying key (and structure) as legacy mixnode rewarding - pub nym_node_rewarding_data: Map<'a, NodeId, NodeRewarding>, + pub nym_node_rewarding_data: Map, /// keeps track of total cumulative work submitted for this rewarding epoch to make sure it never goes above 1 - pub cumulative_epoch_work: Item<'a, WorkFactor>, + pub cumulative_epoch_work: Item, } -impl<'a> RewardingStorage<'a> { - pub const fn new() -> RewardingStorage<'a> { +impl RewardingStorage { + pub const fn new() -> RewardingStorage { RewardingStorage { global_rewarding_params: REWARDING_PARAMS, pending_reward_pool_change: PENDING_REWARD_POOL_CHANGE, @@ -52,7 +52,7 @@ impl<'a> RewardingStorage<'a> { // an 'alias' because a `new` method might be a bit misleading since it'd suggest a brand new storage is created // as opposed to using the same underlying data as before - pub const fn load() -> RewardingStorage<'a> { + pub const fn load() -> RewardingStorage { Self::new() } diff --git a/contracts/mixnet/src/rewards/transactions.rs b/contracts/mixnet/src/rewards/transactions.rs index ab30990bff1..f07a9c64a13 100644 --- a/contracts/mixnet/src/rewards/transactions.rs +++ b/contracts/mixnet/src/rewards/transactions.rs @@ -320,7 +320,7 @@ pub mod tests { use crate::support::tests::fixtures::active_set_update_fixture; use crate::support::tests::test_helpers; use crate::support::tests::test_helpers::TestSetup; - use cosmwasm_std::testing::mock_info; + use cosmwasm_std::testing::message_info; // a simple wrapper to streamline checking for rewarding results trait TestRewarding { @@ -386,9 +386,12 @@ pub mod tests { #[test] fn when_target_mixnode_unbonded() { let mut test = TestSetup::new(); - let node_id_unbonded = test.add_rewarded_legacy_mixnode("mix-owner-unbonded", None); - let node_id_unbonded_leftover = - test.add_rewarded_legacy_mixnode("mix-owner-unbonded-leftover", None); + let node_id_unbonded = + test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner-unbonded"), None); + let node_id_unbonded_leftover = test.add_rewarded_legacy_mixnode( + &test.make_addr("mix-owner-unbonded-leftover"), + None, + ); let node_id_never_existed = 42; test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![ @@ -462,7 +465,10 @@ pub mod tests { #[test] fn when_target_mixnode_has_zero_performance() { let mut test = TestSetup::new(); - let node_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let node_id = test.add_rewarded_legacy_mixnode( + &test.make_addr(test.make_addr("mix-owner")), + None, + ); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![node_id]); @@ -481,7 +487,10 @@ pub mod tests { #[test] fn when_target_mixnode_has_zero_work_factor() { let mut test = TestSetup::new(); - let node_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let node_id = test.add_rewarded_legacy_mixnode( + &test.make_addr(test.make_addr("mix-owner")), + None, + ); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![node_id]); @@ -503,7 +512,7 @@ pub mod tests { #[test] fn when_target_nymnode_has_zero_performance() { let mut test = TestSetup::new(); - let node_id = test.add_dummy_nymnode("node-owner", None); + let node_id = test.add_dummy_nymnode(&test.make_addr("node-owner"), None); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![node_id]); @@ -522,7 +531,8 @@ pub mod tests { #[test] fn when_target_node_has_zero_workfactor() { let mut test = TestSetup::new(); - let node_id = test.add_dummy_nymnode("mix-owner", None); + let node_id = + test.add_dummy_nymnode(&test.make_addr(test.make_addr("mix-owner")), None); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![node_id]); @@ -544,7 +554,10 @@ pub mod tests { #[test] fn when_theres_only_one_node_to_reward() { let mut test = TestSetup::new(); - let node_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let node_id = test.add_rewarded_legacy_mixnode( + &test.make_addr(test.make_addr("mix-owner")), + None, + ); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![node_id]); @@ -566,7 +579,10 @@ pub mod tests { let mut ids = Vec::new(); for i in 0..100 { - let node_id = test.add_rewarded_legacy_mixnode(&format!("mix-owner{i}"), None); + let node_id = test.add_rewarded_legacy_mixnode( + &test.make_addr(test.make_addr(format!("mix-owner{i}"))), + None, + ); ids.push(node_id); } @@ -632,8 +648,9 @@ pub mod tests { #[test] fn can_only_be_performed_by_specified_rewarding_validator() { let mut test = TestSetup::new(); - let node_id = test.add_rewarded_legacy_mixnode("mix-owner", None); - let some_sender = mock_info("foomper", &[]); + let node_id = test + .add_rewarded_legacy_mixnode(&test.make_addr(test.make_addr("mix-owner")), None); + let some_sender = message_info(&test.make_addr("foomper"), &[]); // skip time to when the following epoch is over (since mixnodes are not eligible for rewarding // in the same epoch they're bonded and we need the rewarding epoch to be over) @@ -657,9 +674,10 @@ pub mod tests { fn can_only_be_performed_if_node_is_fully_bonded() { let mut test = TestSetup::new(); let node_id_never_existed = 42; - let node_id_unbonded = test.add_rewarded_legacy_mixnode("mix-owner-unbonded", None); - let node_id_unbonded_leftover = - test.add_rewarded_legacy_mixnode("mix-owner-unbonded-leftover", None); + let node_id_unbonded = + test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner-unbonded"), None); + let node_id_unbonded_leftover = test + .add_rewarded_legacy_mixnode(&test.make_addr("mix-owner-unbonded-leftover"), None); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![ node_id_unbonded, @@ -713,7 +731,8 @@ pub mod tests { fn can_only_be_performed_once_epoch_is_over() { let mut test = TestSetup::new(); - let node_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let node_id = test + .add_rewarded_legacy_mixnode(&test.make_addr(test.make_addr("mix-owner")), None); // node is in the active set BUT the current epoch has just begun test.skip_to_next_epoch(); @@ -736,7 +755,8 @@ pub mod tests { #[test] fn can_only_be_performed_once_per_node_per_epoch() { let mut test = TestSetup::new(); - let node_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let node_id = test + .add_rewarded_legacy_mixnode(&test.make_addr(test.make_addr("mix-owner")), None); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![node_id, 42]); @@ -764,7 +784,8 @@ pub mod tests { #[test] fn requires_nonzero_performance_score() { let mut test = TestSetup::new(); - let node_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let node_id = test + .add_rewarded_legacy_mixnode(&test.make_addr(test.make_addr("mix-owner")), None); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![node_id, 42]); @@ -802,7 +823,8 @@ pub mod tests { #[test] fn requires_nonzero_work_factor() { let mut test = TestSetup::new(); - let node_id = test.add_rewarded_legacy_mixnode("mix-owner", None); + let node_id = test + .add_rewarded_legacy_mixnode(&test.make_addr(test.make_addr("mix-owner")), None); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![node_id, 42]); @@ -842,20 +864,36 @@ pub mod tests { #[test] fn correctly_accounts_for_rewards_distributed() { let mut test = TestSetup::new(); - let node_id1 = test.add_rewarded_legacy_mixnode("mix-owner1", None); - let node_id2 = test.add_rewarded_legacy_mixnode("mix-owner2", None); - let node_id3 = test.add_rewarded_legacy_mixnode("mix-owner3", None); + let node_id1 = test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner1"), None); + let node_id2 = test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner2"), None); + let node_id3 = test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner3"), None); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![node_id1, node_id2, node_id3]); test.start_epoch_transition(); let params = test.active_node_params(98.0); - test.add_immediate_delegation("delegator1", Uint128::new(100_000_000), node_id2); + test.add_immediate_delegation( + &test.make_addr("delegator1"), + Uint128::new(100_000_000), + node_id2, + ); - test.add_immediate_delegation("delegator1", Uint128::new(100_000_000), node_id3); - test.add_immediate_delegation("delegator2", Uint128::new(123_456_000), node_id3); - test.add_immediate_delegation("delegator3", Uint128::new(9_100_000_000), node_id3); + test.add_immediate_delegation( + &test.make_addr("delegator1"), + Uint128::new(100_000_000), + node_id3, + ); + test.add_immediate_delegation( + &test.make_addr("delegator2"), + Uint128::new(123_456_000), + node_id3, + ); + test.add_immediate_delegation( + &test.make_addr("delegator3"), + Uint128::new(9_100_000_000), + node_id3, + ); let change = storage::PENDING_REWARD_POOL_CHANGE .load(test.deps().storage) @@ -907,20 +945,39 @@ pub mod tests { let mut test = TestSetup::new(); let global_rewarding_params = test.rewarding_params(); - let node_id1 = test.add_rewarded_legacy_mixnode("mix-owner1", Some(operator1)); - let node_id2 = test.add_rewarded_legacy_mixnode("mix-owner2", Some(operator2)); - let node_id3 = test.add_rewarded_legacy_mixnode("mix-owner3", Some(operator3)); + let node_id1 = + test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner1"), Some(operator1)); + let node_id2 = + test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner2"), Some(operator2)); + let node_id3 = + test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner3"), Some(operator3)); test.skip_to_next_epoch_end(); test.start_epoch_transition(); test.force_change_mix_rewarded_set(vec![node_id1, node_id2, node_id3]); let performance = test_helpers::performance(98.0); - test.add_immediate_delegation("delegator1", Uint128::new(100_000_000), node_id2); + test.add_immediate_delegation( + &test.make_addr("delegator1"), + Uint128::new(100_000_000), + node_id2, + ); - test.add_immediate_delegation("delegator1", Uint128::new(100_000_000), node_id3); - test.add_immediate_delegation("delegator2", Uint128::new(123_456_000), node_id3); - test.add_immediate_delegation("delegator3", Uint128::new(9_100_000_000), node_id3); + test.add_immediate_delegation( + &test.make_addr("delegator1"), + Uint128::new(100_000_000), + node_id3, + ); + test.add_immediate_delegation( + &test.make_addr("delegator2"), + Uint128::new(123_456_000), + node_id3, + ); + test.add_immediate_delegation( + &test.make_addr("delegator3"), + Uint128::new(9_100_000_000), + node_id3, + ); // bypass proper epoch progression and force change the state test.set_epoch_in_progress_state(); @@ -946,11 +1003,27 @@ pub mod tests { // add few more delegations and repeat it // (note: we're not concerned about whether particular delegation owner got the correct amount, // this is checked in other unit tests) - test.add_immediate_delegation("delegator1", Uint128::new(50_000_000), node_id1); - test.add_immediate_delegation("delegator1", Uint128::new(200_000_000), node_id2); + test.add_immediate_delegation( + &test.make_addr("delegator1"), + Uint128::new(50_000_000), + node_id1, + ); + test.add_immediate_delegation( + &test.make_addr("delegator1"), + Uint128::new(200_000_000), + node_id2, + ); - test.add_immediate_delegation("delegator5", Uint128::new(123_000_000), node_id3); - test.add_immediate_delegation("delegator6", Uint128::new(456_000_000), node_id3); + test.add_immediate_delegation( + &test.make_addr("delegator5"), + Uint128::new(123_000_000), + node_id3, + ); + test.add_immediate_delegation( + &test.make_addr("delegator6"), + Uint128::new(456_000_000), + node_id3, + ); // bypass proper epoch progression and force change the state test.set_epoch_in_progress_state(); @@ -982,21 +1055,35 @@ pub mod tests { let mut test = TestSetup::new(); let global_rewarding_params = test.rewarding_params(); - let node_id1 = test.add_rewarded_legacy_mixnode("mix-owner1", Some(operator1)); - let node_id2 = test.add_rewarded_legacy_mixnode("mix-owner2", Some(operator2)); + let node_id1 = + test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner1"), Some(operator1)); + let node_id2 = + test.add_rewarded_legacy_mixnode(&test.make_addr("mix-owner2"), Some(operator2)); test.skip_to_next_epoch_end(); test.force_change_mix_rewarded_set(vec![node_id1, node_id2]); let performance = test_helpers::performance(98.0); - test.add_immediate_delegation("delegator1", Uint128::new(100_000_000), node_id1); - test.add_immediate_delegation("delegator1", Uint128::new(100_000_000), node_id2); + test.add_immediate_delegation( + &test.make_addr("delegator1"), + Uint128::new(100_000_000), + node_id1, + ); + test.add_immediate_delegation( + &test.make_addr("delegator1"), + Uint128::new(100_000_000), + node_id2, + ); - test.add_immediate_delegation("delegator2", Uint128::new(123_456_000), node_id1); + test.add_immediate_delegation( + &test.make_addr("delegator2"), + Uint128::new(123_456_000), + node_id1, + ); - let del11 = test.delegation(node_id1, "delegator1", &None); - let del12 = test.delegation(node_id1, "delegator2", &None); - let del21 = test.delegation(node_id2, "delegator1", &None); + let del11 = test.delegation(node_id1, &test.make_addr("delegator1"), &None); + let del12 = test.delegation(node_id1, &test.make_addr("delegator2"), &None); + let del21 = test.delegation(node_id2, &test.make_addr("delegator1"), &None); for _ in 0..10 { test.start_epoch_transition(); @@ -1091,11 +1178,19 @@ pub mod tests { } // add more delegations and check few more epochs (so that the delegations would start from non-default unit delegation value) - test.add_immediate_delegation("delegator3", Uint128::new(15_850_000_000), node_id1); - test.add_immediate_delegation("delegator3", Uint128::new(15_850_000_000), node_id2); + test.add_immediate_delegation( + &test.make_addr("delegator3"), + Uint128::new(15_850_000_000), + node_id1, + ); + test.add_immediate_delegation( + &test.make_addr("delegator3"), + Uint128::new(15_850_000_000), + node_id2, + ); - let del13 = test.delegation(node_id1, "delegator3", &None); - let del23 = test.delegation(node_id2, "delegator3", &None); + let del13 = test.delegation(node_id1, &test.make_addr("delegator3"), &None); + let del23 = test.delegation(node_id2, &test.make_addr("delegator3"), &None); for _ in 0..10 { test.start_epoch_transition(); @@ -1222,7 +1317,7 @@ pub mod tests { #[test] fn regardless_of_performance_or_work_they_get_nothing() { let mut test = TestSetup::new(); - let (_, node_id) = test.add_legacy_gateway("owner", None); + let (_, node_id) = test.add_legacy_gateway(&test.make_addr("owner"), None); test.skip_to_next_epoch_end(); test.force_assign_rewarded_set(vec![RoleAssignment::new( @@ -1272,10 +1367,10 @@ pub mod tests { impl RewardingSetup { pub fn new_rewarding_setup() -> Self { let mut inner = TestSetup::new(); - let mixing_node = inner.add_dummy_nymnode("mixing-owner", None); - let entry_node = inner.add_dummy_nymnode("entry-owner", None); - let exit_node = inner.add_dummy_nymnode("exit-owner", None); - let standby_node = inner.add_dummy_nymnode("standby-owner", None); + let mixing_node = inner.add_dummy_nymnode(&inner.make_addr("mixing-owner"), None); + let entry_node = inner.add_dummy_nymnode(&inner.make_addr("entry-owner"), None); + let exit_node = inner.add_dummy_nymnode(&inner.make_addr("exit-owner"), None); + let standby_node = inner.add_dummy_nymnode(&inner.make_addr("standby-owner"), None); RewardingSetup { standby_node, @@ -1459,8 +1554,9 @@ pub mod tests { let env = actual_setup.env(); + let delegator = actual_setup.make_addr("delegator"); // add some delegations to indicate the rewarding information shouldn't get removed - actual_setup.add_immediate_delegation("delegator", Uint128::new(12345678), node); + actual_setup.add_immediate_delegation(&delegator, Uint128::new(12345678), node); pending_events::unbond_nym_node(actual_setup.deps_mut(), &env, 123, node).unwrap(); let mut res = actual_setup.reward_all(100.); @@ -1485,7 +1581,8 @@ pub mod tests { actual_setup.skip_to_next_epoch_end(); - let extra = actual_setup.add_dummy_nymnode("foomp", None); + let addr = &actual_setup.make_addr("foomp"); + let extra = actual_setup.add_dummy_nymnode(addr, None); // add extra node to the rewarded set so rewarding wouldn't immediately go into event reconciliation let role = actual_setup.local_node_role(node); @@ -1518,7 +1615,7 @@ pub mod tests { mod withdrawing_delegator_reward { use crate::interval::pending_events; use crate::support::tests::test_helpers::{assert_eq_with_leeway, TestSetup}; - use cosmwasm_std::testing::mock_info; + use cosmwasm_std::testing::message_info; use cosmwasm_std::{BankMsg, CosmosMsg, Decimal, Uint128}; use mixnet_contract_common::rewarding::helpers::truncate_reward_amount; @@ -1528,23 +1625,27 @@ pub mod tests { fn can_only_be_done_if_delegation_exists() { let mut test = TestSetup::new(); // add relatively huge stake so that the reward would be high enough to offset operating costs - let node_id1 = test - .add_rewarded_legacy_mixnode("mix-owner1", Some(Uint128::new(1_000_000_000_000))); - let node_id2 = test - .add_rewarded_legacy_mixnode("mix-owner2", Some(Uint128::new(1_000_000_000_000))); + let node_id1 = test.add_rewarded_legacy_mixnode( + &test.make_addr("mix-owner1"), + Some(Uint128::new(1_000_000_000_000)), + ); + let node_id2 = test.add_rewarded_legacy_mixnode( + &test.make_addr("mix-owner2"), + Some(Uint128::new(1_000_000_000_000)), + ); let active_params = test.active_node_params(100.); - let delegator1 = "delegator1"; - let delegator2 = "delegator2"; + let delegator1 = test.make_addr("delegator1"); + let delegator2 = test.make_addr("delegator2"); - let sender1 = mock_info(delegator1, &[]); - let sender2 = mock_info(delegator2, &[]); + let sender1 = message_info(&delegator1, &[]); + let sender2 = message_info(&delegator2, &[]); // note that there's no delegation from delegator1 towards mix1 - test.add_immediate_delegation(delegator2, 100_000_000u128, node_id1); + test.add_immediate_delegation(&delegator2, 100_000_000u128, node_id1); - test.add_immediate_delegation(delegator1, 100_000_000u128, node_id2); - test.add_immediate_delegation(delegator2, 100_000_000u128, node_id2); + test.add_immediate_delegation(&delegator1, 100_000_000u128, node_id2); + test.add_immediate_delegation(&delegator2, 100_000_000u128, node_id2); // perform some rewarding so that we'd have non-zero rewards test.skip_to_next_epoch_end(); @@ -1578,22 +1679,28 @@ pub mod tests { fn tokens_are_only_sent_if_reward_is_nonzero() { let mut test = TestSetup::new(); // add relatively huge stake so that the reward would be high enough to offset operating costs - let node_id1 = test - .add_rewarded_legacy_mixnode("mix-owner1", Some(Uint128::new(1_000_000_000_000))); - let node_id2 = test - .add_rewarded_legacy_mixnode("mix-owner2", Some(Uint128::new(1_000_000_000_000))); + let node_id1 = test.add_rewarded_legacy_mixnode( + &test.make_addr("mix-owner1"), + Some(Uint128::new(1_000_000_000_000)), + ); + let node_id2 = test.add_rewarded_legacy_mixnode( + &test.make_addr("mix-owner2"), + Some(Uint128::new(1_000_000_000_000)), + ); let active_params = test.active_node_params(100.); // very low stake so operating cost would be higher than total reward - let low_stake_id = - test.add_rewarded_legacy_mixnode("mix-owner3", Some(Uint128::new(100_000_000))); + let low_stake_id = test.add_rewarded_legacy_mixnode( + &test.make_addr("mix-owner3"), + Some(Uint128::new(100_000_000)), + ); - let delegator = "delegator"; - let sender = mock_info(delegator, &[]); + let delegator = test.make_addr("delegator"); + let sender = message_info(&delegator, &[]); - test.add_immediate_delegation(delegator, 100_000_000u128, node_id1); - test.add_immediate_delegation(delegator, 100_000_000u128, node_id2); - test.add_immediate_delegation(delegator, 1_000u128, low_stake_id); + test.add_immediate_delegation(&delegator, 100_000_000u128, node_id1); + test.add_immediate_delegation(&delegator, 100_000_000u128, node_id2); + test.add_immediate_delegation(&delegator, 1_000u128, low_stake_id); // reward mix1, but don't reward mix2 test.skip_to_next_epoch_end(); @@ -1606,7 +1713,7 @@ pub mod tests { try_withdraw_delegator_reward(test.deps_mut(), sender.clone(), node_id1).unwrap(); assert!(matches!( &res1.messages[0].msg, - CosmosMsg::Bank(BankMsg::Send { to_address, amount }) if to_address == delegator && !amount[0].amount.is_zero() + CosmosMsg::Bank(BankMsg::Send { to_address, amount }) if to_address == delegator.as_str() && !amount[0].amount.is_zero() ),); let res2 = @@ -1623,16 +1730,20 @@ pub mod tests { // note: if node has unbonded or is in the process of unbonding, the expected // way of getting back the rewards is to completely undelegate let mut test = TestSetup::new(); - let node_id_unbonding = test - .add_rewarded_legacy_mixnode("mix-owner1", Some(Uint128::new(1_000_000_000_000))); - let node_id_unbonded_leftover = test - .add_rewarded_legacy_mixnode("mix-owner2", Some(Uint128::new(1_000_000_000_000))); + let node_id_unbonding = test.add_rewarded_legacy_mixnode( + &test.make_addr("mix-owner1"), + Some(Uint128::new(1_000_000_000_000)), + ); + let node_id_unbonded_leftover = test.add_rewarded_legacy_mixnode( + &test.make_addr("mix-owner2"), + Some(Uint128::new(1_000_000_000_000)), + ); - let delegator = "delegator"; - let sender = mock_info(delegator, &[]); + let delegator = test.make_addr("delegator"); + let sender = message_info(&delegator, &[]); - test.add_immediate_delegation(delegator, 100_000_000u128, node_id_unbonding); - test.add_immediate_delegation(delegator, 100_000_000u128, node_id_unbonded_leftover); + test.add_immediate_delegation(&delegator, 100_000_000u128, node_id_unbonding); + test.add_immediate_delegation(&delegator, 100_000_000u128, node_id_unbonded_leftover); let active_params = test.active_node_params(100.); test.skip_to_next_epoch_end(); @@ -1685,19 +1796,23 @@ pub mod tests { #[test] fn correctly_determines_earned_share_and_resets_reward_ratio() { let mut test = TestSetup::new(); - let node_id_single = test - .add_rewarded_legacy_mixnode("mix-owner1", Some(Uint128::new(1_000_000_000_000))); - let node_id_quad = test - .add_rewarded_legacy_mixnode("mix-owner2", Some(Uint128::new(1_000_000_000_000))); - - let delegator1 = "delegator1"; - let delegator2 = "delegator2"; - let delegator3 = "delegator3"; - let delegator4 = "delegator4"; - let sender1 = mock_info(delegator1, &[]); - let sender2 = mock_info(delegator2, &[]); - let sender3 = mock_info(delegator3, &[]); - let sender4 = mock_info(delegator4, &[]); + let node_id_single = test.add_rewarded_legacy_mixnode( + &test.make_addr("mix-owner1"), + Some(Uint128::new(1_000_000_000_000)), + ); + let node_id_quad = test.add_rewarded_legacy_mixnode( + &test.make_addr("mix-owner2"), + Some(Uint128::new(1_000_000_000_000)), + ); + + let delegator1 = test.make_addr("delegator1"); + let delegator2 = test.make_addr("delegator2"); + let delegator3 = test.make_addr("delegator3"); + let delegator4 = test.make_addr("delegator4"); + let sender1 = message_info(&delegator1, &[]); + let sender2 = message_info(&delegator2, &[]); + let sender3 = message_info(&delegator3, &[]); + let sender4 = message_info(&delegator4, &[]); let amount_single = 100_000_000u128; @@ -1706,12 +1821,12 @@ pub mod tests { let amount_quad3 = 250_000_000u128; let amount_quad4 = 500_000_000u128; - test.add_immediate_delegation(delegator1, amount_single, node_id_single); + test.add_immediate_delegation(&delegator1, amount_single, node_id_single); - test.add_immediate_delegation(delegator1, amount_quad1, node_id_quad); - test.add_immediate_delegation(delegator2, amount_quad2, node_id_quad); - test.add_immediate_delegation(delegator3, amount_quad3, node_id_quad); - test.add_immediate_delegation(delegator4, amount_quad4, node_id_quad); + test.add_immediate_delegation(&delegator1, amount_quad1, node_id_quad); + test.add_immediate_delegation(&delegator2, amount_quad2, node_id_quad); + test.add_immediate_delegation(&delegator3, amount_quad3, node_id_quad); + test.add_immediate_delegation(&delegator4, amount_quad4, node_id_quad); let active_params = test.active_node_params(100.); test.skip_to_next_epoch_end(); @@ -1735,14 +1850,14 @@ pub mod tests { test.set_epoch_in_progress_state(); } - let before = test.read_delegation(node_id_single, delegator1, None); + let before = test.read_delegation(node_id_single, &delegator1, None); assert_eq!(before.cumulative_reward_ratio, Decimal::zero()); let res1 = try_withdraw_delegator_reward(test.deps_mut(), sender1.clone(), node_id_single) .unwrap(); let (_, reward) = test_helpers::get_bank_send_msg(&res1).unwrap(); assert_eq!(truncate_reward_amount(accumulated_single), reward[0].amount); - let after = test.read_delegation(node_id_single, delegator1, None); + let after = test.read_delegation(node_id_single, &delegator1, None); assert_ne!(after.cumulative_reward_ratio, Decimal::zero()); assert_eq!( after.cumulative_reward_ratio, @@ -1750,8 +1865,8 @@ pub mod tests { ); // withdraw first two rewards. note that due to scaling we expect second reward to be 4x the first one - let before1 = test.read_delegation(node_id_quad, delegator1, None); - let before2 = test.read_delegation(node_id_quad, delegator2, None); + let before1 = test.read_delegation(node_id_quad, &delegator1, None); + let before2 = test.read_delegation(node_id_quad, &delegator2, None); assert_eq!(before1.cumulative_reward_ratio, Decimal::zero()); assert_eq!(before2.cumulative_reward_ratio, Decimal::zero()); let res1 = @@ -1775,13 +1890,13 @@ pub mod tests { Uint128::new(4), ); - let after1 = test.read_delegation(node_id_quad, delegator1, None); + let after1 = test.read_delegation(node_id_quad, &delegator1, None); assert_ne!(after1.cumulative_reward_ratio, Decimal::zero()); assert_eq!( after1.cumulative_reward_ratio, test.mix_rewarding(node_id_quad).total_unit_reward ); - let after2 = test.read_delegation(node_id_quad, delegator2, None); + let after2 = test.read_delegation(node_id_quad, &delegator2, None); assert_ne!(after2.cumulative_reward_ratio, Decimal::zero()); assert_eq!( after2.cumulative_reward_ratio, @@ -1799,10 +1914,10 @@ pub mod tests { test.set_epoch_in_progress_state(); } - let before1_new = test.read_delegation(node_id_quad, delegator1, None); - let before2_new = test.read_delegation(node_id_quad, delegator2, None); - let before3 = test.read_delegation(node_id_quad, delegator3, None); - let before4 = test.read_delegation(node_id_quad, delegator4, None); + let before1_new = test.read_delegation(node_id_quad, &delegator1, None); + let before2_new = test.read_delegation(node_id_quad, &delegator2, None); + let before3 = test.read_delegation(node_id_quad, &delegator3, None); + let before4 = test.read_delegation(node_id_quad, &delegator4, None); assert_eq!( before1_new.cumulative_reward_ratio, @@ -1869,10 +1984,10 @@ pub mod tests { fn can_only_be_done_if_bond_exists() { let mut test = TestSetup::new(); - let owner = "mix-owner"; + let owner = &test.make_addr(test.make_addr("mix-owner")); let node_id = test.add_rewarded_legacy_mixnode(owner, Some(Uint128::new(1_000_000_000_000))); - let sender = mock_info("random-guy", &[]); + let sender = message_info(&test.make_addr("random-guy"), &[]); let active_params = test.active_node_params(100.); test.skip_to_next_epoch_end(); @@ -1893,15 +2008,15 @@ pub mod tests { fn tokens_are_only_sent_if_reward_is_nonzero() { let mut test = TestSetup::new(); - let owner1 = "mix-owner1"; - let owner2 = "mix-owner2"; + let owner1 = &test.make_addr("mix-owner1"); + let owner2 = &test.make_addr("mix-owner2"); let node_id1 = test.add_rewarded_legacy_mixnode(owner1, Some(Uint128::new(1_000_000_000_000))); test.add_rewarded_legacy_mixnode(owner2, Some(Uint128::new(1_000_000_000_000))); let active_params = test.active_node_params(100.); - let sender1 = mock_info(owner1, &[]); - let sender2 = mock_info(owner2, &[]); + let sender1 = message_info(owner1, &[]); + let sender2 = message_info(owner2, &[]); // reward mix1, but don't reward mix2 test.skip_to_next_epoch_end(); @@ -1912,7 +2027,7 @@ pub mod tests { let res1 = try_withdraw_operator_reward(test.deps_mut(), sender1).unwrap(); assert!(matches!( &res1.messages[0].msg, - CosmosMsg::Bank(BankMsg::Send { to_address, amount }) if to_address == owner1 && !amount[0].amount.is_zero() + CosmosMsg::Bank(BankMsg::Send { to_address, amount }) if to_address == owner1.as_str() && !amount[0].amount.is_zero() ),); let res2 = try_withdraw_operator_reward(test.deps_mut(), sender2).unwrap(); @@ -1924,17 +2039,21 @@ pub mod tests { // note: if node has unbonded or is in the process of unbonding, the expected // way of getting back the rewards is finish the undelegation let mut test = TestSetup::new(); - let owner1 = "mix-owner1"; - let owner2 = "mix-owner2"; - let sender1 = mock_info(owner1, &[]); - let sender2 = mock_info(owner2, &[]); + let owner1 = &test.make_addr("mix-owner1"); + let owner2 = &test.make_addr("mix-owner2"); + let sender1 = message_info(owner1, &[]); + let sender2 = message_info(owner2, &[]); let node_id_unbonding = test.add_rewarded_legacy_mixnode(owner1, Some(Uint128::new(1_000_000_000_000))); let node_id_unbonded_leftover = test.add_rewarded_legacy_mixnode(owner2, Some(Uint128::new(1_000_000_000_000))); // add some delegation to the second node so that it wouldn't be cleared upon unbonding - test.add_immediate_delegation("delegator", 100_000_000u128, node_id_unbonded_leftover); + test.add_immediate_delegation( + &test.make_addr("delegator"), + 100_000_000u128, + node_id_unbonded_leftover, + ); let active_params = test.active_node_params(100.); test.skip_to_next_epoch_end(); @@ -2045,7 +2164,7 @@ pub mod tests { let rewarding_validator = test.rewarding_validator(); let owner = test.owner(); - let random = mock_info("random-guy", &[]); + let random = message_info(&test.make_addr("random-guy"), &[]); let env = test.env(); let res = try_update_active_set_distribution( @@ -2319,7 +2438,7 @@ pub mod tests { let rewarding_validator = test.rewarding_validator(); let owner = test.owner(); - let random = mock_info("random-guy", &[]); + let random = message_info(&test.make_addr("random-guy"), &[]); let update = IntervalRewardingParamsUpdate { reward_pool: None, diff --git a/contracts/mixnet/src/signing/storage.rs b/contracts/mixnet/src/signing/storage.rs index 0e7975a37a6..18b1bdd2f4e 100644 --- a/contracts/mixnet/src/signing/storage.rs +++ b/contracts/mixnet/src/signing/storage.rs @@ -6,7 +6,7 @@ use cosmwasm_std::{Addr, StdResult, Storage}; use cw_storage_plus::Map; use nym_contracts_common::signing::Nonce; -pub const NONCES: Map<'_, Addr, Nonce> = Map::new(SIGNING_NONCES_NAMESPACE); +pub const NONCES: Map = Map::new(SIGNING_NONCES_NAMESPACE); pub fn get_signing_nonce(storage: &dyn Storage, address: Addr) -> StdResult { let nonce = NONCES.may_load(storage, address)?.unwrap_or(0); diff --git a/contracts/mixnet/src/support/helpers.rs b/contracts/mixnet/src/support/helpers.rs index 89978824627..f2fa4921384 100644 --- a/contracts/mixnet/src/support/helpers.rs +++ b/contracts/mixnet/src/support/helpers.rs @@ -235,7 +235,7 @@ pub(crate) fn decode_ed25519_identity_key( ) -> Result<[u8; 32], MixnetContractError> { let mut public_key = [0u8; 32]; let used = bs58::decode(encoded) - .into(&mut public_key) + .onto(&mut public_key) .map_err(|err| MixnetContractError::MalformedEd25519IdentityKey(err.to_string()))?; if used != 32 { diff --git a/contracts/mixnet/src/support/tests/mod.rs b/contracts/mixnet/src/support/tests/mod.rs index b1ccca1c913..d19c800dd6a 100644 --- a/contracts/mixnet/src/support/tests/mod.rs +++ b/contracts/mixnet/src/support/tests/mod.rs @@ -49,12 +49,12 @@ pub mod test_helpers { good_gateway_pledge, good_mixnode_pledge, good_node_plegge, TEST_COIN_DENOM, }; use crate::support::tests::{legacy, test_helpers}; + use cosmwasm_std::testing::message_info; use cosmwasm_std::testing::mock_dependencies; use cosmwasm_std::testing::mock_env; - use cosmwasm_std::testing::mock_info; use cosmwasm_std::testing::MockApi; use cosmwasm_std::testing::MockQuerier; - use cosmwasm_std::{coin, coins, Addr, Api, BankMsg, CosmosMsg, Storage}; + use cosmwasm_std::{coin, coins, Addr, BankMsg, CosmosMsg, Storage}; use cosmwasm_std::{Coin, Order}; use cosmwasm_std::{Decimal, Empty, MemoryStorage}; use cosmwasm_std::{Deps, OwnedDeps}; @@ -97,6 +97,24 @@ pub mod test_helpers { use std::str::FromStr; use std::time::Duration; + pub(crate) fn sorted_addresses(n: usize) -> Vec { + let mut rng = test_rng(); + let mut addrs = Vec::with_capacity(n); + for i in 0..n { + addrs.push(MockApi::default().addr_make(&format!("addr{i}{}", rng.next_u64()))); + } + addrs.sort(); + addrs + } + + pub(crate) fn addr>(raw: S) -> Addr { + mock_dependencies().api.addr_make(raw.as_ref()) + } + + pub(crate) fn sender>(raw: S) -> MessageInfo { + message_info(&addr(raw), &[]) + } + pub(crate) trait ExtractBankMsg { fn unwrap_bank_msg(self) -> Option; } @@ -172,18 +190,14 @@ pub mod test_helpers { let deps = init_contract(); let rewarding_validator_address = rewarding_validator_address(deps.as_ref().storage).unwrap(); - let owner = mixnet_params_storage::ADMIN - .query_admin(deps.as_ref()) - .unwrap() - .admin - .unwrap(); + let owner = ADMIN.query_admin(deps.as_ref()).unwrap().admin.unwrap(); TestSetup { deps, env: mock_env(), rng: test_rng(), - rewarding_validator: mock_info(rewarding_validator_address.as_ref(), &[]), - owner: mock_info(owner.as_str(), &[]), + rewarding_validator: message_info(&rewarding_validator_address, &[]), + owner: message_info(&Addr::unchecked(&owner), &[]), } } @@ -192,17 +206,17 @@ pub mod test_helpers { let mut nodes = Vec::new(); - let problematic_delegator = "n1foomp"; - let problematic_delegator_twin = "n1bar"; - let problematic_delegator_alt_twin = "n1whatever"; + let problematic_delegator = test.make_addr("n1foomp"); + let problematic_delegator_twin = test.make_addr("n1bar"); + let problematic_delegator_alt_twin = test.make_addr("n1whatever"); let choices = [true, false]; // every epoch there's a 3% chance of somebody bonding a node let bonding_weights = [3, 97]; - // and 15% of making a delegation - let delegation_weights = [15, 85]; + // and 14% of making a delegation + let delegation_weights = [14, 85]; // and 1% of making a VESTED delegation let vested_delegation_weights = [1, 99]; @@ -246,26 +260,30 @@ pub mod test_helpers { // make sure we cover our edge case of somebody having both liquid and vested delegation towards the same node if epoch_id == 123 { - test.add_immediate_delegation(problematic_delegator, stake, 3); - test.add_immediate_delegation(problematic_delegator_twin, stake, 3); + test.add_immediate_delegation(&problematic_delegator, stake, 3); + test.add_immediate_delegation(&problematic_delegator_twin, stake, 3); } if epoch_id == 666 { - test.add_immediate_delegation_with_legal_proxy(problematic_delegator, stake, 3); test.add_immediate_delegation_with_legal_proxy( - problematic_delegator_twin, + &problematic_delegator, + stake, + 3, + ); + test.add_immediate_delegation_with_legal_proxy( + &problematic_delegator_twin, stake, 3, ); } if epoch_id == 234 { - test.add_immediate_delegation(problematic_delegator_alt_twin, stake, 3); + test.add_immediate_delegation(&problematic_delegator_alt_twin, stake, 3); } if epoch_id == 420 { test.add_immediate_delegation_with_legal_proxy( - problematic_delegator_alt_twin, + &problematic_delegator_alt_twin, stake, 3, ); @@ -298,6 +316,14 @@ pub mod test_helpers { test } + pub fn make_addr>(&self, raw: S) -> Addr { + self.deps.api.addr_make(raw.as_ref()) + } + + pub fn make_sender>(&self, raw: S) -> MessageInfo { + message_info(&self.make_addr(raw), &[]) + } + #[track_caller] pub fn ensure_delegation_sync(&self, mix_id: NodeId) { let mix_info = self.mix_rewarding(mix_id); @@ -322,8 +348,8 @@ pub mod test_helpers { ADMIN.get(self.deps()).unwrap().unwrap() } - pub fn random_address(&mut self) -> String { - format!("n1foomp{}", self.rng.next_u64()) + pub fn random_address(&mut self) -> Addr { + addr(format!("n1foomp{}", self.rng.next_u64())) } pub fn deps(&self) -> Deps<'_> { @@ -358,10 +384,10 @@ pub mod test_helpers { #[allow(unused)] pub fn execute_no_funds( &mut self, - sender: impl Into, + sender: &Addr, msg: ExecuteMsg, ) -> Result { - self.execute(self.mock_info(sender), msg) + self.execute(message_info(sender, &[]), msg) } pub fn execute_fn( @@ -380,12 +406,12 @@ pub mod test_helpers { pub fn execute_fn_no_funds( &mut self, exec_fn: F, - sender: impl Into, + sender: &Addr, ) -> Result where F: FnOnce(DepsMut<'_>, Env, MessageInfo) -> Result, { - let info = self.mock_info(sender); + let info = message_info(sender, &[]); self.execute_fn(exec_fn, info) } @@ -401,11 +427,7 @@ pub mod test_helpers { #[allow(unused)] #[track_caller] - pub fn assert_simple_execution_no_funds( - &mut self, - exec_fn: F, - sender: impl Into, - ) -> Response + pub fn assert_simple_execution_no_funds(&mut self, exec_fn: F, sender: &Addr) -> Response where F: FnOnce(DepsMut<'_>, Env, MessageInfo) -> Result, { @@ -467,11 +489,6 @@ pub mod test_helpers { } } - #[allow(unused)] - pub fn mock_info(&self, sender: impl Into) -> MessageInfo { - mock_info(&sender.into(), &[]) - } - pub fn rewarding_validator(&self) -> MessageInfo { self.rewarding_validator.clone() } @@ -483,7 +500,7 @@ pub mod test_helpers { } pub fn admin_sender(&self) -> MessageInfo { - mock_info(self.admin().as_ref(), &[]) + message_info(&self.admin(), &[]) } pub fn owner(&self) -> MessageInfo { @@ -630,7 +647,7 @@ pub mod test_helpers { pub fn add_rewarded_set_nymnode( &mut self, - owner: &str, + owner: &Addr, stake: Option, ) -> (NymNodeBond, MessageSignature, KeyPair) { let res = self.add_nymnode(owner, stake); @@ -642,7 +659,7 @@ pub mod test_helpers { pub fn add_rewarded_set_nymnode_id( &mut self, - owner: &str, + owner: &Addr, stake: Option, ) -> NodeId { self.add_rewarded_set_nymnode(owner, stake).0.node_id @@ -650,14 +667,14 @@ pub mod test_helpers { pub fn add_nymnode( &mut self, - owner: &str, + owner: &Addr, stake: Option, ) -> (NymNodeBond, MessageSignature, KeyPair) { let stake = self.make_node_pledge(stake); let (node, owner_signature, keypair) = self.node_with_signature(owner, Some(stake.clone())); - let info = mock_info(owner, stake.as_ref()); + let info = message_info(owner, stake.as_ref()); let env = self.env(); try_add_nym_node( @@ -675,14 +692,14 @@ pub mod test_helpers { (bond, owner_signature, keypair) } - pub fn add_dummy_nymnode(&mut self, owner: &str, stake: Option) -> NodeId { + pub fn add_dummy_nymnode(&mut self, owner: &Addr, stake: Option) -> NodeId { self.add_nymnode(owner, stake).0.node_id } #[allow(unused)] pub fn add_dummy_nym_node_with_keypair( &mut self, - owner: &str, + owner: &Addr, stake: Option, ) -> (NodeId, identity::KeyPair) { let (bond, _, keypair) = self.add_nymnode(owner, stake); @@ -690,11 +707,11 @@ pub mod test_helpers { } #[track_caller] - pub fn add_legacy_mixnode(&mut self, owner: &str, stake: Option) -> NodeId { + pub fn add_legacy_mixnode(&mut self, owner: &Addr, stake: Option) -> NodeId { let stake = self.make_mix_pledge(stake); let (mixnode, _, _) = self.mixnode_with_signature(owner, Some(stake.clone())); - let info = mock_info(owner, stake.as_ref()); + let info = message_info(owner, stake.as_ref()); let env = self.env(); ensure_no_existing_bond(&info.sender, &self.deps.storage).unwrap(); @@ -711,7 +728,7 @@ pub mod test_helpers { .unwrap() } - pub fn add_rewarded_mixing_node(&mut self, owner: &str, stake: Option) -> NodeId { + pub fn add_rewarded_mixing_node(&mut self, owner: &Addr, stake: Option) -> NodeId { let node_id = self.add_dummy_nymnode(owner, stake); self.immediately_assign_lowest_mix_layer(node_id); node_id @@ -719,7 +736,7 @@ pub mod test_helpers { pub fn add_rewarded_entry_gateway_node( &mut self, - owner: &str, + owner: &Addr, stake: Option, ) -> NodeId { let node_id = self.add_dummy_nymnode(owner, stake); @@ -729,7 +746,7 @@ pub mod test_helpers { pub fn add_rewarded_exit_gateway_node( &mut self, - owner: &str, + owner: &Addr, stake: Option, ) -> NodeId { let node_id = self.add_dummy_nymnode(owner, stake); @@ -737,7 +754,7 @@ pub mod test_helpers { node_id } - pub fn add_standby_node(&mut self, owner: &str, stake: Option) -> NodeId { + pub fn add_standby_node(&mut self, owner: &Addr, stake: Option) -> NodeId { let node_id = self.add_dummy_nymnode(owner, stake); self.immediately_assign_standby_role(node_id); node_id @@ -745,7 +762,7 @@ pub mod test_helpers { pub fn add_legacy_mixnode_with_proxy_and_keypair( &mut self, - owner: &str, + owner: &Addr, stake: Option, ) -> (NodeId, identity::KeyPair) { let pledge = self.make_mix_pledge(stake).pop().unwrap(); @@ -800,7 +817,7 @@ pub mod test_helpers { pub fn add_legacy_mixnode_with_legal_proxy( &mut self, - owner: &str, + owner: &Addr, stake: Option, ) -> NodeId { self.add_legacy_mixnode_with_proxy_and_keypair(owner, stake) @@ -809,7 +826,7 @@ pub mod test_helpers { pub fn add_rewarded_legacy_mixnode( &mut self, - owner: &str, + owner: &Addr, stake: Option, ) -> NodeId { let node_id = self.add_legacy_mixnode(owner, stake); @@ -820,14 +837,14 @@ pub mod test_helpers { pub fn add_legacy_gateway( &mut self, - sender: &str, + sender: &Addr, stake: Option, ) -> (IdentityKey, NodeId) { let stake = self.make_gateway_pledge(stake); let (gateway, _) = self.gateway_with_signature(sender, Some(stake.clone())); let env = self.env(); - let info = mock_info(sender, &stake); + let info = message_info(sender, &stake); legacy::save_new_gateway( &mut self.deps.storage, @@ -854,13 +871,13 @@ pub mod test_helpers { pub fn add_legacy_mixnodes(&mut self, n: usize) { for i in 0..n { - self.add_legacy_mixnode(&format!("owner{i}"), None); + self.add_legacy_mixnode(&addr(format!("owner{i}")), None); } } pub fn add_dummy_gateways(&mut self, n: usize) { for i in 0..n { - self.add_legacy_gateway(&format!("owner{i}"), None); + self.add_legacy_gateway(&addr(format!("owner{i}")), None); } } @@ -894,7 +911,7 @@ pub mod test_helpers { pub fn mixnode_bonding_signature( &mut self, key: &identity::PrivateKey, - owner: &str, + owner: &Addr, mixnode: MixNode, stake: Option, ) -> MessageSignature { @@ -905,13 +922,13 @@ pub mod test_helpers { pub fn add_legacy_mixnode_with_keypair( &mut self, - owner: &str, + owner: &Addr, stake: Option, ) -> (NodeId, identity::KeyPair) { let stake = self.make_mix_pledge(stake); let (mixnode, _, keypair) = self.mixnode_with_signature(owner, Some(stake.clone())); - let info = mock_info(owner, stake.as_ref()); + let info = message_info(owner, stake.as_ref()); let env = self.env(); ensure_no_existing_bond(&info.sender, &self.deps.storage).unwrap(); @@ -932,7 +949,7 @@ pub mod test_helpers { pub fn node_with_signature( &mut self, - sender: &str, + sender: &Addr, stake: Option>, ) -> (NymNode, MessageSignature, KeyPair) { let stake = stake.unwrap_or(good_node_plegge()); @@ -953,7 +970,7 @@ pub mod test_helpers { pub fn mixnode_with_signature( &mut self, - sender: &str, + sender: &Addr, stake: Option>, ) -> (MixNode, MessageSignature, KeyPair) { let stake = stake.unwrap_or(good_mixnode_pledge()); @@ -975,7 +992,7 @@ pub mod test_helpers { pub fn gateway_with_signature( &mut self, - sender: impl Into, + sender: &Addr, stake: Option>, ) -> (Gateway, MessageSignature) { let stake = stake.unwrap_or(good_gateway_pledge()); @@ -990,12 +1007,7 @@ pub mod test_helpers { ..tests::fixtures::gateway_fixture() }; - let msg = gateway_bonding_sign_payload( - self.deps(), - sender.into().as_str(), - gateway.clone(), - stake, - ); + let msg = gateway_bonding_sign_payload(self.deps(), sender, gateway.clone(), stake); let owner_signature = ed25519_sign_message(msg, keypair.private_key()); (gateway, owner_signature) @@ -1008,12 +1020,8 @@ pub mod test_helpers { .unwrap(); let env = self.env(); - try_remove_mixnode( - self.deps_mut(), - env, - mock_info(bond_details.owner.as_str(), &[]), - ) - .unwrap(); + try_remove_mixnode(self.deps_mut(), env, message_info(&bond_details.owner, &[])) + .unwrap(); } #[track_caller] @@ -1023,12 +1031,8 @@ pub mod test_helpers { .unwrap(); let env = self.env(); - try_remove_nym_node( - self.deps_mut(), - env, - mock_info(bond_details.owner.as_str(), &[]), - ) - .unwrap(); + try_remove_nym_node(self.deps_mut(), env, message_info(&bond_details.owner, &[])) + .unwrap(); } #[track_caller] @@ -1053,7 +1057,7 @@ pub mod test_helpers { pub fn add_immediate_delegation( &mut self, - delegator: &str, + delegator: &Addr, amount: impl Into, target: NodeId, ) { @@ -1076,7 +1080,7 @@ pub mod test_helpers { pub fn add_immediate_delegation_with_legal_proxy( &mut self, - delegator: &str, + delegator: &Addr, amount: impl Into, target: NodeId, ) { @@ -1087,8 +1091,7 @@ pub mod test_helpers { }; let proxy = self.vesting_contract(); - let owner = self.deps.api.addr_validate(delegator).unwrap(); - let storage_key = Delegation::generate_storage_key(target, &owner, Some(&proxy)); + let storage_key = Delegation::generate_storage_key(target, delegator, Some(&proxy)); let mut mix_rewarding = self.mix_rewarding(target); @@ -1107,7 +1110,7 @@ pub mod test_helpers { .unwrap(); let delegation = Delegation { - owner, + owner: delegator.clone(), node_id: target, cumulative_reward_ratio: mix_rewarding.total_unit_reward, amount: stored_delegation_amount, @@ -1126,7 +1129,7 @@ pub mod test_helpers { #[allow(unused)] pub fn add_delegation( &mut self, - delegator: &str, + delegator: &Addr, amount: impl Into, target: NodeId, ) { @@ -1139,7 +1142,7 @@ pub mod test_helpers { delegate(self.deps_mut(), env, delegator, vec![amount], target) } - pub fn remove_immediate_delegation(&mut self, delegator: &str, target: NodeId) { + pub fn remove_immediate_delegation(&mut self, delegator: &Addr, target: NodeId) { let height = self.env.block.height; pending_events::undelegate(self.deps_mut(), height, Addr::unchecked(delegator), target) .unwrap(); @@ -1204,7 +1207,7 @@ pub mod test_helpers { } #[allow(unused)] - pub fn pending_delegator_reward(&mut self, delegator: &str, target: NodeId) -> Decimal { + pub fn pending_delegator_reward(&mut self, delegator: &Addr, target: NodeId) -> Decimal { query_pending_delegator_reward(self.deps(), delegator.into(), target, None) .unwrap() .amount_earned_detailed @@ -1492,7 +1495,7 @@ pub mod test_helpers { pub fn read_delegation( &mut self, mix: NodeId, - owner: &str, + owner: &Addr, proxy: Option<&str>, ) -> Delegation { read_delegation( @@ -1516,13 +1519,12 @@ pub mod test_helpers { } #[track_caller] - pub fn delegation(&self, mix: NodeId, owner: &str, proxy: &Option) -> Delegation { + pub fn delegation(&self, mix: NodeId, owner: &Addr, proxy: &Option) -> Delegation { let caller = std::panic::Location::caller(); - read_delegation(self.deps().storage, mix, &Addr::unchecked(owner), proxy) - .unwrap_or_else(|| { - panic!("{caller} failed with: delegation for {mix}/{owner} doesn't exist") - }) + read_delegation(self.deps().storage, mix, owner, proxy).unwrap_or_else(|| { + panic!("{caller} failed with: delegation for {mix}/{owner} doesn't exist") + }) } } @@ -1705,14 +1707,14 @@ pub mod test_helpers { n: usize, ) { for i in 0..n { - add_unbonded_mixnode(&mut rng, deps.branch(), None, &format!("owner{}", i)); + add_unbonded_mixnode(&mut rng, deps.branch(), None, &addr(format!("owner{}", i))); } } pub fn add_dummy_unbonded_mixnodes_with_owner( mut rng: impl RngCore + CryptoRng, mut deps: DepsMut<'_>, - owner: &str, + owner: &Addr, n: usize, ) { for _ in 0..n { @@ -1731,7 +1733,7 @@ pub mod test_helpers { &mut rng, deps.branch(), Some(identity), - &format!("owner{}", i), + &addr(format!("owner{i}")), ); } } @@ -1741,7 +1743,7 @@ pub mod test_helpers { mut rng: impl RngCore + CryptoRng, deps: DepsMut<'_>, identity_key: Option<&str>, - owner: &str, + owner: &Addr, ) -> NodeId { let id = loop { let candidate = rng.next_u32(); @@ -1771,7 +1773,7 @@ pub mod test_helpers { pub fn nymnode_bonding_sign_payload( deps: Deps<'_>, - owner: &str, + owner: &Addr, node: NymNode, stake: Vec, ) -> SignableNymNodeBondingMsg { @@ -1786,7 +1788,7 @@ pub mod test_helpers { pub fn mixnode_bonding_sign_payload( deps: Deps<'_>, - owner: &str, + owner: &Addr, mixnode: MixNode, stake: Vec, ) -> SignableMixNodeBondingMsg { @@ -1801,7 +1803,7 @@ pub mod test_helpers { pub fn gateway_bonding_sign_payload( deps: Deps<'_>, - owner: &str, + owner: &Addr, gateway: Gateway, stake: Vec, ) -> SignableGatewayBondingMsg { @@ -1840,8 +1842,8 @@ pub mod test_helpers { pub fn init_contract() -> OwnedDeps> { let mut deps = mock_dependencies(); let msg = InstantiateMsg { - rewarding_validator_address: "rewarder".into(), - vesting_contract_address: "vesting-contract".to_string(), + rewarding_validator_address: deps.api.addr_make("rewarder").to_string(), + vesting_contract_address: deps.api.addr_make("vesting-contract").to_string(), rewarding_denom: TEST_COIN_DENOM.to_string(), epochs_in_interval: 720, epoch_duration: Duration::from_secs(60 * 60), @@ -1853,13 +1855,13 @@ pub mod test_helpers { interval_operating_cost: Default::default(), }; let env = mock_env(); - let info = mock_info("creator", &[]); + let info = sender("creator"); instantiate(deps.as_mut(), env, info, msg).unwrap(); deps } - pub fn delegate(deps: DepsMut<'_>, env: Env, sender: &str, stake: Vec, mix_id: NodeId) { - let info = mock_info(sender, &stake); + pub fn delegate(deps: DepsMut<'_>, env: Env, sender: &Addr, stake: Vec, mix_id: NodeId) { + let info = message_info(sender, &stake); try_delegate_to_node(deps, env, info, mix_id).unwrap(); } diff --git a/contracts/mixnet/src/vesting_migration.rs b/contracts/mixnet/src/vesting_migration.rs index a507ea02d17..152c4adb87f 100644 --- a/contracts/mixnet/src/vesting_migration.rs +++ b/contracts/mixnet/src/vesting_migration.rs @@ -221,14 +221,14 @@ mod tests { use super::*; use crate::mixnodes::helpers::get_mixnode_details_by_id; use crate::support::tests::test_helpers::TestSetup; - use cosmwasm_std::testing::mock_info; - use cosmwasm_std::{from_binary, Addr, CosmosMsg, WasmMsg}; + use cosmwasm_std::testing::message_info; + use cosmwasm_std::{from_json, CosmosMsg, WasmMsg}; #[test] fn with_no_bonded_nodes() { let mut test = TestSetup::new(); - let sender = mock_info("owner", &[]); + let sender = test.make_sender("owner"); let deps = test.deps_mut(); // nothing happens @@ -236,7 +236,7 @@ mod tests { assert_eq!( res, MixnetContractError::NoAssociatedMixNodeBond { - owner: Addr::unchecked("owner") + owner: test.make_addr("owner") } ) } @@ -244,9 +244,9 @@ mod tests { #[test] fn with_liquid_node_bonded() { let mut test = TestSetup::new(); - test.add_legacy_mixnode("owner", None); + test.add_legacy_mixnode(&test.make_addr("owner"), None); - let sender = mock_info("owner", &[]); + let sender = message_info(&test.make_addr("owner"), &[]); let deps = test.deps_mut(); // nothing happens @@ -257,9 +257,9 @@ mod tests { #[test] fn with_vested_node_bonded() { let mut test = TestSetup::new(); - let mix_id = test.add_legacy_mixnode_with_legal_proxy("owner", None); + let mix_id = test.add_legacy_mixnode_with_legal_proxy(&test.make_addr("owner"), None); - let sender = mock_info("owner", &[]); + let sender = message_info(&test.make_addr("owner"), &[]); let deps = test.deps_mut(); let existing_node = get_mixnode_details_by_id(deps.storage, mix_id) @@ -277,9 +277,9 @@ mod tests { }; assert_eq!( - from_binary::(msg).unwrap(), + from_json::(msg).unwrap(), VestingExecuteMsg::TrackMigratedMixnode { - owner: "owner".to_string() + owner: test.make_addr("owner").to_string() } ); } @@ -290,8 +290,8 @@ mod tests { use super::*; use crate::delegations::storage::delegations; use crate::support::tests::test_helpers::{assert_eq_with_leeway, TestSetup}; - use cosmwasm_std::testing::mock_info; - use cosmwasm_std::{from_binary, Addr, CosmosMsg, Order, Uint128, WasmMsg}; + use cosmwasm_std::testing::message_info; + use cosmwasm_std::{from_json, CosmosMsg, Order, Uint128, WasmMsg}; use mixnet_contract_common::helpers::compare_decimals; use mixnet_contract_common::nym_node::Role; use mixnet_contract_common::reward_params::{NodeRewardingParameters, Performance}; @@ -304,7 +304,7 @@ mod tests { let mut test = TestSetup::new_complex(); let env = test.env(); - let sender = mock_info("owner-without-any-delegations", &[]); + let sender = test.make_sender("owner-without-any-delegations"); // it simply fails for there is nothing to migrate let res = try_migrate_vested_delegation(test.deps_mut(), env, sender, 42).unwrap_err(); @@ -329,7 +329,7 @@ mod tests { .filter_map(|d| d.map(|(_, del)| del).ok()) .any(|d| d.proxy.is_some() && d.owner.as_str() == delegation.owner.as_str())); - let sender = mock_info(delegation.owner.as_str(), &[]); + let sender = message_info(&delegation.owner, &[]); let mix_id = delegation.node_id; // it also fails because the method is only allowed for vested delegations @@ -361,7 +361,7 @@ mod tests { expected_liquid.proxy = None; let expected_new_storage_key = expected_liquid.storage_key(); - let sender = mock_info(delegation.owner.as_str(), &[]); + let sender = message_info(&delegation.owner, &[]); let mix_id = delegation.node_id; // a track message is sent into the vesting contract @@ -371,7 +371,7 @@ mod tests { }; assert_eq!( - from_binary::(msg).unwrap(), + from_json::(msg).unwrap(), VestingExecuteMsg::TrackMigratedDelegation { owner: delegation.owner.to_string(), mix_id, @@ -398,18 +398,15 @@ mod tests { let mut test = TestSetup::new_complex(); let env = test.env(); - let problematic_delegator = "n1foomp"; - let problematic_delegator_twin = "n1bar"; + let problematic_delegator = test.make_addr("n1foomp"); + let problematic_delegator_twin = test.make_addr("n1bar"); let mix_id = 3; - let liquid_storage_key = Delegation::generate_storage_key( - mix_id, - &Addr::unchecked(problematic_delegator), - None, - ); + let liquid_storage_key = + Delegation::generate_storage_key(mix_id, &problematic_delegator, None); let vested_storage_key = Delegation::generate_storage_key( mix_id, - &Addr::unchecked(problematic_delegator), + &problematic_delegator, Some(&test.vesting_contract()), ); @@ -431,14 +428,14 @@ mod tests { test.ensure_delegation_sync(mix_id); // a track message is sent into the vesting contract - let sender = mock_info(problematic_delegator, &[]); + let sender = message_info(&problematic_delegator, &[]); let res = try_migrate_vested_delegation(test.deps_mut(), env, sender, mix_id).unwrap(); let CosmosMsg::Wasm(WasmMsg::Execute { msg, .. }) = &res.messages[0].msg else { panic!("no track message present") }; assert_eq!( - from_binary::(msg).unwrap(), + from_json::(msg).unwrap(), VestingExecuteMsg::TrackMigratedDelegation { owner: problematic_delegator.to_string(), mix_id, @@ -488,14 +485,11 @@ mod tests { // would be the same as if the delegations remained separate let all_nodes = test.all_mixnodes(); - let twin_liquid_storage_key = Delegation::generate_storage_key( - mix_id, - &Addr::unchecked(problematic_delegator_twin), - None, - ); + let twin_liquid_storage_key = + Delegation::generate_storage_key(mix_id, &problematic_delegator_twin, None); let twin_vested_storage_key = Delegation::generate_storage_key( mix_id, - &Addr::unchecked(problematic_delegator_twin), + &problematic_delegator_twin, Some(&test.vesting_contract()), ); diff --git a/contracts/mixnet/v2-changes.md b/contracts/mixnet/v2-changes.md index dc09e6784a8..d0652276410 100644 --- a/contracts/mixnet/v2-changes.md +++ b/contracts/mixnet/v2-changes.md @@ -1,30 +1,46 @@ - # Mixnet contract changes -This file shall describe (hopefully) all relevant changes made to the contract as the result of changing reward calculation. +This file shall describe (hopefully) all relevant changes made to the contract as the result of changing reward +calculation. ## Overview -There are two main changes performed to the mixnet contract that have a cascading effect on the rest of the system. They are as follows: - -1. The delegator rewarding is modified so that in order to determine the correct reward, we no longer have to iterate through data from all the epochs to correctly compound the reward. Instead, we assume there's a theoretical "unit" delegation on each mixnode that we keep track of and scale all the actual delegation values accordingly. It's very similar to the idea presented in the [Cosmos' F1 paper](https://drops.dagstuhl.de/opus/volltexte/2020/11974/pdf/OASIcs-Tokenomics-2019-10.pdf). I've explained the entire algorithm on an example in more details on [our gitlab](https://gitlab.nymte.ch/jstuczyn/reward-testing/-/blob/main/README.md). - -2. Mixnodes are no longer stored and indexed by their identity keys. Instead, they get assigned a unique `NodeId` (just an increasing `u64` id). This is to resolve my favourite ~~bug~~ feature (I will explain this in slightly more details in the next sections) that causes rebonded mixnode to retain its delegations. With this change the following would happen: - - new mixnode bonds, gets assigned id `X` - - delegations are being made towards this mixnode - - mixnode decides to unbond - - the same mixnode rebonds, with the same identity key, same owner, same sphinx key, etc. but this time it gets assigned new id `Y` - - as a result the delegations are still pointing to `X` and thus are no longer accumulating any rewards +There are two main changes performed to the mixnet contract that have a cascading effect on the rest of the system. They +are as follows: + +1. The delegator rewarding is modified so that in order to determine the correct reward, we no longer have to iterate + through data from all the epochs to correctly compound the reward. Instead, we assume there's a theoretical "unit" + delegation on each mixnode that we keep track of and scale all the actual delegation values accordingly. It's very + similar to the idea presented in + the [Cosmos' F1 paper](https://drops.dagstuhl.de/opus/volltexte/2020/11974/pdf/OASIcs-Tokenomics-2019-10.pdf). I've + explained the entire algorithm on an example in more details + on [our gitlab](https://gitlab.nymte.ch/jstuczyn/reward-testing/-/blob/main/README.md). + +2. Mixnodes are no longer stored and indexed by their identity keys. Instead, they get assigned a unique `NodeId` (just + an increasing `u64` id). This is to resolve my favourite ~~bug~~ feature (I will explain this in slightly more + details in the next sections) that causes rebonded mixnode to retain its delegations. With this change the following + would happen: + - new mixnode bonds, gets assigned id `X` + - delegations are being made towards this mixnode + - mixnode decides to unbond + - the same mixnode rebonds, with the same identity key, same owner, same sphinx key, etc. but this time it gets + assigned new id `Y` + - as a result the delegations are still pointing to `X` and thus are no longer accumulating any rewards While not as major as the above changes, the other notable changes include: -- introduction of `PendingEpochEvent` and `PendingIntervalEvent`. It means that whenever a relevant request is received, it's only going to get executed once the current epoch (or interval) finishes. This might include, for example, mixnode unbonding or changing mixnode cost parameters. -- rewarding parameters, such as the size of the reward pool are only updated at the end of the current **interval**. They should not get modified between epochs. + +- introduction of `PendingEpochEvent` and `PendingIntervalEvent`. It means that whenever a relevant request is received, + it's only going to get executed once the current epoch (or interval) finishes. This might include, for example, + mixnode unbonding or changing mixnode cost parameters. +- rewarding parameters, such as the size of the reward pool are only updated at the end of the current **interval**. + They should not get modified between epochs. - node uptime/performance can now be a decimal value as opposed to integer between 0-100 - node operators can now set their operating costs - profit margin can now be a decimal value as opposed to integer between 0-100 - delegation/undelegation is no longer instantaneous. They will happen at the end of corresponding epoch. ## 'Benefits' to community: + - compounding happens automatically - you no longer have to keep track of it - node operators can now set their operating costs - profit margin can be more granular @@ -33,7 +49,9 @@ While not as major as the above changes, the other notable changes include: ## Instantiation -The `InstantiateMsg` contains more fields to remove dependency on our "beloved" `NETWORK_DEFAULTS` and constants. Now we have to explicitly specify relevant parameters during instantiation: +The `InstantiateMsg` contains more fields to remove dependency on our "beloved" `NETWORK_DEFAULTS` and constants. Now we +have to explicitly specify relevant parameters during instantiation: + ```rust pub struct InstantiateMsg { pub rewarding_validator_address: String, @@ -45,7 +63,12 @@ pub struct InstantiateMsg { } ``` -We have to explicitly state, as before, address of the rewarding validator which is authorized to update rewarded sets and distribute rewards to mixnodes, but also vesting contract address (since we need to know if we should call `Track...` methods. Without it we could end up attempt to call a vesting contract method on a non-contract address), interval/epoch related parameters (so that we wouldn't accidentally try to set 10min epochs via migration :eyes:) and initial rewarding parameters that include things such as the size of the initial reward pool or the per interval emission. +We have to explicitly state, as before, address of the rewarding validator which is authorized to update rewarded sets +and distribute rewards to mixnodes, but also vesting contract address (since we need to know if we should call +`Track...` methods. Without it we could end up attempt to call a vesting contract method on a non-contract address), +interval/epoch related parameters (so that we wouldn't accidentally try to set 10min epochs via migration :eyes:) and +initial rewarding parameters that include things such as the size of the initial reward pool or the per interval +emission. ## Mixnodes @@ -53,14 +76,19 @@ We have to explicitly state, as before, address of the rewarding validator which The mixnode part (apart from the delegations themselves) is the one most heavily affected by the changes made. -- All the associated structs were either modified or completely replaced, for example our top level `MixNodeBond` has been superseded by `MixNodeDetails` that _mostly_ contains what the original `MixNodeBond` had, but also holds additional information regarding rewarding parameters. -- Furthermore, as mentioned before, the indexing works differently now. We use `NodeId` to identify nodes as opposed to `IdentityKey`. -- However, our unique indices are still in place, i.e. it's impossible to bond multiple mixnodes with the same identities. +- All the associated structs were either modified or completely replaced, for example our top level `MixNodeBond` has + been superseded by `MixNodeDetails` that _mostly_ contains what the original `MixNodeBond` had, but also holds + additional information regarding rewarding parameters. +- Furthermore, as mentioned before, the indexing works differently now. We use `NodeId` to identify nodes as opposed to + `IdentityKey`. +- However, our unique indices are still in place, i.e. it's impossible to bond multiple mixnodes with the same + identities. - "unbonding" is no longer instantaneous, instead it is executed when the current epoch finishes. - at the time of writing this, "bonding" is still instant, though it might change. - operators can now set their interval operating costs alongside their profit margins - profit margin and operating cost changes will only happen at the end of current interval -- operators can now change basic information about their mixnodes without having to unbond, this includes things such as the version or host information +- operators can now change basic information about their mixnodes without having to unbond, this includes things such as + the version or host information - ... ### Types/Models @@ -69,7 +97,8 @@ The mixnode part (apart from the delegations themselves) is the one most heavily ##### MixNodeDetails -Top level struct containing all information about particular mixnode, i.e. public keys, host information, cost function, rewarding parameters, etc. +Top level struct containing all information about particular mixnode, i.e. public keys, host information, cost function, +rewarding parameters, etc. ```rust pub struct MixNodeDetails { @@ -81,9 +110,12 @@ pub struct MixNodeDetails { ##### MixNodeRewarding -New struct containing information required to determine correct rewards for all delegators and the operator. Keeps track of the currently distributed rewards as well as the value of the "unit delegation". +New struct containing information required to determine correct rewards for all delegators and the operator. Keeps track +of the currently distributed rewards as well as the value of the "unit delegation". + +It is stored in separate `Map`, as after mixnode finishes the unbonding process, if there are any delegators who haven't +undelegated yet, we need to know the below information in order to determine their rewards correctly. -It is stored in separate `Map`, as after mixnode finishes the unbonding process, if there are any delegators who haven't undelegated yet, we need to know the below information in order to determine their rewards correctly. ```rust pub struct MixNodeRewarding { /// Information provided by the operator that influence the cost function. @@ -121,7 +153,9 @@ pub struct MixNodeRewarding { ##### MixNodeCostParams -Contains all cost-function related information of a given mixnode, i.e. currently the profit margin and the operating cost (per interval). It is provided by the node operator at the time of bonding and can only be changed as an interval rolls over. +Contains all cost-function related information of a given mixnode, i.e. currently the profit margin and the operating +cost (per interval). It is provided by the node operator at the time of bonding and can only be changed as an interval +rolls over. ```rust pub struct MixNodeCostParams { @@ -135,7 +169,9 @@ pub struct MixNodeCostParams { ##### UnbondedMixnode -This struct is used to keep track very basic information about nodes that have already unbonded, as we would only know their `NodeId`. It is especially useful if your delegation is pointing to an unbonded node and you wanted to know the owner of the node that decided to unbond or its identity key. +This struct is used to keep track very basic information about nodes that have already unbonded, as we would only know +their `NodeId`. It is especially useful if your delegation is pointing to an unbonded node and you wanted to know the +owner of the node that decided to unbond or its identity key. ```rust pub struct UnbondedMixnode { @@ -147,7 +183,8 @@ pub struct UnbondedMixnode { ##### MixNodeConfigUpdate -Encapsulates information sent to the contract whenever operator wants to update basic configuration information about the bonded node. +Encapsulates information sent to the contract whenever operator wants to update basic configuration information about +the bonded node. ```rust pub struct MixNodeConfigUpdate { @@ -163,7 +200,8 @@ pub struct MixNodeConfigUpdate { ##### StoredMixnodeBond -The initial idea behind `StoredMixnodeBond` was to store the total delegation separately to the operator pledge (and accumulated rewards). The same issue is now resolved with the `MixNodeBond` and `MixNodeRewarding`. +The initial idea behind `StoredMixnodeBond` was to store the total delegation separately to the operator pledge (and +accumulated rewards). The same issue is now resolved with the `MixNodeBond` and `MixNodeRewarding`. #### Modified @@ -172,25 +210,25 @@ The initial idea behind `StoredMixnodeBond` was to store the total delegation se ```rust // operator information + data assigned by the contract(s) pub struct MixNodeBond { - // - // ... - // - // `id` field has been added that contains information about the assigned `NodeId` - // +++ pub id: NodeId, - // - // we no longer keep total pledge (alongside delegation) on `MixNodeBond` via the `pledge_amount`, instead we only hold the `original_pledge` which is **NEVER** modified - // --- pub pledge_amount: Coin - // +++ pub original_pledge: Coin, - // - // `block_height` has been renamed to `bonding_height` to be more explicit about the intent - // --- pub block_height: u64 - // +++ pub bonding_height: u64, - // - // `is_unbonding` field has been added to indicate when the mixnode has issue the request to unbond but the epoch hasn't rolled over yet (to prevent delegations on an unbonding node) - // +++ pub is_unbonding: bool, - // - // `accumulated_rewards` field is no longer required to keep track of all rewards for particular node - // --- pub accumulated_rewards: Option + // + // ... + // + // `id` field has been added that contains information about the assigned `NodeId` + // +++ pub id: NodeId, + // + // we no longer keep total pledge (alongside delegation) on `MixNodeBond` via the `pledge_amount`, instead we only hold the `original_pledge` which is **NEVER** modified + // --- pub pledge_amount: Coin + // +++ pub original_pledge: Coin, + // + // `block_height` has been renamed to `bonding_height` to be more explicit about the intent + // --- pub block_height: u64 + // +++ pub bonding_height: u64, + // + // `is_unbonding` field has been added to indicate when the mixnode has issue the request to unbond but the epoch hasn't rolled over yet (to prevent delegations on an unbonding node) + // +++ pub is_unbonding: bool, + // + // `accumulated_rewards` field is no longer required to keep track of all rewards for particular node + // --- pub accumulated_rewards: Option } ``` @@ -199,12 +237,12 @@ pub struct MixNodeBond { ```rust // information provided by the operator pub struct MixNode { - // - // ... - // - // `profit_margin_percent` has been removed and this information is now provided via `MixNodeCostParams` - // --- pub profit_margin_percent: u8, - // +++ + // + // ... + // + // `profit_margin_percent` has been removed and this information is now provided via `MixNodeCostParams` + // --- pub profit_margin_percent: u8, + // +++ } ``` @@ -212,9 +250,9 @@ pub struct MixNode { ```rust pub enum Layer { - // `Gateway` layer has been removed from the `Layer` enum - //--- Gateway = 0, - //+++ + // `Gateway` layer has been removed from the `Layer` enum + //--- Gateway = 0, + //+++ One = 1, Two = 2, Three = 3, @@ -223,39 +261,50 @@ pub enum Layer { ### Transactions -As before, all transactions have their associated `OnBehalf` equivalent that allows them to be called from the vesting contract. +As before, all transactions have their associated `OnBehalf` equivalent that allows them to be called from the vesting +contract. #### Added ##### UpdateMixnodeCostParams -This one allows you to update the cost parameters of your mixnode, i.e. the profit margin and the interval operating costs. Execution of this transaction will result in the creation of a `PendingIntervalEvent` that will get resolved at the end of the current interval. +This one allows you to update the cost parameters of your mixnode, i.e. the profit margin and the interval operating +costs. Execution of this transaction will result in the creation of a `PendingIntervalEvent` that will get resolved at +the end of the current interval. #### Removed ##### CheckpointMixnodes -Due to the changes to the rewarding system, we no longer have to be checkpointing mixnodes every epoch in order to keep track of their stake/parameters at those blocks. +Due to the changes to the rewarding system, we no longer have to be checkpointing mixnodes every epoch in order to keep +track of their stake/parameters at those blocks. #### Modified ##### UpdateMixnodeConfig -Updating mixnode config allowed you to update your profit margin. This functionality has been replaced with `UpdateMixnodeCostParams` and instead `UpdateMixnodeConfig` lets you to instantaneously update basic configuration such as the host information or the node version. +Updating mixnode config allowed you to update your profit margin. This functionality has been replaced with +`UpdateMixnodeCostParams` and instead `UpdateMixnodeConfig` lets you to instantaneously update basic configuration such +as the host information or the node version. ##### UnbondMixnode -In general sense `UnbondMixnode` works as before, i.e. it will eventually result in the mixnode getting removed from the directory. However, it's no longer instant. Whenever the transaction is executed, it will instead push a `PendingEpochEvent` that shall get resolved at the end of the current epoch. +In general sense `UnbondMixnode` works as before, i.e. it will eventually result in the mixnode getting removed from the +directory. However, it's no longer instant. Whenever the transaction is executed, it will instead push a +`PendingEpochEvent` that shall get resolved at the end of the current epoch. -The only immediate effect is that `is_unbonding` field on the `MixNodeBond` is going to be set to `true` and as a result, no new delegations are going to be permitted on this node. +The only immediate effect is that `is_unbonding` field on the `MixNodeBond` is going to be set to `true` and as a +result, no new delegations are going to be permitted on this node. ##### BondMixnode -The only difference made to the mixnode bonding process is that operators need to provide an additional argument, of type `MixNodeCostParams`, to specify the cost function arguments of the node. +The only difference made to the mixnode bonding process is that operators need to provide an additional argument, of +type `MixNodeCostParams`, to specify the cost function arguments of the node. ### Queries -The most relevant thing here to note is that whenever old queries were using `mix_identity` as one of their arguments, they instead use `mix_id` of type `NodeId` (`u64`). +The most relevant thing here to note is that whenever old queries were using `mix_identity` as one of their arguments, +they instead use `mix_id` of type `NodeId` (`u64`). #### Added @@ -265,7 +314,8 @@ New query allowing to grab the details of all mixnodes (paged) that have unbonde ##### GetUnbondedMixNodeInformation -Same as above, but rather than getting information for all the mixnodes, it does it for the node specified by the provided `mix_id`. +Same as above, but rather than getting information for all the mixnodes, it does it for the node specified by the +provided `mix_id`. ##### GetStakeSaturation @@ -273,7 +323,8 @@ Allows to directly obtain stake saturation (i.e. of the full bond (pledge + dele ##### GetMixnodeRewardingDetails -Allows obtaining `MixNodeRewarding` details of a particular node that, among other things, contain total delegation towards this node or the current value of the "unit" delegation. +Allows obtaining `MixNodeRewarding` details of a particular node that, among other things, contain total delegation +towards this node or the current value of the "unit" delegation. #### Removed @@ -289,13 +340,17 @@ No longer needed due to no snapshotting. ##### GetCurrentOperatorCost -In the previous version the operator cost was a constant value shared by all operators. Now it is configurable and it can be queried by getting information associated via the particular node, for example with `GetMixnodeRewardingDetails` or `GetMixnodeDetails`. +In the previous version the operator cost was a constant value shared by all operators. Now it is configurable and it +can be queried by getting information associated via the particular node, for example with `GetMixnodeRewardingDetails` +or `GetMixnodeDetails`. #### Modified ##### GetMixNodes => GetMixNodeBonds, GetMixNodesDetailed -Since the structure of `MixNodeBond` has changed, `GetMixNodes` is replaced by `GetMixNodeBonds` that returns all `MixNodeBond` (paged) while `GetMixNodesDetailed` returns `MixNodeDetails`, that apart from the bond also contains rewarding details. +Since the structure of `MixNodeBond` has changed, `GetMixNodes` is replaced by `GetMixNodeBonds` that returns all +`MixNodeBond` (paged) while `GetMixNodesDetailed` returns `MixNodeDetails`, that apart from the bond also contains +rewarding details. ##### GetMixnodeBond @@ -309,32 +364,41 @@ Similarly to the above query for the bond information of given mixnode has been #### Added -- `MIXNODE_ID_COUNTER` - as mentioned before all mixnodes are indexed by an increasing `NodeId`. This counter keep track of the current value. +- `MIXNODE_ID_COUNTER` - as mentioned before all mixnodes are indexed by an increasing `NodeId`. This counter keep track + of the current value. - `UNBONDED_MIXNODES` - `Map`storing basic information about the mixnodes that have unbonded. #### Removed - memoized `TOTAL_DELEGATION` was removed. Similar functionality is achieved via `rewards_storage::MIXNODE_REWARDING`. -- `LAST_PM_UPDATE_TIME` is also removed. We no longer have to keep track of that since the profit margin updates are enforced to be happening at the end of intervals. +- `LAST_PM_UPDATE_TIME` is also removed. We no longer have to keep track of that since the profit margin updates are + enforced to be happening at the end of intervals. #### Modified -- As mentioned multiple times before, mixnodes are no longer snapshot and we index them with `NodeId` and thus instead of using `IndexedSnapshotMap<'a, IdentityKeyRef<'a>, StoredMixnodeBond, MixnodeBondIndex<'a>>` we use `IndexedMap<'a, NodeId, MixNodeBond, MixnodeBondIndex<'a>>` +- As mentioned multiple times before, mixnodes are no longer snapshot and we index them with `NodeId` and thus instead + of using `IndexedSnapshotMap<'a, IdentityKeyRef<'a>, StoredMixnodeBond, MixnodeBondIndex<'a>>` we use + `IndexedMap>` - To preserve uniqueness on identity keys, `MixnodeBondIndex` now also contains `UniqueIndex` on the `identity_key`. ## Gateways ### Overview -Gateways remain mostly unchanged and unaffected by the fallout of other changes. We still keep the gateways indexed by their identity keys. This might change in the future, but for the time being, there's no reason to do anything about it. +Gateways remain mostly unchanged and unaffected by the fallout of other changes. We still keep the gateways indexed by +their identity keys. This might change in the future, but for the time being, there's no reason to do anything about it. -The only relevant change is that `OwnsGateway` query has been renamed to `GetOwnedGateway` to keep the naming consistent. +The only relevant change is that `OwnsGateway` query has been renamed to `GetOwnedGateway` to keep the naming +consistent. ## Delegation ### Overview -Apart from mixnodes, the delegations are the other part of the system most affected by the introduced changes. Structurally-wise, the differences are relatively minimal, but the logic behind them, especially concerning rewarding and reward estimation (which will be described in more details in the subsequent sections) has changed in a meaningful way. +Apart from mixnodes, the delegations are the other part of the system most affected by the introduced changes. +Structurally-wise, the differences are relatively minimal, but the logic behind them, especially concerning rewarding +and reward estimation (which will be described in more details in the subsequent sections) has changed in a meaningful +way. ### Types/Models @@ -360,7 +424,7 @@ pub struct Delegation { // +++ pub node_id: NodeId, // Value of the "unit delegation" associated with the mixnode at the time of delegation. It's used purely for calculating rewards - // +++ pub cumulative_reward_ratio: Decimal, + // +++ pub cumulative_reward_ratio: Decimal, // `block_height` has been renamed to `height` // --- pub block_height: u64, @@ -370,11 +434,16 @@ pub struct Delegation { ### Transactions -Apart from the changes to the mixnode indexing (i.e. `identity_key => node_id`) there's been no significant changes to the transactions involving delegations. Of course this excludes anything regarding rewards, but this part is going to have its own dedicated section below. +Apart from the changes to the mixnode indexing (i.e. `identity_key => node_id`) there's been no significant changes to +the transactions involving delegations. Of course this excludes anything regarding rewards, but this part is going to +have its own dedicated section below. ### Queries -The same holds true for queries. If we exclude reward estimation-related queries and changes due to the new indexing, there are hardly any changes. The only notable difference is the introduction of `GetAllDelegations` which allows one to query for all delegations in the system as opposed to being restricted to a single owner or a single mixnode. The responses are still, however, paged. +The same holds true for queries. If we exclude reward estimation-related queries and changes due to the new indexing, +there are hardly any changes. The only notable difference is the introduction of `GetAllDelegations` which allows one to +query for all delegations in the system as opposed to being restricted to a single owner or a single mixnode. The +responses are still, however, paged. ### Storage @@ -384,23 +453,34 @@ N/A #### Removed -- `PENDING_DELEGATION_EVENTS` `Map` has been removed as this concept is being superseded by the `PendingEpochEvent` queue. +- `PENDING_DELEGATION_EVENTS` `Map` has been removed as this concept is being superseded by the `PendingEpochEvent` + queue. #### Modified The storage key structure of delegation has been slightly adjusted compared to the previous version: -- The composite storage key no longer includes the block height as we're now able to immediately work with the potentially changed values, -- For the simplicity sake, the composite subkey created for the purpose of querying by the owner/proxy combination has been changed from being a `Vec` to instead being a base58-encoded String (of the same data). This makes it slightly easier for the clients to use it, especially in paged queries. + +- The composite storage key no longer includes the block height as we're now able to immediately work with the + potentially changed values, +- For the simplicity sake, the composite subkey created for the purpose of querying by the owner/proxy combination has + been changed from being a `Vec` to instead being a base58-encoded String (of the same data). This makes it + slightly easier for the clients to use it, especially in paged queries. ## Interval ### Overview -Generally we've been going back and forth with having explicit distinction between epochs and intervals and making this purely implicit. In this iteration of the contract both pieces of data are explicit. `Interval` has an associated id, etc. as well as it holds information about the current epoch, number of epochs in interval, etc. +Generally we've been going back and forth with having explicit distinction between epochs and intervals and making this +purely implicit. In this iteration of the contract both pieces of data are explicit. `Interval` has an associated id, +etc. as well as it holds information about the current epoch, number of epochs in interval, etc. -The other notable change to how interval behaves is that we expanded the concept of particular events being executed as given epoch (or interval) rolls over. Previously this was only applicable to `PendingDelegations`. +The other notable change to how interval behaves is that we expanded the concept of particular events being executed as +given epoch (or interval) rolls over. Previously this was only applicable to `PendingDelegations`. -Also, now advancing epoch happens in the same message as writing the new rewarded set, so it's impossible to perform one without the other. Speaking of updating the rewarded set, I was attempting to be smart and reduce number of storage read by not writing entries that hasn't changed (i.e. if node was `Active` and its updated status is still `Active`, don't do anything). We're about to see if this wasn't a stupid overkill... +Also, now advancing epoch happens in the same message as writing the new rewarded set, so it's impossible to perform one +without the other. Speaking of updating the rewarded set, I was attempting to be smart and reduce number of storage read +by not writing entries that hasn't changed (i.e. if node was `Active` and its updated status is still `Active`, don't do +anything). We're about to see if this wasn't a stupid overkill... ### Types/Models @@ -408,47 +488,49 @@ Also, now advancing epoch happens in the same message as writing the new rewarde ##### PendingEpochEvent -New structure keeping track of events that shall get invoked at the end of the current **epoch** (after rewards have already been distributed). +New structure keeping track of events that shall get invoked at the end of the current **epoch** (after rewards have +already been distributed). ```rust pub enum PendingEpochEvent { - Delegate { - owner: Addr, - mix_id: NodeId, - amount: Coin, - proxy: Option, - }, - Undelegate { - owner: Addr, - mix_id: NodeId, - proxy: Option, - }, - UnbondMixnode { - mix_id: NodeId, - }, - UpdateActiveSetSize { - new_size: u32, - }, + Delegate { + owner: Addr, + mix_id: NodeId, + amount: Coin, + proxy: Option, + }, + Undelegate { + owner: Addr, + mix_id: NodeId, + proxy: Option, + }, + UnbondMixnode { + mix_id: NodeId, + }, + UpdateActiveSetSize { + new_size: u32, + }, } ``` ##### PendingIntervalEvent -New structure keeping track of events that shall get invoked at the end of the current **interval** (after rewards have already been distributed). +New structure keeping track of events that shall get invoked at the end of the current **interval** (after rewards have +already been distributed). ```rust pub enum PendingIntervalEvent { - ChangeMixCostParams { - mix: NodeId, - new_costs: MixNodeCostParams, - }, - UpdateRewardingParams { - update: IntervalRewardingParamsUpdate, - }, - UpdateIntervalConfig { - epochs_in_interval: u32, - epoch_duration_secs: u64, - }, + ChangeMixCostParams { + mix: NodeId, + new_costs: MixNodeCostParams, + }, + UpdateRewardingParams { + update: IntervalRewardingParamsUpdate, + }, + UpdateIntervalConfig { + epochs_in_interval: u32, + epoch_duration_secs: u64, + }, } ``` @@ -487,11 +569,16 @@ pub struct Interval { #### Added -There's a couple of newly added transaction that allow changing rewarding-related parameters, such as the active set size or pool emission, etc. But those changes should preferably only be executed at the end of the current epoch/interval (depending on a particular change requested) so that the current rewarding interval wouldn't be affected in an unexpected way. However, the transactions include the `force_immediately` field to make the change immediate if required. +There's a couple of newly added transaction that allow changing rewarding-related parameters, such as the active set +size or pool emission, etc. But those changes should preferably only be executed at the end of the current +epoch/interval (depending on a particular change requested) so that the current rewarding interval wouldn't be affected +in an unexpected way. However, the transactions include the `force_immediately` field to make the change immediate if +required. ##### UpdateActiveSetSize -Allows updating the active set size. Note that the new size **must** be equal to or smaller than the current rewarded set. If `force_immediately` is not set, the change will be applied at the end of the current epoch. +Allows updating the active set size. Note that the new size **must** be equal to or smaller than the current rewarded +set. If `force_immediately` is not set, the change will be applied at the end of the current epoch. ##### UpdateRewardingParams @@ -499,27 +586,32 @@ Allows updating (almost) all the other rewarding-related global parameters: ```rust pub struct IntervalRewardingParamsUpdate { - pub reward_pool: Option, - pub staking_supply: Option, + pub reward_pool: Option, + pub staking_supply: Option, - pub sybil_resistance_percent: Option, - pub active_set_work_factor: Option, - pub interval_pool_emission: Option, - pub rewarded_set_size: Option, + pub sybil_resistance_percent: Option, + pub active_set_work_factor: Option, + pub interval_pool_emission: Option, + pub rewarded_set_size: Option, } ``` -Note that at least a single change must be specified. If `force_immediately` is not set, the change will be applied at the end of the current interval. +Note that at least a single change must be specified. If `force_immediately` is not set, the change will be applied at +the end of the current interval. ##### UpdateIntervalConfig -Allows adjusting configuration of the interval, i.e. number of epochs it contains as well as the duration of the epochs themselves. Similarly to the above, if `force_immediately` is not set, the change will be applied at the end of the current interval. +Allows adjusting configuration of the interval, i.e. number of epochs it contains as well as the duration of the epochs +themselves. Similarly to the above, if `force_immediately` is not set, the change will be applied at the end of the +current interval. ##### ReconcileEpochEvents -Serves a very similar purpose to the removed `ReconcileDelegations`. But rather than being limited to just delegation creation/removal, this transaction would attempt to execute all pending epoch and interval events. +Serves a very similar purpose to the removed `ReconcileDelegations`. But rather than being limited to just delegation +creation/removal, this transaction would attempt to execute all pending epoch and interval events. -Do note that if current epoch is in **NOT** in progress, nothing is going to happen. Furthermore, interval events will only get executed if apart from the current epoch being over, the interval itself is over. +Do note that if current epoch is in **NOT** in progress, nothing is going to happen. Furthermore, interval events will +only get executed if apart from the current epoch being over, the interval itself is over. Anyone willing to pay the associated gas costs is can call this transaction. It's not limited to the `owner` account. @@ -527,7 +619,8 @@ Anyone willing to pay the associated gas costs is can call this transaction. It' ##### InitEpoch -Since we're going to be creating a brand-new contract, we no longer have to separately initialise the epoch (interval). It's going to be performed implicitly during contract instantiation. +Since we're going to be creating a brand-new contract, we no longer have to separately initialise the epoch (interval). +It's going to be performed implicitly during contract instantiation. ##### ReconcileDelegations @@ -535,11 +628,13 @@ As explained before, superseded by `ReconcileEpochEvents`. ##### CheckpointMixnodes -As explained multiple times before, due to the changes to the reward calculation algorithm, we no longer have to keep track of the state of all the mixnodes at each epoch. +As explained multiple times before, due to the changes to the reward calculation algorithm, we no longer have to keep +track of the state of all the mixnodes at each epoch. ##### WriteRewardedSet -This functionality has been moved into `AdvanceCurrentEpoch` so that it would not be possible to roll over the epoch without explicitly updating the rewarded set. +This functionality has been moved into `AdvanceCurrentEpoch` so that it would not be possible to roll over the epoch +without explicitly updating the rewarded set. ##### GetRewardedSetUpdateDetails @@ -557,10 +652,15 @@ We no longer keep track of rewarded sets at given height thanks to the change to ##### AdvanceCurrentEpoch -The main idea behind this transaction remains unchanged - if the current epoch/interval is over, this call rolls it over to the next one. However, it is now also responsible for additional functionalities: +The main idea behind this transaction remains unchanged - if the current epoch/interval is over, this call rolls it over +to the next one. However, it is now also responsible for additional functionalities: + - updating the rewarded set to the newly provided value, -- emptying the `PendingEpochEvent` and `PendingIntervalEvent` queues if there's anything left in there -> Do note that a separate explicit call in a different transaction is preferred, since there might be a significant amount of events to go through, -- if the interval has rolled over all the pending reward pool changes from `RewardPoolChange` (that will be elaborated in the rewards section) are applied +- emptying the `PendingEpochEvent` and `PendingIntervalEvent` queues if there's anything left in there -> Do note that a + separate explicit call in a different transaction is preferred, since there might be a significant amount of events to + go through, +- if the interval has rolled over all the pending reward pool changes from `RewardPoolChange` (that will be elaborated + in the rewards section) are applied ### Queries @@ -568,21 +668,25 @@ The main idea behind this transaction remains unchanged - if the current epoch/i ##### GetCurrentIntervalDetails -Allows querying for the information about the current `Interval` alongside data on the current blocktime and whether the current epoch and interval are already over. +Allows querying for the information about the current `Interval` alongside data on the current blocktime and whether the +current epoch and interval are already over. ##### GetPendingEpochEvents -Paged query for obtaining all currently pending `PendingEpochEvents` that shall get cleared at the end of the current epoch. +Paged query for obtaining all currently pending `PendingEpochEvents` that shall get cleared at the end of the current +epoch. ##### GetPendingIntervalEvents -Paged query for obtaining all currently pending `PendingIntervalEvents` that shall get cleared at the end of the current interval. +Paged query for obtaining all currently pending `PendingIntervalEvents` that shall get cleared at the end of the current +interval. #### Removed ##### GetEpochsInInterval -This was removed in favour of `GetCurrentIntervalDetails` that returns the same piece of data on top of additional content. +This was removed in favour of `GetCurrentIntervalDetails` that returns the same piece of data on top of additional +content. #### Modified @@ -594,28 +698,38 @@ Querying for the rewarded set no longer lets you specify the block height. It al #### Added -Similarly to `MIXNODE_ID_COUNTER`, we keep an increasing id for epoch and interval events. Essentially we want to ensure that we'd execute them in the order they were created and we can't use block height as it's very possible multiple requests might be created in the same block height. Thus we introduce `EPOCH_EVENT_ID_COUNTER` and `INTERVAL_EVENT_ID_COUNTER` for that purpose. +Similarly to `MIXNODE_ID_COUNTER`, we keep an increasing id for epoch and interval events. Essentially we want to ensure +that we'd execute them in the order they were created and we can't use block height as it's very possible multiple +requests might be created in the same block height. Thus we introduce `EPOCH_EVENT_ID_COUNTER` and +`INTERVAL_EVENT_ID_COUNTER` for that purpose. -Furthermore, we keep track of the ID of the most recently executed event (in both categories), so we'd known more easily if there have been any new ones pushed without having to explicitly query for them. For that end we use `LAST_PROCESSED_EPOCH_EVENT` and `LAST_PROCESSED_INTERVAL_EVENT` +Furthermore, we keep track of the ID of the most recently executed event (in both categories), so we'd known more easily +if there have been any new ones pushed without having to explicitly query for them. For that end we use +`LAST_PROCESSED_EPOCH_EVENT` and `LAST_PROCESSED_INTERVAL_EVENT` -Finally, rather obviously, we have to store the actual events and those are being help in `PENDING_EPOCH_EVENTS` and `PENDING_INTERVAL_EVENTS` `Map`s. +Finally, rather obviously, we have to store the actual events and those are being help in `PENDING_EPOCH_EVENTS` and +`PENDING_INTERVAL_EVENTS` `Map`s. #### Removed -- `CURRENT_EPOCH_REWARD_PARAMS` - in a way superseded by `rewards_storage::REWARDING_PARAMS` that holds current rewarding parameters for the entire interval. +- `CURRENT_EPOCH_REWARD_PARAMS` - in a way superseded by `rewards_storage::REWARDING_PARAMS` that holds current + rewarding parameters for the entire interval. - `CURRENT_REWARDED_SET_HEIGHT` - we only hold a single rewarded set at a time - `EPOCHS` - all epoch related information is included in the `Interval` data. #### Modified - `CURRENT_EPOCH` has been replaced by a differently named `CURRENT_INTERVAL` -- `REWARDED_SET` - the storage key no longer requires using the block height as there's only ever a single rewarded set at a time +- `REWARDED_SET` - the storage key no longer requires using the block height as there's only ever a single rewarded set + at a time ## Contract settings ### Overview -Contract state/settings now explicitly contain information that previously was implicit via the constants or network defaults (such as the `denom`). Also, parameters affecting rewarding, such as the active set size, were moved to more appropriate modules. +Contract state/settings now explicitly contain information that previously was implicit via the constants or network +defaults (such as the `denom`). Also, parameters affecting rewarding, such as the active set size, were moved to more +appropriate modules. ### Types/Models @@ -670,6 +784,7 @@ pub struct ContractStateParams { ### Transactions The only transaction, i.e. updating state params, is no longer a unit enum. It was changed from + ```rust pub enum ExecuteMsg { UpdateContractStateParams(ContractStateParams), @@ -677,6 +792,7 @@ pub enum ExecuteMsg { ``` to + ```rust pub enum ExecuteMsg { UpdateContractStateParams { @@ -691,7 +807,8 @@ pub enum ExecuteMsg { ##### GetState -Introduced new query to get the entire `ContractState` struct, so we'd known about, for example, the rewarding denom or the rewarding validator address. +Introduced new query to get the entire `ContractState` struct, so we'd known about, for example, the rewarding denom or +the rewarding validator address. #### Removed @@ -711,10 +828,15 @@ No relevant changes were performed to the storage structure of the contract sett ### Overview -Changing the logic behind the rewards was the main motivation behind this new contract version. The main things in this section, apart from what was already mentioned before, include but is not limited to: -- reward pool (and the staking supply) being only updated at the end of the given interval. However, the accounting is still happening as the rewards are distributed, so we'd known how much the pool should be adjusted by. -- we no longer keep any historical information regarding past epochs/parameters/etc for the purposes of rewarding. Whatever exists in the storage at the time is the thing that's going to be used for the next distribution. -- queries for reward estimation now require constant(ish) amount of gas as opposed to growing linearly with the number of epochs since last claim/compounding. +Changing the logic behind the rewards was the main motivation behind this new contract version. The main things in this +section, apart from what was already mentioned before, include but is not limited to: + +- reward pool (and the staking supply) being only updated at the end of the given interval. However, the accounting is + still happening as the rewards are distributed, so we'd known how much the pool should be adjusted by. +- we no longer keep any historical information regarding past epochs/parameters/etc for the purposes of rewarding. + Whatever exists in the storage at the time is the thing that's going to be used for the next distribution. +- queries for reward estimation now require constant(ish) amount of gas as opposed to growing linearly with the number + of epochs since last claim/compounding. - ### Types/Models @@ -723,7 +845,9 @@ Changing the logic behind the rewards was the main motivation behind this new co ##### MixNodeRewarding -The most important struct created for the purposes of the changes described. All the data here allows us to correctly determine rewards for all the delegators by scaling the value of `total_unit_reward` based on the ratio the delegation to `unit_delegation` and scaled by the unit delegation reward at the time of delegation of the delegate. +The most important struct created for the purposes of the changes described. All the data here allows us to correctly +determine rewards for all the delegators by scaling the value of `total_unit_reward` based on the ratio the delegation +to `unit_delegation` and scaled by the unit delegation reward at the time of delegation of the delegate. ```rust pub struct MixNodeRewarding { @@ -755,7 +879,8 @@ pub struct MixNodeRewarding { ##### RewardPoolChange -Whenever we distribute rewards, we keep track of how much should get removed from the reward pool and moved into the staking supply when the interval finishes. +Whenever we distribute rewards, we keep track of how much should get removed from the reward pool and moved into the +staking supply when the interval finishes. ```rust pub(crate) struct RewardPoolChange { @@ -773,59 +898,60 @@ pub(crate) struct RewardPoolChange { ##### RewardingParams and IntervalRewardParams -Those are used for keeping track of parameters used for rewarding of all nodes during a particular interval. Unless there's an exceptionally good reason for it, they remain constants within an interval. +Those are used for keeping track of parameters used for rewarding of all nodes during a particular interval. Unless +there's an exceptionally good reason for it, they remain constants within an interval. ```rust pub struct RewardingParams { - /// Parameters that should remain unchanged throughout an interval. - pub interval: IntervalRewardParams, - - // while the active set size can change between epochs to accommodate for bandwidth demands, - // the active set size should be unchanged between epochs and should only be adjusted between - // intervals. However, it makes more sense to keep both of those values together as they're - // very strongly related to each other. - pub rewarded_set_size: u32, - pub active_set_size: u32, + /// Parameters that should remain unchanged throughout an interval. + pub interval: IntervalRewardParams, + + // while the active set size can change between epochs to accommodate for bandwidth demands, + // the active set size should be unchanged between epochs and should only be adjusted between + // intervals. However, it makes more sense to keep both of those values together as they're + // very strongly related to each other. + pub rewarded_set_size: u32, + pub active_set_size: u32, } pub struct IntervalRewardParams { - /// Current value of the rewarding pool. - /// It is expected to be constant throughout the interval. - pub reward_pool: Decimal, - - /// Current value of the staking supply. - /// It is expected to be constant throughout the interval. - pub staking_supply: Decimal, - - // computed values - /// Current value of the computed reward budget per epoch, per node. - /// It is expected to be constant throughout the interval. - pub epoch_reward_budget: Decimal, - - /// Current value of the stake saturation point. - /// It is expected to be constant throughout the interval. - pub stake_saturation_point: Decimal, - - // constants(-ish) - // default: 30% - /// Current value of the sybil resistance percent (`alpha`). - /// It is not really expected to be changing very often. - /// As a matter of fact, unless there's a very specific reason, it should remain constant. - pub sybil_resistance: Percent, - - // default: 10 - /// Current active set work factor. - /// It is not really expected to be changing very often. - /// As a matter of fact, unless there's a very specific reason, it should remain constant. - pub active_set_work_factor: Decimal, - - // default: 2% - /// Current maximum interval pool emission. - /// Assuming all nodes in the rewarded set are fully saturated and have 100% performance, - /// this % of the reward pool would get distributed in rewards to all operators and its delegators. - /// It is not really expected to be changing very often. - /// As a matter of fact, unless there's a very specific reason, it should remain constant. - pub interval_pool_emission: Percent, + /// Current value of the rewarding pool. + /// It is expected to be constant throughout the interval. + pub reward_pool: Decimal, + + /// Current value of the staking supply. + /// It is expected to be constant throughout the interval. + pub staking_supply: Decimal, + + // computed values + /// Current value of the computed reward budget per epoch, per node. + /// It is expected to be constant throughout the interval. + pub epoch_reward_budget: Decimal, + + /// Current value of the stake saturation point. + /// It is expected to be constant throughout the interval. + pub stake_saturation_point: Decimal, + + // constants(-ish) + // default: 30% + /// Current value of the sybil resistance percent (`alpha`). + /// It is not really expected to be changing very often. + /// As a matter of fact, unless there's a very specific reason, it should remain constant. + pub sybil_resistance: Percent, + + // default: 10 + /// Current active set work factor. + /// It is not really expected to be changing very often. + /// As a matter of fact, unless there's a very specific reason, it should remain constant. + pub active_set_work_factor: Decimal, + + // default: 2% + /// Current maximum interval pool emission. + /// Assuming all nodes in the rewarded set are fully saturated and have 100% performance, + /// this % of the reward pool would get distributed in rewards to all operators and its delegators. + /// It is not really expected to be changing very often. + /// As a matter of fact, unless there's a very specific reason, it should remain constant. + pub interval_pool_emission: Percent, } ``` @@ -843,17 +969,22 @@ All reward-related results and parameters got consolidated, mostly into `Rewardi ##### CompoundOperatorReward, CompoundDelegatorReward, CompoundOperatorRewardOnBehalf, CompoundDelegatorRewardOnBehalf -All compounding-related transactions have been removed as they're no longer required since the compounding is happening automatically now. +All compounding-related transactions have been removed as they're no longer required since the compounding is happening +automatically now. #### Modified ##### RewardMixnode -As with previous changes, we're now rewarding given mixnode by its `NodeId` as opposed to `IdentityKey`. Furthermore, we no longer have to pass entire set of `NodeRewardParams`. Everything is implicit from the contract state, with the single exception of node `Performance`, which is now required. However, the strong typing ensures its always in the correct range. +As with previous changes, we're now rewarding given mixnode by its `NodeId` as opposed to `IdentityKey`. Furthermore, we +no longer have to pass entire set of `NodeRewardParams`. Everything is implicit from the contract state, with the single +exception of node `Performance`, which is now required. However, the strong typing ensures its always in the correct +range. ##### ClaimOperator/DelegatorReward -All claim-related operations have been renamed to `Withdraw` for consistency with cosmos-sdk. It essentially zeroes your reward and moves this amount to your account address. +All claim-related operations have been renamed to `Withdraw` for consistency with cosmos-sdk. It essentially zeroes your +reward and moves this amount to your account address. ### Queries @@ -881,23 +1012,33 @@ Renamed to `GetPendingDelegatorReward` for consistency' sake. ### Storage -As with everything in this module, storage was also completely revamped. The changes here mostly follow on the changes to data structs. +As with everything in this module, storage was also completely revamped. The changes here mostly follow on the changes +to data structs. #### Added - `REWARDING_PARAMS` - all the rewarding parameters are consolidated in a single `Item` -- `PENDING_REWARD_POOL_CHANGE` - keeping track of the reward pool changes that shall get applied at the end of the interval -- `MIXNODE_REWARDING` - per mixnode, indexed by `NodeId`, parameters required to determine operator and delegates rewards +- `PENDING_REWARD_POOL_CHANGE` - keeping track of the reward pool changes that shall get applied at the end of the + interval +- `MIXNODE_REWARDING` - per mixnode, indexed by `NodeId`, parameters required to determine operator and delegates + rewards #### Removed - `REWARD_POOL` - incorporated into `REWARDING_PARAMS` - `REWARDING_STATUS` - it was already deprecated to begin with since we're no longer explicitly rewarding delegators, -- `DELEGATOR_REWARD_CLAIMED_HEIGHT`, `OPERATOR_REWARD_CLAIMED_HEIGHT` - due to auto-compounding, we don't have to keep track of heights of reward claiming -- `EPOCH_REWARD_PARAMS` - we no longer have to retroactively determine rewards for past epochs and thus we no longer have to keep track of rewarding params for past epochs +- `DELEGATOR_REWARD_CLAIMED_HEIGHT`, `OPERATOR_REWARD_CLAIMED_HEIGHT` - due to auto-compounding, we don't have to keep + track of heights of reward claiming +- `EPOCH_REWARD_PARAMS` - we no longer have to retroactively determine rewards for past epochs and thus we no longer + have to keep track of rewarding params for past epochs ## Final remarks -As mentioned during multiple chats, I think the migration the rest of our codebase is going to be a huge undertaking mostly because of how many aspects of the system this change is affecting. From the top of my head, we'd need to definitely change our `nyxd client` (and as a result `validator-api`, `clients`, etc.) and also the vesting contract. +As mentioned during multiple chats, I think the migration the rest of our codebase is going to be a huge undertaking +mostly because of how many aspects of the system this change is affecting. From the top of my head, we'd need to +definitely change our `nyxd client` (and as a result `validator-api`, `clients`, etc.) and also the vesting contract. -With the latter case (and with the current mixnet contract), it's going to be even trickier given that the current contract is already live. We will need to adjust how the values are stored, i.e. mixnodes are now indexed by `NodeId` as opposed to `IdentityKey`. My recommendation would be to create a migration such that it would "cancel" / "return" (you name it) all existing delegations and bonds so that the users would have to make new ones under the new contract. +With the latter case (and with the current mixnet contract), it's going to be even trickier given that the current +contract is already live. We will need to adjust how the values are stored, i.e. mixnodes are now indexed by `NodeId` as +opposed to `IdentityKey`. My recommendation would be to create a migration such that it would "cancel" / "return" (you +name it) all existing delegations and bonds so that the users would have to make new ones under the new contract. diff --git a/contracts/multisig/cw3-flex-multisig/.cargo/config b/contracts/multisig/cw3-flex-multisig/.cargo/config index dbc331ef7ac..aef6ed06bd7 100644 --- a/contracts/multisig/cw3-flex-multisig/.cargo/config +++ b/contracts/multisig/cw3-flex-multisig/.cargo/config @@ -3,4 +3,4 @@ wasm = "build --release --lib --target wasm32-unknown-unknown" wasm-debug = "build --lib --target wasm32-unknown-unknown" unit-test = "test --lib" integration-test = "test --test integration" -schema = "run --bin schema --features=schema-gen" \ No newline at end of file +schema = "run --bin schema --features=cosmwasm-schema" \ No newline at end of file diff --git a/contracts/multisig/cw3-flex-multisig/Cargo.toml b/contracts/multisig/cw3-flex-multisig/Cargo.toml index f923697b770..78cd6ee4aed 100644 --- a/contracts/multisig/cw3-flex-multisig/Cargo.toml +++ b/contracts/multisig/cw3-flex-multisig/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw3-flex-multisig" -version = "1.0.0" +version = "2.0.0" authors = ["Ethan Frey "] edition = "2021" description = "Implementing cw3 with multiple voting patterns and dynamic groups" @@ -11,16 +11,14 @@ documentation = "https://docs.cosmwasm.com" [[bin]] name = "schema" -required-features = ["schema-gen"] +required-features = ["cosmwasm-schema"] [lib] crate-type = ["cdylib", "rlib"] [features] -backtraces = ["cosmwasm-std/backtraces"] # use library feature to disable all instantiate/execute/query exports library = [] -schema-gen = ["cosmwasm-schema"] [dependencies] cw-utils = { workspace = true } @@ -38,6 +36,7 @@ nym-multisig-contract-common = { path = "../../../common/cosmwasm-smart-contract nym-contracts-common = { path = "../../../common/cosmwasm-smart-contracts/contracts-common" } [dev-dependencies] -cw4-group = { path = "../cw4-group", version = "1.0.0" } +easy-addr = { path = "../../../common/cosmwasm-smart-contracts/easy_addr" } +cw4-group = { path = "../cw4-group" } cw-multi-test = { workspace = true } -cw20-base = "1.0.0" +cw20-base = { workspace = true } diff --git a/contracts/multisig/cw3-flex-multisig/schema/cw3-flex-multisig.json b/contracts/multisig/cw3-flex-multisig/schema/cw3-flex-multisig.json index 8f116581d2f..50844a73b8c 100644 --- a/contracts/multisig/cw3-flex-multisig/schema/cw3-flex-multisig.json +++ b/contracts/multisig/cw3-flex-multisig/schema/cw3-flex-multisig.json @@ -1,6 +1,6 @@ { "contract_name": "cw3-flex-multisig", - "contract_version": "1.0.0", + "contract_version": "2.0.0", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", @@ -423,7 +423,8 @@ "to_address": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -447,7 +448,8 @@ "$ref": "#/definitions/Coin" } } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -471,7 +473,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "CosmosMsg_for_Empty": { "oneOf": [ @@ -514,8 +517,9 @@ ] }, "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressible in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object", + "additionalProperties": false }, "Expiration": { "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", @@ -692,7 +696,8 @@ } ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -742,7 +747,8 @@ } ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -779,7 +785,8 @@ "format": "uint64", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -804,7 +811,8 @@ "contract_addr": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -825,7 +833,8 @@ "contract_addr": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -1402,7 +1411,8 @@ "to_address": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -1426,7 +1436,8 @@ "$ref": "#/definitions/Coin" } } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -1450,7 +1461,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "CosmosMsg_for_Empty": { "oneOf": [ @@ -1557,8 +1569,9 @@ "additionalProperties": false }, "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressible in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object", + "additionalProperties": false }, "Expiration": { "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", @@ -1853,7 +1866,8 @@ } ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -1903,7 +1917,8 @@ } ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -1940,7 +1955,8 @@ "format": "uint64", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -1965,7 +1981,8 @@ "contract_addr": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -1986,7 +2003,8 @@ "contract_addr": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2205,7 +2223,8 @@ "to_address": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2229,7 +2248,8 @@ "$ref": "#/definitions/Coin" } } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2253,7 +2273,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "CosmosMsg_for_Empty": { "oneOf": [ @@ -2360,8 +2381,9 @@ "additionalProperties": false }, "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressible in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object", + "additionalProperties": false }, "Expiration": { "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", @@ -2595,7 +2617,8 @@ } ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2645,7 +2668,8 @@ } ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2682,7 +2706,8 @@ "format": "uint64", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2707,7 +2732,8 @@ "contract_addr": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2728,7 +2754,8 @@ "contract_addr": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2784,7 +2811,8 @@ "to_address": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2808,7 +2836,8 @@ "$ref": "#/definitions/Coin" } } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2832,7 +2861,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "CosmosMsg_for_Empty": { "oneOf": [ @@ -2939,8 +2969,9 @@ "additionalProperties": false }, "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressible in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object", + "additionalProperties": false }, "Expiration": { "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", @@ -3235,7 +3266,8 @@ } ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -3285,7 +3317,8 @@ } ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -3322,7 +3355,8 @@ "format": "uint64", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -3347,7 +3381,8 @@ "contract_addr": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -3368,7 +3403,8 @@ "contract_addr": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false diff --git a/contracts/multisig/cw3-flex-multisig/schema/raw/execute.json b/contracts/multisig/cw3-flex-multisig/schema/raw/execute.json index 2602f1020c5..88e78bd86fa 100644 --- a/contracts/multisig/cw3-flex-multisig/schema/raw/execute.json +++ b/contracts/multisig/cw3-flex-multisig/schema/raw/execute.json @@ -158,7 +158,8 @@ "to_address": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -182,7 +183,8 @@ "$ref": "#/definitions/Coin" } } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -206,7 +208,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "CosmosMsg_for_Empty": { "oneOf": [ @@ -249,8 +252,9 @@ ] }, "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressible in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object", + "additionalProperties": false }, "Expiration": { "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", @@ -427,7 +431,8 @@ } ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -477,7 +482,8 @@ } ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -514,7 +520,8 @@ "format": "uint64", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -539,7 +546,8 @@ "contract_addr": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -560,7 +568,8 @@ "contract_addr": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false diff --git a/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_list_proposals.json b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_list_proposals.json index 6bf521237ec..d172ea7039a 100644 --- a/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_list_proposals.json +++ b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_list_proposals.json @@ -45,7 +45,8 @@ "to_address": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -69,7 +70,8 @@ "$ref": "#/definitions/Coin" } } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -93,7 +95,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "CosmosMsg_for_Empty": { "oneOf": [ @@ -200,8 +203,9 @@ "additionalProperties": false }, "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressible in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object", + "additionalProperties": false }, "Expiration": { "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", @@ -496,7 +500,8 @@ } ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -546,7 +551,8 @@ } ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -583,7 +589,8 @@ "format": "uint64", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -608,7 +615,8 @@ "contract_addr": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -629,7 +637,8 @@ "contract_addr": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false diff --git a/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_proposal.json b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_proposal.json index 0a16e26436e..d6a98560e37 100644 --- a/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_proposal.json +++ b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_proposal.json @@ -91,7 +91,8 @@ "to_address": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -115,7 +116,8 @@ "$ref": "#/definitions/Coin" } } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -139,7 +141,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "CosmosMsg_for_Empty": { "oneOf": [ @@ -246,8 +249,9 @@ "additionalProperties": false }, "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressible in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object", + "additionalProperties": false }, "Expiration": { "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", @@ -481,7 +485,8 @@ } ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -531,7 +536,8 @@ } ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -568,7 +574,8 @@ "format": "uint64", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -593,7 +600,8 @@ "contract_addr": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -614,7 +622,8 @@ "contract_addr": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false diff --git a/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_reverse_proposals.json b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_reverse_proposals.json index 6bf521237ec..d172ea7039a 100644 --- a/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_reverse_proposals.json +++ b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_reverse_proposals.json @@ -45,7 +45,8 @@ "to_address": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -69,7 +70,8 @@ "$ref": "#/definitions/Coin" } } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -93,7 +95,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "CosmosMsg_for_Empty": { "oneOf": [ @@ -200,8 +203,9 @@ "additionalProperties": false }, "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressible in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object", + "additionalProperties": false }, "Expiration": { "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", @@ -496,7 +500,8 @@ } ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -546,7 +551,8 @@ } ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -583,7 +589,8 @@ "format": "uint64", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -608,7 +615,8 @@ "contract_addr": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -629,7 +637,8 @@ "contract_addr": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false diff --git a/contracts/multisig/cw3-flex-multisig/src/contract.rs b/contracts/multisig/cw3-flex-multisig/src/contract.rs index 555eb02f39e..f1dbf489fe7 100644 --- a/contracts/multisig/cw3-flex-multisig/src/contract.rs +++ b/contracts/multisig/cw3-flex-multisig/src/contract.rs @@ -3,7 +3,7 @@ use std::cmp::Ordering; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_binary, Binary, BlockInfo, CosmosMsg, Deps, DepsMut, Empty, Env, MessageInfo, Order, + to_json_binary, Binary, BlockInfo, CosmosMsg, Deps, DepsMut, Empty, Env, MessageInfo, Order, Response, StdResult, }; @@ -314,26 +314,30 @@ pub fn execute_membership_hook( #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::Threshold {} => to_binary(&query_threshold(deps)?), - QueryMsg::Proposal { proposal_id } => to_binary(&query_proposal(deps, env, proposal_id)?), - QueryMsg::Vote { proposal_id, voter } => to_binary(&query_vote(deps, proposal_id, voter)?), + QueryMsg::Threshold {} => to_json_binary(&query_threshold(deps)?), + QueryMsg::Proposal { proposal_id } => { + to_json_binary(&query_proposal(deps, env, proposal_id)?) + } + QueryMsg::Vote { proposal_id, voter } => { + to_json_binary(&query_vote(deps, proposal_id, voter)?) + } QueryMsg::ListProposals { start_after, limit } => { - to_binary(&list_proposals(deps, env, start_after, limit)?) + to_json_binary(&list_proposals(deps, env, start_after, limit)?) } QueryMsg::ReverseProposals { start_before, limit, - } => to_binary(&reverse_proposals(deps, env, start_before, limit)?), + } => to_json_binary(&reverse_proposals(deps, env, start_before, limit)?), QueryMsg::ListVotes { proposal_id, start_after, limit, - } => to_binary(&list_votes(deps, proposal_id, start_after, limit)?), - QueryMsg::Voter { address } => to_binary(&query_voter(deps, address)?), + } => to_json_binary(&list_votes(deps, proposal_id, start_after, limit)?), + QueryMsg::Voter { address } => to_json_binary(&query_voter(deps, address)?), QueryMsg::ListVoters { start_after, limit } => { - to_binary(&list_voters(deps, start_after, limit)?) + to_json_binary(&list_voters(deps, start_after, limit)?) } - QueryMsg::Config {} => to_binary(&query_config(deps)?), + QueryMsg::Config {} => to_json_binary(&query_config(deps)?), } } @@ -504,6 +508,7 @@ pub fn migrate(deps: DepsMut<'_>, _env: Env, msg: MigrateMsg) -> Result>(addr: T, weight: u64) -> Member { Member { @@ -1797,7 +1801,7 @@ mod tests { // updates VOTER2 power to 21 -> with snapshot, vote doesn't pass proposal // adds NEWBIE with 2 power -> with snapshot, invalid vote // removes VOTER3 -> with snapshot, can vote on proposal - let newbie: &str = "newbie"; + let newbie: &str = addr!("newbie"); let update_msg = nym_group_contract_common::msg::ExecuteMsg::UpdateMembers { remove: vec![VOTER3.into()], add: vec![member(VOTER2, 21), member(newbie, 2)], @@ -2069,7 +2073,7 @@ mod tests { app.update_block(|block| block.height += 2); // admin changes the group (3 -> 0, 2 -> 9, 0 -> 29) - total = 56, require 29 to pass - let newbie: &str = "newbie"; + let newbie: &str = addr!("newbie"); let update_msg = nym_group_contract_common::msg::ExecuteMsg::UpdateMembers { remove: vec![VOTER3.into()], add: vec![member(VOTER2, 9), member(newbie, 29)], @@ -2176,7 +2180,7 @@ mod tests { app.update_block(|block| block.height += 2); // admin changes the group (3 -> 0, 2 -> 9, 0 -> 28) - total = 55, require 28 to pass - let newbie: &str = "newbie"; + let newbie: &str = addr!("newbie"); let update_msg = nym_group_contract_common::msg::ExecuteMsg::UpdateMembers { remove: vec![VOTER3.into()], add: vec![member(VOTER2, 9), member(newbie, 29)], diff --git a/contracts/multisig/cw3-flex-multisig/src/lib.rs b/contracts/multisig/cw3-flex-multisig/src/lib.rs index bc3020f956e..0dd0a102439 100644 --- a/contracts/multisig/cw3-flex-multisig/src/lib.rs +++ b/contracts/multisig/cw3-flex-multisig/src/lib.rs @@ -20,6 +20,6 @@ signers to share signatures off chain. For more information on this contract, please check out the [README](https://github.com/CosmWasm/cw-plus/blob/main/contracts/cw3-flex-multisig/README.md). - */ +*/ pub mod contract; diff --git a/contracts/multisig/cw4-group/Cargo.toml b/contracts/multisig/cw4-group/Cargo.toml index 9798ab626fc..089938fe4e2 100644 --- a/contracts/multisig/cw4-group/Cargo.toml +++ b/contracts/multisig/cw4-group/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw4-group" -version = "1.0.0" +version = "2.0.0" authors = ["Ethan Frey "] edition = "2021" description = "Simple cw4 implementation of group membership controlled by admin " @@ -23,8 +23,6 @@ name = "schema" 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 = [] @@ -44,3 +42,5 @@ schemars = "0.8.1" serde = { version = "1.0.103", default-features = false, features = ["derive"] } thiserror = { workspace = true } +[dev-dependencies] +easy-addr = { path = "../../../common/cosmwasm-smart-contracts/easy_addr" } \ No newline at end of file diff --git a/contracts/multisig/cw4-group/schema/cw4-group.json b/contracts/multisig/cw4-group/schema/cw4-group.json index b6c6f5df448..ce43e09bc97 100644 --- a/contracts/multisig/cw4-group/schema/cw4-group.json +++ b/contracts/multisig/cw4-group/schema/cw4-group.json @@ -1,6 +1,6 @@ { "contract_name": "cw4-group", - "contract_version": "1.0.0", + "contract_version": "2.0.0", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/contracts/multisig/cw4-group/src/contract.rs b/contracts/multisig/cw4-group/src/contract.rs index 46d853e0220..582aac5dcda 100644 --- a/contracts/multisig/cw4-group/src/contract.rs +++ b/contracts/multisig/cw4-group/src/contract.rs @@ -1,8 +1,8 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - attr, to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Order, Response, StdResult, - SubMsg, Uint64, + attr, to_json_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Order, Response, + StdResult, SubMsg, Uint64, }; use cw2::set_contract_version; use cw4::{ @@ -166,15 +166,15 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { QueryMsg::Member { addr, at_height: height, - } => to_binary(&query_member(deps, addr, height)?), + } => to_json_binary(&query_member(deps, addr, height)?), QueryMsg::ListMembers { start_after, limit } => { - to_binary(&query_list_members(deps, start_after, limit)?) + to_json_binary(&query_list_members(deps, start_after, limit)?) } QueryMsg::TotalWeight { at_height: height } => { - to_binary(&query_total_weight(deps, height)?) + to_json_binary(&query_total_weight(deps, height)?) } - QueryMsg::Admin {} => to_binary(&ADMIN.query_admin(deps)?), - QueryMsg::Hooks {} => to_binary(&HOOKS.query_hooks(deps)?), + QueryMsg::Admin {} => to_json_binary(&ADMIN.query_admin(deps)?), + QueryMsg::Hooks {} => to_json_binary(&HOOKS.query_hooks(deps)?), } } diff --git a/contracts/multisig/cw4-group/src/helpers.rs b/contracts/multisig/cw4-group/src/helpers.rs index c6b5d0187c0..e8cb01a33f3 100644 --- a/contracts/multisig/cw4-group/src/helpers.rs +++ b/contracts/multisig/cw4-group/src/helpers.rs @@ -1,7 +1,7 @@ use std::ops::Deref; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{to_binary, Addr, CosmosMsg, StdResult, WasmMsg}; +use cosmwasm_std::{to_json_binary, Addr, CosmosMsg, StdResult, WasmMsg}; use cw4::{Cw4Contract, Member}; use crate::ContractError; @@ -30,7 +30,7 @@ impl Cw4GroupContract { fn encode_msg(&self, msg: ExecuteMsg) -> StdResult { Ok(WasmMsg::Execute { contract_addr: self.addr().into(), - msg: to_binary(&msg)?, + msg: to_json_binary(&msg)?, funds: vec![], } .into()) diff --git a/contracts/multisig/cw4-group/src/tests.rs b/contracts/multisig/cw4-group/src/tests.rs index 11f39f70847..ed49a646727 100644 --- a/contracts/multisig/cw4-group/src/tests.rs +++ b/contracts/multisig/cw4-group/src/tests.rs @@ -1,5 +1,5 @@ -use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; -use cosmwasm_std::{from_slice, Addr, Api, DepsMut, OwnedDeps, Querier, Storage, SubMsg}; +use cosmwasm_std::testing::{message_info, mock_dependencies, mock_env}; +use cosmwasm_std::{from_json, Addr, Api, DepsMut, OwnedDeps, Querier, Storage, SubMsg}; use cw4::{member_key, Member, MemberChangedHookMsg, MemberDiff, TOTAL_KEY}; use cw_controllers::{AdminError, HookError}; @@ -10,10 +10,13 @@ use crate::state::{ADMIN, HOOKS}; use crate::ContractError; use nym_group_contract_common::msg::{ExecuteMsg, InstantiateMsg}; -const INIT_ADMIN: &str = "juan"; -const USER1: &str = "somebody"; -const USER2: &str = "else"; -const USER3: &str = "funny"; +use easy_addr::addr; + +const INIT_ADMIN: &str = addr!("juan"); +const CREATOR: &str = addr!("creator"); +const USER1: &str = addr!("somebody"); +const USER2: &str = addr!("else"); +const USER3: &str = addr!("funny"); fn set_up(deps: DepsMut) { let msg = InstantiateMsg { @@ -29,7 +32,7 @@ fn set_up(deps: DepsMut) { }, ], }; - let info = mock_info("creator", &[]); + let info = message_info(&Addr::unchecked(CREATOR), &[]); instantiate(deps, mock_env(), info, msg).unwrap(); } @@ -86,7 +89,7 @@ fn duplicate_members_instantiation() { }, ], }; - let info = mock_info("creator", &[]); + let info = message_info(&Addr::unchecked(CREATOR), &[]); let err = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap_err(); assert_eq!( err, @@ -278,15 +281,15 @@ fn add_remove_hooks() { let hooks = HOOKS.query_hooks(deps.as_ref()).unwrap(); assert!(hooks.hooks.is_empty()); - let contract1 = String::from("hook1"); - let contract2 = String::from("hook2"); + let contract1 = deps.api.addr_make("hook1").to_string(); + let contract2 = deps.api.addr_make("hook2").to_string(); let add_msg = ExecuteMsg::AddHook { addr: contract1.clone(), }; // non-admin cannot add hook - let user_info = mock_info(USER1, &[]); + let user_info = message_info(&Addr::unchecked(USER1), &[]); let err = execute( deps.as_mut(), mock_env(), @@ -297,7 +300,7 @@ fn add_remove_hooks() { assert_eq!(err, HookError::Admin(AdminError::NotAdmin {}).into()); // admin can add it, and it appears in the query - let admin_info = mock_info(INIT_ADMIN, &[]); + let admin_info = message_info(&Addr::unchecked(INIT_ADMIN), &[]); let _ = execute( deps.as_mut(), mock_env(), @@ -346,11 +349,11 @@ fn hooks_fire() { let hooks = HOOKS.query_hooks(deps.as_ref()).unwrap(); assert!(hooks.hooks.is_empty()); - let contract1 = String::from("hook1"); - let contract2 = String::from("hook2"); + let contract1 = deps.api.addr_make("hook1").to_string(); + let contract2 = deps.api.addr_make("hook2").to_string(); // register 2 hooks - let admin_info = mock_info(INIT_ADMIN, &[]); + let admin_info = message_info(&Addr::unchecked(INIT_ADMIN), &[]); let add_msg = ExecuteMsg::AddHook { addr: contract1.clone(), }; @@ -407,12 +410,12 @@ fn raw_queries_work() { // get total from raw key let total_raw = deps.storage.get(TOTAL_KEY.as_bytes()).unwrap(); - let total: u64 = from_slice(&total_raw).unwrap(); + let total: u64 = from_json(total_raw).unwrap(); assert_eq!(17, total); // get member votes from raw key let member2_raw = deps.storage.get(&member_key(USER2)).unwrap(); - let member2: u64 = from_slice(&member2_raw).unwrap(); + let member2: u64 = from_json(member2_raw).unwrap(); assert_eq!(6, member2); // and execute misses diff --git a/contracts/nym-pool/Cargo.toml b/contracts/nym-pool/Cargo.toml new file mode 100644 index 00000000000..89a9aac7a60 --- /dev/null +++ b/contracts/nym-pool/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "nym-pool-contract" +version = "0.1.0" +edition = { workspace = true } +authors = { workspace = true } +license = { workspace = true } +repository = { workspace = true } + +[[bin]] +name = "schema" +required-features = ["schema-gen"] + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +cosmwasm-std = { workspace = true } +cw2 = { workspace = true } +cw-storage-plus = { workspace = true } +cw-controllers = { workspace = true } + +nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common" } +nym-pool-contract-common = { path = "../../common/cosmwasm-smart-contracts/nym-pool-contract" } + + +[dev-dependencies] +anyhow = { workspace = true } +serde = { workspace = true } +rand_chacha = { workspace = true } +rand = { workspace = true } +cw-multi-test = { workspace = true } diff --git a/contracts/nym-pool/Makefile b/contracts/nym-pool/Makefile new file mode 100644 index 00000000000..086fa71ad3c --- /dev/null +++ b/contracts/nym-pool/Makefile @@ -0,0 +1,5 @@ +wasm: + RUSTFLAGS='-C link-arg=-s' cargo build --release --target wasm32-unknown-unknown + +generate-schema: + cargo schema diff --git a/contracts/nym-pool/src/bin/schema.rs b/contracts/nym-pool/src/bin/schema.rs new file mode 100644 index 00000000000..1ef91538e71 --- /dev/null +++ b/contracts/nym-pool/src/bin/schema.rs @@ -0,0 +1,13 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmwasm_schema::write_api; + +fn main() { + write_api! { + // instantiate: InstantiateMsg, + // query: QueryMsg, + // execute: ExecuteMsg, + // migrate: MigrateMsg, + } +} diff --git a/contracts/nym-pool/src/contract.rs b/contracts/nym-pool/src/contract.rs new file mode 100644 index 00000000000..9978d39fe62 --- /dev/null +++ b/contracts/nym-pool/src/contract.rs @@ -0,0 +1,327 @@ +// Copyright 2025 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::queries::{ + query_admin, query_available_tokens, query_grant, query_granter, query_granters_paged, + query_grants_paged, query_locked_tokens, query_locked_tokens_paged, query_total_locked_tokens, +}; +use crate::storage::NYM_POOL_STORAGE; +use crate::transactions::{ + try_add_new_granter, try_grant_allowance, try_lock_allowance, try_remove_expired, + try_revoke_grant, try_revoke_granter, try_unlock_allowance, try_update_contract_admin, + try_use_allowance, try_use_locked_allowance, try_withdraw_allowance, + try_withdraw_locked_allowance, +}; +use cosmwasm_std::{ + entry_point, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, +}; +use nym_contracts_common::set_build_information; +use nym_pool_contract_common::{ + ExecuteMsg, InstantiateMsg, MigrateMsg, NymPoolContractError, QueryMsg, +}; + +const CONTRACT_NAME: &str = "crate:nym-pool-contract"; +const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +#[entry_point] +pub fn instantiate( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: InstantiateMsg, +) -> Result { + cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + set_build_information!(deps.storage)?; + + NYM_POOL_STORAGE.initialise(deps, env, info.sender, &msg.pool_denomination, msg.grants)?; + + Ok(Response::default()) +} + +#[entry_point] +pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> Result { + match msg { + ExecuteMsg::UpdateAdmin { + admin, + update_granter_set, + } => try_update_contract_admin(deps, env, info, admin, update_granter_set), + ExecuteMsg::GrantAllowance { grantee, allowance } => { + try_grant_allowance(deps, env, info, grantee, *allowance) + } + ExecuteMsg::RevokeAllowance { grantee } => try_revoke_grant(deps, env, info, grantee), + ExecuteMsg::UseAllowance { recipients } => try_use_allowance(deps, env, info, recipients), + ExecuteMsg::WithdrawAllowance { amount } => try_withdraw_allowance(deps, env, info, amount), + ExecuteMsg::LockAllowance { amount } => try_lock_allowance(deps, env, info, amount), + ExecuteMsg::UnlockAllowance { amount } => try_unlock_allowance(deps, env, info, amount), + ExecuteMsg::UseLockedAllowance { recipients } => { + try_use_locked_allowance(deps, env, info, recipients) + } + ExecuteMsg::WithdrawLockedAllowance { amount } => { + try_withdraw_locked_allowance(deps, env, info, amount) + } + ExecuteMsg::AddNewGranter { granter } => try_add_new_granter(deps, env, info, granter), + ExecuteMsg::RevokeGranter { granter } => try_revoke_granter(deps, env, info, granter), + ExecuteMsg::RemoveExpiredGrant { grantee } => try_remove_expired(deps, env, info, grantee), + } +} + +#[entry_point] +pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result { + match msg { + QueryMsg::Admin {} => Ok(to_json_binary(&query_admin(deps)?)?), + QueryMsg::GetAvailableTokens {} => Ok(to_json_binary(&query_available_tokens(deps, env)?)?), + QueryMsg::GetTotalLockedTokens {} => Ok(to_json_binary(&query_total_locked_tokens(deps)?)?), + QueryMsg::GetLockedTokens { grantee } => { + Ok(to_json_binary(&query_locked_tokens(deps, grantee)?)?) + } + QueryMsg::GetLockedTokensPaged { limit, start_after } => Ok(to_json_binary( + &query_locked_tokens_paged(deps, limit, start_after)?, + )?), + QueryMsg::GetGrant { grantee } => Ok(to_json_binary(&query_grant(deps, env, grantee)?)?), + QueryMsg::GetGranter { granter } => Ok(to_json_binary(&query_granter(deps, granter)?)?), + QueryMsg::GetGrantersPaged { limit, start_after } => Ok(to_json_binary( + &query_granters_paged(deps, limit, start_after)?, + )?), + QueryMsg::GetGrantsPaged { limit, start_after } => Ok(to_json_binary( + &query_grants_paged(deps, env, limit, start_after)?, + )?), + } +} + +#[entry_point] +pub fn migrate( + deps: DepsMut, + _env: Env, + _msg: MigrateMsg, +) -> Result { + set_build_information!(deps.storage)?; + cw2::ensure_from_older_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + Ok(Default::default()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(test)] + mod contract_instantiaton { + use super::*; + use crate::storage::NYM_POOL_STORAGE; + use crate::testing::TEST_DENOM; + use cosmwasm_std::testing::{message_info, mock_dependencies, mock_env}; + + #[test] + fn sets_contract_admin_to_the_message_sender() -> anyhow::Result<()> { + let mut deps = mock_dependencies(); + let env = mock_env(); + let init_msg = InstantiateMsg { + pool_denomination: TEST_DENOM.to_string(), + grants: Default::default(), + }; + + let some_sender = deps.api.addr_make("some_sender"); + instantiate( + deps.as_mut(), + env, + message_info(&some_sender, &[]), + init_msg, + )?; + + NYM_POOL_STORAGE + .contract_admin + .assert_admin(deps.as_ref(), &some_sender)?; + + Ok(()) + } + + #[test] + fn sets_the_pool_denomination() -> anyhow::Result<()> { + let mut deps = mock_dependencies(); + let env = mock_env(); + let init_msg = InstantiateMsg { + pool_denomination: "some_denom".to_string(), + grants: Default::default(), + }; + + let some_sender = deps.api.addr_make("some_sender"); + instantiate( + deps.as_mut(), + env, + message_info(&some_sender, &[]), + init_msg, + )?; + + assert_eq!( + NYM_POOL_STORAGE + .pool_denomination + .load(deps.as_ref().storage)?, + "some_denom" + ); + + Ok(()) + } + + #[test] + fn adds_sender_to_set_of_initial_granters() -> anyhow::Result<()> { + let mut deps = mock_dependencies(); + let env = mock_env(); + let init_msg = InstantiateMsg { + pool_denomination: TEST_DENOM.to_string(), + grants: Default::default(), + }; + + let some_sender = deps.api.addr_make("some_sender"); + instantiate( + deps.as_mut(), + env, + message_info(&some_sender, &[]), + init_msg, + )?; + + let granter = query_granter(deps.as_ref(), some_sender.to_string())?; + assert!(granter.information.is_some()); + + Ok(()) + } + + #[cfg(test)] + mod setting_initial_grants { + use super::*; + use crate::testing::deps_with_balance; + use cosmwasm_std::{coin, Order, Storage}; + use nym_pool_contract_common::{Allowance, BasicAllowance, Grant, GranteeAddress}; + use std::collections::HashMap; + + fn all_grants(storage: &dyn Storage) -> HashMap { + NYM_POOL_STORAGE + .grants + .range(storage, None, None, Order::Ascending) + .collect::, _>>() + .unwrap() + } + + #[test] + fn with_empty_map() -> anyhow::Result<()> { + let mut deps = mock_dependencies(); + let env = mock_env(); + let grants = HashMap::new(); + let init_msg = InstantiateMsg { + pool_denomination: TEST_DENOM.to_string(), + grants, + }; + + let some_sender = deps.api.addr_make("some_sender"); + instantiate( + deps.as_mut(), + env, + message_info(&some_sender, &[]), + init_msg, + )?; + + assert!(all_grants(&deps.storage).is_empty()); + Ok(()) + } + + #[test] + fn with_insufficient_tokens() -> anyhow::Result<()> { + // limited grant + let mut deps = mock_dependencies(); + let env = mock_env(); + let mut grants = HashMap::new(); + grants.insert( + deps.api.addr_make("grantee1").to_string(), + Allowance::Basic(BasicAllowance { + spend_limit: Some(coin(100, TEST_DENOM)), + expiration_unix_timestamp: None, + }), + ); + let init_msg = InstantiateMsg { + pool_denomination: TEST_DENOM.to_string(), + grants, + }; + + let some_sender = deps.api.addr_make("some_sender"); + let res = instantiate( + deps.as_mut(), + env, + message_info(&some_sender, &[]), + init_msg, + ); + assert!(res.is_err()); + + // unlimited grant + let mut deps = mock_dependencies(); + let env = mock_env(); + let mut grants = HashMap::new(); + grants.insert( + deps.api.addr_make("grantee1").to_string(), + Allowance::Basic(BasicAllowance::unlimited()), + ); + let init_msg = InstantiateMsg { + pool_denomination: TEST_DENOM.to_string(), + grants, + }; + + let some_sender = deps.api.addr_make("some_sender"); + let res = instantiate( + deps.as_mut(), + env, + message_info(&some_sender, &[]), + init_msg, + ); + assert!(res.is_err()); + + Ok(()) + } + + #[test] + fn with_valid_request() -> anyhow::Result<()> { + let env = mock_env(); + let mut deps = deps_with_balance(&env); + let mut grants = HashMap::new(); + grants.insert( + deps.api.addr_make("grantee1").to_string(), + Allowance::Basic(BasicAllowance { + spend_limit: Some(coin(100, TEST_DENOM)), + expiration_unix_timestamp: None, + }), + ); + grants.insert( + deps.api.addr_make("grantee2").to_string(), + Allowance::Basic(BasicAllowance { + spend_limit: Some(coin(200, TEST_DENOM)), + expiration_unix_timestamp: None, + }), + ); + grants.insert( + deps.api.addr_make("grantee3").to_string(), + Allowance::Basic(BasicAllowance { + spend_limit: Some(coin(300, TEST_DENOM)), + expiration_unix_timestamp: None, + }), + ); + let init_msg = InstantiateMsg { + pool_denomination: TEST_DENOM.to_string(), + grants, + }; + + let some_sender = deps.api.addr_make("some_sender"); + instantiate( + deps.as_mut(), + env, + message_info(&some_sender, &[coin(600, TEST_DENOM)]), + init_msg, + )?; + + assert_eq!(all_grants(&deps.storage).len(), 3); + Ok(()) + } + } + } +} diff --git a/contracts/nym-pool/src/helpers.rs b/contracts/nym-pool/src/helpers.rs new file mode 100644 index 00000000000..e71b9f11483 --- /dev/null +++ b/contracts/nym-pool/src/helpers.rs @@ -0,0 +1,57 @@ +// Copyright 2025 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::storage::NYM_POOL_STORAGE; +use cosmwasm_std::{Coin, Storage}; +use nym_pool_contract_common::NymPoolContractError; + +pub fn validate_usage_coin(storage: &dyn Storage, coin: &Coin) -> Result<(), NymPoolContractError> { + let denom = NYM_POOL_STORAGE.pool_denomination.load(storage)?; + + if coin.amount.is_zero() { + return Err(NymPoolContractError::EmptyUsageRequest); + } + + if coin.denom != denom { + return Err(NymPoolContractError::InvalidDenom { + expected: denom, + got: coin.denom.to_string(), + }); + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::storage::NymPoolStorage; + use crate::testing::TestSetup; + use cosmwasm_std::coin; + + #[test] + fn validating_coin_usage() -> anyhow::Result<()> { + let test = TestSetup::init(); + let storage = NymPoolStorage::new(); + let denom = storage.pool_denomination.load(test.storage())?; + + // amount has to be non-zero + assert_eq!( + validate_usage_coin(test.storage(), &coin(0, &denom)).unwrap_err(), + NymPoolContractError::EmptyUsageRequest + ); + + // denom has to match the value set in the storage + assert_eq!( + validate_usage_coin(test.storage(), &coin(1000, "bad-denom")).unwrap_err(), + NymPoolContractError::InvalidDenom { + expected: denom.to_string(), + got: "bad-denom".to_string(), + } + ); + + assert!(validate_usage_coin(test.storage(), &coin(1000, denom)).is_ok()); + + Ok(()) + } +} diff --git a/contracts/nym-pool/src/lib.rs b/contracts/nym-pool/src/lib.rs new file mode 100644 index 00000000000..a1aca86201d --- /dev/null +++ b/contracts/nym-pool/src/lib.rs @@ -0,0 +1,17 @@ +// Copyright 2025 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +#![warn(clippy::expect_used)] +#![warn(clippy::unwrap_used)] +#![warn(clippy::todo)] +#![warn(clippy::dbg_macro)] + +pub mod contract; +pub mod queued_migrations; +pub mod storage; + +mod helpers; +mod queries; +#[cfg(test)] +pub mod testing; +mod transactions; diff --git a/contracts/nym-pool/src/queries.rs b/contracts/nym-pool/src/queries.rs new file mode 100644 index 00000000000..997d2c5d677 --- /dev/null +++ b/contracts/nym-pool/src/queries.rs @@ -0,0 +1,642 @@ +// Copyright 2025 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::storage::{retrieval_limits, NYM_POOL_STORAGE}; +use cosmwasm_std::{Coin, Deps, Env, Order, StdResult}; +use cw_controllers::AdminResponse; +use cw_storage_plus::Bound; +use nym_pool_contract_common::{ + AvailableTokensResponse, GrantInformation, GrantResponse, GranterDetails, GranterResponse, + GrantersPagedResponse, GrantsPagedResponse, LockedTokens, LockedTokensPagedResponse, + LockedTokensResponse, NymPoolContractError, TotalLockedTokensResponse, +}; + +pub fn query_admin(deps: Deps) -> Result { + NYM_POOL_STORAGE + .contract_admin + .query_admin(deps) + .map_err(Into::into) +} + +pub fn query_available_tokens( + deps: Deps, + env: Env, +) -> Result { + Ok(AvailableTokensResponse { + available: NYM_POOL_STORAGE.available_tokens(deps, &env)?, + }) +} + +pub fn query_total_locked_tokens( + deps: Deps, +) -> Result { + let denom = NYM_POOL_STORAGE.pool_denomination.load(deps.storage)?; + let amount = NYM_POOL_STORAGE.locked.total_locked.load(deps.storage)?; + Ok(TotalLockedTokensResponse { + locked: Coin::new(amount, denom), + }) +} + +pub fn query_locked_tokens( + deps: Deps, + grantee: String, +) -> Result { + let grantee = deps.api.addr_validate(&grantee)?; + let denom = NYM_POOL_STORAGE.pool_denomination.load(deps.storage)?; + let amount = NYM_POOL_STORAGE + .locked + .maybe_grantee_locked(deps.storage, &grantee)?; + + Ok(LockedTokensResponse { + locked: amount.map(|amount| Coin::new(amount, denom)), + grantee, + }) +} + +pub fn query_locked_tokens_paged( + deps: Deps, + limit: Option, + start_after: Option, +) -> Result { + let limit = limit + .unwrap_or(retrieval_limits::LOCKED_TOKENS_DEFAULT_LIMIT) + .min(retrieval_limits::LOCKED_TOKENS_MAX_LIMIT) as usize; + let grantee = start_after + .map(|grantee| deps.api.addr_validate(&grantee)) + .transpose()?; + let denom = NYM_POOL_STORAGE.pool_denomination.load(deps.storage)?; + + let start = grantee.map(Bound::exclusive); + + let locked = NYM_POOL_STORAGE + .locked + .grantees + .range(deps.storage, start, None, Order::Ascending) + .take(limit) + .map(|res| { + res.map(|(grantee, amount)| LockedTokens { + grantee, + locked: Coin::new(amount, &denom), + }) + }) + .collect::>>()?; + + let start_next_after = locked.last().map(|locked| locked.grantee.to_string()); + + Ok(LockedTokensPagedResponse { + locked, + start_next_after, + }) +} + +pub fn query_grant( + deps: Deps, + env: Env, + grantee: String, +) -> Result { + let grantee = deps.api.addr_validate(&grantee)?; + let grant = NYM_POOL_STORAGE.try_load_grant(deps, &grantee)?; + + Ok(GrantResponse { + grant: grant.map(|grant| GrantInformation { + expired: grant.allowance.expired(&env), + grant, + }), + grantee, + }) +} + +pub fn query_granter(deps: Deps, granter: String) -> Result { + let granter = deps.api.addr_validate(&granter)?; + + Ok(GranterResponse { + information: NYM_POOL_STORAGE.try_load_granter(deps, &granter)?, + granter, + }) +} + +pub fn query_grants_paged( + deps: Deps, + env: Env, + limit: Option, + start_after: Option, +) -> Result { + let limit = limit + .unwrap_or(retrieval_limits::GRANTERS_DEFAULT_LIMIT) + .min(retrieval_limits::GRANTERS_MAX_LIMIT) as usize; + let grantee = start_after + .map(|grantee| deps.api.addr_validate(&grantee)) + .transpose()?; + + let start = grantee.map(Bound::exclusive); + + let grants = NYM_POOL_STORAGE + .grants + .range(deps.storage, start, None, Order::Ascending) + .take(limit) + .map(|res| { + res.map(|(_, grant)| GrantInformation { + expired: grant.allowance.expired(&env), + grant, + }) + }) + .collect::>>()?; + + let start_next_after = grants.last().map(|info| info.grant.grantee.to_string()); + + Ok(GrantsPagedResponse { + grants, + start_next_after, + }) +} + +pub fn query_granters_paged( + deps: Deps, + limit: Option, + start_after: Option, +) -> Result { + let limit = limit + .unwrap_or(retrieval_limits::GRANTERS_DEFAULT_LIMIT) + .min(retrieval_limits::GRANTERS_MAX_LIMIT) as usize; + let granter = start_after + .map(|granter| deps.api.addr_validate(&granter)) + .transpose()?; + let start = granter.map(Bound::exclusive); + + let granters = NYM_POOL_STORAGE + .granters + .range(deps.storage, start, None, Order::Ascending) + .take(limit) + .map(|res| res.map(|(granter, info)| GranterDetails::from((granter, info)))) + .collect::>>()?; + + let start_next_after = granters.last().map(|details| details.granter.to_string()); + + Ok(GrantersPagedResponse { + granters, + start_next_after, + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::contract::instantiate; + use crate::testing::{TestSetup, TEST_DENOM}; + use cosmwasm_std::testing::{message_info, mock_dependencies_with_balance, mock_env}; + use cosmwasm_std::{coin, Uint128}; + use nym_pool_contract_common::{Allowance, BasicAllowance, GranterInformation, InstantiateMsg}; + + #[cfg(test)] + mod admin_query { + use super::*; + use crate::testing::TestSetup; + use nym_pool_contract_common::ExecuteMsg; + + #[test] + fn returns_current_admin() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + + let initial_admin = test.admin_unchecked(); + + // initial + let res = query_admin(test.deps())?; + assert_eq!(res.admin, Some(initial_admin.to_string())); + + let new_admin = test.generate_account(); + + // sanity check + assert_ne!(initial_admin, new_admin); + + // after update + test.execute_msg( + initial_admin.clone(), + &ExecuteMsg::UpdateAdmin { + admin: new_admin.to_string(), + update_granter_set: None, + }, + )?; + + let updated_admin = query_admin(test.deps())?; + assert_eq!(updated_admin.admin, Some(new_admin.to_string())); + + Ok(()) + } + } + + #[test] + fn available_tokens_query() { + // no need to test the inner functionalities as this is dealt with in the storage tests + // (i.e. logic to do with calculating diff against locked tokens, etc.) + let env = mock_env(); + let mut deps = mock_dependencies_with_balance(&[coin(100, TEST_DENOM)]); + + let init_msg = InstantiateMsg { + pool_denomination: TEST_DENOM.to_string(), + grants: Default::default(), + }; + + let some_sender = deps.api.addr_make("some_sender"); + instantiate( + deps.as_mut(), + env.clone(), + message_info(&some_sender, &[]), + init_msg, + ) + .unwrap(); + + assert_eq!( + query_available_tokens(deps.as_ref(), env) + .unwrap() + .available, + coin(100, TEST_DENOM) + ); + } + + #[test] + fn total_locked_tokens_query() { + let mut test = TestSetup::init(); + + let locked = query_total_locked_tokens(test.deps()).unwrap().locked; + assert!(locked.amount.is_zero()); + assert_eq!(locked.denom, test.denom()); + + let grantee = test.add_dummy_grant().grantee; + test.lock_allowance(grantee, Uint128::new(1234)); + + let locked = query_total_locked_tokens(test.deps()).unwrap().locked; + assert_eq!(locked.amount, Uint128::new(1234)); + assert_eq!(locked.denom, test.denom()); + } + + #[test] + fn locked_tokens_query() { + let mut test = TestSetup::init(); + + let grantee1 = test.add_dummy_grant().grantee; + test.lock_allowance(grantee1.as_str(), Uint128::new(1234)); + + let grantee2 = test.add_dummy_grant().grantee; + let not_grantee = test.generate_account(); + + let res = query_locked_tokens(test.deps(), grantee1.to_string()).unwrap(); + assert_eq!(res.grantee, grantee1); + assert_eq!(res.locked, Some(coin(1234, TEST_DENOM))); + + let res = query_locked_tokens(test.deps(), grantee2.to_string()).unwrap(); + assert_eq!(res.grantee, grantee2); + assert!(res.locked.is_none()); + + let res = query_locked_tokens(test.deps(), not_grantee.to_string()).unwrap(); + assert_eq!(res.grantee, not_grantee); + assert!(res.locked.is_none()); + } + + #[cfg(test)] + mod locked_tokens_paged_query { + use super::*; + + fn lock_sorted(test: &mut TestSetup, count: usize) -> Vec { + let mut grantees = Vec::new(); + + for _ in 0..count { + let grantee = test.add_dummy_grant().grantee; + test.lock_allowance(grantee.as_str(), Uint128::new(100)); + grantees.push(LockedTokens { + grantee, + locked: coin(100, test.denom()), + }); + } + + grantees.sort_by_key(|g| g.grantee.clone()); + grantees + } + + #[test] + fn obeys_limits() { + let mut test = TestSetup::init(); + let _locked = lock_sorted(&mut test, 1000); + + let limit = 42; + let page1 = query_locked_tokens_paged(test.deps(), Some(limit), None).unwrap(); + assert_eq!(page1.locked.len(), limit as usize); + } + + #[test] + fn has_default_limit() { + let mut test = TestSetup::init(); + let _locked = lock_sorted(&mut test, 1000); + + // query without explicitly setting a limit + let page1 = query_locked_tokens_paged(test.deps(), None, None).unwrap(); + assert_eq!( + page1.locked.len() as u32, + retrieval_limits::LOCKED_TOKENS_DEFAULT_LIMIT + ); + } + + #[test] + fn has_max_limit() { + let mut test = TestSetup::init(); + let _locked = lock_sorted(&mut test, 1000); + + // query with a crazily high limit in an attempt to use too many resources + let crazy_limit = 1000; + let page1 = query_locked_tokens_paged(test.deps(), Some(crazy_limit), None).unwrap(); + + assert_eq!( + page1.locked.len() as u32, + retrieval_limits::LOCKED_TOKENS_MAX_LIMIT + ); + } + + #[test] + fn pagination_works() { + let mut test = TestSetup::init(); + let locked = lock_sorted(&mut test, 1000); + + // first page should return 2 results... + let page1 = query_locked_tokens_paged(test.deps(), Some(2), None).unwrap(); + assert_eq!(page1.locked, locked[..2].to_vec()); + + // if we start after 5th entry, the returned following page should have 6th and onwards + let second = locked[1].clone(); + + let page2 = + query_locked_tokens_paged(test.deps(), Some(3), Some(second.grantee.to_string())) + .unwrap(); + assert_eq!(page2.locked, locked[2..5].to_vec()); + } + } + + #[test] + fn grant_query() { + let mut test = TestSetup::init(); + let env = test.env(); + + // bad address + let bad_address = "not-valid-bech32"; + assert!(query_grant(test.deps(), env.clone(), bad_address.to_string()).is_err()); + + // exists + let grant = test.add_dummy_grant(); + let grantee = grant.grantee.clone(); + + assert_eq!( + query_grant(test.deps(), env.clone(), grantee.to_string()).unwrap(), + GrantResponse { + grantee, + grant: Some(GrantInformation { + grant, + expired: false, + }), + } + ); + + // exists expired + let grantee = test.generate_account(); + let exp = env.block.time.seconds() + 1; + let allowance = Allowance::Basic(BasicAllowance { + spend_limit: None, + expiration_unix_timestamp: Some(exp), + }); + let admin = test.admin_unchecked(); + NYM_POOL_STORAGE + .insert_new_grant(test.deps_mut(), &env, &admin, &grantee, allowance) + .unwrap(); + let grant = NYM_POOL_STORAGE.load_grant(test.deps(), &grantee).unwrap(); + + test.next_block(); + let env = test.env(); + + assert_eq!( + query_grant(test.deps(), env.clone(), grantee.to_string()).unwrap(), + GrantResponse { + grantee, + grant: Some(GrantInformation { + grant, + expired: true, + }), + } + ); + + // doesn't exist + let doesnt_exist = test.generate_account(); + assert_eq!( + query_grant(test.deps(), env.clone(), doesnt_exist.to_string()).unwrap(), + GrantResponse { + grantee: doesnt_exist, + grant: None, + } + ) + } + + #[test] + fn granter_query() { + let mut test = TestSetup::init(); + let admin = test.admin_unchecked(); + let env = test.env(); + + // bad address + let bad_address = "not-valid-bech32"; + assert!(query_granter(test.deps(), bad_address.to_string()).is_err()); + + // exists + let granter = test.generate_account(); + test.add_granter(&granter); + + assert_eq!( + query_granter(test.deps(), granter.to_string()).unwrap(), + GranterResponse { + granter, + information: Some(GranterInformation { + created_by: admin.clone(), + created_at_height: env.block.height, + }), + } + ); + + // (admin is also a granter) + assert_eq!( + query_granter(test.deps(), admin.to_string()).unwrap(), + GranterResponse { + information: Some(GranterInformation { + created_by: admin.clone(), + created_at_height: env.block.height, + }), + granter: admin, + } + ); + + // doesn't exist + let not_granter = test.generate_account(); + assert_eq!( + query_granter(test.deps(), not_granter.to_string()).unwrap(), + GranterResponse { + granter: not_granter, + information: None, + } + ); + } + + #[cfg(test)] + mod granters_paged_query { + use super::*; + + fn granters_sorted(test: &mut TestSetup, count: usize) -> Vec { + let mut granters = Vec::new(); + + for _ in 0..count { + let granter = test.add_dummy_grant().grantee; + test.add_granter(&granter); + granters.push(GranterDetails { + granter, + information: GranterInformation { + created_by: test.admin_unchecked(), + created_at_height: test.env().block.height, + }, + }); + } + + granters.sort_by_key(|g| g.granter.clone()); + granters + } + + #[test] + fn obeys_limits() { + let mut test = TestSetup::init(); + let _granters = granters_sorted(&mut test, 1000); + + let limit = 42; + let page1 = query_granters_paged(test.deps(), Some(limit), None).unwrap(); + assert_eq!(page1.granters.len(), limit as usize); + } + + #[test] + fn has_default_limit() { + let mut test = TestSetup::init(); + let _granters = granters_sorted(&mut test, 1000); + + // query without explicitly setting a limit + let page1 = query_granters_paged(test.deps(), None, None).unwrap(); + assert_eq!( + page1.granters.len() as u32, + retrieval_limits::GRANTERS_DEFAULT_LIMIT + ); + } + + #[test] + fn has_max_limit() { + let mut test = TestSetup::init(); + let _granters = granters_sorted(&mut test, 1000); + + // query with a crazily high limit in an attempt to use too many resources + let crazy_limit = 1000; + let page1 = query_granters_paged(test.deps(), Some(crazy_limit), None).unwrap(); + + assert_eq!( + page1.granters.len() as u32, + retrieval_limits::GRANTERS_MAX_LIMIT + ); + } + + #[test] + fn pagination_works() { + let mut test = TestSetup::init(); + let locked = granters_sorted(&mut test, 1000); + + // first page should return 2 results... + let page1 = query_granters_paged(test.deps(), Some(2), None).unwrap(); + assert_eq!(page1.granters, locked[..2].to_vec()); + + // if we start after 5th entry, the returned following page should have 6th and onwards + let second = locked[1].clone(); + + let page2 = + query_granters_paged(test.deps(), Some(3), Some(second.granter.to_string())) + .unwrap(); + assert_eq!(page2.granters, locked[2..5].to_vec()); + } + } + + #[cfg(test)] + mod grants_paged_query { + use super::*; + + fn grants_sorted(test: &mut TestSetup, count: usize) -> Vec { + let mut grantees = Vec::new(); + + for _ in 0..count { + let grant = test.add_dummy_grant(); + grantees.push(GrantInformation { + grant, + expired: false, + }); + } + + grantees.sort_by_key(|g| g.grant.grantee.clone()); + grantees + } + + #[test] + fn obeys_limits() { + let mut test = TestSetup::init(); + let _grantees = grants_sorted(&mut test, 1000); + + let limit = 42; + let page1 = query_grants_paged(test.deps(), test.env(), Some(limit), None).unwrap(); + assert_eq!(page1.grants.len(), limit as usize); + } + + #[test] + fn has_default_limit() { + let mut test = TestSetup::init(); + let _grantees = grants_sorted(&mut test, 1000); + + // query without explicitly setting a limit + let page1 = query_grants_paged(test.deps(), test.env(), None, None).unwrap(); + assert_eq!( + page1.grants.len() as u32, + retrieval_limits::GRANTS_DEFAULT_LIMIT + ); + } + + #[test] + fn has_max_limit() { + let mut test = TestSetup::init(); + let _grantees = grants_sorted(&mut test, 1000); + + // query with a crazily high limit in an attempt to use too many resources + let crazy_limit = 1000; + let page1 = + query_grants_paged(test.deps(), test.env(), Some(crazy_limit), None).unwrap(); + + assert_eq!( + page1.grants.len() as u32, + retrieval_limits::GRANTS_MAX_LIMIT + ); + } + + #[test] + fn pagination_works() { + let mut test = TestSetup::init(); + let grants = grants_sorted(&mut test, 1000); + + // first page should return 2 results... + let page1 = query_grants_paged(test.deps(), test.env(), Some(2), None).unwrap(); + assert_eq!(page1.grants, grants[..2].to_vec()); + + // if we start after 5th entry, the returned following page should have 6th and onwards + let second = grants[1].clone(); + + let page2 = query_grants_paged( + test.deps(), + test.env(), + Some(3), + Some(second.grant.grantee.to_string()), + ) + .unwrap(); + assert_eq!(page2.grants, grants[2..5].to_vec()); + } + } +} diff --git a/contracts/nym-pool/src/queued_migrations.rs b/contracts/nym-pool/src/queued_migrations.rs new file mode 100644 index 00000000000..7e1e3cacd64 --- /dev/null +++ b/contracts/nym-pool/src/queued_migrations.rs @@ -0,0 +1,2 @@ +// Copyright 2025 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 diff --git a/contracts/nym-pool/src/storage.rs b/contracts/nym-pool/src/storage.rs new file mode 100644 index 00000000000..03d55ef941e --- /dev/null +++ b/contracts/nym-pool/src/storage.rs @@ -0,0 +1,2331 @@ +// Copyright 2025 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::helpers::validate_usage_coin; +use cosmwasm_std::{coin, Addr, Coin, Deps, DepsMut, Env, Storage, Uint128}; +use cw_controllers::Admin; +use cw_storage_plus::{Item, Map}; +use nym_pool_contract_common::constants::storage_keys; +use nym_pool_contract_common::{ + Allowance, Grant, GranteeAddress, GranterAddress, GranterInformation, NymPoolContractError, +}; +use std::cmp::max; +use std::collections::HashMap; + +pub const NYM_POOL_STORAGE: NymPoolStorage = NymPoolStorage::new(); + +pub struct NymPoolStorage { + pub(crate) contract_admin: Admin, + pub(crate) pool_denomination: Item, + pub(crate) granters: Map, + + // pub(crate) expired: (), + + // unlike the feegrant module, we specifically don't allow multiple grants (from different granters) + // towards the same grantee + pub(crate) grants: Map, + pub(crate) locked: LockedStorage, +} + +impl NymPoolStorage { + #[allow(clippy::new_without_default)] + pub const fn new() -> Self { + NymPoolStorage { + contract_admin: Admin::new(storage_keys::CONTRACT_ADMIN), + pool_denomination: Item::new(storage_keys::POOL_DENOMINATION), + granters: Map::new(storage_keys::GRANTERS), + grants: Map::new(storage_keys::GRANTS), + locked: LockedStorage::new(), + } + } + + pub fn initialise( + &self, + mut deps: DepsMut, + env: Env, + admin: Addr, + pool_denom: &String, + initial_grants: HashMap, + ) -> Result<(), NymPoolContractError> { + // set the denom + self.pool_denomination.save(deps.storage, pool_denom)?; + + // set the contract admin + self.contract_admin + .set(deps.branch(), Some(admin.clone()))?; + + // set the admin to be a whitelisted granter + self.add_new_granter(deps.branch(), &env, &admin, &admin)?; + + // initialise the locked storage (with the total of 0) + self.locked.initialise(deps.branch())?; + + let included_grants = !initial_grants.is_empty(); + + // add all initial grants + let mut required_amount = Uint128::zero(); + for (grantee, allowance) in initial_grants { + let grantee = deps.api.addr_validate(&grantee)?; + if let Some(ref limit) = allowance.basic().spend_limit { + required_amount += limit.amount; + } + self.insert_new_grant(deps.branch(), &env, &admin, &grantee, allowance)?; + } + + // special case: during initialisation, even if we're inserting unlimited grants, + // we have to have _some_ tokens available + if included_grants { + let balance = self.contract_balance(deps.as_ref(), &env)?; + if required_amount > balance.amount || balance.amount.is_zero() { + return Err(NymPoolContractError::InsufficientTokens { + required: coin(max(required_amount.u128(), 1), &balance.denom), + available: balance, + }); + } + } + + Ok(()) + } + + fn contract_balance(&self, deps: Deps, env: &Env) -> Result { + let denom = self.pool_denomination.load(deps.storage)?; + Ok(deps.querier.query_balance(&env.contract.address, denom)?) + } + + fn is_admin(&self, deps: Deps, addr: &Addr) -> Result { + self.contract_admin.is_admin(deps, addr).map_err(Into::into) + } + + fn ensure_is_admin(&self, deps: Deps, addr: &Addr) -> Result<(), NymPoolContractError> { + self.contract_admin + .assert_admin(deps, addr) + .map_err(Into::into) + } + + pub fn try_load_granter( + &self, + deps: Deps, + granter: &GranterAddress, + ) -> Result, NymPoolContractError> { + self.granters + .may_load(deps.storage, granter.clone()) + .map_err(Into::into) + } + + fn is_whitelisted_granter( + &self, + deps: Deps, + addr: &GranterAddress, + ) -> Result { + Ok(self.try_load_granter(deps, addr)?.is_some()) + } + + fn ensure_is_whitelisted_granter( + &self, + deps: Deps, + addr: &GranterAddress, + ) -> Result<(), NymPoolContractError> { + if !self.is_whitelisted_granter(deps, addr)? { + return Err(NymPoolContractError::InvalidGranter { + addr: addr.to_string(), + }); + } + Ok(()) + } + + pub fn add_new_granter( + &self, + deps: DepsMut, + env: &Env, + sender: &Addr, + granter: &GranterAddress, + ) -> Result<(), NymPoolContractError> { + // currently only the admin is permitted to add new granters + self.ensure_is_admin(deps.as_ref(), sender)?; + + if self + .granters + .may_load(deps.storage, granter.clone())? + .is_some() + { + return Err(NymPoolContractError::AlreadyAGranter); + } + + self.granters.save( + deps.storage, + granter.clone(), + &GranterInformation { + created_by: sender.clone(), + created_at_height: env.block.height, + }, + )?; + + Ok(()) + } + + pub fn remove_granter( + &self, + deps: DepsMut, + admin: &Addr, + granter: &GranterAddress, + ) -> Result<(), NymPoolContractError> { + // only admin is permitted to remove granters + self.ensure_is_admin(deps.as_ref(), admin)?; + + // the granter has to be, well, an actual granter + self.ensure_is_whitelisted_granter(deps.as_ref(), granter)?; + + self.granters.remove(deps.storage, granter.clone()); + + Ok(()) + } + + pub fn available_tokens(&self, deps: Deps, env: &Env) -> Result { + let locked = self.locked.total_locked.load(deps.storage)?; + let balance = self.contract_balance(deps, env)?; + + // the amount of available tokens is the current contract balance minus all the locked tokens + let mut available = balance; + available.amount = available.amount.saturating_sub(locked); + Ok(available) + } + + pub fn try_load_grant( + &self, + deps: Deps, + grantee: &GranteeAddress, + ) -> Result, NymPoolContractError> { + self.grants + .may_load(deps.storage, grantee.clone()) + .map_err(Into::into) + } + + pub fn load_grant( + &self, + deps: Deps, + grantee: &GranteeAddress, + ) -> Result { + self.try_load_grant(deps, grantee)? + .ok_or(NymPoolContractError::GrantNotFound { + grantee: grantee.to_string(), + }) + } + + pub fn insert_new_grant( + &self, + deps: DepsMut, + env: &Env, + granter: &GranterAddress, + grantee: &GranteeAddress, + mut allowance: Allowance, + ) -> Result<(), NymPoolContractError> { + // the granter should be permitted to add new grants + self.ensure_is_whitelisted_granter(deps.as_ref(), granter)?; + + // check for existing grant + if let Some(existing_grant) = self.try_load_grant(deps.as_ref(), grantee)? { + return Err(NymPoolContractError::GrantAlreadyExist { + granter: existing_grant.granter.to_string(), + grantee: grantee.to_string(), + created_at_height: existing_grant.granted_at_height, + }); + } + + // the allowance should be well-formed + let expected_denom = self.pool_denomination.load(deps.storage)?; + allowance.validate_new(env, &expected_denom)?; + + // if allowance includes explicit limit, + // it should not be higher than the total remaining tokens + // note: we already verified denomination matched when we validated the allowance + if let Some(ref spend_limit) = allowance.basic().spend_limit { + let available = self.available_tokens(deps.as_ref(), env)?; + if spend_limit.amount > available.amount { + return Err(NymPoolContractError::InsufficientTokens { + available, + required: spend_limit.clone(), + }); + } + } + + // set initial state based on the env + allowance.set_initial_state(env); + + self.grants.save( + deps.storage, + grantee.clone(), + &Grant { + granter: granter.clone(), + grantee: grantee.clone(), + granted_at_height: env.block.height, + allowance, + }, + )?; + + Ok(()) + } + + pub fn try_spend_part_of_grant( + &self, + deps: DepsMut, + env: &Env, + grantee_address: &GranteeAddress, + amount: &Coin, + ) -> Result<(), NymPoolContractError> { + let mut grant = self.load_grant(deps.as_ref(), grantee_address)?; + grant.allowance.try_spend(env, amount)?; + + let locked = self.locked.grantee_locked(deps.storage, grantee_address)?; + + // if we used up all allowance and have no locked tokens, we can just remove the grant from storage + if grant.allowance.is_used_up() && locked.is_zero() { + self.grants.remove(deps.storage, grantee_address.clone()) + } else { + self.grants + .save(deps.storage, grantee_address.clone(), &grant)?; + } + + Ok(()) + } + + pub fn remove_grant( + &self, + deps: DepsMut, + grantee_address: &GranteeAddress, + ) -> Result<(), NymPoolContractError> { + self.grants.remove(deps.storage, grantee_address.clone()); + + // if there are any tokens still locked associated with this grantee, unlock them + if let Some(grantee_locked) = self + .locked + .maybe_grantee_locked(deps.storage, grantee_address)? + { + self.locked.unlock(deps, grantee_address, grantee_locked)?; + } + + Ok(()) + } + + pub fn revoke_grant( + &self, + deps: DepsMut, + grantee_address: &GranteeAddress, + revoker: &Addr, + ) -> Result<(), NymPoolContractError> { + let grant = self.load_grant(deps.as_ref(), grantee_address)?; + let original_granter = grant.granter; + + let is_admin = self.is_admin(deps.as_ref(), revoker)?; + + // grant can only be revoked by the granter who has originally granted it (assuming it's still whitelisted) + // or by the admin + if revoker != original_granter && !is_admin { + // request came from a random sender - neither the original granter nor the current admin + return Err(NymPoolContractError::UnauthorizedGrantRevocation); + } + + // at this point we know the request must have come from either the original granter or contract admin, + // however, if it was the former, we still need to verify whether it's still whitelisted + // (if the granter was removed, it shouldn't have any permissions to modify old grants anymore) + if !is_admin && !self.is_whitelisted_granter(deps.as_ref(), revoker)? { + return Err(NymPoolContractError::UnauthorizedGrantRevocation); + } + + self.remove_grant(deps, grantee_address) + } + + pub fn lock_part_of_allowance( + &self, + mut deps: DepsMut, + env: &Env, + grantee: &GranteeAddress, + amount: Coin, + ) -> Result<(), NymPoolContractError> { + // ensure correct coin has been specified + validate_usage_coin(deps.storage, &amount)?; + + // keep track of the locked coins + self.locked.lock(deps.branch(), grantee, amount.amount)?; + + // attempt to deduct the coins from the allowance + self.try_spend_part_of_grant(deps, env, grantee, &amount)?; + + Ok(()) + } + + pub fn unlock_part_of_allowance( + &self, + deps: DepsMut, + grantee: &GranteeAddress, + amount: &Coin, + ) -> Result<(), NymPoolContractError> { + // ensure correct coin has been specified + validate_usage_coin(deps.storage, amount)?; + + // update the underlying spend limit of the grant + let mut grant = self.load_grant(deps.as_ref(), grantee)?; + // note: this will only increase the basic spend limit and will not change any periodic allowances + grant.allowance.increase_spend_limit(amount.amount); + self.grants.save(deps.storage, grantee.clone(), &grant)?; + + // keep track of the locked coins (also checks whether sufficient tokens are locked, etc.) + self.locked.unlock(deps, grantee, amount.amount) + } +} + +pub(crate) struct LockedStorage { + pub(crate) total_locked: Item, + pub(crate) grantees: Map, +} + +impl LockedStorage { + #[allow(clippy::new_without_default)] + const fn new() -> Self { + LockedStorage { + total_locked: Item::new(storage_keys::TOTAL_LOCKED), + grantees: Map::new(storage_keys::LOCKED_GRANTEES), + } + } + + fn initialise(&self, deps: DepsMut) -> Result<(), NymPoolContractError> { + self.total_locked.save(deps.storage, &Uint128::zero())?; + Ok(()) + } + + pub fn grantee_locked( + &self, + storage: &dyn Storage, + grantee: &GranteeAddress, + ) -> Result { + Ok(self + .maybe_grantee_locked(storage, grantee)? + .unwrap_or_default()) + } + + pub fn maybe_grantee_locked( + &self, + storage: &dyn Storage, + grantee: &GranteeAddress, + ) -> Result, NymPoolContractError> { + Ok(self.grantees.may_load(storage, grantee.clone())?) + } + + /// unconditionally attempts to load specified amount of tokens for the particular grantee + /// it does not validate permissions nor allowances - that's up to the caller + fn lock( + &self, + deps: DepsMut, + grantee: &GranteeAddress, + amount: Uint128, + ) -> Result<(), NymPoolContractError> { + let existing_grantee = self.grantee_locked(deps.storage, grantee)?; + let new_locked_grantee = existing_grantee + amount; + + let existing_total = self.total_locked.load(deps.storage)?; + let new_locked_total = existing_total + amount; + + self.grantees + .save(deps.storage, grantee.clone(), &new_locked_grantee)?; + self.total_locked.save(deps.storage, &new_locked_total)?; + Ok(()) + } + + fn unlock( + &self, + deps: DepsMut, + grantee: &GranteeAddress, + amount: Uint128, + ) -> Result<(), NymPoolContractError> { + let locked_grantee = self.grantee_locked(deps.storage, grantee)?; + let total_locked = self.total_locked.load(deps.storage)?; + + if locked_grantee < amount { + return Err(NymPoolContractError::InsufficientLockedTokens { + grantee: grantee.to_string(), + locked: locked_grantee, + requested: amount, + }); + } + + let updated_grantee = locked_grantee - amount; + + // if the updated value is zero, just remove the map entry + if updated_grantee.is_zero() { + self.grantees.remove(deps.storage, grantee.clone()); + } else { + self.grantees + .save(deps.storage, grantee.clone(), &updated_grantee)?; + } + + // we're specifically not using saturating sub here because that operation should ALWAYS be valid + // if it fails, it means there's a pool inconsistency that has to be resolved + self.total_locked + .save(deps.storage, &(total_locked - amount))?; + + Ok(()) + } +} + +pub mod retrieval_limits { + pub const LOCKED_TOKENS_DEFAULT_LIMIT: u32 = 100; + pub const LOCKED_TOKENS_MAX_LIMIT: u32 = 200; + + pub const GRANTERS_DEFAULT_LIMIT: u32 = 100; + pub const GRANTERS_MAX_LIMIT: u32 = 200; + + pub const GRANTS_DEFAULT_LIMIT: u32 = 100; + pub const GRANTS_MAX_LIMIT: u32 = 200; +} + +#[cfg(test)] +mod tests { + use super::*; + use nym_pool_contract_common::BasicAllowance; + + fn dummy_allowance() -> Allowance { + Allowance::Basic(BasicAllowance::unlimited()) + } + + #[cfg(test)] + mod nympool_storage { + use super::*; + use crate::testing::{TestSetup, TEST_DENOM}; + use cosmwasm_std::testing::{ + mock_dependencies, mock_env, MockApi, MockQuerier, MockStorage, + }; + use cosmwasm_std::{coin, coins, Empty, OwnedDeps}; + use nym_pool_contract_common::BasicAllowance; + + #[cfg(test)] + mod initialisation { + use super::*; + use crate::testing::{deps_with_balance, TEST_DENOM}; + use cosmwasm_std::testing::{mock_dependencies, mock_env}; + use cosmwasm_std::{coin, Order}; + use nym_pool_contract_common::BasicAllowance; + + fn all_grants(storage: &dyn Storage) -> HashMap { + NYM_POOL_STORAGE + .grants + .range(storage, None, None, Order::Ascending) + .collect::, _>>() + .unwrap() + } + + #[test] + fn requires_some_tokens_for_unlimited_initial_grants() -> anyhow::Result<()> { + let storage = NymPoolStorage::new(); + let mut deps = mock_dependencies(); + let env = mock_env(); + let admin = deps.api.addr_make("admin"); + + let mut grants = HashMap::new(); + grants.insert( + deps.api.addr_make("gr1").to_string(), + Allowance::Basic(BasicAllowance::unlimited()), + ); + let res = storage.initialise( + deps.as_mut(), + env.clone(), + admin.clone(), + &TEST_DENOM.to_string(), + grants.clone(), + ); + // we haven't got any tokens - this should have failed + assert!(res.is_err()); + + let address = &env.contract.address; + let mem_storage = MockStorage::default(); + let api = MockApi::default(); + + let querier: MockQuerier = + MockQuerier::new(&[(address.as_str(), coins(1, TEST_DENOM).as_slice())]); + + let mut deps: OwnedDeps<_, _, _, Empty> = OwnedDeps { + storage: mem_storage, + api, + querier, + custom_query_type: Default::default(), + }; + + // while we don't have a lot, we have some tokens which should allow this tx to proceed + let res = storage.initialise( + deps.as_mut(), + env.clone(), + admin.clone(), + &TEST_DENOM.to_string(), + grants.clone(), + ); + assert!(res.is_ok()); + + Ok(()) + } + + #[test] + fn requires_specified_amount_of_tokens_for_bounded_grants() -> anyhow::Result<()> { + fn bounded_allowance() -> Allowance { + Allowance::Basic(BasicAllowance { + spend_limit: Some(coin(100, TEST_DENOM)), + expiration_unix_timestamp: None, + }) + } + + let storage = NymPoolStorage::new(); + let mut deps = mock_dependencies(); + let env = mock_env(); + let admin = deps.api.addr_make("admin"); + + let mut grants = HashMap::new(); + grants.insert(deps.api.addr_make("gr1").to_string(), bounded_allowance()); + grants.insert(deps.api.addr_make("gr2").to_string(), bounded_allowance()); + grants.insert(deps.api.addr_make("gr3").to_string(), bounded_allowance()); + grants.insert(deps.api.addr_make("gr4").to_string(), bounded_allowance()); + let res = storage.initialise( + deps.as_mut(), + env.clone(), + admin.clone(), + &TEST_DENOM.to_string(), + grants.clone(), + ); + // we haven't got any tokens - this should have failed + assert!(res.is_err()); + + let address = &env.contract.address; + let mem_storage = MockStorage::default(); + let api = MockApi::default(); + + let querier: MockQuerier = + MockQuerier::new(&[(address.as_str(), coins(399, TEST_DENOM).as_slice())]); + + let mut deps: OwnedDeps<_, _, _, Empty> = OwnedDeps { + storage: mem_storage, + api, + querier, + custom_query_type: Default::default(), + }; + + // we haven't got enough tokens (we need at least 400) - still a failure! + let res = storage.initialise( + deps.as_mut(), + env.clone(), + admin.clone(), + &TEST_DENOM.to_string(), + grants.clone(), + ); + assert!(res.is_err()); + + // finally, with at least 400, it should work + let mem_storage = MockStorage::default(); + let api = MockApi::default(); + + let querier: MockQuerier = + MockQuerier::new(&[(address.as_str(), coins(400, TEST_DENOM).as_slice())]); + + let mut deps: OwnedDeps<_, _, _, Empty> = OwnedDeps { + storage: mem_storage, + api, + querier, + custom_query_type: Default::default(), + }; + + let res = storage.initialise( + deps.as_mut(), + env.clone(), + admin.clone(), + &TEST_DENOM.to_string(), + grants, + ); + assert!(res.is_ok()); + + Ok(()) + } + + #[test] + fn inserts_all_initial_grants() -> anyhow::Result<()> { + let storage = NymPoolStorage::new(); + let env = mock_env(); + + let mut deps = deps_with_balance(&env); + + let admin = deps.api.addr_make("admin"); + let denom = &TEST_DENOM.to_string(); + + // no grants + let grants = HashMap::new(); + storage.initialise(deps.as_mut(), env.clone(), admin.clone(), denom, grants)?; + assert!(all_grants(&deps.storage).is_empty()); + + // one grant + let mut deps = deps_with_balance(&env); + let mut grants = HashMap::new(); + grants.insert(deps.api.addr_make("gr1").to_string(), dummy_allowance()); + storage.initialise(deps.as_mut(), env.clone(), admin.clone(), denom, grants)?; + let all = all_grants(&deps.storage); + assert_eq!(all.len(), 1); + let grant = all.get(&deps.api.addr_make("gr1")).unwrap(); + assert_eq!(grant.grantee, deps.api.addr_make("gr1")); + assert_eq!(grant.allowance, dummy_allowance()); + + // multiple grants + let mut deps = deps_with_balance(&env); + let mut grants = HashMap::new(); + grants.insert(deps.api.addr_make("gr1").to_string(), dummy_allowance()); + grants.insert(deps.api.addr_make("gr2").to_string(), dummy_allowance()); + grants.insert(deps.api.addr_make("gr3").to_string(), dummy_allowance()); + grants.insert(deps.api.addr_make("gr4").to_string(), dummy_allowance()); + storage.initialise(deps.as_mut(), env.clone(), admin.clone(), denom, grants)?; + let all = all_grants(&deps.storage); + assert_eq!(all.len(), 4); + let grant = all.get(&deps.api.addr_make("gr1")).unwrap(); + assert_eq!(grant.grantee, deps.api.addr_make("gr1")); + let grant = all.get(&deps.api.addr_make("gr3")).unwrap(); + assert_eq!(grant.grantee, deps.api.addr_make("gr3")); + + // fails on invalid grantee address + let mut deps = deps_with_balance(&env); + let mut grants = HashMap::new(); + grants.insert(deps.api.addr_make("gr1").to_string(), dummy_allowance()); + grants.insert("invalid_address".to_string(), dummy_allowance()); + assert!(storage + .initialise(deps.as_mut(), env.clone(), admin.clone(), denom, grants) + .is_err()); + + // fails on invalid allowance + let mut deps = deps_with_balance(&env); + let mut grants = HashMap::new(); + grants.insert( + deps.api.addr_make("gr1").to_string(), + Allowance::Basic(BasicAllowance { + spend_limit: Some(coin(0, TEST_DENOM)), + expiration_unix_timestamp: None, + }), + ); + assert!(storage + .initialise(deps.as_mut(), env.clone(), admin, denom, grants) + .is_err()); + Ok(()) + } + + #[test] + fn sets_pool_denomination() -> anyhow::Result<()> { + let storage = NymPoolStorage::new(); + let mut deps = mock_dependencies(); + let env = mock_env(); + let admin = deps.api.addr_make("admin"); + + storage.initialise( + deps.as_mut(), + env.clone(), + admin.clone(), + &"somedenom".to_string(), + HashMap::new(), + )?; + assert_eq!( + storage.pool_denomination.load(deps.as_ref().storage)?, + "somedenom".to_string() + ); + + let mut deps = mock_dependencies(); + storage.initialise( + deps.as_mut(), + env.clone(), + admin.clone(), + &"anotherdenom".to_string(), + HashMap::new(), + )?; + assert_eq!( + storage.pool_denomination.load(deps.as_ref().storage)?, + "anotherdenom".to_string() + ); + + Ok(()) + } + + #[test] + fn sets_contract_admin() -> anyhow::Result<()> { + let storage = NymPoolStorage::new(); + let mut deps = mock_dependencies(); + let env = mock_env(); + let admin1 = deps.api.addr_make("first-admin"); + let admin2 = deps.api.addr_make("secod-admin"); + + storage.initialise( + deps.as_mut(), + env.clone(), + admin1.clone(), + &TEST_DENOM.to_string(), + HashMap::new(), + )?; + assert!(storage.ensure_is_admin(deps.as_ref(), &admin1).is_ok()); + + let mut deps = mock_dependencies(); + storage.initialise( + deps.as_mut(), + env.clone(), + admin2.clone(), + &TEST_DENOM.to_string(), + HashMap::new(), + )?; + assert!(storage.ensure_is_admin(deps.as_ref(), &admin2).is_ok()); + + Ok(()) + } + + #[test] + fn initialises_locked_storage() -> anyhow::Result<()> { + let storage = NymPoolStorage::new(); + let mut deps = mock_dependencies(); + let env = mock_env(); + let admin = deps.api.addr_make("admin"); + let denom = &TEST_DENOM.to_string(); + + storage.initialise(deps.as_mut(), env, admin, denom, HashMap::new())?; + assert!(storage + .locked + .total_locked + .load(deps.as_ref().storage)? + .is_zero()); + + Ok(()) + } + + #[test] + fn adds_admin_to_the_granters_set() -> anyhow::Result<()> { + let storage = NymPoolStorage::new(); + let mut deps = mock_dependencies(); + let env = mock_env(); + let admin1 = deps.api.addr_make("first-admin"); + let admin2 = deps.api.addr_make("second-admin"); + + storage.initialise( + deps.as_mut(), + env.clone(), + admin1.clone(), + &TEST_DENOM.to_string(), + HashMap::new(), + )?; + assert_eq!( + storage + .granters + .load(&deps.storage, admin1.clone())? + .created_by, + admin1 + ); + + let mut deps = mock_dependencies(); + storage.initialise( + deps.as_mut(), + env.clone(), + admin2.clone(), + &TEST_DENOM.to_string(), + HashMap::new(), + )?; + assert_eq!( + storage + .granters + .load(&deps.storage, admin2.clone())? + .created_by, + admin2 + ); + + Ok(()) + } + } + + #[test] + fn getting_contract_balance() -> anyhow::Result<()> { + // it's a simple as running the bank query against the address set in the current env + // using the pool denom + let env = mock_env(); + let address = &env.contract.address; + + let storage = MockStorage::default(); + let api = MockApi::default(); + + let not_contract = api.addr_make("unrelated-address"); + let pool_denom = TEST_DENOM; + + // set some initial balances + let querier: MockQuerier = MockQuerier::new(&[ + ( + address.as_str(), + vec![coin(1000, pool_denom), coin(2000, "anotherdenom")].as_slice(), + ), + (not_contract.as_str(), coins(1234, pool_denom).as_slice()), + ]); + + let mut deps: OwnedDeps<_, _, _, Empty> = OwnedDeps { + storage, + api, + querier, + custom_query_type: Default::default(), + }; + let storage = NymPoolStorage::new(); + let admin = deps.api.addr_make("admin"); + + // regardless of other denoms and other accounts, the balance query only returns target denom + storage.initialise( + deps.as_mut(), + env.clone(), + admin, + &pool_denom.to_string(), + HashMap::new(), + )?; + assert_eq!( + storage.contract_balance(deps.as_ref(), &env)?, + coin(1000, pool_denom) + ); + + Ok(()) + } + + #[test] + fn checking_for_admin() -> anyhow::Result<()> { + let storage = NymPoolStorage::new(); + let mut deps = mock_dependencies(); + let env = mock_env(); + let admin = deps.api.addr_make("admin"); + let non_admin = deps.api.addr_make("non-admin"); + let denom = &TEST_DENOM.to_string(); + + storage.initialise(deps.as_mut(), env, admin.clone(), denom, HashMap::new())?; + assert!(storage.is_admin(deps.as_ref(), &admin)?); + assert!(!storage.is_admin(deps.as_ref(), &non_admin)?); + + Ok(()) + } + + #[test] + fn ensuring_admin_privileges() -> anyhow::Result<()> { + let storage = NymPoolStorage::new(); + let mut deps = mock_dependencies(); + let env = mock_env(); + let admin = deps.api.addr_make("admin"); + let non_admin = deps.api.addr_make("non-admin"); + let denom = &TEST_DENOM.to_string(); + + storage.initialise(deps.as_mut(), env, admin.clone(), denom, HashMap::new())?; + assert!(storage.ensure_is_admin(deps.as_ref(), &admin).is_ok()); + assert!(storage.ensure_is_admin(deps.as_ref(), &non_admin).is_err()); + + Ok(()) + } + + #[test] + fn loading_granter_information() -> anyhow::Result<()> { + let storage = NymPoolStorage::new(); + let mut test = TestSetup::init(); + + let granter = test.generate_account(); + + // not granter + let info = storage.try_load_granter(test.deps(), &granter)?; + assert!(info.is_none()); + + // granter + let admin = test.admin_unchecked(); + test.add_granter(&granter); + + let info = storage.try_load_granter(test.deps(), &granter)?; + assert_eq!( + info, + Some(GranterInformation { + created_by: admin, + created_at_height: test.env().block.height, + }) + ); + + Ok(()) + } + + #[test] + fn checking_granter_permission() -> anyhow::Result<()> { + let storage = NymPoolStorage::new(); + let mut test = TestSetup::init(); + + let granter = test.generate_account(); + test.add_granter(&granter); + let not_granter = test.generate_account(); + + let deps = test.deps(); + assert!(storage.is_whitelisted_granter(deps, &granter)?); + assert!(!storage.is_whitelisted_granter(deps, ¬_granter)?); + + Ok(()) + } + + #[test] + fn ensuring_granter_permission() -> anyhow::Result<()> { + let storage = NymPoolStorage::new(); + let mut test = TestSetup::init(); + + let granter = test.generate_account(); + test.add_granter(&granter); + let not_granter = test.generate_account(); + + let deps = test.deps(); + assert!(storage + .ensure_is_whitelisted_granter(deps, &granter) + .is_ok()); + assert!(storage + .ensure_is_whitelisted_granter(deps, ¬_granter) + .is_err()); + + Ok(()) + } + + #[test] + fn checking_available_tokens() -> anyhow::Result<()> { + // initialise the contract with some tokens + let env = mock_env(); + let address = &env.contract.address; + + let storage = MockStorage::default(); + let api = MockApi::default(); + let pool_denom = TEST_DENOM; + + // set some initial balances + let querier: MockQuerier = + MockQuerier::new(&[(address.as_str(), coins(1000, pool_denom).as_slice())]); + + let mut deps: OwnedDeps<_, _, _, Empty> = OwnedDeps { + storage, + api, + querier, + custom_query_type: Default::default(), + }; + let storage = NymPoolStorage::new(); + let admin = deps.api.addr_make("admin"); + + storage.initialise( + deps.as_mut(), + env.clone(), + admin, + &pool_denom.to_string(), + HashMap::new(), + )?; + + // if there are no locked tokens, the available equals to the balance + assert_eq!( + storage.available_tokens(deps.as_ref(), &env)?, + coin(1000, pool_denom) + ); + + // however, once we start locking them, it becomes the difference between those + + // some locked + let dummy_grantee = deps.api.addr_make("grantee"); + storage + .locked + .lock(deps.as_mut(), &dummy_grantee, Uint128::new(100))?; + + assert_eq!( + storage.available_tokens(deps.as_ref(), &env)?, + coin(900, pool_denom) + ); + + // all locked + storage + .locked + .lock(deps.as_mut(), &dummy_grantee, Uint128::new(900))?; + assert_eq!( + storage.available_tokens(deps.as_ref(), &env)?, + coin(0, pool_denom) + ); + + // locked beyond balance (to check for overflow errors) + storage + .locked + .lock(deps.as_mut(), &dummy_grantee, Uint128::new(1000000))?; + assert_eq!( + storage.available_tokens(deps.as_ref(), &env)?, + coin(0, pool_denom) + ); + + Ok(()) + } + + #[test] + fn attempting_to_load_grant() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + // doesn't exist... + let grantee = test.generate_account(); + assert_eq!(storage.try_load_grant(test.deps(), &grantee)?, None); + + // exists + test.add_dummy_grant_for(&grantee); + assert_eq!( + storage.try_load_grant(test.deps(), &grantee)?, + Some(Grant { + granter: test.admin_unchecked(), + grantee, + granted_at_height: test.env().block.height, + allowance: Allowance::Basic(BasicAllowance::unlimited()), + }) + ); + Ok(()) + } + + #[test] + fn loading_grant() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + // doesn't exist... + let grantee = test.generate_account(); + assert!(storage.load_grant(test.deps(), &grantee).is_err()); + + // exists + test.add_dummy_grant_for(&grantee); + assert_eq!( + storage.load_grant(test.deps(), &grantee)?, + Grant { + granter: test.admin_unchecked(), + grantee, + granted_at_height: test.env().block.height, + allowance: Allowance::Basic(BasicAllowance::unlimited()), + } + ); + Ok(()) + } + + #[cfg(test)] + mod adding_new_granter { + use super::*; + use cw_controllers::AdminError; + + #[test] + fn can_only_be_performed_by_contract_admin() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + let admin = test.admin_unchecked(); + let not_admin = test.generate_account(); + + let granter = test.generate_account(); + + let (deps, env) = test.deps_mut_env(); + let res = storage + .add_new_granter(deps, &env, ¬_admin, &granter) + .unwrap_err(); + assert_eq!(res, NymPoolContractError::Admin(AdminError::NotAdmin {})); + + let (deps, env) = test.deps_mut_env(); + let res = storage.add_new_granter(deps, &env, &admin, &granter); + assert!(res.is_ok()); + + Ok(()) + } + + #[test] + fn can_only_be_performed_if_account_is_not_already_a_granter() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + let admin = test.admin_unchecked(); + let granter = test.generate_account(); + + // adding it for the first time... + let (deps, env) = test.deps_mut_env(); + storage.add_new_granter(deps, &env, &admin, &granter)?; + + // it's already a granter + let (deps, env) = test.deps_mut_env(); + let res = storage + .add_new_granter(deps, &env, &admin, &granter) + .unwrap_err(); + assert_eq!(res, NymPoolContractError::AlreadyAGranter); + + Ok(()) + } + + #[test] + fn saves_basic_metadata() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + let admin = test.admin_unchecked(); + let granter = test.generate_account(); + + // no metadata + let info = storage.granters.may_load(test.storage(), granter.clone())?; + assert!(info.is_none()); + + let (deps, env) = test.deps_mut_env(); + storage.add_new_granter(deps, &env, &admin, &granter)?; + + let info = storage.granters.may_load(test.storage(), granter.clone())?; + // it was added by the admin at the current height + assert_eq!( + info, + Some(GranterInformation { + created_by: admin.clone(), + created_at_height: env.block.height, + }) + ); + + // after changing admin, new address is set as the creator + let new_admin = test.generate_account(); + // sanity check: + assert_ne!(admin, new_admin); + test.change_admin(&new_admin); + + let new_granter = test.generate_account(); + let (deps, env) = test.deps_mut_env(); + storage.add_new_granter(deps, &env, &new_admin, &new_granter)?; + let info = storage + .granters + .may_load(test.storage(), new_granter.clone())?; + // it was added by the new admin at the current height + assert_eq!( + info, + Some(GranterInformation { + created_by: new_admin.clone(), + created_at_height: env.block.height, + }) + ); + + Ok(()) + } + } + + #[cfg(test)] + mod removing_granter { + use super::*; + + #[test] + fn requires_granter_to_exist() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + let admin = test.admin_unchecked(); + let granter = test.generate_account(); + + assert!(storage + .remove_granter(test.deps_mut(), &admin, &granter) + .is_err()); + + test.add_granter(&granter); + assert!(storage + .remove_granter(test.deps_mut(), &admin, &granter) + .is_ok()); + + Ok(()) + } + + #[test] + fn can_only_be_performed_by_admin() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + let random_address = test.generate_account(); + let granter = test.generate_account(); + let admin = test.admin_unchecked(); + test.add_granter(&granter); + + // can't be removed by the granter itself + assert!(storage + .remove_granter(test.deps_mut(), &granter, &granter) + .is_err()); + + // not by some random address + assert!(storage + .remove_granter(test.deps_mut(), &random_address, &granter) + .is_err()); + + // admin can do it though! + assert!(storage + .remove_granter(test.deps_mut(), &admin, &granter) + .is_ok()); + + test.add_granter(&granter); + let new_admin = test.generate_account(); + test.change_admin(&new_admin); + + // old admin can't do anything : ( + assert!(storage + .remove_granter(test.deps_mut(), &admin, &granter) + .is_err()); + + // but new admin can! + assert!(storage + .remove_granter(test.deps_mut(), &new_admin, &granter) + .is_ok()); + + Ok(()) + } + + #[test] + fn removes_it_from_granter_list() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + let admin = test.admin_unchecked(); + let granter = test.generate_account(); + test.add_granter(&granter); + + assert!(storage + .granters + .may_load(test.storage(), granter.clone())? + .is_some()); + + storage.remove_granter(test.deps_mut(), &admin, &granter)?; + + assert!(storage + .granters + .may_load(test.storage(), granter.clone())? + .is_none()); + Ok(()) + } + } + + #[cfg(test)] + mod adding_new_grant { + use super::*; + use nym_pool_contract_common::ClassicPeriodicAllowance; + + #[test] + fn can_only_be_done_by_whitelisted_granter() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + let not_valid_granter = test.generate_account(); + let granter = test.generate_account(); + test.add_granter(&granter); + + let grantee = test.generate_account(); + + let (deps, env) = test.deps_mut_env(); + let res = storage + .insert_new_grant(deps, &env, ¬_valid_granter, &grantee, dummy_allowance()) + .unwrap_err(); + + assert_eq!( + res, + NymPoolContractError::InvalidGranter { + addr: not_valid_granter.to_string(), + } + ); + + let (deps, env) = test.deps_mut_env(); + let res = + storage.insert_new_grant(deps, &env, &granter, &grantee, dummy_allowance()); + + assert!(res.is_ok()); + Ok(()) + } + + #[test] + fn cant_be_done_if_grant_already_existed() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + let admin = test.admin_unchecked(); + let grantee = test.add_dummy_grant().grantee; + + let (deps, env) = test.deps_mut_env(); + let res = storage + .insert_new_grant(deps, &env, &admin, &grantee, dummy_allowance()) + .unwrap_err(); + + assert!(matches!( + res, + NymPoolContractError::GrantAlreadyExist { .. } + )); + + Ok(()) + } + + #[test] + fn only_accepts_valid_allowances() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + // allowance with 0 limit and wrong denom + let bad_allowance = Allowance::Basic(BasicAllowance { + spend_limit: Some(coin(0, "invalid-denom")), + expiration_unix_timestamp: None, + }); + + let admin = test.admin_unchecked(); + let grantee = test.generate_account(); + + let (deps, env) = test.deps_mut_env(); + let res = storage + .insert_new_grant(deps, &env, &admin, &grantee, bad_allowance) + .unwrap_err(); + + assert!(matches!(res, NymPoolContractError::InvalidDenom { .. })); + + Ok(()) + } + + #[test] + fn explicit_limit_cant_be_larger_than_available_tokens() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + let admin = test.admin_unchecked(); + let grantee = test.generate_account(); + + let available = storage.available_tokens(test.deps(), &test.env())?; + + // just above the available + let mut limit = available.clone(); + limit.amount += Uint128::new(1); + let allowance = Allowance::Basic(BasicAllowance { + spend_limit: Some(limit), + expiration_unix_timestamp: None, + }); + + let (deps, env) = test.deps_mut_env(); + let res = storage + .insert_new_grant(deps, &env, &admin, &grantee, allowance) + .unwrap_err(); + + assert!(matches!( + res, + NymPoolContractError::InsufficientTokens { .. } + )); + + // equal to the available + let allowance = Allowance::Basic(BasicAllowance { + spend_limit: Some(available.clone()), + expiration_unix_timestamp: None, + }); + + let (deps, env) = test.deps_mut_env(); + let res = storage.insert_new_grant(deps, &env, &admin, &grantee, allowance); + assert!(res.is_ok()); + + // and below the available + let mut test = TestSetup::init(); + let mut limit = available.clone(); + limit.amount -= Uint128::new(1); + let allowance = Allowance::Basic(BasicAllowance { + spend_limit: Some(limit), + expiration_unix_timestamp: None, + }); + + let (deps, env) = test.deps_mut_env(); + let res = storage.insert_new_grant(deps, &env, &admin, &grantee, allowance); + assert!(res.is_ok()); + + Ok(()) + } + + #[test] + fn updates_allowances_initial_state_and_saves_it_to_storage() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + let admin = test.admin_unchecked(); + let grantee = test.generate_account(); + + let inner = ClassicPeriodicAllowance { + basic: BasicAllowance::unlimited(), + period_duration_secs: 3600, + period_spend_limit: coin(100, TEST_DENOM), + period_can_spend: None, + period_reset_unix_timestamp: 0, + }; + let allowance = Allowance::ClassicPeriodic(inner.clone()); + + let (deps, env) = test.deps_mut_env(); + let res = storage.insert_new_grant(deps, &env, &admin, &grantee, allowance); + assert!(res.is_ok()); + + let stored_grant = storage.load_grant(test.deps(), &grantee)?; + let mut expected_inner = inner; + expected_inner.period_can_spend = Some(expected_inner.period_spend_limit.clone()); + expected_inner.period_reset_unix_timestamp = + env.block.time.seconds() + expected_inner.period_duration_secs; + let expected = Allowance::ClassicPeriodic(expected_inner); + + assert_eq!(stored_grant.allowance, expected); + assert_eq!(stored_grant.grantee, grantee); + assert_eq!(stored_grant.granter, admin); + assert_eq!(stored_grant.granted_at_height, env.block.height); + + Ok(()) + } + } + + #[cfg(test)] + mod spending_part_of_grant { + use super::*; + + #[test] + fn requires_grant_to_exist() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + let grantee = test.generate_account(); + + let (deps, env) = test.deps_mut_env(); + let res = storage + .try_spend_part_of_grant(deps, &env, &grantee, &coin(100, TEST_DENOM)) + .unwrap_err(); + + assert!(matches!(res, NymPoolContractError::GrantNotFound { .. })); + + test.add_dummy_grant_for(&grantee); + assert!(storage + .try_spend_part_of_grant( + test.deps_mut(), + &env, + &grantee, + &coin(100, TEST_DENOM) + ) + .is_ok()); + Ok(()) + } + + #[test] + fn requires_grant_to_be_spendable() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + let admin = test.admin_unchecked(); + let grantee = test.generate_account(); + + let allowance = Allowance::Basic(BasicAllowance { + spend_limit: Some(coin(100, TEST_DENOM)), + expiration_unix_timestamp: None, + }); + + let (deps, env) = test.deps_mut_env(); + storage.insert_new_grant(deps, &env, &admin, &grantee, allowance)?; + + let res = storage + .try_spend_part_of_grant( + test.deps_mut(), + &env, + &grantee, + &coin(200, TEST_DENOM), + ) + .unwrap_err(); + + assert_eq!(res, NymPoolContractError::SpendingAboveAllowance); + Ok(()) + } + + #[test] + fn updates_stored_grant() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + let admin = test.admin_unchecked(); + let grantee = test.generate_account(); + + let allowance = Allowance::Basic(BasicAllowance { + spend_limit: Some(coin(100, TEST_DENOM)), + expiration_unix_timestamp: None, + }); + let (deps, env) = test.deps_mut_env(); + storage.insert_new_grant(deps, &env, &admin, &grantee, allowance)?; + + storage.try_spend_part_of_grant( + test.deps_mut(), + &env, + &grantee, + &coin(40, TEST_DENOM), + )?; + + let stored_grant = storage.load_grant(test.deps(), &grantee)?; + assert_eq!( + stored_grant.allowance, + Allowance::Basic(BasicAllowance { + spend_limit: Some(coin(60, TEST_DENOM)), + expiration_unix_timestamp: None, + }) + ); + + Ok(()) + } + + #[test] + fn removes_grant_from_storage_if_its_used_up() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + let admin = test.admin_unchecked(); + let grantee1 = test.generate_account(); + let grantee2 = test.generate_account(); + + let allowance = Allowance::Basic(BasicAllowance { + spend_limit: Some(coin(100, TEST_DENOM)), + expiration_unix_timestamp: None, + }); + let (deps, env) = test.deps_mut_env(); + storage.insert_new_grant(deps, &env, &admin, &grantee1, allowance.clone())?; + + let (deps, env) = test.deps_mut_env(); + storage.insert_new_grant(deps, &env, &admin, &grantee2, allowance)?; + + // use whole allowance with no locked tokens + storage.try_spend_part_of_grant( + test.deps_mut(), + &env, + &grantee1, + &coin(100, TEST_DENOM), + )?; + assert!(storage.try_load_grant(test.deps(), &grantee1)?.is_none()); + + // use whole allowance with some locked tokens + test.lock_allowance(grantee2.as_str(), Uint128::new(50)); + storage.try_spend_part_of_grant( + test.deps_mut(), + &env, + &grantee2, + &coin(50, TEST_DENOM), + )?; + + let stored_grant = storage.load_grant(test.deps(), &grantee2)?; + assert_eq!( + stored_grant.allowance, + Allowance::Basic(BasicAllowance { + spend_limit: Some(coin(0, TEST_DENOM)), + expiration_unix_timestamp: None, + }) + ); + + // unlock and attempt to spend again + storage.unlock_part_of_allowance( + test.deps_mut(), + &grantee2, + &coin(50, TEST_DENOM), + )?; + storage.try_spend_part_of_grant( + test.deps_mut(), + &env, + &grantee2, + &coin(50, TEST_DENOM), + )?; + assert!(storage.try_load_grant(test.deps(), &grantee2)?.is_none()); + + Ok(()) + } + } + + #[test] + fn removing_grant() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + let grantee = test.generate_account(); + + // no-op if doesn't exist + assert!(storage.remove_grant(test.deps_mut(), &grantee).is_ok()); + + // removes the actual entry from the map + test.add_dummy_grant_for(&grantee); + + assert!(storage + .grants + .may_load(test.storage(), grantee.clone())? + .is_some()); + + assert!(storage.remove_grant(test.deps_mut(), &grantee).is_ok()); + + assert!(storage + .grants + .may_load(test.storage(), grantee.clone())? + .is_none()); + + // if applicable, unlocks any locked tokens + // (all the details of unlocking are already tested in different unit test(s), + // so it's sufficient to check any of those occurred) + let grantee2 = test.add_dummy_grant().grantee; + test.lock_allowance(grantee2.as_str(), Uint128::new(50)); + + assert!(storage.remove_grant(test.deps_mut(), &grantee2).is_ok()); + + assert!(storage + .locked + .grantees + .may_load(test.storage(), grantee2)? + .is_none()); + assert!(storage.locked.total_locked.load(test.storage())?.is_zero()); + + Ok(()) + } + + #[cfg(test)] + mod revoking_grant { + use super::*; + + #[test] + fn requires_grant_to_exist() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + let admin = test.admin_unchecked(); + let grantee = test.generate_account(); + + assert_eq!( + storage + .revoke_grant(test.deps_mut(), &grantee, &admin) + .unwrap_err(), + NymPoolContractError::GrantNotFound { + grantee: grantee.to_string(), + } + ); + + test.add_dummy_grant_for(&grantee); + assert!(storage + .revoke_grant(test.deps_mut(), &grantee, &admin) + .is_ok()); + + Ok(()) + } + + #[test] + fn can_always_be_called_by_current_admin() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + let grantee = test.add_dummy_grant().grantee; + + // current admin + let admin = test.admin_unchecked(); + assert!(storage + .revoke_grant(test.deps_mut(), &grantee, &admin) + .is_ok()); + + // new admin + let new_admin = test.generate_account(); + let grantee = test.add_dummy_grant().grantee; + test.change_admin(&new_admin); + assert!(storage + .revoke_grant(test.deps_mut(), &grantee, &new_admin) + .is_ok()); + + // old admin + let grantee = test.add_dummy_grant().grantee; + assert_eq!( + storage + .revoke_grant(test.deps_mut(), &grantee, &admin) + .unwrap_err(), + NymPoolContractError::UnauthorizedGrantRevocation + ); + + Ok(()) + } + + #[test] + fn can_be_called_by_original_granter_if_its_still_whitelisted() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + let admin = test.admin_unchecked(); + let granter = test.generate_account(); + let grantee1 = test.generate_account(); + let grantee2 = test.generate_account(); + + test.add_granter(&granter); + let env = test.env(); + storage.insert_new_grant( + test.deps_mut(), + &env, + &granter, + &grantee1, + dummy_allowance(), + )?; + storage.insert_new_grant( + test.deps_mut(), + &env, + &granter, + &grantee2, + dummy_allowance(), + )?; + + // still whitelisted + assert!(storage + .revoke_grant(test.deps_mut(), &grantee1, &granter) + .is_ok()); + + // not whitelisted anymore + storage.remove_granter(test.deps_mut(), &admin, &granter)?; + assert_eq!( + storage + .revoke_grant(test.deps_mut(), &grantee2, &granter) + .unwrap_err(), + NymPoolContractError::UnauthorizedGrantRevocation + ); + + Ok(()) + } + + #[test] + fn removes_the_underlying_grant() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + let admin = test.admin_unchecked(); + let grantee = test.add_dummy_grant().grantee; + + storage.revoke_grant(test.deps_mut(), &grantee, &admin)?; + assert!(storage + .grants + .may_load(test.storage(), grantee.clone())? + .is_none()); + + Ok(()) + } + } + + #[cfg(test)] + mod locking_part_of_allowance { + use super::*; + + #[test] + fn requires_providing_valid_coin() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + let grantee = test.add_dummy_grant().grantee; + + let bad_amount = coin(0, "invalid-denom"); + let good_amount = test.coin(100); + + let env = test.env(); + + assert!(storage + .lock_part_of_allowance(test.deps_mut(), &env, &grantee, bad_amount) + .is_err()); + assert!(storage + .lock_part_of_allowance(test.deps_mut(), &env, &grantee, good_amount) + .is_ok()); + + Ok(()) + } + + #[test] + fn requires_grant_to_exist() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + let grantee = test.generate_account(); + let env = test.env(); + + let amount = test.coin(100); + + // doesn't exist + assert!(storage + .lock_part_of_allowance(test.deps_mut(), &env, &grantee, amount.clone()) + .is_err()); + + // does exist + test.add_dummy_grant_for(&grantee); + assert!(storage + .lock_part_of_allowance(test.deps_mut(), &env, &grantee, amount) + .is_ok()); + Ok(()) + } + + #[test] + fn does_not_allow_locking_more_than_spend_limit() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + let admin = test.admin_unchecked(); + let env = test.env(); + + let allowance = Allowance::Basic(BasicAllowance { + spend_limit: Some(test.coin(100)), + expiration_unix_timestamp: None, + }); + let grantee = test.generate_account(); + storage.insert_new_grant(test.deps_mut(), &env, &admin, &grantee, allowance)?; + + let amount = test.coin(101); + assert!(storage + .lock_part_of_allowance(test.deps_mut(), &env, &grantee, amount) + .is_err()); + + let amount = test.coin(100); + assert!(storage + .lock_part_of_allowance(test.deps_mut(), &env, &grantee, amount) + .is_ok()); + + Ok(()) + } + + #[test] + fn deducts_locked_amount_from_the_allowance() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + let admin = test.admin_unchecked(); + let env = test.env(); + + let allowance = Allowance::Basic(BasicAllowance { + spend_limit: Some(test.coin(100)), + expiration_unix_timestamp: None, + }); + let grantee = test.generate_account(); + storage.insert_new_grant(test.deps_mut(), &env, &admin, &grantee, allowance)?; + + let amount = test.coin(40); + storage.lock_part_of_allowance(test.deps_mut(), &env, &grantee, amount)?; + + let allowance = storage + .grants + .load(test.storage(), grantee.clone())? + .allowance; + assert_eq!(allowance.basic().spend_limit, Some(test.coin(60))); + + // no-op if there's no limit + let grantee = test.generate_account(); + let unlimited = Allowance::Basic(BasicAllowance::unlimited()); + storage.insert_new_grant(test.deps_mut(), &env, &admin, &grantee, unlimited)?; + + let amount = test.coin(40); + storage.lock_part_of_allowance(test.deps_mut(), &env, &grantee, amount)?; + let allowance = storage + .grants + .load(test.storage(), grantee.clone())? + .allowance; + assert_eq!(allowance.basic(), &BasicAllowance::unlimited()); + + Ok(()) + } + + #[test] + fn preserves_grant_even_if_resultant_allowance_is_zero() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + let admin = test.admin_unchecked(); + let env = test.env(); + + let allowance = Allowance::Basic(BasicAllowance { + spend_limit: Some(test.coin(100)), + expiration_unix_timestamp: None, + }); + let grantee = test.generate_account(); + storage.insert_new_grant(test.deps_mut(), &env, &admin, &grantee, allowance)?; + + let amount = test.coin(100); + storage.lock_part_of_allowance(test.deps_mut(), &env, &grantee, amount)?; + + let allowance = storage + .grants + .load(test.storage(), grantee.clone())? + .allowance; + assert_eq!(allowance.basic().spend_limit, Some(test.coin(0))); + + Ok(()) + } + + #[test] + fn updates_internal_locked_counter() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + let env = test.env(); + let grantee = test.add_dummy_grant().grantee; + let amount1 = test.coin(100); + let amount2 = test.coin(200); + + storage.lock_part_of_allowance(test.deps_mut(), &env, &grantee, amount1)?; + assert_eq!( + storage.locked.grantee_locked(test.storage(), &grantee)?, + Uint128::new(100) + ); + assert_eq!( + storage.locked.total_locked.load(test.storage())?, + Uint128::new(100) + ); + + // more locked by same grantee + storage.lock_part_of_allowance(test.deps_mut(), &env, &grantee, amount2)?; + assert_eq!( + storage.locked.grantee_locked(test.storage(), &grantee)?, + Uint128::new(300) + ); + assert_eq!( + storage.locked.total_locked.load(test.storage())?, + Uint128::new(300) + ); + + Ok(()) + } + } + + #[cfg(test)] + mod unlocking_part_of_allowance { + use super::*; + + fn setup_locked_grant(test: &mut TestSetup) -> Addr { + let grantee = test.add_dummy_grant().grantee; + test.lock_allowance(&grantee, Uint128::new(100)); + grantee + } + + #[test] + fn requires_providing_valid_coin() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + let grantee = setup_locked_grant(&mut test); + + let bad_amount = coin(0, "invalid-denom"); + let good_amount = test.coin(100); + + assert!(storage + .unlock_part_of_allowance(test.deps_mut(), &grantee, &bad_amount) + .is_err()); + assert!(storage + .unlock_part_of_allowance(test.deps_mut(), &grantee, &good_amount) + .is_ok()); + + Ok(()) + } + + #[test] + fn does_not_allow_unlocking_more_than_currently_locked() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + let grantee = setup_locked_grant(&mut test); + + let amount = test.coin(101); + assert!(storage + .unlock_part_of_allowance(test.deps_mut(), &grantee, &amount) + .is_err()); + + let amount = test.coin(100); + assert!(storage + .unlock_part_of_allowance(test.deps_mut(), &grantee, &amount) + .is_ok()); + Ok(()) + } + + #[test] + fn requires_grant_to_exist() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + let grantee = test.generate_account(); + + let amount = test.coin(100); + + assert!(storage + .unlock_part_of_allowance(test.deps_mut(), &grantee, &amount) + .is_err()); + test.add_dummy_grant_for(&grantee); + test.lock_allowance(&grantee, Uint128::new(100)); + assert!(storage + .unlock_part_of_allowance(test.deps_mut(), &grantee, &amount) + .is_ok()); + Ok(()) + } + + #[test] + fn requires_having_locked_coins() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + let grantee = test.add_dummy_grant().grantee; + + let amount = test.coin(100); + + assert!(storage + .unlock_part_of_allowance(test.deps_mut(), &grantee, &amount) + .is_err()); + test.lock_allowance(&grantee, Uint128::new(100)); + assert!(storage + .unlock_part_of_allowance(test.deps_mut(), &grantee, &amount) + .is_ok()); + Ok(()) + } + + #[test] + fn increases_internal_grant_spend_limit() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + let admin = test.admin_unchecked(); + let env = test.env(); + + let allowance = Allowance::Basic(BasicAllowance { + spend_limit: Some(test.coin(100)), + expiration_unix_timestamp: None, + }); + let grantee = test.generate_account(); + storage.insert_new_grant(test.deps_mut(), &env, &admin, &grantee, allowance)?; + + let amount = test.coin(40); + storage.lock_part_of_allowance(test.deps_mut(), &env, &grantee, amount)?; + + let amount = test.coin(20); + storage.unlock_part_of_allowance(test.deps_mut(), &grantee, &amount)?; + + let allowance = storage + .grants + .load(test.storage(), grantee.clone())? + .allowance; + assert_eq!(allowance.basic().spend_limit, Some(test.coin(80))); + + // no-op if there's no limit + let grantee = test.generate_account(); + let unlimited = Allowance::Basic(BasicAllowance::unlimited()); + storage.insert_new_grant(test.deps_mut(), &env, &admin, &grantee, unlimited)?; + + let amount = test.coin(40); + storage.lock_part_of_allowance(test.deps_mut(), &env, &grantee, amount)?; + + let amount = test.coin(20); + storage.unlock_part_of_allowance(test.deps_mut(), &grantee, &amount)?; + + let allowance = storage + .grants + .load(test.storage(), grantee.clone())? + .allowance; + assert_eq!(allowance.basic(), &BasicAllowance::unlimited()); + + Ok(()) + } + + #[test] + fn updates_internal_locked_counter() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = NymPoolStorage::new(); + + // 100tokens locked + let grantee = setup_locked_grant(&mut test); + + let amount = test.coin(20); + storage.unlock_part_of_allowance(test.deps_mut(), &grantee, &amount)?; + assert_eq!( + storage.locked.grantee_locked(test.storage(), &grantee)?, + Uint128::new(80) + ); + assert_eq!( + storage.locked.total_locked.load(test.storage())?, + Uint128::new(80) + ); + + let amount = test.coin(80); + storage.unlock_part_of_allowance(test.deps_mut(), &grantee, &amount)?; + assert!(storage + .locked + .grantees + .may_load(test.storage(), grantee)? + .is_none(),); + assert!(storage.locked.total_locked.load(test.storage())?.is_zero()); + + Ok(()) + } + } + } + + #[cfg(test)] + mod locked_storage { + use super::*; + use crate::testing::TestSetup; + use cosmwasm_std::testing::mock_dependencies; + + #[test] + fn is_initialised_with_zero_total_locked() -> anyhow::Result<()> { + let mut deps = mock_dependencies(); + let storage = LockedStorage::new(); + + // by default, when created, the `total_locked` is inaccessible + assert!(storage.total_locked.load(&deps.storage).is_err()); + + storage.initialise(deps.as_mut())?; + // but after proper initialisation, it's correctly set to 0 + assert_eq!(storage.total_locked.load(&deps.storage)?, Uint128::zero()); + + Ok(()) + } + + #[test] + fn getting_grantee_locked() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let grantee = test.generate_account(); + + let storage = LockedStorage::new(); + + // returns zero when there's nothing + assert!(storage + .grantee_locked(test.deps().storage, &grantee)? + .is_zero()); + + // even when a grant is created (but with nothing locked!) + test.add_dummy_grant_for(&grantee); + assert!(storage + .grantee_locked(test.deps().storage, &grantee)? + .is_zero()); + let to_lock = Uint128::new(100); + + // lock some tokens... + test.lock_allowance(&grantee, to_lock); + + // now we're talking! + assert_eq!( + storage.grantee_locked(test.deps().storage, &grantee)?, + to_lock + ); + + Ok(()) + } + + #[test] + fn getting_maybe_grantee_locked() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let grantee = test.generate_account(); + + let storage = LockedStorage::new(); + + // returns None when there's nothing + assert!(storage + .maybe_grantee_locked(test.deps().storage, &grantee)? + .is_none()); + + // even when a grant is created (but with nothing locked!) + test.add_dummy_grant_for(&grantee); + assert!(storage + .maybe_grantee_locked(test.deps().storage, &grantee)? + .is_none()); + let to_lock = Uint128::new(100); + + // lock some tokens... + test.lock_allowance(&grantee, to_lock); + + // now we're talking! + assert_eq!( + storage.maybe_grantee_locked(test.deps().storage, &grantee)?, + Some(to_lock) + ); + + Ok(()) + } + + #[test] + fn locking_tokens() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = LockedStorage::new(); + + let grantee1 = test.generate_account(); + let grantee2 = test.generate_account(); + + let first = Uint128::new(100); + let second = Uint128::new(200); + let third = Uint128::new(500); + + let all = test.full_locked_map(); + assert!(all.is_empty()); + + // fresh one creates entry for grantee with the amount + // and updates the total + let deps = test.deps_mut(); + storage.lock(deps, &grantee1, first)?; + + let deps = test.deps(); + assert_eq!(storage.total_locked.load(deps.storage)?, first); + assert_eq!(storage.grantee_locked(deps.storage, &grantee1)?, first); + + let all = test.full_locked_map(); + assert_eq!(all.len(), 1); + + // another one updates existing entries (and doesn't overwrite them!) + let deps = test.deps_mut(); + storage.lock(deps, &grantee1, second)?; + + let deps = test.deps(); + assert_eq!(storage.total_locked.load(deps.storage)?, first + second); + assert_eq!( + storage.grantee_locked(deps.storage, &grantee1)?, + first + second + ); + + let all = test.full_locked_map(); + assert_eq!(all.len(), 1); + + // if we do it for a new grantee, another entry is created, but the same total is updated + let deps = test.deps_mut(); + storage.lock(deps, &grantee2, third)?; + + let deps = test.deps(); + assert_eq!( + storage.total_locked.load(deps.storage)?, + first + second + third + ); + assert_eq!( + storage.grantee_locked(deps.storage, &grantee1)?, + first + second + ); + assert_eq!(storage.grantee_locked(deps.storage, &grantee2)?, third); + + let all = test.full_locked_map(); + assert_eq!(all.len(), 2); + Ok(()) + } + + #[test] + fn unlocking_tokens() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let storage = LockedStorage::new(); + + let grantee1 = test.generate_account(); + let grantee2 = test.generate_account(); + + test.add_dummy_grant_for(&grantee1); + test.add_dummy_grant_for(&grantee2); + + let first = Uint128::new(100); + let second = Uint128::new(200); + let third = Uint128::new(500); + + let all = test.full_locked_map(); + assert!(all.is_empty()); + + let deps = test.deps_mut(); + + // can't unlock anything if there's nothing locked + assert!(matches!( + storage.unlock(deps, &grantee1, first).unwrap_err(), + NymPoolContractError::InsufficientLockedTokens { .. } + )); + + test.lock_allowance(&grantee1, first); + + // can't unlock more than the total locked amount + let deps = test.deps_mut(); + assert!(matches!( + storage.unlock(deps, &grantee1, first + second).unwrap_err(), + NymPoolContractError::InsufficientLockedTokens { .. } + )); + test.lock_allowance(&grantee1, second); + test.lock_allowance(&grantee2, third); + let all = test.full_locked_map(); + assert_eq!(all.len(), 2); + + // unlocking partial amount correctly updates entries + let deps = test.deps_mut(); + assert!(storage.unlock(deps, &grantee1, first).is_ok()); + + let deps = test.deps_mut(); + assert_eq!(storage.total_locked.load(deps.storage)?, second + third); + assert_eq!(storage.grantee_locked(deps.storage, &grantee1)?, second); + let all = test.full_locked_map(); + assert_eq!(all.len(), 2); + + // unlocking the remaining amount will remove the entry + let deps = test.deps_mut(); + assert!(storage.unlock(deps, &grantee1, second).is_ok()); + let deps = test.deps_mut(); + assert_eq!(storage.total_locked.load(deps.storage)?, third); + assert!(storage.grantee_locked(deps.storage, &grantee1)?.is_zero()); + let all = test.full_locked_map(); + assert_eq!(all.len(), 1); + + // similarly if the full amount is unlocked immediately + let deps = test.deps_mut(); + assert!(storage.unlock(deps, &grantee2, third).is_ok()); + let deps = test.deps_mut(); + assert!(storage.total_locked.load(deps.storage)?.is_zero()); + assert!(storage.grantee_locked(deps.storage, &grantee2)?.is_zero()); + + let all = test.full_locked_map(); + assert!(all.is_empty()); + + Ok(()) + } + } +} diff --git a/contracts/nym-pool/src/testing/mod.rs b/contracts/nym-pool/src/testing/mod.rs new file mode 100644 index 00000000000..701dd953de9 --- /dev/null +++ b/contracts/nym-pool/src/testing/mod.rs @@ -0,0 +1,311 @@ +// Copyright 2025 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::contract; +use crate::contract::{execute, instantiate, migrate, query}; +use crate::storage::NYM_POOL_STORAGE; +use crate::testing::storage::{ContractStorageWrapper, StorageWrapper}; +use cosmwasm_std::testing::{message_info, mock_env, MockApi, MockQuerier, MockStorage}; +use cosmwasm_std::{ + coin, coins, Addr, Coin, ContractInfo, Deps, DepsMut, Empty, Env, MemoryStorage, MessageInfo, + Order, OwnedDeps, Response, StdResult, Storage, Uint128, +}; +use cw_multi_test::{ + next_block, App, AppBuilder, AppResponse, BankKeeper, Contract, ContractWrapper, Executor, +}; +use nym_pool_contract_common::{ + Allowance, BasicAllowance, ExecuteMsg, Grant, InstantiateMsg, NymPoolContractError, QueryMsg, +}; +use rand::{RngCore, SeedableRng}; +use rand_chacha::ChaCha20Rng; +use serde::de::DeserializeOwned; +use std::collections::HashMap; + +mod storage; + +pub fn test_rng() -> ChaCha20Rng { + let dummy_seed = [42u8; 32]; + ChaCha20Rng::from_seed(dummy_seed) +} + +pub fn deps_with_balance(env: &Env) -> OwnedDeps> { + OwnedDeps { + storage: MockStorage::default(), + api: MockApi::default(), + querier: MockQuerier::::new(&[( + env.contract.address.as_str(), + coins(100000000000, TEST_DENOM).as_slice(), + )]), + custom_query_type: Default::default(), + } +} + +pub const TEST_DENOM: &str = "unym"; + +pub struct TestSetup { + pub app: App, + pub rng: ChaCha20Rng, + pub contract_address: Addr, + pub master_address: Addr, + pub(crate) storage: ContractStorageWrapper, +} + +pub fn contract() -> Box> { + let contract = ContractWrapper::new(execute, instantiate, query).with_migrate(migrate); + Box::new(contract) +} + +impl TestSetup { + pub fn init() -> TestSetup { + let storage = StorageWrapper::new(); + + let api = MockApi::default().with_prefix("n"); + let master_address = api.addr_make("master-owner"); + + let mut app = AppBuilder::new() + .with_api(api) + .with_storage(storage.clone()) + .build(|router, _api, storage| { + router + .bank + .init_balance( + storage, + &master_address, + coins(1000000000000000, TEST_DENOM), + ) + .unwrap() + }); + let code_id = app.store_code(contract()); + let contract_address = app + .instantiate_contract( + code_id, + master_address.clone(), + &InstantiateMsg { + pool_denomination: TEST_DENOM.to_string(), + grants: Default::default(), + }, + &[], + "nym-pool-contract", + Some(master_address.to_string()), + ) + .unwrap(); + + // send some tokens to the contract + app.send_tokens( + master_address.clone(), + contract_address.clone(), + &[coin(100000000, TEST_DENOM)], + ) + .unwrap(); + + TestSetup { + app, + rng: test_rng(), + storage: storage.contract_storage_wrapper(&contract_address), + contract_address, + master_address, + } + } + + pub fn set_contract_balance(&mut self, balance: Coin) { + let contract_address = &self.contract_address; + self.app + .router() + .bank + .init_balance( + &mut self.storage.inner_storage(), + contract_address, + vec![balance], + ) + .unwrap(); + } + + pub fn deps(&self) -> Deps<'_> { + Deps { + storage: &self.storage, + api: self.app.api(), + querier: self.app.wrap(), + } + } + + pub fn deps_mut(&mut self) -> DepsMut<'_> { + DepsMut { + storage: &mut self.storage, + api: self.app.api(), + querier: self.app.wrap(), + } + } + + pub fn deps_mut_env(&mut self) -> (DepsMut<'_>, Env) { + let env = self.env().clone(); + (self.deps_mut(), env) + } + + pub fn storage(&self) -> &dyn Storage { + &self.storage + } + + pub fn storage_mut(&mut self) -> &mut dyn Storage { + &mut self.storage + } + + pub fn env(&self) -> Env { + Env { + block: self.app.block_info(), + contract: ContractInfo { + address: self.contract_address.clone(), + }, + ..mock_env() + } + } + + pub fn next_block(&mut self) { + self.app.update_block(next_block) + } + + pub fn execute_raw( + &mut self, + sender: Addr, + message: ExecuteMsg, + ) -> Result { + self.execute_raw_with_balance(sender, &[], message) + } + + pub fn execute_raw_with_balance( + &mut self, + sender: Addr, + coins: &[Coin], + message: ExecuteMsg, + ) -> Result { + let env = self.env(); + let info = message_info(&sender, coins); + contract::execute(self.deps_mut(), env, info, message) + } + + pub fn execute_msg( + &mut self, + sender: Addr, + message: &ExecuteMsg, + ) -> anyhow::Result { + self.execute_msg_with_balance(sender, &[], message) + } + + pub fn execute_msg_with_balance( + &mut self, + sender: Addr, + coins: &[Coin], + message: &ExecuteMsg, + ) -> anyhow::Result { + self.app + .execute_contract(sender, self.contract_address.clone(), message, coins) + } + + pub fn query(&self, message: &QueryMsg) -> StdResult { + self.app + .wrap() + .query_wasm_smart(self.contract_address.as_str(), message) + } + + pub fn generate_account(&mut self) -> Addr { + self.app + .api() + .addr_make(&format!("foomp{}", self.rng.next_u64())) + } + + pub fn admin_unchecked(&self) -> Addr { + NYM_POOL_STORAGE + .contract_admin + .get(self.deps()) + .unwrap() + .unwrap() + } + + pub fn change_admin(&mut self, new_admin: &Addr) { + self.execute_msg( + self.admin_unchecked(), + &ExecuteMsg::UpdateAdmin { + admin: new_admin.to_string(), + update_granter_set: Some(true), + }, + ) + .unwrap(); + } + + pub fn admin_msg(&self) -> MessageInfo { + message_info(&self.admin_unchecked(), &[]) + } + + pub fn denom(&self) -> String { + NYM_POOL_STORAGE + .pool_denomination + .load(self.storage()) + .unwrap() + } + + pub fn coin(&self, amount: u128) -> Coin { + coin(amount, self.denom()) + } + + pub fn coins(&self, amount: u128) -> Vec { + coins(amount, self.denom()) + } + + #[track_caller] + pub fn add_dummy_grant(&mut self) -> Grant { + let grantee = self.generate_account(); + self.add_dummy_grant_for(&grantee) + } + + #[track_caller] + pub fn add_dummy_grant_for(&mut self, grantee: impl Into) -> Grant { + let grantee = Addr::unchecked(grantee); + let granter = self.admin_unchecked(); + let env = self.env(); + NYM_POOL_STORAGE + .insert_new_grant( + self.deps_mut(), + &env, + &granter, + &grantee, + Allowance::Basic(BasicAllowance::unlimited()), + ) + .unwrap(); + + NYM_POOL_STORAGE.load_grant(self.deps(), &grantee).unwrap() + } + + #[track_caller] + pub fn lock_allowance(&mut self, grantee: impl Into, amount: impl Into) { + let denom = NYM_POOL_STORAGE + .pool_denomination + .load(self.deps().storage) + .unwrap(); + + self.execute_msg( + Addr::unchecked(grantee), + &ExecuteMsg::LockAllowance { + amount: coin(amount.into().u128(), denom), + }, + ) + .unwrap(); + } + + #[track_caller] + pub fn full_locked_map(&self) -> HashMap { + NYM_POOL_STORAGE + .locked + .grantees + .range(self.deps().storage, None, None, Order::Ascending) + .collect::, _>>() + .unwrap() + } + + #[track_caller] + pub fn add_granter(&mut self, granter: &Addr) { + let env = self.env(); + let admin = self.admin_unchecked(); + NYM_POOL_STORAGE + .add_new_granter(self.deps_mut(), &env, &admin, granter) + .unwrap(); + } +} diff --git a/contracts/nym-pool/src/testing/storage.rs b/contracts/nym-pool/src/testing/storage.rs new file mode 100644 index 00000000000..14ba3e955ac --- /dev/null +++ b/contracts/nym-pool/src/testing/storage.rs @@ -0,0 +1,168 @@ +// Copyright 2025 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmwasm_std::storage_keys::to_length_prefixed_nested; +use cosmwasm_std::testing::MockStorage; +use cosmwasm_std::{Addr, MemoryStorage, Order, Record, Storage}; +use std::cell::RefCell; +use std::rc::Rc; + +#[derive(Debug, Clone)] +pub struct StorageWrapper(Rc>); + +impl StorageWrapper { + pub(super) fn contract_storage_wrapper(&self, contract: &Addr) -> ContractStorageWrapper { + ContractStorageWrapper { + address: contract.clone(), + inner: self.clone(), + } + } + + pub(super) fn new() -> Self { + StorageWrapper(Rc::new(RefCell::new(MockStorage::new()))) + } +} + +#[derive(Debug, Clone)] +pub(crate) struct ContractStorageWrapper { + address: Addr, + inner: StorageWrapper, +} + +impl ContractStorageWrapper { + pub fn inner_storage(&self) -> StorageWrapper { + self.inner.clone() + } +} + +impl Storage for StorageWrapper { + fn get(&self, key: &[u8]) -> Option> { + self.0.borrow().get(key) + } + + fn range<'a>( + &'a self, + start: Option<&[u8]>, + end: Option<&[u8]>, + order: Order, + ) -> Box + 'a> { + // hehe, that's nasty + let vals = self.0.borrow().range(start, end, order).collect::>(); + Box::new(vals.into_iter()) + } + + fn set(&mut self, key: &[u8], value: &[u8]) { + self.0.borrow_mut().set(key, value); + } + + fn remove(&mut self, key: &[u8]) { + self.0.borrow_mut().remove(key); + } +} + +impl ContractStorageWrapper { + fn contract_namespace(&self) -> Vec { + let mut name = b"contract_data/".to_vec(); + name.extend_from_slice(self.address.as_bytes()); + name + } + + fn prefix(&self) -> Vec { + to_length_prefixed_nested(&[b"wasm", &self.contract_namespace()]) + } + + #[inline] + fn trim(namespace: &[u8], key: &[u8]) -> Vec { + key[namespace.len()..].to_vec() + } + + /// Returns a new vec of same length and last byte incremented by one + /// If last bytes are 255, we handle overflow up the chain. + /// If all bytes are 255, this returns wrong data - but that is never possible as a namespace + fn namespace_upper_bound(input: &[u8]) -> Vec { + let mut copy = input.to_vec(); + // zero out all trailing 255, increment first that is not such + for i in (0..input.len()).rev() { + if copy[i] == 255 { + copy[i] = 0; + } else { + copy[i] += 1; + break; + } + } + copy + } + + #[inline] + fn concat(namespace: &[u8], key: &[u8]) -> Vec { + let mut k = namespace.to_vec(); + k.extend_from_slice(key); + k + } + + fn get_with_prefix(storage: &dyn Storage, namespace: &[u8], key: &[u8]) -> Option> { + storage.get(&Self::concat(namespace, key)) + } + + fn set_with_prefix(storage: &mut dyn Storage, namespace: &[u8], key: &[u8], value: &[u8]) { + storage.set(&Self::concat(namespace, key), value); + } + + fn remove_with_prefix(storage: &mut dyn Storage, namespace: &[u8], key: &[u8]) { + storage.remove(&Self::concat(namespace, key)); + } + + fn range_with_prefix<'a>( + storage: &'a dyn Storage, + namespace: &[u8], + start: Option<&[u8]>, + end: Option<&[u8]>, + order: Order, + ) -> Box + 'a> { + // prepare start, end with prefix + let start = match start { + Some(s) => Self::concat(namespace, s), + None => namespace.to_vec(), + }; + let end = match end { + Some(e) => Self::concat(namespace, e), + // end is updating last byte by one + None => Self::namespace_upper_bound(namespace), + }; + + // get iterator from storage + let base_iterator = storage.range(Some(&start), Some(&end), order); + + // make a copy for the closure to handle lifetimes safely + let prefix = namespace.to_vec(); + let mapped = base_iterator.map(move |(k, v)| (Self::trim(&prefix, &k), v)); + Box::new(mapped) + } +} + +impl Storage for ContractStorageWrapper { + fn get(&self, key: &[u8]) -> Option> { + let prefix = self.prefix(); + Self::get_with_prefix(&self.inner, &prefix, key) + } + + fn range<'a>( + &'a self, + start: Option<&[u8]>, + end: Option<&[u8]>, + order: Order, + ) -> Box + 'a> { + let prefix = self.prefix(); + Self::range_with_prefix(&self.inner, &prefix, start, end, order) + } + + fn set(&mut self, key: &[u8], value: &[u8]) { + let prefix = self.prefix(); + Self::set_with_prefix(&mut self.inner, &prefix, key, value); + } + + fn remove(&mut self, key: &[u8]) { + let prefix = self.prefix(); + Self::remove_with_prefix(&mut self.inner, &prefix, key); + } +} diff --git a/contracts/nym-pool/src/transactions.rs b/contracts/nym-pool/src/transactions.rs new file mode 100644 index 00000000000..59181bd07b6 --- /dev/null +++ b/contracts/nym-pool/src/transactions.rs @@ -0,0 +1,1597 @@ +// Copyright 2025 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::helpers::validate_usage_coin; +use crate::storage::NYM_POOL_STORAGE; +use cosmwasm_std::{BankMsg, Coin, CosmosMsg, DepsMut, Env, MessageInfo, Response, Uint128}; +use nym_pool_contract_common::{Allowance, NymPoolContractError, TransferRecipient}; + +pub fn try_update_contract_admin( + mut deps: DepsMut<'_>, + env: Env, + info: MessageInfo, + new_admin: String, + update_granter_set: Option, +) -> Result { + let new_admin = deps.api.addr_validate(&new_admin)?; + let old_admin = info.sender.clone(); + + if let Some(true) = update_granter_set { + // remove current/old admin from the granter set, if present + NYM_POOL_STORAGE + .granters + .remove(deps.storage, old_admin.clone()); + + // insert new admin into the granter set + NYM_POOL_STORAGE.add_new_granter(deps.branch(), &env, &old_admin, &new_admin)?; + } + + let res = NYM_POOL_STORAGE + .contract_admin + .execute_update_admin(deps, info, Some(new_admin))?; + + Ok(res) +} + +pub fn try_grant_allowance( + deps: DepsMut<'_>, + env: Env, + info: MessageInfo, + grantee: String, + allowance: Allowance, +) -> Result { + let grantee = deps.api.addr_validate(&grantee)?; + + NYM_POOL_STORAGE.insert_new_grant(deps, &env, &info.sender, &grantee, allowance)?; + + // TODO: emit events + Ok(Response::new()) +} + +pub fn try_revoke_grant( + deps: DepsMut<'_>, + _env: Env, + info: MessageInfo, + grantee: String, +) -> Result { + let grantee = deps.api.addr_validate(&grantee)?; + + NYM_POOL_STORAGE.revoke_grant(deps, &grantee, &info.sender)?; + + // TODO: emit events + Ok(Response::new()) +} + +pub fn try_use_allowance( + deps: DepsMut<'_>, + env: Env, + info: MessageInfo, + recipients: Vec, +) -> Result { + let denom = NYM_POOL_STORAGE.pool_denomination.load(deps.storage)?; + + if recipients.is_empty() { + return Err(NymPoolContractError::EmptyUsageRequest); + } + + let mut amount = Uint128::zero(); + let mut messages = Vec::new(); + for recipient in recipients { + validate_usage_coin(deps.storage, &recipient.amount)?; + + amount += recipient.amount.amount; + messages.push(CosmosMsg::Bank(BankMsg::Send { + to_address: deps.api.addr_validate(&recipient.recipient)?.to_string(), + amount: vec![recipient.amount], + })) + } + + let available = NYM_POOL_STORAGE.available_tokens(deps.as_ref(), &env)?; + // even if the contract has sufficient amount of tokens (which would be implicit from BankMsg not failing) + // the locked ones take priority + if available.amount < amount { + return Err(NymPoolContractError::InsufficientTokens { + available, + required: Coin { amount, denom }, + }); + } + + NYM_POOL_STORAGE.try_spend_part_of_grant(deps, &env, &info.sender, &Coin { amount, denom })?; + + // TODO: emit events + Ok(Response::new().add_messages(messages)) +} + +pub fn try_withdraw_allowance( + deps: DepsMut<'_>, + env: Env, + info: MessageInfo, + amount: Coin, +) -> Result { + validate_usage_coin(deps.storage, &amount)?; + + let available = NYM_POOL_STORAGE.available_tokens(deps.as_ref(), &env)?; + + // even if the contract has sufficient amount of tokens (which would be implicit from BankMsg not failing) + // the locked ones take priority + if available.amount < amount.amount { + return Err(NymPoolContractError::InsufficientTokens { + available, + required: amount, + }); + } + + NYM_POOL_STORAGE.try_spend_part_of_grant(deps, &env, &info.sender, &amount)?; + + // TODO: emit events + // TODO2: after migrating common to cw2.2 use `send_tokens` from `ResponseExt` trait + Ok(Response::new().add_message(CosmosMsg::Bank(BankMsg::Send { + to_address: info.sender.to_string(), + amount: vec![amount], + }))) +} + +pub fn try_lock_allowance( + deps: DepsMut<'_>, + env: Env, + info: MessageInfo, + amount: Coin, +) -> Result { + NYM_POOL_STORAGE.lock_part_of_allowance(deps, &env, &info.sender, amount)?; + + // TODO: emit events + Ok(Response::new()) +} + +pub fn try_unlock_allowance( + deps: DepsMut<'_>, + _env: Env, + info: MessageInfo, + amount: Coin, +) -> Result { + NYM_POOL_STORAGE.unlock_part_of_allowance(deps, &info.sender, &amount)?; + + // TODO: emit events + Ok(Response::new()) +} + +pub fn try_use_locked_allowance( + deps: DepsMut<'_>, + env: Env, + info: MessageInfo, + recipients: Vec, +) -> Result { + let mut amount = Uint128::zero(); + let mut messages = Vec::new(); + for recipient in recipients { + validate_usage_coin(deps.storage, &recipient.amount)?; + + amount += recipient.amount.amount; + messages.push(CosmosMsg::Bank(BankMsg::Send { + to_address: deps.api.addr_validate(&recipient.recipient)?.to_string(), + amount: vec![recipient.amount], + })) + } + + // if the grant has already expired, locked coins can no longer be used, + // ideally, they'd be immediately unlocked here, but we need to revert the transaction + let grant = NYM_POOL_STORAGE.load_grant(deps.as_ref(), &info.sender)?; + if grant.allowance.expired(&env) { + return Err(NymPoolContractError::GrantExpired); + } + + let denom = NYM_POOL_STORAGE.pool_denomination.load(deps.storage)?; + + // we remove those coins from the locked pool before transferring them to the specified account + NYM_POOL_STORAGE.unlock_part_of_allowance(deps, &info.sender, &Coin { amount, denom })?; + + // TODO: emit events + Ok(Response::new().add_messages(messages)) +} + +pub fn try_withdraw_locked_allowance( + deps: DepsMut<'_>, + env: Env, + info: MessageInfo, + amount: Coin, +) -> Result { + // if the grant has already expired, locked coins can no longer be used, + // ideally, they'd be immediately unlocked here, but we need to revert the transaction + let grant = NYM_POOL_STORAGE.load_grant(deps.as_ref(), &info.sender)?; + if grant.allowance.expired(&env) { + return Err(NymPoolContractError::GrantExpired); + } + + // we remove those coins from the locked pool before transferring them to the specified account + NYM_POOL_STORAGE.unlock_part_of_allowance(deps, &info.sender, &amount)?; + + // TODO: emit events + // TODO2: after migrating common to cw2.2 use `send_tokens` from `ResponseExt` trait + Ok(Response::new().add_message(CosmosMsg::Bank(BankMsg::Send { + to_address: info.sender.to_string(), + amount: vec![amount], + }))) +} + +pub fn try_add_new_granter( + deps: DepsMut<'_>, + env: Env, + info: MessageInfo, + granter: String, +) -> Result { + let granter = deps.api.addr_validate(&granter)?; + NYM_POOL_STORAGE.add_new_granter(deps, &env, &info.sender, &granter)?; + + // TODO: emit events + Ok(Response::new()) +} + +pub fn try_revoke_granter( + deps: DepsMut<'_>, + _env: Env, + info: MessageInfo, + granter: String, +) -> Result { + let granter = deps.api.addr_validate(&granter)?; + NYM_POOL_STORAGE.remove_granter(deps, &info.sender, &granter)?; + + // TODO: emit events + Ok(Response::new()) +} + +// can be called by anyone, because expired grants are unusable anyway +pub fn try_remove_expired( + deps: DepsMut<'_>, + env: Env, + _info: MessageInfo, + grantee: String, +) -> Result { + let grantee = deps.api.addr_validate(&grantee)?; + let grant = NYM_POOL_STORAGE.load_grant(deps.as_ref(), &grantee)?; + + if !grant.allowance.expired(&env) { + return Err(NymPoolContractError::GrantNotExpired); + } + + NYM_POOL_STORAGE.remove_grant(deps, &grantee)?; + + // TODO: emit events + Ok(Response::new()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::testing::TestSetup; + use nym_pool_contract_common::ExecuteMsg; + + #[cfg(test)] + mod updating_contract_admin { + use super::*; + use crate::testing::TestSetup; + use cosmwasm_std::{Deps, Order}; + use cw_controllers::AdminError; + use nym_pool_contract_common::{ExecuteMsg, GranterAddress, GranterInformation}; + use std::collections::HashMap; + + #[test] + fn can_only_be_performed_by_current_admin() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + + let random_acc = test.generate_account(); + let new_admin = test.generate_account(); + let res = test + .execute_raw( + random_acc, + ExecuteMsg::UpdateAdmin { + admin: new_admin.to_string(), + update_granter_set: None, + }, + ) + .unwrap_err(); + + assert_eq!(res, NymPoolContractError::Admin(AdminError::NotAdmin {})); + + let actual_admin = test.admin_unchecked(); + let res = test.execute_raw( + actual_admin.clone(), + ExecuteMsg::UpdateAdmin { + admin: new_admin.to_string(), + update_granter_set: None, + }, + ); + assert!(res.is_ok()); + + let updated_admin = test.admin_unchecked(); + assert_eq!(new_admin, updated_admin); + + Ok(()) + } + + #[test] + fn requires_providing_valid_address() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + + let bad_account = "definitely-not-valid-account"; + let res = test.execute_raw( + test.admin_unchecked(), + ExecuteMsg::UpdateAdmin { + admin: bad_account.to_string(), + update_granter_set: None, + }, + ); + + assert!(res.is_err()); + + let empty_account = ""; + let res = test.execute_raw( + test.admin_unchecked(), + ExecuteMsg::UpdateAdmin { + admin: empty_account.to_string(), + update_granter_set: None, + }, + ); + + assert!(res.is_err()); + + Ok(()) + } + + #[test] + fn updates_granter_set_if_specified() { + fn granters(deps: Deps) -> HashMap { + NYM_POOL_STORAGE + .granters + .range(deps.storage, None, None, Order::Ascending) + .map(|res| res.unwrap()) + .collect() + } + + let mut test = TestSetup::init(); + let current_admin = test.admin_unchecked(); + let new_admin = test.generate_account(); + + let old_granters = granters(test.deps()); + + // no change to the granter set + let res = test.execute_raw( + current_admin.clone(), + ExecuteMsg::UpdateAdmin { + admin: new_admin.to_string(), + update_granter_set: Some(false), + }, + ); + assert!(res.is_ok()); + let new_granters = granters(test.deps()); + assert_eq!(old_granters, new_granters); + + // + // + // + + let mut test = TestSetup::init(); + let current_admin = test.admin_unchecked(); + let new_admin = test.generate_account(); + let old_granters = granters(test.deps()); + + let res = test.execute_raw( + current_admin.clone(), + ExecuteMsg::UpdateAdmin { + admin: new_admin.to_string(), + update_granter_set: Some(true), + }, + ); + assert!(res.is_ok()); + let new_granters = granters(test.deps()); + assert_ne!(old_granters, new_granters); + assert!(old_granters.contains_key(¤t_admin)); + assert!(new_granters.contains_key(&new_admin)); + } + } + + #[cfg(test)] + mod granting_allowance { + use super::*; + use crate::testing::TestSetup; + use cosmwasm_std::StdError; + use nym_pool_contract_common::BasicAllowance; + + #[test] + fn requires_providing_valid_grantee_address() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + + let env = test.env(); + let admin = test.admin_msg(); + let dummy_grant = Allowance::Basic(BasicAllowance::unlimited()); + + assert!(matches!( + try_grant_allowance( + test.deps_mut(), + env.clone(), + admin.clone(), + "not-a-valid-address".to_string(), + dummy_grant.clone() + ) + .unwrap_err(), + NymPoolContractError::StdErr(StdError::GenericErr { msg, .. }) if msg == "Error decoding bech32" + )); + + let valid_address = test.generate_account(); + assert!(try_grant_allowance( + test.deps_mut(), + env.clone(), + admin.clone(), + valid_address.to_string(), + dummy_grant + ) + .is_ok()); + + Ok(()) + } + } + + #[cfg(test)] + mod revoking_allowance { + use super::*; + use crate::testing::TestSetup; + use cosmwasm_std::StdError; + + #[test] + fn requires_providing_valid_grantee_address() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + + let env = test.env(); + let admin = test.admin_msg(); + let grant = test.add_dummy_grant(); + + assert!(matches!( + try_revoke_grant( + test.deps_mut(), + env.clone(), + admin.clone(), + "not-a-valid-address".to_string() + ) + .unwrap_err(), + NymPoolContractError::StdErr(StdError::GenericErr { msg, .. }) if msg == "Error decoding bech32" + )); + + // use a valid address + // note the different error + let valid_address = test.generate_account(); + assert_eq!( + try_revoke_grant( + test.deps_mut(), + env.clone(), + admin.clone(), + valid_address.to_string() + ) + .unwrap_err(), + NymPoolContractError::GrantNotFound { + grantee: valid_address.to_string() + } + ); + + // for sanity’s sake check with an existing grant + assert!(try_revoke_grant( + test.deps_mut(), + env.clone(), + admin.clone(), + grant.grantee.to_string() + ) + .is_ok()); + + Ok(()) + } + } + + #[cfg(test)] + mod using_allowance { + use super::*; + use crate::testing::TestSetup; + use nym_pool_contract_common::{BasicAllowance, ExecuteMsg}; + + #[test] + fn requires_at_least_a_single_coin_receiver() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let grantee = test.add_dummy_grant().grantee; + + let res = test.execute_raw(grantee, ExecuteMsg::UseAllowance { recipients: vec![] }); + assert_eq!(res.unwrap_err(), NymPoolContractError::EmptyUsageRequest); + + Ok(()) + } + + #[test] + fn requires_valid_coin_for_each_receiver() -> anyhow::Result<()> { + // 1 bad receiver + let mut test = TestSetup::init(); + let grantee = test.add_dummy_grant().grantee; + + let res = test.execute_raw( + grantee, + ExecuteMsg::UseAllowance { + recipients: vec![TransferRecipient { + recipient: "invalid-address".to_string(), + amount: test.coin(1234), + }], + }, + ); + assert!(res.is_err()); + + // 3 receivers, one invalid + let mut test = TestSetup::init(); + let grantee = test.add_dummy_grant().grantee; + + let addr1 = test.generate_account(); + let addr2 = test.generate_account(); + let addr3 = test.generate_account(); + let res = test.execute_raw( + grantee, + ExecuteMsg::UseAllowance { + recipients: vec![ + TransferRecipient { + recipient: addr1.to_string(), + amount: test.coin(1234), + }, + TransferRecipient { + recipient: addr2.to_string(), + amount: test.coin(0), + }, + TransferRecipient { + recipient: addr3.to_string(), + amount: test.coin(1234), + }, + ], + }, + ); + assert!(res.is_err()); + + // all fine + let mut test = TestSetup::init(); + let grantee = test.add_dummy_grant().grantee; + + let res = test.execute_raw( + grantee, + ExecuteMsg::UseAllowance { + recipients: vec![ + TransferRecipient { + recipient: addr1.to_string(), + amount: test.coin(1234), + }, + TransferRecipient { + recipient: addr2.to_string(), + amount: test.coin(1), + }, + TransferRecipient { + recipient: addr3.to_string(), + amount: test.coin(1234), + }, + ], + }, + ); + assert!(res.is_ok()); + + Ok(()) + } + + #[test] + fn requires_the_total_to_be_available_for_spending() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let recipient = test.generate_account(); + + // contract balance < required + let grantee = test.add_dummy_grant().grantee; + test.set_contract_balance(test.coin(100)); + let res = test.execute_raw( + grantee.clone(), + ExecuteMsg::UseAllowance { + recipients: vec![ + TransferRecipient { + recipient: recipient.to_string(), + amount: test.coin(50), + }, + TransferRecipient { + recipient: recipient.to_string(), + amount: test.coin(51), + }, + ], + }, + ); + assert_eq!( + NymPoolContractError::InsufficientTokens { + available: test.coin(100), + required: test.coin(101) + }, + res.unwrap_err() + ); + + // contract balance == required + let res = test.execute_raw( + grantee, + ExecuteMsg::UseAllowance { + recipients: vec![ + TransferRecipient { + recipient: recipient.to_string(), + amount: test.coin(50), + }, + TransferRecipient { + recipient: recipient.to_string(), + amount: test.coin(50), + }, + ], + }, + ); + assert!(res.is_ok()); + + // contract balance > required + let grantee = test.add_dummy_grant().grantee; + test.set_contract_balance(test.coin(100)); + let res = test.execute_raw( + grantee, + ExecuteMsg::UseAllowance { + recipients: vec![TransferRecipient { + recipient: recipient.to_string(), + amount: test.coin(50), + }], + }, + ); + assert!(res.is_ok()); + + // contract balance > required BUT (contract balance - locked) < required + let grantee = test.add_dummy_grant().grantee; + test.set_contract_balance(test.coin(100)); + test.lock_allowance(&grantee, Uint128::new(40)); + let another_grantee = test.add_dummy_grant().grantee; + + let res = test.execute_raw( + another_grantee, + ExecuteMsg::UseAllowance { + recipients: vec![TransferRecipient { + recipient: recipient.to_string(), + amount: test.coin(61), + }], + }, + ); + assert_eq!( + NymPoolContractError::InsufficientTokens { + available: test.coin(60), + required: test.coin(61) + }, + res.unwrap_err() + ); + + Ok(()) + } + + #[test] + fn requires_the_total_to_be_within_spend_limit() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let allowance = Allowance::Basic(BasicAllowance { + spend_limit: Some(test.coin(100)), + expiration_unix_timestamp: None, + }); + let grantee = test.generate_account(); + let env = test.env(); + let admin = test.admin_unchecked(); + NYM_POOL_STORAGE.insert_new_grant( + test.deps_mut(), + &env, + &admin, + &grantee, + allowance, + )?; + + let recipient = test.generate_account(); + let res = test.execute_raw( + grantee.clone(), + ExecuteMsg::UseAllowance { + recipients: vec![TransferRecipient { + recipient: recipient.to_string(), + amount: test.coin(101), + }], + }, + ); + assert_eq!( + NymPoolContractError::SpendingAboveAllowance, + res.unwrap_err() + ); + + let res = test.execute_raw( + grantee, + ExecuteMsg::UseAllowance { + recipients: vec![TransferRecipient { + recipient: recipient.to_string(), + amount: test.coin(100), + }], + }, + ); + assert!(res.is_ok()); + + Ok(()) + } + + #[test] + fn attaches_appropriate_bank_message_for_each_receiver() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + + let grantee = test.add_dummy_grant().grantee; + + let recipient1 = test.generate_account(); + let recipient2 = test.generate_account(); + let recipient3 = test.generate_account(); + + let mut res = test.execute_raw( + grantee.clone(), + ExecuteMsg::UseAllowance { + recipients: vec![ + TransferRecipient { + recipient: recipient1.to_string(), + amount: test.coin(100), + }, + TransferRecipient { + recipient: recipient2.to_string(), + amount: test.coin(200), + }, + TransferRecipient { + recipient: recipient3.to_string(), + amount: test.coin(300), + }, + ], + }, + )?; + + // last + let CosmosMsg::Bank(BankMsg::Send { to_address, amount }) = + res.messages.pop().unwrap().msg + else { + panic!("invalid message") + }; + assert_eq!(to_address, recipient3.to_string()); + assert_eq!(amount, test.coins(300)); + + // second + let CosmosMsg::Bank(BankMsg::Send { to_address, amount }) = + res.messages.pop().unwrap().msg + else { + panic!("invalid message") + }; + assert_eq!(to_address, recipient2.to_string()); + assert_eq!(amount, test.coins(200)); + + // first + let CosmosMsg::Bank(BankMsg::Send { to_address, amount }) = + res.messages.pop().unwrap().msg + else { + panic!("invalid message") + }; + assert_eq!(to_address, recipient1.to_string()); + assert_eq!(amount, test.coins(100)); + + assert!(res.messages.is_empty()); + + Ok(()) + } + + #[test] + fn requires_grant_to_not_be_expired() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let env = test.env(); + let allowance = Allowance::Basic(BasicAllowance { + spend_limit: None, + expiration_unix_timestamp: Some(env.block.time.seconds() + 1), + }); + let grantee = test.generate_account(); + let admin = test.admin_unchecked(); + NYM_POOL_STORAGE.insert_new_grant( + test.deps_mut(), + &env, + &admin, + &grantee, + allowance, + )?; + test.next_block(); + + let recipient = test.generate_account(); + let res = test.execute_raw( + grantee.clone(), + ExecuteMsg::UseAllowance { + recipients: vec![TransferRecipient { + recipient: recipient.to_string(), + amount: test.coin(100), + }], + }, + ); + assert_eq!(NymPoolContractError::GrantExpired, res.unwrap_err()); + + Ok(()) + } + } + + #[cfg(test)] + mod withdrawing_from_allowance { + use super::*; + use crate::testing::TestSetup; + use cosmwasm_std::coin; + use nym_pool_contract_common::{BasicAllowance, ExecuteMsg}; + + #[test] + fn requires_valid_coin() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let grantee = test.add_dummy_grant().grantee; + + let res = test.execute_raw( + grantee.clone(), + ExecuteMsg::WithdrawAllowance { + amount: coin(1234, "wtf-denom"), + }, + ); + assert!(res.is_err()); + + let res = test.execute_raw( + grantee.clone(), + ExecuteMsg::WithdrawAllowance { + amount: test.coin(0), + }, + ); + assert!(res.is_err()); + + let res = test.execute_raw( + grantee.clone(), + ExecuteMsg::WithdrawAllowance { + amount: test.coin(123), + }, + ); + assert!(res.is_ok()); + + Ok(()) + } + + #[test] + fn requires_the_amount_to_be_available_for_spending() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + + // contract balance < required + let grantee = test.add_dummy_grant().grantee; + test.set_contract_balance(test.coin(100)); + let res = test.execute_raw( + grantee.clone(), + ExecuteMsg::WithdrawAllowance { + amount: test.coin(101), + }, + ); + assert_eq!( + NymPoolContractError::InsufficientTokens { + available: test.coin(100), + required: test.coin(101) + }, + res.unwrap_err() + ); + + // contract balance == required + let res = test.execute_raw( + grantee, + ExecuteMsg::WithdrawAllowance { + amount: test.coin(100), + }, + ); + assert!(res.is_ok()); + + // contract balance > required + let grantee = test.add_dummy_grant().grantee; + test.set_contract_balance(test.coin(100)); + let res = test.execute_raw( + grantee, + ExecuteMsg::WithdrawAllowance { + amount: test.coin(50), + }, + ); + assert!(res.is_ok()); + + // contract balance > required BUT (contract balance - locked) < required + let grantee = test.add_dummy_grant().grantee; + test.set_contract_balance(test.coin(100)); + test.lock_allowance(&grantee, Uint128::new(40)); + let another_grantee = test.add_dummy_grant().grantee; + + let res = test.execute_raw( + another_grantee, + ExecuteMsg::WithdrawAllowance { + amount: test.coin(61), + }, + ); + assert_eq!( + NymPoolContractError::InsufficientTokens { + available: test.coin(60), + required: test.coin(61) + }, + res.unwrap_err() + ); + + Ok(()) + } + + #[test] + fn requires_the_amount_to_be_within_spend_limit() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let allowance = Allowance::Basic(BasicAllowance { + spend_limit: Some(test.coin(100)), + expiration_unix_timestamp: None, + }); + let grantee = test.generate_account(); + let env = test.env(); + let admin = test.admin_unchecked(); + NYM_POOL_STORAGE.insert_new_grant( + test.deps_mut(), + &env, + &admin, + &grantee, + allowance, + )?; + + let res = test.execute_raw( + grantee.clone(), + ExecuteMsg::WithdrawAllowance { + amount: test.coin(101), + }, + ); + assert_eq!( + NymPoolContractError::SpendingAboveAllowance, + res.unwrap_err() + ); + + let res = test.execute_raw( + grantee, + ExecuteMsg::WithdrawAllowance { + amount: test.coin(100), + }, + ); + assert!(res.is_ok()); + + Ok(()) + } + + #[test] + fn attaches_appropriate_bank_message() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + + let grantee = test.add_dummy_grant().grantee; + + let mut res = test.execute_raw( + grantee.clone(), + ExecuteMsg::WithdrawAllowance { + amount: test.coin(100), + }, + )?; + + let CosmosMsg::Bank(BankMsg::Send { to_address, amount }) = + res.messages.pop().unwrap().msg + else { + panic!("invalid message") + }; + assert_eq!(to_address, grantee.to_string()); + assert_eq!(amount, test.coins(100)); + + assert!(res.messages.is_empty()); + + Ok(()) + } + + #[test] + fn requires_grant_to_not_be_expired() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let env = test.env(); + let allowance = Allowance::Basic(BasicAllowance { + spend_limit: None, + expiration_unix_timestamp: Some(env.block.time.seconds() + 1), + }); + let grantee = test.generate_account(); + let env = test.env(); + let admin = test.admin_unchecked(); + NYM_POOL_STORAGE.insert_new_grant( + test.deps_mut(), + &env, + &admin, + &grantee, + allowance, + )?; + test.next_block(); + + let res = test.execute_raw( + grantee.clone(), + ExecuteMsg::WithdrawAllowance { + amount: test.coin(101), + }, + ); + assert_eq!(NymPoolContractError::GrantExpired, res.unwrap_err()); + + Ok(()) + } + } + + #[test] + fn locking_allowance() -> anyhow::Result<()> { + // internals got tested in storage tests, so this is mostly about checking events (TODO) + let mut test = TestSetup::init(); + let grantee = test.add_dummy_grant().grantee; + + let res = test.execute_raw( + grantee.clone(), + ExecuteMsg::LockAllowance { + amount: test.coin(100), + }, + ); + assert!(res.is_ok()); + + assert_eq!( + NYM_POOL_STORAGE + .locked + .grantee_locked(test.storage(), &grantee)?, + Uint128::new(100) + ); + + Ok(()) + } + + #[test] + fn unlocking_allowance() -> anyhow::Result<()> { + // internals got tested in storage tests, so this is mostly about checking events (TODO) + let mut test = TestSetup::init(); + let grantee = test.add_dummy_grant().grantee; + test.lock_allowance(&grantee, Uint128::new(100)); + + let res = test.execute_raw( + grantee.clone(), + ExecuteMsg::UnlockAllowance { + amount: test.coin(40), + }, + ); + assert!(res.is_ok()); + + assert_eq!( + NYM_POOL_STORAGE + .locked + .grantee_locked(test.storage(), &grantee)?, + Uint128::new(60) + ); + + Ok(()) + } + + #[cfg(test)] + mod using_locked_allowance { + use super::*; + use nym_pool_contract_common::BasicAllowance; + + #[test] + fn requires_at_least_a_single_coin_receiver() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let grantee = test.add_dummy_grant().grantee; + + let res = test.execute_raw( + grantee, + ExecuteMsg::UseLockedAllowance { recipients: vec![] }, + ); + assert_eq!(res.unwrap_err(), NymPoolContractError::EmptyUsageRequest); + + Ok(()) + } + + #[test] + fn requires_valid_coin_for_each_receiver() -> anyhow::Result<()> { + // 1 bad receiver + let mut test = TestSetup::init(); + let grantee = test.add_dummy_grant().grantee; + test.lock_allowance(&grantee, Uint128::new(10000)); + + let res = test.execute_raw( + grantee, + ExecuteMsg::UseLockedAllowance { + recipients: vec![TransferRecipient { + recipient: "invalid-address".to_string(), + amount: test.coin(1234), + }], + }, + ); + assert!(res.is_err()); + + // 3 receivers, one invalid + let mut test = TestSetup::init(); + let grantee = test.add_dummy_grant().grantee; + test.lock_allowance(&grantee, Uint128::new(10000)); + + let addr1 = test.generate_account(); + let addr2 = test.generate_account(); + let addr3 = test.generate_account(); + let res = test.execute_raw( + grantee, + ExecuteMsg::UseLockedAllowance { + recipients: vec![ + TransferRecipient { + recipient: addr1.to_string(), + amount: test.coin(1234), + }, + TransferRecipient { + recipient: addr2.to_string(), + amount: test.coin(0), + }, + TransferRecipient { + recipient: addr3.to_string(), + amount: test.coin(1234), + }, + ], + }, + ); + assert!(res.is_err()); + + // all fine + let mut test = TestSetup::init(); + let grantee = test.add_dummy_grant().grantee; + test.lock_allowance(&grantee, Uint128::new(10000)); + + let res = test.execute_raw( + grantee, + ExecuteMsg::UseLockedAllowance { + recipients: vec![ + TransferRecipient { + recipient: addr1.to_string(), + amount: test.coin(1234), + }, + TransferRecipient { + recipient: addr2.to_string(), + amount: test.coin(1), + }, + TransferRecipient { + recipient: addr3.to_string(), + amount: test.coin(1234), + }, + ], + }, + ); + assert!(res.is_ok()); + + Ok(()) + } + + #[test] + fn requires_the_total_to_be_locked_by_grantee() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let grantee = test.add_dummy_grant().grantee; + test.lock_allowance(&grantee, Uint128::new(100)); + + let recipient = test.generate_account(); + let res = test.execute_raw( + grantee.clone(), + ExecuteMsg::UseLockedAllowance { + recipients: vec![TransferRecipient { + recipient: recipient.to_string(), + amount: test.coin(101), + }], + }, + ); + assert_eq!( + NymPoolContractError::InsufficientLockedTokens { + grantee: grantee.to_string(), + locked: Uint128::new(100), + requested: Uint128::new(101), + }, + res.unwrap_err() + ); + + let res = test.execute_raw( + grantee, + ExecuteMsg::UseLockedAllowance { + recipients: vec![TransferRecipient { + recipient: recipient.to_string(), + amount: test.coin(100), + }], + }, + ); + assert!(res.is_ok()); + + Ok(()) + } + + #[test] + fn attaches_appropriate_bank_message_for_each_receiver() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + + let grantee = test.add_dummy_grant().grantee; + test.lock_allowance(&grantee, Uint128::new(10000)); + + let recipient1 = test.generate_account(); + let recipient2 = test.generate_account(); + let recipient3 = test.generate_account(); + + let mut res = test.execute_raw( + grantee.clone(), + ExecuteMsg::UseLockedAllowance { + recipients: vec![ + TransferRecipient { + recipient: recipient1.to_string(), + amount: test.coin(100), + }, + TransferRecipient { + recipient: recipient2.to_string(), + amount: test.coin(200), + }, + TransferRecipient { + recipient: recipient3.to_string(), + amount: test.coin(300), + }, + ], + }, + )?; + + // last + let CosmosMsg::Bank(BankMsg::Send { to_address, amount }) = + res.messages.pop().unwrap().msg + else { + panic!("invalid message") + }; + assert_eq!(to_address, recipient3.to_string()); + assert_eq!(amount, test.coins(300)); + + // second + let CosmosMsg::Bank(BankMsg::Send { to_address, amount }) = + res.messages.pop().unwrap().msg + else { + panic!("invalid message") + }; + assert_eq!(to_address, recipient2.to_string()); + assert_eq!(amount, test.coins(200)); + + // first + let CosmosMsg::Bank(BankMsg::Send { to_address, amount }) = + res.messages.pop().unwrap().msg + else { + panic!("invalid message") + }; + assert_eq!(to_address, recipient1.to_string()); + assert_eq!(amount, test.coins(100)); + + assert!(res.messages.is_empty()); + + Ok(()) + } + + #[test] + fn requires_grant_to_not_be_expired() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let env = test.env(); + let allowance = Allowance::Basic(BasicAllowance { + spend_limit: None, + expiration_unix_timestamp: Some(env.block.time.seconds() + 1), + }); + let grantee = test.generate_account(); + let admin = test.admin_unchecked(); + NYM_POOL_STORAGE.insert_new_grant( + test.deps_mut(), + &env, + &admin, + &grantee, + allowance, + )?; + test.lock_allowance(&grantee, Uint128::new(10000)); + test.next_block(); + + let recipient = test.generate_account(); + let res = test.execute_raw( + grantee.clone(), + ExecuteMsg::UseLockedAllowance { + recipients: vec![TransferRecipient { + recipient: recipient.to_string(), + amount: test.coin(100), + }], + }, + ); + assert_eq!(NymPoolContractError::GrantExpired, res.unwrap_err()); + + Ok(()) + } + } + + #[cfg(test)] + mod withdrawing_from_locked_allowance { + use super::*; + use cosmwasm_std::coin; + use nym_pool_contract_common::BasicAllowance; + + #[test] + fn requires_valid_coin() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let grantee = test.add_dummy_grant().grantee; + test.lock_allowance(&grantee, Uint128::new(10000)); + + let res = test.execute_raw( + grantee.clone(), + ExecuteMsg::WithdrawLockedAllowance { + amount: coin(1234, "wtf-denom"), + }, + ); + assert!(res.is_err()); + + let res = test.execute_raw( + grantee.clone(), + ExecuteMsg::WithdrawLockedAllowance { + amount: test.coin(0), + }, + ); + assert!(res.is_err()); + + let res = test.execute_raw( + grantee.clone(), + ExecuteMsg::WithdrawLockedAllowance { + amount: test.coin(123), + }, + ); + assert!(res.is_ok()); + + Ok(()) + } + + #[test] + fn attaches_appropriate_bank_message() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + + let grantee = test.add_dummy_grant().grantee; + test.lock_allowance(&grantee, Uint128::new(10000)); + + let mut res = test.execute_raw( + grantee.clone(), + ExecuteMsg::WithdrawLockedAllowance { + amount: test.coin(100), + }, + )?; + + let CosmosMsg::Bank(BankMsg::Send { to_address, amount }) = + res.messages.pop().unwrap().msg + else { + panic!("invalid message") + }; + assert_eq!(to_address, grantee.to_string()); + assert_eq!(amount, test.coins(100)); + + assert!(res.messages.is_empty()); + + Ok(()) + } + + #[test] + fn requires_grant_to_not_be_expired() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let env = test.env(); + let allowance = Allowance::Basic(BasicAllowance { + spend_limit: None, + expiration_unix_timestamp: Some(env.block.time.seconds() + 1), + }); + let grantee = test.generate_account(); + let env = test.env(); + let admin = test.admin_unchecked(); + NYM_POOL_STORAGE.insert_new_grant( + test.deps_mut(), + &env, + &admin, + &grantee, + allowance, + )?; + test.lock_allowance(&grantee, Uint128::new(10000)); + + test.next_block(); + + let res = test.execute_raw( + grantee.clone(), + ExecuteMsg::WithdrawLockedAllowance { + amount: test.coin(101), + }, + ); + assert_eq!(NymPoolContractError::GrantExpired, res.unwrap_err()); + + Ok(()) + } + + #[test] + fn requires_the_amount_to_be_locked_by_grantee() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let grantee = test.add_dummy_grant().grantee; + test.lock_allowance(&grantee, Uint128::new(100)); + + let res = test.execute_raw( + grantee.clone(), + ExecuteMsg::WithdrawLockedAllowance { + amount: test.coin(101), + }, + ); + assert_eq!( + NymPoolContractError::InsufficientLockedTokens { + grantee: grantee.to_string(), + locked: Uint128::new(100), + requested: Uint128::new(101), + }, + res.unwrap_err() + ); + + let res = test.execute_raw( + grantee, + ExecuteMsg::WithdrawLockedAllowance { + amount: test.coin(100), + }, + ); + assert!(res.is_ok()); + + Ok(()) + } + } + + #[test] + fn adding_new_granter() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let bad_address = "foomp"; + let good_address = test.generate_account(); + + // requires valid address + let res = test.execute_raw( + test.admin_unchecked(), + ExecuteMsg::AddNewGranter { + granter: bad_address.to_string(), + }, + ); + assert!(res.is_err()); + + let res = test.execute_raw( + test.admin_unchecked(), + ExecuteMsg::AddNewGranter { + granter: good_address.to_string(), + }, + ); + assert!(res.is_ok()); + + // introduces new granter + assert!(NYM_POOL_STORAGE + .granters + .may_load(test.storage(), good_address)? + .is_some()); + + Ok(()) + } + + #[test] + fn revoking_granter() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let bad_address = "foomp"; + let good_address = test.generate_account(); + let granter_address = test.generate_account(); + test.add_granter(&granter_address); + + // requires valid address + let res = test.execute_raw( + test.admin_unchecked(), + ExecuteMsg::RevokeGranter { + granter: bad_address.to_string(), + }, + ); + assert!(res.is_err()); + + // requires an actual granter + let res = test.execute_raw( + test.admin_unchecked(), + ExecuteMsg::RevokeGranter { + granter: good_address.to_string(), + }, + ); + assert!(res.is_err()); + + // revokes the granter + let res = test.execute_raw( + test.admin_unchecked(), + ExecuteMsg::RevokeGranter { + granter: granter_address.to_string(), + }, + ); + assert!(res.is_ok()); + + assert!(NYM_POOL_STORAGE + .granters + .may_load(test.storage(), granter_address)? + .is_none()); + + Ok(()) + } + + #[cfg(test)] + mod removing_expired { + use super::*; + use nym_pool_contract_common::{BasicAllowance, GranteeAddress}; + + fn setup_with_expired_grant() -> (TestSetup, GranteeAddress) { + let mut test = TestSetup::init(); + let env = test.env(); + let allowance = Allowance::Basic(BasicAllowance { + spend_limit: None, + expiration_unix_timestamp: Some(env.block.time.seconds() + 1), + }); + let grantee = test.generate_account(); + let admin = test.admin_unchecked(); + NYM_POOL_STORAGE + .insert_new_grant(test.deps_mut(), &env, &admin, &grantee, allowance) + .unwrap(); + test.next_block(); + (test, grantee) + } + + #[test] + fn requires_valid_grantee_address() -> anyhow::Result<()> { + let (mut test, grantee) = setup_with_expired_grant(); + let sender = test.generate_account(); + let res = test.execute_raw( + sender.clone(), + ExecuteMsg::RemoveExpiredGrant { + grantee: "bad grantee".to_string(), + }, + ); + assert!(res.is_err()); + + let res = test.execute_raw( + sender, + ExecuteMsg::RemoveExpiredGrant { + grantee: grantee.to_string(), + }, + ); + assert!(res.is_ok()); + + Ok(()) + } + + #[test] + fn requires_grant_to_actually_exist_and_be_expired() -> anyhow::Result<()> { + let mut test = TestSetup::init(); + let sender = test.generate_account(); + let grantee = test.add_dummy_grant().grantee; + let not_grantee = test.generate_account(); + + // doesn't exist + let res = test.execute_raw( + sender.clone(), + ExecuteMsg::RemoveExpiredGrant { + grantee: not_grantee.to_string(), + }, + ); + assert!(res.is_err()); + + // exists but not expired + let res = test.execute_raw( + sender.clone(), + ExecuteMsg::RemoveExpiredGrant { + grantee: grantee.to_string(), + }, + ); + assert!(res.is_err()); + + Ok(()) + } + + #[test] + fn removes_the_grant() -> anyhow::Result<()> { + let (mut test, grantee) = setup_with_expired_grant(); + let sender = test.generate_account(); + + assert!(NYM_POOL_STORAGE + .grants + .may_load(test.storage(), grantee.clone())? + .is_some()); + + test.execute_raw( + sender.clone(), + ExecuteMsg::RemoveExpiredGrant { + grantee: grantee.to_string(), + }, + )?; + + assert!(NYM_POOL_STORAGE + .grants + .may_load(test.storage(), grantee)? + .is_none()); + + Ok(()) + } + } +} diff --git a/contracts/vesting/schema/nym-vesting-contract.json b/contracts/vesting/schema/nym-vesting-contract.json index 26069a16c43..d250394c06f 100644 --- a/contracts/vesting/schema/nym-vesting-contract.json +++ b/contracts/vesting/schema/nym-vesting-contract.json @@ -665,7 +665,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -1765,7 +1766,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -1993,7 +1995,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", @@ -2218,6 +2221,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", @@ -2363,7 +2367,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "PledgeData": { "description": "Information regarding pledge (i.e. mixnode or gateway bonding) made with vesting tokens.", @@ -2426,6 +2431,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", @@ -2458,7 +2464,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "PledgeData": { "description": "Information regarding pledge (i.e. mixnode or gateway bonding) made with vesting tokens.", @@ -2552,7 +2559,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", @@ -2576,6 +2584,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", @@ -2599,6 +2608,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", @@ -2622,6 +2632,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", @@ -2645,6 +2656,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", @@ -2684,6 +2696,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", @@ -2707,6 +2720,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", @@ -2730,6 +2744,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", @@ -2753,6 +2768,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", @@ -2776,6 +2792,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", @@ -2799,6 +2816,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", diff --git a/contracts/vesting/schema/raw/execute.json b/contracts/vesting/schema/raw/execute.json index 81565897d49..6189ff6f6f2 100644 --- a/contracts/vesting/schema/raw/execute.json +++ b/contracts/vesting/schema/raw/execute.json @@ -643,7 +643,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", diff --git a/contracts/vesting/schema/raw/response_to_get_account.json b/contracts/vesting/schema/raw/response_to_get_account.json index 7d8544539b3..2451513a749 100644 --- a/contracts/vesting/schema/raw/response_to_get_account.json +++ b/contracts/vesting/schema/raw/response_to_get_account.json @@ -91,7 +91,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", diff --git a/contracts/vesting/schema/raw/response_to_get_accounts_vesting_coins_paged.json b/contracts/vesting/schema/raw/response_to_get_accounts_vesting_coins_paged.json index 6647e141949..2e6cd68c979 100644 --- a/contracts/vesting/schema/raw/response_to_get_accounts_vesting_coins_paged.json +++ b/contracts/vesting/schema/raw/response_to_get_accounts_vesting_coins_paged.json @@ -79,7 +79,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", diff --git a/contracts/vesting/schema/raw/response_to_get_delegated_coins.json b/contracts/vesting/schema/raw/response_to_get_delegated_coins.json index 6e18ef9a9bc..b1c4c6c716c 100644 --- a/contracts/vesting/schema/raw/response_to_get_delegated_coins.json +++ b/contracts/vesting/schema/raw/response_to_get_delegated_coins.json @@ -14,6 +14,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", diff --git a/contracts/vesting/schema/raw/response_to_get_gateway.json b/contracts/vesting/schema/raw/response_to_get_gateway.json index 1a18cd9f840..931f88ca411 100644 --- a/contracts/vesting/schema/raw/response_to_get_gateway.json +++ b/contracts/vesting/schema/raw/response_to_get_gateway.json @@ -23,7 +23,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "PledgeData": { "description": "Information regarding pledge (i.e. mixnode or gateway bonding) made with vesting tokens.", diff --git a/contracts/vesting/schema/raw/response_to_get_historical_vesting_staking_reward.json b/contracts/vesting/schema/raw/response_to_get_historical_vesting_staking_reward.json index 6e18ef9a9bc..b1c4c6c716c 100644 --- a/contracts/vesting/schema/raw/response_to_get_historical_vesting_staking_reward.json +++ b/contracts/vesting/schema/raw/response_to_get_historical_vesting_staking_reward.json @@ -14,6 +14,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", diff --git a/contracts/vesting/schema/raw/response_to_get_mixnode.json b/contracts/vesting/schema/raw/response_to_get_mixnode.json index 1a18cd9f840..931f88ca411 100644 --- a/contracts/vesting/schema/raw/response_to_get_mixnode.json +++ b/contracts/vesting/schema/raw/response_to_get_mixnode.json @@ -23,7 +23,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "PledgeData": { "description": "Information regarding pledge (i.e. mixnode or gateway bonding) made with vesting tokens.", diff --git a/contracts/vesting/schema/raw/response_to_get_original_vesting.json b/contracts/vesting/schema/raw/response_to_get_original_vesting.json index 0b0ced2dec2..22c1fd64d84 100644 --- a/contracts/vesting/schema/raw/response_to_get_original_vesting.json +++ b/contracts/vesting/schema/raw/response_to_get_original_vesting.json @@ -45,7 +45,8 @@ "denom": { "type": "string" } - } + }, + "additionalProperties": false }, "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", diff --git a/contracts/vesting/schema/raw/response_to_get_pledged_coins.json b/contracts/vesting/schema/raw/response_to_get_pledged_coins.json index 6e18ef9a9bc..b1c4c6c716c 100644 --- a/contracts/vesting/schema/raw/response_to_get_pledged_coins.json +++ b/contracts/vesting/schema/raw/response_to_get_pledged_coins.json @@ -14,6 +14,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", diff --git a/contracts/vesting/schema/raw/response_to_get_spendable_reward_coins.json b/contracts/vesting/schema/raw/response_to_get_spendable_reward_coins.json index 6e18ef9a9bc..b1c4c6c716c 100644 --- a/contracts/vesting/schema/raw/response_to_get_spendable_reward_coins.json +++ b/contracts/vesting/schema/raw/response_to_get_spendable_reward_coins.json @@ -14,6 +14,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", diff --git a/contracts/vesting/schema/raw/response_to_get_spendable_vested_coins.json b/contracts/vesting/schema/raw/response_to_get_spendable_vested_coins.json index 6e18ef9a9bc..b1c4c6c716c 100644 --- a/contracts/vesting/schema/raw/response_to_get_spendable_vested_coins.json +++ b/contracts/vesting/schema/raw/response_to_get_spendable_vested_coins.json @@ -14,6 +14,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", diff --git a/contracts/vesting/schema/raw/response_to_get_staked_coins.json b/contracts/vesting/schema/raw/response_to_get_staked_coins.json index 6e18ef9a9bc..b1c4c6c716c 100644 --- a/contracts/vesting/schema/raw/response_to_get_staked_coins.json +++ b/contracts/vesting/schema/raw/response_to_get_staked_coins.json @@ -14,6 +14,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", diff --git a/contracts/vesting/schema/raw/response_to_get_total_delegation_amount.json b/contracts/vesting/schema/raw/response_to_get_total_delegation_amount.json index 6e18ef9a9bc..b1c4c6c716c 100644 --- a/contracts/vesting/schema/raw/response_to_get_total_delegation_amount.json +++ b/contracts/vesting/schema/raw/response_to_get_total_delegation_amount.json @@ -14,6 +14,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", diff --git a/contracts/vesting/schema/raw/response_to_get_vested_coins.json b/contracts/vesting/schema/raw/response_to_get_vested_coins.json index 6e18ef9a9bc..b1c4c6c716c 100644 --- a/contracts/vesting/schema/raw/response_to_get_vested_coins.json +++ b/contracts/vesting/schema/raw/response_to_get_vested_coins.json @@ -14,6 +14,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", diff --git a/contracts/vesting/schema/raw/response_to_get_vesting_coins.json b/contracts/vesting/schema/raw/response_to_get_vesting_coins.json index 6e18ef9a9bc..b1c4c6c716c 100644 --- a/contracts/vesting/schema/raw/response_to_get_vesting_coins.json +++ b/contracts/vesting/schema/raw/response_to_get_vesting_coins.json @@ -14,6 +14,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", diff --git a/contracts/vesting/schema/raw/response_to_get_withdrawn_coins.json b/contracts/vesting/schema/raw/response_to_get_withdrawn_coins.json index 6e18ef9a9bc..b1c4c6c716c 100644 --- a/contracts/vesting/schema/raw/response_to_get_withdrawn_coins.json +++ b/contracts/vesting/schema/raw/response_to_get_withdrawn_coins.json @@ -14,6 +14,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", diff --git a/contracts/vesting/schema/raw/response_to_locked_coins.json b/contracts/vesting/schema/raw/response_to_locked_coins.json index 6e18ef9a9bc..b1c4c6c716c 100644 --- a/contracts/vesting/schema/raw/response_to_locked_coins.json +++ b/contracts/vesting/schema/raw/response_to_locked_coins.json @@ -14,6 +14,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", diff --git a/contracts/vesting/schema/raw/response_to_spendable_coins.json b/contracts/vesting/schema/raw/response_to_spendable_coins.json index 6e18ef9a9bc..b1c4c6c716c 100644 --- a/contracts/vesting/schema/raw/response_to_spendable_coins.json +++ b/contracts/vesting/schema/raw/response_to_spendable_coins.json @@ -14,6 +14,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", diff --git a/contracts/vesting/src/contract.rs b/contracts/vesting/src/contract.rs index a9ef9401a68..22c3d75dcc8 100644 --- a/contracts/vesting/src/contract.rs +++ b/contracts/vesting/src/contract.rs @@ -3,8 +3,8 @@ use crate::storage::{ADMIN, MIXNET_CONTRACT_ADDRESS, MIX_DENOM}; pub use crate::transactions::*; use contracts_common::set_build_information; use cosmwasm_std::{ - entry_point, to_binary, Addr, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, - Uint128, + entry_point, to_json_binary, Addr, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse, + Response, Uint128, }; use vesting_contract_common::messages::{ExecuteMsg, InitMsg, MigrateMsg, QueryMsg}; use vesting_contract_common::{Account, VestingContractError}; @@ -110,16 +110,18 @@ pub fn query( msg: QueryMsg, ) -> Result { let query_res = match msg { - QueryMsg::GetContractVersion {} => to_binary(&get_contract_version()), - QueryMsg::GetCW2ContractVersion {} => to_binary(&cw2::get_contract_version(deps.storage)?), + QueryMsg::GetContractVersion {} => to_json_binary(&get_contract_version()), + QueryMsg::GetCW2ContractVersion {} => { + to_json_binary(&cw2::get_contract_version(deps.storage)?) + } QueryMsg::GetAccountsPaged { start_next_after, limit, - } => to_binary(&try_get_all_accounts(deps, start_next_after, limit)?), + } => to_json_binary(&try_get_all_accounts(deps, start_next_after, limit)?), QueryMsg::GetAccountsVestingCoinsPaged { start_next_after, limit, - } => to_binary(&try_get_all_accounts_vesting_coins( + } => to_json_binary(&try_get_all_accounts_vesting_coins( deps, env, start_next_after, @@ -128,7 +130,7 @@ pub fn query( QueryMsg::LockedCoins { vesting_account_address, block_time, - } => to_binary(&try_get_locked_coins( + } => to_json_binary(&try_get_locked_coins( &vesting_account_address, block_time, env, @@ -137,7 +139,7 @@ pub fn query( QueryMsg::SpendableCoins { vesting_account_address, block_time, - } => to_binary(&try_get_spendable_coins( + } => to_json_binary(&try_get_spendable_coins( &vesting_account_address, block_time, env, @@ -146,7 +148,7 @@ pub fn query( QueryMsg::GetVestedCoins { vesting_account_address, block_time, - } => to_binary(&try_get_vested_coins( + } => to_json_binary(&try_get_vested_coins( &vesting_account_address, block_time, env, @@ -155,7 +157,7 @@ pub fn query( QueryMsg::GetVestingCoins { vesting_account_address, block_time, - } => to_binary(&try_get_vesting_coins( + } => to_json_binary(&try_get_vesting_coins( &vesting_account_address, block_time, env, @@ -163,69 +165,69 @@ pub fn query( )?), QueryMsg::GetStartTime { vesting_account_address, - } => to_binary(&try_get_start_time(&vesting_account_address, deps)?), + } => to_json_binary(&try_get_start_time(&vesting_account_address, deps)?), QueryMsg::GetEndTime { vesting_account_address, - } => to_binary(&try_get_end_time(&vesting_account_address, deps)?), + } => to_json_binary(&try_get_end_time(&vesting_account_address, deps)?), QueryMsg::GetOriginalVesting { vesting_account_address, - } => to_binary(&try_get_original_vesting(&vesting_account_address, deps)?), + } => to_json_binary(&try_get_original_vesting(&vesting_account_address, deps)?), QueryMsg::GetHistoricalVestingStakingReward { vesting_account_address, - } => to_binary(&try_get_historical_vesting_staking_reward( + } => to_json_binary(&try_get_historical_vesting_staking_reward( &vesting_account_address, deps, )?), QueryMsg::GetSpendableVestedCoins { vesting_account_address, - } => to_binary(&try_get_spendable_vested_coins( + } => to_json_binary(&try_get_spendable_vested_coins( &vesting_account_address, deps, env, )?), QueryMsg::GetSpendableRewardCoins { vesting_account_address, - } => to_binary(&try_get_spendable_reward_coins( + } => to_json_binary(&try_get_spendable_reward_coins( &vesting_account_address, deps, env, )?), QueryMsg::GetDelegatedCoins { vesting_account_address, - } => to_binary(&try_get_delegated_coins(&vesting_account_address, deps)?), + } => to_json_binary(&try_get_delegated_coins(&vesting_account_address, deps)?), QueryMsg::GetPledgedCoins { vesting_account_address, - } => to_binary(&try_get_pledged_coins(&vesting_account_address, deps)?), + } => to_json_binary(&try_get_pledged_coins(&vesting_account_address, deps)?), QueryMsg::GetStakedCoins { vesting_account_address, - } => to_binary(&try_get_staked_coins(&vesting_account_address, deps)?), + } => to_json_binary(&try_get_staked_coins(&vesting_account_address, deps)?), QueryMsg::GetWithdrawnCoins { vesting_account_address, - } => to_binary(&try_get_withdrawn_coins(&vesting_account_address, deps)?), - QueryMsg::GetAccount { address } => to_binary(&try_get_account(&address, deps)?), - QueryMsg::GetMixnode { address } => to_binary(&try_get_mixnode(&address, deps)?), - QueryMsg::GetGateway { address } => to_binary(&try_get_gateway(&address, deps)?), + } => to_json_binary(&try_get_withdrawn_coins(&vesting_account_address, deps)?), + QueryMsg::GetAccount { address } => to_json_binary(&try_get_account(&address, deps)?), + QueryMsg::GetMixnode { address } => to_json_binary(&try_get_mixnode(&address, deps)?), + QueryMsg::GetGateway { address } => to_json_binary(&try_get_gateway(&address, deps)?), QueryMsg::GetCurrentVestingPeriod { address } => { - to_binary(&try_get_current_vesting_period(&address, deps, env)?) + to_json_binary(&try_get_current_vesting_period(&address, deps, env)?) } QueryMsg::GetDelegation { address, mix_id, block_timestamp_secs, - } => to_binary(&try_get_delegation( + } => to_json_binary(&try_get_delegation( deps, &address, mix_id, block_timestamp_secs, )?), QueryMsg::GetTotalDelegationAmount { address, mix_id } => { - to_binary(&try_get_delegation_amount(deps, &address, mix_id)?) + to_json_binary(&try_get_delegation_amount(deps, &address, mix_id)?) } QueryMsg::GetDelegationTimes { address, mix_id } => { - to_binary(&try_get_delegation_times(deps, &address, mix_id)?) + to_json_binary(&try_get_delegation_times(deps, &address, mix_id)?) } QueryMsg::GetAllDelegations { start_after, limit } => { - to_binary(&try_get_all_delegations(deps, start_after, limit)?) + to_json_binary(&try_get_all_delegations(deps, start_after, limit)?) } }; diff --git a/contracts/vesting/src/storage.rs b/contracts/vesting/src/storage.rs index a2122de58f3..690fd7b26c6 100644 --- a/contracts/vesting/src/storage.rs +++ b/contracts/vesting/src/storage.rs @@ -8,49 +8,48 @@ use vesting_contract_common::{Account, PledgeData, VestingContractError}; pub(crate) type BlockTimestampSecs = u64; /// Counter for the unique, monotonically increasing storage key id for the vesting account data. -pub const KEY: Item<'_, VestingAccountStorageKey> = Item::new("key"); +pub const KEY: Item = Item::new("key"); /// Storage map containing vesting account information associated with particular owner address. -pub const ACCOUNTS: Map<'_, Addr, Account> = Map::new("acc"); +pub const ACCOUNTS: Map = Map::new("acc"); /// Storage map containing information about amount of tokens associated with particular vesting account /// that are currently present in the contract (and have not been withdrawn or staked in the mixnet contract) // note: this assumes I understood the intent behind this correctly -const BALANCES: Map<'_, VestingAccountStorageKey, Uint128> = Map::new("blc"); +const BALANCES: Map = Map::new("blc"); /// Storage map containing information about amount of tokens withdrawn from the contract by a particular vesting account. -const WITHDRAWNS: Map<'_, VestingAccountStorageKey, Uint128> = Map::new("wthd"); +const WITHDRAWNS: Map = Map::new("wthd"); /// Storage map containing information about amount of tokens pledged towards bonding mixnodes /// in the mixnet contract using a particular vesting account. -const BOND_PLEDGES: Map<'_, VestingAccountStorageKey, PledgeData> = Map::new("bnd"); +const BOND_PLEDGES: Map = Map::new("bnd"); /// Storage map containing information about amount of tokens pledged towards bonding gateways /// in the mixnet contract using a particular vesting account. -const GATEWAY_PLEDGES: Map<'_, VestingAccountStorageKey, PledgeData> = Map::new("gtw"); +const GATEWAY_PLEDGES: Map = Map::new("gtw"); /// Old, pre-v2 migration, storage map that used to contain information about tokens delegated /// towards particular mixnodes in the mixnet contract with given vesting account. /// It should be completely empty. pub const _OLD_DELEGATIONS: Map< - '_, (VestingAccountStorageKey, IdentityKey, BlockTimestampSecs), Uint128, > = Map::new("dlg"); /// Storage map containing information about tokens delegated towards particular mixnodes /// in the mixnet contract with given vesting account. -pub const DELEGATIONS: Map<'_, (VestingAccountStorageKey, NodeId, BlockTimestampSecs), Uint128> = +pub const DELEGATIONS: Map<(VestingAccountStorageKey, NodeId, BlockTimestampSecs), Uint128> = Map::new("dlg_v2"); /// Explicit contract admin that is allowed, among other things, to create new vesting accounts. -pub const ADMIN: Item<'_, Addr> = Item::new("adm"); +pub const ADMIN: Item = Item::new("adm"); /// Address of the mixnet contract. -pub const MIXNET_CONTRACT_ADDRESS: Item<'_, Addr> = Item::new("mix"); +pub const MIXNET_CONTRACT_ADDRESS: Item = Item::new("mix"); /// The denomination of coin used for staking. -pub const MIX_DENOM: Item<'_, String> = Item::new("den"); +pub const MIX_DENOM: Item = Item::new("den"); pub fn save_delegation( key: (VestingAccountStorageKey, NodeId, BlockTimestampSecs), diff --git a/contracts/vesting/src/support/tests.rs b/contracts/vesting/src/support/tests.rs index 98995b1c8ee..2a3dd718789 100644 --- a/contracts/vesting/src/support/tests.rs +++ b/contracts/vesting/src/support/tests.rs @@ -74,7 +74,7 @@ pub mod helpers { use crate::traits::VestingAccount; use crate::vesting::{populate_vesting_periods, StorableVestingAccountExt}; use contracts_common::Percent; - use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier}; + use cosmwasm_std::testing::{message_info, mock_dependencies, mock_env, MockApi, MockQuerier}; use cosmwasm_std::{ coin, Addr, BlockInfo, Coin, ContractInfo, Deps, DepsMut, Empty, Env, MemoryStorage, MessageInfo, OwnedDeps, Storage, Timestamp, Uint128, @@ -112,7 +112,7 @@ pub mod helpers { deps, env: mock_env(), rng: test_rng(), - admin: mock_info(admin.as_str(), &[]), + admin: message_info(&admin, &[]), } } @@ -143,7 +143,7 @@ pub mod helpers { deps, env, rng: test_rng(), - admin: mock_info(admin.as_str(), &[]), + admin: message_info(&admin, &[]), } } @@ -320,16 +320,19 @@ pub mod helpers { pub fn init_contract() -> OwnedDeps> { let mut deps = mock_dependencies(); let msg = InitMsg { - mixnet_contract_address: "test".to_string(), + mixnet_contract_address: deps.api.addr_make("test").to_string(), mix_denom: TEST_COIN_DENOM.to_string(), }; let env = mock_env(); - let info = mock_info("admin", &[]); + let info = message_info(&deps.api.addr_make("admin"), &[]); instantiate(deps.as_mut(), env, info, msg).unwrap(); deps } - pub fn vesting_account_mid_fixture(storage: &mut dyn Storage, env: &Env) -> Account { + pub fn vesting_account_mid_fixture( + deps: &mut OwnedDeps>, + env: &Env, + ) -> Account { let start_time_ts = env.block.time; let start_time = env.block.time.seconds() - 7200; let periods = populate_vesting_periods( @@ -338,8 +341,8 @@ pub mod helpers { ); Account::save_new( - Addr::unchecked("owner"), - Some(Addr::unchecked("staking")), + deps.api.addr_make("owner"), + Some(deps.api.addr_make("staking")), Coin { amount: Uint128::new(1_000_000_000_000), denom: TEST_COIN_DENOM.to_string(), @@ -347,19 +350,22 @@ pub mod helpers { start_time_ts, periods, None, - storage, + &mut deps.storage, ) .unwrap() } - pub fn vesting_account_new_fixture(storage: &mut dyn Storage, env: &Env) -> Account { + pub fn vesting_account_new_fixture( + deps: &mut OwnedDeps>, + env: &Env, + ) -> Account { let start_time = env.block.time; let periods = populate_vesting_periods(start_time.seconds(), VestingSpecification::default()); Account::save_new( - Addr::unchecked("owner"), - Some(Addr::unchecked("staking")), + deps.api.addr_make("owner"), + Some(deps.api.addr_make("staking")), Coin { amount: Uint128::new(1_000_000_000_000), denom: TEST_COIN_DENOM.to_string(), @@ -367,19 +373,22 @@ pub mod helpers { start_time, periods, Some(PledgeCap::from_str("0.1").unwrap()), - storage, + &mut deps.storage, ) .unwrap() } - pub fn vesting_account_percent_fixture(storage: &mut dyn Storage, env: &Env) -> Account { + pub fn vesting_account_percent_fixture( + deps: &mut OwnedDeps>, + env: &Env, + ) -> Account { let start_time = env.block.time; let periods = populate_vesting_periods(start_time.seconds(), VestingSpecification::default()); Account::save_new( - Addr::unchecked("owner"), - Some(Addr::unchecked("staking")), + deps.api.addr_make("owner"), + Some(deps.api.addr_make("staking")), Coin { amount: Uint128::new(1_000_000_000_000), denom: TEST_COIN_DENOM.to_string(), @@ -389,7 +398,7 @@ pub mod helpers { Some(PledgeCap::Percent( Percent::from_percentage_value(10).unwrap(), )), - storage, + &mut deps.storage, ) .unwrap() } diff --git a/contracts/vesting/src/vesting/account/mod.rs b/contracts/vesting/src/vesting/account/mod.rs index 4353b748eb3..193313db21e 100644 --- a/contracts/vesting/src/vesting/account/mod.rs +++ b/contracts/vesting/src/vesting/account/mod.rs @@ -198,7 +198,7 @@ impl StorableVestingAccountExt for Account { fn absolute_pledge_cap(&self) -> Result { match self.pledge_cap() { PledgeCap::Absolute(cap) => Ok(cap), - PledgeCap::Percent(p) => Ok(p * self.get_original_vesting()?.amount.amount), + PledgeCap::Percent(p) => Ok(self.get_original_vesting()?.amount.amount.mul_floor(p)), } } diff --git a/contracts/vesting/src/vesting/mod.rs b/contracts/vesting/src/vesting/mod.rs index 33cfc2baec5..48e0f48dc3b 100644 --- a/contracts/vesting/src/vesting/mod.rs +++ b/contracts/vesting/src/vesting/mod.rs @@ -24,7 +24,7 @@ mod tests { use crate::vesting::account::StorableVestingAccountExt; use crate::vesting::populate_vesting_periods; use contracts_common::signing::MessageSignature; - use cosmwasm_std::testing::{mock_env, mock_info}; + use cosmwasm_std::testing::{message_info, mock_env}; use cosmwasm_std::{coin, coins, Addr, Coin, Timestamp, Uint128}; use mixnet_contract_common::mixnode::NodeCostParams; use mixnet_contract_common::{Gateway, MixNode, Percent}; @@ -38,13 +38,16 @@ mod tests { let env = mock_env(); let msg = ExecuteMsg::CreateAccount { - owner_address: "owner".to_string(), - staking_address: Some("staking".to_string()), + owner_address: deps.api.addr_make("owner").to_string(), + staking_address: Some(deps.api.addr_make("staking").to_string()), vesting_spec: None, cap: Some(PledgeCap::Absolute(Uint128::from(100_000_000_000u128))), }; - let info = mock_info("admin", &coins(1_000_000_000_000, TEST_COIN_DENOM)); + let info = message_info( + &deps.api.addr_make("admin"), + &coins(1_000_000_000_000, TEST_COIN_DENOM), + ); let response = execute(deps.as_mut(), env.clone(), info.clone(), msg.clone()); assert_eq!( response, @@ -58,7 +61,7 @@ mod tests { fn test_ownership_transfer() { let mut deps = init_contract(); let env = mock_env(); - let info = mock_info("owner", &[]); + let info = message_info(&deps.api.addr_make("owner"), &[]); let msg = ExecuteMsg::TransferOwnership { to_address: "new_owner".to_string(), }; @@ -75,7 +78,7 @@ mod tests { fn test_staking_account() { let mut deps = init_contract(); let env = mock_env(); - let info = mock_info("staking", &[]); + let info = message_info(&deps.api.addr_make("staking"), &[]); let msg = ExecuteMsg::TransferOwnership { to_address: "new_owner".to_string(), }; @@ -92,19 +95,19 @@ mod tests { fn test_staking_address_change() { let mut deps = init_contract(); let env = mock_env(); - let account = vesting_account_new_fixture(&mut deps.storage, &env); + let account = vesting_account_new_fixture(&mut deps, &env); let original_staker = account.staking_address().unwrap(); // can stake on behalf without an issue let stake_msg = ExecuteMsg::DelegateToMixnode { - on_behalf_of: Some("owner".to_string()), + on_behalf_of: Some(deps.api.addr_make("owner").to_string()), mix_id: 42, amount: coin(500, TEST_COIN_DENOM), }; let response = execute( deps.as_mut(), env.clone(), - mock_info(original_staker.as_ref(), &[]), + message_info(original_staker, &[]), stake_msg.clone(), ); assert_eq!( @@ -123,16 +126,18 @@ mod tests { let amount = coin(1000000000, "unym"); // create the accounts + let owner = deps.api.addr_make("vesting1"); let msg = ExecuteMsg::CreateAccount { - owner_address: "vesting1".to_string(), + owner_address: owner.to_string(), staking_address: None, vesting_spec: None, cap: None, }; + let admin = deps.api.addr_make("admin"); let response = execute( deps.as_mut(), env.clone(), - mock_info("admin", &[amount.clone()]), + message_info(&admin, &[amount.clone()]), msg, ); assert_eq!( @@ -150,7 +155,7 @@ mod tests { let num_vesting_periods = 8; let vesting_period = 3 * 30 * 86400; - let account = vesting_account_new_fixture(&mut deps.storage, &env); + let account = vesting_account_new_fixture(&mut deps, &env); assert_eq!(account.periods().len(), num_vesting_periods as usize); @@ -270,7 +275,7 @@ mod tests { fn test_withdraw_case() { let mut deps = init_contract(); let env = mock_env(); - let account = vesting_account_mid_fixture(&mut deps.storage, &env); + let account = vesting_account_mid_fixture(&mut deps, &env); let vested_coins = account.get_vested_coins(None, &env, &deps.storage).unwrap(); let vesting_coins = account @@ -379,7 +384,7 @@ mod tests { let mut deps = init_contract(); let env = mock_env(); - let account = vesting_account_percent_fixture(&mut deps.storage, &env); + let account = vesting_account_percent_fixture(&mut deps, &env); assert_eq!( account.absolute_pledge_cap().unwrap(), @@ -392,15 +397,18 @@ mod tests { let mut deps = init_contract(); let env = mock_env(); - // let account = vesting_account_new_fixture(&mut deps.storage, &env); + // let account = vesting_account_new_fixture(&mut deps, &env); let msg = ExecuteMsg::CreateAccount { - owner_address: "owner".to_string(), - staking_address: Some("staking".to_string()), + owner_address: deps.api.addr_make("owner").to_string(), + staking_address: Some(deps.api.addr_make("staking").to_string()), vesting_spec: None, cap: Some(PledgeCap::Absolute(Uint128::from(100_000_000_000u128))), }; - let info = mock_info("admin", &coins(1_000_000_000_000, TEST_COIN_DENOM)); + let info = message_info( + &deps.api.addr_make("admin"), + &coins(1_000_000_000_000, TEST_COIN_DENOM), + ); let response = execute(deps.as_mut(), env.clone(), info, msg); assert_eq!( @@ -416,7 +424,7 @@ mod tests { let mut deps = init_contract(); let env = mock_env(); - let account = vesting_account_new_fixture(&mut deps.storage, &env); + let account = vesting_account_new_fixture(&mut deps, &env); let mix_node = MixNode { host: "mix.node.org".to_string(), @@ -445,7 +453,7 @@ mod tests { denom: TEST_COIN_DENOM.to_string(), }, }; - let info = mock_info(account.owner_address.as_str(), &[]); + let info = message_info(&account.owner_address, &[]); let response = execute(deps.as_mut(), env.clone(), info, msg); assert_eq!( response, @@ -460,7 +468,7 @@ mod tests { let mut deps = init_contract(); let env = mock_env(); - let account = vesting_account_new_fixture(&mut deps.storage, &env); + let account = vesting_account_new_fixture(&mut deps, &env); let gateway = Gateway { host: "1.1.1.1".to_string(), @@ -539,8 +547,8 @@ mod tests { ); let vesting_account = Account::save_new( - Addr::unchecked("owner"), - Some(Addr::unchecked("staking")), + Addr::unchecked(deps.api.addr_make("owner")), + Some(Addr::unchecked(deps.api.addr_make("staking"))), Coin { amount: Uint128::new(1_000_000_000_000), denom: TEST_COIN_DENOM.to_string(), diff --git a/nym-api/Cargo.toml b/nym-api/Cargo.toml index d3fb90b7260..c4ffbbece4a 100644 --- a/nym-api/Cargo.toml +++ b/nym-api/Cargo.toml @@ -141,4 +141,4 @@ tempfile = { workspace = true } cw3 = { workspace = true } cw-utils = { workspace = true } rand_chacha = { workspace = true } -sha2 = "0.9" +sha2 = { workspace = true } diff --git a/nym-api/src/ecash/state/mod.rs b/nym-api/src/ecash/state/mod.rs index 5d3c6412afe..b31cd255e96 100644 --- a/nym-api/src/ecash/state/mod.rs +++ b/nym-api/src/ecash/state/mod.rs @@ -16,7 +16,7 @@ use crate::ecash::storage::models::{SerialNumberWrapper, TicketProvider}; use crate::ecash::storage::EcashStorageExt; use crate::support::config::Config; use crate::support::storage::NymApiStorage; -use cosmwasm_std::{from_binary, CosmosMsg, WasmMsg}; +use cosmwasm_std::{from_json, CosmosMsg, WasmMsg}; use cw3::Status; use nym_api_requests::ecash::models::{ BatchRedeemTicketsBody, IssuedTicketbooksChallengeRequest, @@ -584,7 +584,7 @@ impl EcashState { } // check if it was actually created by the ecash contract - if proposal.proposer != self.global.contract_address.as_ref() { + if proposal.proposer.as_str() != self.global.contract_address.as_ref() { return Err(RedemptionError::InvalidProposer { proposal_id, received: proposal.proposer.into_string(), @@ -618,7 +618,7 @@ impl EcashState { return Err(RedemptionError::InvalidContract { proposal_id }); } - let Ok(ExecuteMsg::RedeemTickets { n, gw }) = from_binary(&msg) else { + let Ok(ExecuteMsg::RedeemTickets { n, gw }) = from_json(&msg) else { return Err(RedemptionError::InvalidMessage { proposal_id }); }; diff --git a/nym-api/src/ecash/tests/mod.rs b/nym-api/src/ecash/tests/mod.rs index 9a71802f5c5..771ebdbfacc 100644 --- a/nym-api/src/ecash/tests/mod.rs +++ b/nym-api/src/ecash/tests/mod.rs @@ -20,9 +20,9 @@ use async_trait::async_trait; use axum::Router; use axum_test::http::StatusCode; use axum_test::{TestResponse, TestServer}; -use cosmwasm_std::testing::{mock_env, mock_info}; +use cosmwasm_std::testing::{message_info, mock_env}; use cosmwasm_std::{ - from_binary, to_binary, Addr, Binary, BlockInfo, CosmosMsg, Decimal, MessageInfo, WasmMsg, + from_json, to_json_binary, Addr, Binary, BlockInfo, CosmosMsg, Decimal, MessageInfo, WasmMsg, }; use cw3::{Proposal, ProposalResponse, Vote, VoteInfo, VoteResponse, Votes}; use cw4::{Cw4Contract, MemberResponse}; @@ -107,7 +107,7 @@ impl InternalCounters { // just hash the current counter self.tx_hash_counter += 1; - Hash::Sha256(sha2::Sha256::digest(&self.tx_hash_counter.to_be_bytes()).into()) + Hash::Sha256(sha2::Sha256::digest(self.tx_hash_counter.to_be_bytes()).into()) } #[allow(dead_code)] @@ -413,7 +413,7 @@ impl FakeChainState { // TODO: make it return a result fn execute_dkg_contract(&mut self, sender: MessageInfo, msg: &Binary) { - let exec_msg: nym_coconut_dkg_common::msg::ExecuteMsg = from_binary(msg).unwrap(); + let exec_msg: nym_coconut_dkg_common::msg::ExecuteMsg = from_json(msg).unwrap(); match exec_msg { nym_coconut_dkg_common::msg::ExecuteMsg::VerifyVerificationKeyShare { owner, @@ -441,13 +441,13 @@ impl FakeChainState { // TODO: make it return a result fn execute_contract_msg(&mut self, contract: &String, msg: &Binary, sender: MessageInfo) { - if contract == &self.group_contract.address { + if contract == self.group_contract.address.as_str() { unimplemented!("group contract exec") } - if contract == &self.multisig_contract.address { + if contract == self.multisig_contract.address.as_str() { unimplemented!("multisig contract exec") } - if contract == &self.ecash_contract.address { + if contract == self.ecash_contract.address.as_str() { unimplemented!("bandwidth contract exec") } if contract == self.dkg_contract.address.as_ref() { @@ -464,7 +464,7 @@ impl FakeChainState { msg, funds, } => { - let sender = mock_info(sender_address.as_ref(), funds); + let sender = message_info(&sender_address, funds); self.execute_contract_msg(contract_addr, msg, sender) } other => unimplemented!("unimplemented wasm proposal for {other:?}"), @@ -1082,7 +1082,7 @@ impl super::client::Client for DummyClient { }; let verify_vk_share_msg = CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: chain.dkg_contract.address.to_string(), - msg: to_binary(&verify_vk_share_req).unwrap(), + msg: to_json_binary(&verify_vk_share_req).unwrap(), funds: vec![], }); let proposal = Proposal { diff --git a/nym-node-status-api/nym-node-status-api/settings.sql b/nym-node-status-api/nym-node-status-api/settings.sql new file mode 100644 index 00000000000..321fef528aa --- /dev/null +++ b/nym-node-status-api/nym-node-status-api/settings.sql @@ -0,0 +1,2 @@ +.mode columns +.headers on \ No newline at end of file diff --git a/nym-validator-rewarder/Cargo.toml b/nym-validator-rewarder/Cargo.toml index 4d875d0e62b..53febf20f2a 100644 --- a/nym-validator-rewarder/Cargo.toml +++ b/nym-validator-rewarder/Cargo.toml @@ -43,7 +43,6 @@ nym-network-defaults = { path = "../common/network-defaults" } nym-task = { path = "../common/task" } nym-validator-client = { path = "../common/client-libs/validator-client" } nym-coconut-dkg-common = { path = "../common/cosmwasm-smart-contracts/coconut-dkg" } -nym-coconut-bandwidth-contract-common = { path = "../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" } nyxd-scraper = { path = "../common/nyxd-scraper" } nym-ticketbooks-merkle = { path = "../common/ticketbooks-merkle" } nym-serde-helpers = { path = "../common/serde-helpers", features = ["base64"] } diff --git a/nym-validator-rewarder/src/config/mod.rs b/nym-validator-rewarder/src/config/mod.rs index 59e43c2a028..93ca5b92f44 100644 --- a/nym-validator-rewarder/src/config/mod.rs +++ b/nym-validator-rewarder/src/config/mod.rs @@ -233,7 +233,7 @@ impl Config { Uint128::zero() } else { Uint128::new(ticketbook_total_budget.amount) - * Decimal::from_ratio(1u32, whitelist_size as u64) + .mul_floor(Decimal::from_ratio(1u32, whitelist_size as u64)) }; let per_operator = Coin::new(amount.u128(), &ticketbook_total_budget.denom); diff --git a/nym-validator-rewarder/src/rewarder/block_signing/types.rs b/nym-validator-rewarder/src/rewarder/block_signing/types.rs index a45f6cdc077..02fc8a54cf9 100644 --- a/nym-validator-rewarder/src/rewarder/block_signing/types.rs +++ b/nym-validator-rewarder/src/rewarder/block_signing/types.rs @@ -38,8 +38,8 @@ impl ValidatorSigning { return Coin::new(0, &signing_budget.denom); } - let amount = - Uint128::new(signing_budget.amount) * self.ratio_signed * self.voting_power_ratio; + let amount = Uint128::new(signing_budget.amount) + .mul_floor(self.ratio_signed * self.voting_power_ratio); Coin::new(amount.u128(), &signing_budget.denom) } diff --git a/nym-validator-rewarder/src/rewarder/ticketbook_issuance/types.rs b/nym-validator-rewarder/src/rewarder/ticketbook_issuance/types.rs index 6dc48db5dc8..dc396a73218 100644 --- a/nym-validator-rewarder/src/rewarder/ticketbook_issuance/types.rs +++ b/nym-validator-rewarder/src/rewarder/ticketbook_issuance/types.rs @@ -32,7 +32,7 @@ impl OperatorIssuing { return Coin::new(0, &operator_budget.denom); } - let amount = Uint128::new(operator_budget.amount) * self.issued_ratio; + let amount = Uint128::new(operator_budget.amount).mul_floor(self.issued_ratio); Coin::new(amount.u128(), &operator_budget.denom) } diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index 070d93ead9d..06e1c831468 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -60,13 +60,14 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.6" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ - "getrandom 0.2.10", + "cfg-if", "once_cell", "version_check", + "zerocopy", ] [[package]] @@ -93,6 +94,12 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "anstream" version = "0.6.4" @@ -159,6 +166,127 @@ dependencies = [ "password-hash", ] +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", + "num-traits", + "rayon", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rayon", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", + "rayon", +] + [[package]] name = "async-compression" version = "0.4.18" @@ -291,6 +419,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bech32" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" + [[package]] name = "bincode" version = "1.3.3" @@ -319,9 +453,9 @@ dependencies = [ [[package]] name = "bip39" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" +checksum = "33415e24172c1b7d6066f6d999545375ab8e1d95421d6784bdfff9496f292387" dependencies = [ "bitcoin_hashes", "rand 0.8.5", @@ -331,11 +465,21 @@ dependencies = [ "zeroize", ] +[[package]] +name = "bitcoin-internals" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" + [[package]] name = "bitcoin_hashes" -version = "0.11.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" +checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" +dependencies = [ + "bitcoin-internals", + "hex-conservative", +] [[package]] name = "bitflags" @@ -385,24 +529,24 @@ dependencies = [ [[package]] name = "bls12_381" version = "0.8.0" -source = "git+https://github.com/jstuczyn/bls12_381?branch=temp/experimental-serdect#22cd0a16b674af1629110a2dc8b6cf6c73ea4cd9" +source = "git+https://github.com/jstuczyn/bls12_381?branch=temp/experimental-serdect-updated#9bf520059cb28323fc51469cae86868ef4fa6fbd" dependencies = [ - "digest 0.9.0", + "digest 0.10.7", "ff", "group", "pairing", "rand_core 0.6.4", "serde", - "serdect 0.3.0-pre.0", + "serdect 0.3.0", "subtle", "zeroize", ] [[package]] name = "bnum" -version = "0.8.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab9008b6bb9fc80b5277f2fe481c09e828743d9151203e804583eb4c9e15b31d" +checksum = "3e31ea183f6ee62ac8b8a8cf7feddd766317adfb13ff469de57ce033efd6a790" [[package]] name = "brotli" @@ -779,45 +923,14 @@ dependencies = [ "libc", ] -[[package]] -name = "cosmos-sdk-proto" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32560304ab4c365791fd307282f76637213d8083c1a98490c35159cd67852237" -dependencies = [ - "prost 0.12.3", - "prost-types", - "tendermint-proto 0.34.0", -] - [[package]] name = "cosmos-sdk-proto" version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "462e1f6a8e005acc8835d32d60cbd7973ed65ea2a8d8473830e675f050956427" dependencies = [ - "prost 0.13.4", - "tendermint-proto 0.40.1", -] - -[[package]] -name = "cosmrs" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47126f5364df9387b9d8559dcef62e99010e1d4098f39eb3f7ee4b5c254e40ea" -dependencies = [ - "bip32", - "cosmos-sdk-proto 0.20.0", - "ecdsa", - "eyre", - "k256", - "rand_core 0.6.4", - "serde", - "serde_json", - "signature", - "subtle-encoding", - "tendermint 0.34.0", - "thiserror 1.0.64", + "prost", + "tendermint-proto", ] [[package]] @@ -827,7 +940,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1394c263335da09e8ba8c4b2c675d804e3e0deb44cce0866a5f838d3ddd43d02" dependencies = [ "bip32", - "cosmos-sdk-proto 0.26.1", + "cosmos-sdk-proto", "ecdsa", "eyre", "k256", @@ -836,39 +949,57 @@ dependencies = [ "serde_json", "signature", "subtle-encoding", - "tendermint 0.40.1", + "tendermint", "tendermint-rpc", "thiserror 1.0.64", ] +[[package]] +name = "cosmwasm-core" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de32156e4fd80c59be39ed6f4ebb596d59b0a4eaf01d6f146e27628ec7e8f8c1" + [[package]] name = "cosmwasm-crypto" -version = "1.5.3" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9934c79e58d9676edfd592557dee765d2a6ef54c09d5aa2edb06156b00148966" +checksum = "38fe1e6107ae3c9ba5e1f14178dd8bd52210535030d07f0609cf0d754c1f7de2" dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-serialize", + "cosmwasm-core", + "curve25519-dalek", "digest 0.10.7", "ecdsa", "ed25519-zebra", "k256", + "num-traits", + "p256", "rand_core 0.6.4", + "rayon", + "sha2 0.10.8", "thiserror 1.0.64", ] [[package]] name = "cosmwasm-derive" -version = "1.5.3" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5e72e330bd3bdab11c52b5ecbdeb6a8697a004c57964caeb5d876f0b088b3c" +checksum = "484926c9dc8b90c59a717946c86bb272182003cbaabb378560086648d4056656" dependencies = [ - "syn 1.0.109", + "proc-macro2", + "quote", + "syn 2.0.96", ] [[package]] name = "cosmwasm-schema" -version = "1.4.3" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ae2e971fb831d0c4fa3c8c3d2291cdbdd73786a73d65196dbf983d9b2468af" +checksum = "d2a25988c48703d1450a5ac5e7cd3ad82ec8a7552f3dde8f9b8927e682bd02c7" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -879,32 +1010,36 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.4.3" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cadc57fd0825b85bc2f9b972c17da718b9efb4bc17e5935cc2d6036324f853d" +checksum = "bbd08fac60d30e341d9365033f519c0a36fdf38bde6ec196179e653d2723aebd" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] name = "cosmwasm-std" -version = "1.4.3" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e98e19fae6c3f468412f731274b0f9434602722009d6a77432d39c7c4bb09202" +checksum = "c92be4747d9abe3a96a5a78af34d29947992b3f67f602987ff8a87142ce9c413" dependencies = [ - "base64 0.21.4", + "base64 0.22.1", + "bech32", "bnum", + "cosmwasm-core", "cosmwasm-crypto", "cosmwasm-derive", - "derivative", - "forward_ref", + "derive_more 1.0.0", "hex", + "rand_core 0.6.4", + "rmp-serde", "schemars", "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror 1.0.64", ] @@ -937,14 +1072,30 @@ dependencies = [ ] [[package]] -name = "crossbeam-utils" -version = "0.8.16" +name = "crossbeam-deque" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ - "cfg-if", + "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.2" @@ -1016,29 +1167,15 @@ dependencies = [ [[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.2" +version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", "digest 0.10.7", "fiat-crypto", - "platforms", "rustc_version", "serde", "subtle", @@ -1071,9 +1208,9 @@ dependencies = [ [[package]] name = "cw-controllers" -version = "1.1.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d8edce4b78785f36413f67387e4be7d0cb7d032b5d4164bcc024f9c3f3f2ea" +checksum = "50c1804013d21060b994dea28a080f9eab78a3bcb6b617f05e7634b0600bf7b1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1086,9 +1223,9 @@ dependencies = [ [[package]] name = "cw-storage-plus" -version = "1.2.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5ff29294ee99373e2cd5fd21786a3c0ced99a52fec2ca347d565489c61b723c" +checksum = "f13360e9007f51998d42b1bc6b7fa0141f74feae61ed5fd1e5b0a89eec7b5de1" dependencies = [ "cosmwasm-std", "schemars", @@ -1097,24 +1234,22 @@ dependencies = [ [[package]] name = "cw-utils" -version = "1.0.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c80e93d1deccb8588db03945016a292c3c631e6325d349ebb35d2db6f4f946f7" +checksum = "07dfee7f12f802431a856984a32bce1cb7da1e6c006b5409e3981035ce562dec" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw2", "schemars", - "semver", "serde", "thiserror 1.0.64", ] [[package]] name = "cw2" -version = "1.1.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c120b24fbbf5c3bedebb97f2cc85fbfa1c3287e09223428e7e597b5293c1fa" +checksum = "b04852cd38f044c0751259d5f78255d07590d136b8a86d4e09efdd7666bd6d27" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1127,9 +1262,9 @@ dependencies = [ [[package]] name = "cw20" -version = "1.1.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "526e39bb20534e25a1cd0386727f0038f4da294e5e535729ba3ef54055246abd" +checksum = "a42212b6bf29bbdda693743697c621894723f35d3db0d5df930be22903d0e27c" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1140,9 +1275,9 @@ dependencies = [ [[package]] name = "cw3" -version = "1.1.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2967fbd073d4b626dd9e7148e05a84a3bebd9794e71342e12351110ffbb12395" +checksum = "d5e53c2057526c65d9c88be8b2a564729ebad7a3d87ee97b97665a71446f913a" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1155,9 +1290,9 @@ dependencies = [ [[package]] name = "cw4" -version = "1.1.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24754ff6e45f2a1c60adc409d9b2eb87666012c44021329141ffaab3388fccd2" +checksum = "d33f5c8a6b6cd1bd24e212d7f44967697bfa3c4f9cc3f9a8e1c58f5fe5db032d" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1251,6 +1386,27 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", + "unicode-xid", +] + [[package]] name = "digest" version = "0.9.0" @@ -1429,7 +1585,7 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ - "curve25519-dalek 4.1.2", + "curve25519-dalek", "ed25519", "rand_core 0.6.4", "serde", @@ -1440,16 +1596,16 @@ dependencies = [ [[package]] name = "ed25519-zebra" -version = "3.1.0" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +checksum = "7d9ce6874da5d4415896cd45ffbc4d1cfc0c4f9c079427bd870742c30f2f65a9" dependencies = [ - "curve25519-dalek 3.2.0", - "hashbrown 0.12.3", + "curve25519-dalek", + "ed25519", + "hashbrown 0.14.5", "hex", "rand_core 0.6.4", - "serde", - "sha2 0.9.9", + "sha2 0.10.8", "zeroize", ] @@ -1461,9 +1617,9 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "elliptic-curve" -version = "0.13.5" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", @@ -1669,12 +1825,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "forward_ref" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" - [[package]] name = "futf" version = "0.1.5" @@ -2159,6 +2309,12 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ "ahash", ] @@ -2168,6 +2324,10 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "heck" @@ -2211,6 +2371,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-conservative" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" + [[package]] name = "hex-literal" version = "0.3.4" @@ -2804,15 +2970,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.13.0" @@ -2899,9 +3056,9 @@ dependencies = [ [[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", @@ -3182,29 +3339,27 @@ dependencies = [ ] [[package]] -name = "num-conv" -version = "0.1.0" +name = "num-bigint" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] [[package]] -name = "num-derive" -version = "0.3.3" +name = "num-conv" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] @@ -3221,9 +3376,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -3263,7 +3418,7 @@ name = "nym-api-requests" version = "0.1.0" dependencies = [ "bs58", - "cosmrs 0.21.1", + "cosmrs", "cosmwasm-std", "ecdsa", "getset", @@ -3282,7 +3437,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.8", - "tendermint 0.40.1", + "tendermint", "thiserror 2.0.11", "time", "utoipa", @@ -3301,15 +3456,6 @@ dependencies = [ "vergen", ] -[[package]] -name = "nym-coconut-bandwidth-contract-common" -version = "0.1.0" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "nym-multisig-contract-common", -] - [[package]] name = "nym-coconut-dkg-common" version = "0.1.0" @@ -3331,7 +3477,7 @@ dependencies = [ "bls12_381", "bs58", "cfg-if", - "digest 0.9.0", + "digest 0.10.7", "ff", "group", "itertools 0.13.0", @@ -3339,7 +3485,7 @@ dependencies = [ "nym-pemstore", "rand 0.8.5", "serde", - "sha2 0.9.9", + "sha2 0.10.8", "subtle", "thiserror 2.0.11", "zeroize", @@ -3598,7 +3744,7 @@ name = "nym-types" version = "1.0.0" dependencies = [ "base64 0.22.1", - "cosmrs 0.21.1", + "cosmrs", "cosmwasm-std", "eyre", "hmac", @@ -3631,7 +3777,7 @@ dependencies = [ "bip32", "bip39", "colored 2.2.0", - "cosmrs 0.21.1", + "cosmrs", "cosmwasm-std", "cw-controllers", "cw-utils", @@ -3643,7 +3789,6 @@ dependencies = [ "futures", "itertools 0.13.0", "nym-api-requests", - "nym-coconut-bandwidth-contract-common", "nym-coconut-dkg-common", "nym-compact-ecash", "nym-config", @@ -3656,7 +3801,7 @@ dependencies = [ "nym-network-defaults", "nym-serde-helpers", "nym-vesting-contract-common", - "prost 0.13.4", + "prost", "reqwest 0.12.4", "serde", "serde_json", @@ -3702,7 +3847,7 @@ dependencies = [ name = "nym-wallet-types" version = "1.0.0" dependencies = [ - "cosmrs 0.15.0", + "cosmrs", "cosmwasm-std", "hex-literal", "nym-config", @@ -3739,7 +3884,7 @@ dependencies = [ "bip39", "cfg-if", "colored 2.2.0", - "cosmrs 0.21.1", + "cosmrs", "cosmwasm-std", "dirs 4.0.0", "dotenvy", @@ -3908,6 +4053,18 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[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.8", +] + [[package]] name = "pairing" version = "0.23.0" @@ -4222,12 +4379,6 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" -[[package]] -name = "platforms" -version = "3.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8" - [[package]] name = "plist" version = "1.5.0" @@ -4295,6 +4446,15 @@ dependencies = [ "log", ] +[[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-macro-crate" version = "1.3.1" @@ -4366,16 +4526,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "prost" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" -dependencies = [ - "bytes", - "prost-derive 0.12.3", -] - [[package]] name = "prost" version = "0.13.4" @@ -4383,20 +4533,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec" dependencies = [ "bytes", - "prost-derive 0.13.4", -] - -[[package]] -name = "prost-derive" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" -dependencies = [ - "anyhow", - "itertools 0.11.0", - "proc-macro2", - "quote", - "syn 2.0.96", + "prost-derive", ] [[package]] @@ -4412,15 +4549,6 @@ dependencies = [ "syn 2.0.96", ] -[[package]] -name = "prost-types" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" -dependencies = [ - "prost 0.12.3", -] - [[package]] name = "quick-error" version = "1.2.3" @@ -4538,6 +4666,26 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -4767,6 +4915,28 @@ dependencies = [ "digest 0.10.7", ] +[[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 = "rs_merkle" version = "1.4.2" @@ -5035,7 +5205,7 @@ checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" dependencies = [ "bitflags 1.3.2", "cssparser", - "derive_more", + "derive_more 0.99.17", "fxhash", "log", "matches", @@ -5067,9 +5237,9 @@ dependencies = [ [[package]] name = "serde-json-wasm" -version = "0.5.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15bee9b04dd165c3f4e142628982ddde884c2022a89e8ddf99c4829bf2c3a58" +checksum = "f05da0d153dd4595bdffd5099dc0e9ce425b205ee648eb93437ff7302af8c9a5" dependencies = [ "serde", ] @@ -5183,9 +5353,9 @@ dependencies = [ [[package]] name = "serdect" -version = "0.3.0-pre.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791ef964bfaba6be28a5c3f0c56836e17cb711ac009ca1074b9c735a3ebf240a" +checksum = "f42f67da2385b51a5f9652db9c93d78aeaf7610bf5ec366080b6de810604af53" dependencies = [ "base16ct", "serde", @@ -5381,6 +5551,12 @@ dependencies = [ "loom", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "string_cache" version = "0.8.7" @@ -5823,37 +5999,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "tendermint" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc2294fa667c8b548ee27a9ba59115472d0a09c2ba255771092a7f1dcf03a789" -dependencies = [ - "bytes", - "digest 0.10.7", - "ed25519", - "ed25519-consensus", - "flex-error", - "futures", - "k256", - "num-traits", - "once_cell", - "prost 0.12.3", - "prost-types", - "ripemd", - "serde", - "serde_bytes", - "serde_json", - "serde_repr", - "sha2 0.10.8", - "signature", - "subtle", - "subtle-encoding", - "tendermint-proto 0.34.0", - "time", - "zeroize", -] - [[package]] name = "tendermint" version = "0.40.1" @@ -5869,7 +6014,7 @@ dependencies = [ "k256", "num-traits", "once_cell", - "prost 0.13.4", + "prost", "ripemd", "serde", "serde_bytes", @@ -5879,7 +6024,7 @@ dependencies = [ "signature", "subtle", "subtle-encoding", - "tendermint-proto 0.40.1", + "tendermint-proto", "time", "zeroize", ] @@ -5893,29 +6038,11 @@ dependencies = [ "flex-error", "serde", "serde_json", - "tendermint 0.40.1", + "tendermint", "toml 0.8.20", "url", ] -[[package]] -name = "tendermint-proto" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc728a4f9e891d71adf66af6ecaece146f9c7a11312288a3107b3e1d6979aaf" -dependencies = [ - "bytes", - "flex-error", - "num-derive", - "num-traits", - "prost 0.12.3", - "prost-types", - "serde", - "serde_bytes", - "subtle-encoding", - "time", -] - [[package]] name = "tendermint-proto" version = "0.40.1" @@ -5924,7 +6051,7 @@ checksum = "9ae9e1705aa0fa5ecb2c6aa7fb78c2313c4a31158ea5f02048bf318f849352eb" dependencies = [ "bytes", "flex-error", - "prost 0.13.4", + "prost", "serde", "serde_bytes", "subtle-encoding", @@ -5952,9 +6079,9 @@ dependencies = [ "serde_json", "subtle", "subtle-encoding", - "tendermint 0.40.1", + "tendermint", "tendermint-config", - "tendermint-proto 0.40.1", + "tendermint-proto", "thiserror 1.0.64", "time", "tokio", @@ -6407,6 +6534,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "universal-hash" version = "0.5.1" @@ -7248,7 +7381,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" dependencies = [ - "curve25519-dalek 4.1.2", + "curve25519-dalek", "rand_core 0.6.4", "serde", "zeroize", @@ -7287,6 +7420,26 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "zerofrom" version = "0.1.5" @@ -7310,9 +7463,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.6.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "serde", "zeroize_derive", diff --git a/nym-wallet/nym-wallet-types/Cargo.toml b/nym-wallet/nym-wallet-types/Cargo.toml index 720027178bd..83522fa1910 100644 --- a/nym-wallet/nym-wallet-types/Cargo.toml +++ b/nym-wallet/nym-wallet-types/Cargo.toml @@ -12,8 +12,8 @@ serde_json = "1.0" strum = { version = "0.23", features = ["derive"] } ts-rs = "10.0.0" -cosmwasm-std = "1.4.3" -cosmrs = "=0.15.0" +cosmwasm-std = "2.2.1" +cosmrs = "=0.21.1" nym-config = { path = "../../common/config" } nym-network-defaults = { path = "../../common/network-defaults" } diff --git a/nym-wallet/src-tauri/Cargo.toml b/nym-wallet/src-tauri/Cargo.toml index 11f39c81b1d..28d878b7994 100644 --- a/nym-wallet/src-tauri/Cargo.toml +++ b/nym-wallet/src-tauri/Cargo.toml @@ -49,7 +49,7 @@ k256 = { version = "0.13", features = ["ecdsa", "sha256"] } base64 = "0.13" zeroize = { version = "1.5", features = ["zeroize_derive", "serde"] } -cosmwasm-std = "1.3.0" +cosmwasm-std = "2.2.1" cosmrs = { version = "0.21.0" } nym-node-requests = { path = "../../nym-node/nym-node-requests" } diff --git a/tools/internal/contract-state-importer/importer-contract/Cargo.toml b/tools/internal/contract-state-importer/importer-contract/Cargo.toml index cdb639b82b9..2c09e7071e5 100644 --- a/tools/internal/contract-state-importer/importer-contract/Cargo.toml +++ b/tools/internal/contract-state-importer/importer-contract/Cargo.toml @@ -16,7 +16,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] base85rs = { workspace = true } cosmwasm-std = { workspace = true } -cosmwasm-storage = { workspace = true } + cosmwasm-schema = { workspace = true } [features] diff --git a/tools/internal/testnet-manager/dkg-bypass-contract/Cargo.toml b/tools/internal/testnet-manager/dkg-bypass-contract/Cargo.toml index 06ec3f196ef..fa45302f005 100644 --- a/tools/internal/testnet-manager/dkg-bypass-contract/Cargo.toml +++ b/tools/internal/testnet-manager/dkg-bypass-contract/Cargo.toml @@ -13,7 +13,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] cosmwasm-std = { workspace = true } -cosmwasm-storage = { workspace = true } + cosmwasm-schema = { workspace = true } cw-storage-plus = { workspace = true } diff --git a/tools/internal/testnet-manager/dkg-bypass-contract/src/contract.rs b/tools/internal/testnet-manager/dkg-bypass-contract/src/contract.rs index df70e604f47..6765dd610bb 100644 --- a/tools/internal/testnet-manager/dkg-bypass-contract/src/contract.rs +++ b/tools/internal/testnet-manager/dkg-bypass-contract/src/contract.rs @@ -13,7 +13,7 @@ use nym_coconut_dkg_common::verification_key::ContractVKShare; pub(crate) type Dealer<'a> = &'a Addr; -pub(crate) const CURRENT_EPOCH: Item<'_, Epoch> = Item::new("current_epoch"); +pub(crate) const CURRENT_EPOCH: Item = Item::new("current_epoch"); pub const THRESHOLD: Item = Item::new("threshold"); @@ -40,7 +40,7 @@ impl IndexList for VkShareIndex<'_> { } } -pub(crate) fn vk_shares<'a>() -> IndexedMap<'a, VKShareKey<'a>, ContractVKShare, VkShareIndex<'a>> { +pub(crate) fn vk_shares<'a>() -> IndexedMap, ContractVKShare, VkShareIndex<'a>> { let indexes = VkShareIndex { epoch_id: MultiIndex::new(|_pk, d| d.epoch_id, "vksp", "vkse"), }; diff --git a/tools/internal/testnet-manager/src/manager/node.rs b/tools/internal/testnet-manager/src/manager/node.rs index ec5eeae06d1..5728c8c8711 100644 --- a/tools/internal/testnet-manager/src/manager/node.rs +++ b/tools/internal/testnet-manager/src/manager/node.rs @@ -34,7 +34,7 @@ impl NymNode { } pub(crate) fn pledge(&self) -> CosmWasmCoin { - CosmWasmCoin::new(100_000000, "unym") + CosmWasmCoin::new(100_000000u32, "unym") } pub(crate) fn bonding_nym_node(&self) -> nym_mixnet_contract_common::NymNode { @@ -48,7 +48,7 @@ impl NymNode { pub(crate) fn cost_params(&self) -> NodeCostParams { NodeCostParams { profit_margin_percent: Percent::from_percentage_value(10).unwrap(), - interval_operating_cost: CosmWasmCoin::new(40_000000, "unym"), + interval_operating_cost: CosmWasmCoin::new(40_000000u32, "unym"), } }