diff --git a/Cargo.lock b/Cargo.lock index 49b5e3e..47ac85c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -51,14 +51,15 @@ dependencies = [ "schemars", "semver", "serde", + "serde-json-wasm 1.0.0", "thiserror", ] [[package]] name = "anstream" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +checksum = "628a8f9bd1e24b4e0db2b4bc2d000b001e7dd032d54afa60a68836aeec5aa54a" dependencies = [ "anstyle", "anstyle-parse", @@ -104,9 +105,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.76" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" dependencies = [ "backtrace", ] @@ -119,9 +120,9 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "assert_cmd" -version = "2.0.12" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6" +checksum = "00ad3f3a942eee60335ab4342358c161ee296829e0d16ff42fc1d6cb07815467" dependencies = [ "anstyle", "bstr", @@ -161,9 +162,9 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64ct" @@ -196,9 +197,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "bitvec" @@ -238,9 +239,9 @@ checksum = "ab9008b6bb9fc80b5277f2fe481c09e828743d9151203e804583eb4c9e15b31d" [[package]] name = "bstr" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "542f33a8835a0884b006a0c3df3dadd99c0c3f296ed26c2fdc8028e01ad6230c" +checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" dependencies = [ "memchr", "regex-automata", @@ -304,9 +305,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.4.11" +version = "4.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" dependencies = [ "clap_builder", "clap_derive", @@ -314,9 +315,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.11" +version = "4.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" dependencies = [ "anstream", "anstyle", @@ -333,7 +334,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -404,9 +405,9 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.5.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8bb3c77c3b7ce472056968c745eb501c440fbc07be5004eba02782c35bfbbe3" +checksum = "8ed6aa9f904de106fa16443ad14ec2abe75e94ba003bb61c681c0e43d4c58d2a" dependencies = [ "digest 0.10.7", "ecdsa", @@ -418,18 +419,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.5.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea73e9162e6efde00018d55ed0061e93a108b5d6ec4548b4f8ce3c706249687" +checksum = "40abec852f3d4abec6d44ead9a58b78325021a1ead1e7229c3471414e57b2e49" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.5.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0df41ea55f2946b6b43579659eec048cc2f66e8c8e2e3652fc5e5e476f673856" +checksum = "b166215fbfe93dc5575bae062aa57ae7bb41121cffe53bac33b033257949d2a9" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -440,9 +441,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.5.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43609e92ce1b9368aa951b334dd354a2d0dd4d484931a5f83ae10e12a26c8ba9" +checksum = "8bf12f8e20bb29d1db66b7ca590bc2f670b548d21e9be92499bc0f9022a994a8" dependencies = [ "proc-macro2", "quote", @@ -451,9 +452,9 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.5.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04d6864742e3a7662d024b51a94ea81c9af21db6faea2f9a6d2232bb97c6e53e" +checksum = "ad011ae7447188e26e4a7dbca2fcd0fc186aa21ae5c86df0503ea44c78f9e469" dependencies = [ "base64", "bech32", @@ -473,9 +474,9 @@ dependencies = [ [[package]] name = "cosmwasm-vm" -version = "1.5.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ce190445de4a64d1b046f453b1f52e1e3df1fb481ad5cb039f82d7d47375cb9" +checksum = "90ebdf59ae3b9fe66d1eb845cf69aa537c77107d816452595c5796c332af061f" dependencies = [ "bitflags 1.3.2", "bytecheck", @@ -498,9 +499,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] @@ -599,44 +600,37 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.17" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-queue" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc6598521bb5a83d491e8c1fe51db7296019d2ca3cb93cc6c2a20369a4d78a2" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.18" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crypto-bigint" @@ -937,7 +931,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -948,7 +942,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1050,21 +1044,6 @@ dependencies = [ "memmap2 0.5.10", ] -[[package]] -name = "e2e-tests" -version = "0.1.0" -dependencies = [ - "anyhow", - "bash-rs", - "cosmwasm-schema", - "cosmwasm-std", - "home", - "serde", - "serde_json", - "thiserror", - "toml", -] - [[package]] name = "ecdsa" version = "0.16.9" @@ -1166,7 +1145,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1319,9 +1298,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", @@ -1358,9 +1337,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.22" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +checksum = "b553656127a00601c8ae5590fcfdc118e4083a7924b6cf4ffc1ea4b99dc429d7" dependencies = [ "bytes", "fnv", @@ -1583,18 +1562,18 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" dependencies = [ "wasm-bindgen", ] [[package]] name = "k256" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f01b677d82ef7a676aa37e099defd83a28e15687112cafdd112d60236b6115b" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" dependencies = [ "cfg-if", "ecdsa", @@ -1618,15 +1597,15 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.151" +version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "linux-raw-sys" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" @@ -1670,9 +1649,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "memmap2" @@ -1868,7 +1847,7 @@ version = "0.10.62" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "cfg-if", "foreign-types", "libc", @@ -1885,7 +1864,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1955,13 +1934,12 @@ checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" [[package]] name = "predicates" -version = "3.0.4" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dfc28575c2e3f19cb3c73b93af36460ae898d426eba6fc15b9bd2a5220758a0" +checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" dependencies = [ "anstyle", "difflib", - "itertools 0.11.0", "predicates-core", ] @@ -2022,9 +2000,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.71" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ "unicode-ident", ] @@ -2049,7 +2027,7 @@ dependencies = [ "itertools 0.11.0", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -2083,9 +2061,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -2288,11 +2266,11 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "errno", "libc", "linux-raw-sys", @@ -2316,11 +2294,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2418,15 +2396,15 @@ checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba" [[package]] name = "semver" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" [[package]] name = "serde" -version = "1.0.193" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] @@ -2462,13 +2440,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -2484,24 +2462,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" dependencies = [ "itoa", "ryu", "serde", ] -[[package]] -name = "serde_spanned" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" -dependencies = [ - "serde", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2597,9 +2566,9 @@ checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" [[package]] name = "smallvec" -version = "1.11.2" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e" [[package]] name = "socket2" @@ -2658,9 +2627,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.43" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", @@ -2696,21 +2665,21 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.12" +version = "0.12.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" +checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae" [[package]] name = "tempfile" -version = "3.8.1" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" dependencies = [ "cfg-if", "fastrand", "redox_syscall", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2721,22 +2690,22 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.51" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.51" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -2810,40 +2779,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "toml" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" -dependencies = [ - "indexmap 2.1.0", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - [[package]] name = "tower-service" version = "0.3.2" @@ -2869,7 +2804,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -2985,9 +2920,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2995,16 +2930,16 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", "wasm-bindgen-shared", ] @@ -3033,9 +2968,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.39" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" dependencies = [ "cfg-if", "js-sys", @@ -3045,9 +2980,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3055,22 +2990,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" [[package]] name = "wasmer" @@ -3245,9 +3180,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" dependencies = [ "js-sys", "wasm-bindgen", @@ -3459,15 +3394,6 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" -[[package]] -name = "winnow" -version = "0.5.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a4882e6b134d6c28953a387571f1acdd3496830d5e36c5e3a1075580ea641c" -dependencies = [ - "memchr", -] - [[package]] name = "winreg" version = "0.50.0" diff --git a/contracts/airdrop/Cargo.toml b/contracts/airdrop/Cargo.toml index 39ac9e9..9fa6451 100644 --- a/contracts/airdrop/Cargo.toml +++ b/contracts/airdrop/Cargo.toml @@ -5,9 +5,9 @@ edition = "2021" homepage = "https://nibiru.fi" repository = "https://github.com/NibiruChain/cw-nibiru" exclude = [ - # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. - "contract.wasm", - "hash.txt", + # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. + "contract.wasm", + "hash.txt", ] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -33,4 +33,5 @@ cw2 = { workspace = true } semver = "1" [dev-dependencies] -anyhow = { workspace = true } \ No newline at end of file +anyhow = { workspace = true } +serde-json-wasm = "1.0.0" diff --git a/contracts/airdrop/src/contract.rs b/contracts/airdrop/src/contract.rs index 09fafab..283e06c 100644 --- a/contracts/airdrop/src/contract.rs +++ b/contracts/airdrop/src/contract.rs @@ -37,12 +37,12 @@ pub fn instantiate( } let campaign = Campaign { - campaign_id: msg.campaign_id, campaign_name: msg.campaign_name, campaign_description: msg.campaign_description, owner: info.sender.clone(), managers: msg.managers, unallocated_amount: coin.amount, + is_active: true, }; CAMPAIGN.save(deps.storage, &campaign)?; @@ -92,6 +92,7 @@ pub fn execute( } ExecuteMsg::Claim {} => claim(deps, env, info), ExecuteMsg::Withdraw { amount } => withdraw(deps, env, info, amount), + ExecuteMsg::DeactivateCampaign {} => deactivate(deps, env, info), } } @@ -118,6 +119,10 @@ pub fn reward_users( return Err(StdError::generic_err("Unauthorized")); } + if !campaign.is_active { + return Err(StdError::generic_err("Campaign is not active")); + } + for req in requests { if campaign.unallocated_amount < req.amount { return Err(StdError::generic_err( @@ -168,6 +173,12 @@ pub fn claim( ) -> Result { let bond_denom = deps.querier.query_bonded_denom()?; + let campaign = CAMPAIGN.load(deps.storage)?; + + if !campaign.is_active { + return Err(StdError::generic_err("Campaign is not active")); + } + match USER_REWARDS.may_load(deps.storage, info.sender.clone())? { Some(user_reward) => { USER_REWARDS.remove(deps.storage, info.sender.clone()); @@ -186,6 +197,39 @@ pub fn claim( } } +pub fn deactivate( + deps: DepsMut, + env: Env, + info: MessageInfo, +) -> Result { + let mut campaign = CAMPAIGN + .load(deps.storage) + .map_err(|_| StdError::generic_err("Failed to load campaign data"))?; + + if campaign.owner != info.sender && !campaign.managers.contains(&info.sender) + { + return Err(StdError::generic_err("Unauthorized")); + } + + if !campaign.is_active { + return Ok(Response::new() + .add_attribute("method", "deactivate") + .add_attribute("message", "Campaign is already deactivated")); + } + + campaign.is_active = false; + CAMPAIGN.save(deps.storage, &campaign)?; + + let bond_denom = deps.querier.query_bonded_denom()?; + let own_balance: Uint128 = deps + .querier + .query_balance(&env.contract.address, bond_denom.clone()) + .map_err(|_| StdError::generic_err("Failed to query contract balance"))? + .amount; + + return withdraw(deps, env, info, own_balance); +} + /// Allow the contract owner to withdraw native tokens /// /// Ensures the requested amount is available in the contract balance. Transfers @@ -224,7 +268,26 @@ pub fn withdraw( }], })); - Ok(res) + // Update campaign unallocated amount + if amount > campaign.unallocated_amount { + CAMPAIGN.update( + deps.storage, + |mut campaign| -> StdResult { + campaign.unallocated_amount = Uint128::zero(); + Ok(campaign) + }, + )?; + } else { + CAMPAIGN.update( + deps.storage, + |mut campaign| -> StdResult { + campaign.unallocated_amount -= amount; + Ok(campaign) + }, + )?; + } + + return Ok(res); } #[cfg_attr(not(feature = "library"), entry_point)] @@ -249,6 +312,11 @@ pub fn query_user_reward( _env: Env, user_address: Addr, ) -> StdResult { + let campaign = CAMPAIGN.load(deps.storage)?; + if !campaign.is_active { + return Err(StdError::generic_err("Campaign is not active")); + } + match USER_REWARDS.load(deps.storage, user_address) { Ok(user_reward) => to_json_binary(&user_reward), Err(_) => Err(StdError::generic_err("User reward does not exist")), diff --git a/contracts/airdrop/src/msg.rs b/contracts/airdrop/src/msg.rs index 6ab5696..a34515a 100644 --- a/contracts/airdrop/src/msg.rs +++ b/contracts/airdrop/src/msg.rs @@ -28,6 +28,7 @@ pub enum ExecuteMsg { RewardUsers { requests: Vec }, Claim {}, Withdraw { amount: Uint128 }, + DeactivateCampaign {}, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] diff --git a/contracts/airdrop/src/state.rs b/contracts/airdrop/src/state.rs index b486153..76be4d5 100644 --- a/contracts/airdrop/src/state.rs +++ b/contracts/airdrop/src/state.rs @@ -5,13 +5,14 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct Campaign { - pub campaign_id: String, pub campaign_name: String, pub campaign_description: String, pub unallocated_amount: Uint128, pub owner: Addr, pub managers: Vec, + + pub is_active: bool, } pub const CAMPAIGN: Item = Item::new("campaign"); diff --git a/contracts/airdrop/src/tests/execute/reward_users.rs b/contracts/airdrop/src/tests/execute/reward_users.rs index 2ab8ee6..6abd3b0 100644 --- a/contracts/airdrop/src/tests/execute/reward_users.rs +++ b/contracts/airdrop/src/tests/execute/reward_users.rs @@ -1,4 +1,6 @@ -use crate::contract::{instantiate, reward_users}; +use crate::contract::{ + claim, deactivate, instantiate, query_user_reward, reward_users, +}; use crate::msg::{InstantiateMsg, RewardUserRequest, RewardUserResponse}; use crate::state::{Campaign, CAMPAIGN, USER_REWARDS}; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; @@ -69,13 +71,13 @@ fn test_reward_users_fully_allocated() { Campaign { owner: Addr::unchecked("owner"), unallocated_amount: Uint128::zero(), - campaign_id: "campaign_id".to_string(), campaign_name: "campaign_name".to_string(), campaign_description: "campaign_description".to_string(), managers: vec![ Addr::unchecked("manager1"), Addr::unchecked("manager2") ], + is_active: true, } ); @@ -158,13 +160,13 @@ fn test_reward_users_as_manager() { Campaign { owner: Addr::unchecked("owner"), unallocated_amount: Uint128::zero(), - campaign_id: "campaign_id".to_string(), campaign_name: "campaign_name".to_string(), campaign_description: "campaign_description".to_string(), managers: vec![ Addr::unchecked("manager1"), Addr::unchecked("manager2") ], + is_active: true, } ); @@ -229,3 +231,62 @@ fn test_fails_when_we_try_to_allocate_more_than_available() { Err(StdError::generic_err("Not enough funds in the campaign",)) ); } + +#[test] +fn test_fails_we_allocate_inactive() { + let mut deps = mock_dependencies(); + let env = mock_env(); + + instantiate( + deps.as_mut(), + env.clone(), + mock_info("owner", &coins(1000, "")), + InstantiateMsg { + campaign_id: "campaign_id".to_string(), + campaign_name: "campaign_name".to_string(), + campaign_description: "campaign_description".to_string(), + managers: vec![ + Addr::unchecked("manager1"), + Addr::unchecked("manager2"), + ], + }, + ) + .unwrap(); + + reward_users( + deps.as_mut(), + env.clone(), + mock_info("manager1", &[]), + vec![RewardUserRequest { + user_address: Addr::unchecked("user1"), + amount: Uint128::new(1), + }], + ) + .unwrap(); + + // deactivate campaign -- fail because not owner + let resp = deactivate(deps.as_mut(), env.clone(), mock_info("user1", &[])); + assert_eq!(resp, Err(StdError::generic_err("Unauthorized"))); + + deactivate(deps.as_mut(), env.clone(), mock_info("owner", &[])).unwrap(); + + let resp = reward_users( + deps.as_mut(), + env.clone(), + mock_info("manager1", &[]), + vec![RewardUserRequest { + user_address: Addr::unchecked("user2"), + amount: Uint128::new(1), + }], + ); + assert_eq!(resp, Err(StdError::generic_err("Campaign is not active",))); + + // user1 should not be able to claim anymore + let resp = claim(deps.as_mut(), env.clone(), mock_info("user1", &[])); + assert_eq!(resp, Err(StdError::generic_err("Campaign is not active"))); + + // user1 query reward says campaign is not active + let resp = + query_user_reward(deps.as_ref(), env.clone(), Addr::unchecked("user1")); + assert_eq!(resp, Err(StdError::generic_err("Campaign is not active"))); +} diff --git a/contracts/airdrop/src/tests/execute/withdraw.rs b/contracts/airdrop/src/tests/execute/withdraw.rs index f64d519..439dbef 100644 --- a/contracts/airdrop/src/tests/execute/withdraw.rs +++ b/contracts/airdrop/src/tests/execute/withdraw.rs @@ -1,9 +1,11 @@ -use crate::contract::{instantiate, withdraw}; +use crate::contract::{deactivate, instantiate, query_campaign, withdraw}; use crate::msg::InstantiateMsg; +use crate::state::Campaign; use cosmwasm_std::testing::{ mock_dependencies, mock_dependencies_with_balance, mock_env, mock_info, }; use cosmwasm_std::{coins, Addr, BankMsg, CosmosMsg, StdError, SubMsg, Uint128}; +use serde_json_wasm::from_slice; use std::vec; #[test] @@ -43,6 +45,75 @@ fn test_withdraw_ok() { amount: coins(1000, ""), }))] ); + + // check that the contract unallocated amount is zero + let binary_campaign = query_campaign(deps.as_ref(), env).unwrap(); + + let campaign: Campaign = from_slice(&binary_campaign).unwrap(); + assert_eq!(campaign.unallocated_amount, Uint128::zero()); +} + +#[test] +fn test_withdraw_less_than_total_amount() { + let mut deps = mock_dependencies_with_balance(&coins(1000, "")); + let env = mock_env(); + + instantiate( + deps.as_mut(), + env.clone(), + mock_info("owner", &coins(1500, "")), + InstantiateMsg { + campaign_id: "campaign_id".to_string(), + campaign_name: "campaign_name".to_string(), + campaign_description: "campaign_description".to_string(), + managers: vec![ + Addr::unchecked("manager1"), + Addr::unchecked("manager2"), + ], + }, + ) + .unwrap(); + + // try to withdraw + let resp = withdraw( + deps.as_mut(), + env.clone(), + mock_info("owner", &[]), + Uint128::new(500), + ) + .unwrap(); + + assert_eq!( + resp.messages, + vec![SubMsg::new(CosmosMsg::Bank(BankMsg::Send { + to_address: "owner".to_string(), + amount: coins(500, ""), + }))] + ); + + // check that the contract unallocated amount is zero + let binary_campaign = query_campaign(deps.as_ref(), env.clone()).unwrap(); + + let campaign: Campaign = from_slice(&binary_campaign).unwrap(); + assert_eq!(campaign.unallocated_amount, Uint128::new(1000)); + + // if i deactivate the campaign, everything should be withdrawn + let resp = + deactivate(deps.as_mut(), env.clone(), mock_info("owner", &[])).unwrap(); + + // We sent the remaining 1000 coins to the owner + assert_eq!( + resp.messages, + vec![SubMsg::new(CosmosMsg::Bank(BankMsg::Send { + to_address: "owner".to_string(), + amount: coins(1000, ""), + }))] + ); + + // check that the contract unallocated amount is zero + let binary_campaign = query_campaign(deps.as_ref(), env.clone()).unwrap(); + let campaign: Campaign = from_slice(&binary_campaign).unwrap(); + assert_eq!(campaign.unallocated_amount, Uint128::zero()); } #[test] diff --git a/contracts/airdrop/src/tests/instantiate.rs b/contracts/airdrop/src/tests/instantiate.rs index cf0348d..a2e127a 100644 --- a/contracts/airdrop/src/tests/instantiate.rs +++ b/contracts/airdrop/src/tests/instantiate.rs @@ -29,9 +29,9 @@ fn test_instantiate() { Addr::unchecked("manager2") ], unallocated_amount: Uint128::new(1000), - campaign_id: "campaign_id".to_string(), campaign_name: "campaign_name".to_string(), campaign_description: "campaign_description".to_string(), + is_active: true, } ); } diff --git a/contracts/airdrop/src/tests/query/campaign.rs b/contracts/airdrop/src/tests/query/campaign.rs index 36b80bc..b4c95ec 100644 --- a/contracts/airdrop/src/tests/query/campaign.rs +++ b/contracts/airdrop/src/tests/query/campaign.rs @@ -31,7 +31,6 @@ fn test_query_campaign() { assert_eq!( campaign, Campaign { - campaign_id: "campaign_id".to_string(), campaign_name: "campaign_name".to_string(), campaign_description: "campaign_description".to_string(), owner: Addr::unchecked("owner"), @@ -40,6 +39,7 @@ fn test_query_campaign() { Addr::unchecked("manager2") ], unallocated_amount: Uint128::new(1000), + is_active: true, } ); } diff --git a/contracts/core-token-vesting/src/contract.rs b/contracts/core-token-vesting/src/contract.rs index a73c965..b2a4add 100644 --- a/contracts/core-token-vesting/src/contract.rs +++ b/contracts/core-token-vesting/src/contract.rs @@ -1,9 +1,9 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - from_json, to_json_binary, Attribute, BankMsg, Binary, Coin, CosmosMsg, - Deps, DepsMut, Env, MessageInfo, Order, Response, StdError, StdResult, - Storage, Timestamp, Uint128, WasmMsg, + from_json, to_json_binary, Addr, Attribute, BankMsg, Binary, Coin, + CosmosMsg, Deps, DepsMut, Env, MessageInfo, Order, Response, StdError, + StdResult, Storage, Timestamp, Uint128, WasmMsg, }; use serde_json::to_string; @@ -13,10 +13,13 @@ use cw_storage_plus::Bound; use crate::errors::ContractError; use crate::msg::{ - Cw20HookMsg, ExecuteMsg, InstantiateMsg, QueryMsg, VestingAccountResponse, - VestingData, VestingSchedule, + Cw20HookMsg, ExecuteMsg, InstantiateMsg, QueryMsg, RewardUserRequest, + RewardUserResponse, VestingAccountResponse, VestingData, VestingSchedule, +}; +use crate::state::{ + denom_to_key, Campaign, DeregisterResult, VestingAccount, CAMPAIGN, + USER_REWARDS, VESTING_ACCOUNTS, }; -use crate::state::{denom_to_key, VestingAccount, VESTING_ACCOUNTS}; #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( @@ -68,19 +71,226 @@ pub fn execute( denom, vested_token_recipient, left_vesting_token_recipient, - } => deregister_vesting_account( + } => { + let response = deregister_vesting_account( + deps, + &env, + &info, + address, + &denom, + vested_token_recipient, + left_vesting_token_recipient, + ); + + if response.is_err() { + Err(response.err().unwrap().into()) + } else { + let result = response.unwrap(); + Ok(Response::new() + .add_messages(result.msgs) + .add_attributes(result.attributes)) + } + } + ExecuteMsg::Claim { denoms, recipient } => { + claim(deps, env, info, denoms, recipient) + } + ExecuteMsg::CreateCampaign { + vesting_schedule, + campaign_id, + campaign_name, + campaign_description, + managers, + } => create_campaign( deps, env, info, - address, + vesting_schedule, + campaign_id, + campaign_name, + campaign_description, + managers, + ), + ExecuteMsg::RewardUsers { + campaign_id, + requests, + } => reward_users(deps, env, info, campaign_id, requests), + ExecuteMsg::ClaimCampaign { campaign_id } => { + claim_campaign(deps, env, info, campaign_id) + } + ExecuteMsg::DeregisterVestingAccounts { + addresses, denom, vested_token_recipient, left_vesting_token_recipient, + } => deregister_vesting_accounts( + deps, + env, + info, + addresses, + &denom, + vested_token_recipient, + left_vesting_token_recipient, ), - ExecuteMsg::Claim { denoms, recipient } => { - claim(deps, env, info, denoms, recipient) + ExecuteMsg::DeactivateCampaign { campaign_id } => { + deactivate_campaign(deps, env, info, campaign_id) } + ExecuteMsg::Withdraw { + amount, + campaign_id, + } => withdraw(deps, env, info, amount, campaign_id), + } +} + +fn deactivate_campaign( + deps: DepsMut, + env: Env, + info: MessageInfo, + campaign_id: String, +) -> Result { + let mut campaign = CAMPAIGN + .load(deps.storage, campaign_id.clone()) + .map_err(|_| StdError::generic_err("Failed to load campaign data"))?; + + if campaign.owner != info.sender + && !campaign.managers.contains(&info.sender.to_string()) + { + return Err(StdError::generic_err("unauthorized").into()); + } + + if !campaign.is_active { + return Ok(Response::new() + .add_attribute("method", "deactivate") + .add_attribute("message", "Campaign is already deactivated")); + } + + campaign.is_active = false; + CAMPAIGN.save(deps.storage, campaign_id.clone(), &campaign)?; + + let bond_denom = deps.querier.query_bonded_denom()?; + let own_balance: Uint128 = deps + .querier + .query_balance(&env.contract.address, bond_denom.clone()) + .map_err(|_| StdError::generic_err("Failed to query contract balance"))? + .amount; + + return withdraw(deps, env, info, own_balance, campaign_id); +} + +fn claim_campaign( + _deps: DepsMut, + _env: Env, + _info: MessageInfo, + _campaign_id: String, +) -> Result { + todo!() +} + +fn reward_users( + deps: DepsMut, + env: Env, + info: MessageInfo, + campaign_id: String, + requests: Vec, +) -> Result { + let mut res = vec![]; + + let mut campaign = CAMPAIGN + .load(deps.storage, campaign_id.clone()) + .map_err(|_| StdError::generic_err("Failed to load campaign data"))?; + + if campaign.owner != info.sender + && !campaign.managers.contains(&info.sender.into_string()) + { + return Err(StdError::generic_err("Unauthorized").into()); + } + + if !campaign.is_active { + return Err(StdError::generic_err("Campaign is not active").into()); + } + + for req in requests { + if campaign.unallocated_amount < req.amount { + return Err(StdError::generic_err( + "Not enough funds in the campaign", + ) + .into()); + } + + match USER_REWARDS.may_load(deps.storage, req.user_address.clone())? { + Some(mut user_reward) => { + user_reward += req.amount; + USER_REWARDS.save( + deps.storage, + req.user_address.clone(), + &user_reward, + )?; + } + None => { + USER_REWARDS.save( + deps.storage, + req.user_address.clone(), + &req.amount, + )?; + } + }; + campaign.unallocated_amount -= req.amount; + CAMPAIGN.save(deps.storage, campaign_id.clone(), &campaign)?; + + res.push(RewardUserResponse { + user_address: req.user_address.clone(), + success: true, + error_msg: "".to_string(), + }); + } + + Ok(Response::new() + .add_attribute("method", "reward_users") + .set_data(to_json_binary(&res).unwrap())) +} + +fn create_campaign( + deps: DepsMut, + env: Env, + info: MessageInfo, + vesting_schedule: VestingSchedule, + campaign_id: String, + campaign_name: String, + campaign_description: String, + managers: Vec, +) -> Result { + if CAMPAIGN + .may_load(deps.storage, campaign_id.clone())? + .is_some() + { + return Err(StdError::generic_err("Campaign already exists").into()); + } + + if info.funds.len() != 1 { + return Err(StdError::generic_err("Only one coin is allowed").into()); } + + let bond_denom = deps.querier.query_bonded_denom()?; + let coin = info.funds.get(0).unwrap(); + if coin.denom != bond_denom { + return Err( + StdError::generic_err("Only native tokens are allowed").into() + ); + } + + let campaign = Campaign { + campaign_name: campaign_name, + campaign_id: campaign_id.clone(), + campaign_description: campaign_description, + owner: info.sender.into_string(), + managers: managers, + unallocated_amount: coin.amount, + is_active: true, + }; + CAMPAIGN.save(deps.storage, campaign_id.clone(), &campaign)?; + + Ok(Response::new() + .add_attribute("method", "create_campaign") + .add_attribute("campaign_id", campaign_id)) } fn register_vesting_account( @@ -127,17 +337,56 @@ fn register_vesting_account( ])) } -fn deregister_vesting_account( +fn deregister_vesting_accounts( deps: DepsMut, env: Env, info: MessageInfo, - address: String, - denom: Denom, + addresses: Vec, + denom: &Denom, vested_token_recipient: Option, left_vesting_token_recipient: Option, ) -> Result { + let mut messages: Vec = vec![]; + let mut attrs: Vec<(&str, String)> = vec![]; + + for address in addresses.iter() { + let response = deregister_vesting_account( + deps.clone(), + &env, + &info, + address.clone(), + denom, + vested_token_recipient.clone(), + left_vesting_token_recipient.clone(), + ); + + if response.is_err() { + let error_message = response.err().unwrap().to_string(); + attrs.extend(vec![ + ("action", "deregister_vesting_accounts".to_string()), + ("address", address.to_string()), + ("error", error_message), + ]); + } else { + let result = response.unwrap(); + messages.extend(result.msgs); + attrs.extend(result.attributes); + } + } + Ok(Response::new().add_messages(messages).add_attributes(attrs)) +} + +fn deregister_vesting_account<'a>( + deps: DepsMut, + env: &Env, + info: &MessageInfo, + address: String, + denom: &Denom, + vested_token_recipient: Option, + left_vesting_token_recipient: Option, +) -> Result, ContractError> { let denom_key = denom_to_key(denom.clone()); - let sender = info.sender; + let sender = info.sender.clone(); let mut messages: Vec = vec![]; @@ -195,14 +444,19 @@ fn deregister_vesting_account( messages.push(msg_send); } - Ok(Response::new().add_messages(messages).add_attributes(vec![ - ("action", "deregister_vesting_account"), - ("address", address.as_str()), - ("vesting_denom", &to_string(&account.vesting_denom).unwrap()), - ("vesting_amount", &account.vesting_amount.to_string()), - ("vested_amount", &vested_amount.to_string()), - ("left_vesting_amount", &left_vesting_amount.to_string()), - ])) + let result = DeregisterResult { + msgs: messages, + attributes: vec![ + ("action", "deregister_vesting_account".to_string()), + ("address", address), + ("vesting_denom", to_string(&account.vesting_denom).unwrap()), + ("vesting_amount", account.vesting_amount.to_string()), + ("vested_amount", vested_amount.to_string()), + ("left_vesting_amount", left_vesting_amount.to_string()), + ], + }; + + Ok(result) } fn claim( @@ -397,6 +651,94 @@ fn vesting_account( Ok(VestingAccountResponse { address, vestings }) } +/// Allow the contract owner to withdraw native tokens +/// +/// Ensures the requested amount is available in the contract balance. Transfers +/// tokens to the contract owner's account. +pub fn withdraw( + deps: DepsMut, + env: Env, + info: MessageInfo, + amount: Uint128, + campaign_id: String, +) -> Result { + let campaign = CAMPAIGN.load(deps.storage, campaign_id)?; + + if info.sender != campaign.owner { + return Err( + StdError::generic_err("Only contract owner can withdraw").into() + ); + } + + let bond_denom = deps.querier.query_bonded_denom()?; + + let own_balance: Uint128 = deps + .querier + .query_balance(env.contract.address, bond_denom.clone()) + .map_err(|_| { + ContractError::Std(StdError::generic_err( + "Failed to query contract balance", + )) + })? + .amount; + + if amount > own_balance { + return Err( + StdError::generic_err("Not enough funds in the contract").into() + ); + } + + let res = Response::new() + .add_attribute("method", "withdraw") + .add_message(CosmosMsg::Bank(BankMsg::Send { + to_address: info.sender.to_string(), + amount: vec![Coin { + denom: bond_denom.clone(), + amount, + }], + })); + + // Update campaign unallocated amount + if amount > campaign.unallocated_amount { + let update_result = CAMPAIGN.update( + deps.storage, + campaign_id, + |mut campaign| -> StdResult { + if let Some(mut campaign) = campaign { + campaign.unallocated_amount = Uint128::zero(); + Ok(campaign) + } else { + Err(StdError::generic_err("Campaign not found")) + } + }, + ); + + if let Err(e) = update_result { + return Err(e.into()); + } + } else { + let update_result = CAMPAIGN.update( + deps.storage, + campaign_id, + |mut campaign| -> StdResult { + if let Some(mut campaign) = campaign { + campaign.unallocated_amount -= amount; + Ok(campaign) + } else { + Err(StdError::generic_err("Campaign not found")) + } + }, + ); + + if let Err(e) = update_result { + return Err(e.into()); + } + } + Err(StdError::generic_err("Campaign not found"))?; + + return Ok(res); +} + #[cfg(test)] pub mod tests { diff --git a/contracts/core-token-vesting/src/errors.rs b/contracts/core-token-vesting/src/errors.rs index be9b992..336a827 100644 --- a/contracts/core-token-vesting/src/errors.rs +++ b/contracts/core-token-vesting/src/errors.rs @@ -13,6 +13,7 @@ pub enum ContractError { #[error(transparent)] Overflow(#[from] cosmwasm_std::OverflowError), + } #[derive(thiserror::Error, Debug, PartialEq)] diff --git a/contracts/core-token-vesting/src/msg.rs b/contracts/core-token-vesting/src/msg.rs index ca496fb..e10dff1 100644 --- a/contracts/core-token-vesting/src/msg.rs +++ b/contracts/core-token-vesting/src/msg.rs @@ -46,6 +46,82 @@ pub enum ExecuteMsg { denoms: Vec, recipient: Option, }, + + /// Create campaign to reward users with vested tokens + /// Args: + /// - vesting_schedule: VestingSchedule: The vesting schedule of the account. + /// - campaign_id: String: The unique identifier of the campaign. + /// - campaign_name: String: The name of the campaign. + /// - campaign_description: String: The description of the campaign. + /// - managers: Vec: The list of addresses that can manage the campaign (reward users). + CreateCampaign { + vesting_schedule: VestingSchedule, + + campaign_id: String, + campaign_name: String, + campaign_description: String, + managers: Vec, + }, + + /// Reward users with tokens + /// Args: + /// - campaign_id: String: The unique identifier of the campaign. + /// - requests: Vec: The list of reward requests. + RewardUsers { + campaign_id: String, + requests: Vec, + }, + + /// Deregister vesting accounts + /// Args: + /// - address: Vec: The list of addresses of the vesting accounts to be deregistered. + /// - denoms: Vec: The list of denoms of the vesting accounts to be deregistered. + /// - vested_token_recipient: Option: Bech 32 address that will receive the vested + /// tokens after deregistration. If None, tokens are received by the owner address. + DeregisterVestingAccounts { + addresses: Vec, + denom: Denom, + vested_token_recipient: Option, + left_vesting_token_recipient: Option, + }, + + /// Claim campaign: A user can claim vested tokens from a campaign and this + /// will register a vesting account for the user. + /// Args: + /// - campaign_id: String: The unique identifier of the campaign. + ClaimCampaign { + campaign_id: String, + }, + + /// Deactivate campaign: The campaign owner can deactivate the campaign. + /// All the unallocated tokens will be returned to the owner. + /// Args: + /// - campaign_id: String: The unique identifier of the campaign. + DeactivateCampaign { + campaign_id: String, + }, + + /// Withdraw: The campaign owner can withdraw unallocated tokens from the campaign. + /// Args: + /// - campaign_id: String: The unique identifier of the campaign. + /// - amount: Uint128: The amount of tokens to be withdrawn. + Withdraw { + campaign_id: String, + amount: Uint128, + }, +} + +#[cw_serde] +pub struct RewardUserRequest { + pub user_address: String, + pub amount: Uint128, +} + +#[cw_serde] +pub struct RewardUserResponse { + pub user_address: String, + pub success: bool, + pub error_msg: String, } #[cw_serde] diff --git a/contracts/core-token-vesting/src/state.rs b/contracts/core-token-vesting/src/state.rs index 729586a..9eddd58 100644 --- a/contracts/core-token-vesting/src/state.rs +++ b/contracts/core-token-vesting/src/state.rs @@ -1,10 +1,12 @@ use cosmwasm_schema::cw_serde; use crate::msg::VestingSchedule; -use cosmwasm_std::Uint128; +use cosmwasm_std::{CosmosMsg, Uint128}; use cw20::Denom; use cw_storage_plus::Map; +pub const CAMPAIGN: Map = Map::new("campaign"); +pub const USER_REWARDS: Map = Map::new("user_rewards"); pub const VESTING_ACCOUNTS: Map<(&str, &str), VestingAccount> = Map::new("vesting_accounts"); @@ -18,6 +20,24 @@ pub struct VestingAccount { pub claimed_amount: Uint128, } +#[cw_serde] +pub struct Campaign { + pub campaign_id: String, + pub campaign_name: String, + pub campaign_description: String, + + pub unallocated_amount: Uint128, + pub owner: String, + pub managers: Vec, + + pub is_active: bool, +} + +pub struct DeregisterResult<'a> { + pub msgs: Vec, + pub attributes: Vec<(&'a str, String)>, +} + pub fn denom_to_key(denom: Denom) -> String { match denom { Denom::Cw20(addr) => format!("cw20-{}", addr),