diff --git a/Cargo.lock b/Cargo.lock index 2e345b014..e5bf0491f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,273 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "abstract-cw-multi-test" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c77f8d4bac08f74fbc4fce8943cb2d35e742682b6cae8cb65555d6cd3830feb" +dependencies = [ + "anyhow", + "bech32 0.11.0", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw20-ics20", + "derivative", + "hex", + "itertools 0.12.1", + "log", + "prost 0.12.6", + "schemars", + "serde", + "serde_json", + "sha2 0.10.8", + "thiserror", +] + +[[package]] +name = "abstract-cw-plus-interface" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7441425a805439500492977107d154af02b1702aa044945775c245d0b3469968" +dependencies = [ + "abstract-cw1", + "abstract-cw1-subkeys", + "abstract-cw1-whitelist", + "abstract-cw20-base", + "abstract-cw20-ics20", + "abstract-cw3-fixed-multisig", + "abstract-cw3-flex-multisig", + "abstract-cw4-group", + "abstract-cw4-stake", + "cosmwasm-std", + "cw-orch", +] + +[[package]] +name = "abstract-cw1" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0895c076ab6a5165133a453f983ec9ccc9b6c41de256b6eb74e523eb555b3ebb" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "schemars", + "serde", +] + +[[package]] +name = "abstract-cw1-subkeys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab08cdd6008afa38a52427943bf4aef9541bde78cc9c14849a53ad2608a1161e" +dependencies = [ + "abstract-cw1", + "abstract-cw1-whitelist", + "abstract-cw2", + "cosmwasm-schema", + "cosmwasm-std", + "cw-orch", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "schemars", + "semver", + "serde", + "thiserror", +] + +[[package]] +name = "abstract-cw1-whitelist" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171a0b5b3694627cf0fa554500d72431169d4013fffd14650d2b7d660230a205" +dependencies = [ + "abstract-cw1", + "abstract-cw2", + "cosmwasm-schema", + "cosmwasm-std", + "cw-orch", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "abstract-cw2" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "945af4c176b4539be2a74c06aa166287ba964ab58aec98c644addd812431f141" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "abstract-cw20" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00d5e4b8084c3a2b3e42502e6c4fe3ed985dc72e86eb612bcc527f4a0443fa42" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-orch", + "cw-utils 1.0.3", + "schemars", + "serde", +] + +[[package]] +name = "abstract-cw20-base" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d300dec7d602e00841c5ab6fe598d4d290bab32e489c6885c607633c4f3fe67" +dependencies = [ + "abstract-cw2", + "abstract-cw20", + "cosmwasm-schema", + "cosmwasm-std", + "cw-orch", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "schemars", + "semver", + "serde", + "thiserror", +] + +[[package]] +name = "abstract-cw20-ics20" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "027678ddb0e62b4aba5f0167d2b0a3ec0182e1e32c47759be7e30b56775598ee" +dependencies = [ + "abstract-cw2", + "abstract-cw20", + "cosmwasm-schema", + "cosmwasm-std", + "cw-controllers 1.1.2", + "cw-orch", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "schemars", + "semver", + "serde", + "thiserror", +] + +[[package]] +name = "abstract-cw3" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c080cc760333d1d3477857aeac19aa7e6e661f1e58d04a7a78212913d49bf517" +dependencies = [ + "abstract-cw20", + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils 1.0.3", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "abstract-cw3-fixed-multisig" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1882e05bef33bd1c6b25e735eda8a23332a78c4df0b24a18ca56a8ca8ed6f222" +dependencies = [ + "abstract-cw2", + "abstract-cw3", + "cosmwasm-schema", + "cosmwasm-std", + "cw-orch", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "abstract-cw3-flex-multisig" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92379f3e7c467f081312d6953eb8d300456efa352c9f7c5ef095ad99083d92db" +dependencies = [ + "abstract-cw2", + "abstract-cw20", + "abstract-cw3", + "abstract-cw3-fixed-multisig", + "abstract-cw4", + "cosmwasm-schema", + "cosmwasm-std", + "cw-orch", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "abstract-cw4" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aacb0124dce37ee6f2b5636684285bcbaa65a1678980f95ea76366ab74a8912" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "schemars", + "serde", +] + +[[package]] +name = "abstract-cw4-group" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0af5ef484ba1d48fee8485452c81ac3465ba16a5941db90bda4dd6b58b50a9a6" +dependencies = [ + "abstract-cw2", + "abstract-cw4", + "cosmwasm-schema", + "cosmwasm-std", + "cw-controllers 1.1.2", + "cw-orch", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "abstract-cw4-stake" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1eb9985e8b752396a2c5d8fde8ebf65ea81070a95f167a3d31af0746f8e4b4e" +dependencies = [ + "abstract-cw2", + "abstract-cw20", + "abstract-cw4", + "cosmwasm-schema", + "cosmwasm-std", + "cw-controllers 1.1.2", + "cw-orch", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "schemars", + "serde", + "thiserror", +] + [[package]] name = "addr2line" version = "0.22.0" @@ -68,18 +335,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] name = "async-trait" -version = "0.1.80" +version = "0.1.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -184,6 +451,12 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "bech32" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" + [[package]] name = "bindgen" version = "0.68.1" @@ -203,7 +476,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.68", + "syn 2.0.71", "which", ] @@ -307,18 +580,18 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" dependencies = [ "serde", ] [[package]] name = "cc" -version = "1.0.104" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490" +checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" [[package]] name = "cexpr" @@ -380,6 +653,15 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -463,6 +745,17 @@ dependencies = [ "tonic 0.9.2", ] +[[package]] +name = "cosmos-sdk-proto" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e23f6ab56d5f031cde05b8b82a5fefd3a1a223595c79e32317a97189e612bc" +dependencies = [ + "prost 0.12.6", + "prost-types 0.12.6", + "tendermint-proto 0.35.0", +] + [[package]] name = "cosmrs" version = "0.9.0" @@ -559,7 +852,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c1556156fdf892a55cced6115968b961eaaadd6f724a2c2cb7d1e168e32dd3" dependencies = [ "base64 0.21.7", - "bech32", + "bech32 0.9.1", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -845,6 +1138,96 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cw-orch" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1ddc937c28c59ccf2765fa05ddc0437644d3b283408a7cc64f7b371b0b9309" +dependencies = [ + "anyhow", + "cosmwasm-std", + "cw-orch-contract-derive", + "cw-orch-core", + "cw-orch-fns-derive", + "cw-orch-mock", + "cw-orch-traits", + "cw-utils 1.0.3", + "hex", + "log", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw-orch-contract-derive" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bc8ba75692fc7bd30e91c78fad2dc208a738e4e6ea26b232f9352c320e35543" +dependencies = [ + "convert_case", + "quote", + "syn 2.0.71", +] + +[[package]] +name = "cw-orch-core" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81af5ba67fdc434c6e90739ae89b91bff4dc2b87dc85f8a41aa822329c951bf8" +dependencies = [ + "abstract-cw-multi-test", + "anyhow", + "cosmos-sdk-proto 0.21.1", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "dirs", + "log", + "serde", + "serde_json", + "sha2 0.10.8", + "thiserror", +] + +[[package]] +name = "cw-orch-fns-derive" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9acb7a15bfacc52abdf312a9fffb139883c1effb6ea7e645cd39580a8527463" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "cw-orch-mock" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae9536620b86ee78c2729fd8449538feb4f6257a9809c72c5f9e461e720cf3b" +dependencies = [ + "abstract-cw-multi-test", + "cosmwasm-std", + "cw-orch-core", + "cw-utils 1.0.3", + "log", + "serde", + "sha2 0.10.8", +] + +[[package]] +name = "cw-orch-traits" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5959ce29e9d8a52594b47933a0a2736ea94dd9bf5e29b220cbdbe2b097f07c3a" +dependencies = [ + "cw-orch-core", + "prost 0.12.6", + "prost-types 0.12.6", +] + [[package]] name = "cw-ownable" version = "0.5.1" @@ -1019,7 +1402,7 @@ dependencies = [ "dao-interface", "osmosis-std", "osmosis-test-tube", - "prost 0.12.6", + "prost 0.13.1", "schemars", "serde", "serde_json", @@ -1035,8 +1418,8 @@ dependencies = [ "dao-interface", "osmosis-std", "osmosis-std-derive", - "prost 0.12.6", - "prost-types 0.12.6", + "prost 0.13.1", + "prost-types 0.13.1", "schemars", "serde", "serde-cw-value", @@ -1267,6 +1650,25 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cw20-ics20" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76221201da08fed611c857ea3aa21c031a4a7dc771a8b1750559ca987335dc02" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-controllers 1.1.2", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "cw20 1.1.2", + "schemars", + "semver", + "serde", + "thiserror", +] + [[package]] name = "cw20-stake" version = "0.2.6" @@ -2237,6 +2639,27 @@ dependencies = [ "subtle", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "dlv-list" version = "0.3.0" @@ -2511,7 +2934,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -2548,10 +2971,16 @@ dependencies = [ name = "gauge-adapter" version = "2.4.2" dependencies = [ + "abstract-cw-plus-interface", + "abstract-cw20", + "abstract-cw20-base", "anyhow", "cosmwasm-schema", "cosmwasm-std", + "cw-denom", "cw-multi-test", + "cw-orch", + "cw-orch-core", "cw-storage-plus 1.2.0", "cw-utils 1.0.3", "cw2 1.1.2", @@ -2773,9 +3202,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.29" +version = "0.14.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" dependencies = [ "bytes", "futures-channel", @@ -2965,6 +3394,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -3055,6 +3493,16 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -3138,6 +3586,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.71", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -3193,6 +3652,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "ordered-multimap" version = "0.4.3" @@ -3341,7 +3806,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -3372,7 +3837,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -3420,7 +3885,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -3464,6 +3929,16 @@ dependencies = [ "prost-derive 0.12.6", ] +[[package]] +name = "prost" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13db3d3fde688c61e2446b4d843bc27a7e8af269a69440c0308021dc92333cc" +dependencies = [ + "bytes", + "prost-derive 0.13.1", +] + [[package]] name = "prost-derive" version = "0.11.9" @@ -3487,7 +3962,20 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", +] + +[[package]] +name = "prost-derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18bec9b0adc4eba778b33684b7ba3e7137789434769ee3ce3930463ef904cfca" +dependencies = [ + "anyhow", + "itertools 0.13.0", + "proc-macro2", + "quote", + "syn 2.0.71", ] [[package]] @@ -3508,6 +3996,15 @@ dependencies = [ "prost 0.12.6", ] +[[package]] +name = "prost-types" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee5168b05f49d4b0ca581206eb14a7b22fafd963efe729ac48eb03266e25cc2" +dependencies = [ + "prost 0.13.1", +] + [[package]] name = "quote" version = "1.0.36" @@ -3553,6 +4050,17 @@ dependencies = [ "getrandom", ] +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.10.5" @@ -3760,7 +4268,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -3803,9 +4311,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ "bitflags 2.6.0", "core-foundation", @@ -3816,9 +4324,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" dependencies = [ "core-foundation-sys", "libc", @@ -3832,9 +4340,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] @@ -3868,13 +4376,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -3885,7 +4393,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -3907,7 +4415,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -4131,9 +4639,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.68" +version = "2.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" dependencies = [ "proc-macro2", "quote", @@ -4244,7 +4752,7 @@ checksum = "68ce80bf536476db81ecc9ebab834dc329c9c1509a694f211a73858814bfe023" dependencies = [ "bytes", "flex-error", - "num-derive", + "num-derive 0.3.3", "num-traits", "prost 0.11.9", "prost-types 0.11.9", @@ -4262,7 +4770,7 @@ checksum = "974d6330a19dfa6720e9f663fc59101d207a817db3f9c730d3f31caaa565b574" dependencies = [ "bytes", "flex-error", - "num-derive", + "num-derive 0.3.3", "num-traits", "prost 0.11.9", "prost-types 0.11.9", @@ -4280,7 +4788,7 @@ checksum = "c0cec054567d16d85e8c3f6a3139963d1a66d9d3051ed545d31562550e9bcc3d" dependencies = [ "bytes", "flex-error", - "num-derive", + "num-derive 0.3.3", "num-traits", "prost 0.11.9", "prost-types 0.11.9", @@ -4290,6 +4798,24 @@ dependencies = [ "time", ] +[[package]] +name = "tendermint-proto" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff525d5540a9fc535c38dc0d92a98da3ee36fcdfbda99cecb9f3cce5cd4d41d7" +dependencies = [ + "bytes", + "flex-error", + "num-derive 0.4.2", + "num-traits", + "prost 0.12.6", + "prost-types 0.12.6", + "serde", + "serde_bytes", + "subtle-encoding", + "time", +] + [[package]] name = "tendermint-rpc" version = "0.23.9" @@ -4384,7 +4910,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d506c7664333e246f564949bee4ed39062aa0f11918e6f5a95f553cdad65c274" dependencies = [ "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -4405,22 +4931,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -4442,9 +4968,9 @@ checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" [[package]] name = "tinyvec" -version = "1.6.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -4457,9 +4983,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.38.0" +version = "1.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df" dependencies = [ "backtrace", "bytes", @@ -4490,7 +5016,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -4648,7 +5174,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -4709,6 +5235,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + [[package]] name = "unsafe-libyaml" version = "0.2.11" @@ -4802,7 +5334,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", "wasm-bindgen-shared", ] @@ -4824,7 +5356,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5084,5 +5616,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] diff --git a/Cargo.toml b/Cargo.toml index 4b3718b32..98fd3c45f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,8 +60,8 @@ osmosis-std = "0.20.1" osmosis-std-derive = "0.20.1" osmosis-test-tube = "20.1.1" proc-macro2 = "1.0" -prost = { version = "0.12.3", features = ["prost-derive"] } -prost-types = { version = "0.12.3", default-features = false } +prost = { version = "0.13.1", features = ["prost-derive"] } +prost-types = { version = "0.13.1", default-features = false } quote = "1.0" rand = "0.8" schemars = "0.8" @@ -78,6 +78,9 @@ syn = { version = "1.0", features = ["derive"] } test-context = "0.1" thiserror = { version = "1.0" } wynd-utils = "0.4" +cw-orch = "0.22.2" +cw-orch-core = "1.0.0-rc" + # One commit ahead of version 0.3.0. Allows initialization with an # optional owner. diff --git a/contracts/gauges/gauge-adapter/Cargo.toml b/contracts/gauges/gauge-adapter/Cargo.toml index a03773ebe..f8989d57d 100644 --- a/contracts/gauges/gauge-adapter/Cargo.toml +++ b/contracts/gauges/gauge-adapter/Cargo.toml @@ -24,12 +24,18 @@ cosmwasm-std = { workspace = true } cw-storage-plus = { workspace = true } cw2 = { workspace = true } cw20 = { workspace = true } +cw-denom = { workspace = true } +cw-orch = { workspace = true } cw-utils = { workspace = true } semver = { workspace = true } thiserror = { workspace = true } [dev-dependencies] +abstract-cw-plus-interface = "2.0.1" +abstract-cw20 = "2.0.0" +abstract-cw20-base = "2.0.0" anyhow = { workspace = true } cw-multi-test = { workspace = true } cw20 = { workspace = true } cw20-base = { workspace = true } +cw-orch-core = { workspace = true } diff --git a/contracts/gauges/gauge-adapter/src/contract.rs b/contracts/gauges/gauge-adapter/src/contract.rs index a53cab416..fdcbec92d 100644 --- a/contracts/gauges/gauge-adapter/src/contract.rs +++ b/contracts/gauges/gauge-adapter/src/contract.rs @@ -1,15 +1,19 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - coins, from_json, to_json_binary, Addr, BankMsg, Binary, CosmosMsg, Deps, DepsMut, Env, - MessageInfo, Order, Response, StdError, StdResult, Uint128, WasmMsg, + from_json, to_json_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Order, Response, + StdResult, Uint128, }; use cw2::set_contract_version; -use cw20::{Cw20ExecuteMsg, Cw20ReceiveMsg}; - -use crate::error::ContractError; -use crate::msg::{AdapterQueryMsg, ExecuteMsg, InstantiateMsg, MigrateMsg, ReceiveMsg}; -use crate::state::{AssetType, Config, Submission, CONFIG, SUBMISSIONS}; +use cw20::Cw20ReceiveMsg; +use cw_denom::UncheckedDenom; +use cw_utils::{one_coin, PaymentError}; + +use crate::{ + error::ContractError, + msg::{AdapterQueryMsg, AssetUnchecked, ExecuteMsg, InstantiateMsg, MigrateMsg, ReceiveMsg}, + state::{Config, Submission, CONFIG, SUBMISSIONS}, +}; // Version info for migration info. const CONTRACT_NAME: &str = "crates.io:marketing-gauge-adapter"; @@ -37,9 +41,12 @@ pub fn instantiate( let config = Config { admin: deps.api.addr_validate(&msg.admin)?, - required_deposit: msg.required_deposit, + required_deposit: msg + .required_deposit + .map(|x| x.into_checked(deps.as_ref())) + .transpose()?, community_pool, - reward: msg.reward, + reward: msg.reward.into_checked(deps.as_ref())?, }; CONFIG.save(deps.storage, &config)?; @@ -56,17 +63,17 @@ pub fn execute( match msg { ExecuteMsg::Receive(msg) => receive_cw20_message(deps, info, msg), ExecuteMsg::CreateSubmission { name, url, address } => { - // TODO this is very hacky - let received = info.funds.into_iter().next().unwrap_or_default(); - execute::create_submission( - deps, - info.sender, - name, - url, - address, - received.amount, - AssetType::Native(received.denom), - ) + let received = match one_coin(&info) { + Ok(coin) => Ok(Some(coin)), + Err(PaymentError::NoFunds {}) => Ok(None), + Err(error) => Err(error), + }? + .map(|x| AssetUnchecked { + denom: UncheckedDenom::Native(x.denom), + amount: x.amount, + }); + + execute::create_submission(deps, info.sender, name, url, address, received) } ExecuteMsg::ReturnDeposits {} => execute::return_deposits(deps, info.sender), } @@ -78,44 +85,17 @@ fn receive_cw20_message( msg: Cw20ReceiveMsg, ) -> Result { match from_json(&msg.msg)? { - ReceiveMsg::CreateSubmission { name, url, address } => { - let denom = AssetType::Cw20(info.sender.to_string()); - execute::create_submission( - deps, - Addr::unchecked(msg.sender), - name, - url, - address, - msg.amount, - denom, - ) - } - } -} - -fn create_bank_msg( - denom: &AssetType, - amount: Uint128, - recipient: Addr, -) -> Result { - match denom { - AssetType::Cw20(address) => Ok(WasmMsg::Execute { - contract_addr: address.to_string(), - msg: to_json_binary(&Cw20ExecuteMsg::Transfer { - recipient: recipient.to_string(), - amount, - })?, - funds: vec![], - } - .into()), - AssetType::Native(denom) => { - let amount = coins(amount.u128(), denom); - Ok(BankMsg::Send { - to_address: recipient.to_string(), - amount, - } - .into()) - } + ReceiveMsg::CreateSubmission { name, url, address } => execute::create_submission( + deps, + Addr::unchecked(msg.sender), + name, + url, + address, + Some(AssetUnchecked::new_cw20( + info.sender.as_str(), + msg.amount.u128(), + )), + ), } } @@ -130,8 +110,7 @@ pub mod execute { name: String, url: String, address: String, - received_amount: Uint128, - received_denom: AssetType, + received: Option, ) -> Result { let address = deps.api.addr_validate(&address)?; @@ -142,20 +121,23 @@ pub mod execute { admin: _, } = CONFIG.load(deps.storage)?; if let Some(required_deposit) = required_deposit { - if AssetType::Native("".into()) == received_denom { - return Err(ContractError::DepositRequired {}); - } - if required_deposit.denom != received_denom { - return Err(ContractError::InvalidDepositType {}); - } - if received_amount != required_deposit.amount { - return Err(ContractError::InvalidDepositAmount { - correct_amount: required_deposit.amount, - }); + if let Some(received) = received { + let received_denom = received.denom.into_checked(deps.as_ref())?; + + if required_deposit.denom != received_denom { + return Err(ContractError::InvalidDepositType {}); + } + if received.amount != required_deposit.amount { + return Err(ContractError::InvalidDepositAmount { + correct_amount: required_deposit.amount, + }); + } + } else { + return Err(ContractError::PaymentError(PaymentError::NoFunds {})); } - } else { + } else if let Some(received) = received { // If no deposit is required, then any deposit invalidates a submission. - if !received_amount.is_zero() { + if !received.amount.is_zero() { return Err(ContractError::InvalidDepositAmount { correct_amount: Uint128::zero(), }); @@ -190,11 +172,10 @@ pub mod execute { .range(deps.storage, None, None, Order::Ascending) .map(|item| { let (_submission_recipient, submission) = item?; - create_bank_msg( - &required_deposit.denom, - required_deposit.amount, - submission.sender, - ) + + required_deposit + .denom + .get_transfer_to_message(&submission.sender, required_deposit.amount) }) .collect::>>()?; @@ -221,7 +202,7 @@ pub fn query(deps: Deps, _env: Env, msg: AdapterQueryMsg) -> StdResult { } mod query { - use cosmwasm_std::{CosmosMsg, Decimal}; + use cosmwasm_std::{CosmosMsg, Decimal, StdError}; use crate::msg::{ AllOptionsResponse, AllSubmissionsResponse, CheckOptionResponse, SampleGaugeMsgsResponse, @@ -254,12 +235,16 @@ mod query { let execute = winners .into_iter() .map(|(to_address, fraction)| { - // Gauge already sents chosen tally to this query by using results we send in + // Gauge already sends chosen tally to this query by using results we send in // all_options query; they are already validated - create_bank_msg( - &reward.denom, - fraction * reward.amount, - Addr::unchecked(to_address), + let to_address = deps.api.addr_validate(&to_address)?; + + reward.denom.get_transfer_to_message( + &to_address, + reward + .amount + .checked_mul_floor(fraction) + .map_err(|x| StdError::generic_err(x.to_string()))?, ) }) .collect::>>()?; @@ -306,20 +291,22 @@ mod tests { use super::*; use cosmwasm_std::{ + coins, testing::{mock_dependencies, mock_env, mock_info}, - Decimal, Uint128, + BankMsg, CosmosMsg, Decimal, Uint128, }; + use cw_denom::CheckedDenom; - use crate::state::Asset; + use crate::{msg::AssetUnchecked, state::Asset}; #[test] fn proper_initialization() { let mut deps = mock_dependencies(); let msg = InstantiateMsg { admin: "admin".to_owned(), - required_deposit: Some(Asset::new_cw20("wynd", 10_000_000)), + required_deposit: Some(AssetUnchecked::new_native("wynd", 10_000_000)), community_pool: "community".to_owned(), - reward: Asset::new_native("ujuno", 150_000_000_000), + reward: AssetUnchecked::new_native("ujuno", 150_000_000_000), }; instantiate( deps.as_mut(), @@ -335,7 +322,7 @@ mod tests { assert_eq!( config.required_deposit, Some(Asset { - denom: AssetType::Cw20("wynd".to_owned()), + denom: CheckedDenom::Native(String::from("wynd")), amount: Uint128::new(10_000_000) }) ); @@ -343,13 +330,13 @@ mod tests { assert_eq!( config.reward, Asset { - denom: AssetType::Native("ujuno".to_owned()), + denom: CheckedDenom::Native("ujuno".to_owned()), amount: Uint128::new(150_000_000_000) } ); let msg = InstantiateMsg { - reward: Asset::new_native("ujuno", 10_000_000), + reward: AssetUnchecked::new_native("ujuno", 10_000_000), ..msg }; instantiate( @@ -363,7 +350,7 @@ mod tests { assert_eq!( config.reward, Asset { - denom: AssetType::Native("ujuno".to_owned()), + denom: CheckedDenom::Native("ujuno".to_owned()), amount: Uint128::new(10_000_000) } ); @@ -384,9 +371,9 @@ mod tests { let reward = Uint128::new(150_000_000_000); let msg = InstantiateMsg { admin: "admin".to_owned(), - required_deposit: Some(Asset::new_cw20("wynd", 10_000_000)), + required_deposit: Some(AssetUnchecked::new_native("wynd", 10_000_000)), community_pool: "community".to_owned(), - reward: Asset::new_native("ujuno", reward.into()), + reward: AssetUnchecked::new_native("ujuno", reward.into()), }; instantiate(deps.as_mut(), mock_env(), mock_info("user", &[]), msg).unwrap(); @@ -425,69 +412,6 @@ mod tests { ); } - #[test] - fn sample_gauge_msgs_cw20() { - let mut deps = mock_dependencies(); - - let reward = Uint128::new(150_000_000_000); - let msg = InstantiateMsg { - admin: "admin".to_owned(), - required_deposit: Some(Asset::new_cw20("wynd", 10_000_000)), - community_pool: "community".to_owned(), - reward: Asset::new_cw20("wynd", reward.into()), - }; - instantiate(deps.as_mut(), mock_env(), mock_info("user", &[]), msg).unwrap(); - - let selected = vec![ - ( - "juno1t8ehvswxjfn3ejzkjtntcyrqwvmvuknzy3ajxy".to_string(), - Decimal::percent(41), - ), - ( - "juno196ax4vc0lwpxndu9dyhvca7jhxp70rmcl99tyh".to_string(), - Decimal::percent(33), - ), - ( - "juno1y0us8xvsvfvqkk9c6nt5cfyu5au5tww23dmh40".to_string(), - Decimal::percent(26), - ), - ]; - let res = query::sample_gauge_msgs(deps.as_ref(), selected).unwrap(); - assert_eq!(res.execute.len(), 3); - assert_eq!( - res.execute, - [ - CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: "wynd".to_owned(), - msg: to_json_binary(&Cw20ExecuteMsg::Transfer { - recipient: "juno1t8ehvswxjfn3ejzkjtntcyrqwvmvuknzy3ajxy".to_string(), - amount: reward * Decimal::percent(41) - }) - .unwrap(), - funds: vec![] - }), - CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: "wynd".to_owned(), - msg: to_json_binary(&Cw20ExecuteMsg::Transfer { - recipient: "juno196ax4vc0lwpxndu9dyhvca7jhxp70rmcl99tyh".to_string(), - amount: reward * Decimal::percent(33) - }) - .unwrap(), - funds: vec![] - }), - CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: "wynd".to_owned(), - msg: to_json_binary(&Cw20ExecuteMsg::Transfer { - recipient: "juno1y0us8xvsvfvqkk9c6nt5cfyu5au5tww23dmh40".to_string(), - amount: reward * Decimal::percent(26) - }) - .unwrap(), - funds: vec![] - }), - ] - ); - } - #[test] fn return_deposits_authorization() { let mut deps = mock_dependencies(); @@ -495,7 +419,7 @@ mod tests { admin: "admin".to_owned(), required_deposit: None, community_pool: "community".to_owned(), - reward: Asset::new_native("ujuno", 150_000_000_000), + reward: AssetUnchecked::new_native("ujuno", 150_000_000_000), }; instantiate( deps.as_mut(), @@ -509,7 +433,7 @@ mod tests { assert_eq!(err, ContractError::NoDepositToRefund {}); let msg = InstantiateMsg { - required_deposit: Some(Asset::new_native("ujuno", 10_000_000)), + required_deposit: Some(AssetUnchecked::new_native("ujuno", 10_000_000)), ..msg }; instantiate(deps.as_mut(), mock_env(), mock_info("user", &[]), msg).unwrap(); diff --git a/contracts/gauges/gauge-adapter/src/error.rs b/contracts/gauges/gauge-adapter/src/error.rs index 75521219d..6994dc494 100644 --- a/contracts/gauges/gauge-adapter/src/error.rs +++ b/contracts/gauges/gauge-adapter/src/error.rs @@ -1,4 +1,6 @@ use cosmwasm_std::{StdError, Uint128}; +use cw_denom::DenomError; +use cw_utils::PaymentError; use thiserror::Error; #[derive(Error, Debug, PartialEq)] @@ -6,6 +8,12 @@ pub enum ContractError { #[error("{0}")] Std(#[from] StdError), + #[error("{0}")] + PaymentError(#[from] PaymentError), + + #[error("{0}")] + DenomError(#[from] DenomError), + #[error("Operation unauthorized - only admin can release deposits")] Unauthorized {}, @@ -20,7 +28,4 @@ pub enum ContractError { #[error("No deposit was required, therefore no deposit can be returned")] NoDepositToRefund {}, - - #[error("Deposit required, cannot create submission.")] - DepositRequired {}, } diff --git a/contracts/gauges/gauge-adapter/src/helpers.rs b/contracts/gauges/gauge-adapter/src/helpers.rs new file mode 100644 index 000000000..862be36d4 --- /dev/null +++ b/contracts/gauges/gauge-adapter/src/helpers.rs @@ -0,0 +1,27 @@ +use cosmwasm_std::Deps; +use cw_denom::{DenomError, UncheckedDenom}; + +use crate::{msg::AssetUnchecked, state::Asset}; + +impl AssetUnchecked { + pub fn into_checked(self, deps: Deps) -> Result { + Ok(Asset { + denom: self.denom.into_checked(deps)?, + amount: self.amount, + }) + } + + pub fn new_native(denom: &str, amount: u128) -> Self { + Self { + denom: UncheckedDenom::Native(denom.to_owned()), + amount: amount.into(), + } + } + + pub fn new_cw20(denom: &str, amount: u128) -> Self { + Self { + denom: UncheckedDenom::Cw20(denom.to_owned()), + amount: amount.into(), + } + } +} diff --git a/contracts/gauges/gauge-adapter/src/lib.rs b/contracts/gauges/gauge-adapter/src/lib.rs index 31d9a5aa4..aae805d76 100644 --- a/contracts/gauges/gauge-adapter/src/lib.rs +++ b/contracts/gauges/gauge-adapter/src/lib.rs @@ -1,5 +1,6 @@ pub mod contract; mod error; +mod helpers; pub mod msg; pub mod state; diff --git a/contracts/gauges/gauge-adapter/src/msg.rs b/contracts/gauges/gauge-adapter/src/msg.rs index dc1ae2d7c..5d8a16f7d 100644 --- a/contracts/gauges/gauge-adapter/src/msg.rs +++ b/contracts/gauges/gauge-adapter/src/msg.rs @@ -1,22 +1,22 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Addr, CosmosMsg, Decimal}; +use cosmwasm_std::{Addr, CosmosMsg, Decimal, Uint128}; use cw20::Cw20ReceiveMsg; - -use crate::state::Asset; +use cw_denom::UncheckedDenom; #[cw_serde] pub struct InstantiateMsg { /// Address that is allowed to return deposits. pub admin: String, /// Deposit required for valid submission. This option allows to reduce spam. - pub required_deposit: Option, + pub required_deposit: Option, /// Address of contract where each deposit is transferred. pub community_pool: String, /// Total reward amount. - pub reward: Asset, + pub reward: AssetUnchecked, } #[cw_serde] +#[derive(cw_orch::ExecuteFns)] pub enum ExecuteMsg { /// Implements the Cw20 receiver interface. Receive(Cw20ReceiveMsg), @@ -48,7 +48,7 @@ pub enum MigrateMsg {} // Queries copied from gauge-orchestrator for now (we could use a common crate for this). /// Queries the gauge requires from the adapter contract in order to function. #[cw_serde] -#[derive(QueryResponses)] +#[derive(QueryResponses, cw_orch::QueryFns)] pub enum AdapterQueryMsg { #[returns(crate::state::Config)] Config {}, @@ -97,3 +97,9 @@ pub struct SubmissionResponse { pub struct AllSubmissionsResponse { pub submissions: Vec, } + +#[cw_serde] +pub struct AssetUnchecked { + pub denom: UncheckedDenom, + pub amount: Uint128, +} diff --git a/contracts/gauges/gauge-adapter/src/multitest/options.rs b/contracts/gauges/gauge-adapter/src/multitest/options.rs index d1c57e6cf..2d6343cff 100644 --- a/contracts/gauges/gauge-adapter/src/multitest/options.rs +++ b/contracts/gauges/gauge-adapter/src/multitest/options.rs @@ -1,50 +1,80 @@ -use cosmwasm_std::{coin, Addr}; +use cosmwasm_std::{coin, coins}; +use cw_denom::UncheckedDenom; +use cw_orch::{contract::interface_traits::CwOrchQuery, mock::MockBech32}; -use crate::multitest::suite::SuiteBuilder; +use crate::{ + msg::{ + AdapterQueryMsg, AllOptionsResponse, AllSubmissionsResponse, AssetUnchecked, + CheckOptionResponse, + }, + multitest::suite::{native_submission_helper, setup_gauge_adapter}, +}; #[test] fn option_queries() { - let mut suite = SuiteBuilder::new() - .with_community_pool("community_pool") - .with_funds("owner", &[coin(100_000, "juno")]) - .with_funds("einstein", &[coin(100_000, "juno")]) - .with_native_deposit(1_000) - .build(); + let mock = MockBech32::new("mock"); + let adapter = setup_gauge_adapter( + mock.clone(), + Some(AssetUnchecked { + denom: UncheckedDenom::Native("juno".into()), + amount: 1_000u128.into(), + }), + ); - let recipient = "user".to_owned(); + let recipient = mock.addr_make("recipient"); + let newton = mock.addr_make("newton"); + let einstein = mock + .addr_make_with_balance("einstein", coins(1_000, "juno")) + .unwrap(); - let options = suite.query_all_options().unwrap(); + mock.add_balance(&mock.sender, coins(1_000, "juno")) + .unwrap(); + let options: AllSubmissionsResponse = + adapter.query(&AdapterQueryMsg::AllSubmissions {}).unwrap(); // account for a default option - assert_eq!(options.len(), 1); + assert_eq!(options.submissions.len(), 1); // Valid submission. - _ = suite - .execute_create_submission( - suite.owner.clone(), - "WYNDers".to_owned(), - "https://www.wynddao.com/".to_owned(), - recipient.clone(), - &[coin(1_000, "juno")], - ) - .unwrap(); + native_submission_helper( + adapter.clone(), + mock.sender.clone(), + recipient.clone(), + Some(coin(1_000u128, "juno")), + ) + .unwrap(); // Valid submission. - suite - .execute_create_submission( - Addr::unchecked("einstein"), - "MIBers".to_owned(), - "https://www.mib.tech/".to_owned(), - "einstein".to_owned(), - &[coin(1_000, "juno")], - ) - .unwrap(); + native_submission_helper( + adapter.clone(), + einstein.clone(), + einstein.clone(), + Some(coin(1_000u128, "juno")), + ) + .unwrap(); - let options = suite.query_all_options().unwrap(); - assert_eq!(options, vec!["community_pool", "einstein", &recipient],); + let options: AllOptionsResponse = adapter.query(&AdapterQueryMsg::AllOptions {}).unwrap(); + assert_eq!( + options, + AllOptionsResponse { + options: vec![ + einstein.to_string(), + mock.addr_make("community_pool").to_string(), + recipient.to_string() + ] + }, + ); - let option = suite.query_check_option("einstein".to_owned()).unwrap(); - assert!(option); + let option: CheckOptionResponse = adapter + .query(&AdapterQueryMsg::CheckOption { + option: einstein.to_string(), + }) + .unwrap(); + assert!(option.valid); - let option = suite.query_check_option("newton".to_owned()).unwrap(); - assert!(!option); + let option: CheckOptionResponse = adapter + .query(&AdapterQueryMsg::CheckOption { + option: newton.to_string(), + }) + .unwrap(); + assert!(!option.valid); } diff --git a/contracts/gauges/gauge-adapter/src/multitest/submission.rs b/contracts/gauges/gauge-adapter/src/multitest/submission.rs index 4243ace50..41f8a88cb 100644 --- a/contracts/gauges/gauge-adapter/src/multitest/submission.rs +++ b/contracts/gauges/gauge-adapter/src/multitest/submission.rs @@ -1,45 +1,60 @@ -use crate::{msg::SubmissionResponse, ContractError}; - -use super::suite::SuiteBuilder; - -use cosmwasm_std::{coin, Addr, Uint128}; +use crate::{ + msg::{ + AdapterQueryMsg, AdapterQueryMsgFns, AllSubmissionsResponse, AssetUnchecked, ExecuteMsg, + ExecuteMsgFns, ReceiveMsg, SubmissionResponse, + }, + multitest::suite::{ + cw20_helper, native_submission_helper, setup_cw20_reward_gauge_adapter, setup_gauge_adapter, + }, + ContractError, +}; + +use abstract_cw20::{msg::Cw20ExecuteMsgFns, Cw20ExecuteMsg as AbsCw20ExecuteMsg}; +use abstract_cw20_base::msg::QueryMsgFns; +use cosmwasm_std::{coin, to_json_binary, Addr, CosmosMsg, Decimal, Uint128, WasmMsg}; +use cw_denom::UncheckedDenom; +use cw_orch::{contract::interface_traits::CwOrchExecute, mock::MockBech32, prelude::*}; #[test] fn create_default_submission() { - let suite = SuiteBuilder::new() - .with_community_pool("community_pool") - .build(); + let mock = MockBech32::new("mock"); + let treasury = &mock.addr_make("community_pool"); + + let adapter = setup_gauge_adapter(mock.clone(), None); // this one is created by default during instantiation assert_eq!( SubmissionResponse { - sender: suite.gauge_adapter.clone(), + sender: adapter.address().unwrap(), name: "Unimpressed".to_owned(), url: "Those funds go back to the community pool".to_owned(), - address: Addr::unchecked("community_pool"), + address: treasury.clone(), }, - suite.query_submission("community_pool".to_owned()).unwrap() + adapter + .query(&crate::msg::AdapterQueryMsg::Submission { + address: treasury.to_string() + }) + .unwrap() ) } #[test] fn create_submission_no_required_deposit() { - let mut suite = SuiteBuilder::new() - .with_funds("owner", &[coin(100_000, "juno")]) - .build(); + let mock = MockBech32::new("mock"); + let adapter = setup_gauge_adapter(mock.clone(), None); - let recipient = "user".to_owned(); + let recipient = mock.addr_make("recipient"); + mock.add_balance(&mock.sender, vec![coin(1_000, "juno")]) + .unwrap(); // Fails send funds along with the tx. - let err = suite - .execute_create_submission( - suite.owner.clone(), - "WYNDers".to_owned(), - "https://www.wynddao.com/".to_owned(), - recipient.clone(), - &[coin(1_000, "juno")], - ) - .unwrap_err(); + let err = native_submission_helper( + adapter.clone(), + mock.sender.clone(), + recipient.clone(), + Some(coin(1_000, "juno")), + ) + .unwrap_err(); assert_eq!( ContractError::InvalidDepositAmount { @@ -49,131 +64,116 @@ fn create_submission_no_required_deposit() { ); // Valid submission. - _ = suite - .execute_create_submission( - suite.owner.clone(), - "WYNDers".to_owned(), - "https://www.wynddao.com/".to_owned(), - recipient.clone(), - &[], - ) - .unwrap(); + let result = native_submission_helper( + adapter.clone(), + mock.sender.clone(), + recipient.clone(), + None, + ); + assert!(result.is_ok()); assert_eq!( SubmissionResponse { - sender: suite.owner.clone(), - name: "WYNDers".to_owned(), - url: "https://www.wynddao.com/".to_owned(), - address: Addr::unchecked(recipient.clone()), + sender: mock.sender, + name: "DAOers".to_owned(), + url: "https://daodao.zone".to_owned(), + address: recipient.clone(), }, - suite.query_submission(recipient).unwrap() + adapter + .query(&crate::msg::AdapterQueryMsg::Submission { + address: recipient.to_string() + }) + .unwrap(), ) } #[test] fn overwrite_existing_submission() { - let mut suite = SuiteBuilder::new() - .with_funds("owner", &[coin(100_000, "juno")]) - .build(); - - let recipient = "user".to_owned(); - - suite - .execute_create_submission( - suite.owner.clone(), - "WYNDers".to_owned(), - "https://www.wynddao.com/".to_owned(), - recipient.clone(), - &[], - ) - .unwrap(); + let mock = MockBech32::new("mock"); + let adapter = setup_gauge_adapter(mock.clone(), None); + let recipient = mock.addr_make("recipient"); + native_submission_helper( + adapter.clone(), + mock.sender.clone(), + recipient.clone(), + None, + ) + .unwrap(); assert_eq!( SubmissionResponse { - sender: suite.owner.clone(), - name: "WYNDers".to_owned(), - url: "https://www.wynddao.com/".to_owned(), - address: Addr::unchecked(recipient.clone()), + sender: mock.sender.clone(), + name: "DAOers".to_owned(), + url: "https://daodao.zone".to_string(), + address: recipient.clone(), }, - suite.query_submission(recipient.clone()).unwrap() + adapter.submission(recipient.to_string()).unwrap() ); // Try to submit to the same address with different user - let err = suite - .execute_create_submission( - Addr::unchecked("anotheruser"), - "WYNDers".to_owned(), - "https://www.wynddao.com/".to_owned(), - recipient.clone(), - &[], - ) - .unwrap_err(); - assert_eq!( - ContractError::UnauthorizedSubmission {}, - err.downcast().unwrap() - ); + let err = native_submission_helper( + adapter.clone(), + Addr::unchecked("anotheruser"), + recipient.clone(), + None, + ) + .unwrap_err(); - // Overwriting submission as same author works - let err = suite - .execute_create_submission( - Addr::unchecked("anotheruser"), - "WYNDers".to_owned(), - "https://www.wynddao.com/".to_owned(), - recipient.clone(), - &[], - ) - .unwrap_err(); assert_eq!( ContractError::UnauthorizedSubmission {}, err.downcast().unwrap() ); - suite - .execute_create_submission( - suite.owner.clone(), - "WYNDers".to_owned(), - "wynddao".to_owned(), - recipient.clone(), - &[], - ) - .unwrap(); + // Overwriting submission as same author works + native_submission_helper(adapter.clone(), mock.sender, recipient.clone(), None).unwrap(); - let response = suite.query_submission(recipient).unwrap(); - assert_eq!(response.url, "wynddao".to_owned()); + let response = adapter.submission(recipient.to_string()).unwrap(); + assert_eq!(response.url, "https://daodao.zone".to_owned()); } #[test] fn create_submission_required_deposit() { - let mut suite = SuiteBuilder::new() - .with_funds("owner", &[coin(100_000, "juno"), coin(100_000, "wynd")]) - .with_native_deposit(1_000) - .build(); + let mock = MockBech32::new("mock"); + let adapter = setup_gauge_adapter( + mock.clone(), + Some(AssetUnchecked { + denom: UncheckedDenom::Native("juno".into()), + amount: 1_000u128.into(), + }), + ); - let recipient = "user".to_owned(); + let recipient = mock.addr_make("recipient"); + mock.add_balance(&mock.sender.clone(), vec![coin(1_000, "wynd")]) + .unwrap(); + mock.add_balance(&mock.sender.clone(), vec![coin(1_000, "juno")]) + .unwrap(); // Fails if no funds sent. - let err = suite - .execute_create_submission( - suite.owner.clone(), - "WYNDers".to_owned(), - "https://www.wynddao.com/".to_owned(), - recipient.clone(), - &[], - ) - .unwrap_err(); + let err = native_submission_helper( + adapter.clone(), + mock.sender.clone(), + recipient.clone(), + None, + ) + .unwrap_err(); - assert_eq!(ContractError::DepositRequired {}, err.downcast().unwrap()); + assert_eq!( + ContractError::PaymentError(cw_utils::PaymentError::NoFunds {}), + err.downcast().unwrap() + ); - // Fails if correct denom but not enought amount. - let err = suite - .execute_create_submission( - suite.owner.clone(), - "WYNDers".to_owned(), - "https://www.wynddao.com/".to_owned(), - recipient.clone(), - &[coin(1, "juno")], - ) - .unwrap_err(); + // Fails if correct denom but not enough amount. + // Fails if no funds sent. + let err = native_submission_helper( + adapter.clone(), + mock.sender.clone(), + recipient.clone(), + Some(Coin { + denom: "juno".into(), + amount: 999u128.into(), + }), + ) + .unwrap_err(); assert_eq!( ContractError::InvalidDepositAmount { @@ -183,15 +183,16 @@ fn create_submission_required_deposit() { ); // Fails if enough amount but incorrect denom. - let err = suite - .execute_create_submission( - suite.owner.clone(), - "WYNDers".to_owned(), - "https://www.wynddao.com/".to_owned(), - recipient.clone(), - &[coin(1_000, "wynd")], - ) - .unwrap_err(); + let err = native_submission_helper( + adapter.clone(), + mock.sender.clone(), + recipient.clone(), + Some(Coin { + denom: "wynd".into(), + amount: 1_000u128.into(), + }), + ) + .unwrap_err(); assert_eq!( ContractError::InvalidDepositType {}, @@ -199,48 +200,69 @@ fn create_submission_required_deposit() { ); // Valid submission. - _ = suite - .execute_create_submission( - suite.owner.clone(), - "WYNDers".to_owned(), - "https://www.wynddao.com/".to_owned(), - recipient.clone(), - &[coin(1_000, "juno")], - ) - .unwrap(); + native_submission_helper( + adapter.clone(), + mock.sender.clone(), + recipient.clone(), + Some(Coin { + denom: "juno".into(), + amount: 1_000u128.into(), + }), + ) + .unwrap(); assert_eq!( SubmissionResponse { - sender: suite.owner.clone(), - name: "WYNDers".to_owned(), - url: "https://www.wynddao.com/".to_owned(), - address: Addr::unchecked(recipient.clone()), + sender: mock.sender.clone(), + name: "DAOers".to_owned(), + url: "https://daodao.zone".to_owned(), + address: recipient.clone(), }, - suite.query_submission(recipient).unwrap() + adapter + .query(&AdapterQueryMsg::Submission { + address: recipient.to_string() + }) + .unwrap() ) } #[test] fn create_receive_required_deposit() { - let mut suite = SuiteBuilder::new() - .with_funds("owner", &[coin(100_000, "juno")]) - .with_cw20_funds("owner", 1_000) - .with_cw20_deposit(1_000) - .build(); - - let recipient = "user".to_owned(); + let mock = MockBech32::new("mock"); + let cw20 = cw20_helper(mock.clone()); + let bad_cw20 = cw20_helper(mock.clone()); + let cw20_addr = cw20.address().unwrap(); + let bad_cw20_addr = bad_cw20.address().unwrap(); + println!("good cw20: {:#?}", cw20_addr); + println!("bad cw20: {:#?}", bad_cw20_addr); + let adapter = setup_gauge_adapter( + mock.clone(), + Some(AssetUnchecked { + denom: UncheckedDenom::Cw20(cw20_addr.to_string()), + amount: 1_000u128.into(), + }), + ); - let cw20_addr = suite.instantiate_token(suite.owner.clone().as_ref(), "moonbites", 1_000_000); + let recipient = mock.sender_addr().to_string(); + let binary_msg = to_json_binary(&ReceiveMsg::CreateSubmission { + name: "DAOers".into(), + url: "https://daodao.zone".into(), + address: recipient.clone(), + }) + .unwrap(); // Fails by sending wrong cw20. - let err = suite - .execute_receive_through_cw20( - suite.owner.clone(), - "WYNDers".to_owned(), - "https://www.wynddao.com/".to_owned(), - recipient.clone(), - 1_000, - cw20_addr, + let err = adapter + .call_as(&Addr::unchecked( + "mock1mzdhwvvh22wrt07w59wxyd58822qavwkx5lcej7aqfkpqqlhaqfsetqc4t", + )) + .execute( + &ExecuteMsg::Receive(cw20::Cw20ReceiveMsg { + sender: recipient.to_string(), + amount: Uint128::from(1_000u128), + msg: binary_msg.clone(), + }), + None, ) .unwrap_err(); @@ -250,14 +272,15 @@ fn create_receive_required_deposit() { ); // Fails by sending less tokens than required. - let err = suite - .execute_receive_through_cw20( - suite.owner.clone(), - "WYNDers".to_owned(), - "https://www.wynddao.com/".to_owned(), - recipient.clone(), - 1, - suite.default_cw20.clone(), + let err = adapter + .call_as(&cw20.address().unwrap()) + .execute( + &ExecuteMsg::Receive(cw20::Cw20ReceiveMsg { + sender: recipient.to_string(), + amount: Uint128::from(999u128), + msg: binary_msg.clone(), + }), + None, ) .unwrap_err(); @@ -269,38 +292,49 @@ fn create_receive_required_deposit() { ); // Valid submission. - _ = suite - .execute_receive_through_cw20( - suite.owner.clone(), - "WYNDers".to_owned(), - "https://www.wynddao.com/".to_owned(), - recipient.clone(), - 1_000, - suite.default_cw20.clone(), + adapter + .call_as(&cw20.address().unwrap()) + .execute( + &ExecuteMsg::Receive(cw20::Cw20ReceiveMsg { + sender: recipient.to_string(), + amount: Uint128::from(1_000u128), + msg: binary_msg, + }), + None, ) .unwrap(); assert_eq!( SubmissionResponse { - sender: suite.owner.clone(), - name: "WYNDers".to_owned(), - url: "https://www.wynddao.com/".to_owned(), + sender: mock.sender.clone(), + name: "DAOers".to_owned(), + url: "https://daodao.zone".to_owned(), address: Addr::unchecked(recipient.clone()), }, - suite.query_submission(recipient).unwrap() + adapter + .query(&AdapterQueryMsg::Submission { + address: recipient.to_string() + }) + .unwrap() ); - assert_eq!(2, suite.query_submissions().unwrap().len()) + assert_eq!( + 2, + adapter + .query::(&AdapterQueryMsg::AllSubmissions {}) + .unwrap() + .submissions + .len() + ) } #[test] fn return_deposits_no_required_deposit() { - let mut suite = SuiteBuilder::new() - .with_funds("owner", &[coin(100_000, "juno")]) - .build(); + let mock = MockBech32::new("mock"); + let adapter = setup_gauge_adapter(mock.clone(), None); - let err = suite - .execute_return_deposit(suite.owner.clone().as_ref()) + let err = adapter + .execute(&ExecuteMsg::ReturnDeposits {}, None) .unwrap_err(); assert_eq!(ContractError::NoDepositToRefund {}, err.downcast().unwrap()) @@ -308,177 +342,245 @@ fn return_deposits_no_required_deposit() { #[test] fn return_deposits_no_admin() { - let mut suite = SuiteBuilder::new() - .with_funds("owner", &[coin(100_000, "juno")]) - .with_native_deposit(1_000) - .build(); + let mock = MockBech32::new("mock"); + let bad_addr = mock.addr_make("einstien"); + let adapter = setup_gauge_adapter( + mock.clone(), + Some(AssetUnchecked { + denom: UncheckedDenom::Native("juno".into()), + amount: 1_000u128.into(), + }), + ); - let err = suite.execute_return_deposit("einstein").unwrap_err(); + let err = adapter + .call_as(&bad_addr) + .execute(&ExecuteMsg::ReturnDeposits {}, None) + .unwrap_err(); assert_eq!(ContractError::Unauthorized {}, err.downcast().unwrap()) } #[test] fn return_deposits_required_native_deposit() { - let mut suite = SuiteBuilder::new() - .with_funds("owner", &[coin(1_000, "juno")]) - .with_native_deposit(1_000) - .build(); - - let recipient = "user".to_owned(); + let mock = MockBech32::new("mock"); + let adapter = setup_gauge_adapter( + mock.clone(), + Some(AssetUnchecked { + denom: UncheckedDenom::Native("juno".into()), + amount: 1_000u128.into(), + }), + ); + mock.add_balance(&mock.sender, vec![coin(1_000u128, "juno")]) + .unwrap(); + let recipient = mock.addr_make("recipient"); // Valid submission. - _ = suite - .execute_create_submission( - suite.owner.clone(), - "WYNDers".to_owned(), - "https://www.wynddao.com/".to_owned(), - recipient.clone(), - &[coin(1_000, "juno")], - ) - .unwrap(); + native_submission_helper( + adapter.clone(), + mock.sender.clone(), + recipient.clone(), + Some(coin(1_000u128, "juno")), + ) + .unwrap(); assert_eq!( - suite.query_native_balance(suite.owner.as_ref()).unwrap(), - 0u128, + mock.query_balance(&mock.sender.clone(), "juno").unwrap(), + Uint128::zero() ); - assert_eq!(suite.query_native_balance(&recipient).unwrap(), 0u128,); assert_eq!( - suite - .query_native_balance(suite.gauge_adapter.as_ref()) + mock.query_balance(&recipient, "juno").unwrap(), + Uint128::zero() + ); + assert_eq!( + mock.query_balance(&adapter.address().unwrap(), "juno") .unwrap(), - 1_000u128, + Uint128::from(1000u128) ); - _ = suite - .execute_return_deposit(suite.owner.clone().as_ref()) + adapter + .execute(&ExecuteMsg::ReturnDeposits {}, None) .unwrap(); - assert_eq!( - suite.query_native_balance(suite.owner.as_ref()).unwrap(), - 1_000u128, + mock.query_balance(&mock.sender.clone(), "juno").unwrap(), + Uint128::from(1000u128) ); - assert_eq!(suite.query_native_balance(&recipient).unwrap(), 0u128,); assert_eq!( - suite - .query_native_balance(suite.gauge_adapter.as_ref()) + mock.query_balance(&recipient, "juno").unwrap(), + Uint128::zero() + ); + assert_eq!( + mock.query_balance(&adapter.address().unwrap(), "juno") .unwrap(), - 0u128, + Uint128::zero() ); } #[test] fn return_deposits_required_native_deposit_multiple_deposits() { - let mut suite = SuiteBuilder::new() - .with_funds("owner", &[coin(1_000, "juno")]) - .with_funds("einstein", &[coin(1_000, "juno")]) - .with_native_deposit(1_000) - .build(); - - let recipient = "user".to_owned(); - - // Valid submission. - _ = suite - .execute_create_submission( - suite.owner.clone(), - "WYNDers".to_owned(), - "https://www.wynddao.com/".to_owned(), - recipient.clone(), - &[coin(1_000, "juno")], - ) - .unwrap(); + let mock = MockBech32::new("mock"); + let adapter = setup_gauge_adapter( + mock.clone(), + Some(AssetUnchecked { + denom: UncheckedDenom::Native("juno".into()), + amount: 1_000u128.into(), + }), + ); - // Valid submission. - _ = suite - .execute_create_submission( - Addr::unchecked("einstein"), - "MIBers".to_owned(), - "https://www.mib.tech/".to_owned(), - "einstein".to_owned(), - &[coin(1_000, "juno")], - ) + let recipient = mock.addr_make("recipient"); + let einstien = mock + .addr_make_with_balance("einstien", vec![coin(1_000u128, "juno")]) .unwrap(); - - _ = suite - .execute_return_deposit(suite.owner.clone().as_ref()) + mock.add_balance(&mock.sender, vec![coin(1_000u128, "juno")]) .unwrap(); + // Valid submission. + native_submission_helper( + adapter.clone(), + mock.sender.clone(), + recipient.clone(), + Some(coin(1_000u128, "juno")), + ) + .unwrap(); + // Valid submission. + native_submission_helper( + adapter.clone(), + einstien.clone(), + einstien.clone(), + Some(coin(1_000u128, "juno")), + ) + .unwrap(); + adapter.return_deposits().unwrap(); + assert_eq!( + mock.query_balance(&mock.sender.clone(), "juno").unwrap(), + Uint128::from(1000u128) + ); assert_eq!( - suite.query_native_balance(suite.owner.as_ref()).unwrap(), - 1_000u128, + mock.query_balance(&einstien, "juno").unwrap(), + Uint128::from(1000u128) ); - assert_eq!(suite.query_native_balance("einstein").unwrap(), 1_000u128,); - assert_eq!(suite.query_native_balance(&recipient).unwrap(), 0u128,); assert_eq!( - suite - .query_native_balance(suite.gauge_adapter.as_ref()) + mock.query_balance(&recipient, "juno").unwrap(), + Uint128::zero() + ); + assert_eq!( + mock.query_balance(&adapter.address().unwrap(), "juno") .unwrap(), - 0u128, + Uint128::zero() ); } #[test] fn return_deposits_required_cw20_deposit() { - let mut suite = SuiteBuilder::new() - .with_funds("owner", &[coin(1_000, "juno")]) - .with_cw20_funds("owner", 1_000) - .with_cw20_deposit(1_000) - .build(); - - let recipient = "user".to_owned(); + let mock = MockBech32::new("mock"); + let cw20 = cw20_helper(mock.clone()); + let recipient = mock.addr_make("recipient"); + let adapter = setup_gauge_adapter( + mock.clone(), + Some(AssetUnchecked { + denom: UncheckedDenom::Cw20(cw20.addr_str().unwrap()), + amount: 1_000u128.into(), + }), + ); + let binary_msg = to_json_binary(&ReceiveMsg::CreateSubmission { + name: "DAOers".into(), + url: "https://daodao.zone".into(), + address: recipient.to_string(), + }) + .unwrap(); // Valid submission. - _ = suite - .execute_receive_through_cw20( - suite.owner.clone(), - "WYNDers".to_owned(), - "https://www.wynddao.com/".to_owned(), - recipient.clone(), - 1_000, - suite.default_cw20.clone(), - ) + cw20.send(1_000u128.into(), adapter.addr_str().unwrap(), binary_msg) .unwrap(); assert_eq!( - suite - .query_cw20_balance(suite.owner.as_ref(), &suite.default_cw20) - .unwrap(), - 0u128, + cw20.balance(mock.sender.to_string()).unwrap().balance, + Uint128::from(999_000u128) ); assert_eq!( - suite - .query_cw20_balance(&recipient, &suite.default_cw20) - .unwrap(), - 0u128, + cw20.balance(recipient.to_string()).unwrap().balance, + Uint128::zero() ); assert_eq!( - suite - .query_cw20_balance(suite.gauge_adapter.as_ref(), &suite.default_cw20) - .unwrap(), - 1_000u128, + cw20.balance(adapter.address().unwrap().to_string()) + .unwrap() + .balance, + Uint128::from(1_000u128), ); - _ = suite - .execute_return_deposit(suite.owner.clone().as_ref()) - .unwrap(); + adapter.return_deposits().unwrap(); assert_eq!( - suite - .query_cw20_balance(suite.owner.as_ref(), &suite.default_cw20) - .unwrap(), - 1_000u128, + cw20.balance(mock.sender.to_string()).unwrap().balance, + Uint128::from(1_000_000u128), ); - // Tokens are sent back to the address specified in the sumbission. + // Tokens are sent back to submission sender, not recipient. assert_eq!( - suite - .query_cw20_balance(&recipient, &suite.default_cw20) - .unwrap(), - 0u128, + cw20.balance(recipient.to_string()).unwrap().balance, + Uint128::zero(), ); assert_eq!( - suite - .query_cw20_balance(suite.gauge_adapter.as_ref(), &suite.default_cw20) - .unwrap(), - 0u128, + cw20.balance(adapter.address().unwrap().to_string()) + .unwrap() + .balance, + Uint128::zero(), + ); +} + +#[test] +fn sample_gauge_msgs_cw20() { + let mock = MockBech32::new("mock"); + let addr_1 = mock.addr_make("addr1"); + let addr_2 = mock.addr_make("addr2"); + let addr_3 = mock.addr_make("addr3"); + let reward = Uint128::new(1_000_000); + let (adapter, cw20) = setup_cw20_reward_gauge_adapter(mock.clone(), None); + + adapter + .create_submission(addr_1.to_string(), "name".into(), "https://test.url".into()) + .unwrap(); + adapter + .create_submission(addr_2.to_string(), "name".into(), "https://test.url".into()) + .unwrap(); + + let selected = vec![ + (addr_1.to_string(), Decimal::percent(41)), + (addr_2.to_string(), Decimal::percent(33)), + (addr_3.to_string(), Decimal::percent(26)), + ]; + + let res: crate::msg::SampleGaugeMsgsResponse = + adapter.sample_gauge_msgs(selected.clone()).unwrap(); + assert_eq!(res.execute.len(), 3); + assert_eq!( + res.execute, + [ + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: cw20.addr_str().unwrap(), + msg: to_json_binary(&AbsCw20ExecuteMsg::Transfer { + recipient: addr_1.to_string(), + amount: reward * Decimal::percent(41) + }) + .unwrap(), + funds: vec![] + }), + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: cw20.addr_str().unwrap(), + msg: to_json_binary(&AbsCw20ExecuteMsg::Transfer { + recipient: addr_2.to_string(), + amount: reward * Decimal::percent(33) + }) + .unwrap(), + funds: vec![] + }), + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: cw20.addr_str().unwrap(), + msg: to_json_binary(&AbsCw20ExecuteMsg::Transfer { + recipient: addr_3.to_string(), + amount: reward * Decimal::percent(26) + }) + .unwrap(), + funds: vec![] + }), + ] ); } diff --git a/contracts/gauges/gauge-adapter/src/multitest/suite.rs b/contracts/gauges/gauge-adapter/src/multitest/suite.rs index 53ac2b14d..a667097fd 100644 --- a/contracts/gauges/gauge-adapter/src/multitest/suite.rs +++ b/contracts/gauges/gauge-adapter/src/multitest/suite.rs @@ -1,332 +1,116 @@ -use cosmwasm_std::{to_json_binary, Addr, Binary, Coin, Uint128}; -use cw20::{BalanceResponse, Cw20QueryMsg}; -use cw20::{Cw20Coin, MinterResponse}; -use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; +use abstract_cw_plus_interface::cw20_base::Cw20Base; +use cosmwasm_std::{Addr, Coin, Uint128}; +use cw_orch::{interface, mock::cw_multi_test::AppResponse, prelude::*}; +use cw_orch_core::CwEnvError; -use anyhow::Result as AnyResult; -use cw20_base::msg::ExecuteMsg as Cw20BaseExecuteMsg; -use cw20_base::msg::InstantiateMsg as Cw20BaseInstantiateMsg; +use abstract_cw20::{Cw20Coin as AbsCw20Coin, MinterResponse}; -use crate::msg::CheckOptionResponse; use crate::{ - msg::{ - AdapterQueryMsg, AllOptionsResponse, AllSubmissionsResponse, ExecuteMsg, ReceiveMsg, - SubmissionResponse, - }, - state::{Asset, AssetType}, + contract::{execute, instantiate, migrate, query}, + msg::{AdapterQueryMsg as QueryMsg, AssetUnchecked, ExecuteMsg, InstantiateMsg, MigrateMsg}, }; -pub const NATIVE: &str = "juno"; -pub const CW20: &str = "wynd"; -pub const OWNER: &str = "owner"; - // Store the marketing gauge adapter contract and returns the code id. -fn store_gauge_adapter(app: &mut App) -> u64 { - let contract = Box::new(ContractWrapper::new_with_empty( - crate::contract::execute, - crate::contract::instantiate, - crate::contract::query, - )); - - app.store_code(contract) +#[interface(InstantiateMsg, ExecuteMsg, QueryMsg, MigrateMsg)] +pub struct GaugeAdapter; + +impl Uploadable for GaugeAdapter { + /// Return the path to the wasm file corresponding to the contract + fn wasm(_chain: &ChainInfoOwned) -> WasmPath { + artifacts_dir_from_workspace!() + .find_wasm_path("gauge_adapter") + .unwrap() + } + /// Returns a CosmWasm contract wrapper + fn wrapper() -> Box> { + Box::new(ContractWrapper::new_with_empty(execute, instantiate, query).with_migrate(migrate)) + } } -// Store the cw20 contract and returns the code id. -fn store_cw20(app: &mut App) -> u64 { - let contract = Box::new(ContractWrapper::new_with_empty( - cw20_base::contract::execute, - cw20_base::contract::instantiate, - cw20_base::contract::query, - )); - - app.store_code(contract) +pub fn setup_gauge_adapter( + mock: MockBech32, + required_deposit: Option, +) -> GaugeAdapter { + let adapter = GaugeAdapter::new("gauge_adapter", mock.clone()); + adapter.upload().unwrap(); + + let instantiate = InstantiateMsg { + admin: mock.sender_addr().to_string(), + required_deposit, + reward: AssetUnchecked::new_native("juno", 1_000_000), + community_pool: mock.addr_make("community_pool").to_string(), + }; + adapter.instantiate(&instantiate, None, None).unwrap(); + adapter } -#[derive(Debug)] -pub struct SuiteBuilder { - // Gauge adapter's instantiate params - community_pool: String, - required_deposit: Option, - reward: Asset, - funds: Vec<(Addr, Vec)>, - cw20_funds: Vec, +pub fn setup_cw20_reward_gauge_adapter( + mock: MockBech32, + required_deposit: Option, +) -> (GaugeAdapter, Cw20Base) { + let adapter = GaugeAdapter::new("gauge_adapter", mock.clone()); + adapter.upload().unwrap(); + let cw20 = cw20_helper(mock.clone()); + + let instantiate = InstantiateMsg { + admin: mock.sender_addr().to_string(), + required_deposit, + reward: AssetUnchecked::new_cw20(&cw20.addr_str().unwrap(), 1_000_000), + community_pool: mock.addr_make("community_pool").to_string(), + }; + adapter.instantiate(&instantiate, None, None).unwrap(); + (adapter, cw20) } -impl SuiteBuilder { - pub fn new() -> Self { - Self { - community_pool: "community".to_owned(), - required_deposit: None, - reward: Asset { - denom: AssetType::Native(NATIVE.into()), - amount: Uint128::new(1_000_000), +// +pub fn native_submission_helper( + adapter: GaugeAdapter, + sender: Addr, + recipient: Addr, + native_tokens: Option, +) -> Result { + if let Some(assets) = native_tokens.clone() { + adapter.call_as(&sender).execute( + &crate::msg::ExecuteMsg::CreateSubmission { + name: "DAOers".to_string(), + url: "https://daodao.zone".to_string(), + address: recipient.to_string(), }, - funds: vec![], - cw20_funds: vec![], - } - } - - pub fn with_community_pool(mut self, community_pool: &str) -> Self { - self.community_pool = community_pool.to_string(); - self - } - - // Allows to initialize the suite with native coins associated to an address. - pub fn with_funds(mut self, addr: &str, funds: &[Coin]) -> Self { - self.funds.push((Addr::unchecked(addr), funds.into())); - self - } - - // Allows to initialize the suite with default cw20 tokens associated to an address. - pub fn with_cw20_funds(mut self, addr: &str, amount: u128) -> Self { - self.cw20_funds.push(Cw20Coin { - address: addr.into(), - amount: Uint128::from(amount), - }); - self - } - - // Allows to initialize the marketing gauge adapter with required native coins in the config. - pub fn with_native_deposit(mut self, amount: u128) -> Self { - self.required_deposit = Some(Asset { - denom: AssetType::Native(NATIVE.into()), - amount: Uint128::from(amount), - }); - self - } - - // Allows to initialize the marketing gauge adapter with required cw20 tokens in the config. - pub fn with_cw20_deposit(mut self, amount: u128) -> Self { - self.required_deposit = Some(Asset { - denom: AssetType::Cw20("contract1".to_string()), - amount: Uint128::from(amount), - }); - self - } - - // Instantiate a marketing gauge adapter and returns its address. - fn instantiate_marketing_gauge_adapter( - &self, - app: &mut App, - gauge_id: u64, - owner: &str, - ) -> Addr { - app.instantiate_contract( - gauge_id, - Addr::unchecked(owner), - &crate::msg::InstantiateMsg { - admin: owner.to_owned(), - required_deposit: self.required_deposit.clone(), - community_pool: self.community_pool.clone(), - reward: self.reward.clone(), - }, - &[], - "Marketing Gauge Adapter", - None, + Some(&[assets]), ) - .unwrap() - } - - // Instantiate a cw20 and returns its address. - fn instantiate_default_cw20(&self, app: &mut App, cw20_code_id: u64, owner: &str) -> Addr { - app.instantiate_contract( - cw20_code_id, - Addr::unchecked(owner), - &Cw20BaseInstantiateMsg { - name: CW20.to_owned(), - symbol: CW20.to_owned(), - decimals: 6, - initial_balances: self.cw20_funds.clone(), - mint: Some(MinterResponse { - minter: owner.to_string(), - cap: None, - }), - marketing: None, + } else { + adapter.call_as(&sender).execute( + &crate::msg::ExecuteMsg::CreateSubmission { + name: "DAOers".to_string(), + url: "https://daodao.zone".to_string(), + address: recipient.to_string(), }, - &[], - CW20.to_owned(), None, ) - .unwrap() - } - - #[track_caller] - pub fn build(self) -> Suite { - let mut app = App::default(); - let owner = Addr::unchecked(OWNER); - - // Store required contracts. - let cw20_code_id = store_cw20(&mut app); - let gauge_adapter_code_id = store_gauge_adapter(&mut app); - - // Instantiate default contracts. - let gauge_adapter_addr = - self.instantiate_marketing_gauge_adapter(&mut app, gauge_adapter_code_id, OWNER); - - // cw20 address must be "contract1" otherwise cannot use `with_cw20_deposit()`. Not very - // elegant but do its job. - let cw20_addr = self.instantiate_default_cw20(&mut app, cw20_code_id, OWNER); - - // Mint initial native token if any. - app.init_modules(|router, _, storage| -> AnyResult<()> { - for (addr, coin) in self.funds { - router.bank.init_balance(storage, &addr, coin)?; - } - Ok(()) - }) - .unwrap(); - - Suite { - owner, - app, - gauge_adapter: gauge_adapter_addr, - default_cw20: cw20_addr, - cw20_code_id, - } } } -pub struct Suite { - pub owner: Addr, - pub app: App, - pub gauge_adapter: Addr, - pub default_cw20: Addr, - // This is stored to instantiate other cw20 tokens in tests. - cw20_code_id: u64, +pub fn cw20_helper(mock: MockBech32) -> Cw20Base { + let cw20 = Cw20Base::new("cw20", mock.clone()); + cw20.upload().unwrap(); + init_cw20(cw20.clone(), mock.sender.to_string()); + cw20 } -impl Suite { - // --------------------------------------------------------------------------------------------- - // Execute - // --------------------------------------------------------------------------------------------- - pub fn execute_create_submission( - &mut self, - sender: Addr, - name: String, - url: String, - address: String, - funds: &[Coin], - ) -> AnyResult { - self.app.execute_contract( - sender, - self.gauge_adapter.clone(), - &ExecuteMsg::CreateSubmission { name, url, address }, - funds, - ) - } - - pub fn execute_receive_through_cw20( - &mut self, - sender: Addr, - name: String, - url: String, - address: String, - // amount refers to CW20 amount. - amount: u128, - cw20_addr: Addr, - ) -> AnyResult { - let msg: Binary = to_json_binary(&ReceiveMsg::CreateSubmission { name, url, address })?; - - self.app.execute_contract( - sender, - cw20_addr, - &Cw20BaseExecuteMsg::Send { - contract: self.gauge_adapter.to_string(), - amount: Uint128::from(amount), - msg, - }, - &[], - ) - } - - pub fn execute_return_deposit(&mut self, sender: &str) -> AnyResult { - self.app.execute_contract( - Addr::unchecked(sender), - self.gauge_adapter.clone(), - &ExecuteMsg::ReturnDeposits {}, - &[], - ) - } - - // --------------------------------------------------------------------------------------------- - // Queries - // --------------------------------------------------------------------------------------------- - pub fn query_submission(&self, address: String) -> AnyResult { - Ok(self.app.wrap().query_wasm_smart( - self.gauge_adapter.clone(), - &AdapterQueryMsg::Submission { address }, - )?) - } - - pub fn query_submissions(&self) -> AnyResult> { - let res: AllSubmissionsResponse = self.app.wrap().query_wasm_smart( - self.gauge_adapter.clone(), - &AdapterQueryMsg::AllSubmissions {}, - )?; - - Ok(res.submissions) - } - - pub fn query_all_options(&self) -> AnyResult> { - let res: AllOptionsResponse = self - .app - .wrap() - .query_wasm_smart(self.gauge_adapter.clone(), &AdapterQueryMsg::AllOptions {})?; - - Ok(res.options) - } - - pub fn query_check_option(&self, option: String) -> AnyResult { - let res: CheckOptionResponse = self.app.wrap().query_wasm_smart( - self.gauge_adapter.clone(), - &AdapterQueryMsg::CheckOption { option }, - )?; - - Ok(res.valid) - } - - // --------------------------------------------------------------------------------------------- - // Helpers - // --------------------------------------------------------------------------------------------- - - // Instantiate a cw20 token and assign to address a specific amount. - pub fn instantiate_token(&mut self, address: &str, token: &str, amount: u128) -> Addr { - self.app - .instantiate_contract( - self.cw20_code_id, - Addr::unchecked(address), - &Cw20BaseInstantiateMsg { - name: token.to_owned(), - symbol: token.to_owned(), - decimals: 6, - initial_balances: vec![Cw20Coin { - address: address.to_owned(), - amount: Uint128::from(amount), - }], - mint: None, - marketing: None, - }, - &[], - token, - None, - ) - .unwrap() - } - - pub fn query_cw20_balance(&self, user: &str, contract: &Addr) -> AnyResult { - let balance: BalanceResponse = self.app.wrap().query_wasm_smart( - contract, - &Cw20QueryMsg::Balance { - address: user.to_owned(), - }, - )?; - - Ok(balance.balance.into()) - } - - pub fn query_native_balance(&self, user: &str) -> AnyResult { - let balance = self.app.wrap().query_balance(user, NATIVE)?; - - Ok(balance.amount.into()) - } +pub fn init_cw20(cw20: Cw20Base, minter: String) -> String { + let init_msg = abstract_cw20_base::msg::InstantiateMsg { + name: "test".to_string(), + symbol: "TEST".to_string(), + decimals: 6u8, + initial_balances: vec![AbsCw20Coin { + address: minter.clone(), + amount: Uint128::from(1_000_000u128), + }], + mint: Some(MinterResponse { minter, cap: None }), + marketing: None, + }; + cw20.instantiate(&init_msg, None, None).unwrap(); + let addr = cw20.address().unwrap(); + println!("correct cw20 addr: {:#?}", addr.clone()); + addr.to_string() } diff --git a/contracts/gauges/gauge-adapter/src/state.rs b/contracts/gauges/gauge-adapter/src/state.rs index e2d836943..68bd19d12 100644 --- a/contracts/gauges/gauge-adapter/src/state.rs +++ b/contracts/gauges/gauge-adapter/src/state.rs @@ -1,5 +1,6 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, Uint128}; +use cw_denom::CheckedDenom; use cw_storage_plus::{Item, Map}; #[cw_serde] @@ -16,34 +17,12 @@ pub struct Config { pub const CONFIG: Item = Item::new("config"); -#[cw_serde] -pub enum AssetType { - Native(String), - Cw20(String), -} - #[cw_serde] pub struct Asset { - pub denom: AssetType, + pub denom: CheckedDenom, pub amount: Uint128, } -impl Asset { - pub fn new_native(denom: &str, amount: u128) -> Self { - Self { - denom: AssetType::Native(denom.to_owned()), - amount: amount.into(), - } - } - - pub fn new_cw20(denom: &str, amount: u128) -> Self { - Self { - denom: AssetType::Cw20(denom.to_owned()), - amount: amount.into(), - } - } -} - #[cw_serde] pub struct Submission { pub sender: Addr, @@ -51,5 +30,5 @@ pub struct Submission { pub url: String, } -// All submissions indexed by submition's fund destination address. +// All submissions mapped by fund destination address. pub const SUBMISSIONS: Map = Map::new("submissions"); diff --git a/contracts/gauges/gauge/README.md b/contracts/gauges/gauge/README.md index de8f2c035..ef87daf0c 100644 --- a/contracts/gauges/gauge/README.md +++ b/contracts/gauges/gauge/README.md @@ -24,13 +24,13 @@ and eventually stopping them if we don't need them anymore (to avoid extra write ## Gauge Functionality -A gauge is initialised with a set of options. Anyone with voting power may vote for any option at any time, -which is recorded, and also updates the tally. If they revote, it checks their last vote to reduce power on +A gauge is initialized with a set of options. Anyone with voting power may vote for any option at any time, +which is recorded, and also updates the tally. If they re-vote, it checks their last vote to reduce power on that before adding to the new one. When an "update hook" is triggered, it updates the voting power of that user's vote, while maintaining the same option. Either increasing or decreasing the tally for the given option as appropriate. Every epoch (eg 1/week), the current tally of the gauge is sampled, and some cut-off applies (top 20, min 0.5% of votes, etc). The resulting set is the "selected set" and the options along with -their relative vote counts (normalised to 1.0 = total votes within this set) is used to initiate some +their relative vote counts (normalized to 1.0 = total votes within this set) is used to initiate some action (eg. distribute reward tokens). ## Extensibility