diff --git a/framework/Cargo.lock b/framework/Cargo.lock index cad96790e..a97d35ba6 100644 --- a/framework/Cargo.lock +++ b/framework/Cargo.lock @@ -39,13 +39,17 @@ dependencies = [ "cw-asset", "cw-controllers", "cw-orch 0.27.0", + "cw-orch-interchain", "cw-storage-plus", "cw2", "cw20", + "json-patch 3.0.1", + "prost", "rstest", "schemars", "semver", "serde", + "serde_json", "thiserror", "workspace-hack", ] @@ -171,9 +175,8 @@ dependencies = [ [[package]] name = "abstract-cw-multi-test" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12838503965771af5d9299a208548180e8acbbe0507abc33c9f827f0cc61b7db" +version = "2.1.4" +source = "git+https://github.com/abstractsdk/cw-multi-test-fork?branch=feature/add-ics20-transfer-data#3a0f97fd83e3d7f78c4d7a02672a92fb669bb0a0" dependencies = [ "anyhow", "bech32 0.11.0", @@ -182,7 +185,6 @@ dependencies = [ "cw-storage-plus", "cw-utils", "cw20-ics20", - "derivative", "hex", "itertools 0.13.0", "log", @@ -192,6 +194,7 @@ dependencies = [ "serde_json", "sha2 0.10.8", "thiserror", + "tiny-keccak", ] [[package]] @@ -550,6 +553,7 @@ dependencies = [ "abstract-sdk", "abstract-std", "abstract-testing", + "anybuf", "cosmos-sdk-proto 0.24.0", "cosmwasm-schema", "cosmwasm-std", @@ -631,6 +635,7 @@ name = "abstract-std" version = "0.24.1" dependencies = [ "abstract-testing", + "anybuf", "anyhow", "bech32 0.11.0", "cosmwasm-schema", @@ -744,9 +749,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" [[package]] name = "alloy" @@ -794,7 +799,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -810,7 +815,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", "syn-solidity", "tiny-keccak", ] @@ -826,7 +831,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", "syn-solidity", ] @@ -858,9 +863,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.17" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -873,9 +878,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" @@ -913,9 +918,9 @@ checksum = "cb5f1dee23caf80904249463cc4493b6789c2250f88c8f8d9160de5c6099bfe7" [[package]] name = "anyhow" -version = "1.0.91" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" [[package]] name = "arc-swap" @@ -1082,7 +1087,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1104,7 +1109,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1115,7 +1120,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1373,9 +1378,9 @@ dependencies = [ [[package]] name = "bitcoin" -version = "0.32.3" +version = "0.32.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0032b0e8ead7074cda7fc4f034409607e3f03a6f71d66ade8a307f79b4d99e73" +checksum = "788902099d47c8682efe6a7afb01c8d58b9794ba66c06affd81c3d6b560743eb" dependencies = [ "base58ck", "bech32 0.11.0", @@ -1405,9 +1410,9 @@ checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" [[package]] name = "bitcoin-io" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "340e09e8399c7bd8912f495af6aa58bea0c9214773417ffaa8f6460f93aaee56" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" [[package]] name = "bitcoin-units" @@ -1550,9 +1555,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.31" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8" dependencies = [ "shlex", ] @@ -1609,7 +1614,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1835,7 +1840,7 @@ checksum = "1b5658b1dc64e10b56ae7a449f678f96932a96f6cfad1769d608d1d1d656480a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1859,7 +1864,7 @@ checksum = "c8ef1b5835a65fcca3ab8b9a02b4f4dacc78e233a5c2f20b270efb9db0666d12" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1893,9 +1898,9 @@ checksum = "8174551717bb3d1e75935e38d33f5f8ee8f680dd8dd42c90851e6c644faad14e" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" dependencies = [ "libc", ] @@ -1995,7 +2000,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2102,11 +2107,11 @@ dependencies = [ "cosmwasm-std", "cw-orch-contract-derive", "cw-orch-core", - "cw-orch-daemon", + "cw-orch-daemon 0.28.1 (registry+https://github.com/rust-lang/crates.io-index)", "cw-orch-fns-derive", "cw-orch-mock", - "cw-orch-networks", - "cw-orch-traits 0.25.0", + "cw-orch-networks 0.24.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cw-orch-traits 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)", "cw-utils", "hex", "insta", @@ -2126,14 +2131,13 @@ checksum = "bad52865e313bb7ed3f3938f7ad9d566e430fb6143a63476c22bed505ea78cd7" dependencies = [ "convert_case 0.6.0", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] name = "cw-orch-core" version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09c75b59e43cf3ec05bc9fc86748cdd4998bfb53a81e3fa99dda14cf4ded3d4d" +source = "git+https://github.com/abstractsdk/cw-orchestrator?branch=interchain/add-ics-hooks-ack#4ec9cbfd434b2265869e732d876ad34c3ae01d7d" dependencies = [ "abstract-cw-multi-test", "anyhow", @@ -2151,9 +2155,55 @@ dependencies = [ [[package]] name = "cw-orch-daemon" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1b727ba2031725ee51ecafaa101770827689cd61f3b1f4465ad56237606a500" +checksum = "eb318421ff15d3760f21cb851de41b3b6a82cc42bedddebe2abb677507fbc752" +dependencies = [ + "anyhow", + "async-recursion", + "base16", + "base64 0.22.1", + "bech32 0.11.0", + "bip39", + "bitcoin 0.32.4", + "chrono", + "cosmrs 0.19.0", + "cosmwasm-std", + "cw-orch-core", + "cw-orch-networks 0.24.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cw-orch-traits 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dirs", + "ed25519-dalek", + "eyre", + "file-lock", + "flate2", + "hex", + "http 1.1.0", + "lazy_static", + "libc-print", + "log", + "once_cell", + "prost", + "prost-types", + "rand_core", + "reqwest 0.12.9", + "ring", + "ripemd", + "schemars", + "serde", + "serde_json", + "sha2 0.10.8", + "thiserror", + "tokio", + "toml", + "tonic", + "uid", +] + +[[package]] +name = "cw-orch-daemon" +version = "0.28.1" +source = "git+https://github.com/abstractsdk/cw-orchestrator?branch=interchain/add-ics-hooks-ack#4ec9cbfd434b2265869e732d876ad34c3ae01d7d" dependencies = [ "anyhow", "async-recursion", @@ -2161,13 +2211,13 @@ dependencies = [ "base64 0.22.1", "bech32 0.11.0", "bip39", - "bitcoin 0.32.3", + "bitcoin 0.32.4", "chrono", "cosmrs 0.19.0", "cosmwasm-std", "cw-orch-core", - "cw-orch-networks", - "cw-orch-traits 0.25.0", + "cw-orch-networks 0.24.6 (git+https://github.com/abstractsdk/cw-orchestrator?branch=interchain/add-ics-hooks-ack)", + "cw-orch-traits 0.25.0 (git+https://github.com/abstractsdk/cw-orchestrator?branch=interchain/add-ics-hooks-ack)", "dirs", "ed25519-dalek", "eyre", @@ -2182,7 +2232,7 @@ dependencies = [ "prost", "prost-types", "rand_core", - "reqwest 0.12.8", + "reqwest 0.12.9", "ring", "ripemd", "schemars", @@ -2205,7 +2255,7 @@ dependencies = [ "convert_case 0.6.0", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2216,14 +2266,13 @@ checksum = "85806182382aef051a318b9abb41af559e6d7733fbba3c04c42ed23cc03d8e1a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] name = "cw-orch-interchain" version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f43184d2458fe1677a642270f34381defbe01205e897e113b60c3f2998bac7" +source = "git+https://github.com/abstractsdk/cw-orchestrator?branch=interchain/add-ics-hooks-ack#4ec9cbfd434b2265869e732d876ad34c3ae01d7d" dependencies = [ "cosmwasm-std", "cw-orch-interchain-core", @@ -2239,14 +2288,13 @@ dependencies = [ [[package]] name = "cw-orch-interchain-core" version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bb92310efe5711f3e5166b42805d7dd1a1af0d0733c497321ccaefb8f12983" +source = "git+https://github.com/abstractsdk/cw-orchestrator?branch=interchain/add-ics-hooks-ack#4ec9cbfd434b2265869e732d876ad34c3ae01d7d" dependencies = [ "base64 0.22.1", "cosmwasm-schema", "cosmwasm-std", "cw-orch-core", - "cw-orch-daemon", + "cw-orch-daemon 0.28.1 (git+https://github.com/abstractsdk/cw-orchestrator?branch=interchain/add-ics-hooks-ack)", "cw-orch-mock", "futures", "ibc-relayer-types", @@ -2262,8 +2310,7 @@ dependencies = [ [[package]] name = "cw-orch-interchain-daemon" version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "485460d5b250b12d53b47e7ba075b7b828d04e41bb18e30796c5514ff8b020d0" +source = "git+https://github.com/abstractsdk/cw-orchestrator?branch=interchain/add-ics-hooks-ack#4ec9cbfd434b2265869e732d876ad34c3ae01d7d" dependencies = [ "async-recursion", "base64 0.22.1", @@ -2271,7 +2318,7 @@ dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw-orch-core", - "cw-orch-daemon", + "cw-orch-daemon 0.28.1 (git+https://github.com/abstractsdk/cw-orchestrator?branch=interchain/add-ics-hooks-ack)", "cw-orch-interchain-core", "cw-orch-starship", "derive_builder 0.20.2", @@ -2291,8 +2338,7 @@ dependencies = [ [[package]] name = "cw-orch-interchain-mock" version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc064aa62087460a5e21a18f187c339304f58c3658a5e8c9be7fec387777da0a" +source = "git+https://github.com/abstractsdk/cw-orchestrator?branch=interchain/add-ics-hooks-ack#4ec9cbfd434b2265869e732d876ad34c3ae01d7d" dependencies = [ "anyhow", "cosmrs 0.19.0", @@ -2310,9 +2356,8 @@ dependencies = [ [[package]] name = "cw-orch-mock" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bed827dedba3e64ba7372dd9edfcf4e174d0ad3fccc33b648590bd0f0c8a71e3" +version = "0.24.3" +source = "git+https://github.com/abstractsdk/cw-orchestrator?branch=interchain/add-ics-hooks-ack#4ec9cbfd434b2265869e732d876ad34c3ae01d7d" dependencies = [ "abstract-cw-multi-test", "cosmwasm-std", @@ -2333,22 +2378,30 @@ dependencies = [ "serde", ] +[[package]] +name = "cw-orch-networks" +version = "0.24.6" +source = "git+https://github.com/abstractsdk/cw-orchestrator?branch=interchain/add-ics-hooks-ack#4ec9cbfd434b2265869e732d876ad34c3ae01d7d" +dependencies = [ + "cw-orch-core", + "serde", +] + [[package]] name = "cw-orch-starship" version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0de1288ab45095534d6e30f1c5645a1bc0dd13538fa4c9190b8616cb4dfa3eb" +source = "git+https://github.com/abstractsdk/cw-orchestrator?branch=interchain/add-ics-hooks-ack#4ec9cbfd434b2265869e732d876ad34c3ae01d7d" dependencies = [ "cosmwasm-std", "cw-orch-core", - "cw-orch-daemon", + "cw-orch-daemon 0.28.1 (git+https://github.com/abstractsdk/cw-orchestrator?branch=interchain/add-ics-hooks-ack)", "env_logger", "ibc-chain-registry", "ibc-relayer-types", "k8s-openapi", "kube", "log", - "reqwest 0.12.8", + "reqwest 0.12.9", "serde", "serde_json", "thiserror", @@ -2378,6 +2431,15 @@ dependencies = [ "prost-types", ] +[[package]] +name = "cw-orch-traits" +version = "0.25.0" +source = "git+https://github.com/abstractsdk/cw-orchestrator?branch=interchain/add-ics-hooks-ack#4ec9cbfd434b2265869e732d876ad34c3ae01d7d" +dependencies = [ + "cw-orch-core", + "prost-types", +] + [[package]] name = "cw-ownable" version = "2.1.0" @@ -2724,7 +2786,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2746,7 +2808,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2844,7 +2906,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2864,7 +2926,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core 0.20.2", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2877,7 +2939,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2897,7 +2959,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", "unicode-xid", ] @@ -3005,6 +3067,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "doc-comment" version = "0.3.3" @@ -3241,9 +3314,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" [[package]] name = "ff" @@ -3410,7 +3483,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -3488,7 +3561,7 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "regex-syntax 0.8.5", ] @@ -3574,9 +3647,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" [[package]] name = "hashlink" @@ -3863,7 +3936,7 @@ dependencies = [ "hyper 1.5.0", "hyper-util", "log", - "rustls 0.23.15", + "rustls 0.23.16", "rustls-native-certs 0.8.0", "rustls-pki-types", "tokio", @@ -3873,9 +3946,9 @@ dependencies = [ [[package]] name = "hyper-timeout" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ "hyper 1.5.0", "hyper-util", @@ -3902,9 +3975,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", @@ -4117,6 +4190,124 @@ dependencies = [ "sha3", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -4125,12 +4316,23 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -4165,7 +4367,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.1", ] [[package]] @@ -4180,9 +4382,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.40.0" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6593a41c7a73841868772495db7dc1e8ecab43bb5c0b6da2059246c4b506ab60" +checksum = "7e9ffc4d4892617c50a928c52b2961cb5174b6fc6ebf252b2fac9d21955c48b8" dependencies = [ "console", "lazy_static", @@ -4251,7 +4453,19 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc" dependencies = [ - "jsonptr", + "jsonptr 0.4.7", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "json-patch" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" +dependencies = [ + "jsonptr 0.6.3", "serde", "serde_json", "thiserror", @@ -4283,6 +4497,16 @@ dependencies = [ "serde_json", ] +[[package]] +name = "jsonptr" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "k256" version = "0.13.4" @@ -4355,7 +4579,7 @@ dependencies = [ "kube-core", "pem", "rand", - "rustls 0.23.15", + "rustls 0.23.16", "rustls-pemfile 2.2.0", "secrecy", "serde", @@ -4379,7 +4603,7 @@ dependencies = [ "chrono", "form_urlencoded", "http 1.1.0", - "json-patch", + "json-patch 2.0.0", "k8s-openapi", "schemars", "serde", @@ -4397,7 +4621,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -4414,8 +4638,8 @@ dependencies = [ "derivative", "futures", "hashbrown 0.14.5", - "json-patch", - "jsonptr", + "json-patch 2.0.0", + "jsonptr 0.4.7", "k8s-openapi", "kube-client", "parking_lot", @@ -4439,9 +4663,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.161" +version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" [[package]] name = "libc-print" @@ -4454,9 +4678,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libredox" @@ -4480,6 +4704,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + [[package]] name = "lock_api" version = "0.4.12" @@ -4777,7 +5007,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -5016,7 +5246,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -5060,7 +5290,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -5089,7 +5319,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -5264,7 +5494,7 @@ dependencies = [ "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -5402,7 +5632,7 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "regex-syntax 0.8.5", ] @@ -5417,9 +5647,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -5481,9 +5711,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.8" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ "base64 0.22.1", "bytes", @@ -5648,7 +5878,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.85", + "syn 2.0.87", "walkdir", ] @@ -5686,9 +5916,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" dependencies = [ "bitflags 2.6.0", "errno", @@ -5725,9 +5955,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.15" +version = "0.23.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993" +checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" dependencies = [ "log", "once_cell", @@ -5882,7 +6112,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -5981,9 +6211,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" dependencies = [ "core-foundation-sys", "libc", @@ -6000,9 +6230,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.213" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] @@ -6056,13 +6286,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.213" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -6073,7 +6303,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -6106,7 +6336,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -6300,6 +6530,12 @@ dependencies = [ "der", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -6337,7 +6573,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -6374,9 +6610,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.85" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -6392,7 +6628,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -6410,6 +6646,17 @@ dependencies = [ "futures-core", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -6460,9 +6707,9 @@ checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" [[package]] name = "tempfile" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", @@ -6721,22 +6968,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.65" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.65" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -6817,6 +7064,16 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -6834,9 +7091,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.41.0" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" dependencies = [ "backtrace", "bytes", @@ -6858,7 +7115,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -6898,7 +7155,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.15", + "rustls 0.23.16", "rustls-pki-types", "tokio", ] @@ -7092,7 +7349,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -7243,12 +7500,6 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" -[[package]] -name = "unicode-bidi" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" - [[package]] name = "unicode-ident" version = "1.0.13" @@ -7305,9 +7556,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" dependencies = [ "form_urlencoded", "idna", @@ -7326,12 +7577,24 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + [[package]] name = "utf8-width" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -7412,7 +7675,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", "wasm-bindgen-shared", ] @@ -7446,7 +7709,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -7779,10 +8042,10 @@ dependencies = [ "rand_chacha", "rand_core", "regex", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "regex-syntax 0.8.5", "reqwest 0.11.27", - "reqwest 0.12.8", + "reqwest 0.12.9", "ripemd", "sec1", "semver", @@ -7794,7 +8057,7 @@ dependencies = [ "spki", "subtle", "syn 1.0.109", - "syn 2.0.85", + "syn 2.0.87", "tendermint 0.38.1", "tendermint-proto 0.38.1", "tendermint-proto 0.39.1", @@ -7811,6 +8074,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "yaml-rust2" version = "0.8.1" @@ -7822,6 +8097,30 @@ dependencies = [ "hashlink", ] +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -7840,7 +8139,28 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", +] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "synstructure", ] [[package]] @@ -7860,5 +8180,27 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", ] diff --git a/framework/Cargo.toml b/framework/Cargo.toml index cae768626..8d6f6a3e5 100644 --- a/framework/Cargo.toml +++ b/framework/Cargo.toml @@ -50,6 +50,7 @@ schemars = "0.8" serde = { version = "1.0", default-features = false, features = ["derive"] } thiserror = { version = "1.0.50" } protobuf = { version = "2", features = ["with-bytes"] } +anybuf = { version = "0.5.0" } clap = { version = "4.0.32", features = ["derive"] } semver = "1.0" @@ -131,6 +132,19 @@ incremental = false # abstract-polytone-note = { git = "https://github.com/AbstractSDK/polytone.git", branch = "bump/cw2" } # abstract-cw-orch-polytone = { git = "https://github.com/AbstractSDK/polytone.git", branch = "bump/cw2" } +abstract-cw-multi-test = { git = "https://github.com/abstractsdk/cw-multi-test-fork", branch = "feature/add-ics20-transfer-data" } +# abstract-cw-multi-test = { path = "../../cw-multi-test" } +cw-orch-interchain-core = { git = "https://github.com/abstractsdk/cw-orchestrator", branch = "interchain/add-ics-hooks-ack" } +cw-orch-core = { git = "https://github.com/abstractsdk/cw-orchestrator", branch = "interchain/add-ics-hooks-ack" } +cw-orch-mock = { git = "https://github.com/abstractsdk/cw-orchestrator", branch = "interchain/add-ics-hooks-ack" } +cw-orch-interchain = { git = "https://github.com/abstractsdk/cw-orchestrator", branch = "interchain/add-ics-hooks-ack" } +cw-orch-interchain-daemon = { git = "https://github.com/abstractsdk/cw-orchestrator", branch = "interchain/add-ics-hooks-ack" } +cw-orch-interchain-mock = { git = "https://github.com/abstractsdk/cw-orchestrator", branch = "interchain/add-ics-hooks-ack" } +# cw-orch-interchain-core = { path = "../../cw-orchestrator/packages/interchain/interchain-core" } +# cw-orch-core = { path = "../../cw-orchestrator/packages/cw-orch-core" } +# cw-orch-interchain-daemon = { path = "../../cw-orchestrator/packages/interchain/interchain-daemon" } +# cw-orch-interchain-mock = { path = "../../cw-orchestrator/packages/interchain/interchain-mock" } + [workspace.metadata.cargo-udeps.ignore] # ensures CI doens't flag workspace-hack as unused dep normal = ["workspace-hack"] diff --git a/framework/contracts/account/Cargo.toml b/framework/contracts/account/Cargo.toml index bd049b9cb..b745c2a60 100644 --- a/framework/contracts/account/Cargo.toml +++ b/framework/contracts/account/Cargo.toml @@ -48,6 +48,9 @@ abstract-macros = { workspace = true } abstract-ica = { workspace = true } abstract-xion = { workspace = true, optional = true } +prost = "0.13.3" +serde_json = "1.0.132" +json-patch = "3.0.1" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] workspace-hack = { version = "0.1", path = "../../workspace-hack" } @@ -56,8 +59,9 @@ workspace-hack = { version = "0.1", path = "../../workspace-hack" } cw20 = { workspace = true } # cw721-base = { version = "0.18.0", features = ["library"] } # cw721 = { version = "0.18.0" } -abstract-interface = { workspace = true } +abstract-interface = { workspace = true, features = ["interchain"] } cw-orch = { workspace = true, features = ["snapshot-testing"] } +cw-orch-interchain = { workspace = true } anyhow = { workspace = true } ans-host = { workspace = true } registry = { workspace = true } diff --git a/framework/contracts/account/src/contract.rs b/framework/contracts/account/src/contract.rs index 93bdeac14..eee2b9ce3 100644 --- a/framework/contracts/account/src/contract.rs +++ b/framework/contracts/account/src/contract.rs @@ -24,6 +24,7 @@ use abstract_std::{ }, registry::state::LOCAL_ACCOUNT_SEQUENCE, }; + use cosmwasm_std::{ ensure_eq, wasm_execute, Addr, Binary, Coins, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, @@ -36,19 +37,24 @@ use crate::{ execution::{ add_auth_method, admin_execute, admin_execute_on_module, execute_msgs, execute_msgs_with_data, execute_on_module, ica_action, remove_auth_method, + send_funds_with_actions, }, + ics20::ics20_hook_callback, modules::{ _install_modules, install_modules, migration::{assert_modules_dependency_requirements, upgrade_modules}, uninstall_module, MIGRATE_CONTEXT, }, - msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, + msg::{ExecuteMsg, InstantiateMsg, QueryMsg, SudoMsg}, queries::{ handle_account_info_query, handle_config_query, handle_module_address_query, handle_module_info_query, handle_module_versions_query, handle_sub_accounts_query, handle_top_level_owner_query, }, - reply::{admin_action_reply, forward_response_reply, register_dependencies}, + reply::{ + admin_action_reply, forward_response_reply, register_dependencies, + register_sub_sequent_messages, + }, sub_account::{ create_sub_account, handle_sub_account_action, maybe_update_sub_account_governance, remove_account_from_contracts, @@ -66,6 +72,7 @@ pub const FORWARD_RESPONSE_REPLY_ID: u64 = 1; pub const ADMIN_ACTION_REPLY_ID: u64 = 2; pub const REGISTER_MODULES_DEPENDENCIES_REPLY_ID: u64 = 3; pub const ASSERT_MODULE_DEPENDENCIES_REQUIREMENTS_REPLY_ID: u64 = 4; +pub const IBC_TOKEN_FLOW: u64 = 5; #[cfg_attr(feature = "export", cosmwasm_std::entry_point)] pub fn instantiate( @@ -268,24 +275,24 @@ pub fn execute(mut deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) match msg { // ## Execution ## - ExecuteMsg::Execute { msgs } => execute_msgs(deps, &info.sender, msgs), + ExecuteMsg::Execute { msgs } => execute_msgs(deps, env, &info.sender, msgs), ExecuteMsg::AdminExecute { addr, msg } => { let addr = deps.api.addr_validate(&addr)?; admin_execute(deps, info, addr, msg) } ExecuteMsg::ExecuteWithData { msg } => { - execute_msgs_with_data(deps, &info.sender, msg) + execute_msgs_with_data(deps, env, &info.sender, msg) } ExecuteMsg::ExecuteOnModule { module_id, exec_msg, funds, - } => execute_on_module(deps, info, module_id, exec_msg, funds), + } => execute_on_module(deps, env, info, module_id, exec_msg, funds), ExecuteMsg::AdminExecuteOnModule { module_id, msg } => { admin_execute_on_module(deps, info, module_id, msg) } ExecuteMsg::IcaAction { action_query_msg } => { - ica_action(deps, info, action_query_msg) + ica_action(deps, env, info, action_query_msg) } // ## Configuration ## @@ -357,6 +364,11 @@ pub fn execute(mut deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) } #[allow(unused)] ExecuteMsg::RemoveAuthMethod { id } => remove_auth_method(deps, env, id), + ExecuteMsg::SendFundsWithActions { + amount, + host_chain, + actions, + } => send_funds_with_actions(deps, info, env, host_chain, amount, actions), } } }?; @@ -373,6 +385,7 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> AccountResult { ASSERT_MODULE_DEPENDENCIES_REQUIREMENTS_REPLY_ID => { assert_modules_dependency_requirements(deps) } + IBC_TOKEN_FLOW => register_sub_sequent_messages(deps, msg), _ => Err(AccountError::UnexpectedReply {}), } @@ -416,17 +429,14 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { } } -#[cfg(feature = "xion")] #[cfg_attr(feature = "export", cosmwasm_std::entry_point)] -pub fn sudo( - deps: DepsMut, - env: Env, - msg: abstract_xion::contract::AccountSudoMsg, -) -> abstract_xion::error::ContractResult { - if let abstract_xion::contract::AccountSudoMsg::BeforeTx { .. } = &msg { - AUTH_ADMIN.save(deps.storage, &true)?; - }; - abstract_xion::contract::sudo(deps, env, msg) +pub fn sudo(deps: DepsMut, env: Env, msg: SudoMsg) -> AccountResult { + match msg { + SudoMsg::IBCLifecycleComplete(msg) => ics20_hook_callback(deps, env, msg), + + #[cfg(feature = "xion")] + SudoMsg::Xion(msg) => abstract_xion::contract::sudo(deps, env, msg).map_err(Into::into), + } } /// Verifies that *sender* is the owner of *nft_id* of contract *nft_addr* diff --git a/framework/contracts/account/src/error.rs b/framework/contracts/account/src/error.rs index 95e3808ac..b4e973b06 100644 --- a/framework/contracts/account/src/error.rs +++ b/framework/contracts/account/src/error.rs @@ -1,6 +1,9 @@ use abstract_sdk::std::objects::module::ModuleInfo; use abstract_std::{ - objects::{registry::RegistryError, validation::ValidationError}, + objects::{ + ans_host::AnsHostError, registry::RegistryError, validation::ValidationError, + TruncatedChainId, + }, AbstractError, }; use cosmwasm_std::{Instantiate2AddressError, StdError}; @@ -26,6 +29,15 @@ pub enum AccountError { #[error(transparent)] RegistryError(#[from] RegistryError), + #[error(transparent)] + AnsHostError(#[from] AnsHostError), + + #[error(transparent)] + ProstDecodeError(#[from] prost::DecodeError), + + #[error("{0}")] + SerdeJsonError(String), + #[error("Your account is currently suspended")] AccountSuspended {}, @@ -118,4 +130,13 @@ pub enum AccountError { #[cfg(feature = "xion")] #[error(transparent)] AbstractXion(#[from] abstract_xion::error::ContractError), + + #[error("Chain {0} not registered in the IBC Client")] + ChainNotRegistered(TruncatedChainId), +} + +impl From for AccountError { + fn from(value: serde_json::Error) -> Self { + Self::SerdeJsonError(value.to_string()) + } } diff --git a/framework/contracts/account/src/execution.rs b/framework/contracts/account/src/execution.rs index 57b99cd4d..e82aa9b6a 100644 --- a/framework/contracts/account/src/execution.rs +++ b/framework/contracts/account/src/execution.rs @@ -1,22 +1,36 @@ -use abstract_sdk::std::account::state::WHITELISTED_MODULES; +use abstract_sdk::{ + feature_objects::AnsHost, std::account::state::WHITELISTED_MODULES, HookMemoBuilder, Resolve, +}; use abstract_std::{ - account::state::{ACCOUNT_MODULES, CALLING_TO_AS_ADMIN}, - objects::ownership, - ICA_CLIENT, + account::state::{ACCOUNT_ID, ACCOUNT_MODULES, CALLING_TO_AS_ADMIN}, + ibc::PACKET_LIFETIME, + ibc_client::state::IBC_INFRA, + ibc_host, + objects::{ownership, ChannelEntry, TruncatedChainId}, + IBC_CLIENT, ICA_CLIENT, ICS20, }; use cosmwasm_std::{ - Addr, Binary, Coin, CosmosMsg, DepsMut, Empty, Env, MessageInfo, StdError, SubMsg, WasmMsg, - WasmQuery, + to_json_binary, Addr, Binary, Coin, CosmosMsg, DepsMut, Empty, Env, IbcMsg, IbcTimeout, + MessageInfo, Response, StdError, SubMsg, WasmMsg, WasmQuery, }; use crate::{ - contract::{AccountResponse, AccountResult, ADMIN_ACTION_REPLY_ID, FORWARD_RESPONSE_REPLY_ID}, + contract::{ + AccountResponse, AccountResult, ADMIN_ACTION_REPLY_ID, FORWARD_RESPONSE_REPLY_ID, + IBC_TOKEN_FLOW, + }, error::AccountError, modules::load_module_addr, + msg::ExecuteMsg, + reply::TokenFlowPayload, }; /// Check that sender either whitelisted or governance -pub(crate) fn assert_whitelisted_or_owner(deps: &mut DepsMut, sender: &Addr) -> AccountResult<()> { +pub(crate) fn assert_whitelisted_or_owner( + deps: &mut DepsMut, + env: &Env, + sender: &Addr, +) -> AccountResult<()> { #[cfg(feature = "xion")] { if let Some(is_admin) = crate::state::AUTH_ADMIN.may_load(deps.storage)? { @@ -27,6 +41,10 @@ pub(crate) fn assert_whitelisted_or_owner(deps: &mut DepsMut, sender: &Addr) -> } } } + // Account can execute on itself + if env.contract.address == sender { + return Ok(()); + } let whitelisted_modules = WHITELISTED_MODULES.load(deps.storage)?; if whitelisted_modules.0.contains(sender) || ownership::assert_nested_owner(deps.storage, &deps.querier, sender).is_ok() @@ -41,10 +59,11 @@ pub(crate) fn assert_whitelisted_or_owner(deps: &mut DepsMut, sender: &Addr) -> /// Permission: Module pub fn execute_msgs( mut deps: DepsMut, + env: Env, msg_sender: &Addr, msgs: Vec>, ) -> AccountResult { - assert_whitelisted_or_owner(&mut deps, msg_sender)?; + assert_whitelisted_or_owner(&mut deps, &env, msg_sender)?; Ok(AccountResponse::action("execute_module_action").add_messages(msgs)) } @@ -53,10 +72,11 @@ pub fn execute_msgs( /// Permission: Module pub fn execute_msgs_with_data( mut deps: DepsMut, + env: Env, msg_sender: &Addr, msg: CosmosMsg, ) -> AccountResult { - assert_whitelisted_or_owner(&mut deps, msg_sender)?; + assert_whitelisted_or_owner(&mut deps, &env, msg_sender)?; let submsg = SubMsg::reply_on_success(msg, FORWARD_RESPONSE_REPLY_ID); @@ -67,6 +87,7 @@ pub fn execute_msgs_with_data( /// This is a simple wrapper around [`ExecuteMsg::Execute`](abstract_std::account::ExecuteMsg::Execute). pub fn execute_on_module( deps: DepsMut, + env: Env, info: MessageInfo, module_id: String, exec_msg: Binary, @@ -75,6 +96,7 @@ pub fn execute_on_module( let module_addr = load_module_addr(deps.storage, &module_id)?; execute_msgs( deps, + env, &info.sender, vec![CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: module_addr.into(), @@ -149,8 +171,13 @@ pub fn remove_auth_method(_deps: DepsMut, _env: Env, _id: u8) -> AccountResult { /// It then fires a smart-query on that address of type [`QueryMsg::IcaAction`](abstract_ica::msg::QueryMsg). /// /// The resulting `Vec` are then executed on the account contract. -pub fn ica_action(mut deps: DepsMut, msg_info: MessageInfo, action_query: Binary) -> AccountResult { - assert_whitelisted_or_owner(&mut deps, &msg_info.sender)?; +pub fn ica_action( + mut deps: DepsMut, + env: Env, + msg_info: MessageInfo, + action_query: Binary, +) -> AccountResult { + assert_whitelisted_or_owner(&mut deps, &env, &msg_info.sender)?; let ica_client_address = ACCOUNT_MODULES .may_load(deps.storage, ICA_CLIENT)? @@ -171,6 +198,60 @@ pub fn ica_action(mut deps: DepsMut, msg_info: MessageInfo, action_query: Binary Ok(AccountResponse::action("ica_action").add_messages(res.msgs)) } +pub fn send_funds_with_actions( + mut deps: DepsMut, + msg_info: MessageInfo, + env: Env, + host_chain: TruncatedChainId, + funds: Coin, + actions: Vec, +) -> AccountResult { + assert_whitelisted_or_owner(&mut deps, &env, &msg_info.sender)?; + // We send a funds message to the host saying we want to deposit on a remote account + host_chain.verify()?; + + let ans = AnsHost::new(deps.api, &env)?; + + let ics20_channel_entry = ChannelEntry { + connected_chain: host_chain.clone(), + protocol: ICS20.to_string(), + }; + let ics20_channel_id = ics20_channel_entry.resolve(&deps.querier, &ans)?; + + let ibc_client_module = load_module_addr(deps.storage, IBC_CLIENT)?; + let remote_host = IBC_INFRA + .query(&deps.querier, ibc_client_module, &host_chain)? + .ok_or(AccountError::ChainNotRegistered(host_chain))?; + + // Hook for sending the funds correctly to the sender + let action_memo = HookMemoBuilder::new( + remote_host.remote_abstract_host.clone(), + &ibc_host::ExecuteMsg::Fund { + src_account: ACCOUNT_ID.load(deps.storage)?, + src_chain: TruncatedChainId::from_chain_id(&env.block.chain_id), + }, + ) + .callback(&env) + .build()?; + + let transfer_msg = IbcMsg::Transfer { + channel_id: ics20_channel_id.clone(), + to_address: remote_host.remote_abstract_host, + amount: funds, + timeout: IbcTimeout::with_timestamp(env.block.time.plus_seconds(PACKET_LIFETIME)), + memo: Some(action_memo), + }; + + Ok(Response::new().add_submessage( + SubMsg::reply_on_success(transfer_msg, IBC_TOKEN_FLOW).with_payload(to_json_binary( + &TokenFlowPayload { + channel_id: ics20_channel_id, + msgs: actions, + }, + )?), + )) +} + #[cfg(test)] mod test { use crate::contract::execute; diff --git a/framework/contracts/account/src/ics20.rs b/framework/contracts/account/src/ics20.rs new file mode 100644 index 000000000..15d1f9464 --- /dev/null +++ b/framework/contracts/account/src/ics20.rs @@ -0,0 +1,55 @@ +use abstract_std::ibc::{IBCLifecycleComplete, ICS20PacketIdentifier}; +use cosmwasm_std::{wasm_execute, DepsMut, Env, Response}; + +use crate::contract::AccountResult; +use crate::msg::ICS20_CALLBACKS; + +pub fn ics20_hook_callback(deps: DepsMut, env: Env, msg: IBCLifecycleComplete) -> AccountResult { + match msg { + IBCLifecycleComplete::IBCAck { + channel, + sequence, + ack: _, + success, + } => { + let packet_identifier = ICS20PacketIdentifier { + channel_id: channel, + sequence, + }; + + // The acknowledgement has this structure with ibc hooks, we need to coed accordingly + // https://github.com/cosmos/ibc-apps/blob/8cb681e31589bc90b47e0ab58173a579825fd56d/modules/ibc-hooks/wasm_hook.go#L119C1-L119C86 + let (outcome, stored_msgs) = if success { + ( + "result", + ICS20_CALLBACKS + .load(deps.storage, packet_identifier.clone())? + .into_iter() + .map(|msg| wasm_execute(&env.contract.address, &msg, vec![])) + .collect::, _>>()?, + ) + } else { + ("failure", vec![]) + }; + + ICS20_CALLBACKS.remove(deps.storage, packet_identifier.clone()); + + Ok(Response::new() + .add_attribute("action", "ibc_source_callback") + .add_attribute("outcome", outcome) + .add_messages(stored_msgs)) + } + IBCLifecycleComplete::IBCTimeout { channel, sequence } => { + ICS20_CALLBACKS.remove( + deps.storage, + ICS20PacketIdentifier { + channel_id: channel, + sequence, + }, + ); + Ok(Response::new() + .add_attribute("action", "ibc_source_callback") + .add_attribute("outcome", "timeout")) + } + } +} diff --git a/framework/contracts/account/src/lib.rs b/framework/contracts/account/src/lib.rs index dd8ed6dc3..936a782e4 100644 --- a/framework/contracts/account/src/lib.rs +++ b/framework/contracts/account/src/lib.rs @@ -4,6 +4,7 @@ pub mod config; pub mod contract; pub mod error; pub mod execution; +pub mod ics20; pub mod migrate; pub mod modules; pub mod queries; @@ -25,6 +26,8 @@ pub use abstract_xion; // re-export based on the feature pub mod msg { pub use abstract_std::account::{MigrateMsg, QueryMsg}; + use abstract_std::{ibc::ICS20PacketIdentifier, objects::storage_namespaces}; + use cw_storage_plus::Map; #[cfg(feature = "xion")] pub type Authenticator = crate::abstract_xion::AddAuthenticator; @@ -33,6 +36,19 @@ pub mod msg { pub type ExecuteMsg = abstract_std::account::ExecuteMsg; pub type InstantiateMsg = abstract_std::account::InstantiateMsg; + + pub const ICS20_CALLBACKS: Map> = + Map::new(storage_namespaces::account::ICS20_CALLBACKS); + + #[cosmwasm_schema::cw_serde] + pub enum SudoMsg { + /// For IBC hooks acknoledgments + #[serde(rename = "ibc_lifecycle_complete")] + IBCLifecycleComplete(abstract_std::ibc::IBCLifecycleComplete), + #[cfg(feature = "xion")] + #[serde(untagged)] + Xion(abstract_xion::AccountSudoMsg), + } } #[cfg(test)] diff --git a/framework/contracts/account/src/reply.rs b/framework/contracts/account/src/reply.rs index 89af7cfd3..32577b3c9 100644 --- a/framework/contracts/account/src/reply.rs +++ b/framework/contracts/account/src/reply.rs @@ -1,15 +1,18 @@ use crate::{ contract::{AccountResponse, AccountResult}, modules::INSTALL_MODULES_CONTEXT, + msg::{ExecuteMsg, ICS20_CALLBACKS}, }; use abstract_std::{ account::state::CALLING_TO_AS_ADMIN, + ibc::ICS20PacketIdentifier, objects::{ module::{assert_module_data_validity, Module}, module_reference::ModuleReference, }, }; -use cosmwasm_std::{DepsMut, Reply, Response, StdError}; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{from_json, DepsMut, Reply, Response, StdError}; /// Add the message's data to the response pub(crate) fn forward_response_reply(result: Reply) -> AccountResult { @@ -76,3 +79,41 @@ pub(crate) fn register_dependencies(deps: DepsMut) -> AccountResult { Ok(Response::new()) } + +use prost::Message; +#[derive(Clone, PartialEq, Message)] +struct MsgTransferResponse { + #[prost(uint64, tag = "1")] + pub sequence: u64, +} + +pub fn register_sub_sequent_messages(deps: DepsMut, reply: Reply) -> AccountResult { + let res = reply.result.into_result().map_err(StdError::generic_err)?; + println!("{:x?}", res.data); + // TODO, implement for msg_responses (to have both cases covered) + let transfer_response = MsgTransferResponse::decode( + res.data + .expect("Data is set after sending a packet") + .as_slice(), + )?; + + let payload: TokenFlowPayload = from_json(reply.payload)?; + + // We register the callback for later use + ICS20_CALLBACKS.save( + deps.storage, + ICS20PacketIdentifier { + channel_id: payload.channel_id, + sequence: transfer_response.sequence, + }, + &payload.msgs, + )?; + + Ok(Response::new()) +} + +#[cw_serde] +pub struct TokenFlowPayload { + pub channel_id: String, + pub msgs: Vec, +} diff --git a/framework/contracts/account/tests/token_transfers.rs b/framework/contracts/account/tests/token_transfers.rs new file mode 100644 index 000000000..ac710a692 --- /dev/null +++ b/framework/contracts/account/tests/token_transfers.rs @@ -0,0 +1,284 @@ +use abstract_interface::connection::connect_one_way_to; +use abstract_interface::*; +use abstract_std::{ + account, + objects::{gov_type::GovernanceDetails, TruncatedChainId, UncheckedChannelEntry}, + ACCOUNT, ICS20, +}; +use cosmwasm_std::{coin, coins, BankMsg}; +use cw_orch::prelude::*; +use cw_orch_interchain::prelude::*; + +type AResult = anyhow::Result<()>; // alias for Result<(), anyhow::Error> + +pub const SOURCE_CHAIN_ID: &str = "source-1"; +pub const DEST_CHAIN_ID: &str = "dest-1"; +pub const NEW_DUMMY_NAME: &str = "Name that no-one could have thought of"; + +#[test] +fn transfer_with_account_rename_message() -> AResult { + let interchain = + MockBech32InterchainEnv::new(vec![(SOURCE_CHAIN_ID, "source"), (DEST_CHAIN_ID, "dest")]); + + let src = interchain.get_chain(SOURCE_CHAIN_ID)?; + let dst = interchain.get_chain(DEST_CHAIN_ID)?; + + let src_abstr = Abstract::deploy_on_mock(src.clone())?; + let dest_abstr = Abstract::deploy_on_mock(dst.clone())?; + + connect_one_way_to( + &src_abstr.call_as(&Abstract::mock_admin(&src)), + &dest_abstr.call_as(&Abstract::mock_admin(&dst)), + &interchain, + )?; + + let ics20_channel = interchain + .create_channel( + SOURCE_CHAIN_ID, + DEST_CHAIN_ID, + &PortId::transfer(), + &PortId::transfer(), + "ics20-1", + Some(cosmwasm_std::IbcOrder::Unordered), + )? + .interchain_channel; + + // Update the channels on the src side (for sending tokens) + src_abstr + .ans_host + .call_as(&Abstract::mock_admin(&src)) + .update_channels( + vec![( + UncheckedChannelEntry { + connected_chain: TruncatedChainId::from_chain_id(DEST_CHAIN_ID).to_string(), + protocol: ICS20.to_string(), + }, + ics20_channel + .get_chain(SOURCE_CHAIN_ID)? + .channel + .unwrap() + .to_string(), + )], + vec![], + )?; + + // Update the channels on the src side (for sending back tokens) + dest_abstr + .ans_host + .call_as(&Abstract::mock_admin(&dst)) + .update_channels( + vec![( + UncheckedChannelEntry { + connected_chain: TruncatedChainId::from_chain_id(SOURCE_CHAIN_ID).to_string(), + protocol: ICS20.to_string(), + }, + ics20_channel + .get_chain(DEST_CHAIN_ID)? + .channel + .unwrap() + .to_string(), + )], + vec![], + )?; + + let account = AccountI::new(ACCOUNT, src.clone()); + + account.instantiate( + &account::InstantiateMsg { + name: Some(String::from("first_account")), + description: Some(String::from("account_description")), + link: Some(String::from("https://account_link_of_at_least_11_char")), + namespace: None, + install_modules: vec![], + account_id: None, + owner: GovernanceDetails::Monarchy { + monarch: src.sender_addr().to_string(), + }, + authenticator: None, + }, + None, + &[], + )?; + account.set_ibc_status(true)?; + + pub const INITIAL_AMOUNT: u128 = 100_000; + pub const LOCAL_TRANSFER_AMOUNT: u128 = 50_000; + let funds_to_transfer = coin(INITIAL_AMOUNT, "usource"); + let funds_after_ics20 = coin(INITIAL_AMOUNT, "usource1"); + + src.add_balance( + &account.address()?, + vec![funds_to_transfer.clone(), funds_after_ics20.clone()], + )?; + + // Here we send some funds on the remote account and then change the name of the account on callback + let tx_response = account.send_funds_with_actions( + vec![account::ExecuteMsg::Execute { + msgs: vec![BankMsg::Send { + to_address: src.sender_addr().to_string(), + amount: coins(LOCAL_TRANSFER_AMOUNT, "usource1"), + } + .into()], + }], + funds_to_transfer.clone(), + TruncatedChainId::from_chain_id(&dst.chain_id()), + )?; + let src_account_balance = src.balance(&account.address()?, None)?; + assert_eq!(src_account_balance, coins(INITIAL_AMOUNT, "usource1")); + let result = interchain.await_and_check_packets(SOURCE_CHAIN_ID, tx_response)?; + println!("{:?}", result); + let src_account_balance = src.balance(&account.address()?, None)?; + assert_eq!( + src_account_balance, + coins(LOCAL_TRANSFER_AMOUNT, "usource1") + ); + + let dst_host_balance = dst.balance(&dest_abstr.ibc.host.address()?, None)?; + assert!(dst_host_balance.is_empty()); + + // We fetch the remote account balance + let mut remote_account_id = account.id()?; + remote_account_id.push_chain(TruncatedChainId::from_chain_id(SOURCE_CHAIN_ID)); + + let remote_account = AccountI::load_from(&dest_abstr, remote_account_id)?; + + let dest_account_balance = dst.balance(&remote_account.address()?, None)?; + assert_eq!(dest_account_balance.len(), 1); + + // Finally, we assert the sender received their tokens + let src_account_balance = src.balance(&src.sender_addr(), None)?; + assert_eq!( + src_account_balance, + coins(LOCAL_TRANSFER_AMOUNT, "usource1") + ); + + Ok(()) +} + +#[test] +fn transfer_with_account_rename_message_timeout() -> AResult { + let interchain = + MockBech32InterchainEnv::new(vec![(SOURCE_CHAIN_ID, "source"), (DEST_CHAIN_ID, "dest")]); + + let src = interchain.get_chain(SOURCE_CHAIN_ID)?; + let dst = interchain.get_chain(DEST_CHAIN_ID)?; + + let src_abstr = Abstract::deploy_on_mock(src.clone())?; + let dest_abstr = Abstract::deploy_on_mock(dst.clone())?; + + connect_one_way_to( + &src_abstr.call_as(&Abstract::mock_admin(&src)), + &dest_abstr.call_as(&Abstract::mock_admin(&dst)), + &interchain, + )?; + + let ics20_channel = interchain + .create_channel( + SOURCE_CHAIN_ID, + DEST_CHAIN_ID, + &PortId::transfer(), + &PortId::transfer(), + "ics20-1", + Some(cosmwasm_std::IbcOrder::Unordered), + )? + .interchain_channel; + + // Update the channels on the src side (for sending tokens) + src_abstr + .ans_host + .call_as(&Abstract::mock_admin(&src)) + .update_channels( + vec![( + UncheckedChannelEntry { + connected_chain: TruncatedChainId::from_chain_id(DEST_CHAIN_ID).to_string(), + protocol: ICS20.to_string(), + }, + ics20_channel + .get_chain(SOURCE_CHAIN_ID)? + .channel + .unwrap() + .to_string(), + )], + vec![], + )?; + + // Update the channels on the src side (for sending back tokens) + dest_abstr + .ans_host + .call_as(&Abstract::mock_admin(&dst)) + .update_channels( + vec![( + UncheckedChannelEntry { + connected_chain: TruncatedChainId::from_chain_id(SOURCE_CHAIN_ID).to_string(), + protocol: ICS20.to_string(), + }, + ics20_channel + .get_chain(DEST_CHAIN_ID)? + .channel + .unwrap() + .to_string(), + )], + vec![], + )?; + + let account = AccountI::new(ACCOUNT, src.clone()); + + account.instantiate( + &account::InstantiateMsg { + name: Some(String::from("first_account")), + description: Some(String::from("account_description")), + link: Some(String::from("https://account_link_of_at_least_11_char")), + namespace: None, + install_modules: vec![], + account_id: None, + owner: GovernanceDetails::Monarchy { + monarch: src.sender_addr().to_string(), + }, + authenticator: None, + }, + None, + &[], + )?; + account.set_ibc_status(true)?; + + let funds_to_transfer = coin(100_000, "usource"); + + src.add_balance(&account.address()?, vec![funds_to_transfer.clone()])?; + + // Here we send some funds on the remote account and then change the name of the account on callback + let tx_response = account.send_funds_with_actions( + vec![account::ExecuteMsg::UpdateInfo { + name: Some(NEW_DUMMY_NAME.to_string()), + description: None, + link: None, + }], + funds_to_transfer.clone(), + TruncatedChainId::from_chain_id(&dst.chain_id()), + )?; + + // Trigger timeout + dst.wait_seconds(60 * 60 * 24)?; + + interchain + .await_packets(SOURCE_CHAIN_ID, tx_response)? + .assert() + .unwrap_err(); + + let src_account_balance = src.balance(&account.address()?, None)?; + assert_eq!(src_account_balance.len(), 1); + + let dst_host_balance = dst.balance(&dest_abstr.ibc.host.address()?, None)?; + assert!(dst_host_balance.is_empty()); + + // We fetch the remote account balance + let mut remote_account_id = account.id()?; + remote_account_id.push_chain(TruncatedChainId::from_chain_id(SOURCE_CHAIN_ID)); + + // Remote account not created + AccountI::load_from(&dest_abstr, remote_account_id).unwrap_err(); + + // Finally, we assert the name change that happened after the callback + assert_ne!(account.info()?.info.name, Some(NEW_DUMMY_NAME.to_string())); + + Ok(()) +} diff --git a/framework/contracts/native/ibc-client/Cargo.toml b/framework/contracts/native/ibc-client/Cargo.toml index 6985c8410..cd592c73d 100644 --- a/framework/contracts/native/ibc-client/Cargo.toml +++ b/framework/contracts/native/ibc-client/Cargo.toml @@ -31,7 +31,7 @@ abstract-sdk = { workspace = true } abstract-macros = { workspace = true } semver = { workspace = true } cw-paginate = "2.0.0" -anybuf = "0.5.0" +anybuf = { workspace = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] workspace-hack = { version = "0.1", path = "../../../workspace-hack" } diff --git a/framework/contracts/native/ibc-host/src/account_commands.rs b/framework/contracts/native/ibc-host/src/account_commands.rs index 7d14245c5..dbaf39139 100644 --- a/framework/contracts/native/ibc-host/src/account_commands.rs +++ b/framework/contracts/native/ibc-host/src/account_commands.rs @@ -5,12 +5,13 @@ use abstract_sdk::{ }; use abstract_std::{ account::{self, ModuleInstallConfig}, + ibc::PACKET_LIFETIME, objects::{module::ModuleInfo, module_reference::ModuleReference, AccountId, TruncatedChainId}, registry::Account, ACCOUNT, }; use cosmwasm_std::{ - instantiate2_address, to_json_binary, wasm_execute, CosmosMsg, Deps, DepsMut, Empty, Env, + instantiate2_address, to_json_binary, wasm_execute, Coin, CosmosMsg, Deps, DepsMut, Empty, Env, IbcMsg, Response, SubMsg, WasmMsg, }; @@ -20,9 +21,6 @@ use crate::{ HostError, }; -// one hour -const PACKET_LIFETIME: u64 = 60 * 60; - /// Creates and registers account for remote Account #[allow(clippy::too_many_arguments)] pub fn receive_register( @@ -35,6 +33,7 @@ pub fn receive_register( namespace: Option, install_modules: Vec, with_reply: bool, + funds: Vec, ) -> HostResult { let registry = RegistryContract::new(deps.api, &env)?; // verify that the origin last chain is the chain related to this channel, and that it is not `Local` @@ -78,7 +77,7 @@ pub fn receive_register( code_id, label: account_id.to_string(), msg: to_json_binary(&create_account_msg)?, - funds: vec![], + funds, salt, }; diff --git a/framework/contracts/native/ibc-host/src/endpoints/execute.rs b/framework/contracts/native/ibc-host/src/endpoints/execute.rs index 297c20e91..3baec4758 100644 --- a/framework/contracts/native/ibc-host/src/endpoints/execute.rs +++ b/framework/contracts/native/ibc-host/src/endpoints/execute.rs @@ -3,10 +3,11 @@ use abstract_std::{ ibc_host::state::{CHAIN_PROXIES, REVERSE_CHAIN_PROXIES}, objects::TruncatedChainId, }; -use cosmwasm_std::{DepsMut, Env, MessageInfo}; +use cosmwasm_std::{BankMsg, DepsMut, Env, MessageInfo, Response}; use super::packet::{handle_host_action, handle_module_execute}; use crate::{ + account_commands::{self, receive_register}, contract::{HostResponse, HostResult}, HostError, }; @@ -42,6 +43,47 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> H handle_module_execute(deps, env, src_chain, source_module, target_module, msg) } + ExecuteMsg::Fund { + src_account, + src_chain, + } => { + // Push the client chain to the account trace + let account_id = { + let mut account_id = src_account.clone(); + account_id.push_chain(src_chain.clone()); + account_id + }; + if let Ok(account) = account_commands::get_account(deps.as_ref(), &env, &account_id) { + // Send funds to the account + + Ok(Response::new().add_message(BankMsg::Send { + to_address: account.addr().to_string(), + amount: info.funds, + })) + } else { + // If no account is created already, we create one and send the funds during instantiation directly + // The account metadata are not set with this call + // One will have to change them at a later point if they decide to + let name = format!( + "Remote Abstract Account for {}/{}", + src_chain.as_str(), + account_id + ); + + receive_register( + deps, + env, + account_id, + Some(name), + None, + None, + None, + vec![], + false, + info.funds, + ) + } + } } } diff --git a/framework/contracts/native/ibc-host/src/endpoints/packet.rs b/framework/contracts/native/ibc-host/src/endpoints/packet.rs index 55775cbc7..4268836f1 100644 --- a/framework/contracts/native/ibc-host/src/endpoints/packet.rs +++ b/framework/contracts/native/ibc-host/src/endpoints/packet.rs @@ -59,6 +59,7 @@ pub fn handle_host_action( namespace, install_modules, false, + vec![], ), action => { @@ -109,6 +110,7 @@ pub fn handle_host_action( None, vec![], true, + vec![], ) } } diff --git a/framework/contracts/native/ica-client/src/chain_types/evm.rs b/framework/contracts/native/ica-client/src/chain_types/evm.rs index c6b31d193..3889d9db2 100644 --- a/framework/contracts/native/ica-client/src/chain_types/evm.rs +++ b/framework/contracts/native/ica-client/src/chain_types/evm.rs @@ -3,13 +3,16 @@ use abstract_sdk::{ feature_objects::{AnsHost, RegistryContract}, Resolve, }; -use abstract_std::objects::{module::ModuleInfo, ChannelEntry, ContractEntry, TruncatedChainId}; +use abstract_std::{ + ibc::PACKET_LIFETIME, + objects::{module::ModuleInfo, ChannelEntry, ContractEntry, TruncatedChainId}, +}; use cosmwasm_std::{ wasm_execute, Addr, Binary, Coin, CosmosMsg, Deps, Env, HexBinary, QuerierWrapper, WasmMsg, }; use evm_note::msg::{CallbackRequest, EvmMsg}; -use crate::{contract::IcaClientResult, error::IcaClientError, queries::PACKET_LIFETIME}; +use crate::{contract::IcaClientResult, error::IcaClientError}; pub fn execute( querier: &QuerierWrapper, diff --git a/framework/contracts/native/ica-client/src/queries.rs b/framework/contracts/native/ica-client/src/queries.rs index affd8bffa..31ec104d0 100644 --- a/framework/contracts/native/ica-client/src/queries.rs +++ b/framework/contracts/native/ica-client/src/queries.rs @@ -5,9 +5,6 @@ use cosmwasm_std::{ensure_eq, CosmosMsg, Deps, Env}; use crate::{chain_types::evm, contract::IcaClientResult, error::IcaClientError}; -/// Timeout in seconds -pub const PACKET_LIFETIME: u64 = 60 * 60; - pub fn config(deps: Deps, env: &Env) -> IcaClientResult { Ok(ConfigResponse { ans_host: AnsHost::new(deps.api, env)?.address, @@ -176,7 +173,7 @@ mod tests { use std::str::FromStr; use abstract_ica::msg::QueryMsg; - use abstract_std::objects::TruncatedChainId; + use abstract_std::{ibc::PACKET_LIFETIME, objects::TruncatedChainId}; use abstract_testing::mock_env_validated; use cosmwasm_std::{coins, wasm_execute}; diff --git a/framework/packages/abstract-app/src/state.rs b/framework/packages/abstract-app/src/state.rs index bd68c3458..7061df2f5 100644 --- a/framework/packages/abstract-app/src/state.rs +++ b/framework/packages/abstract-app/src/state.rs @@ -1,17 +1,18 @@ use abstract_sdk::{ base::{ModuleIbcHandlerFn, SudoHandlerFn}, - namespaces::{ADMIN_NAMESPACE, BASE_STATE}, + namespaces::{ADMIN_NAMESPACE, BASE_STATE, ICS20_CALLBACKS}, AbstractSdkError, }; use abstract_std::{ app::AppState, + ibc::{Callback, IBCLifecycleComplete, ICS20PacketIdentifier}, objects::{ dependency::StaticDependency, module::ModuleInfo, ownership::nested_admin::NestedAdmin, }, AbstractError, AbstractResult, }; use cosmwasm_std::{Empty, StdResult, Storage}; -use cw_storage_plus::Item; +use cw_storage_plus::{Item, Map}; use crate::{ AbstractContract, AppError, ExecuteHandlerFn, IbcCallbackHandlerFn, InstantiateHandlerFn, @@ -48,6 +49,7 @@ pub struct AppContract< // Custom state for every App pub admin: NestedAdmin, pub(crate) base_state: Item, + pub(crate) ics20_callbacks: Map, // Scaffolding contract that handles type safety and provides helper methods pub(crate) contract: AbstractContract, @@ -71,6 +73,7 @@ impl< Self { base_state: Item::new(BASE_STATE), admin: NestedAdmin::new(ADMIN_NAMESPACE), + ics20_callbacks: Map::new(ICS20_CALLBACKS), contract: AbstractContract::new(name, version, metadata), } } @@ -91,6 +94,32 @@ impl< self.base_state.load(store) } + /// Loads callback and clean ups the state after it + pub fn load_ics20_callback( + &self, + store: &mut dyn Storage, + ibc_cycle: &IBCLifecycleComplete, + ) -> StdResult { + let key = match ibc_cycle { + IBCLifecycleComplete::IBCAck { + channel, + sequence, + ack: _, + success: _, + } => ICS20PacketIdentifier { + channel_id: channel.clone(), + sequence: *sequence, + }, + IBCLifecycleComplete::IBCTimeout { channel, sequence } => ICS20PacketIdentifier { + channel_id: channel.clone(), + sequence: *sequence, + }, + }; + let callback = self.ics20_callbacks.load(store, key.clone())?; + self.ics20_callbacks.remove(store, key); + Ok(callback) + } + /// add dependencies to the contract pub const fn with_dependencies(mut self, dependencies: &'static [StaticDependency]) -> Self { self.contract = self.contract.with_dependencies(dependencies); @@ -148,6 +177,13 @@ impl< self } + /// add ICS20 reply handler for callback to contract + /// See [`crate::sdk::IbcClient::send_funds_with_callback`] for the usage + pub const fn with_ics20_callback_reply(mut self, reply_id: u64) -> Self { + self.contract = self.contract.with_ics20_callback_reply(reply_id); + self + } + /// add Module IBC to contract pub const fn with_module_ibc( mut self, diff --git a/framework/packages/abstract-interface/src/account.rs b/framework/packages/abstract-interface/src/account.rs index 799268df5..b1266ad2d 100644 --- a/framework/packages/abstract-interface/src/account.rs +++ b/framework/packages/abstract-interface/src/account.rs @@ -730,7 +730,8 @@ impl Uploadable for AccountI { ::account::contract::query, ) .with_migrate(::account::contract::migrate) - .with_reply(::account::contract::reply), + .with_reply(::account::contract::reply) + .with_sudo(::account::contract::sudo), ) } fn wasm(chain: &ChainInfoOwned) -> WasmPath { diff --git a/framework/packages/abstract-sdk/Cargo.toml b/framework/packages/abstract-sdk/Cargo.toml index c5d25a0d7..b888be4ac 100644 --- a/framework/packages/abstract-sdk/Cargo.toml +++ b/framework/packages/abstract-sdk/Cargo.toml @@ -40,6 +40,7 @@ semver = { workspace = true } abstract-macros = { workspace = true } cw-clearable = { workspace = true } serde-cw-value = { workspace = true } +anybuf = { workspace = true } # test-utils feature abstract-testing = { workspace = true, optional = true } diff --git a/framework/packages/abstract-sdk/src/apis/ibc.rs b/framework/packages/abstract-sdk/src/apis/ibc.rs index 5a8afff2c..24c6bce3a 100644 --- a/framework/packages/abstract-sdk/src/apis/ibc.rs +++ b/framework/packages/abstract-sdk/src/apis/ibc.rs @@ -3,24 +3,24 @@ //! use abstract_std::{ - account::ExecuteMsg, - account::ModuleInstallConfig, + account::{ExecuteMsg, ModuleInstallConfig}, base, - ibc::{Callback, ModuleQuery}, + ibc::{Callback, ModuleQuery, PACKET_LIFETIME}, ibc_client::{self, ExecuteMsg as IbcClientMsg, InstalledModuleIdentification}, - ibc_host::HostAction, - objects::{module::ModuleInfo, TruncatedChainId}, - ABSTRACT_VERSION, IBC_CLIENT, + ibc_host::{self, HostAction}, + objects::{module::ModuleInfo, ChannelEntry, TruncatedChainId}, + ABSTRACT_VERSION, IBC_CLIENT, ICS20, }; use cosmwasm_std::{ - to_json_binary, wasm_execute, Addr, Coin, CosmosMsg, Deps, Empty, Env, QueryRequest, + to_json_binary, wasm_execute, Addr, Coin, CosmosMsg, Deps, Empty, Env, IbcMsg, IbcTimeout, + QueryRequest, SubMsg, }; use serde::Serialize; use super::AbstractApi; use crate::{ - features::{AccountExecutor, AccountIdentification, ModuleIdentification}, - AbstractSdkResult, ModuleInterface, ModuleRegistryInterface, + features::{AbstractNameService, AccountExecutor, AccountIdentification, ModuleIdentification}, + AbstractSdkResult, HookMemoBuilder, ModuleInterface, ModuleRegistryInterface, }; /// Interact with other chains over IBC. @@ -353,6 +353,62 @@ impl<'a, T: IbcInterface + AccountExecutor> IbcClient<'a, T> { } } +#[cfg(feature = "stargate")] +impl<'a, T: IbcInterface + AccountExecutor + AbstractNameService> IbcClient<'a, T> { + /// Send funds from account to remote account with callback to the module + /// + /// This method should be combined with `.with_ics20_callback_reply` handler on a AbstractContract module object and use same reply_id + /// Callback can be retrieved with `module.load_ics20_callback` + /// + /// Note: Payload occupied for saving callback and shouldn't be edited + pub fn send_funds_with_callback( + &self, + host_chain: TruncatedChainId, + funds: Coin, + callback: Callback, + reply_id: u64, + ) -> AbstractSdkResult { + let name_service = self.base.name_service(self.deps(), &self.env); + let ics20_channel_entry = ChannelEntry { + connected_chain: host_chain.clone(), + protocol: ICS20.to_string(), + }; + let ics20_channel_id = name_service.query(&ics20_channel_entry)?; + let payload = to_json_binary(&abstract_std::ibc::ICS20CallbackPayload { + channel_id: ics20_channel_id.clone(), + callback, + })?; + + let ibc_client_addr = self.module_address()?; + let remote_host = ibc_client::state::IBC_INFRA + .query(&self.deps.querier, ibc_client_addr, &host_chain)? + .ok_or(cosmwasm_std::StdError::generic_err(format!( + "chain {host_chain} not registered in ibc_client" + )))?; + + // Hook for sending the funds correctly to the sender + let account_id = self.base.account_id(self.deps)?; + let action_memo = HookMemoBuilder::new( + remote_host.remote_abstract_host.clone(), + &ibc_host::ExecuteMsg::Fund { + src_account: account_id, + src_chain: TruncatedChainId::from_chain_id(&self.env.block.chain_id), + }, + ) + .callback(&self.env) + .build()?; + + let transfer_msg = IbcMsg::Transfer { + channel_id: ics20_channel_id, + to_address: remote_host.remote_abstract_host, + amount: funds, + timeout: IbcTimeout::with_timestamp(self.env.block.time.plus_seconds(PACKET_LIFETIME)), + memo: Some(action_memo), + }; + Ok(SubMsg::reply_on_success(transfer_msg, reply_id).with_payload(payload)) + } +} + #[cfg(test)] mod test { #![allow(clippy::needless_borrows_for_generic_args)] diff --git a/framework/packages/abstract-sdk/src/apis/ibc_memo/hooks.rs b/framework/packages/abstract-sdk/src/apis/ibc_memo/hooks.rs index 1f26e4147..da86cb04e 100644 --- a/framework/packages/abstract-sdk/src/apis/ibc_memo/hooks.rs +++ b/framework/packages/abstract-sdk/src/apis/ibc_memo/hooks.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use cosmwasm_std::{from_json, to_json_binary, Addr, Binary}; +use cosmwasm_std::{from_json, to_json_binary, Addr, Binary, Env}; use serde_cw_value::Value; /// Builder for [IbcHooks](https://github.com/cosmos/ibc-apps/tree/main/modules/ibc-hooks) memo field. @@ -29,6 +29,12 @@ impl HookMemoBuilder { self } + /// The current contract will receive a callback + /// https://github.com/cosmos/ibc-apps/blob/main/modules/ibc-hooks/README.md#interface-for-receiving-the-acks-and-timeouts + pub fn callback(self, env: &Env) -> Self { + self.callback_contract(env.contract.address.clone()) + } + /// Build memo json string pub fn build(self) -> cosmwasm_std::StdResult { let execute_wasm_value = BTreeMap::from([ diff --git a/framework/packages/abstract-sdk/src/base/contract_base.rs b/framework/packages/abstract-sdk/src/base/contract_base.rs index 4b18bb0f5..f34e9f46d 100644 --- a/framework/packages/abstract-sdk/src/base/contract_base.rs +++ b/framework/packages/abstract-sdk/src/base/contract_base.rs @@ -88,6 +88,8 @@ pub struct AbstractContract)]; MAX_REPLY_COUNT], /// IBC callback handler following an IBC action pub(crate) ibc_callback_handler: Option>, + /// ICS20 Callback handler for resolving callback after + pub(crate) ics20_callback_reply_handler: Option, /// Module IBC handler for passing messages between a module on different chains. pub(crate) module_ibc_handler: Option>, } @@ -109,6 +111,7 @@ where instantiate_handler: None, query_handler: None, module_ibc_handler: None, + ics20_callback_reply_handler: None, } } /// Gets the cw2 version of the contract. @@ -142,6 +145,12 @@ where self } + /// add ICS20 reply handler for callback to contract + pub const fn with_ics20_callback_reply(mut self, reply_id: u64) -> Self { + self.ics20_callback_reply_handler = Some(reply_id); + self + } + /// add IBC callback handler to contract pub const fn with_module_ibc( mut self, diff --git a/framework/packages/abstract-sdk/src/base/endpoints/reply.rs b/framework/packages/abstract-sdk/src/base/endpoints/reply.rs index 64d24b399..d4b47ccec 100644 --- a/framework/packages/abstract-sdk/src/base/endpoints/reply.rs +++ b/framework/packages/abstract-sdk/src/base/endpoints/reply.rs @@ -1,13 +1,23 @@ use cosmwasm_std::{DepsMut, Env, Reply, Response}; -use crate::base::Handler; +use crate::{base::Handler, cw_helpers::ics20_callback_reply}; /// Trait for a contract's Reply entry point. pub trait ReplyEndpoint: Handler { /// Handler for the Reply endpoint. fn reply(self, deps: DepsMut, env: Env, msg: Reply) -> Result { let id = msg.id; - let handler = self.reply_handler(id)?; - handler(deps, env, self, msg) + // Handle ICS20 callback Reply if present and id matches + if self.maybe_ics20_callback_handler() == Some(id) { + ics20_callback_reply(deps.storage, msg.clone())?; + // User might want to have extra handling under this reply id + match self.maybe_reply_handler(id) { + Some(handler) => handler(deps, env, self, msg), + None => Ok(Response::new()), + } + } else { + let handler = self.reply_handler(id)?; + handler(deps, env, self, msg) + } } } diff --git a/framework/packages/abstract-sdk/src/base/handler.rs b/framework/packages/abstract-sdk/src/base/handler.rs index dfd0a01fe..51c1f61a5 100644 --- a/framework/packages/abstract-sdk/src/base/handler.rs +++ b/framework/packages/abstract-sdk/src/base/handler.rs @@ -147,6 +147,13 @@ where let contract = self.contract(); contract.ibc_callback_handler } + + /// Get an ibc callback handler if it exists. + fn maybe_ics20_callback_handler(&self) -> Option { + let contract = self.contract(); + contract.ics20_callback_reply_handler + } + /// Get an IBC module call handler if it exists. fn maybe_module_ibc_handler(&self) -> Option> { let contract = self.contract(); diff --git a/framework/packages/abstract-sdk/src/cw_helpers/ics20.rs b/framework/packages/abstract-sdk/src/cw_helpers/ics20.rs new file mode 100644 index 000000000..86e46b1b9 --- /dev/null +++ b/framework/packages/abstract-sdk/src/cw_helpers/ics20.rs @@ -0,0 +1,31 @@ +use abstract_std::{ + ibc::{ICS20CallbackPayload, ICS20PacketIdentifier, MsgTransferResponse}, + objects::storage_namespaces::ICS20_CALLBACKS, +}; +use cosmwasm_std::{from_json, DepsMut, Reply, StdError, Storage}; + +use crate::AbstractSdkResult; + +/// Reply handler for ics20 callbacks +pub fn ics20_callback_reply(storage: &mut dyn Storage, reply: Reply) -> AbstractSdkResult<()> { + let res = reply.result.into_result().map_err(StdError::generic_err)?; + println!("{:x?}", res.data); + // TODO, implement for msg_responses (to have both cases covered) + let transfer_response = + MsgTransferResponse::decode(&res.data.expect("Data is set after sending a packet")) + .map_err(|e| StdError::generic_err(e.to_string()))?; + + let payload: ICS20CallbackPayload = from_json(reply.payload)?; + + // We register the callback for later use + cw_storage_plus::Map::new(ICS20_CALLBACKS).save( + storage, + ICS20PacketIdentifier { + channel_id: payload.channel_id, + sequence: transfer_response.sequence, + }, + &payload.callback, + )?; + + Ok(()) +} diff --git a/framework/packages/abstract-sdk/src/cw_helpers/mod.rs b/framework/packages/abstract-sdk/src/cw_helpers/mod.rs index 05579fde4..e2e1d0541 100644 --- a/framework/packages/abstract-sdk/src/cw_helpers/mod.rs +++ b/framework/packages/abstract-sdk/src/cw_helpers/mod.rs @@ -2,9 +2,11 @@ mod cosmwasm_std; mod cw_ownable; mod fees; +mod ics20; mod migrate_instantiate; pub use cw_clearable::*; +pub use ics20::*; pub use migrate_instantiate::*; pub use self::{cosmwasm_std::*, fees::*}; diff --git a/framework/packages/abstract-sdk/src/error.rs b/framework/packages/abstract-sdk/src/error.rs index 8cad53653..0e2d2bba5 100644 --- a/framework/packages/abstract-sdk/src/error.rs +++ b/framework/packages/abstract-sdk/src/error.rs @@ -2,6 +2,7 @@ use std::fmt::{Display, Formatter}; use crate::std::AbstractError; +use anybuf::BufanyError; use cosmwasm_std::Addr; use cw_asset::AssetError; use thiserror::Error; diff --git a/framework/packages/abstract-std/Cargo.toml b/framework/packages/abstract-std/Cargo.toml index 208a382f9..fcf93fed7 100644 --- a/framework/packages/abstract-std/Cargo.toml +++ b/framework/packages/abstract-std/Cargo.toml @@ -35,6 +35,7 @@ cw-clearable = { workspace = true } bech32 = { version = "0.11.0" } ripemd = { version = "0.1.3", default-features = false } cw-blob = { workspace = true } +anybuf = { workspace = true } ## Stringify function names function_name = { version = "0.3.0" } diff --git a/framework/packages/abstract-std/src/account.rs b/framework/packages/abstract-std/src/account.rs index 72bb33b7d..bc4a72b88 100644 --- a/framework/packages/abstract-std/src/account.rs +++ b/framework/packages/abstract-std/src/account.rs @@ -17,14 +17,15 @@ //! use cosmwasm_schema::QueryResponses; use cosmwasm_std::{Binary, Coin, CosmosMsg, Empty}; +use cw_storage_plus::PrimaryKey; +use crate::objects::TruncatedChainId; use crate::objects::{ gov_type::{GovAction, GovernanceDetails, TopLevelOwnerResponse}, module::ModuleInfo, ownership::Ownership, AccountId, }; - use cosmwasm_std::Addr; use cw2::ContractVersion; @@ -226,6 +227,11 @@ pub enum ExecuteMsg { RemoveAuthMethod { id: u8, }, + SendFundsWithActions { + host_chain: TruncatedChainId, + amount: Coin, + actions: Vec>, + }, } #[cosmwasm_schema::cw_serde] diff --git a/framework/packages/abstract-std/src/native/ibc.rs b/framework/packages/abstract-std/src/native/ibc.rs index 9a84944e9..cfa6b848c 100644 --- a/framework/packages/abstract-std/src/native/ibc.rs +++ b/framework/packages/abstract-std/src/native/ibc.rs @@ -8,6 +8,7 @@ use cosmwasm_std::{ to_json_binary, wasm_execute, Binary, CosmosMsg, Empty, Event, QueryRequest, StdError, StdResult, }; +use cw_storage_plus::PrimaryKey; use schemars::JsonSchema; use serde::Serialize; @@ -17,6 +18,8 @@ use crate::{ }; use polytone_callbacks::{Callback as PolytoneCallback, ErrorResponse, ExecutionResponse}; +pub const PACKET_LIFETIME: u64 = 60 * 60; + /// Callback from modules, that is turned into an IbcResponseMsg by the ibc client /// A callback can only be sent to itself #[cosmwasm_schema::cw_serde] @@ -189,3 +192,72 @@ pub struct ModuleQuery { pub msg: Binary, } // ANCHOR_END: module_ibc_query + +pub struct MsgTransferResponse { + pub sequence: u64, // 1 +} + +impl MsgTransferResponse { + pub fn decode(data: &cosmwasm_std::Binary) -> Result { + let bufany = anybuf::Bufany::deserialize(data.as_ref())?; + let sequence = bufany + .uint64(1) + .ok_or(anybuf::BufanyError::UnexpectedEndOfData)?; + Ok(Self { sequence }) + } +} + +// Source: https://github.com/cosmos/ibc-apps/blob/8cb681e31589bc90b47e0ab58173a579825fd56d/modules/ibc-hooks/README.md#interface-for-receiving-the-acks-and-timeouts +#[cosmwasm_schema::cw_serde] +pub enum IBCLifecycleComplete { + #[serde(rename = "ibc_ack")] + 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, + /// Weather an `Ack` is a success of failure according to the transfer spec + success: bool, + }, + #[serde(rename = "ibc_timeout")] + IBCTimeout { + /// The source channel of the IBC packet + channel: String, + /// The sequence number that the packet was sent with + sequence: u64, + }, +} + +#[cosmwasm_schema::cw_serde] +pub struct ICS20CallbackPayload { + pub channel_id: String, + pub callback: Callback, +} + +#[cosmwasm_schema::cw_serde] +pub struct ICS20PacketIdentifier { + pub channel_id: String, + pub sequence: u64, +} + +impl<'a> PrimaryKey<'a> for ICS20PacketIdentifier { + /// channel id + type Prefix = String; + + /// channel id + type SubPrefix = String; + + /// sequence + type Suffix = u64; + + // sequence + type SuperSuffix = u64; + + fn key(&self) -> Vec { + let mut keys = self.channel_id.key(); + keys.extend(self.sequence.key()); + keys + } +} diff --git a/framework/packages/abstract-std/src/native/ibc/ibc_host.rs b/framework/packages/abstract-std/src/native/ibc/ibc_host.rs index dfe088e8c..eb4afc16e 100644 --- a/framework/packages/abstract-std/src/native/ibc/ibc_host.rs +++ b/framework/packages/abstract-std/src/native/ibc/ibc_host.rs @@ -106,6 +106,11 @@ pub enum ExecuteMsg { target_module: ModuleInfo, msg: Binary, }, + /// Sends the associated funds to the local account corresponding to the source account id + Fund { + src_account: AccountId, + src_chain: TruncatedChainId, + }, } /// Query Host message diff --git a/framework/packages/abstract-std/src/objects/storage_namespaces.rs b/framework/packages/abstract-std/src/objects/storage_namespaces.rs index 126c1f53f..c259e7d15 100644 --- a/framework/packages/abstract-std/src/objects/storage_namespaces.rs +++ b/framework/packages/abstract-std/src/objects/storage_namespaces.rs @@ -6,8 +6,10 @@ pub const ADMIN_NAMESPACE: &str = "admin"; pub const OWNERSHIP_STORAGE_KEY: &str = "ownership"; /// storage key for ModuleData pub const MODULE_STORAGE_KEY: &str = "mod"; -/// Storage key for config in all modules +/// storage key for config in all modules pub const CONFIG_STORAGE_KEY: &str = "cfg"; +/// storage key for ics20 callback in all modules +pub const ICS20_CALLBACKS: &str = "i20cb"; pub mod account { pub const SUSPENSION_STATUS: &str = "aa"; @@ -20,6 +22,7 @@ pub mod account { pub const INSTALL_MODULES_CONTEXT: &str = "ah"; pub const MIGRATE_CONTEXT: &str = "ai"; pub const CALLING_TO_AS_ADMIN: &str = "aj"; + pub const ICS20_CALLBACKS: &str = "ak"; // XION authentificators, could be there could be not pub const AUTH_ADMIN: &str = "ax"; diff --git a/interchain/Cargo.toml b/interchain/Cargo.toml index 58c800687..bef650dbb 100644 --- a/interchain/Cargo.toml +++ b/interchain/Cargo.toml @@ -111,6 +111,10 @@ abstract-cw-orch-polytone = { git = "https://github.com/AbstractSDK/polytone.git # Backup release profile, will result in warnings during optimization + +abstract-cw-multi-test = { git = "https://github.com/abstractsdk/cw-multi-test-fork", branch = "feature/add-ics20-transfer-data" } +# abstract-cw-multi-test = { path = "../../cw-multi-test" } + [patch.'https://github.com/AbstractSDK/cw-orchestrator'] cw-orch = { version = "0.26.0" } cw-orch-interchain = { version = "=0.7.2" } diff --git a/interchain/four-chain-starship/configs/two-junos.yaml b/interchain/four-chain-starship/configs/two-junos.yaml index d2c5744ad..49e229b00 100644 --- a/interchain/four-chain-starship/configs/two-junos.yaml +++ b/interchain/four-chain-starship/configs/two-junos.yaml @@ -1,6 +1,6 @@ chains: - id: juno-1 - name: juno + name: stargaze numValidators: 1 ports: rest: 1313 @@ -8,7 +8,7 @@ chains: grpc: 30658 faucet: 8000 - id: junotwo-1 - name: osmosis + name: stargaze numValidators: 1 ports: rest: 1317 diff --git a/interchain/interchain-end_to_end_testing/Cargo.toml b/interchain/interchain-end_to_end_testing/Cargo.toml index 69de461dc..c185fc1e2 100644 --- a/interchain/interchain-end_to_end_testing/Cargo.toml +++ b/interchain/interchain-end_to_end_testing/Cargo.toml @@ -41,5 +41,6 @@ thiserror.workspace = true base64 = "0.22.1" # Testing contract counter-contract = { version = "0.27.0" } # Use tag if breaks +cosmos-sdk-proto = { version = "0.24.0" } ping-pong = { path = "../../modules/contracts/apps/ping-pong" } diff --git a/interchain/interchain-end_to_end_testing/src/bin/ibc_hook.rs b/interchain/interchain-end_to_end_testing/src/bin/ibc_hook.rs index b3dc20292..f13df4607 100644 --- a/interchain/interchain-end_to_end_testing/src/bin/ibc_hook.rs +++ b/interchain/interchain-end_to_end_testing/src/bin/ibc_hook.rs @@ -55,8 +55,8 @@ pub fn test_ibc_hook() -> AnyResult<()> { CosmosOptions::default(), ))?; - // Create a channel between the 2 chains for the transfer ports - // JUNO>JUNO2 + // // Create a channel between the 2 chains for the transfer ports + // // JUNO>JUNO2 let juno_juno2_channel = interchain .create_channel( JUNO, @@ -68,11 +68,14 @@ pub fn test_ibc_hook() -> AnyResult<()> { )? .interchain_channel; - let (abstr_juno, abstr_juno2) = abstract_starship_interfaces( - &interchain, - &juno_abstract_deployer, - &juno2_abstract_deployer, - )?; + // let (abstr_juno, abstr_juno2) = abstract_starship_interfaces( + // &interchain, + // &juno_abstract_deployer, + // &juno2_abstract_deployer, + // )?; + + let abstr_juno = abstract_interface::Abstract::load_from(juno.clone())?; + let abstr_juno2 = abstract_interface::Abstract::load_from(juno2.clone())?; let counter_juno2 = init_counter(juno2.clone())?; @@ -154,7 +157,7 @@ pub fn test_ibc_hook() -> AnyResult<()> { coins(10_000_000_000, get_denom(&juno, token_subdenom.as_str())), )?; - origin_account.execute_on_module( + let send_tx = origin_account.execute_on_module( IBC_CLIENT, &abstract_std::ibc_client::ExecuteMsg::SendFunds { host_chain: TruncatedChainId::from_chain_id(JUNO2), @@ -164,6 +167,12 @@ pub fn test_ibc_hook() -> AnyResult<()> { coins(10_000_000_000, get_denom(&juno, token_subdenom.as_str())), )?; + let ibc_result = interchain.await_and_check_packets(&juno.chain_id(), send_tx)?; + println!( + "Ibc Result of sending funds + memo : {:?}", + ibc_result.packets[0] + ); + log::info!("waiting for ibc_hook to finish tx"); std::thread::sleep(Duration::from_secs(15)); diff --git a/interchain/interchain-end_to_end_testing/src/bin/module_ics20_callback.rs b/interchain/interchain-end_to_end_testing/src/bin/module_ics20_callback.rs new file mode 100644 index 000000000..769b6cfd1 --- /dev/null +++ b/interchain/interchain-end_to_end_testing/src/bin/module_ics20_callback.rs @@ -0,0 +1,174 @@ +// This script is used for testing a connection between 4 chains +// This script checks ibc-hook memo implementation on ibc-client + +use std::{ + sync::Arc, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; + +use abstract_interchain_tests::{abstract_starship_interfaces, set_starship_env, JUNO, JUNO2}; +use abstract_interface::{Abstract, AccountDetails, AccountI, AppDeployer}; +use abstract_sdk::HookMemoBuilder; +use abstract_std::{ + ans_host::ExecuteMsgFns, + ibc::Callback, + objects::{TruncatedChainId, UncheckedChannelEntry, ABSTRACT_ACCOUNT_ID}, + IBC_CLIENT, ICS20, +}; +use anyhow::Result as AnyResult; +use cosmwasm_std::{coin, coins}; +use counter_contract::CounterContract; +use cw_orch::{ + daemon::{senders::CosmosSender, CosmosOptions, RUNTIME}, + prelude::*, +}; +use cw_orch_interchain::prelude::*; +use cw_orch_proto::tokenfactory::{create_denom, get_denom, mint}; +use networks::ChainKind; +use ping_pong::{AppExecuteMsgFns, AppQueryMsgFns}; + +pub fn test_ics20_callback() -> AnyResult<()> { + dotenv::dotenv().ok(); + set_starship_env(); + env_logger::init(); + + let starship = Starship::new(None).unwrap(); + let interchain = starship.interchain_env(); + + let juno = interchain.get_chain(JUNO).unwrap(); + let juno2 = interchain.get_chain(JUNO2).unwrap(); + + // // Using chainkind local so we can use mnemonic from env + let juno_chain_info = ChainInfoOwned { + kind: ChainKind::Local, + ..juno.chain_info().clone() + }; + let juno2_chain_info = ChainInfoOwned { + kind: ChainKind::Local, + ..juno2.chain_info().clone() + }; + + let juno_abstract_deployer = juno.rt_handle.block_on(CosmosSender::new( + &Arc::new(juno_chain_info), + CosmosOptions::default(), + ))?; + let juno2_abstract_deployer = juno2.rt_handle.block_on(CosmosSender::new( + &Arc::new(juno2_chain_info), + CosmosOptions::default(), + ))?; + + // // Create a channel between the 2 chains for the transfer ports + // // JUNO>JUNO2 + let juno_juno2_channel = interchain + .create_channel( + JUNO, + JUNO2, + &PortId::transfer(), + &PortId::transfer(), + "ics20-1", + Some(cosmwasm_std::IbcOrder::Unordered), + )? + .interchain_channel; + + let (abstr_juno, abstr_juno2) = abstract_starship_interfaces( + &interchain, + &juno_abstract_deployer, + &juno2_abstract_deployer, + )?; + let root_account_juno = AccountI::load_from(&abstr_juno, ABSTRACT_ACCOUNT_ID)?; + root_account_juno.set_ibc_status(true)?; + root_account_juno.create_remote_account( + AccountDetails::default(), + TruncatedChainId::from_chain_id(JUNO2), + )?; + // let root_account_juno2 = AccountI::load_from(&abstr_juno2, ABSTRACT_ACCOUNT_ID)?; + + let ping_pong_juno = init_ping_pong(&root_account_juno)?; + // let ping_pong_juno2 = init_ping_pong(&root_account_juno2)?; + + let sender = juno.sender_addr().to_string(); + + let test_amount: u128 = 100_000_000_000; + let token_subdenom = format!( + "testtoken{}", + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs() + ); + + // Create Denom + create_denom(&juno, token_subdenom.as_str())?; + + // Mint Denom + mint(&juno, sender.as_str(), token_subdenom.as_str(), test_amount)?; + mint( + &juno, + root_account_juno.addr_str()?.as_str(), + token_subdenom.as_str(), + test_amount, + )?; + + // Register this channel with the abstract ibc implementation for sending tokens + abstr_juno + .ans_host + .call_as(&juno_abstract_deployer) + .update_channels( + vec![( + UncheckedChannelEntry { + connected_chain: TruncatedChainId::from_chain_id(JUNO2).to_string(), + protocol: ICS20.to_string(), + }, + juno_juno2_channel + .get_chain(JUNO)? + .channel + .unwrap() + .to_string(), + )], + vec![], + )?; + + let tx_response = ping_pong_juno.fund_opponent( + Callback { msg: b"foo".into() }, + Coin::new( + 10_000_000_000_u128, + get_denom(&juno, token_subdenom.as_str()), + ), + TruncatedChainId::from_chain_id(JUNO2), + )?; + + let ibc_result = interchain.await_and_check_packets(&juno.chain_id(), tx_response)?; + + println!( + "Ibc Result of sending funds + hook : {:?}", + ibc_result.packets[0] + ); + + let callbacks = ping_pong_juno.ics_20_callbacks()?; + dbg!(callbacks); + + log::info!("waiting for ibc_hook to finish tx"); + std::thread::sleep(Duration::from_secs(15)); + + Ok(()) +} + +pub fn init_ping_pong( + abstract_account: &AccountI, +) -> anyhow::Result> { + let ping_pong = ping_pong::AppInterface::new( + ping_pong::contract::APP_ID, + abstract_account.environment().clone(), + ); + ping_pong.deploy( + ping_pong::contract::APP_VERSION.parse().unwrap(), + abstract_interface::DeployStrategy::Try, + )?; + abstract_account.environment(); + abstract_account.install_app(&ping_pong, &ping_pong::msg::AppInstantiateMsg {}, &[])?; + Ok(ping_pong) +} + +pub fn main() { + test_ics20_callback().unwrap(); +} diff --git a/modules/contracts/apps/ping-pong/Cargo.toml b/modules/contracts/apps/ping-pong/Cargo.toml index f2c5ba98e..2d5978227 100644 --- a/modules/contracts/apps/ping-pong/Cargo.toml +++ b/modules/contracts/apps/ping-pong/Cargo.toml @@ -20,7 +20,7 @@ cw-storage-plus = { workspace = true } thiserror = { workspace = true } schemars = { workspace = true } cw-asset = { workspace = true } -abstract-app = { workspace = true } +abstract-app = { workspace = true, features = ["stargate"] } cw-orch = { workspace = true } abstract-ibc-client = { version = "0.24.1", default-features = false, path = "../../../../framework/contracts/native/ibc-client" } diff --git a/modules/contracts/apps/ping-pong/src/contract.rs b/modules/contracts/apps/ping-pong/src/contract.rs index 3ede38020..1767a8067 100644 --- a/modules/contracts/apps/ping-pong/src/contract.rs +++ b/modules/contracts/apps/ping-pong/src/contract.rs @@ -4,7 +4,7 @@ use cosmwasm_std::Response; use crate::{ error::AppError, handlers, ibc, - msg::{AppExecuteMsg, AppInstantiateMsg, AppMigrateMsg, AppQueryMsg}, + msg::{AppExecuteMsg, AppInstantiateMsg, AppMigrateMsg, AppQueryMsg, AppSudoMsg}, }; /// The version of your app @@ -16,7 +16,8 @@ pub const APP_ID: &str = "abstract:ping-pong"; pub type AppResult = Result; /// The type of the app that is used to build your app and access the Abstract SDK features. -pub type App = AppContract; +pub type App = + AppContract; const APP: App = App::new(APP_ID, APP_VERSION, None) .with_instantiate(handlers::instantiate_handler) @@ -27,7 +28,9 @@ const APP: App = App::new(APP_ID, APP_VERSION, None) &[abstract_ibc_client::contract::CONTRACT_VERSION], )]) .with_module_ibc(ibc::receive_module_ibc) - .with_ibc_callback(ibc::ibc_callback); + .with_ibc_callback(ibc::ibc_callback) + .with_ics20_callback_reply(handlers::ICS20_CALLBACK_ID) + .with_sudo(handlers::sudo::sudo_handler); // Export handlers #[cfg(feature = "export")] diff --git a/modules/contracts/apps/ping-pong/src/handlers/execute.rs b/modules/contracts/apps/ping-pong/src/handlers/execute.rs index 7c6cc0b61..a24760080 100644 --- a/modules/contracts/apps/ping-pong/src/handlers/execute.rs +++ b/modules/contracts/apps/ping-pong/src/handlers/execute.rs @@ -4,13 +4,15 @@ use abstract_app::{ std::{ibc::Callback, ibc_client::InstalledModuleIdentification}, traits::{AbstractResponse, AccountIdentification}, }; -use cosmwasm_std::{CosmosMsg, DepsMut, Env, MessageInfo}; +use cosmwasm_std::{Coin, CosmosMsg, DepsMut, Env, MessageInfo}; use crate::{ contract::{App, AppResult}, msg::{AppExecuteMsg, AppQueryMsg, PingOrPong, PingPongCallbackMsg, PingPongIbcMsg}, }; +use super::ICS20_CALLBACK_ID; + pub fn execute_handler( deps: DepsMut, env: Env, @@ -23,6 +25,11 @@ pub fn execute_handler( AppExecuteMsg::QueryAndMaybePingPong { opponent_chain: host_chain, } => query_and_ping(deps, &env, host_chain, module), + AppExecuteMsg::FundOpponent { + opponent_chain, + funds, + callback, + } => fund_opponent(deps, &env, opponent_chain, funds, callback, module), } } @@ -77,3 +84,18 @@ fn query_and_ping( Ok(module.response("rematch").add_message(module_query)) } + +pub(crate) fn fund_opponent( + deps: DepsMut, + env: &Env, + opponent_chain: TruncatedChainId, + funds: Coin, + callback: Callback, + module: App, +) -> AppResult { + let ibc_client: IbcClient<_> = module.ibc_client(deps.as_ref(), env); + let msg = + ibc_client.send_funds_with_callback(opponent_chain, funds, callback, ICS20_CALLBACK_ID)?; + + Ok(module.response("fund_opponent").add_submessage(msg)) +} diff --git a/modules/contracts/apps/ping-pong/src/handlers/instantiate.rs b/modules/contracts/apps/ping-pong/src/handlers/instantiate.rs index 4d77843b8..8b519ea80 100644 --- a/modules/contracts/apps/ping-pong/src/handlers/instantiate.rs +++ b/modules/contracts/apps/ping-pong/src/handlers/instantiate.rs @@ -3,7 +3,7 @@ use cosmwasm_std::{DepsMut, Env, MessageInfo, Response}; use crate::{ contract::{App, AppResult}, msg::AppInstantiateMsg, - state::{LOSSES, WINS}, + state::{ICS20_CALLBACKS, LOSSES, WINS}, }; pub fn instantiate_handler( @@ -15,6 +15,7 @@ pub fn instantiate_handler( ) -> AppResult { WINS.save(deps.storage, &0)?; LOSSES.save(deps.storage, &0)?; + ICS20_CALLBACKS.save(deps.storage, &vec![])?; Ok(Response::new()) } diff --git a/modules/contracts/apps/ping-pong/src/handlers/mod.rs b/modules/contracts/apps/ping-pong/src/handlers/mod.rs index d27bf0ac0..e1223ef09 100644 --- a/modules/contracts/apps/ping-pong/src/handlers/mod.rs +++ b/modules/contracts/apps/ping-pong/src/handlers/mod.rs @@ -1,7 +1,11 @@ pub mod execute; pub mod instantiate; pub mod query; +pub mod sudo; pub use crate::handlers::{ execute::execute_handler, instantiate::instantiate_handler, query::query_handler, + sudo::sudo_handler, }; + +pub const ICS20_CALLBACK_ID: u64 = 1; diff --git a/modules/contracts/apps/ping-pong/src/handlers/query.rs b/modules/contracts/apps/ping-pong/src/handlers/query.rs index d718ddc84..64f7abf58 100644 --- a/modules/contracts/apps/ping-pong/src/handlers/query.rs +++ b/modules/contracts/apps/ping-pong/src/handlers/query.rs @@ -1,15 +1,17 @@ +use abstract_app::std::ibc::{Callback, IBCLifecycleComplete}; use cosmwasm_std::{to_json_binary, Binary, Deps, Env, StdResult}; use crate::{ contract::{App, AppResult}, msg::{AppQueryMsg, BlockHeightResponse, GameStatusResponse}, - state::{LOSSES, WINS}, + state::{ICS20_CALLBACKS, LOSSES, WINS}, }; pub fn query_handler(deps: Deps, env: Env, _module: &App, msg: AppQueryMsg) -> AppResult { match msg { AppQueryMsg::GameStatus {} => to_json_binary(&query_wins(deps)?), AppQueryMsg::BlockHeight {} => to_json_binary(&query_block_height(env)?), + AppQueryMsg::ICS20Callbacks {} => to_json_binary(&query_ics20_callbacks(deps)?), } .map_err(Into::into) } @@ -26,3 +28,8 @@ fn query_block_height(env: Env) -> StdResult { height: env.block.height, }) } + +fn query_ics20_callbacks(deps: Deps) -> StdResult> { + let callbacks = ICS20_CALLBACKS.load(deps.storage)?; + Ok(callbacks) +} diff --git a/modules/contracts/apps/ping-pong/src/handlers/sudo.rs b/modules/contracts/apps/ping-pong/src/handlers/sudo.rs new file mode 100644 index 000000000..145be7453 --- /dev/null +++ b/modules/contracts/apps/ping-pong/src/handlers/sudo.rs @@ -0,0 +1,20 @@ +use cosmwasm_std::{DepsMut, Env, Response}; + +use crate::{ + contract::{App, AppResult}, + msg::AppSudoMsg, + state::ICS20_CALLBACKS, +}; + +pub fn sudo_handler(deps: DepsMut, _env: Env, module: App, msg: AppSudoMsg) -> AppResult { + match msg { + AppSudoMsg::IBCLifecycleComplete(ibclifecycle_complete) => { + let callback = module.load_ics20_callback(deps.storage, &ibclifecycle_complete)?; + ICS20_CALLBACKS.update(deps.storage, |mut list| { + list.push((callback, ibclifecycle_complete)); + AppResult::Ok(list) + })?; + Ok(Response::new()) + } + } +} diff --git a/modules/contracts/apps/ping-pong/src/msg.rs b/modules/contracts/apps/ping-pong/src/msg.rs index b498ee327..598151fe9 100644 --- a/modules/contracts/apps/ping-pong/src/msg.rs +++ b/modules/contracts/apps/ping-pong/src/msg.rs @@ -1,5 +1,9 @@ -use abstract_app::objects::TruncatedChainId; +use abstract_app::{ + objects::TruncatedChainId, + std::ibc::{Callback, IBCLifecycleComplete}, +}; use cosmwasm_schema::QueryResponses; +use cosmwasm_std::Coin; use crate::contract::App; @@ -19,6 +23,11 @@ pub enum AppExecuteMsg { /// Same as PingPong but first queries the state of the opponent chain. /// If the opponent chain should lose (block height not even), it will try to play. QueryAndMaybePingPong { opponent_chain: TruncatedChainId }, + FundOpponent { + opponent_chain: TruncatedChainId, + funds: Coin, + callback: Callback, + }, } /// App query messages @@ -30,6 +39,8 @@ pub enum AppQueryMsg { /// Returns last ping pong that was initiated through this smart contract #[returns(BlockHeightResponse)] BlockHeight {}, + #[returns(Vec<(Callback, IBCLifecycleComplete)>)] + ICS20Callbacks {}, } #[cosmwasm_schema::cw_serde] @@ -68,3 +79,10 @@ pub struct PreviousPingPongResponse { pub pongs: Option, pub host_chain: Option, } + +/// Message type for `sudo` entry_point +#[cosmwasm_schema::cw_serde] +pub enum AppSudoMsg { + #[serde(rename = "ibc_lifecycle_complete")] + IBCLifecycleComplete(IBCLifecycleComplete), +} diff --git a/modules/contracts/apps/ping-pong/src/state.rs b/modules/contracts/apps/ping-pong/src/state.rs index c28eb8ff1..a47a15b40 100644 --- a/modules/contracts/apps/ping-pong/src/state.rs +++ b/modules/contracts/apps/ping-pong/src/state.rs @@ -1,4 +1,9 @@ -use cw_storage_plus::Item; +use abstract_app::std::ibc::{Callback, IBCLifecycleComplete}; +use cosmwasm_std::Binary; +use cw_storage_plus::{Item, Map}; pub const WINS: Item = Item::new("wins"); pub const LOSSES: Item = Item::new("losses"); + +pub const ICS20_CALLBACKS: Item> = + Item::new("ics20_callbacks");