diff --git a/.gitignore b/.gitignore index 009e165f..783266a7 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,8 @@ hash.txt contracts.txt artifacts/ interchaintest/**/wasms/*.wasm +local-interchaintest/configs/logs.json +local-interchaintest/code_id_cache.json # code coverage tarpaulin-report.* diff --git a/Cargo.lock b/Cargo.lock index c04e7cff..fcee5b9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "ahash" version = "0.7.8" @@ -13,11 +28,69 @@ dependencies = [ "version_check", ] +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "astroport" @@ -28,17 +101,95 @@ dependencies = [ "cosmwasm-std", "cw-storage-plus 0.15.1", "cw-utils 0.15.1", - "cw20", + "cw20 0.15.1", + "itertools 0.10.5", + "uint", +] + +[[package]] +name = "astroport" +version = "3.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdebdf96895f363e121710cb84bbbfa659cea1bb1470260d4976d1a7206b3b16" +dependencies = [ + "astroport-circular-buffer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cosmwasm-schema", + "cosmwasm-std", + "cw-asset", + "cw-storage-plus 0.15.1", + "cw-utils 1.0.3", + "cw20 0.15.1", + "cw3", "itertools 0.10.5", "uint", ] +[[package]] +name = "astroport" +version = "5.0.0" +source = "git+https://github.com/astroport-fi/astroport-core.git?tag=v5.0.0#4b3bff8626d33ef5cc3ce40f68939cb84b50fda1" +dependencies = [ + "astroport-circular-buffer 0.2.0 (git+https://github.com/astroport-fi/astroport-core.git?tag=v5.0.0)", + "cosmos-sdk-proto 0.19.0", + "cosmwasm-schema", + "cosmwasm-std", + "cw-asset", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw20 1.1.2", + "itertools 0.12.1", + "prost 0.11.9", + "uint", +] + +[[package]] +name = "astroport" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c4af74ada17a13fe7b75d0d529367eb5dcb15e06fd7e744892191856eedc742" +dependencies = [ + "astroport-circular-buffer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cosmos-sdk-proto 0.19.0", + "cosmwasm-schema", + "cosmwasm-std", + "cw-asset", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw20 1.1.2", + "itertools 0.12.1", + "prost 0.11.9", + "uint", +] + +[[package]] +name = "astroport-circular-buffer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c7369d3c4126804f861620db2221c15b5fa7b7718f12180e265b087c933fb6" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 0.15.1", + "thiserror", +] + +[[package]] +name = "astroport-circular-buffer" +version = "0.2.0" +source = "git+https://github.com/astroport-fi/astroport-core.git?tag=v5.0.0#4b3bff8626d33ef5cc3ce40f68939cb84b50fda1" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "thiserror", +] + [[package]] name = "astroport-factory" version = "1.5.1" source = "git+https://github.com/astroport-fi/astroport-core.git?rev=700f66d#700f66d677a173686cb15cb9cb8c7a4d20c84ad8" dependencies = [ - "astroport", + "astroport 2.9.5", "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus 0.15.1", @@ -48,12 +199,40 @@ dependencies = [ "thiserror", ] +[[package]] +name = "astroport-factory" +version = "1.8.0" +source = "git+https://github.com/astroport-fi/astroport-core.git?tag=v5.0.0#4b3bff8626d33ef5cc3ce40f68939cb84b50fda1" +dependencies = [ + "astroport 5.0.0", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "itertools 0.12.1", + "thiserror", +] + +[[package]] +name = "astroport-native-coin-registry" +version = "1.0.1" +source = "git+https://github.com/astroport-fi/astroport-core.git?tag=v5.0.0#4b3bff8626d33ef5cc3ce40f68939cb84b50fda1" +dependencies = [ + "astroport 3.12.2", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 0.15.1", + "cw2 1.1.2", + "thiserror", +] + [[package]] name = "astroport-native-coin-registry" version = "1.0.1" source = "git+https://github.com/astroport-fi/astroport-core.git?rev=700f66d#700f66d677a173686cb15cb9cb8c7a4d20c84ad8" dependencies = [ - "astroport", + "astroport 2.9.5", "cosmwasm-schema", "cosmwasm-std", "cosmwasm-storage", @@ -67,60 +246,127 @@ name = "astroport-pair" version = "1.3.3" source = "git+https://github.com/astroport-fi/astroport-core.git?rev=700f66d#700f66d677a173686cb15cb9cb8c7a4d20c84ad8" dependencies = [ - "astroport", + "astroport 2.9.5", "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus 0.15.1", "cw2 0.15.1", - "cw20", + "cw20 0.15.1", "integer-sqrt", "protobuf 2.28.0", "thiserror", ] +[[package]] +name = "astroport-pair" +version = "2.0.0" +source = "git+https://github.com/astroport-fi/astroport-core.git?tag=v5.0.0#4b3bff8626d33ef5cc3ce40f68939cb84b50fda1" +dependencies = [ + "astroport 5.0.0", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "cw20 1.1.2", + "integer-sqrt", + "thiserror", +] + [[package]] name = "astroport-pair-concentrated" version = "1.2.13" source = "git+https://github.com/astroport-fi/astroport-core.git?rev=700f66d#700f66d677a173686cb15cb9cb8c7a4d20c84ad8" dependencies = [ - "astroport", - "astroport-factory", + "astroport 2.9.5", + "astroport-factory 1.5.1", "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus 0.15.1", "cw-utils 0.15.1", "cw2 0.15.1", - "cw20", + "cw20 0.15.1", "itertools 0.10.5", "thiserror", ] +[[package]] +name = "astroport-pair-concentrated" +version = "4.0.0" +source = "git+https://github.com/astroport-fi/astroport-core.git?tag=v5.0.0#4b3bff8626d33ef5cc3ce40f68939cb84b50fda1" +dependencies = [ + "astroport 5.0.0", + "astroport-circular-buffer 0.2.0 (git+https://github.com/astroport-fi/astroport-core.git?tag=v5.0.0)", + "astroport-factory 1.8.0", + "astroport-pcl-common", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "cw20 1.1.2", + "itertools 0.12.1", + "thiserror", +] + [[package]] name = "astroport-pair-stable" version = "2.1.4" source = "git+https://github.com/astroport-fi/astroport-core.git?rev=700f66d#700f66d677a173686cb15cb9cb8c7a4d20c84ad8" dependencies = [ - "astroport", + "astroport 2.9.5", "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus 0.15.1", "cw-utils 1.0.3", "cw2 0.15.1", - "cw20", + "cw20 0.15.1", "itertools 0.10.5", "thiserror", ] +[[package]] +name = "astroport-pair-stable" +version = "4.0.0" +source = "git+https://github.com/astroport-fi/astroport-core.git?tag=v5.0.0#4b3bff8626d33ef5cc3ce40f68939cb84b50fda1" +dependencies = [ + "astroport 5.0.0", + "astroport-circular-buffer 0.2.0 (git+https://github.com/astroport-fi/astroport-core.git?tag=v5.0.0)", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "cw20 1.1.2", + "itertools 0.12.1", + "thiserror", +] + +[[package]] +name = "astroport-pcl-common" +version = "2.0.1" +source = "git+https://github.com/astroport-fi/astroport-core.git?tag=v5.0.0#4b3bff8626d33ef5cc3ce40f68939cb84b50fda1" +dependencies = [ + "astroport 5.0.0", + "astroport-factory 1.8.0", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw20 1.1.2", + "itertools 0.12.1", + "thiserror", +] + [[package]] name = "astroport-token" version = "1.1.1" source = "git+https://github.com/astroport-fi/astroport-core.git?rev=700f66d#700f66d677a173686cb15cb9cb8c7a4d20c84ad8" dependencies = [ - "astroport", + "astroport 2.9.5", "cosmwasm-schema", "cosmwasm-std", "cw2 0.15.1", - "cw20", + "cw20 0.15.1", "cw20-base", "snafu", ] @@ -130,7 +376,7 @@ name = "astroport-whitelist" version = "1.0.1" source = "git+https://github.com/astroport-fi/astroport-core.git?rev=700f66d#700f66d677a173686cb15cb9cb8c7a4d20c84ad8" dependencies = [ - "astroport", + "astroport 2.9.5", "cosmwasm-schema", "cosmwasm-std", "cw1-whitelist", @@ -138,12 +384,42 @@ dependencies = [ "thiserror", ] +[[package]] +name = "astroport-xastro-token" +version = "1.1.0" +source = "git+https://github.com/astroport-fi/astroport-core.git?tag=v5.0.0#4b3bff8626d33ef5cc3ce40f68939cb84b50fda1" +dependencies = [ + "astroport 3.12.2", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 0.15.1", + "cw2 0.15.1", + "cw20 0.15.1", + "cw20-base", + "snafu", +] + [[package]] name = "autocfg" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base16ct" version = "0.2.0" @@ -168,6 +444,18 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + [[package]] name = "block-buffer" version = "0.9.0" @@ -192,6 +480,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56953345e39537a3e18bdaeba4cb0c58a78c1f61f361dc0fa7c5c7340ae87c5f" +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "byteorder" version = "1.5.0" @@ -207,6 +501,12 @@ dependencies = [ "serde", ] +[[package]] +name = "cc" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" + [[package]] name = "cfg-if" version = "1.0.0" @@ -222,6 +522,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + [[package]] name = "colored" version = "1.9.4" @@ -259,6 +565,22 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + [[package]] name = "cosmos-sdk-proto" version = "0.14.0" @@ -270,6 +592,17 @@ dependencies = [ "tendermint-proto 0.23.9", ] +[[package]] +name = "cosmos-sdk-proto" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73c9d2043a9e617b0d602fbc0a0ecd621568edbf3a9774890a6d562389bd8e1c" +dependencies = [ + "prost 0.11.9", + "prost-types 0.11.9", + "tendermint-proto 0.32.2", +] + [[package]] name = "cosmos-sdk-proto" version = "0.20.0" @@ -283,9 +616,9 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.5.4" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b4c3f9c4616d6413d4b5fc4c270a4cc32a374b9be08671e80e1a019f805d8f" +checksum = "dd50718a2b6830ce9eb5d465de5a018a12e71729d66b70807ce97e6dd14f931d" dependencies = [ "digest 0.10.7", "ecdsa", @@ -297,9 +630,9 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.5.4" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c586ced10c3b00e809ee664a895025a024f60d65d34fe4c09daed4a4db68a3f3" +checksum = "242e98e7a231c122e08f300d9db3262d1007b51758a8732cd6210b3e9faa4f3a" dependencies = [ "syn 1.0.109", ] @@ -330,9 +663,9 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.5.4" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712fe58f39d55c812f7b2c84e097cdede3a39d520f89b6dc3153837e31741927" +checksum = "78c1556156fdf892a55cced6115968b961eaaadd6f724a2c2cb7d1e168e32dd3" dependencies = [ "base64", "bech32", @@ -369,21 +702,52 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "covenant-macros" +version = "0.1.0" +source = "git+https://github.com/timewave-computer/covenants?tag=v0.1.0#e41120b6f0e9fe11934b5274033db21cd7deb271" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "covenant-utils" version = "0.1.0" dependencies = [ - "astroport", + "astroport 2.9.5", "bech32", "cosmos-sdk-proto 0.14.0", "cosmwasm-schema", "cosmwasm-std", - "covenant-macros", + "covenant-macros 0.1.0", "cw-storage-plus 1.2.0", "cw-utils 1.0.3", - "cw20", + "cw20 0.15.1", "neutron-sdk", - "polytone", + "polytone 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "prost 0.11.9", + "serde-json-wasm 0.4.1", + "sha2 0.10.8", +] + +[[package]] +name = "covenant-utils" +version = "0.1.0" +source = "git+https://github.com/timewave-computer/covenants?tag=v0.1.0#e41120b6f0e9fe11934b5274033db21cd7deb271" +dependencies = [ + "astroport 2.9.5", + "bech32", + "cosmos-sdk-proto 0.14.0", + "cosmwasm-schema", + "cosmwasm-std", + "covenant-macros 0.1.0 (git+https://github.com/timewave-computer/covenants?tag=v0.1.0)", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw20 0.15.1", + "neutron-sdk", + "polytone 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "prost 0.11.9", "serde-json-wasm 0.4.1", "sha2 0.10.8", @@ -439,6 +803,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "cw-address-like" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451a4691083a88a3c0630a8a88799e9d4cd6679b7ce8ff22b8da2873ff31d380" +dependencies = [ + "cosmwasm-std", +] + +[[package]] +name = "cw-asset" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c999a12f8cd8736f6f86e9a4ede5905530cb23cfdef946b9da1c506ad1b70799" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-address-like", + "cw-storage-plus 1.2.0", + "cw20 1.1.2", + "thiserror", +] + [[package]] name = "cw-fifo" version = "0.1.0" @@ -449,7 +836,17 @@ dependencies = [ ] [[package]] -name = "cw-multi-test" +name = "cw-fifo" +version = "0.1.0" +source = "git+https://github.com/timewave-computer/covenants?tag=v0.1.0#e41120b6f0e9fe11934b5274033db21cd7deb271" +dependencies = [ + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "serde", +] + +[[package]] +name = "cw-multi-test" version = "0.20.0" source = "git+https://github.com/Art3miX/cw-multi-test?branch=main#a09a3c0c3672ee33565a0f5281a71786c491afe1" dependencies = [ @@ -589,6 +986,19 @@ dependencies = [ "serde", ] +[[package]] +name = "cw20" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "526e39bb20534e25a1cd0386727f0038f4da294e5e535729ba3ef54055246abd" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils 1.0.3", + "schemars", + "serde", +] + [[package]] name = "cw20-base" version = "0.15.1" @@ -600,13 +1010,63 @@ dependencies = [ "cw-storage-plus 0.15.1", "cw-utils 0.15.1", "cw2 0.15.1", - "cw20", + "cw20 0.15.1", "schemars", "semver", "serde", "thiserror", ] +[[package]] +name = "cw3" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2967fbd073d4b626dd9e7148e05a84a3bebd9794e71342e12351110ffbb12395" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils 1.0.3", + "cw20 1.1.2", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.60", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.60", +] + [[package]] name = "der" version = "0.7.9" @@ -628,6 +1088,37 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_builder" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0350b5cb0331628a5916d6c5c0b72e97393b8b6b03b47a9284f4e7f5a405ffd7" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d48cda787f839151732d396ac69e3473923d54312c070ee21e9effcaa8ca0b1d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" +dependencies = [ + "derive_builder_core", + "syn 2.0.60", +] + [[package]] name = "digest" version = "0.9.0" @@ -682,7 +1173,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" dependencies = [ "curve25519-dalek", - "hashbrown", + "hashbrown 0.12.3", "hex", "rand_core 0.6.4", "serde", @@ -715,6 +1206,60 @@ dependencies = [ "zeroize", ] +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + [[package]] name = "ff" version = "0.13.0" @@ -734,12 +1279,90 @@ dependencies = [ "paste", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "forward_ref" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-io", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -762,6 +1385,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + [[package]] name = "group" version = "0.13.0" @@ -773,6 +1402,25 @@ dependencies = [ "subtle", ] +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -782,6 +1430,12 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "heck" version = "0.4.1" @@ -809,6 +1463,123 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", +] + [[package]] name = "integer-sqrt" version = "0.1.5" @@ -818,6 +1589,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "is-terminal" version = "0.4.12" @@ -826,9 +1603,15 @@ checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ "hermit-abi", "libc", - "windows-sys", + "windows-sys 0.52.0", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -839,45 +1622,189 @@ dependencies = [ ] [[package]] -name = "itertools" -version = "0.12.1" +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2 0.10.8", + "signature", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.154" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "local-ictest-e2e" +version = "0.1.0" +dependencies = [ + "anyhow", + "astroport 2.9.5", + "astroport 5.0.0", + "cosmwasm-std", + "covenant-utils 0.1.0", + "cw-utils 1.0.3", + "cw20 0.15.1", + "env_logger", + "localic-std", + "localic-utils", + "log", + "polytone-note", + "polytone-voice", + "reqwest", + "serde", + "serde_json", + "sha2 0.10.8", + "valence-astroport-liquid-pooler", + "valence-astroport-tf-liquid-pooler", + "valence-covenant-single-party-pol", + "valence-covenant-swap", + "valence-covenant-two-party-pol", + "valence-ibc-forwarder", + "valence-osmo-liquid-pooler", + "valence-outpost-osmo-liquid-pooler", + "valence-single-party-pol-holder", + "valence-stride-liquid-staker", + "valence-two-party-pol-holder", +] + +[[package]] +name = "localic-std" +version = "0.0.1" +source = "git+https://github.com/strangelove-ventures/interchaintest?branch=main#54e5595d5f2552977f24e0d53f860ee50e2678f1" +dependencies = [ + "cosmwasm-std", + "reqwest", + "serde_json", + "thiserror", + "tokio", +] + +[[package]] +name = "localic-utils" +version = "0.1.0" +source = "git+https://github.com/timewave-computer/localic-utils?branch=main#32135559ba2fc62d54db6cd2137fd04f7704ccf4" +dependencies = [ + "astroport 5.2.0", + "cosmwasm-std", + "derive_builder", + "localic-std", + "log", + "reqwest", + "serde", + "serde_json", + "sha2 0.10.8", + "thiserror", +] + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] -name = "itoa" -version = "1.0.11" +name = "mime" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] -name = "k256" -version = "0.13.1" +name = "miniz_oxide" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "once_cell", - "sha2 0.10.8", - "signature", + "adler", ] [[package]] -name = "lazy_static" -version = "1.4.0" +name = "mio" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" +dependencies = [ + "hermit-abi", + "libc", + "wasi", + "windows-sys 0.52.0", +] [[package]] -name = "libc" -version = "0.2.154" +name = "native-tls" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] [[package]] name = "neutron-sdk" @@ -928,6 +1855,15 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.36.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -940,6 +1876,50 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "osmosis-std" version = "0.13.2" @@ -997,12 +1977,53 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.5", +] + [[package]] name = "paste" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "pkcs8" version = "0.10.2" @@ -1013,6 +2034,12 @@ dependencies = [ "spki", ] +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + [[package]] name = "polytone" version = "1.0.0" @@ -1025,6 +2052,61 @@ dependencies = [ "thiserror", ] +[[package]] +name = "polytone" +version = "1.0.0" +source = "git+https://github.com/DA0-DA0/polytone?rev=a94feae#a94feae54ee20cb51d8533d79229296e52164859" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "thiserror", +] + +[[package]] +name = "polytone-note" +version = "1.0.0" +source = "git+https://github.com/DA0-DA0/polytone?rev=a94feae#a94feae54ee20cb51d8533d79229296e52164859" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "polytone 1.0.0 (git+https://github.com/DA0-DA0/polytone?rev=a94feae)", + "thiserror", +] + +[[package]] +name = "polytone-proxy" +version = "1.0.0" +source = "git+https://github.com/DA0-DA0/polytone?rev=a94feae#a94feae54ee20cb51d8533d79229296e52164859" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "polytone 1.0.0 (git+https://github.com/DA0-DA0/polytone?rev=a94feae)", + "thiserror", +] + +[[package]] +name = "polytone-voice" +version = "1.0.0" +source = "git+https://github.com/DA0-DA0/polytone?rev=a94feae#a94feae54ee20cb51d8533d79229296e52164859" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "polytone 1.0.0 (git+https://github.com/DA0-DA0/polytone?rev=a94feae)", + "polytone-proxy", + "sha2 0.10.8", + "thiserror", +] + [[package]] name = "proc-macro2" version = "1.0.81" @@ -1151,6 +2233,88 @@ dependencies = [ "getrandom", ] +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-rustls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -1161,6 +2325,71 @@ dependencies = [ "subtle", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.15" @@ -1173,6 +2402,15 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "schemars" version = "0.8.17" @@ -1197,6 +2435,22 @@ dependencies = [ "syn 2.0.60", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "sec1" version = "0.7.3" @@ -1211,6 +2465,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.22" @@ -1219,9 +2496,9 @@ checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" -version = "1.0.199" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] @@ -1273,9 +2550,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.199" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", @@ -1295,15 +2572,27 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" dependencies = [ "itoa", "ryu", "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha2" version = "0.9.9" @@ -1328,6 +2617,15 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "signature" version = "2.2.0" @@ -1338,6 +2636,21 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + [[package]] name = "snafu" version = "0.6.10" @@ -1352,11 +2665,21 @@ dependencies = [ name = "snafu-derive" version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1508efa03c362e23817f96cde18abed596a25219a8b2c66e8db33c03543d315b" +checksum = "1508efa03c362e23817f96cde18abed596a25219a8b2c66e8db33c03543d315b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "libc", + "windows-sys 0.52.0", ] [[package]] @@ -1369,6 +2692,12 @@ dependencies = [ "strum_macros", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "spki" version = "0.7.3" @@ -1385,6 +2714,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.25.0" @@ -1444,6 +2779,45 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "tendermint-proto" version = "0.23.9" @@ -1462,6 +2836,24 @@ dependencies = [ "time", ] +[[package]] +name = "tendermint-proto" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cec054567d16d85e8c3f6a3139963d1a66d9d3051ed545d31562550e9bcc3d" +dependencies = [ + "bytes", + "flex-error", + "num-derive", + "num-traits", + "prost 0.11.9", + "prost-types 0.11.9", + "serde", + "serde_bytes", + "subtle-encoding", + "time", +] + [[package]] name = "tendermint-proto" version = "0.34.1" @@ -1482,18 +2874,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", @@ -1517,6 +2909,114 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d040ac2b29ab03b09d4129c2f5bbd012a3ac2f79d38ff506a4bf8dd34b0eac8a" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "typenum" version = "1.17.0" @@ -1535,12 +3035,27 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-xid" version = "0.2.4" @@ -1551,12 +3066,12 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" name = "unit-tests" version = "0.1.0" dependencies = [ - "astroport", - "astroport-factory", - "astroport-native-coin-registry", - "astroport-pair", - "astroport-pair-concentrated", - "astroport-pair-stable", + "astroport 2.9.5", + "astroport-factory 1.5.1", + "astroport-native-coin-registry 1.0.1 (git+https://github.com/astroport-fi/astroport-core.git?rev=700f66d)", + "astroport-pair 1.3.3", + "astroport-pair-concentrated 1.2.13", + "astroport-pair-stable 2.1.4", "astroport-token", "astroport-whitelist", "colored", @@ -1564,12 +3079,12 @@ dependencies = [ "cosmos-sdk-proto 0.14.0", "cosmwasm-schema", "cosmwasm-std", - "covenant-utils", + "covenant-utils 0.1.0", "cw-multi-test", "cw-storage-plus 1.2.0", "cw-utils 1.0.3", "cw1-whitelist", - "cw20", + "cw20 0.15.1", "cw20-base", "neutron-sdk", "osmosis-std 0.13.2", @@ -1577,7 +3092,7 @@ dependencies = [ "prost-types 0.11.9", "sha2 0.10.8", "valence-astroport-liquid-pooler", - "valence-clock", + "valence-clock 0.1.0", "valence-covenant-single-party-pol", "valence-covenant-swap", "valence-covenant-two-party-pol", @@ -1594,26 +3109,77 @@ dependencies = [ "valence-two-party-pol-holder", ] +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "valence-astroport-liquid-pooler" version = "0.1.0" dependencies = [ - "astroport", + "astroport 2.9.5", + "bech32", + "cosmwasm-schema", + "cosmwasm-std", + "covenant-macros 0.1.0", + "covenant-utils 0.1.0", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "cw20 0.15.1", + "neutron-sdk", + "schemars", + "serde", + "sha2 0.10.8", + "thiserror", + "valence-clock 0.1.0", +] + +[[package]] +name = "valence-astroport-tf-liquid-pooler" +version = "0.1.0" +dependencies = [ + "astroport 5.0.0", + "astroport-factory 1.8.0", + "astroport-native-coin-registry 1.0.1 (git+https://github.com/astroport-fi/astroport-core.git?tag=v5.0.0)", + "astroport-pair 2.0.0", + "astroport-pair-concentrated 4.0.0", + "astroport-pair-stable 4.0.0", + "astroport-xastro-token", "bech32", "cosmwasm-schema", "cosmwasm-std", - "covenant-macros", - "covenant-utils", + "covenant-macros 0.1.0 (git+https://github.com/timewave-computer/covenants?tag=v0.1.0)", + "covenant-utils 0.1.0 (git+https://github.com/timewave-computer/covenants?tag=v0.1.0)", "cw-storage-plus 1.2.0", "cw-utils 1.0.3", "cw2 1.1.2", - "cw20", + "cw20 0.15.1", "neutron-sdk", "schemars", "serde", "sha2 0.10.8", "thiserror", - "valence-clock", + "valence-clock 0.1.0 (git+https://github.com/timewave-computer/covenants?tag=v0.1.0)", ] [[package]] @@ -1623,8 +3189,8 @@ dependencies = [ "anyhow", "cosmwasm-schema", "cosmwasm-std", - "covenant-macros", - "cw-fifo", + "covenant-macros 0.1.0", + "cw-fifo 0.1.0", "cw-multi-test", "cw-storage-plus 1.2.0", "cw2 1.1.2", @@ -1634,6 +3200,22 @@ dependencies = [ "valence-clock-tester", ] +[[package]] +name = "valence-clock" +version = "0.1.0" +source = "git+https://github.com/timewave-computer/covenants?tag=v0.1.0#e41120b6f0e9fe11934b5274033db21cd7deb271" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "covenant-macros 0.1.0 (git+https://github.com/timewave-computer/covenants?tag=v0.1.0)", + "cw-fifo 0.1.0 (git+https://github.com/timewave-computer/covenants?tag=v0.1.0)", + "cw-storage-plus 1.2.0", + "cw2 1.1.2", + "neutron-sdk", + "serde", + "thiserror", +] + [[package]] name = "valence-clock-tester" version = "0.1.0" @@ -1649,12 +3231,12 @@ dependencies = [ name = "valence-covenant-single-party-pol" version = "0.1.0" dependencies = [ - "astroport", + "astroport 2.9.5", "bech32", "cosmos-sdk-proto 0.14.0", "cosmwasm-schema", "cosmwasm-std", - "covenant-utils", + "covenant-utils 0.1.0", "cw-storage-plus 1.2.0", "cw-utils 1.0.3", "cw2 1.1.2", @@ -1667,7 +3249,7 @@ dependencies = [ "sha2 0.10.8", "thiserror", "valence-astroport-liquid-pooler", - "valence-clock", + "valence-clock 0.1.0", "valence-ibc-forwarder", "valence-interchain-router", "valence-osmo-liquid-pooler", @@ -1684,7 +3266,7 @@ dependencies = [ "cosmos-sdk-proto 0.14.0", "cosmwasm-schema", "cosmwasm-std", - "covenant-utils", + "covenant-utils 0.1.0", "cw-storage-plus 1.2.0", "cw-utils 1.0.3", "cw2 1.1.2", @@ -1696,7 +3278,7 @@ dependencies = [ "serde-json-wasm 0.4.1", "sha2 0.10.8", "thiserror", - "valence-clock", + "valence-clock 0.1.0", "valence-ibc-forwarder", "valence-interchain-router", "valence-native-router", @@ -1708,12 +3290,12 @@ dependencies = [ name = "valence-covenant-two-party-pol" version = "0.1.0" dependencies = [ - "astroport", + "astroport 2.9.5", "bech32", "cosmos-sdk-proto 0.14.0", "cosmwasm-schema", "cosmwasm-std", - "covenant-utils", + "covenant-utils 0.1.0", "cw-storage-plus 1.2.0", "cw-utils 1.0.3", "cw2 1.1.2", @@ -1726,7 +3308,7 @@ dependencies = [ "sha2 0.10.8", "thiserror", "valence-astroport-liquid-pooler", - "valence-clock", + "valence-clock 0.1.0", "valence-ibc-forwarder", "valence-interchain-router", "valence-native-router", @@ -1742,8 +3324,8 @@ dependencies = [ "cosmos-sdk-proto 0.14.0", "cosmwasm-schema", "cosmwasm-std", - "covenant-macros", - "covenant-utils", + "covenant-macros 0.1.0", + "covenant-utils 0.1.0", "cw-storage-plus 1.2.0", "cw-utils 1.0.3", "cw2 1.1.2", @@ -1755,7 +3337,7 @@ dependencies = [ "serde-json-wasm 0.4.1", "sha2 0.10.8", "thiserror", - "valence-clock", + "valence-clock 0.1.0", ] [[package]] @@ -1767,8 +3349,8 @@ dependencies = [ "cosmos-sdk-proto 0.14.0", "cosmwasm-schema", "cosmwasm-std", - "covenant-macros", - "covenant-utils", + "covenant-macros 0.1.0", + "covenant-utils 0.1.0", "cw-multi-test", "cw-storage-plus 1.2.0", "cw-utils 1.0.3", @@ -1780,7 +3362,7 @@ dependencies = [ "serde", "sha2 0.10.8", "thiserror", - "valence-clock", + "valence-clock 0.1.0", ] [[package]] @@ -1791,8 +3373,8 @@ dependencies = [ "cosmos-sdk-proto 0.14.0", "cosmwasm-schema", "cosmwasm-std", - "covenant-macros", - "covenant-utils", + "covenant-macros 0.1.0", + "covenant-utils 0.1.0", "cw-storage-plus 1.2.0", "cw2 1.1.2", "neutron-sdk", @@ -1802,7 +3384,7 @@ dependencies = [ "serde", "sha2 0.10.8", "thiserror", - "valence-clock", + "valence-clock 0.1.0", ] [[package]] @@ -1811,14 +3393,14 @@ version = "0.1.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "covenant-macros", - "covenant-utils", + "covenant-macros 0.1.0", + "covenant-utils 0.1.0", "cw-storage-plus 1.2.0", "cw2 1.1.2", "schemars", "serde", "thiserror", - "valence-clock", + "valence-clock 0.1.0", ] [[package]] @@ -1828,21 +3410,21 @@ dependencies = [ "bech32", "cosmwasm-schema", "cosmwasm-std", - "covenant-macros", - "covenant-utils", + "covenant-macros 0.1.0", + "covenant-utils 0.1.0", "cw-storage-plus 1.2.0", "cw-utils 1.0.3", "cw2 1.1.2", - "cw20", + "cw20 0.15.1", "neutron-sdk", "osmosis-std 0.20.1", - "polytone", + "polytone 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "prost 0.11.9", "schemars", "serde", "sha2 0.10.8", "thiserror", - "valence-clock", + "valence-clock 0.1.0", "valence-outpost-osmo-liquid-pooler", ] @@ -1869,8 +3451,8 @@ dependencies = [ "cosmos-sdk-proto 0.14.0", "cosmwasm-schema", "cosmwasm-std", - "covenant-macros", - "covenant-utils", + "covenant-macros 0.1.0", + "covenant-utils 0.1.0", "cw-storage-plus 1.2.0", "cw-utils 1.0.3", "cw2 1.1.2", @@ -1879,7 +3461,7 @@ dependencies = [ "serde", "serde-json-wasm 0.4.1", "thiserror", - "valence-clock", + "valence-clock 0.1.0", ] [[package]] @@ -1888,8 +3470,8 @@ version = "0.1.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "covenant-macros", - "covenant-utils", + "covenant-macros 0.1.0", + "covenant-utils 0.1.0", "cw-storage-plus 1.2.0", "cw-utils 1.0.3", "cw2 1.1.2", @@ -1904,8 +3486,8 @@ dependencies = [ "cosmos-sdk-proto 0.14.0", "cosmwasm-schema", "cosmwasm-std", - "covenant-macros", - "covenant-utils", + "covenant-macros 0.1.0", + "covenant-utils 0.1.0", "cw-storage-plus 1.2.0", "cw2 1.1.2", "neutron-sdk", @@ -1913,7 +3495,7 @@ dependencies = [ "serde", "serde-json-wasm 0.4.1", "thiserror", - "valence-clock", + "valence-clock 0.1.0", ] [[package]] @@ -1923,48 +3505,145 @@ dependencies = [ "cosmos-sdk-proto 0.14.0", "cosmwasm-schema", "cosmwasm-std", - "covenant-macros", - "covenant-utils", + "covenant-macros 0.1.0", + "covenant-utils 0.1.0", "cw-storage-plus 1.2.0", "cw-utils 1.0.3", "cw2 1.1.2", "neutron-sdk", "serde", "thiserror", - "valence-clock", + "valence-clock 0.1.0", ] [[package]] name = "valence-two-party-pol-holder" version = "0.1.0" dependencies = [ - "astroport", + "astroport 2.9.5", "cosmwasm-schema", "cosmwasm-std", - "covenant-macros", - "covenant-utils", + "covenant-macros 0.1.0", + "covenant-utils 0.1.0", "cw-storage-plus 1.2.0", "cw-utils 1.0.3", "cw2 1.1.2", - "cw20", + "cw20 0.15.1", "schemars", "serde", "thiserror", - "valence-clock", + "valence-clock 0.1.0", ] +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.60", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + [[package]] name = "winapi" version = "0.3.9" @@ -1987,13 +3666,37 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -2002,28 +3705,46 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.5" @@ -2036,30 +3757,64 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "zeroize" version = "1.7.0" diff --git a/Cargo.toml b/Cargo.toml index 4364a85d..df6b4a1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "packages/*", "contracts/*", "unit-tests/", + "local-interchaintest", ] resolver = "2" @@ -43,6 +44,7 @@ valence-interchain-router = { path = "contracts/interchain-router" } valence-two-party-pol-holder = { path = "contracts/two-party-pol-holder" } valence-covenant-two-party-pol = { path = "contracts/two-party-pol-covenant" } valence-astroport-liquid-pooler = { path = "contracts/astroport-liquid-pooler" } +valence-astroport-tf-liquid-pooler = { path = "contracts/astroport-tf-liquid-pooler" } valence-native-router = { path = "contracts/native-router" } valence-osmo-liquid-pooler = { path = "contracts/osmo-liquid-pooler" } valence-outpost-osmo-liquid-pooler = { path = "contracts/outpost-osmo-liquid-pooler" } @@ -96,11 +98,12 @@ astroport = { git = "https://github.com/astroport-fi/astroport-core.git", rev = cw-multi-test = { git = "https://github.com/Art3miX/cw-multi-test", branch = "main", features = ["cosmwasm_1_2"] } anyhow = { version = "1.0.51" } cw1-whitelist = "0.15" +unit-tests = { path = "unit-tests" } + astroport-token = { git = "https://github.com/astroport-fi/astroport-core.git", rev = "700f66d" } astroport-whitelist = { git = "https://github.com/astroport-fi/astroport-core.git", rev = "700f66d" } astroport-factory = { git = "https://github.com/astroport-fi/astroport-core.git", rev = "700f66d" } astroport-native-coin-registry = { git = "https://github.com/astroport-fi/astroport-core.git", rev = "700f66d" } astroport-pair-stable = { git = "https://github.com/astroport-fi/astroport-core.git", rev = "700f66d" } astroport-pair-concentrated = { git = "https://github.com/astroport-fi/astroport-core.git", rev = "700f66d" } -astroport-pair = { git = "https://github.com/astroport-fi/astroport-core.git", rev = "700f66d" } -unit-tests = { path = "unit-tests" } +astroport-pair = { git = "https://github.com/astroport-fi/astroport-core.git", rev = "700f66d" } diff --git a/contracts/astroport-tf-liquid-pooler/.cargo/config b/contracts/astroport-tf-liquid-pooler/.cargo/config new file mode 100644 index 00000000..6a6f2852 --- /dev/null +++ b/contracts/astroport-tf-liquid-pooler/.cargo/config @@ -0,0 +1,3 @@ +[alias] +wasm = "build --release --lib --target wasm32-unknown-unknown" +schema = "run --bin schema" \ No newline at end of file diff --git a/contracts/astroport-tf-liquid-pooler/Cargo.toml b/contracts/astroport-tf-liquid-pooler/Cargo.toml new file mode 100644 index 00000000..87b26db8 --- /dev/null +++ b/contracts/astroport-tf-liquid-pooler/Cargo.toml @@ -0,0 +1,52 @@ +[package] +name = "valence-astroport-tf-liquid-pooler" +authors = ["benskey bekauz@protonmail.com"] +description = "Astroport tokenfactory liquid pooler contract for covenants" +license = { workspace = true } +repository = { workspace = true } +version = { workspace = true } +edition = { workspace = true } +# rust-version = { workspace = true } + +exclude = ["contract.wasm", "hash.txt"] + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +# for more explicit tests, cargo test --features=backtraces +backtraces = ["cosmwasm-std/backtraces"] +# use library feature to disable all instantiate/execute/query exports +library = [] + +[dependencies] +covenant-macros = { git = "https://github.com/timewave-computer/covenants", tag = "v0.1.0" } +valence-clock = { git = "https://github.com/timewave-computer/covenants", tag = "v0.1.0", features = ["library"] } + +cosmwasm-schema = { workspace = true } +cosmwasm-std = { workspace = true } +cw-storage-plus = { workspace = true } +cw-utils = { workspace = true } +cw2 = { workspace = true } +serde = { workspace = true } +thiserror = { workspace = true } +# the sha2 version here is the same as the one used by +# cosmwasm-std. when bumping cosmwasm-std, this should also be +# updated. to find cosmwasm_std's sha function: +# ```cargo tree --package cosmwasm-std``` +sha2 = { workspace = true } +neutron-sdk = { workspace = true } +schemars = { workspace = true } +bech32 = { workspace = true } + + +astroport = { git = "https://github.com/astroport-fi/astroport-core.git", tag = "v5.0.0" } +astroport-xastro-token = { git = "https://github.com/astroport-fi/astroport-core.git", tag = "v5.0.0" } +astroport-factory = { git = "https://github.com/astroport-fi/astroport-core.git", tag = "v5.0.0" } +astroport-native-coin-registry = { git = "https://github.com/astroport-fi/astroport-core.git", tag = "v5.0.0" } +astroport-pair-stable = { git = "https://github.com/astroport-fi/astroport-core.git", tag = "v5.0.0" } +astroport-pair-concentrated = { git = "https://github.com/astroport-fi/astroport-core.git", tag = "v5.0.0" } +astroport-pair = { git = "https://github.com/astroport-fi/astroport-core.git", tag = "v5.0.0" } + +cw20 = { workspace = true } +covenant-utils = { git = "https://github.com/timewave-computer/covenants", tag = "v0.1.0" } diff --git a/contracts/astroport-tf-liquid-pooler/README.md b/contracts/astroport-tf-liquid-pooler/README.md new file mode 100644 index 00000000..f87d7176 --- /dev/null +++ b/contracts/astroport-tf-liquid-pooler/README.md @@ -0,0 +1,29 @@ +# astroport liquid pooler + +Contract responsible for providing liquidity to a specified pool. + +## Instantiation + +The following parameters are expected to instantiate the liquid pooler: + +`pool_address` - address of the liquidity pool we wish to interact with + +`clock_address` - address of the authorized clock contract to receive ticks from + +`slippage_tolerance` - optional parameter to specify the acceptable slippage tolerance for providing liquidity + +`assets` - TODO + +`single_side_lp_limits` - TODO + +`expected_pool_ratio` - the price at which we expect to provide liquidity at + +`acceptable_pool_ratio_delta` - the acceptable deviation from the expected price above + +`pair_type` - the expected pair type of the pool we wish to enter. used for validation of cases where pool migrates. + +## Flow + +After instantiation, liquid pooler continuously attempts to provide liquidity to the specified pool. +If possible, double sided liquidity is provided. If it is not, liquid pooler attempts to provide single-sided liquidity. +If neither are possible, nothing happens until the next tick is received, at which point it retries. diff --git a/contracts/astroport-tf-liquid-pooler/schema/valence-astroport-liquid-pooler.json b/contracts/astroport-tf-liquid-pooler/schema/valence-astroport-liquid-pooler.json new file mode 100644 index 00000000..e1859b3c --- /dev/null +++ b/contracts/astroport-tf-liquid-pooler/schema/valence-astroport-liquid-pooler.json @@ -0,0 +1,792 @@ +{ + "contract_name": "valence-astroport-liquid-pooler", + "contract_version": "1.0.0", + "idl_version": "1.0.0", + "instantiate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "assets", + "clock_address", + "holder_address", + "pair_type", + "pool_address", + "pool_price_config", + "single_side_lp_limits" + ], + "properties": { + "assets": { + "$ref": "#/definitions/AssetData" + }, + "clock_address": { + "type": "string" + }, + "holder_address": { + "type": "string" + }, + "pair_type": { + "$ref": "#/definitions/PairType" + }, + "pool_address": { + "type": "string" + }, + "pool_price_config": { + "$ref": "#/definitions/PoolPriceConfig" + }, + "single_side_lp_limits": { + "$ref": "#/definitions/SingleSideLpLimits" + }, + "slippage_tolerance": { + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "AssetData": { + "description": "holds the both asset denoms relevant for providing liquidity", + "type": "object", + "required": [ + "asset_a_denom", + "asset_b_denom" + ], + "properties": { + "asset_a_denom": { + "type": "string" + }, + "asset_b_denom": { + "type": "string" + } + }, + "additionalProperties": false + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "PairType": { + "description": "This enum describes available pair types. ## Available pool types ``` # use astroport::factory::PairType::{Custom, Stable, Xyk}; Xyk {}; Stable {}; Custom(String::from(\"Custom\")); ```", + "oneOf": [ + { + "description": "XYK pair type", + "type": "object", + "required": [ + "xyk" + ], + "properties": { + "xyk": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Stable pair type", + "type": "object", + "required": [ + "stable" + ], + "properties": { + "stable": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Custom pair type", + "type": "object", + "required": [ + "custom" + ], + "properties": { + "custom": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "PoolPriceConfig": { + "description": "config for the pool price expectations upon covenant instantiation", + "type": "object", + "required": [ + "acceptable_price_spread", + "expected_spot_price" + ], + "properties": { + "acceptable_price_spread": { + "$ref": "#/definitions/Decimal" + }, + "expected_spot_price": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false + }, + "SingleSideLpLimits": { + "description": "single side lp limits define the highest amount (in `Uint128`) that we consider acceptable to provide single-sided. if asset balance exceeds these limits, double-sided liquidity should be provided.", + "type": "object", + "required": [ + "asset_a_limit", + "asset_b_limit" + ], + "properties": { + "asset_a_limit": { + "$ref": "#/definitions/Uint128" + }, + "asset_b_limit": { + "$ref": "#/definitions/Uint128" + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "execute": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "description": "Wakes the state machine up. The caller should check the sender of the tick is the clock if they'd like to pause when the clock does.", + "type": "object", + "required": [ + "tick" + ], + "properties": { + "tick": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Tells the LPer to withdraw his position Should only be called by the holder of the covenant", + "type": "object", + "required": [ + "withdraw" + ], + "properties": { + "withdraw": { + "type": "object", + "properties": { + "percentage": { + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + } + } + }, + "query": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "contract_state" + ], + "properties": { + "contract_state": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "holder_address" + ], + "properties": { + "holder_address": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "lp_config" + ], + "properties": { + "lp_config": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "provided_liquidity_info" + ], + "properties": { + "provided_liquidity_info": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the associated clock address authorized to submit ticks", + "type": "object", + "required": [ + "clock_address" + ], + "properties": { + "clock_address": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the address a contract expects to receive funds to", + "type": "object", + "required": [ + "deposit_address" + ], + "properties": { + "deposit_address": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "migrate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MigrateMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "update_config" + ], + "properties": { + "update_config": { + "type": "object", + "properties": { + "clock_addr": { + "type": [ + "string", + "null" + ] + }, + "holder_address": { + "type": [ + "string", + "null" + ] + }, + "lp_config": { + "anyOf": [ + { + "$ref": "#/definitions/LpConfig" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_code_id" + ], + "properties": { + "update_code_id": { + "type": "object", + "properties": { + "data": { + "anyOf": [ + { + "$ref": "#/definitions/Binary" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "AssetData": { + "description": "holds the both asset denoms relevant for providing liquidity", + "type": "object", + "required": [ + "asset_a_denom", + "asset_b_denom" + ], + "properties": { + "asset_a_denom": { + "type": "string" + }, + "asset_b_denom": { + "type": "string" + } + }, + "additionalProperties": false + }, + "Binary": { + "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", + "type": "string" + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "DecimalRange": { + "type": "object", + "required": [ + "max", + "min" + ], + "properties": { + "max": { + "$ref": "#/definitions/Decimal" + }, + "min": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false + }, + "LpConfig": { + "type": "object", + "required": [ + "asset_data", + "expected_pool_ratio_range", + "pair_type", + "pool_address", + "single_side_lp_limits" + ], + "properties": { + "asset_data": { + "description": "denoms of both parties", + "allOf": [ + { + "$ref": "#/definitions/AssetData" + } + ] + }, + "expected_pool_ratio_range": { + "description": "expected price range", + "allOf": [ + { + "$ref": "#/definitions/DecimalRange" + } + ] + }, + "pair_type": { + "description": "pair type specified in the covenant", + "allOf": [ + { + "$ref": "#/definitions/PairType" + } + ] + }, + "pool_address": { + "description": "address of the liquidity pool we plan to enter", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "single_side_lp_limits": { + "description": "amounts of both tokens we consider ok to single-side lp", + "allOf": [ + { + "$ref": "#/definitions/SingleSideLpLimits" + } + ] + }, + "slippage_tolerance": { + "description": "slippage tolerance parameter for liquidity provisioning", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "PairType": { + "description": "This enum describes available pair types. ## Available pool types ``` # use astroport::factory::PairType::{Custom, Stable, Xyk}; Xyk {}; Stable {}; Custom(String::from(\"Custom\")); ```", + "oneOf": [ + { + "description": "XYK pair type", + "type": "object", + "required": [ + "xyk" + ], + "properties": { + "xyk": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Stable pair type", + "type": "object", + "required": [ + "stable" + ], + "properties": { + "stable": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Custom pair type", + "type": "object", + "required": [ + "custom" + ], + "properties": { + "custom": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "SingleSideLpLimits": { + "description": "single side lp limits define the highest amount (in `Uint128`) that we consider acceptable to provide single-sided. if asset balance exceeds these limits, double-sided liquidity should be provided.", + "type": "object", + "required": [ + "asset_a_limit", + "asset_b_limit" + ], + "properties": { + "asset_a_limit": { + "$ref": "#/definitions/Uint128" + }, + "asset_b_limit": { + "$ref": "#/definitions/Uint128" + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "sudo": null, + "responses": { + "clock_address": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Addr", + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "contract_state": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ContractState", + "description": "state of the LP state machine", + "type": "string", + "enum": [ + "instantiated" + ] + }, + "deposit_address": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Nullable_String", + "type": [ + "string", + "null" + ] + }, + "holder_address": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Addr", + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "lp_config": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "LpConfig", + "type": "object", + "required": [ + "asset_data", + "expected_pool_ratio_range", + "pair_type", + "pool_address", + "single_side_lp_limits" + ], + "properties": { + "asset_data": { + "description": "denoms of both parties", + "allOf": [ + { + "$ref": "#/definitions/AssetData" + } + ] + }, + "expected_pool_ratio_range": { + "description": "expected price range", + "allOf": [ + { + "$ref": "#/definitions/DecimalRange" + } + ] + }, + "pair_type": { + "description": "pair type specified in the covenant", + "allOf": [ + { + "$ref": "#/definitions/PairType" + } + ] + }, + "pool_address": { + "description": "address of the liquidity pool we plan to enter", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "single_side_lp_limits": { + "description": "amounts of both tokens we consider ok to single-side lp", + "allOf": [ + { + "$ref": "#/definitions/SingleSideLpLimits" + } + ] + }, + "slippage_tolerance": { + "description": "slippage tolerance parameter for liquidity provisioning", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "AssetData": { + "description": "holds the both asset denoms relevant for providing liquidity", + "type": "object", + "required": [ + "asset_a_denom", + "asset_b_denom" + ], + "properties": { + "asset_a_denom": { + "type": "string" + }, + "asset_b_denom": { + "type": "string" + } + }, + "additionalProperties": false + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "DecimalRange": { + "type": "object", + "required": [ + "max", + "min" + ], + "properties": { + "max": { + "$ref": "#/definitions/Decimal" + }, + "min": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false + }, + "PairType": { + "description": "This enum describes available pair types. ## Available pool types ``` # use astroport::factory::PairType::{Custom, Stable, Xyk}; Xyk {}; Stable {}; Custom(String::from(\"Custom\")); ```", + "oneOf": [ + { + "description": "XYK pair type", + "type": "object", + "required": [ + "xyk" + ], + "properties": { + "xyk": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Stable pair type", + "type": "object", + "required": [ + "stable" + ], + "properties": { + "stable": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Custom pair type", + "type": "object", + "required": [ + "custom" + ], + "properties": { + "custom": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "SingleSideLpLimits": { + "description": "single side lp limits define the highest amount (in `Uint128`) that we consider acceptable to provide single-sided. if asset balance exceeds these limits, double-sided liquidity should be provided.", + "type": "object", + "required": [ + "asset_a_limit", + "asset_b_limit" + ], + "properties": { + "asset_a_limit": { + "$ref": "#/definitions/Uint128" + }, + "asset_b_limit": { + "$ref": "#/definitions/Uint128" + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "provided_liquidity_info": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ProvidedLiquidityInfo", + "description": "keeps track of provided asset liquidities in `Uint128`.", + "type": "object", + "required": [ + "provided_coin_a", + "provided_coin_b" + ], + "properties": { + "provided_coin_a": { + "$ref": "#/definitions/Coin" + }, + "provided_coin_b": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + } + } +} diff --git a/contracts/astroport-tf-liquid-pooler/src/bin/schema.rs b/contracts/astroport-tf-liquid-pooler/src/bin/schema.rs new file mode 100644 index 00000000..e6d85b40 --- /dev/null +++ b/contracts/astroport-tf-liquid-pooler/src/bin/schema.rs @@ -0,0 +1,11 @@ +use cosmwasm_schema::write_api; +use valence_astroport_tf_liquid_pooler::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + execute: ExecuteMsg, + query: QueryMsg, + migrate: MigrateMsg, + } +} diff --git a/contracts/astroport-tf-liquid-pooler/src/contract.rs b/contracts/astroport-tf-liquid-pooler/src/contract.rs new file mode 100644 index 00000000..290d3d71 --- /dev/null +++ b/contracts/astroport-tf-liquid-pooler/src/contract.rs @@ -0,0 +1,635 @@ +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::{ + coin, coins, ensure, to_json_binary, Binary, Coin, CosmosMsg, Decimal, Deps, DepsMut, Env, + MessageInfo, Reply, Response, StdError, StdResult, SubMsg, SubMsgResult, Uint128, WasmMsg, +}; +use covenant_utils::withdraw_lp_helper::WithdrawLPMsgs; +use cw2::set_contract_version; +use valence_clock::helpers::{enqueue_msg, verify_clock}; + +use astroport::{ + asset::{Asset, PairInfo}, + factory::PairType, + pair::{ + ExecuteMsg::{ProvideLiquidity, WithdrawLiquidity as WithdrawAstroLiquidity}, + PoolResponse, SimulationResponse, + }, + DecimalCheckedOps, +}; + +use crate::{ + error::ContractError, + msg::{ + ContractState, DecimalRange, ExecuteMsg, InstantiateMsg, LpConfig, MigrateMsg, + ProvidedLiquidityInfo, QueryMsg, + }, + state::{HOLDER_ADDRESS, LP_CONFIG, PROVIDED_LIQUIDITY_INFO}, +}; + +use neutron_sdk::NeutronResult; + +use crate::state::{CLOCK_ADDRESS, CONTRACT_STATE}; + +const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); +const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +const DOUBLE_SIDED_REPLY_ID: u64 = 321u64; +const SINGLE_SIDED_REPLY_ID: u64 = 322u64; +const SWAP_REPLY_ID: u64 = 323u64; + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + deps: DepsMut, + _env: Env, + _info: MessageInfo, + msg: InstantiateMsg, +) -> Result { + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + // validate the contract addresses + let clock_addr = deps.api.addr_validate(&msg.clock_address)?; + let pool_addr = deps.api.addr_validate(&msg.pool_address)?; + let holder_addr = deps.api.addr_validate(&msg.holder_address)?; + + // validate that the pool did not migrate to a new pair type + let pool_response: PairInfo = deps + .querier + .query_wasm_smart(pool_addr.to_string(), &astroport::pair::QueryMsg::Pair {})?; + + ensure!( + pool_response.pair_type.eq(&msg.pair_type), + ContractError::PairTypeMismatch {} + ); + + // contract starts at Instantiated state + CONTRACT_STATE.save(deps.storage, &ContractState::Instantiated)?; + + // store the relevant module addresses + CLOCK_ADDRESS.save(deps.storage, &clock_addr)?; + + HOLDER_ADDRESS.save(deps.storage, &holder_addr)?; + + let decimal_range = DecimalRange::try_from( + msg.pool_price_config.expected_spot_price, + msg.pool_price_config.acceptable_price_spread, + )?; + + let lp_config = LpConfig { + pool_address: pool_addr, + single_side_lp_limits: msg.single_side_lp_limits, + slippage_tolerance: msg.slippage_tolerance, + expected_pool_ratio_range: decimal_range, + pair_type: msg.pair_type, + asset_data: msg.assets, + }; + LP_CONFIG.save(deps.storage, &lp_config)?; + + // we begin with no liquidity provided + PROVIDED_LIQUIDITY_INFO.save( + deps.storage, + &ProvidedLiquidityInfo { + provided_coin_a: coin(0, lp_config.asset_data.asset_a_denom.as_str()), + provided_coin_b: coin(0, lp_config.asset_data.asset_b_denom.as_str()), + }, + )?; + + Ok(Response::default() + .add_message(enqueue_msg(clock_addr.as_str())?) + .add_attribute("method", "lp_instantiate") + .add_attribute("clock_addr", clock_addr) + .add_attributes(lp_config.to_response_attributes())) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> Result { + match msg { + ExecuteMsg::Tick {} => try_tick(deps, env, info), + ExecuteMsg::Withdraw { percentage } => try_withdraw(deps, env, info, percentage), + } +} + +fn try_withdraw( + deps: DepsMut, + env: Env, + info: MessageInfo, + percent: Option, +) -> Result { + let percent = percent.unwrap_or(Decimal::one()); + ensure!( + percent > Decimal::zero() && percent <= Decimal::one(), + ContractError::WithdrawPercentageRangeError {} + ); + + let holder_addr = HOLDER_ADDRESS.load(deps.storage)?; + ensure!(info.sender == holder_addr, ContractError::NotHolder {}); + + // Query LP position of the LPer + let lp_config = LP_CONFIG.load(deps.storage)?; + + // first we query the pair info of our configured pool to obtain the tokenfactory denom + let pair_info: PairInfo = deps.querier.query_wasm_smart( + lp_config.pool_address.clone(), + &astroport::pair::QueryMsg::Pair {}, + )?; + + // now we query our own balance of the lp token + let lp_token_bal = deps + .querier + .query_balance(env.contract.address.clone(), pair_info.liquidity_token)?; + + // if no lp tokens are available, we attempt to withdraw any available denoms + if lp_token_bal.amount.is_zero() { + let asset_a_bal = deps.querier.query_balance( + env.contract.address.to_string(), + lp_config.asset_data.asset_a_denom.as_str(), + )?; + let asset_b_bal = deps.querier.query_balance( + env.contract.address.to_string(), + lp_config.asset_data.asset_b_denom.as_str(), + )?; + + let mut funds = vec![]; + + if !asset_a_bal.amount.is_zero() { + funds.push(asset_a_bal); + } + + if !asset_b_bal.amount.is_zero() { + funds.push(asset_b_bal); + } + + ensure!(!funds.is_empty(), ContractError::NothingToWithdraw {}); + + return Ok(Response::default().add_message(WasmMsg::Execute { + contract_addr: holder_addr.to_string(), + msg: to_json_binary(&WithdrawLPMsgs::Distribute {})?, + funds, + })); + } + + // If percentage is 100%, use the whole balance + // If percentage is less than 100%, calculate the percentage of share we want to withdraw + let withdraw_shares_amount = if percent == Decimal::one() { + lp_token_bal.amount + } else { + Decimal::from_atomics(lp_token_bal.amount, 0)? + .checked_mul(percent)? + .to_uint_floor() + }; + + // Calculate the withdrawn amount of A and B tokens from the shares we have + let withdrawn_assets: Vec = deps.querier.query_wasm_smart::>( + lp_config.pool_address.clone(), + &astroport::pair::QueryMsg::Share { + amount: withdraw_shares_amount, + }, + )?; + + // exit pool and withdraw funds with the shares calculated + let withdraw_msg = WithdrawAstroLiquidity { + assets: vec![], + min_assets_to_receive: Some(withdrawn_assets.clone()), + }; + + let wasm_withdraw_msg = WasmMsg::Execute { + contract_addr: lp_config.pool_address.to_string(), + msg: to_json_binary(&withdraw_msg)?, + funds: coins(withdraw_shares_amount.u128(), lp_token_bal.denom.as_str()), + }; + + let withdrawn_coins = withdrawn_assets + .into_iter() + .map(|asset| asset.as_coin()) + .collect::, _>>()?; + + // send message to holder that we finished with the withdrawal + // with the funds we withdrew from the pool + let to_holder_msg = WasmMsg::Execute { + contract_addr: holder_addr.to_string(), + msg: to_json_binary(&WithdrawLPMsgs::Distribute {})?, + funds: withdrawn_coins, + }; + + Ok(Response::default() + .add_message(wasm_withdraw_msg) + .add_message(to_holder_msg)) +} + +/// attempts to advance the state machine. performs `info.sender` validation. +fn try_tick(deps: DepsMut, env: Env, info: MessageInfo) -> Result { + // Verify caller is the clock + verify_clock(&info.sender, &CLOCK_ADDRESS.load(deps.storage)?)?; + + let current_state = CONTRACT_STATE.load(deps.storage)?; + match current_state { + ContractState::Instantiated => try_lp(deps, env), + } +} + +/// method which attempts to provision liquidity to the pool. +/// if both desired asset balances are non-zero, double sided liquidity +/// is provided. +/// otherwise, single-sided liquidity provision is attempted. +fn try_lp(mut deps: DepsMut, env: Env) -> Result { + let lp_config = LP_CONFIG.load(deps.storage)?; + + let pool_response: PoolResponse = deps + .querier + .query_wasm_smart(&lp_config.pool_address, &astroport::pair::QueryMsg::Pool {})?; + + let (pool_token_a_bal, pool_token_b_bal) = get_pool_asset_amounts( + pool_response.assets, + lp_config.asset_data.asset_a_denom.as_str(), + lp_config.asset_data.asset_b_denom.as_str(), + )?; + + // `get_pool_asset_amounts` ensures that both a and b balances are non-zero so this is safe + let a_to_b_ratio = Decimal::from_ratio(pool_token_a_bal, pool_token_b_bal); + + // validate the current pool ratio against our expectations + lp_config + .expected_pool_ratio_range + .is_within_range(a_to_b_ratio)?; + + // first we query our own balances + let coin_a = deps.querier.query_balance( + env.contract.address.to_string(), + lp_config.asset_data.asset_a_denom.as_str(), + )?; + let coin_b = deps.querier.query_balance( + env.contract.address.to_string(), + lp_config.asset_data.asset_b_denom.as_str(), + )?; + let assets = lp_config + .asset_data + .to_asset_vec(coin_a.amount, coin_b.amount); + + // depending on available balances we attempt a different action: + match (coin_a.amount.is_zero(), coin_b.amount.is_zero()) { + // asset_b balance is non-zero, we attempt single-side + (true, false) => { + ensure!( + coin_b.amount <= lp_config.single_side_lp_limits.asset_b_limit, + ContractError::SingleSideLpLimitError {} + ); + + let single_sided_submsgs = + try_get_single_side_lp_submsg(deps.branch(), env, coin_b, assets, lp_config)?; + if !single_sided_submsgs.is_empty() { + return Ok(Response::default() + .add_submessages(single_sided_submsgs) + .add_attribute("method", "single_side_lp")); + } + } + // asset_a balance is non-zero, we attempt single-side + (false, true) => { + ensure!( + coin_a.amount <= lp_config.single_side_lp_limits.asset_a_limit, + ContractError::SingleSideLpLimitError {} + ); + let single_sided_submsgs = + try_get_single_side_lp_submsg(deps.branch(), env, coin_a, assets, lp_config)?; + if !single_sided_submsgs.is_empty() { + return Ok(Response::default() + .add_submessages(single_sided_submsgs) + .add_attribute("method", "single_side_lp")); + } + } + // both balances are non-zero, we attempt double-side + (false, false) => { + let double_sided_submsg = try_get_double_side_lp_submsg( + deps.branch(), + env, + (coin_a, coin_b), + a_to_b_ratio, + (pool_token_a_bal, pool_token_b_bal), + lp_config, + )?; + if let Some(msg) = double_sided_submsg { + return Ok(Response::default() + .add_submessage(msg) + .add_attribute("method", "double_side_lp")); + } + } + // both balances zero, no liquidity can be provisioned + _ => (), + } + + // if no message could be constructed, we keep waiting for funds + Ok(Response::default() + .add_attribute("method", "try_lp") + .add_attribute("status", "not enough funds")) +} + +/// attempts to get a double sided ProvideLiquidity submessage. +/// amounts here do not matter. as long as we have non-zero balances of both +/// a and b tokens, the maximum amount of liquidity is provided to maintain +/// the existing pool ratio. +fn try_get_double_side_lp_submsg( + deps: DepsMut, + env: Env, + (token_a, token_b): (Coin, Coin), + pool_token_ratio: Decimal, + (pool_token_a_bal, pool_token_b_bal): (Uint128, Uint128), + lp_config: LpConfig, +) -> Result, ContractError> { + // we thus find the required token amount to enter into the position using all available b tokens: + let required_token_a_amount = pool_token_ratio.checked_mul_uint128(token_b.amount)?; + + // depending on available balances we determine the highest amount + // of liquidity we can provide: + let (asset_a_double_sided, asset_b_double_sided) = if token_a.amount >= required_token_a_amount + { + // if we are able to satisfy the required amount, we do that: + // provide all b tokens along with required amount of a tokens + lp_config + .asset_data + .to_tuple(required_token_a_amount, token_b.amount) + } else { + // otherwise, our token a amount is insufficient to provide double + // sided liquidity using all of our b tokens. + // this means that we should provide all of our available a tokens, + // and as many b tokens as needed to satisfy the existing ratio + let ratio = Decimal::from_ratio(pool_token_b_bal, pool_token_a_bal); + lp_config + .asset_data + .to_tuple(token_a.amount, ratio.checked_mul_uint128(token_a.amount)?) + }; + + let a_coin = asset_a_double_sided.as_coin()?; + let b_coin = asset_b_double_sided.as_coin()?; + + // craft a ProvideLiquidity message with the determined assets + let double_sided_liq_msg = ProvideLiquidity { + assets: vec![asset_a_double_sided, asset_b_double_sided], + slippage_tolerance: lp_config.slippage_tolerance, + auto_stake: Some(false), + receiver: Some(env.contract.address.to_string()), + min_lp_to_receive: None, + }; + + // update the provided amounts and leftover assets + PROVIDED_LIQUIDITY_INFO.update( + deps.storage, + |mut info: ProvidedLiquidityInfo| -> StdResult<_> { + info.provided_coin_b.amount = info.provided_coin_b.amount.checked_add(b_coin.amount)?; + info.provided_coin_a.amount = info.provided_coin_a.amount.checked_add(a_coin.amount)?; + Ok(info) + }, + )?; + + Ok(Some(SubMsg::reply_on_success( + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: lp_config.pool_address.to_string(), + msg: to_json_binary(&double_sided_liq_msg)?, + funds: vec![a_coin, b_coin], + }), + DOUBLE_SIDED_REPLY_ID, + ))) +} + +/// attempts to build a single sided `ProvideLiquidity` message. +/// pool ratio and single-side limit validations are performed by +/// the calling method. +fn try_get_single_side_lp_submsg( + deps: DepsMut, + env: Env, + coin: Coin, + mut assets: Vec, + lp_config: LpConfig, +) -> Result, ContractError> { + match lp_config.pair_type { + // xyk pools do not allow for automatic single-sided liquidity provision. + // we therefore perform a manual swap with 1/2 of the available denom, and execute + // two-sided lp provision with the resulting assets. + PairType::Xyk {} => { + // we halve the non-zero coin we have in order to swap it for the other denom. + // the halved coin amount here is the floor of the division result, + // so it is safe to assume that after the swap we will have at least + // the same amount of the offer asset left. + let halved_coin = Coin { + denom: coin.denom.clone(), + amount: coin.amount / Uint128::from(2u128), + }; + + let (offer_asset, offer_coin, mut ask_asset) = { + if assets[0].as_coin()?.denom == halved_coin.denom { + assets[0].amount = halved_coin.amount; + (assets[0].clone(), halved_coin, assets[1].clone()) + } else { + assets[1].amount = halved_coin.amount; + (assets[1].clone(), halved_coin, assets[0].clone()) + } + }; + + // we simulate a swap with 1/2 of the offer asset + let simulation: SimulationResponse = deps.querier.query_wasm_smart( + &lp_config.pool_address, + &astroport::pair::QueryMsg::Simulation { + offer_asset: offer_asset.clone(), + ask_asset_info: None, + }, + )?; + ask_asset.amount = simulation.return_amount; + let ask_coin = ask_asset.as_coin()?; + + let swap_wasm_msg: CosmosMsg = WasmMsg::Execute { + contract_addr: lp_config.pool_address.to_string(), + msg: to_json_binary(&astroport::pair::ExecuteMsg::Swap { + offer_asset: offer_asset.clone(), + max_spread: lp_config.slippage_tolerance, + belief_price: None, + to: None, + ask_asset_info: None, + })?, + funds: vec![offer_coin.clone()], + } + .into(); + + PROVIDED_LIQUIDITY_INFO.update(deps.storage, |mut info| -> StdResult<_> { + if offer_coin.denom == info.provided_coin_a.denom { + info.provided_coin_a.amount = + info.provided_coin_a.amount.checked_add(offer_coin.amount)?; + info.provided_coin_b.amount = + info.provided_coin_b.amount.checked_add(ask_coin.amount)?; + } else { + info.provided_coin_b.amount = + info.provided_coin_b.amount.checked_add(offer_coin.amount)?; + info.provided_coin_a.amount = + info.provided_coin_a.amount.checked_add(ask_coin.amount)?; + } + Ok(info) + })?; + + let provide_liquidity_msg: CosmosMsg = WasmMsg::Execute { + contract_addr: lp_config.pool_address.to_string(), + msg: to_json_binary(&ProvideLiquidity { + assets: vec![offer_asset, ask_asset], + slippage_tolerance: lp_config.slippage_tolerance, + auto_stake: Some(false), + receiver: Some(env.contract.address.to_string()), + min_lp_to_receive: None, + })?, + funds: vec![offer_coin, ask_coin], + } + .into(); + let swap_submsg = SubMsg::reply_on_success(swap_wasm_msg, SWAP_REPLY_ID); + let provide_liquidity_submsg = + SubMsg::reply_on_success(provide_liquidity_msg, DOUBLE_SIDED_REPLY_ID); + + Ok(vec![swap_submsg, provide_liquidity_submsg]) + } + PairType::Stable {} | PairType::Custom(_) => { + // given one non-zero asset, we build the ProvideLiquidity message + let single_sided_liq_msg = ProvideLiquidity { + assets, + slippage_tolerance: lp_config.slippage_tolerance, + auto_stake: Some(false), + receiver: Some(env.contract.address.to_string()), + min_lp_to_receive: None, + }; + + // update the provided liquidity info + PROVIDED_LIQUIDITY_INFO.update(deps.storage, |mut info| -> StdResult<_> { + if coin.denom == info.provided_coin_a.denom { + info.provided_coin_a.amount = + info.provided_coin_a.amount.checked_add(coin.amount)?; + } else { + info.provided_coin_b.amount = + info.provided_coin_b.amount.checked_add(coin.amount)?; + } + Ok(info) + })?; + + let submsg = SubMsg::reply_on_success( + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: lp_config.pool_address.to_string(), + msg: to_json_binary(&single_sided_liq_msg)?, + funds: vec![coin], + }), + SINGLE_SIDED_REPLY_ID, + ); + + Ok(vec![submsg]) + } + } +} + +/// filters out irrelevant balances and returns a and b token amounts +fn get_pool_asset_amounts( + assets: Vec, + a_denom: &str, + b_denom: &str, +) -> Result<(Uint128, Uint128), StdError> { + let (mut a_bal, mut b_bal) = (Uint128::zero(), Uint128::zero()); + + for asset in assets { + let coin = asset.as_coin()?; + if coin.denom == b_denom { + // found b balance + b_bal = coin.amount; + } else if coin.denom == a_denom { + // found a token balance + a_bal = coin.amount; + } + } + + if a_bal.is_zero() || b_bal.is_zero() { + return Err(StdError::generic_err("all pool assets must be non-zero")); + } + + Ok((a_bal, b_bal)) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { + match msg { + QueryMsg::ClockAddress {} => Ok(to_json_binary(&CLOCK_ADDRESS.may_load(deps.storage)?)?), + QueryMsg::ContractState {} => Ok(to_json_binary(&CONTRACT_STATE.may_load(deps.storage)?)?), + QueryMsg::HolderAddress {} => Ok(to_json_binary(&HOLDER_ADDRESS.may_load(deps.storage)?)?), + QueryMsg::LpConfig {} => Ok(to_json_binary(&LP_CONFIG.may_load(deps.storage)?)?), + // the deposit address for LP module is the contract itself + QueryMsg::DepositAddress {} => { + Ok(to_json_binary(&Some(&env.contract.address.to_string()))?) + } + QueryMsg::ProvidedLiquidityInfo {} => Ok(to_json_binary( + &PROVIDED_LIQUIDITY_INFO.load(deps.storage)?, + )?), + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> NeutronResult { + match msg { + MigrateMsg::UpdateConfig { + clock_addr, + holder_address, + lp_config, + } => { + let mut response = Response::default().add_attribute("method", "update_config"); + + if let Some(clock_addr) = clock_addr { + CLOCK_ADDRESS.save(deps.storage, &deps.api.addr_validate(&clock_addr)?)?; + response = response.add_attribute("clock_addr", clock_addr); + } + + if let Some(holder_address) = holder_address { + HOLDER_ADDRESS.save(deps.storage, &deps.api.addr_validate(&holder_address)?)?; + response = response.add_attribute("holder_address", holder_address); + } + + if let Some(config) = lp_config { + // validate the address before storing it + deps.api.addr_validate(config.pool_address.as_str())?; + LP_CONFIG.save(deps.storage, &config)?; + response = response.add_attributes(config.to_response_attributes()); + } + + Ok(response) + } + MigrateMsg::UpdateCodeId { data: _ } => { + // This is a migrate message to update code id, + // Data is optional base64 that we can parse to any data we would like in the future + // let data: SomeStruct = from_binary(&data)?; + Ok(Response::default()) + } + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn reply(_deps: DepsMut, _env: Env, msg: Reply) -> Result { + match msg.result { + SubMsgResult::Ok(_) => { + let response = Response::default().add_attribute("reply_id", msg.id.to_string()); + + match msg.id { + DOUBLE_SIDED_REPLY_ID => handle_double_sided_reply_id(response), + SINGLE_SIDED_REPLY_ID => handle_single_sided_reply_id(response), + SWAP_REPLY_ID => handle_swap_reply_id(response), + _ => Err(ContractError::from(StdError::generic_err(format!( + "unknown reply id: {}", + msg.id + )))), + } + } + SubMsgResult::Err(e) => Err(ContractError::from(StdError::generic_err(e))), + } +} + +fn handle_swap_reply_id(response: Response) -> Result { + Ok(response.add_attribute("method", "handle_swap_reply_id")) +} + +fn handle_double_sided_reply_id(response: Response) -> Result { + Ok(response.add_attribute("method", "handle_double_sided_reply_id")) +} + +fn handle_single_sided_reply_id(response: Response) -> Result { + Ok(response.add_attribute("method", "handle_single_sided_reply_id")) +} diff --git a/contracts/astroport-tf-liquid-pooler/src/error.rs b/contracts/astroport-tf-liquid-pooler/src/error.rs new file mode 100644 index 00000000..d002ea46 --- /dev/null +++ b/contracts/astroport-tf-liquid-pooler/src/error.rs @@ -0,0 +1,54 @@ +use cosmwasm_std::{DecimalRangeExceeded, OverflowError, StdError}; +use neutron_sdk::NeutronError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum ContractError { + #[error("{0}")] + Std(#[from] StdError), + + #[error(transparent)] + NeutronError(#[from] NeutronError), + + #[error(transparent)] + OverflowError(#[from] OverflowError), + + #[error(transparent)] + DecimalRangeExceeded(#[from] DecimalRangeExceeded), + + #[error("Not clock")] + ClockVerificationError {}, + + #[error("Single side LP limit exceeded")] + SingleSideLpLimitError {}, + + #[error("Non zero balances for single side liquidity")] + SingleSideLpNonZeroBalanceError {}, + + #[error("Zero balance for double side liquidity")] + DoubleSideLpZeroBalanceError {}, + + #[error("Insufficient funds for double sided LP")] + DoubleSideLpLimitError {}, + + #[error("Incomplete pool assets")] + IncompletePoolAssets {}, + + #[error("Pool validation error")] + PoolValidationError {}, + + #[error("Price range error")] + PriceRangeError {}, + + #[error("Pair type mismatch")] + PairTypeMismatch {}, + + #[error("Only holder can withdraw the position")] + NotHolder {}, + + #[error("no covenant denom or lp tokens available")] + NothingToWithdraw {}, + + #[error("Withdraw percentage range must belong to range (0.0, 1.0]")] + WithdrawPercentageRangeError {}, +} diff --git a/contracts/astroport-tf-liquid-pooler/src/lib.rs b/contracts/astroport-tf-liquid-pooler/src/lib.rs new file mode 100644 index 00000000..0faea8f4 --- /dev/null +++ b/contracts/astroport-tf-liquid-pooler/src/lib.rs @@ -0,0 +1,8 @@ +#![warn(clippy::unwrap_used, clippy::expect_used)] + +extern crate core; + +pub mod contract; +pub mod error; +pub mod msg; +pub mod state; diff --git a/contracts/astroport-tf-liquid-pooler/src/msg.rs b/contracts/astroport-tf-liquid-pooler/src/msg.rs new file mode 100644 index 00000000..5af6c8a7 --- /dev/null +++ b/contracts/astroport-tf-liquid-pooler/src/msg.rs @@ -0,0 +1,229 @@ +use astroport::{ + asset::{Asset, AssetInfo}, + factory::PairType, +}; +use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::{ + ensure, to_json_binary, Addr, Attribute, Binary, Coin, Decimal, StdResult, Uint128, WasmMsg, +}; +use covenant_macros::{ + clocked, covenant_clock_address, covenant_deposit_address, covenant_lper_withdraw, +}; +use covenant_utils::{ + instantiate2_helper::Instantiate2HelperConfig, PoolPriceConfig, SingleSideLpLimits, +}; + +use crate::error::ContractError; + +#[cw_serde] +pub struct InstantiateMsg { + pub pool_address: String, + pub clock_address: String, + pub slippage_tolerance: Option, + pub assets: AssetData, + pub single_side_lp_limits: SingleSideLpLimits, + pub pool_price_config: PoolPriceConfig, + pub pair_type: PairType, + pub holder_address: String, +} + +impl InstantiateMsg { + pub fn to_instantiate2_msg( + &self, + instantiate2_helper: &Instantiate2HelperConfig, + admin: String, + label: String, + ) -> StdResult { + Ok(WasmMsg::Instantiate2 { + admin: Some(admin), + code_id: instantiate2_helper.code, + label, + msg: to_json_binary(self)?, + funds: vec![], + salt: instantiate2_helper.salt.clone(), + }) + } +} + +#[cw_serde] +pub struct AstroportLiquidPoolerConfig { + pub pool_pair_type: PairType, + pub pool_address: String, + pub asset_a_denom: String, + pub asset_b_denom: String, + pub single_side_lp_limits: SingleSideLpLimits, +} + +impl AstroportLiquidPoolerConfig { + pub fn to_instantiate_msg( + &self, + clock_address: String, + holder_address: String, + pool_price_config: PoolPriceConfig, + ) -> InstantiateMsg { + InstantiateMsg { + pool_address: self.pool_address.to_string(), + clock_address, + single_side_lp_limits: self.single_side_lp_limits.clone(), + pool_price_config, + pair_type: self.pool_pair_type.clone(), + holder_address, + slippage_tolerance: None, + assets: AssetData { + asset_a_denom: self.asset_a_denom.to_string(), + asset_b_denom: self.asset_b_denom.to_string(), + }, + } + } +} + +#[cw_serde] +pub struct DecimalRange { + min: Decimal, + max: Decimal, +} + +impl DecimalRange { + pub fn try_from(mid: Decimal, delta: Decimal) -> Result { + Ok(DecimalRange { + min: mid.checked_sub(delta)?, + max: mid.checked_add(delta)?, + }) + } + + pub fn is_within_range(&self, value: Decimal) -> Result<(), ContractError> { + ensure!( + value >= self.min && value <= self.max, + ContractError::PriceRangeError {} + ); + Ok(()) + } +} + +#[cw_serde] +pub struct LpConfig { + /// address of the liquidity pool we plan to enter + pub pool_address: Addr, + /// denoms of both parties + pub asset_data: AssetData, + /// amounts of both tokens we consider ok to single-side lp + pub single_side_lp_limits: SingleSideLpLimits, + /// slippage tolerance parameter for liquidity provisioning + pub slippage_tolerance: Option, + /// expected price range + pub expected_pool_ratio_range: DecimalRange, + /// pair type specified in the covenant + pub pair_type: PairType, +} + +impl LpConfig { + pub fn to_response_attributes(self) -> Vec { + let slippage_tolerance = match self.slippage_tolerance { + Some(val) => val.to_string(), + None => "None".to_string(), + }; + vec![ + Attribute::new("pool_address", self.pool_address.to_string()), + Attribute::new( + "single_side_asset_a_limit", + self.single_side_lp_limits.asset_a_limit.to_string(), + ), + Attribute::new( + "single_side_asset_b_limit", + self.single_side_lp_limits.asset_b_limit.to_string(), + ), + Attribute::new("slippage_tolerance", slippage_tolerance), + Attribute::new("party_a_denom", self.asset_data.asset_a_denom), + Attribute::new("party_b_denom", self.asset_data.asset_b_denom), + ] + } +} + +/// holds the both asset denoms relevant for providing liquidity +#[cw_serde] +pub struct AssetData { + pub asset_a_denom: String, + pub asset_b_denom: String, +} + +impl AssetData { + pub fn to_asset_vec(&self, a_bal: Uint128, b_bal: Uint128) -> Vec { + vec![ + Asset { + info: AssetInfo::NativeToken { + denom: self.asset_a_denom.to_string(), + }, + amount: a_bal, + }, + Asset { + info: AssetInfo::NativeToken { + denom: self.asset_b_denom.to_string(), + }, + amount: b_bal, + }, + ] + } + + /// returns tuple of (asset_A, asset_B) + pub fn to_tuple(&self, a_bal: Uint128, b_bal: Uint128) -> (Asset, Asset) { + ( + Asset { + info: AssetInfo::NativeToken { + denom: self.asset_a_denom.to_string(), + }, + amount: a_bal, + }, + Asset { + info: AssetInfo::NativeToken { + denom: self.asset_b_denom.to_string(), + }, + amount: b_bal, + }, + ) + } +} + +#[clocked] +#[covenant_lper_withdraw] +#[cw_serde] +pub enum ExecuteMsg {} + +#[covenant_clock_address] +#[covenant_deposit_address] +#[cw_serde] +#[derive(QueryResponses)] +pub enum QueryMsg { + #[returns(ContractState)] + ContractState {}, + #[returns(Addr)] + HolderAddress {}, + #[returns(LpConfig)] + LpConfig {}, + #[returns(ProvidedLiquidityInfo)] + ProvidedLiquidityInfo {}, +} + +#[cw_serde] +pub enum MigrateMsg { + UpdateConfig { + clock_addr: Option, + holder_address: Option, + lp_config: Option>, + }, + UpdateCodeId { + data: Option, + }, +} + +/// keeps track of provided asset liquidities in `Uint128`. +#[cw_serde] +pub struct ProvidedLiquidityInfo { + pub provided_coin_a: Coin, + pub provided_coin_b: Coin, +} + +/// state of the LP state machine +#[cw_serde] +pub enum ContractState { + Instantiated, +} diff --git a/contracts/astroport-tf-liquid-pooler/src/state.rs b/contracts/astroport-tf-liquid-pooler/src/state.rs new file mode 100644 index 00000000..0952fde5 --- /dev/null +++ b/contracts/astroport-tf-liquid-pooler/src/state.rs @@ -0,0 +1,19 @@ +use cosmwasm_std::Addr; +use cw_storage_plus::Item; + +use crate::msg::{ContractState, LpConfig, ProvidedLiquidityInfo}; + +/// contract state tracks the state machine progress +pub const CONTRACT_STATE: Item = Item::new("contract_state"); + +/// clock module address to verify the incoming ticks sender +pub const CLOCK_ADDRESS: Item = Item::new("clock_address"); +/// holder module address to verify withdrawal requests +pub const HOLDER_ADDRESS: Item = Item::new("holder_address"); + +/// keeps track of both token amounts we provided to the pool +pub const PROVIDED_LIQUIDITY_INFO: Item = + Item::new("provided_liquidity_info"); + +/// configuration relevant to entering into an LP position +pub const LP_CONFIG: Item = Item::new("lp_config"); diff --git a/contracts/stride-liquid-staker/src/contract.rs b/contracts/stride-liquid-staker/src/contract.rs index 5c6f7ba1..eb37de8d 100644 --- a/contracts/stride-liquid-staker/src/contract.rs +++ b/contracts/stride-liquid-staker/src/contract.rs @@ -235,19 +235,7 @@ pub fn query(deps: QueryDeps, env: Env, msg: QueryMsg) -> NeutronResult )? .0; - let autopilot = Autopilot { - autopilot: AutopilotConfig { - receiver: ica.to_string(), - stakeibc: crate::helpers::Stakeibc { - action: "LiquidStake".to_string(), - stride_address: ica, - }, - }, - }; - - let autopilot_str = to_json_string(&autopilot)?; - - Ok(to_json_binary(&autopilot_str)?) + Ok(to_json_binary(&ica)?) } QueryMsg::RemoteChainInfo {} => { Ok(to_json_binary(&REMOTE_CHAIN_INFO.may_load(deps.storage)?)?) diff --git a/local-interchaintest/Cargo.toml b/local-interchaintest/Cargo.toml new file mode 100644 index 00000000..1fe6c529 --- /dev/null +++ b/local-interchaintest/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "local-ictest-e2e" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +localic-std = { git = "https://github.com/strangelove-ventures/interchaintest", branch = "main" } +reqwest = { version = "0.11.27", features = ["blocking", "json"] } +serde_json = "1.0.119" +serde = { version = "1.0.203", features = ["derive"] } +cosmwasm-std = "1.5.5" +astroport = { git = "https://github.com/astroport-fi/astroport-core.git", tag = "v5.0.0" } +astroport-old = { package = "astroport", git = "https://github.com/astroport-fi/astroport-core.git", rev = "700f66d" } +localic-utils = { git = "https://github.com/timewave-computer/localic-utils", branch = "main" } +sha2 = "0.10.8" +anyhow = "1.0.86" +env_logger = "0.11.3" +log = "0.4.22" +polytone-note = { git = "https://github.com/DA0-DA0/polytone", rev = "a94feae" } +polytone-voice = { git = "https://github.com/DA0-DA0/polytone", rev = "a94feae" } +cw-utils = { workspace = true } +valence-covenant-two-party-pol = { workspace = true } +valence-covenant-single-party-pol = { workspace = true } +valence-covenant-swap = { workspace = true } +valence-two-party-pol-holder = { workspace = true } +valence-single-party-pol-holder = { workspace = true } +valence-astroport-liquid-pooler = { workspace = true } +valence-osmo-liquid-pooler = { workspace = true } +valence-outpost-osmo-liquid-pooler = { workspace = true } +valence-stride-liquid-staker = { workspace = true } +valence-ibc-forwarder = { workspace = true } +valence-astroport-tf-liquid-pooler = { workspace = true } + +covenant-utils = { workspace = true } +cw20 = { workspace = true } diff --git a/local-interchaintest/Makefile b/local-interchaintest/Makefile new file mode 100644 index 00000000..8847c247 --- /dev/null +++ b/local-interchaintest/Makefile @@ -0,0 +1,19 @@ +#!/usr/bin/make -f + +CWD := $(dir $(abspath $(firstword $(MAKEFILE_LIST)))) + +ldflags = -X main.MakeFileInstallDirectory=$(CWD) +ldflags := $(strip $(ldflags)) +BUILD_FLAGS := -ldflags '$(ldflags)' + +.PHONY: build +build: + go build $(BUILD_FLAGS) -o ../bin/local-ic ./cmd/local-ic + +.PHONY: run +run: + go run ./cmd/local-ic $(filter-out $@,$(MAKECMDGOALS)) + +.PHONY: install +install: + go install $(BUILD_FLAGS) ./cmd/local-ic ./interchain diff --git a/local-interchaintest/README.md b/local-interchaintest/README.md new file mode 100644 index 00000000..087ff2ce --- /dev/null +++ b/local-interchaintest/README.md @@ -0,0 +1,57 @@ +# local interchaintest + +## setup + +### non-ics stride image setup + +Prior to running the interchaintests, a modification of the stride image is needed. +We are using the [v9.2.1 tagged version](https://github.com/Stride-Labs/stride/tree/v9.2.1) image. + +In there, we alter the `utils/admins.go` as follows to allow minting tokens from our address in the tests: + +```go +var Admins = map[string]bool{ +- "stride1k8c2m5cn322akk5wy8lpt87dd2f4yh9azg7jlh": true, // F5 ++ "stride1u20df3trc2c2zdhm8qvh2hdjx9ewh00sv6eyy8": true, // F5 + "stride10d07y265gmmuvt4z0w9aw880jnsr700jefnezl": true, // gov module +} +``` + +Then we use heighliner by strangelove to build a local docker image, [as described in their documentation](https://github.com/strangelove-ventures/heighliner#example-cosmos-sdk-chain-development-cycle-build-a-local-repository): + +```bash +# in the stride directory +heighliner build -c stride --local -t non-ics +``` + +### install interchaintest + +```bash +git clone --depth 1 --branch v8.3.0 https://github.com/strangelove-ventures/interchaintest; cd interchaintest; git switch -c v8.3.0 +``` + +```bash +cd local-interchain +``` + +```bash +# NOTE: your binary will link back to this location of where you install. +# If you rename the folder or move it, you need to `make install` the binary again. +make install +``` + +### spinning up the env + +```bash +local-ic start neutron_gaia_osmosis_stride --api-port 42069 +``` + +> note: you may need to specify the ICTEST_HOME path here + +### running tests + +From the covenant root directory, run the following command: + +```bash +cargo run --package local-ictest-e2e --bin local-ictest-e2e +``` diff --git a/local-interchaintest/chains/neutron_gaia_osmosis_stride.json b/local-interchaintest/chains/neutron_gaia_osmosis_stride.json new file mode 100644 index 00000000..7bdb6bce --- /dev/null +++ b/local-interchaintest/chains/neutron_gaia_osmosis_stride.json @@ -0,0 +1,322 @@ +{ + "chains": [ + { + "name": "gaia", + "chain_id": "localcosmos-1", + "denom": "uatom", + "binary": "gaiad", + "bech32_prefix": "cosmos", + "docker_image": { + "version": "v15.0.0-rc2" + }, + "gas_prices": "0%DENOM%", + "chain_type": "cosmos", + "coin_type": 118, + "trusting_period": "336h", + "gas_adjustment": 2.0, + "number_vals": 1, + "number_node": 0, + "ibc_paths": ["gaia-stride", "osmosis-gaia"], + "debugging": true, + "block_time": "1s", + "genesis": { + "modify": [ + { + "key": "app_state.gov.params.voting_period", + "value": "3s" + }, + { + "key": "app_state.interchainaccounts.host_genesis_state.params.allow_messages", + "value": [ + "/cosmos.bank.v1beta1.MsgSend", + "/cosmos.bank.v1beta1.MsgMultiSend", + "/cosmos.staking.v1beta1.MsgDelegate", + "/cosmos.staking.v1beta1.MsgUndelegate", + "/cosmos.staking.v1beta1.MsgBeginRedelegate", + "/cosmos.staking.v1beta1.MsgRedeemTokensforShares", + "/cosmos.staking.v1beta1.MsgTokenizeShares", + "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward", + "/cosmos.distribution.v1beta1.MsgSetWithdrawAddress", + "/ibc.applications.transfer.v1.MsgTransfer" + ] + } + ], + "accounts": [ + { + "name": "acc0", + "address": "cosmos1hj5fveer5cjtn4wd6wstzugjfdxzl0xpxvjjvr", + "amount": "20000000000000%DENOM%", + "mnemonic": "decorate bright ozone fork gallery riot bus exhaust worth way bone indoor calm squirrel merry zero scheme cotton until shop any excess stage laundry" + }, + { + "name": "acc1", + "address": "cosmos1kljf09rj77uxeu5lye7muejx6ajsu55cc3re5h", + "amount": "20000000000000%DENOM%", + "mnemonic": "across army acoustic hurt help sad turkey switch popular fade purse obvious session tuition file asset cover agree number motor pupil slim hundred busy" + }, + { + "name": "acc2", + "address": "cosmos17lp3n649rxt2jadn455frcj0q6anjndsw0xwrz", + "amount": "20000000000000%DENOM%", + "mnemonic": "demise erode feature decade dune uncle limb stock quit nation neck marriage pledge achieve tell cat baby wrist expect scrub welcome hole ribbon mirror" + }, + { + "name": "acc3", + "address": "cosmos1p0var04vhr03r2j8zwv4jfrz73rxgjt5v29x49", + "amount": "20000000000000%DENOM%", + "mnemonic": "scheme force walk answer decide submit crowd flush slim raw type tackle lend follow multiply sting rule jealous coyote slight toddler skirt crawl decade" + } + ] + } + }, + { + "name": "neutron", + "chain_id": "localneutron-1", + "denom": "untrn", + "binary": "neutrond", + "bech32_prefix": "neutron", + "docker_image": { + "version": "v3.0.4", + "repository": "ghcr.io/strangelove-ventures/heighliner/neutron" + }, + "gas_prices": "0.0untrn,0.0uatom", + "chain_type": "cosmos", + "coin_type": 118, + "trusting_period": "336h", + "gas_adjustment": 1.3, + "number_vals": 1, + "number_node": 0, + "ics_consumer_link": "localcosmos-1", + "ibc_paths": ["neutron-stride", "neutron-osmosis"], + "debugging": true, + "block_time": "1s", + "genesis": { + "modify": [ + { + "key": "consensus_params.block.max_gas", + "value": "100000000" + }, + { + "key": "app_state.ccvconsumer.params.soft_opt_out_threshold", + "value": "0.05" + }, + { + "key": "app_state.ccvconsumer.params.reward_denoms", + "value": ["untrn"] + }, + { + "key": "app_state.ccvconsumer.params.provider_reward_denoms", + "value": ["uatom"] + }, + { + "key": "consensus_params.block.max_gas", + "value": "1000000000" + }, + { + "key": "app_state.globalfee.params.minimum_gas_prices", + "value": [ + { + "denom": "untrn", + "amount": "0" + } + ] + }, + { + "key": "app_state.feeburner.params.treasury_address", + "value": "neutron1hj5fveer5cjtn4wd6wstzugjfdxzl0xpznmsky" + }, + { + "key": "app_state.tokenfactory.params.fee_collector_address", + "value": "neutron1hj5fveer5cjtn4wd6wstzugjfdxzl0xpznmsky" + }, + { + "key": "app_state.interchainaccounts.host_genesis_state.params.allow_messages", + "value": [ + "/cosmos.bank.v1beta1.MsgSend", + "/cosmos.bank.v1beta1.MsgMultiSend", + "/cosmos.staking.v1beta1.MsgDelegate", + "/cosmos.staking.v1beta1.MsgUndelegate", + "/cosmos.staking.v1beta1.MsgBeginRedelegate", + "/cosmos.staking.v1beta1.MsgRedeemTokensforShares", + "/cosmos.staking.v1beta1.MsgTokenizeShares", + "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward", + "/cosmos.distribution.v1beta1.MsgSetWithdrawAddress", + "/ibc.applications.transfer.v1.MsgTransfer", + "/ibc.lightclients.localhost.v2.ClientState", + "/ibc.core.client.v1.MsgCreateClient", + "/ibc.core.client.v1.Query/ClientState", + "/ibc.core.client.v1.Query/ConsensusState", + "/ibc.core.connection.v1.Query/Connection" + ] + } + ], + "accounts": [ + { + "name": "acc0", + "address": "neutron1hj5fveer5cjtn4wd6wstzugjfdxzl0xpznmsky", + "amount": "10000000000000%DENOM%", + "mnemonic": "decorate bright ozone fork gallery riot bus exhaust worth way bone indoor calm squirrel merry zero scheme cotton until shop any excess stage laundry" + }, + { + "name": "acc1", + "address": "neutron1kljf09rj77uxeu5lye7muejx6ajsu55cuw2mws", + "amount": "10000000000000%DENOM%", + "mnemonic": "across army acoustic hurt help sad turkey switch popular fade purse obvious session tuition file asset cover agree number motor pupil slim hundred busy" + }, + { + "name": "acc2", + "address": "neutron17lp3n649rxt2jadn455frcj0q6anjnds2s0ve9", + "amount": "10000000000000%DENOM%", + "mnemonic": "demise erode feature decade dune uncle limb stock quit nation neck marriage pledge achieve tell cat baby wrist expect scrub welcome hole ribbon mirror" + }, + { + "name": "acc3", + "address": "neutron1p0var04vhr03r2j8zwv4jfrz73rxgjt5g4vy0z", + "amount": "10000000000000%DENOM%", + "mnemonic": "scheme force walk answer decide submit crowd flush slim raw type tackle lend follow multiply sting rule jealous coyote slight toddler skirt crawl decade" + } + ] + } + }, + { + "name": "stride", + "chain_id": "localstride-1", + "denom": "ustrd", + "binary": "strided", + "bech32_prefix": "stride", + "docker_image": { + "version": "non-ics", + "repository": "stride" + }, + "gas_prices": "0.0ustrd", + "chain_type": "cosmos", + "coin_type": 118, + "trusting_period": "336h", + "gas_adjustment": 1.3, + "number_vals": 1, + "number_node": 0, + "ibc_paths": ["gaia-stride", "neutron-stride"], + "debugging": true, + "block_time": "1s", + "genesis": { + "modify": [ + { + "key": "consensus_params.block.max_gas", + "value": "100000000" + }, + { + "key": "app_state.autopilot.params.stakeibc_active", + "value": true + }, + { + "key": "app_state.interchainaccounts.host_genesis_state.params.allow_messages", + "value": [ + "/cosmos.bank.v1beta1.MsgSend", + "/cosmos.bank.v1beta1.MsgMultiSend", + "/cosmos.staking.v1beta1.MsgDelegate", + "/cosmos.staking.v1beta1.MsgUndelegate", + "/cosmos.staking.v1beta1.MsgBeginRedelegate", + "/cosmos.staking.v1beta1.MsgRedeemTokensforShares", + "/cosmos.staking.v1beta1.MsgTokenizeShares", + "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward", + "/cosmos.distribution.v1beta1.MsgSetWithdrawAddress", + "/ibc.applications.transfer.v1.MsgTransfer" + ] + } + ], + "accounts": [ + { + "name": "admin", + "address": "stride1u20df3trc2c2zdhm8qvh2hdjx9ewh00sv6eyy8", + "amount": "10000000000%DENOM%", + "mnemonic": "tone cause tribe this switch near host damage idle fragile antique tail soda alien depth write wool they rapid unfold body scan pledge soft" + }, + { + "name": "acc0", + "address": "stride1hj5fveer5cjtn4wd6wstzugjfdxzl0xp98jwc0", + "amount": "10000000000%DENOM%", + "mnemonic": "decorate bright ozone fork gallery riot bus exhaust worth way bone indoor calm squirrel merry zero scheme cotton until shop any excess stage laundry" + } + ] + } + }, + { + "name": "osmosis", + "chain_id": "localosmosis-1", + "denom": "uosmo", + "binary": "osmosisd", + "bech32_prefix": "osmo", + "docker_image": { + "version": "v21.0.0", + "repository": "ghcr.io/strangelove-ventures/heighliner/osmosis" + }, + "chain_type": "cosmos", + "coin_type": 118, + "trusting_period": "336h", + "gas_adjustment": 3.0, + "number_vals": 1, + "number_node": 0, + "ibc_paths": ["neutron-osmosis", "osmosis-gaia"], + "debugging": true, + "block_time": "1s", + "genesis": { + "modify": [ + { + "key": "app_state.gov.params.voting_period", + "value": "3s" + }, + { + "key": "app_state.gov.params.max_deposit_period", + "value": "15s" + }, + { + "key": "app_state.gov.params.min_deposit.0.denom", + "value": "uosmo" + }, + { + "key": "app_state.interchainaccounts.host_genesis_state.params.allow_messages", + "value": [ + "/cosmos.bank.v1beta1.MsgSend", + "/cosmos.bank.v1beta1.MsgMultiSend", + "/cosmos.staking.v1beta1.MsgDelegate", + "/cosmos.staking.v1beta1.MsgUndelegate", + "/cosmos.staking.v1beta1.MsgBeginRedelegate", + "/cosmos.staking.v1beta1.MsgRedeemTokensforShares", + "/cosmos.staking.v1beta1.MsgTokenizeShares", + "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward", + "/cosmos.distribution.v1beta1.MsgSetWithdrawAddress", + "/ibc.applications.transfer.v1.MsgTransfer", + "/ibc.applications.interchain_accounts.v1.InterchainAccount" + ] + } + ], + "accounts": [ + { + "name": "acc0", + "address": "osmo1hj5fveer5cjtn4wd6wstzugjfdxzl0xpwhpz63", + "amount": "10000000000000%DENOM%", + "mnemonic": "decorate bright ozone fork gallery riot bus exhaust worth way bone indoor calm squirrel merry zero scheme cotton until shop any excess stage laundry" + }, + { + "name": "acc1", + "address": "osmo1kljf09rj77uxeu5lye7muejx6ajsu55cs2sfz9", + "amount": "10000000000000%DENOM%", + "mnemonic": "across army acoustic hurt help sad turkey switch popular fade purse obvious session tuition file asset cover agree number motor pupil slim hundred busy" + }, + { + "name": "acc2", + "address": "osmo17lp3n649rxt2jadn455frcj0q6anjndsx5474s", + "amount": "10000000000000%DENOM%", + "mnemonic": "demise erode feature decade dune uncle limb stock quit nation neck marriage pledge achieve tell cat baby wrist expect scrub welcome hole ribbon mirror" + }, + { + "name": "acc3", + "address": "osmo1p0var04vhr03r2j8zwv4jfrz73rxgjt5y3kkrh", + "amount": "10000000000000%DENOM%", + "mnemonic": "scheme force walk answer decide submit crowd flush slim raw type tackle lend follow multiply sting rule jealous coyote slight toddler skirt crawl decade" + } + ] + } + } + ] +} diff --git a/local-interchaintest/configs/relayer.json b/local-interchaintest/configs/relayer.json new file mode 100644 index 00000000..9fcad214 --- /dev/null +++ b/local-interchaintest/configs/relayer.json @@ -0,0 +1,18 @@ +{ + "relayer": { + "docker_image": { + "repository": "ghcr.io/cosmos/relayer", + "version": "v2.4.0", + "uid_gid": "1000:1000" + }, + "startup_flags": [ + "-p", + "events", + "--block-history", + "100", + "-d", + "--log-format", + "console" + ] + } +} diff --git a/local-interchaintest/configs/server.json b/local-interchaintest/configs/server.json new file mode 100644 index 00000000..e864662c --- /dev/null +++ b/local-interchaintest/configs/server.json @@ -0,0 +1,6 @@ +{ + "server": { + "host": "127.0.0.1", + "port": "8080" + } +} diff --git a/local-interchaintest/contracts/astroport/astroport_factory.wasm b/local-interchaintest/contracts/astroport/astroport_factory.wasm new file mode 100644 index 00000000..be59e6b4 Binary files /dev/null and b/local-interchaintest/contracts/astroport/astroport_factory.wasm differ diff --git a/local-interchaintest/contracts/astroport/astroport_native_coin_registry.wasm b/local-interchaintest/contracts/astroport/astroport_native_coin_registry.wasm new file mode 100644 index 00000000..c80589f1 Binary files /dev/null and b/local-interchaintest/contracts/astroport/astroport_native_coin_registry.wasm differ diff --git a/local-interchaintest/contracts/astroport/astroport_pair.wasm b/local-interchaintest/contracts/astroport/astroport_pair.wasm new file mode 100644 index 00000000..3d284c73 Binary files /dev/null and b/local-interchaintest/contracts/astroport/astroport_pair.wasm differ diff --git a/local-interchaintest/contracts/astroport/astroport_pair_stable.wasm b/local-interchaintest/contracts/astroport/astroport_pair_stable.wasm new file mode 100644 index 00000000..dd12e7e0 Binary files /dev/null and b/local-interchaintest/contracts/astroport/astroport_pair_stable.wasm differ diff --git a/local-interchaintest/contracts/astroport/astroport_token.wasm b/local-interchaintest/contracts/astroport/astroport_token.wasm new file mode 100644 index 00000000..6663163a Binary files /dev/null and b/local-interchaintest/contracts/astroport/astroport_token.wasm differ diff --git a/local-interchaintest/contracts/astroport/astroport_whitelist.wasm b/local-interchaintest/contracts/astroport/astroport_whitelist.wasm new file mode 100644 index 00000000..d1415643 Binary files /dev/null and b/local-interchaintest/contracts/astroport/astroport_whitelist.wasm differ diff --git a/local-interchaintest/contracts/polytone/polytone_listener.wasm b/local-interchaintest/contracts/polytone/polytone_listener.wasm new file mode 100644 index 00000000..04c451bb Binary files /dev/null and b/local-interchaintest/contracts/polytone/polytone_listener.wasm differ diff --git a/local-interchaintest/contracts/polytone/polytone_note.wasm b/local-interchaintest/contracts/polytone/polytone_note.wasm new file mode 100644 index 00000000..7b855e1a Binary files /dev/null and b/local-interchaintest/contracts/polytone/polytone_note.wasm differ diff --git a/local-interchaintest/contracts/polytone/polytone_proxy.wasm b/local-interchaintest/contracts/polytone/polytone_proxy.wasm new file mode 100644 index 00000000..d033f0bd Binary files /dev/null and b/local-interchaintest/contracts/polytone/polytone_proxy.wasm differ diff --git a/local-interchaintest/contracts/polytone/polytone_tester.wasm b/local-interchaintest/contracts/polytone/polytone_tester.wasm new file mode 100644 index 00000000..14d2d39e Binary files /dev/null and b/local-interchaintest/contracts/polytone/polytone_tester.wasm differ diff --git a/local-interchaintest/contracts/polytone/polytone_voice.wasm b/local-interchaintest/contracts/polytone/polytone_voice.wasm new file mode 100644 index 00000000..5fbc34fe Binary files /dev/null and b/local-interchaintest/contracts/polytone/polytone_voice.wasm differ diff --git a/local-interchaintest/src/helpers/astroport.rs b/local-interchaintest/src/helpers/astroport.rs new file mode 100644 index 00000000..b52aebbb --- /dev/null +++ b/local-interchaintest/src/helpers/astroport.rs @@ -0,0 +1,59 @@ +use astroport::asset::AssetInfo; +use localic_std::{modules::cosmwasm::contract_query, transactions::ChainRequestBuilder}; + +pub fn get_pool_address( + rb: &ChainRequestBuilder, + factory_address: &str, + asset1: AssetInfo, + asset2: AssetInfo, +) -> String { + let pair_info = contract_query( + rb, + factory_address, + &serde_json::to_string(&astroport::factory::QueryMsg::Pair { + asset_infos: vec![asset1, asset2], + }) + .unwrap(), + ); + pair_info["data"]["contract_addr"] + .as_str() + .unwrap() + .to_string() +} + +pub fn get_lp_token_address( + rb: &ChainRequestBuilder, + factory_address: &str, + asset1: AssetInfo, + asset2: AssetInfo, +) -> String { + let pair_info = contract_query( + rb, + factory_address, + &serde_json::to_string(&astroport::factory::QueryMsg::Pair { + asset_infos: vec![asset1, asset2], + }) + .unwrap(), + ); + pair_info["data"]["liquidity_token"] + .as_str() + .unwrap() + .to_string() +} + +pub fn get_lp_token_balance( + rb: &ChainRequestBuilder, + token_address: &str, + account_address: &str, +) -> String { + let balance = contract_query( + rb, + token_address, + &serde_json::to_string(&cw20::Cw20QueryMsg::Balance { + address: account_address.to_string(), + }) + .unwrap(), + ); + + balance["data"]["balance"].as_str().unwrap().to_string() +} diff --git a/local-interchaintest/src/helpers/common.rs b/local-interchaintest/src/helpers/common.rs new file mode 100644 index 00000000..a8c5ed50 --- /dev/null +++ b/local-interchaintest/src/helpers/common.rs @@ -0,0 +1,39 @@ +use localic_std::{ + modules::cosmwasm::{contract_execute, contract_query}, + transactions::ChainRequestBuilder, +}; +use serde::{Deserialize, Serialize}; +use serde_json::json; + +use crate::helpers::constants::EXECUTE_FLAGS; + +#[derive(Deserialize, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum Messages { + ContractState {}, + Tick {}, +} + +pub fn query_contract_state(rb: &ChainRequestBuilder, contract_address: &str) -> String { + let query_response = contract_query( + rb, + contract_address, + &serde_json::to_string(&Messages::ContractState {}).unwrap(), + ); + + if query_response["data"].as_str().is_none() { + return json!(query_response).to_string(); + } + query_response["data"].as_str().unwrap().to_string() +} + +pub fn tick(rb: &ChainRequestBuilder, from_key: &str, contract_address: &str) { + contract_execute( + rb, + contract_address, + from_key, + &serde_json::to_string(&Messages::Tick {}).unwrap(), + EXECUTE_FLAGS, + ) + .unwrap(); +} diff --git a/local-interchaintest/src/helpers/constants.rs b/local-interchaintest/src/helpers/constants.rs new file mode 100644 index 00000000..1fb7f1c1 --- /dev/null +++ b/local-interchaintest/src/helpers/constants.rs @@ -0,0 +1,18 @@ +pub const LOCAL_CODE_ID_CACHE_PATH: &str = "local-interchaintest/code_id_cache.json"; + +pub const ACC_1_KEY: &str = "acc1"; +pub const ACC_2_KEY: &str = "acc2"; + +pub const VALENCE_PATH: &str = "./artifacts"; +pub const ASTROPORT_PATH: &str = "local-interchaintest/contracts/astroport"; +pub const POLYTONE_PATH: &str = "local-interchaintest/contracts/polytone"; + +pub const EXECUTE_FLAGS: &str = "--gas=auto --gas-adjustment=3.0"; +pub const OSMOSIS_FEES: &str = "--fees=100000uosmo"; + +pub const ACC1_ADDRESS_GAIA: &str = "cosmos1kljf09rj77uxeu5lye7muejx6ajsu55cc3re5h"; +pub const ACC1_ADDRESS_NEUTRON: &str = "neutron1kljf09rj77uxeu5lye7muejx6ajsu55cuw2mws"; +pub const ACC1_ADDRESS_OSMO: &str = "osmo1kljf09rj77uxeu5lye7muejx6ajsu55cs2sfz9"; +pub const ACC2_ADDRESS_GAIA: &str = "cosmos17lp3n649rxt2jadn455frcj0q6anjndsw0xwrz"; +pub const ACC2_ADDRESS_NEUTRON: &str = "neutron17lp3n649rxt2jadn455frcj0q6anjnds2s0ve9"; +pub const ACC2_ADDRESS_OSMO: &str = "osmo17lp3n649rxt2jadn455frcj0q6anjndsx5474s"; diff --git a/local-interchaintest/src/helpers/covenant.rs b/local-interchaintest/src/helpers/covenant.rs new file mode 100644 index 00000000..513476cf --- /dev/null +++ b/local-interchaintest/src/helpers/covenant.rs @@ -0,0 +1,191 @@ +use localic_std::{modules::cosmwasm::contract_query, transactions::ChainRequestBuilder}; + +pub enum Covenant<'a> { + TwoPartyPol { + rb: &'a ChainRequestBuilder, + contract_address: &'a str, + }, + SinglePartyPol { + rb: &'a ChainRequestBuilder, + contract_address: &'a str, + }, + Swap { + rb: &'a ChainRequestBuilder, + contract_address: &'a str, + }, +} + +impl<'a> Covenant<'a> { + fn get_rb(&self) -> &ChainRequestBuilder { + match self { + Covenant::TwoPartyPol { rb, .. } => rb, + Covenant::SinglePartyPol { rb, .. } => rb, + Covenant::Swap { rb, .. } => rb, + } + } + + fn get_contract_address(&self) -> &str { + match self { + Covenant::TwoPartyPol { + contract_address, .. + } => contract_address, + Covenant::SinglePartyPol { + contract_address, .. + } => contract_address, + Covenant::Swap { + contract_address, .. + } => contract_address, + } + } + + fn query(&self, query_msg: &str) -> String { + let query_response = contract_query(self.get_rb(), self.get_contract_address(), query_msg); + query_response["data"] + .as_str() + .unwrap_or_default() + .to_string() + } + + pub fn query_clock_address(&self) -> String { + let query_msg = match self { + Covenant::TwoPartyPol { .. } => &serde_json::to_string( + &valence_covenant_two_party_pol::msg::QueryMsg::ClockAddress {}, + ) + .unwrap(), + Covenant::SinglePartyPol { .. } => &serde_json::to_string( + &valence_covenant_single_party_pol::msg::QueryMsg::ClockAddress {}, + ) + .unwrap(), + Covenant::Swap { .. } => { + &serde_json::to_string(&valence_covenant_swap::msg::QueryMsg::ClockAddress {}) + .unwrap() + } + }; + + self.query(query_msg) + } + + pub fn query_holder_address(&self) -> String { + let query_msg = match self { + Covenant::TwoPartyPol { .. } => &serde_json::to_string( + &valence_covenant_two_party_pol::msg::QueryMsg::HolderAddress {}, + ) + .unwrap(), + Covenant::SinglePartyPol { .. } => &serde_json::to_string( + &valence_covenant_single_party_pol::msg::QueryMsg::HolderAddress {}, + ) + .unwrap(), + Covenant::Swap { .. } => { + &serde_json::to_string(&valence_covenant_swap::msg::QueryMsg::HolderAddress {}) + .unwrap() + } + }; + + self.query(query_msg) + } + + pub fn query_liquid_pooler_address(&self) -> String { + let query_msg = match self { + Covenant::TwoPartyPol { .. } => &serde_json::to_string( + &valence_covenant_two_party_pol::msg::QueryMsg::LiquidPoolerAddress {}, + ) + .unwrap(), + Covenant::SinglePartyPol { .. } => &serde_json::to_string( + &valence_covenant_single_party_pol::msg::QueryMsg::LiquidPoolerAddress {}, + ) + .unwrap(), + Covenant::Swap { .. } => return String::new(), + }; + + self.query(query_msg) + } + + pub fn query_liquid_staker_address(&self) -> String { + let query_msg = match self { + Covenant::TwoPartyPol { .. } => return String::new(), + Covenant::SinglePartyPol { .. } => &serde_json::to_string( + &valence_covenant_single_party_pol::msg::QueryMsg::LiquidStakerAddress {}, + ) + .unwrap(), + Covenant::Swap { .. } => return String::new(), + }; + + self.query(query_msg) + } + + pub fn query_splitter_address(&self) -> String { + let query_msg = match self { + Covenant::TwoPartyPol { .. } => return String::new(), + Covenant::SinglePartyPol { .. } => &serde_json::to_string( + &valence_covenant_single_party_pol::msg::QueryMsg::SplitterAddress {}, + ) + .unwrap(), + Covenant::Swap { .. } => { + &serde_json::to_string(&valence_covenant_swap::msg::QueryMsg::SplitterAddress {}) + .unwrap() + } + }; + + self.query(query_msg) + } + + pub fn query_interchain_router_address(&self, party: String) -> String { + let query_msg = match self { + Covenant::TwoPartyPol { .. } => &serde_json::to_string( + &valence_covenant_two_party_pol::msg::QueryMsg::InterchainRouterAddress { party }, + ) + .unwrap(), + Covenant::SinglePartyPol { .. } => &serde_json::to_string( + &valence_covenant_single_party_pol::msg::QueryMsg::InterchainRouterAddress {}, + ) + .unwrap(), + Covenant::Swap { .. } => &serde_json::to_string( + &valence_covenant_swap::msg::QueryMsg::InterchainRouterAddress { party }, + ) + .unwrap(), + }; + + self.query(query_msg) + } + + pub fn query_ibc_forwarder_address(&self, party: String) -> String { + let query_msg = + match self { + Covenant::TwoPartyPol { .. } => &serde_json::to_string( + &valence_covenant_two_party_pol::msg::QueryMsg::IbcForwarderAddress { party }, + ) + .unwrap(), + Covenant::SinglePartyPol { .. } => &serde_json::to_string( + &valence_covenant_single_party_pol::msg::QueryMsg::IbcForwarderAddress { + ty: party, + }, + ) + .unwrap(), + Covenant::Swap { .. } => &serde_json::to_string( + &valence_covenant_swap::msg::QueryMsg::IbcForwarderAddress { party }, + ) + .unwrap(), + }; + self.query(query_msg) + } + + pub fn query_deposit_address(&self, party: String) -> String { + let query_msg = + match self { + Covenant::TwoPartyPol { .. } => &serde_json::to_string( + &valence_covenant_two_party_pol::msg::QueryMsg::PartyDepositAddress { party }, + ) + .unwrap(), + Covenant::SinglePartyPol { .. } => &serde_json::to_string( + &valence_covenant_single_party_pol::msg::QueryMsg::PartyDepositAddress {}, + ) + .unwrap(), + Covenant::Swap { .. } => &serde_json::to_string( + &valence_covenant_swap::msg::QueryMsg::PartyDepositAddress { party }, + ) + .unwrap(), + }; + + self.query(query_msg) + } +} diff --git a/local-interchaintest/src/helpers/mod.rs b/local-interchaintest/src/helpers/mod.rs new file mode 100644 index 00000000..71b24905 --- /dev/null +++ b/local-interchaintest/src/helpers/mod.rs @@ -0,0 +1,4 @@ +pub mod astroport; +pub mod common; +pub mod constants; +pub mod covenant; diff --git a/local-interchaintest/src/lib.rs b/local-interchaintest/src/lib.rs new file mode 100644 index 00000000..e530bccc --- /dev/null +++ b/local-interchaintest/src/lib.rs @@ -0,0 +1,34 @@ +use localic_std::modules::bank::get_balance; +use localic_utils::utils::test_context::TestContext; + +pub mod helpers; +pub mod tests; + +pub fn send_non_native_balances( + test_ctx: &mut TestContext, + chain_name: &str, + key: &str, + source: &str, + destination: &str, + native_denom: &str, +) { + let balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(chain_name), + source, + ); + for coin in balances { + if coin.denom != native_denom { + test_ctx + .build_tx_transfer() + .with_chain_name(chain_name) + .with_amount(coin.amount.u128()) + .with_recipient(destination) + .with_denom(&coin.denom) + .with_key(key) + .send() + .unwrap(); + } + } +} diff --git a/local-interchaintest/src/main.rs b/local-interchaintest/src/main.rs new file mode 100644 index 00000000..0dc5888e --- /dev/null +++ b/local-interchaintest/src/main.rs @@ -0,0 +1,50 @@ +#![allow(dead_code, unused_must_use)] + +use std::error::Error; + +use local_ictest_e2e::tests::{ + single_party_pol::single_party_pol_stride::test_single_party_pol_stride, + swap::token_swap::test_token_swap, + two_party_pol::{ + two_party_pol_native::test_two_party_pol_native, + two_party_pol_not_native::test_two_party_pol, two_party_pol_osmo::test_two_party_pol_osmo, + }, +}; + +use localic_std::polling::poll_for_start; +use localic_utils::{ + ConfigChainBuilder, TestContextBuilder, GAIA_CHAIN_NAME, LOCAL_IC_API_URL, NEUTRON_CHAIN_NAME, + OSMOSIS_CHAIN_NAME, STRIDE_CHAIN_NAME, +}; +use reqwest::blocking::Client; + +// Run `local-ic start neutron_gaia --api-port 42069` before running this test inside the local-interchaintest directory to spin up the environment +fn main() -> Result<(), Box> { + env_logger::init(); + let client = Client::new(); + poll_for_start(&client, LOCAL_IC_API_URL, 300); + + let mut test_ctx = TestContextBuilder::default() + .with_unwrap_raw_logs(true) + .with_chain(ConfigChainBuilder::default_neutron().build()?) + .with_chain(ConfigChainBuilder::default_osmosis().build()?) + .with_chain(ConfigChainBuilder::default_stride().build()?) + .with_chain(ConfigChainBuilder::default_gaia().build()?) + .with_artifacts_dir("artifacts") + .with_transfer_channels(OSMOSIS_CHAIN_NAME, NEUTRON_CHAIN_NAME) + .with_transfer_channels(OSMOSIS_CHAIN_NAME, GAIA_CHAIN_NAME) + .with_transfer_channels(NEUTRON_CHAIN_NAME, GAIA_CHAIN_NAME) + .with_transfer_channels(STRIDE_CHAIN_NAME, GAIA_CHAIN_NAME) + .with_transfer_channels(STRIDE_CHAIN_NAME, NEUTRON_CHAIN_NAME) + .build()?; + + test_ctx.set_up_stride_host_zone(GAIA_CHAIN_NAME); + + test_single_party_pol_stride(&mut test_ctx); + test_token_swap(&mut test_ctx); + test_two_party_pol_osmo(&mut test_ctx); + test_two_party_pol_native(&mut test_ctx); + test_two_party_pol(&mut test_ctx); + + Ok(()) +} diff --git a/local-interchaintest/src/tests/mod.rs b/local-interchaintest/src/tests/mod.rs new file mode 100644 index 00000000..e3b508df --- /dev/null +++ b/local-interchaintest/src/tests/mod.rs @@ -0,0 +1,3 @@ +pub mod single_party_pol; +pub mod swap; +pub mod two_party_pol; diff --git a/local-interchaintest/src/tests/single_party_pol/mod.rs b/local-interchaintest/src/tests/single_party_pol/mod.rs new file mode 100644 index 00000000..3e4808a3 --- /dev/null +++ b/local-interchaintest/src/tests/single_party_pol/mod.rs @@ -0,0 +1 @@ +pub mod single_party_pol_stride; diff --git a/local-interchaintest/src/tests/single_party_pol/single_party_pol_stride.rs b/local-interchaintest/src/tests/single_party_pol/single_party_pol_stride.rs new file mode 100644 index 00000000..01098a76 --- /dev/null +++ b/local-interchaintest/src/tests/single_party_pol/single_party_pol_stride.rs @@ -0,0 +1,1024 @@ +use std::{collections::BTreeMap, str::FromStr, thread, time::Duration}; + +use astroport::{ + asset::{Asset, AssetInfo}, + factory::{PairConfig, PairType}, + pair::StablePoolParams, +}; +use cosmwasm_std::{Binary, Coin, Decimal, Uint128, Uint64}; +use covenant_utils::{InterchainCovenantParty, PoolPriceConfig, SingleSideLpLimits}; +use cw_utils::Expiration; +use localic_std::{ + errors::LocalError, + modules::{ + bank::{get_balance, send}, + cosmwasm::{contract_execute, contract_instantiate, contract_query}, + }, +}; +use localic_utils::{ + types::ibc::get_multihop_ibc_denom, utils::test_context::TestContext, ADMIN_KEY, DEFAULT_KEY, + GAIA_CHAIN_NAME, NEUTRON_CHAIN_ADMIN_ADDR, NEUTRON_CHAIN_NAME, STRIDE_CHAIN_ADMIN_ADDR, + STRIDE_CHAIN_NAME, +}; +use log::info; +use valence_astroport_liquid_pooler::msg::AstroportLiquidPoolerConfig; +use valence_covenant_single_party_pol::msg::{ + CovenantContractCodeIds, CovenantPartyConfig, LiquidPoolerConfig, LsInfo, + RemoteChainSplitterConfig, Timeouts, +}; + +use crate::{ + helpers::{ + astroport::get_pool_address, + common::{query_contract_state, tick}, + constants::{ + ACC1_ADDRESS_GAIA, ACC1_ADDRESS_NEUTRON, ACC2_ADDRESS_NEUTRON, ACC_1_KEY, + ASTROPORT_PATH, EXECUTE_FLAGS, LOCAL_CODE_ID_CACHE_PATH, VALENCE_PATH, + }, + covenant::Covenant, + }, + send_non_native_balances, +}; + +const NATIVE_STATOM_DENOM: &str = "stuatom"; + +pub fn test_single_party_pol_stride(test_ctx: &mut TestContext) -> Result<(), LocalError> { + let mut uploader = test_ctx.build_tx_upload_contracts(); + + uploader + .send_with_local_cache(VALENCE_PATH, NEUTRON_CHAIN_NAME, LOCAL_CODE_ID_CACHE_PATH) + .unwrap(); + + uploader + .send_with_local_cache(ASTROPORT_PATH, NEUTRON_CHAIN_NAME, LOCAL_CODE_ID_CACHE_PATH) + .unwrap(); + + info!("Starting single party POL tests..."); + let astroport_native_coin_registry_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("astroport_native_coin_registry") + .unwrap(); + + let astroport_pair_stable_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("astroport_pair_stable") + .unwrap(); + + let astroport_token_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("astroport_token") + .unwrap(); + + let astroport_whitelist_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("astroport_whitelist") + .unwrap(); + + let astroport_factory_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("astroport_factory") + .unwrap(); + + let native_coin_registry_instantiate_msg = astroport::native_coin_registry::InstantiateMsg { + owner: NEUTRON_CHAIN_ADMIN_ADDR.to_string(), + }; + + let native_coin_registry_contract = contract_instantiate( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + astroport_native_coin_registry_code_id, + &serde_json::to_string(&native_coin_registry_instantiate_msg).unwrap(), + "native-coin-registry", + None, + "", + )?; + + let factory_instantiate_msg = astroport::factory::InstantiateMsg { + pair_configs: vec![PairConfig { + code_id: astroport_pair_stable_code_id, + pair_type: PairType::Stable {}, + total_fee_bps: 0, + maker_fee_bps: 0, + is_disabled: false, + is_generator_disabled: true, + permissioned: false, + }], + token_code_id: astroport_token_code_id, + fee_address: None, + generator_address: None, + owner: NEUTRON_CHAIN_ADMIN_ADDR.to_string(), + whitelist_code_id: astroport_whitelist_code_id, + coin_registry_address: native_coin_registry_contract.address.to_string(), + tracker_config: None, + }; + + let factory_contract = contract_instantiate( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + astroport_factory_code_id, + &serde_json::to_string(&factory_instantiate_msg).unwrap(), + "astroport-factory", + None, + "", + )?; + + let atom_denom = test_ctx.get_native_denom().src(GAIA_CHAIN_NAME).get(); + let neutron_denom = test_ctx.get_native_denom().src(NEUTRON_CHAIN_NAME).get(); + let atom_on_stride = test_ctx.get_ibc_denom(&atom_denom, GAIA_CHAIN_NAME, STRIDE_CHAIN_NAME); + // Add the coins to the registry + let statom_on_neutron = + test_ctx.get_ibc_denom(NATIVE_STATOM_DENOM, STRIDE_CHAIN_NAME, NEUTRON_CHAIN_NAME); + + let atom_on_neutron = test_ctx.get_ibc_denom(&atom_denom, GAIA_CHAIN_NAME, NEUTRON_CHAIN_NAME); + let statom_on_gaia_via_neutron = get_multihop_ibc_denom( + NATIVE_STATOM_DENOM, + vec![ + &test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + &test_ctx + .get_transfer_channels() + .src(STRIDE_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + ], + ); + + let add_to_registry_msg = astroport::native_coin_registry::ExecuteMsg::Add { + native_coins: vec![(atom_on_neutron.clone(), 6), (statom_on_neutron.clone(), 6)], + }; + + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &native_coin_registry_contract.address, + DEFAULT_KEY, + &serde_json::to_string(&add_to_registry_msg).unwrap(), + EXECUTE_FLAGS, + )?; + + // Wait for the coins to be added + thread::sleep(Duration::from_secs(5)); + + // Create the stable pair + let create_pair_msg = astroport::factory::ExecuteMsg::CreatePair { + pair_type: PairType::Stable {}, + asset_infos: vec![ + AssetInfo::NativeToken { + denom: atom_on_neutron.clone(), + }, + AssetInfo::NativeToken { + denom: statom_on_neutron.clone(), + }, + ], + init_params: Some(Binary::from( + serde_json::to_vec(&StablePoolParams { + amp: 3, + owner: None, + }) + .unwrap(), + )), + }; + + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &factory_contract.address, + DEFAULT_KEY, + &serde_json::to_string(&create_pair_msg).unwrap(), + EXECUTE_FLAGS, + )?; + + info!("Liquid Stake some ATOM"); + let amount_to_liquid_stake = 100_000_000_000; + loop { + test_ctx + .build_tx_transfer() + .with_chain_name(GAIA_CHAIN_NAME) + .with_amount(amount_to_liquid_stake) + .with_recipient(STRIDE_CHAIN_ADMIN_ADDR) + .with_denom(&atom_denom) + .send() + .unwrap(); + + info!("Waiting to receive IBC transfer..."); + thread::sleep(Duration::from_secs(5)); + let balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(STRIDE_CHAIN_NAME), + STRIDE_CHAIN_ADMIN_ADDR, + ); + if balance.iter().any(|c| c.denom == atom_on_stride) { + break; + } + } + + // Liquid stake the ibc'd atoms for stuatom + test_ctx + .build_tx_liquid_stake() + .with_key(ADMIN_KEY) + .with_amount(amount_to_liquid_stake) + .with_denom(&atom_denom) + .send() + .unwrap(); + + info!("Send the StATOM and some ATOM to Neutron..."); + test_ctx + .build_tx_transfer() + .with_chain_name(STRIDE_CHAIN_NAME) + .with_key(ADMIN_KEY) + .with_amount(amount_to_liquid_stake) + .with_recipient(NEUTRON_CHAIN_ADMIN_ADDR) + .with_denom(NATIVE_STATOM_DENOM) + .send() + .unwrap(); + + let uatom_contribution_amount: u128 = 5_000_000_000; + loop { + test_ctx + .build_tx_transfer() + .with_chain_name(GAIA_CHAIN_NAME) + .with_amount(uatom_contribution_amount) + .with_recipient(NEUTRON_CHAIN_ADMIN_ADDR) + .with_denom(&atom_denom) + .send() + .unwrap(); + + info!("Waiting to receive IBC transfers..."); + thread::sleep(Duration::from_secs(5)); + let balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + NEUTRON_CHAIN_ADMIN_ADDR, + ); + if balance.iter().any(|c| c.denom == atom_on_neutron) + && balance.iter().any(|c| c.denom == statom_on_neutron) + { + break; + } + } + + let pool_addr = get_pool_address( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &factory_contract.address, + AssetInfo::NativeToken { + denom: atom_on_neutron.clone(), + }, + AssetInfo::NativeToken { + denom: statom_on_neutron.clone(), + }, + ); + + let liquidity_contribution = uatom_contribution_amount / 2; + let provide_liquidity_msg = astroport::pair::ExecuteMsg::ProvideLiquidity { + assets: vec![ + Asset { + info: AssetInfo::NativeToken { + denom: atom_on_neutron.clone(), + }, + amount: Uint128::from(liquidity_contribution), + }, + Asset { + info: AssetInfo::NativeToken { + denom: statom_on_neutron.clone(), + }, + amount: Uint128::from(liquidity_contribution), + }, + ], + slippage_tolerance: Some(Decimal::percent(1)), + auto_stake: Some(false), + receiver: Some(NEUTRON_CHAIN_ADMIN_ADDR.to_string()), + min_lp_to_receive: None, + }; + + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &pool_addr, + DEFAULT_KEY, + &serde_json::to_string(&provide_liquidity_msg).unwrap(), + &format!( + "--amount {liquidity_contribution}{atom_on_neutron},{liquidity_contribution}{statom_on_neutron} {EXECUTE_FLAGS}" + ), + )?; + + thread::sleep(Duration::from_secs(5)); + let balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + NEUTRON_CHAIN_ADMIN_ADDR, + ); + + let balance = balance + .iter() + .find(|c| c.denom.starts_with("factory")) + .unwrap() + .amount; + + info!("Neutron User LP Token balance: {}", balance); + + let code_id_ibc_forwarder = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_ibc_forwarder") + .unwrap(); + + let code_id_single_party_pol_holder = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_single_party_pol_holder") + .unwrap(); + + let code_id_remote_chain_splitter = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_remote_chain_splitter") + .unwrap(); + + let code_id_astroport_tf_liquid_pooler = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_astroport_tf_liquid_pooler") + .unwrap(); + + let code_id_stride_liquid_staker = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_stride_liquid_staker") + .unwrap(); + + let code_id_interchain_router = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_interchain_router") + .unwrap(); + + let code_id_single_party_pol_covenant = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_covenant_single_party_pol") + .unwrap(); + + let code_id_clock = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_clock") + .unwrap(); + + // Instantiate the covenant + let chain = localic_std::node::Chain::new( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ); + let current_height = chain.get_height(); + + let instantiate_covenant_msg = valence_covenant_single_party_pol::msg::InstantiateMsg { + label: "single_party_pol_stride_covenant".to_string(), + timeouts: Timeouts { + ica_timeout: Uint64::new(10000), + ibc_transfer_timeout: Uint64::new(10000), + }, + contract_codes: CovenantContractCodeIds { + ibc_forwarder_code: code_id_ibc_forwarder, + holder_code: code_id_single_party_pol_holder, + remote_chain_splitter_code: code_id_remote_chain_splitter, + liquid_pooler_code: code_id_astroport_tf_liquid_pooler, + liquid_staker_code: code_id_stride_liquid_staker, + interchain_router_code: code_id_interchain_router, + clock_code: code_id_clock, + }, + clock_tick_max_gas: None, + lockup_period: Expiration::AtHeight(current_height + 110), + ls_info: LsInfo { + ls_denom: NATIVE_STATOM_DENOM.to_string(), + ls_denom_on_neutron: statom_on_neutron.to_string(), + ls_chain_to_neutron_channel_id: test_ctx + .get_transfer_channels() + .src(STRIDE_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + ls_neutron_connection_id: test_ctx + .get_connections() + .src(NEUTRON_CHAIN_NAME) + .dest(STRIDE_CHAIN_NAME) + .get(), + }, + ls_forwarder_config: CovenantPartyConfig::Interchain(InterchainCovenantParty { + party_receiver_addr: ACC2_ADDRESS_NEUTRON.to_string(), + party_chain_connection_id: test_ctx + .get_connections() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + ibc_transfer_timeout: Uint64::new(10000), + party_to_host_chain_channel_id: test_ctx + .get_transfer_channels() + .src(GAIA_CHAIN_NAME) + .dest(STRIDE_CHAIN_NAME) + .get(), + host_to_party_chain_channel_id: test_ctx + .get_transfer_channels() + .src(STRIDE_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + remote_chain_denom: atom_denom.clone(), + addr: ACC2_ADDRESS_NEUTRON.to_string(), + native_denom: atom_on_neutron.clone(), + contribution: Coin { + denom: atom_denom.to_string(), + amount: Uint128::new(liquidity_contribution), + }, + denom_to_pfm_map: BTreeMap::new(), + fallback_address: None, + }), + lp_forwarder_config: CovenantPartyConfig::Interchain(InterchainCovenantParty { + party_receiver_addr: ACC2_ADDRESS_NEUTRON.to_string(), + party_chain_connection_id: test_ctx + .get_connections() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + ibc_transfer_timeout: Uint64::new(10000), + party_to_host_chain_channel_id: test_ctx + .get_transfer_channels() + .src(GAIA_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + host_to_party_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + remote_chain_denom: atom_denom.clone(), + addr: ACC2_ADDRESS_NEUTRON.to_string(), + native_denom: atom_on_neutron.clone(), + contribution: Coin { + denom: atom_denom.clone(), + amount: Uint128::new(liquidity_contribution), + }, + denom_to_pfm_map: BTreeMap::new(), + fallback_address: None, + }), + pool_price_config: PoolPriceConfig { + expected_spot_price: Decimal::one(), + acceptable_price_spread: Decimal::from_str("0.1").unwrap(), + }, + remote_chain_splitter_config: RemoteChainSplitterConfig { + channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + connection_id: test_ctx + .get_connections() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + denom: atom_denom.clone(), + amount: Uint128::from(uatom_contribution_amount), + ls_share: Decimal::percent(50), + native_share: Decimal::percent(50), + fallback_address: None, + }, + emergency_committee: None, + covenant_party_config: InterchainCovenantParty { + party_receiver_addr: ACC1_ADDRESS_GAIA.to_string(), + party_chain_connection_id: test_ctx + .get_connections() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + ibc_transfer_timeout: Uint64::new(300), + party_to_host_chain_channel_id: test_ctx + .get_transfer_channels() + .src(GAIA_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + host_to_party_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + remote_chain_denom: atom_denom.clone(), + addr: ACC1_ADDRESS_NEUTRON.to_string(), + native_denom: atom_on_neutron.clone(), + contribution: Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }, + denom_to_pfm_map: BTreeMap::new(), + fallback_address: None, + }, + liquid_pooler_config: LiquidPoolerConfig::Astroport(AstroportLiquidPoolerConfig { + pool_pair_type: astroport_old::factory::PairType::Stable {}, + pool_address: pool_addr.to_string(), + asset_a_denom: statom_on_neutron.clone(), + asset_b_denom: atom_on_neutron.clone(), + single_side_lp_limits: SingleSideLpLimits { + asset_a_limit: Uint128::new(1000000), + asset_b_limit: Uint128::new(1000000), + }, + }), + }; + + let covenant_contract = contract_instantiate( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + code_id_single_party_pol_covenant, + &serde_json::to_string(&instantiate_covenant_msg).unwrap(), + "single-party-pol-stride-covenant", + None, + "", + )?; + info!("Covenant contract: {:?}", covenant_contract.address); + + let covenant = Covenant::SinglePartyPol { + rb: test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + contract_address: &covenant_contract.address, + }; + + // Query the covenant addresses + let clock_address = covenant.query_clock_address(); + let holder_address = covenant.query_holder_address(); + let liquid_pooler_address = covenant.query_liquid_pooler_address(); + let liquid_staker_address = covenant.query_liquid_staker_address(); + let ls_forwarder_address = covenant.query_ibc_forwarder_address("ls".to_string()); + let liquid_pooler_forwarder_address = covenant.query_ibc_forwarder_address("lp".to_string()); + let remote_chain_splitter_address = covenant.query_splitter_address(); + let interchain_router_address = covenant.query_interchain_router_address("".to_string()); + + info!("Fund covenant addresses with NTRN..."); + let addresses = vec![ + clock_address.clone(), + holder_address.clone(), + liquid_pooler_address.clone(), + liquid_staker_address.clone(), + ls_forwarder_address.clone(), + liquid_pooler_forwarder_address.clone(), + remote_chain_splitter_address.clone(), + interchain_router_address.clone(), + ]; + + for address in &addresses { + send( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + address, + &[Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000000000), + }], + &Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + } + + info!("Tick until forwarders create ICA..."); + let party_deposit_address; + let stride_ica_address; + let ls_forwarder_ica_address; + let liquid_pooler_forwarder_ica_address; + loop { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + let ls_forwarder_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &ls_forwarder_address, + ); + info!("Liquid Staker forwarder state: {:?}", ls_forwarder_state); + + let liquid_pooler_forwarder_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_forwarder_address, + ); + info!( + "Liquid Pooler forwarder state: {:?}", + liquid_pooler_forwarder_state + ); + + let splitter_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &remote_chain_splitter_address, + ); + info!("Remote Chain Splitter state: {:?}", splitter_state); + + let liquid_staker_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_staker_address, + ); + info!("Liquid Staker state: {:?}", liquid_staker_state); + + if splitter_state == "ica_created" + && liquid_staker_state == "ica_created" + && ls_forwarder_state == "ica_created" + && liquid_pooler_forwarder_state == "ica_created" + { + party_deposit_address = covenant.query_deposit_address("".to_string()); + + let query_response = contract_query( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_staker_address, + &serde_json::to_string(&valence_stride_liquid_staker::msg::QueryMsg::IcaAddress {}) + .unwrap(), + ); + stride_ica_address = query_response["data"].as_str().unwrap().to_string(); + + let query_response = contract_query( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &ls_forwarder_address, + &serde_json::to_string(&valence_ibc_forwarder::msg::QueryMsg::DepositAddress {}) + .unwrap(), + ); + ls_forwarder_ica_address = query_response["data"].as_str().unwrap().to_string(); + + let query_response = contract_query( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_forwarder_address, + &serde_json::to_string( + &valence_astroport_liquid_pooler::msg::QueryMsg::DepositAddress {}, + ) + .unwrap(), + ); + liquid_pooler_forwarder_ica_address = + query_response["data"].as_str().unwrap().to_string(); + + info!("LS forwarder ICA address: {}", ls_forwarder_ica_address); + info!( + "Liquid Pooler forwarder ICA address: {}", + liquid_pooler_forwarder_ica_address + ); + break; + } + } + + info!("Fund the forwarder with sufficient funds..."); + send( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + DEFAULT_KEY, + &party_deposit_address, + &[Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }], + &Coin { + denom: atom_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + + info!("Tick until splitter splits the funds to ls and lp forwarders..."); + loop { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + + let ls_forwarder_ica_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + &ls_forwarder_ica_address, + ); + + let liquid_pooler_forwarder_ica_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + &liquid_pooler_forwarder_ica_address, + ); + + let party_deposit_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + &party_deposit_address, + ); + + info!("LS forwarder ICA balance: {:?}", ls_forwarder_ica_balance); + info!( + "Liquid Pooler forwarder ICA balance: {:?}", + liquid_pooler_forwarder_ica_balance + ); + info!("Party deposit balance: {:?}", party_deposit_balance); + + if ls_forwarder_ica_balance + .iter() + .any(|c| c.denom == atom_denom.clone()) + && liquid_pooler_forwarder_ica_balance + .iter() + .any(|c| c.denom == atom_denom.clone()) + { + info!("Liquid staker forwarder and liquid pooler forwarder received ATOM & StATOM"); + break; + } + } + + info!("Tick until liquid staker stakes..."); + let mut stride_ica_statom_balance; + loop { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + + let liquid_pooler_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + ); + + let stride_ica_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(STRIDE_CHAIN_NAME), + &stride_ica_address, + ); + + info!("Liquid Pooler balance: {:?}", liquid_pooler_balance); + info!("Stride ICA balance: {:?}", stride_ica_balance); + stride_ica_statom_balance = match stride_ica_balance + .iter() + .find(|c| c.denom == NATIVE_STATOM_DENOM) + { + Some(c) => c.amount, + None => Uint128::zero(), + }; + + if !stride_ica_statom_balance.is_zero() { + info!("Stride ICA received StATOM"); + break; + } + } + + info!("Permissionless forward..."); + loop { + thread::sleep(Duration::from_secs(5)); + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_staker_address, + DEFAULT_KEY, + &serde_json::to_string(&valence_stride_liquid_staker::msg::ExecuteMsg::Transfer { + amount: stride_ica_statom_balance, + }) + .unwrap(), + EXECUTE_FLAGS, + ) + .unwrap(); + + let stride_ica_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(STRIDE_CHAIN_NAME), + &stride_ica_address, + ); + + info!("Stride ICA balance: {:?}", stride_ica_balance); + + let statom_balance = match stride_ica_balance + .iter() + .find(|c| c.denom == NATIVE_STATOM_DENOM) + { + Some(c) => c.amount, + None => Uint128::zero(), + }; + + if statom_balance.is_zero() { + let liquid_pooler_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + ); + info!("Liquid Pooler balance: {:?}", liquid_pooler_balance); + break; + } + } + + info!("Tick until liquid pooler provides liquidity..."); + loop { + let balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + ); + + let liquid_pooler_lp_balance = balance + .iter() + .find(|c| c.denom.starts_with("factory")) + .unwrap_or(&Coin { + denom: "".to_string(), + amount: Uint128::zero(), + }) + .amount; + + info!( + "Liquid pooler LP token balance: {}", + liquid_pooler_lp_balance + ); + + let balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + + let holder_lp_balance = balance + .iter() + .find(|c| c.denom.starts_with("factory")) + .unwrap_or(&Coin { + denom: "".to_string(), + amount: Uint128::zero(), + }) + .amount; + + info!("Holder LP token balance: {}", holder_lp_balance); + + let balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ACC2_ADDRESS_NEUTRON, + ); + + let neutron_user_lp_balance = balance + .iter() + .find(|c| c.denom.starts_with("factory")) + .unwrap_or(&Coin { + denom: "".to_string(), + amount: Uint128::zero(), + }) + .amount; + + info!("Neutron User LP token balance: {}", neutron_user_lp_balance); + + if !liquid_pooler_lp_balance.is_zero() { + break; + } + + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + + info!("User redeems LP tokens for underlying liquidity..."); + let covenant_party_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + ACC1_ADDRESS_GAIA, + ); + info!("Covenant party balance: {:?}", covenant_party_balance); + + thread::sleep(Duration::from_secs(10)); + loop { + match contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ACC_1_KEY, + &serde_json::to_string(&valence_single_party_pol_holder::msg::ExecuteMsg::Claim {}) + .unwrap(), + EXECUTE_FLAGS, + ) { + Ok(_) => break, + Err(_) => { + info!("Waiting for lock up period to be over..."); + thread::sleep(Duration::from_secs(10)); + continue; + } + } + } + + loop { + let hub_user_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + ACC1_ADDRESS_GAIA, + ); + + let liquid_pooler_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + ); + + let holder_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + + let interchain_router_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &interchain_router_address, + ); + + info!("Hub user balance: {:?}", hub_user_balance); + info!("Liquid Pooler balance: {:?}", liquid_pooler_balance); + info!("Holder balance: {:?}", holder_balance); + info!("Interchain router balance: {:?}", interchain_router_balance); + + if hub_user_balance.iter().any(|c| c.denom == atom_denom) + && hub_user_balance + .iter() + .any(|c| c.denom == statom_on_gaia_via_neutron) + { + info!("Covenant party received the funds!"); + break; + } + + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + + // Send the balances back so we have a fresh start for the next test + send_non_native_balances( + test_ctx, + GAIA_CHAIN_NAME, + ACC_1_KEY, + ACC1_ADDRESS_GAIA, + NEUTRON_CHAIN_ADMIN_ADDR, + &atom_denom, + ); + + info!("Finished single party POL stride tests!"); + + Ok(()) +} diff --git a/local-interchaintest/src/tests/swap/mod.rs b/local-interchaintest/src/tests/swap/mod.rs new file mode 100644 index 00000000..7963a2ef --- /dev/null +++ b/local-interchaintest/src/tests/swap/mod.rs @@ -0,0 +1 @@ +pub mod token_swap; diff --git a/local-interchaintest/src/tests/swap/token_swap.rs b/local-interchaintest/src/tests/swap/token_swap.rs new file mode 100644 index 00000000..6e244fdc --- /dev/null +++ b/local-interchaintest/src/tests/swap/token_swap.rs @@ -0,0 +1,457 @@ +use std::collections::BTreeMap; + +use cosmwasm_std::{Coin, Decimal, Uint128, Uint64}; +use covenant_utils::{split::SplitConfig, InterchainCovenantParty, NativeCovenantParty}; +use cw_utils::Expiration; +use localic_std::{ + errors::LocalError, + modules::{ + bank::{get_balance, send}, + cosmwasm::contract_instantiate, + }, + node::Chain, +}; +use localic_utils::{ + utils::test_context::TestContext, DEFAULT_KEY, GAIA_CHAIN_ADMIN_ADDR, GAIA_CHAIN_NAME, + NEUTRON_CHAIN_ADMIN_ADDR, NEUTRON_CHAIN_NAME, +}; +use log::info; +use valence_covenant_swap::msg::{CovenantPartyConfig, SwapCovenantContractCodeIds, Timeouts}; + +use crate::{ + helpers::{ + common::{query_contract_state, tick}, + constants::{ + ACC1_ADDRESS_GAIA, ACC1_ADDRESS_NEUTRON, ACC2_ADDRESS_NEUTRON, ACC_1_KEY, ACC_2_KEY, + LOCAL_CODE_ID_CACHE_PATH, VALENCE_PATH, + }, + covenant::Covenant, + }, + send_non_native_balances, +}; + +pub fn test_token_swap(test_ctx: &mut TestContext) -> Result<(), LocalError> { + let mut uploader = test_ctx.build_tx_upload_contracts(); + + uploader + .send_with_local_cache(VALENCE_PATH, NEUTRON_CHAIN_NAME, LOCAL_CODE_ID_CACHE_PATH) + .unwrap(); + + let atom_denom = test_ctx.get_native_denom().src(GAIA_CHAIN_NAME).get(); + let neutron_denom = test_ctx.get_native_denom().src(NEUTRON_CHAIN_NAME).get(); + let atom_on_neutron = test_ctx.get_ibc_denom(&atom_denom, GAIA_CHAIN_NAME, NEUTRON_CHAIN_NAME); + let neutron_on_gaia = + test_ctx.get_ibc_denom(&neutron_denom, NEUTRON_CHAIN_NAME, GAIA_CHAIN_NAME); + + let valence_ibc_forwarder_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_ibc_forwarder") + .unwrap(); + + let valence_swap_holder_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_swap_holder") + .unwrap(); + + let valence_clock_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_clock") + .unwrap(); + + let valence_interchain_router_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_interchain_router") + .unwrap(); + + let valence_native_router_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_native_router") + .unwrap(); + + let valence_native_splitter_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_native_splitter") + .unwrap(); + + let valence_covenant_swap_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_covenant_swap") + .unwrap(); + + let uatom_contribution_amount: u128 = 5_000_000_000; + let untrn_contribution_amount: u128 = 100_000_000_000; + + // Instantiate covenant + info!("Starting swap covenant test..."); + let current_block_height = Chain::new( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ) + .get_height(); + + let covenant_instantiate_msg = valence_covenant_swap::msg::InstantiateMsg { + label: "swap-covenant".to_string(), + timeouts: Timeouts { + ica_timeout: Uint64::new(10000), // seconds + ibc_transfer_timeout: Uint64::new(10000), // seconds + }, + contract_codes: SwapCovenantContractCodeIds { + ibc_forwarder_code: valence_ibc_forwarder_code_id, + interchain_router_code: valence_interchain_router_code_id, + native_router_code: valence_native_router_code_id, + splitter_code: valence_native_splitter_code_id, + holder_code: valence_swap_holder_code_id, + clock_code: valence_clock_code_id, + }, + clock_tick_max_gas: None, + lockup_config: Expiration::AtHeight(current_block_height + 350), + party_a_config: CovenantPartyConfig::Interchain(InterchainCovenantParty { + party_receiver_addr: ACC1_ADDRESS_GAIA.to_string(), + party_chain_connection_id: test_ctx + .get_connections() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + ibc_transfer_timeout: Uint64::new(10000), + party_to_host_chain_channel_id: test_ctx + .get_transfer_channels() + .src(GAIA_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + host_to_party_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + remote_chain_denom: atom_denom.clone(), + addr: ACC1_ADDRESS_NEUTRON.to_string(), + native_denom: atom_on_neutron.clone(), + contribution: Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }, + denom_to_pfm_map: BTreeMap::new(), + fallback_address: None, + }), + party_b_config: CovenantPartyConfig::Native(NativeCovenantParty { + party_receiver_addr: ACC2_ADDRESS_NEUTRON.to_string(), + native_denom: neutron_denom.clone(), + addr: ACC2_ADDRESS_NEUTRON.to_string(), + contribution: Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(untrn_contribution_amount), + }, + }), + splits: BTreeMap::from([ + ( + atom_on_neutron.clone(), + SplitConfig { + receivers: BTreeMap::from([ + (ACC2_ADDRESS_NEUTRON.to_string(), Decimal::percent(100)), + (ACC1_ADDRESS_GAIA.to_string(), Decimal::percent(0)), + ]), + }, + ), + ( + neutron_denom.clone(), + SplitConfig { + receivers: BTreeMap::from([ + (ACC2_ADDRESS_NEUTRON.to_string(), Decimal::percent(0)), + (ACC1_ADDRESS_GAIA.to_string(), Decimal::percent(100)), + ]), + }, + ), + ]), + fallback_split: None, + fallback_address: None, + }; + + let covenant_contract = contract_instantiate( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + valence_covenant_swap_code_id, + &serde_json::to_string(&covenant_instantiate_msg).unwrap(), + "swap-covenant", + None, + "", + )?; + info!("Covenant contract: {:?}", covenant_contract.address); + let covenant = Covenant::Swap { + rb: test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + contract_address: &covenant_contract.address, + }; + + // Query the covenant addresses + let clock_address = covenant.query_clock_address(); + let holder_address = covenant.query_holder_address(); + let splitter_address = covenant.query_splitter_address(); + let party_a_router_address = covenant.query_interchain_router_address("party_a".to_string()); + let party_b_router_address = covenant.query_interchain_router_address("party_b".to_string()); + let party_a_ibc_forwarder_address = covenant.query_ibc_forwarder_address("party_a".to_string()); + let party_b_ibc_forwarder_address = covenant.query_ibc_forwarder_address("party_b".to_string()); + + info!("Fund covenant addresses with NTRN..."); + let mut addresses = vec![ + clock_address.clone(), + holder_address.clone(), + splitter_address.clone(), + party_a_router_address.clone(), + party_b_router_address.clone(), + ]; + if !party_a_ibc_forwarder_address.is_empty() { + addresses.push(party_a_ibc_forwarder_address.clone()); + } + if !party_b_ibc_forwarder_address.is_empty() { + addresses.push(party_b_ibc_forwarder_address.clone()); + } + for address in &addresses { + send( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + address, + &[Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000000000), + }], + &Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + } + + info!("Tick until forwarders create ICA..."); + let party_a_deposit_address; + let party_b_deposit_address; + loop { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + let forwarder_a_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_a_ibc_forwarder_address, + ); + info!("Forwarder A state: {:?}", forwarder_a_state); + if forwarder_a_state == "ica_created" { + party_a_deposit_address = covenant.query_deposit_address("party_a".to_string()); + party_b_deposit_address = covenant.query_deposit_address("party_b".to_string()); + break; + } + } + + info!("Party A deposit address: {}", party_a_deposit_address); + info!("Party B deposit address: {}", party_b_deposit_address); + + info!("Fund the forwarders with sufficient funds..."); + send( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + DEFAULT_KEY, + &party_a_deposit_address, + &[Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }], + &Coin { + denom: atom_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + send( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &party_b_deposit_address, + &[Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(untrn_contribution_amount), + }], + &Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + + info!("Tick until forwarders forward the funds to the holder..."); + loop { + let holder_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + let holder_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + if holder_balance.iter().any(|c| { + c.denom == atom_on_neutron.clone() + && c.amount >= Uint128::new(uatom_contribution_amount) + }) && holder_balance.iter().any(|c| { + c.denom == neutron_denom.clone() && c.amount >= Uint128::new(untrn_contribution_amount) + }) { + info!("Holder received ATOM & NTRN"); + break; + } else if holder_state == "complete" { + info!("Holder is complete"); + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!("Tick until holder sends the funds to splitter"); + loop { + let splitter_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + info!("Splitter balance: {:?}", splitter_balance); + if splitter_balance.iter().any(|c| { + c.denom == atom_on_neutron.clone() + && c.amount >= Uint128::new(uatom_contribution_amount) + }) && splitter_balance.iter().any(|c| { + c.denom == neutron_denom.clone() && c.amount >= Uint128::new(untrn_contribution_amount) + }) { + info!("Splitter received contributions"); + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!("Tick until splitter sends funds to routers"); + loop { + let router_a_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_a_router_address, + ); + info!("Router A balances: {:?}", router_a_balances); + let router_b_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_b_router_address, + ); + info!("Router B balances: {:?}", router_b_balances); + if router_a_balances.iter().any(|c| { + c.denom == neutron_denom.clone() && c.amount >= Uint128::new(untrn_contribution_amount) + }) && router_b_balances.iter().any(|c| { + c.denom == atom_on_neutron.clone() + && c.amount >= Uint128::new(uatom_contribution_amount) + }) { + info!("Routers received contributions"); + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!("Tick until routers route the funds to final receivers"); + loop { + let hub_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + ACC1_ADDRESS_GAIA, + ); + info!("Hub receiver balances: {:?}", hub_receiver_balances); + let neutron_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ACC2_ADDRESS_NEUTRON, + ); + info!("Neutron receiver balances: {:?}", neutron_receiver_balances); + if hub_receiver_balances + .iter() + .any(|c| c.denom == neutron_on_gaia.clone()) + && neutron_receiver_balances + .iter() + .any(|c| c.denom == atom_on_neutron.clone()) + { + info!("Final receivers received their funds!"); + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + // Send the balances back so we have a fresh start for the next test + send_non_native_balances( + test_ctx, + GAIA_CHAIN_NAME, + ACC_1_KEY, + ACC1_ADDRESS_GAIA, + NEUTRON_CHAIN_ADMIN_ADDR, + &atom_denom, + ); + + send_non_native_balances( + test_ctx, + NEUTRON_CHAIN_NAME, + ACC_2_KEY, + ACC2_ADDRESS_NEUTRON, + GAIA_CHAIN_ADMIN_ADDR, + &neutron_denom, + ); + + info!("Finished swap covenant test!"); + + Ok(()) +} diff --git a/local-interchaintest/src/tests/two_party_pol/mod.rs b/local-interchaintest/src/tests/two_party_pol/mod.rs new file mode 100644 index 00000000..36441112 --- /dev/null +++ b/local-interchaintest/src/tests/two_party_pol/mod.rs @@ -0,0 +1,3 @@ +pub mod two_party_pol_native; +pub mod two_party_pol_not_native; +pub mod two_party_pol_osmo; diff --git a/local-interchaintest/src/tests/two_party_pol/two_party_pol_native.rs b/local-interchaintest/src/tests/two_party_pol/two_party_pol_native.rs new file mode 100644 index 00000000..b73306eb --- /dev/null +++ b/local-interchaintest/src/tests/two_party_pol/two_party_pol_native.rs @@ -0,0 +1,2033 @@ +use std::{collections::BTreeMap, str::FromStr, thread, time::Duration}; + +use astroport::{ + asset::{Asset, AssetInfo}, + factory::{InstantiateMsg as FactoryInstantiateMsg, PairConfig, PairType}, + native_coin_registry::{ + ExecuteMsg as NativeCoinRegistryExecuteMsg, + InstantiateMsg as NativeCoinRegistryInstantiateMsg, + }, + pair::StablePoolParams, +}; +use cosmwasm_std::{Binary, Coin, Decimal, Uint128, Uint64}; +use covenant_utils::{ + split::SplitConfig, InterchainCovenantParty, NativeCovenantParty, PoolPriceConfig, + SingleSideLpLimits, +}; +use cw_utils::Expiration; +use localic_std::{ + errors::LocalError, + modules::{ + bank::{get_balance, send}, + cosmwasm::{contract_execute, contract_instantiate}, + }, + node::Chain, +}; +use localic_utils::{ + utils::test_context::TestContext, DEFAULT_KEY, GAIA_CHAIN_ADMIN_ADDR, GAIA_CHAIN_NAME, + NEUTRON_CHAIN_ADMIN_ADDR, NEUTRON_CHAIN_NAME, +}; +use log::info; +use valence_astroport_liquid_pooler::msg::AstroportLiquidPoolerConfig; +use valence_covenant_two_party_pol::msg::{CovenantContractCodeIds, CovenantPartyConfig, Timeouts}; +use valence_two_party_pol_holder::msg::{CovenantType, RagequitConfig, RagequitTerms}; + +use crate::{ + helpers::{ + astroport::get_pool_address, + common::{query_contract_state, tick}, + constants::{ + ACC1_ADDRESS_GAIA, ACC1_ADDRESS_NEUTRON, ACC2_ADDRESS_NEUTRON, ACC_1_KEY, ACC_2_KEY, + ASTROPORT_PATH, EXECUTE_FLAGS, LOCAL_CODE_ID_CACHE_PATH, VALENCE_PATH, + }, + covenant::Covenant, + }, + send_non_native_balances, +}; + +pub fn test_two_party_pol_native(test_ctx: &mut TestContext) -> Result<(), LocalError> { + let mut uploader = test_ctx.build_tx_upload_contracts(); + + uploader + .send_with_local_cache(VALENCE_PATH, NEUTRON_CHAIN_NAME, LOCAL_CODE_ID_CACHE_PATH) + .unwrap(); + + uploader + .send_with_local_cache(ASTROPORT_PATH, NEUTRON_CHAIN_NAME, LOCAL_CODE_ID_CACHE_PATH) + .unwrap(); + + info!("Starting two party POL native tests..."); + let astroport_native_coin_registry_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("astroport_native_coin_registry") + .unwrap(); + + let astroport_pair_stable_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("astroport_pair_stable") + .unwrap(); + + let astroport_token_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("astroport_token") + .unwrap(); + + let astroport_whitelist_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("astroport_whitelist") + .unwrap(); + + let astroport_factory_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("astroport_factory") + .unwrap(); + + let native_coin_registry_instantiate_msg = NativeCoinRegistryInstantiateMsg { + owner: NEUTRON_CHAIN_ADMIN_ADDR.to_string(), + }; + let native_coin_registry_contract = contract_instantiate( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + astroport_native_coin_registry_code_id, + &serde_json::to_string(&native_coin_registry_instantiate_msg).unwrap(), + "native-coin-registry", + None, + "", + )?; + info!( + "Native coin registry contract: {:?}", + native_coin_registry_contract.address + ); + + let atom_denom = test_ctx.get_native_denom().src(GAIA_CHAIN_NAME).get(); + let neutron_denom = test_ctx.get_native_denom().src(NEUTRON_CHAIN_NAME).get(); + let atom_on_neutron = test_ctx.get_ibc_denom(&atom_denom, GAIA_CHAIN_NAME, NEUTRON_CHAIN_NAME); + let neutron_on_atom = + test_ctx.get_ibc_denom(&neutron_denom, NEUTRON_CHAIN_NAME, GAIA_CHAIN_NAME); + + let add_to_registry_msg = NativeCoinRegistryExecuteMsg::Add { + native_coins: vec![(atom_on_neutron.clone(), 6), (neutron_denom.clone(), 6)], + }; + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &native_coin_registry_contract.address, + DEFAULT_KEY, + &serde_json::to_string(&add_to_registry_msg).unwrap(), + EXECUTE_FLAGS, + )?; + thread::sleep(Duration::from_secs(3)); + + let factory_instantiate_msg = FactoryInstantiateMsg { + pair_configs: vec![PairConfig { + code_id: astroport_pair_stable_code_id, + pair_type: PairType::Stable {}, + total_fee_bps: 0, + maker_fee_bps: 0, + is_disabled: false, + is_generator_disabled: true, + permissioned: false, + }], + token_code_id: astroport_token_code_id, + fee_address: None, + generator_address: None, + owner: NEUTRON_CHAIN_ADMIN_ADDR.to_string(), + whitelist_code_id: astroport_whitelist_code_id, + coin_registry_address: native_coin_registry_contract.address.to_string(), + tracker_config: None, + }; + let factory_contract = contract_instantiate( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + astroport_factory_code_id, + &serde_json::to_string(&factory_instantiate_msg).unwrap(), + "astroport-factory", + None, + "", + )?; + info!("Factory contract: {:?}", factory_contract.address); + + let create_pair_msg = astroport::factory::ExecuteMsg::CreatePair { + pair_type: PairType::Stable {}, + asset_infos: vec![ + AssetInfo::NativeToken { + denom: atom_on_neutron.clone(), + }, + AssetInfo::NativeToken { + denom: neutron_denom.clone(), + }, + ], + init_params: Some(Binary::from( + serde_json::to_vec(&StablePoolParams { + amp: 3, + owner: None, + }) + .unwrap(), + )), + }; + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &factory_contract.address, + DEFAULT_KEY, + &serde_json::to_string(&create_pair_msg).unwrap(), + EXECUTE_FLAGS, + )?; + + // Send some ATOM to NTRN + let amount_to_send = 5_000_000_000; + loop { + test_ctx + .build_tx_transfer() + .with_chain_name(GAIA_CHAIN_NAME) + .with_amount(amount_to_send) + .with_recipient(NEUTRON_CHAIN_ADMIN_ADDR) + .with_denom(&atom_denom) + .send() + .unwrap(); + + info!("Waiting to receive IBC transfer..."); + thread::sleep(Duration::from_secs(5)); + let balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + NEUTRON_CHAIN_ADMIN_ADDR, + ); + if balance + .iter() + .any(|c| c.denom == atom_on_neutron && c.amount >= Uint128::new(amount_to_send)) + { + break; + } + } + + // Provide the ATOM/NTRN liquidity to the pair + let pool_addr = get_pool_address( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &factory_contract.address, + AssetInfo::NativeToken { + denom: atom_on_neutron.clone(), + }, + AssetInfo::NativeToken { + denom: neutron_denom.clone(), + }, + ); + + let uatom_contribution_amount: u128 = 5_000_000_000; + let untrn_contribution_amount: u128 = 50_000_000_000; + let provide_liquidity_msg = astroport::pair::ExecuteMsg::ProvideLiquidity { + assets: vec![ + Asset { + info: AssetInfo::NativeToken { + denom: atom_on_neutron.clone(), + }, + amount: Uint128::from(uatom_contribution_amount), + }, + Asset { + info: AssetInfo::NativeToken { + denom: neutron_denom.clone(), + }, + amount: Uint128::from(untrn_contribution_amount), + }, + ], + slippage_tolerance: Some(Decimal::percent(1)), + auto_stake: Some(false), + receiver: Some(NEUTRON_CHAIN_ADMIN_ADDR.to_string()), + min_lp_to_receive: None, + }; + + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &pool_addr, + DEFAULT_KEY, + &serde_json::to_string(&provide_liquidity_msg).unwrap(), + &format!("--amount {uatom_contribution_amount}{atom_on_neutron},{untrn_contribution_amount}{neutron_denom} {EXECUTE_FLAGS}"), + ).unwrap(); + thread::sleep(Duration::from_secs(3)); + + let valence_ibc_forwarder_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_ibc_forwarder") + .unwrap(); + + let valence_two_party_pol_holder_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_two_party_pol_holder") + .unwrap(); + + let valence_clock_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_clock") + .unwrap(); + + let valence_interchain_router_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_interchain_router") + .unwrap(); + + let valence_native_router_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_native_router") + .unwrap(); + + let valence_astroport_tf_liquid_pooler_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_astroport_tf_liquid_pooler") + .unwrap(); + + let valence_covenant_two_party_pol_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_covenant_two_party_pol") + .unwrap(); + + let current_block_height = Chain::new( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ) + .get_height(); + + // Instantiate the covenants + let target = "Two party POL native happy path"; + info!(target: target,"Starting Two party POL happy path test..."); + let covenant_instantiate_msg = valence_covenant_two_party_pol::msg::InstantiateMsg { + label: "two-party-pol-covenant-happy".to_string(), + timeouts: Timeouts { + ica_timeout: Uint64::new(10000), // seconds + ibc_transfer_timeout: Uint64::new(10000), // seconds + }, + contract_codes: CovenantContractCodeIds { + ibc_forwarder_code: valence_ibc_forwarder_code_id, + holder_code: valence_two_party_pol_holder_code_id, + clock_code: valence_clock_code_id, + interchain_router_code: valence_interchain_router_code_id, + native_router_code: valence_native_router_code_id, + liquid_pooler_code: valence_astroport_tf_liquid_pooler_code_id, + }, + clock_tick_max_gas: None, + lockup_config: Expiration::AtHeight(current_block_height + 130), + party_a_config: CovenantPartyConfig::Interchain(InterchainCovenantParty { + party_receiver_addr: ACC1_ADDRESS_GAIA.to_string(), + party_chain_connection_id: test_ctx + .get_connections() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + ibc_transfer_timeout: Uint64::new(10000), + party_to_host_chain_channel_id: test_ctx + .get_transfer_channels() + .src(GAIA_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + host_to_party_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + remote_chain_denom: atom_denom.clone(), + addr: ACC1_ADDRESS_NEUTRON.to_string(), + native_denom: atom_on_neutron.clone(), + contribution: Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }, + denom_to_pfm_map: BTreeMap::new(), + fallback_address: None, + }), + party_b_config: CovenantPartyConfig::Native(NativeCovenantParty { + party_receiver_addr: ACC2_ADDRESS_NEUTRON.to_string(), + native_denom: neutron_denom.clone(), + addr: ACC2_ADDRESS_NEUTRON.to_string(), + contribution: Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(untrn_contribution_amount), + }, + }), + covenant_type: CovenantType::Share, + ragequit_config: Some(RagequitConfig::Enabled(RagequitTerms { + penalty: Decimal::from_str("0.1").unwrap(), + state: None, + })), + deposit_deadline: Expiration::AtHeight(current_block_height + 110), + party_a_share: Decimal::percent(50), + party_b_share: Decimal::percent(50), + pool_price_config: PoolPriceConfig { + expected_spot_price: Decimal::from_str("0.1").unwrap(), + acceptable_price_spread: Decimal::from_str("0.09").unwrap(), + }, + splits: BTreeMap::from([ + ( + atom_on_neutron.clone(), + SplitConfig { + receivers: BTreeMap::from([ + (ACC1_ADDRESS_GAIA.to_string(), Decimal::percent(50)), + (ACC2_ADDRESS_NEUTRON.to_string(), Decimal::percent(50)), + ]), + }, + ), + ( + neutron_denom.clone(), + SplitConfig { + receivers: BTreeMap::from([ + (ACC1_ADDRESS_GAIA.to_string(), Decimal::percent(50)), + (ACC2_ADDRESS_NEUTRON.to_string(), Decimal::percent(50)), + ]), + }, + ), + ]), + fallback_split: None, + emergency_committee: None, + liquid_pooler_config: valence_covenant_two_party_pol::msg::LiquidPoolerConfig::Astroport( + AstroportLiquidPoolerConfig { + pool_pair_type: astroport_old::factory::PairType::Stable {}, + pool_address: pool_addr.to_string(), + asset_a_denom: atom_on_neutron.clone(), + asset_b_denom: neutron_denom.clone(), + single_side_lp_limits: SingleSideLpLimits { + asset_a_limit: Uint128::new(100000), + asset_b_limit: Uint128::new(100000), + }, + }, + ), + fallback_address: None, + }; + + let covenant_contract = contract_instantiate( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + valence_covenant_two_party_pol_code_id, + &serde_json::to_string(&covenant_instantiate_msg).unwrap(), + "two-party-pol-covenant-happy-path", + None, + "", + )?; + info!(target: target,"Covenant contract: {:?}", covenant_contract.address); + let covenant = Covenant::TwoPartyPol { + rb: test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + contract_address: &covenant_contract.address, + }; + + // Query the covenant addresses + let clock_address = covenant.query_clock_address(); + let holder_address = covenant.query_holder_address(); + let liquid_pooler_address = covenant.query_liquid_pooler_address(); + let party_a_router_address = covenant.query_interchain_router_address("party_a".to_string()); + let party_b_router_address = covenant.query_interchain_router_address("party_b".to_string()); + let party_a_ibc_forwarder_address = covenant.query_ibc_forwarder_address("party_a".to_string()); + let party_b_ibc_forwarder_address = covenant.query_ibc_forwarder_address("party_b".to_string()); + + info!(target: target,"Fund covenant addresses with NTRN..."); + let mut addresses = vec![ + clock_address.clone(), + holder_address.clone(), + liquid_pooler_address.clone(), + party_a_router_address.clone(), + party_b_router_address.clone(), + ]; + if !party_a_ibc_forwarder_address.is_empty() { + addresses.push(party_a_ibc_forwarder_address.clone()); + } + if !party_b_ibc_forwarder_address.is_empty() { + addresses.push(party_b_ibc_forwarder_address.clone()); + } + for address in &addresses { + send( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + address, + &[Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000000000), + }], + &Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + } + + info!(target: target,"Tick until forwarders create ICA..."); + let party_a_deposit_address; + let party_b_deposit_address; + loop { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + let forwarder_a_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_a_ibc_forwarder_address, + ); + info!(target: target,"Forwarder A state: {:?}", forwarder_a_state); + if forwarder_a_state == "ica_created" { + party_a_deposit_address = covenant.query_deposit_address("party_a".to_string()); + party_b_deposit_address = covenant.query_deposit_address("party_b".to_string()); + break; + } + } + + info!(target: target,"Party A deposit address: {}", party_a_deposit_address); + info!(target: target,"Party B deposit address: {}", party_b_deposit_address); + + info!(target: target,"Fund the forwarders with sufficient funds..."); + send( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + DEFAULT_KEY, + &party_a_deposit_address, + &[Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }], + &Coin { + denom: atom_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + send( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &party_b_deposit_address, + &[Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(untrn_contribution_amount), + }], + &Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + + info!(target: target,"Tick until forwarders forward the funds to the holder..."); + loop { + let holder_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + let holder_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + if holder_balance.iter().any(|c| { + c.denom == atom_on_neutron.clone() + && c.amount >= Uint128::new(uatom_contribution_amount) + }) && holder_balance.iter().any(|c| { + c.denom == neutron_denom.clone() && c.amount >= Uint128::new(untrn_contribution_amount) + }) { + info!(target: target,"Holder received ATOM & NTRN"); + break; + } else if holder_state == "active" { + info!(target: target,"Holder is active"); + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!(target: target,"Tick until holder sends funds to LiquidPooler and LPer receives LP tokens..."); + + loop { + let balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + ); + + let liquid_pooler_lp_balance = balance + .iter() + .find(|c| c.denom.starts_with("factory")) + .unwrap_or(&Coin { + denom: "".to_string(), + amount: Uint128::zero(), + }) + .amount; + + if liquid_pooler_lp_balance.is_zero() { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } else { + break; + } + } + + info!(target: target,"Tick until holder expires..."); + loop { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + let holder_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + info!(target: target,"Holder state: {:?}", holder_state); + if holder_state == "expired" { + break; + } + } + + info!(target: target,"Party A claims and router receives the funds"); + let router_a_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_a_router_address, + ); + info!(target: target,"Router A balances: {:?}", router_a_balances); + let router_b_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_b_router_address, + ); + info!(target: target,"Router B balances: {:?}", router_b_balances); + let holder_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + info!(target: target,"Holder balances: {:?}", holder_balances); + + thread::sleep(Duration::from_secs(10)); + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ACC_1_KEY, + &serde_json::to_string(&valence_two_party_pol_holder::msg::ExecuteMsg::Claim {}).unwrap(), + EXECUTE_FLAGS, + ) + .unwrap(); + thread::sleep(Duration::from_secs(5)); + + let router_a_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_a_router_address, + ); + info!(target: target,"Router A balances: {:?}", router_a_balances); + let router_b_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_b_router_address, + ); + info!(target: target,"Router B balances: {:?}", router_b_balances); + let holder_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + info!(target: target,"Holder balances: {:?}", holder_balances); + + info!(target: target,"Tick until party A claim is distributed"); + info!(target: target,"Hub receiver address: {}", ACC1_ADDRESS_GAIA); + loop { + let router_a_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_a_router_address, + ); + info!(target: target,"Router A balances: {:?}", router_a_balances); + let router_b_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_b_router_address, + ); + info!(target: target,"Router B balances: {:?}", router_b_balances); + let holder_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + info!(target: target,"Holder balances: {:?}", holder_balances); + let hub_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + ACC1_ADDRESS_GAIA, + ); + info!(target: target,"Hub receiver balances: {:?}", hub_receiver_balances); + let neutron_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ACC2_ADDRESS_NEUTRON, + ); + info!(target: target,"Neutron receiver balances: {:?}", neutron_receiver_balances); + if hub_receiver_balances + .iter() + .any(|c| c.denom == atom_denom.clone()) + && hub_receiver_balances + .iter() + .any(|c| c.denom == neutron_on_atom.clone()) + { + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!(target: target,"Party B claims and router receives the funds"); + let router_b_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_b_router_address, + ); + info!(target: target,"Router B balances: {:?}", router_b_balances); + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ACC_2_KEY, + &serde_json::to_string(&valence_two_party_pol_holder::msg::ExecuteMsg::Claim {}).unwrap(), + EXECUTE_FLAGS, + ) + .unwrap(); + thread::sleep(Duration::from_secs(5)); + + info!(target: target,"Tick until both parties receive their funds"); + loop { + let hub_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + ACC1_ADDRESS_GAIA, + ); + info!(target: target,"Hub receiver balances: {:?}", hub_receiver_balances); + let neutron_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ACC2_ADDRESS_NEUTRON, + ); + info!(target: target,"Neutron receiver balances: {:?}", neutron_receiver_balances); + if neutron_receiver_balances + .iter() + .any(|c| c.denom == neutron_denom.clone()) + && neutron_receiver_balances + .iter() + .any(|c| c.denom == atom_on_neutron.clone()) + { + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + // Send the balances back so we have a fresh start for the next test + send_non_native_balances( + test_ctx, + GAIA_CHAIN_NAME, + ACC_1_KEY, + ACC1_ADDRESS_GAIA, + NEUTRON_CHAIN_ADMIN_ADDR, + &atom_denom, + ); + + send_non_native_balances( + test_ctx, + NEUTRON_CHAIN_NAME, + ACC_2_KEY, + ACC2_ADDRESS_NEUTRON, + GAIA_CHAIN_ADMIN_ADDR, + &neutron_denom, + ); + + let current_block_height = Chain::new( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ) + .get_height(); + + let target = "Two party POL native share based ragequit path"; + info!(target: target,"Starting Two party POL share based ragequit path tests..."); + let covenant_instantiate_msg = valence_covenant_two_party_pol::msg::InstantiateMsg { + label: "two-party-pol-covenant-ragequit".to_string(), + timeouts: Timeouts { + ica_timeout: Uint64::new(100), // seconds + ibc_transfer_timeout: Uint64::new(100), // seconds + }, + contract_codes: CovenantContractCodeIds { + ibc_forwarder_code: valence_ibc_forwarder_code_id, + holder_code: valence_two_party_pol_holder_code_id, + clock_code: valence_clock_code_id, + interchain_router_code: valence_interchain_router_code_id, + native_router_code: valence_native_router_code_id, + liquid_pooler_code: valence_astroport_tf_liquid_pooler_code_id, + }, + clock_tick_max_gas: None, + lockup_config: Expiration::AtHeight(current_block_height + 300), + party_a_config: CovenantPartyConfig::Interchain(InterchainCovenantParty { + party_receiver_addr: ACC1_ADDRESS_GAIA.to_string(), + party_chain_connection_id: test_ctx + .get_connections() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + ibc_transfer_timeout: Uint64::new(10000), + party_to_host_chain_channel_id: test_ctx + .get_transfer_channels() + .src(GAIA_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + host_to_party_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + remote_chain_denom: atom_denom.clone(), + addr: ACC1_ADDRESS_NEUTRON.to_string(), + native_denom: atom_on_neutron.clone(), + contribution: Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }, + denom_to_pfm_map: BTreeMap::new(), + fallback_address: None, + }), + party_b_config: CovenantPartyConfig::Native(NativeCovenantParty { + party_receiver_addr: ACC2_ADDRESS_NEUTRON.to_string(), + native_denom: neutron_denom.clone(), + addr: ACC2_ADDRESS_NEUTRON.to_string(), + contribution: Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(untrn_contribution_amount), + }, + }), + covenant_type: CovenantType::Share, + ragequit_config: Some(RagequitConfig::Enabled(RagequitTerms { + penalty: Decimal::from_str("0.1").unwrap(), + state: None, + })), + deposit_deadline: Expiration::AtHeight(current_block_height + 200), + party_a_share: Decimal::percent(50), + party_b_share: Decimal::percent(50), + pool_price_config: PoolPriceConfig { + expected_spot_price: Decimal::from_str("0.1").unwrap(), + acceptable_price_spread: Decimal::from_str("0.09").unwrap(), + }, + splits: BTreeMap::from([ + ( + atom_on_neutron.clone(), + SplitConfig { + receivers: BTreeMap::from([ + (ACC1_ADDRESS_GAIA.to_string(), Decimal::percent(50)), + (ACC2_ADDRESS_NEUTRON.to_string(), Decimal::percent(50)), + ]), + }, + ), + ( + neutron_denom.clone(), + SplitConfig { + receivers: BTreeMap::from([ + (ACC1_ADDRESS_GAIA.to_string(), Decimal::percent(50)), + (ACC2_ADDRESS_NEUTRON.to_string(), Decimal::percent(50)), + ]), + }, + ), + ]), + fallback_split: None, + emergency_committee: None, + liquid_pooler_config: valence_covenant_two_party_pol::msg::LiquidPoolerConfig::Astroport( + AstroportLiquidPoolerConfig { + pool_pair_type: astroport_old::factory::PairType::Stable {}, + pool_address: pool_addr.to_string(), + asset_a_denom: atom_on_neutron.clone(), + asset_b_denom: neutron_denom.clone(), + single_side_lp_limits: SingleSideLpLimits { + asset_a_limit: Uint128::new(100000), + asset_b_limit: Uint128::new(100000), + }, + }, + ), + fallback_address: None, + }; + + let covenant_contract = contract_instantiate( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + valence_covenant_two_party_pol_code_id, + &serde_json::to_string(&covenant_instantiate_msg).unwrap(), + "two-party-pol-covenant-ragequit", + None, + "", + )?; + info!(target: target,"Covenant contract: {:?}", covenant_contract.address); + let covenant = Covenant::TwoPartyPol { + rb: test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + contract_address: &covenant_contract.address, + }; + + // Query the covenant addresses + let clock_address = covenant.query_clock_address(); + let holder_address = covenant.query_holder_address(); + let liquid_pooler_address = covenant.query_liquid_pooler_address(); + let party_a_router_address = covenant.query_interchain_router_address("party_a".to_string()); + let party_b_router_address = covenant.query_interchain_router_address("party_b".to_string()); + let party_a_ibc_forwarder_address = covenant.query_ibc_forwarder_address("party_a".to_string()); + let party_b_ibc_forwarder_address = covenant.query_ibc_forwarder_address("party_b".to_string()); + + info!(target: target,"Fund covenant addresses with NTRN..."); + let mut addresses = vec![ + clock_address.clone(), + holder_address.clone(), + liquid_pooler_address.clone(), + party_a_router_address.clone(), + party_b_router_address.clone(), + ]; + if !party_a_ibc_forwarder_address.is_empty() { + addresses.push(party_a_ibc_forwarder_address.clone()); + } + if !party_b_ibc_forwarder_address.is_empty() { + addresses.push(party_b_ibc_forwarder_address.clone()); + } + for address in &addresses { + send( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + address, + &[Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000000000), + }], + &Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + } + + info!(target: target,"Tick until forwarders create ICA..."); + let party_a_deposit_address; + let party_b_deposit_address; + loop { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + let forwarder_a_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_a_ibc_forwarder_address, + ); + info!(target: target,"Forwarder A state: {:?}", forwarder_a_state); + if forwarder_a_state == "ica_created" { + party_a_deposit_address = covenant.query_deposit_address("party_a".to_string()); + party_b_deposit_address = covenant.query_deposit_address("party_b".to_string()); + break; + } + } + + info!(target: target,"Party A deposit address: {}", party_a_deposit_address); + info!(target: target,"Party B deposit address: {}", party_b_deposit_address); + + info!(target: target,"Fund the forwarders with sufficient funds..."); + send( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + DEFAULT_KEY, + &party_a_deposit_address, + &[Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }], + &Coin { + denom: atom_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + send( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &party_b_deposit_address, + &[Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(untrn_contribution_amount), + }], + &Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + + info!(target: target,"Tick until forwarders forward the funds to the holder..."); + loop { + let holder_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + let holder_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + if holder_balance.iter().any(|c| { + c.denom == atom_on_neutron.clone() + && c.amount >= Uint128::new(uatom_contribution_amount) + }) && holder_balance.iter().any(|c| { + c.denom == neutron_denom.clone() && c.amount >= Uint128::new(untrn_contribution_amount) + }) { + info!(target: target,"Holder received ATOM & NTRN"); + break; + } else if holder_state == "active" { + info!(target: target,"Holder is active"); + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!(target: target,"Tick until holder sends funds to LiquidPooler and LPer receives LP tokens..."); + loop { + let balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + ); + + let liquid_pooler_lp_balance = balance + .iter() + .find(|c| c.denom.starts_with("factory")) + .unwrap_or(&Coin { + denom: "".to_string(), + amount: Uint128::zero(), + }) + .amount; + + if liquid_pooler_lp_balance.is_zero() { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } else { + break; + } + } + + info!(target: target,"Party A ragequits..."); + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ACC_1_KEY, + &serde_json::to_string(&valence_two_party_pol_holder::msg::ExecuteMsg::Ragequit {}) + .unwrap(), + EXECUTE_FLAGS, + ) + .unwrap(); + + info!(target: target,"Party B claims and router receives the funds"); + let router_b_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_b_router_address, + ); + info!(target: target,"Router B balances: {:?}", router_b_balances); + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ACC_2_KEY, + &serde_json::to_string(&valence_two_party_pol_holder::msg::ExecuteMsg::Claim {}).unwrap(), + EXECUTE_FLAGS, + ) + .unwrap(); + thread::sleep(Duration::from_secs(5)); + + info!(target: target,"Tick routers until both parties receive their funds"); + loop { + let hub_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + ACC1_ADDRESS_GAIA, + ); + info!(target: target,"Hub receiver balances: {:?}", hub_receiver_balances); + let neutron_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ACC2_ADDRESS_NEUTRON, + ); + info!(target: target,"Neutron receiver balances: {:?}", neutron_receiver_balances); + if neutron_receiver_balances + .iter() + .any(|c| c.denom == neutron_denom.clone()) + && neutron_receiver_balances + .iter() + .any(|c| c.denom == atom_on_neutron.clone()) + { + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + // Send the balances back so we have a fresh start for the next test + send_non_native_balances( + test_ctx, + GAIA_CHAIN_NAME, + ACC_1_KEY, + ACC1_ADDRESS_GAIA, + NEUTRON_CHAIN_ADMIN_ADDR, + &atom_denom, + ); + + send_non_native_balances( + test_ctx, + NEUTRON_CHAIN_NAME, + ACC_2_KEY, + ACC2_ADDRESS_NEUTRON, + GAIA_CHAIN_ADMIN_ADDR, + &neutron_denom, + ); + + let current_block_height = Chain::new( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ) + .get_height(); + + let target = "Two party POL native side based ragequit path"; + info!(target: target,"Starting Two party POL side based ragequit path tests..."); + let covenant_instantiate_msg = valence_covenant_two_party_pol::msg::InstantiateMsg { + label: "two-party-pol-covenant-side-ragequit".to_string(), + timeouts: Timeouts { + ica_timeout: Uint64::new(100), // seconds + ibc_transfer_timeout: Uint64::new(100), // seconds + }, + contract_codes: CovenantContractCodeIds { + ibc_forwarder_code: valence_ibc_forwarder_code_id, + holder_code: valence_two_party_pol_holder_code_id, + clock_code: valence_clock_code_id, + interchain_router_code: valence_interchain_router_code_id, + native_router_code: valence_native_router_code_id, + liquid_pooler_code: valence_astroport_tf_liquid_pooler_code_id, + }, + clock_tick_max_gas: None, + lockup_config: Expiration::AtHeight(current_block_height + 300), + party_a_config: CovenantPartyConfig::Interchain(InterchainCovenantParty { + party_receiver_addr: ACC1_ADDRESS_GAIA.to_string(), + party_chain_connection_id: test_ctx + .get_connections() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + ibc_transfer_timeout: Uint64::new(10000), + party_to_host_chain_channel_id: test_ctx + .get_transfer_channels() + .src(GAIA_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + host_to_party_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + remote_chain_denom: atom_denom.clone(), + addr: ACC1_ADDRESS_NEUTRON.to_string(), + native_denom: atom_on_neutron.clone(), + contribution: Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }, + denom_to_pfm_map: BTreeMap::new(), + fallback_address: None, + }), + party_b_config: CovenantPartyConfig::Native(NativeCovenantParty { + party_receiver_addr: ACC2_ADDRESS_NEUTRON.to_string(), + native_denom: neutron_denom.clone(), + addr: ACC2_ADDRESS_NEUTRON.to_string(), + contribution: Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(untrn_contribution_amount), + }, + }), + covenant_type: CovenantType::Side, + ragequit_config: Some(RagequitConfig::Enabled(RagequitTerms { + penalty: Decimal::from_str("0.1").unwrap(), + state: None, + })), + deposit_deadline: Expiration::AtHeight(current_block_height + 200), + party_a_share: Decimal::percent(50), + party_b_share: Decimal::percent(50), + pool_price_config: PoolPriceConfig { + expected_spot_price: Decimal::from_str("0.1").unwrap(), + acceptable_price_spread: Decimal::from_str("0.09").unwrap(), + }, + splits: BTreeMap::from([ + ( + atom_on_neutron.clone(), + SplitConfig { + receivers: BTreeMap::from([ + (ACC1_ADDRESS_GAIA.to_string(), Decimal::percent(100)), + (ACC2_ADDRESS_NEUTRON.to_string(), Decimal::percent(0)), + ]), + }, + ), + ( + neutron_denom.clone(), + SplitConfig { + receivers: BTreeMap::from([ + (ACC1_ADDRESS_GAIA.to_string(), Decimal::percent(0)), + (ACC2_ADDRESS_NEUTRON.to_string(), Decimal::percent(100)), + ]), + }, + ), + ]), + fallback_split: None, + emergency_committee: None, + liquid_pooler_config: valence_covenant_two_party_pol::msg::LiquidPoolerConfig::Astroport( + AstroportLiquidPoolerConfig { + pool_pair_type: astroport_old::factory::PairType::Stable {}, + pool_address: pool_addr.to_string(), + asset_a_denom: atom_on_neutron.clone(), + asset_b_denom: neutron_denom.clone(), + single_side_lp_limits: SingleSideLpLimits { + asset_a_limit: Uint128::new(100000), + asset_b_limit: Uint128::new(100000), + }, + }, + ), + fallback_address: None, + }; + + let covenant_contract = contract_instantiate( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + valence_covenant_two_party_pol_code_id, + &serde_json::to_string(&covenant_instantiate_msg).unwrap(), + "two-party-pol-covenant-side-ragequit", + None, + "", + )?; + info!(target: target,"Covenant contract: {:?}", covenant_contract.address); + let covenant = Covenant::TwoPartyPol { + rb: test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + contract_address: &covenant_contract.address, + }; + + // Query the covenant addresses + let clock_address = covenant.query_clock_address(); + let holder_address = covenant.query_holder_address(); + let liquid_pooler_address = covenant.query_liquid_pooler_address(); + let party_a_router_address = covenant.query_interchain_router_address("party_a".to_string()); + let party_b_router_address = covenant.query_interchain_router_address("party_b".to_string()); + let party_a_ibc_forwarder_address = covenant.query_ibc_forwarder_address("party_a".to_string()); + let party_b_ibc_forwarder_address = covenant.query_ibc_forwarder_address("party_b".to_string()); + + info!(target: target,"Fund covenant addresses with NTRN..."); + let mut addresses = vec![ + clock_address.clone(), + holder_address.clone(), + liquid_pooler_address.clone(), + party_a_router_address.clone(), + party_b_router_address.clone(), + ]; + if !party_a_ibc_forwarder_address.is_empty() { + addresses.push(party_a_ibc_forwarder_address.clone()); + } + if !party_b_ibc_forwarder_address.is_empty() { + addresses.push(party_b_ibc_forwarder_address.clone()); + } + for address in &addresses { + send( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + address, + &[Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000000000), + }], + &Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + } + + let party_a_deposit_address; + let party_b_deposit_address; + loop { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + let forwarder_a_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_a_ibc_forwarder_address, + ); + info!(target: target,"Forwarder A state: {:?}", forwarder_a_state); + if forwarder_a_state == "ica_created" { + party_a_deposit_address = covenant.query_deposit_address("party_a".to_string()); + party_b_deposit_address = covenant.query_deposit_address("party_b".to_string()); + break; + } + } + + info!(target: target,"Party A deposit address: {}", party_a_deposit_address); + info!(target: target,"Party B deposit address: {}", party_b_deposit_address); + + info!(target: target,"Fund the forwarders with sufficient funds..."); + send( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + DEFAULT_KEY, + &party_a_deposit_address, + &[Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }], + &Coin { + denom: atom_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + send( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &party_b_deposit_address, + &[Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(untrn_contribution_amount), + }], + &Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + + info!(target: target,"Tick until forwarders forward the funds to the holder..."); + loop { + let holder_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + let holder_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + if holder_balance.iter().any(|c| { + c.denom == atom_on_neutron.clone() + && c.amount >= Uint128::new(uatom_contribution_amount) + }) && holder_balance.iter().any(|c| { + c.denom == neutron_denom.clone() && c.amount >= Uint128::new(untrn_contribution_amount) + }) { + info!(target: target,"Holder received ATOM & NTRN"); + break; + } else if holder_state == "active" { + info!(target: target,"Holder is active"); + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!(target: target,"Tick until holder sends funds to LiquidPooler and LPer receives LP tokens..."); + + loop { + let balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + ); + + let liquid_pooler_lp_balance = balance + .iter() + .find(|c| c.denom.starts_with("factory")) + .unwrap_or(&Coin { + denom: "".to_string(), + amount: Uint128::zero(), + }) + .amount; + + if liquid_pooler_lp_balance.is_zero() { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } else { + break; + } + } + + let previous_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ACC2_ADDRESS_NEUTRON, + ); + info!(target: target,"Party A ragequits..."); + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ACC_1_KEY, + &serde_json::to_string(&valence_two_party_pol_holder::msg::ExecuteMsg::Ragequit {}) + .unwrap(), + EXECUTE_FLAGS, + ) + .unwrap(); + + info!(target: target,"Tick routers until both parties receive their funds"); + loop { + let router_a_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_a_router_address, + ); + info!(target: target,"Router A balances: {:?}", router_a_balances); + let router_b_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_b_router_address, + ); + info!(target: target,"Router B balances: {:?}", router_b_balances); + let hub_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + ACC1_ADDRESS_GAIA, + ); + info!(target: target,"Hub receiver balances: {:?}", hub_receiver_balances); + let neutron_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ACC2_ADDRESS_NEUTRON, + ); + info!(target: target,"Neutron receiver balances: {:?}", neutron_receiver_balances); + if previous_balance != neutron_receiver_balances + && neutron_receiver_balances + .iter() + .any(|c| c.denom == atom_on_neutron.clone()) + { + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + // Send the balances back so we have a fresh start for the next test + send_non_native_balances( + test_ctx, + GAIA_CHAIN_NAME, + ACC_1_KEY, + ACC1_ADDRESS_GAIA, + NEUTRON_CHAIN_ADMIN_ADDR, + &atom_denom, + ); + + send_non_native_balances( + test_ctx, + NEUTRON_CHAIN_NAME, + ACC_2_KEY, + ACC2_ADDRESS_NEUTRON, + GAIA_CHAIN_ADMIN_ADDR, + &neutron_denom, + ); + + let current_block_height = Chain::new( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ) + .get_height(); + + let target = "Two party POL native side based happy path"; + info!(target: target,"Starting Two party POL side based happy path tests"); + let covenant_instantiate_msg = valence_covenant_two_party_pol::msg::InstantiateMsg { + label: "two-party-pol-covenant-side-happy".to_string(), + timeouts: Timeouts { + ica_timeout: Uint64::new(100), // seconds + ibc_transfer_timeout: Uint64::new(100), // seconds + }, + contract_codes: CovenantContractCodeIds { + ibc_forwarder_code: valence_ibc_forwarder_code_id, + holder_code: valence_two_party_pol_holder_code_id, + clock_code: valence_clock_code_id, + interchain_router_code: valence_interchain_router_code_id, + native_router_code: valence_native_router_code_id, + liquid_pooler_code: valence_astroport_tf_liquid_pooler_code_id, + }, + clock_tick_max_gas: None, + lockup_config: Expiration::AtHeight(current_block_height + 200), + party_a_config: CovenantPartyConfig::Interchain(InterchainCovenantParty { + party_receiver_addr: ACC1_ADDRESS_GAIA.to_string(), + party_chain_connection_id: test_ctx + .get_connections() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + ibc_transfer_timeout: Uint64::new(10000), + party_to_host_chain_channel_id: test_ctx + .get_transfer_channels() + .src(GAIA_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + host_to_party_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + remote_chain_denom: atom_denom.clone(), + addr: ACC1_ADDRESS_NEUTRON.to_string(), + native_denom: atom_on_neutron.clone(), + contribution: Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }, + denom_to_pfm_map: BTreeMap::new(), + fallback_address: None, + }), + party_b_config: CovenantPartyConfig::Native(NativeCovenantParty { + party_receiver_addr: ACC2_ADDRESS_NEUTRON.to_string(), + native_denom: neutron_denom.clone(), + addr: ACC2_ADDRESS_NEUTRON.to_string(), + contribution: Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(untrn_contribution_amount), + }, + }), + covenant_type: CovenantType::Side, + ragequit_config: Some(RagequitConfig::Enabled(RagequitTerms { + penalty: Decimal::from_str("0.1").unwrap(), + state: None, + })), + deposit_deadline: Expiration::AtHeight(current_block_height + 180), + party_a_share: Decimal::percent(50), + party_b_share: Decimal::percent(50), + pool_price_config: PoolPriceConfig { + expected_spot_price: Decimal::from_str("0.1").unwrap(), + acceptable_price_spread: Decimal::from_str("0.09").unwrap(), + }, + splits: BTreeMap::from([ + ( + atom_on_neutron.clone(), + SplitConfig { + receivers: BTreeMap::from([ + (ACC1_ADDRESS_GAIA.to_string(), Decimal::percent(100)), + (ACC2_ADDRESS_NEUTRON.to_string(), Decimal::percent(0)), + ]), + }, + ), + ( + neutron_denom.clone(), + SplitConfig { + receivers: BTreeMap::from([ + (ACC1_ADDRESS_GAIA.to_string(), Decimal::percent(0)), + (ACC2_ADDRESS_NEUTRON.to_string(), Decimal::percent(100)), + ]), + }, + ), + ]), + fallback_split: None, + emergency_committee: None, + liquid_pooler_config: valence_covenant_two_party_pol::msg::LiquidPoolerConfig::Astroport( + AstroportLiquidPoolerConfig { + pool_pair_type: astroport_old::factory::PairType::Stable {}, + pool_address: pool_addr.to_string(), + asset_a_denom: atom_on_neutron.clone(), + asset_b_denom: neutron_denom.clone(), + single_side_lp_limits: SingleSideLpLimits { + asset_a_limit: Uint128::new(100000), + asset_b_limit: Uint128::new(100000), + }, + }, + ), + fallback_address: None, + }; + + let covenant_contract = contract_instantiate( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + valence_covenant_two_party_pol_code_id, + &serde_json::to_string(&covenant_instantiate_msg).unwrap(), + "two-party-pol-covenant-side-happy", + None, + "", + )?; + info!(target: target,"Covenant contract: {:?}", covenant_contract.address); + let covenant = Covenant::TwoPartyPol { + rb: test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + contract_address: &covenant_contract.address, + }; + + // Query the covenant addresses + let clock_address = covenant.query_clock_address(); + let holder_address = covenant.query_holder_address(); + let liquid_pooler_address = covenant.query_liquid_pooler_address(); + let party_a_router_address = covenant.query_interchain_router_address("party_a".to_string()); + let party_b_router_address = covenant.query_interchain_router_address("party_b".to_string()); + let party_a_ibc_forwarder_address = covenant.query_ibc_forwarder_address("party_a".to_string()); + let party_b_ibc_forwarder_address = covenant.query_ibc_forwarder_address("party_b".to_string()); + + info!(target: target,"Fund covenant addresses with NTRN..."); + let mut addresses = vec![ + clock_address.clone(), + holder_address.clone(), + liquid_pooler_address.clone(), + party_a_router_address.clone(), + party_b_router_address.clone(), + ]; + if !party_a_ibc_forwarder_address.is_empty() { + addresses.push(party_a_ibc_forwarder_address.clone()); + } + if !party_b_ibc_forwarder_address.is_empty() { + addresses.push(party_b_ibc_forwarder_address.clone()); + } + for address in &addresses { + send( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + address, + &[Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000000000), + }], + &Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + } + + info!(target: target,"Tick until forwarders create ICA..."); + let party_a_deposit_address; + let party_b_deposit_address; + loop { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + let forwarder_a_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_a_ibc_forwarder_address, + ); + info!(target: target,"Forwarder A state: {:?}", forwarder_a_state); + if forwarder_a_state == "ica_created" { + party_a_deposit_address = covenant.query_deposit_address("party_a".to_string()); + party_b_deposit_address = covenant.query_deposit_address("party_b".to_string()); + break; + } + } + + info!(target: target,"Party A deposit address: {}", party_a_deposit_address); + info!(target: target,"Party B deposit address: {}", party_b_deposit_address); + + info!(target: target,"Fund the forwarders with sufficient funds..."); + send( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + DEFAULT_KEY, + &party_a_deposit_address, + &[Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }], + &Coin { + denom: atom_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + send( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &party_b_deposit_address, + &[Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(untrn_contribution_amount), + }], + &Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + + info!(target: target,"Tick until forwarders forward the funds to the holder..."); + loop { + let holder_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + let holder_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + if holder_balance.iter().any(|c| { + c.denom == atom_on_neutron.clone() + && c.amount >= Uint128::new(uatom_contribution_amount) + }) && holder_balance.iter().any(|c| { + c.denom == neutron_denom.clone() && c.amount >= Uint128::new(untrn_contribution_amount) + }) { + info!(target: target,"Holder received ATOM & NTRN"); + break; + } else if holder_state == "active" { + info!(target: target,"Holder is active"); + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!(target: target,"Tick until holder sends funds to LiquidPooler and LPer receives LP tokens..."); + + loop { + let balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + ); + + let liquid_pooler_lp_balance = balance + .iter() + .find(|c| c.denom.starts_with("factory")) + .unwrap_or(&Coin { + denom: "".to_string(), + amount: Uint128::zero(), + }) + .amount; + + if liquid_pooler_lp_balance.is_zero() { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } else { + break; + } + } + + info!(target: target,"Tick until holder expires..."); + loop { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + let holder_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + info!(target: target,"Holder state: {:?}", holder_state); + if holder_state == "expired" { + break; + } + } + + info!(target: target,"Party A claims and router receives the funds"); + let router_a_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_a_router_address, + ); + info!(target: target,"Router A balances: {:?}", router_a_balances); + let router_b_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_b_router_address, + ); + info!(target: target,"Router B balances: {:?}", router_b_balances); + let hub_receiver_balances_before_claim = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + ACC1_ADDRESS_GAIA, + ); + info!(target: target, + "Hub receiver balances before claim: {:?}", + hub_receiver_balances_before_claim + ); + let neutron_receiver_balances_before_claim = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ACC2_ADDRESS_NEUTRON, + ); + info!(target: target, + "Neutron receiver balances before claim: {:?}", + neutron_receiver_balances_before_claim + ); + + thread::sleep(Duration::from_secs(10)); + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ACC_1_KEY, + &serde_json::to_string(&valence_two_party_pol_holder::msg::ExecuteMsg::Claim {}).unwrap(), + EXECUTE_FLAGS, + ) + .unwrap(); + thread::sleep(Duration::from_secs(5)); + + let router_a_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_a_router_address, + ); + info!(target: target,"Router A balances: {:?}", router_a_balances); + let router_b_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_b_router_address, + ); + info!(target: target,"Router B balances: {:?}", router_b_balances); + + info!(target: target,"Tick until both parties receive their funds"); + loop { + let hub_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + ACC1_ADDRESS_GAIA, + ); + info!(target: target,"Hub receiver balances: {:?}", hub_receiver_balances); + let neutron_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ACC2_ADDRESS_NEUTRON, + ); + info!(target: target,"Neutron receiver balances: {:?}", neutron_receiver_balances); + if hub_receiver_balances_before_claim != hub_receiver_balances + && neutron_receiver_balances_before_claim != neutron_receiver_balances + { + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + // Send the balances back so we have a fresh start for the next test + send_non_native_balances( + test_ctx, + GAIA_CHAIN_NAME, + ACC_1_KEY, + ACC1_ADDRESS_GAIA, + NEUTRON_CHAIN_ADMIN_ADDR, + &atom_denom, + ); + + send_non_native_balances( + test_ctx, + NEUTRON_CHAIN_NAME, + ACC_2_KEY, + ACC2_ADDRESS_NEUTRON, + GAIA_CHAIN_ADMIN_ADDR, + &neutron_denom, + ); + + info!("Finished two party POL native tests!"); + + Ok(()) +} diff --git a/local-interchaintest/src/tests/two_party_pol/two_party_pol_not_native.rs b/local-interchaintest/src/tests/two_party_pol/two_party_pol_not_native.rs new file mode 100644 index 00000000..75550665 --- /dev/null +++ b/local-interchaintest/src/tests/two_party_pol/two_party_pol_not_native.rs @@ -0,0 +1,2108 @@ +use std::{collections::BTreeMap, str::FromStr, thread, time::Duration}; + +use astroport::{ + asset::{Asset, AssetInfo}, + factory::{InstantiateMsg as FactoryInstantiateMsg, PairConfig, PairType}, + native_coin_registry::{ + ExecuteMsg as NativeCoinRegistryExecuteMsg, + InstantiateMsg as NativeCoinRegistryInstantiateMsg, + }, + pair::StablePoolParams, +}; +use cosmwasm_std::{Binary, Coin, Decimal, Uint128, Uint64}; +use covenant_utils::{ + split::SplitConfig, InterchainCovenantParty, PoolPriceConfig, SingleSideLpLimits, +}; +use cw_utils::Expiration; +use localic_std::{ + errors::LocalError, + modules::{ + bank::{get_balance, send}, + cosmwasm::{contract_execute, contract_instantiate}, + }, + node::Chain, +}; +use localic_utils::{ + types::ibc::get_multihop_ibc_denom, utils::test_context::TestContext, DEFAULT_KEY, + GAIA_CHAIN_NAME, NEUTRON_CHAIN_ADMIN_ADDR, NEUTRON_CHAIN_NAME, OSMOSIS_CHAIN_NAME, +}; +use log::info; +use valence_astroport_liquid_pooler::msg::AstroportLiquidPoolerConfig; +use valence_covenant_two_party_pol::msg::{CovenantContractCodeIds, CovenantPartyConfig, Timeouts}; +use valence_two_party_pol_holder::msg::{CovenantType, RagequitConfig, RagequitTerms}; + +use crate::{ + helpers::{ + astroport::get_pool_address, + common::{query_contract_state, tick}, + constants::{ + ACC1_ADDRESS_GAIA, ACC1_ADDRESS_NEUTRON, ACC2_ADDRESS_NEUTRON, ACC2_ADDRESS_OSMO, + ACC_1_KEY, ACC_2_KEY, ASTROPORT_PATH, EXECUTE_FLAGS, LOCAL_CODE_ID_CACHE_PATH, + VALENCE_PATH, + }, + covenant::Covenant, + }, + send_non_native_balances, +}; + +pub fn test_two_party_pol(test_ctx: &mut TestContext) -> Result<(), LocalError> { + let mut uploader = test_ctx.build_tx_upload_contracts(); + + uploader + .send_with_local_cache(VALENCE_PATH, NEUTRON_CHAIN_NAME, LOCAL_CODE_ID_CACHE_PATH) + .unwrap(); + + uploader + .send_with_local_cache(ASTROPORT_PATH, NEUTRON_CHAIN_NAME, LOCAL_CODE_ID_CACHE_PATH) + .unwrap(); + + info!("Starting two party POL tests..."); + let astroport_native_coin_registry_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("astroport_native_coin_registry") + .unwrap(); + + let astroport_pair_stable_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("astroport_pair_stable") + .unwrap(); + + let astroport_token_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("astroport_token") + .unwrap(); + + let astroport_whitelist_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("astroport_whitelist") + .unwrap(); + + let astroport_factory_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("astroport_factory") + .unwrap(); + + let neutron_admin_acc = test_ctx.get_admin_addr().src(NEUTRON_CHAIN_NAME).get(); + + let native_coin_registry_instantiate_msg = NativeCoinRegistryInstantiateMsg { + owner: neutron_admin_acc.clone(), + }; + let native_coin_registry_contract = contract_instantiate( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + astroport_native_coin_registry_code_id, + &serde_json::to_string(&native_coin_registry_instantiate_msg).unwrap(), + "native-coin-registry", + None, + "", + )?; + info!( + "Native coin registry contract: {:?}", + native_coin_registry_contract.address + ); + + let atom_denom = test_ctx.get_native_denom().src(GAIA_CHAIN_NAME).get(); + let neutron_denom = test_ctx.get_native_denom().src(NEUTRON_CHAIN_NAME).get(); + let osmo_denom = test_ctx.get_native_denom().src(OSMOSIS_CHAIN_NAME).get(); + let atom_on_neutron = test_ctx.get_ibc_denom(&atom_denom, GAIA_CHAIN_NAME, NEUTRON_CHAIN_NAME); + let osmo_on_neutron = + test_ctx.get_ibc_denom(&osmo_denom, OSMOSIS_CHAIN_NAME, NEUTRON_CHAIN_NAME); + + let atom_on_osmo_via_neutron = get_multihop_ibc_denom( + &atom_denom, + vec![ + &test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(OSMOSIS_CHAIN_NAME) + .get(), + &test_ctx + .get_transfer_channels() + .src(GAIA_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + ], + ); + let osmo_on_gaia_via_neutron = get_multihop_ibc_denom( + &osmo_denom, + vec![ + &test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + &test_ctx + .get_transfer_channels() + .src(OSMOSIS_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + ], + ); + + let add_to_registry_msg = NativeCoinRegistryExecuteMsg::Add { + native_coins: vec![(atom_on_neutron.clone(), 6), (osmo_on_neutron.clone(), 6)], + }; + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &native_coin_registry_contract.address, + DEFAULT_KEY, + &serde_json::to_string(&add_to_registry_msg).unwrap(), + EXECUTE_FLAGS, + )?; + thread::sleep(Duration::from_secs(3)); + + let factory_instantiate_msg = FactoryInstantiateMsg { + pair_configs: vec![PairConfig { + code_id: astroport_pair_stable_code_id, + pair_type: PairType::Stable {}, + total_fee_bps: 0, + maker_fee_bps: 0, + is_disabled: false, + is_generator_disabled: true, + permissioned: false, + }], + token_code_id: astroport_token_code_id, + fee_address: None, + generator_address: None, + owner: neutron_admin_acc.clone(), + whitelist_code_id: astroport_whitelist_code_id, + coin_registry_address: native_coin_registry_contract.address.to_string(), + tracker_config: None, + }; + let factory_contract = contract_instantiate( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + astroport_factory_code_id, + &serde_json::to_string(&factory_instantiate_msg).unwrap(), + "astroport-factory", + None, + "", + )?; + info!("Factory contract: {:?}", factory_contract.address); + + let create_pair_msg = astroport::factory::ExecuteMsg::CreatePair { + pair_type: PairType::Stable {}, + asset_infos: vec![ + AssetInfo::NativeToken { + denom: atom_on_neutron.clone(), + }, + AssetInfo::NativeToken { + denom: osmo_on_neutron.clone(), + }, + ], + init_params: Some(Binary::from( + serde_json::to_vec(&StablePoolParams { + amp: 3, + owner: None, + }) + .unwrap(), + )), + }; + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &factory_contract.address, + DEFAULT_KEY, + &serde_json::to_string(&create_pair_msg).unwrap(), + EXECUTE_FLAGS, + )?; + + // Send some ATOM and OSMO to NTRN + let uatom_contribution_amount = 500_000_000; + loop { + test_ctx + .build_tx_transfer() + .with_chain_name(GAIA_CHAIN_NAME) + .with_amount(uatom_contribution_amount) + .with_recipient(&neutron_admin_acc) + .with_denom(&atom_denom) + .send() + .unwrap(); + + info!("Waiting to receive ATOM IBC transfer..."); + thread::sleep(Duration::from_secs(5)); + let balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &neutron_admin_acc, + ); + if balance.iter().any(|c| { + c.denom == atom_on_neutron && c.amount >= Uint128::new(uatom_contribution_amount) + }) { + break; + } + } + + let uosmo_contribution_amount = 5_000_000_000; + loop { + test_ctx + .build_tx_transfer() + .with_chain_name(OSMOSIS_CHAIN_NAME) + .with_amount(uosmo_contribution_amount) + .with_recipient(&neutron_admin_acc) + .with_denom(&osmo_denom) + .send() + .unwrap(); + + info!("Waiting to receive OSMO IBC transfer..."); + thread::sleep(Duration::from_secs(5)); + let balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &neutron_admin_acc, + ); + if balance.iter().any(|c| { + c.denom == osmo_on_neutron && c.amount >= Uint128::new(uosmo_contribution_amount) + }) { + break; + } + } + + // Provide the ATOM/OSMO liquidity to the pair + let pool_addr = get_pool_address( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &factory_contract.address, + AssetInfo::NativeToken { + denom: atom_on_neutron.clone(), + }, + AssetInfo::NativeToken { + denom: osmo_on_neutron.clone(), + }, + ); + + let provide_liquidity_msg = astroport::pair::ExecuteMsg::ProvideLiquidity { + assets: vec![ + Asset { + info: AssetInfo::NativeToken { + denom: atom_on_neutron.clone(), + }, + amount: Uint128::from(uatom_contribution_amount), + }, + Asset { + info: AssetInfo::NativeToken { + denom: osmo_on_neutron.clone(), + }, + amount: Uint128::from(uosmo_contribution_amount), + }, + ], + slippage_tolerance: Some(Decimal::percent(1)), + auto_stake: Some(false), + receiver: Some(neutron_admin_acc.clone()), + min_lp_to_receive: None, + }; + + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &pool_addr, + DEFAULT_KEY, + &serde_json::to_string(&provide_liquidity_msg).unwrap(), + &format!("--amount {uatom_contribution_amount}{atom_on_neutron},{uosmo_contribution_amount}{osmo_on_neutron} {EXECUTE_FLAGS}"), + ).unwrap(); + thread::sleep(Duration::from_secs(3)); + + let valence_ibc_forwarder_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_ibc_forwarder") + .unwrap(); + + let valence_two_party_pol_holder_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_two_party_pol_holder") + .unwrap(); + + let valence_clock_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_clock") + .unwrap(); + + let valence_interchain_router_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_interchain_router") + .unwrap(); + + let valence_native_router_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_native_router") + .unwrap(); + + let valence_astroport_tf_liquid_pooler_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_astroport_tf_liquid_pooler") + .unwrap(); + + let valence_covenant_two_party_pol_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_covenant_two_party_pol") + .unwrap(); + + let current_block_height = Chain::new( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ) + .get_height(); + + // Instantiate the covenants + let target = "Two party POL happy path"; + info!(target: target,"Starting Two party POL happy path test..."); + + let covenant_instantiate_msg = valence_covenant_two_party_pol::msg::InstantiateMsg { + label: "two-party-pol-covenant-happy".to_string(), + timeouts: Timeouts { + ica_timeout: Uint64::new(100), // seconds + ibc_transfer_timeout: Uint64::new(100), // seconds + }, + contract_codes: CovenantContractCodeIds { + ibc_forwarder_code: valence_ibc_forwarder_code_id, + holder_code: valence_two_party_pol_holder_code_id, + clock_code: valence_clock_code_id, + interchain_router_code: valence_interchain_router_code_id, + native_router_code: valence_native_router_code_id, + liquid_pooler_code: valence_astroport_tf_liquid_pooler_code_id, + }, + clock_tick_max_gas: None, + lockup_config: Expiration::AtHeight(current_block_height + 200), + party_a_config: CovenantPartyConfig::Interchain(InterchainCovenantParty { + party_receiver_addr: ACC1_ADDRESS_GAIA.to_string(), + party_chain_connection_id: test_ctx + .get_connections() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + ibc_transfer_timeout: Uint64::new(100), + party_to_host_chain_channel_id: test_ctx + .get_transfer_channels() + .src(GAIA_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + host_to_party_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + remote_chain_denom: atom_denom.clone(), + addr: ACC1_ADDRESS_NEUTRON.to_string(), + native_denom: atom_on_neutron.clone(), + contribution: Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }, + denom_to_pfm_map: BTreeMap::new(), + fallback_address: None, + }), + party_b_config: CovenantPartyConfig::Interchain(InterchainCovenantParty { + party_receiver_addr: ACC2_ADDRESS_OSMO.to_string(), + party_chain_connection_id: test_ctx + .get_connections() + .src(NEUTRON_CHAIN_NAME) + .dest(OSMOSIS_CHAIN_NAME) + .get(), + ibc_transfer_timeout: Uint64::new(100), + party_to_host_chain_channel_id: test_ctx + .get_transfer_channels() + .src(OSMOSIS_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + host_to_party_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(OSMOSIS_CHAIN_NAME) + .get(), + remote_chain_denom: osmo_denom.clone(), + addr: ACC2_ADDRESS_NEUTRON.to_string(), + native_denom: osmo_on_neutron.clone(), + contribution: Coin { + denom: osmo_denom.clone(), + amount: Uint128::new(uosmo_contribution_amount), + }, + denom_to_pfm_map: BTreeMap::new(), + fallback_address: None, + }), + covenant_type: CovenantType::Share, + ragequit_config: Some(RagequitConfig::Enabled(RagequitTerms { + penalty: Decimal::from_str("0.1").unwrap(), + state: None, + })), + deposit_deadline: Expiration::AtHeight(current_block_height + 180), + party_a_share: Decimal::percent(50), + party_b_share: Decimal::percent(50), + pool_price_config: PoolPriceConfig { + expected_spot_price: Decimal::from_str("0.1").unwrap(), + acceptable_price_spread: Decimal::from_str("0.09").unwrap(), + }, + splits: BTreeMap::from([ + ( + atom_on_neutron.clone(), + SplitConfig { + receivers: BTreeMap::from([ + (ACC1_ADDRESS_GAIA.to_string(), Decimal::percent(50)), + (ACC2_ADDRESS_OSMO.to_string(), Decimal::percent(50)), + ]), + }, + ), + ( + osmo_on_neutron.clone(), + SplitConfig { + receivers: BTreeMap::from([ + (ACC1_ADDRESS_GAIA.to_string(), Decimal::percent(50)), + (ACC2_ADDRESS_OSMO.to_string(), Decimal::percent(50)), + ]), + }, + ), + ]), + fallback_split: None, + emergency_committee: None, + liquid_pooler_config: valence_covenant_two_party_pol::msg::LiquidPoolerConfig::Astroport( + AstroportLiquidPoolerConfig { + pool_pair_type: astroport_old::factory::PairType::Stable {}, + pool_address: pool_addr.to_string(), + asset_a_denom: atom_on_neutron.clone(), + asset_b_denom: osmo_on_neutron.clone(), + single_side_lp_limits: SingleSideLpLimits { + asset_a_limit: Uint128::new(100000), + asset_b_limit: Uint128::new(1000000), + }, + }, + ), + fallback_address: None, + }; + + let covenant_contract = contract_instantiate( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + valence_covenant_two_party_pol_code_id, + &serde_json::to_string(&covenant_instantiate_msg).unwrap(), + "two-party-pol-covenant-happy-path", + None, + "", + )?; + info!(target: target,"Covenant contract: {:?}", covenant_contract.address); + let covenant = Covenant::TwoPartyPol { + rb: test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + contract_address: &covenant_contract.address, + }; + + // Query the covenant addresses + let clock_address = covenant.query_clock_address(); + let holder_address = covenant.query_holder_address(); + let liquid_pooler_address = covenant.query_liquid_pooler_address(); + let party_a_router_address = covenant.query_interchain_router_address("party_a".to_string()); + let party_b_router_address = covenant.query_interchain_router_address("party_b".to_string()); + let party_a_ibc_forwarder_address = covenant.query_ibc_forwarder_address("party_a".to_string()); + let party_b_ibc_forwarder_address = covenant.query_ibc_forwarder_address("party_b".to_string()); + + info!(target: target,"Fund covenant addresses with NTRN..."); + let mut addresses = vec![ + clock_address.clone(), + holder_address.clone(), + liquid_pooler_address.clone(), + party_a_router_address.clone(), + party_b_router_address.clone(), + ]; + if !party_a_ibc_forwarder_address.is_empty() { + addresses.push(party_a_ibc_forwarder_address.clone()); + } + if !party_b_ibc_forwarder_address.is_empty() { + addresses.push(party_b_ibc_forwarder_address.clone()); + } + for address in &addresses { + send( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + address, + &[Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000000000), + }], + &Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + } + + info!(target: target,"Tick until forwarders create ICA..."); + let party_a_deposit_address; + let party_b_deposit_address; + loop { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + let forwarder_a_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_a_ibc_forwarder_address, + ); + let forwarder_b_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_b_ibc_forwarder_address, + ); + info!(target: target,"Forwarder A state: {:?}", forwarder_a_state); + info!(target: target,"Forwarder B state: {:?}", forwarder_b_state); + if forwarder_a_state == "ica_created" && forwarder_b_state == "ica_created" { + party_a_deposit_address = covenant.query_deposit_address("party_a".to_string()); + party_b_deposit_address = covenant.query_deposit_address("party_b".to_string()); + break; + } + } + info!(target: target,"Party A deposit address: {}", party_a_deposit_address); + info!(target: target,"Party B deposit address: {}", party_b_deposit_address); + + info!(target: target,"Fund the forwarders with sufficient funds..."); + send( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + DEFAULT_KEY, + &party_a_deposit_address, + &[Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }], + &Coin { + denom: atom_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + send( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + DEFAULT_KEY, + &party_b_deposit_address, + &[Coin { + denom: osmo_denom.clone(), + amount: Uint128::new(uosmo_contribution_amount), + }], + &Coin { + denom: osmo_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + + info!(target: target,"Tick until forwarders forward the funds to the holder..."); + loop { + let holder_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + let holder_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + if holder_balance.iter().any(|c| { + c.denom == atom_on_neutron.clone() + && c.amount >= Uint128::new(uatom_contribution_amount) + }) && holder_balance.iter().any(|c| { + c.denom == osmo_on_neutron.clone() + && c.amount >= Uint128::new(uosmo_contribution_amount) + }) { + info!(target: target,"Holder received ATOM & OSMO"); + break; + } else if holder_state == "active" { + info!(target: target,"Holder is active"); + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!(target: target,"Tick until holder sends funds to LiquidPooler and LPer receives LP tokens..."); + + loop { + let balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + ); + + let liquid_pooler_lp_balance = balance + .iter() + .find(|c| c.denom.starts_with("factory")) + .unwrap_or(&Coin { + denom: "".to_string(), + amount: Uint128::zero(), + }) + .amount; + + if liquid_pooler_lp_balance.is_zero() { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } else { + break; + } + } + + info!(target: target,"Tick until holder expires..."); + loop { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + let holder_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + info!(target: target,"Holder state: {:?}", holder_state); + if holder_state == "expired" { + break; + } + } + + info!(target: target,"Party A claims and router receives the funds"); + let router_a_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_a_router_address, + ); + info!(target: target,"Router A balances: {:?}", router_a_balances); + + thread::sleep(Duration::from_secs(10)); + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ACC_1_KEY, + &serde_json::to_string(&valence_two_party_pol_holder::msg::ExecuteMsg::Claim {}).unwrap(), + EXECUTE_FLAGS, + ) + .unwrap(); + thread::sleep(Duration::from_secs(5)); + + let router_a_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_a_router_address, + ); + info!(target: target,"Router A balances: {:?}", router_a_balances); + + info!(target: target,"Tick until party A claim is distributed"); + info!(target: target,"Hub receiver address: {}", ACC1_ADDRESS_GAIA); + loop { + let hub_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + ACC1_ADDRESS_GAIA, + ); + info!(target: target,"Hub receiver balances: {:?}", hub_receiver_balances); + if hub_receiver_balances + .iter() + .any(|c| c.denom == atom_denom.clone()) + && hub_receiver_balances + .iter() + .any(|c| c.denom == osmo_on_gaia_via_neutron.clone()) + { + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!(target: target,"Party B claims and router receives the funds"); + let router_b_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_b_router_address, + ); + info!(target: target,"Router B balances: {:?}", router_b_balances); + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ACC_2_KEY, + &serde_json::to_string(&valence_two_party_pol_holder::msg::ExecuteMsg::Claim {}).unwrap(), + EXECUTE_FLAGS, + ) + .unwrap(); + thread::sleep(Duration::from_secs(5)); + + let router_b_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_b_router_address, + ); + info!(target: target,"Router B balances: {:?}", router_b_balances); + + info!(target: target,"Tick until both parties receive their funds"); + loop { + let hub_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + ACC1_ADDRESS_GAIA, + ); + info!(target: target,"Hub receiver balances: {:?}", hub_receiver_balances); + let osmo_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + ACC2_ADDRESS_OSMO, + ); + info!(target: target,"Osmosis receiver balances: {:?}", osmo_receiver_balances); + if osmo_receiver_balances + .iter() + .any(|c| c.denom == osmo_denom.clone()) + && osmo_receiver_balances + .iter() + .any(|c| c.denom == atom_on_osmo_via_neutron.clone()) + { + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + // Send the balances back so we have a fresh start for the next test + send_non_native_balances( + test_ctx, + GAIA_CHAIN_NAME, + ACC_1_KEY, + ACC1_ADDRESS_GAIA, + NEUTRON_CHAIN_ADMIN_ADDR, + &atom_denom, + ); + + send_non_native_balances( + test_ctx, + OSMOSIS_CHAIN_NAME, + ACC_2_KEY, + ACC2_ADDRESS_OSMO, + NEUTRON_CHAIN_ADMIN_ADDR, + &osmo_denom, + ); + + let current_block_height = Chain::new( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ) + .get_height(); + + let target = "Two party share based POL ragequit path"; + info!(target: target,"Starting Two party share based POL ragequit test..."); + + let covenant_instantiate_msg = valence_covenant_two_party_pol::msg::InstantiateMsg { + label: "two-party-pol-covenant-ragequit".to_string(), + timeouts: Timeouts { + ica_timeout: Uint64::new(100), // seconds + ibc_transfer_timeout: Uint64::new(100), // seconds + }, + contract_codes: CovenantContractCodeIds { + ibc_forwarder_code: valence_ibc_forwarder_code_id, + holder_code: valence_two_party_pol_holder_code_id, + clock_code: valence_clock_code_id, + interchain_router_code: valence_interchain_router_code_id, + native_router_code: valence_native_router_code_id, + liquid_pooler_code: valence_astroport_tf_liquid_pooler_code_id, + }, + clock_tick_max_gas: None, + lockup_config: Expiration::AtHeight(current_block_height + 300), + party_a_config: CovenantPartyConfig::Interchain(InterchainCovenantParty { + party_receiver_addr: ACC1_ADDRESS_GAIA.to_string(), + party_chain_connection_id: test_ctx + .get_connections() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + ibc_transfer_timeout: Uint64::new(100), + party_to_host_chain_channel_id: test_ctx + .get_transfer_channels() + .src(GAIA_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + host_to_party_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + remote_chain_denom: atom_denom.clone(), + addr: ACC1_ADDRESS_NEUTRON.to_string(), + native_denom: atom_on_neutron.clone(), + contribution: Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }, + denom_to_pfm_map: BTreeMap::new(), + fallback_address: None, + }), + party_b_config: CovenantPartyConfig::Interchain(InterchainCovenantParty { + party_receiver_addr: ACC2_ADDRESS_OSMO.to_string(), + party_chain_connection_id: test_ctx + .get_connections() + .src(NEUTRON_CHAIN_NAME) + .dest(OSMOSIS_CHAIN_NAME) + .get(), + ibc_transfer_timeout: Uint64::new(100), + party_to_host_chain_channel_id: test_ctx + .get_transfer_channels() + .src(OSMOSIS_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + host_to_party_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(OSMOSIS_CHAIN_NAME) + .get(), + remote_chain_denom: osmo_denom.clone(), + addr: ACC2_ADDRESS_NEUTRON.to_string(), + native_denom: osmo_on_neutron.clone(), + contribution: Coin { + denom: osmo_denom.clone(), + amount: Uint128::new(uosmo_contribution_amount), + }, + denom_to_pfm_map: BTreeMap::new(), + fallback_address: None, + }), + covenant_type: CovenantType::Share, + ragequit_config: Some(RagequitConfig::Enabled(RagequitTerms { + penalty: Decimal::from_str("0.1").unwrap(), + state: None, + })), + deposit_deadline: Expiration::AtHeight(current_block_height + 200), + party_a_share: Decimal::percent(50), + party_b_share: Decimal::percent(50), + pool_price_config: PoolPriceConfig { + expected_spot_price: Decimal::from_str("0.1").unwrap(), + acceptable_price_spread: Decimal::from_str("0.09").unwrap(), + }, + splits: BTreeMap::from([ + ( + atom_on_neutron.clone(), + SplitConfig { + receivers: BTreeMap::from([ + (ACC1_ADDRESS_GAIA.to_string(), Decimal::percent(50)), + (ACC2_ADDRESS_OSMO.to_string(), Decimal::percent(50)), + ]), + }, + ), + ( + osmo_on_neutron.clone(), + SplitConfig { + receivers: BTreeMap::from([ + (ACC1_ADDRESS_GAIA.to_string(), Decimal::percent(50)), + (ACC2_ADDRESS_OSMO.to_string(), Decimal::percent(50)), + ]), + }, + ), + ]), + fallback_split: None, + emergency_committee: None, + liquid_pooler_config: valence_covenant_two_party_pol::msg::LiquidPoolerConfig::Astroport( + AstroportLiquidPoolerConfig { + pool_pair_type: astroport_old::factory::PairType::Stable {}, + pool_address: pool_addr.to_string(), + asset_a_denom: atom_on_neutron.clone(), + asset_b_denom: osmo_on_neutron.clone(), + single_side_lp_limits: SingleSideLpLimits { + asset_a_limit: Uint128::new(100000), + asset_b_limit: Uint128::new(100000), + }, + }, + ), + fallback_address: None, + }; + + let covenant_contract = contract_instantiate( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + valence_covenant_two_party_pol_code_id, + &serde_json::to_string(&covenant_instantiate_msg).unwrap(), + "two-party-pol-covenant-ragequit", + None, + "", + )?; + info!(target: target,"Covenant contract: {:?}", covenant_contract.address); + let covenant = Covenant::TwoPartyPol { + rb: test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + contract_address: &covenant_contract.address, + }; + + // Query the covenant addresses + let clock_address = covenant.query_clock_address(); + let holder_address = covenant.query_holder_address(); + let liquid_pooler_address = covenant.query_liquid_pooler_address(); + let party_a_router_address = covenant.query_interchain_router_address("party_a".to_string()); + let party_b_router_address = covenant.query_interchain_router_address("party_b".to_string()); + let party_a_ibc_forwarder_address = covenant.query_ibc_forwarder_address("party_a".to_string()); + let party_b_ibc_forwarder_address = covenant.query_ibc_forwarder_address("party_b".to_string()); + + info!(target: target,"Fund covenant addresses with NTRN..."); + let mut addresses = vec![ + clock_address.clone(), + holder_address.clone(), + liquid_pooler_address.clone(), + party_a_router_address.clone(), + party_b_router_address.clone(), + ]; + if !party_a_ibc_forwarder_address.is_empty() { + addresses.push(party_a_ibc_forwarder_address.clone()); + } + if !party_b_ibc_forwarder_address.is_empty() { + addresses.push(party_b_ibc_forwarder_address.clone()); + } + for address in &addresses { + send( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + address, + &[Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000000000), + }], + &Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + } + + info!(target: target,"Tick until forwarders create ICA..."); + let party_a_deposit_address; + let party_b_deposit_address; + loop { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + let forwarder_a_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_a_ibc_forwarder_address, + ); + let forwarder_b_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_b_ibc_forwarder_address, + ); + info!(target: target,"Forwarder A state: {:?}", forwarder_a_state); + info!(target: target,"Forwarder B state: {:?}", forwarder_b_state); + if forwarder_a_state == "ica_created" && forwarder_b_state == "ica_created" { + party_a_deposit_address = covenant.query_deposit_address("party_a".to_string()); + party_b_deposit_address = covenant.query_deposit_address("party_b".to_string()); + break; + } + } + info!(target: target,"Party A deposit address: {}", party_a_deposit_address); + info!(target: target,"Party B deposit address: {}", party_b_deposit_address); + + info!(target: target,"Fund the forwarders with sufficient funds..."); + send( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + DEFAULT_KEY, + &party_a_deposit_address, + &[Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }], + &Coin { + denom: atom_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + send( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + DEFAULT_KEY, + &party_b_deposit_address, + &[Coin { + denom: osmo_denom.clone(), + amount: Uint128::new(uosmo_contribution_amount), + }], + &Coin { + denom: osmo_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + + info!(target: target,"Tick until forwarders forward the funds to the holder..."); + loop { + let holder_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + let holder_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + if holder_balance.iter().any(|c| { + c.denom == atom_on_neutron.clone() + && c.amount >= Uint128::new(uatom_contribution_amount) + }) && holder_balance.iter().any(|c| { + c.denom == osmo_on_neutron.clone() + && c.amount >= Uint128::new(uosmo_contribution_amount) + }) { + info!(target: target,"Holder received ATOM & OSMO"); + break; + } else if holder_state == "active" { + info!(target: target,"Holder is active"); + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!(target: target,"Tick until holder sends funds to LiquidPooler and LPer receives LP tokens..."); + + loop { + let balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + ); + + let liquid_pooler_lp_balance = balance + .iter() + .find(|c| c.denom.starts_with("factory")) + .unwrap_or(&Coin { + denom: "".to_string(), + amount: Uint128::zero(), + }) + .amount; + + if liquid_pooler_lp_balance.is_zero() { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } else { + break; + } + } + + info!(target: target,"Party A ragequits..."); + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ACC_1_KEY, + &serde_json::to_string(&valence_two_party_pol_holder::msg::ExecuteMsg::Ragequit {}) + .unwrap(), + EXECUTE_FLAGS, + ) + .unwrap(); + + info!(target: target,"Party B claims and router receives the funds"); + let router_b_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_b_router_address, + ); + info!(target: target,"Router B balances: {:?}", router_b_balances); + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ACC_2_KEY, + &serde_json::to_string(&valence_two_party_pol_holder::msg::ExecuteMsg::Claim {}).unwrap(), + EXECUTE_FLAGS, + ) + .unwrap(); + thread::sleep(Duration::from_secs(5)); + + let router_b_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_b_router_address, + ); + info!(target: target,"Router B balances: {:?}", router_b_balances); + + info!(target: target,"Tick until both parties receive their funds"); + loop { + let hub_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + ACC1_ADDRESS_GAIA, + ); + info!(target: target,"Hub receiver balances: {:?}", hub_receiver_balances); + let osmo_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + ACC2_ADDRESS_OSMO, + ); + info!(target: target,"Osmosis receiver balances: {:?}", osmo_receiver_balances); + if hub_receiver_balances + .iter() + .any(|c| c.denom == osmo_on_gaia_via_neutron.clone()) + && osmo_receiver_balances + .iter() + .any(|c| c.denom == atom_on_osmo_via_neutron.clone()) + { + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + // Send the balances back so we have a fresh start for the next test + send_non_native_balances( + test_ctx, + GAIA_CHAIN_NAME, + ACC_1_KEY, + ACC1_ADDRESS_GAIA, + NEUTRON_CHAIN_ADMIN_ADDR, + &atom_denom, + ); + + send_non_native_balances( + test_ctx, + OSMOSIS_CHAIN_NAME, + ACC_2_KEY, + ACC2_ADDRESS_OSMO, + NEUTRON_CHAIN_ADMIN_ADDR, + &osmo_denom, + ); + let current_block_height = Chain::new( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ) + .get_height(); + + let target = "Two party side based POL ragequit path"; + info!(target: target,"Starting Two party side based POL ragequit test..."); + + let covenant_instantiate_msg = valence_covenant_two_party_pol::msg::InstantiateMsg { + label: "two-party-pol-covenant-side-ragequit".to_string(), + timeouts: Timeouts { + ica_timeout: Uint64::new(100), // seconds + ibc_transfer_timeout: Uint64::new(100), // seconds + }, + contract_codes: CovenantContractCodeIds { + ibc_forwarder_code: valence_ibc_forwarder_code_id, + holder_code: valence_two_party_pol_holder_code_id, + clock_code: valence_clock_code_id, + interchain_router_code: valence_interchain_router_code_id, + native_router_code: valence_native_router_code_id, + liquid_pooler_code: valence_astroport_tf_liquid_pooler_code_id, + }, + clock_tick_max_gas: None, + lockup_config: Expiration::AtHeight(current_block_height + 300), + party_a_config: CovenantPartyConfig::Interchain(InterchainCovenantParty { + party_receiver_addr: ACC1_ADDRESS_GAIA.to_string(), + party_chain_connection_id: test_ctx + .get_connections() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + ibc_transfer_timeout: Uint64::new(100), + party_to_host_chain_channel_id: test_ctx + .get_transfer_channels() + .src(GAIA_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + host_to_party_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + remote_chain_denom: atom_denom.clone(), + addr: ACC1_ADDRESS_NEUTRON.to_string(), + native_denom: atom_on_neutron.clone(), + contribution: Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }, + denom_to_pfm_map: BTreeMap::new(), + fallback_address: None, + }), + party_b_config: CovenantPartyConfig::Interchain(InterchainCovenantParty { + party_receiver_addr: ACC2_ADDRESS_OSMO.to_string(), + party_chain_connection_id: test_ctx + .get_connections() + .src(NEUTRON_CHAIN_NAME) + .dest(OSMOSIS_CHAIN_NAME) + .get(), + ibc_transfer_timeout: Uint64::new(100), + party_to_host_chain_channel_id: test_ctx + .get_transfer_channels() + .src(OSMOSIS_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + host_to_party_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(OSMOSIS_CHAIN_NAME) + .get(), + remote_chain_denom: osmo_denom.clone(), + addr: ACC2_ADDRESS_NEUTRON.to_string(), + native_denom: osmo_on_neutron.clone(), + contribution: Coin { + denom: osmo_denom.clone(), + amount: Uint128::new(uosmo_contribution_amount), + }, + denom_to_pfm_map: BTreeMap::new(), + fallback_address: None, + }), + covenant_type: CovenantType::Side, + ragequit_config: Some(RagequitConfig::Enabled(RagequitTerms { + penalty: Decimal::from_str("0.1").unwrap(), + state: None, + })), + deposit_deadline: Expiration::AtHeight(current_block_height + 200), + party_a_share: Decimal::percent(50), + party_b_share: Decimal::percent(50), + pool_price_config: PoolPriceConfig { + expected_spot_price: Decimal::from_str("0.1").unwrap(), + acceptable_price_spread: Decimal::from_str("0.09").unwrap(), + }, + splits: BTreeMap::from([ + ( + atom_on_neutron.clone(), + SplitConfig { + receivers: BTreeMap::from([ + (ACC1_ADDRESS_GAIA.to_string(), Decimal::percent(100)), + (ACC2_ADDRESS_OSMO.to_string(), Decimal::percent(0)), + ]), + }, + ), + ( + osmo_on_neutron.clone(), + SplitConfig { + receivers: BTreeMap::from([ + (ACC1_ADDRESS_GAIA.to_string(), Decimal::percent(0)), + (ACC2_ADDRESS_OSMO.to_string(), Decimal::percent(100)), + ]), + }, + ), + ]), + fallback_split: None, + emergency_committee: None, + liquid_pooler_config: valence_covenant_two_party_pol::msg::LiquidPoolerConfig::Astroport( + AstroportLiquidPoolerConfig { + pool_pair_type: astroport_old::factory::PairType::Stable {}, + pool_address: pool_addr.to_string(), + asset_a_denom: atom_on_neutron.clone(), + asset_b_denom: osmo_on_neutron.clone(), + single_side_lp_limits: SingleSideLpLimits { + asset_a_limit: Uint128::new(1000000), + asset_b_limit: Uint128::new(1000000), + }, + }, + ), + fallback_address: None, + }; + + let covenant_contract = contract_instantiate( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + valence_covenant_two_party_pol_code_id, + &serde_json::to_string(&covenant_instantiate_msg).unwrap(), + "two-party-pol-covenant-side-ragequit", + None, + "", + )?; + info!(target: target,"Covenant contract: {:?}", covenant_contract.address); + let covenant = Covenant::TwoPartyPol { + rb: test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + contract_address: &covenant_contract.address, + }; + + // Query the covenant addresses + let clock_address = covenant.query_clock_address(); + let holder_address = covenant.query_holder_address(); + let liquid_pooler_address = covenant.query_liquid_pooler_address(); + let party_a_router_address = covenant.query_interchain_router_address("party_a".to_string()); + let party_b_router_address = covenant.query_interchain_router_address("party_b".to_string()); + let party_a_ibc_forwarder_address = covenant.query_ibc_forwarder_address("party_a".to_string()); + let party_b_ibc_forwarder_address = covenant.query_ibc_forwarder_address("party_b".to_string()); + + info!(target: target,"Fund covenant addresses with NTRN..."); + let mut addresses = vec![ + clock_address.clone(), + holder_address.clone(), + liquid_pooler_address.clone(), + party_a_router_address.clone(), + party_b_router_address.clone(), + ]; + if !party_a_ibc_forwarder_address.is_empty() { + addresses.push(party_a_ibc_forwarder_address.clone()); + } + if !party_b_ibc_forwarder_address.is_empty() { + addresses.push(party_b_ibc_forwarder_address.clone()); + } + for address in &addresses { + send( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + address, + &[Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000000000), + }], + &Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + } + + info!(target: target,"Tick until forwarders create ICA..."); + let party_a_deposit_address; + let party_b_deposit_address; + loop { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + let forwarder_a_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_a_ibc_forwarder_address, + ); + let forwarder_b_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_b_ibc_forwarder_address, + ); + info!(target: target,"Forwarder A state: {:?}", forwarder_a_state); + info!(target: target,"Forwarder B state: {:?}", forwarder_b_state); + if forwarder_a_state == "ica_created" && forwarder_b_state == "ica_created" { + party_a_deposit_address = covenant.query_deposit_address("party_a".to_string()); + party_b_deposit_address = covenant.query_deposit_address("party_b".to_string()); + break; + } + } + info!(target: target,"Party A deposit address: {}", party_a_deposit_address); + info!(target: target,"Party B deposit address: {}", party_b_deposit_address); + + info!(target: target,"Fund the forwarders with sufficient funds..."); + send( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + DEFAULT_KEY, + &party_a_deposit_address, + &[Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }], + &Coin { + denom: atom_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + send( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + DEFAULT_KEY, + &party_b_deposit_address, + &[Coin { + denom: osmo_denom.clone(), + amount: Uint128::new(uosmo_contribution_amount), + }], + &Coin { + denom: osmo_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + + info!(target: target,"Tick until forwarders forward the funds to the holder..."); + loop { + let holder_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + let holder_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + if holder_balance.iter().any(|c| { + c.denom == atom_on_neutron.clone() + && c.amount >= Uint128::new(uatom_contribution_amount) + }) && holder_balance.iter().any(|c| { + c.denom == osmo_on_neutron.clone() + && c.amount >= Uint128::new(uosmo_contribution_amount) + }) { + info!(target: target,"Holder received ATOM & OSMO"); + break; + } else if holder_state == "active" { + info!(target: target,"Holder is active"); + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!(target: target,"Tick until holder sends funds to LiquidPooler and LPer receives LP tokens..."); + + loop { + let balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + ); + + let liquid_pooler_lp_balance = balance + .iter() + .find(|c| c.denom.starts_with("factory")) + .unwrap_or(&Coin { + denom: "".to_string(), + amount: Uint128::zero(), + }) + .amount; + + if liquid_pooler_lp_balance.is_zero() { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } else { + break; + } + } + + let previous_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + ACC1_ADDRESS_GAIA, + ); + info!(target: target,"Party A ragequits..."); + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ACC_1_KEY, + &serde_json::to_string(&valence_two_party_pol_holder::msg::ExecuteMsg::Ragequit {}) + .unwrap(), + EXECUTE_FLAGS, + ) + .unwrap(); + + info!(target: target,"Tick until both parties receive their funds"); + loop { + let hub_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + ACC1_ADDRESS_GAIA, + ); + info!(target: target,"Hub receiver balances: {:?}", hub_receiver_balances); + let osmo_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + ACC2_ADDRESS_OSMO, + ); + info!(target: target,"Osmosis receiver balances: {:?}", osmo_receiver_balances); + if previous_balance != hub_receiver_balances + && osmo_receiver_balances + .iter() + .any(|c| c.denom == atom_on_osmo_via_neutron.clone()) + { + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + // Send the balances back so we have a fresh start for the next test + send_non_native_balances( + test_ctx, + GAIA_CHAIN_NAME, + ACC_1_KEY, + ACC1_ADDRESS_GAIA, + NEUTRON_CHAIN_ADMIN_ADDR, + &atom_denom, + ); + + send_non_native_balances( + test_ctx, + OSMOSIS_CHAIN_NAME, + ACC_2_KEY, + ACC2_ADDRESS_OSMO, + NEUTRON_CHAIN_ADMIN_ADDR, + &osmo_denom, + ); + + let current_block_height = Chain::new( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ) + .get_height(); + + let target = "Two party POL side based happy path"; + info!(target: target,"Starting Two party POL side based happy path test..."); + + let covenant_instantiate_msg = valence_covenant_two_party_pol::msg::InstantiateMsg { + label: "two-party-pol-covenant-side-happy".to_string(), + timeouts: Timeouts { + ica_timeout: Uint64::new(100), // seconds + ibc_transfer_timeout: Uint64::new(100), // seconds + }, + contract_codes: CovenantContractCodeIds { + ibc_forwarder_code: valence_ibc_forwarder_code_id, + holder_code: valence_two_party_pol_holder_code_id, + clock_code: valence_clock_code_id, + interchain_router_code: valence_interchain_router_code_id, + native_router_code: valence_native_router_code_id, + liquid_pooler_code: valence_astroport_tf_liquid_pooler_code_id, + }, + clock_tick_max_gas: None, + lockup_config: Expiration::AtHeight(current_block_height + 230), + party_a_config: CovenantPartyConfig::Interchain(InterchainCovenantParty { + party_receiver_addr: ACC1_ADDRESS_GAIA.to_string(), + party_chain_connection_id: test_ctx + .get_connections() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + ibc_transfer_timeout: Uint64::new(100), + party_to_host_chain_channel_id: test_ctx + .get_transfer_channels() + .src(GAIA_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + host_to_party_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + remote_chain_denom: atom_denom.clone(), + addr: ACC1_ADDRESS_NEUTRON.to_string(), + native_denom: atom_on_neutron.clone(), + contribution: Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }, + denom_to_pfm_map: BTreeMap::new(), + fallback_address: None, + }), + party_b_config: CovenantPartyConfig::Interchain(InterchainCovenantParty { + party_receiver_addr: ACC2_ADDRESS_OSMO.to_string(), + party_chain_connection_id: test_ctx + .get_connections() + .src(NEUTRON_CHAIN_NAME) + .dest(OSMOSIS_CHAIN_NAME) + .get(), + ibc_transfer_timeout: Uint64::new(100), + party_to_host_chain_channel_id: test_ctx + .get_transfer_channels() + .src(OSMOSIS_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + host_to_party_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(OSMOSIS_CHAIN_NAME) + .get(), + remote_chain_denom: osmo_denom.clone(), + addr: ACC2_ADDRESS_NEUTRON.to_string(), + native_denom: osmo_on_neutron.clone(), + contribution: Coin { + denom: osmo_denom.clone(), + amount: Uint128::new(uosmo_contribution_amount), + }, + denom_to_pfm_map: BTreeMap::new(), + fallback_address: None, + }), + covenant_type: CovenantType::Side, + ragequit_config: Some(RagequitConfig::Enabled(RagequitTerms { + penalty: Decimal::from_str("0.1").unwrap(), + state: None, + })), + deposit_deadline: Expiration::AtHeight(current_block_height + 210), + party_a_share: Decimal::percent(50), + party_b_share: Decimal::percent(50), + pool_price_config: PoolPriceConfig { + expected_spot_price: Decimal::from_str("0.1").unwrap(), + acceptable_price_spread: Decimal::from_str("0.09").unwrap(), + }, + splits: BTreeMap::from([ + ( + atom_on_neutron.clone(), + SplitConfig { + receivers: BTreeMap::from([ + (ACC1_ADDRESS_GAIA.to_string(), Decimal::percent(100)), + (ACC2_ADDRESS_OSMO.to_string(), Decimal::percent(0)), + ]), + }, + ), + ( + osmo_on_neutron.clone(), + SplitConfig { + receivers: BTreeMap::from([ + (ACC1_ADDRESS_GAIA.to_string(), Decimal::percent(0)), + (ACC2_ADDRESS_OSMO.to_string(), Decimal::percent(100)), + ]), + }, + ), + ]), + fallback_split: None, + emergency_committee: None, + liquid_pooler_config: valence_covenant_two_party_pol::msg::LiquidPoolerConfig::Astroport( + AstroportLiquidPoolerConfig { + pool_pair_type: astroport_old::factory::PairType::Stable {}, + pool_address: pool_addr.to_string(), + asset_a_denom: atom_on_neutron.clone(), + asset_b_denom: osmo_on_neutron.clone(), + single_side_lp_limits: SingleSideLpLimits { + asset_a_limit: Uint128::new(1000000), + asset_b_limit: Uint128::new(10000000), + }, + }, + ), + fallback_address: None, + }; + + let covenant_contract = contract_instantiate( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + valence_covenant_two_party_pol_code_id, + &serde_json::to_string(&covenant_instantiate_msg).unwrap(), + "two-party-pol-covenant-side-happy", + None, + "", + )?; + info!(target: target,"Covenant contract: {:?}", covenant_contract.address); + let covenant = Covenant::TwoPartyPol { + rb: test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + contract_address: &covenant_contract.address, + }; + + // Query the covenant addresses + let clock_address = covenant.query_clock_address(); + let holder_address = covenant.query_holder_address(); + let liquid_pooler_address = covenant.query_liquid_pooler_address(); + let party_a_router_address = covenant.query_interchain_router_address("party_a".to_string()); + let party_b_router_address = covenant.query_interchain_router_address("party_b".to_string()); + let party_a_ibc_forwarder_address = covenant.query_ibc_forwarder_address("party_a".to_string()); + let party_b_ibc_forwarder_address = covenant.query_ibc_forwarder_address("party_b".to_string()); + + info!(target: target,"Fund covenant addresses with NTRN..."); + let mut addresses = vec![ + clock_address.clone(), + holder_address.clone(), + liquid_pooler_address.clone(), + party_a_router_address.clone(), + party_b_router_address.clone(), + ]; + if !party_a_ibc_forwarder_address.is_empty() { + addresses.push(party_a_ibc_forwarder_address.clone()); + } + if !party_b_ibc_forwarder_address.is_empty() { + addresses.push(party_b_ibc_forwarder_address.clone()); + } + for address in &addresses { + send( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + address, + &[Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000000000), + }], + &Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + } + + info!(target: target,"Tick until forwarders create ICA..."); + let party_a_deposit_address; + let party_b_deposit_address; + loop { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + let forwarder_a_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_a_ibc_forwarder_address, + ); + let forwarder_b_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_b_ibc_forwarder_address, + ); + info!(target: target,"Forwarder A state: {:?}", forwarder_a_state); + info!(target: target,"Forwarder B state: {:?}", forwarder_b_state); + if forwarder_a_state == "ica_created" && forwarder_b_state == "ica_created" { + party_a_deposit_address = covenant.query_deposit_address("party_a".to_string()); + party_b_deposit_address = covenant.query_deposit_address("party_b".to_string()); + break; + } + } + info!(target: target,"Party A deposit address: {}", party_a_deposit_address); + info!(target: target,"Party B deposit address: {}", party_b_deposit_address); + + info!(target: target,"Fund the forwarders with sufficient funds..."); + send( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + DEFAULT_KEY, + &party_a_deposit_address, + &[Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }], + &Coin { + denom: atom_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + send( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + DEFAULT_KEY, + &party_b_deposit_address, + &[Coin { + denom: osmo_denom.clone(), + amount: Uint128::new(uosmo_contribution_amount), + }], + &Coin { + denom: osmo_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + + info!(target: target,"Tick until forwarders forward the funds to the holder..."); + loop { + let holder_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + let holder_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + if holder_balance.iter().any(|c| { + c.denom == atom_on_neutron.clone() + && c.amount >= Uint128::new(uatom_contribution_amount) + }) && holder_balance.iter().any(|c| { + c.denom == osmo_on_neutron.clone() + && c.amount >= Uint128::new(uosmo_contribution_amount) + }) { + info!(target: target,"Holder received ATOM & OSMO"); + break; + } else if holder_state == "active" { + info!(target: target,"Holder is active"); + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!(target: target,"Tick until holder sends funds to LiquidPooler and LPer receives LP tokens..."); + + loop { + let balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + ); + + let liquid_pooler_lp_balance = balance + .iter() + .find(|c| c.denom.starts_with("factory")) + .unwrap_or(&Coin { + denom: "".to_string(), + amount: Uint128::zero(), + }) + .amount; + + if liquid_pooler_lp_balance.is_zero() { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } else { + break; + } + } + + info!(target: target,"Tick until holder expires..."); + loop { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + let holder_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + info!(target: target,"Holder state: {:?}", holder_state); + if holder_state == "expired" { + break; + } + } + + let previous_balance_gaia = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + ACC1_ADDRESS_GAIA, + ); + let previous_balance_osmosis = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + ACC2_ADDRESS_OSMO, + ); + info!(target: target,"Party A claims"); + + thread::sleep(Duration::from_secs(10)); + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ACC_1_KEY, + &serde_json::to_string(&valence_two_party_pol_holder::msg::ExecuteMsg::Claim {}).unwrap(), + EXECUTE_FLAGS, + ) + .unwrap(); + thread::sleep(Duration::from_secs(5)); + + info!(target: target,"Tick until both parties receive their funds"); + loop { + let hub_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + ACC1_ADDRESS_GAIA, + ); + info!(target: target,"Hub receiver balances: {:?}", hub_receiver_balances); + let osmo_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + ACC2_ADDRESS_OSMO, + ); + info!(target: target,"Osmosis receiver balances: {:?}", osmo_receiver_balances); + if previous_balance_gaia != hub_receiver_balances + && previous_balance_osmosis != osmo_receiver_balances + { + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + // Send the balances back so we have a fresh start for the next test + send_non_native_balances( + test_ctx, + GAIA_CHAIN_NAME, + ACC_1_KEY, + ACC1_ADDRESS_GAIA, + NEUTRON_CHAIN_ADMIN_ADDR, + &atom_denom, + ); + + send_non_native_balances( + test_ctx, + OSMOSIS_CHAIN_NAME, + ACC_2_KEY, + ACC2_ADDRESS_OSMO, + NEUTRON_CHAIN_ADMIN_ADDR, + &osmo_denom, + ); + + info!("Finished two party POL tests!"); + + Ok(()) +} diff --git a/local-interchaintest/src/tests/two_party_pol/two_party_pol_osmo.rs b/local-interchaintest/src/tests/two_party_pol/two_party_pol_osmo.rs new file mode 100644 index 00000000..cac4fc75 --- /dev/null +++ b/local-interchaintest/src/tests/two_party_pol/two_party_pol_osmo.rs @@ -0,0 +1,1579 @@ +use std::{collections::BTreeMap, env, path::Path, str::FromStr, thread, time::Duration}; + +use anyhow::Error; +use cosmwasm_std::{Coin, Decimal, Uint128, Uint64}; +use covenant_utils::{ + split::SplitConfig, ForwardMetadata, InterchainCovenantParty, PacketForwardMiddlewareConfig, + PoolPriceConfig, SingleSideLpLimits, +}; +use cw_utils::Expiration; +use localic_std::{ + modules::{ + bank::{get_balance, send}, + cosmwasm::{contract_execute, contract_instantiate, contract_query, CosmWasm}, + }, + node::Chain, + relayer::Relayer, +}; +use localic_utils::{ + utils::test_context::TestContext, DEFAULT_KEY, GAIA_CHAIN_NAME, NEUTRON_CHAIN_ADMIN_ADDR, + NEUTRON_CHAIN_NAME, OSMOSIS_CHAIN_NAME, TRANSFER_PORT, +}; +use log::info; +use valence_covenant_two_party_pol::msg::{CovenantContractCodeIds, CovenantPartyConfig, Timeouts}; +use valence_osmo_liquid_pooler::msg::{OsmosisLiquidPoolerConfig, PartyChainInfo, PartyDenomInfo}; +use valence_two_party_pol_holder::msg::CovenantType; + +use crate::{ + helpers::{ + common::{query_contract_state, tick}, + constants::{ + ACC1_ADDRESS_GAIA, ACC1_ADDRESS_NEUTRON, ACC1_ADDRESS_OSMO, ACC2_ADDRESS_GAIA, + ACC2_ADDRESS_NEUTRON, ACC2_ADDRESS_OSMO, ACC_1_KEY, ACC_2_KEY, EXECUTE_FLAGS, + LOCAL_CODE_ID_CACHE_PATH, OSMOSIS_FEES, POLYTONE_PATH, VALENCE_PATH, + }, + covenant::Covenant, + }, + send_non_native_balances, +}; + +pub fn test_two_party_pol_osmo(test_ctx: &mut TestContext) -> Result<(), Error> { + let mut uploader = test_ctx.build_tx_upload_contracts(); + + uploader + .send_with_local_cache(VALENCE_PATH, NEUTRON_CHAIN_NAME, LOCAL_CODE_ID_CACHE_PATH) + .unwrap(); + + // Store only the ones we need for the test + let polytone_note_path = format!("{POLYTONE_PATH}/polytone_note.wasm"); + let polytone_voice_path = format!("{POLYTONE_PATH}/polytone_voice.wasm"); + let polytone_proxy_path = format!("{POLYTONE_PATH}/polytone_proxy.wasm"); + let osmosis_outpost_path = format!("{VALENCE_PATH}/valence_outpost_osmo_liquid_pooler.wasm"); + + let current_dir = env::current_dir()?; + + let mut cw = CosmWasm::new( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ); + let polytone_note_code_id = cw + .store( + DEFAULT_KEY, + &Path::canonicalize(current_dir.join(polytone_note_path).as_path())?, + ) + .unwrap(); + + let mut cw = CosmWasm::new( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + ); + + let polytone_voice_code_id = cw + .store( + DEFAULT_KEY, + &Path::canonicalize(current_dir.join(polytone_voice_path).as_path())?, + ) + .unwrap(); + + let polytone_proxy_code_id = cw + .store( + DEFAULT_KEY, + &Path::canonicalize(current_dir.join(polytone_proxy_path).as_path())?, + ) + .unwrap(); + + let osmosis_outpost_code_id = cw + .store( + DEFAULT_KEY, + &Path::canonicalize(current_dir.join(osmosis_outpost_path).as_path())?, + ) + .unwrap(); + + info!("Starting two party POL osmo tests..."); + info!("Create and add liquidity to ATOM-OSMO pool"); + + let osmosis_admin_acc = test_ctx.get_admin_addr().src(OSMOSIS_CHAIN_NAME).get(); + let atom_denom = test_ctx.get_native_denom().src(GAIA_CHAIN_NAME).get(); + let neutron_denom = test_ctx.get_native_denom().src(NEUTRON_CHAIN_NAME).get(); + let osmo_denom = test_ctx.get_native_denom().src(OSMOSIS_CHAIN_NAME).get(); + let osmo_on_neutron = + test_ctx.get_ibc_denom(&osmo_denom, OSMOSIS_CHAIN_NAME, NEUTRON_CHAIN_NAME); + let osmo_on_gaia = test_ctx.get_ibc_denom(&osmo_denom, OSMOSIS_CHAIN_NAME, GAIA_CHAIN_NAME); + let atom_on_neutron = test_ctx.get_ibc_denom(&atom_denom, GAIA_CHAIN_NAME, NEUTRON_CHAIN_NAME); + let atom_on_osmosis = test_ctx.get_ibc_denom(&atom_denom, GAIA_CHAIN_NAME, OSMOSIS_CHAIN_NAME); + + // Send some ATOM to Osmosis to feed the pool + let uatom_liquidity = 40_000_000_000; + let uosmo_liquidity = 330_000_000_000; + loop { + test_ctx + .build_tx_transfer() + .with_chain_name(GAIA_CHAIN_NAME) + .with_amount(uatom_liquidity) + .with_recipient(&osmosis_admin_acc) + .with_denom(&atom_denom) + .send() + .unwrap(); + + info!("Waiting to receive ATOM IBC transfer..."); + thread::sleep(Duration::from_secs(5)); + let balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + &osmosis_admin_acc, + ); + if balance + .iter() + .any(|c| c.denom == atom_on_osmosis && c.amount >= Uint128::new(uatom_liquidity)) + { + break; + } + } + + test_ctx + .build_tx_create_osmo_pool() + .with_weight(&atom_on_osmosis, 50) + .with_weight(&osmo_denom, 50) + .with_initial_deposit(&atom_on_osmosis, uatom_liquidity as u64) + .with_initial_deposit(&osmo_denom, uosmo_liquidity) + .with_swap_fee(Decimal::from_str("0.003").unwrap()) + .send()?; + + let pool_id = test_ctx.get_osmo_pool(osmo_denom.clone(), atom_on_osmosis.clone())?; + + info!("Instantiate Osmosis outpost contract"); + let osmo_liquid_pooler_instantiate_msg = + valence_outpost_osmo_liquid_pooler::msg::InstantiateMsg {}; + let osmosis_outpost_contract = contract_instantiate( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + DEFAULT_KEY, + osmosis_outpost_code_id, + &serde_json::to_string(&osmo_liquid_pooler_instantiate_msg).unwrap(), + "osmosis-outpost", + None, + OSMOSIS_FEES, + ) + .unwrap(); + + info!("Instantiate Polytone Note on Neutron"); + let polytone_note_instantiate_msg = polytone_note::msg::InstantiateMsg { + pair: None, + block_max_gas: Uint64::new(3010000), + }; + let polytone_note_contract = contract_instantiate( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + polytone_note_code_id, + &serde_json::to_string(&polytone_note_instantiate_msg).unwrap(), + "polytone-note", + None, + "", + ) + .unwrap(); + + info!("Instantiate Polytone Voice on Osmosis"); + let polytone_voice_instantiate_msg = polytone_voice::msg::InstantiateMsg { + proxy_code_id: Uint64::new(polytone_proxy_code_id), + block_max_gas: Uint64::new(3010000), + }; + + let polytone_voice_contract = contract_instantiate( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + DEFAULT_KEY, + polytone_voice_code_id, + &serde_json::to_string(&polytone_voice_instantiate_msg).unwrap(), + "polytone-voice", + None, + OSMOSIS_FEES, + ) + .unwrap(); + + info!("Create polytone channel"); + let relayer = Relayer::new( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ); + + CosmWasm::new_from_existing( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + None, + None, + Some(polytone_note_contract.address.clone()), + ) + .create_wasm_connection( + &relayer, + "neutron-osmosis", + &CosmWasm::new_from_existing( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + None, + None, + Some(polytone_voice_contract.address.clone()), + ), + "unordered", + "polytone-1", + ) + .unwrap(); + + let valence_ibc_forwarder_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_ibc_forwarder") + .unwrap(); + + let valence_two_party_pol_holder_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_two_party_pol_holder") + .unwrap(); + + let valence_clock_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_clock") + .unwrap(); + + let valence_interchain_router_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_interchain_router") + .unwrap(); + + let valence_native_router_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_native_router") + .unwrap(); + + let valence_liquid_pooler_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_osmo_liquid_pooler") + .unwrap(); + + let valence_covenant_two_party_pol_code_id = *test_ctx + .get_chain(NEUTRON_CHAIN_NAME) + .contract_codes + .get("valence_covenant_two_party_pol") + .unwrap(); + + let uatom_contribution_amount = 500_000_000; + let uosmo_contribution_amount = 5_000_000_000; + + let target = "Two party Osmosis POL withdraw pre-lp tokens"; + info!(target: target,"Starting Two party Osmosis POL withdraw pre-lp tokens test..."); + let current_block_height = Chain::new( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ) + .get_height(); + + let covenant_instantiate_msg = valence_covenant_two_party_pol::msg::InstantiateMsg { + label: "covenant-osmo".to_string(), + timeouts: Timeouts { + ica_timeout: Uint64::new(100), // seconds + ibc_transfer_timeout: Uint64::new(100), // seconds + }, + contract_codes: CovenantContractCodeIds { + ibc_forwarder_code: valence_ibc_forwarder_code_id, + holder_code: valence_two_party_pol_holder_code_id, + clock_code: valence_clock_code_id, + interchain_router_code: valence_interchain_router_code_id, + native_router_code: valence_native_router_code_id, + liquid_pooler_code: valence_liquid_pooler_code_id, + }, + clock_tick_max_gas: None, + lockup_config: Expiration::AtHeight(current_block_height + 210), + party_a_config: CovenantPartyConfig::Interchain(InterchainCovenantParty { + party_receiver_addr: ACC1_ADDRESS_GAIA.to_string(), + party_chain_connection_id: test_ctx + .get_connections() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + ibc_transfer_timeout: Uint64::new(100), + party_to_host_chain_channel_id: test_ctx + .get_transfer_channels() + .src(GAIA_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + host_to_party_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + remote_chain_denom: atom_denom.clone(), + addr: ACC1_ADDRESS_NEUTRON.to_string(), + native_denom: atom_on_neutron.clone(), + contribution: Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }, + denom_to_pfm_map: BTreeMap::from([( + osmo_on_neutron.clone(), + PacketForwardMiddlewareConfig { + local_to_hop_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(OSMOSIS_CHAIN_NAME) + .get(), + hop_to_destination_chain_channel_id: test_ctx + .get_transfer_channels() + .src(OSMOSIS_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + hop_chain_receiver_address: ACC1_ADDRESS_OSMO.to_string(), + }, + )]), + fallback_address: None, + }), + party_b_config: CovenantPartyConfig::Interchain(InterchainCovenantParty { + party_receiver_addr: ACC2_ADDRESS_OSMO.to_string(), + party_chain_connection_id: test_ctx + .get_connections() + .src(NEUTRON_CHAIN_NAME) + .dest(OSMOSIS_CHAIN_NAME) + .get(), + ibc_transfer_timeout: Uint64::new(100), + party_to_host_chain_channel_id: test_ctx + .get_transfer_channels() + .src(OSMOSIS_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + host_to_party_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(OSMOSIS_CHAIN_NAME) + .get(), + remote_chain_denom: osmo_denom.clone(), + addr: ACC2_ADDRESS_NEUTRON.to_string(), + native_denom: osmo_on_neutron.clone(), + contribution: Coin { + denom: osmo_denom.clone(), + amount: Uint128::new(uosmo_contribution_amount), + }, + denom_to_pfm_map: BTreeMap::from([( + atom_on_neutron.clone(), + PacketForwardMiddlewareConfig { + local_to_hop_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + hop_to_destination_chain_channel_id: test_ctx + .get_transfer_channels() + .src(GAIA_CHAIN_NAME) + .dest(OSMOSIS_CHAIN_NAME) + .get(), + hop_chain_receiver_address: ACC2_ADDRESS_GAIA.to_string(), + }, + )]), + fallback_address: None, + }), + covenant_type: CovenantType::Share, + ragequit_config: None, + deposit_deadline: Expiration::AtHeight(current_block_height + 200), + party_a_share: Decimal::percent(50), + party_b_share: Decimal::percent(50), + pool_price_config: PoolPriceConfig { + expected_spot_price: Decimal::from_str("0.51").unwrap(), + acceptable_price_spread: Decimal::from_str("0.09").unwrap(), + }, + splits: BTreeMap::from([ + ( + atom_on_neutron.clone(), + SplitConfig { + receivers: BTreeMap::from([ + (ACC1_ADDRESS_GAIA.to_string(), Decimal::percent(50)), + (ACC2_ADDRESS_OSMO.to_string(), Decimal::percent(50)), + ]), + }, + ), + ( + osmo_on_neutron.clone(), + SplitConfig { + receivers: BTreeMap::from([ + (ACC1_ADDRESS_GAIA.to_string(), Decimal::percent(50)), + (ACC2_ADDRESS_OSMO.to_string(), Decimal::percent(50)), + ]), + }, + ), + ]), + fallback_split: None, + emergency_committee: Some(NEUTRON_CHAIN_ADMIN_ADDR.to_string()), + liquid_pooler_config: valence_covenant_two_party_pol::msg::LiquidPoolerConfig::Osmosis( + Box::new(OsmosisLiquidPoolerConfig { + note_address: polytone_note_contract.address.clone(), + pool_id: Uint64::new(pool_id), + osmo_ibc_timeout: Uint64::new(300), + osmo_outpost: osmosis_outpost_contract.address.clone(), + party_1_chain_info: PartyChainInfo { + neutron_to_party_chain_channel: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + party_chain_to_neutron_channel: test_ctx + .get_transfer_channels() + .src(GAIA_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + outwards_pfm: Some(ForwardMetadata { + receiver: ACC1_ADDRESS_GAIA.to_string(), + port: TRANSFER_PORT.to_string(), + channel: test_ctx + .get_transfer_channels() + .src(GAIA_CHAIN_NAME) + .dest(OSMOSIS_CHAIN_NAME) + .get(), + }), + inwards_pfm: Some(ForwardMetadata { + receiver: ACC1_ADDRESS_GAIA.to_string(), + port: TRANSFER_PORT.to_string(), + channel: test_ctx + .get_transfer_channels() + .src(OSMOSIS_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + }), + ibc_timeout: Uint64::new(300), + }, + party_2_chain_info: PartyChainInfo { + neutron_to_party_chain_channel: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(OSMOSIS_CHAIN_NAME) + .get(), + party_chain_to_neutron_channel: test_ctx + .get_transfer_channels() + .src(OSMOSIS_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + outwards_pfm: None, + inwards_pfm: None, + ibc_timeout: Uint64::new(300), + }, + lp_token_denom: format!("gamm/pool/{pool_id}"), + osmo_to_neutron_channel_id: test_ctx + .get_transfer_channels() + .src(OSMOSIS_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + party_1_denom_info: PartyDenomInfo { + osmosis_coin: Coin { + denom: atom_on_osmosis.clone(), + amount: Uint128::new(uatom_contribution_amount), + }, + local_denom: atom_on_neutron.clone(), + }, + party_2_denom_info: PartyDenomInfo { + osmosis_coin: Coin { + denom: osmo_denom.clone(), + amount: Uint128::new(uosmo_contribution_amount), + }, + local_denom: osmo_on_neutron.clone(), + }, + funding_duration: cw_utils::Duration::Time(400), + single_side_lp_limits: SingleSideLpLimits { + asset_a_limit: Uint128::new(10000), + asset_b_limit: Uint128::new(975000004), + }, + }), + ), + fallback_address: None, + }; + + let covenant_contract = contract_instantiate( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + valence_covenant_two_party_pol_code_id, + &serde_json::to_string(&covenant_instantiate_msg).unwrap(), + "covenant-osmo", + None, + "", + ) + .unwrap(); + info!(target: target,"Covenant contract: {:?}", covenant_contract.address); + let covenant = Covenant::TwoPartyPol { + rb: test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + contract_address: &covenant_contract.address, + }; + + let clock_address = covenant.query_clock_address(); + let holder_address = covenant.query_holder_address(); + let liquid_pooler_address = covenant.query_liquid_pooler_address(); + let party_a_router_address = covenant.query_interchain_router_address("party_a".to_string()); + let party_b_router_address = covenant.query_interchain_router_address("party_b".to_string()); + let party_a_ibc_forwarder_address = covenant.query_ibc_forwarder_address("party_a".to_string()); + let party_b_ibc_forwarder_address = covenant.query_ibc_forwarder_address("party_b".to_string()); + + info!(target: target,"Fund covenant addresses with NTRN..."); + let addresses = vec![ + clock_address.clone(), + holder_address.clone(), + liquid_pooler_address.clone(), + party_a_router_address.clone(), + party_b_router_address.clone(), + party_a_ibc_forwarder_address.clone(), + party_b_ibc_forwarder_address.clone(), + ]; + + for address in &addresses { + send( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + address, + &[Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000000000), + }], + &Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + } + + info!(target: target,"Tick until forwarders create ICA..."); + let party_a_deposit_address; + let party_b_deposit_address; + loop { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + let forwarder_a_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_a_ibc_forwarder_address, + ); + let forwarder_b_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_b_ibc_forwarder_address, + ); + info!(target: target,"Forwarder A state: {:?}", forwarder_a_state); + info!(target: target,"Forwarder B state: {:?}", forwarder_b_state); + if forwarder_a_state == "ica_created" && forwarder_b_state == "ica_created" { + party_a_deposit_address = covenant.query_deposit_address("party_a".to_string()); + party_b_deposit_address = covenant.query_deposit_address("party_b".to_string()); + break; + } + } + info!(target: target,"Party A deposit address: {}", party_a_deposit_address); + info!(target: target,"Party B deposit address: {}", party_b_deposit_address); + + info!(target: target,"Fund the forwarders with sufficient funds..."); + send( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + DEFAULT_KEY, + &party_a_deposit_address, + &[Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }], + &Coin { + denom: atom_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + send( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + DEFAULT_KEY, + &party_b_deposit_address, + &[Coin { + denom: osmo_denom.clone(), + amount: Uint128::new(uosmo_contribution_amount), + }], + &Coin { + denom: osmo_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + + info!(target: target,"Tick until forwarders forward the funds to the holder..."); + loop { + let holder_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + let holder_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + if holder_balance.iter().any(|c| { + c.denom == atom_on_neutron.clone() + && c.amount >= Uint128::new(uatom_contribution_amount) + }) && holder_balance.iter().any(|c| { + c.denom == osmo_on_neutron.clone() + && c.amount >= Uint128::new(uosmo_contribution_amount) + }) { + info!(target: target,"Holder received ATOM & OSMO"); + break; + } else if holder_state == "active" { + info!(target: target,"Holder is active"); + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!(target: target,"Tick until holder sends funds to LiquidPooler and LPer receives LP tokens..."); + loop { + let liquid_pooler_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + ); + if liquid_pooler_balance.iter().any(|c| { + c.denom == atom_on_neutron.clone() + && c.amount >= Uint128::new(uatom_contribution_amount) + }) && liquid_pooler_balance.iter().any(|c| { + c.denom == osmo_on_neutron.clone() + && c.amount >= Uint128::new(uosmo_contribution_amount) + }) { + break; + } + { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!(target: target,"Tick until Liquid Pooler Proxy is created..."); + let proxy_address; + loop { + let liquid_pooler_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + ); + info!(target: target,"Liquid Pooler state: {:?}", liquid_pooler_state); + if liquid_pooler_state == "proxy_created" { + let query_response = contract_query( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + &serde_json::to_string(&valence_osmo_liquid_pooler::msg::QueryMsg::ProxyAddress {}) + .unwrap(), + ); + + proxy_address = query_response["data"] + .as_str() + .unwrap_or_default() + .to_string(); + + info!(target: target,"Proxy address: {}", proxy_address); + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!(target: target,"Tick until Proxy is funded..."); + loop { + let proxy_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + &proxy_address, + ); + info!(target: target,"Proxy balance: {:?}", proxy_balance); + + if proxy_balance + .iter() + .any(|c| c.denom == atom_on_osmosis || c.denom == osmo_denom) + { + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!(target: target,"Perform emergency withdrawal..."); + let liquid_pooler_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + ); + info!(target: target,"Liquid Pooler state: {:?}", liquid_pooler_state); + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + DEFAULT_KEY, + &serde_json::to_string( + &valence_two_party_pol_holder::msg::ExecuteMsg::EmergencyWithdraw {}, + ) + .unwrap(), + EXECUTE_FLAGS, + ) + .unwrap(); + + loop { + let proxy_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + &proxy_address, + ); + info!(target: target,"Proxy balance: {:?}", proxy_balance); + + let liquid_pooler_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + ); + info!(target: target,"Liquid Pooler balance: {:?}", liquid_pooler_balance); + + let party_a_router_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_a_router_address, + ); + info!(target: target,"Party A router balance: {:?}", party_a_router_balance); + + let party_b_router_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_b_router_address, + ); + info!(target: target,"Party B router balance: {:?}", party_b_router_balance); + + if party_a_router_balance + .iter() + .any(|c| c.denom == atom_on_neutron || c.denom == osmo_on_neutron) + || party_b_router_balance + .iter() + .any(|c| c.denom == atom_on_neutron || c.denom == osmo_on_neutron) + { + info!(target: target,"Withdraw successful!"); + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!(target: target,"Tick until parties get the funds"); + loop { + let hub_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + ACC1_ADDRESS_GAIA, + ); + info!(target: target,"Hub receiver balances: {:?}", hub_receiver_balances); + let osmo_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + ACC2_ADDRESS_OSMO, + ); + info!(target: target,"Osmo receiver balances: {:?}", osmo_receiver_balances); + if hub_receiver_balances + .iter() + .any(|c| c.denom == osmo_on_gaia) + && osmo_receiver_balances + .iter() + .any(|c| c.denom == atom_on_osmosis) + { + info!(target: target, "Parties received the funds!"); + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + // Send the balances back so we have a fresh start for the next test + send_non_native_balances( + test_ctx, + GAIA_CHAIN_NAME, + ACC_1_KEY, + ACC1_ADDRESS_GAIA, + NEUTRON_CHAIN_ADMIN_ADDR, + &atom_denom, + ); + + send_non_native_balances( + test_ctx, + OSMOSIS_CHAIN_NAME, + ACC_2_KEY, + ACC2_ADDRESS_OSMO, + NEUTRON_CHAIN_ADMIN_ADDR, + &osmo_denom, + ); + + let target = "Two party Osmosis POL full path"; + info!(target: target,"Two party Osmosis POL full path test..."); + let current_block_height = Chain::new( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + ) + .get_height(); + + let covenant_instantiate_msg = valence_covenant_two_party_pol::msg::InstantiateMsg { + label: "covenant-osmo".to_string(), + timeouts: Timeouts { + ica_timeout: Uint64::new(100), // seconds + ibc_transfer_timeout: Uint64::new(100), // seconds + }, + contract_codes: CovenantContractCodeIds { + ibc_forwarder_code: valence_ibc_forwarder_code_id, + holder_code: valence_two_party_pol_holder_code_id, + clock_code: valence_clock_code_id, + interchain_router_code: valence_interchain_router_code_id, + native_router_code: valence_native_router_code_id, + liquid_pooler_code: valence_liquid_pooler_code_id, + }, + clock_tick_max_gas: None, + lockup_config: Expiration::AtHeight(current_block_height + 300), + party_a_config: CovenantPartyConfig::Interchain(InterchainCovenantParty { + party_receiver_addr: ACC1_ADDRESS_GAIA.to_string(), + party_chain_connection_id: test_ctx + .get_connections() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + ibc_transfer_timeout: Uint64::new(100), + party_to_host_chain_channel_id: test_ctx + .get_transfer_channels() + .src(GAIA_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + host_to_party_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + remote_chain_denom: atom_denom.clone(), + addr: ACC1_ADDRESS_NEUTRON.to_string(), + native_denom: atom_on_neutron.clone(), + contribution: Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }, + denom_to_pfm_map: BTreeMap::from([( + osmo_on_neutron.clone(), + PacketForwardMiddlewareConfig { + local_to_hop_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(OSMOSIS_CHAIN_NAME) + .get(), + hop_to_destination_chain_channel_id: test_ctx + .get_transfer_channels() + .src(OSMOSIS_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + hop_chain_receiver_address: ACC1_ADDRESS_OSMO.to_string(), + }, + )]), + fallback_address: None, + }), + party_b_config: CovenantPartyConfig::Interchain(InterchainCovenantParty { + party_receiver_addr: ACC2_ADDRESS_OSMO.to_string(), + party_chain_connection_id: test_ctx + .get_connections() + .src(NEUTRON_CHAIN_NAME) + .dest(OSMOSIS_CHAIN_NAME) + .get(), + ibc_transfer_timeout: Uint64::new(100), + party_to_host_chain_channel_id: test_ctx + .get_transfer_channels() + .src(OSMOSIS_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + host_to_party_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(OSMOSIS_CHAIN_NAME) + .get(), + remote_chain_denom: osmo_denom.clone(), + addr: ACC2_ADDRESS_NEUTRON.to_string(), + native_denom: osmo_on_neutron.clone(), + contribution: Coin { + denom: osmo_denom.clone(), + amount: Uint128::new(uosmo_contribution_amount), + }, + denom_to_pfm_map: BTreeMap::from([( + atom_on_neutron.clone(), + PacketForwardMiddlewareConfig { + local_to_hop_chain_channel_id: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + hop_to_destination_chain_channel_id: test_ctx + .get_transfer_channels() + .src(GAIA_CHAIN_NAME) + .dest(OSMOSIS_CHAIN_NAME) + .get(), + hop_chain_receiver_address: ACC2_ADDRESS_GAIA.to_string(), + }, + )]), + fallback_address: None, + }), + covenant_type: CovenantType::Share, + ragequit_config: None, + deposit_deadline: Expiration::AtHeight(current_block_height + 250), + party_a_share: Decimal::percent(50), + party_b_share: Decimal::percent(50), + pool_price_config: PoolPriceConfig { + expected_spot_price: Decimal::from_str("0.1").unwrap(), + acceptable_price_spread: Decimal::from_str("0.09").unwrap(), + }, + splits: BTreeMap::from([ + ( + atom_on_neutron.clone(), + SplitConfig { + receivers: BTreeMap::from([ + (ACC1_ADDRESS_GAIA.to_string(), Decimal::percent(50)), + (ACC2_ADDRESS_OSMO.to_string(), Decimal::percent(50)), + ]), + }, + ), + ( + osmo_on_neutron.clone(), + SplitConfig { + receivers: BTreeMap::from([ + (ACC1_ADDRESS_GAIA.to_string(), Decimal::percent(50)), + (ACC2_ADDRESS_OSMO.to_string(), Decimal::percent(50)), + ]), + }, + ), + ]), + fallback_split: None, + emergency_committee: Some(NEUTRON_CHAIN_ADMIN_ADDR.to_string()), + liquid_pooler_config: valence_covenant_two_party_pol::msg::LiquidPoolerConfig::Osmosis( + Box::new(OsmosisLiquidPoolerConfig { + note_address: polytone_note_contract.address, + pool_id: Uint64::new(pool_id), + osmo_ibc_timeout: Uint64::new(300), + osmo_outpost: osmosis_outpost_contract.address.clone(), + party_1_chain_info: PartyChainInfo { + neutron_to_party_chain_channel: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + party_chain_to_neutron_channel: test_ctx + .get_transfer_channels() + .src(GAIA_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + outwards_pfm: Some(ForwardMetadata { + receiver: ACC1_ADDRESS_GAIA.to_string(), + port: TRANSFER_PORT.to_string(), + channel: test_ctx + .get_transfer_channels() + .src(GAIA_CHAIN_NAME) + .dest(OSMOSIS_CHAIN_NAME) + .get(), + }), + inwards_pfm: Some(ForwardMetadata { + receiver: ACC1_ADDRESS_GAIA.to_string(), + port: TRANSFER_PORT.to_string(), + channel: test_ctx + .get_transfer_channels() + .src(OSMOSIS_CHAIN_NAME) + .dest(GAIA_CHAIN_NAME) + .get(), + }), + ibc_timeout: Uint64::new(300), + }, + party_2_chain_info: PartyChainInfo { + neutron_to_party_chain_channel: test_ctx + .get_transfer_channels() + .src(NEUTRON_CHAIN_NAME) + .dest(OSMOSIS_CHAIN_NAME) + .get(), + party_chain_to_neutron_channel: test_ctx + .get_transfer_channels() + .src(OSMOSIS_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + outwards_pfm: None, + inwards_pfm: None, + ibc_timeout: Uint64::new(300), + }, + lp_token_denom: format!("gamm/pool/{pool_id}"), + osmo_to_neutron_channel_id: test_ctx + .get_transfer_channels() + .src(OSMOSIS_CHAIN_NAME) + .dest(NEUTRON_CHAIN_NAME) + .get(), + party_1_denom_info: PartyDenomInfo { + osmosis_coin: Coin { + denom: atom_on_osmosis.clone(), + amount: Uint128::new(uatom_contribution_amount), + }, + local_denom: atom_on_neutron.clone(), + }, + party_2_denom_info: PartyDenomInfo { + osmosis_coin: Coin { + denom: osmo_denom.clone(), + amount: Uint128::new(uosmo_contribution_amount), + }, + local_denom: osmo_on_neutron.clone(), + }, + funding_duration: cw_utils::Duration::Time(400), + single_side_lp_limits: SingleSideLpLimits { + asset_a_limit: Uint128::new(10000), + asset_b_limit: Uint128::new(975000004), + }, + }), + ), + fallback_address: None, + }; + + let covenant_contract = contract_instantiate( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + valence_covenant_two_party_pol_code_id, + &serde_json::to_string(&covenant_instantiate_msg).unwrap(), + "covenant-osmo", + None, + "", + ) + .unwrap(); + info!(target: target,"Covenant contract: {:?}", covenant_contract.address); + let covenant = Covenant::TwoPartyPol { + rb: test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + contract_address: &covenant_contract.address, + }; + + let clock_address = covenant.query_clock_address(); + let holder_address = covenant.query_holder_address(); + let liquid_pooler_address = covenant.query_liquid_pooler_address(); + let party_a_router_address = covenant.query_interchain_router_address("party_a".to_string()); + let party_b_router_address = covenant.query_interchain_router_address("party_b".to_string()); + let party_a_ibc_forwarder_address = covenant.query_ibc_forwarder_address("party_a".to_string()); + let party_b_ibc_forwarder_address = covenant.query_ibc_forwarder_address("party_b".to_string()); + + info!(target: target,"Fund covenant addresses with NTRN..."); + let addresses = vec![ + clock_address.clone(), + holder_address.clone(), + liquid_pooler_address.clone(), + party_a_router_address.clone(), + party_b_router_address.clone(), + party_a_ibc_forwarder_address.clone(), + party_b_ibc_forwarder_address.clone(), + ]; + + for address in &addresses { + send( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + address, + &[Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000000000), + }], + &Coin { + denom: neutron_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + } + + info!(target: target,"Tick until forwarders create ICA..."); + let party_a_deposit_address; + let party_b_deposit_address; + loop { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + let forwarder_a_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_a_ibc_forwarder_address, + ); + let forwarder_b_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &party_b_ibc_forwarder_address, + ); + info!(target: target,"Forwarder A state: {:?}", forwarder_a_state); + info!(target: target,"Forwarder B state: {:?}", forwarder_b_state); + if forwarder_a_state == "ica_created" && forwarder_b_state == "ica_created" { + party_a_deposit_address = covenant.query_deposit_address("party_a".to_string()); + party_b_deposit_address = covenant.query_deposit_address("party_b".to_string()); + break; + } + } + info!(target: target,"Party A deposit address: {}", party_a_deposit_address); + info!(target: target,"Party B deposit address: {}", party_b_deposit_address); + + info!(target: target,"Fund the forwarders with sufficient funds..."); + send( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + DEFAULT_KEY, + &party_a_deposit_address, + &[Coin { + denom: atom_denom.clone(), + amount: Uint128::new(uatom_contribution_amount), + }], + &Coin { + denom: atom_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + send( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + DEFAULT_KEY, + &party_b_deposit_address, + &[Coin { + denom: osmo_denom.clone(), + amount: Uint128::new(uosmo_contribution_amount), + }], + &Coin { + denom: osmo_denom.clone(), + amount: Uint128::new(5000), + }, + ) + .unwrap(); + + info!(target: target,"Tick until forwarders forward the funds to the holder..."); + loop { + let holder_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + let holder_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + if holder_balance.iter().any(|c| { + c.denom == atom_on_neutron.clone() + && c.amount >= Uint128::new(uatom_contribution_amount) + }) && holder_balance.iter().any(|c| { + c.denom == osmo_on_neutron.clone() + && c.amount >= Uint128::new(uosmo_contribution_amount) + }) { + info!(target: target,"Holder received ATOM & OSMO"); + break; + } else if holder_state == "active" { + info!(target: target,"Holder is active"); + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!(target: target,"Tick until holder sends funds to LiquidPooler and LPer receives LP tokens..."); + loop { + let liquid_pooler_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + ); + if liquid_pooler_balance.iter().any(|c| { + c.denom == atom_on_neutron.clone() + && c.amount >= Uint128::new(uatom_contribution_amount) + }) && liquid_pooler_balance.iter().any(|c| { + c.denom == osmo_on_neutron.clone() + && c.amount >= Uint128::new(uosmo_contribution_amount) + }) { + break; + } + { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!(target: target,"Tick until Liquid Pooler Proxy is created..."); + let proxy_address; + loop { + let liquid_pooler_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + ); + info!(target: target,"Liquid Pooler state: {:?}", liquid_pooler_state); + if liquid_pooler_state == "proxy_created" { + let query_response = contract_query( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + &serde_json::to_string(&valence_osmo_liquid_pooler::msg::QueryMsg::ProxyAddress {}) + .unwrap(), + ); + + proxy_address = query_response["data"] + .as_str() + .unwrap_or_default() + .to_string(); + + info!(target: target,"Proxy address: {}", proxy_address); + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!(target: target,"Tick until Proxy is funded..."); + loop { + let proxy_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + &proxy_address, + ); + info!(target: target,"Proxy balance: {:?}", proxy_balance); + + if proxy_balance + .iter() + .any(|c| c.denom == atom_on_osmosis || c.denom == osmo_denom) + { + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!(target: target,"Tick until liquidity is provided and proxy receives gamm tokens"); + loop { + let liquid_pooler_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + ); + info!(target: target,"Liquid Pooler balance: {:?}", liquid_pooler_balance); + + let proxy_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + &proxy_address, + ); + info!(target: target,"Proxy balance: {:?}", proxy_balance); + + let liquid_pooler_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + ); + info!(target: target,"Liquid Pooler state: {:?}", liquid_pooler_state); + + let osmosis_outpost_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + &osmosis_outpost_contract.address, + ); + info!(target: target,"Osmosis Outpost balance: {:?}", osmosis_outpost_balance); + if proxy_balance.len() == 1 + && proxy_balance.first().unwrap().denom == format!("gamm/pool/{pool_id}") + { + break; + } else { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + } + } + + info!(target: target,"Tick until holder expires..."); + loop { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + let holder_state = query_contract_state( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + info!(target: target,"Holder state: {:?}", holder_state); + if holder_state == "expired" { + break; + } + } + + info!(target: target, "Osmosis party claims"); + thread::sleep(Duration::from_secs(10)); + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ACC_2_KEY, + &serde_json::to_string(&valence_two_party_pol_holder::msg::ExecuteMsg::Claim {}).unwrap(), + EXECUTE_FLAGS, + ) + .unwrap(); + + loop { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + + let liquid_pooler_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + ); + info!(target: target,"Liquid Pooler balance: {:?}", liquid_pooler_balance); + + let proxy_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + &proxy_address, + ); + info!(target: target,"Proxy balance: {:?}", proxy_balance); + + let holder_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + info!(target: target,"Holder balance: {:?}", holder_balance); + + let osmo_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + ACC2_ADDRESS_OSMO, + ); + info!(target: target,"Osmo receiver balances: {:?}", osmo_receiver_balances); + if osmo_receiver_balances + .iter() + .any(|c| c.denom == atom_on_osmosis) + { + info!(target: target, "Osmosis party received the funds"); + break; + } + } + + info!(target: target, "Tick until we are able to withdraw"); + + contract_execute( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ACC_1_KEY, + &serde_json::to_string(&valence_two_party_pol_holder::msg::ExecuteMsg::Claim {}).unwrap(), + EXECUTE_FLAGS, + ) + .unwrap(); + thread::sleep(Duration::from_secs(10)); + + loop { + tick( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + DEFAULT_KEY, + &clock_address, + ); + + let liquid_pooler_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &liquid_pooler_address, + ); + info!(target: target,"Liquid Pooler balance: {:?}", liquid_pooler_balance); + + let proxy_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(OSMOSIS_CHAIN_NAME), + &proxy_address, + ); + info!(target: target,"Proxy balance: {:?}", proxy_balance); + + let holder_balance = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(NEUTRON_CHAIN_NAME), + &holder_address, + ); + info!(target: target,"Holder balance: {:?}", holder_balance); + + let hub_receiver_balances = get_balance( + test_ctx + .get_request_builder() + .get_request_builder(GAIA_CHAIN_NAME), + ACC1_ADDRESS_GAIA, + ); + info!(target: target,"Hub receiver balances: {:?}", hub_receiver_balances); + if hub_receiver_balances + .iter() + .any(|c| c.denom == osmo_on_gaia) + { + info!(target: target, "Both parties received the funds"); + break; + } + } + + // Send the balances back so we have a fresh start for the next test + send_non_native_balances( + test_ctx, + GAIA_CHAIN_NAME, + ACC_1_KEY, + ACC1_ADDRESS_GAIA, + NEUTRON_CHAIN_ADMIN_ADDR, + &atom_denom, + ); + + send_non_native_balances( + test_ctx, + OSMOSIS_CHAIN_NAME, + ACC_2_KEY, + ACC2_ADDRESS_OSMO, + NEUTRON_CHAIN_ADMIN_ADDR, + &osmo_denom, + ); + + info!("Finished two party POL Osmosis tests!"); + Ok(()) +} diff --git a/unit-tests/src/setup/contracts.rs b/unit-tests/src/setup/contracts.rs index beed17e0..c6f47f24 100644 --- a/unit-tests/src/setup/contracts.rs +++ b/unit-tests/src/setup/contracts.rs @@ -9,7 +9,7 @@ use neutron_sdk::bindings::{msg::NeutronMsg, query::NeutronQuery}; /// Turn a neutron response into an empty response /// This is fine because the contract return an empty response, but our testing enviroment expects a neutron response /// the contract that uses this function will never emit a neutron response anyways -pub(crate) fn execute_into_neutron( +pub fn execute_into_neutron( into: Result, ) -> Result, E> { into.map(|r| { @@ -49,7 +49,7 @@ pub(crate) fn execute_into_neutron( } /// Turn neutron DepsMut into empty DepsMut -pub(crate) fn get_empty_depsmut(deps: DepsMut) -> DepsMut<'_, Empty> { +pub fn get_empty_depsmut(deps: DepsMut) -> DepsMut<'_, Empty> { DepsMut { storage: deps.storage, api: deps.api,