From c666c8ac55d049ec92e8373c5728919066578be6 Mon Sep 17 00:00:00 2001 From: DrPresident Date: Mon, 9 Dec 2024 14:44:59 -0600 Subject: [PATCH 01/29] rough start on shade adapter --- Cargo.lock | 549 +++++++++--- .../adapters/swap/shade-protocol/Cargo.toml | 41 + .../adapters/swap/shade-protocol/README.md | 123 +++ .../src/bin/shade-protocol-schema.rs | 10 + .../swap/shade-protocol/src/contract.rs | 812 ++++++++++++++++++ .../adapters/swap/shade-protocol/src/error.rs | 38 + .../adapters/swap/shade-protocol/src/lib.rs | 4 + .../src/shade_swap_router_msg.rs | 127 +++ .../adapters/swap/shade-protocol/src/state.rs | 13 + 9 files changed, 1604 insertions(+), 113 deletions(-) create mode 100644 contracts/adapters/swap/shade-protocol/Cargo.toml create mode 100644 contracts/adapters/swap/shade-protocol/README.md create mode 100644 contracts/adapters/swap/shade-protocol/src/bin/shade-protocol-schema.rs create mode 100644 contracts/adapters/swap/shade-protocol/src/contract.rs create mode 100644 contracts/adapters/swap/shade-protocol/src/error.rs create mode 100644 contracts/adapters/swap/shade-protocol/src/lib.rs create mode 100644 contracts/adapters/swap/shade-protocol/src/shade_swap_router_msg.rs create mode 100644 contracts/adapters/swap/shade-protocol/src/state.rs diff --git a/Cargo.lock b/Cargo.lock index 72335784..e1f485a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,15 +15,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" [[package]] name = "astroport" @@ -72,9 +72,9 @@ dependencies = [ [[package]] name = "astrovault" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00162d5a60a1463f5f35f7f5c5a60b01a7ab391693176028757559d90118a39" +checksum = "3a16c325554ef0760871dc4a0c2a3d527b5ba8559a4b7ad79a24753f1ba99805" dependencies = [ "bigint", "cosmwasm-schema", @@ -89,9 +89,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "base16ct" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" [[package]] name = "base16ct" @@ -99,6 +105,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.7" @@ -127,6 +139,16 @@ dependencies = [ "crunchy 0.1.6", ] +[[package]] +name = "bincode2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49f6183038e081170ebbbadee6678966c7d54728938a3e7de7f4e780770318f" +dependencies = [ + "byteorder", + "serde", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -159,9 +181,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" dependencies = [ "serde", ] @@ -174,9 +196,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "num-traits", ] @@ -189,18 +211,18 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const_format" -version = "0.2.32" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" -version = "0.2.32" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" dependencies = [ "proc-macro2", "quote", @@ -231,32 +253,31 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.5.5" +version = "1.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd50718a2b6830ce9eb5d465de5a018a12e71729d66b70807ce97e6dd14f931d" +checksum = "58535cbcd599b3c193e3967c8292fe1dbbb5de7c2a2d87380661091dd4744044" dependencies = [ "digest 0.10.7", - "ecdsa", "ed25519-zebra", - "k256", + "k256 0.13.4", "rand_core 0.6.4", "thiserror", ] [[package]] name = "cosmwasm-derive" -version = "1.5.5" +version = "1.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "242e98e7a231c122e08f300d9db3262d1007b51758a8732cd6210b3e9faa4f3a" +checksum = "a8e07de16c800ac82fd188d055ecdb923ead0cf33960d3350089260bb982c09f" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.5.5" +version = "1.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7879036156092ad1c22fe0d7316efc5a5eceec2bc3906462a2560215f2a2f929" +checksum = "93d388adfa9cb449557a92e9318121ac1a481fc4f599213b03a5b62699b403b4" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -267,9 +288,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.5.5" +version = "1.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb57855fbfc83327f8445ae0d413b1a05ac0d68c396ab4d122b2abd7bb82cb6" +checksum = "2411b389e56e6484f81ba955b758d02522d620c98fc960c4bd2251d48b7aa19f" dependencies = [ "proc-macro2", "quote", @@ -278,11 +299,11 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.5.5" +version = "1.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c1556156fdf892a55cced6115968b961eaaadd6f724a2c2cb7d1e168e32dd3" +checksum = "c21fde95ccd20044a23c0ac6fd8c941f3e8c158169dc94b5aa6491a2d9551a8d" dependencies = [ - "base64", + "base64 0.21.7", "bech32", "bnum", "cosmwasm-crypto", @@ -310,9 +331,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -329,6 +350,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-bigint" version = "0.5.5" @@ -672,6 +705,16 @@ dependencies = [ "thiserror", ] +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "der" version = "0.7.9" @@ -856,7 +899,7 @@ checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" [[package]] name = "drop-factory" version = "1.0.0" -source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#a4da7691eb86e0c2364b9d4daccba9b1f036d2ab" +source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#318f0d8d24e1f90f33987ff8da6e2e6f3c9fc62f" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -875,24 +918,27 @@ dependencies = [ [[package]] name = "drop-helpers" version = "1.0.0" -source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#a4da7691eb86e0c2364b9d4daccba9b1f036d2ab" +source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#318f0d8d24e1f90f33987ff8da6e2e6f3c9fc62f" dependencies = [ "cosmos-sdk-proto 0.20.0", "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus 1.2.0", + "hex", "neutron-sdk 0.10.0 (git+https://github.com/neutron-org/neutron-sdk?branch=feat/proposal-votes)", + "once_cell", "prost 0.12.6", "schemars", "serde", "serde-json-wasm 1.0.1", + "sha3", "thiserror", ] [[package]] name = "drop-macros" version = "1.0.0" -source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#a4da7691eb86e0c2364b9d4daccba9b1f036d2ab" +source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#318f0d8d24e1f90f33987ff8da6e2e6f3c9fc62f" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -901,10 +947,21 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "drop-proto" +version = "1.0.0" +source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#318f0d8d24e1f90f33987ff8da6e2e6f3c9fc62f" +dependencies = [ + "cosmwasm-std", + "prost 0.12.6", + "prost-types 0.12.6", + "tendermint-proto 0.34.1", +] + [[package]] name = "drop-puppeteer-base" version = "1.0.0" -source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#a4da7691eb86e0c2364b9d4daccba9b1f036d2ab" +source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#318f0d8d24e1f90f33987ff8da6e2e6f3c9fc62f" dependencies = [ "cosmos-sdk-proto 0.20.0", "cosmwasm-schema", @@ -925,7 +982,7 @@ dependencies = [ [[package]] name = "drop-staking-base" version = "1.0.0" -source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#a4da7691eb86e0c2364b9d4daccba9b1f036d2ab" +source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#318f0d8d24e1f90f33987ff8da6e2e6f3c9fc62f" dependencies = [ "astroport 3.12.2", "cosmos-sdk-proto 0.20.0", @@ -938,6 +995,7 @@ dependencies = [ "cw721-base 0.18.0", "drop-helpers", "drop-macros", + "drop-proto", "drop-puppeteer-base", "neutron-sdk 0.10.0 (git+https://github.com/neutron-org/neutron-sdk?branch=feat/proposal-votes)", "optfield", @@ -953,18 +1011,30 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + [[package]] name = "ecdsa" version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der", + "der 0.7.9", "digest 0.10.7", - "elliptic-curve", - "rfc6979", - "signature", - "spki", + "elliptic-curve 0.13.8", + "rfc6979 0.4.0", + "signature 2.2.0", + "spki 0.7.3", ] [[package]] @@ -988,25 +1058,55 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest 0.10.7", + "ff 0.12.1", + "generic-array", + "group 0.12.1", + "pkcs8 0.9.0", + "rand_core 0.6.4", + "sec1 0.3.0", + "subtle", + "zeroize", +] + [[package]] name = "elliptic-curve" version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ - "base16ct", - "crypto-bigint", + "base16ct 0.2.0", + "crypto-bigint 0.5.5", "digest 0.10.7", - "ff", + "ff 0.13.0", "generic-array", - "group", - "pkcs8", + "group 0.13.0", + "pkcs8 0.10.2", "rand_core 0.6.4", - "sec1", + "sec1 0.7.3", "subtle", "zeroize", ] +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "ff" version = "0.13.0" @@ -1060,13 +1160,24 @@ dependencies = [ "wasi", ] +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "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", + "ff 0.13.0", "rand_core 0.6.4", "subtle", ] @@ -1103,8 +1214,8 @@ dependencies = [ [[package]] name = "hpl-interface" -version = "0.0.6" -source = "git+https://github.com/many-things/cw-hyperlane.git?branch=main#89d3943ee997d6da9b13fc3599eb62024b3bee67" +version = "0.0.7" +source = "git+https://github.com/many-things/cw-hyperlane.git?branch=main#d07e55e17c791a5f6557f114e3fb6cb433d9b800" dependencies = [ "bech32", "cosmwasm-schema", @@ -1128,7 +1239,7 @@ version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11c352715b36685c2543556a77091fb16af5d26257d5ce9c28e6756c1ccd71aa" dependencies = [ - "base64", + "base64 0.21.7", "bytes", "flex-error", "ics23", @@ -1179,22 +1290,34 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "k256" -version = "0.13.1" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" dependencies = [ "cfg-if", - "ecdsa", - "elliptic-curve", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.8", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", "once_cell", "sha2 0.10.8", - "signature", + "signature 2.2.0", ] [[package]] @@ -1214,9 +1337,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" [[package]] name = "lido-satellite" @@ -1261,7 +1384,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.90", ] [[package]] @@ -1289,7 +1412,7 @@ dependencies = [ "cosmwasm-std", "prost 0.12.6", "prost-types 0.12.6", - "protobuf 3.4.0", + "protobuf 3.7.1", "schemars", "serde", "serde-json-wasm 1.0.1", @@ -1310,7 +1433,7 @@ dependencies = [ "cosmwasm-std", "prost 0.12.6", "prost-types 0.12.6", - "protobuf 3.4.0", + "protobuf 3.7.1", "schemars", "serde", "serde-json-wasm 1.0.1", @@ -1348,9 +1471,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "opaque-debug" @@ -1366,7 +1489,7 @@ checksum = "fa59f025cde9c698fcb4fcb3533db4621795374065bee908215263488f2d2a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.90", ] [[package]] @@ -1403,14 +1526,24 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der 0.6.1", + "spki 0.6.0", +] + [[package]] name = "pkcs8" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der", - "spki", + "der 0.7.9", + "spki 0.7.3", ] [[package]] @@ -1447,9 +1580,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -1497,7 +1630,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.90", ] [[package]] @@ -1529,9 +1662,9 @@ dependencies = [ [[package]] name = "protobuf" -version = "3.4.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58678a64de2fced2bdec6bca052a6716a0efe692d6e3f53d1bda6a1def64cfc0" +checksum = "a3a7c64d9bf75b1b8d981124c14c179074e8caa7dfe7b6a12e6222ddcd0c8f72" dependencies = [ "bytes", "once_cell", @@ -1541,18 +1674,18 @@ dependencies = [ [[package]] name = "protobuf-support" -version = "3.4.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1ed294a835b0f30810e13616b1cd34943c6d1e84a8f3b0dcfe466d256c3e7e7" +checksum = "b088fd20b938a875ea00843b6faf48579462630015c3788d397ad6a786663252" dependencies = [ "thiserror", ] [[package]] name = "pryzm-std" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f30262f6c45bc128cb866a7c26f5ff9137e090eda1a29780eec48d32a2f51d" +checksum = "97d295897f6a1a3a05ef5b50794590bee0bc29cb2afb5d2117ef07aafd995d5a" dependencies = [ "chrono", "cosmwasm-std", @@ -1579,9 +1712,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -1601,6 +1734,17 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint 0.4.9", + "hmac", + "zeroize", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -1622,9 +1766,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "ryu" @@ -1653,7 +1797,21 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.72", + "syn 2.0.90", +] + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct 0.1.1", + "der 0.6.1", + "generic-array", + "pkcs8 0.9.0", + "subtle", + "zeroize", ] [[package]] @@ -1662,14 +1820,134 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ - "base16ct", - "der", + "base16ct 0.2.0", + "der 0.7.9", "generic-array", - "pkcs8", + "pkcs8 0.10.2", "subtle", "zeroize", ] +[[package]] +name = "secret-cosmwasm-crypto" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8535d61c88d0a6c222df2cebb69859d8e9ba419a299a1bc84c904b0d9c00c7b2" +dependencies = [ + "digest 0.10.7", + "ed25519-zebra", + "k256 0.11.6", + "rand_core 0.6.4", + "thiserror", +] + +[[package]] +name = "secret-cosmwasm-std" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e4393b01aa6587007161a6bb193859deaa8165ab06c8a35f253d329ff99e4d" +dependencies = [ + "base64 0.13.1", + "cosmwasm-derive", + "derivative", + "forward_ref", + "hex", + "schemars", + "secret-cosmwasm-crypto", + "serde", + "serde-json-wasm 0.4.1", + "thiserror", + "uint", +] + +[[package]] +name = "secret-cosmwasm-storage" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb43da2cb72a53b16ea1555bca794fb828b48ab24ebeb45f8e26f1881c45a783" +dependencies = [ + "secret-cosmwasm-std", + "serde", +] + +[[package]] +name = "secret-storage-plus" +version = "0.13.4" +source = "git+https://github.com/securesecrets/secret-plus-utils?tag=v0.1.1#96438a5bf7f1fb0acc540fe3a43e934f01e6711f" +dependencies = [ + "bincode2", + "schemars", + "secret-cosmwasm-std", + "serde", +] + +[[package]] +name = "secret-toolkit" +version = "0.10.0" +source = "git+https://github.com/scrtlabs/secret-toolkit?tag=v0.10.0#9e139bedab9eeb60f2bdbf9d4f1d2bb069886ea9" +dependencies = [ + "secret-toolkit-serialization", + "secret-toolkit-snip20", + "secret-toolkit-snip721", + "secret-toolkit-storage", + "secret-toolkit-utils", +] + +[[package]] +name = "secret-toolkit-serialization" +version = "0.10.0" +source = "git+https://github.com/scrtlabs/secret-toolkit?tag=v0.10.0#9e139bedab9eeb60f2bdbf9d4f1d2bb069886ea9" +dependencies = [ + "bincode2", + "schemars", + "secret-cosmwasm-std", + "serde", +] + +[[package]] +name = "secret-toolkit-snip20" +version = "0.10.0" +source = "git+https://github.com/scrtlabs/secret-toolkit?tag=v0.10.0#9e139bedab9eeb60f2bdbf9d4f1d2bb069886ea9" +dependencies = [ + "schemars", + "secret-cosmwasm-std", + "secret-toolkit-utils", + "serde", +] + +[[package]] +name = "secret-toolkit-snip721" +version = "0.10.0" +source = "git+https://github.com/scrtlabs/secret-toolkit?tag=v0.10.0#9e139bedab9eeb60f2bdbf9d4f1d2bb069886ea9" +dependencies = [ + "schemars", + "secret-cosmwasm-std", + "secret-toolkit-utils", + "serde", +] + +[[package]] +name = "secret-toolkit-storage" +version = "0.10.0" +source = "git+https://github.com/scrtlabs/secret-toolkit?tag=v0.10.0#9e139bedab9eeb60f2bdbf9d4f1d2bb069886ea9" +dependencies = [ + "secret-cosmwasm-std", + "secret-cosmwasm-storage", + "secret-toolkit-serialization", + "serde", +] + +[[package]] +name = "secret-toolkit-utils" +version = "0.10.0" +source = "git+https://github.com/scrtlabs/secret-toolkit?tag=v0.10.0#9e139bedab9eeb60f2bdbf9d4f1d2bb069886ea9" +dependencies = [ + "schemars", + "secret-cosmwasm-std", + "secret-cosmwasm-storage", + "serde", +] + [[package]] name = "semver" version = "1.0.23" @@ -1678,9 +1956,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.204" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] @@ -1694,6 +1972,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde-json-wasm" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5" +dependencies = [ + "serde", +] + [[package]] name = "serde-json-wasm" version = "0.5.2" @@ -1723,13 +2010,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.90", ] [[package]] @@ -1740,14 +2027,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.90", ] [[package]] name = "serde_json" -version = "1.0.122" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -1789,6 +2076,16 @@ dependencies = [ "keccak", ] +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + [[package]] name = "signature" version = "2.2.0" @@ -2040,6 +2337,22 @@ dependencies = [ "thiserror", ] +[[package]] +name = "skip-go-swap-adapter-shade-protocol" +version = "0.3.0" +dependencies = [ + "cosmwasm-schema", + "cw2 1.1.2", + "schemars", + "secret-cosmwasm-std", + "secret-storage-plus", + "secret-toolkit", + "serde", + "skip", + "test-case", + "thiserror", +] + [[package]] name = "skip-go-swap-adapter-white-whale" version = "0.3.0" @@ -2088,6 +2401,16 @@ dependencies = [ "strum_macros", ] +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der 0.6.1", +] + [[package]] name = "spki" version = "0.7.3" @@ -2095,7 +2418,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der", + "der 0.7.9", ] [[package]] @@ -2123,7 +2446,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.72", + "syn 2.0.90", ] [[package]] @@ -2154,9 +2477,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.72" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -2223,7 +2546,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.90", ] [[package]] @@ -2234,35 +2557,35 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.90", "test-case-core", ] [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.90", ] [[package]] name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "num-conv", @@ -2279,9 +2602,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ "num-conv", "time-core", @@ -2307,15 +2630,15 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "version_check" @@ -2342,7 +2665,7 @@ dependencies = [ "osmosis-std-derive", "prost 0.11.9", "prost-types 0.11.9", - "protobuf 3.4.0", + "protobuf 3.7.1", "schemars", "serde", "uint", diff --git a/contracts/adapters/swap/shade-protocol/Cargo.toml b/contracts/adapters/swap/shade-protocol/Cargo.toml new file mode 100644 index 00000000..6d707208 --- /dev/null +++ b/contracts/adapters/swap/shade-protocol/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "skip-go-swap-adapter-shade-protocol" +version = { workspace = true } +rust-version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +homepage = { workspace = true } +repository = { workspace = true } +documentation = { workspace = true } +keywords = { workspace = true } + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +# for more explicit tests, cargo test --features=backtraces +backtraces = ["cosmwasm-std/backtraces"] +# use library feature to disable all instantiate/execute/query exports +library = [] + +[dependencies] +#astroport = { workspace = true } +#cosmwasm-schema = { workspace = true } +#cosmwasm-std = { workspace = true } +cw2 = { workspace = true } +#cw20 = { workspace = true } +#cw-storage-plus = { workspace = true } +#cw-utils = { workspace = true } +skip = { workspace = true } +thiserror = { workspace = true } + +cosmwasm-std = { package = "secret-cosmwasm-std", version = "1.1.11"} +cosmwasm-schema = "1.4.0" +secret-toolkit = { git = "https://github.com/scrtlabs/secret-toolkit", tag = "v0.10.0" } +secret-storage-plus = { git = "https://github.com/securesecrets/secret-plus-utils", tag = "v0.1.1", features = [] } +serde = "1.0.114" +schemars = "0.8.1" + +[dev-dependencies] +test-case = { workspace = true } diff --git a/contracts/adapters/swap/shade-protocol/README.md b/contracts/adapters/swap/shade-protocol/README.md new file mode 100644 index 00000000..0354b959 --- /dev/null +++ b/contracts/adapters/swap/shade-protocol/README.md @@ -0,0 +1,123 @@ +# Neutron Astroport Swap Adapter Contract + +The Neutron Astroport swap adapter contract is responsible for: +1. Taking the standardized entry point swap operations message format and converting it to Astroport pool swaps message format. +2. Swapping by dispatching swaps to Astroport pool contracts. +3. Providing query methods that can be called by the entry point contract (generally, to any external actor) to simulate multi-hop swaps that either specify an exact amount in (estimating how much would be received from the swap) or an exact amount out (estimating how much is required to get the specified amount out). + +Note: Swap adapter contracts expect to be called by an entry point contract that provides basic validation and minimum amount out safety guarantees for the caller. There are no slippage guarantees provided by swap adapter contracts. + +WARNING: Do not send funds directly to the contract without calling one of its functions. Funds sent directly to the contract do not trigger any contract logic that performs validation / safety checks (as the Cosmos SDK handles direct fund transfers in the `Bank` module and not the `Wasm` module). There are no explicit recovery mechanisms for accidentally sent funds. + +## InstantiateMsg + +Instantiates a new Neutron Astroport swap adapter contract using the Entrypoint contract address provided in the instantiation message. + +``` json +{ + "entry_point_contract_address": "neutron..." +} +``` + +## ExecuteMsg + +### `swap` + +Swaps the coin sent using the operations provided. + +``` json +{ + "swap": { + "operations": [ + { + "pool": "neutron...", + "denom_in": "uatom", + "denom_out": "untrn" + }, + { + "pool": "neutron...", + "denom_in": "untrn", + "denom_out": "uosmo" + } + ] + } +} +``` + +### `transfer_funds_back` + +Transfers all contract funds to the address provided, called by the swap adapter contract to send back the entry point contract the assets received from swapping. + +Note: This function can be called by anyone as the contract is assumed to have no balance before/after it's called by the entry point contract. Do not send funds directly to this contract without calling a function. + +``` json +{ + "transfer_funds_back": { + "caller": "neutron..." + } +} +``` + +## QueryMsg + +### `simulate_swap_exact_coin_out` + +Returns the coin in required to receive the `coin_out` specified in the call (swapped through the `swap_operatons` provided) + +Query: +``` json +{ + "simulate_swap_exact_coin_out": { + "coin_out": { + "denom": "untrn", + "amount": "200000" + }, + "swap_operations": [ + { + "pool": "neutron...", + "denom_in": "uatom", + "denom_out": "untrn" + } + ] + } +} +``` + +Response: +``` json +{ + "denom": "uatom", + "amount": "100" +} +``` + +### `simulate_swap_exact_coin_in` + +Returns the coin out that would be received from swapping the `coin_in` specified in the call (swapped through the `swap_operatons` provided) + +Query: +``` json +{ + "simulate_swap_exact_coin_in": { + "coin_in": { + "denom": "uatom", + "amount": "100" + }, + "swap_operations": [ + { + "pool": "neutron...", + "denom_in": "uatom", + "denom_out": "untrn" + } + ] + } +} +``` + +Response: +``` json +{ + "denom": "untrn", + "amount": "100000" +} +``` \ No newline at end of file diff --git a/contracts/adapters/swap/shade-protocol/src/bin/shade-protocol-schema.rs b/contracts/adapters/swap/shade-protocol/src/bin/shade-protocol-schema.rs new file mode 100644 index 00000000..4f4733f0 --- /dev/null +++ b/contracts/adapters/swap/shade-protocol/src/bin/shade-protocol-schema.rs @@ -0,0 +1,10 @@ +use cosmwasm_schema::write_api; +use skip::swap::{ExecuteMsg, InstantiateMsg, QueryMsg}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + execute: ExecuteMsg, + query: QueryMsg + } +} diff --git a/contracts/adapters/swap/shade-protocol/src/contract.rs b/contracts/adapters/swap/shade-protocol/src/contract.rs new file mode 100644 index 00000000..5ecf8e92 --- /dev/null +++ b/contracts/adapters/swap/shade-protocol/src/contract.rs @@ -0,0 +1,812 @@ +use crate::{ + error::{ContractError, ContractResult}, + state::{ + ENTRY_POINT_CONTRACT, REGISTERED_TOKENS, SHADE_POOL_CODE_HASH, SHADE_ROUTER_CONTRACT, + VIEWING_KEY, + }, +}; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{ + entry_point, from_binary, to_binary, Addr, Binary, ContractInfo, CosmosMsg, Decimal, Deps, + DepsMut, Env, MessageInfo, Response, Uint128, WasmMsg, +}; +// use cw2::set_contract_version; +use secret_toolkit::snip20; +use skip::{ + asset::{get_current_asset_available, Asset}, + error::SkipError, + swap::{ + get_ask_denom_for_routes, Cw20HookMsg, QueryMsg, Route, + SimulateSmartSwapExactAssetInResponse, SimulateSwapExactAssetInResponse, + SimulateSwapExactAssetOutResponse, SwapOperation, + }, +}; + +use crate::shade_swap_router_msg::{Hop, InvokeMsg}; + +#[cw_serde] +pub struct InstantiateMsg { + pub entry_point_contract: ContractInfo, + pub shade_protocol_router_contract: ContractInfo, + pub shade_pool_code_hash: String, + pub viewing_key: String, +} + +#[cw_serde] +pub struct MigrateMsg { + pub entry_point_contract: ContractInfo, + pub shade_protocol_router_contract: ContractInfo, + pub shade_pool_code_hash: String, + pub viewing_key: String, +} + +#[cw_serde] +pub enum ExecuteMsg { + Receive(Snip20ReceiveMsg), + TransferFundsBack { swapper: Addr, return_denom: String }, + RegisterTokens { contracts: Vec }, +} + +#[cw_serde] +pub struct Snip20ReceiveMsg { + pub sender: Addr, + pub from: Addr, + pub amount: Uint128, + pub memo: Option, + pub msg: Option, +} + +// Contract name and version used for migration. +const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); +const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +/////////////// +/// MIGRATE /// +/////////////// + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> ContractResult { + // Set contract version + // set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + // Validate entry point contract address + let checked_entry_point_contract = ContractInfo { + address: deps + .api + .addr_validate(&msg.entry_point_contract.address.to_string())?, + code_hash: msg.entry_point_contract.code_hash, + }; + + // Store the entry point contract address + ENTRY_POINT_CONTRACT.save(deps.storage, &checked_entry_point_contract)?; + + Ok(Response::new() + .add_attribute("action", "migrate") + .add_attribute( + "entry_point_contract_address", + checked_entry_point_contract.address.to_string(), + )) +} + +/////////////////// +/// INSTANTIATE /// +/////////////////// + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + deps: DepsMut, + _env: Env, + _info: MessageInfo, + msg: InstantiateMsg, +) -> ContractResult { + // Set contract version + // set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + // Validate entry point contract + let checked_entry_point_contract = ContractInfo { + address: deps + .api + .addr_validate(&msg.entry_point_contract.address.to_string())?, + code_hash: msg.entry_point_contract.code_hash, + }; + + // Store the entry point contract address + ENTRY_POINT_CONTRACT.save(deps.storage, &checked_entry_point_contract)?; + SHADE_ROUTER_CONTRACT.save(deps.storage, &msg.shade_protocol_router_contract)?; + SHADE_POOL_CODE_HASH.save(deps.storage, &msg.shade_pool_code_hash)?; + VIEWING_KEY.save(deps.storage, &msg.viewing_key)?; + + Ok(Response::new() + .add_attribute("action", "instantiate") + .add_attribute( + "entry_point_contract_address", + checked_entry_point_contract.address.to_string(), + ) + .add_attribute( + "shade_protocol_router_contract_address", + msg.shade_protocol_router_contract.address, + )) +} + +/////////////// +/// RECEIVE /// +/////////////// + +// Receive is the main entry point for the contract to +// receive cw20 tokens and execute the swap +pub fn receive_snip20( + deps: DepsMut, + env: Env, + mut info: MessageInfo, + snip20_msg: Snip20ReceiveMsg, +) -> ContractResult { + // Set the sender to the originating address that triggered the cw20 send call + // This is later validated / enforced to be the entry point contract address + info.sender = deps.api.addr_validate(&snip20_msg.sender.to_string())?; + + match snip20_msg.msg { + Some(msg) => match from_binary(&msg)? { + Cw20HookMsg::Swap { operations } => { + execute_swap(deps, env, info, operations, snip20_msg.amount) + } + }, + None => Ok(Response::default()), + } +} + +/////////////// +/// EXECUTE /// +/////////////// + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> ContractResult { + match msg { + ExecuteMsg::Receive(snip20_msg) => receive_snip20(deps, env, info, snip20_msg), + ExecuteMsg::TransferFundsBack { + swapper, + return_denom, + } => Ok(execute_transfer_funds_back( + deps, + env, + info, + swapper, + return_denom, + )?), + ExecuteMsg::RegisterTokens { contracts } => register_tokens(deps, env, contracts), + _ => unimplemented!(), + } +} + +fn execute_swap( + deps: DepsMut, + env: Env, + info: MessageInfo, + operations: Vec, + input_amount: Uint128, +) -> ContractResult { + // Get entry point contract address from storage + let entry_point_contract = ENTRY_POINT_CONTRACT.load(deps.storage)?; + // Get swap router from storage + let shade_router_contract = SHADE_ROUTER_CONTRACT.load(deps.storage)?; + + // Get pool code hash from storage + let pool_code_hash = SHADE_POOL_CODE_HASH.load(deps.storage)?; + + // Enforce the caller is the entry point contract + if info.sender != entry_point_contract.address { + return Err(ContractError::Unauthorized); + } + + // Build shade router swap message + let mut path = vec![]; + for operation in &operations { + path.push(Hop { + addr: operation.pool.to_string(), + code_hash: pool_code_hash.clone(), + }); + } + + // Input denom will be sent to router + let input_denom = match operations.first() { + Some(first_op) => first_op.denom_in.clone(), + None => return Err(ContractError::SwapOperationsEmpty), + }; + // Used for transfer funds back + let return_denom = match operations.last() { + Some(last_op) => last_op.denom_out.clone(), + None => return Err(ContractError::SwapOperationsEmpty), + }; + + // Create a response object to return + Ok(Response::new() + .add_attribute("action", "execute_swap") + .add_attribute("action", "dispatch_swaps_and_transfer_back") + // Swap router execution + .add_message(snip20::send_msg( + shade_router_contract.address.to_string(), + input_amount, + Some(to_binary(&InvokeMsg::SwapTokensForExact { + path, + expected_return: None, + recipient: None, + })?), + None, + None, + 255, + shade_router_contract.code_hash, + input_denom, + )?) + // TransferFundsBack message to self + .add_message(WasmMsg::Execute { + contract_addr: env.contract.address.to_string(), + code_hash: env.contract.code_hash, + msg: to_binary(&ExecuteMsg::TransferFundsBack { + swapper: entry_point_contract.address, + return_denom, + })?, + funds: vec![], + })) +} + +fn register_tokens( + deps: DepsMut, + env: Env, + contracts: Vec, +) -> ContractResult { + let mut response = Response::new(); + + for contract in contracts.iter() { + // Add to storage for later use of code hash + REGISTERED_TOKENS.save(deps.storage, contract.address.clone(), contract)?; + // register receive, set viewing key, & add attribute + response = response + .add_attribute("register_token", contract.address.clone()) + .add_messages(vec![ + snip20::set_viewing_key_msg( + VIEWING_KEY.load(deps.storage)?, + None, + 255, + contract.code_hash.clone(), + contract.address.to_string(), + )?, + snip20::register_receive_msg( + env.contract.code_hash.clone(), + None, + 255, + contract.code_hash.clone(), + contract.address.to_string(), + )?, + ]); + } + + Ok(response) +} + +pub fn execute_transfer_funds_back( + deps: DepsMut, + env: Env, + info: MessageInfo, + swapper: Addr, + return_denom: String, +) -> Result { + // Ensure the caller is the contract itself + if info.sender != env.contract.address { + return Err(SkipError::Unauthorized); + } + + // load entry point contract for sending funds to + let entry_point_contract = match ENTRY_POINT_CONTRACT.load(deps.storage) { + Ok(contract) => contract, + Err(e) => return Err(SkipError::Std(e)), + }; + + // Validate return_denom + let return_denom = match deps.api.addr_validate(&return_denom) { + Ok(addr) => addr, + Err(_) => return Err(SkipError::InvalidCw20Coin), + }; + + // Load token contract + let token_contract = match REGISTERED_TOKENS.load(deps.storage, return_denom) { + Ok(contract) => contract, + Err(_) => return Err(SkipError::InvalidCw20Coin), + }; + + // Load viewing key for balance fetch + let viewing_key = match VIEWING_KEY.load(deps.storage) { + Ok(key) => key, + Err(e) => return Err(SkipError::Std(e)), + }; + + let balance = match snip20::balance_query( + deps.querier, + env.contract.address.to_string(), + viewing_key, + 255, + token_contract.code_hash, + token_contract.address.to_string(), + ) { + Ok(balance) => balance, + Err(e) => return Err(SkipError::Std(e)), + }; + + let transfer_msg = match snip20::send_msg( + entry_point_contract.address.to_string(), + balance.amount, + None, + None, + None, + 255, + token_contract.code_hash, + token_contract.address.to_string(), + ) { + Ok(msg) => msg, + Err(e) => return Err(SkipError::Std(e)), + }; + + Ok(Response::new() + .add_message(transfer_msg) + .add_attribute("action", "dispatch_transfer_funds_back_bank_send")) +} + +///////////// +/// QUERY /// +///////////// + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> ContractResult { + match msg { + QueryMsg::SimulateSwapExactAssetIn { + asset_in, + swap_operations, + } => to_binary(&query_simulate_swap_exact_asset_in( + deps, + asset_in, + swap_operations, + )?), + QueryMsg::SimulateSwapExactAssetOut { + asset_out, + swap_operations, + } => to_binary(&query_simulate_swap_exact_asset_out( + deps, + asset_out, + swap_operations, + )?), + QueryMsg::SimulateSwapExactAssetInWithMetadata { + asset_in, + swap_operations, + include_spot_price, + } => to_binary(&query_simulate_swap_exact_asset_in_with_metadata( + deps, + asset_in, + swap_operations, + include_spot_price, + )?), + QueryMsg::SimulateSwapExactAssetOutWithMetadata { + asset_out, + swap_operations, + include_spot_price, + } => to_binary(&query_simulate_swap_exact_asset_out_with_metadata( + deps, + asset_out, + swap_operations, + include_spot_price, + )?), + QueryMsg::SimulateSmartSwapExactAssetIn { routes, .. } => { + let ask_denom = get_ask_denom_for_routes(&routes)?; + + to_binary(&query_simulate_smart_swap_exact_asset_in( + deps, ask_denom, routes, + )?) + } + QueryMsg::SimulateSmartSwapExactAssetInWithMetadata { + asset_in, + routes, + include_spot_price, + } => { + let ask_denom = get_ask_denom_for_routes(&routes)?; + + to_binary(&query_simulate_smart_swap_exact_asset_in_with_metadata( + deps, + asset_in, + ask_denom, + routes, + include_spot_price, + )?) + } + } + .map_err(From::from) +} + +// Queries the astroport pool contracts to simulate a swap exact amount in +fn query_simulate_swap_exact_asset_in( + deps: Deps, + asset_in: Asset, + swap_operations: Vec, +) -> ContractResult { + // Error if swap operations is empty + let Some(first_op) = swap_operations.first() else { + return Err(ContractError::SwapOperationsEmpty); + }; + + // Ensure asset_in's denom is the same as the first swap operation's denom in + if asset_in.denom() != first_op.denom_in { + return Err(ContractError::CoinInDenomMismatch); + } + + let (asset_out, _) = simulate_swap_exact_asset_in(deps, asset_in, swap_operations, false)?; + + // Return the asset out + Ok(asset_out) +} + +// Queries the astroport pool contracts to simulate a multi-hop swap exact amount out +fn query_simulate_swap_exact_asset_out( + deps: Deps, + asset_out: Asset, + swap_operations: Vec, +) -> ContractResult { + // Error if swap operations is empty + let Some(last_op) = swap_operations.last() else { + return Err(ContractError::SwapOperationsEmpty); + }; + + // Ensure asset_out's denom is the same as the last swap operation's denom out + if asset_out.denom() != last_op.denom_out { + return Err(ContractError::CoinOutDenomMismatch); + } + + let (asset_in, _) = simulate_swap_exact_asset_out(deps, asset_out, swap_operations, false)?; + + // Return the asset in needed + Ok(asset_in) +} + +fn query_simulate_smart_swap_exact_asset_in( + deps: Deps, + ask_denom: String, + routes: Vec, +) -> ContractResult { + let (asset_out, _) = simulate_smart_swap_exact_asset_in(deps, ask_denom, routes, false)?; + + Ok(asset_out) +} + +// Queries the astroport pool contracts to simulate a swap exact amount in with metadata +fn query_simulate_swap_exact_asset_in_with_metadata( + deps: Deps, + asset_in: Asset, + swap_operations: Vec, + include_spot_price: bool, +) -> ContractResult { + // Error if swap operations is empty + let Some(first_op) = swap_operations.first() else { + return Err(ContractError::SwapOperationsEmpty); + }; + + // Ensure asset_in's denom is the same as the first swap operation's denom in + if asset_in.denom() != first_op.denom_in { + return Err(ContractError::CoinInDenomMismatch); + } + + // Determine if we should request the simulation responses from simulate_swap_exact_asset_in + let mut include_sim_resps = false; + if include_spot_price { + include_sim_resps = true; + } + + // Simulate the swap exact amount in + let (asset_out, sim_resps) = simulate_swap_exact_asset_in( + deps, + asset_in.clone(), + swap_operations.clone(), + include_sim_resps, + )?; + + // Create the response + let mut response = SimulateSwapExactAssetInResponse { + asset_out, + spot_price: None, + }; + + // Include the spot price in the response if requested + if include_spot_price { + response.spot_price = Some(calculate_spot_price_from_simulation_responses( + deps, + asset_in, + swap_operations, + sim_resps, + )?) + } + + Ok(response) +} + +// Queries the astroport pool contracts to simulate a multi-hop swap exact amount out with metadata +fn query_simulate_swap_exact_asset_out_with_metadata( + deps: Deps, + asset_out: Asset, + swap_operations: Vec, + include_spot_price: bool, +) -> ContractResult { + // Error if swap operations is empty + let Some(last_op) = swap_operations.last() else { + return Err(ContractError::SwapOperationsEmpty); + }; + + // Ensure asset_out's denom is the same as the last swap operation's denom out + if asset_out.denom() != last_op.denom_out { + return Err(ContractError::CoinOutDenomMismatch); + } + + // Determine if we should request the simulation responses from simulate_swap_exact_asset_out + let mut include_sim_resps = false; + if include_spot_price { + include_sim_resps = true; + } + + // Simulate the swap exact amount out + let (asset_in, sim_resps) = simulate_swap_exact_asset_out( + deps, + asset_out.clone(), + swap_operations.clone(), + include_sim_resps, + )?; + + // Create the response + let mut response = SimulateSwapExactAssetOutResponse { + asset_in, + spot_price: None, + }; + + // Include the spot price in the response if requested + if include_spot_price { + response.spot_price = Some(calculate_spot_price_from_reverse_simulation_responses( + deps, + asset_out, + swap_operations, + sim_resps, + )?) + } + + Ok(response) +} + +fn query_simulate_smart_swap_exact_asset_in_with_metadata( + deps: Deps, + asset_in: Asset, + ask_denom: String, + routes: Vec, + include_spot_price: bool, +) -> ContractResult { + let (asset_out, simulation_responses) = + simulate_smart_swap_exact_asset_in(deps, ask_denom, routes.clone(), include_spot_price)?; + + let mut response = SimulateSmartSwapExactAssetInResponse { + asset_out, + spot_price: None, + }; + + if include_spot_price { + response.spot_price = Some(calculate_weighted_spot_price_from_simulation_responses( + deps, + asset_in, + routes, + simulation_responses, + )?) + } + + Ok(response) +} + +fn assert_max_spread(return_amount: Uint128, spread_amount: Uint128) -> ContractResult<()> { + let max_spread = MAX_ALLOWED_SLIPPAGE.parse::()?; + if Decimal::from_ratio(spread_amount, return_amount + spread_amount) > max_spread { + return Err(ContractError::MaxSpreadAssertion {}); + } + Ok(()) +} + +// Simulates a swap exact amount in request, returning the asset out and optionally the reverse simulation responses +fn simulate_swap_exact_asset_in( + deps: Deps, + asset_in: Asset, + swap_operations: Vec, + include_responses: bool, +) -> ContractResult<(Asset, Vec)> { + let (asset_out, responses) = swap_operations.iter().try_fold( + (asset_in, Vec::new()), + |(asset_out, mut responses), operation| -> Result<_, ContractError> { + // Get the astroport offer asset type + let astroport_offer_asset = asset_out.into_astroport_asset(deps.api)?; + + // Query the astroport pool contract to get the simulation response + let res: SimulationResponse = deps.querier.query_wasm_smart( + &operation.pool, + &PairQueryMsg::Simulation { + offer_asset: astroport_offer_asset, + ask_asset_info: None, + }, + )?; + + // Assert the operation does not exceed the max spread limit + assert_max_spread(res.return_amount, res.spread_amount)?; + + if include_responses { + responses.push(res.clone()); + } + + Ok(( + Asset::new(deps.api, &operation.denom_out, res.return_amount), + responses, + )) + }, + )?; + + Ok((asset_out, responses)) +} + +// Simulates a swap exact amount out request, returning the asset in needed and optionally the reverse simulation responses +fn simulate_swap_exact_asset_out( + deps: Deps, + asset_out: Asset, + swap_operations: Vec, + include_responses: bool, +) -> ContractResult<(Asset, Vec)> { + let (asset_in, responses) = swap_operations.iter().rev().try_fold( + (asset_out, Vec::new()), + |(asset_in_needed, mut responses), operation| -> Result<_, ContractError> { + // Get the astroport ask asset type + let astroport_ask_asset = asset_in_needed.into_astroport_asset(deps.api)?; + + // Query the astroport pool contract to get the reverse simulation response + let res: ReverseSimulationResponse = deps.querier.query_wasm_smart( + &operation.pool, + &PairQueryMsg::ReverseSimulation { + offer_asset_info: None, + ask_asset: astroport_ask_asset, + }, + )?; + + // Assert the operation does not exceed the max spread limit + assert_max_spread(res.offer_amount, res.spread_amount)?; + + if include_responses { + responses.push(res.clone()); + } + + Ok(( + Asset::new( + deps.api, + &operation.denom_in, + res.offer_amount.checked_add(Uint128::one())?, + ), + responses, + )) + }, + )?; + + Ok((asset_in, responses)) +} + +fn simulate_smart_swap_exact_asset_in( + deps: Deps, + ask_denom: String, + routes: Vec, + include_responses: bool, +) -> ContractResult<(Asset, Vec>)> { + let mut asset_out = Asset::new(deps.api, &ask_denom, Uint128::zero()); + let mut simulation_responses = Vec::new(); + + for route in &routes { + let (route_asset_out, route_simulation_responses) = simulate_swap_exact_asset_in( + deps, + route.offer_asset.clone(), + route.operations.clone(), + include_responses, + )?; + + asset_out.add(route_asset_out.amount())?; + + if include_responses { + simulation_responses.push(route_simulation_responses); + } + } + + Ok((asset_out, simulation_responses)) +} + +fn calculate_weighted_spot_price_from_simulation_responses( + deps: Deps, + asset_in: Asset, + routes: Vec, + simulation_responses: Vec>, +) -> ContractResult { + let spot_price = routes.into_iter().zip(simulation_responses).try_fold( + Decimal::zero(), + |curr_spot_price, (route, res)| -> ContractResult { + let route_spot_price = calculate_spot_price_from_simulation_responses( + deps, + asset_in.clone(), + route.operations, + res, + )?; + + let weight = Decimal::from_ratio(route.offer_asset.amount(), asset_in.amount()); + + Ok(curr_spot_price + (route_spot_price * weight)) + }, + )?; + + Ok(spot_price) +} + +// Calculate the spot price using simulation responses +fn calculate_spot_price_from_simulation_responses( + deps: Deps, + asset_in: Asset, + swap_operations: Vec, + simulation_responses: Vec, +) -> ContractResult { + let (_, spot_price) = swap_operations.iter().zip(simulation_responses).try_fold( + (asset_in, Decimal::one()), + |(asset_out, curr_spot_price), (op, res)| -> Result<_, ContractError> { + // Calculate the amount out without slippage + let amount_out_without_slippage = res + .return_amount + .checked_add(res.spread_amount)? + .checked_add(res.commission_amount)?; + + Ok(( + Asset::new(deps.api, &op.denom_out, res.return_amount), + curr_spot_price.checked_mul(Decimal::from_ratio( + amount_out_without_slippage, + asset_out.amount(), + ))?, + )) + }, + )?; + + Ok(spot_price) +} + +// Calculates the spot price using reverse simulaation responses +fn calculate_spot_price_from_reverse_simulation_responses( + deps: Deps, + asset_out: Asset, + swap_operations: Vec, + reverse_simulation_responses: Vec, +) -> ContractResult { + let (_, spot_price) = swap_operations + .iter() + .rev() + .zip(reverse_simulation_responses) + .try_fold( + (asset_out, Decimal::one()), + |(asset_in_needed, curr_spot_price), (op, res)| -> Result<_, ContractError> { + let amount_out_without_slippage = asset_in_needed + .amount() + .checked_add(res.spread_amount)? + .checked_add(res.commission_amount)?; + + Ok(( + Asset::new( + deps.api, + &op.denom_in, + res.offer_amount.checked_add(Uint128::one())?, + ), + curr_spot_price.checked_mul(Decimal::from_ratio( + amount_out_without_slippage, + res.offer_amount, + ))?, + )) + }, + )?; + + Ok(spot_price) +} diff --git a/contracts/adapters/swap/shade-protocol/src/error.rs b/contracts/adapters/swap/shade-protocol/src/error.rs new file mode 100644 index 00000000..84d8b0e9 --- /dev/null +++ b/contracts/adapters/swap/shade-protocol/src/error.rs @@ -0,0 +1,38 @@ +use cosmwasm_std::{OverflowError, StdError}; +use skip::error::SkipError; +use thiserror::Error; + +pub type ContractResult = core::result::Result; + +#[derive(Error, Debug, PartialEq)] +pub enum ContractError { + #[error(transparent)] + Std(#[from] StdError), + + #[error(transparent)] + Overflow(#[from] OverflowError), + + #[error(transparent)] + Skip(#[from] SkipError), + + #[error(transparent)] + Payment(#[from] cw_utils::PaymentError), + + #[error("Unauthorized")] + Unauthorized, + + #[error("swap_operations cannot be empty")] + SwapOperationsEmpty, + + #[error("coin_in denom must match the first swap operation's denom in")] + CoinInDenomMismatch, + + #[error("coin_out denom must match the last swap operation's denom out")] + CoinOutDenomMismatch, + + #[error("Operation exceeds max spread limit")] + MaxSpreadAssertion, + + #[error("Contract has no balance of offer asset")] + NoOfferAssetAmount, +} diff --git a/contracts/adapters/swap/shade-protocol/src/lib.rs b/contracts/adapters/swap/shade-protocol/src/lib.rs new file mode 100644 index 00000000..588dba76 --- /dev/null +++ b/contracts/adapters/swap/shade-protocol/src/lib.rs @@ -0,0 +1,4 @@ +pub mod contract; +pub mod error; +pub mod shade_swap_router_msg; +pub mod state; diff --git a/contracts/adapters/swap/shade-protocol/src/shade_swap_router_msg.rs b/contracts/adapters/swap/shade-protocol/src/shade_swap_router_msg.rs new file mode 100644 index 00000000..43299593 --- /dev/null +++ b/contracts/adapters/swap/shade-protocol/src/shade_swap_router_msg.rs @@ -0,0 +1,127 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{ + Addr, Binary, ContractInfo, Decimal, Decimal256, Deps, DepsMut, Env, MessageInfo, Response, + Uint128, Uint256, WasmMsg, +}; +use schemars::JsonSchema; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[cw_serde] +pub struct TokenAmount { + pub token: TokenType, + pub amount: Uint128, +} + +#[cw_serde] +pub enum TokenType { + CustomToken { + contract_addr: Addr, + token_code_hash: String, + }, + NativeToken { + denom: String, + }, +} + +#[cw_serde] +pub struct StableTokenData { + pub oracle_key: String, + pub decimals: u8, +} + +#[cw_serde] +pub struct StableTokenType { + pub token: TokenType, + pub stable_token_data: StableTokenData, +} + +#[derive(Clone, Debug, JsonSchema)] +pub struct TokenPair(pub TokenType, pub TokenType, pub bool); + +pub struct TokenPairIterator<'a> { + pair: &'a TokenPair, + index: u8, +} + +impl Serialize for TokenPair { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + (self.0.clone(), self.1.clone(), self.2.clone()).serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for TokenPair { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Deserialize::deserialize(deserializer) + .map(|(token_0, token_1, is_stable)| TokenPair(token_0, token_1, is_stable)) + } +} + +#[cw_serde] +pub enum ExecuteMsgResponse { + SwapResult { + amount_in: Uint128, + amount_out: Uint128, + }, +} + +#[cw_serde] +pub enum InvokeMsg { + SwapTokensForExact { + path: Vec, + expected_return: Option, + recipient: Option, + }, +} + +#[cw_serde] +pub struct Hop { + pub addr: String, + pub code_hash: String, +} + +#[cw_serde] +pub struct SwapResult { + pub return_amount: Uint128, +} + +/* +#[cw_serde] +pub enum ExecuteMsg { + // SNIP20 receiver interface + Receive(Snip20ReceiveMsg), + SwapTokensForExact { + /// The token type to swap from. + offer: TokenAmount, + expected_return: Option, + path: Vec, + recipient: Option, + padding: Option, + }, +} +*/ + +#[cw_serde] +pub enum QueryMsg { + SwapSimulation { + offer: TokenAmount, + path: Vec, + exclude_fee: Option, + }, +} + +#[cw_serde] +pub enum QueryMsgResponse { + SwapSimulation { + total_fee_amount: Uint128, + lp_fee_amount: Uint128, + shade_dao_fee_amount: Uint128, + result: SwapResult, + price: String, + }, +} diff --git a/contracts/adapters/swap/shade-protocol/src/state.rs b/contracts/adapters/swap/shade-protocol/src/state.rs new file mode 100644 index 00000000..a0856f50 --- /dev/null +++ b/contracts/adapters/swap/shade-protocol/src/state.rs @@ -0,0 +1,13 @@ +use cosmwasm_std::{Addr, ContractInfo}; +use secret_storage_plus::Map; +use secret_toolkit::storage::Item; + +pub const ENTRY_POINT_CONTRACT: Item = Item::new(b"entry_point_contract"); + +pub const SHADE_ROUTER_CONTRACT: Item = Item::new(b"shade_router_contract"); + +pub const SHADE_POOL_CODE_HASH: Item = Item::new(b"shade_pair_code_hash"); + +pub const VIEWING_KEY: Item = Item::new(b"viewing_key"); + +pub const REGISTERED_TOKENS: Map = Map::new("registered_tokens"); From ef5c55a7b800e18e5cad84ec3a0600f8a5766569 Mon Sep 17 00:00:00 2001 From: DrPresident Date: Mon, 9 Dec 2024 15:38:11 -0600 Subject: [PATCH 02/29] need to fix error types --- Cargo.lock | 1 + .../adapters/swap/shade-protocol/Cargo.toml | 2 +- .../swap/shade-protocol/src/contract.rs | 384 +++--------------- .../src/shade_swap_router_msg.rs | 16 - 4 files changed, 53 insertions(+), 350 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e1f485a5..e38bb8fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2343,6 +2343,7 @@ version = "0.3.0" dependencies = [ "cosmwasm-schema", "cw2 1.1.2", + "cw20 1.1.2", "schemars", "secret-cosmwasm-std", "secret-storage-plus", diff --git a/contracts/adapters/swap/shade-protocol/Cargo.toml b/contracts/adapters/swap/shade-protocol/Cargo.toml index 6d707208..2bcb11fd 100644 --- a/contracts/adapters/swap/shade-protocol/Cargo.toml +++ b/contracts/adapters/swap/shade-protocol/Cargo.toml @@ -24,7 +24,7 @@ library = [] #cosmwasm-schema = { workspace = true } #cosmwasm-std = { workspace = true } cw2 = { workspace = true } -#cw20 = { workspace = true } +cw20 = { workspace = true } #cw-storage-plus = { workspace = true } #cw-utils = { workspace = true } skip = { workspace = true } diff --git a/contracts/adapters/swap/shade-protocol/src/contract.rs b/contracts/adapters/swap/shade-protocol/src/contract.rs index 5ecf8e92..4a11adae 100644 --- a/contracts/adapters/swap/shade-protocol/src/contract.rs +++ b/contracts/adapters/swap/shade-protocol/src/contract.rs @@ -11,6 +11,7 @@ use cosmwasm_std::{ DepsMut, Env, MessageInfo, Response, Uint128, WasmMsg, }; // use cw2::set_contract_version; +use cw20::Cw20Coin; use secret_toolkit::snip20; use skip::{ asset::{get_current_asset_available, Asset}, @@ -22,7 +23,7 @@ use skip::{ }, }; -use crate::shade_swap_router_msg::{Hop, InvokeMsg}; +use crate::shade_swap_router_msg as shade_router; #[cw_serde] pub struct InstantiateMsg { @@ -205,7 +206,7 @@ fn execute_swap( // Build shade router swap message let mut path = vec![]; for operation in &operations { - path.push(Hop { + path.push(shade_router::Hop { addr: operation.pool.to_string(), code_hash: pool_code_hash.clone(), }); @@ -230,7 +231,7 @@ fn execute_swap( .add_message(snip20::send_msg( shade_router_contract.address.to_string(), input_amount, - Some(to_binary(&InvokeMsg::SwapTokensForExact { + Some(to_binary(&shade_router::InvokeMsg::SwapTokensForExact { path, expected_return: None, recipient: None, @@ -369,6 +370,7 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> ContractResult { asset_in, swap_operations, )?), + /* QueryMsg::SimulateSwapExactAssetOut { asset_out, swap_operations, @@ -377,48 +379,8 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> ContractResult { asset_out, swap_operations, )?), - QueryMsg::SimulateSwapExactAssetInWithMetadata { - asset_in, - swap_operations, - include_spot_price, - } => to_binary(&query_simulate_swap_exact_asset_in_with_metadata( - deps, - asset_in, - swap_operations, - include_spot_price, - )?), - QueryMsg::SimulateSwapExactAssetOutWithMetadata { - asset_out, - swap_operations, - include_spot_price, - } => to_binary(&query_simulate_swap_exact_asset_out_with_metadata( - deps, - asset_out, - swap_operations, - include_spot_price, - )?), - QueryMsg::SimulateSmartSwapExactAssetIn { routes, .. } => { - let ask_denom = get_ask_denom_for_routes(&routes)?; - - to_binary(&query_simulate_smart_swap_exact_asset_in( - deps, ask_denom, routes, - )?) - } - QueryMsg::SimulateSmartSwapExactAssetInWithMetadata { - asset_in, - routes, - include_spot_price, - } => { - let ask_denom = get_ask_denom_for_routes(&routes)?; - - to_binary(&query_simulate_smart_swap_exact_asset_in_with_metadata( - deps, - asset_in, - ask_denom, - routes, - include_spot_price, - )?) - } + */ + _ => unimplemented!(), } .map_err(From::from) } @@ -439,12 +401,13 @@ fn query_simulate_swap_exact_asset_in( return Err(ContractError::CoinInDenomMismatch); } - let (asset_out, _) = simulate_swap_exact_asset_in(deps, asset_in, swap_operations, false)?; + let asset_out = simulate_swap_exact_asset_in(deps, asset_in, swap_operations)?; // Return the asset out Ok(asset_out) } +/* // Queries the astroport pool contracts to simulate a multi-hop swap exact amount out fn query_simulate_swap_exact_asset_out( deps: Deps, @@ -466,191 +429,62 @@ fn query_simulate_swap_exact_asset_out( // Return the asset in needed Ok(asset_in) } +*/ -fn query_simulate_smart_swap_exact_asset_in( - deps: Deps, - ask_denom: String, - routes: Vec, -) -> ContractResult { - let (asset_out, _) = simulate_smart_swap_exact_asset_in(deps, ask_denom, routes, false)?; - - Ok(asset_out) -} - -// Queries the astroport pool contracts to simulate a swap exact amount in with metadata -fn query_simulate_swap_exact_asset_in_with_metadata( +// Simulates a swap exact amount in request, returning the asset out and optionally the reverse simulation responses +fn simulate_swap_exact_asset_in( deps: Deps, asset_in: Asset, swap_operations: Vec, - include_spot_price: bool, -) -> ContractResult { - // Error if swap operations is empty - let Some(first_op) = swap_operations.first() else { - return Err(ContractError::SwapOperationsEmpty); - }; - - // Ensure asset_in's denom is the same as the first swap operation's denom in - if asset_in.denom() != first_op.denom_in { - return Err(ContractError::CoinInDenomMismatch); - } - - // Determine if we should request the simulation responses from simulate_swap_exact_asset_in - let mut include_sim_resps = false; - if include_spot_price { - include_sim_resps = true; - } - - // Simulate the swap exact amount in - let (asset_out, sim_resps) = simulate_swap_exact_asset_in( - deps, - asset_in.clone(), - swap_operations.clone(), - include_sim_resps, - )?; - - // Create the response - let mut response = SimulateSwapExactAssetInResponse { - asset_out, - spot_price: None, - }; - - // Include the spot price in the response if requested - if include_spot_price { - response.spot_price = Some(calculate_spot_price_from_simulation_responses( - deps, - asset_in, - swap_operations, - sim_resps, - )?) - } - - Ok(response) -} - -// Queries the astroport pool contracts to simulate a multi-hop swap exact amount out with metadata -fn query_simulate_swap_exact_asset_out_with_metadata( - deps: Deps, - asset_out: Asset, - swap_operations: Vec, - include_spot_price: bool, -) -> ContractResult { - // Error if swap operations is empty - let Some(last_op) = swap_operations.last() else { - return Err(ContractError::SwapOperationsEmpty); - }; - - // Ensure asset_out's denom is the same as the last swap operation's denom out - if asset_out.denom() != last_op.denom_out { - return Err(ContractError::CoinOutDenomMismatch); - } - - // Determine if we should request the simulation responses from simulate_swap_exact_asset_out - let mut include_sim_resps = false; - if include_spot_price { - include_sim_resps = true; - } - - // Simulate the swap exact amount out - let (asset_in, sim_resps) = simulate_swap_exact_asset_out( - deps, - asset_out.clone(), - swap_operations.clone(), - include_sim_resps, - )?; - - // Create the response - let mut response = SimulateSwapExactAssetOutResponse { - asset_in, - spot_price: None, - }; - - // Include the spot price in the response if requested - if include_spot_price { - response.spot_price = Some(calculate_spot_price_from_reverse_simulation_responses( - deps, - asset_out, - swap_operations, - sim_resps, - )?) - } - - Ok(response) -} +) -> ContractResult { + // Get pool code hash from storage + let pool_code_hash = SHADE_POOL_CODE_HASH.load(deps.storage)?; + // Get router data + let shade_router_contract = SHADE_ROUTER_CONTRACT.load(deps.storage)?; + // Get contract data for asset_in + let asset_in_contract = + REGISTERED_TOKENS.load(deps.storage, deps.api.addr_validate(asset_in.denom())?)?; -fn query_simulate_smart_swap_exact_asset_in_with_metadata( - deps: Deps, - asset_in: Asset, - ask_denom: String, - routes: Vec, - include_spot_price: bool, -) -> ContractResult { - let (asset_out, simulation_responses) = - simulate_smart_swap_exact_asset_in(deps, ask_denom, routes.clone(), include_spot_price)?; - - let mut response = SimulateSmartSwapExactAssetInResponse { - asset_out, - spot_price: None, + let denom_out = match swap_operations.last() { + Some(last_op) => last_op.denom_out.clone(), + None => return Err(ContractError::SwapOperationsEmpty), }; - if include_spot_price { - response.spot_price = Some(calculate_weighted_spot_price_from_simulation_responses( - deps, - asset_in, - routes, - simulation_responses, - )?) - } - - Ok(response) -} - -fn assert_max_spread(return_amount: Uint128, spread_amount: Uint128) -> ContractResult<()> { - let max_spread = MAX_ALLOWED_SLIPPAGE.parse::()?; - if Decimal::from_ratio(spread_amount, return_amount + spread_amount) > max_spread { - return Err(ContractError::MaxSpreadAssertion {}); + let mut path = vec![]; + for operation in swap_operations.iter() { + path.push(shade_router::Hop { + addr: operation.pool.to_string(), + code_hash: pool_code_hash.clone(), + }); } - Ok(()) -} -// Simulates a swap exact amount in request, returning the asset out and optionally the reverse simulation responses -fn simulate_swap_exact_asset_in( - deps: Deps, - asset_in: Asset, - swap_operations: Vec, - include_responses: bool, -) -> ContractResult<(Asset, Vec)> { - let (asset_out, responses) = swap_operations.iter().try_fold( - (asset_in, Vec::new()), - |(asset_out, mut responses), operation| -> Result<_, ContractError> { - // Get the astroport offer asset type - let astroport_offer_asset = asset_out.into_astroport_asset(deps.api)?; - - // Query the astroport pool contract to get the simulation response - let res: SimulationResponse = deps.querier.query_wasm_smart( - &operation.pool, - &PairQueryMsg::Simulation { - offer_asset: astroport_offer_asset, - ask_asset_info: None, + let sim_response: shade_router::QueryMsgResponse = deps.querier.query_wasm_smart( + &shade_router_contract.address, + &shade_router_contract.code_hash, + &shade_router::QueryMsg::SwapSimulation { + offer: shade_router::TokenAmount { + token: shade_router::TokenType::CustomToken { + contract_addr: deps.api.addr_validate(asset_in.denom())?, + token_code_hash: asset_in_contract.code_hash, }, - )?; - - // Assert the operation does not exceed the max spread limit - assert_max_spread(res.return_amount, res.spread_amount)?; - - if include_responses { - responses.push(res.clone()); - } - - Ok(( - Asset::new(deps.api, &operation.denom_out, res.return_amount), - responses, - )) + amount: Uint128::new(asset_in.amount().u128()), + }, + path, + exclude_fee: None, }, )?; - Ok((asset_out, responses)) + let amount_out = match sim_response { + shade_router::QueryMsgResponse::SwapSimulation { result, .. } => result.return_amount, + }; + + Ok(Asset::Cw20(Cw20Coin { + address: denom_out.to_string(), + amount: amount_out.u128().into(), + })) } +/* // Simulates a swap exact amount out request, returning the asset in needed and optionally the reverse simulation responses fn simulate_swap_exact_asset_out( deps: Deps, @@ -693,120 +527,4 @@ fn simulate_swap_exact_asset_out( Ok((asset_in, responses)) } - -fn simulate_smart_swap_exact_asset_in( - deps: Deps, - ask_denom: String, - routes: Vec, - include_responses: bool, -) -> ContractResult<(Asset, Vec>)> { - let mut asset_out = Asset::new(deps.api, &ask_denom, Uint128::zero()); - let mut simulation_responses = Vec::new(); - - for route in &routes { - let (route_asset_out, route_simulation_responses) = simulate_swap_exact_asset_in( - deps, - route.offer_asset.clone(), - route.operations.clone(), - include_responses, - )?; - - asset_out.add(route_asset_out.amount())?; - - if include_responses { - simulation_responses.push(route_simulation_responses); - } - } - - Ok((asset_out, simulation_responses)) -} - -fn calculate_weighted_spot_price_from_simulation_responses( - deps: Deps, - asset_in: Asset, - routes: Vec, - simulation_responses: Vec>, -) -> ContractResult { - let spot_price = routes.into_iter().zip(simulation_responses).try_fold( - Decimal::zero(), - |curr_spot_price, (route, res)| -> ContractResult { - let route_spot_price = calculate_spot_price_from_simulation_responses( - deps, - asset_in.clone(), - route.operations, - res, - )?; - - let weight = Decimal::from_ratio(route.offer_asset.amount(), asset_in.amount()); - - Ok(curr_spot_price + (route_spot_price * weight)) - }, - )?; - - Ok(spot_price) -} - -// Calculate the spot price using simulation responses -fn calculate_spot_price_from_simulation_responses( - deps: Deps, - asset_in: Asset, - swap_operations: Vec, - simulation_responses: Vec, -) -> ContractResult { - let (_, spot_price) = swap_operations.iter().zip(simulation_responses).try_fold( - (asset_in, Decimal::one()), - |(asset_out, curr_spot_price), (op, res)| -> Result<_, ContractError> { - // Calculate the amount out without slippage - let amount_out_without_slippage = res - .return_amount - .checked_add(res.spread_amount)? - .checked_add(res.commission_amount)?; - - Ok(( - Asset::new(deps.api, &op.denom_out, res.return_amount), - curr_spot_price.checked_mul(Decimal::from_ratio( - amount_out_without_slippage, - asset_out.amount(), - ))?, - )) - }, - )?; - - Ok(spot_price) -} - -// Calculates the spot price using reverse simulaation responses -fn calculate_spot_price_from_reverse_simulation_responses( - deps: Deps, - asset_out: Asset, - swap_operations: Vec, - reverse_simulation_responses: Vec, -) -> ContractResult { - let (_, spot_price) = swap_operations - .iter() - .rev() - .zip(reverse_simulation_responses) - .try_fold( - (asset_out, Decimal::one()), - |(asset_in_needed, curr_spot_price), (op, res)| -> Result<_, ContractError> { - let amount_out_without_slippage = asset_in_needed - .amount() - .checked_add(res.spread_amount)? - .checked_add(res.commission_amount)?; - - Ok(( - Asset::new( - deps.api, - &op.denom_in, - res.offer_amount.checked_add(Uint128::one())?, - ), - curr_spot_price.checked_mul(Decimal::from_ratio( - amount_out_without_slippage, - res.offer_amount, - ))?, - )) - }, - )?; - - Ok(spot_price) -} +*/ diff --git a/contracts/adapters/swap/shade-protocol/src/shade_swap_router_msg.rs b/contracts/adapters/swap/shade-protocol/src/shade_swap_router_msg.rs index 43299593..5e9a8d15 100644 --- a/contracts/adapters/swap/shade-protocol/src/shade_swap_router_msg.rs +++ b/contracts/adapters/swap/shade-protocol/src/shade_swap_router_msg.rs @@ -90,22 +90,6 @@ pub struct SwapResult { pub return_amount: Uint128, } -/* -#[cw_serde] -pub enum ExecuteMsg { - // SNIP20 receiver interface - Receive(Snip20ReceiveMsg), - SwapTokensForExact { - /// The token type to swap from. - offer: TokenAmount, - expected_return: Option, - path: Vec, - recipient: Option, - padding: Option, - }, -} -*/ - #[cw_serde] pub enum QueryMsg { SwapSimulation { From 404bd24fcde168e77ec6bd719ad03fa8cc0428d3 Mon Sep 17 00:00:00 2001 From: DrPresident Date: Mon, 9 Dec 2024 16:07:19 -0600 Subject: [PATCH 03/29] switch storage to a single object --- .../swap/shade-protocol/src/contract.rs | 176 ++++++------------ .../adapters/swap/shade-protocol/src/state.rs | 4 + 2 files changed, 59 insertions(+), 121 deletions(-) diff --git a/contracts/adapters/swap/shade-protocol/src/contract.rs b/contracts/adapters/swap/shade-protocol/src/contract.rs index 4a11adae..f0b4d6c8 100644 --- a/contracts/adapters/swap/shade-protocol/src/contract.rs +++ b/contracts/adapters/swap/shade-protocol/src/contract.rs @@ -1,34 +1,35 @@ use crate::{ error::{ContractError, ContractResult}, - state::{ - ENTRY_POINT_CONTRACT, REGISTERED_TOKENS, SHADE_POOL_CODE_HASH, SHADE_ROUTER_CONTRACT, - VIEWING_KEY, - }, + state::{REGISTERED_TOKENS, STATE}, }; use cosmwasm_schema::cw_serde; use cosmwasm_std::{ - entry_point, from_binary, to_binary, Addr, Binary, ContractInfo, CosmosMsg, Decimal, Deps, - DepsMut, Env, MessageInfo, Response, Uint128, WasmMsg, + entry_point, from_binary, to_binary, Addr, Binary, ContractInfo, Deps, DepsMut, Env, + MessageInfo, Response, Uint128, WasmMsg, }; // use cw2::set_contract_version; use cw20::Cw20Coin; use secret_toolkit::snip20; use skip::{ - asset::{get_current_asset_available, Asset}, + asset::Asset, error::SkipError, - swap::{ - get_ask_denom_for_routes, Cw20HookMsg, QueryMsg, Route, - SimulateSmartSwapExactAssetInResponse, SimulateSwapExactAssetInResponse, - SimulateSwapExactAssetOutResponse, SwapOperation, - }, + swap::{Cw20HookMsg, QueryMsg, SwapOperation}, }; use crate::shade_swap_router_msg as shade_router; +#[cw_serde] +pub struct State { + pub entry_point_contract: ContractInfo, + pub shade_router_contract: ContractInfo, + pub shade_pool_code_hash: String, + pub viewing_key: String, +} + #[cw_serde] pub struct InstantiateMsg { pub entry_point_contract: ContractInfo, - pub shade_protocol_router_contract: ContractInfo, + pub shade_router_contract: ContractInfo, pub shade_pool_code_hash: String, pub viewing_key: String, } @@ -36,7 +37,7 @@ pub struct InstantiateMsg { #[cw_serde] pub struct MigrateMsg { pub entry_point_contract: ContractInfo, - pub shade_protocol_router_contract: ContractInfo, + pub shade_router_contract: ContractInfo, pub shade_pool_code_hash: String, pub viewing_key: String, } @@ -79,7 +80,15 @@ pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> ContractResult, input_amount: Uint128, ) -> ContractResult { - // Get entry point contract address from storage - let entry_point_contract = ENTRY_POINT_CONTRACT.load(deps.storage)?; - // Get swap router from storage - let shade_router_contract = SHADE_ROUTER_CONTRACT.load(deps.storage)?; - - // Get pool code hash from storage - let pool_code_hash = SHADE_POOL_CODE_HASH.load(deps.storage)?; + // Get contract state from storage + let state = STATE.load(deps.storage)?; // Enforce the caller is the entry point contract - if info.sender != entry_point_contract.address { + if info.sender != state.entry_point_contract.address { return Err(ContractError::Unauthorized); } @@ -208,7 +217,7 @@ fn execute_swap( for operation in &operations { path.push(shade_router::Hop { addr: operation.pool.to_string(), - code_hash: pool_code_hash.clone(), + code_hash: state.shade_pool_code_hash.clone(), }); } @@ -229,7 +238,7 @@ fn execute_swap( .add_attribute("action", "dispatch_swaps_and_transfer_back") // Swap router execution .add_message(snip20::send_msg( - shade_router_contract.address.to_string(), + state.shade_router_contract.address.to_string(), input_amount, Some(to_binary(&shade_router::InvokeMsg::SwapTokensForExact { path, @@ -239,7 +248,7 @@ fn execute_swap( None, None, 255, - shade_router_contract.code_hash, + state.shade_router_contract.code_hash, input_denom, )?) // TransferFundsBack message to self @@ -247,7 +256,7 @@ fn execute_swap( contract_addr: env.contract.address.to_string(), code_hash: env.contract.code_hash, msg: to_binary(&ExecuteMsg::TransferFundsBack { - swapper: entry_point_contract.address, + swapper: state.entry_point_contract.address, return_denom, })?, funds: vec![], @@ -261,6 +270,8 @@ fn register_tokens( ) -> ContractResult { let mut response = Response::new(); + let state = STATE.load(deps.storage)?; + for contract in contracts.iter() { // Add to storage for later use of code hash REGISTERED_TOKENS.save(deps.storage, contract.address.clone(), contract)?; @@ -269,7 +280,7 @@ fn register_tokens( .add_attribute("register_token", contract.address.clone()) .add_messages(vec![ snip20::set_viewing_key_msg( - VIEWING_KEY.load(deps.storage)?, + state.viewing_key.clone(), None, 255, contract.code_hash.clone(), @@ -300,9 +311,9 @@ pub fn execute_transfer_funds_back( return Err(SkipError::Unauthorized); } - // load entry point contract for sending funds to - let entry_point_contract = match ENTRY_POINT_CONTRACT.load(deps.storage) { - Ok(contract) => contract, + // load state from storage + let state = match STATE.load(deps.storage) { + Ok(state) => state, Err(e) => return Err(SkipError::Std(e)), }; @@ -318,16 +329,10 @@ pub fn execute_transfer_funds_back( Err(_) => return Err(SkipError::InvalidCw20Coin), }; - // Load viewing key for balance fetch - let viewing_key = match VIEWING_KEY.load(deps.storage) { - Ok(key) => key, - Err(e) => return Err(SkipError::Std(e)), - }; - let balance = match snip20::balance_query( deps.querier, env.contract.address.to_string(), - viewing_key, + state.viewing_key, 255, token_contract.code_hash, token_contract.address.to_string(), @@ -337,7 +342,7 @@ pub fn execute_transfer_funds_back( }; let transfer_msg = match snip20::send_msg( - entry_point_contract.address.to_string(), + state.entry_point_contract.address.to_string(), balance.amount, None, None, @@ -407,40 +412,14 @@ fn query_simulate_swap_exact_asset_in( Ok(asset_out) } -/* -// Queries the astroport pool contracts to simulate a multi-hop swap exact amount out -fn query_simulate_swap_exact_asset_out( - deps: Deps, - asset_out: Asset, - swap_operations: Vec, -) -> ContractResult { - // Error if swap operations is empty - let Some(last_op) = swap_operations.last() else { - return Err(ContractError::SwapOperationsEmpty); - }; - - // Ensure asset_out's denom is the same as the last swap operation's denom out - if asset_out.denom() != last_op.denom_out { - return Err(ContractError::CoinOutDenomMismatch); - } - - let (asset_in, _) = simulate_swap_exact_asset_out(deps, asset_out, swap_operations, false)?; - - // Return the asset in needed - Ok(asset_in) -} -*/ - // Simulates a swap exact amount in request, returning the asset out and optionally the reverse simulation responses fn simulate_swap_exact_asset_in( deps: Deps, asset_in: Asset, swap_operations: Vec, ) -> ContractResult { - // Get pool code hash from storage - let pool_code_hash = SHADE_POOL_CODE_HASH.load(deps.storage)?; - // Get router data - let shade_router_contract = SHADE_ROUTER_CONTRACT.load(deps.storage)?; + // Load state from storage + let state = STATE.load(deps.storage)?; // Get contract data for asset_in let asset_in_contract = REGISTERED_TOKENS.load(deps.storage, deps.api.addr_validate(asset_in.denom())?)?; @@ -454,13 +433,13 @@ fn simulate_swap_exact_asset_in( for operation in swap_operations.iter() { path.push(shade_router::Hop { addr: operation.pool.to_string(), - code_hash: pool_code_hash.clone(), + code_hash: state.shade_pool_code_hash.clone(), }); } let sim_response: shade_router::QueryMsgResponse = deps.querier.query_wasm_smart( - &shade_router_contract.address, - &shade_router_contract.code_hash, + &state.shade_router_contract.address, + &state.shade_router_contract.code_hash, &shade_router::QueryMsg::SwapSimulation { offer: shade_router::TokenAmount { token: shade_router::TokenType::CustomToken { @@ -483,48 +462,3 @@ fn simulate_swap_exact_asset_in( amount: amount_out.u128().into(), })) } - -/* -// Simulates a swap exact amount out request, returning the asset in needed and optionally the reverse simulation responses -fn simulate_swap_exact_asset_out( - deps: Deps, - asset_out: Asset, - swap_operations: Vec, - include_responses: bool, -) -> ContractResult<(Asset, Vec)> { - let (asset_in, responses) = swap_operations.iter().rev().try_fold( - (asset_out, Vec::new()), - |(asset_in_needed, mut responses), operation| -> Result<_, ContractError> { - // Get the astroport ask asset type - let astroport_ask_asset = asset_in_needed.into_astroport_asset(deps.api)?; - - // Query the astroport pool contract to get the reverse simulation response - let res: ReverseSimulationResponse = deps.querier.query_wasm_smart( - &operation.pool, - &PairQueryMsg::ReverseSimulation { - offer_asset_info: None, - ask_asset: astroport_ask_asset, - }, - )?; - - // Assert the operation does not exceed the max spread limit - assert_max_spread(res.offer_amount, res.spread_amount)?; - - if include_responses { - responses.push(res.clone()); - } - - Ok(( - Asset::new( - deps.api, - &operation.denom_in, - res.offer_amount.checked_add(Uint128::one())?, - ), - responses, - )) - }, - )?; - - Ok((asset_in, responses)) -} -*/ diff --git a/contracts/adapters/swap/shade-protocol/src/state.rs b/contracts/adapters/swap/shade-protocol/src/state.rs index a0856f50..e7e94b72 100644 --- a/contracts/adapters/swap/shade-protocol/src/state.rs +++ b/contracts/adapters/swap/shade-protocol/src/state.rs @@ -1,7 +1,10 @@ +use crate::contract::State; use cosmwasm_std::{Addr, ContractInfo}; use secret_storage_plus::Map; use secret_toolkit::storage::Item; +pub const STATE: Item = Item::new(b"state"); +/* pub const ENTRY_POINT_CONTRACT: Item = Item::new(b"entry_point_contract"); pub const SHADE_ROUTER_CONTRACT: Item = Item::new(b"shade_router_contract"); @@ -9,5 +12,6 @@ pub const SHADE_ROUTER_CONTRACT: Item = Item::new(b"shade_router_c pub const SHADE_POOL_CODE_HASH: Item = Item::new(b"shade_pair_code_hash"); pub const VIEWING_KEY: Item = Item::new(b"viewing_key"); +*/ pub const REGISTERED_TOKENS: Map = Map::new("registered_tokens"); From 91cdcbfc2eeeec9e4671491e473ab16024b440ed Mon Sep 17 00:00:00 2001 From: DrPresident Date: Tue, 10 Dec 2024 13:08:54 -0600 Subject: [PATCH 04/29] shade protocol contract building --- .../adapters/swap/shade-protocol/Cargo.toml | 2 +- .../swap/shade-protocol/src/contract.rs | 27 +++++----- .../adapters/swap/shade-protocol/src/error.rs | 4 ++ .../adapters/swap/shade-protocol/src/lib.rs | 1 + .../swap/shade-protocol/src/skip_error.rs | 54 +++++++++++++++++++ .../adapters/swap/shade-protocol/src/state.rs | 9 ---- 6 files changed, 75 insertions(+), 22 deletions(-) create mode 100644 contracts/adapters/swap/shade-protocol/src/skip_error.rs diff --git a/contracts/adapters/swap/shade-protocol/Cargo.toml b/contracts/adapters/swap/shade-protocol/Cargo.toml index 2bcb11fd..a9b91b99 100644 --- a/contracts/adapters/swap/shade-protocol/Cargo.toml +++ b/contracts/adapters/swap/shade-protocol/Cargo.toml @@ -26,7 +26,7 @@ library = [] cw2 = { workspace = true } cw20 = { workspace = true } #cw-storage-plus = { workspace = true } -#cw-utils = { workspace = true } +cw-utils = { workspace = true } skip = { workspace = true } thiserror = { workspace = true } diff --git a/contracts/adapters/swap/shade-protocol/src/contract.rs b/contracts/adapters/swap/shade-protocol/src/contract.rs index f0b4d6c8..a8431228 100644 --- a/contracts/adapters/swap/shade-protocol/src/contract.rs +++ b/contracts/adapters/swap/shade-protocol/src/contract.rs @@ -1,5 +1,6 @@ use crate::{ error::{ContractError, ContractResult}, + // skip_error::ContractError, state::{REGISTERED_TOKENS, STATE}, }; use cosmwasm_schema::cw_serde; @@ -12,7 +13,6 @@ use cw20::Cw20Coin; use secret_toolkit::snip20; use skip::{ asset::Asset, - error::SkipError, swap::{Cw20HookMsg, QueryMsg, SwapOperation}, }; @@ -26,6 +26,7 @@ pub struct State { pub viewing_key: String, } +/* #[cw_serde] pub struct InstantiateMsg { pub entry_point_contract: ContractInfo, @@ -41,6 +42,7 @@ pub struct MigrateMsg { pub shade_pool_code_hash: String, pub viewing_key: String, } +*/ #[cw_serde] pub enum ExecuteMsg { @@ -67,7 +69,7 @@ const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); /////////////// #[cfg_attr(not(feature = "library"), entry_point)] -pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> ContractResult { +pub fn migrate(deps: DepsMut, _env: Env, msg: State) -> ContractResult { // Set contract version // set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; @@ -107,7 +109,7 @@ pub fn instantiate( deps: DepsMut, _env: Env, _info: MessageInfo, - msg: InstantiateMsg, + msg: State, ) -> ContractResult { // Set contract version // set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; @@ -192,6 +194,7 @@ pub fn execute( swapper, return_denom, )?), + // Tokens must be registered before they can be swapped ExecuteMsg::RegisterTokens { contracts } => register_tokens(deps, env, contracts), _ => unimplemented!(), } @@ -305,28 +308,28 @@ pub fn execute_transfer_funds_back( info: MessageInfo, swapper: Addr, return_denom: String, -) -> Result { +) -> ContractResult { // Ensure the caller is the contract itself if info.sender != env.contract.address { - return Err(SkipError::Unauthorized); + return Err(ContractError::Unauthorized); } // load state from storage let state = match STATE.load(deps.storage) { Ok(state) => state, - Err(e) => return Err(SkipError::Std(e)), + Err(e) => return Err(ContractError::Std(e)), }; // Validate return_denom let return_denom = match deps.api.addr_validate(&return_denom) { Ok(addr) => addr, - Err(_) => return Err(SkipError::InvalidCw20Coin), + Err(_) => return Err(ContractError::InvalidSnip20Coin), }; // Load token contract let token_contract = match REGISTERED_TOKENS.load(deps.storage, return_denom) { Ok(contract) => contract, - Err(_) => return Err(SkipError::InvalidCw20Coin), + Err(_) => return Err(ContractError::InvalidSnip20Coin), }; let balance = match snip20::balance_query( @@ -334,11 +337,11 @@ pub fn execute_transfer_funds_back( env.contract.address.to_string(), state.viewing_key, 255, - token_contract.code_hash, + token_contract.code_hash.clone(), token_contract.address.to_string(), ) { Ok(balance) => balance, - Err(e) => return Err(SkipError::Std(e)), + Err(e) => return Err(ContractError::Std(e)), }; let transfer_msg = match snip20::send_msg( @@ -348,11 +351,11 @@ pub fn execute_transfer_funds_back( None, None, 255, - token_contract.code_hash, + token_contract.code_hash.clone(), token_contract.address.to_string(), ) { Ok(msg) => msg, - Err(e) => return Err(SkipError::Std(e)), + Err(e) => return Err(ContractError::Std(e)), }; Ok(Response::new() diff --git a/contracts/adapters/swap/shade-protocol/src/error.rs b/contracts/adapters/swap/shade-protocol/src/error.rs index 84d8b0e9..3916fdb3 100644 --- a/contracts/adapters/swap/shade-protocol/src/error.rs +++ b/contracts/adapters/swap/shade-protocol/src/error.rs @@ -1,4 +1,5 @@ use cosmwasm_std::{OverflowError, StdError}; +use cw_utils; use skip::error::SkipError; use thiserror::Error; @@ -35,4 +36,7 @@ pub enum ContractError { #[error("Contract has no balance of offer asset")] NoOfferAssetAmount, + + #[error("Snip20 Coin Sent To Contract Does Not Match Asset")] + InvalidSnip20Coin, } diff --git a/contracts/adapters/swap/shade-protocol/src/lib.rs b/contracts/adapters/swap/shade-protocol/src/lib.rs index 588dba76..f3715d80 100644 --- a/contracts/adapters/swap/shade-protocol/src/lib.rs +++ b/contracts/adapters/swap/shade-protocol/src/lib.rs @@ -1,4 +1,5 @@ pub mod contract; pub mod error; pub mod shade_swap_router_msg; +pub mod skip_error; pub mod state; diff --git a/contracts/adapters/swap/shade-protocol/src/skip_error.rs b/contracts/adapters/swap/shade-protocol/src/skip_error.rs new file mode 100644 index 00000000..1d327f04 --- /dev/null +++ b/contracts/adapters/swap/shade-protocol/src/skip_error.rs @@ -0,0 +1,54 @@ +use cosmwasm_std::{OverflowError, StdError}; +use thiserror::Error; + +#[derive(Error, Debug, PartialEq)] +pub enum SkipError { + /////////////// + /// GENERAL /// + /////////////// + + #[error(transparent)] + Std(#[from] StdError), + + #[error("Unauthorized")] + Unauthorized, + + #[error(transparent)] + Payment(#[from] cw_utils::PaymentError), + + #[error(transparent)] + Overflow(#[from] OverflowError), + + //////////// + /// SWAP /// + //////////// + + #[error("Swap Operations Empty")] + SwapOperationsEmpty, + + #[error("First Swap Operations' Denom In Differs From Swap Asset In Denom")] + SwapOperationsAssetInDenomMismatch, + + #[error("Last Swap Operations' Denom Out Differs From Swap Asset Out Denom")] + SwapOperationsAssetOutDenomMismatch, + + #[error("Routes Empty")] + RoutesEmpty, + + /////////// + /// IBC /// + /////////// + + #[error("Ibc Fees Are Not A Single Coin, Either Multiple Denoms Or No Coin Specified")] + IbcFeesNotOneCoin, + + ///////////// + /// ASSET /// + ///////////// + + #[error("Native Coin Sent To Contract Does Not Match Asset")] + InvalidNativeCoin, + + #[error("Cw20 Coin Sent To Contract Does Not Match Asset")] + InvalidCw20Coin, +} diff --git a/contracts/adapters/swap/shade-protocol/src/state.rs b/contracts/adapters/swap/shade-protocol/src/state.rs index e7e94b72..2addcc98 100644 --- a/contracts/adapters/swap/shade-protocol/src/state.rs +++ b/contracts/adapters/swap/shade-protocol/src/state.rs @@ -4,14 +4,5 @@ use secret_storage_plus::Map; use secret_toolkit::storage::Item; pub const STATE: Item = Item::new(b"state"); -/* -pub const ENTRY_POINT_CONTRACT: Item = Item::new(b"entry_point_contract"); - -pub const SHADE_ROUTER_CONTRACT: Item = Item::new(b"shade_router_contract"); - -pub const SHADE_POOL_CODE_HASH: Item = Item::new(b"shade_pair_code_hash"); - -pub const VIEWING_KEY: Item = Item::new(b"viewing_key"); -*/ pub const REGISTERED_TOKENS: Map = Map::new("registered_tokens"); From ea1a105e106dd90e09969eade9468854a27ff7a1 Mon Sep 17 00:00:00 2001 From: DrPresident Date: Tue, 10 Dec 2024 14:52:09 -0600 Subject: [PATCH 05/29] contract building, no tests --- .../swap/shade-protocol/src/contract.rs | 16 ++---- .../adapters/swap/shade-protocol/src/lib.rs | 1 - .../swap/shade-protocol/src/skip_error.rs | 54 ------------------- 3 files changed, 3 insertions(+), 68 deletions(-) delete mode 100644 contracts/adapters/swap/shade-protocol/src/skip_error.rs diff --git a/contracts/adapters/swap/shade-protocol/src/contract.rs b/contracts/adapters/swap/shade-protocol/src/contract.rs index a8431228..ee0a0dd4 100644 --- a/contracts/adapters/swap/shade-protocol/src/contract.rs +++ b/contracts/adapters/swap/shade-protocol/src/contract.rs @@ -26,7 +26,6 @@ pub struct State { pub viewing_key: String, } -/* #[cw_serde] pub struct InstantiateMsg { pub entry_point_contract: ContractInfo, @@ -42,7 +41,6 @@ pub struct MigrateMsg { pub shade_pool_code_hash: String, pub viewing_key: String, } -*/ #[cw_serde] pub enum ExecuteMsg { @@ -61,8 +59,10 @@ pub struct Snip20ReceiveMsg { } // Contract name and version used for migration. +/* const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +*/ /////////////// /// MIGRATE /// @@ -306,7 +306,7 @@ pub fn execute_transfer_funds_back( deps: DepsMut, env: Env, info: MessageInfo, - swapper: Addr, + _swapper: Addr, return_denom: String, ) -> ContractResult { // Ensure the caller is the contract itself @@ -378,16 +378,6 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> ContractResult { asset_in, swap_operations, )?), - /* - QueryMsg::SimulateSwapExactAssetOut { - asset_out, - swap_operations, - } => to_binary(&query_simulate_swap_exact_asset_out( - deps, - asset_out, - swap_operations, - )?), - */ _ => unimplemented!(), } .map_err(From::from) diff --git a/contracts/adapters/swap/shade-protocol/src/lib.rs b/contracts/adapters/swap/shade-protocol/src/lib.rs index f3715d80..588dba76 100644 --- a/contracts/adapters/swap/shade-protocol/src/lib.rs +++ b/contracts/adapters/swap/shade-protocol/src/lib.rs @@ -1,5 +1,4 @@ pub mod contract; pub mod error; pub mod shade_swap_router_msg; -pub mod skip_error; pub mod state; diff --git a/contracts/adapters/swap/shade-protocol/src/skip_error.rs b/contracts/adapters/swap/shade-protocol/src/skip_error.rs deleted file mode 100644 index 1d327f04..00000000 --- a/contracts/adapters/swap/shade-protocol/src/skip_error.rs +++ /dev/null @@ -1,54 +0,0 @@ -use cosmwasm_std::{OverflowError, StdError}; -use thiserror::Error; - -#[derive(Error, Debug, PartialEq)] -pub enum SkipError { - /////////////// - /// GENERAL /// - /////////////// - - #[error(transparent)] - Std(#[from] StdError), - - #[error("Unauthorized")] - Unauthorized, - - #[error(transparent)] - Payment(#[from] cw_utils::PaymentError), - - #[error(transparent)] - Overflow(#[from] OverflowError), - - //////////// - /// SWAP /// - //////////// - - #[error("Swap Operations Empty")] - SwapOperationsEmpty, - - #[error("First Swap Operations' Denom In Differs From Swap Asset In Denom")] - SwapOperationsAssetInDenomMismatch, - - #[error("Last Swap Operations' Denom Out Differs From Swap Asset Out Denom")] - SwapOperationsAssetOutDenomMismatch, - - #[error("Routes Empty")] - RoutesEmpty, - - /////////// - /// IBC /// - /////////// - - #[error("Ibc Fees Are Not A Single Coin, Either Multiple Denoms Or No Coin Specified")] - IbcFeesNotOneCoin, - - ///////////// - /// ASSET /// - ///////////// - - #[error("Native Coin Sent To Contract Does Not Match Asset")] - InvalidNativeCoin, - - #[error("Cw20 Coin Sent To Contract Does Not Match Asset")] - InvalidCw20Coin, -} From 8be7000ab3d3ea0bc97c5d64b0f62fdb9cd34eea Mon Sep 17 00:00:00 2001 From: DrPresident Date: Tue, 10 Dec 2024 14:53:11 -0600 Subject: [PATCH 06/29] rough start on entry point --- Cargo.lock | 18 + Cargo.toml | 1 + contracts/secret-entry-point/Cargo.toml | 38 + contracts/secret-entry-point/README.md | 268 +++++ contracts/secret-entry-point/src/asset.rs | 610 +++++++++++ .../secret-entry-point/src/bin/schema.rs | 10 + contracts/secret-entry-point/src/contract.rs | 266 +++++ contracts/secret-entry-point/src/error.rs | 96 ++ contracts/secret-entry-point/src/execute.rs | 944 ++++++++++++++++++ contracts/secret-entry-point/src/lib.rs | 8 + contracts/secret-entry-point/src/msg.rs | 180 ++++ contracts/secret-entry-point/src/query.rs | 12 + contracts/secret-entry-point/src/reply.rs | 42 + contracts/secret-entry-point/src/state.rs | 18 + 14 files changed, 2511 insertions(+) create mode 100644 contracts/secret-entry-point/Cargo.toml create mode 100644 contracts/secret-entry-point/README.md create mode 100644 contracts/secret-entry-point/src/asset.rs create mode 100644 contracts/secret-entry-point/src/bin/schema.rs create mode 100644 contracts/secret-entry-point/src/contract.rs create mode 100644 contracts/secret-entry-point/src/error.rs create mode 100644 contracts/secret-entry-point/src/execute.rs create mode 100644 contracts/secret-entry-point/src/lib.rs create mode 100644 contracts/secret-entry-point/src/msg.rs create mode 100644 contracts/secret-entry-point/src/query.rs create mode 100644 contracts/secret-entry-point/src/reply.rs create mode 100644 contracts/secret-entry-point/src/state.rs diff --git a/Cargo.lock b/Cargo.lock index e38bb8fe..8b18b4b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2188,6 +2188,23 @@ dependencies = [ "cw2 1.1.2", ] +[[package]] +name = "skip-go-secret-entry-point" +version = "0.3.0" +dependencies = [ + "cosmwasm-schema", + "cw-utils 1.0.3", + "cw20 1.1.2", + "schemars", + "secret-cosmwasm-std", + "secret-storage-plus", + "secret-toolkit", + "serde", + "skip", + "test-case", + "thiserror", +] + [[package]] name = "skip-go-swap-adapter-astroport" version = "0.3.0" @@ -2342,6 +2359,7 @@ name = "skip-go-swap-adapter-shade-protocol" version = "0.3.0" dependencies = [ "cosmwasm-schema", + "cw-utils 1.0.3", "cw2 1.1.2", "cw20 1.1.2", "schemars", diff --git a/Cargo.toml b/Cargo.toml index dfcdab6e..2b0d6065 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ resolver = "2" members = [ "contracts/entry-point", + "contracts/secret-entry-point", "contracts/adapters/hyperlane", "contracts/adapters/ibc/*", "contracts/adapters/swap/*", diff --git a/contracts/secret-entry-point/Cargo.toml b/contracts/secret-entry-point/Cargo.toml new file mode 100644 index 00000000..2814ab18 --- /dev/null +++ b/contracts/secret-entry-point/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "skip-go-secret-entry-point" +version = { workspace = true } +rust-version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +homepage = { workspace = true } +repository = { workspace = true } +documentation = { workspace = true } +keywords = { workspace = true } + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +# for more explicit tests, cargo test --features=backtraces +backtraces = ["cosmwasm-std/backtraces"] +# use library feature to disable all instantiate/execute/query exports +library = [] + +[dependencies] +cosmwasm-schema = { workspace = true } +#cosmwasm-std = { workspace = true } +#cw2 = { workspace = true } +cw20 = { workspace = true } +#cw-storage-plus = { workspace = true } +cw-utils = { workspace = true } +skip = { workspace = true } +thiserror = { workspace = true } +serde = "1.0.114" +schemars = "0.8.1" +secret-toolkit = { git = "https://github.com/scrtlabs/secret-toolkit", tag = "v0.10.0" } +secret-storage-plus = { git = "https://github.com/securesecrets/secret-plus-utils", tag = "v0.1.1", features = [] } +cosmwasm-std = { package = "secret-cosmwasm-std", version = "1.1.11"} + +[dev-dependencies] +test-case = { workspace = true } diff --git a/contracts/secret-entry-point/README.md b/contracts/secret-entry-point/README.md new file mode 100644 index 00000000..15676583 --- /dev/null +++ b/contracts/secret-entry-point/README.md @@ -0,0 +1,268 @@ +# Entry Point Contract + +The entry point contract is responsible for providing a standardized interface (w/ safety checks) to interact with Skip Swap across all CosmWasm-enabled chains. The contract: +1. Performs basic validation on the call data +2. If a fee swap is provided, queries the swap adapter contract to determine how much of the coin sent with the contract call is needed to receive the required fee coin(s), and dispatches the swap. +3. Dispatches the user swap provided in the call data to the relevant swap adapter contract. +4. Handles affiliate fee payments if provided. +5. Verifies the amount out received from the swap(s) is greater than the minimum amount required by the caller after all fees have been subtracted (swap, ibc, affiliate) +6. Dispatches one of the following post-swap actions with the received funds from the swap: + - Transfer to an address on the same chain + - IBC transfer to an address on a different chain (which allows for multi-hop IBC transfers or contract calls if the destination chains support it) + - Call a contract on the same chain + +WARNING: Do not send funds directly to the entry point contract without calling one of its functions. Funds sent directly to the contract do not trigger any contract logic that performs validation / safety checks (as the Cosmos SDK handles direct fund transfers in the `Bank` module and not the `Wasm` module). There are no explicit recovery mechanisms for accidentally sent funds. + +## InstantiateMsg + +Instantiates a new entry point contract using the adapter contracts provided in the instantiation message. + +``` json +{ + "swap_venues": [ + { + "name": "neutron-astroport", + "adapter_contract_address": "neutron..." + } + ], + "ibc_transfer_contract_address": "neutron..." +} +``` + +## ExecuteMsg + +### `swap_and_action` + +Swaps the coin sent and performs a post-swap action. + +Optional fields: +- `fee_swap` is used if a fee is required by the IBC transfer. + +Notes: +- Only one coin can be sent to the contract when calling `swap_and_action` otherwise the transaction will fail. +- `timeout_timestamp` is Unix epoch time in nanoseconds. The transaction will fail if the `timeout_timestamp` has passed when the contract is called. +- `post_swap_action` can be one of three actions: `bank_send`, `ibc_transfer`, or `contract_call`. + - `bank_send`: Sends the assets received from the `user_swap` to an address on the same chain the swap occured on. + - `ibc_transfer`: ICS-20 transfers the assets received from the swap(s) to an address on a different chain than the swap occured on. The ICS-20 transfer supports including a memo in the outgoing transfer, allowing for multi-hop transfers via Packet Forward Middleware and/or contract calls via IBC-hooks. + - `contract_call`: Calls a contract on the same chain the swap occured, using the assets received from the swap as the contract call's funds. +- `affiliates` is a list of affiliates that will take a fee (in basis points) from the `min_coin` provided. If no affiliates are associated with a call then an empty list is to be provided. +- The vector of coins provided in `ibc_info.fee` must all be the same denom. +- A `fee_swap` is only valid if the `post_swap_action` is an `ibc_transfer` with a provided `ibc_info.fee`. The `coin_out` used for the fee swap is dervied from the provided `ibc_info.fee`. +- The `coin_in` used in the `user_swap` is derived based on the coin sent to the contract from the user's contract call, after accounting for the fee swap and if the `user_swap` is a `SwapExactCoinIn` or `SwapExactCoinOut` + +#### Examples + +SwapExactCoinIn: + +``` json +{ + "swap_and_action": { + "user_swap": { + "swap_exact_coin_in": { + "swap_venue_name": "neutron-astroport", + "operations": [ + { + "pool": "neutron...", + "denom_in": "uatom", + "denom_out": "untrn" + }, + { + "pool": "neutron...", + "denom_in": "untrn", + "denom_out": "uosmo" + } + ] + }, + }, + "min_coin": { + "denom": "uosmo", + "amount": "1000000" + }, + "timeout_timestamp": 1000000000000, + "post_swap_action": { + "ibc_transfer": { + "ibc_info": { + "source_channel": "channel-1", + "receiver": "cosmos...", + "fee": { + "recv_fee": [], + "ack_fee": [ + { + "denom": "untrn", + "amount": "100" + } + ], + "timeout_fee": [ + { + "denom": "untrn", + "amount": "100" + } + ] + }, + "memo": "", + "recover_address": "neutron..." + } + "fee_swap": { + "swap_venue_name": "neutron-astroport", + "operations": [ + { + "pool": "neutron...", + "denom_in": "uatom", + "denom_out": "untrn" + } + ] + }, + } + }, + "affiliates": [ + { + "basis_points_fee": 10, + "address": "neutron..." + } + ] + } +} +``` + +SwapExactCoinOut: + +``` json +{ + "swap_and_action": { + "user_swap": { + "swap_exact_coin_out": { + "swap_venue_name": "neutron-astroport", + "operations": [ + { + "pool": "neutron...", + "denom_in": "uatom", + "denom_out": "untrn" + }, + { + "pool": "neutron...", + "denom_in": "untrn", + "denom_out": "uosmo" + } + ], + "refund_address": "neutron..." + }, + }, + "min_coin": { + "denom": "uosmo", + "amount": "1000000" + }, + "timeout_timestamp": 1000000000000, + "post_swap_action": { + "bank_send": { + "to_address": "neutron..." + } + }, + "affiliates": [ + { + "basis_points_fee": 10, + "address": "neutron..." + } + ] + } +} +``` + +### `user_swap` + +Dispatches the user swap to the relevant swap adapter contract and affiliate fee bank send messages. If the user swap is a `SwapExactCoinOut` it also dispatches the refund bank send message to the provided `refund_address` + +Note: Can only be called by the entry point contract itself, any external calls to this function will fail. + +``` json +{ + "user_swap": { + "swap": { + "swap_exact_coin_out": { + "swap_venue_name": "neutron-astroport", + "operations": [ + { + "pool": "neutron...", + "denom_in": "uatom", + "denom_out": "untrn" + }, + { + "pool": "neutron...", + "denom_in": "untrn", + "denom_out": "uosmo" + } + ], + "refund_address": "neutron..." + }, + }, + "min_coin": { + "denom": "uosmo", + "amount": "1000000" + }, + "remaining_coin": { + "denom": "uatom", + "amount": "100000" + }, + "affiliates": [] + } +} +``` + +### `post_swap_action` + +Performs a post swap action. + +Note: Can only be called by the entry point contract itself, any external calls to this function will fail. + +``` json +{ + "post_swap_action": { + "min_coin": { + "denom": "uosmo", + "amount": "1000000" + }, + "timeout_timestamp": 1000000000000, + "post_swap_action": { + "bank_send": { + "to_address": "neutron..." + } + }, + "exact_out": false, + } +} +``` + +## QueryMsg + +### `swap_venue_adapter_contract` + +Returns the swap adapter contract set at instantiation for the given swap venue name provided as an argument. + +Query: +``` json +{ + "swap_venue_adapter_contract": { + "name": "neutron-astroport" + } +} +``` + +Response: +``` json +"neutron..." +``` + +### `ibc_transfer_adapter_contract` + +Returns the IBC transfer adapter contract set at instantiation, requires no arguments. + +Query: +``` json +{ + "ibc_transfer_adapter_contract": {} +} +``` + +Response: +``` json +"neutron..." +``` \ No newline at end of file diff --git a/contracts/secret-entry-point/src/asset.rs b/contracts/secret-entry-point/src/asset.rs new file mode 100644 index 00000000..34e0ef39 --- /dev/null +++ b/contracts/secret-entry-point/src/asset.rs @@ -0,0 +1,610 @@ +use crate::error::SkipError; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{ + to_binary, Api, BankMsg, Binary, Coin, ContractInfo, CosmosMsg, DepsMut, Env, MessageInfo, + Uint128, WasmMsg, +}; +use cw20::{Cw20Coin, Cw20CoinVerified, Cw20Contract, Cw20ExecuteMsg}; +use cw_utils::nonpayable; + +#[cw_serde] +pub enum Asset { + Native(Coin), + Cw20(Cw20Coin), +} + +impl From for Asset { + fn from(coin: Coin) -> Self { + Asset::Native(coin) + } +} + +impl From for Asset { + fn from(cw20_coin: Cw20Coin) -> Self { + Asset::Cw20(cw20_coin) + } +} + +impl From for Asset { + fn from(cw20_coin_verified: Cw20CoinVerified) -> Self { + Asset::Cw20(Cw20Coin { + address: cw20_coin_verified.address.to_string(), + amount: cw20_coin_verified.amount, + }) + } +} + +impl Asset { + pub fn default_native() -> Self { + Asset::Native(Coin::default()) + } + + pub fn new(api: &dyn Api, denom: &str, amount: Uint128) -> Self { + match api.addr_validate(denom) { + Ok(addr) => Asset::Cw20(Cw20Coin { + address: addr.to_string(), + amount: amount.u128().into(), + }), + Err(_) => Asset::Native(Coin { + denom: denom.to_string(), + amount, + }), + } + } + + pub fn denom(&self) -> &str { + match self { + Asset::Native(coin) => &coin.denom, + Asset::Cw20(coin) => &coin.address, + } + } + + pub fn amount(&self) -> Uint128 { + match self { + Asset::Native(coin) => coin.amount, + Asset::Cw20(coin) => coin.amount.u128().into(), + } + } + + pub fn add(&mut self, amount: Uint128) -> Result { + match self { + Asset::Native(coin) => { + coin.amount = coin.amount.checked_add(amount)?; + Ok(coin.amount) + } + Asset::Cw20(coin) => { + coin.amount = coin.amount.checked_add(amount.u128().into())?; + Ok(coin.amount.u128().into()) + } + } + } + + pub fn sub(&mut self, amount: Uint128) -> Result { + match self { + Asset::Native(coin) => { + coin.amount = coin.amount.checked_sub(amount)?; + Ok(coin.amount) + } + Asset::Cw20(coin) => { + coin.amount = coin.amount.checked_sub(amount.u128().into())?; + Ok(coin.amount.u128().into()) + } + } + } + + /* + pub fn transfer(self, to_address: &str) -> CosmosMsg { + match self { + Asset::Native(coin) => CosmosMsg::Bank(BankMsg::Send { + to_address: to_address.to_string(), + amount: vec![coin], + }), + Asset::Cw20(coin) => CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: coin.address.clone(), + msg: to_binary(&Cw20ExecuteMsg::Transfer { + recipient: to_address.to_string(), + amount: coin.amount, + }) + .unwrap(), + funds: vec![], + }), + } + } + */ + + /* + pub fn into_wasm_msg(self, contract_addr: String, msg: Binary) -> Result { + match self { + Asset::Native(coin) => Ok(WasmMsg::Execute { + contract_addr, + msg, + funds: vec![coin], + }), + Asset::Cw20(coin) => Ok(WasmMsg::Execute { + contract_addr: coin.address, + msg: to_binary(&Cw20ExecuteMsg::Send { + contract: contract_addr, + amount: coin.amount, + msg, + })?, + funds: vec![], + }), + } + } + */ + + /* + pub fn validate(&self, deps: &DepsMut, env: &Env, info: &MessageInfo) -> Result<(), SkipError> { + match self { + Asset::Native(coin) => { + unimplemented!(); + } + Asset::Cw20(coin) => { + // Validate that the message is nonpayable + nonpayable(info)?; + + let verified_cw20_coin_addr = deps.api.addr_validate(&coin.address)?; + + let cw20_contract = Cw20Contract(verified_cw20_coin_addr); + + let balance = cw20_contract.balance(&deps.querier, &env.contract.address)?; + + if coin.amount <= balance { + Ok(()) + } else { + Err(SkipError::InvalidCw20Coin) + } + } + } + } + */ +} + +/* +pub fn get_current_asset_available( + deps: &DepsMut, + env: &Env, + denom: &str, +) -> Result { + match deps.api.addr_validate(denom) { + Ok(addr) => { + let cw20_contract = Cw20Contract(addr.clone()); + + let amount = cw20_contract.balance(&deps.querier, &env.contract.address)?; + + Ok(Asset::Cw20(Cw20Coin { + address: addr.to_string(), + amount, + })) + } + Err(_) => { + let coin = deps.querier.query_balance(&env.contract.address, denom)?; + + Ok(Asset::Native(coin)) + } + } +} +*/ + +#[cfg(test)] +mod tests { + use super::*; + use cosmwasm_std::{ + testing::{mock_dependencies_with_balances, mock_env, mock_info}, + Addr, ContractResult, QuerierResult, SystemResult, WasmQuery, + }; + use cw20::BalanceResponse; + use cw_utils::PaymentError; + + #[test] + fn test_default_native() { + let asset = Asset::default_native(); + + assert_eq!( + asset, + Asset::Native(Coin { + denom: "".to_string(), + amount: Uint128::zero(), + }) + ); + } + + #[test] + fn test_new() { + // TEST 1: Native asset + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let asset = Asset::new(deps.as_mut().api, "ua", Uint128::new(100)); + + assert_eq!( + asset, + Asset::Native(Coin { + denom: "ua".to_string(), + amount: Uint128::new(100), + }) + ); + + // TEST 2: Cw20 asset + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let asset = Asset::new(deps.as_mut().api, "asset", Uint128::new(100)); + + assert_eq!( + asset, + Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }) + ); + } + + #[test] + fn test_asset_native() { + let asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + assert_eq!(asset.denom(), "uatom"); + assert_eq!(asset.amount(), Uint128::new(100)); + } + + #[test] + fn test_asset_cw20() { + let asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + assert_eq!(asset.denom(), "asset"); + assert_eq!(asset.amount(), Uint128::new(100)); + } + + #[test] + fn test_add() { + // TEST 1: Native asset + let mut asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + asset.add(Uint128::new(20)).unwrap(); + + assert_eq!(asset.amount(), Uint128::new(120)); + + // TEST 2: Cw20 asset + let mut asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + asset.add(Uint128::new(20)).unwrap(); + + assert_eq!(asset.amount(), Uint128::new(120)); + } + + #[test] + fn test_sub() { + // TEST 1: Native asset + let mut asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + asset.sub(Uint128::new(20)).unwrap(); + + assert_eq!(asset.amount(), Uint128::new(80)); + + // TEST 2: Cw20 asset + let mut asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + asset.sub(Uint128::new(20)).unwrap(); + + assert_eq!(asset.amount(), Uint128::new(80)); + } + + #[test] + fn test_asset_transfer_native() { + let asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + let msg = asset.transfer("addr"); + + match msg { + CosmosMsg::Bank(BankMsg::Send { to_address, amount }) => { + assert_eq!(to_address, "addr"); + assert_eq!(amount.len(), 1); + assert_eq!(amount[0].denom, "uatom"); + assert_eq!(amount[0].amount, Uint128::new(100)); + } + _ => panic!("Unexpected message type"), + } + } + + #[test] + fn test_asset_transfer_cw20() { + let asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + let msg = asset.transfer("addr"); + + match msg { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr, + msg, + funds, + }) => { + assert_eq!(contract_addr, "asset"); + assert_eq!( + msg, + to_binary(&Cw20ExecuteMsg::Transfer { + recipient: "addr".to_string(), + amount: Uint128::new(100), + }) + .unwrap() + ); + assert_eq!(funds.len(), 0); + } + _ => panic!("Unexpected message type"), + } + } + + #[test] + fn test_into_astroport_asset() { + // TEST 1: Native asset + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + let astroport_asset = asset.into_astroport_asset(deps.as_mut().api).unwrap(); + + assert_eq!( + astroport_asset, + AstroportAsset { + info: AssetInfo::NativeToken { + denom: "uatom".to_string() + }, + amount: Uint128::new(100), + } + ); + + // TEST 2: Cw20 asset + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + let astroport_asset = asset.into_astroport_asset(deps.as_mut().api).unwrap(); + + assert_eq!( + astroport_asset, + AstroportAsset { + info: AssetInfo::Token { + contract_addr: Addr::unchecked("asset") + }, + amount: Uint128::new(100), + } + ); + } + + #[test] + fn test_validate_native() { + // TEST 1: Valid asset + let asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let env = mock_env(); + + let info = mock_info( + "sender", + &[Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }], + ); + + assert!(asset.validate(&deps.as_mut(), &env, &info).is_ok()); + + // TEST 2: Invalid asset due to less amount of denom sent + let asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let env = mock_env(); + + let info = mock_info( + "sender", + &[Coin { + denom: "uatom".to_string(), + amount: Uint128::new(50), + }], + ); + + let res = asset.validate(&deps.as_mut(), &env, &info); + + assert_eq!(res, Err(SkipError::InvalidNativeCoin)); + + // TEST 3: Invalid asset due to more than one coin sent + let asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let env = mock_env(); + + let info = mock_info( + "sender", + &[ + Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }, + Coin { + denom: "uosmo".to_string(), + amount: Uint128::new(50), + }, + ], + ); + + let res = asset.validate(&deps.as_mut(), &env, &info); + + assert_eq!( + res, + Err(SkipError::Payment(PaymentError::MultipleDenoms {})) + ); + } + + #[test] + fn test_validate_cw20() { + // TEST 1: Valid asset + let asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + // Create mock wasm handler to handle the cw20 balance query + let wasm_handler = |query: &WasmQuery| -> QuerierResult { + match query { + WasmQuery::Smart { .. } => SystemResult::Ok(ContractResult::Ok( + to_binary(&BalanceResponse { + balance: Uint128::from(100u128), + }) + .unwrap(), + )), + _ => panic!("Unsupported query: {:?}", query), + } + }; + + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + deps.querier.update_wasm(wasm_handler); + + let env = mock_env(); + + let info = mock_info("sender", &[]); + + assert!(asset.validate(&deps.as_mut(), &env, &info).is_ok()); + + // TEST 2: Invalid asset due to native coin sent in info + let asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let env = mock_env(); + + let info = mock_info( + "sender", + &[Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }], + ); + + let res = asset.validate(&deps.as_mut(), &env, &info); + + assert_eq!(res, Err(SkipError::Payment(PaymentError::NonPayable {}))); + + // TEST 3: Invalid asset due to invalid cw20 balance + let asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + // Create mock wasm handler to handle the cw20 balance query + let wasm_handler = |query: &WasmQuery| -> QuerierResult { + match query { + WasmQuery::Smart { .. } => SystemResult::Ok(ContractResult::Ok( + to_binary(&BalanceResponse { + balance: Uint128::from(50u128), + }) + .unwrap(), + )), + _ => panic!("Unsupported query: {:?}", query), + } + }; + + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + deps.querier.update_wasm(wasm_handler); + + let env = mock_env(); + + let info = mock_info("sender", &[]); + + let res = asset.validate(&deps.as_mut(), &env, &info); + + assert_eq!(res, Err(SkipError::InvalidCw20Coin)); + } + + #[test] + fn test_get_current_asset_available() { + // TEST 1: Native asset + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[Coin::new(100, "ua")])]); + + let mut env = mock_env(); + env.contract.address = Addr::unchecked("entry_point"); + + let asset = get_current_asset_available(&deps.as_mut(), &env, "ua").unwrap(); + + assert_eq!( + asset, + Asset::Native(Coin { + denom: "ua".to_string(), + amount: Uint128::new(100), + }) + ); + + // TEST 2: Cw20 asset + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let wasm_handler = |query: &WasmQuery| -> QuerierResult { + match query { + WasmQuery::Smart { .. } => SystemResult::Ok(ContractResult::Ok( + to_binary(&BalanceResponse { + balance: Uint128::from(100u128), + }) + .unwrap(), + )), + _ => panic!("Unsupported query: {:?}", query), + } + }; + + deps.querier.update_wasm(wasm_handler); + + let env = mock_env(); + + let asset = get_current_asset_available(&deps.as_mut(), &env, "asset").unwrap(); + + assert_eq!( + asset, + Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }) + ); + } +} diff --git a/contracts/secret-entry-point/src/bin/schema.rs b/contracts/secret-entry-point/src/bin/schema.rs new file mode 100644 index 00000000..1046f8be --- /dev/null +++ b/contracts/secret-entry-point/src/bin/schema.rs @@ -0,0 +1,10 @@ +use cosmwasm_schema::write_api; +use skip::entry_point::{ExecuteMsg, InstantiateMsg, QueryMsg}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + execute: ExecuteMsg, + query: QueryMsg + } +} diff --git a/contracts/secret-entry-point/src/contract.rs b/contracts/secret-entry-point/src/contract.rs new file mode 100644 index 00000000..2f4bbec4 --- /dev/null +++ b/contracts/secret-entry-point/src/contract.rs @@ -0,0 +1,266 @@ +use crate::{ + error::{ContractError, ContractResult}, + execute::{ + execute_action, execute_action_with_recover, execute_post_swap_action, + execute_swap_and_action, execute_swap_and_action_with_recover, execute_user_swap, + receive_snip20, + }, + query::{query_ibc_transfer_adapter_contract, query_swap_venue_adapter_contract}, + reply::{reply_swap_and_action_with_recover, RECOVER_REPLY_ID}, + state::{ + BLOCKED_CONTRACT_ADDRESSES, HYPERLANE_TRANSFER_CONTRACT_ADDRESS, + IBC_TRANSFER_CONTRACT_ADDRESS, SWAP_VENUE_MAP, + }, +}; +use cosmwasm_std::{ + entry_point, to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, +}; +// use cw2::set_contract_version; +use skip::entry_point::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; + +/////////////// +/// MIGRATE /// +/////////////// + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> ContractResult { + unimplemented!() +} + +/////////////////// +/// INSTANTIATE /// +/////////////////// + +// Contract name and version used for migration. +const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); +const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + deps: DepsMut, + env: Env, + _info: MessageInfo, + msg: InstantiateMsg, +) -> ContractResult { + // Set contract version + // set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + // Create response object to return + let mut response: Response = Response::new().add_attribute("action", "instantiate"); + + // Insert the entry point contract address into the blocked contract addresses map + BLOCKED_CONTRACT_ADDRESSES.save(deps.storage, &env.contract.address, &())?; + + // Iterate through the swap venues provided and create a map of venue names to swap adapter contract addresses + for swap_venue in msg.swap_venues.iter() { + // Validate the swap contract address + let checked_swap_contract_address = deps + .api + .addr_validate(&swap_venue.adapter_contract_address)?; + + // Prevent duplicate swap venues by erroring if the venue name is already stored + if SWAP_VENUE_MAP.has(deps.storage, &swap_venue.name) { + return Err(ContractError::DuplicateSwapVenueName); + } + + // Store the swap venue name and contract address inside the swap venue map + SWAP_VENUE_MAP.save( + deps.storage, + &swap_venue.name, + &checked_swap_contract_address, + )?; + + // Insert the swap contract address into the blocked contract addresses map + BLOCKED_CONTRACT_ADDRESSES.save(deps.storage, &checked_swap_contract_address, &())?; + + // Add the swap venue and contract address to the response + response = response + .add_attribute("action", "add_swap_venue") + .add_attribute("name", &swap_venue.name) + .add_attribute("contract_address", &checked_swap_contract_address); + } + + // Validate ibc transfer adapter contract addresses + let checked_ibc_transfer_contract_address = + deps.api.addr_validate(&msg.ibc_transfer_contract_address)?; + + // Store the ibc transfer adapter contract address + IBC_TRANSFER_CONTRACT_ADDRESS.save(deps.storage, &checked_ibc_transfer_contract_address)?; + + // Insert the ibc transfer adapter contract address into the blocked contract addresses map + BLOCKED_CONTRACT_ADDRESSES.save(deps.storage, &checked_ibc_transfer_contract_address, &())?; + + // Add the ibc transfer adapter contract address to the response + response = response + .add_attribute("action", "add_ibc_transfer_adapter") + .add_attribute("contract_address", &checked_ibc_transfer_contract_address); + + // If the hyperlane transfer contract address is provided, validate and store it + if let Some(hyperlane_transfer_contract_address) = msg.hyperlane_transfer_contract_address { + // Validate hyperlane transfer adapter contract address + let checked_hyperlane_transfer_contract_address = deps + .api + .addr_validate(&hyperlane_transfer_contract_address)?; + + // Store the hyperlane transfer adapter contract address + HYPERLANE_TRANSFER_CONTRACT_ADDRESS + .save(deps.storage, &checked_hyperlane_transfer_contract_address)?; + + // Insert the hyperlane transfer adapter contract address into the blocked contract addresses map + BLOCKED_CONTRACT_ADDRESSES.save( + deps.storage, + &checked_hyperlane_transfer_contract_address, + &(), + )?; + + // Add the hyperlane transfer adapter contract address to the response + response = response + .add_attribute("action", "add_hyperlane_transfer_adapter") + .add_attribute( + "contract_address", + &checked_hyperlane_transfer_contract_address, + ); + } + + Ok(response) +} + +/////////////// +/// EXECUTE /// +/////////////// + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> ContractResult { + match msg { + ExecuteMsg::Receive(msg) => receive_snip20(deps, env, info, msg), + ExecuteMsg::SwapAndActionWithRecover { + sent_asset, + user_swap, + min_asset, + timeout_timestamp, + post_swap_action, + affiliates, + recovery_addr, + } => execute_swap_and_action_with_recover( + deps, + env, + info, + sent_asset, + user_swap, + min_asset, + timeout_timestamp, + post_swap_action, + affiliates, + recovery_addr, + ), + ExecuteMsg::SwapAndAction { + sent_asset, + user_swap, + min_asset, + timeout_timestamp, + post_swap_action, + affiliates, + } => execute_swap_and_action( + deps, + env, + info, + sent_asset, + user_swap, + min_asset, + timeout_timestamp, + post_swap_action, + affiliates, + ), + ExecuteMsg::UserSwap { + swap, + min_asset, + remaining_asset, + affiliates, + } => execute_user_swap( + deps, + env, + info, + swap, + min_asset, + remaining_asset, + affiliates, + ), + ExecuteMsg::PostSwapAction { + min_asset, + timeout_timestamp, + post_swap_action, + exact_out, + } => execute_post_swap_action( + deps, + env, + info, + min_asset, + timeout_timestamp, + post_swap_action, + exact_out, + ), + ExecuteMsg::Action { + sent_asset, + timeout_timestamp, + action, + exact_out, + min_asset, + } => execute_action( + deps, + env, + info, + sent_asset, + timeout_timestamp, + action, + exact_out, + min_asset, + ), + ExecuteMsg::ActionWithRecover { + sent_asset, + timeout_timestamp, + action, + exact_out, + min_asset, + recovery_addr, + } => execute_action_with_recover( + deps, + env, + info, + sent_asset, + timeout_timestamp, + action, + exact_out, + min_asset, + recovery_addr, + ), + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { + match msg.id { + RECOVER_REPLY_ID => reply_swap_and_action_with_recover(deps, msg), + _ => Err(ContractError::ReplyIdError(msg.id)), + } +} + +///////////// +/// QUERY /// +///////////// + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { + match msg { + QueryMsg::SwapVenueAdapterContract { name } => { + to_binary(&query_swap_venue_adapter_contract(deps, name)?) + } + QueryMsg::IbcTransferAdapterContract {} => { + to_binary(&query_ibc_transfer_adapter_contract(deps)?) + } + } +} diff --git a/contracts/secret-entry-point/src/error.rs b/contracts/secret-entry-point/src/error.rs new file mode 100644 index 00000000..a8810748 --- /dev/null +++ b/contracts/secret-entry-point/src/error.rs @@ -0,0 +1,96 @@ +use cosmwasm_std::{OverflowError, StdError}; +use skip::error::SkipError; +use thiserror::Error; + +pub type ContractResult = core::result::Result; + +#[derive(Error, Debug, PartialEq)] +pub enum ContractError { + /////////////// + /// GENERAL /// + /////////////// + + #[error(transparent)] + Std(#[from] StdError), + + #[error(transparent)] + Skip(#[from] SkipError), + + #[error(transparent)] + Overflow(#[from] OverflowError), + + #[error(transparent)] + Payment(#[from] cw_utils::PaymentError), + + #[error("Unauthorized")] + Unauthorized, + + #[error("Timeout Timestamp Less Than Current Timestamp")] + Timeout, + + #[error("Duplicate Swap Venue Name Provided")] + DuplicateSwapVenueName, + + #[error("IBC fee denom differs from asset received without a fee swap to convert")] + IBCFeeDenomDiffersFromAssetReceived, + + //////////////// + /// FEE SWAP /// + //////////////// + + #[error("Fee Swap Not Allowed: No IBC Fees Provided")] + FeeSwapWithoutIbcFees, + + #[error("Fee Swap Asset In Denom Differs From Asset Sent To Contract")] + FeeSwapAssetInDenomMismatch, + + ///////////////// + /// USER SWAP /// + ///////////////// + + #[error("User Swap Asset In Denom Differs From Asset Sent To Contract")] + UserSwapAssetInDenomMismatch, + + #[error("No Refund Address Provided For Swap Exact Asset Out User Swap")] + NoRefundAddress, + + //////////////////////// + /// POST SWAP ACTION /// + //////////////////////// + + #[error("Received Less Asset From Swaps Than Minimum Asset Required")] + ReceivedLessAssetFromSwapsThanMinAsset, + + #[error("Contract Call Address Cannot Be The Entry Point Or Adapter Contracts")] + ContractCallAddressBlocked, + + #[error( + "IBC Transfer Adapter Only Supports Native Coins, Cw20 IBC Transfers Are Contract Calls" + )] + NonNativeIbcTransfer, + + #[error("Hyperlane Transfer Adapter Only Supports Native Coins")] + NonNativeHplTransfer, + + #[error("Reply id: {0} not valid")] + ReplyIdError(u64), + + ////////////////// + /// ACTION /// + ////////////////// + + #[error("No Minimum Asset Provided with Exact Out Action")] + NoMinAssetProvided, + + #[error("Sent Asset and Min Asset Denoms Do Not Match with Exact Out Action")] + ActionDenomMismatch, + + #[error("Remaining Asset Less Than Min Asset with Exact Out Action")] + RemainingAssetLessThanMinAsset, + + #[error("No Snip20 Receive Msg Provided")] + NoSnip20ReceiveMsg, + + #[error("Native Coin Not Supported")] + NativeCoinNotSupported, +} diff --git a/contracts/secret-entry-point/src/execute.rs b/contracts/secret-entry-point/src/execute.rs new file mode 100644 index 00000000..29932172 --- /dev/null +++ b/contracts/secret-entry-point/src/execute.rs @@ -0,0 +1,944 @@ +use std::vec; + +use crate::{ + asset::Asset, + error::{ContractError, ContractResult}, + msg::{Action, Affiliate, ExecuteMsg, Snip20HookMsg, Snip20ReceiveMsg}, + reply::{RecoverTempStorage, RECOVER_REPLY_ID}, + state::{ + BLOCKED_CONTRACT_ADDRESSES, HYPERLANE_TRANSFER_CONTRACT_ADDRESS, + IBC_TRANSFER_CONTRACT_ADDRESS, PRE_SWAP_OUT_ASSET_AMOUNT, RECOVER_TEMP_STORAGE, + SWAP_VENUE_MAP, + }, +}; +use cosmwasm_std::{ + from_binary, to_binary, Addr, BankMsg, Binary, Coin, CosmosMsg, DepsMut, Env, MessageInfo, + Response, SubMsg, Uint128, WasmMsg, +}; +use cw20::Cw20Coin; +// use cw_utils::one_coin; +use skip::{ + hyperlane::{ExecuteMsg as HplExecuteMsg, ExecuteMsg::HplTransfer}, + ibc::{ExecuteMsg as IbcTransferExecuteMsg, IbcInfo, IbcTransfer}, + swap::{ + validate_swap_operations, ExecuteMsg as SwapExecuteMsg, QueryMsg as SwapQueryMsg, Swap, + SwapExactAssetOut, + }, +}; + +////////////////////////// +/// RECEIVE ENTRYPOINT /// +////////////////////////// + +// Receive is the main entry point for the contract to +// receive snip20 tokens and execute the swap and action message +pub fn receive_snip20( + deps: DepsMut, + env: Env, + info: MessageInfo, + snip20_msg: Snip20ReceiveMsg, +) -> ContractResult { + let sent_asset = Asset::Cw20(Cw20Coin { + address: info.sender.to_string(), + amount: snip20_msg.amount.u128().into(), + }); + + let msg = match snip20_msg.msg { + Some(msg) => msg, + None => { + return Err(ContractError::NoSnip20ReceiveMsg); + } + }; + match from_binary(&msg)? { + Snip20HookMsg::SwapAndActionWithRecover { + user_swap, + min_asset, + timeout_timestamp, + post_swap_action, + affiliates, + recovery_addr, + } => execute_swap_and_action_with_recover( + deps, + env, + info, + Some(sent_asset), + user_swap, + min_asset, + timeout_timestamp, + post_swap_action, + affiliates, + recovery_addr, + ), + Snip20HookMsg::SwapAndAction { + user_swap, + min_asset, + timeout_timestamp, + post_swap_action, + affiliates, + } => execute_swap_and_action( + deps, + env, + info, + Some(sent_asset), + user_swap, + min_asset, + timeout_timestamp, + post_swap_action, + affiliates, + ), + Snip20HookMsg::Action { + timeout_timestamp, + action, + exact_out, + min_asset, + } => execute_action( + deps, + env, + info, + Some(sent_asset), + timeout_timestamp, + action, + exact_out, + min_asset, + ), + Snip20HookMsg::ActionWithRecover { + timeout_timestamp, + action, + exact_out, + min_asset, + recovery_addr, + } => execute_action_with_recover( + deps, + env, + info, + Some(sent_asset), + timeout_timestamp, + action, + exact_out, + min_asset, + recovery_addr, + ), + } +} + +/////////////////////////// +/// EXECUTE ENTRYPOINTS /// +/////////////////////////// + +// Main entry point for the contract +// Dispatches the swap and post swap action +#[allow(clippy::too_many_arguments)] +pub fn execute_swap_and_action( + deps: DepsMut, + env: Env, + info: MessageInfo, + sent_asset: Option, + mut user_swap: Swap, + min_asset: Asset, + timeout_timestamp: u64, + post_swap_action: Action, + affiliates: Vec, +) -> ContractResult { + // Create a response object to return + let mut response: Response = Response::new().add_attribute("action", "execute_swap_and_action"); + + // Validate and unwrap the sent asset + let sent_asset = match sent_asset { + Some(sent_asset) => { + match &sent_asset { + Asset::Cw20(cw20) => { + if cw20.address != info.sender.to_string() { + return Err(ContractError::InvalidCw20Sender); + } + } + Asset::Native(_) => { + return Err(ContractError::NativeCoinNotSupported); + } + } + // sent_asset.validate(&deps, &env, &info)?; + sent_asset + } + None => { + return Err(ContractError::NativeCoinNotSupported); + } + }; + + // Error if the current block time is greater than the timeout timestamp + if env.block.time.nanos() > timeout_timestamp { + return Err(ContractError::Timeout); + } + + // Save the current out asset amount to storage as the pre swap out asset amount + let pre_swap_out_asset_amount = + get_current_asset_available(&deps, &env, min_asset.denom())?.amount(); + PRE_SWAP_OUT_ASSET_AMOUNT.save(deps.storage, &pre_swap_out_asset_amount)?; + + // Already validated at entrypoints (both direct and snip20_receive) + let mut remaining_asset = sent_asset; + + // If the post swap action is an IBC transfer, then handle the ibc fees + // by either creating a fee swap message or deducting the ibc fees from + // the remaining asset received amount. + if let Action::IbcTransfer { ibc_info, fee_swap } = &post_swap_action { + response = + handle_ibc_transfer_fees(&deps, ibc_info, fee_swap, &mut remaining_asset, response)?; + } + + // Set a boolean to determine if the user swap is exact out or not + let exact_out = match &user_swap { + Swap::SwapExactAssetIn(_) => false, + Swap::SwapExactAssetOut(_) => true, + Swap::SmartSwapExactAssetIn(_) => false, + }; + + if let Swap::SmartSwapExactAssetIn(smart_swap) = &mut user_swap { + if smart_swap.routes.is_empty() { + return Err(ContractError::Skip(skip::error::SkipError::RoutesEmpty)); + } + + match smart_swap.amount().cmp(&remaining_asset.amount()) { + std::cmp::Ordering::Equal => {} + std::cmp::Ordering::Less => { + let diff = remaining_asset.amount().checked_sub(smart_swap.amount())?; + + // If the total swap in amount is less than remaining asset, + // adjust the routes to match the remaining asset amount + let largest_route_idx = smart_swap.largest_route_index()?; + + smart_swap.routes[largest_route_idx].offer_asset.add(diff)?; + } + std::cmp::Ordering::Greater => { + let diff = smart_swap.amount().checked_sub(remaining_asset.amount())?; + + // If the total swap in amount is greater than remaining asset, + // adjust the routes to match the remaining asset amount + let largest_route_idx = smart_swap.largest_route_index()?; + + smart_swap.routes[largest_route_idx].offer_asset.sub(diff)?; + } + } + } + + let user_swap_msg = WasmMsg::Execute { + contract_addr: env.contract.address.to_string(), + msg: to_binary(&ExecuteMsg::UserSwap { + swap: user_swap, + min_asset: min_asset.clone(), + remaining_asset, + affiliates, + })?, + funds: vec![], + }; + + // Add the user swap message to the response + response = response + .add_message(user_swap_msg) + .add_attribute("action", "dispatch_user_swap"); + + // Create the post swap action message + let post_swap_action_msg = WasmMsg::Execute { + contract_addr: env.contract.address.to_string(), + msg: to_binary(&ExecuteMsg::PostSwapAction { + min_asset, + timeout_timestamp, + post_swap_action, + exact_out, + })?, + funds: vec![], + }; + + // Add the post swap action message to the response and return the response + Ok(response + .add_message(post_swap_action_msg) + .add_attribute("action", "dispatch_post_swap_action")) +} + +// Entrypoint that catches all errors in SwapAndAction and recovers +// the original funds sent to the contract to a recover address. +#[allow(clippy::too_many_arguments)] +pub fn execute_swap_and_action_with_recover( + deps: DepsMut, + env: Env, + info: MessageInfo, + sent_asset: Option, + user_swap: Swap, + min_asset: Asset, + timeout_timestamp: u64, + post_swap_action: Action, + affiliates: Vec, + recovery_addr: Addr, +) -> ContractResult { + let mut assets: Vec = info.funds.iter().cloned().map(Asset::Native).collect(); + + if let Some(asset) = &sent_asset { + if let Asset::Cw20(_) = asset { + assets.push(asset.clone()); + } + } + + // Store all parameters into a temporary storage. + RECOVER_TEMP_STORAGE.save( + deps.storage, + &RecoverTempStorage { + assets, + recovery_addr, + }, + )?; + + // Then call ExecuteMsg::SwapAndAction using a SubMsg. + let sub_msg = SubMsg::reply_always( + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: env.contract.address.to_string(), + code_hash: env.contract.code_hash, + msg: to_binary(&ExecuteMsg::SwapAndAction { + sent_asset, + user_swap, + min_asset, + timeout_timestamp, + post_swap_action, + affiliates, + })?, + funds: info.funds, + }), + RECOVER_REPLY_ID, + ); + + Ok(Response::new().add_submessage(sub_msg)) +} + +// Dispatches the user swap and refund/affiliate fee bank sends if needed +pub fn execute_user_swap( + deps: DepsMut, + env: Env, + info: MessageInfo, + swap: Swap, + mut min_asset: Asset, + mut remaining_asset: Asset, + affiliates: Vec, +) -> ContractResult { + // Enforce the caller is the contract itself + if info.sender != env.contract.address { + return Err(ContractError::Unauthorized); + } + + // Create a response object to return + let mut response: Response = Response::new() + .add_attribute("action", "execute_user_swap") + .add_attribute("denom_in", remaining_asset.denom()) + .add_attribute("denom_out", min_asset.denom()); + + // Create affiliate response and total affiliate fee amount + let mut affiliate_response: Response = Response::new(); + let mut total_affiliate_fee_amount: Uint128 = Uint128::zero(); + + // If affiliates exist, create the affiliate fee messages and attributes and + // add them to the affiliate response, updating the total affiliate fee amount + for affiliate in affiliates.iter() { + // Verify, calculate, and get the affiliate fee amount + let affiliate_fee_amount = + verify_and_calculate_affiliate_fee_amount(&deps, &min_asset, affiliate)?; + + if affiliate_fee_amount > Uint128::zero() { + // Add the affiliate fee amount to the total affiliate fee amount + total_affiliate_fee_amount = + total_affiliate_fee_amount.checked_add(affiliate_fee_amount)?; + + // Create the affiliate_fee_asset + let affiliate_fee_asset = Asset::new(deps.api, min_asset.denom(), affiliate_fee_amount); + + // Create the affiliate fee message + let affiliate_fee_msg = affiliate_fee_asset.transfer(&affiliate.address); + + // Add the affiliate fee message and attributes to the response + affiliate_response = affiliate_response + .add_message(affiliate_fee_msg) + .add_attribute("action", "dispatch_affiliate_fee_bank_send") + .add_attribute("address", &affiliate.address) + .add_attribute("amount", affiliate_fee_amount); + } + } + + // Create the user swap message + match swap { + Swap::SwapExactAssetIn(swap) => { + // Validate swap operations + validate_swap_operations(&swap.operations, remaining_asset.denom(), min_asset.denom())?; + + // Get swap adapter contract address from venue name + let user_swap_adapter_contract_address = + SWAP_VENUE_MAP.load(deps.storage, &swap.swap_venue_name)?; + + // Create the user swap message args + let user_swap_msg_args: SwapExecuteMsg = swap.into(); + + // Create the user swap message + let user_swap_msg = remaining_asset.into_wasm_msg( + user_swap_adapter_contract_address.to_string(), + to_binary(&user_swap_msg_args)?, + )?; + + response = response + .add_message(user_swap_msg) + .add_attribute("action", "dispatch_user_swap_exact_asset_in"); + } + Swap::SwapExactAssetOut(swap) => { + // Validate swap operations + validate_swap_operations(&swap.operations, remaining_asset.denom(), min_asset.denom())?; + + // Get swap adapter contract address from venue name + let user_swap_adapter_contract_address = + SWAP_VENUE_MAP.load(deps.storage, &swap.swap_venue_name)?; + + // Calculate the swap asset out by adding the min asset amount to the total affiliate fee amount + min_asset.add(total_affiliate_fee_amount)?; + + // Query the swap adapter to get the asset in needed to obtain the min asset plus affiliates + let user_swap_asset_in = query_swap_asset_in( + &deps, + &user_swap_adapter_contract_address, + &swap, + &min_asset, + )?; + + // Verify the user swap in denom is the same as the denom received from the message to the contract + if user_swap_asset_in.denom() != remaining_asset.denom() { + return Err(ContractError::UserSwapAssetInDenomMismatch); + } + + // Calculate refund amount to send back to the user + remaining_asset.sub(user_swap_asset_in.amount())?; + + // If refund amount gt zero, then create the refund message and add it to the response + if remaining_asset.amount() > Uint128::zero() { + // Get the refund address from the swap + let to_address = swap + .refund_address + .clone() + .ok_or(ContractError::NoRefundAddress)?; + + // Validate the refund address + deps.api.addr_validate(&to_address)?; + + // Get the refund amount + let refund_amount = remaining_asset.amount(); + + // Create the refund message + let refund_msg = remaining_asset.transfer(&to_address); + + // Add the refund message and attributes to the response + response = response + .add_message(refund_msg) + .add_attribute("action", "dispatch_refund") + .add_attribute("address", &to_address) + .add_attribute("amount", refund_amount); + } + + // Create the user swap message args + let user_swap_msg_args: SwapExecuteMsg = swap.into(); + + // Create the user swap message + let user_swap_msg = user_swap_asset_in.into_wasm_msg( + user_swap_adapter_contract_address.to_string(), + to_binary(&user_swap_msg_args)?, + )?; + + response = response + .add_message(user_swap_msg) + .add_attribute("action", "dispatch_user_swap_exact_asset_out"); + } + Swap::SmartSwapExactAssetIn(swap) => { + for route in swap.routes { + // Validate swap operations + validate_swap_operations( + &route.operations, + remaining_asset.denom(), + min_asset.denom(), + )?; + + // Get swap adapter contract address from venue name + let user_swap_adapter_contract_address = + SWAP_VENUE_MAP.load(deps.storage, &swap.swap_venue_name)?; + + // Create the user swap message args + let user_swap_msg_args = SwapExecuteMsg::Swap { + operations: route.operations, + }; + + // Create the user swap message + let user_swap_msg = route.offer_asset.into_wasm_msg( + user_swap_adapter_contract_address.to_string(), + to_binary(&user_swap_msg_args)?, + )?; + + response = response + .add_message(user_swap_msg) + .add_attribute("action", "dispatch_user_swap_exact_asset_in"); + } + } + } + + // Add the affiliate messages and attributes to the response and return the response + // Having the affiliate messages after the swap is purposeful, so that the affiliate + // bank sends are valid and the contract has funds to send to the affiliates. + Ok(response + .add_submessages(affiliate_response.messages) + .add_attributes(affiliate_response.attributes)) +} + +// Dispatches the post swap action +// Can only be called by the contract itself +pub fn execute_post_swap_action( + deps: DepsMut, + env: Env, + info: MessageInfo, + min_asset: Asset, + timeout_timestamp: u64, + post_swap_action: Action, + exact_out: bool, +) -> ContractResult { + // Enforce the caller is the contract itself + if info.sender != env.contract.address { + return Err(ContractError::Unauthorized); + } + + // Create a response object to return + let mut response: Response = + Response::new().add_attribute("action", "execute_post_swap_action"); + + // Get the pre swap out asset amount from storage + let pre_swap_out_asset_amount = PRE_SWAP_OUT_ASSET_AMOUNT.load(deps.storage)?; + + // Get contract balance of min out asset post swap + // for fee deduction and transfer out amount enforcement + let post_swap_out_asset = get_current_asset_available(&deps, &env, min_asset.denom())?; + + // Set the transfer out asset to the post swap out asset amount minus the pre swap out asset amount + // Since we only want to transfer out the amount received from the swap + let transfer_out_asset = Asset::new( + deps.api, + min_asset.denom(), + post_swap_out_asset + .amount() + .checked_sub(pre_swap_out_asset_amount)?, + ); + + // Error if the contract balance is less than the min asset out amount + if transfer_out_asset.amount() < min_asset.amount() { + return Err(ContractError::ReceivedLessAssetFromSwapsThanMinAsset); + } + + // Set the transfer out asset to the min asset if exact out is true + let transfer_out_asset = if exact_out { + min_asset + } else { + transfer_out_asset + }; + + response = response + .add_attribute( + "post_swap_action_amount_out", + transfer_out_asset.amount().to_string(), + ) + .add_attribute("post_swap_action_denom_out", transfer_out_asset.denom()); + + // Dispatch the action message + response = validate_and_dispatch_action( + deps, + post_swap_action, + transfer_out_asset, + timeout_timestamp, + response, + )?; + + Ok(response) +} + +// Dispatches an action +#[allow(clippy::too_many_arguments)] +pub fn execute_action( + deps: DepsMut, + env: Env, + info: MessageInfo, + sent_asset: Option, + timeout_timestamp: u64, + action: Action, + exact_out: bool, + min_asset: Option, +) -> ContractResult { + // Create a response object to return + let mut response: Response = Response::new().add_attribute("action", "execute_action"); + + // Validate and unwrap the sent asset + let sent_asset = match sent_asset { + Some(sent_asset) => { + sent_asset.validate(&deps, &env, &info)?; + sent_asset + } + None => one_coin(&info)?.into(), + }; + + // Error if the current block time is greater than the timeout timestamp + if env.block.time.nanos() > timeout_timestamp { + return Err(ContractError::Timeout); + } + + // Already validated at entrypoints (both direct and snip20_receive) + let mut remaining_asset = sent_asset; + + // If the post swap action is an IBC transfer, then handle the ibc fees + // by either creating a fee swap message or deducting the ibc fees from + // the remaining asset received amount. + if let Action::IbcTransfer { ibc_info, fee_swap } = &action { + response = + handle_ibc_transfer_fees(&deps, ibc_info, fee_swap, &mut remaining_asset, response)?; + } + + // Validate and determine the asset to be used for the action + let action_asset = if exact_out { + let min_asset = min_asset.ok_or(ContractError::NoMinAssetProvided)?; + + // Ensure remaining_asset and min_asset have the same denom + if remaining_asset.denom() != min_asset.denom() { + return Err(ContractError::ActionDenomMismatch); + } + + // Ensure remaining_asset is greater than or equal to min_asset + if remaining_asset.amount() < min_asset.amount() { + return Err(ContractError::RemainingAssetLessThanMinAsset); + } + + min_asset + } else { + remaining_asset.clone() + }; + + // Dispatch the action message + response = + validate_and_dispatch_action(deps, action, action_asset, timeout_timestamp, response)?; + + // Return the response + Ok(response) +} + +// Entrypoint that catches all errors in Action and recovers +// the original funds sent to the contract to a recover address. +#[allow(clippy::too_many_arguments)] +pub fn execute_action_with_recover( + deps: DepsMut, + env: Env, + info: MessageInfo, + sent_asset: Option, + timeout_timestamp: u64, + action: Action, + exact_out: bool, + min_asset: Option, + recovery_addr: Addr, +) -> ContractResult { + let mut assets: Vec = info.funds.iter().cloned().map(Asset::Native).collect(); + + if let Some(asset) = &sent_asset { + if let Asset::Cw20(_) = asset { + assets.push(asset.clone()); + } + } + + // Store all parameters into a temporary storage. + RECOVER_TEMP_STORAGE.save( + deps.storage, + &RecoverTempStorage { + assets, + recovery_addr, + }, + )?; + + // Then call ExecuteMsg::Action using a SubMsg. + let sub_msg = SubMsg::reply_always( + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: env.contract.address.to_string(), + msg: to_binary(&ExecuteMsg::Action { + sent_asset, + timeout_timestamp, + action, + exact_out, + min_asset, + })?, + funds: info.funds, + }), + RECOVER_REPLY_ID, + ); + + Ok(Response::new().add_submessage(sub_msg)) +} + +//////////////////////// +/// HELPER FUNCTIONS /// +//////////////////////// + +// ACTION HELPER FUNCTIONS + +// Validates and adds an action message to the response +fn validate_and_dispatch_action( + deps: DepsMut, + action: Action, + action_asset: Asset, + timeout_timestamp: u64, + mut response: Response, +) -> Result { + match action { + Action::Transfer { to_address } => { + // Error if the destination address is not a valid address on the current chain + deps.api.addr_validate(&to_address)?; + + // Create the transfer message + let transfer_msg = action_asset.transfer(&to_address); + + // Add the transfer message to the response + response = response + .add_message(transfer_msg) + .add_attribute("action", "dispatch_action_transfer"); + } + Action::IbcTransfer { ibc_info, .. } => { + // Validates recover address, errors if invalid + deps.api.addr_validate(&ibc_info.recover_address)?; + + let transfer_out_coin = match action_asset { + Asset::Native(coin) => coin, + _ => return Err(ContractError::NonNativeIbcTransfer), + }; + + // Create the IBC transfer message + let ibc_transfer_msg: IbcTransferExecuteMsg = IbcTransfer { + info: ibc_info, + coin: transfer_out_coin.clone(), + timeout_timestamp, + } + .into(); + + // Get the IBC transfer adapter contract address + let ibc_transfer_contract_address = IBC_TRANSFER_CONTRACT_ADDRESS.load(deps.storage)?; + + // Send the IBC transfer by calling the IBC transfer contract + let ibc_transfer_msg = WasmMsg::Execute { + contract_addr: ibc_transfer_contract_address.to_string(), + msg: to_binary(&ibc_transfer_msg)?, + funds: vec![transfer_out_coin], + }; + + // Add the IBC transfer message to the response + response = response + .add_message(ibc_transfer_msg) + .add_attribute("action", "dispatch_action_ibc_transfer"); + } + Action::ContractCall { + contract_address, + msg, + } => { + // Verify the contract address is valid, error if invalid + let checked_contract_address = deps.api.addr_validate(&contract_address)?; + + // Error if the contract address is in the blocked contract addresses map + if BLOCKED_CONTRACT_ADDRESSES.has(deps.storage, &checked_contract_address) { + return Err(ContractError::ContractCallAddressBlocked); + } + + // Create the contract call message + let contract_call_msg = action_asset.into_wasm_msg(contract_address, msg)?; + + // Add the contract call message to the response + response = response + .add_message(contract_call_msg) + .add_attribute("action", "dispatch_action_contract_call"); + } + Action::HplTransfer { + dest_domain, + recipient, + hook, + metadata, + warp_address, + } => { + let transfer_out_coin = match action_asset { + Asset::Native(coin) => coin, + _ => return Err(ContractError::NonNativeHplTransfer), + }; + + // Create the Hyperlane transfer message + let hpl_transfer_msg: HplExecuteMsg = HplTransfer { + dest_domain, + recipient, + hook, + metadata, + warp_address, + }; + + // Get the Hyperlane transfer adapter contract address + let hpl_transfer_contract_address = + HYPERLANE_TRANSFER_CONTRACT_ADDRESS.load(deps.storage)?; + + // Send the Hyperlane transfer by calling the Hyperlane transfer contract + let hpl_transfer_msg = WasmMsg::Execute { + contract_addr: hpl_transfer_contract_address.to_string(), + msg: to_binary(&hpl_transfer_msg)?, + funds: vec![transfer_out_coin], + }; + + // Add the Hyperlane transfer message to the response + response = response + .add_message(hpl_transfer_msg) + .add_attribute("action", "dispatch_action_ibc_transfer"); + } + }; + + Ok(response) +} + +// IBC FEE HELPER FUNCTIONS + +// Creates the fee swap and ibc transfer messages and adds them to the response +fn handle_ibc_transfer_fees( + deps: &DepsMut, + ibc_info: &IbcInfo, + fee_swap: &Option, + remaining_asset: &mut Asset, + mut response: Response, +) -> Result { + let ibc_fee_coin = ibc_info + .fee + .as_ref() + .map(|fee| fee.one_coin()) + .transpose()?; + + if let Some(fee_swap) = fee_swap { + let ibc_fee_coin = ibc_fee_coin + .clone() + .ok_or(ContractError::FeeSwapWithoutIbcFees)?; + + // NOTE: this call mutates remaining_asset by deducting ibc_fee_coin's amount from it + let fee_swap_msg = + verify_and_create_fee_swap_msg(deps, fee_swap, remaining_asset, &ibc_fee_coin)?; + + // Add the fee swap message to the response + response = response + .add_message(fee_swap_msg) + .add_attribute("action", "dispatch_fee_swap"); + } else if let Some(ibc_fee_coin) = &ibc_fee_coin { + if remaining_asset.denom() != ibc_fee_coin.denom { + return Err(ContractError::IBCFeeDenomDiffersFromAssetReceived); + } + + // Deduct the ibc_fee_coin amount from the remaining asset amount + remaining_asset.sub(ibc_fee_coin.amount)?; + } + + // Dispatch the ibc fee bank send to the ibc transfer adapter contract if needed + if let Some(ibc_fee_coin) = ibc_fee_coin { + // Get the ibc transfer adapter contract address + let ibc_transfer_contract_address = IBC_TRANSFER_CONTRACT_ADDRESS.load(deps.storage)?; + + // Create the ibc fee bank send message + let ibc_fee_msg = BankMsg::Send { + to_address: ibc_transfer_contract_address.to_string(), + amount: vec![ibc_fee_coin], + }; + + // Add the ibc fee message to the response + response = response + .add_message(ibc_fee_msg) + .add_attribute("action", "dispatch_ibc_fee_bank_send"); + } + + Ok(response) +} + +// SWAP MESSAGE HELPER FUNCTIONS + +// Creates the fee swap message and returns it +// Also deducts the fee swap in amount from the mutable remaining asset +fn verify_and_create_fee_swap_msg( + deps: &DepsMut, + fee_swap: &SwapExactAssetOut, + remaining_asset: &mut Asset, + ibc_fee_coin: &Coin, +) -> ContractResult { + // Validate swap operations + validate_swap_operations( + &fee_swap.operations, + remaining_asset.denom(), + &ibc_fee_coin.denom, + )?; + + // Get swap adapter contract address from venue name + let fee_swap_adapter_contract_address = + SWAP_VENUE_MAP.load(deps.storage, &fee_swap.swap_venue_name)?; + + // Query the swap adapter to get the asset in needed for the fee swap + let fee_swap_asset_in = query_swap_asset_in( + deps, + &fee_swap_adapter_contract_address, + fee_swap, + &ibc_fee_coin.clone().into(), + )?; + + // Verify the fee swap in denom is the same as the denom received from the message to the contract + if fee_swap_asset_in.denom() != remaining_asset.denom() { + return Err(ContractError::FeeSwapAssetInDenomMismatch); + } + + // Deduct the fee swap in amount from the remaining asset amount + // Error if swap requires more than the remaining asset amount + remaining_asset.sub(fee_swap_asset_in.amount())?; + + // Create the fee swap message args + let fee_swap_msg_args: SwapExecuteMsg = fee_swap.clone().into(); + + // Create the fee swap message + let fee_swap_msg = fee_swap_asset_in.into_wasm_msg( + fee_swap_adapter_contract_address.to_string(), + to_binary(&fee_swap_msg_args)?, + )?; + + Ok(fee_swap_msg) +} + +// AFFILIATE FEE HELPER FUNCTIONS + +// Verifies the affiliate address is valid, if so then +// returns the calculated affiliate fee amount. +fn verify_and_calculate_affiliate_fee_amount( + deps: &DepsMut, + min_asset: &Asset, + affiliate: &Affiliate, +) -> ContractResult { + // Verify the affiliate address is valid + deps.api.addr_validate(&affiliate.address)?; + + // Get the affiliate fee amount by multiplying the min_asset + // amount by the affiliate basis points fee divided by 10000 + let affiliate_fee_amount = min_asset + .amount() + .multiply_ratio(affiliate.basis_points_fee, Uint128::new(10000)); + + Ok(affiliate_fee_amount) +} + +// QUERY HELPER FUNCTIONS + +// Unexposed query helper function that queries the swap adapter contract to get the +// asset in needed for a given swap. Verifies the swap's in denom is the same as the +// swap asset denom from the message. Returns the swap asset in. +fn query_swap_asset_in( + deps: &DepsMut, + swap_adapter_contract_address: &Addr, + swap: &SwapExactAssetOut, + swap_asset_out: &Asset, +) -> ContractResult { + // Query the swap adapter to get the asset in needed for the fee swap + let fee_swap_asset_in: Asset = deps.querier.query_wasm_smart( + swap_adapter_contract_address, + &SwapQueryMsg::SimulateSwapExactAssetOut { + asset_out: swap_asset_out.clone(), + swap_operations: swap.operations.clone(), + }, + )?; + + Ok(fee_swap_asset_in) +} diff --git a/contracts/secret-entry-point/src/lib.rs b/contracts/secret-entry-point/src/lib.rs new file mode 100644 index 00000000..4bf5afc2 --- /dev/null +++ b/contracts/secret-entry-point/src/lib.rs @@ -0,0 +1,8 @@ +pub mod asset; +pub mod contract; +pub mod error; +pub mod execute; +pub mod msg; +pub mod query; +pub mod reply; +pub mod state; diff --git a/contracts/secret-entry-point/src/msg.rs b/contracts/secret-entry-point/src/msg.rs new file mode 100644 index 00000000..543b5eba --- /dev/null +++ b/contracts/secret-entry-point/src/msg.rs @@ -0,0 +1,180 @@ +use crate::asset::Asset; +use skip::{ + ibc::IbcInfo, + swap::{Swap, SwapExactAssetOut, SwapVenue}, +}; + +use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::{Addr, Binary, HexBinary, Uint128}; + +#[cw_serde] +pub struct Snip20ReceiveMsg { + pub sender: Addr, + pub from: Addr, + pub amount: Uint128, + pub memo: Option, + pub msg: Option, +} + +/////////////// +/// MIGRATE /// +/////////////// + +// The MigrateMsg struct defines the migration parameters for the entry point contract. +#[cw_serde] +pub struct MigrateMsg {} + +/////////////////// +/// INSTANTIATE /// +/////////////////// + +// The InstantiateMsg struct defines the initialization parameters for the entry point contract. +#[cw_serde] +pub struct InstantiateMsg { + pub swap_venues: Vec, + pub ibc_transfer_contract_address: String, + pub hyperlane_transfer_contract_address: Option, +} + +/////////////// +/// EXECUTE /// +/////////////// + +// The ExecuteMsg enum defines the execution messages that the entry point contract can handle. +// Only the SwapAndAction message is callable by external users. +#[cw_serde] +#[allow(clippy::large_enum_variant)] +pub enum ExecuteMsg { + Receive(Snip20ReceiveMsg), + SwapAndActionWithRecover { + sent_asset: Option, + user_swap: Swap, + min_asset: Asset, + timeout_timestamp: u64, + post_swap_action: Action, + affiliates: Vec, + recovery_addr: Addr, + }, + SwapAndAction { + sent_asset: Option, + user_swap: Swap, + min_asset: Asset, + timeout_timestamp: u64, + post_swap_action: Action, + affiliates: Vec, + }, + UserSwap { + swap: Swap, + min_asset: Asset, + remaining_asset: Asset, + affiliates: Vec, + }, + PostSwapAction { + min_asset: Asset, + timeout_timestamp: u64, + post_swap_action: Action, + exact_out: bool, + }, + Action { + sent_asset: Option, + timeout_timestamp: u64, + action: Action, + exact_out: bool, + min_asset: Option, + }, + ActionWithRecover { + sent_asset: Option, + timeout_timestamp: u64, + action: Action, + exact_out: bool, + min_asset: Option, + recovery_addr: Addr, + }, +} + +/// This structure describes a CW20 hook message. +#[cw_serde] +pub enum Snip20HookMsg { + SwapAndActionWithRecover { + user_swap: Swap, + min_asset: Asset, + timeout_timestamp: u64, + post_swap_action: Action, + affiliates: Vec, + recovery_addr: Addr, + }, + SwapAndAction { + user_swap: Swap, + min_asset: Asset, + timeout_timestamp: u64, + post_swap_action: Action, + affiliates: Vec, + }, + Action { + timeout_timestamp: u64, + action: Action, + exact_out: bool, + min_asset: Option, + }, + ActionWithRecover { + timeout_timestamp: u64, + action: Action, + exact_out: bool, + min_asset: Option, + recovery_addr: Addr, + }, +} + +///////////// +/// QUERY /// +///////////// + +// The QueryMsg enum defines the queries the entry point contract provides. +#[cw_serde] +#[derive(QueryResponses)] +pub enum QueryMsg { + // SwapVenueAdapterContract returns the address of the swap + // adapter contract for the given swap venue name. + #[returns(cosmwasm_std::Addr)] + SwapVenueAdapterContract { name: String }, + + // IbcTransferAdapterContract returns the address of the IBC + // transfer adapter contract. + #[returns(cosmwasm_std::Addr)] + IbcTransferAdapterContract {}, +} + +//////////////////// +/// COMMON TYPES /// +//////////////////// + +// The Action enum is used to specify what action to take after a swap. +#[cw_serde] +pub enum Action { + Transfer { + to_address: String, + }, + IbcTransfer { + ibc_info: IbcInfo, + fee_swap: Option, + }, + ContractCall { + contract_address: String, + msg: Binary, + }, + HplTransfer { + dest_domain: u32, + recipient: HexBinary, + hook: Option, + metadata: Option, + warp_address: String, + }, +} + +// The Affiliate struct is used to specify an affiliate address and BPS fee taken +// from the min_asset to send to that address. +#[cw_serde] +pub struct Affiliate { + pub basis_points_fee: Uint128, + pub address: String, +} diff --git a/contracts/secret-entry-point/src/query.rs b/contracts/secret-entry-point/src/query.rs new file mode 100644 index 00000000..26cdf6f3 --- /dev/null +++ b/contracts/secret-entry-point/src/query.rs @@ -0,0 +1,12 @@ +use crate::state::{IBC_TRANSFER_CONTRACT_ADDRESS, SWAP_VENUE_MAP}; +use cosmwasm_std::{Addr, Deps, StdResult}; + +// Queries the swap venue map by name and returns the swap adapter contract address if it exists +pub fn query_swap_venue_adapter_contract(deps: Deps, name: String) -> StdResult { + SWAP_VENUE_MAP.load(deps.storage, &name) +} + +// Queries the IBC transfer adapter contract address and returns it if it exists +pub fn query_ibc_transfer_adapter_contract(deps: Deps) -> StdResult { + IBC_TRANSFER_CONTRACT_ADDRESS.load(deps.storage) +} diff --git a/contracts/secret-entry-point/src/reply.rs b/contracts/secret-entry-point/src/reply.rs new file mode 100644 index 00000000..2c11e18e --- /dev/null +++ b/contracts/secret-entry-point/src/reply.rs @@ -0,0 +1,42 @@ +use crate::error::ContractError; +use crate::state::RECOVER_TEMP_STORAGE; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, CosmosMsg, DepsMut, Reply, Response, SubMsgResult}; +use skip::asset::Asset; + +pub const RECOVER_REPLY_ID: u64 = 1; + +#[cw_serde] +pub struct RecoverTempStorage { + pub assets: Vec, + pub recovery_addr: Addr, +} + +pub fn reply_swap_and_action_with_recover( + deps: DepsMut, + msg: Reply, +) -> Result { + match msg.result { + SubMsgResult::Ok(_response) => { + RECOVER_TEMP_STORAGE.remove(deps.storage); + + Ok(Response::new().add_attribute("status", "swap_and_action_successful")) + } + SubMsgResult::Err(e) => { + let storage = RECOVER_TEMP_STORAGE.load(deps.storage)?; + + let return_assets_msgs: Vec = storage + .assets + .into_iter() + .map(|asset| asset.transfer(storage.recovery_addr.as_str())) + .collect(); + + RECOVER_TEMP_STORAGE.remove(deps.storage); + + Ok(Response::new() + .add_messages(return_assets_msgs) + .add_attribute("status", "swap_and_action_failed") + .add_attribute("error", e)) + } + } +} diff --git a/contracts/secret-entry-point/src/state.rs b/contracts/secret-entry-point/src/state.rs new file mode 100644 index 00000000..2c668b81 --- /dev/null +++ b/contracts/secret-entry-point/src/state.rs @@ -0,0 +1,18 @@ +use crate::reply::RecoverTempStorage; +use cosmwasm_std::{Addr, Uint128}; +use secret_storage_plus::{Item, Map}; + +pub const BLOCKED_CONTRACT_ADDRESSES: Map<&Addr, ()> = Map::new("blocked_contract_addresses"); +pub const SWAP_VENUE_MAP: Map<&str, Addr> = Map::new("swap_venue_map"); +pub const IBC_TRANSFER_CONTRACT_ADDRESS: Item = Item::new("ibc_transfer_contract_address"); +pub const HYPERLANE_TRANSFER_CONTRACT_ADDRESS: Item = + Item::new("hyperlane_transfer_contract_address"); + +// Temporary state to save variables to be used in +// reply handling in case of recovering from an error +pub const RECOVER_TEMP_STORAGE: Item = Item::new("recover_temp_storage"); + +// Temporary state to save the amount of the out asset the contract +// has pre swap so that we can ensure the amount transferred out does not +// exceed the amount the contract obtained from the current swap/call +pub const PRE_SWAP_OUT_ASSET_AMOUNT: Item = Item::new("pre_swap_out_asset_amount"); From d9198490f47024693f443f92d86b7dcb0da88a06 Mon Sep 17 00:00:00 2001 From: DrPresident Date: Tue, 10 Dec 2024 15:40:49 -0600 Subject: [PATCH 07/29] moved more skip code into contract --- .../adapters/swap/shade-protocol/src/asset.rs | 612 ++++++++++++++++++ .../swap/shade-protocol/src/contract.rs | 45 +- .../adapters/swap/shade-protocol/src/lib.rs | 2 + .../adapters/swap/shade-protocol/src/msg.rs | 59 ++ 4 files changed, 681 insertions(+), 37 deletions(-) create mode 100644 contracts/adapters/swap/shade-protocol/src/asset.rs create mode 100644 contracts/adapters/swap/shade-protocol/src/msg.rs diff --git a/contracts/adapters/swap/shade-protocol/src/asset.rs b/contracts/adapters/swap/shade-protocol/src/asset.rs new file mode 100644 index 00000000..0e900023 --- /dev/null +++ b/contracts/adapters/swap/shade-protocol/src/asset.rs @@ -0,0 +1,612 @@ +use crate::error::ContractError; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Api, Coin, Uint128}; +use cw20::{Cw20Coin, Cw20CoinVerified}; +// use cw_utils::nonpayable; + +#[cw_serde] +pub enum Asset { + Native(Coin), + Cw20(Cw20Coin), +} + +impl From for Asset { + fn from(coin: Coin) -> Self { + Asset::Native(coin) + } +} + +impl From for Asset { + fn from(cw20_coin: Cw20Coin) -> Self { + Asset::Cw20(cw20_coin) + } +} + +impl From for Asset { + fn from(cw20_coin_verified: Cw20CoinVerified) -> Self { + Asset::Cw20(Cw20Coin { + address: cw20_coin_verified.address.to_string(), + amount: cw20_coin_verified.amount, + }) + } +} + +impl Asset { + pub fn default_native() -> Self { + Asset::Native(Coin::default()) + } + + pub fn new(api: &dyn Api, denom: &str, amount: Uint128) -> Self { + match api.addr_validate(denom) { + Ok(addr) => Asset::Cw20(Cw20Coin { + address: addr.to_string(), + amount: amount.u128().into(), + }), + Err(_) => Asset::Native(Coin { + denom: denom.to_string(), + amount, + }), + } + } + + pub fn denom(&self) -> &str { + match self { + Asset::Native(coin) => &coin.denom, + Asset::Cw20(coin) => &coin.address, + } + } + + pub fn amount(&self) -> Uint128 { + match self { + Asset::Native(coin) => coin.amount, + Asset::Cw20(coin) => coin.amount.u128().into(), + } + } + + /* + pub fn add(&mut self, amount: Uint128) -> Result { + match self { + Asset::Native(coin) => { + coin.amount = coin.amount.checked_add(amount)?; + Ok(coin.amount) + } + Asset::Cw20(coin) => { + coin.amount = coin.amount.checked_add(amount.u128().into())?; + Ok(coin.amount.u128().into()) + } + } + } + + pub fn sub(&mut self, amount: Uint128) -> Result { + match self { + Asset::Native(coin) => { + coin.amount = coin.amount.checked_sub(amount)?; + Ok(coin.amount) + } + Asset::Cw20(coin) => { + coin.amount = coin.amount.checked_sub(amount.u128().into())?; + Ok(coin.amount.u128().into()) + } + } + } + */ + + /* + pub fn transfer(self, to_address: &str) -> CosmosMsg { + match self { + Asset::Native(coin) => CosmosMsg::Bank(BankMsg::Send { + to_address: to_address.to_string(), + amount: vec![coin], + }), + Asset::Cw20(coin) => CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: coin.address.clone(), + msg: to_binary(&Cw20ExecuteMsg::Transfer { + recipient: to_address.to_string(), + amount: coin.amount, + }) + .unwrap(), + funds: vec![], + }), + } + } + */ + + /* + pub fn into_wasm_msg(self, contract_addr: String, msg: Binary) -> Result { + match self { + Asset::Native(coin) => Ok(WasmMsg::Execute { + contract_addr, + msg, + funds: vec![coin], + }), + Asset::Cw20(coin) => Ok(WasmMsg::Execute { + contract_addr: coin.address, + msg: to_binary(&Cw20ExecuteMsg::Send { + contract: contract_addr, + amount: coin.amount, + msg, + })?, + funds: vec![], + }), + } + } + */ + + /* + pub fn validate(&self, deps: &DepsMut, env: &Env, info: &MessageInfo) -> Result<(), SkipError> { + match self { + Asset::Native(coin) => { + unimplemented!(); + } + Asset::Cw20(coin) => { + // Validate that the message is nonpayable + nonpayable(info)?; + + let verified_cw20_coin_addr = deps.api.addr_validate(&coin.address)?; + + let cw20_contract = Cw20Contract(verified_cw20_coin_addr); + + let balance = cw20_contract.balance(&deps.querier, &env.contract.address)?; + + if coin.amount <= balance { + Ok(()) + } else { + Err(SkipError::InvalidCw20Coin) + } + } + } + } + */ +} + +/* +pub fn get_current_asset_available( + deps: &DepsMut, + env: &Env, + denom: &str, +) -> Result { + match deps.api.addr_validate(denom) { + Ok(addr) => { + let cw20_contract = Cw20Contract(addr.clone()); + + let amount = cw20_contract.balance(&deps.querier, &env.contract.address)?; + + Ok(Asset::Cw20(Cw20Coin { + address: addr.to_string(), + amount, + })) + } + Err(_) => { + let coin = deps.querier.query_balance(&env.contract.address, denom)?; + + Ok(Asset::Native(coin)) + } + } +} +*/ + +#[cfg(test)] +mod tests { + use super::*; + use cosmwasm_std::{ + testing::{mock_dependencies_with_balances, mock_env, mock_info}, + Addr, ContractResult, QuerierResult, SystemResult, WasmQuery, + }; + use cw20::BalanceResponse; + use cw_utils::PaymentError; + + #[test] + fn test_default_native() { + let asset = Asset::default_native(); + + assert_eq!( + asset, + Asset::Native(Coin { + denom: "".to_string(), + amount: Uint128::zero(), + }) + ); + } + + #[test] + fn test_new() { + // TEST 1: Native asset + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let asset = Asset::new(deps.as_mut().api, "ua", Uint128::new(100)); + + assert_eq!( + asset, + Asset::Native(Coin { + denom: "ua".to_string(), + amount: Uint128::new(100), + }) + ); + + // TEST 2: Cw20 asset + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let asset = Asset::new(deps.as_mut().api, "asset", Uint128::new(100)); + + assert_eq!( + asset, + Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }) + ); + } + + #[test] + fn test_asset_native() { + let asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + assert_eq!(asset.denom(), "uatom"); + assert_eq!(asset.amount(), Uint128::new(100)); + } + + #[test] + fn test_asset_cw20() { + let asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + assert_eq!(asset.denom(), "asset"); + assert_eq!(asset.amount(), Uint128::new(100)); + } + + #[test] + fn test_add() { + // TEST 1: Native asset + let mut asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + asset.add(Uint128::new(20)).unwrap(); + + assert_eq!(asset.amount(), Uint128::new(120)); + + // TEST 2: Cw20 asset + let mut asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + asset.add(Uint128::new(20)).unwrap(); + + assert_eq!(asset.amount(), Uint128::new(120)); + } + + #[test] + fn test_sub() { + // TEST 1: Native asset + let mut asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + asset.sub(Uint128::new(20)).unwrap(); + + assert_eq!(asset.amount(), Uint128::new(80)); + + // TEST 2: Cw20 asset + let mut asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + asset.sub(Uint128::new(20)).unwrap(); + + assert_eq!(asset.amount(), Uint128::new(80)); + } + + #[test] + fn test_asset_transfer_native() { + let asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + let msg = asset.transfer("addr"); + + match msg { + CosmosMsg::Bank(BankMsg::Send { to_address, amount }) => { + assert_eq!(to_address, "addr"); + assert_eq!(amount.len(), 1); + assert_eq!(amount[0].denom, "uatom"); + assert_eq!(amount[0].amount, Uint128::new(100)); + } + _ => panic!("Unexpected message type"), + } + } + + #[test] + fn test_asset_transfer_cw20() { + let asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + let msg = asset.transfer("addr"); + + match msg { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr, + msg, + funds, + }) => { + assert_eq!(contract_addr, "asset"); + assert_eq!( + msg, + to_binary(&Cw20ExecuteMsg::Transfer { + recipient: "addr".to_string(), + amount: Uint128::new(100), + }) + .unwrap() + ); + assert_eq!(funds.len(), 0); + } + _ => panic!("Unexpected message type"), + } + } + + #[test] + fn test_into_astroport_asset() { + // TEST 1: Native asset + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + let astroport_asset = asset.into_astroport_asset(deps.as_mut().api).unwrap(); + + assert_eq!( + astroport_asset, + AstroportAsset { + info: AssetInfo::NativeToken { + denom: "uatom".to_string() + }, + amount: Uint128::new(100), + } + ); + + // TEST 2: Cw20 asset + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + let astroport_asset = asset.into_astroport_asset(deps.as_mut().api).unwrap(); + + assert_eq!( + astroport_asset, + AstroportAsset { + info: AssetInfo::Token { + contract_addr: Addr::unchecked("asset") + }, + amount: Uint128::new(100), + } + ); + } + + #[test] + fn test_validate_native() { + // TEST 1: Valid asset + let asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let env = mock_env(); + + let info = mock_info( + "sender", + &[Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }], + ); + + assert!(asset.validate(&deps.as_mut(), &env, &info).is_ok()); + + // TEST 2: Invalid asset due to less amount of denom sent + let asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let env = mock_env(); + + let info = mock_info( + "sender", + &[Coin { + denom: "uatom".to_string(), + amount: Uint128::new(50), + }], + ); + + let res = asset.validate(&deps.as_mut(), &env, &info); + + assert_eq!(res, Err(ContractError::InvalidNativeCoin)); + + // TEST 3: Invalid asset due to more than one coin sent + let asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let env = mock_env(); + + let info = mock_info( + "sender", + &[ + Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }, + Coin { + denom: "uosmo".to_string(), + amount: Uint128::new(50), + }, + ], + ); + + let res = asset.validate(&deps.as_mut(), &env, &info); + + assert_eq!( + res, + Err(ContractError::Payment(PaymentError::MultipleDenoms {})) + ); + } + + #[test] + fn test_validate_cw20() { + // TEST 1: Valid asset + let asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + // Create mock wasm handler to handle the cw20 balance query + let wasm_handler = |query: &WasmQuery| -> QuerierResult { + match query { + WasmQuery::Smart { .. } => SystemResult::Ok(ContractResult::Ok( + to_binary(&BalanceResponse { + balance: Uint128::from(100u128), + }) + .unwrap(), + )), + _ => panic!("Unsupported query: {:?}", query), + } + }; + + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + deps.querier.update_wasm(wasm_handler); + + let env = mock_env(); + + let info = mock_info("sender", &[]); + + assert!(asset.validate(&deps.as_mut(), &env, &info).is_ok()); + + // TEST 2: Invalid asset due to native coin sent in info + let asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let env = mock_env(); + + let info = mock_info( + "sender", + &[Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }], + ); + + let res = asset.validate(&deps.as_mut(), &env, &info); + + assert_eq!( + res, + Err(ContractError::Payment(PaymentError::NonPayable {})) + ); + + // TEST 3: Invalid asset due to invalid cw20 balance + let asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + // Create mock wasm handler to handle the cw20 balance query + let wasm_handler = |query: &WasmQuery| -> QuerierResult { + match query { + WasmQuery::Smart { .. } => SystemResult::Ok(ContractResult::Ok( + to_binary(&BalanceResponse { + balance: Uint128::from(50u128), + }) + .unwrap(), + )), + _ => panic!("Unsupported query: {:?}", query), + } + }; + + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + deps.querier.update_wasm(wasm_handler); + + let env = mock_env(); + + let info = mock_info("sender", &[]); + + let res = asset.validate(&deps.as_mut(), &env, &info); + + assert_eq!(res, Err(ContractError::InvalidCw20Coin)); + } + + #[test] + fn test_get_current_asset_available() { + // TEST 1: Native asset + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[Coin::new(100, "ua")])]); + + let mut env = mock_env(); + env.contract.address = Addr::unchecked("entry_point"); + + let asset = get_current_asset_available(&deps.as_mut(), &env, "ua").unwrap(); + + assert_eq!( + asset, + Asset::Native(Coin { + denom: "ua".to_string(), + amount: Uint128::new(100), + }) + ); + + // TEST 2: Cw20 asset + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let wasm_handler = |query: &WasmQuery| -> QuerierResult { + match query { + WasmQuery::Smart { .. } => SystemResult::Ok(ContractResult::Ok( + to_binary(&BalanceResponse { + balance: Uint128::from(100u128), + }) + .unwrap(), + )), + _ => panic!("Unsupported query: {:?}", query), + } + }; + + deps.querier.update_wasm(wasm_handler); + + let env = mock_env(); + + let asset = get_current_asset_available(&deps.as_mut(), &env, "asset").unwrap(); + + assert_eq!( + asset, + Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }) + ); + } +} diff --git a/contracts/adapters/swap/shade-protocol/src/contract.rs b/contracts/adapters/swap/shade-protocol/src/contract.rs index ee0a0dd4..741a64c8 100644 --- a/contracts/adapters/swap/shade-protocol/src/contract.rs +++ b/contracts/adapters/swap/shade-protocol/src/contract.rs @@ -1,4 +1,5 @@ use crate::{ + asset::Asset, error::{ContractError, ContractResult}, // skip_error::ContractError, state::{REGISTERED_TOKENS, STATE}, @@ -11,12 +12,14 @@ use cosmwasm_std::{ // use cw2::set_contract_version; use cw20::Cw20Coin; use secret_toolkit::snip20; -use skip::{ - asset::Asset, - swap::{Cw20HookMsg, QueryMsg, SwapOperation}, -}; -use crate::shade_swap_router_msg as shade_router; +use crate::{ + msg::{ + Cw20HookMsg, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, Snip20ReceiveMsg, + SwapOperation, + }, + shade_swap_router_msg as shade_router, +}; #[cw_serde] pub struct State { @@ -26,38 +29,6 @@ pub struct State { pub viewing_key: String, } -#[cw_serde] -pub struct InstantiateMsg { - pub entry_point_contract: ContractInfo, - pub shade_router_contract: ContractInfo, - pub shade_pool_code_hash: String, - pub viewing_key: String, -} - -#[cw_serde] -pub struct MigrateMsg { - pub entry_point_contract: ContractInfo, - pub shade_router_contract: ContractInfo, - pub shade_pool_code_hash: String, - pub viewing_key: String, -} - -#[cw_serde] -pub enum ExecuteMsg { - Receive(Snip20ReceiveMsg), - TransferFundsBack { swapper: Addr, return_denom: String }, - RegisterTokens { contracts: Vec }, -} - -#[cw_serde] -pub struct Snip20ReceiveMsg { - pub sender: Addr, - pub from: Addr, - pub amount: Uint128, - pub memo: Option, - pub msg: Option, -} - // Contract name and version used for migration. /* const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); diff --git a/contracts/adapters/swap/shade-protocol/src/lib.rs b/contracts/adapters/swap/shade-protocol/src/lib.rs index 588dba76..8895750d 100644 --- a/contracts/adapters/swap/shade-protocol/src/lib.rs +++ b/contracts/adapters/swap/shade-protocol/src/lib.rs @@ -1,4 +1,6 @@ +pub mod asset; pub mod contract; pub mod error; +pub mod msg; pub mod shade_swap_router_msg; pub mod state; diff --git a/contracts/adapters/swap/shade-protocol/src/msg.rs b/contracts/adapters/swap/shade-protocol/src/msg.rs new file mode 100644 index 00000000..2b29c0b0 --- /dev/null +++ b/contracts/adapters/swap/shade-protocol/src/msg.rs @@ -0,0 +1,59 @@ +use crate::asset::Asset; +use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::{Addr, Binary, ContractInfo, Uint128}; + +#[cw_serde] +pub struct InstantiateMsg { + pub entry_point_contract: ContractInfo, + pub shade_router_contract: ContractInfo, + pub shade_pool_code_hash: String, + pub viewing_key: String, +} + +#[cw_serde] +pub struct MigrateMsg { + pub entry_point_contract: ContractInfo, + pub shade_router_contract: ContractInfo, + pub shade_pool_code_hash: String, + pub viewing_key: String, +} + +#[cw_serde] +pub enum ExecuteMsg { + Receive(Snip20ReceiveMsg), + TransferFundsBack { swapper: Addr, return_denom: String }, + RegisterTokens { contracts: Vec }, +} + +#[cw_serde] +#[derive(QueryResponses)] +pub enum QueryMsg { + // SimulateSwapExactAssetIn returns the asset out received from the specified asset in + #[returns(Asset)] + SimulateSwapExactAssetIn { + asset_in: Asset, + swap_operations: Vec, + }, +} + +#[cw_serde] +pub enum Cw20HookMsg { + Swap { operations: Vec }, +} + +#[cw_serde] +pub struct SwapOperation { + pub pool: String, + pub denom_in: String, + pub denom_out: String, + pub interface: Option, +} + +#[cw_serde] +pub struct Snip20ReceiveMsg { + pub sender: Addr, + pub from: Addr, + pub amount: Uint128, + pub memo: Option, + pub msg: Option, +} From 276598bf67f7f26127a352a3de53d98d9a80d953 Mon Sep 17 00:00:00 2001 From: DrPresident Date: Wed, 11 Dec 2024 16:10:06 -0600 Subject: [PATCH 08/29] secret-skip package builds --- packages/secret-skip/Cargo.toml | 32 ++ packages/secret-skip/README.md | 4 + packages/secret-skip/src/asset.rs | 659 ++++++++++++++++++++++++ packages/secret-skip/src/entry_point.rs | 172 +++++++ packages/secret-skip/src/error.rs | 54 ++ packages/secret-skip/src/hyperlane.rs | 37 ++ packages/secret-skip/src/ibc.rs | 288 +++++++++++ packages/secret-skip/src/lib.rs | 8 + packages/secret-skip/src/proto_coin.rs | 92 ++++ packages/secret-skip/src/sudo.rs | 27 + packages/secret-skip/src/swap.rs | 422 +++++++++++++++ 11 files changed, 1795 insertions(+) create mode 100644 packages/secret-skip/Cargo.toml create mode 100644 packages/secret-skip/README.md create mode 100644 packages/secret-skip/src/asset.rs create mode 100644 packages/secret-skip/src/entry_point.rs create mode 100644 packages/secret-skip/src/error.rs create mode 100644 packages/secret-skip/src/hyperlane.rs create mode 100644 packages/secret-skip/src/ibc.rs create mode 100644 packages/secret-skip/src/lib.rs create mode 100644 packages/secret-skip/src/proto_coin.rs create mode 100644 packages/secret-skip/src/sudo.rs create mode 100644 packages/secret-skip/src/swap.rs diff --git a/packages/secret-skip/Cargo.toml b/packages/secret-skip/Cargo.toml new file mode 100644 index 00000000..d3273020 --- /dev/null +++ b/packages/secret-skip/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "secret-skip" +version = { workspace = true } +rust-version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +homepage = { workspace = true } +repository = { workspace = true } +documentation = { workspace = true } +keywords = { workspace = true } + +[features] +# for more explicit tests, cargo test --features=backtraces +backtraces = ["cosmwasm-std/backtraces"] + +[dependencies] +#astroport = { workspace = true } +cosmos-sdk-proto = { workspace = true } +cosmwasm-schema = { workspace = true } +#cosmwasm-std = { workspace = true } +cw-utils = { workspace = true } +cw20 = { workspace = true } +ibc-proto = { workspace = true } +#neutron-proto = { workspace = true } +#osmosis-std = { workspace = true } +thiserror = { workspace = true } +#white-whale-std = { workspace = true } + +cosmwasm-std = { package = "secret-cosmwasm-std", version = "1.1.11"} +secret-toolkit = { git = "https://github.com/scrtlabs/secret-toolkit", tag = "v0.10.0" } +secret-storage-plus = { git = "https://github.com/securesecrets/secret-plus-utils", tag = "v0.1.1", features = [] } diff --git a/packages/secret-skip/README.md b/packages/secret-skip/README.md new file mode 100644 index 00000000..b6e903e6 --- /dev/null +++ b/packages/secret-skip/README.md @@ -0,0 +1,4 @@ +# Skip: Common Types and Functions + +This Skip packages folder contains common types and functions that are used across multiple Skip Go contracts. + diff --git a/packages/secret-skip/src/asset.rs b/packages/secret-skip/src/asset.rs new file mode 100644 index 00000000..38720e99 --- /dev/null +++ b/packages/secret-skip/src/asset.rs @@ -0,0 +1,659 @@ +use crate::error::SkipError; +// use astroport::asset::{Asset as AstroportAsset, AssetInfo}; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{ + to_binary, Api, BankMsg, Binary, Coin, CosmosMsg, DepsMut, Env, MessageInfo, OverflowError, + OverflowOperation, Uint128, WasmMsg, +}; +use cw20::{Cw20Coin, Cw20CoinVerified, Cw20Contract, Cw20ExecuteMsg}; +use cw_utils::{nonpayable, one_coin}; +/* +use white_whale_std::pool_network::asset::{ + Asset as WhiteWhaleAsset, AssetInfo as WhiteWhaleAssetInfo, +}; +*/ + +#[cw_serde] +pub enum Asset { + Native(Coin), + Cw20(Cw20Coin), +} + +impl From for Asset { + fn from(coin: Coin) -> Self { + Asset::Native(coin) + } +} + +impl From for Asset { + fn from(cw20_coin: Cw20Coin) -> Self { + Asset::Cw20(cw20_coin) + } +} + +impl From for Asset { + fn from(cw20_coin_verified: Cw20CoinVerified) -> Self { + Asset::Cw20(Cw20Coin { + address: cw20_coin_verified.address.to_string(), + amount: cw20_coin_verified.amount, + }) + } +} + +impl Asset { + pub fn default_native() -> Self { + Asset::Native(Coin::default()) + } + + pub fn new(api: &dyn Api, denom: &str, amount: Uint128) -> Self { + match api.addr_validate(denom) { + Ok(addr) => Asset::Cw20(Cw20Coin { + address: addr.to_string(), + amount: amount.u128().into(), + }), + Err(_) => Asset::Native(Coin { + denom: denom.to_string(), + amount, + }), + } + } + + pub fn denom(&self) -> &str { + match self { + Asset::Native(coin) => &coin.denom, + Asset::Cw20(coin) => &coin.address, + } + } + + pub fn amount(&self) -> Uint128 { + match self { + Asset::Native(coin) => coin.amount.u128().into(), + Asset::Cw20(coin) => coin.amount.u128().into(), + } + } + + pub fn add(&mut self, amount: Uint128) -> Result { + match self { + Asset::Native(coin) => { + coin.amount = coin.amount.checked_add(amount)?; + Ok(coin.amount) + } + Asset::Cw20(coin) => { + coin.amount = (coin.amount.u128() + amount.u128()).into(); + Ok(coin.amount.u128().into()) + } + } + } + + pub fn sub(&mut self, amount: Uint128) -> Result { + match self { + Asset::Native(coin) => { + coin.amount = coin.amount.checked_sub(amount)?; + Ok(coin.amount) + } + Asset::Cw20(coin) => { + if amount > coin.amount.u128().into() { + return Err( + OverflowError::new(OverflowOperation::Sub, amount, coin.amount).into(), + ); + } + coin.amount = (coin.amount.u128() - amount.u128()).into(); + Ok(coin.amount.u128().into()) + } + } + } + + /* + pub fn transfer(self, to_address: &str) -> CosmosMsg { + match self { + Asset::Native(coin) => CosmosMsg::Bank(BankMsg::Send { + to_address: to_address.to_string(), + amount: vec![coin], + }), + Asset::Cw20(coin) => CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: coin.address.clone(), + msg: to_binary(&Cw20ExecuteMsg::Transfer { + recipient: to_address.to_string(), + amount: coin.amount, + }) + .unwrap(), + funds: vec![], + }), + } + } + + pub fn into_wasm_msg(self, contract_addr: String, msg: Binary) -> Result { + match self { + Asset::Native(coin) => Ok(WasmMsg::Execute { + contract_addr, + msg, + funds: vec![coin], + }), + Asset::Cw20(coin) => Ok(WasmMsg::Execute { + contract_addr: coin.address, + msg: to_binary(&Cw20ExecuteMsg::Send { + contract: contract_addr, + amount: coin.amount, + msg, + })?, + funds: vec![], + }), + } + } + */ + + /* + pub fn into_astroport_asset(&self, api: &dyn Api) -> Result { + match self { + Asset::Native(coin) => Ok(AstroportAsset { + info: AssetInfo::NativeToken { + denom: coin.denom.clone(), + }, + amount: coin.amount, + }), + Asset::Cw20(cw20_coin) => Ok(AstroportAsset { + info: AssetInfo::Token { + contract_addr: api.addr_validate(&cw20_coin.address)?, + }, + amount: cw20_coin.amount, + }), + } + } + + pub fn into_white_whale_asset(&self, api: &dyn Api) -> Result { + match self { + Asset::Native(coin) => Ok(WhiteWhaleAsset { + info: WhiteWhaleAssetInfo::NativeToken { + denom: coin.denom.clone(), + }, + amount: coin.amount, + }), + Asset::Cw20(cw20_coin) => Ok(WhiteWhaleAsset { + info: WhiteWhaleAssetInfo::Token { + contract_addr: api.addr_validate(&cw20_coin.address)?.into_string(), + }, + amount: cw20_coin.amount, + }), + } + } + + pub fn validate(&self, deps: &DepsMut, env: &Env, info: &MessageInfo) -> Result<(), SkipError> { + match self { + Asset::Native(coin) => { + let compare_coin = one_coin(info)?; + + if compare_coin.eq(coin) { + Ok(()) + } else { + Err(SkipError::InvalidNativeCoin) + } + } + Asset::Cw20(coin) => { + // Validate that the message is nonpayable + nonpayable(info)?; + + let verified_cw20_coin_addr = deps.api.addr_validate(&coin.address)?; + + let cw20_contract = Cw20Contract(verified_cw20_coin_addr); + + let balance = cw20_contract.balance(&deps.querier, &env.contract.address)?; + + if coin.amount <= balance { + Ok(()) + } else { + Err(SkipError::InvalidCw20Coin) + } + } + } + } + */ +} + +/* +pub fn get_current_asset_available( + deps: &DepsMut, + env: &Env, + denom: &str, +) -> Result { + match deps.api.addr_validate(denom) { + Ok(addr) => { + let cw20_contract = Cw20Contract(addr.clone()); + + let amount = cw20_contract.balance(&deps.querier, &env.contract.address)?; + + Ok(Asset::Cw20(Cw20Coin { + address: addr.to_string(), + amount, + })) + } + Err(_) => { + let coin = deps.querier.query_balance(&env.contract.address, denom)?; + + Ok(Asset::Native(coin)) + } + } +} +*/ + +#[cfg(test)] +mod tests { + use super::*; + use cosmwasm_std::{ + testing::{mock_dependencies_with_balances, mock_env, mock_info}, + Addr, ContractResult, QuerierResult, SystemResult, WasmQuery, + }; + use cw20::BalanceResponse; + use cw_utils::PaymentError; + + #[test] + fn test_default_native() { + let asset = Asset::default_native(); + + assert_eq!( + asset, + Asset::Native(Coin { + denom: "".to_string(), + amount: Uint128::zero(), + }) + ); + } + + #[test] + fn test_new() { + // TEST 1: Native asset + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let asset = Asset::new(deps.as_mut().api, "ua", Uint128::new(100)); + + assert_eq!( + asset, + Asset::Native(Coin { + denom: "ua".to_string(), + amount: Uint128::new(100), + }) + ); + + // TEST 2: Cw20 asset + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let asset = Asset::new(deps.as_mut().api, "asset", Uint128::new(100)); + + assert_eq!( + asset, + Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }) + ); + } + + #[test] + fn test_asset_native() { + let asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + assert_eq!(asset.denom(), "uatom"); + assert_eq!(asset.amount(), Uint128::new(100)); + } + + #[test] + fn test_asset_cw20() { + let asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + assert_eq!(asset.denom(), "asset"); + assert_eq!(asset.amount(), Uint128::new(100)); + } + + #[test] + fn test_add() { + // TEST 1: Native asset + let mut asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + asset.add(Uint128::new(20)).unwrap(); + + assert_eq!(asset.amount(), Uint128::new(120)); + + // TEST 2: Cw20 asset + let mut asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + asset.add(Uint128::new(20)).unwrap(); + + assert_eq!(asset.amount(), Uint128::new(120)); + } + + #[test] + fn test_sub() { + // TEST 1: Native asset + let mut asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + asset.sub(Uint128::new(20)).unwrap(); + + assert_eq!(asset.amount(), Uint128::new(80)); + + // TEST 2: Cw20 asset + let mut asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + asset.sub(Uint128::new(20)).unwrap(); + + assert_eq!(asset.amount(), Uint128::new(80)); + } + + #[test] + fn test_asset_transfer_native() { + let asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + let msg = asset.transfer("addr"); + + match msg { + CosmosMsg::Bank(BankMsg::Send { to_address, amount }) => { + assert_eq!(to_address, "addr"); + assert_eq!(amount.len(), 1); + assert_eq!(amount[0].denom, "uatom"); + assert_eq!(amount[0].amount, Uint128::new(100)); + } + _ => panic!("Unexpected message type"), + } + } + + #[test] + fn test_asset_transfer_cw20() { + let asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + let msg = asset.transfer("addr"); + + match msg { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr, + msg, + funds, + }) => { + assert_eq!(contract_addr, "asset"); + assert_eq!( + msg, + to_binary(&Cw20ExecuteMsg::Transfer { + recipient: "addr".to_string(), + amount: Uint128::new(100), + }) + .unwrap() + ); + assert_eq!(funds.len(), 0); + } + _ => panic!("Unexpected message type"), + } + } + + #[test] + fn test_into_astroport_asset() { + // TEST 1: Native asset + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + let astroport_asset = asset.into_astroport_asset(deps.as_mut().api).unwrap(); + + assert_eq!( + astroport_asset, + AstroportAsset { + info: AssetInfo::NativeToken { + denom: "uatom".to_string() + }, + amount: Uint128::new(100), + } + ); + + // TEST 2: Cw20 asset + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + let astroport_asset = asset.into_astroport_asset(deps.as_mut().api).unwrap(); + + assert_eq!( + astroport_asset, + AstroportAsset { + info: AssetInfo::Token { + contract_addr: Addr::unchecked("asset") + }, + amount: Uint128::new(100), + } + ); + } + + #[test] + fn test_validate_native() { + // TEST 1: Valid asset + let asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let env = mock_env(); + + let info = mock_info( + "sender", + &[Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }], + ); + + assert!(asset.validate(&deps.as_mut(), &env, &info).is_ok()); + + // TEST 2: Invalid asset due to less amount of denom sent + let asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let env = mock_env(); + + let info = mock_info( + "sender", + &[Coin { + denom: "uatom".to_string(), + amount: Uint128::new(50), + }], + ); + + let res = asset.validate(&deps.as_mut(), &env, &info); + + assert_eq!(res, Err(SkipError::InvalidNativeCoin)); + + // TEST 3: Invalid asset due to more than one coin sent + let asset = Asset::Native(Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let env = mock_env(); + + let info = mock_info( + "sender", + &[ + Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }, + Coin { + denom: "uosmo".to_string(), + amount: Uint128::new(50), + }, + ], + ); + + let res = asset.validate(&deps.as_mut(), &env, &info); + + assert_eq!( + res, + Err(SkipError::Payment(PaymentError::MultipleDenoms {})) + ); + } + + #[test] + fn test_validate_cw20() { + // TEST 1: Valid asset + let asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + // Create mock wasm handler to handle the cw20 balance query + let wasm_handler = |query: &WasmQuery| -> QuerierResult { + match query { + WasmQuery::Smart { .. } => SystemResult::Ok(ContractResult::Ok( + to_binary(&BalanceResponse { + balance: Uint128::from(100u128), + }) + .unwrap(), + )), + _ => panic!("Unsupported query: {:?}", query), + } + }; + + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + deps.querier.update_wasm(wasm_handler); + + let env = mock_env(); + + let info = mock_info("sender", &[]); + + assert!(asset.validate(&deps.as_mut(), &env, &info).is_ok()); + + // TEST 2: Invalid asset due to native coin sent in info + let asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let env = mock_env(); + + let info = mock_info( + "sender", + &[Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }], + ); + + let res = asset.validate(&deps.as_mut(), &env, &info); + + assert_eq!(res, Err(SkipError::Payment(PaymentError::NonPayable {}))); + + // TEST 3: Invalid asset due to invalid cw20 balance + let asset = Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }); + + // Create mock wasm handler to handle the cw20 balance query + let wasm_handler = |query: &WasmQuery| -> QuerierResult { + match query { + WasmQuery::Smart { .. } => SystemResult::Ok(ContractResult::Ok( + to_binary(&BalanceResponse { + balance: Uint128::from(50u128), + }) + .unwrap(), + )), + _ => panic!("Unsupported query: {:?}", query), + } + }; + + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + deps.querier.update_wasm(wasm_handler); + + let env = mock_env(); + + let info = mock_info("sender", &[]); + + let res = asset.validate(&deps.as_mut(), &env, &info); + + assert_eq!(res, Err(SkipError::InvalidCw20Coin)); + } + + #[test] + fn test_get_current_asset_available() { + // TEST 1: Native asset + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[Coin::new(100, "ua")])]); + + let mut env = mock_env(); + env.contract.address = Addr::unchecked("entry_point"); + + let asset = get_current_asset_available(&deps.as_mut(), &env, "ua").unwrap(); + + assert_eq!( + asset, + Asset::Native(Coin { + denom: "ua".to_string(), + amount: Uint128::new(100), + }) + ); + + // TEST 2: Cw20 asset + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + let wasm_handler = |query: &WasmQuery| -> QuerierResult { + match query { + WasmQuery::Smart { .. } => SystemResult::Ok(ContractResult::Ok( + to_binary(&BalanceResponse { + balance: Uint128::from(100u128), + }) + .unwrap(), + )), + _ => panic!("Unsupported query: {:?}", query), + } + }; + + deps.querier.update_wasm(wasm_handler); + + let env = mock_env(); + + let asset = get_current_asset_available(&deps.as_mut(), &env, "asset").unwrap(); + + assert_eq!( + asset, + Asset::Cw20(Cw20Coin { + address: "asset".to_string(), + amount: Uint128::new(100), + }) + ); + } +} diff --git a/packages/secret-skip/src/entry_point.rs b/packages/secret-skip/src/entry_point.rs new file mode 100644 index 00000000..2217cbe9 --- /dev/null +++ b/packages/secret-skip/src/entry_point.rs @@ -0,0 +1,172 @@ +use crate::{ + asset::Asset, + ibc::IbcInfo, + swap::{Swap, SwapExactAssetOut, SwapVenue}, +}; + +use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::{Addr, Binary, HexBinary, Uint128}; +use cw20::Cw20ReceiveMsg; + +/////////////// +/// MIGRATE /// +/////////////// + +// The MigrateMsg struct defines the migration parameters for the entry point contract. +#[cw_serde] +pub struct MigrateMsg {} + +/////////////////// +/// INSTANTIATE /// +/////////////////// + +// The InstantiateMsg struct defines the initialization parameters for the entry point contract. +#[cw_serde] +pub struct InstantiateMsg { + pub swap_venues: Vec, + pub ibc_transfer_contract_address: String, + pub hyperlane_transfer_contract_address: Option, +} + +/////////////// +/// EXECUTE /// +/////////////// + +// The ExecuteMsg enum defines the execution messages that the entry point contract can handle. +// Only the SwapAndAction message is callable by external users. +#[cw_serde] +#[allow(clippy::large_enum_variant)] +pub enum ExecuteMsg { + Receive(Cw20ReceiveMsg), + SwapAndActionWithRecover { + sent_asset: Option, + user_swap: Swap, + min_asset: Asset, + timeout_timestamp: u64, + post_swap_action: Action, + affiliates: Vec, + recovery_addr: Addr, + }, + SwapAndAction { + sent_asset: Option, + user_swap: Swap, + min_asset: Asset, + timeout_timestamp: u64, + post_swap_action: Action, + affiliates: Vec, + }, + UserSwap { + swap: Swap, + min_asset: Asset, + remaining_asset: Asset, + affiliates: Vec, + }, + PostSwapAction { + min_asset: Asset, + timeout_timestamp: u64, + post_swap_action: Action, + exact_out: bool, + }, + Action { + sent_asset: Option, + timeout_timestamp: u64, + action: Action, + exact_out: bool, + min_asset: Option, + }, + ActionWithRecover { + sent_asset: Option, + timeout_timestamp: u64, + action: Action, + exact_out: bool, + min_asset: Option, + recovery_addr: Addr, + }, +} + +/// This structure describes a CW20 hook message. +#[cw_serde] +pub enum Cw20HookMsg { + SwapAndActionWithRecover { + user_swap: Swap, + min_asset: Asset, + timeout_timestamp: u64, + post_swap_action: Action, + affiliates: Vec, + recovery_addr: Addr, + }, + SwapAndAction { + user_swap: Swap, + min_asset: Asset, + timeout_timestamp: u64, + post_swap_action: Action, + affiliates: Vec, + }, + Action { + timeout_timestamp: u64, + action: Action, + exact_out: bool, + min_asset: Option, + }, + ActionWithRecover { + timeout_timestamp: u64, + action: Action, + exact_out: bool, + min_asset: Option, + recovery_addr: Addr, + }, +} + +///////////// +/// QUERY /// +///////////// + +// The QueryMsg enum defines the queries the entry point contract provides. +#[cw_serde] +#[derive(QueryResponses)] +pub enum QueryMsg { + // SwapVenueAdapterContract returns the address of the swap + // adapter contract for the given swap venue name. + #[returns(cosmwasm_std::Addr)] + SwapVenueAdapterContract { name: String }, + + // IbcTransferAdapterContract returns the address of the IBC + // transfer adapter contract. + #[returns(cosmwasm_std::Addr)] + IbcTransferAdapterContract {}, +} + +//////////////////// +/// COMMON TYPES /// +//////////////////// + +// The Action enum is used to specify what action to take after a swap. +#[cw_serde] +pub enum Action { + Transfer { + to_address: String, + }, + IbcTransfer { + ibc_info: IbcInfo, + fee_swap: Option, + }, + ContractCall { + contract_address: String, + msg: Binary, + }, + HplTransfer { + dest_domain: u32, + recipient: HexBinary, + hook: Option, + metadata: Option, + warp_address: String, + }, +} + +// The Affiliate struct is used to specify an affiliate address and BPS fee taken +// from the min_asset to send to that address. +#[cw_serde] +pub struct Affiliate { + pub basis_points_fee: Uint128, + pub address: String, +} diff --git a/packages/secret-skip/src/error.rs b/packages/secret-skip/src/error.rs new file mode 100644 index 00000000..1d327f04 --- /dev/null +++ b/packages/secret-skip/src/error.rs @@ -0,0 +1,54 @@ +use cosmwasm_std::{OverflowError, StdError}; +use thiserror::Error; + +#[derive(Error, Debug, PartialEq)] +pub enum SkipError { + /////////////// + /// GENERAL /// + /////////////// + + #[error(transparent)] + Std(#[from] StdError), + + #[error("Unauthorized")] + Unauthorized, + + #[error(transparent)] + Payment(#[from] cw_utils::PaymentError), + + #[error(transparent)] + Overflow(#[from] OverflowError), + + //////////// + /// SWAP /// + //////////// + + #[error("Swap Operations Empty")] + SwapOperationsEmpty, + + #[error("First Swap Operations' Denom In Differs From Swap Asset In Denom")] + SwapOperationsAssetInDenomMismatch, + + #[error("Last Swap Operations' Denom Out Differs From Swap Asset Out Denom")] + SwapOperationsAssetOutDenomMismatch, + + #[error("Routes Empty")] + RoutesEmpty, + + /////////// + /// IBC /// + /////////// + + #[error("Ibc Fees Are Not A Single Coin, Either Multiple Denoms Or No Coin Specified")] + IbcFeesNotOneCoin, + + ///////////// + /// ASSET /// + ///////////// + + #[error("Native Coin Sent To Contract Does Not Match Asset")] + InvalidNativeCoin, + + #[error("Cw20 Coin Sent To Contract Does Not Match Asset")] + InvalidCw20Coin, +} diff --git a/packages/secret-skip/src/hyperlane.rs b/packages/secret-skip/src/hyperlane.rs new file mode 100644 index 00000000..65506a5b --- /dev/null +++ b/packages/secret-skip/src/hyperlane.rs @@ -0,0 +1,37 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::HexBinary; + +/////////////// +/// MIGRATE /// +/////////////// + +// The MigrateMsg struct defines the migration parameters used. +#[cw_serde] +pub struct MigrateMsg { + pub entry_point_contract_address: String, +} +/////////////////// +/// INSTANTIATE /// +/////////////////// + +// The InstantiateMsg struct defines the initialization parameters for the IBC Transfer Adapter contracts. +#[cw_serde] +pub struct InstantiateMsg { + pub entry_point_contract_address: String, +} + +/////////////// +/// EXECUTE /// +/////////////// + +// The ExecuteMsg enum defines the execution message that the IBC Transfer Adapter contracts can handle. +#[cw_serde] +pub enum ExecuteMsg { + HplTransfer { + dest_domain: u32, + recipient: HexBinary, + hook: Option, + metadata: Option, + warp_address: String, + }, +} diff --git a/packages/secret-skip/src/ibc.rs b/packages/secret-skip/src/ibc.rs new file mode 100644 index 00000000..94eb2582 --- /dev/null +++ b/packages/secret-skip/src/ibc.rs @@ -0,0 +1,288 @@ +use crate::{error::SkipError, proto_coin::ProtoCoin}; + +use std::convert::From; + +use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::{Coin, StdError}; +// use neutron_proto::neutron::feerefunder::Fee as NeutronFee; + +/////////////// +/// MIGRATE /// +/////////////// + +// The MigrateMsg struct defines the migration parameters used. +#[cw_serde] +pub struct MigrateMsg { + pub entry_point_contract_address: String, +} +/////////////////// +/// INSTANTIATE /// +/////////////////// + +// The InstantiateMsg struct defines the initialization parameters for the IBC Transfer Adapter contracts. +#[cw_serde] +pub struct InstantiateMsg { + pub entry_point_contract_address: String, +} + +/////////////// +/// EXECUTE /// +/////////////// + +// The ExecuteMsg enum defines the execution message that the IBC Transfer Adapter contracts can handle. +#[cw_serde] +pub enum ExecuteMsg { + IbcTransfer { + info: IbcInfo, + coin: Coin, + timeout_timestamp: u64, + }, +} + +///////////// +/// QUERY /// +///////////// + +// The QueryMsg enum defines the queries the IBC Transfer Adapter Contract provides. +#[cw_serde] +#[derive(QueryResponses)] +pub enum QueryMsg { + #[returns(String)] + InProgressRecoverAddress { + channel_id: String, + sequence_id: u64, + }, +} + +//////////////////// +/// COMMON TYPES /// +//////////////////// + +// The IbcFee struct defines the fees for an IBC transfer standardized across all IBC Transfer Adapter contracts. +#[cw_serde] +#[derive(Default)] +pub struct IbcFee { + pub recv_fee: Vec, + pub ack_fee: Vec, + pub timeout_fee: Vec, +} + +/* +// Converts an IbcFee struct to a neutron_proto::neutron::feerefunder Fee +impl From for NeutronFee { + fn from(ibc_fee: IbcFee) -> Self { + NeutronFee { + recv_fee: ibc_fee + .recv_fee + .iter() + .map(|coin| ProtoCoin(coin.clone()).into()) + .collect(), + ack_fee: ibc_fee + .ack_fee + .iter() + .map(|coin| ProtoCoin(coin.clone()).into()) + .collect(), + timeout_fee: ibc_fee + .timeout_fee + .iter() + .map(|coin| ProtoCoin(coin.clone()).into()) + .collect(), + } + } +} +*/ + +/* +// Converts an IbcFee struct to a cosmwasm_std::Coins struct +// Must be TryFrom since adding the ibc_fees can overflow. +impl TryFrom for Coins { + type Error = StdError; + + fn try_from(ibc_fee: IbcFee) -> Result { + let mut ibc_fees = Coins::default(); + + [ibc_fee.recv_fee, ibc_fee.ack_fee, ibc_fee.timeout_fee] + .into_iter() + .flatten() + .try_for_each(|coin| ibc_fees.add(coin))?; + + Ok(ibc_fees) + } +} +*/ + +/* +impl IbcFee { + // one_coin aims to mimic the behavior of cw_utls::one_coin, + // returing the single coin in the IbcFee struct if it exists, + // erroring if 0 or more than 1 coins exist. + // + // one_coin is used because the entry_point contract only supports + // the handling of a single denomination for IBC fees. + pub fn one_coin(&self) -> Result { + let ibc_fees_map: Coins = self.clone().try_into()?; + + if ibc_fees_map.len() != 1 { + return Err(SkipError::IbcFeesNotOneCoin); + } + + Ok(ibc_fees_map.to_vec().first().unwrap().clone()) + } +} +*/ + +// The IbcInfo struct defines the information for an IBC transfer standardized across all IBC Transfer Adapter contracts. +#[cw_serde] +pub struct IbcInfo { + pub source_channel: String, + pub receiver: String, + pub fee: Option, + pub memo: String, + pub recover_address: String, +} + +// The IbcTransfer struct defines the parameters for an IBC transfer standardized across all IBC Transfer Adapter contracts. +#[cw_serde] +pub struct IbcTransfer { + pub info: IbcInfo, + pub coin: Coin, + pub timeout_timestamp: u64, +} + +// Converts an IbcTransfer struct to an ExecuteMsg::IbcTransfer enum +impl From for ExecuteMsg { + fn from(ibc_transfer: IbcTransfer) -> Self { + ExecuteMsg::IbcTransfer { + info: ibc_transfer.info, + coin: ibc_transfer.coin, + timeout_timestamp: ibc_transfer.timeout_timestamp, + } + } +} + +// AckID is a type alias for a tuple of a str and a u64 +// which is used as a lookup key to store the in progress +// ibc transfer upon receiving a successful sub msg reply. +pub type AckID<'a> = (&'a str, u64); + +// The IbcLifecycleComplete enum defines the possible sudo messages that the +// ibc transfer adapter contract on ibc-hook enabled chains can expect to received +// from the ibc-hooks module. +#[cw_serde] +pub enum IbcLifecycleComplete { + IbcAck { + /// The source channel of the IBC packet + channel: String, + /// The sequence number that the packet was sent with + sequence: u64, + /// String encoded version of the ack as seen by OnAcknowledgementPacket(..) + ack: String, + /// Whether an ack is a success of failure according to the transfer spec + success: bool, + }, + IbcTimeout { + /// The source channel of the IBC packet + channel: String, + /// The sequence number that the packet was sent with + sequence: u64, + }, +} + +#[cfg(test)] +mod tests { + use super::*; + use cosmwasm_std::Uint128; + + #[test] + fn test_from_ibc_fee_for_neutron_proto_fee() { + let ibc_fee = IbcFee { + recv_fee: vec![Coin::new(100, "atom")], + ack_fee: vec![Coin::new(100, "osmo")], + timeout_fee: vec![Coin::new(100, "ntrn")], + }; + + let neutron_fee: NeutronFee = ibc_fee.into(); + + assert_eq!(neutron_fee.recv_fee.len(), 1); + assert_eq!(neutron_fee.ack_fee.len(), 1); + assert_eq!(neutron_fee.timeout_fee.len(), 1); + + assert_eq!(neutron_fee.recv_fee[0].denom, "atom"); + assert_eq!(neutron_fee.recv_fee[0].amount, "100"); + + assert_eq!(neutron_fee.ack_fee[0].denom, "osmo"); + assert_eq!(neutron_fee.ack_fee[0].amount, "100"); + + assert_eq!(neutron_fee.timeout_fee[0].denom, "ntrn"); + assert_eq!(neutron_fee.timeout_fee[0].amount, "100"); + } + + /* + #[test] + fn test_try_from_ibc_fee_for_coins() { + // TEST CASE 1: Same Denom For All Fees + let ibc_fee = IbcFee { + recv_fee: vec![Coin::new(100, "atom")], + ack_fee: vec![Coin::new(100, "atom")], + timeout_fee: vec![Coin::new(100, "atom")], + }; + + let coins: Coins = ibc_fee.try_into().unwrap(); + + assert_eq!(coins.len(), 1); + assert_eq!(coins.amount_of("atom"), Uint128::from(300u128)); + + // TEST CASE 2: Different Denom For Some Fees + let ibc_fee = IbcFee { + recv_fee: vec![Coin::new(100, "atom")], + ack_fee: vec![Coin::new(100, "osmo")], + timeout_fee: vec![Coin::new(100, "atom")], + }; + + let coins: Coins = ibc_fee.try_into().unwrap(); + + assert_eq!(coins.len(), 2); + assert_eq!(coins.amount_of("atom"), Uint128::from(200u128)); + assert_eq!(coins.amount_of("osmo"), Uint128::from(100u128)); + } + */ + + #[test] + fn test_one_coin() { + // TEST CASE 1: No Coins + let ibc_fee = IbcFee { + recv_fee: vec![], + ack_fee: vec![], + timeout_fee: vec![], + }; + + let result = ibc_fee.one_coin(); + + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), SkipError::IbcFeesNotOneCoin); + + // TEST CASE 2: One Coin + let ibc_fee = IbcFee { + recv_fee: vec![Coin::new(100, "atom")], + ack_fee: vec![], + timeout_fee: vec![], + }; + + let result = ibc_fee.one_coin(); + + assert!(result.is_ok()); + assert_eq!(result.unwrap(), Coin::new(100, "atom")); + + // TEST CASE 3: More Than One Coin + let ibc_fee = IbcFee { + recv_fee: vec![Coin::new(100, "atom")], + ack_fee: vec![Coin::new(100, "osmo")], + timeout_fee: vec![], + }; + + let result = ibc_fee.one_coin(); + + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), SkipError::IbcFeesNotOneCoin); + } +} diff --git a/packages/secret-skip/src/lib.rs b/packages/secret-skip/src/lib.rs new file mode 100644 index 00000000..5f1d7195 --- /dev/null +++ b/packages/secret-skip/src/lib.rs @@ -0,0 +1,8 @@ +pub mod asset; +pub mod entry_point; +pub mod error; +pub mod hyperlane; +pub mod ibc; +pub mod proto_coin; +pub mod sudo; +pub mod swap; diff --git a/packages/secret-skip/src/proto_coin.rs b/packages/secret-skip/src/proto_coin.rs new file mode 100644 index 00000000..717d900c --- /dev/null +++ b/packages/secret-skip/src/proto_coin.rs @@ -0,0 +1,92 @@ +use cosmos_sdk_proto::cosmos::base::v1beta1::Coin as CosmosSdkCoin; +use cosmwasm_schema::cw_serde; +use ibc_proto::cosmos::base::v1beta1::Coin as IbcCoin; +// use osmosis_std::types::cosmos::base::v1beta1::Coin as OsmosisStdCoin; + +// Skip wrapper coin type that is used to wrap cosmwasm_std::Coin +// and be able to implement type conversions on the wrapped type. +#[cw_serde] +pub struct ProtoCoin(pub cosmwasm_std::Coin); + +// Converts a skip coin to a cosmos_sdk_proto coin +impl From for CosmosSdkCoin { + fn from(coin: ProtoCoin) -> Self { + // Convert the skip coin to a cosmos_sdk_proto coin and return it + CosmosSdkCoin { + denom: coin.0.denom.clone(), + amount: coin.0.amount.to_string(), + } + } +} + +// Converts a skip coin to an ibc_proto coin +impl From for IbcCoin { + fn from(coin: ProtoCoin) -> Self { + // Convert the skip coin to an ibc_proto coin and return it + IbcCoin { + denom: coin.0.denom, + amount: coin.0.amount.to_string(), + } + } +} + +/* +// Converts a skip coin to an osmosis_std coin +impl From for OsmosisStdCoin { + fn from(coin: ProtoCoin) -> Self { + // Convert the skip coin to an osmosis coin and return it + OsmosisStdCoin { + denom: coin.0.denom, + amount: coin.0.amount.to_string(), + } + } +} +*/ + +#[cfg(test)] +mod tests { + use super::*; + + use cosmwasm_std::Uint128; + + #[test] + fn test_from_skip_proto_coin_to_cosmos_sdk_proto_coin() { + let skip_coin = ProtoCoin(cosmwasm_std::Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + let cosmos_sdk_proto_coin: CosmosSdkCoin = skip_coin.into(); + + assert_eq!(cosmos_sdk_proto_coin.denom, "uatom"); + assert_eq!(cosmos_sdk_proto_coin.amount, "100"); + } + + #[test] + fn test_from_skip_proto_coin_to_ibc_proto_coin() { + let skip_coin = ProtoCoin(cosmwasm_std::Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + let ibc_proto_coin: IbcCoin = skip_coin.into(); + + assert_eq!(ibc_proto_coin.denom, "uatom"); + assert_eq!(ibc_proto_coin.amount, "100"); + } + + /* + #[test] + fn test_from_skip_proto_coin_to_osmosis_std_coin() { + let skip_coin = ProtoCoin(cosmwasm_std::Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }); + + let osmosis_std_coin: OsmosisStdCoin = skip_coin.into(); + + assert_eq!(osmosis_std_coin.denom, "uatom"); + assert_eq!(osmosis_std_coin.amount, "100"); + } + */ +} diff --git a/packages/secret-skip/src/sudo.rs b/packages/secret-skip/src/sudo.rs new file mode 100644 index 00000000..6d10744d --- /dev/null +++ b/packages/secret-skip/src/sudo.rs @@ -0,0 +1,27 @@ +use crate::ibc::IbcLifecycleComplete; + +use cosmwasm_schema::cw_serde; + +// SudoType used to give info in response attributes when the sudo function is called +pub enum SudoType { + Response, + Error, + Timeout, +} + +// Implement the From trait for SudoType to convert it to a string to be used in response attributes +impl From for String { + fn from(sudo_type: SudoType) -> Self { + match sudo_type { + SudoType::Response => "sudo_ack_success".into(), + SudoType::Error => "sudo_ack_error_and_bank_send".into(), + SudoType::Timeout => "sudo_timeout_and_bank_send".into(), + } + } +} + +// Message type for Osmosis `sudo` entry_point to interact with callbacks from the ibc hooks module +#[cw_serde] +pub enum OsmosisSudoMsg { + IbcLifecycleComplete(IbcLifecycleComplete), +} diff --git a/packages/secret-skip/src/swap.rs b/packages/secret-skip/src/swap.rs new file mode 100644 index 00000000..f7ebf17a --- /dev/null +++ b/packages/secret-skip/src/swap.rs @@ -0,0 +1,422 @@ +use crate::{asset::Asset, error::SkipError}; + +use std::{convert::TryFrom, num::ParseIntError}; + +// use astroport::{asset::AssetInfo, router::SwapOperation as AstroportSwapOperation}; +use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::{ + Addr, BankMsg, Binary, ContractInfo, CosmosMsg, Decimal, DepsMut, Env, MessageInfo, Response, + Uint128, +}; +use cw20::Cw20Contract; +use cw20::Cw20ReceiveMsg; +/* +use osmosis_std::types::osmosis::poolmanager::v1beta1::{ + SwapAmountInRoute as OsmosisSwapAmountInRoute, SwapAmountOutRoute as OsmosisSwapAmountOutRoute, +}; +*/ + +/////////////// +/// MIGRATE /// +/////////////// + +// The MigrateMsg struct defines the migration parameters used. +#[cw_serde] +pub struct MigrateMsg { + pub entry_point_contract_address: String, +} + +/////////////////// +/// INSTANTIATE /// +/////////////////// + +// The InstantiateMsg struct defines the initialization parameters for the +// Osmosis Poolmanager and Astroport swap adapter contracts. +#[cw_serde] +pub struct InstantiateMsg { + pub entry_point_contract_address: String, +} + +///////////////////////// +/// EXECUTE /// +///////////////////////// + +// The ExecuteMsg enum defines the execution message that the swap adapter contracts can handle. +// Only the Swap message is callable by external users. +#[cw_serde] +pub enum ExecuteMsg { + Receive(Cw20ReceiveMsg), + Swap { operations: Vec }, + TransferFundsBack { swapper: Addr, return_denom: String }, +} + +#[cw_serde] +pub enum Cw20HookMsg { + Swap { operations: Vec }, +} + +///////////////////////// +/// QUERY /// +///////////////////////// + +// The QueryMsg enum defines the queries the swap adapter contracts provide. +#[cw_serde] +#[derive(QueryResponses)] +pub enum QueryMsg { + // SimulateSwapExactAssetOut returns the asset in necessary to receive the specified asset out + #[returns(Asset)] + SimulateSwapExactAssetOut { + asset_out: Asset, + swap_operations: Vec, + }, + // SimulateSwapExactAssetIn returns the asset out received from the specified asset in + #[returns(Asset)] + SimulateSwapExactAssetIn { + asset_in: Asset, + swap_operations: Vec, + }, + // SimulateSwapExactAssetOutWithSpotPrice returns the asset in necessary to receive the specified asset out with metadata + #[returns(SimulateSwapExactAssetOutResponse)] + SimulateSwapExactAssetOutWithMetadata { + asset_out: Asset, + swap_operations: Vec, + include_spot_price: bool, + }, + // SimulateSwapExactAssetInWithSpotPrice returns the asset out received from the specified asset in with metadata + #[returns(SimulateSwapExactAssetInResponse)] + SimulateSwapExactAssetInWithMetadata { + asset_in: Asset, + swap_operations: Vec, + include_spot_price: bool, + }, + // SimulateSmartSwapExactAssetIn returns the asset out received from the specified asset in over multiple routes + #[returns(Asset)] + SimulateSmartSwapExactAssetIn { asset_in: Asset, routes: Vec }, + // SimulateSmartSwapExactAssetInWithMetadata returns the asset out received from the specified asset in over multiple routes with metadata + #[returns(SimulateSmartSwapExactAssetInResponse)] + SimulateSmartSwapExactAssetInWithMetadata { + asset_in: Asset, + routes: Vec, + include_spot_price: bool, + }, +} + +// The SimulateSwapExactAssetInResponse struct defines the response for the +// SimulateSwapExactAssetIn query. +#[cw_serde] +pub struct SimulateSwapExactAssetInResponse { + pub asset_out: Asset, + pub spot_price: Option, +} + +// The SimulateSwapExactAssetOutResponse struct defines the response for the +// SimulateSwapExactAssetOut query. +#[cw_serde] +pub struct SimulateSwapExactAssetOutResponse { + pub asset_in: Asset, + pub spot_price: Option, +} + +// The SimulateSmartSwapExactAssetInResponse struct defines the response for the +// SimulateSmartSwapExactAssetIn query. +#[cw_serde] +pub struct SimulateSmartSwapExactAssetInResponse { + pub asset_out: Asset, + pub spot_price: Option, +} + +//////////////////// +/// COMMON TYPES /// +//////////////////// + +// Swap venue object that contains the name of the swap venue and adapter contract address. +#[cw_serde] +pub struct SwapVenue { + pub name: String, + pub adapter_contract: ContractInfo, +} + +#[cw_serde] +pub struct Route { + pub offer_asset: Asset, + pub operations: Vec, +} + +impl Route { + pub fn ask_denom(&self) -> Result { + match self.operations.last() { + Some(op) => Ok(op.denom_out.clone()), + None => Err(SkipError::SwapOperationsEmpty), + } + } +} + +pub fn get_ask_denom_for_routes(routes: &[Route]) -> Result { + match routes.last() { + Some(route) => route.ask_denom(), + None => Err(SkipError::RoutesEmpty), + } +} + +// Standard swap operation type that contains the pool, denom in, and denom out +// for the swap operation. The type is converted into the respective swap venues +// expected format in each adapter contract. +#[cw_serde] +pub struct SwapOperation { + pub pool: String, + pub denom_in: String, + pub denom_out: String, + pub interface: Option, +} + +// Converts a vector of skip swap operation to vector of osmosis swap +// amount in/out routes, returning an error if any of the swap operations +// fail to convert. This only happens if the given String for pool in the +// swap operation is not a valid u64, which is the pool_id type for Osmosis. +pub fn convert_swap_operations( + swap_operations: Vec, +) -> Result, ParseIntError> +where + T: TryFrom, +{ + swap_operations.into_iter().map(T::try_from).collect() +} + +// Swap object to get the exact amount of a given asset with the given vector of swap operations +#[cw_serde] +pub struct SwapExactAssetOut { + pub swap_venue_name: String, + pub operations: Vec, + pub refund_address: Option, +} + +// Swap object that swaps the remaining asset recevied +// from the contract call minus fee swap (if present) +#[cw_serde] +pub struct SwapExactAssetIn { + pub swap_venue_name: String, + pub operations: Vec, +} + +// Swap object that swaps the remaining asset recevied +// over multiple routes from the contract call minus fee swap (if present) +#[cw_serde] +pub struct SmartSwapExactAssetIn { + pub swap_venue_name: String, + pub routes: Vec, +} + +impl SmartSwapExactAssetIn { + pub fn amount(&self) -> Uint128 { + self.routes + .iter() + .map(|route| route.offer_asset.amount()) + .sum() + } + + pub fn ask_denom(&self) -> Result { + match self.routes.last() { + Some(route) => route.ask_denom(), + None => Err(SkipError::RoutesEmpty), + } + } + + pub fn largest_route_index(&self) -> Result { + match self + .routes + .iter() + .enumerate() + .max_by_key(|(_, route)| route.offer_asset.amount()) + .map(|(index, _)| index) + { + Some(idx) => Ok(idx), + None => Err(SkipError::RoutesEmpty), + } + } +} + +// Converts a SwapExactAssetOut used in the entry point contract +// to a swap adapter Swap execute message +impl From for ExecuteMsg { + fn from(swap: SwapExactAssetOut) -> Self { + ExecuteMsg::Swap { + operations: swap.operations, + } + } +} + +// Converts a SwapExactAssetIn used in the entry point contract +// to a swap adapter Swap execute message +impl From for ExecuteMsg { + fn from(swap: SwapExactAssetIn) -> Self { + ExecuteMsg::Swap { + operations: swap.operations, + } + } +} + +#[cw_serde] +pub enum Swap { + SwapExactAssetIn(SwapExactAssetIn), + SwapExactAssetOut(SwapExactAssetOut), + SmartSwapExactAssetIn(SmartSwapExactAssetIn), +} + +//////////////////////// +/// COMMON FUNCTIONS /// +//////////////////////// + +// Query the contract's balance and transfer the funds back to the swapper +/* +pub fn execute_transfer_funds_back( + deps: DepsMut, + env: Env, + info: MessageInfo, + swapper: Addr, + return_denom: String, +) -> Result { + // Ensure the caller is the contract itself + if info.sender != env.contract.address { + return Err(SkipError::Unauthorized); + } + // Create the transfer funds back message + let transfer_funds_back_msg: CosmosMsg = match deps.api.addr_validate(&return_denom) { + Ok(contract_addr) => Asset::new( + deps.api, + contract_addr.as_str(), + Cw20Contract(contract_addr.clone()).balance(&deps.querier, &env.contract.address)?, + ) + .transfer(swapper.as_str()), + Err(_) => CosmosMsg::Bank(BankMsg::Send { + to_address: swapper.to_string(), + amount: deps.querier.query_all_balances(env.contract.address)?, + }), + }; + + Ok(Response::new() + .add_message(transfer_funds_back_msg) + .add_attribute("action", "dispatch_transfer_funds_back_bank_send")) +} +*/ + +// Validates the swap operations +pub fn validate_swap_operations( + swap_operations: &[SwapOperation], + asset_in_denom: &str, + asset_out_denom: &str, +) -> Result<(), SkipError> { + // Verify the swap operations are not empty + let (Some(first_op), Some(last_op)) = (swap_operations.first(), swap_operations.last()) else { + return Err(SkipError::SwapOperationsEmpty); + }; + + // Verify the first swap operation denom in is the same as the asset in denom + if first_op.denom_in != asset_in_denom { + return Err(SkipError::SwapOperationsAssetInDenomMismatch); + } + + // Verify the last swap operation denom out is the same as the asset out denom + if last_op.denom_out != asset_out_denom { + return Err(SkipError::SwapOperationsAssetOutDenomMismatch); + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + use cosmwasm_std::testing::mock_dependencies; + + #[test] + fn test_validate_swap_operations() { + // TEST CASE 1: Valid Swap Operations + let swap_operations = vec![ + SwapOperation { + pool: "1".to_string(), + denom_in: "uatom".to_string(), + denom_out: "uosmo".to_string(), + interface: None, + }, + SwapOperation { + pool: "2".to_string(), + denom_in: "uosmo".to_string(), + denom_out: "untrn".to_string(), + interface: None, + }, + ]; + + let asset_in_denom = "uatom"; + let asset_out_denom = "untrn"; + + let result = validate_swap_operations(&swap_operations, asset_in_denom, asset_out_denom); + + assert!(result.is_ok()); + + // TEST CASE 2: Empty Swap Operations + let swap_operations: Vec = vec![]; + + let asset_in_denom = "uatom"; + let asset_out_denom = "untrn"; + + let result = validate_swap_operations(&swap_operations, asset_in_denom, asset_out_denom); + + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), SkipError::SwapOperationsEmpty); + + // TEST CASE 3: First Swap Operation Denom In Mismatch + let swap_operations = vec![ + SwapOperation { + pool: "1".to_string(), + denom_in: "uosmo".to_string(), + denom_out: "uatom".to_string(), + interface: None, + }, + SwapOperation { + pool: "2".to_string(), + denom_in: "uatom".to_string(), + denom_out: "untrn".to_string(), + interface: None, + }, + ]; + + let asset_in_denom = "uatom"; + let asset_out_denom = "untrn"; + + let result = validate_swap_operations(&swap_operations, asset_in_denom, asset_out_denom); + + assert!(result.is_err()); + assert_eq!( + result.unwrap_err(), + SkipError::SwapOperationsAssetInDenomMismatch + ); + + // TEST CASE 4: Last Swap Operation Denom Out Mismatch + let swap_operations = vec![ + SwapOperation { + pool: "1".to_string(), + denom_in: "uatom".to_string(), + denom_out: "uosmo".to_string(), + interface: None, + }, + SwapOperation { + pool: "2".to_string(), + denom_in: "uosmo".to_string(), + denom_out: "uatom".to_string(), + interface: None, + }, + ]; + + let asset_in_denom = "uatom"; + let asset_out_denom = "untrn"; + + let result = validate_swap_operations(&swap_operations, asset_in_denom, asset_out_denom); + + assert!(result.is_err()); + assert_eq!( + result.unwrap_err(), + SkipError::SwapOperationsAssetOutDenomMismatch + ); + } +} From 69c8a6c7f891a002e0f76dbb3ddfc9ac1cabcd42 Mon Sep 17 00:00:00 2001 From: DrPresident Date: Wed, 11 Dec 2024 16:10:33 -0600 Subject: [PATCH 09/29] secret-entry-point close but needs ibc compatibility --- contracts/secret-entry-point/Cargo.toml | 4 +- contracts/secret-entry-point/src/asset.rs | 610 ------------------ contracts/secret-entry-point/src/contract.rs | 96 ++- contracts/secret-entry-point/src/error.rs | 10 +- contracts/secret-entry-point/src/execute.rs | 283 ++++++-- contracts/secret-entry-point/src/hyperlane.rs | 37 ++ contracts/secret-entry-point/src/lib.rs | 2 +- contracts/secret-entry-point/src/msg.rs | 22 +- contracts/secret-entry-point/src/query.rs | 4 +- contracts/secret-entry-point/src/reply.rs | 32 +- contracts/secret-entry-point/src/state.rs | 15 +- 11 files changed, 398 insertions(+), 717 deletions(-) delete mode 100644 contracts/secret-entry-point/src/asset.rs create mode 100644 contracts/secret-entry-point/src/hyperlane.rs diff --git a/contracts/secret-entry-point/Cargo.toml b/contracts/secret-entry-point/Cargo.toml index 2814ab18..f7f29c70 100644 --- a/contracts/secret-entry-point/Cargo.toml +++ b/contracts/secret-entry-point/Cargo.toml @@ -21,12 +21,14 @@ library = [] [dependencies] cosmwasm-schema = { workspace = true } +skip-go-swap-adapter-shade-protocol = { path = "../adapters/swap/shade-protocol" } +secret-skip = { workspace = true } #cosmwasm-std = { workspace = true } #cw2 = { workspace = true } cw20 = { workspace = true } #cw-storage-plus = { workspace = true } cw-utils = { workspace = true } -skip = { workspace = true } +#skip = { workspace = true } thiserror = { workspace = true } serde = "1.0.114" schemars = "0.8.1" diff --git a/contracts/secret-entry-point/src/asset.rs b/contracts/secret-entry-point/src/asset.rs deleted file mode 100644 index 34e0ef39..00000000 --- a/contracts/secret-entry-point/src/asset.rs +++ /dev/null @@ -1,610 +0,0 @@ -use crate::error::SkipError; -use cosmwasm_schema::cw_serde; -use cosmwasm_std::{ - to_binary, Api, BankMsg, Binary, Coin, ContractInfo, CosmosMsg, DepsMut, Env, MessageInfo, - Uint128, WasmMsg, -}; -use cw20::{Cw20Coin, Cw20CoinVerified, Cw20Contract, Cw20ExecuteMsg}; -use cw_utils::nonpayable; - -#[cw_serde] -pub enum Asset { - Native(Coin), - Cw20(Cw20Coin), -} - -impl From for Asset { - fn from(coin: Coin) -> Self { - Asset::Native(coin) - } -} - -impl From for Asset { - fn from(cw20_coin: Cw20Coin) -> Self { - Asset::Cw20(cw20_coin) - } -} - -impl From for Asset { - fn from(cw20_coin_verified: Cw20CoinVerified) -> Self { - Asset::Cw20(Cw20Coin { - address: cw20_coin_verified.address.to_string(), - amount: cw20_coin_verified.amount, - }) - } -} - -impl Asset { - pub fn default_native() -> Self { - Asset::Native(Coin::default()) - } - - pub fn new(api: &dyn Api, denom: &str, amount: Uint128) -> Self { - match api.addr_validate(denom) { - Ok(addr) => Asset::Cw20(Cw20Coin { - address: addr.to_string(), - amount: amount.u128().into(), - }), - Err(_) => Asset::Native(Coin { - denom: denom.to_string(), - amount, - }), - } - } - - pub fn denom(&self) -> &str { - match self { - Asset::Native(coin) => &coin.denom, - Asset::Cw20(coin) => &coin.address, - } - } - - pub fn amount(&self) -> Uint128 { - match self { - Asset::Native(coin) => coin.amount, - Asset::Cw20(coin) => coin.amount.u128().into(), - } - } - - pub fn add(&mut self, amount: Uint128) -> Result { - match self { - Asset::Native(coin) => { - coin.amount = coin.amount.checked_add(amount)?; - Ok(coin.amount) - } - Asset::Cw20(coin) => { - coin.amount = coin.amount.checked_add(amount.u128().into())?; - Ok(coin.amount.u128().into()) - } - } - } - - pub fn sub(&mut self, amount: Uint128) -> Result { - match self { - Asset::Native(coin) => { - coin.amount = coin.amount.checked_sub(amount)?; - Ok(coin.amount) - } - Asset::Cw20(coin) => { - coin.amount = coin.amount.checked_sub(amount.u128().into())?; - Ok(coin.amount.u128().into()) - } - } - } - - /* - pub fn transfer(self, to_address: &str) -> CosmosMsg { - match self { - Asset::Native(coin) => CosmosMsg::Bank(BankMsg::Send { - to_address: to_address.to_string(), - amount: vec![coin], - }), - Asset::Cw20(coin) => CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: coin.address.clone(), - msg: to_binary(&Cw20ExecuteMsg::Transfer { - recipient: to_address.to_string(), - amount: coin.amount, - }) - .unwrap(), - funds: vec![], - }), - } - } - */ - - /* - pub fn into_wasm_msg(self, contract_addr: String, msg: Binary) -> Result { - match self { - Asset::Native(coin) => Ok(WasmMsg::Execute { - contract_addr, - msg, - funds: vec![coin], - }), - Asset::Cw20(coin) => Ok(WasmMsg::Execute { - contract_addr: coin.address, - msg: to_binary(&Cw20ExecuteMsg::Send { - contract: contract_addr, - amount: coin.amount, - msg, - })?, - funds: vec![], - }), - } - } - */ - - /* - pub fn validate(&self, deps: &DepsMut, env: &Env, info: &MessageInfo) -> Result<(), SkipError> { - match self { - Asset::Native(coin) => { - unimplemented!(); - } - Asset::Cw20(coin) => { - // Validate that the message is nonpayable - nonpayable(info)?; - - let verified_cw20_coin_addr = deps.api.addr_validate(&coin.address)?; - - let cw20_contract = Cw20Contract(verified_cw20_coin_addr); - - let balance = cw20_contract.balance(&deps.querier, &env.contract.address)?; - - if coin.amount <= balance { - Ok(()) - } else { - Err(SkipError::InvalidCw20Coin) - } - } - } - } - */ -} - -/* -pub fn get_current_asset_available( - deps: &DepsMut, - env: &Env, - denom: &str, -) -> Result { - match deps.api.addr_validate(denom) { - Ok(addr) => { - let cw20_contract = Cw20Contract(addr.clone()); - - let amount = cw20_contract.balance(&deps.querier, &env.contract.address)?; - - Ok(Asset::Cw20(Cw20Coin { - address: addr.to_string(), - amount, - })) - } - Err(_) => { - let coin = deps.querier.query_balance(&env.contract.address, denom)?; - - Ok(Asset::Native(coin)) - } - } -} -*/ - -#[cfg(test)] -mod tests { - use super::*; - use cosmwasm_std::{ - testing::{mock_dependencies_with_balances, mock_env, mock_info}, - Addr, ContractResult, QuerierResult, SystemResult, WasmQuery, - }; - use cw20::BalanceResponse; - use cw_utils::PaymentError; - - #[test] - fn test_default_native() { - let asset = Asset::default_native(); - - assert_eq!( - asset, - Asset::Native(Coin { - denom: "".to_string(), - amount: Uint128::zero(), - }) - ); - } - - #[test] - fn test_new() { - // TEST 1: Native asset - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); - - let asset = Asset::new(deps.as_mut().api, "ua", Uint128::new(100)); - - assert_eq!( - asset, - Asset::Native(Coin { - denom: "ua".to_string(), - amount: Uint128::new(100), - }) - ); - - // TEST 2: Cw20 asset - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); - - let asset = Asset::new(deps.as_mut().api, "asset", Uint128::new(100)); - - assert_eq!( - asset, - Asset::Cw20(Cw20Coin { - address: "asset".to_string(), - amount: Uint128::new(100), - }) - ); - } - - #[test] - fn test_asset_native() { - let asset = Asset::Native(Coin { - denom: "uatom".to_string(), - amount: Uint128::new(100), - }); - - assert_eq!(asset.denom(), "uatom"); - assert_eq!(asset.amount(), Uint128::new(100)); - } - - #[test] - fn test_asset_cw20() { - let asset = Asset::Cw20(Cw20Coin { - address: "asset".to_string(), - amount: Uint128::new(100), - }); - - assert_eq!(asset.denom(), "asset"); - assert_eq!(asset.amount(), Uint128::new(100)); - } - - #[test] - fn test_add() { - // TEST 1: Native asset - let mut asset = Asset::Native(Coin { - denom: "uatom".to_string(), - amount: Uint128::new(100), - }); - - asset.add(Uint128::new(20)).unwrap(); - - assert_eq!(asset.amount(), Uint128::new(120)); - - // TEST 2: Cw20 asset - let mut asset = Asset::Cw20(Cw20Coin { - address: "asset".to_string(), - amount: Uint128::new(100), - }); - - asset.add(Uint128::new(20)).unwrap(); - - assert_eq!(asset.amount(), Uint128::new(120)); - } - - #[test] - fn test_sub() { - // TEST 1: Native asset - let mut asset = Asset::Native(Coin { - denom: "uatom".to_string(), - amount: Uint128::new(100), - }); - - asset.sub(Uint128::new(20)).unwrap(); - - assert_eq!(asset.amount(), Uint128::new(80)); - - // TEST 2: Cw20 asset - let mut asset = Asset::Cw20(Cw20Coin { - address: "asset".to_string(), - amount: Uint128::new(100), - }); - - asset.sub(Uint128::new(20)).unwrap(); - - assert_eq!(asset.amount(), Uint128::new(80)); - } - - #[test] - fn test_asset_transfer_native() { - let asset = Asset::Native(Coin { - denom: "uatom".to_string(), - amount: Uint128::new(100), - }); - - let msg = asset.transfer("addr"); - - match msg { - CosmosMsg::Bank(BankMsg::Send { to_address, amount }) => { - assert_eq!(to_address, "addr"); - assert_eq!(amount.len(), 1); - assert_eq!(amount[0].denom, "uatom"); - assert_eq!(amount[0].amount, Uint128::new(100)); - } - _ => panic!("Unexpected message type"), - } - } - - #[test] - fn test_asset_transfer_cw20() { - let asset = Asset::Cw20(Cw20Coin { - address: "asset".to_string(), - amount: Uint128::new(100), - }); - - let msg = asset.transfer("addr"); - - match msg { - CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr, - msg, - funds, - }) => { - assert_eq!(contract_addr, "asset"); - assert_eq!( - msg, - to_binary(&Cw20ExecuteMsg::Transfer { - recipient: "addr".to_string(), - amount: Uint128::new(100), - }) - .unwrap() - ); - assert_eq!(funds.len(), 0); - } - _ => panic!("Unexpected message type"), - } - } - - #[test] - fn test_into_astroport_asset() { - // TEST 1: Native asset - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); - - let asset = Asset::Native(Coin { - denom: "uatom".to_string(), - amount: Uint128::new(100), - }); - - let astroport_asset = asset.into_astroport_asset(deps.as_mut().api).unwrap(); - - assert_eq!( - astroport_asset, - AstroportAsset { - info: AssetInfo::NativeToken { - denom: "uatom".to_string() - }, - amount: Uint128::new(100), - } - ); - - // TEST 2: Cw20 asset - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); - - let asset = Asset::Cw20(Cw20Coin { - address: "asset".to_string(), - amount: Uint128::new(100), - }); - - let astroport_asset = asset.into_astroport_asset(deps.as_mut().api).unwrap(); - - assert_eq!( - astroport_asset, - AstroportAsset { - info: AssetInfo::Token { - contract_addr: Addr::unchecked("asset") - }, - amount: Uint128::new(100), - } - ); - } - - #[test] - fn test_validate_native() { - // TEST 1: Valid asset - let asset = Asset::Native(Coin { - denom: "uatom".to_string(), - amount: Uint128::new(100), - }); - - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); - - let env = mock_env(); - - let info = mock_info( - "sender", - &[Coin { - denom: "uatom".to_string(), - amount: Uint128::new(100), - }], - ); - - assert!(asset.validate(&deps.as_mut(), &env, &info).is_ok()); - - // TEST 2: Invalid asset due to less amount of denom sent - let asset = Asset::Native(Coin { - denom: "uatom".to_string(), - amount: Uint128::new(100), - }); - - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); - - let env = mock_env(); - - let info = mock_info( - "sender", - &[Coin { - denom: "uatom".to_string(), - amount: Uint128::new(50), - }], - ); - - let res = asset.validate(&deps.as_mut(), &env, &info); - - assert_eq!(res, Err(SkipError::InvalidNativeCoin)); - - // TEST 3: Invalid asset due to more than one coin sent - let asset = Asset::Native(Coin { - denom: "uatom".to_string(), - amount: Uint128::new(100), - }); - - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); - - let env = mock_env(); - - let info = mock_info( - "sender", - &[ - Coin { - denom: "uatom".to_string(), - amount: Uint128::new(100), - }, - Coin { - denom: "uosmo".to_string(), - amount: Uint128::new(50), - }, - ], - ); - - let res = asset.validate(&deps.as_mut(), &env, &info); - - assert_eq!( - res, - Err(SkipError::Payment(PaymentError::MultipleDenoms {})) - ); - } - - #[test] - fn test_validate_cw20() { - // TEST 1: Valid asset - let asset = Asset::Cw20(Cw20Coin { - address: "asset".to_string(), - amount: Uint128::new(100), - }); - - // Create mock wasm handler to handle the cw20 balance query - let wasm_handler = |query: &WasmQuery| -> QuerierResult { - match query { - WasmQuery::Smart { .. } => SystemResult::Ok(ContractResult::Ok( - to_binary(&BalanceResponse { - balance: Uint128::from(100u128), - }) - .unwrap(), - )), - _ => panic!("Unsupported query: {:?}", query), - } - }; - - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); - - deps.querier.update_wasm(wasm_handler); - - let env = mock_env(); - - let info = mock_info("sender", &[]); - - assert!(asset.validate(&deps.as_mut(), &env, &info).is_ok()); - - // TEST 2: Invalid asset due to native coin sent in info - let asset = Asset::Cw20(Cw20Coin { - address: "asset".to_string(), - amount: Uint128::new(100), - }); - - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); - - let env = mock_env(); - - let info = mock_info( - "sender", - &[Coin { - denom: "uatom".to_string(), - amount: Uint128::new(100), - }], - ); - - let res = asset.validate(&deps.as_mut(), &env, &info); - - assert_eq!(res, Err(SkipError::Payment(PaymentError::NonPayable {}))); - - // TEST 3: Invalid asset due to invalid cw20 balance - let asset = Asset::Cw20(Cw20Coin { - address: "asset".to_string(), - amount: Uint128::new(100), - }); - - // Create mock wasm handler to handle the cw20 balance query - let wasm_handler = |query: &WasmQuery| -> QuerierResult { - match query { - WasmQuery::Smart { .. } => SystemResult::Ok(ContractResult::Ok( - to_binary(&BalanceResponse { - balance: Uint128::from(50u128), - }) - .unwrap(), - )), - _ => panic!("Unsupported query: {:?}", query), - } - }; - - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); - - deps.querier.update_wasm(wasm_handler); - - let env = mock_env(); - - let info = mock_info("sender", &[]); - - let res = asset.validate(&deps.as_mut(), &env, &info); - - assert_eq!(res, Err(SkipError::InvalidCw20Coin)); - } - - #[test] - fn test_get_current_asset_available() { - // TEST 1: Native asset - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[Coin::new(100, "ua")])]); - - let mut env = mock_env(); - env.contract.address = Addr::unchecked("entry_point"); - - let asset = get_current_asset_available(&deps.as_mut(), &env, "ua").unwrap(); - - assert_eq!( - asset, - Asset::Native(Coin { - denom: "ua".to_string(), - amount: Uint128::new(100), - }) - ); - - // TEST 2: Cw20 asset - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); - - let wasm_handler = |query: &WasmQuery| -> QuerierResult { - match query { - WasmQuery::Smart { .. } => SystemResult::Ok(ContractResult::Ok( - to_binary(&BalanceResponse { - balance: Uint128::from(100u128), - }) - .unwrap(), - )), - _ => panic!("Unsupported query: {:?}", query), - } - }; - - deps.querier.update_wasm(wasm_handler); - - let env = mock_env(); - - let asset = get_current_asset_available(&deps.as_mut(), &env, "asset").unwrap(); - - assert_eq!( - asset, - Asset::Cw20(Cw20Coin { - address: "asset".to_string(), - amount: Uint128::new(100), - }) - ); - } -} diff --git a/contracts/secret-entry-point/src/contract.rs b/contracts/secret-entry-point/src/contract.rs index 2f4bbec4..bc51c606 100644 --- a/contracts/secret-entry-point/src/contract.rs +++ b/contracts/secret-entry-point/src/contract.rs @@ -5,18 +5,20 @@ use crate::{ execute_swap_and_action, execute_swap_and_action_with_recover, execute_user_swap, receive_snip20, }, + msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}, query::{query_ibc_transfer_adapter_contract, query_swap_venue_adapter_contract}, reply::{reply_swap_and_action_with_recover, RECOVER_REPLY_ID}, state::{ BLOCKED_CONTRACT_ADDRESSES, HYPERLANE_TRANSFER_CONTRACT_ADDRESS, - IBC_TRANSFER_CONTRACT_ADDRESS, SWAP_VENUE_MAP, + IBC_TRANSFER_CONTRACT_ADDRESS, REGISTERED_TOKENS, SWAP_VENUE_MAP, VIEWING_KEY, }, }; use cosmwasm_std::{ - entry_point, to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, + entry_point, to_binary, Binary, ContractInfo, Deps, DepsMut, Env, MessageInfo, Reply, Response, + StdResult, }; +use secret_toolkit::snip20; // use cw2::set_contract_version; -use skip::entry_point::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; /////////////// /// MIGRATE /// @@ -50,13 +52,17 @@ pub fn instantiate( // Insert the entry point contract address into the blocked contract addresses map BLOCKED_CONTRACT_ADDRESSES.save(deps.storage, &env.contract.address, &())?; + VIEWING_KEY.save(deps.storage, &msg.viewing_key)?; // Iterate through the swap venues provided and create a map of venue names to swap adapter contract addresses for swap_venue in msg.swap_venues.iter() { // Validate the swap contract address - let checked_swap_contract_address = deps - .api - .addr_validate(&swap_venue.adapter_contract_address)?; + let checked_swap_contract = ContractInfo { + address: deps + .api + .addr_validate(&swap_venue.adapter_contract.address.to_string())?, + code_hash: swap_venue.adapter_contract.code_hash.clone(), + }; // Prevent duplicate swap venues by erroring if the venue name is already stored if SWAP_VENUE_MAP.has(deps.storage, &swap_venue.name) { @@ -64,52 +70,55 @@ pub fn instantiate( } // Store the swap venue name and contract address inside the swap venue map - SWAP_VENUE_MAP.save( - deps.storage, - &swap_venue.name, - &checked_swap_contract_address, - )?; + SWAP_VENUE_MAP.save(deps.storage, &swap_venue.name, &checked_swap_contract)?; // Insert the swap contract address into the blocked contract addresses map - BLOCKED_CONTRACT_ADDRESSES.save(deps.storage, &checked_swap_contract_address, &())?; + BLOCKED_CONTRACT_ADDRESSES.save(deps.storage, &checked_swap_contract.address, &())?; // Add the swap venue and contract address to the response response = response .add_attribute("action", "add_swap_venue") .add_attribute("name", &swap_venue.name) - .add_attribute("contract_address", &checked_swap_contract_address); + .add_attribute("contract_address", &checked_swap_contract.address); } // Validate ibc transfer adapter contract addresses - let checked_ibc_transfer_contract_address = - deps.api.addr_validate(&msg.ibc_transfer_contract_address)?; + let checked_ibc_transfer_contract = ContractInfo { + address: deps + .api + .addr_validate(&msg.ibc_transfer_contract.address.to_string())?, + code_hash: msg.ibc_transfer_contract.code_hash.clone(), + }; // Store the ibc transfer adapter contract address - IBC_TRANSFER_CONTRACT_ADDRESS.save(deps.storage, &checked_ibc_transfer_contract_address)?; + IBC_TRANSFER_CONTRACT_ADDRESS.save(deps.storage, &checked_ibc_transfer_contract)?; // Insert the ibc transfer adapter contract address into the blocked contract addresses map - BLOCKED_CONTRACT_ADDRESSES.save(deps.storage, &checked_ibc_transfer_contract_address, &())?; + BLOCKED_CONTRACT_ADDRESSES.save(deps.storage, &checked_ibc_transfer_contract.address, &())?; // Add the ibc transfer adapter contract address to the response response = response .add_attribute("action", "add_ibc_transfer_adapter") - .add_attribute("contract_address", &checked_ibc_transfer_contract_address); + .add_attribute("contract_address", &checked_ibc_transfer_contract.address); // If the hyperlane transfer contract address is provided, validate and store it - if let Some(hyperlane_transfer_contract_address) = msg.hyperlane_transfer_contract_address { + if let Some(hyperlane_transfer_contract) = msg.hyperlane_transfer_contract { // Validate hyperlane transfer adapter contract address - let checked_hyperlane_transfer_contract_address = deps - .api - .addr_validate(&hyperlane_transfer_contract_address)?; + let checked_hyperlane_transfer_contract = ContractInfo { + address: deps + .api + .addr_validate(&hyperlane_transfer_contract.address.to_string())?, + code_hash: hyperlane_transfer_contract.code_hash.clone(), + }; // Store the hyperlane transfer adapter contract address HYPERLANE_TRANSFER_CONTRACT_ADDRESS - .save(deps.storage, &checked_hyperlane_transfer_contract_address)?; + .save(deps.storage, &checked_hyperlane_transfer_contract)?; // Insert the hyperlane transfer adapter contract address into the blocked contract addresses map BLOCKED_CONTRACT_ADDRESSES.save( deps.storage, - &checked_hyperlane_transfer_contract_address, + &checked_hyperlane_transfer_contract.address, &(), )?; @@ -118,7 +127,7 @@ pub fn instantiate( .add_attribute("action", "add_hyperlane_transfer_adapter") .add_attribute( "contract_address", - &checked_hyperlane_transfer_contract_address, + &checked_hyperlane_transfer_contract.address, ); } @@ -137,6 +146,7 @@ pub fn execute( msg: ExecuteMsg, ) -> ContractResult { match msg { + ExecuteMsg::RegisterTokens { contracts } => register_tokens(deps, env, contracts), ExecuteMsg::Receive(msg) => receive_snip20(deps, env, info, msg), ExecuteMsg::SwapAndActionWithRecover { sent_asset, @@ -241,6 +251,42 @@ pub fn execute( } } +fn register_tokens( + deps: DepsMut, + env: Env, + contracts: Vec, +) -> ContractResult { + let mut response = Response::new(); + + let viewing_key = VIEWING_KEY.load(deps.storage)?; + + for contract in contracts.iter() { + // Add to storage for later use of code hash + REGISTERED_TOKENS.save(deps.storage, contract.address.clone(), contract)?; + // register receive, set viewing key, & add attribute + response = response + .add_attribute("register_token", contract.address.clone()) + .add_messages(vec![ + snip20::set_viewing_key_msg( + viewing_key.clone(), + None, + 255, + contract.code_hash.clone(), + contract.address.to_string(), + )?, + snip20::register_receive_msg( + env.contract.code_hash.clone(), + None, + 255, + contract.code_hash.clone(), + contract.address.to_string(), + )?, + ]); + } + + Ok(response) +} + #[cfg_attr(not(feature = "library"), entry_point)] pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { match msg.id { diff --git a/contracts/secret-entry-point/src/error.rs b/contracts/secret-entry-point/src/error.rs index a8810748..375cbc90 100644 --- a/contracts/secret-entry-point/src/error.rs +++ b/contracts/secret-entry-point/src/error.rs @@ -1,5 +1,5 @@ -use cosmwasm_std::{OverflowError, StdError}; -use skip::error::SkipError; +use cosmwasm_std::{Addr, OverflowError, StdError}; +use secret_skip::error::SkipError; use thiserror::Error; pub type ContractResult = core::result::Result; @@ -93,4 +93,10 @@ pub enum ContractError { #[error("Native Coin Not Supported")] NativeCoinNotSupported, + + #[error("Invalid Snip20 Sender")] + InvalidSnip20Sender, + + #[error("Snip20 Token Not Registered {0}")] + TokenNotRegistered(Addr), } diff --git a/contracts/secret-entry-point/src/execute.rs b/contracts/secret-entry-point/src/execute.rs index 29932172..0a4f113a 100644 --- a/contracts/secret-entry-point/src/execute.rs +++ b/contracts/secret-entry-point/src/execute.rs @@ -1,29 +1,33 @@ use std::vec; use crate::{ - asset::Asset, error::{ContractError, ContractResult}, + hyperlane::{ExecuteMsg as HplExecuteMsg, ExecuteMsg::HplTransfer}, msg::{Action, Affiliate, ExecuteMsg, Snip20HookMsg, Snip20ReceiveMsg}, reply::{RecoverTempStorage, RECOVER_REPLY_ID}, state::{ BLOCKED_CONTRACT_ADDRESSES, HYPERLANE_TRANSFER_CONTRACT_ADDRESS, IBC_TRANSFER_CONTRACT_ADDRESS, PRE_SWAP_OUT_ASSET_AMOUNT, RECOVER_TEMP_STORAGE, - SWAP_VENUE_MAP, + REGISTERED_TOKENS, SWAP_VENUE_MAP, VIEWING_KEY, }, }; + +use secret_skip::asset::Asset; + +use secret_toolkit::snip20; + use cosmwasm_std::{ - from_binary, to_binary, Addr, BankMsg, Binary, Coin, CosmosMsg, DepsMut, Env, MessageInfo, - Response, SubMsg, Uint128, WasmMsg, + from_binary, to_binary, Addr, BankMsg, Coin, ContractInfo, CosmosMsg, DepsMut, Env, + MessageInfo, Response, StdError, SubMsg, Uint128, WasmMsg, }; use cw20::Cw20Coin; -// use cw_utils::one_coin; -use skip::{ - hyperlane::{ExecuteMsg as HplExecuteMsg, ExecuteMsg::HplTransfer}, +use secret_skip::{ + error::SkipError, ibc::{ExecuteMsg as IbcTransferExecuteMsg, IbcInfo, IbcTransfer}, - swap::{ - validate_swap_operations, ExecuteMsg as SwapExecuteMsg, QueryMsg as SwapQueryMsg, Swap, - SwapExactAssetOut, - }, + swap::{validate_swap_operations, Swap, SwapExactAssetOut}, +}; +use skip_go_swap_adapter_shade_protocol::msg::{ + Cw20HookMsg as SwapHookMsg, ExecuteMsg as SwapExecuteMsg, QueryMsg as SwapQueryMsg, }; ////////////////////////// @@ -148,7 +152,7 @@ pub fn execute_swap_and_action( match &sent_asset { Asset::Cw20(cw20) => { if cw20.address != info.sender.to_string() { - return Err(ContractError::InvalidCw20Sender); + return Err(ContractError::InvalidSnip20Sender); } } Asset::Native(_) => { @@ -168,9 +172,22 @@ pub fn execute_swap_and_action( return Err(ContractError::Timeout); } + let viewing_key = VIEWING_KEY.load(deps.storage)?; + let min_asset_contract = + REGISTERED_TOKENS.load(deps.storage, deps.api.addr_validate(min_asset.denom())?)?; + // Save the current out asset amount to storage as the pre swap out asset amount - let pre_swap_out_asset_amount = - get_current_asset_available(&deps, &env, min_asset.denom())?.amount(); + let pre_swap_out_asset_amount = match snip20::balance_query( + deps.querier, + env.contract.address.to_string(), + viewing_key, + 255, + min_asset_contract.code_hash.clone(), + min_asset_contract.address.to_string(), + ) { + Ok(balance) => balance.amount, + Err(e) => return Err(ContractError::Std(e)), + }; PRE_SWAP_OUT_ASSET_AMOUNT.save(deps.storage, &pre_swap_out_asset_amount)?; // Already validated at entrypoints (both direct and snip20_receive) @@ -193,22 +210,31 @@ pub fn execute_swap_and_action( if let Swap::SmartSwapExactAssetIn(smart_swap) = &mut user_swap { if smart_swap.routes.is_empty() { - return Err(ContractError::Skip(skip::error::SkipError::RoutesEmpty)); + return Err(ContractError::Skip(SkipError::RoutesEmpty)); } - match smart_swap.amount().cmp(&remaining_asset.amount()) { + match smart_swap + .amount() + .cmp(&remaining_asset.amount().u128().into()) + { std::cmp::Ordering::Equal => {} std::cmp::Ordering::Less => { - let diff = remaining_asset.amount().checked_sub(smart_swap.amount())?; + let diff = remaining_asset + .amount() + .checked_sub(smart_swap.amount().u128().into())?; // If the total swap in amount is less than remaining asset, // adjust the routes to match the remaining asset amount let largest_route_idx = smart_swap.largest_route_index()?; - smart_swap.routes[largest_route_idx].offer_asset.add(diff)?; + smart_swap.routes[largest_route_idx] + .offer_asset + .add(diff.u128().into())?; } std::cmp::Ordering::Greater => { - let diff = smart_swap.amount().checked_sub(remaining_asset.amount())?; + let diff = smart_swap + .amount() + .checked_sub(remaining_asset.amount().u128().into())?; // If the total swap in amount is greater than remaining asset, // adjust the routes to match the remaining asset amount @@ -221,6 +247,7 @@ pub fn execute_swap_and_action( let user_swap_msg = WasmMsg::Execute { contract_addr: env.contract.address.to_string(), + code_hash: env.contract.code_hash.clone(), msg: to_binary(&ExecuteMsg::UserSwap { swap: user_swap, min_asset: min_asset.clone(), @@ -238,6 +265,7 @@ pub fn execute_swap_and_action( // Create the post swap action message let post_swap_action_msg = WasmMsg::Execute { contract_addr: env.contract.address.to_string(), + code_hash: env.contract.code_hash.clone(), msg: to_binary(&ExecuteMsg::PostSwapAction { min_asset, timeout_timestamp, @@ -345,9 +373,25 @@ pub fn execute_user_swap( // Create the affiliate_fee_asset let affiliate_fee_asset = Asset::new(deps.api, min_asset.denom(), affiliate_fee_amount); + let affiliate_fee_contract = REGISTERED_TOKENS.load( + deps.storage, + deps.api.addr_validate(affiliate_fee_asset.denom())?, + )?; // Create the affiliate fee message - let affiliate_fee_msg = affiliate_fee_asset.transfer(&affiliate.address); + // let affiliate_fee_msg = affiliate_fee_asset.transfer(&affiliate.address); + let affiliate_fee_msg = match snip20::transfer_msg( + affiliate.address.to_string(), + affiliate_fee_asset.amount(), + None, + None, + 255, + affiliate_fee_contract.code_hash.clone(), + affiliate_fee_contract.address.to_string(), + ) { + Ok(msg) => msg, + Err(e) => return Err(ContractError::Std(e)), + }; // Add the affiliate fee message and attributes to the response affiliate_response = affiliate_response @@ -358,6 +402,11 @@ pub fn execute_user_swap( } } + let remaining_asset_contract = REGISTERED_TOKENS.load( + deps.storage, + deps.api.addr_validate(remaining_asset.denom())?, + )?; + // Create the user swap message match swap { Swap::SwapExactAssetIn(swap) => { @@ -365,17 +414,35 @@ pub fn execute_user_swap( validate_swap_operations(&swap.operations, remaining_asset.denom(), min_asset.denom())?; // Get swap adapter contract address from venue name - let user_swap_adapter_contract_address = + let user_swap_adapter_contract = SWAP_VENUE_MAP.load(deps.storage, &swap.swap_venue_name)?; // Create the user swap message args - let user_swap_msg_args: SwapExecuteMsg = swap.into(); + let user_swap_msg_args = SwapHookMsg::Swap { + operations: swap.operations, + }; // Create the user swap message + /* let user_swap_msg = remaining_asset.into_wasm_msg( user_swap_adapter_contract_address.to_string(), to_binary(&user_swap_msg_args)?, )?; + */ + + let user_swap_msg = match snip20::send_msg( + user_swap_adapter_contract.address.to_string(), + remaining_asset.amount(), + Some(to_binary(&user_swap_msg_args)?), + None, + None, + 255, + remaining_asset_contract.code_hash.clone(), + remaining_asset_contract.address.to_string(), + ) { + Ok(msg) => msg, + Err(e) => return Err(ContractError::Std(e)), + }; response = response .add_message(user_swap_msg) @@ -386,19 +453,15 @@ pub fn execute_user_swap( validate_swap_operations(&swap.operations, remaining_asset.denom(), min_asset.denom())?; // Get swap adapter contract address from venue name - let user_swap_adapter_contract_address = + let user_swap_adapter_contract = SWAP_VENUE_MAP.load(deps.storage, &swap.swap_venue_name)?; // Calculate the swap asset out by adding the min asset amount to the total affiliate fee amount min_asset.add(total_affiliate_fee_amount)?; // Query the swap adapter to get the asset in needed to obtain the min asset plus affiliates - let user_swap_asset_in = query_swap_asset_in( - &deps, - &user_swap_adapter_contract_address, - &swap, - &min_asset, - )?; + let user_swap_asset_in = + query_swap_asset_in(&deps, &user_swap_adapter_contract, &swap, &min_asset)?; // Verify the user swap in denom is the same as the denom received from the message to the contract if user_swap_asset_in.denom() != remaining_asset.denom() { @@ -422,8 +485,25 @@ pub fn execute_user_swap( // Get the refund amount let refund_amount = remaining_asset.amount(); + let remaining_asset_contract = REGISTERED_TOKENS.load( + deps.storage, + deps.api.addr_validate(remaining_asset.denom())?, + )?; // Create the refund message - let refund_msg = remaining_asset.transfer(&to_address); + // let refund_msg = remaining_asset.transfer(&to_address); + let refund_msg = match snip20::send_msg( + to_address.to_string(), + remaining_asset.amount(), + None, + None, + None, + 255, + remaining_asset_contract.code_hash.clone(), + remaining_asset_contract.address.to_string(), + ) { + Ok(msg) => msg, + Err(e) => return Err(ContractError::Std(e)), + }; // Add the refund message and attributes to the response response = response @@ -434,13 +514,28 @@ pub fn execute_user_swap( } // Create the user swap message args - let user_swap_msg_args: SwapExecuteMsg = swap.into(); + let user_swap_msg_args = swap; // Create the user swap message + /* let user_swap_msg = user_swap_asset_in.into_wasm_msg( - user_swap_adapter_contract_address.to_string(), + user_swap_adapter_contract.address.to_string(), to_binary(&user_swap_msg_args)?, )?; + */ + let user_swap_msg = match snip20::send_msg( + user_swap_adapter_contract.address.to_string(), + remaining_asset.amount(), + Some(to_binary(&user_swap_msg_args)?), + None, + None, + 255, + remaining_asset_contract.code_hash.clone(), + remaining_asset_contract.address.to_string(), + ) { + Ok(msg) => msg, + Err(e) => return Err(ContractError::Std(e)), + }; response = response .add_message(user_swap_msg) @@ -456,19 +551,34 @@ pub fn execute_user_swap( )?; // Get swap adapter contract address from venue name - let user_swap_adapter_contract_address = + let user_swap_adapter_contract = SWAP_VENUE_MAP.load(deps.storage, &swap.swap_venue_name)?; // Create the user swap message args - let user_swap_msg_args = SwapExecuteMsg::Swap { + let user_swap_msg_args = SwapHookMsg::Swap { operations: route.operations, }; // Create the user swap message + /* let user_swap_msg = route.offer_asset.into_wasm_msg( user_swap_adapter_contract_address.to_string(), to_binary(&user_swap_msg_args)?, )?; + */ + let user_swap_msg = match snip20::send_msg( + user_swap_adapter_contract.address.to_string(), + remaining_asset.amount(), + Some(to_binary(&user_swap_msg_args)?), + None, + None, + 255, + remaining_asset_contract.code_hash.clone(), + remaining_asset_contract.address.to_string(), + ) { + Ok(msg) => msg, + Err(e) => return Err(ContractError::Std(e)), + }; response = response .add_message(user_swap_msg) @@ -510,16 +620,28 @@ pub fn execute_post_swap_action( // Get contract balance of min out asset post swap // for fee deduction and transfer out amount enforcement - let post_swap_out_asset = get_current_asset_available(&deps, &env, min_asset.denom())?; + // let post_swap_out_asset = get_current_asset_available(&deps, &env, min_asset.denom())?; + let min_asset_contract = + REGISTERED_TOKENS.load(deps.storage, deps.api.addr_validate(min_asset.denom())?)?; + let viewing_key = VIEWING_KEY.load(deps.storage)?; + let post_swap_out_asset_amount = match snip20::balance_query( + deps.querier, + env.contract.address.to_string(), + viewing_key, + 255, + min_asset_contract.code_hash.clone(), + min_asset_contract.address.to_string(), + ) { + Ok(balance) => balance.amount, + Err(e) => return Err(ContractError::Std(e)), + }; // Set the transfer out asset to the post swap out asset amount minus the pre swap out asset amount // Since we only want to transfer out the amount received from the swap let transfer_out_asset = Asset::new( deps.api, min_asset.denom(), - post_swap_out_asset - .amount() - .checked_sub(pre_swap_out_asset_amount)?, + post_swap_out_asset_amount - pre_swap_out_asset_amount, ); // Error if the contract balance is less than the min asset out amount @@ -571,10 +693,13 @@ pub fn execute_action( // Validate and unwrap the sent asset let sent_asset = match sent_asset { Some(sent_asset) => { - sent_asset.validate(&deps, &env, &info)?; + // sent_asset.validate(&deps, &env, &info)?; + // TODO validate sent_asset } - None => one_coin(&info)?.into(), + None => { + return Err(ContractError::NativeCoinNotSupported); + } }; // Error if the current block time is greater than the timeout timestamp @@ -655,6 +780,7 @@ pub fn execute_action_with_recover( let sub_msg = SubMsg::reply_always( CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: env.contract.address.to_string(), + code_hash: env.contract.code_hash.to_string(), msg: to_binary(&ExecuteMsg::Action { sent_asset, timeout_timestamp, @@ -690,7 +816,21 @@ fn validate_and_dispatch_action( deps.api.addr_validate(&to_address)?; // Create the transfer message - let transfer_msg = action_asset.transfer(&to_address); + // let transfer_msg = action_asset.transfer(&to_address); + let action_asset_contract = REGISTERED_TOKENS + .load(deps.storage, deps.api.addr_validate(action_asset.denom())?)?; + let transfer_msg = match snip20::transfer_msg( + to_address.to_string(), + action_asset.amount(), + None, + None, + 255, + action_asset_contract.code_hash.clone(), + action_asset_contract.address.to_string(), + ) { + Ok(msg) => msg, + Err(e) => return Err(ContractError::Std(e)), + }; // Add the transfer message to the response response = response @@ -702,9 +842,12 @@ fn validate_and_dispatch_action( deps.api.addr_validate(&ibc_info.recover_address)?; let transfer_out_coin = match action_asset { - Asset::Native(coin) => coin, + Asset::Native(coin) => { + return Err(ContractError::NativeCoinNotSupported); + } _ => return Err(ContractError::NonNativeIbcTransfer), }; + todo!("Implement IBC Transfer for Snip20"); // Create the IBC transfer message let ibc_transfer_msg: IbcTransferExecuteMsg = IbcTransfer { @@ -715,11 +858,12 @@ fn validate_and_dispatch_action( .into(); // Get the IBC transfer adapter contract address - let ibc_transfer_contract_address = IBC_TRANSFER_CONTRACT_ADDRESS.load(deps.storage)?; + let ibc_transfer_contract = IBC_TRANSFER_CONTRACT_ADDRESS.load(deps.storage)?; // Send the IBC transfer by calling the IBC transfer contract let ibc_transfer_msg = WasmMsg::Execute { - contract_addr: ibc_transfer_contract_address.to_string(), + contract_addr: ibc_transfer_contract.address.to_string(), + code_hash: ibc_transfer_contract.code_hash.clone(), msg: to_binary(&ibc_transfer_msg)?, funds: vec![transfer_out_coin], }; @@ -771,12 +915,12 @@ fn validate_and_dispatch_action( }; // Get the Hyperlane transfer adapter contract address - let hpl_transfer_contract_address = - HYPERLANE_TRANSFER_CONTRACT_ADDRESS.load(deps.storage)?; + let hpl_transfer_contract = HYPERLANE_TRANSFER_CONTRACT_ADDRESS.load(deps.storage)?; // Send the Hyperlane transfer by calling the Hyperlane transfer contract let hpl_transfer_msg = WasmMsg::Execute { - contract_addr: hpl_transfer_contract_address.to_string(), + contract_addr: hpl_transfer_contract.address.to_string(), + code_hash: hpl_transfer_contract.code_hash, msg: to_binary(&hpl_transfer_msg)?, funds: vec![transfer_out_coin], }; @@ -832,11 +976,11 @@ fn handle_ibc_transfer_fees( // Dispatch the ibc fee bank send to the ibc transfer adapter contract if needed if let Some(ibc_fee_coin) = ibc_fee_coin { // Get the ibc transfer adapter contract address - let ibc_transfer_contract_address = IBC_TRANSFER_CONTRACT_ADDRESS.load(deps.storage)?; + let ibc_transfer_contract = IBC_TRANSFER_CONTRACT_ADDRESS.load(deps.storage)?; // Create the ibc fee bank send message let ibc_fee_msg = BankMsg::Send { - to_address: ibc_transfer_contract_address.to_string(), + to_address: ibc_transfer_contract.address.to_string(), amount: vec![ibc_fee_coin], }; @@ -867,13 +1011,12 @@ fn verify_and_create_fee_swap_msg( )?; // Get swap adapter contract address from venue name - let fee_swap_adapter_contract_address = - SWAP_VENUE_MAP.load(deps.storage, &fee_swap.swap_venue_name)?; + let fee_swap_adapter_contract = SWAP_VENUE_MAP.load(deps.storage, &fee_swap.swap_venue_name)?; // Query the swap adapter to get the asset in needed for the fee swap let fee_swap_asset_in = query_swap_asset_in( deps, - &fee_swap_adapter_contract_address, + &fee_swap_adapter_contract, fee_swap, &ibc_fee_coin.clone().into(), )?; @@ -888,13 +1031,36 @@ fn verify_and_create_fee_swap_msg( remaining_asset.sub(fee_swap_asset_in.amount())?; // Create the fee swap message args - let fee_swap_msg_args: SwapExecuteMsg = fee_swap.clone().into(); + let fee_swap_msg_args = fee_swap.clone(); + + let fee_swap_asset_contract = REGISTERED_TOKENS.load( + deps.storage, + deps.api.addr_validate(fee_swap_asset_in.denom())?, + )?; // Create the fee swap message + /* let fee_swap_msg = fee_swap_asset_in.into_wasm_msg( - fee_swap_adapter_contract_address.to_string(), + fee_swap_adapter_contract.address.to_string(), to_binary(&fee_swap_msg_args)?, )?; + */ + let fee_swap_msg = match snip20::send_msg( + fee_swap_adapter_contract.address.to_string(), + remaining_asset.amount(), + Some(to_binary(&fee_swap_msg_args)?), + None, + None, + 255, + fee_swap_asset_contract.code_hash.clone(), + fee_swap_asset_contract.address.to_string(), + ) { + Ok(msg) => match msg { + CosmosMsg::Wasm(wasm_msg) => wasm_msg, + _ => return Err(ContractError::Std(StdError::generic_err("Invalid WasmMsg"))), + }, + Err(e) => return Err(ContractError::Std(e)), + }; Ok(fee_swap_msg) } @@ -927,15 +1093,16 @@ fn verify_and_calculate_affiliate_fee_amount( // swap asset denom from the message. Returns the swap asset in. fn query_swap_asset_in( deps: &DepsMut, - swap_adapter_contract_address: &Addr, + swap_adapter_contract: &ContractInfo, swap: &SwapExactAssetOut, swap_asset_out: &Asset, ) -> ContractResult { // Query the swap adapter to get the asset in needed for the fee swap let fee_swap_asset_in: Asset = deps.querier.query_wasm_smart( - swap_adapter_contract_address, - &SwapQueryMsg::SimulateSwapExactAssetOut { - asset_out: swap_asset_out.clone(), + swap_adapter_contract.address.clone(), + swap_adapter_contract.code_hash.clone(), + &SwapQueryMsg::SimulateSwapExactAssetIn { + asset_in: swap_asset_out.clone(), swap_operations: swap.operations.clone(), }, )?; diff --git a/contracts/secret-entry-point/src/hyperlane.rs b/contracts/secret-entry-point/src/hyperlane.rs new file mode 100644 index 00000000..65506a5b --- /dev/null +++ b/contracts/secret-entry-point/src/hyperlane.rs @@ -0,0 +1,37 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::HexBinary; + +/////////////// +/// MIGRATE /// +/////////////// + +// The MigrateMsg struct defines the migration parameters used. +#[cw_serde] +pub struct MigrateMsg { + pub entry_point_contract_address: String, +} +/////////////////// +/// INSTANTIATE /// +/////////////////// + +// The InstantiateMsg struct defines the initialization parameters for the IBC Transfer Adapter contracts. +#[cw_serde] +pub struct InstantiateMsg { + pub entry_point_contract_address: String, +} + +/////////////// +/// EXECUTE /// +/////////////// + +// The ExecuteMsg enum defines the execution message that the IBC Transfer Adapter contracts can handle. +#[cw_serde] +pub enum ExecuteMsg { + HplTransfer { + dest_domain: u32, + recipient: HexBinary, + hook: Option, + metadata: Option, + warp_address: String, + }, +} diff --git a/contracts/secret-entry-point/src/lib.rs b/contracts/secret-entry-point/src/lib.rs index 4bf5afc2..31ee4b92 100644 --- a/contracts/secret-entry-point/src/lib.rs +++ b/contracts/secret-entry-point/src/lib.rs @@ -1,7 +1,7 @@ -pub mod asset; pub mod contract; pub mod error; pub mod execute; +pub mod hyperlane; pub mod msg; pub mod query; pub mod reply; diff --git a/contracts/secret-entry-point/src/msg.rs b/contracts/secret-entry-point/src/msg.rs index 543b5eba..bddb5f9c 100644 --- a/contracts/secret-entry-point/src/msg.rs +++ b/contracts/secret-entry-point/src/msg.rs @@ -1,11 +1,17 @@ -use crate::asset::Asset; -use skip::{ +use secret_skip::{ + asset::Asset, ibc::IbcInfo, - swap::{Swap, SwapExactAssetOut, SwapVenue}, + swap::{Swap, SwapExactAssetOut}, }; use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Addr, Binary, HexBinary, Uint128}; +use cosmwasm_std::{Addr, Binary, ContractInfo, HexBinary, Uint128}; + +#[cw_serde] +pub struct SwapVenue { + pub name: String, + pub adapter_contract: ContractInfo, +} #[cw_serde] pub struct Snip20ReceiveMsg { @@ -32,8 +38,9 @@ pub struct MigrateMsg {} #[cw_serde] pub struct InstantiateMsg { pub swap_venues: Vec, - pub ibc_transfer_contract_address: String, - pub hyperlane_transfer_contract_address: Option, + pub ibc_transfer_contract: ContractInfo, + pub hyperlane_transfer_contract: Option, + pub viewing_key: String, } /////////////// @@ -45,6 +52,9 @@ pub struct InstantiateMsg { #[cw_serde] #[allow(clippy::large_enum_variant)] pub enum ExecuteMsg { + RegisterTokens { + contracts: Vec, + }, Receive(Snip20ReceiveMsg), SwapAndActionWithRecover { sent_asset: Option, diff --git a/contracts/secret-entry-point/src/query.rs b/contracts/secret-entry-point/src/query.rs index 26cdf6f3..ba521df9 100644 --- a/contracts/secret-entry-point/src/query.rs +++ b/contracts/secret-entry-point/src/query.rs @@ -3,10 +3,10 @@ use cosmwasm_std::{Addr, Deps, StdResult}; // Queries the swap venue map by name and returns the swap adapter contract address if it exists pub fn query_swap_venue_adapter_contract(deps: Deps, name: String) -> StdResult { - SWAP_VENUE_MAP.load(deps.storage, &name) + Ok(SWAP_VENUE_MAP.load(deps.storage, &name)?.address) } // Queries the IBC transfer adapter contract address and returns it if it exists pub fn query_ibc_transfer_adapter_contract(deps: Deps) -> StdResult { - IBC_TRANSFER_CONTRACT_ADDRESS.load(deps.storage) + Ok(IBC_TRANSFER_CONTRACT_ADDRESS.load(deps.storage)?.address) } diff --git a/contracts/secret-entry-point/src/reply.rs b/contracts/secret-entry-point/src/reply.rs index 2c11e18e..cda4ea26 100644 --- a/contracts/secret-entry-point/src/reply.rs +++ b/contracts/secret-entry-point/src/reply.rs @@ -1,8 +1,11 @@ -use crate::error::ContractError; -use crate::state::RECOVER_TEMP_STORAGE; +use crate::{ + error::ContractError, + state::{RECOVER_TEMP_STORAGE, REGISTERED_TOKENS}, +}; use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, CosmosMsg, DepsMut, Reply, Response, SubMsgResult}; -use skip::asset::Asset; +use secret_skip::asset::Asset; +use secret_toolkit::snip20; pub const RECOVER_REPLY_ID: u64 = 1; @@ -25,11 +28,24 @@ pub fn reply_swap_and_action_with_recover( SubMsgResult::Err(e) => { let storage = RECOVER_TEMP_STORAGE.load(deps.storage)?; - let return_assets_msgs: Vec = storage - .assets - .into_iter() - .map(|asset| asset.transfer(storage.recovery_addr.as_str())) - .collect(); + let mut return_assets_msgs: Vec = vec![]; + + for return_asset in storage.assets.into_iter() { + let return_asset_contract = REGISTERED_TOKENS + .load(deps.storage, deps.api.addr_validate(return_asset.denom())?)?; + match snip20::transfer_msg( + storage.recovery_addr.to_string(), + return_asset.amount(), + None, + None, + 255, + return_asset_contract.code_hash.clone(), + return_asset_contract.address.to_string(), + ) { + Ok(msg) => return_assets_msgs.push(msg), + Err(e) => return Err(ContractError::Std(e)), + }; + } RECOVER_TEMP_STORAGE.remove(deps.storage); diff --git a/contracts/secret-entry-point/src/state.rs b/contracts/secret-entry-point/src/state.rs index 2c668b81..91dcbfae 100644 --- a/contracts/secret-entry-point/src/state.rs +++ b/contracts/secret-entry-point/src/state.rs @@ -1,11 +1,12 @@ use crate::reply::RecoverTempStorage; -use cosmwasm_std::{Addr, Uint128}; +use cosmwasm_std::{Addr, ContractInfo, Uint128}; use secret_storage_plus::{Item, Map}; pub const BLOCKED_CONTRACT_ADDRESSES: Map<&Addr, ()> = Map::new("blocked_contract_addresses"); -pub const SWAP_VENUE_MAP: Map<&str, Addr> = Map::new("swap_venue_map"); -pub const IBC_TRANSFER_CONTRACT_ADDRESS: Item = Item::new("ibc_transfer_contract_address"); -pub const HYPERLANE_TRANSFER_CONTRACT_ADDRESS: Item = +pub const SWAP_VENUE_MAP: Map<&str, ContractInfo> = Map::new("swap_venue_map"); +pub const IBC_TRANSFER_CONTRACT_ADDRESS: Item = + Item::new("ibc_transfer_contract_address"); +pub const HYPERLANE_TRANSFER_CONTRACT_ADDRESS: Item = Item::new("hyperlane_transfer_contract_address"); // Temporary state to save variables to be used in @@ -16,3 +17,9 @@ pub const RECOVER_TEMP_STORAGE: Item = Item::new("recover_te // has pre swap so that we can ensure the amount transferred out does not // exceed the amount the contract obtained from the current swap/call pub const PRE_SWAP_OUT_ASSET_AMOUNT: Item = Item::new("pre_swap_out_asset_amount"); + +// Secret Network tokens need to be registered for viewing key setup +// and storing contract code hash +pub const REGISTERED_TOKENS: Map = Map::new("registered_tokens"); + +pub const VIEWING_KEY: Item = Item::new("viewing_key"); From 2f797ca5fbef057609df1d960b96da33ebd98c3f Mon Sep 17 00:00:00 2001 From: DrPresident Date: Wed, 11 Dec 2024 16:11:17 -0600 Subject: [PATCH 10/29] switched to secret-skip package --- .../adapters/swap/shade-protocol/Cargo.toml | 4 +- .../adapters/swap/shade-protocol/src/asset.rs | 612 ------------------ .../src/bin/shade-protocol-schema.rs | 2 +- .../swap/shade-protocol/src/contract.rs | 7 +- .../adapters/swap/shade-protocol/src/error.rs | 2 +- .../adapters/swap/shade-protocol/src/lib.rs | 1 - .../adapters/swap/shade-protocol/src/msg.rs | 4 +- 7 files changed, 10 insertions(+), 622 deletions(-) delete mode 100644 contracts/adapters/swap/shade-protocol/src/asset.rs diff --git a/contracts/adapters/swap/shade-protocol/Cargo.toml b/contracts/adapters/swap/shade-protocol/Cargo.toml index a9b91b99..49d91601 100644 --- a/contracts/adapters/swap/shade-protocol/Cargo.toml +++ b/contracts/adapters/swap/shade-protocol/Cargo.toml @@ -27,9 +27,11 @@ cw2 = { workspace = true } cw20 = { workspace = true } #cw-storage-plus = { workspace = true } cw-utils = { workspace = true } -skip = { workspace = true } +#skip = { workspace = true } thiserror = { workspace = true } +secret-skip = { workspace = true } + cosmwasm-std = { package = "secret-cosmwasm-std", version = "1.1.11"} cosmwasm-schema = "1.4.0" secret-toolkit = { git = "https://github.com/scrtlabs/secret-toolkit", tag = "v0.10.0" } diff --git a/contracts/adapters/swap/shade-protocol/src/asset.rs b/contracts/adapters/swap/shade-protocol/src/asset.rs deleted file mode 100644 index 0e900023..00000000 --- a/contracts/adapters/swap/shade-protocol/src/asset.rs +++ /dev/null @@ -1,612 +0,0 @@ -use crate::error::ContractError; -use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Api, Coin, Uint128}; -use cw20::{Cw20Coin, Cw20CoinVerified}; -// use cw_utils::nonpayable; - -#[cw_serde] -pub enum Asset { - Native(Coin), - Cw20(Cw20Coin), -} - -impl From for Asset { - fn from(coin: Coin) -> Self { - Asset::Native(coin) - } -} - -impl From for Asset { - fn from(cw20_coin: Cw20Coin) -> Self { - Asset::Cw20(cw20_coin) - } -} - -impl From for Asset { - fn from(cw20_coin_verified: Cw20CoinVerified) -> Self { - Asset::Cw20(Cw20Coin { - address: cw20_coin_verified.address.to_string(), - amount: cw20_coin_verified.amount, - }) - } -} - -impl Asset { - pub fn default_native() -> Self { - Asset::Native(Coin::default()) - } - - pub fn new(api: &dyn Api, denom: &str, amount: Uint128) -> Self { - match api.addr_validate(denom) { - Ok(addr) => Asset::Cw20(Cw20Coin { - address: addr.to_string(), - amount: amount.u128().into(), - }), - Err(_) => Asset::Native(Coin { - denom: denom.to_string(), - amount, - }), - } - } - - pub fn denom(&self) -> &str { - match self { - Asset::Native(coin) => &coin.denom, - Asset::Cw20(coin) => &coin.address, - } - } - - pub fn amount(&self) -> Uint128 { - match self { - Asset::Native(coin) => coin.amount, - Asset::Cw20(coin) => coin.amount.u128().into(), - } - } - - /* - pub fn add(&mut self, amount: Uint128) -> Result { - match self { - Asset::Native(coin) => { - coin.amount = coin.amount.checked_add(amount)?; - Ok(coin.amount) - } - Asset::Cw20(coin) => { - coin.amount = coin.amount.checked_add(amount.u128().into())?; - Ok(coin.amount.u128().into()) - } - } - } - - pub fn sub(&mut self, amount: Uint128) -> Result { - match self { - Asset::Native(coin) => { - coin.amount = coin.amount.checked_sub(amount)?; - Ok(coin.amount) - } - Asset::Cw20(coin) => { - coin.amount = coin.amount.checked_sub(amount.u128().into())?; - Ok(coin.amount.u128().into()) - } - } - } - */ - - /* - pub fn transfer(self, to_address: &str) -> CosmosMsg { - match self { - Asset::Native(coin) => CosmosMsg::Bank(BankMsg::Send { - to_address: to_address.to_string(), - amount: vec![coin], - }), - Asset::Cw20(coin) => CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: coin.address.clone(), - msg: to_binary(&Cw20ExecuteMsg::Transfer { - recipient: to_address.to_string(), - amount: coin.amount, - }) - .unwrap(), - funds: vec![], - }), - } - } - */ - - /* - pub fn into_wasm_msg(self, contract_addr: String, msg: Binary) -> Result { - match self { - Asset::Native(coin) => Ok(WasmMsg::Execute { - contract_addr, - msg, - funds: vec![coin], - }), - Asset::Cw20(coin) => Ok(WasmMsg::Execute { - contract_addr: coin.address, - msg: to_binary(&Cw20ExecuteMsg::Send { - contract: contract_addr, - amount: coin.amount, - msg, - })?, - funds: vec![], - }), - } - } - */ - - /* - pub fn validate(&self, deps: &DepsMut, env: &Env, info: &MessageInfo) -> Result<(), SkipError> { - match self { - Asset::Native(coin) => { - unimplemented!(); - } - Asset::Cw20(coin) => { - // Validate that the message is nonpayable - nonpayable(info)?; - - let verified_cw20_coin_addr = deps.api.addr_validate(&coin.address)?; - - let cw20_contract = Cw20Contract(verified_cw20_coin_addr); - - let balance = cw20_contract.balance(&deps.querier, &env.contract.address)?; - - if coin.amount <= balance { - Ok(()) - } else { - Err(SkipError::InvalidCw20Coin) - } - } - } - } - */ -} - -/* -pub fn get_current_asset_available( - deps: &DepsMut, - env: &Env, - denom: &str, -) -> Result { - match deps.api.addr_validate(denom) { - Ok(addr) => { - let cw20_contract = Cw20Contract(addr.clone()); - - let amount = cw20_contract.balance(&deps.querier, &env.contract.address)?; - - Ok(Asset::Cw20(Cw20Coin { - address: addr.to_string(), - amount, - })) - } - Err(_) => { - let coin = deps.querier.query_balance(&env.contract.address, denom)?; - - Ok(Asset::Native(coin)) - } - } -} -*/ - -#[cfg(test)] -mod tests { - use super::*; - use cosmwasm_std::{ - testing::{mock_dependencies_with_balances, mock_env, mock_info}, - Addr, ContractResult, QuerierResult, SystemResult, WasmQuery, - }; - use cw20::BalanceResponse; - use cw_utils::PaymentError; - - #[test] - fn test_default_native() { - let asset = Asset::default_native(); - - assert_eq!( - asset, - Asset::Native(Coin { - denom: "".to_string(), - amount: Uint128::zero(), - }) - ); - } - - #[test] - fn test_new() { - // TEST 1: Native asset - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); - - let asset = Asset::new(deps.as_mut().api, "ua", Uint128::new(100)); - - assert_eq!( - asset, - Asset::Native(Coin { - denom: "ua".to_string(), - amount: Uint128::new(100), - }) - ); - - // TEST 2: Cw20 asset - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); - - let asset = Asset::new(deps.as_mut().api, "asset", Uint128::new(100)); - - assert_eq!( - asset, - Asset::Cw20(Cw20Coin { - address: "asset".to_string(), - amount: Uint128::new(100), - }) - ); - } - - #[test] - fn test_asset_native() { - let asset = Asset::Native(Coin { - denom: "uatom".to_string(), - amount: Uint128::new(100), - }); - - assert_eq!(asset.denom(), "uatom"); - assert_eq!(asset.amount(), Uint128::new(100)); - } - - #[test] - fn test_asset_cw20() { - let asset = Asset::Cw20(Cw20Coin { - address: "asset".to_string(), - amount: Uint128::new(100), - }); - - assert_eq!(asset.denom(), "asset"); - assert_eq!(asset.amount(), Uint128::new(100)); - } - - #[test] - fn test_add() { - // TEST 1: Native asset - let mut asset = Asset::Native(Coin { - denom: "uatom".to_string(), - amount: Uint128::new(100), - }); - - asset.add(Uint128::new(20)).unwrap(); - - assert_eq!(asset.amount(), Uint128::new(120)); - - // TEST 2: Cw20 asset - let mut asset = Asset::Cw20(Cw20Coin { - address: "asset".to_string(), - amount: Uint128::new(100), - }); - - asset.add(Uint128::new(20)).unwrap(); - - assert_eq!(asset.amount(), Uint128::new(120)); - } - - #[test] - fn test_sub() { - // TEST 1: Native asset - let mut asset = Asset::Native(Coin { - denom: "uatom".to_string(), - amount: Uint128::new(100), - }); - - asset.sub(Uint128::new(20)).unwrap(); - - assert_eq!(asset.amount(), Uint128::new(80)); - - // TEST 2: Cw20 asset - let mut asset = Asset::Cw20(Cw20Coin { - address: "asset".to_string(), - amount: Uint128::new(100), - }); - - asset.sub(Uint128::new(20)).unwrap(); - - assert_eq!(asset.amount(), Uint128::new(80)); - } - - #[test] - fn test_asset_transfer_native() { - let asset = Asset::Native(Coin { - denom: "uatom".to_string(), - amount: Uint128::new(100), - }); - - let msg = asset.transfer("addr"); - - match msg { - CosmosMsg::Bank(BankMsg::Send { to_address, amount }) => { - assert_eq!(to_address, "addr"); - assert_eq!(amount.len(), 1); - assert_eq!(amount[0].denom, "uatom"); - assert_eq!(amount[0].amount, Uint128::new(100)); - } - _ => panic!("Unexpected message type"), - } - } - - #[test] - fn test_asset_transfer_cw20() { - let asset = Asset::Cw20(Cw20Coin { - address: "asset".to_string(), - amount: Uint128::new(100), - }); - - let msg = asset.transfer("addr"); - - match msg { - CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr, - msg, - funds, - }) => { - assert_eq!(contract_addr, "asset"); - assert_eq!( - msg, - to_binary(&Cw20ExecuteMsg::Transfer { - recipient: "addr".to_string(), - amount: Uint128::new(100), - }) - .unwrap() - ); - assert_eq!(funds.len(), 0); - } - _ => panic!("Unexpected message type"), - } - } - - #[test] - fn test_into_astroport_asset() { - // TEST 1: Native asset - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); - - let asset = Asset::Native(Coin { - denom: "uatom".to_string(), - amount: Uint128::new(100), - }); - - let astroport_asset = asset.into_astroport_asset(deps.as_mut().api).unwrap(); - - assert_eq!( - astroport_asset, - AstroportAsset { - info: AssetInfo::NativeToken { - denom: "uatom".to_string() - }, - amount: Uint128::new(100), - } - ); - - // TEST 2: Cw20 asset - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); - - let asset = Asset::Cw20(Cw20Coin { - address: "asset".to_string(), - amount: Uint128::new(100), - }); - - let astroport_asset = asset.into_astroport_asset(deps.as_mut().api).unwrap(); - - assert_eq!( - astroport_asset, - AstroportAsset { - info: AssetInfo::Token { - contract_addr: Addr::unchecked("asset") - }, - amount: Uint128::new(100), - } - ); - } - - #[test] - fn test_validate_native() { - // TEST 1: Valid asset - let asset = Asset::Native(Coin { - denom: "uatom".to_string(), - amount: Uint128::new(100), - }); - - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); - - let env = mock_env(); - - let info = mock_info( - "sender", - &[Coin { - denom: "uatom".to_string(), - amount: Uint128::new(100), - }], - ); - - assert!(asset.validate(&deps.as_mut(), &env, &info).is_ok()); - - // TEST 2: Invalid asset due to less amount of denom sent - let asset = Asset::Native(Coin { - denom: "uatom".to_string(), - amount: Uint128::new(100), - }); - - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); - - let env = mock_env(); - - let info = mock_info( - "sender", - &[Coin { - denom: "uatom".to_string(), - amount: Uint128::new(50), - }], - ); - - let res = asset.validate(&deps.as_mut(), &env, &info); - - assert_eq!(res, Err(ContractError::InvalidNativeCoin)); - - // TEST 3: Invalid asset due to more than one coin sent - let asset = Asset::Native(Coin { - denom: "uatom".to_string(), - amount: Uint128::new(100), - }); - - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); - - let env = mock_env(); - - let info = mock_info( - "sender", - &[ - Coin { - denom: "uatom".to_string(), - amount: Uint128::new(100), - }, - Coin { - denom: "uosmo".to_string(), - amount: Uint128::new(50), - }, - ], - ); - - let res = asset.validate(&deps.as_mut(), &env, &info); - - assert_eq!( - res, - Err(ContractError::Payment(PaymentError::MultipleDenoms {})) - ); - } - - #[test] - fn test_validate_cw20() { - // TEST 1: Valid asset - let asset = Asset::Cw20(Cw20Coin { - address: "asset".to_string(), - amount: Uint128::new(100), - }); - - // Create mock wasm handler to handle the cw20 balance query - let wasm_handler = |query: &WasmQuery| -> QuerierResult { - match query { - WasmQuery::Smart { .. } => SystemResult::Ok(ContractResult::Ok( - to_binary(&BalanceResponse { - balance: Uint128::from(100u128), - }) - .unwrap(), - )), - _ => panic!("Unsupported query: {:?}", query), - } - }; - - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); - - deps.querier.update_wasm(wasm_handler); - - let env = mock_env(); - - let info = mock_info("sender", &[]); - - assert!(asset.validate(&deps.as_mut(), &env, &info).is_ok()); - - // TEST 2: Invalid asset due to native coin sent in info - let asset = Asset::Cw20(Cw20Coin { - address: "asset".to_string(), - amount: Uint128::new(100), - }); - - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); - - let env = mock_env(); - - let info = mock_info( - "sender", - &[Coin { - denom: "uatom".to_string(), - amount: Uint128::new(100), - }], - ); - - let res = asset.validate(&deps.as_mut(), &env, &info); - - assert_eq!( - res, - Err(ContractError::Payment(PaymentError::NonPayable {})) - ); - - // TEST 3: Invalid asset due to invalid cw20 balance - let asset = Asset::Cw20(Cw20Coin { - address: "asset".to_string(), - amount: Uint128::new(100), - }); - - // Create mock wasm handler to handle the cw20 balance query - let wasm_handler = |query: &WasmQuery| -> QuerierResult { - match query { - WasmQuery::Smart { .. } => SystemResult::Ok(ContractResult::Ok( - to_binary(&BalanceResponse { - balance: Uint128::from(50u128), - }) - .unwrap(), - )), - _ => panic!("Unsupported query: {:?}", query), - } - }; - - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); - - deps.querier.update_wasm(wasm_handler); - - let env = mock_env(); - - let info = mock_info("sender", &[]); - - let res = asset.validate(&deps.as_mut(), &env, &info); - - assert_eq!(res, Err(ContractError::InvalidCw20Coin)); - } - - #[test] - fn test_get_current_asset_available() { - // TEST 1: Native asset - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[Coin::new(100, "ua")])]); - - let mut env = mock_env(); - env.contract.address = Addr::unchecked("entry_point"); - - let asset = get_current_asset_available(&deps.as_mut(), &env, "ua").unwrap(); - - assert_eq!( - asset, - Asset::Native(Coin { - denom: "ua".to_string(), - amount: Uint128::new(100), - }) - ); - - // TEST 2: Cw20 asset - let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); - - let wasm_handler = |query: &WasmQuery| -> QuerierResult { - match query { - WasmQuery::Smart { .. } => SystemResult::Ok(ContractResult::Ok( - to_binary(&BalanceResponse { - balance: Uint128::from(100u128), - }) - .unwrap(), - )), - _ => panic!("Unsupported query: {:?}", query), - } - }; - - deps.querier.update_wasm(wasm_handler); - - let env = mock_env(); - - let asset = get_current_asset_available(&deps.as_mut(), &env, "asset").unwrap(); - - assert_eq!( - asset, - Asset::Cw20(Cw20Coin { - address: "asset".to_string(), - amount: Uint128::new(100), - }) - ); - } -} diff --git a/contracts/adapters/swap/shade-protocol/src/bin/shade-protocol-schema.rs b/contracts/adapters/swap/shade-protocol/src/bin/shade-protocol-schema.rs index 4f4733f0..e5cc9e2f 100644 --- a/contracts/adapters/swap/shade-protocol/src/bin/shade-protocol-schema.rs +++ b/contracts/adapters/swap/shade-protocol/src/bin/shade-protocol-schema.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::write_api; -use skip::swap::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use secret_skip::swap::{ExecuteMsg, InstantiateMsg, QueryMsg}; fn main() { write_api! { diff --git a/contracts/adapters/swap/shade-protocol/src/contract.rs b/contracts/adapters/swap/shade-protocol/src/contract.rs index 741a64c8..60c32bc7 100644 --- a/contracts/adapters/swap/shade-protocol/src/contract.rs +++ b/contracts/adapters/swap/shade-protocol/src/contract.rs @@ -1,5 +1,4 @@ use crate::{ - asset::Asset, error::{ContractError, ContractResult}, // skip_error::ContractError, state::{REGISTERED_TOKENS, STATE}, @@ -9,15 +8,13 @@ use cosmwasm_std::{ entry_point, from_binary, to_binary, Addr, Binary, ContractInfo, Deps, DepsMut, Env, MessageInfo, Response, Uint128, WasmMsg, }; +use secret_skip::{asset::Asset, swap::SwapOperation}; // use cw2::set_contract_version; use cw20::Cw20Coin; use secret_toolkit::snip20; use crate::{ - msg::{ - Cw20HookMsg, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, Snip20ReceiveMsg, - SwapOperation, - }, + msg::{Cw20HookMsg, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, Snip20ReceiveMsg}, shade_swap_router_msg as shade_router, }; diff --git a/contracts/adapters/swap/shade-protocol/src/error.rs b/contracts/adapters/swap/shade-protocol/src/error.rs index 3916fdb3..4c433822 100644 --- a/contracts/adapters/swap/shade-protocol/src/error.rs +++ b/contracts/adapters/swap/shade-protocol/src/error.rs @@ -1,6 +1,6 @@ use cosmwasm_std::{OverflowError, StdError}; use cw_utils; -use skip::error::SkipError; +use secret_skip::error::SkipError; use thiserror::Error; pub type ContractResult = core::result::Result; diff --git a/contracts/adapters/swap/shade-protocol/src/lib.rs b/contracts/adapters/swap/shade-protocol/src/lib.rs index 8895750d..be2d81d2 100644 --- a/contracts/adapters/swap/shade-protocol/src/lib.rs +++ b/contracts/adapters/swap/shade-protocol/src/lib.rs @@ -1,4 +1,3 @@ -pub mod asset; pub mod contract; pub mod error; pub mod msg; diff --git a/contracts/adapters/swap/shade-protocol/src/msg.rs b/contracts/adapters/swap/shade-protocol/src/msg.rs index 2b29c0b0..4b7fb5f8 100644 --- a/contracts/adapters/swap/shade-protocol/src/msg.rs +++ b/contracts/adapters/swap/shade-protocol/src/msg.rs @@ -1,6 +1,6 @@ -use crate::asset::Asset; use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{Addr, Binary, ContractInfo, Uint128}; +use secret_skip::{asset::Asset, swap::SwapOperation}; #[cw_serde] pub struct InstantiateMsg { @@ -41,6 +41,7 @@ pub enum Cw20HookMsg { Swap { operations: Vec }, } +/* #[cw_serde] pub struct SwapOperation { pub pool: String, @@ -48,6 +49,7 @@ pub struct SwapOperation { pub denom_out: String, pub interface: Option, } +*/ #[cw_serde] pub struct Snip20ReceiveMsg { From 470c1e43483dcfdaf3778653963ddf9062d9cc9d Mon Sep 17 00:00:00 2001 From: DrPresident Date: Wed, 11 Dec 2024 16:11:48 -0600 Subject: [PATCH 11/29] added secret-skip to cargo.toml --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 2b0d6065..77aa31ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,7 @@ serde = { version = "1.0.194", default-features = false, features serde-cw-value = "0.7.0" serde-json-wasm = "1.0.1" skip = { version = "0.3.0", path = "./packages/skip" } +secret-skip = { version = "0.3.0", path = "./packages/secret-skip" } test-case = "3.3.1" thiserror = "1" white-whale-std = "1.1.1" From e65341fca41e615e16740def9876a481835f2edc Mon Sep 17 00:00:00 2001 From: DrPresident Date: Mon, 16 Dec 2024 10:50:12 -0600 Subject: [PATCH 12/29] secret entry point building --- .../secret-entry-point/src/bin/schema.rs | 2 +- contracts/secret-entry-point/src/execute.rs | 41 ++++++++++++++--- packages/secret-skip/src/ibc.rs | 45 +++---------------- 3 files changed, 42 insertions(+), 46 deletions(-) diff --git a/contracts/secret-entry-point/src/bin/schema.rs b/contracts/secret-entry-point/src/bin/schema.rs index 1046f8be..8599f00e 100644 --- a/contracts/secret-entry-point/src/bin/schema.rs +++ b/contracts/secret-entry-point/src/bin/schema.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::write_api; -use skip::entry_point::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use skip_go_secret_entry_point::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; fn main() { write_api! { diff --git a/contracts/secret-entry-point/src/execute.rs b/contracts/secret-entry-point/src/execute.rs index 0a4f113a..884b0e39 100644 --- a/contracts/secret-entry-point/src/execute.rs +++ b/contracts/secret-entry-point/src/execute.rs @@ -841,31 +841,52 @@ fn validate_and_dispatch_action( // Validates recover address, errors if invalid deps.api.addr_validate(&ibc_info.recover_address)?; - let transfer_out_coin = match action_asset { + let transfer_out_contract = match action_asset { Asset::Native(coin) => { return Err(ContractError::NativeCoinNotSupported); } - _ => return Err(ContractError::NonNativeIbcTransfer), + _ => REGISTERED_TOKENS + .load(deps.storage, deps.api.addr_validate(action_asset.denom())?)?, }; - todo!("Implement IBC Transfer for Snip20"); // Create the IBC transfer message + // TODO send ICS20 message + /* let ibc_transfer_msg: IbcTransferExecuteMsg = IbcTransfer { info: ibc_info, - coin: transfer_out_coin.clone(), + coin: action_asset.clone(), timeout_timestamp, } .into(); + */ // Get the IBC transfer adapter contract address let ibc_transfer_contract = IBC_TRANSFER_CONTRACT_ADDRESS.load(deps.storage)?; // Send the IBC transfer by calling the IBC transfer contract + /* let ibc_transfer_msg = WasmMsg::Execute { contract_addr: ibc_transfer_contract.address.to_string(), code_hash: ibc_transfer_contract.code_hash.clone(), msg: to_binary(&ibc_transfer_msg)?, - funds: vec![transfer_out_coin], + funds: vec![], + };*/ + + let ibc_transfer_msg = match snip20::send_msg( + ibc_transfer_contract.address.to_string(), + action_asset.amount(), + None, //Some(to_binary(&fee_swap_msg_args)?), + None, + None, + 255, + transfer_out_contract.code_hash.clone(), + transfer_out_contract.address.to_string(), + ) { + Ok(msg) => match msg { + CosmosMsg::Wasm(wasm_msg) => wasm_msg, + _ => return Err(ContractError::Std(StdError::generic_err("Invalid WasmMsg"))), + }, + Err(e) => return Err(ContractError::Std(e)), }; // Add the IBC transfer message to the response @@ -885,8 +906,16 @@ fn validate_and_dispatch_action( return Err(ContractError::ContractCallAddressBlocked); } + let action_asset_contract = REGISTERED_TOKENS + .load(deps.storage, deps.api.addr_validate(action_asset.denom())?)?; + // Create the contract call message - let contract_call_msg = action_asset.into_wasm_msg(contract_address, msg)?; + let contract_call_msg = WasmMsg::Execute { + contract_addr: action_asset_contract.address.to_string(), + code_hash: action_asset_contract.code_hash, + msg: to_binary(&msg)?, + funds: vec![], + }; // Add the contract call message to the response response = response diff --git a/packages/secret-skip/src/ibc.rs b/packages/secret-skip/src/ibc.rs index 94eb2582..c106da45 100644 --- a/packages/secret-skip/src/ibc.rs +++ b/packages/secret-skip/src/ibc.rs @@ -67,51 +67,19 @@ pub struct IbcFee { pub timeout_fee: Vec, } -/* -// Converts an IbcFee struct to a neutron_proto::neutron::feerefunder Fee -impl From for NeutronFee { - fn from(ibc_fee: IbcFee) -> Self { - NeutronFee { - recv_fee: ibc_fee - .recv_fee - .iter() - .map(|coin| ProtoCoin(coin.clone()).into()) - .collect(), - ack_fee: ibc_fee - .ack_fee - .iter() - .map(|coin| ProtoCoin(coin.clone()).into()) - .collect(), - timeout_fee: ibc_fee - .timeout_fee - .iter() - .map(|coin| ProtoCoin(coin.clone()).into()) - .collect(), - } - } -} -*/ - -/* // Converts an IbcFee struct to a cosmwasm_std::Coins struct // Must be TryFrom since adding the ibc_fees can overflow. -impl TryFrom for Coins { +impl TryFrom for Vec { type Error = StdError; fn try_from(ibc_fee: IbcFee) -> Result { - let mut ibc_fees = Coins::default(); - - [ibc_fee.recv_fee, ibc_fee.ack_fee, ibc_fee.timeout_fee] + Ok([ibc_fee.recv_fee, ibc_fee.ack_fee, ibc_fee.timeout_fee] .into_iter() .flatten() - .try_for_each(|coin| ibc_fees.add(coin))?; - - Ok(ibc_fees) + .collect()) } } -*/ -/* impl IbcFee { // one_coin aims to mimic the behavior of cw_utls::one_coin, // returing the single coin in the IbcFee struct if it exists, @@ -120,16 +88,15 @@ impl IbcFee { // one_coin is used because the entry_point contract only supports // the handling of a single denomination for IBC fees. pub fn one_coin(&self) -> Result { - let ibc_fees_map: Coins = self.clone().try_into()?; + let ibc_fees: Vec = self.clone().try_into()?; - if ibc_fees_map.len() != 1 { + if ibc_fees.len() != 1 { return Err(SkipError::IbcFeesNotOneCoin); } - Ok(ibc_fees_map.to_vec().first().unwrap().clone()) + Ok(ibc_fees.first().unwrap().clone()) } } -*/ // The IbcInfo struct defines the information for an IBC transfer standardized across all IBC Transfer Adapter contracts. #[cw_serde] From 43a77b84af84817ad000941608f3b2605068ff5b Mon Sep 17 00:00:00 2001 From: DrPresident Date: Tue, 17 Dec 2024 10:43:51 -0600 Subject: [PATCH 13/29] all contracts building --- Cargo.lock | 41 +- .../adapters/ibc/secret-ibc-hooks/Cargo.toml | 43 ++ .../adapters/ibc/secret-ibc-hooks/README.md | 68 +++ .../src/bin/ibc-hooks-schema.rs | 10 + .../ibc/secret-ibc-hooks/src/contract.rs | 502 ++++++++++++++++++ .../ibc/secret-ibc-hooks/src/error.rs | 46 ++ .../adapters/ibc/secret-ibc-hooks/src/lib.rs | 3 + .../ibc/secret-ibc-hooks/src/state.rs | 14 + .../adapters/swap/shade-protocol/src/msg.rs | 14 +- contracts/secret-entry-point/src/execute.rs | 4 +- contracts/secret-entry-point/src/msg.rs | 11 +- packages/secret-skip/src/asset.rs | 22 +- packages/secret-skip/src/ibc.rs | 22 +- 13 files changed, 761 insertions(+), 39 deletions(-) create mode 100644 contracts/adapters/ibc/secret-ibc-hooks/Cargo.toml create mode 100644 contracts/adapters/ibc/secret-ibc-hooks/README.md create mode 100644 contracts/adapters/ibc/secret-ibc-hooks/src/bin/ibc-hooks-schema.rs create mode 100644 contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs create mode 100644 contracts/adapters/ibc/secret-ibc-hooks/src/error.rs create mode 100644 contracts/adapters/ibc/secret-ibc-hooks/src/lib.rs create mode 100644 contracts/adapters/ibc/secret-ibc-hooks/src/state.rs diff --git a/Cargo.lock b/Cargo.lock index 8b18b4b4..db6be416 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1870,6 +1870,21 @@ dependencies = [ "serde", ] +[[package]] +name = "secret-skip" +version = "0.3.0" +dependencies = [ + "cosmos-sdk-proto 0.19.0", + "cosmwasm-schema", + "cw-utils 1.0.3", + "cw20 1.1.2", + "ibc-proto", + "secret-cosmwasm-std", + "secret-storage-plus", + "secret-toolkit", + "thiserror", +] + [[package]] name = "secret-storage-plus" version = "0.13.4" @@ -2197,10 +2212,32 @@ dependencies = [ "cw20 1.1.2", "schemars", "secret-cosmwasm-std", + "secret-skip", "secret-storage-plus", "secret-toolkit", "serde", - "skip", + "skip-go-swap-adapter-shade-protocol", + "test-case", + "thiserror", +] + +[[package]] +name = "skip-go-secret-ibc-adapter-ibc-hooks" +version = "0.3.0" +dependencies = [ + "cosmwasm-schema", + "cw2 1.1.2", + "cw20 1.1.2", + "ibc-proto", + "prost 0.11.9", + "schemars", + "secret-cosmwasm-std", + "secret-skip", + "secret-storage-plus", + "secret-toolkit", + "serde", + "serde-cw-value", + "serde-json-wasm 1.0.1", "test-case", "thiserror", ] @@ -2364,10 +2401,10 @@ dependencies = [ "cw20 1.1.2", "schemars", "secret-cosmwasm-std", + "secret-skip", "secret-storage-plus", "secret-toolkit", "serde", - "skip", "test-case", "thiserror", ] diff --git a/contracts/adapters/ibc/secret-ibc-hooks/Cargo.toml b/contracts/adapters/ibc/secret-ibc-hooks/Cargo.toml new file mode 100644 index 00000000..019558fe --- /dev/null +++ b/contracts/adapters/ibc/secret-ibc-hooks/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "skip-go-secret-ibc-adapter-ibc-hooks" +version = { workspace = true } +rust-version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +homepage = { workspace = true } +repository = { workspace = true } +documentation = { workspace = true } +keywords = { workspace = true } + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +# for more explicit tests, cargo test --features=backtraces +backtraces = ["cosmwasm-std/backtraces"] +# use library feature to disable all instantiate/execute/query exports +library = [] + +[dependencies] +cosmwasm-schema = { workspace = true } +#cosmwasm-std = { workspace = true } +cw2 = { workspace = true } +cw20 = { workspace = true } +#cw-storage-plus = { workspace = true } +ibc-proto = { workspace = true } +prost = { workspace = true } +serde-json-wasm = { workspace = true } +serde-cw-value = { workspace = true } +# skip = { workspace = true } +thiserror = { workspace = true } + +secret-skip = { workspace = true } +serde = "1.0.114" +schemars = "0.8.1" +secret-toolkit = { git = "https://github.com/scrtlabs/secret-toolkit", tag = "v0.10.0" } +secret-storage-plus = { git = "https://github.com/securesecrets/secret-plus-utils", tag = "v0.1.1", features = [] } +cosmwasm-std = { package = "secret-cosmwasm-std", version = "1.1.11"} + +[dev-dependencies] +test-case = { workspace = true } diff --git a/contracts/adapters/ibc/secret-ibc-hooks/README.md b/contracts/adapters/ibc/secret-ibc-hooks/README.md new file mode 100644 index 00000000..d7d51667 --- /dev/null +++ b/contracts/adapters/ibc/secret-ibc-hooks/README.md @@ -0,0 +1,68 @@ +# Osmosis IBC Transfer Adapter Contract + +The Osmosis IBC Transfer adapter contract is responsible for: +1. Dispatching the IBC transfer. +2. Failing the entire transaction if the IBC transfer errors on the swap chain (sending the caller back their original funds). +3. Refunding the caller on the swap chain if the IBC transfer errors or times out once it reaches the destination chain. + +WARNING: Do not send funds directly to the contract without calling one of its functions. Funds sent directly to the contract do not trigger any contract logic that performs validation / safety checks (as the Cosmos SDK handles direct fund transfers in the `Bank` module and not the `Wasm` module). There are no explicit recovery mechanisms for accidentally sent funds. + +## InstantiateMsg + +Instantiates a new Osmosis IBC Transfer adapter contract. + +``` json +{} +``` + +## ExecuteMsg + +### `ibc_transfer` + +Dispatches an ICS-20 IBC Transfer given the parameters provided in the contract call. + +Note: Fees sent as parameters with the contract call are unused by the contract since Osmosis currently does not require ICS-29 fees for outgoing ibc transfers. The fee field is still included in the call data to keep the interface the same across all IBC transfer adapter contracts. + +``` json +{ + "ibc_transfer": { + "info": { + "source_channel": "channel-1", + "receiver": "cosmos...", + "fee": { + "recv_fee": [], + "ack_fee": [], + "timeout_fee": [] + }, + "memo": "", + "recover_address": "osmo..." + }, + "coin": { + "denom": "uosmo", + "amount": "1000000" + }, + "timeout_timestamp": 1000000000000 + } +} +``` + +## QueryMsg + +### `in_progress_recover_address` + +Returns the in progress recover address associated with the given `channel_id` and `sequence_id` (which make up a unique identifier mapped to in progress ibc transfers in the sub msg reply handler). + +Query: +``` json +{ + "in_progress_recover_address": { + "channel_id": "channel-1", + "sequence_id": 420 + } +} +``` + +Response: +``` json +"osmo..." +``` \ No newline at end of file diff --git a/contracts/adapters/ibc/secret-ibc-hooks/src/bin/ibc-hooks-schema.rs b/contracts/adapters/ibc/secret-ibc-hooks/src/bin/ibc-hooks-schema.rs new file mode 100644 index 00000000..3c4f61ad --- /dev/null +++ b/contracts/adapters/ibc/secret-ibc-hooks/src/bin/ibc-hooks-schema.rs @@ -0,0 +1,10 @@ +use cosmwasm_schema::write_api; +use secret_skip::ibc::{ExecuteMsg, InstantiateMsg, QueryMsg}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + execute: ExecuteMsg, + query: QueryMsg + } +} diff --git a/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs b/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs new file mode 100644 index 00000000..78a07838 --- /dev/null +++ b/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs @@ -0,0 +1,502 @@ +use crate::{ + error::{ContractError, ContractResult}, + state::{ + ACK_ID_TO_RECOVER_ADDRESS, ENTRY_POINT_CONTRACT, ICS20_CONTRACT, IN_PROGRESS_CHANNEL_ID, + IN_PROGRESS_RECOVER_ADDRESS, REGISTERED_TOKENS, VIEWING_KEY, + }, +}; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{ + entry_point, from_binary, to_binary, BankMsg, Binary, Coin, ContractInfo, CosmosMsg, Deps, + DepsMut, Env, MessageInfo, Reply, Response, SubMsg, SubMsgResult, +}; +use cw20::Cw20Coin; +// use cw2::set_contract_version; +use ibc_proto::ibc::applications::transfer::v1::{MsgTransfer, MsgTransferResponse}; +use prost::Message; +use secret_skip::{ + asset::{Asset, Snip20ReceiveMsg}, + ibc::{ + AckID, ExecuteMsg, IbcInfo, IbcLifecycleComplete, InstantiateMsg, MigrateMsg, QueryMsg, + Snip20HookMsg, + }, + proto_coin::ProtoCoin, + sudo::{OsmosisSudoMsg as SudoMsg, SudoType}, +}; +use secret_toolkit::snip20; +use serde_cw_value::Value; + +const IBC_MSG_TRANSFER_TYPE_URL: &str = "/ibc.applications.transfer.v1.MsgTransfer"; +const REPLY_ID: u64 = 1; + +#[cw_serde] +pub struct Ics20TransferMsg { + /// The local channel to send the packets on + pub channel: String, + /// The remote address to send to + /// Don't use HumanAddress as this will likely have a different Bech32 prefix than we use + /// and cannot be validated locally + pub remote_address: String, + /// How long the packet lives in seconds. If not specified, use default_timeout + pub timeout: Option, +} + +/////////////// +/// MIGRATE /// +/////////////// + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> ContractResult { + // Set contract version + // set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + // Validate entry point contract address + let checked_entry_point_contract = ContractInfo { + address: deps + .api + .addr_validate(&msg.entry_point_contract.address.to_string())?, + code_hash: msg.entry_point_contract.code_hash, + }; + + // Store the entry point contract address + ENTRY_POINT_CONTRACT.save(deps.storage, &checked_entry_point_contract)?; + + let checked_ics20_contract = ContractInfo { + address: deps + .api + .addr_validate(&msg.ics20_contract.address.to_string())?, + code_hash: msg.ics20_contract.code_hash, + }; + + ICS20_CONTRACT.save(deps.storage, &checked_ics20_contract)?; + + Ok(Response::new() + .add_attribute("action", "migrate") + .add_attribute( + "entry_point_contract_address", + checked_entry_point_contract.address.to_string(), + ) + .add_attribute( + "ics20_contract_address", + checked_ics20_contract.address.to_string(), + )) +} + +/////////////////// +/// INSTANTIATE /// +/////////////////// + +// Contract name and version used for migration. +const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); +const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + deps: DepsMut, + _env: Env, + _info: MessageInfo, + msg: InstantiateMsg, +) -> ContractResult { + // Set contract version + + // Validate entry point contract address + let checked_entry_point_contract = ContractInfo { + address: deps + .api + .addr_validate(&msg.entry_point_contract.address.to_string())?, + code_hash: msg.entry_point_contract.code_hash, + }; + + // Store the entry point contract address + ENTRY_POINT_CONTRACT.save(deps.storage, &checked_entry_point_contract)?; + + let checked_ics20_contract = ContractInfo { + address: deps + .api + .addr_validate(&msg.ics20_contract.address.to_string())?, + code_hash: msg.ics20_contract.code_hash, + }; + + ICS20_CONTRACT.save(deps.storage, &checked_ics20_contract)?; + + Ok(Response::new() + .add_attribute("action", "instantiate") + .add_attribute( + "entry_point_contract_address", + checked_entry_point_contract.address.to_string(), + ) + .add_attribute( + "ics20_contract_address", + checked_ics20_contract.address.to_string(), + )) +} + +/////////////// +/// EXECUTE /// +/////////////// + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> ContractResult { + match msg { + ExecuteMsg::Receive(msg) => receive_snip20(deps, env, info, msg), + ExecuteMsg::RegisterTokens { contracts } => register_tokens(deps, env, contracts), + _ => Err(ContractError::UnsupportedExecuteMsg), + } +} + +pub fn receive_snip20( + deps: DepsMut, + env: Env, + info: MessageInfo, + snip20_msg: Snip20ReceiveMsg, +) -> ContractResult { + let sent_asset = Asset::Cw20(Cw20Coin { + address: info.sender.to_string(), + amount: snip20_msg.amount.u128().into(), + }); + + match snip20_msg.msg { + Some(msg) => match from_binary(&msg)? { + // Transfer tokens out over ICS20 + Snip20HookMsg::IbcTransfer { + ibc_info, + timeout_timestamp, + } => { + execute_ics20_ibc_transfer(deps, env, info, ibc_info, sent_asset, timeout_timestamp) + } + }, + None => Err(ContractError::NoSnip20ReceiveMsg), + } +} + +fn execute_ics20_ibc_transfer( + deps: DepsMut, + env: Env, + info: MessageInfo, + ibc_info: IbcInfo, + sent_asset: Asset, + timeout_timestamp: u64, +) -> ContractResult { + // Get entry point contract address from storage + let entry_point_contract = ENTRY_POINT_CONTRACT.load(deps.storage)?; + + let ics20_contract = ICS20_CONTRACT.load(deps.storage)?; + + // Enforce the caller is the entry point contract + if info.sender != entry_point_contract.address { + return Err(ContractError::Unauthorized); + } + + // Error if ibc_info.fee is not None since Osmosis does not support fees + if ibc_info.fee.is_some() { + return Err(ContractError::IbcFeesNotSupported); + } + + let sent_asset_contract = + REGISTERED_TOKENS.load(deps.storage, deps.api.addr_validate(sent_asset.denom())?)?; + + let ibc_transfer_msg = match snip20::send_msg_with_code_hash( + ics20_contract.address.to_string(), + Some(ics20_contract.code_hash), + sent_asset.amount(), + Some(to_binary(&Ics20TransferMsg { + channel: ibc_info.source_channel.clone(), + remote_address: ibc_info.receiver, + timeout: Some(timeout_timestamp), + })?), + None, + None, + 255, + sent_asset_contract.code_hash.clone(), + sent_asset_contract.address.to_string(), + ) { + Ok(msg) => msg, + Err(e) => return Err(ContractError::Std(e)), + }; + + // Save in progress recover address to storage, to be used in sudo handler + IN_PROGRESS_RECOVER_ADDRESS.save( + deps.storage, + &ibc_info.recover_address, // This address is verified in entry point + )?; + + // Save in progress channel id to storage, to be used in sudo handler + IN_PROGRESS_CHANNEL_ID.save(deps.storage, &ibc_info.source_channel)?; + + // Verify memo is valid json and add the necessary key/value pair to trigger the ibc hooks callback logic. + // let memo = verify_and_create_memo(ibc_info.memo, env.contract.address.to_string())?; + + // Create sub message from osmosis ibc transfer message to receive a reply + let sub_msg = SubMsg::reply_on_success(ibc_transfer_msg, REPLY_ID); + + Ok(Response::new() + .add_submessage(sub_msg) + .add_attribute("action", "execute_ics20_ibc_transfer")) +} + +/* +// Converts the given info and coin into an ibc transfer message, +// saves necessary info in case the ibc transfer fails to send funds back to +// a recovery address, and then emits the ibc transfer message as a sub message +fn execute_ibc_transfer( + deps: DepsMut, + env: Env, + info: MessageInfo, + ibc_info: IbcInfo, + coin: Coin, + timeout_timestamp: u64, +) -> ContractResult { + // Get entry point contract address from storage + let entry_point_contract = ENTRY_POINT_CONTRACT.load(deps.storage)?; + + // Enforce the caller is the entry point contract + if info.sender != entry_point_contract.address { + return Err(ContractError::Unauthorized); + } + + // Error if ibc_info.fee is not None since Osmosis does not support fees + if ibc_info.fee.is_some() { + return Err(ContractError::IbcFeesNotSupported); + } + + // Save in progress recover address to storage, to be used in sudo handler + IN_PROGRESS_RECOVER_ADDRESS.save( + deps.storage, + &ibc_info.recover_address, // This address is verified in entry point + )?; + + // Save in progress channel id to storage, to be used in sudo handler + IN_PROGRESS_CHANNEL_ID.save(deps.storage, &ibc_info.source_channel)?; + + // Verify memo is valid json and add the necessary key/value pair to trigger the ibc hooks callback logic. + let memo = verify_and_create_memo(ibc_info.memo, env.contract.address.to_string())?; + + // Create osmosis ibc transfer message + let msg = MsgTransfer { + source_port: "transfer".to_string(), + source_channel: ibc_info.source_channel, + token: Some(ProtoCoin(coin).into()), + sender: env.contract.address.to_string(), + receiver: ibc_info.receiver, + timeout_height: None, + timeout_timestamp, + memo, + }; + + // Create stargate message from osmosis ibc transfer message + let msg = CosmosMsg::Stargate { + type_url: IBC_MSG_TRANSFER_TYPE_URL.to_string(), + value: msg.encode_to_vec().into(), + }; + + // Create sub message from osmosis ibc transfer message to receive a reply + let sub_msg = SubMsg::reply_on_success(msg, REPLY_ID); + + Ok(Response::new() + .add_submessage(sub_msg) + .add_attribute("action", "execute_ibc_transfer")) +} +*/ + +///////////// +/// REPLY /// +///////////// + +// Handles the reply from the ibc transfer sub message +// Upon success, maps the sub msg AckID (channel_id, sequence_id) +// to the in progress ibc transfer struct, and saves it to storage. +// Now that the map entry is stored, it also removes the in progress +// ibc transfer from storage. +#[entry_point] +pub fn reply(deps: DepsMut, _env: Env, reply: Reply) -> ContractResult { + // Error if the reply id is not the same as the one used in the sub message dispatched + // This should never happen since we are using a constant reply id, but added in case + // the wasm module doesn't behave as expected. + if reply.id != REPLY_ID { + unreachable!() + } + + // Get the sub message response from the reply and error if it does not exist + // This should never happen since sub msg was set to reply on success only, + // but added in case the wasm module doesn't behave as expected. + let SubMsgResult::Ok(sub_msg_response) = reply.result else { + unreachable!() + }; + + // Parse the response from the sub message + let resp: MsgTransferResponse = MsgTransferResponse::decode( + sub_msg_response + .data + .ok_or(ContractError::MissingResponseData)? + .as_slice(), + )?; + + // Get and delete the in progress recover address from storage + let in_progress_recover_address = IN_PROGRESS_RECOVER_ADDRESS.load(deps.storage)?; + IN_PROGRESS_RECOVER_ADDRESS.remove(deps.storage); + + // Get and delete the in progress channel id from storage + let in_progress_channel_id = IN_PROGRESS_CHANNEL_ID.load(deps.storage)?; + IN_PROGRESS_CHANNEL_ID.remove(deps.storage); + + // Set ack_id to be the channel id and sequence id from the response as a tuple + let ack_id: AckID = (&in_progress_channel_id, resp.sequence); + + // Error if unique ack_id (channel id, sequence id) already exists in storage + if ACK_ID_TO_RECOVER_ADDRESS.has(deps.storage, ack_id) { + return Err(ContractError::AckIDAlreadyExists { + channel_id: ack_id.0.into(), + sequence_id: ack_id.1, + }); + } + + // Set the in progress recover address to storage, keyed by channel id and sequence id + ACK_ID_TO_RECOVER_ADDRESS.save(deps.storage, ack_id, &in_progress_recover_address)?; + + Ok(Response::new().add_attribute("action", "sub_msg_reply_success")) +} + +//////////// +/// SUDO /// +//////////// + +// Handles the ibc callback from the ibc hooks module +// Upon success, removes the in progress ibc transfer from storage and returns immediately. +// Upon error or timeout, sends the attempted ibc transferred funds back to the user's recover address. +#[entry_point] +pub fn sudo(deps: DepsMut, env: Env, msg: SudoMsg) -> ContractResult { + // Get the channel id, sequence id, and sudo type from the sudo message + let (channel, sequence, sudo_type) = match msg { + SudoMsg::IbcLifecycleComplete(IbcLifecycleComplete::IbcAck { + channel, + sequence, + ack: _, + success, + }) => { + // Remove the AckID <> in progress ibc transfer from storage + // and return immediately if the ibc transfer was successful + // since no further action is needed. + if success { + let ack_id: AckID = (&channel, sequence); + ACK_ID_TO_RECOVER_ADDRESS.remove(deps.storage, ack_id); + + return Ok(Response::new().add_attribute("action", SudoType::Response)); + } + + (channel, sequence, SudoType::Error) + } + SudoMsg::IbcLifecycleComplete(IbcLifecycleComplete::IbcTimeout { channel, sequence }) => { + (channel, sequence, SudoType::Timeout) + } + }; + + // Get and remove the AckID <> in progress recover address from storage + let ack_id: AckID = (&channel, sequence); + let to_address = ACK_ID_TO_RECOVER_ADDRESS.load(deps.storage, ack_id)?; + ACK_ID_TO_RECOVER_ADDRESS.remove(deps.storage, ack_id); + + // Get all coins from contract's balance, which will be the the + // failed ibc transfer coin and any leftover dust on the contract + let amount = deps.querier.query_all_balances(env.contract.address)?; + + // If amount is empty, return a no funds to refund error + if amount.is_empty() { + return Err(ContractError::NoFundsToRefund); + } + + // Create bank send message to send funds back to user's recover address + let bank_send_msg = BankMsg::Send { to_address, amount }; + + Ok(Response::new() + .add_message(bank_send_msg) + .add_attribute("action", sudo_type)) +} + +//////////////////////// +/// HELPER FUNCTIONS /// +//////////////////////// + +// Verifies the given memo is empty or valid json, and then adds the necessary +// key/value pair to trigger the ibc hooks callback logic. +fn verify_and_create_memo(memo: String, contract_address: String) -> ContractResult { + // If the memo given is empty, then set it to "{}" to avoid json parsing errors. Then, + // get Value object from json string, erroring if the memo was not null while not being valid json + let mut memo: Value = serde_json_wasm::from_str(if memo.is_empty() { "{}" } else { &memo })?; + + // Transform the Value object into a Value map representation of the json string + // and insert the necessary key value pair into the memo map to trigger + // the ibc hooks callback logic. That key value pair is: + // { "ibc_callback": } + // + // If the "ibc_callback" key was already set, this will override + // the value with the current contract address. + if let Value::Map(ref mut memo) = memo { + memo.insert( + Value::String("ibc_callback".to_string()), + Value::String(contract_address), + ) + } else { + unreachable!() + }; + + // Transform the memo Value map back into a json string + let memo = serde_json_wasm::to_string(&memo)?; + + Ok(memo) +} + +fn register_tokens( + deps: DepsMut, + env: Env, + contracts: Vec, +) -> ContractResult { + let mut response = Response::new(); + + let viewing_key = VIEWING_KEY.load(deps.storage)?; + + for contract in contracts.iter() { + // Add to storage for later use of code hash + REGISTERED_TOKENS.save(deps.storage, contract.address.clone(), contract)?; + // register receive, set viewing key, & add attribute + response = response + .add_attribute("register_token", contract.address.clone()) + .add_messages(vec![ + snip20::set_viewing_key_msg( + viewing_key.clone(), + None, + 255, + contract.code_hash.clone(), + contract.address.to_string(), + )?, + snip20::register_receive_msg( + env.contract.code_hash.clone(), + None, + 255, + contract.code_hash.clone(), + contract.address.to_string(), + )?, + ]); + } + + Ok(response) +} + +///////////// +/// QUERY /// +///////////// + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> ContractResult { + match msg { + QueryMsg::InProgressRecoverAddress { + channel_id, + sequence_id, + } => to_binary(&ACK_ID_TO_RECOVER_ADDRESS.load(deps.storage, (&channel_id, sequence_id))?), + } + .map_err(From::from) +} diff --git a/contracts/adapters/ibc/secret-ibc-hooks/src/error.rs b/contracts/adapters/ibc/secret-ibc-hooks/src/error.rs new file mode 100644 index 00000000..6bbce185 --- /dev/null +++ b/contracts/adapters/ibc/secret-ibc-hooks/src/error.rs @@ -0,0 +1,46 @@ +use cosmwasm_std::{OverflowError, StdError}; +use thiserror::Error; + +pub type ContractResult = core::result::Result; + +#[derive(Error, Debug)] +pub enum ContractError { + #[error(transparent)] + Std(#[from] StdError), + + #[error(transparent)] + Decode(#[from] prost::DecodeError), + + #[error(transparent)] + JsonDecode(#[from] serde_json_wasm::de::Error), + + #[error(transparent)] + JsonEncode(#[from] serde_json_wasm::ser::Error), + + #[error(transparent)] + Overflow(#[from] OverflowError), + + #[error("IBC fees are not supported, vectors must be empty")] + IbcFeesNotSupported, + + #[error("SubMsgResponse does not contain data")] + MissingResponseData, + + #[error("Failed to receive ibc funds to refund the user")] + NoFundsToRefund, + + #[error("Unauthorized")] + Unauthorized, + + #[error("ACK ID already exists for channel ID {channel_id} and sequence ID {sequence_id}")] + AckIDAlreadyExists { + channel_id: String, + sequence_id: u64, + }, + + #[error("No Snip20 Receive Message sent")] + NoSnip20ReceiveMsg, + + #[error("Unsupported Execute Message")] + UnsupportedExecuteMsg, +} diff --git a/contracts/adapters/ibc/secret-ibc-hooks/src/lib.rs b/contracts/adapters/ibc/secret-ibc-hooks/src/lib.rs new file mode 100644 index 00000000..3d3e89c8 --- /dev/null +++ b/contracts/adapters/ibc/secret-ibc-hooks/src/lib.rs @@ -0,0 +1,3 @@ +pub mod contract; +pub mod error; +pub mod state; diff --git a/contracts/adapters/ibc/secret-ibc-hooks/src/state.rs b/contracts/adapters/ibc/secret-ibc-hooks/src/state.rs new file mode 100644 index 00000000..2268b8d3 --- /dev/null +++ b/contracts/adapters/ibc/secret-ibc-hooks/src/state.rs @@ -0,0 +1,14 @@ +use cosmwasm_std::{Addr, ContractInfo}; +use secret_skip::ibc::AckID; +use secret_storage_plus::{Item, Map}; + +pub const ENTRY_POINT_CONTRACT: Item = Item::new("entry_point_contract_address"); +pub const IN_PROGRESS_RECOVER_ADDRESS: Item = Item::new("in_progress_recover_address"); +pub const IN_PROGRESS_CHANNEL_ID: Item = Item::new("in_progress_channel_id"); +pub const ACK_ID_TO_RECOVER_ADDRESS: Map = Map::new("ack_id_to_recover_address"); + +pub const ICS20_CONTRACT: Item = Item::new("ics20_contract"); + +pub const REGISTERED_TOKENS: Map = Map::new("registered_tokens"); + +pub const VIEWING_KEY: Item = Item::new("viewing_key"); diff --git a/contracts/adapters/swap/shade-protocol/src/msg.rs b/contracts/adapters/swap/shade-protocol/src/msg.rs index 4b7fb5f8..b842a393 100644 --- a/contracts/adapters/swap/shade-protocol/src/msg.rs +++ b/contracts/adapters/swap/shade-protocol/src/msg.rs @@ -1,6 +1,9 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{Addr, Binary, ContractInfo, Uint128}; -use secret_skip::{asset::Asset, swap::SwapOperation}; +use secret_skip::{ + asset::{Asset, Snip20ReceiveMsg}, + swap::SwapOperation, +}; #[cw_serde] pub struct InstantiateMsg { @@ -50,12 +53,3 @@ pub struct SwapOperation { pub interface: Option, } */ - -#[cw_serde] -pub struct Snip20ReceiveMsg { - pub sender: Addr, - pub from: Addr, - pub amount: Uint128, - pub memo: Option, - pub msg: Option, -} diff --git a/contracts/secret-entry-point/src/execute.rs b/contracts/secret-entry-point/src/execute.rs index 884b0e39..d2839957 100644 --- a/contracts/secret-entry-point/src/execute.rs +++ b/contracts/secret-entry-point/src/execute.rs @@ -3,7 +3,7 @@ use std::vec; use crate::{ error::{ContractError, ContractResult}, hyperlane::{ExecuteMsg as HplExecuteMsg, ExecuteMsg::HplTransfer}, - msg::{Action, Affiliate, ExecuteMsg, Snip20HookMsg, Snip20ReceiveMsg}, + msg::{Action, Affiliate, ExecuteMsg, Snip20HookMsg}, reply::{RecoverTempStorage, RECOVER_REPLY_ID}, state::{ BLOCKED_CONTRACT_ADDRESSES, HYPERLANE_TRANSFER_CONTRACT_ADDRESS, @@ -12,7 +12,7 @@ use crate::{ }, }; -use secret_skip::asset::Asset; +use secret_skip::asset::{Asset, Snip20ReceiveMsg}; use secret_toolkit::snip20; diff --git a/contracts/secret-entry-point/src/msg.rs b/contracts/secret-entry-point/src/msg.rs index bddb5f9c..e43dd668 100644 --- a/contracts/secret-entry-point/src/msg.rs +++ b/contracts/secret-entry-point/src/msg.rs @@ -1,5 +1,5 @@ use secret_skip::{ - asset::Asset, + asset::{Asset, Snip20ReceiveMsg}, ibc::IbcInfo, swap::{Swap, SwapExactAssetOut}, }; @@ -13,15 +13,6 @@ pub struct SwapVenue { pub adapter_contract: ContractInfo, } -#[cw_serde] -pub struct Snip20ReceiveMsg { - pub sender: Addr, - pub from: Addr, - pub amount: Uint128, - pub memo: Option, - pub msg: Option, -} - /////////////// /// MIGRATE /// /////////////// diff --git a/packages/secret-skip/src/asset.rs b/packages/secret-skip/src/asset.rs index 38720e99..bc2edfb3 100644 --- a/packages/secret-skip/src/asset.rs +++ b/packages/secret-skip/src/asset.rs @@ -1,17 +1,17 @@ use crate::error::SkipError; // use astroport::asset::{Asset as AstroportAsset, AssetInfo}; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{ - to_binary, Api, BankMsg, Binary, Coin, CosmosMsg, DepsMut, Env, MessageInfo, OverflowError, - OverflowOperation, Uint128, WasmMsg, -}; -use cw20::{Cw20Coin, Cw20CoinVerified, Cw20Contract, Cw20ExecuteMsg}; -use cw_utils::{nonpayable, one_coin}; -/* -use white_whale_std::pool_network::asset::{ - Asset as WhiteWhaleAsset, AssetInfo as WhiteWhaleAssetInfo, -}; -*/ +use cosmwasm_std::{Addr, Api, Binary, Coin, OverflowError, OverflowOperation, Uint128}; +use cw20::{Cw20Coin, Cw20CoinVerified}; + +#[cw_serde] +pub struct Snip20ReceiveMsg { + pub sender: Addr, + pub from: Addr, + pub amount: Uint128, + pub memo: Option, + pub msg: Option, +} #[cw_serde] pub enum Asset { diff --git a/packages/secret-skip/src/ibc.rs b/packages/secret-skip/src/ibc.rs index c106da45..84cec882 100644 --- a/packages/secret-skip/src/ibc.rs +++ b/packages/secret-skip/src/ibc.rs @@ -1,9 +1,9 @@ -use crate::{error::SkipError, proto_coin::ProtoCoin}; +use crate::{asset::Snip20ReceiveMsg, error::SkipError, proto_coin::ProtoCoin}; use std::convert::From; use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Coin, StdError}; +use cosmwasm_std::{Addr, Binary, Coin, ContractInfo, StdError, Uint128}; // use neutron_proto::neutron::feerefunder::Fee as NeutronFee; /////////////// @@ -13,7 +13,8 @@ use cosmwasm_std::{Coin, StdError}; // The MigrateMsg struct defines the migration parameters used. #[cw_serde] pub struct MigrateMsg { - pub entry_point_contract_address: String, + pub entry_point_contract: ContractInfo, + pub ics20_contract: ContractInfo, } /////////////////// /// INSTANTIATE /// @@ -22,7 +23,8 @@ pub struct MigrateMsg { // The InstantiateMsg struct defines the initialization parameters for the IBC Transfer Adapter contracts. #[cw_serde] pub struct InstantiateMsg { - pub entry_point_contract_address: String, + pub entry_point_contract: ContractInfo, + pub ics20_contract: ContractInfo, } /////////////// @@ -37,6 +39,18 @@ pub enum ExecuteMsg { coin: Coin, timeout_timestamp: u64, }, + RegisterTokens { + contracts: Vec, + }, + Receive(Snip20ReceiveMsg), +} + +#[cw_serde] +pub enum Snip20HookMsg { + IbcTransfer { + ibc_info: IbcInfo, + timeout_timestamp: u64, + }, } ///////////// From 52cac1417ee009a358659f4faae2d5b7e37c0c14 Mon Sep 17 00:00:00 2001 From: DrPresident Date: Tue, 17 Dec 2024 11:12:04 -0600 Subject: [PATCH 14/29] clean up warnings and tidy up types --- .../ibc/secret-ibc-hooks/src/contract.rs | 18 +-- .../swap/shade-protocol/src/contract.rs | 124 ++++++++++-------- .../adapters/swap/shade-protocol/src/msg.rs | 2 +- .../src/shade_swap_router_msg.rs | 10 +- .../adapters/swap/shade-protocol/src/state.rs | 6 +- contracts/secret-entry-point/src/contract.rs | 3 +- contracts/secret-entry-point/src/execute.rs | 38 ++---- packages/secret-skip/src/ibc.rs | 6 +- packages/secret-skip/src/swap.rs | 20 +-- 9 files changed, 103 insertions(+), 124 deletions(-) diff --git a/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs b/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs index 78a07838..fb40cff3 100644 --- a/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs +++ b/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs @@ -7,12 +7,12 @@ use crate::{ }; use cosmwasm_schema::cw_serde; use cosmwasm_std::{ - entry_point, from_binary, to_binary, BankMsg, Binary, Coin, ContractInfo, CosmosMsg, Deps, - DepsMut, Env, MessageInfo, Reply, Response, SubMsg, SubMsgResult, + entry_point, from_binary, to_binary, BankMsg, Binary, ContractInfo, Deps, DepsMut, Env, + MessageInfo, Reply, Response, SubMsg, SubMsgResult, }; use cw20::Cw20Coin; // use cw2::set_contract_version; -use ibc_proto::ibc::applications::transfer::v1::{MsgTransfer, MsgTransferResponse}; +use ibc_proto::ibc::applications::transfer::v1::MsgTransferResponse; use prost::Message; use secret_skip::{ asset::{Asset, Snip20ReceiveMsg}, @@ -20,13 +20,11 @@ use secret_skip::{ AckID, ExecuteMsg, IbcInfo, IbcLifecycleComplete, InstantiateMsg, MigrateMsg, QueryMsg, Snip20HookMsg, }, - proto_coin::ProtoCoin, sudo::{OsmosisSudoMsg as SudoMsg, SudoType}, }; use secret_toolkit::snip20; -use serde_cw_value::Value; -const IBC_MSG_TRANSFER_TYPE_URL: &str = "/ibc.applications.transfer.v1.MsgTransfer"; +// const IBC_MSG_TRANSFER_TYPE_URL: &str = "/ibc.applications.transfer.v1.MsgTransfer"; const REPLY_ID: u64 = 1; #[cw_serde] @@ -87,8 +85,10 @@ pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> ContractResult match from_binary(&msg)? { // Transfer tokens out over ICS20 Snip20HookMsg::IbcTransfer { - ibc_info, + info: ibc_info, timeout_timestamp, } => { execute_ics20_ibc_transfer(deps, env, info, ibc_info, sent_asset, timeout_timestamp) @@ -176,7 +176,7 @@ pub fn receive_snip20( fn execute_ics20_ibc_transfer( deps: DepsMut, - env: Env, + _env: Env, info: MessageInfo, ibc_info: IbcInfo, sent_asset: Asset, @@ -423,6 +423,7 @@ pub fn sudo(deps: DepsMut, env: Env, msg: SudoMsg) -> ContractResult { // Verifies the given memo is empty or valid json, and then adds the necessary // key/value pair to trigger the ibc hooks callback logic. +/* fn verify_and_create_memo(memo: String, contract_address: String) -> ContractResult { // If the memo given is empty, then set it to "{}" to avoid json parsing errors. Then, // get Value object from json string, erroring if the memo was not null while not being valid json @@ -449,6 +450,7 @@ fn verify_and_create_memo(memo: String, contract_address: String) -> ContractRes Ok(memo) } +*/ fn register_tokens( deps: DepsMut, diff --git a/contracts/adapters/swap/shade-protocol/src/contract.rs b/contracts/adapters/swap/shade-protocol/src/contract.rs index 60c32bc7..5e1e373c 100644 --- a/contracts/adapters/swap/shade-protocol/src/contract.rs +++ b/contracts/adapters/swap/shade-protocol/src/contract.rs @@ -1,31 +1,27 @@ use crate::{ error::{ContractError, ContractResult}, - // skip_error::ContractError, - state::{REGISTERED_TOKENS, STATE}, + state::{ + ENTRY_POINT_CONTRACT, REGISTERED_TOKENS, SHADE_POOL_CODE_HASH, SHADE_ROUTER_CONTRACT, + VIEWING_KEY, + }, }; -use cosmwasm_schema::cw_serde; use cosmwasm_std::{ entry_point, from_binary, to_binary, Addr, Binary, ContractInfo, Deps, DepsMut, Env, MessageInfo, Response, Uint128, WasmMsg, }; -use secret_skip::{asset::Asset, swap::SwapOperation}; +use secret_skip::{ + asset::{Asset, Snip20ReceiveMsg}, + swap::SwapOperation, +}; // use cw2::set_contract_version; use cw20::Cw20Coin; use secret_toolkit::snip20; use crate::{ - msg::{Cw20HookMsg, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, Snip20ReceiveMsg}, + msg::{Cw20HookMsg, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}, shade_swap_router_msg as shade_router, }; -#[cw_serde] -pub struct State { - pub entry_point_contract: ContractInfo, - pub shade_router_contract: ContractInfo, - pub shade_pool_code_hash: String, - pub viewing_key: String, -} - // Contract name and version used for migration. /* const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); @@ -37,7 +33,7 @@ const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); /////////////// #[cfg_attr(not(feature = "library"), entry_point)] -pub fn migrate(deps: DepsMut, _env: Env, msg: State) -> ContractResult { +pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> ContractResult { // Set contract version // set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; @@ -49,22 +45,28 @@ pub fn migrate(deps: DepsMut, _env: Env, msg: State) -> ContractResult code_hash: msg.entry_point_contract.code_hash, }; - // Store the entry point contract address - STATE.save( - deps.storage, - &State { - entry_point_contract: checked_entry_point_contract.clone(), - shade_router_contract: msg.shade_router_contract.clone(), - shade_pool_code_hash: msg.shade_pool_code_hash.clone(), - viewing_key: msg.viewing_key.clone(), - }, - )?; + ENTRY_POINT_CONTRACT.save(deps.storage, &checked_entry_point_contract)?; + + let checked_shade_router_contract = ContractInfo { + address: deps + .api + .addr_validate(&msg.shade_router_contract.address.to_string())?, + code_hash: msg.shade_router_contract.code_hash, + }; + + SHADE_ROUTER_CONTRACT.save(deps.storage, &checked_shade_router_contract)?; + + VIEWING_KEY.save(deps.storage, &msg.viewing_key)?; Ok(Response::new() .add_attribute("action", "migrate") .add_attribute( "entry_point_contract_address", checked_entry_point_contract.address.to_string(), + ) + .add_attribute( + "shade_router_contract_address", + checked_shade_router_contract.address.to_string(), )) } @@ -77,7 +79,7 @@ pub fn instantiate( deps: DepsMut, _env: Env, _info: MessageInfo, - msg: State, + msg: InstantiateMsg, ) -> ContractResult { // Set contract version // set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; @@ -90,16 +92,18 @@ pub fn instantiate( code_hash: msg.entry_point_contract.code_hash, }; - // Store the entry point contract address - STATE.save( - deps.storage, - &State { - entry_point_contract: checked_entry_point_contract.clone(), - shade_router_contract: msg.shade_router_contract.clone(), - shade_pool_code_hash: msg.shade_pool_code_hash.clone(), - viewing_key: msg.viewing_key.clone(), - }, - )?; + ENTRY_POINT_CONTRACT.save(deps.storage, &checked_entry_point_contract)?; + + let checked_shade_router_contract = ContractInfo { + address: deps + .api + .addr_validate(&msg.shade_router_contract.address.to_string())?, + code_hash: msg.shade_router_contract.code_hash, + }; + + SHADE_ROUTER_CONTRACT.save(deps.storage, &checked_shade_router_contract)?; + + VIEWING_KEY.save(deps.storage, &msg.viewing_key)?; Ok(Response::new() .add_attribute("action", "instantiate") @@ -164,7 +168,6 @@ pub fn execute( )?), // Tokens must be registered before they can be swapped ExecuteMsg::RegisterTokens { contracts } => register_tokens(deps, env, contracts), - _ => unimplemented!(), } } @@ -175,20 +178,23 @@ fn execute_swap( operations: Vec, input_amount: Uint128, ) -> ContractResult { - // Get contract state from storage - let state = STATE.load(deps.storage)?; + // Get entry point contract from storage + let entry_point_contract = ENTRY_POINT_CONTRACT.load(deps.storage)?; // Enforce the caller is the entry point contract - if info.sender != state.entry_point_contract.address { + if info.sender != entry_point_contract.address { return Err(ContractError::Unauthorized); } + // Get pool code hash from storage + let shade_pool_code_hash = SHADE_POOL_CODE_HASH.load(deps.storage)?; + // Build shade router swap message let mut path = vec![]; for operation in &operations { path.push(shade_router::Hop { addr: operation.pool.to_string(), - code_hash: state.shade_pool_code_hash.clone(), + code_hash: shade_pool_code_hash.clone(), }); } @@ -203,13 +209,16 @@ fn execute_swap( None => return Err(ContractError::SwapOperationsEmpty), }; + // Get shade router contract from storage + let shade_router_contract = SHADE_ROUTER_CONTRACT.load(deps.storage)?; + // Create a response object to return Ok(Response::new() .add_attribute("action", "execute_swap") .add_attribute("action", "dispatch_swaps_and_transfer_back") // Swap router execution .add_message(snip20::send_msg( - state.shade_router_contract.address.to_string(), + shade_router_contract.address.to_string(), input_amount, Some(to_binary(&shade_router::InvokeMsg::SwapTokensForExact { path, @@ -219,7 +228,7 @@ fn execute_swap( None, None, 255, - state.shade_router_contract.code_hash, + shade_router_contract.code_hash, input_denom, )?) // TransferFundsBack message to self @@ -227,7 +236,7 @@ fn execute_swap( contract_addr: env.contract.address.to_string(), code_hash: env.contract.code_hash, msg: to_binary(&ExecuteMsg::TransferFundsBack { - swapper: state.entry_point_contract.address, + swapper: entry_point_contract.address, return_denom, })?, funds: vec![], @@ -241,7 +250,7 @@ fn register_tokens( ) -> ContractResult { let mut response = Response::new(); - let state = STATE.load(deps.storage)?; + let viewing_key = VIEWING_KEY.load(deps.storage)?; for contract in contracts.iter() { // Add to storage for later use of code hash @@ -251,7 +260,7 @@ fn register_tokens( .add_attribute("register_token", contract.address.clone()) .add_messages(vec![ snip20::set_viewing_key_msg( - state.viewing_key.clone(), + viewing_key.clone(), None, 255, contract.code_hash.clone(), @@ -282,12 +291,6 @@ pub fn execute_transfer_funds_back( return Err(ContractError::Unauthorized); } - // load state from storage - let state = match STATE.load(deps.storage) { - Ok(state) => state, - Err(e) => return Err(ContractError::Std(e)), - }; - // Validate return_denom let return_denom = match deps.api.addr_validate(&return_denom) { Ok(addr) => addr, @@ -300,10 +303,12 @@ pub fn execute_transfer_funds_back( Err(_) => return Err(ContractError::InvalidSnip20Coin), }; + let viewing_key = VIEWING_KEY.load(deps.storage)?; + let balance = match snip20::balance_query( deps.querier, env.contract.address.to_string(), - state.viewing_key, + viewing_key, 255, token_contract.code_hash.clone(), token_contract.address.to_string(), @@ -312,8 +317,10 @@ pub fn execute_transfer_funds_back( Err(e) => return Err(ContractError::Std(e)), }; + let entry_point_contract = ENTRY_POINT_CONTRACT.load(deps.storage)?; + let transfer_msg = match snip20::send_msg( - state.entry_point_contract.address.to_string(), + entry_point_contract.address.to_string(), balance.amount, None, None, @@ -346,7 +353,6 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> ContractResult { asset_in, swap_operations, )?), - _ => unimplemented!(), } .map_err(From::from) } @@ -380,7 +386,9 @@ fn simulate_swap_exact_asset_in( swap_operations: Vec, ) -> ContractResult { // Load state from storage - let state = STATE.load(deps.storage)?; + let shade_pool_code_hash = SHADE_POOL_CODE_HASH.load(deps.storage)?; + let shade_router_contract = SHADE_ROUTER_CONTRACT.load(deps.storage)?; + // Get contract data for asset_in let asset_in_contract = REGISTERED_TOKENS.load(deps.storage, deps.api.addr_validate(asset_in.denom())?)?; @@ -394,13 +402,13 @@ fn simulate_swap_exact_asset_in( for operation in swap_operations.iter() { path.push(shade_router::Hop { addr: operation.pool.to_string(), - code_hash: state.shade_pool_code_hash.clone(), + code_hash: shade_pool_code_hash.clone(), }); } let sim_response: shade_router::QueryMsgResponse = deps.querier.query_wasm_smart( - &state.shade_router_contract.address, - &state.shade_router_contract.code_hash, + &shade_router_contract.address, + &shade_router_contract.code_hash, &shade_router::QueryMsg::SwapSimulation { offer: shade_router::TokenAmount { token: shade_router::TokenType::CustomToken { diff --git a/contracts/adapters/swap/shade-protocol/src/msg.rs b/contracts/adapters/swap/shade-protocol/src/msg.rs index b842a393..e9d9b9d0 100644 --- a/contracts/adapters/swap/shade-protocol/src/msg.rs +++ b/contracts/adapters/swap/shade-protocol/src/msg.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Addr, Binary, ContractInfo, Uint128}; +use cosmwasm_std::{Addr, ContractInfo}; use secret_skip::{ asset::{Asset, Snip20ReceiveMsg}, swap::SwapOperation, diff --git a/contracts/adapters/swap/shade-protocol/src/shade_swap_router_msg.rs b/contracts/adapters/swap/shade-protocol/src/shade_swap_router_msg.rs index 5e9a8d15..b0efe845 100644 --- a/contracts/adapters/swap/shade-protocol/src/shade_swap_router_msg.rs +++ b/contracts/adapters/swap/shade-protocol/src/shade_swap_router_msg.rs @@ -1,8 +1,5 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{ - Addr, Binary, ContractInfo, Decimal, Decimal256, Deps, DepsMut, Env, MessageInfo, Response, - Uint128, Uint256, WasmMsg, -}; +use cosmwasm_std::{Addr, Uint128}; use schemars::JsonSchema; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -38,11 +35,6 @@ pub struct StableTokenType { #[derive(Clone, Debug, JsonSchema)] pub struct TokenPair(pub TokenType, pub TokenType, pub bool); -pub struct TokenPairIterator<'a> { - pair: &'a TokenPair, - index: u8, -} - impl Serialize for TokenPair { fn serialize(&self, serializer: S) -> Result where diff --git a/contracts/adapters/swap/shade-protocol/src/state.rs b/contracts/adapters/swap/shade-protocol/src/state.rs index 2addcc98..f968ae87 100644 --- a/contracts/adapters/swap/shade-protocol/src/state.rs +++ b/contracts/adapters/swap/shade-protocol/src/state.rs @@ -1,8 +1,10 @@ -use crate::contract::State; use cosmwasm_std::{Addr, ContractInfo}; use secret_storage_plus::Map; use secret_toolkit::storage::Item; -pub const STATE: Item = Item::new(b"state"); +pub const ENTRY_POINT_CONTRACT: Item = Item::new(b"entry_point_contract"); +pub const SHADE_ROUTER_CONTRACT: Item = Item::new(b"shade_router_contract"); +pub const SHADE_POOL_CODE_HASH: Item = Item::new(b"shade_pool_code_hash"); +pub const VIEWING_KEY: Item = Item::new(b"viewing_key"); pub const REGISTERED_TOKENS: Map = Map::new("registered_tokens"); diff --git a/contracts/secret-entry-point/src/contract.rs b/contracts/secret-entry-point/src/contract.rs index bc51c606..23970a67 100644 --- a/contracts/secret-entry-point/src/contract.rs +++ b/contracts/secret-entry-point/src/contract.rs @@ -18,7 +18,6 @@ use cosmwasm_std::{ StdResult, }; use secret_toolkit::snip20; -// use cw2::set_contract_version; /////////////// /// MIGRATE /// @@ -34,8 +33,10 @@ pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> ContractResult, timeout_timestamp: u64, action: Action, @@ -815,10 +814,9 @@ fn validate_and_dispatch_action( // Error if the destination address is not a valid address on the current chain deps.api.addr_validate(&to_address)?; - // Create the transfer message - // let transfer_msg = action_asset.transfer(&to_address); let action_asset_contract = REGISTERED_TOKENS .load(deps.storage, deps.api.addr_validate(action_asset.denom())?)?; + // Create the transfer message let transfer_msg = match snip20::transfer_msg( to_address.to_string(), action_asset.amount(), @@ -842,40 +840,24 @@ fn validate_and_dispatch_action( deps.api.addr_validate(&ibc_info.recover_address)?; let transfer_out_contract = match action_asset { - Asset::Native(coin) => { + Asset::Native(_) => { return Err(ContractError::NativeCoinNotSupported); } _ => REGISTERED_TOKENS .load(deps.storage, deps.api.addr_validate(action_asset.denom())?)?, }; - // Create the IBC transfer message - // TODO send ICS20 message - /* - let ibc_transfer_msg: IbcTransferExecuteMsg = IbcTransfer { - info: ibc_info, - coin: action_asset.clone(), - timeout_timestamp, - } - .into(); - */ - // Get the IBC transfer adapter contract address let ibc_transfer_contract = IBC_TRANSFER_CONTRACT_ADDRESS.load(deps.storage)?; // Send the IBC transfer by calling the IBC transfer contract - /* - let ibc_transfer_msg = WasmMsg::Execute { - contract_addr: ibc_transfer_contract.address.to_string(), - code_hash: ibc_transfer_contract.code_hash.clone(), - msg: to_binary(&ibc_transfer_msg)?, - funds: vec![], - };*/ - let ibc_transfer_msg = match snip20::send_msg( ibc_transfer_contract.address.to_string(), action_asset.amount(), - None, //Some(to_binary(&fee_swap_msg_args)?), + Some(to_binary(&ibc::Snip20HookMsg::IbcTransfer { + info: ibc_info, + timeout_timestamp, + })?), None, None, 255, diff --git a/packages/secret-skip/src/ibc.rs b/packages/secret-skip/src/ibc.rs index 84cec882..e8a23a9d 100644 --- a/packages/secret-skip/src/ibc.rs +++ b/packages/secret-skip/src/ibc.rs @@ -1,9 +1,9 @@ -use crate::{asset::Snip20ReceiveMsg, error::SkipError, proto_coin::ProtoCoin}; +use crate::{asset::Snip20ReceiveMsg, error::SkipError}; use std::convert::From; use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Addr, Binary, Coin, ContractInfo, StdError, Uint128}; +use cosmwasm_std::{Coin, ContractInfo, StdError}; // use neutron_proto::neutron::feerefunder::Fee as NeutronFee; /////////////// @@ -48,7 +48,7 @@ pub enum ExecuteMsg { #[cw_serde] pub enum Snip20HookMsg { IbcTransfer { - ibc_info: IbcInfo, + info: IbcInfo, timeout_timestamp: u64, }, } diff --git a/packages/secret-skip/src/swap.rs b/packages/secret-skip/src/swap.rs index f7ebf17a..83763676 100644 --- a/packages/secret-skip/src/swap.rs +++ b/packages/secret-skip/src/swap.rs @@ -1,20 +1,12 @@ -use crate::{asset::Asset, error::SkipError}; +use crate::{ + asset::{Asset, Snip20ReceiveMsg}, + error::SkipError, +}; use std::{convert::TryFrom, num::ParseIntError}; -// use astroport::{asset::AssetInfo, router::SwapOperation as AstroportSwapOperation}; use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{ - Addr, BankMsg, Binary, ContractInfo, CosmosMsg, Decimal, DepsMut, Env, MessageInfo, Response, - Uint128, -}; -use cw20::Cw20Contract; -use cw20::Cw20ReceiveMsg; -/* -use osmosis_std::types::osmosis::poolmanager::v1beta1::{ - SwapAmountInRoute as OsmosisSwapAmountInRoute, SwapAmountOutRoute as OsmosisSwapAmountOutRoute, -}; -*/ +use cosmwasm_std::{Addr, Binary, ContractInfo, Decimal, Uint128}; /////////////// /// MIGRATE /// @@ -45,7 +37,7 @@ pub struct InstantiateMsg { // Only the Swap message is callable by external users. #[cw_serde] pub enum ExecuteMsg { - Receive(Cw20ReceiveMsg), + Receive(Snip20ReceiveMsg), Swap { operations: Vec }, TransferFundsBack { swapper: Addr, return_denom: String }, } From 4f2b852064da20ac8d6b1e23c841eaab5219924b Mon Sep 17 00:00:00 2001 From: DrPresident Date: Tue, 17 Dec 2024 15:47:00 -0600 Subject: [PATCH 15/29] execute receive test passing --- .../swap/shade-protocol/src/contract.rs | 24 +- .../adapters/swap/shade-protocol/src/msg.rs | 7 +- .../tests/test_execute_receive.rs | 222 ++++++++++++++++++ packages/secret-skip/src/asset.rs | 12 +- packages/secret-skip/src/ibc.rs | 2 +- packages/secret-skip/src/lib.rs | 1 + packages/secret-skip/src/snip20.rs | 33 +++ packages/secret-skip/src/swap.rs | 5 +- 8 files changed, 275 insertions(+), 31 deletions(-) create mode 100644 contracts/adapters/swap/shade-protocol/tests/test_execute_receive.rs create mode 100644 packages/secret-skip/src/snip20.rs diff --git a/contracts/adapters/swap/shade-protocol/src/contract.rs b/contracts/adapters/swap/shade-protocol/src/contract.rs index 5e1e373c..806ebb41 100644 --- a/contracts/adapters/swap/shade-protocol/src/contract.rs +++ b/contracts/adapters/swap/shade-protocol/src/contract.rs @@ -9,16 +9,13 @@ use cosmwasm_std::{ entry_point, from_binary, to_binary, Addr, Binary, ContractInfo, Deps, DepsMut, Env, MessageInfo, Response, Uint128, WasmMsg, }; -use secret_skip::{ - asset::{Asset, Snip20ReceiveMsg}, - swap::SwapOperation, -}; +use secret_skip::{asset::Asset, snip20::Snip20ReceiveMsg, swap::SwapOperation}; // use cw2::set_contract_version; use cw20::Cw20Coin; use secret_toolkit::snip20; use crate::{ - msg::{Cw20HookMsg, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}, + msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, Snip20HookMsg}, shade_swap_router_msg as shade_router, }; @@ -135,7 +132,7 @@ pub fn receive_snip20( match snip20_msg.msg { Some(msg) => match from_binary(&msg)? { - Cw20HookMsg::Swap { operations } => { + Snip20HookMsg::Swap { operations } => { execute_swap(deps, env, info, operations, snip20_msg.amount) } }, @@ -209,16 +206,23 @@ fn execute_swap( None => return Err(ContractError::SwapOperationsEmpty), }; + let input_denom_contract = REGISTERED_TOKENS.load( + deps.storage, + deps.api.addr_validate(&input_denom.to_string())?, + )?; + // Get shade router contract from storage let shade_router_contract = SHADE_ROUTER_CONTRACT.load(deps.storage)?; + println!("PATH {:?}", path); // Create a response object to return Ok(Response::new() .add_attribute("action", "execute_swap") .add_attribute("action", "dispatch_swaps_and_transfer_back") // Swap router execution - .add_message(snip20::send_msg( + .add_message(snip20::send_msg_with_code_hash( shade_router_contract.address.to_string(), + Some(shade_router_contract.code_hash), input_amount, Some(to_binary(&shade_router::InvokeMsg::SwapTokensForExact { path, @@ -227,9 +231,9 @@ fn execute_swap( })?), None, None, - 255, - shade_router_contract.code_hash, - input_denom, + 0, + input_denom_contract.code_hash, + input_denom_contract.address.to_string(), )?) // TransferFundsBack message to self .add_message(WasmMsg::Execute { diff --git a/contracts/adapters/swap/shade-protocol/src/msg.rs b/contracts/adapters/swap/shade-protocol/src/msg.rs index e9d9b9d0..9e8931cf 100644 --- a/contracts/adapters/swap/shade-protocol/src/msg.rs +++ b/contracts/adapters/swap/shade-protocol/src/msg.rs @@ -1,9 +1,6 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{Addr, ContractInfo}; -use secret_skip::{ - asset::{Asset, Snip20ReceiveMsg}, - swap::SwapOperation, -}; +use secret_skip::{asset::Asset, snip20::Snip20ReceiveMsg, swap::SwapOperation}; #[cw_serde] pub struct InstantiateMsg { @@ -40,7 +37,7 @@ pub enum QueryMsg { } #[cw_serde] -pub enum Cw20HookMsg { +pub enum Snip20HookMsg { Swap { operations: Vec }, } diff --git a/contracts/adapters/swap/shade-protocol/tests/test_execute_receive.rs b/contracts/adapters/swap/shade-protocol/tests/test_execute_receive.rs new file mode 100644 index 00000000..7fabf4d9 --- /dev/null +++ b/contracts/adapters/swap/shade-protocol/tests/test_execute_receive.rs @@ -0,0 +1,222 @@ +use core::panic; +use cosmwasm_std::{ + testing::{mock_dependencies, mock_env, mock_info}, + to_binary, Addr, Coin, ContractInfo, ContractResult as SystemContractResult, QuerierResult, + ReplyOn::Never, + SubMsg, SystemResult, Uint128, WasmMsg, WasmQuery, +}; +use cw20::{BalanceResponse, Cw20Coin}; +use secret_skip::{asset::Asset, snip20, swap::SwapOperation}; +use skip_go_swap_adapter_shade_protocol::{ + error::{ContractError, ContractResult}, + msg::{ExecuteMsg, Snip20HookMsg}, + shade_swap_router_msg as shade_router, + state::{ENTRY_POINT_CONTRACT, REGISTERED_TOKENS, SHADE_POOL_CODE_HASH, SHADE_ROUTER_CONTRACT}, +}; +use test_case::test_case; + +/* +Test Cases: + +Expect Success + - One Swap Operation - Cw20 In + - One Swap Operation - Cw20 In And Out + +Expect Error + - Coin sent with cw20 + + */ + +// Define test parameters +struct Params { + caller: String, + info_funds: Vec, + sent_asset: Asset, + swap_operations: Vec, + expected_messages: Vec, + expected_error: Option, +} + +// Test execute_swap +#[test_case( + Params { + caller: "entry_point".to_string(), + info_funds: vec![], + sent_asset: Asset::Cw20(Cw20Coin { + address: "secret123".to_string(), + amount: 100u128.into(), + }), + swap_operations: vec![ + SwapOperation { + pool: "pool_1".to_string(), + denom_in: "secret123".to_string(), + denom_out: "ua".to_string(), + interface: None, + } + ], + expected_messages: vec![ + + SubMsg { + id: 0, + gas_limit: None, + reply_on: Never, + msg: WasmMsg::Execute { + contract_addr: "secret123".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&snip20::ExecuteMsg::Send { + amount: Uint128::from(100u128), + recipient: "shade_router".to_string(), + recipient_code_hash: Some("code_hash".to_string()), + memo: None, + padding: None, + msg: Some(to_binary(&shade_router::InvokeMsg::SwapTokensForExact { + path: vec![shade_router::Hop { + addr: "pool_1".to_string(), + code_hash: "code_hash".to_string(), + }], + expected_return: None, + recipient: None, + })?), + })?, + funds: vec![], + }.into(), + }, + SubMsg { + id: 0, + gas_limit: None, + reply_on: Never, + msg: WasmMsg::Execute { + contract_addr: "swap_contract_address".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&ExecuteMsg::TransferFundsBack { + swapper: Addr::unchecked("entry_point"), + return_denom: "ua".to_string(), + })?, + funds: vec![], + }.into(), + }, + ], + expected_error: None, + }; + "One Swap Operation - Snip20 In & Out")] +#[test_case( + Params { + caller: "entry_point".to_string(), + info_funds: vec![ + Coin::new(100, "un"), + ], + sent_asset: Asset::Cw20(Cw20Coin { + address: "secret123".to_string(), + amount: 100u128.into() + }), + swap_operations: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::SwapOperationsEmpty), + }; + "Coin sent with cw20 - Expect Error")] +fn test_execute_swap(params: Params) -> ContractResult<()> { + // Create mock dependencies + let mut deps = mock_dependencies(); + + // Create mock wasm handler to handle the swap adapter contract query + let wasm_handler = |query: &WasmQuery| -> QuerierResult { + match query { + WasmQuery::Smart { contract_addr, .. } => { + if contract_addr == "secret123" { + SystemResult::Ok(SystemContractResult::Ok( + to_binary(&BalanceResponse { + balance: 100u128.into(), + }) + .unwrap(), + )) + } else { + panic!("Unsupported contract: {:?}", query); + } + } + _ => panic!("Unsupported query: {:?}", query), + } + }; + + // Update querier with mock wasm handler + deps.querier.update_wasm(wasm_handler); + + // Create mock env + let mut env = mock_env(); + env.contract.address = Addr::unchecked("swap_contract_address"); + env.contract.code_hash = "code_hash".to_string(); + + // Convert info funds vector into a slice of Coin objects + let info_funds: &[Coin] = ¶ms.info_funds; + + // Create mock info with entry point contract address + let info = mock_info(params.sent_asset.denom(), info_funds); + + // Store the entry point contract address + ENTRY_POINT_CONTRACT.save( + deps.as_mut().storage, + &ContractInfo { + address: Addr::unchecked("entry_point"), + code_hash: "code_hash".to_string(), + }, + )?; + SHADE_ROUTER_CONTRACT.save( + deps.as_mut().storage, + &ContractInfo { + address: Addr::unchecked("shade_router"), + code_hash: "code_hash".to_string(), + }, + )?; + SHADE_POOL_CODE_HASH.save(deps.as_mut().storage, &"code_hash".to_string())?; + REGISTERED_TOKENS.save( + deps.as_mut().storage, + Addr::unchecked("secret123"), + &ContractInfo { + address: Addr::unchecked("secret123"), + code_hash: "code_hash".to_string(), + }, + )?; + + // Call execute_swap with the given test parameters + let res = skip_go_swap_adapter_shade_protocol::contract::execute( + deps.as_mut(), + env, + info, + ExecuteMsg::Receive(snip20::Snip20ReceiveMsg { + sender: Addr::unchecked(params.caller), + amount: params.sent_asset.amount(), + from: Addr::unchecked("entry_point".to_string()), + memo: None, + msg: Some(to_binary(&Snip20HookMsg::Swap { + operations: params.swap_operations, + })?), + }), + ); + + // Assert the behavior is correct + match res { + Ok(res) => { + // Assert the test did not expect an error + assert!( + params.expected_error.is_none(), + "expected test to error with {:?}, but it succeeded", + params.expected_error + ); + + // Assert the messages are correct + assert_eq!(res.messages, params.expected_messages); + } + Err(err) => { + // Assert the test expected an error + assert!( + params.expected_error.is_some(), + "expected test to succeed, but it errored with {:?}", + err + ); + + // Assert the error is correct + assert_eq!(err, params.expected_error.unwrap()); + } + } + + Ok(()) +} diff --git a/packages/secret-skip/src/asset.rs b/packages/secret-skip/src/asset.rs index bc2edfb3..bf675e2f 100644 --- a/packages/secret-skip/src/asset.rs +++ b/packages/secret-skip/src/asset.rs @@ -1,18 +1,8 @@ use crate::error::SkipError; -// use astroport::asset::{Asset as AstroportAsset, AssetInfo}; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, Api, Binary, Coin, OverflowError, OverflowOperation, Uint128}; +use cosmwasm_std::{Api, Coin, OverflowError, OverflowOperation, Uint128}; use cw20::{Cw20Coin, Cw20CoinVerified}; -#[cw_serde] -pub struct Snip20ReceiveMsg { - pub sender: Addr, - pub from: Addr, - pub amount: Uint128, - pub memo: Option, - pub msg: Option, -} - #[cw_serde] pub enum Asset { Native(Coin), diff --git a/packages/secret-skip/src/ibc.rs b/packages/secret-skip/src/ibc.rs index e8a23a9d..7ae70215 100644 --- a/packages/secret-skip/src/ibc.rs +++ b/packages/secret-skip/src/ibc.rs @@ -1,4 +1,4 @@ -use crate::{asset::Snip20ReceiveMsg, error::SkipError}; +use crate::{error::SkipError, snip20::Snip20ReceiveMsg}; use std::convert::From; diff --git a/packages/secret-skip/src/lib.rs b/packages/secret-skip/src/lib.rs index 5f1d7195..673c1826 100644 --- a/packages/secret-skip/src/lib.rs +++ b/packages/secret-skip/src/lib.rs @@ -4,5 +4,6 @@ pub mod error; pub mod hyperlane; pub mod ibc; pub mod proto_coin; +pub mod snip20; pub mod sudo; pub mod swap; diff --git a/packages/secret-skip/src/snip20.rs b/packages/secret-skip/src/snip20.rs new file mode 100644 index 00000000..fdce01e6 --- /dev/null +++ b/packages/secret-skip/src/snip20.rs @@ -0,0 +1,33 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, Binary, Uint128}; + +#[cw_serde] +pub struct Snip20ReceiveMsg { + pub sender: Addr, + pub from: Addr, + pub amount: Uint128, + pub memo: Option, + pub msg: Option, +} + +#[cw_serde] +pub enum ExecuteMsg { + Transfer { + recipient: String, + amount: Uint128, + memo: Option, + //decoys: Option>, + // entropy: Option, + padding: Option, + }, + Send { + recipient: String, + recipient_code_hash: Option, + amount: Uint128, + msg: Option, + memo: Option, + //decoys: Option>, + // entropy: Option, + padding: Option, + }, +} diff --git a/packages/secret-skip/src/swap.rs b/packages/secret-skip/src/swap.rs index 83763676..38ce37e0 100644 --- a/packages/secret-skip/src/swap.rs +++ b/packages/secret-skip/src/swap.rs @@ -1,7 +1,4 @@ -use crate::{ - asset::{Asset, Snip20ReceiveMsg}, - error::SkipError, -}; +use crate::{asset::Asset, error::SkipError, snip20::Snip20ReceiveMsg}; use std::{convert::TryFrom, num::ParseIntError}; From c0e5c01e2bc23a7caa2cc6658cd58d9548c4de25 Mon Sep 17 00:00:00 2001 From: DrPresident Date: Tue, 17 Dec 2024 15:48:31 -0600 Subject: [PATCH 16/29] cleaned cargo toml --- .../adapters/swap/shade-protocol/Cargo.toml | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/contracts/adapters/swap/shade-protocol/Cargo.toml b/contracts/adapters/swap/shade-protocol/Cargo.toml index 49d91601..6e589711 100644 --- a/contracts/adapters/swap/shade-protocol/Cargo.toml +++ b/contracts/adapters/swap/shade-protocol/Cargo.toml @@ -20,24 +20,18 @@ backtraces = ["cosmwasm-std/backtraces"] library = [] [dependencies] -#astroport = { workspace = true } -#cosmwasm-schema = { workspace = true } -#cosmwasm-std = { workspace = true } cw2 = { workspace = true } cw20 = { workspace = true } -#cw-storage-plus = { workspace = true } cw-utils = { workspace = true } -#skip = { workspace = true } thiserror = { workspace = true } +secret-skip = { workspace = true } -secret-skip = { workspace = true } - -cosmwasm-std = { package = "secret-cosmwasm-std", version = "1.1.11"} -cosmwasm-schema = "1.4.0" -secret-toolkit = { git = "https://github.com/scrtlabs/secret-toolkit", tag = "v0.10.0" } +cosmwasm-std = { package = "secret-cosmwasm-std", version = "1.1.11"} +cosmwasm-schema = "1.4.0" +secret-toolkit = { git = "https://github.com/scrtlabs/secret-toolkit", tag = "v0.10.0" } secret-storage-plus = { git = "https://github.com/securesecrets/secret-plus-utils", tag = "v0.1.1", features = [] } -serde = "1.0.114" -schemars = "0.8.1" +serde = "1.0.114" +schemars = "0.8.1" [dev-dependencies] test-case = { workspace = true } From 75b84f2fb1c2eafdc46888bbf05b82a89431b6fc Mon Sep 17 00:00:00 2001 From: DrPresident Date: Thu, 19 Dec 2024 12:03:56 -0600 Subject: [PATCH 17/29] execute receive test passing --- contracts/secret-entry-point/src/execute.rs | 7 +- contracts/secret-entry-point/src/msg.rs | 3 +- .../tests/test_execute_receive.rs | 377 ++++++++++++++++++ 3 files changed, 383 insertions(+), 4 deletions(-) create mode 100644 contracts/secret-entry-point/tests/test_execute_receive.rs diff --git a/contracts/secret-entry-point/src/execute.rs b/contracts/secret-entry-point/src/execute.rs index 7265092b..bb9b9cda 100644 --- a/contracts/secret-entry-point/src/execute.rs +++ b/contracts/secret-entry-point/src/execute.rs @@ -20,13 +20,14 @@ use cosmwasm_std::{ }; use cw20::Cw20Coin; use secret_skip::{ - asset::{Asset, Snip20ReceiveMsg}, + asset::Asset, error::SkipError, ibc::{self, IbcInfo}, + snip20::Snip20ReceiveMsg, swap::{validate_swap_operations, Swap, SwapExactAssetOut}, }; use skip_go_swap_adapter_shade_protocol::msg::{ - Cw20HookMsg as SwapHookMsg, QueryMsg as SwapQueryMsg, + QueryMsg as SwapQueryMsg, Snip20HookMsg as SwapHookMsg, }; ////////////////////////// @@ -180,7 +181,7 @@ pub fn execute_swap_and_action( deps.querier, env.contract.address.to_string(), viewing_key, - 255, + 0, min_asset_contract.code_hash.clone(), min_asset_contract.address.to_string(), ) { diff --git a/contracts/secret-entry-point/src/msg.rs b/contracts/secret-entry-point/src/msg.rs index e43dd668..ad817006 100644 --- a/contracts/secret-entry-point/src/msg.rs +++ b/contracts/secret-entry-point/src/msg.rs @@ -1,6 +1,7 @@ use secret_skip::{ - asset::{Asset, Snip20ReceiveMsg}, + asset::Asset, ibc::IbcInfo, + snip20::Snip20ReceiveMsg, swap::{Swap, SwapExactAssetOut}, }; diff --git a/contracts/secret-entry-point/tests/test_execute_receive.rs b/contracts/secret-entry-point/tests/test_execute_receive.rs new file mode 100644 index 00000000..526bb621 --- /dev/null +++ b/contracts/secret-entry-point/tests/test_execute_receive.rs @@ -0,0 +1,377 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{ + testing::{mock_dependencies_with_balances, mock_env, mock_info}, + to_binary, Addr, Coin, ContractInfo, ContractResult, QuerierResult, + ReplyOn::{Always, Never}, + StdError, SubMsg, SystemError, SystemResult, Timestamp, Uint128, WasmMsg, WasmQuery, +}; +use cw20::Cw20Coin; +use secret_skip::{ + asset::Asset, + snip20::Snip20ReceiveMsg, + swap::{Swap, SwapExactAssetIn, SwapOperation}, +}; +use secret_toolkit::snip20::{AuthenticatedQueryResponse, Balance}; +use skip_go_secret_entry_point::{ + error::ContractError, + msg::{Action, Affiliate, ExecuteMsg, Snip20HookMsg}, + reply::RECOVER_REPLY_ID, + state::{IBC_TRANSFER_CONTRACT_ADDRESS, REGISTERED_TOKENS, SWAP_VENUE_MAP, VIEWING_KEY}, +}; +use test_case::test_case; + +#[cw_serde] +pub enum Snip20Response { + Balance { amount: Uint128 }, +} + +/* +Test Cases: + +Expect Response + - Valid Swap And Action Msg + - Valid Swap And Action With Recover Msg + */ + +// Define test parameters +struct Params { + info_funds: Vec, + sent_asset: Asset, + user_swap: Swap, + min_asset: Asset, + timeout_timestamp: u64, + post_swap_action: Action, + affiliates: Vec, + recovery_addr: Option, + expected_messages: Vec, + expected_error: Option, +} + +// Test execute_receive +#[test_case( + Params { + info_funds: vec![], + sent_asset: Asset::Cw20(Cw20Coin { + address: "secret123".to_string(), + amount: 1_000_000u128.into(), + }), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "secret123".to_string(), + denom_out: "secret456".to_string(), + interface: None, + } + ], + } + ), + min_asset: Asset::Cw20(Cw20Coin { + address: "secret456".to_string(), + amount: 1000u128.into(), + }), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + recovery_addr: None, + expected_messages: vec![ + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&ExecuteMsg::UserSwap { + swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "secret123".to_string(), + denom_out: "secret456".to_string(), + interface: None, + } + ], + } + ), + remaining_asset: Asset::Cw20(Cw20Coin { + address: "secret123".to_string(), + amount: 1_000_000u128.into(), + }), + min_asset: Asset::Cw20(Cw20Coin { + address: "secret456".to_string(), + amount: 1000u128.into(), + }), + affiliates: vec![], + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&ExecuteMsg::PostSwapAction { + min_asset: Asset::Cw20(Cw20Coin { + address: "secret456".to_string(), + amount: 1000u128.into(), + }), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + exact_out: false, + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "Valid Swap And Action Msg")] +/* +#[test_case( + Params { + info_funds: vec![], + sent_asset: Asset::Cw20(Cw20Coin{address: "secret123".to_string(), amount: Uint128::from(1_000_000u128)}), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "secret123".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + } + ), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + recovery_addr: Some(Addr::unchecked("recovery_addr")), + expected_messages: vec![ + SubMsg { + id: RECOVER_REPLY_ID, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_binary(&ExecuteMsg::SwapAndAction { + sent_asset: Some(Asset::Cw20(Cw20Coin{address: "secret123".to_string(), amount: Uint128::from(1_000_000u128)})), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "secret123".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + } + ), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![] }) + .unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Always, + }, + ], + expected_error: None, + }; + "Valid Swap And Action With Recover Msg")] +*/ +fn test_execute_receive(params: Params) { + // Create mock dependencies + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + // Create mock wasm handler to handle the swap adapter contract query + let wasm_handler = |query: &WasmQuery| -> QuerierResult { + match query { + WasmQuery::Smart { contract_addr, .. } => { + if contract_addr == "swap_venue_adapter" { + SystemResult::Ok(ContractResult::Ok( + to_binary(&Asset::Native(Coin::new(200_000, "osmo"))).unwrap(), + )) + } else if vec!["secret123", "secret456"].contains(&contract_addr.as_str()) { + SystemResult::Ok(ContractResult::Ok( + to_binary(&Snip20Response::Balance { + amount: 1_000_000u128.into(), + }) + .unwrap(), + )) + } else { + SystemResult::Err(SystemError::UnsupportedRequest { + kind: format!("query {}", contract_addr), + }) + } + } + _ => panic!("Unsupported query: {:?}", query), + } + }; + + // Update querier with mock wasm handler + deps.querier.update_wasm(wasm_handler); + + // Create mock env with parameters that make testing easier + let mut env = mock_env(); + env.contract.address = Addr::unchecked("entry_point"); + env.contract.code_hash = "code_hash".to_string(); + env.block.time = Timestamp::from_nanos(100); + + // Convert info funds vector into a slice of Coin objects + let info_funds: &[Coin] = ¶ms.info_funds; + + // Create mock info with entry point contract address + let info = mock_info("secret123", info_funds); + + // Store the swap venue adapter contract address + let swap_venue_adapter = Addr::unchecked("swap_venue_adapter"); + SWAP_VENUE_MAP + .save( + deps.as_mut().storage, + "swap_venue_name", + &ContractInfo { + address: swap_venue_adapter, + code_hash: "code_hash".to_string(), + }, + ) + .unwrap(); + + // Store the ibc transfer adapter contract address + let ibc_transfer_adapter = ContractInfo { + address: Addr::unchecked("ibc_transfer_adapter"), + code_hash: "code_hash".to_string(), + }; + IBC_TRANSFER_CONTRACT_ADDRESS + .save(deps.as_mut().storage, &ibc_transfer_adapter) + .unwrap(); + + REGISTERED_TOKENS + .save( + deps.as_mut().storage, + Addr::unchecked("secret123"), + &ContractInfo { + address: Addr::unchecked("secret123"), + code_hash: "code_hash".to_string(), + }, + ) + .unwrap(); + REGISTERED_TOKENS + .save( + deps.as_mut().storage, + Addr::unchecked(params.min_asset.clone().denom()), + &ContractInfo { + address: Addr::unchecked(params.min_asset.clone().denom()), + code_hash: "code_hash".to_string(), + }, + ) + .unwrap(); + + VIEWING_KEY + .save(deps.as_mut().storage, &"viewing_key".to_string()) + .unwrap(); + + // Call execute_receive with the given test case params + let res = match params.recovery_addr { + Some(recovery_addr) => skip_go_secret_entry_point::contract::execute( + deps.as_mut(), + env, + info, + ExecuteMsg::Receive(Snip20ReceiveMsg { + sender: Addr::unchecked("swapper".to_string()), + amount: params.sent_asset.amount(), + from: Addr::unchecked("swapper".to_string()), + memo: None, + msg: Some( + to_binary(&Snip20HookMsg::SwapAndActionWithRecover { + user_swap: params.user_swap, + min_asset: params.min_asset.clone(), + timeout_timestamp: params.timeout_timestamp, + post_swap_action: params.post_swap_action, + affiliates: params.affiliates, + recovery_addr, + }) + .unwrap(), + ), + }), + ), + None => skip_go_secret_entry_point::contract::execute( + deps.as_mut(), + env, + info, + ExecuteMsg::Receive(Snip20ReceiveMsg { + sender: Addr::unchecked("swapper".to_string()), + amount: params.sent_asset.amount(), + from: Addr::unchecked("swapper".to_string()), + memo: None, + msg: Some( + to_binary(&Snip20HookMsg::SwapAndAction { + user_swap: params.user_swap, + min_asset: params.min_asset, + timeout_timestamp: params.timeout_timestamp, + post_swap_action: params.post_swap_action, + affiliates: params.affiliates, + }) + .unwrap(), + ), + }), + ), + }; + + match res { + Ok(res) => { + // Assert the test did not expect an error + assert!( + params.expected_error.is_none(), + "expected test to error with {:?}, but it succeeded", + params.expected_error + ); + + // Assert the number of messages in the response is correct + assert_eq!( + res.messages.len(), + params.expected_messages.len(), + "expected {:?} messages, but got {:?}", + params.expected_messages.len(), + res.messages.len() + ); + + // Assert the messages in the response are correct + assert_eq!(res.messages, params.expected_messages,); + } + Err(err) => { + // Assert the test expected an error + assert!( + params.expected_error.is_some(), + "expected test to succeed, but it errored with {:?}", + err + ); + + // Assert the error is correct + assert_eq!(err, params.expected_error.unwrap()); + } + } +} From 50907c1beb9018fccc04d0c26f4c976ae347ccb7 Mon Sep 17 00:00:00 2001 From: DrPresident Date: Thu, 19 Dec 2024 13:36:22 -0600 Subject: [PATCH 18/29] 1 test passing for ibc hook --- .../ibc/secret-ibc-hooks/src/contract.rs | 26 +- .../tests/test_execute_ibc_transfer.rs | 332 ++++++++++++++++++ packages/secret-skip/src/ibc.rs | 16 +- 3 files changed, 356 insertions(+), 18 deletions(-) create mode 100644 contracts/adapters/ibc/secret-ibc-hooks/tests/test_execute_ibc_transfer.rs diff --git a/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs b/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs index fb40cff3..cefab02c 100644 --- a/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs +++ b/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs @@ -15,11 +15,12 @@ use cw20::Cw20Coin; use ibc_proto::ibc::applications::transfer::v1::MsgTransferResponse; use prost::Message; use secret_skip::{ - asset::{Asset, Snip20ReceiveMsg}, + asset::Asset, ibc::{ - AckID, ExecuteMsg, IbcInfo, IbcLifecycleComplete, InstantiateMsg, MigrateMsg, QueryMsg, - Snip20HookMsg, + AckID, ExecuteMsg, IbcInfo, IbcLifecycleComplete, Ics20TransferMsg, InstantiateMsg, + MigrateMsg, QueryMsg, Snip20HookMsg, }, + snip20::Snip20ReceiveMsg, sudo::{OsmosisSudoMsg as SudoMsg, SudoType}, }; use secret_toolkit::snip20; @@ -27,18 +28,6 @@ use secret_toolkit::snip20; // const IBC_MSG_TRANSFER_TYPE_URL: &str = "/ibc.applications.transfer.v1.MsgTransfer"; const REPLY_ID: u64 = 1; -#[cw_serde] -pub struct Ics20TransferMsg { - /// The local channel to send the packets on - pub channel: String, - /// The remote address to send to - /// Don't use HumanAddress as this will likely have a different Bech32 prefix than we use - /// and cannot be validated locally - pub remote_address: String, - /// How long the packet lives in seconds. If not specified, use default_timeout - pub timeout: Option, -} - /////////////// /// MIGRATE /// /////////////// @@ -152,7 +141,7 @@ pub fn execute( pub fn receive_snip20( deps: DepsMut, env: Env, - info: MessageInfo, + mut info: MessageInfo, snip20_msg: Snip20ReceiveMsg, ) -> ContractResult { let sent_asset = Asset::Cw20(Cw20Coin { @@ -160,6 +149,9 @@ pub fn receive_snip20( amount: snip20_msg.amount.u128().into(), }); + // Set the sender to the originating address that triggered the snip20 send call + // This is later validated / enforced to be the entry point contract address + info.sender = deps.api.addr_validate(&snip20_msg.sender.to_string())?; match snip20_msg.msg { Some(msg) => match from_binary(&msg)? { // Transfer tokens out over ICS20 @@ -211,7 +203,7 @@ fn execute_ics20_ibc_transfer( })?), None, None, - 255, + 0, sent_asset_contract.code_hash.clone(), sent_asset_contract.address.to_string(), ) { diff --git a/contracts/adapters/ibc/secret-ibc-hooks/tests/test_execute_ibc_transfer.rs b/contracts/adapters/ibc/secret-ibc-hooks/tests/test_execute_ibc_transfer.rs new file mode 100644 index 00000000..3a7467cd --- /dev/null +++ b/contracts/adapters/ibc/secret-ibc-hooks/tests/test_execute_ibc_transfer.rs @@ -0,0 +1,332 @@ +use cosmwasm_std::{ + to_binary, + WasmMsg, + testing::{mock_dependencies, mock_env, mock_info}, + Addr, Coin, ContractInfo, + ReplyOn::Success, + SubMsg, +}; +use ibc_proto::cosmos::base::v1beta1::Coin as IbcCoin; +use ibc_proto::ibc::applications::transfer::v1::MsgTransfer; +use prost::Message; +use secret_skip::{ + asset::Asset, + ibc::{ExecuteMsg, IbcFee, IbcInfo, Snip20HookMsg, + Ics20TransferMsg}, + snip20::{self, Snip20ReceiveMsg}, +}; +use cw20::Cw20Coin; +use skip_go_secret_ibc_adapter_ibc_hooks::{ + error::ContractResult, + state::{ + ENTRY_POINT_CONTRACT, ICS20_CONTRACT, IN_PROGRESS_CHANNEL_ID, + IN_PROGRESS_RECOVER_ADDRESS, REGISTERED_TOKENS, VIEWING_KEY, + }, +}; +use test_case::test_case; + +/* +Test Cases: + +Expect Response (Output Message Is Correct, In Progress Ibc Transfer Is Saved, No Error) + - Empty String Memo + - Override Already Set Ibc Callback Memo + - Add Ibc Callback Key/Value Pair To Other Key/Value In Memo + +Expect Error + - Unauthorized Caller (Only the stored entry point contract can call this function) + - Non Empty String, Invalid Json Memo + - Non Empty IBC Fees, IBC Fees Not Supported + + */ + +// Define test parameters +struct Params { + caller: String, + ibc_adapter_contract_address: Addr, + sent_asset: Asset, + ibc_info: IbcInfo, + timeout_timestamp: u64, + expected_messages: Vec, + expected_error_string: String, +} + +// Test execute_ibc_transfer +#[test_case( + Params { + caller: "entry_point".to_string(), + ibc_adapter_contract_address: Addr::unchecked("ibc_transfer".to_string()), + sent_asset: Asset::Cw20(Cw20Coin { + address: "secret123".to_string(), + amount: 100u128.into(), + }), + ibc_info: IbcInfo { + source_channel: "source_channel".to_string(), + receiver: "receiver".to_string(), + fee: None, + memo: "".to_string(), + recover_address: "recover_address".to_string(), + }, + timeout_timestamp: 100, + expected_messages: vec![SubMsg { + id: 1, + msg: WasmMsg::Execute { + contract_addr: "secret123".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&snip20::ExecuteMsg::Send { + amount: 100u128.into(), + recipient: "ics20".to_string(), + recipient_code_hash: Some("code_hash".to_string()), + memo: None, + padding: None, + msg: Some(to_binary(&Ics20TransferMsg { + channel: "source_channel".to_string(), + remote_address: "receiver".to_string(), + timeout: Some(100), + })?), + })?, + funds: vec![], + }.into(), + gas_limit: None, + reply_on: Success, + }], + expected_error_string: "".to_string(), + }; + "Empty String Memo")] + /* +#[test_case( + Params { + caller: "entry_point".to_string(), + ibc_adapter_contract_address: Addr::unchecked("ibc_transfer".to_string()), + coin: Coin::new(100, "osmo"), + ibc_info: IbcInfo { + source_channel: "source_channel".to_string(), + receiver: "receiver".to_string(), + fee: None, + memo: r#"{"ibc_callback":"random_address"}"#.to_string(), + recover_address: "recover_address".to_string(), + }, + timeout_timestamp: 100, + expected_messages: vec![SubMsg { + id: 1, + msg: cosmwasm_std::CosmosMsg::Stargate { + type_url: "/ibc.applications.transfer.v1.MsgTransfer".to_string(), + value: MsgTransfer { + source_port: "transfer".to_string(), + source_channel: "source_channel".to_string(), + token: Some(IbcCoin { + denom: "osmo".to_string(), + amount: "100".to_string(), + }), + sender: "ibc_transfer".to_string(), + receiver: "receiver".to_string(), + timeout_height: None, + timeout_timestamp: 100, + memo: r#"{"ibc_callback":"ibc_transfer"}"#.to_string(), + } + .encode_to_vec().into(), + }, + gas_limit: None, + reply_on: Success, + } + ], + expected_error_string: "".to_string(), + }; + "Override Already Set Ibc Callback Memo")] +#[test_case( + Params { + caller: "entry_point".to_string(), + ibc_adapter_contract_address: Addr::unchecked("ibc_transfer".to_string()), + coin: Coin::new(100, "osmo"), + ibc_info: IbcInfo { + source_channel: "source_channel".to_string(), + receiver: "receiver".to_string(), + fee: None, + memo: r#"{"pfm":"example_value","wasm":"example_contract"}"#.to_string(), + recover_address: "recover_address".to_string(), + }, + timeout_timestamp: 100, + expected_messages: vec![SubMsg { + id: 1, + msg: cosmwasm_std::CosmosMsg::Stargate { + type_url: "/ibc.applications.transfer.v1.MsgTransfer".to_string(), + value: MsgTransfer { + source_port: "transfer".to_string(), + source_channel: "source_channel".to_string(), + token: Some(IbcCoin { + denom: "osmo".to_string(), + amount: "100".to_string(), + }), + sender: "ibc_transfer".to_string(), + receiver: "receiver".to_string(), + timeout_height: None, + timeout_timestamp: 100, + memo: r#"{"ibc_callback":"ibc_transfer","pfm":"example_value","wasm":"example_contract"}"#.to_string(), + } + .encode_to_vec().into(), + }, + gas_limit: None, + reply_on: Success, + } + ], + expected_error_string: "".to_string(), + }; + "Add Ibc Callback Key/Value Pair To Other Key/Value In Memo")] +#[test_case( + Params { + caller: "entry_point".to_string(), + ibc_adapter_contract_address: Addr::unchecked("ibc_transfer".to_string()), + coin: Coin::new(100, "osmo"), + ibc_info: IbcInfo { + source_channel: "source_channel".to_string(), + receiver: "receiver".to_string(), + fee: None, + memo: "{invalid}".to_string(), + recover_address: "recover_address".to_string(), + }, + timeout_timestamp: 100, + expected_messages: vec![], + expected_error_string: "Object key is not a string.".to_string(), + }; + "Non Empty String, Invalid Json Memo - Expect Error")] +#[test_case( + Params { + caller: "entry_point".to_string(), + ibc_adapter_contract_address: Addr::unchecked("ibc_transfer".to_string()), + coin: Coin::new(100, "osmo"), + ibc_info: IbcInfo { + source_channel: "source_channel".to_string(), + receiver: "receiver".to_string(), + fee: Some(IbcFee { + recv_fee: vec![ + Coin::new(100, "atom"), + ], + ack_fee: vec![], + timeout_fee: vec![], + }), + memo: "{}".to_string(), + recover_address: "recover_address".to_string(), + }, + timeout_timestamp: 100, + expected_messages: vec![], + expected_error_string: "IBC fees are not supported, vectors must be empty".to_string(), + }; + "IBC Fees Not Supported - Expect Error")] +#[test_case( + Params { + caller: "random".to_string(), + ibc_adapter_contract_address: Addr::unchecked("ibc_transfer".to_string()), + coin: Coin::new(100, "osmo"), + ibc_info: IbcInfo { + source_channel: "source_channel".to_string(), + receiver: "receiver".to_string(), + fee: None, + memo: "{}".to_string(), + recover_address: "recover_address".to_string(), + }, + timeout_timestamp: 100, + expected_messages: vec![], + expected_error_string: "Unauthorized".to_string(), + }; + "Unauthorized Caller - Expect Error")] +*/ +fn test_execute_ibc_transfer(params: Params) -> ContractResult<()> { + // Create mock dependencies + let mut deps = mock_dependencies(); + + // Create mock env + let mut env = mock_env(); + env.contract.address = params.ibc_adapter_contract_address.clone(); + env.contract.code_hash = "code_hash".to_string(); + + + // Store the entry point contract address + ENTRY_POINT_CONTRACT.save(deps.as_mut().storage, &ContractInfo { + address: Addr::unchecked("entry_point"), + code_hash: "code_hash".to_string(), + })?; + + ICS20_CONTRACT.save( + deps.as_mut().storage, + &ContractInfo { + address: Addr::unchecked("ics20"), + code_hash: "code_hash".to_string(), + }, + )?; + REGISTERED_TOKENS.save( + deps.as_mut().storage, + Addr::unchecked("secret123"), + &ContractInfo { + address: Addr::unchecked("secret123"), + code_hash: "code_hash".to_string(), + }, + )?; + VIEWING_KEY.save(deps.as_mut().storage, &"viewing_key".to_string())?; + + // Call execute_ibc_transfer with the given test parameters + let res = skip_go_secret_ibc_adapter_ibc_hooks::contract::execute( + deps.as_mut(), + env, + mock_info(&"secret123", &[]), + ExecuteMsg::Receive(Snip20ReceiveMsg { + sender: Addr::unchecked(params.caller.clone()), + amount: params.sent_asset.amount(), + from: Addr::unchecked(params.caller), + memo: None, + msg: Some( + to_binary(&Snip20HookMsg::IbcTransfer { + info: params.ibc_info.clone(), + timeout_timestamp: params.timeout_timestamp, + }) + .unwrap(), + ), + }), + ); + + // Assert the behavior is correct + match res { + Ok(res) => { + // Assert the test did not expect an error + assert!( + params.expected_error_string.is_empty(), + "expected test to error with {:?}, but it succeeded", + params.expected_error_string + ); + + // Assert the messages in the response are correct + assert_eq!(res.messages, params.expected_messages); + + // Load the in progress recover address from state and verify it is correct + let stored_in_progress_recover_address = + IN_PROGRESS_RECOVER_ADDRESS.load(&deps.storage)?; + + // Assert the in progress recover address is correct + assert_eq!( + stored_in_progress_recover_address, + params.ibc_info.recover_address + ); + + // Load the in progress channel id from state and verify it is correct + let stored_in_progress_channel_id = IN_PROGRESS_CHANNEL_ID.load(&deps.storage)?; + + // Assert the in progress channel id is correct + assert_eq!( + stored_in_progress_channel_id, + params.ibc_info.source_channel + ); + } + Err(err) => { + // Assert the test expected an error + assert!( + !params.expected_error_string.is_empty(), + "expected test to succeed, but it errored with {:?}", + err + ); + + // Assert the error is correct + assert_eq!(err.to_string(), params.expected_error_string); + } + } + + Ok(()) +} diff --git a/packages/secret-skip/src/ibc.rs b/packages/secret-skip/src/ibc.rs index 7ae70215..0828d5f6 100644 --- a/packages/secret-skip/src/ibc.rs +++ b/packages/secret-skip/src/ibc.rs @@ -4,7 +4,21 @@ use std::convert::From; use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{Coin, ContractInfo, StdError}; -// use neutron_proto::neutron::feerefunder::Fee as NeutronFee; + +// Used with Secret network ICS20 contract +// for auto wrapping & unrapping of snip20 tokens +// for IBC transfers +#[cw_serde] +pub struct Ics20TransferMsg { + /// The local channel to send the packets on + pub channel: String, + /// The remote address to send to + /// Don't use HumanAddress as this will likely have a different Bech32 prefix than we use + /// and cannot be validated locally + pub remote_address: String, + /// How long the packet lives in seconds. If not specified, use default_timeout + pub timeout: Option, +} /////////////// /// MIGRATE /// From ee7c8b6c6ffd7e57bcf22472471f305fbcbde06e Mon Sep 17 00:00:00 2001 From: DrPresident Date: Fri, 20 Dec 2024 11:17:16 -0600 Subject: [PATCH 19/29] ibc hook tests passing --- .../ibc/secret-ibc-hooks/src/contract.rs | 19 +- .../tests/test_execute_ibc_transfer.rs | 100 ++++--- .../ibc/secret-ibc-hooks/tests/test_reply.rs | 258 ++++++++++++++++++ .../ibc/secret-ibc-hooks/tests/test_sudo.rs | 205 ++++++++++++++ 4 files changed, 529 insertions(+), 53 deletions(-) create mode 100644 contracts/adapters/ibc/secret-ibc-hooks/tests/test_reply.rs create mode 100644 contracts/adapters/ibc/secret-ibc-hooks/tests/test_sudo.rs diff --git a/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs b/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs index cefab02c..e7052a84 100644 --- a/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs +++ b/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs @@ -5,12 +5,12 @@ use crate::{ IN_PROGRESS_RECOVER_ADDRESS, REGISTERED_TOKENS, VIEWING_KEY, }, }; -use cosmwasm_schema::cw_serde; use cosmwasm_std::{ entry_point, from_binary, to_binary, BankMsg, Binary, ContractInfo, Deps, DepsMut, Env, MessageInfo, Reply, Response, SubMsg, SubMsgResult, }; use cw20::Cw20Coin; +use serde_cw_value::Value; // use cw2::set_contract_version; use ibc_proto::ibc::applications::transfer::v1::MsgTransferResponse; use prost::Message; @@ -168,7 +168,7 @@ pub fn receive_snip20( fn execute_ics20_ibc_transfer( deps: DepsMut, - _env: Env, + env: Env, info: MessageInfo, ibc_info: IbcInfo, sent_asset: Asset, @@ -192,6 +192,10 @@ fn execute_ics20_ibc_transfer( let sent_asset_contract = REGISTERED_TOKENS.load(deps.storage, deps.api.addr_validate(sent_asset.denom())?)?; + // Verify memo is valid json and add the necessary key/value pair to trigger the ibc hooks callback logic. + let memo = verify_and_create_memo(ibc_info.memo, env.contract.address.to_string())?; + + println!("memo {}", memo); let ibc_transfer_msg = match snip20::send_msg_with_code_hash( ics20_contract.address.to_string(), Some(ics20_contract.code_hash), @@ -201,7 +205,7 @@ fn execute_ics20_ibc_transfer( remote_address: ibc_info.receiver, timeout: Some(timeout_timestamp), })?), - None, + Some(memo), None, 0, sent_asset_contract.code_hash.clone(), @@ -220,10 +224,7 @@ fn execute_ics20_ibc_transfer( // Save in progress channel id to storage, to be used in sudo handler IN_PROGRESS_CHANNEL_ID.save(deps.storage, &ibc_info.source_channel)?; - // Verify memo is valid json and add the necessary key/value pair to trigger the ibc hooks callback logic. - // let memo = verify_and_create_memo(ibc_info.memo, env.contract.address.to_string())?; - - // Create sub message from osmosis ibc transfer message to receive a reply + // Create sub message from ICS20 send message to receive a reply let sub_msg = SubMsg::reply_on_success(ibc_transfer_msg, REPLY_ID); Ok(Response::new() @@ -331,6 +332,7 @@ pub fn reply(deps: DepsMut, _env: Env, reply: Reply) -> ContractResult // Get and delete the in progress recover address from storage let in_progress_recover_address = IN_PROGRESS_RECOVER_ADDRESS.load(deps.storage)?; IN_PROGRESS_RECOVER_ADDRESS.remove(deps.storage); + println!("IN PROG {}", in_progress_recover_address); // Get and delete the in progress channel id from storage let in_progress_channel_id = IN_PROGRESS_CHANNEL_ID.load(deps.storage)?; @@ -338,6 +340,7 @@ pub fn reply(deps: DepsMut, _env: Env, reply: Reply) -> ContractResult // Set ack_id to be the channel id and sequence id from the response as a tuple let ack_id: AckID = (&in_progress_channel_id, resp.sequence); + println!("ACKID {:?}", ack_id); // Error if unique ack_id (channel id, sequence id) already exists in storage if ACK_ID_TO_RECOVER_ADDRESS.has(deps.storage, ack_id) { @@ -415,7 +418,6 @@ pub fn sudo(deps: DepsMut, env: Env, msg: SudoMsg) -> ContractResult { // Verifies the given memo is empty or valid json, and then adds the necessary // key/value pair to trigger the ibc hooks callback logic. -/* fn verify_and_create_memo(memo: String, contract_address: String) -> ContractResult { // If the memo given is empty, then set it to "{}" to avoid json parsing errors. Then, // get Value object from json string, erroring if the memo was not null while not being valid json @@ -442,7 +444,6 @@ fn verify_and_create_memo(memo: String, contract_address: String) -> ContractRes Ok(memo) } -*/ fn register_tokens( deps: DepsMut, diff --git a/contracts/adapters/ibc/secret-ibc-hooks/tests/test_execute_ibc_transfer.rs b/contracts/adapters/ibc/secret-ibc-hooks/tests/test_execute_ibc_transfer.rs index 3a7467cd..64063d37 100644 --- a/contracts/adapters/ibc/secret-ibc-hooks/tests/test_execute_ibc_transfer.rs +++ b/contracts/adapters/ibc/secret-ibc-hooks/tests/test_execute_ibc_transfer.rs @@ -77,7 +77,7 @@ struct Params { amount: 100u128.into(), recipient: "ics20".to_string(), recipient_code_hash: Some("code_hash".to_string()), - memo: None, + memo: Some(r#"{"ibc_callback":"ibc_transfer"}"#.to_string()), padding: None, msg: Some(to_binary(&Ics20TransferMsg { channel: "source_channel".to_string(), @@ -93,12 +93,14 @@ struct Params { expected_error_string: "".to_string(), }; "Empty String Memo")] - /* #[test_case( Params { caller: "entry_point".to_string(), ibc_adapter_contract_address: Addr::unchecked("ibc_transfer".to_string()), - coin: Coin::new(100, "osmo"), + sent_asset: Asset::Cw20(Cw20Coin { + address: "secret123".to_string(), + amount: 100u128.into(), + }), ibc_info: IbcInfo { source_channel: "source_channel".to_string(), receiver: "receiver".to_string(), @@ -109,23 +111,23 @@ struct Params { timeout_timestamp: 100, expected_messages: vec![SubMsg { id: 1, - msg: cosmwasm_std::CosmosMsg::Stargate { - type_url: "/ibc.applications.transfer.v1.MsgTransfer".to_string(), - value: MsgTransfer { - source_port: "transfer".to_string(), - source_channel: "source_channel".to_string(), - token: Some(IbcCoin { - denom: "osmo".to_string(), - amount: "100".to_string(), - }), - sender: "ibc_transfer".to_string(), - receiver: "receiver".to_string(), - timeout_height: None, - timeout_timestamp: 100, - memo: r#"{"ibc_callback":"ibc_transfer"}"#.to_string(), - } - .encode_to_vec().into(), - }, + msg: WasmMsg::Execute { + contract_addr: "secret123".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&snip20::ExecuteMsg::Send { + amount: 100u128.into(), + recipient: "ics20".to_string(), + recipient_code_hash: Some("code_hash".to_string()), + memo: Some(r#"{"ibc_callback":"ibc_transfer"}"#.to_string()), + padding: None, + msg: Some(to_binary(&Ics20TransferMsg { + channel: "source_channel".to_string(), + remote_address: "receiver".to_string(), + timeout: Some(100), + })?), + })?, + funds: vec![], + }.into(), gas_limit: None, reply_on: Success, } @@ -137,7 +139,10 @@ struct Params { Params { caller: "entry_point".to_string(), ibc_adapter_contract_address: Addr::unchecked("ibc_transfer".to_string()), - coin: Coin::new(100, "osmo"), + sent_asset: Asset::Cw20(Cw20Coin { + address: "secret123".to_string(), + amount: 100u128.into(), + }), ibc_info: IbcInfo { source_channel: "source_channel".to_string(), receiver: "receiver".to_string(), @@ -148,27 +153,26 @@ struct Params { timeout_timestamp: 100, expected_messages: vec![SubMsg { id: 1, - msg: cosmwasm_std::CosmosMsg::Stargate { - type_url: "/ibc.applications.transfer.v1.MsgTransfer".to_string(), - value: MsgTransfer { - source_port: "transfer".to_string(), - source_channel: "source_channel".to_string(), - token: Some(IbcCoin { - denom: "osmo".to_string(), - amount: "100".to_string(), - }), - sender: "ibc_transfer".to_string(), - receiver: "receiver".to_string(), - timeout_height: None, - timeout_timestamp: 100, - memo: r#"{"ibc_callback":"ibc_transfer","pfm":"example_value","wasm":"example_contract"}"#.to_string(), - } - .encode_to_vec().into(), - }, + msg: WasmMsg::Execute { + contract_addr: "secret123".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&snip20::ExecuteMsg::Send { + amount: 100u128.into(), + recipient: "ics20".to_string(), + recipient_code_hash: Some("code_hash".to_string()), + memo: Some(r#"{"ibc_callback":"ibc_transfer","pfm":"example_value","wasm":"example_contract"}"#.to_string()), + padding: None, + msg: Some(to_binary(&Ics20TransferMsg { + channel: "source_channel".to_string(), + remote_address: "receiver".to_string(), + timeout: Some(100), + })?), + })?, + funds: vec![], + }.into(), gas_limit: None, reply_on: Success, - } - ], + }], expected_error_string: "".to_string(), }; "Add Ibc Callback Key/Value Pair To Other Key/Value In Memo")] @@ -176,7 +180,10 @@ struct Params { Params { caller: "entry_point".to_string(), ibc_adapter_contract_address: Addr::unchecked("ibc_transfer".to_string()), - coin: Coin::new(100, "osmo"), + sent_asset: Asset::Cw20(Cw20Coin { + address: "secret123".to_string(), + amount: 100u128.into(), + }), ibc_info: IbcInfo { source_channel: "source_channel".to_string(), receiver: "receiver".to_string(), @@ -193,7 +200,10 @@ struct Params { Params { caller: "entry_point".to_string(), ibc_adapter_contract_address: Addr::unchecked("ibc_transfer".to_string()), - coin: Coin::new(100, "osmo"), + sent_asset: Asset::Cw20(Cw20Coin { + address: "secret123".to_string(), + amount: 100u128.into(), + }), ibc_info: IbcInfo { source_channel: "source_channel".to_string(), receiver: "receiver".to_string(), @@ -216,7 +226,10 @@ struct Params { Params { caller: "random".to_string(), ibc_adapter_contract_address: Addr::unchecked("ibc_transfer".to_string()), - coin: Coin::new(100, "osmo"), + sent_asset: Asset::Cw20(Cw20Coin { + address: "secret123".to_string(), + amount: 100u128.into(), + }), ibc_info: IbcInfo { source_channel: "source_channel".to_string(), receiver: "receiver".to_string(), @@ -229,7 +242,6 @@ struct Params { expected_error_string: "Unauthorized".to_string(), }; "Unauthorized Caller - Expect Error")] -*/ fn test_execute_ibc_transfer(params: Params) -> ContractResult<()> { // Create mock dependencies let mut deps = mock_dependencies(); diff --git a/contracts/adapters/ibc/secret-ibc-hooks/tests/test_reply.rs b/contracts/adapters/ibc/secret-ibc-hooks/tests/test_reply.rs new file mode 100644 index 00000000..f715e259 --- /dev/null +++ b/contracts/adapters/ibc/secret-ibc-hooks/tests/test_reply.rs @@ -0,0 +1,258 @@ +use cosmwasm_std::{ + testing::{mock_dependencies, mock_env}, + Reply, StdError, SubMsgResponse, SubMsgResult, +}; +use ibc_proto::ibc::applications::transfer::v1::MsgTransferResponse; +use prost::Message; +use skip_go_secret_ibc_adapter_ibc_hooks::{ + error::ContractResult, + state::{ACK_ID_TO_RECOVER_ADDRESS, IN_PROGRESS_CHANNEL_ID, IN_PROGRESS_RECOVER_ADDRESS}, +}; +use test_case::test_case; + +/* +Test Cases: + +Expect Success + - Happy Path (tests the in progress ibc transfer is removed from storage and the ack id to in progress ibc transfer map entry is correct) + +Expect Error + - Missing Sub Msg Response Data + - Invalid Sub Msg Response Data To Convert To MsgTransferResponse + - No In Progress Recover Address To Load + - No In Progress Channel ID To Load + - Ack ID Already Exists + +Expect Panic + - SubMsgResult Error + - Should panic because the sub msg is set to reply only on success, so should never happen + unless the wasm module worked unexpectedly + - SubMsg Incorrect Reply ID + - Should panic because the reply id is set to a constant, so should never happen unless + the wasm module worked unexpectedly + */ + +// Define test parameters +struct Params { + channel_id: String, + sequence_id: u64, + reply: Reply, + pre_reply_in_progress_recover_address: Option, + pre_reply_in_progress_channel_id: Option, + store_ack_id_to_recover_address: bool, + expected_error_string: String, +} + +// Test reply +#[test_case( + Params { + channel_id: "channel_id".to_string(), + sequence_id: 5, + reply: Reply { + id: 1, + result: SubMsgResult::Ok(SubMsgResponse { + events: vec![], + data: Some(MsgTransferResponse {sequence: 5}.encode_to_vec().as_slice().into()), + }), + }, + pre_reply_in_progress_recover_address: Some("recover_address".to_string()), + pre_reply_in_progress_channel_id: Some("channel_id".to_string()), + store_ack_id_to_recover_address: false, + expected_error_string: "".to_string(), + }; + "Happy Path")] +#[test_case( + Params { + channel_id: "channel_id".to_string(), + sequence_id: 1, + reply: Reply { + id: 1, + result: SubMsgResult::Ok(SubMsgResponse { + events: vec![], + data: None, + }), + }, + pre_reply_in_progress_recover_address: None, + pre_reply_in_progress_channel_id: None, + store_ack_id_to_recover_address: false, + expected_error_string: "SubMsgResponse does not contain data".to_string(), + }; + "Missing Sub Msg Response Data - Expect Error")] +#[test_case( + Params { + channel_id: "channel_id".to_string(), + sequence_id: 1, + reply: Reply { + id: 1, + result: SubMsgResult::Ok(SubMsgResponse { + events: vec![], + data: Some(b"invalid".into()), + }), + }, + pre_reply_in_progress_recover_address: None, + pre_reply_in_progress_channel_id: None, + store_ack_id_to_recover_address: false, + expected_error_string: "failed to decode Protobuf message: buffer underflow".to_string(), + }; + "Invalid Sub Msg Response Data To Convert To MsgTransferResponse - Expect Error")] +#[test_case( + Params { + channel_id: "channel_id".to_string(), + sequence_id: 1, + reply: Reply { + id: 1, + result: SubMsgResult::Ok(SubMsgResponse { + events: vec![], + data: Some(MsgTransferResponse {sequence: 5}.encode_to_vec().as_slice().into()), + }), + }, + pre_reply_in_progress_recover_address: None, + pre_reply_in_progress_channel_id: Some("channel_id".to_string()), + store_ack_id_to_recover_address: false, + expected_error_string: "alloc::string::String not found".to_string(), + }; + "No In Progress Recover Address To Load - Expect Error")] +#[test_case( + Params { + channel_id: "channel_id".to_string(), + sequence_id: 1, + reply: Reply { + id: 1, + result: SubMsgResult::Ok(SubMsgResponse { + events: vec![], + data: Some(MsgTransferResponse {sequence: 5}.encode_to_vec().as_slice().into()), + }), + }, + pre_reply_in_progress_recover_address: Some("recover_address".to_string()), + pre_reply_in_progress_channel_id: None, + store_ack_id_to_recover_address: false, + expected_error_string: "alloc::string::String not found".to_string(), + }; + "No In Progress Channel ID To Load - Expect Error")] +#[test_case( + Params { + channel_id: "channel_id".to_string(), + sequence_id: 5, + reply: Reply { + id: 1, + result: SubMsgResult::Ok(SubMsgResponse { + events: vec![], + data: Some(MsgTransferResponse {sequence: 5}.encode_to_vec().as_slice().into()), + }), + }, + pre_reply_in_progress_recover_address: Some("recover_address".to_string()), + pre_reply_in_progress_channel_id: Some("channel_id".to_string()), + store_ack_id_to_recover_address: true, + expected_error_string: "ACK ID already exists for channel ID channel_id and sequence ID 5".to_string(), + }; + "Ack ID Already Exists - Expect Error")] +#[test_case( + Params { + channel_id: "channel_id".to_string(), + sequence_id: 1, + reply: Reply { + id: 2, + result: SubMsgResult::Err("".to_string()), + }, + pre_reply_in_progress_recover_address: Some("recover_address".to_string()), + pre_reply_in_progress_channel_id: Some("channel_id".to_string()), + store_ack_id_to_recover_address: false, + expected_error_string: "".to_string(), + } => panics "internal error: entered unreachable code"; + "SubMsg Incorrect Reply ID - Expect Panic")] +#[test_case( + Params { + channel_id: "channel_id".to_string(), + sequence_id: 1, + reply: Reply { + id: 1, + result: SubMsgResult::Err("".to_string()), + }, + pre_reply_in_progress_recover_address: Some("recover_address".to_string()), + pre_reply_in_progress_channel_id: Some("channel_id".to_string()), + expected_error_string: "".to_string(), + store_ack_id_to_recover_address: false, + } => panics "internal error: entered unreachable code"; + "SubMsgResult Error - Expect Panic")] +fn test_reply(params: Params) -> ContractResult<()> { + // Create mock dependencies + let mut deps = mock_dependencies(); + + // Create mock env + let env = mock_env(); + + // Store the in progress recover address to state if it exists + if let Some(in_progress_recover_address) = params.pre_reply_in_progress_recover_address.clone() + { + IN_PROGRESS_RECOVER_ADDRESS.save(deps.as_mut().storage, &in_progress_recover_address)?; + } + + // Store the in progress channel id to state if it exists + if let Some(in_progress_channel_id) = params.pre_reply_in_progress_channel_id.clone() { + IN_PROGRESS_CHANNEL_ID.save(deps.as_mut().storage, &in_progress_channel_id)?; + } + + // If the test expects the ack id to in progress ibc transfer map entry to be stored, + // store it to state + if params.store_ack_id_to_recover_address { + ACK_ID_TO_RECOVER_ADDRESS.save( + deps.as_mut().storage, + (¶ms.channel_id, params.sequence_id), + ¶ms + .pre_reply_in_progress_recover_address + .clone() + .unwrap(), + )?; + } + + // Call reply with the given test parameters + let res = + skip_go_secret_ibc_adapter_ibc_hooks::contract::reply(deps.as_mut(), env, params.reply); + + // Assert the behavior is correct + match res { + Ok(_) => { + // Assert the test did not expect an error + assert!( + params.expected_error_string.is_empty(), + "expected test to error with {:?}, but it succeeded", + params.expected_error_string + ); + + // Verify the in progress ibc transfer was removed from storage + match IN_PROGRESS_RECOVER_ADDRESS.load(&deps.storage) { + Ok(in_progress_ibc_transfer) => { + panic!( + "expected in progress ibc transfer to be removed: {:?}", + in_progress_ibc_transfer + ) + } + Err(err) => assert!( + matches!(err, StdError::NotFound { .. }), + "unexpected error: {:?}", + err + ), + }; + + // Verify the stored ack id to in progress ibc transfer map entry is correct + assert_eq!( + ACK_ID_TO_RECOVER_ADDRESS + .load(&deps.storage, (¶ms.channel_id, params.sequence_id))?, + params.pre_reply_in_progress_recover_address.unwrap() + ); + } + Err(err) => { + // Assert the test expected an error + assert!( + !params.expected_error_string.is_empty(), + "expected test to succeed, but it errored with {:?}", + err + ); + + // Assert the error is correct + assert_eq!(err.to_string(), params.expected_error_string); + } + } + + Ok(()) +} diff --git a/contracts/adapters/ibc/secret-ibc-hooks/tests/test_sudo.rs b/contracts/adapters/ibc/secret-ibc-hooks/tests/test_sudo.rs new file mode 100644 index 00000000..deb2bea3 --- /dev/null +++ b/contracts/adapters/ibc/secret-ibc-hooks/tests/test_sudo.rs @@ -0,0 +1,205 @@ +use cosmwasm_std::{ + testing::{mock_dependencies_with_balances, mock_env}, + Addr, BankMsg, Coin, + ReplyOn::Never, + StdError, SubMsg, +}; +use secret_skip::{ibc::IbcLifecycleComplete, sudo::OsmosisSudoMsg as SudoMsg}; +use skip_go_secret_ibc_adapter_ibc_hooks::{ + error::ContractResult, state::ACK_ID_TO_RECOVER_ADDRESS, +}; +use test_case::test_case; + +/* +Test Cases: + +Expect Success + - Sudo Response - Happy Path Response + - Sudo Timeout - Send Failed Ibc Coin To Recover Address + - Sudo Error - Send Failed Ibc Coin To Recover Address + +Expect Error + - No In Progress Recover Address Mapped To Sudo Ack ID - Expect Error + - No Contract Balance To Refund - Expect Error + + */ + +// Define test parameters +struct Params { + contract_balance: Vec, + channel_id: String, + sequence_id: u64, + sudo_msg: SudoMsg, + stored_in_progress_recover_address: Option, + expected_messages: Vec, + expected_error_string: String, +} + +// Test sudo +#[test_case( + Params { + contract_balance: vec![], + channel_id: "channel_id".to_string(), + sequence_id: 1, + sudo_msg: SudoMsg::IbcLifecycleComplete(IbcLifecycleComplete::IbcAck{ + channel: "channel_id".to_string(), + sequence: 1, + ack: "".to_string(), + success: true, + }), + stored_in_progress_recover_address: Some("recover_address".to_string()), + expected_messages: vec![], + expected_error_string: "".to_string(), + }; + "Sudo Response - Happy Path")] +#[test_case( + Params { + contract_balance: vec![Coin::new(100, "uosmo")], + channel_id: "channel_id".to_string(), + sequence_id: 1, + sudo_msg: SudoMsg::IbcLifecycleComplete(IbcLifecycleComplete::IbcTimeout{ + channel: "channel_id".to_string(), + sequence: 1, + }), + stored_in_progress_recover_address: Some("recover_address".to_string()), + expected_messages: vec![ + SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "recover_address".to_string(), + amount: vec![Coin::new(100, "uosmo")], + }.into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error_string: "".to_string(), + }; + "Sudo Timeout - Send Failed Ibc Coin")] +#[test_case( + Params { + contract_balance: vec![Coin::new(100, "uosmo")], + channel_id: "channel_id".to_string(), + sequence_id: 1, + sudo_msg: SudoMsg::IbcLifecycleComplete(IbcLifecycleComplete::IbcAck{ + channel: "channel_id".to_string(), + sequence: 1, + ack: "".to_string(), + success: false, + }), + stored_in_progress_recover_address: Some("recover_address".to_string()), + expected_messages: vec![ + SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "recover_address".to_string(), + amount: vec![Coin::new(100, "uosmo")], + }.into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error_string: "".to_string(), + }; + "Sudo Error - Send Failed Ibc Coin To Recover Address")] +#[test_case( + Params { + contract_balance: vec![Coin::new(100, "uosmo")], + channel_id: "channel_id".to_string(), + sequence_id: 1, + sudo_msg: SudoMsg::IbcLifecycleComplete(IbcLifecycleComplete::IbcAck{ + channel: "channel_id".to_string(), + sequence: 1, + ack: "".to_string(), + success: false, + }), + stored_in_progress_recover_address: None, + expected_messages: vec![], + expected_error_string: "alloc::string::String not found".to_string(), + }; + "No In Progress Ibc Transfer Mapped To Sudo Ack ID - Expect Error")] +#[test_case( + Params { + contract_balance: vec![], + channel_id: "channel_id".to_string(), + sequence_id: 1, + sudo_msg: SudoMsg::IbcLifecycleComplete(IbcLifecycleComplete::IbcAck{ + channel: "channel_id".to_string(), + sequence: 1, + ack: "".to_string(), + success: false, + }), + stored_in_progress_recover_address: Some("recover_address".to_string()), + expected_messages: vec![], + expected_error_string: "Failed to receive ibc funds to refund the user".to_string(), + }; + "No Contract Balance To Refund - Expect Error")] +fn test_sudo(params: Params) -> ContractResult<()> { + // Convert params contract balance to a slice + let contract_balance: &[Coin] = ¶ms.contract_balance; + + // Create mock dependencies + let mut deps = mock_dependencies_with_balances(&[("ibc_transfer_adapter", contract_balance)]); + + // Create mock env + let mut env = mock_env(); + env.contract.address = Addr::unchecked("ibc_transfer_adapter"); + + // Store the in progress recover address to state if it exists + if let Some(in_progress_recover_address) = params.stored_in_progress_recover_address.clone() { + ACK_ID_TO_RECOVER_ADDRESS.save( + deps.as_mut().storage, + (¶ms.channel_id, params.sequence_id), + &in_progress_recover_address, + )?; + } + + // Call sudo with the given test parameters + let res = + skip_go_secret_ibc_adapter_ibc_hooks::contract::sudo(deps.as_mut(), env, params.sudo_msg); + + // Assert the behavior is correct + match res { + Ok(res) => { + // Assert the test did not expect an error + assert!( + params.expected_error_string.is_empty(), + "expected test to error with {:?}, but it succeeded", + params.expected_error_string + ); + + // Verify the in progress recover address was removed from storage + match ACK_ID_TO_RECOVER_ADDRESS + .load(&deps.storage, (¶ms.channel_id, params.sequence_id)) + { + Ok(in_progress_recover_address) => { + panic!( + "expected in progress recover address to be removed: {:?}", + in_progress_recover_address + ) + } + Err(err) => assert!( + matches!(err, StdError::NotFound { .. }), + "unexpected error: {:?}", + err + ), + }; + + // Verify the messages in the response are correct + assert_eq!(res.messages, params.expected_messages); + } + Err(err) => { + // Assert the test expected an error + assert!( + !params.expected_error_string.is_empty(), + "expected test to succeed, but it errored with {:?}", + err + ); + + // Assert the error is correct + assert_eq!(err.to_string(), params.expected_error_string); + } + } + + Ok(()) +} From 419b78b119875dcc40649ff33f800ce1feaf6329 Mon Sep 17 00:00:00 2001 From: DrPresident Date: Fri, 20 Dec 2024 14:57:20 -0600 Subject: [PATCH 20/29] reworked some things --- contracts/secret-entry-point/Cargo.toml | 1 + contracts/secret-entry-point/src/contract.rs | 8 +- contracts/secret-entry-point/src/error.rs | 3 + contracts/secret-entry-point/src/execute.rs | 41 +- contracts/secret-entry-point/src/msg.rs | 2 +- contracts/secret-entry-point/src/query.rs | 4 +- contracts/secret-entry-point/src/state.rs | 5 +- .../tests/test_execute_action.rs | 494 ++++++++++++++++++ .../tests/test_execute_receive.rs | 21 +- .../tests/test_instantiate.rs | 140 +++++ 10 files changed, 686 insertions(+), 33 deletions(-) create mode 100644 contracts/secret-entry-point/tests/test_execute_action.rs create mode 100644 contracts/secret-entry-point/tests/test_instantiate.rs diff --git a/contracts/secret-entry-point/Cargo.toml b/contracts/secret-entry-point/Cargo.toml index f7f29c70..80b6d27f 100644 --- a/contracts/secret-entry-point/Cargo.toml +++ b/contracts/secret-entry-point/Cargo.toml @@ -38,3 +38,4 @@ cosmwasm-std = { package = "secret-cosmwasm-std", version = "1.1.11"} [dev-dependencies] test-case = { workspace = true } +serde_json = "1.0.64" diff --git a/contracts/secret-entry-point/src/contract.rs b/contracts/secret-entry-point/src/contract.rs index 23970a67..85148c7c 100644 --- a/contracts/secret-entry-point/src/contract.rs +++ b/contracts/secret-entry-point/src/contract.rs @@ -9,8 +9,8 @@ use crate::{ query::{query_ibc_transfer_adapter_contract, query_swap_venue_adapter_contract}, reply::{reply_swap_and_action_with_recover, RECOVER_REPLY_ID}, state::{ - BLOCKED_CONTRACT_ADDRESSES, HYPERLANE_TRANSFER_CONTRACT_ADDRESS, - IBC_TRANSFER_CONTRACT_ADDRESS, REGISTERED_TOKENS, SWAP_VENUE_MAP, VIEWING_KEY, + BLOCKED_CONTRACT_ADDRESSES, IBC_TRANSFER_CONTRACT, REGISTERED_TOKENS, SWAP_VENUE_MAP, + VIEWING_KEY, }, }; use cosmwasm_std::{ @@ -92,7 +92,7 @@ pub fn instantiate( }; // Store the ibc transfer adapter contract address - IBC_TRANSFER_CONTRACT_ADDRESS.save(deps.storage, &checked_ibc_transfer_contract)?; + IBC_TRANSFER_CONTRACT.save(deps.storage, &checked_ibc_transfer_contract)?; // Insert the ibc transfer adapter contract address into the blocked contract addresses map BLOCKED_CONTRACT_ADDRESSES.save(deps.storage, &checked_ibc_transfer_contract.address, &())?; @@ -103,6 +103,7 @@ pub fn instantiate( .add_attribute("contract_address", &checked_ibc_transfer_contract.address); // If the hyperlane transfer contract address is provided, validate and store it + /* if let Some(hyperlane_transfer_contract) = msg.hyperlane_transfer_contract { // Validate hyperlane transfer adapter contract address let checked_hyperlane_transfer_contract = ContractInfo { @@ -131,6 +132,7 @@ pub fn instantiate( &checked_hyperlane_transfer_contract.address, ); } + */ Ok(response) } diff --git a/contracts/secret-entry-point/src/error.rs b/contracts/secret-entry-point/src/error.rs index 375cbc90..601954b8 100644 --- a/contracts/secret-entry-point/src/error.rs +++ b/contracts/secret-entry-point/src/error.rs @@ -99,4 +99,7 @@ pub enum ContractError { #[error("Snip20 Token Not Registered {0}")] TokenNotRegistered(Addr), + + #[error("Unsupported Action")] + UnsupportedAction, } diff --git a/contracts/secret-entry-point/src/execute.rs b/contracts/secret-entry-point/src/execute.rs index bb9b9cda..0f5f15ec 100644 --- a/contracts/secret-entry-point/src/execute.rs +++ b/contracts/secret-entry-point/src/execute.rs @@ -2,13 +2,12 @@ use std::vec; use crate::{ error::{ContractError, ContractResult}, - hyperlane::{ExecuteMsg as HplExecuteMsg, ExecuteMsg::HplTransfer}, + //hyperlane::{ExecuteMsg as HplExecuteMsg, ExecuteMsg::HplTransfer}, msg::{Action, Affiliate, ExecuteMsg, Snip20HookMsg}, reply::{RecoverTempStorage, RECOVER_REPLY_ID}, state::{ - BLOCKED_CONTRACT_ADDRESSES, HYPERLANE_TRANSFER_CONTRACT_ADDRESS, - IBC_TRANSFER_CONTRACT_ADDRESS, PRE_SWAP_OUT_ASSET_AMOUNT, RECOVER_TEMP_STORAGE, - REGISTERED_TOKENS, SWAP_VENUE_MAP, VIEWING_KEY, + BLOCKED_CONTRACT_ADDRESSES, IBC_TRANSFER_CONTRACT, PRE_SWAP_OUT_ASSET_AMOUNT, + RECOVER_TEMP_STORAGE, REGISTERED_TOKENS, SWAP_VENUE_MAP, VIEWING_KEY, }, }; @@ -385,7 +384,7 @@ pub fn execute_user_swap( affiliate_fee_asset.amount(), None, None, - 255, + 0, affiliate_fee_contract.code_hash.clone(), affiliate_fee_contract.address.to_string(), ) { @@ -436,7 +435,7 @@ pub fn execute_user_swap( Some(to_binary(&user_swap_msg_args)?), None, None, - 255, + 0, remaining_asset_contract.code_hash.clone(), remaining_asset_contract.address.to_string(), ) { @@ -497,7 +496,7 @@ pub fn execute_user_swap( None, None, None, - 255, + 0, remaining_asset_contract.code_hash.clone(), remaining_asset_contract.address.to_string(), ) { @@ -529,7 +528,7 @@ pub fn execute_user_swap( Some(to_binary(&user_swap_msg_args)?), None, None, - 255, + 0, remaining_asset_contract.code_hash.clone(), remaining_asset_contract.address.to_string(), ) { @@ -572,7 +571,7 @@ pub fn execute_user_swap( Some(to_binary(&user_swap_msg_args)?), None, None, - 255, + 0, remaining_asset_contract.code_hash.clone(), remaining_asset_contract.address.to_string(), ) { @@ -628,7 +627,7 @@ pub fn execute_post_swap_action( deps.querier, env.contract.address.to_string(), viewing_key, - 255, + 0, min_asset_contract.code_hash.clone(), min_asset_contract.address.to_string(), ) { @@ -818,12 +817,13 @@ fn validate_and_dispatch_action( let action_asset_contract = REGISTERED_TOKENS .load(deps.storage, deps.api.addr_validate(action_asset.denom())?)?; // Create the transfer message - let transfer_msg = match snip20::transfer_msg( + let transfer_msg = match snip20::send_msg( to_address.to_string(), action_asset.amount(), None, None, - 255, + None, + 0, action_asset_contract.code_hash.clone(), action_asset_contract.address.to_string(), ) { @@ -849,11 +849,12 @@ fn validate_and_dispatch_action( }; // Get the IBC transfer adapter contract address - let ibc_transfer_contract = IBC_TRANSFER_CONTRACT_ADDRESS.load(deps.storage)?; + let ibc_transfer_contract = IBC_TRANSFER_CONTRACT.load(deps.storage)?; // Send the IBC transfer by calling the IBC transfer contract - let ibc_transfer_msg = match snip20::send_msg( + let ibc_transfer_msg = match snip20::send_msg_with_code_hash( ibc_transfer_contract.address.to_string(), + Some(ibc_transfer_contract.code_hash), action_asset.amount(), Some(to_binary(&ibc::Snip20HookMsg::IbcTransfer { info: ibc_info, @@ -861,7 +862,7 @@ fn validate_and_dispatch_action( })?), None, None, - 255, + 0, transfer_out_contract.code_hash.clone(), transfer_out_contract.address.to_string(), ) { @@ -877,6 +878,7 @@ fn validate_and_dispatch_action( .add_message(ibc_transfer_msg) .add_attribute("action", "dispatch_action_ibc_transfer"); } + /* Action::ContractCall { contract_address, msg, @@ -942,6 +944,10 @@ fn validate_and_dispatch_action( .add_message(hpl_transfer_msg) .add_attribute("action", "dispatch_action_ibc_transfer"); } + */ + _ => { + return Err(ContractError::UnsupportedAction); + } }; Ok(response) @@ -964,6 +970,7 @@ fn handle_ibc_transfer_fees( .transpose()?; if let Some(fee_swap) = fee_swap { + // NOTE unsure if this works let ibc_fee_coin = ibc_fee_coin .clone() .ok_or(ContractError::FeeSwapWithoutIbcFees)?; @@ -988,7 +995,7 @@ fn handle_ibc_transfer_fees( // Dispatch the ibc fee bank send to the ibc transfer adapter contract if needed if let Some(ibc_fee_coin) = ibc_fee_coin { // Get the ibc transfer adapter contract address - let ibc_transfer_contract = IBC_TRANSFER_CONTRACT_ADDRESS.load(deps.storage)?; + let ibc_transfer_contract = IBC_TRANSFER_CONTRACT.load(deps.storage)?; // Create the ibc fee bank send message let ibc_fee_msg = BankMsg::Send { @@ -1063,7 +1070,7 @@ fn verify_and_create_fee_swap_msg( Some(to_binary(&fee_swap_msg_args)?), None, None, - 255, + 0, fee_swap_asset_contract.code_hash.clone(), fee_swap_asset_contract.address.to_string(), ) { diff --git a/contracts/secret-entry-point/src/msg.rs b/contracts/secret-entry-point/src/msg.rs index ad817006..20383ae2 100644 --- a/contracts/secret-entry-point/src/msg.rs +++ b/contracts/secret-entry-point/src/msg.rs @@ -31,7 +31,7 @@ pub struct MigrateMsg {} pub struct InstantiateMsg { pub swap_venues: Vec, pub ibc_transfer_contract: ContractInfo, - pub hyperlane_transfer_contract: Option, + // pub hyperlane_transfer_contract: Option, pub viewing_key: String, } diff --git a/contracts/secret-entry-point/src/query.rs b/contracts/secret-entry-point/src/query.rs index ba521df9..7842aac2 100644 --- a/contracts/secret-entry-point/src/query.rs +++ b/contracts/secret-entry-point/src/query.rs @@ -1,4 +1,4 @@ -use crate::state::{IBC_TRANSFER_CONTRACT_ADDRESS, SWAP_VENUE_MAP}; +use crate::state::{IBC_TRANSFER_CONTRACT, SWAP_VENUE_MAP}; use cosmwasm_std::{Addr, Deps, StdResult}; // Queries the swap venue map by name and returns the swap adapter contract address if it exists @@ -8,5 +8,5 @@ pub fn query_swap_venue_adapter_contract(deps: Deps, name: String) -> StdResult< // Queries the IBC transfer adapter contract address and returns it if it exists pub fn query_ibc_transfer_adapter_contract(deps: Deps) -> StdResult { - Ok(IBC_TRANSFER_CONTRACT_ADDRESS.load(deps.storage)?.address) + Ok(IBC_TRANSFER_CONTRACT.load(deps.storage)?.address) } diff --git a/contracts/secret-entry-point/src/state.rs b/contracts/secret-entry-point/src/state.rs index 91dcbfae..df07d625 100644 --- a/contracts/secret-entry-point/src/state.rs +++ b/contracts/secret-entry-point/src/state.rs @@ -4,10 +4,11 @@ use secret_storage_plus::{Item, Map}; pub const BLOCKED_CONTRACT_ADDRESSES: Map<&Addr, ()> = Map::new("blocked_contract_addresses"); pub const SWAP_VENUE_MAP: Map<&str, ContractInfo> = Map::new("swap_venue_map"); -pub const IBC_TRANSFER_CONTRACT_ADDRESS: Item = - Item::new("ibc_transfer_contract_address"); +pub const IBC_TRANSFER_CONTRACT: Item = Item::new("ibc_transfer_contract"); +/* pub const HYPERLANE_TRANSFER_CONTRACT_ADDRESS: Item = Item::new("hyperlane_transfer_contract_address"); +*/ // Temporary state to save variables to be used in // reply handling in case of recovering from an error diff --git a/contracts/secret-entry-point/tests/test_execute_action.rs b/contracts/secret-entry-point/tests/test_execute_action.rs new file mode 100644 index 00000000..aa96ec20 --- /dev/null +++ b/contracts/secret-entry-point/tests/test_execute_action.rs @@ -0,0 +1,494 @@ +use cosmwasm_std::{ + testing::{mock_dependencies_with_balances, mock_env, mock_info}, + to_binary, Addr, BankMsg, Coin, ContractInfo, ContractResult, QuerierResult, + ReplyOn::Never, + SubMsg, SystemResult, Timestamp, Uint128, WasmMsg, WasmQuery, +}; +use cw20::{BalanceResponse, Cw20Coin, Cw20ExecuteMsg}; +use secret_skip::{ + asset::Asset, + ibc::{self, ExecuteMsg as IbcTransferExecuteMsg, IbcFee, IbcInfo, Ics20TransferMsg}, + snip20, +}; +//use secret_toolkit::snip20; +use skip_go_secret_entry_point::{ + error::ContractError, + msg::{Action, ExecuteMsg}, + state::{BLOCKED_CONTRACT_ADDRESSES, IBC_TRANSFER_CONTRACT, REGISTERED_TOKENS}, +}; +use test_case::test_case; + +/* +Test Cases: + +Expect Response + // General + - Native Asset Transfer + - Cw20 Asset Transfer + - Ibc Transfer + - Native Asset Contract Call + - Cw20 Asset Contract Call + + // Exact Out + - Ibc Transfer With Exact Out Set To True + - Ibc Transfer w/ IBC Fees of same denom as min coin With Exact Out Set To True + +Expect Error + - Remaining Asset Less Than Min Asset - Native + - Remaining Asset Less Than Min Asset - CW20 + - Contract Call Address Blocked + - Ibc Transfer w/ IBC Fees of different denom than min coin no fee swap + */ + +// Define test parameters +struct Params { + info_funds: Vec, + sent_asset: Option, + action: Action, + exact_out: bool, + min_asset: Option, + expected_messages: Vec, + expected_error: Option, +} + +// Test execute_action +#[test_case( + Params { + info_funds: vec![Coin::new(1_000_000, "os")], + sent_asset: Some(Asset::Cw20(Cw20Coin { + address: "secret123".to_string(), + amount: 1_000_000u128.into(), + })), + min_asset: None, + action: Action::Transfer { + to_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5".to_string(), + }, + exact_out: false, + expected_messages: vec![SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "secret123".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&snip20::ExecuteMsg::Send { + amount: 1_000_000u128.into(), + recipient: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5".to_string(), + recipient_code_hash: None, + memo: None, + padding: None, + msg: None, + }).unwrap(), + funds: vec![], + }.into(), + gas_limit: None, + reply_on: Never, + }], + expected_error: None, + }; + "Cw20 Asset Transfer")] +#[test_case( + Params { + info_funds: vec![Coin::new(1_000_000, "os")], + sent_asset: Some(Asset::Cw20(Cw20Coin { + address: "secret123".to_string(), + amount: 1_000_000u128.into(), + })), + min_asset: None, + action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: None, + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5".to_string(), + }, + fee_swap: None, + }, + exact_out: false, + expected_messages: vec![SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "secret123".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&snip20::ExecuteMsg::Send { + amount: 1_000_000u128.into(), + recipient: "ibc_transfer_adapter".to_string(), + recipient_code_hash: Some("code_hash".to_string()), + memo: None, + padding: None, + msg: Some(to_binary(&ibc::Snip20HookMsg::IbcTransfer { + info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: None, + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5".to_string(), + }, + timeout_timestamp: 101, + }).unwrap()), + }).unwrap(), + funds: vec![], + }.into(), + gas_limit: None, + reply_on: Never, + }], + expected_error: None, + }; + "Ibc Transfer")] +/* +#[test_case( + Params { + info_funds: vec![], + sent_asset: Some(Asset::Cw20(Cw20Coin{ + address: "secret123".to_string(), + amount: 1_000_000u128.into(), + })), + min_asset: None, + action: Action::ContractCall { + contract_address: "contract_call".to_string(), + msg: to_binary(&"contract_call_msg").unwrap(), + }, + exact_out: false, + expected_messages: vec![SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "secret123".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&Cw20ExecuteMsg::Send { + contract: "contract_call".to_string(), + amount: Uint128::new(1_000_000), + msg: to_binary(&"contract_call_msg").unwrap(), + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }], + expected_error: None, + }; + "Cw20 Asset Contract Call")] +*/ +#[test_case( + Params { + info_funds: vec![Coin::new(1_000_000, "os")], + sent_asset: Some(Asset::Cw20(Cw20Coin { + address: "secret123".to_string(), + amount: 1_000_000u128.into(), + })), + min_asset: Some(Asset::Cw20(Cw20Coin { + address: "secret123".to_string(), + amount: 900_000u128.into(), + })), + action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: None, + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: None, + }, + exact_out: true, + expected_messages: vec![SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "secret123".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&snip20::ExecuteMsg::Send { + amount: 900_000u128.into(), + recipient: "ibc_transfer_adapter".to_string(), + recipient_code_hash: Some("code_hash".to_string()), + memo: None, + padding: None, + msg: Some(to_binary(&ibc::Snip20HookMsg::IbcTransfer { + info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: None, + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5".to_string(), + }, + timeout_timestamp: 101, + }).unwrap()), + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }], + expected_error: None, + }; + "Ibc Transfer With Exact Out Set To True")] +/* +#[test_case( + Params { + info_funds: vec![Coin::new(1_200_000, "os")], + sent_asset: Some(Asset::Native(Coin::new(1_200_000, "os"))), + min_asset: Some(Asset::Native(Coin::new(900_000, "os"))), + action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "os")], + timeout_fee: vec![Coin::new(100_000, "os")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: None, + }, + exact_out: true, + expected_messages: vec![ + SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "ibc_transfer_adapter".to_string(), + amount: vec![Coin::new(200_000, "os")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "ibc_transfer_adapter".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&IbcTransferExecuteMsg::IbcTransfer { + info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "os")], + timeout_fee: vec![Coin::new(100_000, "os")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + coin: Coin::new(900_000, "os"), + timeout_timestamp: 101, + }) + .unwrap(), + funds: vec![Coin::new(900_000, "os")], + } + .into(), + gas_limit: None, + reply_on: Never, + }], + expected_error: None, + }; + "Ibc Transfer w/ IBC Fees of same denom as min coin With Exact Out Set To True")] +#[test_case( + Params { + info_funds: vec![Coin::new(1_000_000, "os")], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "os"))), + min_asset: Some(Asset::Native(Coin::new(900_000, "os"))), + action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "un")], + timeout_fee: vec![Coin::new(100_000, "un")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: None, + }, + exact_out: true, + expected_messages: vec![SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "ibc_transfer_adapter".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&IbcTransferExecuteMsg::IbcTransfer { + info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "un")], + timeout_fee: vec![Coin::new(100_000, "un")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + coin: Coin::new(900_000, "os"), + timeout_timestamp: 101, + }) + .unwrap(), + funds: vec![Coin::new(900_000, "os")], + } + .into(), + gas_limit: None, + reply_on: Never, + }], + expected_error: Some(ContractError::IBCFeeDenomDiffersFromAssetReceived), + }; + "Ibc Transfer w/ IBC Fees of different denom than min coin no fee swap - Expect Error")] +#[test_case( + Params { + info_funds: vec![Coin::new(1_000_000, "os")], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "os"))), + min_asset: Some(Asset::Native(Coin::new(1_100_000, "os"))), + action: Action::ContractCall { + contract_address: "entry_point".to_string(), + msg: to_binary(&"contract_call_msg").unwrap(), + }, + exact_out: true, + expected_messages: vec![], + expected_error: Some(ContractError::RemainingAssetLessThanMinAsset), + }; + "Remaining Asset Less Than Min Asset Native - Expect Error")] +#[test_case( + Params { + info_funds: vec![], + sent_asset: Some(Asset::Cw20(Cw20Coin{ + address: "secret123".to_string(), + amount: Uint128::new(1_000_000), + })), + min_asset: Some(Asset::Cw20(Cw20Coin{ + address: "secret123".to_string(), + amount: Uint128::new(2_100_000), + })), + action: Action::ContractCall { + contract_address: "entry_point".to_string(), + msg: to_binary(&"contract_call_msg").unwrap(), + }, + exact_out: true, + expected_messages: vec![], + expected_error: Some(ContractError::RemainingAssetLessThanMinAsset), + }; + "Remaining Asset Less Than Min Asset CW20 - Expect Error")] +#[test_case( + Params { + info_funds: vec![Coin::new(1_000_000, "os")], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "os"))), + min_asset: None, + action: Action::ContractCall { + contract_address: "entry_point".to_string(), + msg: to_binary(&"contract_call_msg").unwrap(), + }, + exact_out: false, + expected_messages: vec![], + expected_error: Some(ContractError::ContractCallAddressBlocked), + }; + "Contract Call Address Blocked - Expect Error")] +*/ +fn test_execute_post_swap_action(params: Params) { + // Create mock dependencies + let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); + + // Create mock wasm handler to handle the swap adapter contract query + let wasm_handler = |query: &WasmQuery| -> QuerierResult { + match query { + WasmQuery::Smart { .. } => SystemResult::Ok(ContractResult::Ok( + to_binary(&BalanceResponse { + balance: 1_000_000u128.into(), + }) + .unwrap(), + )), + _ => panic!("Unsupported query: {:?}", query), + } + }; + + // Update querier with mock wasm handler + deps.querier.update_wasm(wasm_handler); + + // Create mock env with parameters that make testing easier + let mut env = mock_env(); + env.contract.address = Addr::unchecked("entry_point"); + env.contract.code_hash = "code_hash".to_string(); + env.block.time = Timestamp::from_nanos(100); + + // Convert info funds vector into a slice of Coin objects + let info_funds: &[Coin] = ¶ms.info_funds; + + // Create mock info with entry point contract address + let info = mock_info("actioner", info_funds); + + // Store the ibc transfer adapter contract address + let ibc_transfer_adapter = ContractInfo { + address: Addr::unchecked("ibc_transfer_adapter"), + code_hash: "code_hash".to_string(), + }; + IBC_TRANSFER_CONTRACT + .save(deps.as_mut().storage, &ibc_transfer_adapter) + .unwrap(); + + REGISTERED_TOKENS + .save( + deps.as_mut().storage, + Addr::unchecked("secret123"), + &ContractInfo { + address: Addr::unchecked("secret123"), + code_hash: "code_hash".to_string(), + }, + ) + .unwrap(); + + // Store the entry point contract address in the blocked contract addresses map + BLOCKED_CONTRACT_ADDRESSES + .save(deps.as_mut().storage, &Addr::unchecked("entry_point"), &()) + .unwrap(); + + // Call execute_post_swap_action with the given test parameters + let res = skip_go_secret_entry_point::contract::execute( + deps.as_mut(), + env, + info, + ExecuteMsg::Action { + sent_asset: params.sent_asset, + timeout_timestamp: 101, + action: params.action, + exact_out: params.exact_out, + min_asset: params.min_asset, + }, + ); + + match res { + Ok(res) => { + // Assert the test did not expect an error + assert!( + params.expected_error.is_none(), + "expected test to error with {:?}, but it succeeded", + params.expected_error + ); + + // Assert the number of messages in the response is correct + assert_eq!( + res.messages.len(), + params.expected_messages.len(), + "expected {:?} messages, but got {:?}", + params.expected_messages.len(), + res.messages.len() + ); + + // Assert the messages in the response are correct + assert_eq!(res.messages, params.expected_messages,); + } + Err(err) => { + // Assert the test expected an error + assert!( + params.expected_error.is_some(), + "expected test to succeed, but it errored with {:?}", + err + ); + + // Assert the error is correct + assert_eq!(err, params.expected_error.unwrap()); + } + } +} diff --git a/contracts/secret-entry-point/tests/test_execute_receive.rs b/contracts/secret-entry-point/tests/test_execute_receive.rs index 526bb621..7563012a 100644 --- a/contracts/secret-entry-point/tests/test_execute_receive.rs +++ b/contracts/secret-entry-point/tests/test_execute_receive.rs @@ -16,7 +16,7 @@ use skip_go_secret_entry_point::{ error::ContractError, msg::{Action, Affiliate, ExecuteMsg, Snip20HookMsg}, reply::RECOVER_REPLY_ID, - state::{IBC_TRANSFER_CONTRACT_ADDRESS, REGISTERED_TOKENS, SWAP_VENUE_MAP, VIEWING_KEY}, + state::{IBC_TRANSFER_CONTRACT, REGISTERED_TOKENS, SWAP_VENUE_MAP, VIEWING_KEY}, }; use test_case::test_case; @@ -140,11 +140,10 @@ struct Params { expected_error: None, }; "Valid Swap And Action Msg")] -/* #[test_case( Params { info_funds: vec![], - sent_asset: Asset::Cw20(Cw20Coin{address: "secret123".to_string(), amount: Uint128::from(1_000_000u128)}), + sent_asset: Asset::Cw20(Cw20Coin{address: "secret123".to_string(), amount: 1_000_000u128.into()}), user_swap: Swap::SwapExactAssetIn ( SwapExactAssetIn{ swap_venue_name: "swap_venue_name".to_string(), @@ -158,7 +157,10 @@ struct Params { ], } ), - min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + min_asset: Asset::Cw20(Cw20Coin { + address: "secret456".to_string(), + amount: 1000u128.into(), + }), timeout_timestamp: 101, post_swap_action: Action::Transfer { to_address: "to_address".to_string(), @@ -170,8 +172,9 @@ struct Params { id: RECOVER_REPLY_ID, msg: WasmMsg::Execute { contract_addr: "entry_point".to_string(), + code_hash: "code_hash".to_string(), msg: to_binary(&ExecuteMsg::SwapAndAction { - sent_asset: Some(Asset::Cw20(Cw20Coin{address: "secret123".to_string(), amount: Uint128::from(1_000_000u128)})), + sent_asset: Some(Asset::Cw20(Cw20Coin{address: "secret123".to_string(), amount: 1_000_000u128.into()})), user_swap: Swap::SwapExactAssetIn ( SwapExactAssetIn{ swap_venue_name: "swap_venue_name".to_string(), @@ -185,7 +188,10 @@ struct Params { ], } ), - min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + min_asset: Asset::Cw20(Cw20Coin { + address: "secret456".to_string(), + amount: 1000u128.into(), + }), timeout_timestamp: 101, post_swap_action: Action::Transfer { to_address: "to_address".to_string(), @@ -202,7 +208,6 @@ struct Params { expected_error: None, }; "Valid Swap And Action With Recover Msg")] -*/ fn test_execute_receive(params: Params) { // Create mock dependencies let mut deps = mock_dependencies_with_balances(&[("entry_point", &[])]); @@ -265,7 +270,7 @@ fn test_execute_receive(params: Params) { address: Addr::unchecked("ibc_transfer_adapter"), code_hash: "code_hash".to_string(), }; - IBC_TRANSFER_CONTRACT_ADDRESS + IBC_TRANSFER_CONTRACT .save(deps.as_mut().storage, &ibc_transfer_adapter) .unwrap(); diff --git a/contracts/secret-entry-point/tests/test_instantiate.rs b/contracts/secret-entry-point/tests/test_instantiate.rs new file mode 100644 index 00000000..0bfbd6af --- /dev/null +++ b/contracts/secret-entry-point/tests/test_instantiate.rs @@ -0,0 +1,140 @@ +use cosmwasm_std::{ + testing::{mock_dependencies, mock_env, mock_info}, + Addr, ContractInfo, +}; +use skip_go_secret_entry_point::{ + error::ContractError, + msg::{InstantiateMsg, SwapVenue}, + state::{BLOCKED_CONTRACT_ADDRESSES, IBC_TRANSFER_CONTRACT, SWAP_VENUE_MAP}, +}; +use test_case::test_case; + +/* +Test Cases: + +Expect Response + - Happy Path (tests the adapter and blocked contract addresses are stored correctly) + +Expect Error + - Duplicate Swap Venue Names + */ + +// Define test parameters +struct Params { + swap_venues: Vec, + ibc_transfer_contract: ContractInfo, + viewing_key: String, + expected_error: Option, +} + +// Test instantiate +#[test_case( + Params { + swap_venues: vec![ + SwapVenue { + name: "shade-swap".to_string(), + adapter_contract: ContractInfo { + address: Addr::unchecked("secret123".to_string()), + code_hash: "code_hash".to_string(), + }, + }, + ], + ibc_transfer_contract: ContractInfo { + address: Addr::unchecked("ibc_transfer_adapter".to_string()), + code_hash: "code_hash".to_string(), + }, + viewing_key: "viewing_key".to_string(), + expected_error: None, + }; + "Happy Path")] +/* +#[test_case( + Params { + swap_venues: vec![ + SwapVenue { + name: "neutron-astroport".to_string(), + adapter_contract_address: "neutron123".to_string(), + }, + SwapVenue { + name: "neutron-astroport".to_string(), + adapter_contract_address: "neutron456".to_string(), + }, + ], + ibc_transfer_contract_address: "ibc_transfer_adapter".to_string(), + expected_error: Some(ContractError::DuplicateSwapVenueName), + }; + "Duplicate Swap Venue Names")] +*/ +fn test_instantiate(params: Params) { + // Create mock dependencies + let mut deps = mock_dependencies(); + + // Create mock info + let info = mock_info("creator", &[]); + + // Create mock env with the entry point contract address + let mut env = mock_env(); + env.contract.address = Addr::unchecked("entry_point"); + + // Call instantiate with the given test parameters + let res = skip_go_secret_entry_point::contract::instantiate( + deps.as_mut(), + env, + info, + InstantiateMsg { + swap_venues: params.swap_venues.clone(), + ibc_transfer_contract: params.ibc_transfer_contract, + viewing_key: params.viewing_key, + }, + ); + + match res { + Ok(_) => { + // Assert the test did not expect an error + assert!( + params.expected_error.is_none(), + "expected test to error with {:?}, but it succeeded", + params.expected_error + ); + + // Assert the entry point contract address exists in the blocked contract addresses map + assert!(BLOCKED_CONTRACT_ADDRESSES + .has(deps.as_ref().storage, &Addr::unchecked("entry_point"))); + + // Get stored ibc transfer adapter contract address + let stored_ibc_transfer_contract = + IBC_TRANSFER_CONTRACT.load(deps.as_ref().storage).unwrap(); + + // Assert the ibc transfer adapter contract address exists in the blocked contract addresses map + assert!(BLOCKED_CONTRACT_ADDRESSES + .has(deps.as_ref().storage, &stored_ibc_transfer_contract.address)); + + params.swap_venues.into_iter().for_each(|swap_venue| { + // Get stored swap venue adapter contract address + let stored_swap_venue_contract = SWAP_VENUE_MAP + .may_load(deps.as_ref().storage, &swap_venue.name) + .unwrap() + .unwrap(); + + // Assert the swap venue name exists in the map and that + // the adapter contract address stored is correct + assert_eq!(&stored_swap_venue_contract, &swap_venue.adapter_contract); + + // Assert the swap adapter contract address exists in the blocked contract addresses map + assert!(BLOCKED_CONTRACT_ADDRESSES + .has(deps.as_ref().storage, &stored_swap_venue_contract.address)); + }); + } + Err(err) => { + // Assert the test expected an error + assert!( + params.expected_error.is_some(), + "expected test to succeed, but it errored with {:?}", + err + ); + + // Assert the error is correct + assert_eq!(err, params.expected_error.unwrap()); + } + } +} From 8940e429c2a8e77b6e66dcd0dc8f8c6901acea50 Mon Sep 17 00:00:00 2001 From: DrPresident Date: Fri, 20 Dec 2024 14:58:38 -0600 Subject: [PATCH 21/29] error instantiate case --- .../tests/test_instantiate.rs | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/contracts/secret-entry-point/tests/test_instantiate.rs b/contracts/secret-entry-point/tests/test_instantiate.rs index 0bfbd6af..a8d3f682 100644 --- a/contracts/secret-entry-point/tests/test_instantiate.rs +++ b/contracts/secret-entry-point/tests/test_instantiate.rs @@ -47,24 +47,32 @@ struct Params { expected_error: None, }; "Happy Path")] -/* #[test_case( Params { swap_venues: vec![ SwapVenue { - name: "neutron-astroport".to_string(), - adapter_contract_address: "neutron123".to_string(), + name: "shade-swap".to_string(), + adapter_contract: ContractInfo { + address: Addr::unchecked("secret123".to_string()), + code_hash: "code_hash".to_string(), + }, }, SwapVenue { - name: "neutron-astroport".to_string(), - adapter_contract_address: "neutron456".to_string(), + name: "shade-swap".to_string(), + adapter_contract: ContractInfo { + address: Addr::unchecked("secret123".to_string()), + code_hash: "code_hash".to_string(), + }, }, ], - ibc_transfer_contract_address: "ibc_transfer_adapter".to_string(), + ibc_transfer_contract: ContractInfo { + address: Addr::unchecked("ibc_transfer_adapter".to_string()), + code_hash: "code_hash".to_string(), + }, + viewing_key: "viewing_key".to_string(), expected_error: Some(ContractError::DuplicateSwapVenueName), }; "Duplicate Swap Venue Names")] -*/ fn test_instantiate(params: Params) { // Create mock dependencies let mut deps = mock_dependencies(); From 97d7ba187686855f63d49c84fea0863af5674a81 Mon Sep 17 00:00:00 2001 From: DrPresident Date: Fri, 20 Dec 2024 15:17:21 -0600 Subject: [PATCH 22/29] instantiate and reply tests passing --- contracts/secret-entry-point/src/reply.rs | 2 +- .../secret-entry-point/tests/test_reply.rs | 190 ++++++++++++++++++ 2 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 contracts/secret-entry-point/tests/test_reply.rs diff --git a/contracts/secret-entry-point/src/reply.rs b/contracts/secret-entry-point/src/reply.rs index cda4ea26..3c547f78 100644 --- a/contracts/secret-entry-point/src/reply.rs +++ b/contracts/secret-entry-point/src/reply.rs @@ -38,7 +38,7 @@ pub fn reply_swap_and_action_with_recover( return_asset.amount(), None, None, - 255, + 0, return_asset_contract.code_hash.clone(), return_asset_contract.address.to_string(), ) { diff --git a/contracts/secret-entry-point/tests/test_reply.rs b/contracts/secret-entry-point/tests/test_reply.rs new file mode 100644 index 00000000..52e83114 --- /dev/null +++ b/contracts/secret-entry-point/tests/test_reply.rs @@ -0,0 +1,190 @@ +use cosmwasm_std::{ + testing::{mock_dependencies_with_balances, mock_env}, + to_binary, Addr, BankMsg, Coin, ContractInfo, CosmosMsg, Reply, StdError, SubMsg, + SubMsgResponse, SubMsgResult, Uint128, WasmMsg, +}; +use cw20::{Cw20Coin, Cw20ExecuteMsg}; +use secret_skip::{asset::Asset, snip20}; +use skip_go_secret_entry_point::{ + reply::RecoverTempStorage, + state::{RECOVER_TEMP_STORAGE, REGISTERED_TOKENS}, +}; +use test_case::test_case; + +/* +Test Cases: + +Expect Response + - Native Asset Sent On Error + - Native Asset Not Sent On Success + - Cw20 Asset Sent On Error + - Cw20 Asset Not Sent On Success + - Native And Cw20 Asset Sent On Error + +Expect Error + - Verify error on invalid reply id + +*/ + +// Define test parameters +struct Params { + reply: Reply, + expected_error_string: String, + storage: Option, + expected_messages: Vec, +} + +// Test reply +#[test_case( + Params { + reply: Reply { + id: 1, + result: SubMsgResult::Ok(SubMsgResponse { + events: vec![], + data: None, + }), + }, + expected_error_string: "".to_string(), + storage: Some(RecoverTempStorage { + assets: vec![Asset::Native(Coin::new(1_000_000, "osmo"))], + recovery_addr: Addr::unchecked("recovery_addr"), + }), + expected_messages: vec![], + }; + "Native Asset Funds Not Sent On Success" +)] +#[test_case( + Params { + reply: Reply { + id: 1, + result: SubMsgResult::Err("Anything".to_string()), + }, + expected_error_string: "".to_string(), + storage: Some(RecoverTempStorage { + assets: vec![Asset::Cw20(Cw20Coin { + address: "secret123".to_string(), + amount: 1_000_000u128.into(), + })], + recovery_addr: Addr::unchecked("recovery_addr"), + }), + expected_messages: vec![SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: "secret123".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&snip20::ExecuteMsg::Transfer { + recipient: "recovery_addr".to_string(), + amount: 1_000_000u128.into(), + memo: None, + padding: None, + }).unwrap(), + funds: vec![], + }))], + }; + "Cw20 Asset Sent On Error" +)] +#[test_case( + Params { + reply: Reply { + id: 1, + result: SubMsgResult::Ok(SubMsgResponse { + events: vec![], + data: None, + }), + }, + expected_error_string: "".to_string(), + storage: Some(RecoverTempStorage { + assets: vec![Asset::Cw20(Cw20Coin{ + address: "secret123".to_string(), + amount: 1_000_000u128.into(), + })], + recovery_addr: Addr::unchecked("recovery_addr"), + }), + expected_messages: vec![], + }; + "Cw20 Asset Not Sent On Success" +)] +#[test_case( + Params { + reply: Reply { + id: 2, + result: SubMsgResult::Ok(SubMsgResponse { + events: vec![], + data: None, + }), + }, + expected_error_string: "Reply id: 2 not valid".to_string(), + storage: None, + expected_messages: vec![], + }; + "Verify error on invalid reply id" +)] +fn test_reply(params: Params) { + // Create mock dependencies + let mut deps = mock_dependencies_with_balances(&[( + "entry_point", + &[Coin::new(1_000_000, "osmo"), Coin::new(1_000_000, "untrn")], + )]); + + // Create mock env + let env = mock_env(); + + // Update storage + if let Some(recover_temp_storage) = params.storage.clone() { + RECOVER_TEMP_STORAGE + .save(deps.as_mut().storage, &recover_temp_storage) + .unwrap(); + } + + REGISTERED_TOKENS + .save( + deps.as_mut().storage, + Addr::unchecked("secret123"), + &ContractInfo { + address: Addr::unchecked("secret123"), + code_hash: "code_hash".to_string(), + }, + ) + .unwrap(); + + // Call reply with the given test parameters + let res = skip_go_secret_entry_point::contract::reply(deps.as_mut(), env, params.reply); + + // Assert the behavior is correct + match res { + Ok(res) => { + // Assert the test did not expect an error + assert!( + params.expected_error_string.is_empty(), + "expected test to error with {:?}, but it succeeded", + params.expected_error_string + ); + + assert_eq!(res.messages, params.expected_messages); + + // Verify the in progress recover temp storage was removed from storage + match RECOVER_TEMP_STORAGE.load(&deps.storage) { + Ok(recover_temp_storage) => { + panic!( + "expected in progress recover_temp_storage to be removed: {:?}", + recover_temp_storage + ) + } + Err(err) => assert!( + matches!(err, StdError::NotFound { .. }), + "unexpected error: {:?}", + err + ), + }; + } + Err(err) => { + // Assert the test expected an error + assert!( + !params.expected_error_string.is_empty(), + "expected test to succeed, but it errored with {:?}", + err + ); + + // Assert the error is correct + assert_eq!(err.to_string(), params.expected_error_string); + } + } +} From 26942e03753d6b0df495d2f81257b3f946c90146 Mon Sep 17 00:00:00 2001 From: DrPresident Date: Fri, 20 Dec 2024 16:07:08 -0600 Subject: [PATCH 23/29] execute swap tests --- .../shade-protocol/tests/test_execute_swap.rs | 314 ++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100644 contracts/adapters/swap/shade-protocol/tests/test_execute_swap.rs diff --git a/contracts/adapters/swap/shade-protocol/tests/test_execute_swap.rs b/contracts/adapters/swap/shade-protocol/tests/test_execute_swap.rs new file mode 100644 index 00000000..1daa276d --- /dev/null +++ b/contracts/adapters/swap/shade-protocol/tests/test_execute_swap.rs @@ -0,0 +1,314 @@ +use cosmwasm_std::{ + testing::{mock_dependencies, mock_env, mock_info}, + to_binary, Addr, Coin, ContractInfo, + ReplyOn::Never, + SubMsg, WasmMsg, +}; +use cw20::Cw20Coin; +use secret_skip::{ + asset::Asset, + snip20::{self, Snip20ReceiveMsg}, + swap::SwapOperation, +}; +use skip_go_swap_adapter_shade_protocol::{ + error::{ContractError, ContractResult}, + msg::{ExecuteMsg, Snip20HookMsg}, + state::{ENTRY_POINT_CONTRACT, REGISTERED_TOKENS}, +}; +use test_case::test_case; + +/* +Test Cases: + +Expect Success + - One Swap Operation + - Multiple Swap Operations + - No Swap Operations (This is prevented in the entry point contract; and will not add any swap messages to the response) + +Expect Error + - Unauthorized Caller (Only the stored entry point contract can call this function) + - No Coin Sent + - More Than One Coin Sent + + */ + +// Define test parameters +struct Params { + caller: String, + sent_asset: Asset, + swap_operations: Vec, + expected_messages: Vec, + expected_error: Option, +} + +// Test execute_swap +#[test_case( + Params { + caller: "entry_point".to_string(), + sent_asset: Asset::Cw20(Cw20Coin { + address: "secret123".to_string(), + amount: 100u128.into(), + }), + swap_operations: vec![ + SwapOperation { + pool: "pool_1".to_string(), + denom_in: "secret123".to_string(), + denom_out: "secret456".to_string(), + interface: None, + } + ], + expected_messages: vec![ + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "swap_contract_address".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&snip20::ExecuteMsg::Send { + recipient: "shade_router".to_string(), + recipient_code_hash: Some("code_hash".to_string()), + amount: 100u128.into(), + msg: Some(to_binary(&Snip20HookMsg::Swap { + operations: vec![SwapOperation { + pool: "pool_1".to_string(), + denom_in: "secret123".to_string(), + denom_out: "secret456".to_string(), + interface: None, + }], + }).unwrap()), + memo: None, + padding: None, + })?, + funds: vec![], + }.into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "swap_contract_address".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&ExecuteMsg::TransferFundsBack { + return_denom: "secret456".to_string(), + swapper: Addr::unchecked("entry_point"), + })?, + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "One Swap Operation")] +/* +#[test_case( + Params { + caller: "entry_point".to_string(), + sent_asset: Asset::Cw20(Cw20Coin { + address: "secret123".to_string(), + amount: 100u128.into(), + }), + swap_operations: vec![ + SwapOperation { + pool: "pool_1".to_string(), + denom_in: "secret123".to_string(), + denom_out: "secret456".to_string(), + interface: None, + }, + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "secret456".to_string(), + denom_out: "secret789".to_string(), + interface: None, + } + ], + expected_messages: vec![ + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "swap_contract_address".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&ExecuteMsg::AstroportPoolSwap { + operation: SwapOperation { + pool: "pool_1".to_string(), + denom_in: "secret123".to_string(), + denom_out: "secret456".to_string(), + interface: None, + } + })?, + funds: vec![], + }.into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "swap_contract_address".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&ExecuteMsg::AstroportPoolSwap { + operation: SwapOperation { + pool: "pool_2".to_string(), + denom_in: "secret456".to_string(), + denom_out: "secret789".to_string(), + interface: None, + } + })?, + funds: vec![], + }.into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "swap_contract_address".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&ExecuteMsg::TransferFundsBack { + return_denom: "secret789".to_string(), + swapper: Addr::unchecked("entry_point"), + })?, + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "Multiple Swap Operations")] +#[test_case( + Params { + caller: "entry_point".to_string(), + sent_asset: Asset::Cw20(Cw20Coin { + address: "secret123".to_string(), + amount: 100u128.into(), + }), + swap_operations: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::SwapOperationsEmpty), + }; + "No Swap Operations")] +#[test_case( + Params { + caller: "entry_point".to_string(), + sent_asset: Asset::Cw20(Cw20Coin { + address: "secret123".to_string(), + amount: 100u128.into(), + }), + swap_operations: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Payment(cw_utils::PaymentError::NoFunds{})), + }; + "No Coin Sent - Expect Error")] +#[test_case( + Params { + caller: "random".to_string(), + sent_asset: Asset::Cw20(Cw20Coin { + address: "secret123".to_string(), + amount: 100u128.into(), + }), + swap_operations: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Unauthorized), + }; + "Unauthorized Caller - Expect Error")] +*/ +fn test_execute_swap(params: Params) -> ContractResult<()> { + // Create mock dependencies + let mut deps = mock_dependencies(); + + // Create mock env + let mut env = mock_env(); + env.contract.address = Addr::unchecked("swap_contract_address"); + env.contract.code_hash = "code_hash".to_string(); + + // Store the entry point contract address + ENTRY_POINT_CONTRACT.save( + deps.as_mut().storage, + &ContractInfo { + address: Addr::unchecked("entry_point"), + code_hash: "code_hash".to_string(), + }, + )?; + + REGISTERED_TOKENS + .save( + deps.as_mut().storage, + Addr::unchecked("secret123".to_string()), + &ContractInfo { + address: Addr::unchecked("secret123".to_string()), + code_hash: "code_hash".to_string(), + }, + ) + .unwrap(); + + REGISTERED_TOKENS + .save( + deps.as_mut().storage, + Addr::unchecked("secret456".to_string()), + &ContractInfo { + address: Addr::unchecked("secret456".to_string()), + code_hash: "code_hash".to_string(), + }, + ) + .unwrap(); + + REGISTERED_TOKENS + .save( + deps.as_mut().storage, + Addr::unchecked("secret789".to_string()), + &ContractInfo { + address: Addr::unchecked("secret789".to_string()), + code_hash: "code_hash".to_string(), + }, + ) + .unwrap(); + + // Call execute_swap with the given test parameters + let res = skip_go_swap_adapter_shade_protocol::contract::execute( + deps.as_mut(), + env, + mock_info(¶ms.sent_asset.denom(), &vec![]), + ExecuteMsg::Receive(Snip20ReceiveMsg { + sender: Addr::unchecked(params.caller), + from: Addr::unchecked(params.caller), + amount: params.sent_asset.amount(), + memo: None, + msg: Some(to_binary(&Snip20HookMsg::Swap { + operations: params.swap_operations.clone(), + })?), + }), + ); + + // Assert the behavior is correct + match res { + Ok(res) => { + // Assert the test did not expect an error + assert!( + params.expected_error.is_none(), + "expected test to error with {:?}, but it succeeded", + params.expected_error + ); + + // Assert the messages are correct + assert_eq!(res.messages, params.expected_messages); + } + Err(err) => { + // Assert the test expected an error + assert!( + params.expected_error.is_some(), + "expected test to succeed, but it errored with {:?}", + err + ); + + // Assert the error is correct + assert_eq!(err, params.expected_error.unwrap()); + } + } + + Ok(()) +} From e8a171b7bf13b70320f464816d7cb10f95a45aaf Mon Sep 17 00:00:00 2001 From: DrPresident Date: Thu, 26 Dec 2024 07:40:34 -0600 Subject: [PATCH 24/29] transfer funds back query not working yet --- .../swap/shade-protocol/src/contract.rs | 14 +- .../adapters/swap/shade-protocol/src/msg.rs | 10 - .../shade-protocol/tests/test_execute_swap.rs | 29 ++- .../tests/test_execute_transfer_funds_back.rs | 215 ++++++++++++++++++ 4 files changed, 242 insertions(+), 26 deletions(-) create mode 100644 contracts/adapters/swap/shade-protocol/tests/test_execute_transfer_funds_back.rs diff --git a/contracts/adapters/swap/shade-protocol/src/contract.rs b/contracts/adapters/swap/shade-protocol/src/contract.rs index 806ebb41..36ee6519 100644 --- a/contracts/adapters/swap/shade-protocol/src/contract.rs +++ b/contracts/adapters/swap/shade-protocol/src/contract.rs @@ -213,7 +213,6 @@ fn execute_swap( // Get shade router contract from storage let shade_router_contract = SHADE_ROUTER_CONTRACT.load(deps.storage)?; - println!("PATH {:?}", path); // Create a response object to return Ok(Response::new() @@ -266,14 +265,14 @@ fn register_tokens( snip20::set_viewing_key_msg( viewing_key.clone(), None, - 255, + 0, contract.code_hash.clone(), contract.address.to_string(), )?, snip20::register_receive_msg( env.contract.code_hash.clone(), None, - 255, + 0, contract.code_hash.clone(), contract.address.to_string(), )?, @@ -313,7 +312,7 @@ pub fn execute_transfer_funds_back( deps.querier, env.contract.address.to_string(), viewing_key, - 255, + 0, token_contract.code_hash.clone(), token_contract.address.to_string(), ) { @@ -323,13 +322,14 @@ pub fn execute_transfer_funds_back( let entry_point_contract = ENTRY_POINT_CONTRACT.load(deps.storage)?; - let transfer_msg = match snip20::send_msg( + let send_msg = match snip20::send_msg_with_code_hash( entry_point_contract.address.to_string(), + Some(entry_point_contract.code_hash), balance.amount, None, None, None, - 255, + 0, token_contract.code_hash.clone(), token_contract.address.to_string(), ) { @@ -338,7 +338,7 @@ pub fn execute_transfer_funds_back( }; Ok(Response::new() - .add_message(transfer_msg) + .add_message(send_msg) .add_attribute("action", "dispatch_transfer_funds_back_bank_send")) } diff --git a/contracts/adapters/swap/shade-protocol/src/msg.rs b/contracts/adapters/swap/shade-protocol/src/msg.rs index 9e8931cf..c945bd07 100644 --- a/contracts/adapters/swap/shade-protocol/src/msg.rs +++ b/contracts/adapters/swap/shade-protocol/src/msg.rs @@ -40,13 +40,3 @@ pub enum QueryMsg { pub enum Snip20HookMsg { Swap { operations: Vec }, } - -/* -#[cw_serde] -pub struct SwapOperation { - pub pool: String, - pub denom_in: String, - pub denom_out: String, - pub interface: Option, -} -*/ diff --git a/contracts/adapters/swap/shade-protocol/tests/test_execute_swap.rs b/contracts/adapters/swap/shade-protocol/tests/test_execute_swap.rs index 1daa276d..27e5fa15 100644 --- a/contracts/adapters/swap/shade-protocol/tests/test_execute_swap.rs +++ b/contracts/adapters/swap/shade-protocol/tests/test_execute_swap.rs @@ -13,7 +13,8 @@ use secret_skip::{ use skip_go_swap_adapter_shade_protocol::{ error::{ContractError, ContractResult}, msg::{ExecuteMsg, Snip20HookMsg}, - state::{ENTRY_POINT_CONTRACT, REGISTERED_TOKENS}, + shade_swap_router_msg as shade_router, + state::{ENTRY_POINT_CONTRACT, REGISTERED_TOKENS, SHADE_POOL_CODE_HASH, SHADE_ROUTER_CONTRACT}, }; use test_case::test_case; @@ -61,19 +62,19 @@ struct Params { SubMsg { id: 0, msg: WasmMsg::Execute { - contract_addr: "swap_contract_address".to_string(), + contract_addr: "secret123".to_string(), code_hash: "code_hash".to_string(), msg: to_binary(&snip20::ExecuteMsg::Send { recipient: "shade_router".to_string(), recipient_code_hash: Some("code_hash".to_string()), amount: 100u128.into(), - msg: Some(to_binary(&Snip20HookMsg::Swap { - operations: vec![SwapOperation { - pool: "pool_1".to_string(), - denom_in: "secret123".to_string(), - denom_out: "secret456".to_string(), - interface: None, + msg: Some(to_binary(&shade_router::InvokeMsg::SwapTokensForExact { + path: vec![shade_router::Hop { + addr: "pool_1".to_string(), + code_hash: "code_hash".to_string(), }], + expected_return: None, + recipient: None, }).unwrap()), memo: None, padding: None, @@ -234,6 +235,16 @@ fn test_execute_swap(params: Params) -> ContractResult<()> { code_hash: "code_hash".to_string(), }, )?; + SHADE_ROUTER_CONTRACT.save( + deps.as_mut().storage, + &ContractInfo { + address: Addr::unchecked("shade_router".to_string()), + code_hash: "code_hash".to_string(), + }, + )?; + SHADE_POOL_CODE_HASH + .save(deps.as_mut().storage, &"code_hash".to_string()) + .unwrap(); REGISTERED_TOKENS .save( @@ -274,7 +285,7 @@ fn test_execute_swap(params: Params) -> ContractResult<()> { env, mock_info(¶ms.sent_asset.denom(), &vec![]), ExecuteMsg::Receive(Snip20ReceiveMsg { - sender: Addr::unchecked(params.caller), + sender: Addr::unchecked(params.caller.clone()), from: Addr::unchecked(params.caller), amount: params.sent_asset.amount(), memo: None, diff --git a/contracts/adapters/swap/shade-protocol/tests/test_execute_transfer_funds_back.rs b/contracts/adapters/swap/shade-protocol/tests/test_execute_transfer_funds_back.rs new file mode 100644 index 00000000..16ea6090 --- /dev/null +++ b/contracts/adapters/swap/shade-protocol/tests/test_execute_transfer_funds_back.rs @@ -0,0 +1,215 @@ +use cosmwasm_std::{ + testing::{mock_dependencies, mock_env, mock_info}, + to_binary, Addr, BankMsg, Coin, ContractInfo, ContractResult as SystemContractResult, + QuerierResult, + ReplyOn::Never, + SubMsg, SystemResult, Uint128, WasmMsg, WasmQuery, +}; +use cw20::BalanceResponse; +use skip_go_swap_adapter_shade_protocol::{ + error::{ContractError, ContractResult}, + msg::ExecuteMsg, + state::{ENTRY_POINT_CONTRACT, REGISTERED_TOKENS, VIEWING_KEY}, +}; +use test_case::test_case; + +/* +Test Cases: + +Expect Success + - One Coin Balance + - Multiple Coin Balance + - No Coin Balance (This will fail at the bank module if attempted) + +Expect Error + - Unauthorized Caller (Only contract itself can call this function) + */ + +// Define test parameters +struct Params { + caller: String, + contract_balance: Vec, + return_denom: String, + expected_messages: Vec, + expected_error: Option, +} + +// Test execute_transfer_funds_back +#[test_case( + Params { + caller: "swap_contract_address".to_string(), + contract_balance: vec![Coin::new(100, "secret123")], + return_denom: "secret123".to_string(), + expected_messages: vec![ + SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "swapper".to_string(), + amount: vec![Coin::new(100, "secret123")], + }.into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "Transfers One Coin Balance")] +#[test_case( + Params { + caller: "swap_contract_address".to_string(), + contract_balance: vec![ + Coin::new(100, "secret123"), + Coin::new(100, "secret456"), + ], + return_denom: "secret123".to_string(), + expected_messages: vec![ + SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "swapper".to_string(), + amount: vec![ + Coin::new(100, "secret123"), + Coin::new(100, "secret456") + ], + }.into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "Transfers Multiple Coin Balance")] +#[test_case( + Params { + caller: "swap_contract_address".to_string(), + contract_balance: vec![], + return_denom: "secret123".to_string(), + expected_messages: vec![ + SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "swapper".to_string(), + amount: vec![], + }.into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "Transfers No Coin Balance")] +#[test_case( + Params { + caller: "random".to_string(), + contract_balance: vec![], + return_denom: "secret123".to_string(), + expected_messages: vec![ + SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "swapper".to_string(), + amount: vec![], + }.into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: Some(ContractError::Unauthorized), + }; + "Unauthorized Caller")] +fn test_execute_transfer_funds_back(params: Params) -> ContractResult<()> { + // Create mock dependencies + let mut deps = mock_dependencies(); + + let contract_balance = params.contract_balance.clone(); + + // Mock contract balance querys + let wasm_handler = move |query: &WasmQuery| -> QuerierResult { + match query { + WasmQuery::Smart { contract_addr, .. } => { + match contract_balance + .iter() + .find(|coin| coin.denom == *contract_addr) + { + Some(coin) => SystemResult::Ok(SystemContractResult::Ok( + to_binary(&BalanceResponse { + balance: coin.amount.u128().into(), + }) + .unwrap(), + )), + None => panic!("Unsupported contract balance query: {:?}", query), + } + } + _ => panic!("Unsupported query: {:?}", query), + } + }; + + // Update querier with mock wasm handler + deps.querier.update_wasm(wasm_handler); + + // Create mock env + let mut env = mock_env(); + env.contract.address = Addr::unchecked("swap_contract_address"); + env.contract.code_hash = "code_hash".to_string(); + + // Create mock info + let info = mock_info(¶ms.caller.clone(), &[]); + + VIEWING_KEY.save(&mut deps.storage, &"viewing_key".to_string())?; + ENTRY_POINT_CONTRACT.save( + &mut deps.storage, + &ContractInfo { + address: Addr::unchecked("entry_point_contract"), + code_hash: "code_hash".to_string(), + }, + )?; + for coin in params.contract_balance.iter() { + REGISTERED_TOKENS.save( + &mut deps.storage, + Addr::unchecked(coin.denom.clone()), + &ContractInfo { + address: Addr::unchecked(coin.denom.clone()), + code_hash: "code_hash".to_string(), + }, + )?; + } + + // Call execute_swap with the given test parameters + let res = skip_go_swap_adapter_shade_protocol::contract::execute( + deps.as_mut(), + env, + info, + ExecuteMsg::TransferFundsBack { + return_denom: params.return_denom, + swapper: Addr::unchecked("swapper"), + }, + ); + + // Assert the behavior is correct + match res { + Ok(res) => { + // Assert the test did not expect an error + assert!( + params.expected_error.is_none(), + "expected test to error with {:?}, but it succeeded", + params.expected_error + ); + + // Assert the messages are correct + assert_eq!(res.messages, params.expected_messages); + } + Err(err) => { + // Assert the test expected an error + assert!( + params.expected_error.is_some(), + "expected test to succeed, but it errored with {:?}", + err + ); + + // Assert the error is correct + assert_eq!(err, params.expected_error.unwrap()); + } + } + + Ok(()) +} From 9ba48a571338ea7dc76786576a3d639993ca49b4 Mon Sep 17 00:00:00 2001 From: DrPresident Date: Thu, 26 Dec 2024 08:31:34 -0600 Subject: [PATCH 25/29] transfer funds working --- .../tests/test_execute_transfer_funds_back.rs | 74 +++++++++++-------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/contracts/adapters/swap/shade-protocol/tests/test_execute_transfer_funds_back.rs b/contracts/adapters/swap/shade-protocol/tests/test_execute_transfer_funds_back.rs index 16ea6090..74b1f6bf 100644 --- a/contracts/adapters/swap/shade-protocol/tests/test_execute_transfer_funds_back.rs +++ b/contracts/adapters/swap/shade-protocol/tests/test_execute_transfer_funds_back.rs @@ -6,6 +6,7 @@ use cosmwasm_std::{ SubMsg, SystemResult, Uint128, WasmMsg, WasmQuery, }; use cw20::BalanceResponse; +use secret_skip::snip20; use skip_go_swap_adapter_shade_protocol::{ error::{ContractError, ContractResult}, msg::ExecuteMsg, @@ -43,9 +44,18 @@ struct Params { expected_messages: vec![ SubMsg { id: 0, - msg: BankMsg::Send { - to_address: "swapper".to_string(), - amount: vec![Coin::new(100, "secret123")], + msg: WasmMsg::Execute { + contract_addr: "secret123".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&snip20::ExecuteMsg::Send { + recipient: "swapper".to_string(), + recipient_code_hash: None, + amount: 100u128.into(), + msg: None, + memo: None, + padding: None, + })?, + funds: vec![], }.into(), gas_limit: None, reply_on: Never, @@ -54,6 +64,7 @@ struct Params { expected_error: None, }; "Transfers One Coin Balance")] +/* #[test_case( Params { caller: "swap_contract_address".to_string(), @@ -65,12 +76,18 @@ struct Params { expected_messages: vec![ SubMsg { id: 0, - msg: BankMsg::Send { - to_address: "swapper".to_string(), - amount: vec![ - Coin::new(100, "secret123"), - Coin::new(100, "secret456") - ], + msg: WasmMsg::Execute { + contract_addr: "secret123".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&snip20::ExecuteMsg::Send { + recipient: "swapper".to_string(), + recipient_code_hash: None, + amount: 100u128.into(), + msg: None, + memo: None, + padding: None, + })?, + funds: vec![], }.into(), gas_limit: None, reply_on: Never, @@ -79,25 +96,7 @@ struct Params { expected_error: None, }; "Transfers Multiple Coin Balance")] -#[test_case( - Params { - caller: "swap_contract_address".to_string(), - contract_balance: vec![], - return_denom: "secret123".to_string(), - expected_messages: vec![ - SubMsg { - id: 0, - msg: BankMsg::Send { - to_address: "swapper".to_string(), - amount: vec![], - }.into(), - gas_limit: None, - reply_on: Never, - }, - ], - expected_error: None, - }; - "Transfers No Coin Balance")] +*/ #[test_case( Params { caller: "random".to_string(), @@ -132,12 +131,17 @@ fn test_execute_transfer_funds_back(params: Params) -> ContractResult<()> { .find(|coin| coin.denom == *contract_addr) { Some(coin) => SystemResult::Ok(SystemContractResult::Ok( - to_binary(&BalanceResponse { - balance: coin.amount.u128().into(), + to_binary(&snip20::QueryResponse::Balance { + amount: coin.amount.u128().into(), + }) + .unwrap(), + )), + None => SystemResult::Ok(SystemContractResult::Ok( + to_binary(&snip20::QueryResponse::Balance { + amount: 0u128.into(), }) .unwrap(), )), - None => panic!("Unsupported contract balance query: {:?}", query), } } _ => panic!("Unsupported query: {:?}", query), @@ -163,6 +167,14 @@ fn test_execute_transfer_funds_back(params: Params) -> ContractResult<()> { code_hash: "code_hash".to_string(), }, )?; + REGISTERED_TOKENS.save( + &mut deps.storage, + Addr::unchecked(params.return_denom.clone()), + &ContractInfo { + address: Addr::unchecked(params.return_denom.clone()), + code_hash: "code_hash".to_string(), + }, + )?; for coin in params.contract_balance.iter() { REGISTERED_TOKENS.save( &mut deps.storage, From fc5f5df155b09eda02d0cbe32cf15fd3edeb3638 Mon Sep 17 00:00:00 2001 From: DrPresident Date: Mon, 6 Jan 2025 15:05:34 -0600 Subject: [PATCH 26/29] fixed builds with secret contracts --- Cargo.lock | 1522 +------------ Cargo.toml | 26 +- .../ibc/secret-ibc-hooks/src/contract.rs | 2 +- .../tests/test_execute_ibc_transfer.rs | 2 +- .../adapters/swap/shade-protocol/Cargo.toml | 6 +- .../swap/shade-protocol/src/contract.rs | 12 +- .../adapters/swap/shade-protocol/src/error.rs | 4 +- .../tests/test_execute_receive.rs | 7 +- .../shade-protocol/tests/test_execute_swap.rs | 2 +- .../tests/test_execute_transfer_funds_back.rs | 1 - contracts/secret-entry-point/Cargo.toml | 4 +- contracts/secret-entry-point/src/error.rs | 3 +- contracts/secret-entry-point/src/execute.rs | 2 +- .../tests/test_execute_action.rs | 6 +- .../tests/test_execute_post_swap_action.rs | 723 ++++++ .../tests/test_execute_receive.rs | 2 +- .../tests/test_execute_swap_and_action.rs | 1969 +++++++++++++++++ ...st_execute_swap_and_action_with_recover.rs | 447 ++++ .../secret-entry-point/tests/test_reply.rs | 3 +- .../tests/test_user_swap.rs | 1282 +++++++++++ packages/secret-skip/Cargo.toml | 4 +- packages/secret-skip/src/asset.rs | 10 +- packages/secret-skip/src/cw20.rs | 39 + packages/secret-skip/src/entry_point.rs | 4 +- packages/secret-skip/src/error.rs | 9 +- packages/secret-skip/src/lib.rs | 1 + packages/secret-skip/src/snip20.rs | 14 +- 27 files changed, 4618 insertions(+), 1488 deletions(-) create mode 100644 contracts/secret-entry-point/tests/test_execute_post_swap_action.rs create mode 100644 contracts/secret-entry-point/tests/test_execute_swap_and_action.rs create mode 100644 contracts/secret-entry-point/tests/test_execute_swap_and_action_with_recover.rs create mode 100644 contracts/secret-entry-point/tests/test_user_swap.rs create mode 100644 packages/secret-skip/src/cw20.rs diff --git a/Cargo.lock b/Cargo.lock index db6be416..40d15b99 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,79 +13,11 @@ dependencies = [ "version_check", ] -[[package]] -name = "anstyle" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" - [[package]] name = "anyhow" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" - -[[package]] -name = "astroport" -version = "2.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d102b618016b3c1f1ebb5750617a73dbd294a3c941e54b12deabc931d771bc6e" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.15.1", - "cw-utils 0.15.1", - "cw20 0.15.1", - "itertools 0.10.5", - "uint", -] - -[[package]] -name = "astroport" -version = "3.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdebdf96895f363e121710cb84bbbfa659cea1bb1470260d4976d1a7206b3b16" -dependencies = [ - "astroport-circular-buffer", - "cosmwasm-schema", - "cosmwasm-std", - "cw-asset", - "cw-storage-plus 0.15.1", - "cw-utils 1.0.3", - "cw20 0.15.1", - "cw3", - "itertools 0.10.5", - "uint", -] - -[[package]] -name = "astroport-circular-buffer" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c7369d3c4126804f861620db2221c15b5fa7b7718f12180e265b087c933fb6" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.15.1", - "thiserror", -] - -[[package]] -name = "astrovault" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a16c325554ef0760871dc4a0c2a3d527b5ba8559a4b7ad79a24753f1ba99805" -dependencies = [ - "bigint", - "cosmwasm-schema", - "cosmwasm-std", - "cw20 1.1.2", - "cw721 0.16.0", - "schemars", - "serde", - "sha2 0.10.8", - "thiserror", -] +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "autocfg" @@ -129,16 +61,6 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" -[[package]] -name = "bigint" -version = "4.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0e8c8a600052b52482eff2cf4d810e462fdff1f656ac1ecb6232132a1ed7def" -dependencies = [ - "byteorder", - "crunchy 0.1.6", -] - [[package]] name = "bincode2" version = "2.0.1" @@ -194,68 +116,28 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chrono" -version = "0.4.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" -dependencies = [ - "num-traits", -] - [[package]] name = "const-oid" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "const_format" -version = "0.2.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" -dependencies = [ - "const_format_proc_macros", -] - -[[package]] -name = "const_format_proc_macros" -version = "0.2.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - [[package]] name = "cosmos-sdk-proto" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73c9d2043a9e617b0d602fbc0a0ecd621568edbf3a9774890a6d562389bd8e1c" dependencies = [ - "prost 0.11.9", - "prost-types 0.11.9", - "tendermint-proto 0.32.2", -] - -[[package]] -name = "cosmos-sdk-proto" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32560304ab4c365791fd307282f76637213d8083c1a98490c35159cd67852237" -dependencies = [ - "prost 0.12.6", - "prost-types 0.12.6", - "tendermint-proto 0.34.1", + "prost", + "prost-types", + "tendermint-proto", ] [[package]] name = "cosmwasm-crypto" -version = "1.5.8" +version = "1.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58535cbcd599b3c193e3967c8292fe1dbbb5de7c2a2d87380661091dd4744044" +checksum = "eba94b9f3fb79b9f1101b3e0c61995a557828e2c0d3f5579c1d0bfbea333c19e" dependencies = [ "digest 0.10.7", "ed25519-zebra", @@ -266,18 +148,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.5.8" +version = "1.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8e07de16c800ac82fd188d055ecdb923ead0cf33960d3350089260bb982c09f" +checksum = "d67457e4acb04e738788d3489e343957455df2c4643f2b53050eb052ca631d19" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.5.8" +version = "1.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d388adfa9cb449557a92e9318121ac1a481fc4f599213b03a5b62699b403b4" +checksum = "13bf06bf1c7ea737f6b3d955d9cabeb8cbbe4dcb8dea392e30f6fab4493a4b7a" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -288,9 +170,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.5.8" +version = "1.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2411b389e56e6484f81ba955b758d02522d620c98fc960c4bd2251d48b7aa19f" +checksum = "077fe378f16b54e3d0a57adb3f39a65bcf7bdbda6a5eade2f8ba7755c2fb1250" dependencies = [ "proc-macro2", "quote", @@ -299,9 +181,9 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.5.8" +version = "1.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c21fde95ccd20044a23c0ac6fd8c941f3e8c158169dc94b5aa6491a2d9551a8d" +checksum = "3745e9fd9aad96236c3d6f1a6d844249ed3bb6b92fcdae16d8fe067c7a5121e8" dependencies = [ "base64 0.21.7", "bech32", @@ -319,16 +201,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "cosmwasm-storage" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66de2ab9db04757bcedef2b5984fbe536903ada4a8a9766717a4a71197ef34f6" -dependencies = [ - "cosmwasm-std", - "serde", -] - [[package]] name = "cpufeatures" version = "0.2.16" @@ -338,12 +210,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crunchy" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" - [[package]] name = "crunchy" version = "0.2.2" @@ -397,97 +263,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "cw-address-like" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451a4691083a88a3c0630a8a88799e9d4cd6679b7ce8ff22b8da2873ff31d380" -dependencies = [ - "cosmwasm-std", -] - -[[package]] -name = "cw-asset" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c999a12f8cd8736f6f86e9a4ede5905530cb23cfdef946b9da1c506ad1b70799" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-address-like", - "cw-storage-plus 1.2.0", - "cw20 1.1.2", - "thiserror", -] - -[[package]] -name = "cw-multi-test" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc392a5cb7e778e3f90adbf7faa43c4db7f35b6623224b08886d796718edb875" -dependencies = [ - "anyhow", - "bech32", - "cosmwasm-std", - "cw-storage-plus 1.2.0", - "cw-utils 1.0.3", - "derivative", - "itertools 0.12.1", - "prost 0.12.6", - "schemars", - "serde", - "sha2 0.10.8", - "thiserror", -] - -[[package]] -name = "cw-ownable" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093dfb4520c48b5848274dd88ea99e280a04bc08729603341c7fb0d758c74321" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-address-like", - "cw-ownable-derive", - "cw-storage-plus 1.2.0", - "cw-utils 1.0.3", - "thiserror", -] - -[[package]] -name = "cw-ownable-derive" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d3bf2e0f341bb6cc100d7d441d31cf713fbd3ce0c511f91e79f14b40a889af" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "cw-storage-plus" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6cf70ef7686e2da9ad7b067c5942cd3e88dd9453f7af42f54557f8af300fb0" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", -] - -[[package]] -name = "cw-storage-plus" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b6f91c0b94481a3e9ef1ceb183c37d00764f8751e39b45fc09f4d9b970d469" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", -] - [[package]] name = "cw-storage-plus" version = "1.2.0" @@ -499,36 +274,6 @@ dependencies = [ "serde", ] -[[package]] -name = "cw-utils" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ae0b69fa7679de78825b4edeeec045066aa2b2c4b6e063d80042e565bb4da5c" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw2 0.15.1", - "schemars", - "semver", - "serde", - "thiserror", -] - -[[package]] -name = "cw-utils" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6a84c6c1c0acc3616398eba50783934bd6c964bad6974241eaee3460c8f5b26" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw2 0.16.0", - "schemars", - "semver", - "serde", - "thiserror", -] - [[package]] name = "cw-utils" version = "1.0.3" @@ -537,39 +282,13 @@ checksum = "1c4a657e5caacc3a0d00ee96ca8618745d050b8f757c709babafb81208d4239c" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw2 1.1.2", + "cw2", "schemars", "semver", "serde", "thiserror", ] -[[package]] -name = "cw2" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5abb8ecea72e09afff830252963cb60faf945ce6cef2c20a43814516082653da" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.15.1", - "schemars", - "serde", -] - -[[package]] -name = "cw2" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91398113b806f4d2a8d5f8d05684704a20ffd5968bf87e3473e1973710b884ad" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.16.0", - "schemars", - "serde", -] - [[package]] name = "cw2" version = "1.1.2" @@ -578,26 +297,13 @@ checksum = "c6c120b24fbbf5c3bedebb97f2cc85fbfa1c3287e09223428e7e597b5293c1fa" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus 1.2.0", + "cw-storage-plus", "schemars", "semver", "serde", "thiserror", ] -[[package]] -name = "cw20" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6025276fb6e603e974c21f3e4606982cdc646080e8fba3198816605505e1d9a" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-utils 0.15.1", - "schemars", - "serde", -] - [[package]] name = "cw20" version = "1.1.2" @@ -606,105 +312,11 @@ checksum = "526e39bb20534e25a1cd0386727f0038f4da294e5e535729ba3ef54055246abd" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-utils 1.0.3", + "cw-utils", "schemars", "serde", ] -[[package]] -name = "cw20-base" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ad79e86ea3707229bf78df94e08732e8f713207b4a77b2699755596725e7d9" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 1.2.0", - "cw2 1.1.2", - "cw20 1.1.2", - "schemars", - "semver", - "serde", - "thiserror", -] - -[[package]] -name = "cw3" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2967fbd073d4b626dd9e7148e05a84a3bebd9794e71342e12351110ffbb12395" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-utils 1.0.3", - "cw20 1.1.2", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "cw721" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94a1ea6e6277bdd6dfc043a9b1380697fe29d6e24b072597439523658d21d791" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-utils 0.16.0", - "schemars", - "serde", -] - -[[package]] -name = "cw721" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c4d286625ccadc957fe480dd3bdc54ada19e0e6b5b9325379db3130569e914" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-utils 1.0.3", - "schemars", - "serde", -] - -[[package]] -name = "cw721-base" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77518e27431d43214cff4cdfbd788a7508f68d9b1f32389e6fce513e7eaccbef" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.16.0", - "cw-utils 0.16.0", - "cw2 0.16.0", - "cw721 0.16.0", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "cw721-base" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da518d9f68bfda7d972cbaca2e8fcf04651d0edc3de72b04ae2bcd9289c81614" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-ownable", - "cw-storage-plus 1.2.0", - "cw-utils 1.0.3", - "cw2 1.1.2", - "cw721 0.18.0", - "cw721-base 0.16.0", - "schemars", - "serde", - "thiserror", -] - [[package]] name = "der" version = "0.6.1" @@ -731,278 +343,39 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ - "powerfmt", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "dexter" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27ebda8ed00010da0d68688fff006ca6a7df9c1e1313ef570ac71015288dde4" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 1.2.0", - "cw20 1.1.2", - "cw20-base", - "itertools 0.10.5", - "protobuf 2.28.0", - "schemars", - "serde", - "thiserror", - "uint", -] - -[[package]] -name = "dexter-lp-token" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143d553fd974bfb90862b379d9b459adfe29c5a261c9fa15aadd3aca404b47f3" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw2 1.1.2", - "cw20 1.1.2", - "cw20-base", - "dexter", - "snafu", -] - -[[package]] -name = "dexter-router" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479e68c1739b01b7d4baa05b0406f91e6b146b82f840d9101b0bf54e58a69382" -dependencies = [ - "const_format", - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 1.2.0", - "cw2 1.1.2", - "cw20 1.1.2", - "cw20-base", - "dexter", - "schemars", - "serde", - "serde-json-wasm 0.5.2", - "thiserror", -] - -[[package]] -name = "dexter-stable-pool" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2161888b4ea26f670bee45487f9f7dbb73ea2081537d0dfec7ee75a9a2c9cc44" -dependencies = [ - "const_format", - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 1.2.0", - "cw2 1.1.2", - "cw20 1.1.2", - "dexter", - "integer-sqrt", - "itertools 0.10.5", - "protobuf 2.28.0", - "schemars", - "serde", - "serde-json-wasm 0.5.2", - "thiserror", -] - -[[package]] -name = "dexter-vault" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a47906dd22d8baf86129c269e75029954baf950a8b1a7ab32f7d614ae6a8597" -dependencies = [ - "const_format", - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 1.2.0", - "cw2 1.1.2", - "cw20 1.1.2", - "dexter", - "itertools 0.10.5", - "protobuf 2.28.0", - "schemars", - "serde", - "serde-json-wasm 0.5.2", - "thiserror", -] - -[[package]] -name = "dexter-weighted-pool" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef54b4987392026a78bf5204c4d5bb9a8767aefb5fa988ce3809cbf01ddea525" -dependencies = [ - "const_format", - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 1.2.0", - "cw2 1.1.2", - "cw20 1.1.2", - "dexter", - "integer-sqrt", - "protobuf 2.28.0", - "schemars", - "serde", - "serde-json-wasm 0.5.2", - "thiserror", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer 0.10.4", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - -[[package]] -name = "downcast" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" - -[[package]] -name = "drop-factory" -version = "1.0.0" -source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#318f0d8d24e1f90f33987ff8da6e2e6f3c9fc62f" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-ownable", - "cw-storage-plus 1.2.0", - "cw2 1.1.2", - "drop-helpers", - "drop-macros", - "drop-puppeteer-base", - "drop-staking-base", - "neutron-sdk 0.10.0 (git+https://github.com/neutron-org/neutron-sdk?branch=feat/proposal-votes)", - "semver", - "thiserror", -] - -[[package]] -name = "drop-helpers" -version = "1.0.0" -source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#318f0d8d24e1f90f33987ff8da6e2e6f3c9fc62f" -dependencies = [ - "cosmos-sdk-proto 0.20.0", - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 1.2.0", - "hex", - "neutron-sdk 0.10.0 (git+https://github.com/neutron-org/neutron-sdk?branch=feat/proposal-votes)", - "once_cell", - "prost 0.12.6", - "schemars", - "serde", - "serde-json-wasm 1.0.1", - "sha3", - "thiserror", + "powerfmt", ] [[package]] -name = "drop-macros" -version = "1.0.0" -source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#318f0d8d24e1f90f33987ff8da6e2e6f3c9fc62f" +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", "proc-macro2", "quote", "syn 1.0.109", ] [[package]] -name = "drop-proto" -version = "1.0.0" -source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#318f0d8d24e1f90f33987ff8da6e2e6f3c9fc62f" -dependencies = [ - "cosmwasm-std", - "prost 0.12.6", - "prost-types 0.12.6", - "tendermint-proto 0.34.1", -] - -[[package]] -name = "drop-puppeteer-base" -version = "1.0.0" -source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#318f0d8d24e1f90f33987ff8da6e2e6f3c9fc62f" +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "cosmos-sdk-proto 0.20.0", - "cosmwasm-schema", - "cosmwasm-std", - "cw-ownable", - "cw-storage-plus 1.2.0", - "drop-helpers", - "neutron-sdk 0.10.0 (git+https://github.com/neutron-org/neutron-sdk?branch=feat/proposal-votes)", - "prost 0.12.6", - "prost-types 0.12.6", - "schemars", - "semver", - "serde", - "serde-json-wasm 1.0.1", - "thiserror", + "generic-array", ] [[package]] -name = "drop-staking-base" -version = "1.0.0" -source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#318f0d8d24e1f90f33987ff8da6e2e6f3c9fc62f" +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "astroport 3.12.2", - "cosmos-sdk-proto 0.20.0", - "cosmwasm-schema", - "cosmwasm-std", - "cw-ownable", - "cw-storage-plus 1.2.0", - "cw-utils 1.0.3", - "cw721 0.18.0", - "cw721-base 0.18.0", - "drop-helpers", - "drop-macros", - "drop-proto", - "drop-puppeteer-base", - "neutron-sdk 0.10.0 (git+https://github.com/neutron-org/neutron-sdk?branch=feat/proposal-votes)", - "optfield", - "prost 0.12.6", - "semver", - "serde", - "thiserror", + "block-buffer 0.10.4", + "const-oid", + "crypto-common", + "subtle", ] [[package]] @@ -1132,12 +505,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" -[[package]] -name = "fragile" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" - [[package]] name = "generic-array" version = "0.14.7" @@ -1191,12 +558,6 @@ dependencies = [ "ahash", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "hex" version = "0.4.3" @@ -1212,27 +573,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "hpl-interface" -version = "0.0.7" -source = "git+https://github.com/many-things/cw-hyperlane.git?branch=main#d07e55e17c791a5f6557f114e3fb6cb433d9b800" -dependencies = [ - "bech32", - "cosmwasm-schema", - "cosmwasm-std", - "cosmwasm-storage", - "cw-storage-plus 1.2.0", - "cw2 1.1.2", - "cw20 1.1.2", - "cw20-base", - "ripemd", - "schemars", - "serde", - "sha2 0.10.8", - "sha3", - "thiserror", -] - [[package]] name = "ibc-proto" version = "0.32.1" @@ -1243,10 +583,10 @@ dependencies = [ "bytes", "flex-error", "ics23", - "prost 0.11.9", + "prost", "serde", "subtle-encoding", - "tendermint-proto 0.32.2", + "tendermint-proto", ] [[package]] @@ -1258,16 +598,7 @@ dependencies = [ "anyhow", "bytes", "hex", - "prost 0.11.9", -] - -[[package]] -name = "integer-sqrt" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" -dependencies = [ - "num-traits", + "prost", ] [[package]] @@ -1279,15 +610,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.14" @@ -1320,39 +642,11 @@ dependencies = [ "signature 2.2.0", ] -[[package]] -name = "keccak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - [[package]] name = "libc" -version = "0.2.168" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" - -[[package]] -name = "lido-satellite" -version = "0.1.0" -source = "git+https://github.com/hadronlabs-org/lido-satellite?branch=main#3fe0e145d7975f949b6519284ddafcfeadabf443" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 1.2.0", - "cw2 1.1.2", - "neutron-sdk 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror", -] +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "memchr" @@ -1360,89 +654,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "mockall" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43766c2b5203b10de348ffe19f7e54564b64f3d6018ff7648d1e2d6d3a0f0a48" -dependencies = [ - "cfg-if", - "downcast", - "fragile", - "lazy_static", - "mockall_derive", - "predicates", - "predicates-tree", -] - -[[package]] -name = "mockall_derive" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cbce79ec385a1d4f54baa90a76401eb15d9cab93685f62e7e9f942aa00ae2" -dependencies = [ - "cfg-if", - "proc-macro2", - "quote", - "syn 2.0.90", -] - -[[package]] -name = "neutron-proto" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943ccd9f9749e96095237502e1c61abc867fd5cf28668097b359b0d61aa50c96" -dependencies = [ - "cosmos-sdk-proto 0.19.0", - "cosmwasm-std", - "prost 0.11.9", - "prost-types 0.11.9", - "tendermint-proto 0.32.2", -] - -[[package]] -name = "neutron-sdk" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fb513aae0c82b3185228e96664d8312e79c3aa763f6ebdc70cf4b8d513d533" -dependencies = [ - "bech32", - "cosmos-sdk-proto 0.20.0", - "cosmwasm-schema", - "cosmwasm-std", - "prost 0.12.6", - "prost-types 0.12.6", - "protobuf 3.7.1", - "schemars", - "serde", - "serde-json-wasm 1.0.1", - "serde_json", - "speedate", - "tendermint-proto 0.34.1", - "thiserror", -] - -[[package]] -name = "neutron-sdk" -version = "0.10.0" -source = "git+https://github.com/neutron-org/neutron-sdk?branch=feat/proposal-votes#2dfca1734dfc4cfba478b3067417648733d66cd9" -dependencies = [ - "bech32", - "cosmos-sdk-proto 0.20.0", - "cosmwasm-schema", - "cosmwasm-std", - "prost 0.12.6", - "prost-types 0.12.6", - "protobuf 3.7.1", - "schemars", - "serde", - "serde-json-wasm 1.0.1", - "serde_json", - "speedate", - "tendermint-proto 0.34.1", - "thiserror", -] - [[package]] name = "num-conv" version = "0.1.0" @@ -1481,45 +692,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" -[[package]] -name = "optfield" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa59f025cde9c698fcb4fcb3533db4621795374065bee908215263488f2d2a1d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - -[[package]] -name = "osmosis-std" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87725a7480b98887167edf878daa52201a13322ad88e34355a7f2ddc663e047e" -dependencies = [ - "chrono", - "cosmwasm-std", - "osmosis-std-derive", - "prost 0.11.9", - "prost-types 0.11.9", - "schemars", - "serde", - "serde-cw-value", -] - -[[package]] -name = "osmosis-std-derive" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4d482a16be198ee04e0f94e10dd9b8d02332dcf33bc5ea4b255e7e25eedc5df" -dependencies = [ - "itertools 0.10.5", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "paste" version = "1.0.15" @@ -1548,173 +720,56 @@ dependencies = [ [[package]] name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "predicates" -version = "3.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" -dependencies = [ - "anstyle", - "predicates-core", -] - -[[package]] -name = "predicates-core" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" - -[[package]] -name = "predicates-tree" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" -dependencies = [ - "predicates-core", - "termtree", -] - -[[package]] -name = "proc-macro2" -version = "1.0.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "prost" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" -dependencies = [ - "bytes", - "prost-derive 0.11.9", -] - -[[package]] -name = "prost" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" -dependencies = [ - "bytes", - "prost-derive 0.12.6", -] - -[[package]] -name = "prost-derive" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" -dependencies = [ - "anyhow", - "itertools 0.10.5", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "prost-derive" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" -dependencies = [ - "anyhow", - "itertools 0.12.1", - "proc-macro2", - "quote", - "syn 2.0.90", -] - -[[package]] -name = "prost-types" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" -dependencies = [ - "prost 0.11.9", -] - -[[package]] -name = "prost-types" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" -dependencies = [ - "prost 0.12.6", -] - -[[package]] -name = "protobuf" -version = "2.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" -dependencies = [ - "bytes", -] - -[[package]] -name = "protobuf" -version = "3.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3a7c64d9bf75b1b8d981124c14c179074e8caa7dfe7b6a12e6222ddcd0c8f72" -dependencies = [ - "bytes", - "once_cell", - "protobuf-support", - "thiserror", -] +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] -name = "protobuf-support" -version = "3.7.1" +name = "proc-macro2" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b088fd20b938a875ea00843b6faf48579462630015c3788d397ad6a786663252" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ - "thiserror", + "unicode-ident", ] [[package]] -name = "pryzm-std" -version = "0.1.8" +name = "prost" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d295897f6a1a3a05ef5b50794590bee0bc29cb2afb5d2117ef07aafd995d5a" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ - "chrono", - "cosmwasm-std", - "prost 0.12.6", - "prost-types 0.12.6", - "pryzm-std-derive", - "schemars", - "serde", - "serde-cw-value", + "bytes", + "prost-derive", ] [[package]] -name = "pryzm-std-derive" -version = "0.1.2" +name = "prost-derive" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27015d082aa4edc995247d0f2fa81b53d6b4e116ed06e3532c162cd34d3bc010" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ - "itertools 0.10.5", + "anyhow", + "itertools", "proc-macro2", - "prost-types 0.11.9", "quote", "syn 1.0.109", ] +[[package]] +name = "prost-types" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +dependencies = [ + "prost", +] + [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -1755,21 +810,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "ripemd" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "rustversion" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" - [[package]] name = "ryu" version = "1.0.18" @@ -1797,7 +837,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] @@ -1874,10 +914,8 @@ dependencies = [ name = "secret-skip" version = "0.3.0" dependencies = [ - "cosmos-sdk-proto 0.19.0", + "cosmos-sdk-proto", "cosmwasm-schema", - "cw-utils 1.0.3", - "cw20 1.1.2", "ibc-proto", "secret-cosmwasm-std", "secret-storage-plus", @@ -1965,15 +1003,15 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" [[package]] name = "serde" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] @@ -2025,13 +1063,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] @@ -2042,14 +1080,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] name = "serde_json" -version = "1.0.133" +version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" dependencies = [ "itoa", "memchr", @@ -2081,16 +1119,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest 0.10.7", - "keccak", -] - [[package]] name = "signature" version = "1.6.4" @@ -2111,111 +1139,18 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "skip" -version = "0.3.0" -dependencies = [ - "astroport 2.9.5", - "cosmos-sdk-proto 0.19.0", - "cosmwasm-schema", - "cosmwasm-std", - "cw-utils 1.0.3", - "cw20 1.1.2", - "ibc-proto", - "neutron-proto", - "osmosis-std", - "thiserror", - "white-whale-std", -] - -[[package]] -name = "skip-go-entry-point" -version = "0.3.0" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 1.2.0", - "cw-utils 1.0.3", - "cw2 1.1.2", - "cw20 1.1.2", - "skip", - "test-case", - "thiserror", -] - -[[package]] -name = "skip-go-hyperlane-adapter" -version = "0.3.0" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 1.2.0", - "cw-utils 1.0.3", - "cw2 1.1.2", - "hpl-interface", - "prost 0.11.9", - "serde-cw-value", - "serde-json-wasm 1.0.1", - "skip", - "test-case", - "thiserror", -] - -[[package]] -name = "skip-go-ibc-adapter-ibc-hooks" -version = "0.3.0" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 1.2.0", - "cw2 1.1.2", - "ibc-proto", - "prost 0.11.9", - "serde-cw-value", - "serde-json-wasm 1.0.1", - "skip", - "test-case", - "thiserror", -] - -[[package]] -name = "skip-go-ibc-adapter-neutron-transfer" -version = "0.3.0" -dependencies = [ - "cosmos-sdk-proto 0.19.0", - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 1.2.0", - "cw2 1.1.2", - "neutron-proto", - "neutron-sdk 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "prost 0.11.9", - "skip", - "test-case", - "thiserror", -] - -[[package]] -name = "skip-go-placeholder" -version = "0.3.0" -dependencies = [ - "cosmwasm-std", - "cw2 1.1.2", -] - [[package]] name = "skip-go-secret-entry-point" version = "0.3.0" dependencies = [ "cosmwasm-schema", - "cw-utils 1.0.3", - "cw20 1.1.2", "schemars", "secret-cosmwasm-std", "secret-skip", "secret-storage-plus", "secret-toolkit", "serde", + "serde_json", "skip-go-swap-adapter-shade-protocol", "test-case", "thiserror", @@ -2226,10 +1161,10 @@ name = "skip-go-secret-ibc-adapter-ibc-hooks" version = "0.3.0" dependencies = [ "cosmwasm-schema", - "cw2 1.1.2", - "cw20 1.1.2", + "cw2", + "cw20", "ibc-proto", - "prost 0.11.9", + "prost", "schemars", "secret-cosmwasm-std", "secret-skip", @@ -2242,163 +1177,11 @@ dependencies = [ "thiserror", ] -[[package]] -name = "skip-go-swap-adapter-astroport" -version = "0.3.0" -dependencies = [ - "astroport 2.9.5", - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 1.2.0", - "cw-utils 1.0.3", - "cw2 1.1.2", - "cw20 1.1.2", - "skip", - "test-case", - "thiserror", -] - -[[package]] -name = "skip-go-swap-adapter-astrovault" -version = "0.3.0" -dependencies = [ - "astrovault", - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 1.2.0", - "cw-utils 1.0.3", - "cw2 1.1.2", - "cw20 1.1.2", - "skip", - "test-case", - "thiserror", -] - -[[package]] -name = "skip-go-swap-adapter-dexter" -version = "0.3.0" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-multi-test", - "cw-storage-plus 1.2.0", - "cw-utils 1.0.3", - "cw2 1.1.2", - "cw20 1.1.2", - "dexter", - "dexter-lp-token", - "dexter-router", - "dexter-stable-pool", - "dexter-vault", - "dexter-weighted-pool", - "skip", - "test-case", - "thiserror", -] - -[[package]] -name = "skip-go-swap-adapter-drop" -version = "0.3.0" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 1.2.0", - "cw-utils 1.0.3", - "cw2 1.1.2", - "drop-factory", - "drop-staking-base", - "skip", - "test-case", - "thiserror", -] - -[[package]] -name = "skip-go-swap-adapter-duality" -version = "0.3.0" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 1.2.0", - "cw-utils 1.0.3", - "cw2 1.1.2", - "cw20 1.1.2", - "neutron-sdk 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "skip", - "test-case", - "thiserror", -] - -[[package]] -name = "skip-go-swap-adapter-hallswap" -version = "0.3.0" -dependencies = [ - "astroport 2.9.5", - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 1.2.0", - "cw-utils 1.0.3", - "cw2 1.1.2", - "cw20 1.1.2", - "skip", - "test-case", - "thiserror", -] - -[[package]] -name = "skip-go-swap-adapter-lido-satellite" -version = "0.3.0" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 1.2.0", - "cw-utils 1.0.3", - "cw2 1.1.2", - "lido-satellite", - "skip", - "test-case", - "thiserror", -] - -[[package]] -name = "skip-go-swap-adapter-osmosis-poolmanager" -version = "0.3.0" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 1.2.0", - "cw-utils 1.0.3", - "cw2 1.1.2", - "osmosis-std", - "skip", - "test-case", - "thiserror", -] - -[[package]] -name = "skip-go-swap-adapter-pryzm" -version = "0.3.0" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 1.2.0", - "cw-utils 1.0.3", - "cw2 1.1.2", - "mockall", - "pryzm-std", - "serde", - "skip", - "test-case", - "thiserror", -] - [[package]] name = "skip-go-swap-adapter-shade-protocol" version = "0.3.0" dependencies = [ "cosmwasm-schema", - "cw-utils 1.0.3", - "cw2 1.1.2", - "cw20 1.1.2", "schemars", "secret-cosmwasm-std", "secret-skip", @@ -2409,54 +1192,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "skip-go-swap-adapter-white-whale" -version = "0.3.0" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 1.2.0", - "cw-utils 1.0.3", - "cw2 1.1.2", - "cw20 1.1.2", - "skip", - "test-case", - "thiserror", - "white-whale-std", -] - -[[package]] -name = "snafu" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" -dependencies = [ - "doc-comment", - "snafu-derive", -] - -[[package]] -name = "snafu-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "speedate" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "242f76c50fd18cbf098607090ade73a08d39cfd84ea835f3796a2c855223b19b" -dependencies = [ - "strum", - "strum_macros", -] - [[package]] name = "spki" version = "0.6.0" @@ -2483,28 +1218,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "strum" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.25.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.90", -] - [[package]] name = "subtle" version = "2.6.1" @@ -2533,9 +1246,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.90" +version = "2.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a" dependencies = [ "proc-macro2", "quote", @@ -2552,38 +1265,14 @@ dependencies = [ "flex-error", "num-derive", "num-traits", - "prost 0.11.9", - "prost-types 0.11.9", - "serde", - "serde_bytes", - "subtle-encoding", - "time", -] - -[[package]] -name = "tendermint-proto" -version = "0.34.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b797dd3d2beaaee91d2f065e7bdf239dc8d80bba4a183a288bc1279dd5a69a1e" -dependencies = [ - "bytes", - "flex-error", - "num-derive", - "num-traits", - "prost 0.12.6", - "prost-types 0.12.6", + "prost", + "prost-types", "serde", "serde_bytes", "subtle-encoding", "time", ] -[[package]] -name = "termtree" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" - [[package]] name = "test-case" version = "3.3.1" @@ -2602,7 +1291,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] @@ -2613,7 +1302,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", "test-case-core", ] @@ -2634,7 +1323,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] @@ -2679,7 +1368,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" dependencies = [ "byteorder", - "crunchy 0.2.2", + "crunchy", "hex", "static_assertions", ] @@ -2690,12 +1379,6 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "version_check" version = "0.9.5" @@ -2708,25 +1391,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "white-whale-std" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e8a9b86670af680cc07207c24cd2b380e2b185cd489907576ec21f11d0694bd" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw2 1.1.2", - "cw20 1.1.2", - "osmosis-std-derive", - "prost 0.11.9", - "prost-types 0.11.9", - "protobuf 3.7.1", - "schemars", - "serde", - "uint", -] - [[package]] name = "zeroize" version = "1.8.1" diff --git a/Cargo.toml b/Cargo.toml index 77aa31ba..1c6c1ec3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,13 @@ [workspace] resolver = "2" members = [ - "contracts/entry-point", + #"contracts/entry-point", "contracts/secret-entry-point", - "contracts/adapters/hyperlane", - "contracts/adapters/ibc/*", - "contracts/adapters/swap/*", - "contracts/placeholder", - "packages/*", + #"contracts/adapters/hyperlane", + "contracts/adapters/ibc/secret-ibc-hooks", + "contracts/adapters/swap/shade-protocol", + #"contracts/placeholder", + "packages/secret-skip", ] [workspace.package] @@ -31,7 +31,7 @@ dexter-weighted-pool = "1.1.1" dexter-lp-token = "1.0.0" dexter-router = "1.1.0" cosmwasm-schema = "1.5.4" -cosmwasm-std = { version = "1.5.4", features = ["stargate"] } +#cosmwasm-std = { version = "1.5.4", features = ["stargate"] } cosmos-sdk-proto = { version = "0.19", default-features = false } cw2 = "1.1" cw20 = "1.1" @@ -44,19 +44,19 @@ lido-satellite = { git = "https://github.com/hadronlabs-org/lido-satellite drop-factory = { git = "https://github.com/hadronlabs-org/drop-contracts.git", branch = "feat/audit-fixes", features = ["library"] } drop-staking-base = { git = "https://github.com/hadronlabs-org/drop-contracts.git", branch = "feat/audit-fixes", features = ["library"] } mockall = "0.12.1" -neutron-proto = { version = "0.1.1", default-features = false, features = ["cosmwasm"] } -neutron-sdk = "0.10.0" -osmosis-std = "0.15.3" +# neutron-proto = { version = "0.1.1", default-features = false, features = ["cosmwasm"] } +# neutron-sdk = "0.10.0" +# osmosis-std = "0.15.3" prost = "0.11" -pryzm-std = "0.1.7" +# pryzm-std = "0.1.7" serde = { version = "1.0.194", default-features = false, features = ["derive"] } serde-cw-value = "0.7.0" serde-json-wasm = "1.0.1" -skip = { version = "0.3.0", path = "./packages/skip" } +#skip = { version = "0.3.0", path = "./packages/skip" } secret-skip = { version = "0.3.0", path = "./packages/secret-skip" } test-case = "3.3.1" thiserror = "1" -white-whale-std = "1.1.1" +#white-whale-std = "1.1.1" [profile.release] codegen-units = 1 diff --git a/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs b/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs index e7052a84..63c26894 100644 --- a/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs +++ b/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs @@ -9,13 +9,13 @@ use cosmwasm_std::{ entry_point, from_binary, to_binary, BankMsg, Binary, ContractInfo, Deps, DepsMut, Env, MessageInfo, Reply, Response, SubMsg, SubMsgResult, }; -use cw20::Cw20Coin; use serde_cw_value::Value; // use cw2::set_contract_version; use ibc_proto::ibc::applications::transfer::v1::MsgTransferResponse; use prost::Message; use secret_skip::{ asset::Asset, + cw20::Cw20Coin, ibc::{ AckID, ExecuteMsg, IbcInfo, IbcLifecycleComplete, Ics20TransferMsg, InstantiateMsg, MigrateMsg, QueryMsg, Snip20HookMsg, diff --git a/contracts/adapters/ibc/secret-ibc-hooks/tests/test_execute_ibc_transfer.rs b/contracts/adapters/ibc/secret-ibc-hooks/tests/test_execute_ibc_transfer.rs index 64063d37..733d0f75 100644 --- a/contracts/adapters/ibc/secret-ibc-hooks/tests/test_execute_ibc_transfer.rs +++ b/contracts/adapters/ibc/secret-ibc-hooks/tests/test_execute_ibc_transfer.rs @@ -14,8 +14,8 @@ use secret_skip::{ ibc::{ExecuteMsg, IbcFee, IbcInfo, Snip20HookMsg, Ics20TransferMsg}, snip20::{self, Snip20ReceiveMsg}, + cw20::Cw20Coin, }; -use cw20::Cw20Coin; use skip_go_secret_ibc_adapter_ibc_hooks::{ error::ContractResult, state::{ diff --git a/contracts/adapters/swap/shade-protocol/Cargo.toml b/contracts/adapters/swap/shade-protocol/Cargo.toml index 6e589711..a3ca6b2b 100644 --- a/contracts/adapters/swap/shade-protocol/Cargo.toml +++ b/contracts/adapters/swap/shade-protocol/Cargo.toml @@ -20,9 +20,9 @@ backtraces = ["cosmwasm-std/backtraces"] library = [] [dependencies] -cw2 = { workspace = true } -cw20 = { workspace = true } -cw-utils = { workspace = true } +#cw2 = { workspace = true } +#cw20 = { workspace = true } +#cw-utils = { workspace = true } thiserror = { workspace = true } secret-skip = { workspace = true } diff --git a/contracts/adapters/swap/shade-protocol/src/contract.rs b/contracts/adapters/swap/shade-protocol/src/contract.rs index 36ee6519..d5b76e7c 100644 --- a/contracts/adapters/swap/shade-protocol/src/contract.rs +++ b/contracts/adapters/swap/shade-protocol/src/contract.rs @@ -9,9 +9,8 @@ use cosmwasm_std::{ entry_point, from_binary, to_binary, Addr, Binary, ContractInfo, Deps, DepsMut, Env, MessageInfo, Response, Uint128, WasmMsg, }; -use secret_skip::{asset::Asset, snip20::Snip20ReceiveMsg, swap::SwapOperation}; +use secret_skip::{asset::Asset, cw20::Cw20Coin, snip20::Snip20ReceiveMsg, swap::SwapOperation}; // use cw2::set_contract_version; -use cw20::Cw20Coin; use secret_toolkit::snip20; use crate::{ @@ -286,7 +285,7 @@ pub fn execute_transfer_funds_back( deps: DepsMut, env: Env, info: MessageInfo, - _swapper: Addr, + swapper: Addr, return_denom: String, ) -> ContractResult { // Ensure the caller is the contract itself @@ -320,11 +319,10 @@ pub fn execute_transfer_funds_back( Err(e) => return Err(ContractError::Std(e)), }; - let entry_point_contract = ENTRY_POINT_CONTRACT.load(deps.storage)?; + // let entry_point_contract = ENTRY_POINT_CONTRACT.load(deps.storage)?; - let send_msg = match snip20::send_msg_with_code_hash( - entry_point_contract.address.to_string(), - Some(entry_point_contract.code_hash), + let send_msg = match snip20::send_msg( + swapper.to_string(), balance.amount, None, None, diff --git a/contracts/adapters/swap/shade-protocol/src/error.rs b/contracts/adapters/swap/shade-protocol/src/error.rs index 4c433822..34ffab5a 100644 --- a/contracts/adapters/swap/shade-protocol/src/error.rs +++ b/contracts/adapters/swap/shade-protocol/src/error.rs @@ -1,5 +1,4 @@ use cosmwasm_std::{OverflowError, StdError}; -use cw_utils; use secret_skip::error::SkipError; use thiserror::Error; @@ -16,9 +15,10 @@ pub enum ContractError { #[error(transparent)] Skip(#[from] SkipError), + /* #[error(transparent)] Payment(#[from] cw_utils::PaymentError), - + */ #[error("Unauthorized")] Unauthorized, diff --git a/contracts/adapters/swap/shade-protocol/tests/test_execute_receive.rs b/contracts/adapters/swap/shade-protocol/tests/test_execute_receive.rs index 7fabf4d9..776eea0c 100644 --- a/contracts/adapters/swap/shade-protocol/tests/test_execute_receive.rs +++ b/contracts/adapters/swap/shade-protocol/tests/test_execute_receive.rs @@ -5,8 +5,7 @@ use cosmwasm_std::{ ReplyOn::Never, SubMsg, SystemResult, Uint128, WasmMsg, WasmQuery, }; -use cw20::{BalanceResponse, Cw20Coin}; -use secret_skip::{asset::Asset, snip20, swap::SwapOperation}; +use secret_skip::{asset::Asset, cw20::Cw20Coin, snip20, swap::SwapOperation}; use skip_go_swap_adapter_shade_protocol::{ error::{ContractError, ContractResult}, msg::{ExecuteMsg, Snip20HookMsg}, @@ -124,8 +123,8 @@ fn test_execute_swap(params: Params) -> ContractResult<()> { WasmQuery::Smart { contract_addr, .. } => { if contract_addr == "secret123" { SystemResult::Ok(SystemContractResult::Ok( - to_binary(&BalanceResponse { - balance: 100u128.into(), + to_binary(&snip20::BalanceResponse { + amount: 100u128.into(), }) .unwrap(), )) diff --git a/contracts/adapters/swap/shade-protocol/tests/test_execute_swap.rs b/contracts/adapters/swap/shade-protocol/tests/test_execute_swap.rs index 27e5fa15..83e0772c 100644 --- a/contracts/adapters/swap/shade-protocol/tests/test_execute_swap.rs +++ b/contracts/adapters/swap/shade-protocol/tests/test_execute_swap.rs @@ -4,9 +4,9 @@ use cosmwasm_std::{ ReplyOn::Never, SubMsg, WasmMsg, }; -use cw20::Cw20Coin; use secret_skip::{ asset::Asset, + cw20::Cw20Coin, snip20::{self, Snip20ReceiveMsg}, swap::SwapOperation, }; diff --git a/contracts/adapters/swap/shade-protocol/tests/test_execute_transfer_funds_back.rs b/contracts/adapters/swap/shade-protocol/tests/test_execute_transfer_funds_back.rs index 74b1f6bf..1097c833 100644 --- a/contracts/adapters/swap/shade-protocol/tests/test_execute_transfer_funds_back.rs +++ b/contracts/adapters/swap/shade-protocol/tests/test_execute_transfer_funds_back.rs @@ -5,7 +5,6 @@ use cosmwasm_std::{ ReplyOn::Never, SubMsg, SystemResult, Uint128, WasmMsg, WasmQuery, }; -use cw20::BalanceResponse; use secret_skip::snip20; use skip_go_swap_adapter_shade_protocol::{ error::{ContractError, ContractResult}, diff --git a/contracts/secret-entry-point/Cargo.toml b/contracts/secret-entry-point/Cargo.toml index 80b6d27f..5a6b2b87 100644 --- a/contracts/secret-entry-point/Cargo.toml +++ b/contracts/secret-entry-point/Cargo.toml @@ -25,9 +25,9 @@ skip-go-swap-adapter-shade-protocol = { path = "../adapters/swap/shade-protocol" secret-skip = { workspace = true } #cosmwasm-std = { workspace = true } #cw2 = { workspace = true } -cw20 = { workspace = true } +#cw20 = { workspace = true } #cw-storage-plus = { workspace = true } -cw-utils = { workspace = true } +#cw-utils = { workspace = true } #skip = { workspace = true } thiserror = { workspace = true } serde = "1.0.114" diff --git a/contracts/secret-entry-point/src/error.rs b/contracts/secret-entry-point/src/error.rs index 601954b8..2a62f35e 100644 --- a/contracts/secret-entry-point/src/error.rs +++ b/contracts/secret-entry-point/src/error.rs @@ -19,9 +19,10 @@ pub enum ContractError { #[error(transparent)] Overflow(#[from] OverflowError), + /* #[error(transparent)] Payment(#[from] cw_utils::PaymentError), - + */ #[error("Unauthorized")] Unauthorized, diff --git a/contracts/secret-entry-point/src/execute.rs b/contracts/secret-entry-point/src/execute.rs index 0f5f15ec..319a14c3 100644 --- a/contracts/secret-entry-point/src/execute.rs +++ b/contracts/secret-entry-point/src/execute.rs @@ -17,9 +17,9 @@ use cosmwasm_std::{ from_binary, to_binary, Addr, BankMsg, Coin, ContractInfo, CosmosMsg, DepsMut, Env, MessageInfo, Response, StdError, SubMsg, Uint128, WasmMsg, }; -use cw20::Cw20Coin; use secret_skip::{ asset::Asset, + cw20::Cw20Coin, error::SkipError, ibc::{self, IbcInfo}, snip20::Snip20ReceiveMsg, diff --git a/contracts/secret-entry-point/tests/test_execute_action.rs b/contracts/secret-entry-point/tests/test_execute_action.rs index aa96ec20..feff21e6 100644 --- a/contracts/secret-entry-point/tests/test_execute_action.rs +++ b/contracts/secret-entry-point/tests/test_execute_action.rs @@ -4,9 +4,9 @@ use cosmwasm_std::{ ReplyOn::Never, SubMsg, SystemResult, Timestamp, Uint128, WasmMsg, WasmQuery, }; -use cw20::{BalanceResponse, Cw20Coin, Cw20ExecuteMsg}; use secret_skip::{ asset::Asset, + cw20::Cw20Coin, ibc::{self, ExecuteMsg as IbcTransferExecuteMsg, IbcFee, IbcInfo, Ics20TransferMsg}, snip20, }; @@ -395,8 +395,8 @@ fn test_execute_post_swap_action(params: Params) { let wasm_handler = |query: &WasmQuery| -> QuerierResult { match query { WasmQuery::Smart { .. } => SystemResult::Ok(ContractResult::Ok( - to_binary(&BalanceResponse { - balance: 1_000_000u128.into(), + to_binary(&snip20::BalanceResponse { + amount: 1_000_000u128.into(), }) .unwrap(), )), diff --git a/contracts/secret-entry-point/tests/test_execute_post_swap_action.rs b/contracts/secret-entry-point/tests/test_execute_post_swap_action.rs new file mode 100644 index 00000000..b71a93ec --- /dev/null +++ b/contracts/secret-entry-point/tests/test_execute_post_swap_action.rs @@ -0,0 +1,723 @@ +/* +use cosmwasm_std::{ + testing::{mock_dependencies_with_balances, mock_env, mock_info}, + to_binary, Addr, BankMsg, Coin, ContractResult, QuerierResult, + ReplyOn::Never, + SubMsg, SystemResult, Timestamp, Uint128, WasmMsg, WasmQuery, +}; +use cw20::{BalanceResponse, Cw20Coin, Cw20ExecuteMsg}; +use secret_skip::{ + asset::Asset, + entry_point::{Action, ExecuteMsg}, + ibc::{ExecuteMsg as IbcTransferExecuteMsg, IbcFee, IbcInfo}, +}; +use skip_go_secret_entry_point::{ + error::ContractError, + state::{BLOCKED_CONTRACT_ADDRESSES, IBC_TRANSFER_CONTRACT_ADDRESS, PRE_SWAP_OUT_ASSET_AMOUNT}, +}; +use test_case::test_case; +*/ + +/* +Test Cases: + +Expect Response + // General + - Native Asset Transfer + - Cw20 Asset Transfer + - Ibc Transfer + - Native Asset Contract Call + - Cw20 Asset Contract Call + + // With IBC Fees + - Ibc Transfer w/ IBC Fees of different denom than min coin + - Ibc Transfer w/ IBC Fees of same denom as min coin + + // Exact Out + - Native Asset Transfer With Exact Out Set To True + - Ibc Transfer With Exact Out Set To True + - Ibc Transfer w/ IBC Fees of different denom than min coin With Exact Out Set To True + - Ibc Transfer w/ IBC Fees of same denom as min coin With Exact Out Set To True + - Contract Call With Exact Out Set To True + + // Invariants + - Pre Swap Out Asset Contract Balance Preserved + +Expect Error + - Transfer Timeout + - Received Less Native Asset From Swap Than Min Asset + - Received Less Cw20 Asset From Swap Than Min Asset + - Unauthorized Caller + - Contract Call Address Blocked + - Cw20 Out Asset With IBC Transfer + */ + +/* +// Define test parameters +struct Params { + caller: String, + min_asset: Asset, + post_swap_action: Action, + exact_out: bool, + pre_swap_out_asset_amount: Uint128, + expected_messages: Vec, + expected_error: Option, +} + +// Test execute_post_swap_action +#[test_case( + Params { + caller: "entry_point".to_string(), + min_asset: Asset::Cw20(Cw20Coin{ + address: "secret123".to_string(), + amount: 1_000_000u128.into(), + }), + post_swap_action: Action::Transfer { + to_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5".to_string(), + }, + exact_out: true, + pre_swap_out_asset_amount: Uint128::new(0), + expected_messages: vec![SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5".to_string(), + amount: vec![Coin::new(100_000, "os")], + } + .into(), + gas_limit: None, + reply_on: Never, + }], + expected_error: None, + }; + "Native Asset Transfer With Exact Out Set To True")] +#[test_case( + Params { + caller: "entry_point".to_string(), + min_asset: Asset::Native(Coin::new(1_000_000, "os")), + post_swap_action: Action::Transfer { + to_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5".to_string(), + }, + exact_out: false, + pre_swap_out_asset_amount: Uint128::new(0), + expected_messages: vec![SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5".to_string(), + amount: vec![Coin::new(1_000_000, "os")], + } + .into(), + gas_limit: None, + reply_on: Never, + }], + expected_error: None, + }; + "Native Asset Transfer")] +#[test_case( + Params { + caller: "entry_point".to_string(), + min_asset: Asset::Cw20(Cw20Coin{ + address: "secret123".to_string(), + amount: 1_000_000u128.into(), + }), + post_swap_action: Action::Transfer { + to_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5".to_string(), + }, + exact_out: false, + pre_swap_out_asset_amount: Uint128::new(0), + expected_messages: vec![SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "secret123".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&Cw20ExecuteMsg::Transfer { + recipient: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5".to_string(), + amount: 1_000_000u128.into(), + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }], + expected_error: None, + }; + "Cw20 Asset Transfer")] +#[test_case( + Params { + caller: "entry_point".to_string(), + min_asset: Asset::Native(Coin::new(1_000_000, "os")), + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: None, + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: None, + }, + exact_out: false, + pre_swap_out_asset_amount: 0u128.into(), + expected_messages: vec![SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "ibc_transfer_adapter".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&IbcTransferExecuteMsg::IbcTransfer { + info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: None, + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + coin: Coin::new(1_000_000, "os"), + timeout_timestamp: 101, + }) + .unwrap(), + funds: vec![Coin::new(1_000_000, "os")], + } + .into(), + gas_limit: None, + reply_on: Never, + }], + expected_error: None, + }; + "Ibc Transfer")] +#[test_case( + Params { + caller: "entry_point".to_string(), + min_asset: Asset::Native(Coin::new(100_000, "os")), + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: None, + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: None, + }, + exact_out: true, + pre_swap_out_asset_amount: 0u128.into(), + expected_messages: vec![SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "ibc_transfer_adapter".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&IbcTransferExecuteMsg::IbcTransfer { + info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: None, + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + coin: Coin::new(100_000, "os"), + timeout_timestamp: 101, + }) + .unwrap(), + funds: vec![Coin::new(100_000, "os")], + } + .into(), + gas_limit: None, + reply_on: Never, + }], + expected_error: None, + }; + "Ibc Transfer With Exact Out Set To True")] +#[test_case( + Params { + caller: "entry_point".to_string(), + min_asset: Asset::Native(Coin::new(100_000, "os")), + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "un")], + timeout_fee: vec![Coin::new(100_000, "un")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: None, + }, + exact_out: true, + pre_swap_out_asset_amount: Uint128::new(0), + expected_messages: vec![SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "ibc_transfer_adapter".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&IbcTransferExecuteMsg::IbcTransfer { + info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "un")], + timeout_fee: vec![Coin::new(100_000, "un")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + coin: Coin::new(100_000, "os"), + timeout_timestamp: 101, + }) + .unwrap(), + funds: vec![ + Coin::new(100_000, "os"), + ], + } + .into(), + gas_limit: None, + reply_on: Never, + }], + expected_error: None, + }; + "Ibc Transfer w/ IBC Fees of different denom than min coin With Exact Out Set To True")] +#[test_case( + Params { + caller: "entry_point".to_string(), + min_asset: Asset::Native(Coin::new(100_000, "un")), + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "un")], + timeout_fee: vec![Coin::new(100_000, "un")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: None, + }, + exact_out: true, + pre_swap_out_asset_amount: Uint128::new(0), + expected_messages: vec![SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "ibc_transfer_adapter".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&IbcTransferExecuteMsg::IbcTransfer { + info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "un")], + timeout_fee: vec![Coin::new(100_000, "un")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + coin: Coin::new(100_000, "un"), + timeout_timestamp: 101, + }) + .unwrap(), + funds: vec![Coin::new(100_000, "un")], + } + .into(), + gas_limit: None, + reply_on: Never, + }], + expected_error: None, + }; + "Ibc Transfer w/ IBC Fees of same denom as min coin With Exact Out Set To True")] +#[test_case( + Params { + caller: "entry_point".to_string(), + min_asset: Asset::Native(Coin::new(1_000_000, "os")), + post_swap_action: Action::ContractCall { + contract_address: "contract_call".to_string(), + msg: to_binary(&"contract_call_msg").unwrap(), + }, + exact_out: false, + pre_swap_out_asset_amount: Uint128::new(0), + expected_messages: vec![SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "contract_call".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&"contract_call_msg").unwrap(), + funds: vec![Coin::new(1_000_000, "os")], + } + .into(), + gas_limit: None, + reply_on: Never, + }], + expected_error: None, + }; + "Native Asset Contract Call")] +#[test_case( + Params { + caller: "entry_point".to_string(), + min_asset: Asset::Cw20(Cw20Coin{ + address: "secret123".to_string(), + amount: 1_000_000u128.into(), + }), + post_swap_action: Action::ContractCall { + contract_address: "contract_call".to_string(), + msg: to_binary(&"contract_call_msg").unwrap(), + }, + exact_out: false, + pre_swap_out_asset_amount: Uint128::new(0), + expected_messages: vec![SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "secret123".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&Cw20ExecuteMsg::Send { + contract: "contract_call".to_string(), + amount: 1_000_000u128.into(), + msg: to_binary(&"contract_call_msg").unwrap(), + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }], + expected_error: None, + }; + "Cw20 Asset Contract Call")] +#[test_case( + Params { + caller: "entry_point".to_string(), + min_asset: Asset::Native(Coin::new(100_000, "os")), + post_swap_action: Action::ContractCall { + contract_address: "contract_call".to_string(), + msg: to_binary(&"contract_call_msg").unwrap(), + }, + exact_out: true, + pre_swap_out_asset_amount: Uint128::new(0), + expected_messages: vec![SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "contract_call".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&"contract_call_msg").unwrap(), + funds: vec![Coin::new(100_000, "os")], + } + .into(), + gas_limit: None, + reply_on: Never, + }], + expected_error: None, + }; + "Contract Call With Exact Out Set To True")] +#[test_case( + Params { + caller: "entry_point".to_string(), + min_asset: Asset::Native(Coin::new(1_000_000, "os")), + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "un")], + timeout_fee: vec![Coin::new(100_000, "un")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: None, + }, + exact_out: false, + pre_swap_out_asset_amount: Uint128::new(0), + expected_messages: vec![SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "ibc_transfer_adapter".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&IbcTransferExecuteMsg::IbcTransfer { + info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "un")], + timeout_fee: vec![Coin::new(100_000, "un")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + coin: Coin::new(1_000_000, "os"), + timeout_timestamp: 101, + }) + .unwrap(), + funds: vec![ + Coin::new(1_000_000, "os"), + ], + } + .into(), + gas_limit: None, + reply_on: Never, + }], + expected_error: None, + }; + "Ibc Transfer w/ IBC Fees of different denom than min coin")] +#[test_case( + Params { + caller: "entry_point".to_string(), + min_asset: Asset::Native(Coin::new(800_000, "un")), + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "un")], + timeout_fee: vec![Coin::new(100_000, "un")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: None, + }, + exact_out: false, + pre_swap_out_asset_amount: Uint128::new(0), + expected_messages: vec![SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "ibc_transfer_adapter".to_string(), + code_hash: "code_hash".to_string(), + msg: to_binary(&IbcTransferExecuteMsg::IbcTransfer { + info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "un")], + timeout_fee: vec![Coin::new(100_000, "un")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + coin: Coin::new(1_000_000, "un"), + timeout_timestamp: 101, + }) + .unwrap(), + funds: vec![Coin::new(1_000_000, "un")], + } + .into(), + gas_limit: None, + reply_on: Never, + }], + expected_error: None, + }; + "Ibc Transfer w/ IBC Fees of same denom as min coin")] +#[test_case( + Params { + caller: "entry_point".to_string(), + min_asset: Asset::Native(Coin::new(700_000, "os")), + post_swap_action: Action::Transfer { + to_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5".to_string(), + }, + exact_out: false, + pre_swap_out_asset_amount: Uint128::new(200_000), + expected_messages: vec![SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5".to_string(), + amount: vec![Coin::new(800_000, "os")], + } + .into(), + gas_limit: None, + reply_on: Never, + }], + expected_error: None, + }; + "Pre Swap Out Asset Contract Balance Preserved")] +#[test_case( + Params { + caller: "entry_point".to_string(), + min_asset: Asset::Cw20(Cw20Coin{ + address: "secret123".to_string(), + amount: 1_000_000u128.into(), + }), + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: None, + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: None, + }, + exact_out: false, + pre_swap_out_asset_amount: Uint128::new(0), + expected_messages: vec![], + expected_error: Some(ContractError::NonNativeIbcTransfer), + }; + "Cw20 Out Asset With IBC Transfer")] +#[test_case( + Params { + caller: "entry_point".to_string(), + min_asset: Asset::Native(Coin::new(1_100_000, "un")), + post_swap_action: Action::Transfer { + to_address: "swapper".to_string(), + }, + exact_out: false, + pre_swap_out_asset_amount: Uint128::new(0), + expected_messages: vec![], + expected_error: Some(ContractError::ReceivedLessAssetFromSwapsThanMinAsset), + }; + "Received Less Native Asset From Swap Than Min Asset - Expect Error")] +#[test_case( + Params { + caller: "entry_point".to_string(), + min_asset: Asset::Cw20(Cw20Coin{ + address: "secret123".to_string(), + amount: 2_100_000u128.into(), + }), + post_swap_action: Action::Transfer { + to_address: "swapper".to_string(), + }, + exact_out: false, + pre_swap_out_asset_amount: Uint128::new(0), + expected_messages: vec![], + expected_error: Some(ContractError::ReceivedLessAssetFromSwapsThanMinAsset), + }; + "Received Less Cw20 Asset From Swap Than Min Asset - Expect Error")] +#[test_case( + Params { + caller: "unauthorized".to_string(), + min_asset: Asset::Native(Coin::new(1_100_000, "un")), + post_swap_action: Action::Transfer { + to_address: "swapper".to_string(), + }, + exact_out: false, + pre_swap_out_asset_amount: Uint128::new(0), + expected_messages: vec![], + expected_error: Some(ContractError::Unauthorized), + }; + "Unauthorized Caller - Expect Error")] +#[test_case( + Params { + caller: "entry_point".to_string(), + min_asset: Asset::Native(Coin::new(900_000, "un")), + post_swap_action: Action::ContractCall { + contract_address: "entry_point".to_string(), + msg: to_binary(&"contract_call_msg").unwrap(), + }, + exact_out: false, + pre_swap_out_asset_amount: Uint128::new(0), + expected_messages: vec![], + expected_error: Some(ContractError::ContractCallAddressBlocked), + }; + "Contract Call Address Blocked - Expect Error")] +fn test_execute_post_swap_action(params: Params) { + // Create mock dependencies + let mut deps = mock_dependencies_with_balances(&[( + "entry_point", + &[Coin::new(1_000_000, "os"), Coin::new(1_000_000, "un")], + )]); + + // Create mock wasm handler to handle the swap adapter contract query + let wasm_handler = |query: &WasmQuery| -> QuerierResult { + match query { + WasmQuery::Smart { .. } => SystemResult::Ok(ContractResult::Ok( + to_binary(&BalanceResponse { + balance: Uint128::from(1_000_000u128), + }) + .unwrap(), + )), + _ => panic!("Unsupported query: {:?}", query), + } + }; + + // Update querier with mock wasm handler + deps.querier.update_wasm(wasm_handler); + + // Create mock env with parameters that make testing easier + let mut env = mock_env(); + env.contract.address = Addr::unchecked("entry_point"); + env.block.time = Timestamp::from_nanos(100); + + // Create mock info with entry point contract address + let info = mock_info(¶ms.caller, &[]); + + // Store the ibc transfer adapter contract address + let ibc_transfer_adapter = Addr::unchecked("ibc_transfer_adapter"); + IBC_TRANSFER_CONTRACT_ADDRESS + .save(deps.as_mut().storage, &ibc_transfer_adapter) + .unwrap(); + + // Store the entry point contract address in the blocked contract addresses map + BLOCKED_CONTRACT_ADDRESSES + .save(deps.as_mut().storage, &Addr::unchecked("entry_point"), &()) + .unwrap(); + + // Store the pre swap out asset amount + PRE_SWAP_OUT_ASSET_AMOUNT + .save(deps.as_mut().storage, ¶ms.pre_swap_out_asset_amount) + .unwrap(); + + // Call execute_post_swap_action with the given test parameters + let res = skip_go_entry_point::contract::execute( + deps.as_mut(), + env, + info, + ExecuteMsg::PostSwapAction { + min_asset: params.min_asset, + timeout_timestamp: 101, + post_swap_action: params.post_swap_action, + exact_out: params.exact_out, + }, + ); + + match res { + Ok(res) => { + // Assert the test did not expect an error + assert!( + params.expected_error.is_none(), + "expected test to error with {:?}, but it succeeded", + params.expected_error + ); + + // Assert the number of messages in the response is correct + assert_eq!( + res.messages.len(), + params.expected_messages.len(), + "expected {:?} messages, but got {:?}", + params.expected_messages.len(), + res.messages.len() + ); + + // Assert the messages in the response are correct + assert_eq!(res.messages, params.expected_messages,); + } + Err(err) => { + // Assert the test expected an error + assert!( + params.expected_error.is_some(), + "expected test to succeed, but it errored with {:?}", + err + ); + + // Assert the error is correct + assert_eq!(err, params.expected_error.unwrap()); + } + } +} +*/ diff --git a/contracts/secret-entry-point/tests/test_execute_receive.rs b/contracts/secret-entry-point/tests/test_execute_receive.rs index 7563012a..960e6628 100644 --- a/contracts/secret-entry-point/tests/test_execute_receive.rs +++ b/contracts/secret-entry-point/tests/test_execute_receive.rs @@ -5,9 +5,9 @@ use cosmwasm_std::{ ReplyOn::{Always, Never}, StdError, SubMsg, SystemError, SystemResult, Timestamp, Uint128, WasmMsg, WasmQuery, }; -use cw20::Cw20Coin; use secret_skip::{ asset::Asset, + cw20::Cw20Coin, snip20::Snip20ReceiveMsg, swap::{Swap, SwapExactAssetIn, SwapOperation}, }; diff --git a/contracts/secret-entry-point/tests/test_execute_swap_and_action.rs b/contracts/secret-entry-point/tests/test_execute_swap_and_action.rs new file mode 100644 index 00000000..12a56de0 --- /dev/null +++ b/contracts/secret-entry-point/tests/test_execute_swap_and_action.rs @@ -0,0 +1,1969 @@ +/* +use cosmwasm_std::{ + testing::{mock_dependencies_with_balances, mock_env, mock_info}, + to_json_binary, Addr, BankMsg, Coin, ContractResult, OverflowError, OverflowOperation, + QuerierResult, + ReplyOn::Never, + SubMsg, SystemResult, Timestamp, Uint128, WasmMsg, WasmQuery, +}; +use cw20::{BalanceResponse, Cw20Coin, Cw20ExecuteMsg}; +use cw_utils::PaymentError::{MultipleDenoms, NoFunds, NonPayable}; +use skip::{ + asset::Asset, + entry_point::{Action, Affiliate, ExecuteMsg}, + error::SkipError::{ + IbcFeesNotOneCoin, InvalidCw20Coin, Overflow, Payment, SwapOperationsAssetInDenomMismatch, + SwapOperationsAssetOutDenomMismatch, SwapOperationsEmpty, + }, + ibc::{IbcFee, IbcInfo}, + swap::{ + ExecuteMsg as SwapExecuteMsg, Route, SmartSwapExactAssetIn, Swap, SwapExactAssetIn, + SwapExactAssetOut, SwapOperation, + }, +}; +use skip_go_entry_point::{ + error::ContractError, + state::{IBC_TRANSFER_CONTRACT_ADDRESS, PRE_SWAP_OUT_ASSET_AMOUNT, SWAP_VENUE_MAP}, +}; +use test_case::test_case; +*/ + +/* +Test Cases: + +Expect Response + Native Asset + - User Swap Exact Coin In With Transfer + - User Swap Exact Coin Out With Transfer + - User Swap Exact Coin In With IBC Transfer With IBC Fees + - User Swap Exact Coin In With IBC Transfer Without IBC Fees + - Fee Swap And User Swap Exact Coin In With IBC Fees + - Sent Asset Not Given With Valid One Coin + + CW20 Asset + - User Swap Exact Cw20 Asset In With Transfer + - Fee Swap And User Swap Exact Cw20 Asset In With IBC Fees + - Cw20 Min Asset + +Expect Error + // Fee Swap + - Fee Swap Coin In Amount More Than Remaining Coin Received Amount + - Fee Swap Coin In Denom Is Not The Same As Remaining Coin Received Denom + - Fee Swap First Swap Operation Denom In Is Not The Same As Remaining Coin Received Denom + - Fee Swap Last Swap Operation Denom Out Is Not The Same As IBC Fee Coin Denom + - Fee Swap With IBC Transfer But Without IBC Fees + + // User Swap + - User Swap With IBC Transfer With IBC Fees But IBC Fee Coin Denom Is Not The Same As Remaining Coin Received Denom + + // Invalid Assets Sent To Contract + - No Coins Sent To Contract + - More Than One Coin Sent To Contract + - Not Enough Cw20 Tokens Sent To Contract + - Cw20 Token Specified As Sent Asset With Native Coin Sent To Contract + + // Empty Swap Operations + - Empty Fee Swap Operations + + // Timeout + - Current Block Time Greater Than Timeout Timestamp + + // IBC Transfer + - IBC Transfer With IBC Fees But More Than One IBC Fee Denom Specified + - IBC Transfer With IBC Fees But No IBC Fee Coins Specified + - IBC Transfer With IBC Fee Coin Amount Zero + + // Sent Asset + - Sent Asset Not Given with Invalid One Coin + */ + +/* +// Define test parameters +struct Params { + info_funds: Vec, + sent_asset: Option, + user_swap: Swap, + min_asset: Asset, + timeout_timestamp: u64, + post_swap_action: Action, + affiliates: Vec, + expected_messages: Vec, + expected_error: Option, +} + +// Test execute_swap_and_action +#[test_case( + Params { + info_funds: vec![ + Coin::new(1_000_000, "untrn"), + ], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "untrn"))), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + } + ), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + expected_messages: vec![ + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::UserSwap { + swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + } + ), + remaining_asset: Asset::Native(Coin::new(1_000_000, "untrn")), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + affiliates: vec![], + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::PostSwapAction { + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + exact_out: false, + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "User Swap Exact Coin In With Transfer")] +#[test_case( + Params { + info_funds: vec![ + Coin::new(1_000_000, "untrn"), + ], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "untrn"))), + user_swap: Swap::SwapExactAssetOut ( + SwapExactAssetOut{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + refund_address: Some("refund_address".to_string()), + } + ), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + expected_messages: vec![ + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::UserSwap { + swap: Swap::SwapExactAssetOut ( + SwapExactAssetOut{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + refund_address: Some("refund_address".to_string()), + } + ), + remaining_asset: Asset::Native(Coin::new(1_000_000, "untrn")), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + affiliates: vec![], + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::PostSwapAction { + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + exact_out: true, + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "User Swap Exact Coin Out With Transfer")] +#[test_case( + Params { + info_funds: vec![ + Coin::new(1_000_000, "untrn"), + ], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "untrn"))), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + }, + ), + min_asset: Asset::Native(Coin::new(800_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "untrn")], + timeout_fee: vec![Coin::new(100_000, "untrn")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: None, + }, + affiliates: vec![], + expected_messages: vec![ + SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "ibc_transfer_adapter".to_string(), + amount: vec![Coin::new(200_000, "untrn")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::UserSwap { + swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + } + ), + remaining_asset: Asset::Native(Coin::new(800_000, "untrn")), + min_asset: Asset::Native(Coin::new(800_000, "osmo")), + affiliates: vec![], + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::PostSwapAction { + min_asset: Asset::Native(Coin::new(800_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "untrn")], + timeout_fee: vec![Coin::new(100_000, "untrn")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: None, + }, + exact_out: false, + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "User Swap Exact Coin In With IBC Transfer With IBC Fees")] +#[test_case( + Params { + info_funds: vec![ + Coin::new(1_000_000, "untrn"), + ], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "untrn"))), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + }, + ), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: None, + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: None, + }, + affiliates: vec![], + expected_messages: vec![ + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::UserSwap { + swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + } + ), + remaining_asset: Asset::Native(Coin::new(1_000_000, "untrn")), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + affiliates: vec![], + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::PostSwapAction { + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: None, + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: None, + }, + exact_out: false, + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "User Swap Exact Coin In With IBC Transfer Without IBC Fees")] +#[test_case( + Params { + info_funds: vec![ + Coin::new(1_000_000, "osmo"), + ], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "osmo"))), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "osmo".to_string(), + denom_out: "uatom".to_string(), + interface: None, + } + ], + }, + ), + min_asset: Asset::Native(Coin::new(100_000, "uatom")), + timeout_timestamp: 101, + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "untrn")], + timeout_fee: vec![Coin::new(100_000, "untrn")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: Some( + SwapExactAssetOut { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "osmo".to_string(), + denom_out: "untrn".to_string(), + interface: None, + } + ], + refund_address: None, + } + ), + }, + affiliates: vec![], + expected_messages: vec![ + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "swap_venue_adapter".to_string(), + msg: to_json_binary(&SwapExecuteMsg::Swap { + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "osmo".to_string(), + denom_out: "untrn".to_string(), + interface: None, + } + ], + }).unwrap(), + funds: vec![Coin::new(200_000, "osmo")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "ibc_transfer_adapter".to_string(), + amount: vec![Coin::new(200_000, "untrn")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::UserSwap { + swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "osmo".to_string(), + denom_out: "uatom".to_string(), + interface: None, + } + ], + } + ), + remaining_asset: Asset::Native(Coin::new(800_000, "osmo")), + min_asset: Asset::Native(Coin::new(100_000, "uatom")), + affiliates: vec![], + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::PostSwapAction { + min_asset: Asset::Native(Coin::new(100_000, "uatom")), + timeout_timestamp: 101, + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "untrn")], + timeout_fee: vec![Coin::new(100_000, "untrn")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: Some( + SwapExactAssetOut { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "osmo".to_string(), + denom_out: "untrn".to_string(), + interface: None, + } + ], + refund_address: None, + } + ), + }, + exact_out: false, + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "Fee Swap And User Swap Exact Coin In With IBC Fees")] +#[test_case( + Params { + info_funds: vec![ + Coin::new(1_000_000, "untrn"), + ], + sent_asset: None, + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + } + ), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + expected_messages: vec![ + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::UserSwap { + swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + } + ), + remaining_asset: Asset::Native(Coin::new(1_000_000, "untrn")), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + affiliates: vec![], + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::PostSwapAction { + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + exact_out: false, + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "Sent Asset Not Given With Valid One Coin")] +#[test_case( + Params { + info_funds: vec![], + sent_asset: Some(Asset::Cw20(Cw20Coin { + address: "neutron123".to_string(), + amount: Uint128::from(1_000_000u128), + })), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "neutron123".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + } + ), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + expected_messages: vec![ + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::UserSwap { + swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "neutron123".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + } + ), + remaining_asset: Asset::Cw20(Cw20Coin { + address: "neutron123".to_string(), + amount: Uint128::from(1_000_000u128), + }), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + affiliates: vec![], + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::PostSwapAction { + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + exact_out: false, + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "User Swap Exact Cw20 Asset In With Transfer")] +#[test_case( + Params { + info_funds: vec![], + sent_asset: Some(Asset::Cw20(Cw20Coin { + address: "neutron123".to_string(), + amount: Uint128::from(1_000_000u128), + })), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "neutron123".to_string(), + denom_out: "uatom".to_string(), + interface: None, + } + ], + }, + ), + min_asset: Asset::Native(Coin::new(100_000, "uatom")), + timeout_timestamp: 101, + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "untrn")], + timeout_fee: vec![Coin::new(100_000, "untrn")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: Some( + SwapExactAssetOut { + swap_venue_name: "swap_venue_name_2".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "neutron123".to_string(), + denom_out: "untrn".to_string(), + interface: None, + } + ], + refund_address: None, + } + ), + }, + affiliates: vec![], + expected_messages: vec![ + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "neutron123".to_string(), + msg: to_json_binary(&Cw20ExecuteMsg::Send { + contract: "swap_venue_adapter_2".to_string(), + amount: Uint128::from(200_000u128), + msg: to_json_binary(&SwapExecuteMsg::Swap { + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "neutron123".to_string(), + denom_out: "untrn".to_string(), + interface: None, + } + ], + }).unwrap(), + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "ibc_transfer_adapter".to_string(), + amount: vec![Coin::new(200_000, "untrn")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::UserSwap { + swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "neutron123".to_string(), + denom_out: "uatom".to_string(), + interface: None, + } + ], + } + ), + remaining_asset: Asset::Cw20(Cw20Coin { + address: "neutron123".to_string(), + amount: Uint128::from(800_000u128), + }), + min_asset: Asset::Native(Coin::new(100_000, "uatom")), + affiliates: vec![], + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::PostSwapAction { + min_asset: Asset::Native(Coin::new(100_000, "uatom")), + timeout_timestamp: 101, + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "untrn")], + timeout_fee: vec![Coin::new(100_000, "untrn")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: Some( + SwapExactAssetOut { + swap_venue_name: "swap_venue_name_2".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "neutron123".to_string(), + denom_out: "untrn".to_string(), + interface: None, + } + ], + refund_address: None, + } + ), + }, + exact_out: false, + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "Fee Swap And User Swap Exact Cw20 Asset In With IBC Fees")] +#[test_case( + Params { + info_funds: vec![ + Coin::new(1_000_000, "untrn"), + ], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "untrn"))), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + } + ), + min_asset: Asset::Cw20(Cw20Coin { + address: "neutron123".to_string(), + amount: Uint128::from(1_000_000u128), + }), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + expected_messages: vec![ + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::UserSwap { + swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + } + ), + remaining_asset: Asset::Native(Coin::new(1_000_000, "untrn")), + min_asset: Asset::Cw20(Cw20Coin { + address: "neutron123".to_string(), + amount: Uint128::from(1_000_000u128), + }), + affiliates: vec![], + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::PostSwapAction { + min_asset: Asset::Cw20(Cw20Coin { + address: "neutron123".to_string(), + amount: Uint128::from(1_000_000u128), + }), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + exact_out: false, + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "Cw20 Min Asset")] +#[test_case( + Params { + info_funds: vec![ + Coin::new(100_000, "osmo"), + ], + sent_asset: Some(Asset::Native(Coin::new(100_000, "osmo"))), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "osmo".to_string(), + denom_out: "uatom".to_string(), + interface: None, + } + ], + }, + ), + min_asset: Asset::Native(Coin::new(100_000, "uatom")), + timeout_timestamp: 101, + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "untrn")], + timeout_fee: vec![Coin::new(100_000, "untrn")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: Some( + SwapExactAssetOut { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "osmo".to_string(), + denom_out: "untrn".to_string(), + interface: None, + } + ], + refund_address: None, + } + ), + }, + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Skip(Overflow(OverflowError { + operation: OverflowOperation::Sub, + operand1: "100000".to_string(), + operand2: "200000".to_string(), + }))), + }; + "Fee Swap Coin In Amount More Than Remaining Coin Received Amount- Expect Error")] +#[test_case( + Params { + info_funds: vec![ + Coin::new(1_000_000, "uatom"), + ], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "uatom"))), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "osmo".to_string(), + denom_out: "uatom".to_string(), + interface: None, + } + ], + }, + ), + min_asset: Asset::Native(Coin::new(100_000, "uatom")), + timeout_timestamp: 101, + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "untrn")], + timeout_fee: vec![Coin::new(100_000, "untrn")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: Some( + SwapExactAssetOut { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "uatom".to_string(), + denom_out: "untrn".to_string(), + interface: None, + } + ], + refund_address: None, + } + ), + }, + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::FeeSwapAssetInDenomMismatch), + }; + "Fee Swap Coin In Denom In Not The Same As Remaining Coin Received Denom - Expect Error")] +#[test_case( + Params { + info_funds: vec![ + Coin::new(1_000_000, "osmo"), + ], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "osmo"))), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "osmo".to_string(), + denom_out: "uatom".to_string(), + interface: None, + } + ], + }, + ), + min_asset: Asset::Native(Coin::new(100_000, "uatom")), + timeout_timestamp: 101, + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "untrn")], + timeout_fee: vec![Coin::new(100_000, "untrn")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: Some( + SwapExactAssetOut { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "uatom".to_string(), + denom_out: "untrn".to_string(), + interface: None, + } + ], + refund_address: None, + } + ), + }, + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Skip(SwapOperationsAssetInDenomMismatch)), + }; + "Fee Swap First Swap Operation Denom In Is Not The Same As Remaining Coin Received Denom - Expect Error")] +#[test_case( + Params { + info_funds: vec![ + Coin::new(1_000_000, "osmo"), + ], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "osmo"))), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "osmo".to_string(), + denom_out: "uatom".to_string(), + interface: None, + } + ], + }, + ), + min_asset: Asset::Native(Coin::new(100_000, "uatom")), + timeout_timestamp: 101, + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "untrn")], + timeout_fee: vec![Coin::new(100_000, "untrn")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: Some( + SwapExactAssetOut { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "osmo".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + refund_address: None, + } + ), + }, + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Skip(SwapOperationsAssetOutDenomMismatch)), + }; + "Fee Swap Last Swap Operation Denom Out Is Not The Same As IBC Fee Coin Denom- Expect Error")] +#[test_case( + Params { + info_funds: vec![ + Coin::new(1_000_000, "untrn"), + ], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "untrn"))), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + }, + ), + min_asset: Asset::Native(Coin::new(800_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "uatom")], + timeout_fee: vec![Coin::new(100_000, "uatom")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: None, + }, + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::IBCFeeDenomDiffersFromAssetReceived), + }; + "User Swap With IBC Transfer With IBC Fees But IBC Fee Coin Denom Is Not The Same As Remaining Coin Received Denom - Expect Error")] +#[test_case( + Params { + info_funds: vec![ + Coin::new(1_000_000, "osmo"), + ], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "osmo"))), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "osmo".to_string(), + denom_out: "atom".to_string(), + interface: None, + } + ], + }, + ), + min_asset: Asset::Native(Coin::new(100_000, "atom")), + timeout_timestamp: 101, + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: None, + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: Some( + SwapExactAssetOut { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "osmo".to_string(), + denom_out: "untrn".to_string(), + interface: None, + } + ], + refund_address: None, + } + ), + }, + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::FeeSwapWithoutIbcFees), + }; + "Fee Swap With IBC Trnasfer But Without IBC Fees - Expect Error")] +#[test_case( + Params { + info_funds: vec![ + Coin::new(1_000_000, "osmo"), + ], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "osmo"))), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "osmo".to_string(), + denom_out: "atom".to_string(), + interface: None, + } + ], + }, + ), + min_asset: Asset::Native(Coin::new(100_000, "atom")), + timeout_timestamp: 101, + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "uatom")], + timeout_fee: vec![Coin::new(100_000, "untrn")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: Some( + SwapExactAssetOut { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "osmo".to_string(), + denom_out: "untrn".to_string(), + interface: None, + } + ], + refund_address: None, + } + ), + }, + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Skip(IbcFeesNotOneCoin)), + }; + "IBC Transfer With IBC Fees But More Than One IBC Fee Denom Specified - Expect Error")] +#[test_case( + Params { + info_funds: vec![ + Coin::new(1_000_000, "osmo"), + ], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "osmo"))), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "osmo".to_string(), + denom_out: "atom".to_string(), + interface: None, + } + ], + }, + ), + min_asset: Asset::Native(Coin::new(100_000, "atom")), + timeout_timestamp: 101, + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![], + timeout_fee: vec![], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: Some( + SwapExactAssetOut { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "osmo".to_string(), + denom_out: "untrn".to_string(), + interface: None, + } + ], + refund_address: None, + } + ), + }, + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Skip(IbcFeesNotOneCoin)), + }; + "IBC Transfer With IBC Fees But No IBC Fee Coins Specified - Expect Error")] +#[test_case( + Params { + info_funds: vec![ + Coin::new(1_000_000, "osmo"), + ], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "osmo"))), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "osmo".to_string(), + denom_out: "atom".to_string(), + interface: None, + } + ], + }, + ), + min_asset: Asset::Native(Coin::new(100_000, "atom")), + timeout_timestamp: 101, + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![Coin::new(0, "uatom")], + ack_fee: vec![], + timeout_fee: vec![], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: Some( + SwapExactAssetOut { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "osmo".to_string(), + denom_out: "untrn".to_string(), + interface: None, + } + ], + refund_address: None, + } + ), + }, + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Skip(IbcFeesNotOneCoin)), + }; + "IBC Transfer With IBC Fee Coin Amount Zero - Expect Error")] +#[test_case( + Params { + info_funds: vec![], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "osmo"))), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + }, + ), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Skip(Payment(NoFunds{}))), + }; + "No Coins Sent to Contract - Expect Error")] +#[test_case( + Params { + info_funds: vec![ + Coin::new(1_000_000, "untrn"), + Coin::new(1_000_000, "osmo"), + ], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "untrn"))), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + }, + ), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Skip(Payment(MultipleDenoms{}))), + }; + "More Than One Coin Sent to Contract - Expect Error")] +#[test_case( + Params { + info_funds: vec![], + sent_asset: Some(Asset::Cw20(Cw20Coin { + address: "neutron123".to_string(), + amount: Uint128::from(2_000_000u128), + })), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + }, + ), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Skip(InvalidCw20Coin)), + }; + "Not Enough Cw20 Tokens Sent To Contract - Expect Error")] +#[test_case( + Params { + info_funds: vec![Coin::new(1_000_000, "osmo")], + sent_asset: Some(Asset::Cw20(Cw20Coin { + address: "neutron123".to_string(), + amount: Uint128::from(1_000_000u128), + })), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + }, + ), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Skip(Payment(NonPayable{}))), + }; + "Cw20 Token Specified As Sent Asset With Native Coin Sent To Contract - Expect Error")] +#[test_case( + Params { + info_funds: vec![ + Coin::new(1_000_000, "osmo"), + ], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "osmo"))), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "osmo".to_string(), + denom_out: "uatom".to_string(), + interface: None, + } + ], + }, + ), + min_asset: Asset::Native(Coin::new(100_000, "uatom")), + timeout_timestamp: 101, + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "untrn")], + timeout_fee: vec![Coin::new(100_000, "untrn")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: Some( + SwapExactAssetOut { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![], + refund_address: None, + } + ), + }, + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Skip(SwapOperationsEmpty)), + }; + "Empty Fee Swap Operations - Expect Error")] +#[test_case( + Params { + info_funds: vec![ + Coin::new(1_000_000, "untrn"), + ], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "untrn"))), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![], + }, + ), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 99, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Timeout), + }; + "Current Block Time Greater Than Timeout Timestamp - Expect Error")] +#[test_case( + Params { + info_funds: vec![], + sent_asset: None, + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + } + ], + } + ), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Payment(NoFunds{})), + }; + "Sent Asset Not Given with Invalid One Coin")] +#[test_case( + Params { + info_funds: vec![Coin::new(1_000_000, "untrn")], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "untrn"))), + user_swap: Swap::SmartSwapExactAssetIn(SmartSwapExactAssetIn { + swap_venue_name: "swap_venue_name".to_string(), + routes: vec![ + Route { + offer_asset: Asset::Native(Coin::new(250_000, "untrn")), + operations: vec![SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + }], + }, + Route { + offer_asset: Asset::Native(Coin::new(750_000, "untrn")), + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "untrn".to_string(), + denom_out: "neutron123".to_string(), + interface: None, + }, + SwapOperation { + pool: "pool_3".to_string(), + denom_in: "neutron123".to_string(), + denom_out: "osmo".to_string(), + interface: None, + }, + ], + }, + ], + }), + min_asset: Asset::Native(Coin::new(800_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "untrn")], + timeout_fee: vec![Coin::new(100_000, "untrn")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5".to_string(), + }, + fee_swap: None, + }, + affiliates: vec![], + expected_messages: vec![ + SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "ibc_transfer_adapter".to_string(), + amount: vec![Coin::new(200_000, "untrn")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::UserSwap { + swap: Swap::SmartSwapExactAssetIn(SmartSwapExactAssetIn { + swap_venue_name: "swap_venue_name".to_string(), + routes: vec![ + Route { + offer_asset: Asset::Native(Coin::new(250_000, "untrn")), + operations: vec![SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + }], + }, + Route { + offer_asset: Asset::Native(Coin::new(550_000, "untrn")), + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "untrn".to_string(), + denom_out: "neutron123".to_string(), + interface: None, + }, + SwapOperation { + pool: "pool_3".to_string(), + denom_in: "neutron123".to_string(), + denom_out: "osmo".to_string(), + interface: None, + }, + ], + }, + ], + }), + remaining_asset: Asset::Native(Coin::new(800_000, "untrn")), + min_asset: Asset::Native(Coin::new(800_000, "osmo")), + affiliates: vec![], + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::PostSwapAction { + min_asset: Asset::Native(Coin::new(800_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::IbcTransfer { + ibc_info: IbcInfo { + source_channel: "channel-0".to_string(), + receiver: "receiver".to_string(), + memo: "".to_string(), + fee: Some(IbcFee { + recv_fee: vec![], + ack_fee: vec![Coin::new(100_000, "untrn")], + timeout_fee: vec![Coin::new(100_000, "untrn")], + }), + recover_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5" + .to_string(), + }, + fee_swap: None, + }, + exact_out: false, + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "Adjusts SmartSwapExactAssetIn route offer_assets sum to match remaining_asset" +)] +fn test_execute_swap_and_action(params: Params) { + // Create mock dependencies + let mut deps = mock_dependencies_with_balances(&[( + "entry_point", + &[Coin::new(1_000_000, "osmo"), Coin::new(1_000_000, "untrn")], + )]); + + // Create mock wasm handler to handle the swap adapter contract query + let wasm_handler = |query: &WasmQuery| -> QuerierResult { + match query { + WasmQuery::Smart { contract_addr, .. } => { + if contract_addr == "swap_venue_adapter" { + SystemResult::Ok(ContractResult::Ok( + to_json_binary(&Asset::Native(Coin::new(200_000, "osmo"))).unwrap(), + )) + } else if contract_addr == "swap_venue_adapter_2" { + SystemResult::Ok(ContractResult::Ok( + to_json_binary(&Asset::Cw20(Cw20Coin { + address: "neutron123".to_string(), + amount: Uint128::from(200_000u128), + })) + .unwrap(), + )) + } else { + SystemResult::Ok(ContractResult::Ok( + to_json_binary(&BalanceResponse { + balance: Uint128::from(1_000_000u128), + }) + .unwrap(), + )) + } + } + _ => panic!("Unsupported query: {:?}", query), + } + }; + + // Update querier with mock wasm handler + deps.querier.update_wasm(wasm_handler); + + // Create mock env with parameters that make testing easier + let mut env = mock_env(); + env.contract.address = Addr::unchecked("entry_point"); + env.block.time = Timestamp::from_nanos(100); + + // Convert info funds vector into a slice of Coin objects + let info_funds: &[Coin] = ¶ms.info_funds; + + // Create mock info with entry point contract address + let info = mock_info("swapper", info_funds); + + // Store the swap venue adapter contract address + let swap_venue_adapter = Addr::unchecked("swap_venue_adapter"); + let swap_venue_adapter_2 = Addr::unchecked("swap_venue_adapter_2"); + SWAP_VENUE_MAP + .save( + deps.as_mut().storage, + "swap_venue_name", + &swap_venue_adapter, + ) + .unwrap(); + SWAP_VENUE_MAP + .save( + deps.as_mut().storage, + "swap_venue_name_2", + &swap_venue_adapter_2, + ) + .unwrap(); + + // Store the ibc transfer adapter contract address + let ibc_transfer_adapter = Addr::unchecked("ibc_transfer_adapter"); + IBC_TRANSFER_CONTRACT_ADDRESS + .save(deps.as_mut().storage, &ibc_transfer_adapter) + .unwrap(); + + // Call execute_swap_and_action with the given test case params + let res = skip_go_entry_point::contract::execute( + deps.as_mut(), + env, + info, + ExecuteMsg::SwapAndAction { + sent_asset: params.sent_asset, + user_swap: params.user_swap, + min_asset: params.min_asset, + timeout_timestamp: params.timeout_timestamp, + post_swap_action: params.post_swap_action, + affiliates: params.affiliates, + }, + ); + + match res { + Ok(res) => { + // Assert the test did not expect an error + assert!( + params.expected_error.is_none(), + "expected test to error with {:?}, but it succeeded", + params.expected_error + ); + + // Assert the number of messages in the response is correct + assert_eq!( + res.messages.len(), + params.expected_messages.len(), + "expected {:?} messages, but got {:?}", + params.expected_messages.len(), + res.messages.len() + ); + + // Assert the messages in the response are correct + assert_eq!(res.messages, params.expected_messages,); + + // Assert the pre swap out asset amount set is correct + let pre_swap_out_asset_amount = PRE_SWAP_OUT_ASSET_AMOUNT.load(&deps.storage).unwrap(); + assert_eq!(pre_swap_out_asset_amount, Uint128::from(1_000_000u128)); + } + Err(err) => { + // Assert the test expected an error + assert!( + params.expected_error.is_some(), + "expected test to succeed, but it errored with {:?}", + err + ); + + // Assert the error is correct + assert_eq!(err, params.expected_error.unwrap()); + } + } +} +*/ diff --git a/contracts/secret-entry-point/tests/test_execute_swap_and_action_with_recover.rs b/contracts/secret-entry-point/tests/test_execute_swap_and_action_with_recover.rs new file mode 100644 index 00000000..b33ac046 --- /dev/null +++ b/contracts/secret-entry-point/tests/test_execute_swap_and_action_with_recover.rs @@ -0,0 +1,447 @@ +/* +use cosmwasm_std::{ + testing::{mock_dependencies_with_balances, mock_env, mock_info}, + to_json_binary, Addr, Coin, CosmosMsg, ReplyOn, SubMsg, Timestamp, Uint128, WasmMsg, +}; +use cw20::Cw20Coin; +use skip::{ + asset::Asset, + entry_point::{Action, Affiliate, ExecuteMsg}, + swap::{Swap, SwapExactAssetIn, SwapOperation}, +}; +use skip_go_entry_point::{error::ContractError, state::RECOVER_TEMP_STORAGE}; +use test_case::test_case; +*/ + +/* +Test Cases: + +Expect Response + - Happy Path Single Coin + - Happy Path Multiple Coins + - Happy Path Cw20 Asset + - Sent Asset Not Given With Valid One Coin + - Sent Asset Not Given With Invalid One Coin + + // Note: The following test case is an invalid call to the contract + // showing that under the circumstance both coins and a Cw20 token + // is sent to the contract, the contract will recover all assets. + - Happy Path Multiple Coins And Cw20 Asset + +*/ + +/* +// Define test parameters +struct Params { + info_funds: Vec, + sent_asset: Option, + user_swap: Swap, + min_asset: Asset, + timeout_timestamp: u64, + post_swap_action: Action, + affiliates: Vec, + expected_assets: Vec, + expected_messages: Vec, + expected_error: Option, +} + +// Test execute_swap_and_action_with_recover +#[test_case( + Params { + info_funds: vec![Coin::new(1_000_000, "untrn")], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "untrn"))), + user_swap: Swap::SwapExactAssetIn(SwapExactAssetIn { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + }], + }), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + expected_assets: vec![Asset::Native(Coin::new(1_000_000, "untrn"))], + expected_messages: vec![SubMsg { + id: 1, + msg: CosmosMsg::from(WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::SwapAndAction { + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "untrn"))), + user_swap: Swap::SwapExactAssetIn(SwapExactAssetIn { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + }], + }), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + }) + .unwrap(), + funds: vec![Coin::new(1000000, "untrn")], + }), + gas_limit: None, + reply_on: ReplyOn::Always, + }], + expected_error: None, + }; + "Happy Path Single Coin")] +#[test_case( + Params { + info_funds: vec![Coin::new(1_000_000, "untrn"), Coin::new(1_000_000, "osmo")], + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "untrn"))), + user_swap: Swap::SwapExactAssetIn(SwapExactAssetIn { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + }], + }), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + expected_assets: vec![Asset::Native(Coin::new(1_000_000, "untrn")), Asset::Native(Coin::new(1_000_000, "osmo"))], + expected_messages: vec![SubMsg { + id: 1, + msg: CosmosMsg::from(WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::SwapAndAction { + sent_asset: Some(Asset::Native(Coin::new(1_000_000, "untrn"))), + user_swap: Swap::SwapExactAssetIn(SwapExactAssetIn { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + }], + }), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + }) + .unwrap(), + funds: vec![Coin::new(1000000, "untrn"), Coin::new(1000000, "osmo")], + }), + gas_limit: None, + reply_on: ReplyOn::Always, + }], + expected_error: None, + }; + "Happy Path Multiple Coins")] +#[test_case( + Params { + info_funds: vec![], + sent_asset: Some(Asset::Cw20(Cw20Coin{ + address: "neutron123".to_string(), + amount: Uint128::from(1_000_000u128), + })), + user_swap: Swap::SwapExactAssetIn(SwapExactAssetIn { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![SwapOperation { + pool: "pool".to_string(), + denom_in: "neutron123".to_string(), + denom_out: "osmo".to_string(), + interface: None, + }], + }), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + expected_assets: vec![Asset::Cw20(Cw20Coin{ + address: "neutron123".to_string(), + amount: Uint128::from(1_000_000u128), + })], + expected_messages: vec![SubMsg { + id: 1, + msg: CosmosMsg::from(WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::SwapAndAction { + sent_asset: Some(Asset::Cw20(Cw20Coin{ + address: "neutron123".to_string(), + amount: Uint128::from(1_000_000u128), + })), + user_swap: Swap::SwapExactAssetIn(SwapExactAssetIn { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![SwapOperation { + pool: "pool".to_string(), + denom_in: "neutron123".to_string(), + denom_out: "osmo".to_string(), + interface: None, + }], + }), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + }) + .unwrap(), + funds: vec![], + }), + gas_limit: None, + reply_on: ReplyOn::Always, + }], + expected_error: None, + }; + "Happy Path Cw20 Asset")] +#[test_case( + Params { + info_funds: vec![Coin::new(1_000_000, "untrn")], + sent_asset: None, + user_swap: Swap::SwapExactAssetIn(SwapExactAssetIn { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + }], + }), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + expected_assets: vec![Asset::Native(Coin::new(1_000_000, "untrn"))], + expected_messages: vec![SubMsg { + id: 1, + msg: CosmosMsg::from(WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::SwapAndAction { + sent_asset: None, + user_swap: Swap::SwapExactAssetIn(SwapExactAssetIn { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + }], + }), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + }) + .unwrap(), + funds: vec![Coin::new(1000000, "untrn")], + }), + gas_limit: None, + reply_on: ReplyOn::Always, + }], + expected_error: None, + }; + "Sent Asset Not Given With Valid One Coin")] +#[test_case( + Params { + info_funds: vec![Coin::new(1_000_000, "untrn"), Coin::new(1_000_000, "osmo")], + sent_asset: None, + user_swap: Swap::SwapExactAssetIn(SwapExactAssetIn { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + }], + }), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + expected_assets: vec![Asset::Native(Coin::new(1_000_000, "untrn")), Asset::Native(Coin::new(1_000_000, "osmo"))], + expected_messages: vec![SubMsg { + id: 1, + msg: CosmosMsg::from(WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::SwapAndAction { + sent_asset: None, + user_swap: Swap::SwapExactAssetIn(SwapExactAssetIn { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + }], + }), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + }) + .unwrap(), + funds: vec![Coin::new(1000000, "untrn"), Coin::new(1000000, "osmo")], + }), + gas_limit: None, + reply_on: ReplyOn::Always, + }], + expected_error: None, + }; + "Sent Asset Not Given With Invalid One Coin")] +#[test_case( + Params { + info_funds: vec![Coin::new(1_000_000, "untrn"), Coin::new(1_000_000, "osmo")], + sent_asset: Some(Asset::Cw20(Cw20Coin{ + address: "neutron123".to_string(), + amount: Uint128::from(1_000_000u128), + })), + user_swap: Swap::SwapExactAssetIn(SwapExactAssetIn { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + }], + }), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + expected_assets: vec![Asset::Native(Coin::new(1_000_000, "untrn")), Asset::Native(Coin::new(1_000_000, "osmo")), Asset::Cw20(Cw20Coin{ + address: "neutron123".to_string(), + amount: Uint128::from(1_000_000u128), + })], + expected_messages: vec![SubMsg { + id: 1, + msg: CosmosMsg::from(WasmMsg::Execute { + contract_addr: "entry_point".to_string(), + msg: to_json_binary(&ExecuteMsg::SwapAndAction { + sent_asset: Some(Asset::Cw20(Cw20Coin{ + address: "neutron123".to_string(), + amount: Uint128::from(1_000_000u128), + })), + user_swap: Swap::SwapExactAssetIn(SwapExactAssetIn { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![SwapOperation { + pool: "pool".to_string(), + denom_in: "untrn".to_string(), + denom_out: "osmo".to_string(), + interface: None, + }], + }), + min_asset: Asset::Native(Coin::new(1_000_000, "osmo")), + timeout_timestamp: 101, + post_swap_action: Action::Transfer { + to_address: "to_address".to_string(), + }, + affiliates: vec![], + }) + .unwrap(), + funds: vec![Coin::new(1000000, "untrn"), Coin::new(1000000, "osmo")], + }), + gas_limit: None, + reply_on: ReplyOn::Always, + }], + expected_error: None, + }; + "Happy Path Multiple Coins And Cw20 Asset")] +fn test_execute_swap_and_action_with_recover(params: Params) { + // Create mock dependencies + let mut deps = mock_dependencies_with_balances(&[( + "entry_point", + &[Coin::new(1_000_000, "osmo"), Coin::new(1_000_000, "untrn")], + )]); + + // Create mock env with parameters that make testing easier + let mut env = mock_env(); + env.contract.address = Addr::unchecked("entry_point"); + env.block.time = Timestamp::from_nanos(100); + + // Convert info funds vector into a slice of Coin objects + let info_funds: &[Coin] = ¶ms.info_funds; + + // Create mock info with entry point contract address + let info = mock_info("swapper", info_funds); + + let recovery_addr = Addr::unchecked("recovery_address"); + // Call execute_swap_and_action with the given test case params + let res = skip_go_entry_point::contract::execute( + deps.as_mut(), + env, + info, + ExecuteMsg::SwapAndActionWithRecover { + sent_asset: params.sent_asset, + user_swap: params.user_swap, + min_asset: params.min_asset, + timeout_timestamp: params.timeout_timestamp, + post_swap_action: params.post_swap_action, + affiliates: params.affiliates, + recovery_addr: recovery_addr.clone(), + }, + ); + + match res { + Ok(res) => { + // Assert the test did not expect an error + assert!( + params.expected_error.is_none(), + "expected test to error with {:?}, but it succeeded", + params.expected_error + ); + + // Assert the number of messages in the response is correct + assert_eq!( + res.messages.len(), + params.expected_messages.len(), + "expected {:?} messages, but got {:?}", + params.expected_messages.len(), + res.messages.len() + ); + + // Assert the messages in the response are correct + assert_eq!(res.messages, params.expected_messages,); + + // Assert the recover temp storage is correct + let recover_temp_storage = RECOVER_TEMP_STORAGE.load(&deps.storage).unwrap(); + assert_eq!(recover_temp_storage.recovery_addr, recovery_addr); + assert_eq!(recover_temp_storage.assets, params.expected_assets); + } + Err(err) => { + // Assert the test expected an error + assert!( + params.expected_error.is_some(), + "expected test to succeed, but it errored with {:?}", + err + ); + + // Assert the error is correct + assert_eq!(err, params.expected_error.unwrap()); + } + } +} +*/ diff --git a/contracts/secret-entry-point/tests/test_reply.rs b/contracts/secret-entry-point/tests/test_reply.rs index 52e83114..83110207 100644 --- a/contracts/secret-entry-point/tests/test_reply.rs +++ b/contracts/secret-entry-point/tests/test_reply.rs @@ -3,8 +3,7 @@ use cosmwasm_std::{ to_binary, Addr, BankMsg, Coin, ContractInfo, CosmosMsg, Reply, StdError, SubMsg, SubMsgResponse, SubMsgResult, Uint128, WasmMsg, }; -use cw20::{Cw20Coin, Cw20ExecuteMsg}; -use secret_skip::{asset::Asset, snip20}; +use secret_skip::{asset::Asset, cw20::Cw20Coin, snip20}; use skip_go_secret_entry_point::{ reply::RecoverTempStorage, state::{RECOVER_TEMP_STORAGE, REGISTERED_TOKENS}, diff --git a/contracts/secret-entry-point/tests/test_user_swap.rs b/contracts/secret-entry-point/tests/test_user_swap.rs new file mode 100644 index 00000000..b3b80583 --- /dev/null +++ b/contracts/secret-entry-point/tests/test_user_swap.rs @@ -0,0 +1,1282 @@ +/* +use cosmwasm_std::{ + testing::{mock_dependencies_with_balances, mock_env, mock_info}, + to_json_binary, Addr, BankMsg, Coin, ContractResult, OverflowError, OverflowOperation, + QuerierResult, + ReplyOn::Never, + SubMsg, SystemResult, Timestamp, Uint128, WasmMsg, WasmQuery, +}; +use cw20::{Cw20Coin, Cw20ExecuteMsg}; +use skip::{ + asset::Asset, + entry_point::{Affiliate, ExecuteMsg}, + error::SkipError::{ + Overflow, SwapOperationsAssetInDenomMismatch, SwapOperationsAssetOutDenomMismatch, + SwapOperationsEmpty, + }, + swap::{ + ExecuteMsg as SwapExecuteMsg, Route, SmartSwapExactAssetIn, Swap, SwapExactAssetIn, + SwapExactAssetOut, SwapOperation, + }, +}; +use skip_go_entry_point::{error::ContractError, state::SWAP_VENUE_MAP}; +use test_case::test_case; +*/ + +/* +Test Cases: + +Expect Response + // Swap Exact Coin In + - User Swap Exact Coin In With No Affiliates + - User Swap Exact Coin In With Single Affiliate + - User Swap Exact Coin In With Multiple Affiliates + - User Swap Exact Coin In With Zero Fee Affiliate + - User Swap Exact Cw20 Asset In With Single Affiliate + + // Swap Exact Coin Out + - User Swap Exact Coin Out With No Affiliates + - User Swap Exact Coin Out With Single Affiliate + - User Swap Exact Coin Out With Multiple Affiliates + - User Swap Exact Coin Out With Zero Fee Affiliate + - User Swap Exact Coin Out With Refund Amount Zero (Ensure No Refund Message Included) + - User Swap Exact Cw20 Asset Out With Single Affiliate + +Expect Error + // Swap Exact Coin In + - User Swap Exact Coin In First Swap Operation Denom In Is Not The Same As Remaining Coin Received Denom + - User Swap Exact Coin In Last Swap Operation Denom Out Is Not The Same As Min Coin Out Denom + - User Swap Exact Coin In Empty Swap Operations + + // Swap Exact Coin Out + - User Swap Exact Coin Out First Swap Operation Denom In Is Not The Same As Remaining Coin Received Denom + - User Swap Exact Coin Out Last Swap Operation Denom Out Is Not The Same As Min Coin Out Denom + - User Swap Exact Coin Out Empty Swap Operations + - User Swap Exact Coin Out With No Refund Address + - User Swap Exact Coin Out Where Coin In Denom Is Not The Same As Remaining Coin Received Denom + - User Swap Exact Coin Out Where Coin In Amount More Than Remaining Coin Received Amount + - User Swap Exact Asset Out Where Asset In Amount More Than Remaining Asset Received Amount + + // General + - Unauthorized Caller + + */ + +/* +// Define test parameters +struct Params { + caller: String, + user_swap: Swap, + remaining_asset: Asset, + min_asset: Asset, + affiliates: Vec, + expected_messages: Vec, + expected_error: Option, +} + +// Test execute_swap_and_action +#[test_case( + Params { + caller: "entry_point".to_string(), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + } + ), + remaining_asset: Asset::Native(Coin::new(1_000_000, "un")), + min_asset: Asset::Native(Coin::new(1_000_000, "os")), + affiliates: vec![], + expected_messages: vec![ + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "swap_venue_adapter".to_string(), + msg: to_json_binary(&SwapExecuteMsg::Swap { + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + }).unwrap(), + funds: vec![Coin::new(1_000_000, "un")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "User Swap Exact Coin In With No Affiliates")] +#[test_case( + Params { + caller: "entry_point".to_string(), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + } + ), + remaining_asset: Asset::Native(Coin::new(1_000_000, "un")), + min_asset: Asset::Native(Coin::new(1_000_000, "os")), + affiliates: vec![Affiliate { + address: "affiliate".to_string(), + basis_points_fee: Uint128::new(1000), + }], + expected_messages: vec![ + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "swap_venue_adapter".to_string(), + msg: to_json_binary(&SwapExecuteMsg::Swap { + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + }).unwrap(), + funds: vec![Coin::new(1_000_000, "un")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "affiliate".to_string(), + amount: vec![Coin::new(100_000, "os")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "User Swap Exact Coin In With Single Affiliate")] +#[test_case( + Params { + caller: "entry_point".to_string(), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name_2".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "neutron123".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + } + ), + remaining_asset: Asset::Cw20(Cw20Coin { + address: "neutron123".to_string(), + amount: Uint128::new(1_000_000), + }), + min_asset: Asset::Native(Coin::new(1_000_000, "os")), + affiliates: vec![Affiliate { + address: "affiliate".to_string(), + basis_points_fee: Uint128::new(1000), + }], + expected_messages: vec![ + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "neutron123".to_string(), + msg: to_json_binary(&Cw20ExecuteMsg::Send { + contract: "swap_venue_adapter_2".to_string(), + amount: Uint128::new(1_000_000), + msg: to_json_binary(&SwapExecuteMsg::Swap { + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "neutron123".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + }).unwrap(), + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "affiliate".to_string(), + amount: vec![Coin::new(100_000, "os")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "User Swap Exact Cw20 Asset In With Single Affiliate")] +#[test_case( + Params { + caller: "entry_point".to_string(), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + } + ), + remaining_asset: Asset::Native(Coin::new(1_000_000, "un")), + min_asset: Asset::Native(Coin::new(1_000_000, "os")), + affiliates: vec![ + Affiliate { + address: "affiliate_1".to_string(), + basis_points_fee: Uint128::new(1000), + }, + Affiliate { + address: "affiliate_2".to_string(), + basis_points_fee: Uint128::new(1000), + }, + ], + expected_messages: vec![ + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "swap_venue_adapter".to_string(), + msg: to_json_binary(&SwapExecuteMsg::Swap { + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + }).unwrap(), + funds: vec![Coin::new(1_000_000, "un")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "affiliate_1".to_string(), + amount: vec![Coin::new(100_000, "os")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "affiliate_2".to_string(), + amount: vec![Coin::new(100_000, "os")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "User Swap Exact Coin In With Multiple Affiliates")] +#[test_case( + Params { + caller: "entry_point".to_string(), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + } + ), + remaining_asset: Asset::Native(Coin::new(1_000_000, "un")), + min_asset: Asset::Native(Coin::new(1_000_000, "os")), + affiliates: vec![Affiliate { + address: "affiliate".to_string(), + basis_points_fee: Uint128::new(0), + }], + expected_messages: vec![ + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "swap_venue_adapter".to_string(), + msg: to_json_binary(&SwapExecuteMsg::Swap { + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + }).unwrap(), + funds: vec![Coin::new(1_000_000, "un")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "User Swap Exact Coin In With Zero Fee Affiliate")] +#[test_case( + Params { + caller: "entry_point".to_string(), + user_swap: Swap::SwapExactAssetOut ( + SwapExactAssetOut{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + refund_address: Some("refund_address".to_string()), + } + ), + remaining_asset: Asset::Native(Coin::new(1_000_000, "un")), + min_asset: Asset::Native(Coin::new(500_000, "os")), + affiliates: vec![], + expected_messages: vec![ + SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "refund_address".to_string(), + amount: vec![Coin::new(500_000, "un")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "swap_venue_adapter".to_string(), + msg: to_json_binary(&SwapExecuteMsg::Swap { + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + }).unwrap(), + funds: vec![Coin::new(500_000, "un")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "User Swap Exact Coin Out With No Affiliates")] +#[test_case( + Params { + caller: "entry_point".to_string(), + user_swap: Swap::SwapExactAssetOut ( + SwapExactAssetOut{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + refund_address: Some("refund_address".to_string()), + } + ), + remaining_asset: Asset::Native(Coin::new(1_000_000, "un")), + min_asset: Asset::Native(Coin::new(500_000, "os")), + affiliates: vec![ + Affiliate { + address: "affiliate".to_string(), + basis_points_fee: Uint128::new(1000), + }, + ], + expected_messages: vec![ + SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "refund_address".to_string(), + amount: vec![Coin::new(500_000, "un")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "swap_venue_adapter".to_string(), + msg: to_json_binary(&SwapExecuteMsg::Swap { + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + }).unwrap(), + funds: vec![Coin::new(500_000, "un")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "affiliate".to_string(), + amount: vec![Coin::new(50_000, "os")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "User Swap Exact Coin Out With Single Affiliate")] +#[test_case( + Params { + caller: "entry_point".to_string(), + user_swap: Swap::SwapExactAssetOut ( + SwapExactAssetOut{ + swap_venue_name: "swap_venue_name_2".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "neutron123".to_string(), + denom_out: "neutron987".to_string(), + interface: None, + } + ], + refund_address: Some("refund_address".to_string()), + } + ), + remaining_asset: Asset::Cw20(Cw20Coin { + address: "neutron123".to_string(), + amount: Uint128::new(1_000_000), + }), + min_asset: Asset::Cw20(Cw20Coin { + address: "neutron987".to_string(), + amount: Uint128::new(1_000_000), + }), + affiliates: vec![Affiliate { + address: "affiliate".to_string(), + basis_points_fee: Uint128::new(1000), + }], + expected_messages: vec![ + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "neutron123".to_string(), + msg: to_json_binary(&Cw20ExecuteMsg::Transfer { + recipient: "refund_address".to_string(), + amount: Uint128::new(500_000), + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "neutron123".to_string(), + msg: to_json_binary(&Cw20ExecuteMsg::Send { + contract: "swap_venue_adapter_2".to_string(), + amount: Uint128::new(500_000), + msg: to_json_binary(&SwapExecuteMsg::Swap { + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "neutron123".to_string(), + denom_out: "neutron987".to_string(), + interface: None, + } + ], + }).unwrap(), + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "neutron987".to_string(), + msg: to_json_binary(&Cw20ExecuteMsg::Transfer { + recipient: "affiliate".to_string(), + amount: Uint128::new(100_000), + }).unwrap(), + funds: vec![], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "User Swap Exact Cw20 Asset Out With Single Affiliate")] +#[test_case( + Params { + caller: "entry_point".to_string(), + user_swap: Swap::SwapExactAssetOut ( + SwapExactAssetOut{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + refund_address: Some("refund_address".to_string()), + } + ), + remaining_asset: Asset::Native(Coin::new(1_000_000, "un")), + min_asset: Asset::Native(Coin::new(500_000, "os")), + affiliates: vec![ + Affiliate { + address: "affiliate_1".to_string(), + basis_points_fee: Uint128::new(1000), + }, + Affiliate { + address: "affiliate_2".to_string(), + basis_points_fee: Uint128::new(1000), + }, + ], + expected_messages: vec![ + SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "refund_address".to_string(), + amount: vec![Coin::new(500_000, "un")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "swap_venue_adapter".to_string(), + msg: to_json_binary(&SwapExecuteMsg::Swap { + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + }).unwrap(), + funds: vec![Coin::new(500_000, "un")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "affiliate_1".to_string(), + amount: vec![Coin::new(50_000, "os")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "affiliate_2".to_string(), + amount: vec![Coin::new(50_000, "os")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "User Swap Exact Coin Out With Multiple Affiliates")] +#[test_case( + Params { + caller: "entry_point".to_string(), + user_swap: Swap::SwapExactAssetOut ( + SwapExactAssetOut{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + refund_address: Some("refund_address".to_string()), + } + ), + remaining_asset: Asset::Native(Coin::new(1_000_000, "un")), + min_asset: Asset::Native(Coin::new(1_000_000, "os")), + affiliates: vec![ + Affiliate { + address: "affiliate".to_string(), + basis_points_fee: Uint128::new(0), + }, + ], + expected_messages: vec![ + SubMsg { + id: 0, + msg: BankMsg::Send { + to_address: "refund_address".to_string(), + amount: vec![Coin::new(500_000, "un")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "swap_venue_adapter".to_string(), + msg: to_json_binary(&SwapExecuteMsg::Swap { + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + }).unwrap(), + funds: vec![Coin::new(500_000, "un")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "User Swap Exact Coin Out With Zero Fee Affiliate")] +#[test_case( + Params { + caller: "entry_point".to_string(), + user_swap: Swap::SwapExactAssetOut ( + SwapExactAssetOut{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + refund_address: Some("refund_address".to_string()), + } + ), + remaining_asset: Asset::Native(Coin::new(500_000, "un")), + min_asset: Asset::Native(Coin::new(500_000, "os")), + affiliates: vec![], + expected_messages: vec![ + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "swap_venue_adapter".to_string(), + msg: to_json_binary(&SwapExecuteMsg::Swap { + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + }).unwrap(), + funds: vec![Coin::new(500_000, "un")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, + }; + "User Swap Exact Coin Out With Refund Amount Zero")] +#[test_case( + Params { + caller: "entry_point".to_string(), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "un".to_string(), + denom_out: "ua".to_string(), + interface: None, + } + ], + }, + ), + remaining_asset: Asset::Native(Coin::new(1_000_000, "uo")), + min_asset: Asset::Native(Coin::new(100_000, "ua")), + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Skip(SwapOperationsAssetInDenomMismatch)), + }; + "User Swap Exact Coin In First Swap Operation Denom In Is Not The Same As Remaining Coin Received Denom - Expect Error")] +#[test_case( + Params { + caller: "entry_point".to_string(), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "uo".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + }, + ), + remaining_asset: Asset::Native(Coin::new(1_000_000, "uo")), + min_asset: Asset::Native(Coin::new(100_000, "ua")), + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Skip(SwapOperationsAssetOutDenomMismatch)), + }; + "User Swap Exact Coin In Last Swap Operation Denom Out Is Not The Same As Min Coin Out Denom - Expect Error")] +#[test_case( + Params { + caller: "entry_point".to_string(), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![], + }, + ), + remaining_asset: Asset::Native(Coin::new(1_000_000, "un")), + min_asset: Asset::Native(Coin::new(1_000_000, "os")), + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Skip(SwapOperationsEmpty)), + }; + "User Swap Exact Coin In Empty Swap Operations - Expect Error")] +#[test_case( + Params { + caller: "entry_point".to_string(), + user_swap: Swap::SwapExactAssetOut ( + SwapExactAssetOut{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "un".to_string(), + denom_out: "ua".to_string(), + interface: None, + } + ], + refund_address: Some("refund_address".to_string()), + }, + ), + remaining_asset: Asset::Native(Coin::new(1_000_000, "uo")), + min_asset: Asset::Native(Coin::new(100_000, "ua")), + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Skip(SwapOperationsAssetInDenomMismatch)), + }; + "User Swap Exact Coin Out First Swap Operation Denom In Is Not The Same As Remaining Coin Received Denom - Expect Error")] +#[test_case( + Params { + caller: "entry_point".to_string(), + user_swap: Swap::SwapExactAssetOut ( + SwapExactAssetOut{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "os".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + refund_address: Some("refund_address".to_string()), + }, + ), + remaining_asset: Asset::Native(Coin::new(1_000_000, "os")), + min_asset: Asset::Native(Coin::new(100_000, "ua")), + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Skip(SwapOperationsAssetOutDenomMismatch)), + }; + "User Swap Exact Coin Out Last Swap Operation Denom Out Is Not The Same As Min Coin Out Denom - Expect Error")] +#[test_case( + Params { + caller: "entry_point".to_string(), + user_swap: Swap::SwapExactAssetOut ( + SwapExactAssetOut{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![], + refund_address: Some("refund_address".to_string()), + }, + ), + remaining_asset: Asset::Native(Coin::new(1_000_000, "un")), + min_asset: Asset::Native(Coin::new(1_000_000, "os")), + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Skip(SwapOperationsEmpty)), + }; + "User Swap Exact Coin Out Empty Swap Operations - Expect Error")] +#[test_case( + Params { + caller: "entry_point".to_string(), + user_swap: Swap::SwapExactAssetOut ( + SwapExactAssetOut{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + refund_address: None, + } + ), + remaining_asset: Asset::Native(Coin::new(1_000_000, "un")), + min_asset: Asset::Native(Coin::new(500_000, "os")), + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::NoRefundAddress), + }; + "User Swap Exact Coin Out With No Refund Address - Expect Error")] +#[test_case( + Params { + caller: "entry_point".to_string(), + user_swap: Swap::SwapExactAssetOut ( + SwapExactAssetOut{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "ua".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + refund_address: Some("refund_address".to_string()), + } + ), + remaining_asset: Asset::Native(Coin::new(1_000_000, "ua")), + min_asset: Asset::Native(Coin::new(500_000, "os")), + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::UserSwapAssetInDenomMismatch), + }; + "User Swap Exact Coin Out Where Coin In Denom Is Not The Same As Remaining Coin Received Denom - Expect Error")] +#[test_case( + Params { + caller: "entry_point".to_string(), + user_swap: Swap::SwapExactAssetOut ( + SwapExactAssetOut{ + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + refund_address: Some("refund_address".to_string()), + } + ), + remaining_asset: Asset::Native(Coin::new(499_999, "un")), + min_asset: Asset::Native(Coin::new(500_000, "os")), + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Skip(Overflow(OverflowError { + operation: OverflowOperation::Sub, + operand1: "499999".to_string(), + operand2: "500000".to_string(), + }))), + }; + "User Swap Exact Coin Out Where Coin In Amount More Than Remaining Coin Received Amount - Expect Error")] +#[test_case( + Params { + caller: "entry_point".to_string(), + user_swap: Swap::SwapExactAssetOut ( + SwapExactAssetOut{ + swap_venue_name: "swap_venue_name_2".to_string(), + operations: vec![ + SwapOperation { + pool: "pool".to_string(), + denom_in: "neutron123".to_string(), + denom_out: "os".to_string(), + interface: None, + } + ], + refund_address: Some("refund_address".to_string()), + } + ), + remaining_asset: Asset::Cw20(Cw20Coin { + address: "neutron123".to_string(), + amount: Uint128::new(499_999), + }), + min_asset: Asset::Native(Coin::new(500_000, "os")), + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Skip(Overflow(OverflowError { + operation: OverflowOperation::Sub, + operand1: "499999".to_string(), + operand2: "500000".to_string(), + }))), + }; + "User Swap Exact Asset Out Where Asset In Amount More Than Remaining Asset Received Amount - Expect Error")] +#[test_case( + Params { + caller: "random".to_string(), + user_swap: Swap::SwapExactAssetIn ( + SwapExactAssetIn { + swap_venue_name: "swap_venue_name".to_string(), + operations: vec![], + }, + ), + remaining_asset: Asset::Native(Coin::new(1_000_000, "os")), + min_asset: Asset::Native(Coin::new(1_000_000, "os")), + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Unauthorized), + }; + "Unauthorized Caller - Expect Error")] +#[test_case(Params { + caller: "entry_point".to_string(), + user_swap: Swap::SmartSwapExactAssetIn(SmartSwapExactAssetIn { + swap_venue_name: "swap_venue_name".to_string(), + routes: vec![ + Route { + offer_asset: Asset::Native(Coin::new(250_000, "un")), + operations: vec![SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + }], + }, + Route { + offer_asset: Asset::Native(Coin::new(750_000, "un")), + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "un".to_string(), + denom_out: "neutron123".to_string(), + interface: None, + }, + SwapOperation { + pool: "pool_3".to_string(), + denom_in: "neutron123".to_string(), + denom_out: "os".to_string(), + interface: None, + }, + ], + }, + ], + }), + remaining_asset: Asset::Native(Coin::new(1_000_000, "un")), + min_asset: Asset::Native(Coin::new(1_000_000, "os")), + affiliates: vec![], + expected_messages: vec![ + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "swap_venue_adapter".to_string(), + msg: to_json_binary(&SwapExecuteMsg::Swap { + operations: vec![SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + }], + }) + .unwrap(), + funds: vec![Coin::new(250_000, "un")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + SubMsg { + id: 0, + msg: WasmMsg::Execute { + contract_addr: "swap_venue_adapter".to_string(), + msg: to_json_binary(&SwapExecuteMsg::Swap { + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "un".to_string(), + denom_out: "neutron123".to_string(), + interface: None, + }, + SwapOperation { + pool: "pool_3".to_string(), + denom_in: "neutron123".to_string(), + denom_out: "os".to_string(), + interface: None, + }, + ], + }) + .unwrap(), + funds: vec![Coin::new(750_000, "un")], + } + .into(), + gas_limit: None, + reply_on: Never, + }, + ], + expected_error: None, +}; "SmartSwapExactAssetIn")] +#[test_case(Params { + caller: "entry_point".to_string(), + user_swap: Swap::SmartSwapExactAssetIn(SmartSwapExactAssetIn { + swap_venue_name: "swap_venue_name".to_string(), + routes: vec![ + Route { + offer_asset: Asset::Native(Coin::new(250_000, "un")), + operations: vec![SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + }], + }, + Route { + offer_asset: Asset::Native(Coin::new(750_000, "un")), + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "neutron123".to_string(), + denom_out: "un".to_string(), + interface: None, + }, + SwapOperation { + pool: "pool_3".to_string(), + denom_in: "neutron123".to_string(), + denom_out: "os".to_string(), + interface: None, + }, + ], + }, + ], + }), + remaining_asset: Asset::Native(Coin::new(1_000_000, "un")), + min_asset: Asset::Native(Coin::new(1_000_000, "os")), + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Skip(SwapOperationsAssetInDenomMismatch)), +}; "SmartSwapExactAssetIn With Mismatched Denom In - Expect Error")] +#[test_case(Params { + caller: "entry_point".to_string(), + user_swap: Swap::SmartSwapExactAssetIn(SmartSwapExactAssetIn { + swap_venue_name: "swap_venue_name".to_string(), + routes: vec![ + Route { + offer_asset: Asset::Native(Coin::new(250_000, "un")), + operations: vec![SwapOperation { + pool: "pool".to_string(), + denom_in: "un".to_string(), + denom_out: "os".to_string(), + interface: None, + }], + }, + Route { + offer_asset: Asset::Native(Coin::new(750_000, "un")), + operations: vec![ + SwapOperation { + pool: "pool_2".to_string(), + denom_in: "un".to_string(), + denom_out: "neutron123".to_string(), + interface: None, + }, + SwapOperation { + pool: "pool_3".to_string(), + denom_in: "neutron123".to_string(), + denom_out: "oa".to_string(), + interface: None, + }, + ], + }, + ], + }), + remaining_asset: Asset::Native(Coin::new(1_000_000, "un")), + min_asset: Asset::Native(Coin::new(1_000_000, "os")), + affiliates: vec![], + expected_messages: vec![], + expected_error: Some(ContractError::Skip(SwapOperationsAssetOutDenomMismatch)), +}; "SmartSwapExactAssetIn With Mismatched Denom Out - Expect Error")] +fn test_execute_user_swap(params: Params) { + // Create mock dependencies + let mut deps = mock_dependencies_with_balances(&[( + "entry_point", + &[Coin::new(1_000_000, "os"), Coin::new(1_000_000, "un")], + )]); + + // Create mock wasm handler to handle the swap adapter contract query + let wasm_handler = |query: &WasmQuery| -> QuerierResult { + match query { + WasmQuery::Smart { contract_addr, .. } => { + if contract_addr == "swap_venue_adapter" { + SystemResult::Ok(ContractResult::Ok( + to_json_binary(&Asset::Native(Coin::new(500_000, "un"))).unwrap(), + )) + } else { + SystemResult::Ok(ContractResult::Ok( + to_json_binary(&Asset::Cw20(Cw20Coin { + address: "neutron123".to_string(), + amount: Uint128::new(500_000), + })) + .unwrap(), + )) + } + } + _ => panic!("Unsupported query: {:?}", query), + } + }; + + // Update querier with mock wasm handler + deps.querier.update_wasm(wasm_handler); + + // Create mock env with parameters that make testing easier + let mut env = mock_env(); + env.contract.address = Addr::unchecked("entry_point"); + env.block.time = Timestamp::from_nanos(100); + + // Create mock info with entry point contract address + let info = mock_info(¶ms.caller, &[]); + + // Store the swap venue adapter contract address in the swap venue map + let swap_venue_adapter = Addr::unchecked("swap_venue_adapter"); + let swap_venue_adapter_2 = Addr::unchecked("swap_venue_adapter_2"); + SWAP_VENUE_MAP + .save( + deps.as_mut().storage, + "swap_venue_name", + &swap_venue_adapter, + ) + .unwrap(); + SWAP_VENUE_MAP + .save( + deps.as_mut().storage, + "swap_venue_name_2", + &swap_venue_adapter_2, + ) + .unwrap(); + + // Call execute_swap_and_action with the given test case params + let res = skip_go_entry_point::contract::execute( + deps.as_mut(), + env, + info, + ExecuteMsg::UserSwap { + swap: params.user_swap, + remaining_asset: params.remaining_asset, + min_asset: params.min_asset, + affiliates: params.affiliates, + }, + ); + + match res { + Ok(res) => { + // Assert the test did not expect an error + assert!( + params.expected_error.is_none(), + "expected test to error with {:?}, but it succeeded", + params.expected_error + ); + + // Assert the number of messages in the response is correct + assert_eq!( + res.messages.len(), + params.expected_messages.len(), + "expected {:?} messages, but got {:?}", + params.expected_messages.len(), + res.messages.len() + ); + + // Assert the messages in the response are correct + assert_eq!(res.messages, params.expected_messages,); + } + Err(err) => { + // Assert the test expected an error + assert!( + params.expected_error.is_some(), + "expected test to succeed, but it errored with {:?}", + err + ); + + // Assert the error is correct + assert_eq!(err, params.expected_error.unwrap()); + } + } +} +*/ diff --git a/packages/secret-skip/Cargo.toml b/packages/secret-skip/Cargo.toml index d3273020..89412f0d 100644 --- a/packages/secret-skip/Cargo.toml +++ b/packages/secret-skip/Cargo.toml @@ -19,8 +19,8 @@ backtraces = ["cosmwasm-std/backtraces"] cosmos-sdk-proto = { workspace = true } cosmwasm-schema = { workspace = true } #cosmwasm-std = { workspace = true } -cw-utils = { workspace = true } -cw20 = { workspace = true } +#cw-utils = { workspace = true } +#cw20 = { workspace = true } ibc-proto = { workspace = true } #neutron-proto = { workspace = true } #osmosis-std = { workspace = true } diff --git a/packages/secret-skip/src/asset.rs b/packages/secret-skip/src/asset.rs index bf675e2f..af8525f4 100644 --- a/packages/secret-skip/src/asset.rs +++ b/packages/secret-skip/src/asset.rs @@ -1,7 +1,7 @@ +use crate::cw20::{Cw20Coin, Cw20CoinVerified}; use crate::error::SkipError; use cosmwasm_schema::cw_serde; use cosmwasm_std::{Api, Coin, OverflowError, OverflowOperation, Uint128}; -use cw20::{Cw20Coin, Cw20CoinVerified}; #[cw_serde] pub enum Asset { @@ -233,7 +233,6 @@ mod tests { Addr, ContractResult, QuerierResult, SystemResult, WasmQuery, }; use cw20::BalanceResponse; - use cw_utils::PaymentError; #[test] fn test_default_native() { @@ -508,10 +507,7 @@ mod tests { let res = asset.validate(&deps.as_mut(), &env, &info); - assert_eq!( - res, - Err(SkipError::Payment(PaymentError::MultipleDenoms {})) - ); + assert_eq!(res, Err(SkipError::MultipleDenoms {})); } #[test] @@ -565,7 +561,7 @@ mod tests { let res = asset.validate(&deps.as_mut(), &env, &info); - assert_eq!(res, Err(SkipError::Payment(PaymentError::NonPayable {}))); + assert_eq!(res, Err(SkipError::NonPayable {})); // TEST 3: Invalid asset due to invalid cw20 balance let asset = Asset::Cw20(Cw20Coin { diff --git a/packages/secret-skip/src/cw20.rs b/packages/secret-skip/src/cw20.rs new file mode 100644 index 00000000..09d461ec --- /dev/null +++ b/packages/secret-skip/src/cw20.rs @@ -0,0 +1,39 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, Uint128}; +use std::fmt; + +#[cw_serde] +pub struct Cw20Coin { + pub address: String, + pub amount: Uint128, +} + +impl Cw20Coin { + pub fn is_empty(&self) -> bool { + self.amount == Uint128::zero() + } +} + +impl fmt::Display for Cw20Coin { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "address: {}, amount: {}", self.address, self.amount) + } +} + +#[cw_serde] +pub struct Cw20CoinVerified { + pub address: Addr, + pub amount: Uint128, +} + +impl Cw20CoinVerified { + pub fn is_empty(&self) -> bool { + self.amount == Uint128::zero() + } +} + +impl fmt::Display for Cw20CoinVerified { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "address: {}, amount: {}", self.address, self.amount) + } +} diff --git a/packages/secret-skip/src/entry_point.rs b/packages/secret-skip/src/entry_point.rs index 2217cbe9..5642e786 100644 --- a/packages/secret-skip/src/entry_point.rs +++ b/packages/secret-skip/src/entry_point.rs @@ -1,12 +1,12 @@ use crate::{ asset::Asset, ibc::IbcInfo, + snip20::Snip20ReceiveMsg, swap::{Swap, SwapExactAssetOut, SwapVenue}, }; use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{Addr, Binary, HexBinary, Uint128}; -use cw20::Cw20ReceiveMsg; /////////////// /// MIGRATE /// @@ -37,7 +37,7 @@ pub struct InstantiateMsg { #[cw_serde] #[allow(clippy::large_enum_variant)] pub enum ExecuteMsg { - Receive(Cw20ReceiveMsg), + Receive(Snip20ReceiveMsg), SwapAndActionWithRecover { sent_asset: Option, user_swap: Swap, diff --git a/packages/secret-skip/src/error.rs b/packages/secret-skip/src/error.rs index 1d327f04..ab6d1403 100644 --- a/packages/secret-skip/src/error.rs +++ b/packages/secret-skip/src/error.rs @@ -13,8 +13,15 @@ pub enum SkipError { #[error("Unauthorized")] Unauthorized, + /* #[error(transparent)] - Payment(#[from] cw_utils::PaymentError), + Payment(#[from] PaymentError), + */ + #[error("Multiple Denoms")] + MultipleDenoms {}, + + #[error("NonPayable")] + NonPayable {}, #[error(transparent)] Overflow(#[from] OverflowError), diff --git a/packages/secret-skip/src/lib.rs b/packages/secret-skip/src/lib.rs index 673c1826..0a30d471 100644 --- a/packages/secret-skip/src/lib.rs +++ b/packages/secret-skip/src/lib.rs @@ -1,4 +1,5 @@ pub mod asset; +pub mod cw20; pub mod entry_point; pub mod error; pub mod hyperlane; diff --git a/packages/secret-skip/src/snip20.rs b/packages/secret-skip/src/snip20.rs index fdce01e6..36fa246c 100644 --- a/packages/secret-skip/src/snip20.rs +++ b/packages/secret-skip/src/snip20.rs @@ -16,8 +16,6 @@ pub enum ExecuteMsg { recipient: String, amount: Uint128, memo: Option, - //decoys: Option>, - // entropy: Option, padding: Option, }, Send { @@ -26,8 +24,16 @@ pub enum ExecuteMsg { amount: Uint128, msg: Option, memo: Option, - //decoys: Option>, - // entropy: Option, padding: Option, }, } + +#[cw_serde] +pub enum QueryResponse { + Balance { amount: Uint128 }, +} + +#[cw_serde] +pub struct BalanceResponse { + pub amount: Uint128, +} From 2883b8f6e5196626cdf3ab0c31312b31da8cd149 Mon Sep 17 00:00:00 2001 From: DrPresident Date: Mon, 6 Jan 2025 15:14:38 -0600 Subject: [PATCH 27/29] contracts building into wasm --- Cargo.lock | 1440 ++++++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 26 +- 2 files changed, 1398 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 40d15b99..e5dd0f61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,12 +13,80 @@ dependencies = [ "version_check", ] +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + [[package]] name = "anyhow" version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +[[package]] +name = "astroport" +version = "2.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d102b618016b3c1f1ebb5750617a73dbd294a3c941e54b12deabc931d771bc6e" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 0.15.1", + "cw-utils 0.15.1", + "cw20 0.15.1", + "itertools 0.10.5", + "uint", +] + +[[package]] +name = "astroport" +version = "3.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdebdf96895f363e121710cb84bbbfa659cea1bb1470260d4976d1a7206b3b16" +dependencies = [ + "astroport-circular-buffer", + "cosmwasm-schema", + "cosmwasm-std", + "cw-asset", + "cw-storage-plus 0.15.1", + "cw-utils 1.0.3", + "cw20 0.15.1", + "cw3", + "itertools 0.10.5", + "uint", +] + +[[package]] +name = "astroport-circular-buffer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c7369d3c4126804f861620db2221c15b5fa7b7718f12180e265b087c933fb6" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 0.15.1", + "thiserror", +] + +[[package]] +name = "astrovault" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a16c325554ef0760871dc4a0c2a3d527b5ba8559a4b7ad79a24753f1ba99805" +dependencies = [ + "bigint", + "cosmwasm-schema", + "cosmwasm-std", + "cw20 1.1.2", + "cw721 0.16.0", + "schemars", + "serde", + "sha2 0.10.8", + "thiserror", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -61,6 +129,16 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "bigint" +version = "4.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0e8c8a600052b52482eff2cf4d810e462fdff1f656ac1ecb6232132a1ed7def" +dependencies = [ + "byteorder", + "crunchy 0.1.6", +] + [[package]] name = "bincode2" version = "2.0.1" @@ -116,21 +194,61 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "num-traits", +] + [[package]] name = "const-oid" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const_format" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "cosmos-sdk-proto" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73c9d2043a9e617b0d602fbc0a0ecd621568edbf3a9774890a6d562389bd8e1c" dependencies = [ - "prost", - "prost-types", - "tendermint-proto", + "prost 0.11.9", + "prost-types 0.11.9", + "tendermint-proto 0.32.2", +] + +[[package]] +name = "cosmos-sdk-proto" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32560304ab4c365791fd307282f76637213d8083c1a98490c35159cd67852237" +dependencies = [ + "prost 0.12.6", + "prost-types 0.12.6", + "tendermint-proto 0.34.1", ] [[package]] @@ -201,6 +319,16 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cosmwasm-storage" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66de2ab9db04757bcedef2b5984fbe536903ada4a8a9766717a4a71197ef34f6" +dependencies = [ + "cosmwasm-std", + "serde", +] + [[package]] name = "cpufeatures" version = "0.2.16" @@ -210,6 +338,12 @@ dependencies = [ "libc", ] +[[package]] +name = "crunchy" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" + [[package]] name = "crunchy" version = "0.2.2" @@ -263,6 +397,97 @@ dependencies = [ "zeroize", ] +[[package]] +name = "cw-address-like" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451a4691083a88a3c0630a8a88799e9d4cd6679b7ce8ff22b8da2873ff31d380" +dependencies = [ + "cosmwasm-std", +] + +[[package]] +name = "cw-asset" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c999a12f8cd8736f6f86e9a4ede5905530cb23cfdef946b9da1c506ad1b70799" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-address-like", + "cw-storage-plus 1.2.0", + "cw20 1.1.2", + "thiserror", +] + +[[package]] +name = "cw-multi-test" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc392a5cb7e778e3f90adbf7faa43c4db7f35b6623224b08886d796718edb875" +dependencies = [ + "anyhow", + "bech32", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "derivative", + "itertools 0.12.1", + "prost 0.12.6", + "schemars", + "serde", + "sha2 0.10.8", + "thiserror", +] + +[[package]] +name = "cw-ownable" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093dfb4520c48b5848274dd88ea99e280a04bc08729603341c7fb0d758c74321" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-address-like", + "cw-ownable-derive", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "thiserror", +] + +[[package]] +name = "cw-ownable-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d3bf2e0f341bb6cc100d7d441d31cf713fbd3ce0c511f91e79f14b40a889af" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "cw-storage-plus" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6cf70ef7686e2da9ad7b067c5942cd3e88dd9453f7af42f54557f8af300fb0" +dependencies = [ + "cosmwasm-std", + "schemars", + "serde", +] + +[[package]] +name = "cw-storage-plus" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b6f91c0b94481a3e9ef1ceb183c37d00764f8751e39b45fc09f4d9b970d469" +dependencies = [ + "cosmwasm-std", + "schemars", + "serde", +] + [[package]] name = "cw-storage-plus" version = "1.2.0" @@ -274,6 +499,36 @@ dependencies = [ "serde", ] +[[package]] +name = "cw-utils" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae0b69fa7679de78825b4edeeec045066aa2b2c4b6e063d80042e565bb4da5c" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw2 0.15.1", + "schemars", + "semver", + "serde", + "thiserror", +] + +[[package]] +name = "cw-utils" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6a84c6c1c0acc3616398eba50783934bd6c964bad6974241eaee3460c8f5b26" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw2 0.16.0", + "schemars", + "semver", + "serde", + "thiserror", +] + [[package]] name = "cw-utils" version = "1.0.3" @@ -282,13 +537,39 @@ checksum = "1c4a657e5caacc3a0d00ee96ca8618745d050b8f757c709babafb81208d4239c" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw2", + "cw2 1.1.2", "schemars", "semver", "serde", "thiserror", ] +[[package]] +name = "cw2" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5abb8ecea72e09afff830252963cb60faf945ce6cef2c20a43814516082653da" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 0.15.1", + "schemars", + "serde", +] + +[[package]] +name = "cw2" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91398113b806f4d2a8d5f8d05684704a20ffd5968bf87e3473e1973710b884ad" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 0.16.0", + "schemars", + "serde", +] + [[package]] name = "cw2" version = "1.1.2" @@ -297,13 +578,26 @@ checksum = "c6c120b24fbbf5c3bedebb97f2cc85fbfa1c3287e09223428e7e597b5293c1fa" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus", + "cw-storage-plus 1.2.0", "schemars", "semver", "serde", "thiserror", ] +[[package]] +name = "cw20" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6025276fb6e603e974c21f3e4606982cdc646080e8fba3198816605505e1d9a" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils 0.15.1", + "schemars", + "serde", +] + [[package]] name = "cw20" version = "1.1.2" @@ -312,11 +606,105 @@ checksum = "526e39bb20534e25a1cd0386727f0038f4da294e5e535729ba3ef54055246abd" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-utils", + "cw-utils 1.0.3", + "schemars", + "serde", +] + +[[package]] +name = "cw20-base" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ad79e86ea3707229bf78df94e08732e8f713207b4a77b2699755596725e7d9" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw2 1.1.2", + "cw20 1.1.2", + "schemars", + "semver", + "serde", + "thiserror", +] + +[[package]] +name = "cw3" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2967fbd073d4b626dd9e7148e05a84a3bebd9794e71342e12351110ffbb12395" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils 1.0.3", + "cw20 1.1.2", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw721" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94a1ea6e6277bdd6dfc043a9b1380697fe29d6e24b072597439523658d21d791" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils 0.16.0", "schemars", "serde", ] +[[package]] +name = "cw721" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c4d286625ccadc957fe480dd3bdc54ada19e0e6b5b9325379db3130569e914" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils 1.0.3", + "schemars", + "serde", +] + +[[package]] +name = "cw721-base" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77518e27431d43214cff4cdfbd788a7508f68d9b1f32389e6fce513e7eaccbef" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 0.16.0", + "cw-utils 0.16.0", + "cw2 0.16.0", + "cw721 0.16.0", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw721-base" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da518d9f68bfda7d972cbaca2e8fcf04651d0edc3de72b04ae2bcd9289c81614" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-ownable", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "cw721 0.18.0", + "cw721-base 0.16.0", + "schemars", + "serde", + "thiserror", +] + [[package]] name = "der" version = "0.6.1" @@ -343,39 +731,278 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ - "powerfmt", + "powerfmt", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "dexter" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a27ebda8ed00010da0d68688fff006ca6a7df9c1e1313ef570ac71015288dde4" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw20 1.1.2", + "cw20-base", + "itertools 0.10.5", + "protobuf 2.28.0", + "schemars", + "serde", + "thiserror", + "uint", +] + +[[package]] +name = "dexter-lp-token" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143d553fd974bfb90862b379d9b459adfe29c5a261c9fa15aadd3aca404b47f3" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw2 1.1.2", + "cw20 1.1.2", + "cw20-base", + "dexter", + "snafu", +] + +[[package]] +name = "dexter-router" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479e68c1739b01b7d4baa05b0406f91e6b146b82f840d9101b0bf54e58a69382" +dependencies = [ + "const_format", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw2 1.1.2", + "cw20 1.1.2", + "cw20-base", + "dexter", + "schemars", + "serde", + "serde-json-wasm 0.5.2", + "thiserror", +] + +[[package]] +name = "dexter-stable-pool" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2161888b4ea26f670bee45487f9f7dbb73ea2081537d0dfec7ee75a9a2c9cc44" +dependencies = [ + "const_format", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw2 1.1.2", + "cw20 1.1.2", + "dexter", + "integer-sqrt", + "itertools 0.10.5", + "protobuf 2.28.0", + "schemars", + "serde", + "serde-json-wasm 0.5.2", + "thiserror", +] + +[[package]] +name = "dexter-vault" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a47906dd22d8baf86129c269e75029954baf950a8b1a7ab32f7d614ae6a8597" +dependencies = [ + "const_format", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw2 1.1.2", + "cw20 1.1.2", + "dexter", + "itertools 0.10.5", + "protobuf 2.28.0", + "schemars", + "serde", + "serde-json-wasm 0.5.2", + "thiserror", +] + +[[package]] +name = "dexter-weighted-pool" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef54b4987392026a78bf5204c4d5bb9a8767aefb5fa988ce3809cbf01ddea525" +dependencies = [ + "const_format", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw2 1.1.2", + "cw20 1.1.2", + "dexter", + "integer-sqrt", + "protobuf 2.28.0", + "schemars", + "serde", + "serde-json-wasm 0.5.2", + "thiserror", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + +[[package]] +name = "drop-factory" +version = "1.0.0" +source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#318f0d8d24e1f90f33987ff8da6e2e6f3c9fc62f" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-ownable", + "cw-storage-plus 1.2.0", + "cw2 1.1.2", + "drop-helpers", + "drop-macros", + "drop-puppeteer-base", + "drop-staking-base", + "neutron-sdk 0.10.0 (git+https://github.com/neutron-org/neutron-sdk?branch=feat/proposal-votes)", + "semver", + "thiserror", +] + +[[package]] +name = "drop-helpers" +version = "1.0.0" +source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#318f0d8d24e1f90f33987ff8da6e2e6f3c9fc62f" +dependencies = [ + "cosmos-sdk-proto 0.20.0", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "hex", + "neutron-sdk 0.10.0 (git+https://github.com/neutron-org/neutron-sdk?branch=feat/proposal-votes)", + "once_cell", + "prost 0.12.6", + "schemars", + "serde", + "serde-json-wasm 1.0.1", + "sha3", + "thiserror", ] [[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +name = "drop-macros" +version = "1.0.0" +source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#318f0d8d24e1f90f33987ff8da6e2e6f3c9fc62f" dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", "proc-macro2", "quote", "syn 1.0.109", ] [[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +name = "drop-proto" +version = "1.0.0" +source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#318f0d8d24e1f90f33987ff8da6e2e6f3c9fc62f" dependencies = [ - "generic-array", + "cosmwasm-std", + "prost 0.12.6", + "prost-types 0.12.6", + "tendermint-proto 0.34.1", ] [[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +name = "drop-puppeteer-base" +version = "1.0.0" +source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#318f0d8d24e1f90f33987ff8da6e2e6f3c9fc62f" dependencies = [ - "block-buffer 0.10.4", - "const-oid", - "crypto-common", - "subtle", + "cosmos-sdk-proto 0.20.0", + "cosmwasm-schema", + "cosmwasm-std", + "cw-ownable", + "cw-storage-plus 1.2.0", + "drop-helpers", + "neutron-sdk 0.10.0 (git+https://github.com/neutron-org/neutron-sdk?branch=feat/proposal-votes)", + "prost 0.12.6", + "prost-types 0.12.6", + "schemars", + "semver", + "serde", + "serde-json-wasm 1.0.1", + "thiserror", +] + +[[package]] +name = "drop-staking-base" +version = "1.0.0" +source = "git+https://github.com/hadronlabs-org/drop-contracts.git?branch=feat/audit-fixes#318f0d8d24e1f90f33987ff8da6e2e6f3c9fc62f" +dependencies = [ + "astroport 3.12.2", + "cosmos-sdk-proto 0.20.0", + "cosmwasm-schema", + "cosmwasm-std", + "cw-ownable", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw721 0.18.0", + "cw721-base 0.18.0", + "drop-helpers", + "drop-macros", + "drop-proto", + "drop-puppeteer-base", + "neutron-sdk 0.10.0 (git+https://github.com/neutron-org/neutron-sdk?branch=feat/proposal-votes)", + "optfield", + "prost 0.12.6", + "semver", + "serde", + "thiserror", ] [[package]] @@ -505,6 +1132,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" +[[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + [[package]] name = "generic-array" version = "0.14.7" @@ -558,6 +1191,12 @@ dependencies = [ "ahash", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hex" version = "0.4.3" @@ -573,6 +1212,27 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "hpl-interface" +version = "0.0.7" +source = "git+https://github.com/many-things/cw-hyperlane.git?branch=main#7573576c97fe9ee9a91c3e4557ff5a32bfbcee40" +dependencies = [ + "bech32", + "cosmwasm-schema", + "cosmwasm-std", + "cosmwasm-storage", + "cw-storage-plus 1.2.0", + "cw2 1.1.2", + "cw20 1.1.2", + "cw20-base", + "ripemd", + "schemars", + "serde", + "sha2 0.10.8", + "sha3", + "thiserror", +] + [[package]] name = "ibc-proto" version = "0.32.1" @@ -583,10 +1243,10 @@ dependencies = [ "bytes", "flex-error", "ics23", - "prost", + "prost 0.11.9", "serde", "subtle-encoding", - "tendermint-proto", + "tendermint-proto 0.32.2", ] [[package]] @@ -598,7 +1258,16 @@ dependencies = [ "anyhow", "bytes", "hex", - "prost", + "prost 0.11.9", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits", ] [[package]] @@ -610,6 +1279,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.14" @@ -642,18 +1320,129 @@ dependencies = [ "signature 2.2.0", ] +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +[[package]] +name = "lido-satellite" +version = "0.1.0" +source = "git+https://github.com/hadronlabs-org/lido-satellite?branch=main#3fe0e145d7975f949b6519284ddafcfeadabf443" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw2 1.1.2", + "neutron-sdk 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror", +] + [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "mockall" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43766c2b5203b10de348ffe19f7e54564b64f3d6018ff7648d1e2d6d3a0f0a48" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "lazy_static", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cbce79ec385a1d4f54baa90a76401eb15d9cab93685f62e7e9f942aa00ae2" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 2.0.95", +] + +[[package]] +name = "neutron-proto" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943ccd9f9749e96095237502e1c61abc867fd5cf28668097b359b0d61aa50c96" +dependencies = [ + "cosmos-sdk-proto 0.19.0", + "cosmwasm-std", + "prost 0.11.9", + "prost-types 0.11.9", + "tendermint-proto 0.32.2", +] + +[[package]] +name = "neutron-sdk" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fb513aae0c82b3185228e96664d8312e79c3aa763f6ebdc70cf4b8d513d533" +dependencies = [ + "bech32", + "cosmos-sdk-proto 0.20.0", + "cosmwasm-schema", + "cosmwasm-std", + "prost 0.12.6", + "prost-types 0.12.6", + "protobuf 3.7.1", + "schemars", + "serde", + "serde-json-wasm 1.0.1", + "serde_json", + "speedate", + "tendermint-proto 0.34.1", + "thiserror", +] + +[[package]] +name = "neutron-sdk" +version = "0.10.0" +source = "git+https://github.com/neutron-org/neutron-sdk?branch=feat/proposal-votes#2dfca1734dfc4cfba478b3067417648733d66cd9" +dependencies = [ + "bech32", + "cosmos-sdk-proto 0.20.0", + "cosmwasm-schema", + "cosmwasm-std", + "prost 0.12.6", + "prost-types 0.12.6", + "protobuf 3.7.1", + "schemars", + "serde", + "serde-json-wasm 1.0.1", + "serde_json", + "speedate", + "tendermint-proto 0.34.1", + "thiserror", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -692,6 +1481,45 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "optfield" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa59f025cde9c698fcb4fcb3533db4621795374065bee908215263488f2d2a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.95", +] + +[[package]] +name = "osmosis-std" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87725a7480b98887167edf878daa52201a13322ad88e34355a7f2ddc663e047e" +dependencies = [ + "chrono", + "cosmwasm-std", + "osmosis-std-derive", + "prost 0.11.9", + "prost-types 0.11.9", + "schemars", + "serde", + "serde-cw-value", +] + +[[package]] +name = "osmosis-std-derive" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4d482a16be198ee04e0f94e10dd9b8d02332dcf33bc5ea4b255e7e25eedc5df" +dependencies = [ + "itertools 0.10.5", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "paste" version = "1.0.15" @@ -725,44 +1553,161 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] -name = "proc-macro2" -version = "1.0.92" +name = "predicates" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" +dependencies = [ + "anstyle", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" + +[[package]] +name = "predicates-tree" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes", + "prost-derive 0.11.9", +] + +[[package]] +name = "prost" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +dependencies = [ + "bytes", + "prost-derive 0.12.6", +] + +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools 0.10.5", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "prost-derive" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +dependencies = [ + "anyhow", + "itertools 0.10.5", + "proc-macro2", + "quote", + "syn 2.0.95", +] + +[[package]] +name = "prost-types" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +dependencies = [ + "prost 0.11.9", +] + +[[package]] +name = "prost-types" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" +dependencies = [ + "prost 0.12.6", +] + +[[package]] +name = "protobuf" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" +dependencies = [ + "bytes", +] + +[[package]] +name = "protobuf" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "a3a7c64d9bf75b1b8d981124c14c179074e8caa7dfe7b6a12e6222ddcd0c8f72" dependencies = [ - "unicode-ident", + "bytes", + "once_cell", + "protobuf-support", + "thiserror", ] [[package]] -name = "prost" -version = "0.11.9" +name = "protobuf-support" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +checksum = "b088fd20b938a875ea00843b6faf48579462630015c3788d397ad6a786663252" dependencies = [ - "bytes", - "prost-derive", + "thiserror", ] [[package]] -name = "prost-derive" -version = "0.11.9" +name = "pryzm-std" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +checksum = "97d295897f6a1a3a05ef5b50794590bee0bc29cb2afb5d2117ef07aafd995d5a" dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn 1.0.109", + "chrono", + "cosmwasm-std", + "prost 0.12.6", + "prost-types 0.12.6", + "pryzm-std-derive", + "schemars", + "serde", + "serde-cw-value", ] [[package]] -name = "prost-types" -version = "0.11.9" +name = "pryzm-std-derive" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +checksum = "27015d082aa4edc995247d0f2fa81b53d6b4e116ed06e3532c162cd34d3bc010" dependencies = [ - "prost", + "itertools 0.10.5", + "proc-macro2", + "prost-types 0.11.9", + "quote", + "syn 1.0.109", ] [[package]] @@ -810,6 +1755,21 @@ dependencies = [ "subtle", ] +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + [[package]] name = "ryu" version = "1.0.18" @@ -914,7 +1874,7 @@ dependencies = [ name = "secret-skip" version = "0.3.0" dependencies = [ - "cosmos-sdk-proto", + "cosmos-sdk-proto 0.19.0", "cosmwasm-schema", "ibc-proto", "secret-cosmwasm-std", @@ -1119,6 +2079,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + [[package]] name = "signature" version = "1.6.4" @@ -1139,6 +2109,98 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "skip" +version = "0.3.0" +dependencies = [ + "astroport 2.9.5", + "cosmos-sdk-proto 0.19.0", + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils 1.0.3", + "cw20 1.1.2", + "ibc-proto", + "neutron-proto", + "osmosis-std", + "thiserror", + "white-whale-std", +] + +[[package]] +name = "skip-go-entry-point" +version = "0.3.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "cw20 1.1.2", + "skip", + "test-case", + "thiserror", +] + +[[package]] +name = "skip-go-hyperlane-adapter" +version = "0.3.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "hpl-interface", + "prost 0.11.9", + "serde-cw-value", + "serde-json-wasm 1.0.1", + "skip", + "test-case", + "thiserror", +] + +[[package]] +name = "skip-go-ibc-adapter-ibc-hooks" +version = "0.3.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw2 1.1.2", + "ibc-proto", + "prost 0.11.9", + "serde-cw-value", + "serde-json-wasm 1.0.1", + "skip", + "test-case", + "thiserror", +] + +[[package]] +name = "skip-go-ibc-adapter-neutron-transfer" +version = "0.3.0" +dependencies = [ + "cosmos-sdk-proto 0.19.0", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw2 1.1.2", + "neutron-proto", + "neutron-sdk 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "prost 0.11.9", + "skip", + "test-case", + "thiserror", +] + +[[package]] +name = "skip-go-placeholder" +version = "0.3.0" +dependencies = [ + "cosmwasm-std", + "cw2 1.1.2", +] + [[package]] name = "skip-go-secret-entry-point" version = "0.3.0" @@ -1161,10 +2223,10 @@ name = "skip-go-secret-ibc-adapter-ibc-hooks" version = "0.3.0" dependencies = [ "cosmwasm-schema", - "cw2", - "cw20", + "cw2 1.1.2", + "cw20 1.1.2", "ibc-proto", - "prost", + "prost 0.11.9", "schemars", "secret-cosmwasm-std", "secret-skip", @@ -1177,6 +2239,155 @@ dependencies = [ "thiserror", ] +[[package]] +name = "skip-go-swap-adapter-astroport" +version = "0.3.0" +dependencies = [ + "astroport 2.9.5", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "cw20 1.1.2", + "skip", + "test-case", + "thiserror", +] + +[[package]] +name = "skip-go-swap-adapter-astrovault" +version = "0.3.0" +dependencies = [ + "astrovault", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "cw20 1.1.2", + "skip", + "test-case", + "thiserror", +] + +[[package]] +name = "skip-go-swap-adapter-dexter" +version = "0.3.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-multi-test", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "cw20 1.1.2", + "dexter", + "dexter-lp-token", + "dexter-router", + "dexter-stable-pool", + "dexter-vault", + "dexter-weighted-pool", + "skip", + "test-case", + "thiserror", +] + +[[package]] +name = "skip-go-swap-adapter-drop" +version = "0.3.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "drop-factory", + "drop-staking-base", + "skip", + "test-case", + "thiserror", +] + +[[package]] +name = "skip-go-swap-adapter-duality" +version = "0.3.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "cw20 1.1.2", + "neutron-sdk 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "skip", + "test-case", + "thiserror", +] + +[[package]] +name = "skip-go-swap-adapter-hallswap" +version = "0.3.0" +dependencies = [ + "astroport 2.9.5", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "cw20 1.1.2", + "skip", + "test-case", + "thiserror", +] + +[[package]] +name = "skip-go-swap-adapter-lido-satellite" +version = "0.3.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "lido-satellite", + "skip", + "test-case", + "thiserror", +] + +[[package]] +name = "skip-go-swap-adapter-osmosis-poolmanager" +version = "0.3.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "osmosis-std", + "skip", + "test-case", + "thiserror", +] + +[[package]] +name = "skip-go-swap-adapter-pryzm" +version = "0.3.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "mockall", + "pryzm-std", + "serde", + "skip", + "test-case", + "thiserror", +] + [[package]] name = "skip-go-swap-adapter-shade-protocol" version = "0.3.0" @@ -1192,6 +2403,54 @@ dependencies = [ "thiserror", ] +[[package]] +name = "skip-go-swap-adapter-white-whale" +version = "0.3.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "cw20 1.1.2", + "skip", + "test-case", + "thiserror", + "white-whale-std", +] + +[[package]] +name = "snafu" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" +dependencies = [ + "doc-comment", + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "speedate" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "242f76c50fd18cbf098607090ade73a08d39cfd84ea835f3796a2c855223b19b" +dependencies = [ + "strum", + "strum_macros", +] + [[package]] name = "spki" version = "0.6.0" @@ -1218,6 +2477,28 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.95", +] + [[package]] name = "subtle" version = "2.6.1" @@ -1265,14 +2546,38 @@ dependencies = [ "flex-error", "num-derive", "num-traits", - "prost", - "prost-types", + "prost 0.11.9", + "prost-types 0.11.9", + "serde", + "serde_bytes", + "subtle-encoding", + "time", +] + +[[package]] +name = "tendermint-proto" +version = "0.34.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b797dd3d2beaaee91d2f065e7bdf239dc8d80bba4a183a288bc1279dd5a69a1e" +dependencies = [ + "bytes", + "flex-error", + "num-derive", + "num-traits", + "prost 0.12.6", + "prost-types 0.12.6", "serde", "serde_bytes", "subtle-encoding", "time", ] +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + [[package]] name = "test-case" version = "3.3.1" @@ -1368,7 +2673,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" dependencies = [ "byteorder", - "crunchy", + "crunchy 0.2.2", "hex", "static_assertions", ] @@ -1379,6 +2684,12 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "version_check" version = "0.9.5" @@ -1391,6 +2702,25 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "white-whale-std" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e8a9b86670af680cc07207c24cd2b380e2b185cd489907576ec21f11d0694bd" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw2 1.1.2", + "cw20 1.1.2", + "osmosis-std-derive", + "prost 0.11.9", + "prost-types 0.11.9", + "protobuf 3.7.1", + "schemars", + "serde", + "uint", +] + [[package]] name = "zeroize" version = "1.8.1" diff --git a/Cargo.toml b/Cargo.toml index 1c6c1ec3..77aa31ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,13 @@ [workspace] resolver = "2" members = [ - #"contracts/entry-point", + "contracts/entry-point", "contracts/secret-entry-point", - #"contracts/adapters/hyperlane", - "contracts/adapters/ibc/secret-ibc-hooks", - "contracts/adapters/swap/shade-protocol", - #"contracts/placeholder", - "packages/secret-skip", + "contracts/adapters/hyperlane", + "contracts/adapters/ibc/*", + "contracts/adapters/swap/*", + "contracts/placeholder", + "packages/*", ] [workspace.package] @@ -31,7 +31,7 @@ dexter-weighted-pool = "1.1.1" dexter-lp-token = "1.0.0" dexter-router = "1.1.0" cosmwasm-schema = "1.5.4" -#cosmwasm-std = { version = "1.5.4", features = ["stargate"] } +cosmwasm-std = { version = "1.5.4", features = ["stargate"] } cosmos-sdk-proto = { version = "0.19", default-features = false } cw2 = "1.1" cw20 = "1.1" @@ -44,19 +44,19 @@ lido-satellite = { git = "https://github.com/hadronlabs-org/lido-satellite drop-factory = { git = "https://github.com/hadronlabs-org/drop-contracts.git", branch = "feat/audit-fixes", features = ["library"] } drop-staking-base = { git = "https://github.com/hadronlabs-org/drop-contracts.git", branch = "feat/audit-fixes", features = ["library"] } mockall = "0.12.1" -# neutron-proto = { version = "0.1.1", default-features = false, features = ["cosmwasm"] } -# neutron-sdk = "0.10.0" -# osmosis-std = "0.15.3" +neutron-proto = { version = "0.1.1", default-features = false, features = ["cosmwasm"] } +neutron-sdk = "0.10.0" +osmosis-std = "0.15.3" prost = "0.11" -# pryzm-std = "0.1.7" +pryzm-std = "0.1.7" serde = { version = "1.0.194", default-features = false, features = ["derive"] } serde-cw-value = "0.7.0" serde-json-wasm = "1.0.1" -#skip = { version = "0.3.0", path = "./packages/skip" } +skip = { version = "0.3.0", path = "./packages/skip" } secret-skip = { version = "0.3.0", path = "./packages/secret-skip" } test-case = "3.3.1" thiserror = "1" -#white-whale-std = "1.1.1" +white-whale-std = "1.1.1" [profile.release] codegen-units = 1 From 48c8235c7d98a1a8ff6080645bbbc8d1cafc3ec6 Mon Sep 17 00:00:00 2001 From: Jack Swenson Date: Thu, 16 Jan 2025 10:56:23 -0600 Subject: [PATCH 28/29] Update contracts/adapters/ibc/secret-ibc-hooks/README.md Co-authored-by: Not Jeremy Liu <31809888+NotJeremyLiu@users.noreply.github.com> --- contracts/adapters/ibc/secret-ibc-hooks/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/adapters/ibc/secret-ibc-hooks/README.md b/contracts/adapters/ibc/secret-ibc-hooks/README.md index d7d51667..f2b526a8 100644 --- a/contracts/adapters/ibc/secret-ibc-hooks/README.md +++ b/contracts/adapters/ibc/secret-ibc-hooks/README.md @@ -1,4 +1,4 @@ -# Osmosis IBC Transfer Adapter Contract +# Secret IBC Transfer Adapter Contract The Osmosis IBC Transfer adapter contract is responsible for: 1. Dispatching the IBC transfer. From 336a0319a53fb1643b0387539e0ccebed77d0906 Mon Sep 17 00:00:00 2001 From: Jack Swenson Date: Thu, 16 Jan 2025 10:59:23 -0600 Subject: [PATCH 29/29] Update contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs Co-authored-by: Not Jeremy Liu <31809888+NotJeremyLiu@users.noreply.github.com> --- contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs b/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs index 63c26894..3a079475 100644 --- a/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs +++ b/contracts/adapters/ibc/secret-ibc-hooks/src/contract.rs @@ -184,7 +184,7 @@ fn execute_ics20_ibc_transfer( return Err(ContractError::Unauthorized); } - // Error if ibc_info.fee is not None since Osmosis does not support fees + // Error if ibc_info.fee is not None since Secret does not support fees if ibc_info.fee.is_some() { return Err(ContractError::IbcFeesNotSupported); }