diff --git a/Cargo.lock b/Cargo.lock index 1964793..6f3a464 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,9 +15,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "base16ct" @@ -25,11 +25,17 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" -version = "0.13.1" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" [[package]] name = "base64ct" @@ -61,6 +67,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128a44527fc0d6abf05f9eda748b9027536e12dff93f5acc8449f51583309350" + [[package]] name = "byteorder" version = "1.4.3" @@ -69,9 +81,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cfg-if" @@ -81,37 +93,37 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "const-oid" -version = "0.9.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" [[package]] name = "cosmwasm-crypto" -version = "1.2.5" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75836a10cb9654c54e77ee56da94d592923092a10b369cdb0dbd56acefc16340" +checksum = "1ca101fbf2f76723711a30ea3771ef312ec3ec254ad021b237871ed802f9f175" dependencies = [ "digest 0.10.7", "ed25519-zebra", - "k256", + "k256 0.13.1", "rand_core 0.6.4", "thiserror", ] [[package]] name = "cosmwasm-derive" -version = "1.2.5" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c9f7f0e51bfc7295f7b2664fe8513c966428642aa765dad8a74acdab5e0c773" +checksum = "c73d2dd292f60e42849d2b07c03d809cf31e128a4299a805abd6d24553bcaaf5" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.2.5" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f00b363610218eea83f24bbab09e1a7c3920b79f068334fdfcc62f6129ef9fc" +checksum = "6ce34a08020433989af5cc470104f6bd22134320fe0221bd8aeb919fd5ec92d5" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -122,9 +134,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.5" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae38f909b2822d32b275c9e2db9728497aa33ffe67dd463bc67c6a3b7092785c" +checksum = "96694ec781a7dd6dea1f968a2529ade009c21ad999c88b5f53d6cc495b3b96f7" dependencies = [ "proc-macro2", "quote", @@ -133,11 +145,12 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.5" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a49b85345e811c8e80ec55d0d091e4fcb4f00f97ab058f9be5f614c444a730cb" +checksum = "2a44d3f9c25b2f864737c6605a98f2e4675d53fd8bbc7cf4d7c02475661a793d" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -146,16 +159,15 @@ dependencies = [ "schemars", "serde", "serde-json-wasm", - "sha2 0.10.6", + "sha2 0.10.7", "thiserror", - "uint", ] [[package]] name = "cosmwasm-storage" -version = "1.2.5" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3737a3aac48f5ed883b5b73bfb731e77feebd8fc6b43419844ec2971072164d" +checksum = "ab544dfcad7c9e971933d522d99ec75cc8ddfa338854bb992b092e11bcd7e818" dependencies = [ "cosmwasm-std", "serde", @@ -163,24 +175,30 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] [[package]] -name = "crunchy" -version = "0.2.2" +name = "crypto-bigint" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] [[package]] name = "crypto-bigint" -version = "0.4.9" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" dependencies = [ "generic-array", "rand_core 0.6.4", @@ -213,13 +231,13 @@ dependencies = [ [[package]] name = "cw-controllers" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91440ce8ec4f0642798bc8c8cb6b9b53c1926c6dadaf0eed267a5145cd529071" +checksum = "d5d8edce4b78785f36413f67387e4be7d0cb7d032b5d4164bcc024f9c3f3f2ea" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus 1.0.1", + "cw-storage-plus 1.1.0", "cw-utils 1.0.1", "schemars", "serde", @@ -228,15 +246,15 @@ dependencies = [ [[package]] name = "cw-ics20" -version = "1.0.2" +version = "1.0.8" dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw-controllers", "cw-multi-test", - "cw-storage-plus 1.0.1", + "cw-storage-plus 1.1.0", "cw-utils 0.16.0", - "cw2 1.0.1", + "cw2 1.1.0", "cw20", "cw20-ics20-msg", "oraiswap", @@ -248,17 +266,17 @@ dependencies = [ [[package]] name = "cw-multi-test" -version = "0.16.4" +version = "0.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a18afd2e201221c6d72a57f0886ef2a22151bbc9e6db7af276fde8a91081042" +checksum = "127c7bb95853b8e828bdab97065c81cb5ddc20f7339180b61b2300565aaa99d1" dependencies = [ "anyhow", "cosmwasm-std", - "cw-storage-plus 1.0.1", + "cw-storage-plus 1.1.0", "cw-utils 1.0.1", "derivative", "itertools", - "k256", + "k256 0.11.6", "prost", "schemars", "serde", @@ -278,9 +296,9 @@ dependencies = [ [[package]] name = "cw-storage-plus" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053a5083c258acd68386734f428a5a171b29f7d733151ae83090c6fcc9417ffa" +checksum = "3f0e92a069d62067f3472c62e30adedb4cab1754725c0f2a682b3128d2bf3c79" dependencies = [ "cosmwasm-std", "schemars", @@ -310,7 +328,7 @@ checksum = "c80e93d1deccb8588db03945016a292c3c631e6325d349ebb35d2db6f4f946f7" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw2 1.0.1", + "cw2 1.1.0", "schemars", "semver", "serde", @@ -332,22 +350,23 @@ dependencies = [ [[package]] name = "cw2" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb70cee2cf0b4a8ff7253e6bc6647107905e8eb37208f87d54f67810faa62f8" +checksum = "29ac2dc7a55ad64173ca1e0a46697c31b7a5c51342f55a1e84a724da4eb99908" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus 1.0.1", + "cw-storage-plus 1.1.0", "schemars", "serde", + "thiserror", ] [[package]] name = "cw20" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91666da6c7b40c8dd5ff94df655a28114efc10c79b70b4d06f13c31e37d60609" +checksum = "011c45920f8200bd5d32d4fe52502506f64f2f75651ab408054d4cfc75ca3a9b" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -358,15 +377,15 @@ dependencies = [ [[package]] name = "cw20-base" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afcd279230b08ed8afd8be5828221622bd5b9ce25d0b01d58bad626c6ce0169c" +checksum = "0b3ad456059901a36cfa68b596d85d579c3df2b797dae9950dc34c27e14e995f" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus 1.0.1", + "cw-storage-plus 1.1.0", "cw-utils 1.0.1", - "cw2 1.0.1", + "cw2 1.1.0", "cw20", "schemars", "semver", @@ -381,7 +400,7 @@ dependencies = [ "bech32", "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus 1.0.1", + "cw-storage-plus 1.1.0", "cw20", "oraiswap", "schemars", @@ -398,6 +417,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "derivative" version = "2.2.0" @@ -425,15 +454,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", + "const-oid", "crypto-common", "subtle", ] [[package]] name = "dyn-clone" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" +checksum = "23d2f3407d9a573d666de4b5bdf10569d73ca9478087346697dcbae6244bfbcd" [[package]] name = "ecdsa" @@ -441,10 +471,24 @@ version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + +[[package]] +name = "ecdsa" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +dependencies = [ + "der 0.7.8", + "digest 0.10.7", + "elliptic-curve 0.13.5", + "rfc6979 0.4.0", + "signature 2.1.0", + "spki 0.7.2", ] [[package]] @@ -464,9 +508,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "elliptic-curve" @@ -474,16 +518,35 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ - "base16ct", - "crypto-bigint", - "der", + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", "digest 0.10.7", - "ff", + "ff 0.12.1", "generic-array", - "group", - "pkcs8", + "group 0.12.1", + "pkcs8 0.9.0", "rand_core 0.6.4", - "sec1", + "sec1 0.3.0", + "subtle", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.3", + "digest 0.10.7", + "ff 0.13.0", + "generic-array", + "group 0.13.0", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "sec1 0.7.3", "subtle", "zeroize", ] @@ -498,6 +561,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "forward_ref" version = "1.0.0" @@ -512,13 +585,14 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", @@ -531,7 +605,18 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ - "ff", + "ff 0.12.1", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", "rand_core 0.6.4", "subtle", ] @@ -571,9 +656,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "k256" @@ -582,22 +667,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" dependencies = [ "cfg-if", - "ecdsa", - "elliptic-curve", - "sha2 0.10.6", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.7", +] + +[[package]] +name = "k256" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +dependencies = [ + "cfg-if", + "ecdsa 0.16.8", + "elliptic-curve 0.13.5", + "once_cell", + "sha2 0.10.7", + "signature 2.1.0", ] [[package]] name = "libc" -version = "0.2.144" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "opaque-debug" @@ -628,15 +727,25 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" dependencies = [ - "der", - "spki", + "der 0.6.1", + "spki 0.6.0", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.8", + "spki 0.7.2", ] [[package]] name = "proc-macro2" -version = "1.0.59" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] @@ -687,9 +796,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.28" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -715,22 +824,32 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" dependencies = [ - "crypto-bigint", + "crypto-bigint 0.4.9", "hmac", "zeroize", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "schemars" -version = "0.8.12" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" +checksum = "1f7b0ce13155372a76ee2e1c5ffba1fe61ede73fbea5630d61eee6fac4929c0c" dependencies = [ "dyn-clone", "schemars_derive", @@ -740,9 +859,9 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.12" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c" +checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c" dependencies = [ "proc-macro2", "quote", @@ -756,25 +875,39 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ - "base16ct", - "der", + "base16ct 0.1.1", + "der 0.6.1", + "generic-array", + "pkcs8 0.9.0", + "subtle", + "zeroize", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.8", "generic-array", - "pkcs8", + "pkcs8 0.10.2", "subtle", "zeroize", ] [[package]] name = "semver" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.163" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] @@ -790,13 +923,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.163" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.17", + "syn 2.0.37", ] [[package]] @@ -812,9 +945,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -836,9 +969,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", @@ -855,6 +988,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + [[package]] name = "spki" version = "0.6.0" @@ -862,14 +1005,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" dependencies = [ "base64ct", - "der", + "der 0.6.1", ] [[package]] -name = "static_assertions" -version = "1.1.0" +name = "spki" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der 0.7.8", +] [[package]] name = "subtle" @@ -890,9 +1037,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.17" +version = "2.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45b6ddbb36c5b969c182aec3c4a0bce7df3fbad4b77114706a49aacc80567388" +checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" dependencies = [ "proc-macro2", "quote", @@ -901,47 +1048,35 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", - "syn 2.0.17", + "syn 2.0.37", ] [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "uint" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" -version = "1.0.9" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "version_check" diff --git a/contracts/cw-ics20-latest/Cargo.toml b/contracts/cw-ics20-latest/Cargo.toml index 7ebf4a7..756b05d 100644 --- a/contracts/cw-ics20-latest/Cargo.toml +++ b/contracts/cw-ics20-latest/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw-ics20" -version = "1.0.2" +version = "1.0.8" authors = ["Ethan Frey , Oraichain Labs"] edition = "2021" description = "IBC Enabled contracts that receives CW20 tokens and sends them over ICS20 to a remote chain" @@ -24,7 +24,7 @@ cw2 = "1.0.1" cw20 = "1.0.1" cw20-ics20-msg = { path = "../../packages/cw20-ics20-msg" } oraiswap = "1.0.1" -cosmwasm-std = { version = "1.1.0", features = ["stargate"] } +cosmwasm-std = { version = "1.1.9", features = ["stargate", "ibc3"] } cw-storage-plus = "1.0.1" cw-controllers = "1.0.1" schemars = "0.8.1" diff --git a/contracts/cw-ics20-latest/src/contract.rs b/contracts/cw-ics20-latest/src/contract.rs index 19eb40a..a04f6a9 100644 --- a/contracts/cw-ics20-latest/src/contract.rs +++ b/contracts/cw-ics20-latest/src/contract.rs @@ -1,8 +1,10 @@ +use std::vec; + #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ from_binary, to_binary, Addr, Binary, Deps, DepsMut, Empty, Env, IbcEndpoint, IbcQuery, - MessageInfo, Order, PortIdResponse, Response, StdResult, Storage, + MessageInfo, Order, PortIdResponse, Response, StdError, StdResult, Storage, Uint128, }; use cw2::set_contract_version; use cw20::{Cw20Coin, Cw20ReceiveMsg}; @@ -16,15 +18,16 @@ use crate::ibc::{ build_ibc_send_packet, collect_fee_msgs, parse_voucher_denom, process_deduct_fee, }; use crate::msg::{ - AllowMsg, AllowedInfo, AllowedResponse, ChannelResponse, ConfigResponse, DeletePairMsg, - ExecuteMsg, InitMsg, ListAllowedResponse, ListChannelsResponse, ListMappingResponse, - MigrateMsg, PairQuery, PortResponse, QueryMsg, RelayerFeeResponse, TransferBackMsg, - UpdatePairMsg, + AllowMsg, AllowedInfo, AllowedResponse, ChannelResponse, ChannelWithKeyResponse, + ConfigResponse, DeletePairMsg, ExecuteMsg, InitMsg, ListAllowedResponse, ListChannelsResponse, + ListMappingResponse, MigrateMsg, PairQuery, PortResponse, QueryMsg, RelayerFeeResponse, + TransferBackMsg, UpdatePairMsg, }; use crate::state::{ - get_key_ics20_ibc_denom, ics20_denoms, reduce_channel_balance, AllowInfo, Config, - MappingMetadata, RelayerFee, TokenFee, ADMIN, ALLOW_LIST, CHANNEL_INFO, CHANNEL_REVERSE_STATE, - CONFIG, RELAYER_FEE, RELAYER_FEE_ACCUMULATOR, TOKEN_FEE, TOKEN_FEE_ACCUMULATOR, + get_key_ics20_ibc_denom, ics20_denoms, increase_channel_balance, override_channel_balance, + reduce_channel_balance, AllowInfo, Config, MappingMetadata, RelayerFee, ReplyArgs, TokenFee, + ADMIN, ALLOW_LIST, CHANNEL_INFO, CHANNEL_REVERSE_STATE, CONFIG, RELAYER_FEE, + RELAYER_FEE_ACCUMULATOR, REPLY_ARGS, SINGLE_STEP_REPLY_ARGS, TOKEN_FEE, TOKEN_FEE_ACCUMULATOR, }; use cw20_ics20_msg::amount::{convert_local_to_remote, Amount}; use cw_utils::{maybe_addr, nonpayable, one_coin}; @@ -108,7 +111,151 @@ pub fn execute( relayer_fee_receiver, relayer_fee, ), + // self-called msgs for ibc_packet_receive + ExecuteMsg::IncreaseChannelBalanceIbcReceive { + dest_channel_id, + ibc_denom, + amount, + local_receiver, + } => handle_increase_channel_balance_ibc_receive( + deps, + info.sender, + env.contract.address, + dest_channel_id, + ibc_denom, + amount, + local_receiver, + ), + ExecuteMsg::ReduceChannelBalanceIbcReceive { + src_channel_id, + ibc_denom, + amount, + local_receiver, + } => handle_reduce_channel_balance_ibc_receive( + deps.storage, + info.sender, + env.contract.address, + src_channel_id, + ibc_denom, + amount, + local_receiver, + ), + ExecuteMsg::OverrideChannelBalance { + channel_id, + ibc_denom, + outstanding, + total_sent, + } => handle_override_channel_balance( + deps, + info, + channel_id, + ibc_denom, + outstanding, + total_sent, + ), + } +} + +pub fn is_caller_contract(caller: Addr, contract_addr: Addr) -> StdResult<()> { + if caller.ne(&contract_addr) { + return Err(cosmwasm_std::StdError::generic_err( + "Caller is not the contract itself!", + )); } + Ok(()) +} + +pub fn handle_override_channel_balance( + deps: DepsMut, + info: MessageInfo, + channel_id: String, + ibc_denom: String, + outstanding: Uint128, + total_sent: Option, +) -> Result { + ADMIN.assert_admin(deps.as_ref(), &info.sender)?; + override_channel_balance( + deps.storage, + &channel_id, + &ibc_denom, + outstanding, + total_sent, + )?; + Ok(Response::new().add_attributes(vec![ + ("action", "override_channel_balance"), + ("channel_id", &channel_id), + ("ibc_denom", &ibc_denom), + ("new_outstanding", &outstanding.to_string()), + ("total_sent", &total_sent.unwrap_or_default().to_string()), + ])) +} + +pub fn handle_increase_channel_balance_ibc_receive( + deps: DepsMut, + caller: Addr, + contract_addr: Addr, + dst_channel_id: String, + ibc_denom: String, + remote_amount: Uint128, + local_receiver: String, +) -> Result { + is_caller_contract(caller, contract_addr)?; + // will have to increase balance here because if this tx fails then it will be reverted, and the balance on the remote chain will also be reverted + increase_channel_balance( + deps.storage, + &dst_channel_id, + &ibc_denom, + remote_amount.clone(), + )?; + // we need to save the data to update the balances in reply + let reply_args = ReplyArgs { + channel: dst_channel_id.clone(), + denom: ibc_denom.clone(), + amount: remote_amount.clone(), + local_receiver: local_receiver.clone(), + }; + REPLY_ARGS.save(deps.storage, &reply_args)?; + Ok(Response::default().add_attributes(vec![ + ("action", "increase_channel_balance_ibc_receive"), + ("channel_id", dst_channel_id.as_str()), + ("ibc_denom", ibc_denom.as_str()), + ("amount", remote_amount.to_string().as_str()), + ("local_receiver", local_receiver.as_str()), + ])) +} + +pub fn handle_reduce_channel_balance_ibc_receive( + storage: &mut dyn Storage, + caller: Addr, + contract_addr: Addr, + src_channel_id: String, + ibc_denom: String, + remote_amount: Uint128, + local_receiver: String, +) -> Result { + is_caller_contract(caller, contract_addr)?; + // because we are transferring back, we reduce the channel's balance + reduce_channel_balance(storage, src_channel_id.as_str(), &ibc_denom, remote_amount) + .map_err(|err| StdError::generic_err(err.to_string()))?; + + // keep track of the single-step reply since we need ibc data to undo reducing channel balance and local data for refunding. + // we use a different item to not override REPLY_ARGS + SINGLE_STEP_REPLY_ARGS.save( + storage, + &ReplyArgs { + channel: src_channel_id.to_string(), + denom: ibc_denom.clone(), + amount: remote_amount, + local_receiver: local_receiver.to_string(), + }, + )?; + Ok(Response::default().add_attributes(vec![ + ("action", "reduce_channel_balance_ibc_receive"), + ("channel_id", src_channel_id.as_str()), + ("ibc_denom", ibc_denom.as_str()), + ("amount", remote_amount.to_string().as_str()), + ("local_receiver", local_receiver.as_str()), + ])) } pub fn update_config( @@ -351,7 +498,6 @@ pub fn execute_transfer_back_to_remote_chain( &msg.local_channel_id, &ibc_denom, amount_remote, - false, )?; // prepare ibc message @@ -519,7 +665,10 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { QueryMsg::Port {} => to_binary(&query_port(deps)?), QueryMsg::ListChannels {} => to_binary(&query_list(deps)?), - QueryMsg::Channel { id, forward } => to_binary(&query_channel(deps, id, forward)?), + QueryMsg::Channel { id } => to_binary(&query_channel(deps, id)?), + QueryMsg::ChannelWithKey { channel_id, denom } => { + to_binary(&query_channel_with_key(deps, channel_id, denom)?) + } QueryMsg::Config {} => to_binary(&query_config(deps)?), QueryMsg::Allowed { contract } => to_binary(&query_allowed(deps, contract)?), QueryMsg::ListAllowed { @@ -558,7 +707,7 @@ fn query_list(deps: Deps) -> StdResult { } // make public for ibc tests -pub fn query_channel(deps: Deps, id: String, _forward: Option) -> StdResult { +pub fn query_channel(deps: Deps, id: String) -> StdResult { let info = CHANNEL_INFO.load(deps.storage, &id)?; // this returns Vec<(outstanding, total)> let channel_state = CHANNEL_REVERSE_STATE; @@ -584,6 +733,28 @@ pub fn query_channel(deps: Deps, id: String, _forward: Option) -> StdResul }) } +pub fn query_channel_with_key( + deps: Deps, + channel_id: String, + denom: String, +) -> StdResult { + let info = CHANNEL_INFO.load(deps.storage, &channel_id)?; + // this returns Vec<(outstanding, total)> + let (balance, total_sent) = CHANNEL_REVERSE_STATE + .load(deps.storage, (&channel_id, &denom)) + .map(|channel_state| { + let outstanding = Amount::from_parts(denom.clone(), channel_state.outstanding); + let total = Amount::from_parts(denom, channel_state.total_sent); + (outstanding, total) + })?; + + Ok(ChannelWithKeyResponse { + info, + balance, + total_sent, + }) +} + fn query_config(deps: Deps) -> StdResult { let cfg = CONFIG.load(deps.storage)?; let admin = ADMIN.get(deps)?.unwrap_or_else(|| Addr::unchecked("")); @@ -770,7 +941,6 @@ mod test { mock_env(), QueryMsg::Channel { id: "channel-3".to_string(), - forward: Some(true), }, ) .unwrap(); @@ -784,7 +954,6 @@ mod test { mock_env(), QueryMsg::Channel { id: "channel-10".to_string(), - forward: Some(true), }, ) .unwrap_err(); @@ -1218,6 +1387,7 @@ mod test { #[test] fn proper_checks_on_execute_native_transfer_back_to_remote() { // arrange + let relayer = Addr::unchecked("relayer"); let remote_channel = "channel-5"; let remote_address = "cosmos1603j3e4juddh7cuhfquxspl0p0nsun046us7n0"; let custom_addr = "custom-addr"; @@ -1230,6 +1400,7 @@ mod test { }; let cw20_raw_denom = token_addr.as_str(); let local_channel = "channel-1234"; + let ibc_denom = get_key_ics20_ibc_denom("wasm.cosmos2contract", local_channel, denom); let ratio = Ratio { nominator: 1, denominator: 10, @@ -1275,7 +1446,6 @@ mod test { // insufficient funds case because we need to receive from remote chain first let info = mock_info(cw20_raw_denom, &[]); let res = execute(deps.as_mut(), mock_env(), info.clone(), msg.clone()); - println!("res: {:?}", res); assert_eq!( res.unwrap_err(), ContractError::NoSuchChannelState { @@ -1298,8 +1468,21 @@ mod test { mock_receive_packet(remote_channel, local_channel, amount, denom, custom_addr); // receive some tokens. Assume that the function works perfectly because the test case is elsewhere - let ibc_msg = IbcPacketReceiveMsg::new(recv_packet.clone()); + let ibc_msg = IbcPacketReceiveMsg::new(recv_packet.clone(), relayer); ibc_packet_receive(deps.as_mut(), mock_env(), ibc_msg).unwrap(); + // need to trigger increase channel balance because it is executed through submsg + execute( + deps.as_mut(), + mock_env(), + mock_info(mock_env().contract.address.as_str(), &[]), + ExecuteMsg::IncreaseChannelBalanceIbcReceive { + dest_channel_id: local_channel.to_string(), + ibc_denom: ibc_denom.clone(), + amount: Uint128::from(amount), + local_receiver: custom_addr.to_string(), + }, + ) + .unwrap(); // error cases // revert transfer state to correct state @@ -1360,7 +1543,7 @@ mod test { } // check new channel state after reducing balance - let chan = query_channel(deps.as_ref(), local_channel.into(), None).unwrap(); + let chan = query_channel(deps.as_ref(), local_channel.into()).unwrap(); assert_eq!( chan.balances, vec![Amount::native( @@ -1408,6 +1591,7 @@ mod test { #[test] fn proper_checks_on_execute_cw20_transfer_back_to_remote() { // arrange + let relayer = Addr::unchecked("relayer"); let remote_channel = "channel-5"; let remote_address = "cosmos1603j3e4juddh7cuhfquxspl0p0nsun046us7n0"; let custom_addr = "custom-addr"; @@ -1419,6 +1603,7 @@ mod test { }; let cw20_raw_denom = original_sender; let local_channel = "channel-1234"; + let ibc_denom = get_key_ics20_ibc_denom("wasm.cosmos2contract", local_channel, denom); let ratio = Ratio { nominator: 1, denominator: 10, @@ -1464,7 +1649,7 @@ mod test { res.unwrap_err(), ContractError::NoSuchChannelState { id: local_channel.to_string(), - denom: get_key_ics20_ibc_denom("wasm.cosmos2contract", local_channel, denom) + denom: ibc_denom.clone() } ); @@ -1482,8 +1667,21 @@ mod test { mock_receive_packet(remote_channel, local_channel, amount, denom, custom_addr); // receive some tokens. Assume that the function works perfectly because the test case is elsewhere - let ibc_msg = IbcPacketReceiveMsg::new(recv_packet.clone()); + let ibc_msg = IbcPacketReceiveMsg::new(recv_packet.clone(), relayer); ibc_packet_receive(deps.as_mut(), mock_env(), ibc_msg).unwrap(); + // need to trigger increase channel balance because it is executed through submsg + execute( + deps.as_mut(), + mock_env(), + mock_info(mock_env().contract.address.as_str(), &[]), + ExecuteMsg::IncreaseChannelBalanceIbcReceive { + dest_channel_id: local_channel.to_string(), + ibc_denom: ibc_denom.clone(), + amount: Uint128::from(amount), + local_receiver: custom_addr.to_string(), + }, + ) + .unwrap(); // error cases // revert transfer state to correct state @@ -1532,7 +1730,7 @@ mod test { } // check new channel state after reducing balance - let chan = query_channel(deps.as_ref(), local_channel.into(), None).unwrap(); + let chan = query_channel(deps.as_ref(), local_channel.into()).unwrap(); assert_eq!( chan.balances, vec![Amount::native( @@ -1704,4 +1902,194 @@ mod test { ) ); } + + #[test] + fn test_increase_channel_balance_ibc_receive() { + let local_channel_id = "channel-0"; + let amount = Uint128::from(10u128); + let ibc_denom = "foobar"; + let local_receiver = "receiver"; + let mut deps = setup(&[local_channel_id], &[]); + + assert_eq!( + execute( + deps.as_mut(), + mock_env(), + mock_info("attacker", &vec![]), + ExecuteMsg::IncreaseChannelBalanceIbcReceive { + dest_channel_id: local_channel_id.to_string(), + ibc_denom: ibc_denom.to_string(), + amount: amount.clone(), + local_receiver: local_receiver.to_string(), + }, + ) + .unwrap_err(), + ContractError::Std(StdError::generic_err("Caller is not the contract itself!")) + ); + + execute( + deps.as_mut(), + mock_env(), + mock_info(mock_env().contract.address.as_str(), &vec![]), + ExecuteMsg::IncreaseChannelBalanceIbcReceive { + dest_channel_id: local_channel_id.to_string(), + ibc_denom: ibc_denom.to_string(), + amount: amount.clone(), + local_receiver: local_receiver.to_string(), + }, + ) + .unwrap(); + let channel_state = CHANNEL_REVERSE_STATE + .load(deps.as_ref().storage, (local_channel_id, ibc_denom)) + .unwrap(); + assert_eq!(channel_state.outstanding, amount.clone()); + assert_eq!(channel_state.total_sent, amount.clone()); + let reply_args = REPLY_ARGS.load(deps.as_ref().storage).unwrap(); + assert_eq!(reply_args.amount, amount.clone()); + assert_eq!(reply_args.channel, local_channel_id.clone()); + assert_eq!(reply_args.denom, ibc_denom.to_string()); + assert_eq!(reply_args.local_receiver, local_receiver.to_string()); + } + + #[test] + fn test_reduce_channel_balance_ibc_receive() { + let local_channel_id = "channel-0"; + let amount = Uint128::from(10u128); + let ibc_denom = "foobar"; + let local_receiver = "receiver"; + let mut deps = setup(&[local_channel_id], &[]); + execute( + deps.as_mut(), + mock_env(), + mock_info(mock_env().contract.address.as_str(), &vec![]), + ExecuteMsg::IncreaseChannelBalanceIbcReceive { + dest_channel_id: local_channel_id.to_string(), + ibc_denom: ibc_denom.to_string(), + amount: amount.clone(), + local_receiver: local_receiver.to_string(), + }, + ) + .unwrap(); + + assert_eq!( + execute( + deps.as_mut(), + mock_env(), + mock_info("attacker", &vec![]), + ExecuteMsg::ReduceChannelBalanceIbcReceive { + src_channel_id: local_channel_id.to_string(), + ibc_denom: ibc_denom.to_string(), + amount: amount.clone(), + local_receiver: local_receiver.to_string(), + }, + ) + .unwrap_err(), + ContractError::Std(StdError::generic_err("Caller is not the contract itself!")) + ); + + execute( + deps.as_mut(), + mock_env(), + mock_info(mock_env().contract.address.as_str(), &vec![]), + ExecuteMsg::ReduceChannelBalanceIbcReceive { + src_channel_id: local_channel_id.to_string(), + ibc_denom: ibc_denom.to_string(), + amount: amount.clone(), + local_receiver: local_receiver.to_string(), + }, + ) + .unwrap(); + let channel_state = CHANNEL_REVERSE_STATE + .load(deps.as_ref().storage, (local_channel_id, ibc_denom)) + .unwrap(); + assert_eq!(channel_state.outstanding, Uint128::zero()); + assert_eq!(channel_state.total_sent, Uint128::from(10u128)); + let reply_args = REPLY_ARGS.load(deps.as_ref().storage).unwrap(); + assert_eq!(reply_args.amount, amount.clone()); + assert_eq!(reply_args.channel, local_channel_id.clone()); + assert_eq!(reply_args.denom, ibc_denom.to_string()); + assert_eq!(reply_args.local_receiver, local_receiver.to_string()); + } + + #[test] + fn test_query_channel_balance_with_key() { + // fixture + let channel = "foo-channel"; + let ibc_denom = "port/channel/denom"; + let amount = Uint128::from(10u128); + let reduce_amount = Uint128::from(1u128); + let mut deps = setup(&[channel], &[]); + increase_channel_balance(deps.as_mut().storage, channel, ibc_denom, amount).unwrap(); + reduce_channel_balance( + deps.as_mut().storage, + channel, + ibc_denom, + Uint128::from(1u128), + ) + .unwrap(); + + let result = + query_channel_with_key(deps.as_ref(), channel.to_string(), ibc_denom.to_string()) + .unwrap(); + assert_eq!( + result.balance, + Amount::from_parts( + ibc_denom.to_string(), + amount.checked_sub(reduce_amount).unwrap() + ) + ); + assert_eq!( + result.total_sent, + Amount::from_parts(ibc_denom.to_string(), amount) + ); + } + + #[test] + fn test_handle_override_channel_balance() { + // fixture + let channel = "foo-channel"; + let ibc_denom = "port/channel/denom"; + let amount = Uint128::from(10u128); + let override_amount = Uint128::from(100u128); + let total_sent_override = Uint128::from(1000u128); + let mut deps = setup(&[channel], &[]); + increase_channel_balance(deps.as_mut().storage, channel, ibc_denom, amount).unwrap(); + + // unauthorized case + let unauthorized = handle_override_channel_balance( + deps.as_mut(), + mock_info("attacker", &vec![]), + channel.to_string(), + ibc_denom.to_string(), + amount, + None, + ) + .unwrap_err(); + assert_eq!(unauthorized, ContractError::Admin(AdminError::NotAdmin {})); + + // execution, valid case + handle_override_channel_balance( + deps.as_mut(), + mock_info("gov", &vec![]), + channel.to_string(), + ibc_denom.to_string(), + override_amount, + Some(total_sent_override), + ) + .unwrap(); + + // we query to validate the result after overriding + + let result = + query_channel_with_key(deps.as_ref(), channel.to_string(), ibc_denom.to_string()) + .unwrap(); + assert_eq!( + result.balance, + Amount::from_parts(ibc_denom.to_string(), override_amount) + ); + assert_eq!( + result.total_sent, + Amount::from_parts(ibc_denom.to_string(), total_sent_override) + ); + } } diff --git a/contracts/cw-ics20-latest/src/error.rs b/contracts/cw-ics20-latest/src/error.rs index 66a51c4..15e984b 100644 --- a/contracts/cw-ics20-latest/src/error.rs +++ b/contracts/cw-ics20-latest/src/error.rs @@ -63,9 +63,6 @@ pub enum ContractError { #[error("You cannot lower the gas limit for a contract on the allow list")] CannotLowerGas, - #[error("Only the governance contract can do this")] - Unauthorized, - #[error("You can only send tokens that have been explicitly allowed by governance")] NotOnAllowList, diff --git a/contracts/cw-ics20-latest/src/ibc.rs b/contracts/cw-ics20-latest/src/ibc.rs index 0bffacc..29a5697 100644 --- a/contracts/cw-ics20-latest/src/ibc.rs +++ b/contracts/cw-ics20-latest/src/ibc.rs @@ -3,11 +3,11 @@ use std::ops::Mul; use cosmwasm_schema::cw_serde; use cosmwasm_std::{ attr, coin, entry_point, from_binary, to_binary, Addr, Api, Binary, CosmosMsg, Decimal, Deps, - DepsMut, Env, IbcBasicResponse, IbcChannel, IbcChannelCloseMsg, IbcChannelConnectMsg, - IbcChannelOpenMsg, IbcEndpoint, IbcMsg, IbcOrder, IbcPacket, IbcPacketAckMsg, - IbcPacketReceiveMsg, IbcPacketTimeoutMsg, IbcReceiveResponse, IbcTimeout, Order, - QuerierWrapper, Reply, Response, StdError, StdResult, Storage, SubMsg, SubMsgResult, Timestamp, - Uint128, + DepsMut, Env, Ibc3ChannelOpenResponse, IbcBasicResponse, IbcChannel, IbcChannelCloseMsg, + IbcChannelConnectMsg, IbcChannelOpenMsg, IbcEndpoint, IbcMsg, IbcOrder, IbcPacket, + IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg, IbcReceiveResponse, IbcTimeout, + Order, QuerierWrapper, Reply, Response, StdError, StdResult, Storage, SubMsg, SubMsgResult, + Timestamp, Uint128, }; use cw20_ics20_msg::helper::{ denom_to_asset_info, get_prefix_decode_bech32, parse_asset_info_denom, @@ -18,11 +18,11 @@ use oraiswap::asset::AssetInfo; use oraiswap::router::{RouterController, SwapOperation}; use crate::error::{ContractError, Never}; +use crate::msg::ExecuteMsg; use crate::state::{ - get_key_ics20_ibc_denom, ics20_denoms, increase_channel_balance, reduce_channel_balance, - undo_reduce_channel_balance, ChannelInfo, MappingMetadata, Ratio, ReplyArgs, ALLOW_LIST, - CHANNEL_INFO, CONFIG, RELAYER_FEE, RELAYER_FEE_ACCUMULATOR, REPLY_ARGS, SINGLE_STEP_REPLY_ARGS, - TOKEN_FEE, TOKEN_FEE_ACCUMULATOR, + get_key_ics20_ibc_denom, ics20_denoms, undo_reduce_channel_balance, ChannelInfo, + MappingMetadata, Ratio, ALLOW_LIST, CHANNEL_INFO, CONFIG, RELAYER_FEE, RELAYER_FEE_ACCUMULATOR, + REPLY_ARGS, SINGLE_STEP_REPLY_ARGS, TOKEN_FEE, TOKEN_FEE_ACCUMULATOR, }; use cw20_ics20_msg::amount::{convert_local_to_remote, convert_remote_to_local, Amount}; @@ -202,9 +202,9 @@ pub fn ibc_channel_open( _deps: DepsMut, _env: Env, msg: IbcChannelOpenMsg, -) -> Result<(), ContractError> { +) -> Result, ContractError> { enforce_order_and_version(msg.channel(), msg.counterparty_version())?; - Ok(()) + Ok(None) } #[entry_point] @@ -271,13 +271,29 @@ pub fn ibc_packet_receive( ) -> Result { let packet = msg.packet; - do_ibc_packet_receive(deps, env, &packet).or_else(|err| { + do_ibc_packet_receive( + deps.storage, + deps.api, + &deps.querier, + env, + &packet, + &msg.relayer.into_string(), + ) + .or_else(|err| { + // reset relayer & token fees to prevent re-entrancy when failing + TOKEN_FEE_ACCUMULATOR.clear(deps.storage); + RELAYER_FEE_ACCUMULATOR.clear(deps.storage); Ok(IbcReceiveResponse::new() + // trade-off between reentrancy & refunding. If error, then it should be a serious error => refund to oraibridge + // that's better than trying to update balance & let it stay in this contract and expose to reentrancy .set_ack(ack_fail(err.to_string())) .add_attributes(vec![ attr("action", "receive"), attr("success", "false"), attr("error", err.to_string()), + attr("src_channel_id", packet.src.channel_id), + attr("dst_channel_id", packet.dest.channel_id), + attr("packet_data", packet.data.to_base64()), ])) }) } @@ -340,9 +356,12 @@ pub fn parse_ibc_channel_without_sanity_checks<'a>(ibc_denom: &'a str) -> StdRes // this does the work of ibc_packet_receive, we wrap it to turn errors into acknowledgements fn do_ibc_packet_receive( - deps: DepsMut, + storage: &mut dyn Storage, + api: &dyn Api, + querier: &QuerierWrapper, env: Env, packet: &IbcPacket, + relayer: &str, ) -> Result { let msg: Ics20Packet = from_binary(&packet.data)?; // let channel = packet.dest.channel_id.clone(); @@ -354,13 +373,7 @@ fn do_ibc_packet_receive( // if denom is native, we handle it the native way if denom.1 { return handle_ibc_packet_receive_native_remote_chain( - deps.storage, - deps.api, - &deps.querier, - env, - &denom.0, - &packet, - &msg, + storage, api, &querier, env, &denom.0, &packet, &msg, relayer, ); } @@ -375,6 +388,7 @@ fn handle_ibc_packet_receive_native_remote_chain( denom: &str, packet: &IbcPacket, msg: &Ics20Packet, + relayer: &str, ) -> Result { let config = CONFIG.load(storage)?; @@ -392,22 +406,6 @@ fn handle_ibc_packet_receive_native_remote_chain( pair_mapping.asset_info_decimals, )?, ); - // will have to increase balance here because if this tx fails then it will be reverted, and the balance on the remote chain will also be reverted - increase_channel_balance( - storage, - &packet.dest.channel_id, - &ibc_denom, - msg.amount.clone(), - false, - )?; - // we need to save the data to update the balances in reply - let reply_args = ReplyArgs { - channel: packet.dest.channel_id.clone(), - denom: ibc_denom.clone(), - amount: msg.amount, - local_receiver: msg.receiver.clone(), - }; - REPLY_ARGS.save(storage, &reply_args)?; let (new_deducted_amount, token_fee, relayer_fee) = process_deduct_fee( storage, @@ -435,28 +433,42 @@ fn handle_ibc_packet_receive_native_remote_chain( packet.dest.channel_id.as_str(), )?; - let mut fee_msgs = collect_fee_msgs( + let mut cosmos_msgs = collect_fee_msgs( storage, config.token_fee_receiver.into_string(), TOKEN_FEE_ACCUMULATOR, )?; - fee_msgs.append(&mut collect_fee_msgs( + cosmos_msgs.append(&mut collect_fee_msgs( storage, - config.relayer_fee_receiver.to_string(), + relayer.to_string(), RELAYER_FEE_ACCUMULATOR, )?); + // increase channel balance submsg. We increase it first before doing other tasks + cosmos_msgs.push(CosmosMsg::Wasm(cosmwasm_std::WasmMsg::Execute { + contract_addr: env.contract.address.to_string(), + msg: to_binary(&ExecuteMsg::IncreaseChannelBalanceIbcReceive { + dest_channel_id: packet.dest.channel_id.clone(), + ibc_denom, + amount: msg.amount, + local_receiver: msg.receiver.clone(), + })?, + funds: vec![], + })); let mut res = IbcReceiveResponse::new() .set_ack(ack_success()) - .add_messages(fee_msgs) + .add_messages(cosmos_msgs) .add_submessages(submsgs) - .add_attribute("action", "receive_native") - .add_attribute("sender", msg.sender.clone()) - .add_attribute("receiver", msg.receiver.clone()) - .add_attribute("denom", denom) - .add_attribute("amount", msg.amount.to_string()) - .add_attribute("success", "true") - .add_attribute("token_fee", token_fee) - .add_attribute("relayer_fee", relayer_fee); + .add_attributes(vec![ + ("action", "receive_native"), + ("sender", &msg.sender), + ("receiver", &msg.receiver), + ("denom", denom), + ("amount", &msg.amount.to_string()), + ("success", "true"), + ("token_fee", &token_fee.to_string()), + ("relayer_fee", &relayer_fee.to_string()), + ("relayer", relayer), + ]); if !ibc_error_msg.is_empty() { res = res.add_attribute("ibc_error_msg", ibc_error_msg); } @@ -513,7 +525,7 @@ pub fn get_follow_up_msgs( minimum_receive = response.unwrap().amount; } - let ibc_msg = build_ibc_msg( + let mut ibc_msg = build_ibc_msg( storage, env, receiver_asset_info, @@ -527,8 +539,8 @@ pub fn get_follow_up_msgs( // by default, the receiver is the original address sent in ics20packet let mut to = Some(api.addr_validate(receiver)?); - let ibc_error_msg = if let Some(ibc_msg) = ibc_msg.as_ref().ok() { - sub_msgs.push(ibc_msg.to_owned()); + let ibc_error_msg = if let Some(ibc_msg) = ibc_msg.as_mut().ok() { + sub_msgs.append(ibc_msg); // if there's an ibc msg => swap receiver is None so the receiver is this ibc wasm address to = None; String::from("") @@ -638,7 +650,7 @@ pub fn build_ibc_msg( remote_address: &str, destination: &DestinationInfo, default_timeout: u64, -) -> StdResult { +) -> StdResult> { // if there's no dest channel then we stop, no need to transfer ibc if destination.destination_channel.is_empty() { return Err(StdError::generic_err( @@ -674,6 +686,7 @@ pub fn build_ibc_msg( return process_ibc_msg( storage, mapping, + env.contract.address.to_string(), receiver_asset_info, local_receiver, local_channel_id, @@ -698,6 +711,7 @@ pub fn build_ibc_msg( return process_ibc_msg( storage, mapping, + env.contract.address.to_string(), receiver_asset_info, local_receiver, &destination.destination_channel, @@ -720,10 +734,10 @@ pub fn build_ibc_msg( timeout: timeout.into(), } .into(); - return Ok(SubMsg::reply_on_error( + return Ok(vec![SubMsg::reply_on_error( ibc_msg, IBC_TRANSFER_NATIVE_ERROR_ID, - )); + )]); } Err(StdError::generic_err( "The destination info is neither evm or cosmos based", @@ -734,6 +748,7 @@ pub fn build_ibc_msg( pub fn process_ibc_msg( storage: &mut dyn Storage, pair_mapping: (String, MappingMetadata), + contract_addr: String, receiver_asset_info: AssetInfo, local_receiver: &str, src_channel: &str, @@ -742,7 +757,7 @@ pub fn process_ibc_msg( memo: Option, amount: Uint128, timeout: Timestamp, -) -> StdResult { +) -> StdResult> { let (new_deducted_amount, _) = deduct_token_fee( storage, parse_ibc_denom_without_sanity_checks(&pair_mapping.0)?, // denom mapping in the form port/channel/denom @@ -755,16 +770,6 @@ pub fn process_ibc_msg( pair_mapping.1.asset_info_decimals, )?; - // because we are transferring back, we reduce the channel's balance - reduce_channel_balance( - storage, - src_channel.clone(), - &pair_mapping.0.clone(), - remote_amount, - false, - ) - .map_err(|err| StdError::generic_err(err.to_string()))?; - // prepare ibc message let msg: CosmosMsg = build_ibc_send_packet( remote_amount, @@ -777,18 +782,21 @@ pub fn process_ibc_msg( )? .into(); - // keep track of the single-step reply since we need ibc data to undo reducing channel balance and local data for refunding. - // we use a different item to not override REPLY_ARGS - SINGLE_STEP_REPLY_ARGS.save( - storage, - &ReplyArgs { - channel: src_channel.to_string(), - denom: pair_mapping.0.to_string(), - amount: remote_amount, + let reduce_balance_msg = SubMsg::new(CosmosMsg::Wasm(cosmwasm_std::WasmMsg::Execute { + contract_addr, + msg: to_binary(&ExecuteMsg::ReduceChannelBalanceIbcReceive { + src_channel_id: src_channel.to_string(), + ibc_denom: pair_mapping.0.clone(), + amount: remote_amount.clone(), local_receiver: local_receiver.to_string(), - }, - )?; - Ok(SubMsg::reply_on_error(msg, FOLLOW_UP_IBC_SEND_FAILURE_ID)) + })?, + funds: vec![], + })); + + Ok(vec![ + reduce_balance_msg, + SubMsg::reply_on_error(msg, FOLLOW_UP_IBC_SEND_FAILURE_ID), + ]) } pub fn check_gas_limit(deps: Deps, amount: &Amount) -> Result, ContractError> { @@ -819,7 +827,7 @@ pub fn process_deduct_fee( decimals: u8, swap_router_contract: &RouterController, ) -> StdResult<(Uint128, Uint128, Uint128)> { - let (_, token_fee) = deduct_token_fee( + let (deducted_amount, token_fee) = deduct_token_fee( storage, remote_token_denom, local_amount.amount(), @@ -827,37 +835,24 @@ pub fn process_deduct_fee( )?; // simulate for relayer fee let offer_asset_info = denom_to_asset_info(querier, api, &local_amount.raw_denom())?; - let offer_amount = Uint128::from(10u64.pow((decimals + 1) as u32) as u64); // +1 to make sure the offer amount is large enough to swap successfully - let token_price = swap_router_contract - .simulate_swap( - querier, - offer_amount, - vec![SwapOperation::OraiSwap { - offer_asset_info, - // always swap with orai. If it does not share a pool with ORAI => ignore, no fee - ask_asset_info: AssetInfo::NativeToken { - denom: "orai".to_string(), - }, - }], - ) - .map(|data| data.amount) - .unwrap_or_default(); + let simulate_amount = Uint128::from(10u64.pow((decimals + 1) as u32) as u64); // +1 to make sure the offer amount is large enough to swap successfully + let token_price = get_token_price( + querier, + simulate_amount, + swap_router_contract, + offer_asset_info, + ); let (_, relayer_fee) = deduct_relayer_fee( storage, api, remote_sender, remote_token_denom, local_amount.amount(), - offer_amount, + simulate_amount, &local_amount.denom(), token_price, )?; - let new_amount = local_amount - .amount() - .checked_sub(token_fee) - .unwrap_or_default() - .checked_sub(relayer_fee) - .unwrap_or_default(); + let new_amount = deducted_amount.checked_sub(relayer_fee).unwrap_or_default(); if new_amount.is_zero() { return Err(StdError::generic_err( "Not enough transfer amount to cover the token and relayer fees", @@ -891,9 +886,9 @@ pub fn deduct_relayer_fee( _api: &dyn Api, remote_address: &str, remote_token_denom: &str, - amount: Uint128, // local amount - offer_amount: Uint128, // offer amount of token that swaps to ORAI - local_token_denom: &str, // local denom + amount: Uint128, // local amount + simulate_amount: Uint128, // offer amount of token that swaps to ORAI + local_token_denom: &str, // local denom token_price: Uint128, ) -> StdResult<(Uint128, Uint128)> { // api.debug(format!("token price: {}", token_price).as_str()); @@ -917,7 +912,7 @@ pub fn deduct_relayer_fee( } let relayer_fee = relayer_fee.unwrap(); let required_fee_needed = relayer_fee - .checked_mul(offer_amount) + .checked_mul(simulate_amount) .unwrap_or_default() .checked_div(token_price) .unwrap_or_default(); @@ -950,6 +945,29 @@ pub fn deduct_fee(token_fee: Ratio, amount: Uint128) -> Uint128 { )) } +pub fn get_token_price( + querier: &QuerierWrapper, + simulate_amount: Uint128, + swap_router_contract: &RouterController, + offer_asset_info: AssetInfo, +) -> Uint128 { + let token_price = swap_router_contract + .simulate_swap( + querier, + simulate_amount, + vec![SwapOperation::OraiSwap { + offer_asset_info, + // always swap with orai. If it does not share a pool with ORAI => ignore, no fee + ask_asset_info: AssetInfo::NativeToken { + denom: "orai".to_string(), + }, + }], + ) + .map(|data| data.amount) + .unwrap_or_default(); + token_price +} + pub fn convert_remote_denom_to_evm_prefix(remote_denom: &str) -> String { match remote_denom.split_once("0x") { Some((evm_prefix, _)) => return evm_prefix.to_string(), @@ -976,11 +994,7 @@ pub fn collect_fee_msgs( .flatten() .collect::>(); // we reset all the accumulator keys to zero so that it wont accumulate more in the next txs. This action will be reverted if the fee payment txs fail. - fee_accumulator - .keys(storage, None, None, Order::Ascending) - .collect::, StdError>>()? - .into_iter() - .for_each(|key| fee_accumulator.remove(storage, &key)); + fee_accumulator.clear(storage); Ok(cosmos_msgs) } diff --git a/contracts/cw-ics20-latest/src/ibc_tests.rs b/contracts/cw-ics20-latest/src/ibc_tests.rs index b487970..90c2905 100644 --- a/contracts/cw-ics20-latest/src/ibc_tests.rs +++ b/contracts/cw-ics20-latest/src/ibc_tests.rs @@ -23,13 +23,13 @@ mod test { use crate::error::ContractError; use crate::state::{ get_key_ics20_ibc_denom, increase_channel_balance, ChannelState, MappingMetadata, Ratio, - ReplyArgs, CHANNEL_REVERSE_STATE, RELAYER_FEE, RELAYER_FEE_ACCUMULATOR, - SINGLE_STEP_REPLY_ARGS, TOKEN_FEE, TOKEN_FEE_ACCUMULATOR, + CHANNEL_REVERSE_STATE, RELAYER_FEE, RELAYER_FEE_ACCUMULATOR, TOKEN_FEE, + TOKEN_FEE_ACCUMULATOR, }; use cw20::{Cw20Coin, Cw20ExecuteMsg}; use cw20_ics20_msg::amount::{convert_local_to_remote, Amount}; - use crate::contract::{execute, migrate, query_channel}; + use crate::contract::{execute, migrate}; use crate::msg::{ExecuteMsg, MigrateMsg, UpdatePairMsg}; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; use cosmwasm_std::{coins, to_vec}; @@ -200,6 +200,7 @@ mod test { #[test] fn send_native_from_remote_mapping_not_found() { + let relayer = Addr::unchecked("relayer"); let send_channel = "channel-9"; let cw20_addr = "token-addr"; let custom_addr = "custom-addr"; @@ -220,17 +221,22 @@ mod test { ); // we can receive this denom, channel balance should increase - let msg = IbcPacketReceiveMsg::new(recv_packet.clone()); + let msg = IbcPacketReceiveMsg::new(recv_packet.clone(), relayer); let res = ibc_packet_receive(deps.as_mut(), mock_env(), msg).unwrap(); // assert_eq!(res, StdError) assert_eq!( - res.attributes.last().unwrap().value, + res.attributes + .into_iter() + .find(|attr| attr.key.eq("error")) + .unwrap() + .value, "You can only send native tokens that has a map to the corresponding asset info" ); } #[test] fn send_from_remote_to_local_receive_happy_path() { + let relayer = Addr::unchecked("relayer"); let send_channel = "channel-9"; let cw20_addr = "token-addr"; let custom_addr = "custom-addr"; @@ -281,11 +287,11 @@ mod test { ); // we can receive this denom, channel balance should increase - let msg = IbcPacketReceiveMsg::new(recv_packet.clone()); + let msg = IbcPacketReceiveMsg::new(recv_packet.clone(), relayer); let res = ibc_packet_receive(deps.as_mut(), mock_env(), msg).unwrap(); println!("res: {:?}", res); // TODO: fix test cases. Possibly because we are adding two add_submessages? - assert_eq!(res.messages.len(), 2); // 2 messages because we also have deduct fee msg + assert_eq!(res.messages.len(), 3); // 3 messages because we also have deduct fee msg and increase channel balance msg match res.messages[0].msg.clone() { CosmosMsg::Wasm(WasmMsg::Execute { contract_addr, @@ -308,21 +314,26 @@ mod test { assert!(matches!(ack, Ics20Ack::Result(_))); // query channel state|_| - let state = query_channel(deps.as_ref(), send_channel.to_string(), None).unwrap(); - assert_eq!( - state.balances, - vec![Amount::native( - 876543210, - &get_key_ics20_ibc_denom(CONTRACT_PORT, send_channel, denom) - )] - ); - assert_eq!( - state.total_sent, - vec![Amount::native( - 876543210, - &get_key_ics20_ibc_denom(CONTRACT_PORT, send_channel, denom) - )] - ); + match res.messages[1].msg.clone() { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr, + msg, + funds: _, + }) => { + assert_eq!(contract_addr, "cosmos2contract".to_string()); // self-call msg + assert_eq!( + msg, + to_binary(&ExecuteMsg::IncreaseChannelBalanceIbcReceive { + dest_channel_id: send_channel.to_string(), + ibc_denom: get_key_ics20_ibc_denom(CONTRACT_PORT, send_channel, denom), + amount: Uint128::from(876543210u64), + local_receiver: custom_addr.to_string(), + }) + .unwrap() + ); + } + _ => panic!("Unexpected return message: {:?}", res.messages[0]), + } } #[test] @@ -602,7 +613,6 @@ mod test { receive_channel, pair_mapping_key.as_str(), remote_amount.clone(), - false, ) .unwrap(); destination.receiver = "trx-mainnet0x73Ddc880916021EFC4754Cb42B53db6EAB1f9D64".to_string(); @@ -621,7 +631,7 @@ mod test { .unwrap(); assert_eq!( - result, + result[1], SubMsg::reply_on_error( CosmosMsg::Ibc(IbcMsg::SendPacket { channel_id: receive_channel.to_string(), @@ -638,11 +648,20 @@ mod test { FOLLOW_UP_IBC_SEND_FAILURE_ID ) ); - let reply_args = SINGLE_STEP_REPLY_ARGS.load(deps.as_mut().storage).unwrap(); - assert_eq!(reply_args.amount, remote_amount); - assert_eq!(reply_args.channel, receive_channel); - assert_eq!(reply_args.denom, pair_mapping_key); - assert_eq!(reply_args.local_receiver, local_receiver.to_string()); + assert_eq!( + result[0], + SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: env.contract.address.into_string(), + msg: to_binary(&ExecuteMsg::ReduceChannelBalanceIbcReceive { + src_channel_id: receive_channel.to_string(), + ibc_denom: pair_mapping_key, + amount: remote_amount, + local_receiver: local_receiver.to_string() + }) + .unwrap(), + funds: vec![] + })) + ); } #[test] @@ -712,7 +731,7 @@ mod test { ) .unwrap(); assert_eq!( - result, + result[0], SubMsg::reply_on_error( CosmosMsg::Ibc(IbcMsg::Transfer { channel_id: send_channel.to_string(), @@ -765,7 +784,7 @@ mod test { .unwrap(); assert_eq!( - result, + result[1], SubMsg::reply_on_error( CosmosMsg::Ibc(IbcMsg::SendPacket { channel_id: send_channel.to_string(), @@ -782,6 +801,20 @@ mod test { FOLLOW_UP_IBC_SEND_FAILURE_ID ) ); + assert_eq!( + result[0], + SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: env.contract.address.into_string(), + msg: to_binary(&ExecuteMsg::ReduceChannelBalanceIbcReceive { + src_channel_id: send_channel.to_string(), + ibc_denom: pair_mapping_key, + amount: remote_amount, + local_receiver: local_receiver.to_string() + }) + .unwrap(), + funds: vec![] + })) + ); } #[test] @@ -1215,6 +1248,7 @@ mod test { let result = process_ibc_msg( storage, pair_mapping, + mock_env().contract.address.into_string(), receiver_asset_info, local_receiver, local_channel_id, @@ -1226,27 +1260,23 @@ mod test { ) .unwrap(); - // assert - // channel balance should reduce to 0 - assert_eq!( - CHANNEL_REVERSE_STATE - .load(storage, (local_channel_id, ibc_denom)) - .unwrap() - .outstanding, - Uint128::from(0u64) - ); - // reply args should have ibc data now assert_eq!( - SINGLE_STEP_REPLY_ARGS.load(storage).unwrap(), - ReplyArgs { - channel: local_channel_id.to_string(), - denom: ibc_denom.to_string(), - local_receiver: local_receiver.to_string(), - amount: remote_amount.clone(), - } + result[0], + SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: mock_env().contract.address.into_string(), + msg: to_binary(&ExecuteMsg::ReduceChannelBalanceIbcReceive { + src_channel_id: local_channel_id.to_string(), + ibc_denom: ibc_denom.to_string(), + amount: remote_amount, + local_receiver: local_receiver.to_string() + }) + .unwrap(), + funds: vec![] + })) ); + assert_eq!( - result, + result[1], SubMsg::reply_on_error( IbcMsg::SendPacket { channel_id: local_channel_id.to_string(), diff --git a/contracts/cw-ics20-latest/src/msg.rs b/contracts/cw-ics20-latest/src/msg.rs index b161174..f3b3588 100644 --- a/contracts/cw-ics20-latest/src/msg.rs +++ b/contracts/cw-ics20-latest/src/msg.rs @@ -60,6 +60,25 @@ pub enum ExecuteMsg { fee_receiver: Option, relayer_fee_receiver: Option, }, + // self-call msgs to deal with on_ibc_receive reentrancy error + IncreaseChannelBalanceIbcReceive { + dest_channel_id: String, + ibc_denom: String, + amount: Uint128, + local_receiver: String, + }, + ReduceChannelBalanceIbcReceive { + src_channel_id: String, + ibc_denom: String, + amount: Uint128, + local_receiver: String, + }, + OverrideChannelBalance { + channel_id: String, + ibc_denom: String, + outstanding: Uint128, + total_sent: Option, + }, } #[cw_serde] @@ -134,7 +153,10 @@ pub enum QueryMsg { ListChannels {}, /// Returns the details of the name channel, error if not created. #[returns(ChannelResponse)] - Channel { id: String, forward: Option }, + Channel { id: String }, + /// Returns the details of the name channel, error if not created. + #[returns(ChannelWithKeyResponse)] + ChannelWithKey { channel_id: String, denom: String }, /// Show the Config. #[returns(ConfigResponse)] Config {}, @@ -181,6 +203,17 @@ pub struct ChannelResponse { pub total_sent: Vec, } +#[cw_serde] +pub struct ChannelWithKeyResponse { + /// Information on the channel's connection + pub info: ChannelInfo, + /// How many tokens we currently have pending over this channel + pub balance: Amount, + /// The total number of tokens that have been sent over this channel + /// (even if many have been returned, so balance is low) + pub total_sent: Amount, +} + #[cw_serde] pub struct PortResponse { pub port_id: String, diff --git a/contracts/cw-ics20-latest/src/state.rs b/contracts/cw-ics20-latest/src/state.rs index 678dbb0..878f79f 100644 --- a/contracts/cw-ics20-latest/src/state.rs +++ b/contracts/cw-ics20-latest/src/state.rs @@ -140,15 +140,10 @@ pub struct ReplyArgs { pub fn increase_channel_balance( storage: &mut dyn Storage, channel: &str, - denom: &str, + denom: &str, // should be ibc denom amount: Uint128, - forward: bool, ) -> Result<(), ContractError> { - let mut state = CHANNEL_REVERSE_STATE; - if forward { - state = CHANNEL_FORWARD_STATE; - } - + let state = CHANNEL_REVERSE_STATE; state.update(storage, (channel, denom), |orig| -> StdResult<_> { let mut state = orig.unwrap_or_default(); state.outstanding += amount; @@ -161,14 +156,10 @@ pub fn increase_channel_balance( pub fn reduce_channel_balance( storage: &mut dyn Storage, channel: &str, - denom: &str, + denom: &str, // should be ibc denom amount: Uint128, - forward: bool, ) -> Result<(), ContractError> { - let mut state = CHANNEL_REVERSE_STATE; - if forward { - state = CHANNEL_FORWARD_STATE; - } + let state = CHANNEL_REVERSE_STATE; state.update( storage, (channel, denom), @@ -191,6 +182,26 @@ pub fn reduce_channel_balance( Ok(()) } +// only used for admin of the contract +pub fn override_channel_balance( + storage: &mut dyn Storage, + channel: &str, + denom: &str, // should be ibc denom + outstanding: Uint128, + total_sent: Option, +) -> Result<(), ContractError> { + let state = CHANNEL_REVERSE_STATE; + state.update(storage, (channel, denom), |orig| -> StdResult<_> { + let mut state = orig.unwrap_or_default(); + state.outstanding = outstanding; + if let Some(total_sent) = total_sent { + state.total_sent = total_sent; + } + Ok(state) + })?; + Ok(()) +} + // this is like increase, but it only "un-subtracts" (= adds) outstanding, not total_sent // calling `reduce_channel_balance` and then `undo_reduce_channel_balance` should leave state unchanged. pub fn undo_reduce_channel_balance(