diff --git a/Cargo.lock b/Cargo.lock index 0cc446c..b9b16da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,9 +80,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -114,6 +114,32 @@ version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +[[package]] +name = "apollo-cw-asset" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "423502406a307052f6877030f48b5fb4e9fb338fc5e7c8ca1064210def52876b" +dependencies = [ + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw20 1.1.2", + "schemars", + "serde", +] + +[[package]] +name = "apollo-utils" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e669b1a4c378832d63a87b3ca2efafc6f4881a61af5dd3e5a6b222c54f2a02ec" +dependencies = [ + "apollo-cw-asset", + "cosmwasm-schema", + "cosmwasm-std", + "cw20 1.1.2", + "regex", +] + [[package]] name = "astroport" version = "2.8.0" @@ -125,18 +151,18 @@ dependencies = [ "cw-utils 0.15.1", "cw20 0.15.1", "itertools 0.10.5", - "uint", + "uint 0.9.5", ] [[package]] name = "async-recursion" -version = "1.0.5" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" +checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -158,18 +184,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] name = "async-trait" -version = "0.1.77" +version = "0.1.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -191,7 +217,7 @@ dependencies = [ [[package]] name = "authzpp-utils" version = "0.3.2" -source = "git+https://github.com/kakucodes/authzpp#556d90c8d4eade3e0d16964f3e63788849588ac7" +source = "git+https://github.com/kakucodes/authzpp#e5f294c0976d3277f71e6fa6eb34c2f0db0010af" dependencies = [ "cosmos-sdk-proto 0.20.0", "cosmwasm-schema", @@ -206,9 +232,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "axum" @@ -257,9 +283,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -329,7 +355,7 @@ version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "cexpr", "clang-sys", "itertools 0.12.1", @@ -342,7 +368,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.52", + "syn 2.0.57", "which", ] @@ -400,9 +426,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "block-buffer" @@ -447,11 +473,56 @@ dependencies = [ "wyndex-stake", ] +[[package]] +name = "borsh" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" +dependencies = [ + "borsh-derive", + "hashbrown 0.11.2", +] + +[[package]] +name = "borsh-derive" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "bs58" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ "sha2 0.10.8", ] @@ -470,9 +541,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" dependencies = [ "serde", ] @@ -500,9 +571,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.35" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" dependencies = [ "android-tzdata", "iana-time-zone", @@ -696,7 +767,7 @@ dependencies = [ "hex", "schemars", "serde", - "serde-json-wasm", + "serde-json-wasm 0.5.2", "sha2 0.10.8", "static_assertions", "thiserror", @@ -805,7 +876,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -830,6 +901,17 @@ dependencies = [ "cosmwasm-std", ] +[[package]] +name = "cw-coins" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ef23b8172fcf1764a2193d6ac97a9c69e1c8aff194519032290eb98b887e18" +dependencies = [ + "cosmwasm-std", + "schemars", + "serde", +] + [[package]] name = "cw-controllers" version = "0.13.4" @@ -896,6 +978,23 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "cw-dex" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c4c002da51161e832615de09aa796e6915507418a7e4658204cd6c91ce89e7" +dependencies = [ + "apollo-cw-asset", + "apollo-utils", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw20 1.1.2", + "osmosis-std 0.14.0", + "thiserror", +] + [[package]] name = "cw-fable" version = "0.1.0" @@ -909,7 +1008,7 @@ dependencies = [ [[package]] name = "cw-grant-spec" version = "0.3.2" -source = "git+https://github.com/kakucodes/authzpp#556d90c8d4eade3e0d16964f3e63788849588ac7" +source = "git+https://github.com/kakucodes/authzpp#e5f294c0976d3277f71e6fa6eb34c2f0db0010af" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -921,9 +1020,9 @@ dependencies = [ [[package]] name = "cw-multi-test" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fff029689ae89127cf6d7655809a68d712f3edbdb9686c70b018ba438b26ca" +checksum = "cc392a5cb7e778e3f90adbf7faa43c4db7f35b6623224b08886d796718edb875" dependencies = [ "anyhow", "bech32", @@ -976,9 +1075,9 @@ dependencies = [ "cw-orch-contract-derive 0.20.1", "cw-orch-core 0.21.1", "cw-orch-daemon 0.21.0", - "cw-orch-fns-derive 0.18.0", + "cw-orch-fns-derive 0.18.1", "cw-orch-mock 0.21.1", - "cw-orch-networks 0.20.3", + "cw-orch-networks 0.20.4", "cw-orch-traits 0.21.0", "cw-utils 1.0.3", "hex", @@ -1013,7 +1112,7 @@ checksum = "94e9c3e46e2511c26d07da1fb5fcb47f83fa6d169dbb6eeff42c1b899b849726" dependencies = [ "convert_case", "quote", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -1111,7 +1210,7 @@ dependencies = [ "cosmrs 0.15.0", "cosmwasm-std", "cw-orch-core 0.21.1", - "cw-orch-networks 0.20.3", + "cw-orch-networks 0.20.4", "cw-orch-traits 0.21.0", "dirs", "ed25519-dalek", @@ -1151,9 +1250,9 @@ dependencies = [ [[package]] name = "cw-orch-fns-derive" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650dd1b708742406023b51e1b49a83a91ba31f8dd653c5b6cc7d3c3c0c214bfd" +checksum = "6423cd853c9a4aa07cc0a92a179b99bd8f8f6c696a468e3ca2ecd2575905db21" dependencies = [ "convert_case", "proc-macro2", @@ -1201,9 +1300,9 @@ dependencies = [ [[package]] name = "cw-orch-networks" -version = "0.20.3" +version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd0d95f5ac0bf86e491d6e6a11dcbde0bb9d5a7f943d60be10e03ebff19efb9f" +checksum = "05f473a79a1c2e90053cdab960eea905425e4678942ec1f4fd5f16c45e4d3dd4" dependencies = [ "cw-orch-core 0.21.1", "ibc-chain-registry", @@ -1988,9 +2087,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" [[package]] name = "ff" @@ -2004,9 +2103,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1676f435fc1dadde4d03e43f5d62b259e1ce5f40bd4ffb21db2b42ebe59c1382" +checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f" [[package]] name = "fixed-hash" @@ -2152,7 +2251,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -2234,9 +2333,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +checksum = "4fbd2820c5e49886948654ab546d0688ff24530286bdcf8fca3cefb16d4618eb" dependencies = [ "bytes", "fnv", @@ -2244,13 +2343,22 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.2.5", + "indexmap 2.2.6", "slab", "tokio", "tokio-util", "tracing", ] +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -2301,6 +2409,9 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] [[package]] name = "hex_lit" @@ -2568,7 +2679,7 @@ dependencies = [ "tendermint-light-client-verifier", "tendermint-proto 0.32.2", "time", - "uint", + "uint 0.9.5", ] [[package]] @@ -2631,9 +2742,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.5" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -2695,9 +2806,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" @@ -2777,12 +2888,12 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-targets 0.52.4", ] [[package]] @@ -2791,7 +2902,7 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "libc", "redox_syscall", ] @@ -2824,11 +2935,52 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +[[package]] +name = "membrane" +version = "0.1.0" +source = "git+https://github.com/MembraneFinance/membrane-core.git?branch=oracle_pricing#f2043a8830bfe5ab44d43dbd37663ec063701c74" +dependencies = [ + "apollo-cw-asset", + "cosmwasm-schema", + "cosmwasm-std", + "cw-coins", + "cw-dex", + "cw-storage-plus 0.13.4", + "osmosis-std 0.16.2", + "prost 0.11.9", + "pyth-sdk-cw", + "schemars", + "serde", + "serde-json-wasm 1.0.1", + "thiserror", + "uint 0.8.5", +] + +[[package]] +name = "membrane-helpers" +version = "0.2.2" +dependencies = [ + "cosmos-sdk-proto 0.21.1", + "cosmwasm-schema", + "cosmwasm-std", + "cw-grant-spec", + "cw-storage-plus 1.2.0", + "cw20 1.1.2", + "membrane", + "osmosis-destinations", + "osmosis-helpers", + "osmosis-std 0.21.0", + "outpost-utils", + "schemars", + "serde", + "thiserror", +] + [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "migaloo-destinations" @@ -2948,7 +3100,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -3018,7 +3170,7 @@ version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "cfg-if", "foreign-types", "libc", @@ -3035,7 +3187,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -3046,9 +3198,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.101" +version = "0.9.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ "cc", "libc", @@ -3101,6 +3253,38 @@ dependencies = [ "thiserror", ] +[[package]] +name = "osmosis-std" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fc0a9075efd64ed5a8be3bf134cbf1080570d68384f2ad58ffaac6c00d063fd" +dependencies = [ + "chrono", + "cosmwasm-std", + "osmosis-std-derive 0.13.2", + "prost 0.11.9", + "prost-types 0.11.9", + "schemars", + "serde", + "serde-cw-value", +] + +[[package]] +name = "osmosis-std" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75895e4db1a81ca29118e366365744f64314938327e4eedba8e6e462fb15e94f" +dependencies = [ + "chrono", + "cosmwasm-std", + "osmosis-std-derive 0.16.2", + "prost 0.11.9", + "prost-types 0.11.9", + "schemars", + "serde", + "serde-cw-value", +] + [[package]] name = "osmosis-std" version = "0.20.1" @@ -3157,6 +3341,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "osmosis-std-derive" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f47f0b2f22adb341bb59e5a3a1b464dde033181954bd055b9ae86d6511ba465b" +dependencies = [ + "itertools 0.10.5", + "proc-macro2", + "prost-types 0.11.9", + "quote", + "syn 1.0.109", +] + [[package]] name = "osmosis-std-derive" version = "0.20.1" @@ -3328,14 +3525,14 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -3361,9 +3558,9 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "platforms" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" +checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" [[package]] name = "powerfmt" @@ -3379,12 +3576,12 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "prettyplease" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" dependencies = [ "proc-macro2", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -3395,7 +3592,16 @@ checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", "impl-serde", - "uint", + "uint 0.9.5", +] + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", ] [[package]] @@ -3450,7 +3656,7 @@ dependencies = [ "itertools 0.11.0", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -3492,6 +3698,31 @@ dependencies = [ "thiserror", ] +[[package]] +name = "pyth-sdk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00bf2540203ca3c7a5712fdb8b5897534b7f6a0b6e7b0923ff00466c5f9efcb3" +dependencies = [ + "borsh", + "borsh-derive", + "hex", + "schemars", + "serde", +] + +[[package]] +name = "pyth-sdk-cw" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c04e9f2961bce1ef13b09afcdb5aee7d4ddde83669e5f9d2824ba422cb00de48" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "pyth-sdk", + "thiserror", +] + [[package]] name = "quote" version = "1.0.35" @@ -3559,9 +3790,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.3" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", @@ -3582,15 +3813,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "reqwest" -version = "0.11.26" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bf93c4af7a8bb7d879d51cebe797356ff10ae8516ace542b5182d9dcac10b2" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ "base64 0.21.7", "bytes", @@ -3692,6 +3923,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + [[package]] name = "rustc_version" version = "0.4.0" @@ -3703,11 +3940,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.31" +version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", @@ -3931,9 +4168,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.2" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -3944,9 +4181,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" dependencies = [ "core-foundation-sys", "libc", @@ -3985,6 +4222,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde-json-wasm" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05da0d153dd4595bdffd5099dc0e9ce425b205ee648eb93437ff7302af8c9a5" +dependencies = [ + "serde", +] + [[package]] name = "serde_bytes" version = "0.11.14" @@ -4002,7 +4248,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -4018,9 +4264,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" dependencies = [ "itoa", "ryu", @@ -4035,7 +4281,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -4153,9 +4399,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" @@ -4221,7 +4467,7 @@ dependencies = [ "proc-macro2", "quote", "struct_iterable_internal", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -4264,9 +4510,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.52" +version = "2.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "11a6ae1e52eb25aab8f3fb9fca13be982a373b8f1157ca14b897a825ba4a2d35" dependencies = [ "proc-macro2", "quote", @@ -4534,7 +4780,7 @@ dependencies = [ "tokio", "tracing", "url", - "uuid 1.7.0", + "uuid 1.8.0", "walkdir", ] @@ -4596,7 +4842,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -4657,9 +4903,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.36.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", @@ -4692,7 +4938,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -4739,9 +4985,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", @@ -4885,7 +5131,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -4930,6 +5176,18 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "uint" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9db035e67dfaf7edd9aebfe8676afcd63eed53c8a4044fed514c8cccf1835177" +dependencies = [ + "byteorder", + "crunchy", + "rustc-hex", + "static_assertions", +] + [[package]] name = "uint" version = "0.9.5" @@ -5024,9 +5282,9 @@ checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" [[package]] name = "uuid" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" [[package]] name = "vcpkg" @@ -5086,7 +5344,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.57", "wasm-bindgen-shared", ] @@ -5120,7 +5378,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.57", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5203,7 +5461,7 @@ dependencies = [ "protobuf", "schemars", "serde", - "uint", + "uint 0.9.5", ] [[package]] @@ -5391,7 +5649,7 @@ dependencies = [ [[package]] name = "withdraw-rewards-tax-grant" version = "0.3.2" -source = "git+https://github.com/kakucodes/authzpp#556d90c8d4eade3e0d16964f3e63788849588ac7" +source = "git+https://github.com/kakucodes/authzpp#e5f294c0976d3277f71e6fa6eb34c2f0db0010af" dependencies = [ "authzpp-utils", "cosmos-sdk-proto 0.20.0", @@ -5498,7 +5756,7 @@ dependencies = [ "cw20-base 1.1.2", "itertools 0.10.5", "thiserror", - "uint", + "uint 0.9.5", ] [[package]] @@ -5514,7 +5772,7 @@ dependencies = [ "cw20-base 1.1.2", "itertools 0.10.5", "thiserror", - "uint", + "uint 0.9.5", ] [[package]] @@ -5779,6 +6037,7 @@ dependencies = [ "cw20 1.1.2", "cw20-stake 2.1.0", "cw20-vesting 1.6.0", + "membrane-helpers", "osmosis-destinations", "osmosis-helpers", "osmosis-std 0.21.0", @@ -5813,6 +6072,7 @@ dependencies = [ "cw20 1.1.2", "cw20-stake 2.1.0", "cw20-vesting 1.6.0", + "membrane-helpers", "osmosis-destinations", "osmosis-helpers", "osmosis-std 0.21.0", @@ -5884,5 +6144,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.57", ] diff --git a/Cargo.toml b/Cargo.toml index dafac19..83d4bb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,12 +44,10 @@ wynd-stake = { git = "https://github.com/wynddao/wynddao.git", tag = "v1.7.0", f wyndex-factory = { git = "https://github.com/wynddao/wynddex.git", tag = "v2.2.0" } outpost-utils = { path = "./packages/utils" } wynd-helpers = { path = "./packages/wynd-helpers" } +membrane-helpers = { path = "./packages/membrane-helpers" } terraswap-helpers = { path = "./packages/terraswap-helpers" } -# wyndex-stake = { git = "https://github.com/wynddao/wynddex.git", tag = "v2.2.0" } osmosis-std = "0.21.0" - osmosis-helpers = { path = "./packages/osmosis-helpers" } -# mars-types = { git = "https://github.com/mars-protocol/contracts.git", version = "2.0.0" } white-whale = { git = "https://github.com/White-Whale-Defi-Platform/white-whale-core.git", tag = "v1.4.0-juno-1-token_factory-hotfix" } cw-grant-spec = { git = "https://github.com/kakucodes/authzpp", version = "0.3.2" } @@ -59,14 +57,9 @@ withdraw-rewards-tax-grant = { git = "https://github.com/kakucodes/authzpp", ver balance-token-swap = { git = "https://github.com/racoon-supply-rac/balance-dao", features = [ "library", ] } -# stake-easy = { git = "https://github.com/arufa-research/stakeeasy-juno-contracts", features = [ -# "library", -# ], package = "staking-contract" } - bond-router = { git = "https://github.com/wynddao/wynd-lsd", features = [ "library", ] } - furnace = { git = "https://github.com/deving-zone-community/community-furnace.git", features = [ "library", ] } diff --git a/contracts/osmodca/Cargo.toml b/contracts/osmodca/Cargo.toml index 10f910c..874ed14 100644 --- a/contracts/osmodca/Cargo.toml +++ b/contracts/osmodca/Cargo.toml @@ -65,6 +65,7 @@ cw-orch = { workspace = true, optional = true } terraswap-helpers = { workspace = true } white-whale = { workspace = true } osmosis-helpers = { workspace = true } +membrane-helpers = { workspace = true } [dev-dependencies] diff --git a/contracts/osmodca/src/error.rs b/contracts/osmodca/src/error.rs index 5ef563c..8b33ba9 100644 --- a/contracts/osmodca/src/error.rs +++ b/contracts/osmodca/src/error.rs @@ -21,6 +21,9 @@ pub enum ContractError { #[error("Osmosis Helper Error: &{0}")] OsmosisHelperError(#[from] osmosis_helpers::errors::OsmosisHelperError), + #[error("Membrane Helper Error: &{0}")] + MembraneHelperError(#[from] membrane_helpers::errors::MembraneHelperError), + #[error("Semver parsing error: {0}")] SemVer(String), diff --git a/contracts/osmodca/src/execute.rs b/contracts/osmodca/src/execute.rs index 28bd1ec..f23ddb7 100644 --- a/contracts/osmodca/src/execute.rs +++ b/contracts/osmodca/src/execute.rs @@ -1,9 +1,10 @@ use std::iter; use cosmwasm_std::{coin, Addr, Attribute, Decimal, Deps, DepsMut, Env, Event, MessageInfo, Response, SubMsg, Timestamp}; +use membrane_helpers::msg_gen::stake_mbrn_msgs; use osmosis_destinations::{ comp_prefs::{OsmosisCompPrefs, OsmosisDestinationProject, OsmosisLsd, OsmosisPoolSettings}, - dest_project_gen::{mint_milk_tia_msgs, stake_ion_msgs, stake_mbrn_msgs}, + dest_project_gen::{mint_milk_tia_msgs, stake_ion_msgs}, pools::MultipleStoredPools, }; use osmosis_helpers::{ @@ -182,7 +183,7 @@ pub fn prefs_to_msgs( denoms: project_addrs.destination_projects.denoms.clone(), }, "uosmo", - target_asset.clone(), + &target_asset, )?; Ok(DestProjectMsgs { @@ -220,7 +221,7 @@ pub fn prefs_to_msgs( }, user_addr, &coin(comp_token_amount.u128(), "uosmo"), - target_asset.clone(), + &target_asset, current_timestamp.clone(), )?; @@ -390,6 +391,11 @@ pub fn prefs_to_msgs( token_min_amount_1, current_timestamp.clone(), )?), + OsmosisDestinationProject::DepositCollateral { as_asset, protocol } => unimplemented!(), + OsmosisDestinationProject::RepayDebt { + ltv_ratio_threshold, + protocol, + } => unimplemented!(), OsmosisDestinationProject::Unallocated {} => Ok(DestProjectMsgs::default()), _ => unimplemented!(), } diff --git a/contracts/osmodca/src/queries.rs b/contracts/osmodca/src/queries.rs index 91e1fc3..42e86c6 100644 --- a/contracts/osmodca/src/queries.rs +++ b/contracts/osmodca/src/queries.rs @@ -6,8 +6,11 @@ use crate::{ use cosmwasm_std::{Addr, Coin, Decimal, Deps, StdResult, Timestamp, Uint128}; use cw_grant_spec::grantable_trait::{dedupe_grant_reqs, GrantStructure, Grantable}; use cw_grant_spec::grants::{AuthorizationType, GrantBase, GrantRequirement, RevokeRequirement}; -use osmosis_destinations::comp_prefs::{OsmosisDestinationProject, OsmosisLsd, OsmosisPoolSettings}; -use osmosis_destinations::grants::{membrane_stake_grant, mint_milk_tia_grant, stake_ion_grants}; +use membrane_helpers::grants::membrane_stake_grant; +use osmosis_destinations::comp_prefs::{ + OsmosisDepositCollateral, OsmosisDestinationProject, OsmosisLsd, OsmosisPoolSettings, +}; +use osmosis_destinations::grants::{mint_milk_tia_grant, stake_ion_grants}; use osmosis_helpers::osmosis_lp::{join_cl_pool_grants, join_classic_pool_grants}; use osmosis_helpers::osmosis_swap::osmosis_swap_grants; use outpost_utils::comp_prefs::TakeRate; @@ -207,6 +210,14 @@ pub fn gen_comp_pref_grants( pool_id: _, pool_settings: OsmosisPoolSettings::ConcentratedLiquidity { .. }, } => join_cl_pool_grants(base), + OsmosisDestinationProject::DepositCollateral { + as_asset, + protocol: OsmosisDepositCollateral::Membrane { position_id, and_then }, + } => unimplemented!(), + OsmosisDestinationProject::RepayDebt { + ltv_ratio_threshold, + protocol, + } => unimplemented!(), } }); diff --git a/contracts/osmostake/Cargo.toml b/contracts/osmostake/Cargo.toml index de56cf3..1a4a8e3 100644 --- a/contracts/osmostake/Cargo.toml +++ b/contracts/osmostake/Cargo.toml @@ -66,6 +66,7 @@ terraswap-helpers = { workspace = true } white-whale = { workspace = true } osmosis-helpers = { workspace = true } withdraw-rewards-tax-grant = { workspace = true } +membrane-helpers = { workspace = true } [dev-dependencies] diff --git a/contracts/osmostake/src/contract.rs b/contracts/osmostake/src/contract.rs index 35a7442..089fa23 100644 --- a/contracts/osmostake/src/contract.rs +++ b/contracts/osmostake/src/contract.rs @@ -1,17 +1,22 @@ use crate::error::ContractError; use crate::msg::{CompPrefsWithAddresses, ExecuteMsg, InstantiateMsg, MigrateMsg, OsmostakeCompoundPrefs, QueryMsg}; -use crate::state::{ADMIN, AUTHORIZED_ADDRS, KNOWN_DENOMS, KNOWN_OSMO_POOLS, KNOWN_USDC_POOLS, PROJECT_ADDRS, TAKE_RATE}; +use crate::state::{ + SubmsgData, ADMIN, AUTHORIZED_ADDRS, KNOWN_DENOMS, KNOWN_OSMO_POOLS, KNOWN_USDC_POOLS, PROJECT_ADDRS, SUBMSG_DATA, + TAKE_RATE, +}; use crate::{execute, queries}; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; +use cosmwasm_std::Event; use cosmwasm_std::{ to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdError, StdResult, Timestamp, }; use cw2::{get_contract_version, set_contract_version}; use cw_grant_spec::grantable_trait::{GrantStructure, Grantable}; +use membrane_helpers::utils::membrane_mint_cdt; use osmosis_destinations::pools::PoolForEach; - use outpost_utils::comp_prefs::TakeRate; +use outpost_utils::helpers::DestProjectMsgs; use semver::Version; // version info for migration info @@ -19,13 +24,45 @@ const CONTRACT_NAME: &str = "crates.io:ac-outpost-osmostake"; const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); #[cfg_attr(not(feature = "library"), entry_point)] -pub fn reply(_deps: DepsMut, _env: Env, msg: Reply) -> Result { - match msg { +pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result { + let mut msgs = match msg { // an id of 0 means we don't care about the response - Reply { id: 0, .. } => Ok(Response::default()), - // TODO handle non-zero ids - _ => Err(ContractError::Unauthorized {}), - } + Reply { id: 0, .. } => Ok(DestProjectMsgs::default()), + Reply { id, result: _result } => match SUBMSG_DATA.may_load(deps.as_ref().storage, &id) { + Ok(Some(SubmsgData::BondGamms { pool_id: _pool_id })) => { + // TODO: Implement the BondGamms submsg + Ok(DestProjectMsgs::default()) + } + Ok(Some(SubmsgData::MintCdt { + user_addr, + position_id, + and_then, + })) => { + let project_addrs = PROJECT_ADDRS.load(deps.as_ref().storage)?; + + let mut cdt_msgs = membrane_mint_cdt( + &deps.querier, + &project_addrs.destination_projects.projects.membrane, + &project_addrs.destination_projects.denoms.cdt, + &user_addr, + position_id, + and_then, + env.block.time, + )?; + + cdt_msgs.append_events(vec![Event::new("mint_cdt_reply") + .add_attribute("user_addr", user_addr.to_string()) + .add_attribute("position_id", position_id.to_string())]); + + Ok(cdt_msgs) + } + _ => Err(ContractError::SubMsgReplyIdNotFound { reply_id: msg.id }), + }, + }?; + + msgs.prepend_events(vec![Event::new("reply").add_attribute("id", msg.id.to_string())]); + + Ok(msgs.to_response(&env.contract.address)?) } #[cfg_attr(not(feature = "library"), entry_point)] @@ -144,7 +181,7 @@ pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result Result { +pub fn execute(deps: &mut DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> Result { match msg { ExecuteMsg::UpdateProjectAddresses(addresses) => { if info.sender != ADMIN.load(deps.storage)? { diff --git a/contracts/osmostake/src/error.rs b/contracts/osmostake/src/error.rs index 9f1daea..48dd35e 100644 --- a/contracts/osmostake/src/error.rs +++ b/contracts/osmostake/src/error.rs @@ -24,6 +24,9 @@ pub enum ContractError { #[error("Osmosis Helper Error: &{0}")] OsmosisHelperError(#[from] osmosis_helpers::errors::OsmosisHelperError), + #[error("Membrane Helper Error: &{0}")] + MembraneHelperError(#[from] membrane_helpers::errors::MembraneHelperError), + #[error("Semver parsing error: {0}")] SemVer(String), @@ -62,6 +65,9 @@ pub enum ContractError { #[error("Contract Migration Version Mismatch: expected higher than {expected}, receieved {received}")] MigrationVersionMismatch { expected: String, received: String }, + + #[error("Submessage reply id not found {reply_id}")] + SubMsgReplyIdNotFound { reply_id: u64 }, } impl From for ContractError { diff --git a/contracts/osmostake/src/execute.rs b/contracts/osmostake/src/execute.rs index a8fc623..96e3dfc 100644 --- a/contracts/osmostake/src/execute.rs +++ b/contracts/osmostake/src/execute.rs @@ -1,48 +1,53 @@ -use std::iter; - -use cosmwasm_std::{coin, Addr, Attribute, Decimal, Deps, DepsMut, Env, Event, MessageInfo, Response, SubMsg, Timestamp}; +use cosmwasm_std::{coin, coins, Addr, Attribute, Decimal, DepsMut, Env, Event, MessageInfo, Response, Timestamp}; +use membrane_helpers::{ + msg_gen::{deposit_into_cdp_msgs, repay_cdt_msgs, stake_mbrn_msgs}, + utils::{ltv_in_range, membrane_deposit_collateral_and_then}, +}; use osmosis_destinations::{ - comp_prefs::{OsmosisCompPrefs, OsmosisDestinationProject, OsmosisLsd, OsmosisPoolSettings}, - dest_project_gen::{mint_milk_tia_msgs, stake_ion_msgs, stake_mbrn_msgs}, + comp_prefs::{ + OsmosisCompPrefs, OsmosisDepositCollateral, OsmosisDestinationProject, OsmosisLsd, OsmosisPoolSettings, + OsmosisRepayDebt, RepayThreshold, + }, + dest_project_gen::{mint_milk_tia_msgs, stake_ion_msgs}, pools::MultipleStoredPools, }; use osmosis_helpers::{ osmosis_lp::{gen_join_cl_pool_single_sided_msgs, gen_join_classic_pool_single_sided_msgs}, osmosis_swap::{ estimate_token_out_min_amount, generate_known_to_known_swap_and_sim_msg, generate_known_to_unknown_route, - generate_known_to_unknown_swap_and_sim_msg, generate_swap, OsmosisRoutePools, + generate_known_to_unknown_swap_and_sim_msg, generate_swap, generate_swap_and_sim_msg, OsmosisRoutePools, }, }; - use outpost_utils::{ - comp_prefs::{DestinationAction, TakeRate}, - helpers::{calculate_compound_amounts, is_authorized_compounder, prefs_sum_to_one, sum_coins, DestProjectMsgs}, - msg_gen::create_exec_msg, + comp_prefs::{CompoundPrefs, DestinationAction, TakeRate}, + helpers::{ + calculate_compound_amounts, combine_responses, is_authorized_compounder, prefs_sum_to_one, sum_coins, + DestProjectMsgs, + }, }; use sail_destinations::dest_project_gen::mint_eris_lsd_msgs; - +use std::iter; use universal_destinations::dest_project_gen::{native_staking_msg, send_tokens_msgs}; use white_whale::pool_network::asset::{Asset, AssetInfo}; use withdraw_rewards_tax_grant::{client::WithdrawRewardsTaxClient, msg::SimulateExecuteResponse}; use crate::{ msg::ContractAddrs, - state::{ADMIN, AUTHORIZED_ADDRS, KNOWN_DENOMS, KNOWN_OSMO_POOLS, KNOWN_USDC_POOLS, PROJECT_ADDRS}, + state::{ + SubmsgData, ADMIN, AUTHORIZED_ADDRS, KNOWN_DENOMS, KNOWN_OSMO_POOLS, KNOWN_USDC_POOLS, SUBMSG_DATA, SUBMSG_REPLY_ID, + }, ContractError, }; pub fn compound( - deps: DepsMut, + deps: &mut DepsMut, env: Env, info: MessageInfo, project_addresses: ContractAddrs, user_address: String, comp_prefs: OsmosisCompPrefs, fee_to_charge: Option, - TakeRate { - max_tax_fee, - take_rate_addr, - }: TakeRate, + TakeRate { .. }: TakeRate, ) -> Result { // validate that the preference quantites sum to 1 let _ = prefs_sum_to_one(&comp_prefs)?; @@ -73,63 +78,35 @@ pub fn compound( // the list of all the compounding msgs to broadcast on behalf of the user based on their comp prefs let all_msgs = prefs_to_msgs( &project_addresses, - // &env.block, - // staking_denom, &user_addr, total_rewards.clone(), comp_prefs, - deps.as_ref(), + &mut deps.branch(), env.block.time, )?; - let combined_msgs = all_msgs.iter().fold(DestProjectMsgs::default(), |mut acc, msg| { - acc.msgs.append(&mut msg.msgs.clone()); - acc.sub_msgs.append(&mut msg.sub_msgs.clone()); - acc.events.append(&mut msg.events.clone()); - acc - }); - - let amount_automated_event = - Event::new("amount_automated").add_attributes([total_rewards].iter().enumerate().map(|(i, coin)| Attribute { - key: format!("amount_{}", i), - value: coin.to_string(), - })); - - // the final exec message that will be broadcast and contains all the sub msgs - let exec_msg = create_exec_msg(&env.contract.address, combined_msgs.msgs)?; - - let resp = Response::default() + // prepare the response to the user with the withdraw rewards and other general metadata + let withdraw_response = Response::default() .add_attribute("action", "outpost compound") .add_message(withdraw_msg) - .add_attribute("subaction", "withdraw rewards") - .add_event(amount_automated_event) - // .add_attribute("amount_automated", to_json_binary(&[total_rewards])?.to_string()) - .add_message(exec_msg) - .add_submessages( - combined_msgs - .sub_msgs - .into_iter() - .filter_map(|sub_msg| { - if let (Ok(exec_msg), false) = ( - create_exec_msg(&env.contract.address, sub_msg.1.clone()), - sub_msg.1.is_empty(), - ) { - Some((sub_msg.0, exec_msg, sub_msg.2)) - } else { - None - } - }) - .map(|(id, msg, reply_on)| SubMsg { - msg, - gas_limit: None, - id, - reply_on, - }) - .collect::>(), - ) - .add_events(combined_msgs.events); - - Ok(resp) + .add_attributes(vec![("subaction", "withdraw rewards"), ("user", &user_addr.to_string())]) + // event to track the amount of rewards that were automated + .add_event( + Event::new("amount_automated").add_attributes([total_rewards].iter().enumerate().map(|(i, coin)| Attribute { + key: format!("amount_{}", i), + value: coin.to_string(), + })), + ); + + let resps = combine_responses(vec![ + withdraw_response, + all_msgs + .into_iter() + .collect::() + .to_response(&env.contract.address)?, + ]); + + Ok(resps) } /// Converts the user's compound preferences into a list of @@ -139,9 +116,10 @@ pub fn prefs_to_msgs( user_addr: &Addr, total_rewards: cosmwasm_std::Coin, comp_prefs: OsmosisCompPrefs, - deps: Deps, + deps_mut: &mut DepsMut<'_>, current_timestamp: Timestamp, ) -> Result, ContractError> { + // let deps = deps_mut.branch().as_ref(); let dca_denom = total_rewards.denom.clone(); // calculates the amount of ujuno that will be used for each target project accurately. @@ -174,7 +152,7 @@ pub fn prefs_to_msgs( )?), OsmosisDestinationProject::TokenSwap { target_asset } => { let route = generate_known_to_unknown_route( - deps.storage, + deps_mut.as_ref().storage, OsmosisRoutePools { stored_denoms: KNOWN_DENOMS, stored_pools: MultipleStoredPools { @@ -185,7 +163,7 @@ pub fn prefs_to_msgs( denoms: project_addrs.destination_projects.denoms.clone(), }, "uosmo", - target_asset.clone(), + &target_asset, )?; Ok(DestProjectMsgs { @@ -194,7 +172,7 @@ pub fn prefs_to_msgs( user_addr, route.clone(), estimate_token_out_min_amount( - &deps.querier, + &deps_mut.as_ref().querier, &route, "uosmo".to_string(), comp_token_amount, @@ -210,8 +188,8 @@ pub fn prefs_to_msgs( target_asset, } => { let (sim, swap_msgs) = generate_known_to_unknown_swap_and_sim_msg( - &deps.querier, - deps.storage, + &deps_mut.querier, + deps_mut.as_ref().storage, OsmosisRoutePools { stored_denoms: KNOWN_DENOMS, stored_pools: MultipleStoredPools { @@ -223,14 +201,14 @@ pub fn prefs_to_msgs( }, user_addr, &coin(comp_token_amount.u128(), "uosmo"), - target_asset.clone(), + &target_asset, current_timestamp, )?; // after the swap we can send the estimated funds to the target address let mut send_msgs = send_tokens_msgs( user_addr, - &deps.api.addr_validate(&to_address)?, + &deps_mut.api.addr_validate(&to_address)?, Asset { info: AssetInfo::NativeToken { denom: target_asset.denom, @@ -254,8 +232,8 @@ pub fn prefs_to_msgs( } => { // swap OSMO to TIA let (est_tia, swap_to_tia_msgs) = generate_known_to_known_swap_and_sim_msg( - &deps.querier, - deps.storage, + &deps_mut.querier, + deps_mut.as_ref().storage, OsmosisRoutePools { stored_denoms: KNOWN_DENOMS, stored_pools: MultipleStoredPools { @@ -285,8 +263,8 @@ pub fn prefs_to_msgs( OsmosisDestinationProject::IonStaking {} => { // swap OSMO to ION let (est_ion, swap_to_ion_msgs) = generate_known_to_known_swap_and_sim_msg( - &deps.querier, - deps.storage, + &deps_mut.querier, + deps_mut.as_ref().storage, OsmosisRoutePools { stored_denoms: KNOWN_DENOMS, stored_pools: MultipleStoredPools { @@ -312,8 +290,8 @@ pub fn prefs_to_msgs( OsmosisDestinationProject::MembraneStake {} => { // swap OSMO to MBRN let (est_mbrn, swap_to_mbrn_msgs) = generate_known_to_known_swap_and_sim_msg( - &deps.querier, - deps.storage, + &deps_mut.querier, + deps_mut.as_ref().storage, OsmosisRoutePools { stored_denoms: KNOWN_DENOMS, stored_pools: MultipleStoredPools { @@ -355,8 +333,8 @@ pub fn prefs_to_msgs( pool_id, pool_settings: OsmosisPoolSettings::Standard { bond_tokens }, } => Ok(gen_join_classic_pool_single_sided_msgs( - &deps.querier, - deps.storage, + &deps_mut.querier, + deps_mut.as_ref().storage, OsmosisRoutePools { stored_denoms: KNOWN_DENOMS, stored_pools: MultipleStoredPools { @@ -383,7 +361,7 @@ pub fn prefs_to_msgs( token_min_amount_1, }, } => Ok(gen_join_cl_pool_single_sided_msgs( - &deps.querier, + &deps_mut.querier, user_addr, pool_id, &coin(comp_token_amount.u128(), "uosmo"), @@ -394,26 +372,124 @@ pub fn prefs_to_msgs( current_timestamp.clone(), )?), - // OsmosisDestinationProject::RedBankLendAsset { - // target_asset, - // account_id, - // } => Ok(DestProjectMsgs::default()), - // OsmosisDestinationProject::WhiteWhaleSatellite { asset } => Ok(white_whale_satellite_msgs( - // user_addr, - // &project_addrs.destination_projects.projects.white_whale_satellite, - // comp_token_amount, - // )?), - // OsmosisDestinationProject::MembraneRepay { - // asset, - // ltv_ratio_threshold, - // } => Ok(DestProjectMsgs::default()), - // OsmosisDestinationProject::MarginedRepay { - // asset, - // ltv_ratio_threshold, - // } => Ok(DestProjectMsgs::default()), - // OsmosisDestinationProject::MembraneDeposit { position_id, asset } => Ok(DestProjectMsgs::default()), - - // OsmosisDestinationProject::DaoDaoStake { dao } => Ok(DestProjectMsgs::default()), + OsmosisDestinationProject::DepositCollateral { + as_asset, + protocol: OsmosisDepositCollateral::Membrane { position_id, and_then }, + } => { + let (amount, swap_msgs) = generate_known_to_unknown_swap_and_sim_msg( + &deps_mut.querier, + deps_mut.as_ref().storage, + OsmosisRoutePools { + stored_denoms: KNOWN_DENOMS, + stored_pools: MultipleStoredPools { + osmo: KNOWN_OSMO_POOLS, + usdc: KNOWN_USDC_POOLS, + }, + pools: project_addrs.destination_projects.swap_routes.clone(), + denoms: project_addrs.destination_projects.denoms.clone(), + }, + user_addr, + &coin(comp_token_amount.u128(), "uosmo"), + &as_asset, + current_timestamp, + )?; + let expected_deposits = coins(amount.u128(), as_asset.denom); + + Ok(match and_then.clone() { + // if there is no and_then action we just deposit the collateral and be done + None => deposit_into_cdp_msgs( + user_addr, + &project_addrs.destination_projects.projects.membrane.cdp, + position_id, + &expected_deposits, + None, + ), + // if there is a followup we likely will wind up spawning a submessage so there's more data to pass + Some(and_then) => membrane_deposit_collateral_and_then( + deps_mut.storage, + user_addr, + &project_addrs.destination_projects.projects.membrane.cdp, + position_id, + &expected_deposits, + &and_then, + SubmsgData::MintCdt { + user_addr: user_addr.clone(), + position_id, + and_then: and_then.clone(), + }, + SUBMSG_REPLY_ID, + SUBMSG_DATA, + ), + }?) + } + // repaying debt when the ltv has passed the threshold or there is no threshold set + // this means we should repay and be done with it + OsmosisDestinationProject::RepayDebt { + ltv_ratio_threshold: threshold, + protocol: OsmosisRepayDebt::Membrane { position_id }, + } if ltv_in_range( + &deps_mut.querier, + &project_addrs.destination_projects.projects.membrane.cdp, + user_addr, + position_id, + threshold.clone(), + ) => + { + // swap OSMO to CDT + let (est_cdt, swap_to_cdt_msgs) = generate_known_to_known_swap_and_sim_msg( + &deps_mut.querier, + deps_mut.as_ref().storage, + OsmosisRoutePools { + stored_denoms: KNOWN_DENOMS, + stored_pools: MultipleStoredPools { + osmo: KNOWN_OSMO_POOLS, + usdc: KNOWN_USDC_POOLS, + }, + pools: project_addrs.destination_projects.swap_routes.clone(), + denoms: project_addrs.destination_projects.denoms.clone(), + }, + user_addr, + &coin(comp_token_amount.u128(), "uosmo"), + &project_addrs.destination_projects.denoms.cdt, + current_timestamp, + )?; + + let mut repay_msgs = repay_cdt_msgs( + user_addr, + &project_addrs.destination_projects.projects.membrane.cdp, + position_id, + coin(est_cdt.u128(), project_addrs.destination_projects.denoms.cdt.clone()), + )?; + + repay_msgs.prepend_msgs(swap_to_cdt_msgs); + + Ok(repay_msgs) + } + // repaying debt when the ltv has not passed the threshold so we do the fallback + OsmosisDestinationProject::RepayDebt { + ltv_ratio_threshold: Some(RepayThreshold { otherwise, .. }), + .. + } => Ok(prefs_to_msgs( + project_addrs, + user_addr, + coin(comp_token_amount.u128(), "uosmo"), + CompoundPrefs { + relative: vec![DestinationAction { + destination: *otherwise, + amount: 1u128, + }], + }, + deps_mut, + current_timestamp, + )? + .into_iter() + .collect::()), + OsmosisDestinationProject::RepayDebt { + ltv_ratio_threshold: None, + .. + } => unimplemented!( + "this is already taken care of as the ltv in range qury is always true for 'No Threshold'" + ), OsmosisDestinationProject::Unallocated {} => Ok(DestProjectMsgs::default()), } }, diff --git a/contracts/osmostake/src/queries.rs b/contracts/osmostake/src/queries.rs index 59023e2..8b01e8c 100644 --- a/contracts/osmostake/src/queries.rs +++ b/contracts/osmostake/src/queries.rs @@ -3,14 +3,21 @@ use crate::{ msg::{AuthorizedCompoundersResponse, VersionResponse}, state::{ADMIN, AUTHORIZED_ADDRS}, }; - -use cosmwasm_std::{Addr, Decimal, Deps, StdResult, Timestamp}; +use cosmwasm_std::{Addr, Deps, StdResult, Timestamp}; use cw_grant_spec::grantable_trait::{dedupe_grant_reqs, GrantStructure, Grantable}; -use cw_grant_spec::grants::{AuthorizationType, GrantBase, GrantRequirement, RevokeRequirement}; -use osmosis_destinations::comp_prefs::{OsmosisDestinationProject, OsmosisLsd, OsmosisPoolSettings}; -use osmosis_destinations::grants::{membrane_stake_grant, mint_milk_tia_grant, stake_ion_grants}; -use osmosis_helpers::osmosis_lp::{join_cl_pool_grants, join_classic_pool_grants}; +use cw_grant_spec::grants::{GrantBase, GrantRequirement, RevokeRequirement}; +use membrane_helpers::grants::{ + membrane_deposit_grant, membrane_deposit_into_stability_pool_grant, membrane_mint_cdt_grant, membrane_repay_cdt_grant, + membrane_stake_grant, +}; +use osmosis_destinations::comp_prefs::{ + MembraneDepositCollateralAction, OsmosisDepositCollateral, OsmosisDestinationProject, OsmosisLsd, OsmosisPoolSettings, + OsmosisRepayDebt, RepayThreshold, +}; +use osmosis_destinations::grants::{mint_milk_tia_grant, stake_ion_grants}; +use osmosis_helpers::osmosis_lp::{join_cl_pool_grants, join_classic_pool_grants, join_osmosis_pool_grants}; use osmosis_helpers::osmosis_swap::osmosis_swap_grants; +use outpost_utils::comp_prefs::{CompoundPrefs, DestinationAction}; use sail_destinations::grants::eris_lsd_grant; use universal_destinations::grants::{native_send_token, native_staking_grant}; use white_whale::pool_network::asset::AssetInfo; @@ -111,12 +118,17 @@ pub fn gen_comp_pref_grants( granter, grantee, expiration, - grant_contract: _grant_contract, + grant_contract, grant_data: CompPrefsWithAddresses { - comp_prefs: OsmostakeCompoundPrefs { comp_prefs, .. }, + comp_prefs: + OsmostakeCompoundPrefs { + comp_prefs, + user_address, + tax_fee, + }, project_addresses, - .. + take_rate, }, }: GrantStructure, ) -> StdResult> { @@ -192,6 +204,96 @@ pub fn gen_comp_pref_grants( pool_id: _, pool_settings: OsmosisPoolSettings::ConcentratedLiquidity { .. }, } => join_cl_pool_grants(base), + OsmosisDestinationProject::DepositCollateral { + as_asset, + protocol: OsmosisDepositCollateral::Membrane { position_id, and_then }, + } => { + // we can skip the swap if it's already in the right asset (osmo) + let swap_grant = if as_asset.denom != project_addresses.destination_projects.denoms.osmo { + osmosis_swap_grants(base.clone()) + } else { + vec![] + }; + + // permission for the initial deposit into the CDP + let deposit_grant = membrane_deposit_grant( + base.clone(), + project_addresses.destination_projects.projects.membrane.cdp.clone(), + position_id, + vec![&as_asset.denom], + ); + + // permission for whatever the follow-up action may be requested + let and_then_grant = match and_then { + Some(MembraneDepositCollateralAction::MintCdt { desired_ltv }) => membrane_mint_cdt_grant( + base, + project_addresses.destination_projects.projects.membrane.cdp.clone(), + position_id.clone(), + desired_ltv, + ), + Some(MembraneDepositCollateralAction::EnterStabilityPool { .. }) => { + membrane_deposit_into_stability_pool_grant( + base, + project_addresses + .destination_projects + .projects + .membrane + .stability_pool + .clone(), + &project_addresses.destination_projects.denoms.cdt, + ) + } + Some(MembraneDepositCollateralAction::ProvideLiquidity { pool_settings, .. }) => { + join_osmosis_pool_grants(base, pool_settings) + } + + None => vec![], + }; + + [swap_grant, deposit_grant, and_then_grant].concat() + } + OsmosisDestinationProject::RepayDebt { + ltv_ratio_threshold, + protocol, + } => { + // permission for the initial repay + let repay_grants = match protocol { + OsmosisRepayDebt::Membrane { position_id } => membrane_repay_cdt_grant( + base.clone(), + project_addresses.destination_projects.projects.membrane.cdp.clone(), + position_id, + ), + }; + + // permission for whatever the follow-up action may be + let other_grants = match ltv_ratio_threshold { + Some(RepayThreshold { otherwise, .. }) => gen_comp_pref_grants(GrantStructure { + granter: granter.clone(), + grantee: grantee.clone(), + expiration: expiration.clone(), + grant_contract: grant_contract.clone(), + grant_data: CompPrefsWithAddresses { + comp_prefs: OsmostakeCompoundPrefs { + comp_prefs: CompoundPrefs { + relative: vec![DestinationAction { + amount: 1u128.into(), + destination: *otherwise, + }], + }, + user_address: user_address.clone(), + tax_fee: tax_fee.clone(), + }, + project_addresses: project_addresses.clone(), + take_rate: take_rate.clone(), + }, + }) + // Normally we wouldn't unwrap but since this is solely the simulation query it should be alright + .unwrap(), + None => vec![], + }; + + [repay_grants, other_grants].concat() + } } }); diff --git a/contracts/osmostake/src/state.rs b/contracts/osmostake/src/state.rs index 1444a69..f41cc23 100644 --- a/contracts/osmostake/src/state.rs +++ b/contracts/osmostake/src/state.rs @@ -1,11 +1,13 @@ -use cosmwasm_std::Addr; - +use crate::msg::ContractAddrs; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, Uint128}; use cw_storage_plus::{Item, Map}; -use osmosis_destinations::pools::{StoredDenoms, StoredPools}; +use osmosis_destinations::{ + comp_prefs::MembraneDepositCollateralAction, + pools::{StoredDenoms, StoredPools}, +}; use outpost_utils::comp_prefs::TakeRate; -use crate::msg::ContractAddrs; - pub const ADMIN: Item = Item::new("admin"); pub const AUTHORIZED_ADDRS: Item> = Item::new("allowed_addrs"); pub const TAKE_RATE: Item = Item::new("take_rate"); @@ -14,3 +16,18 @@ pub const PROJECT_ADDRS: Item = Item::new("project_addrs"); pub const KNOWN_OSMO_POOLS: StoredPools = Map::new("known_osmo_pools"); pub const KNOWN_USDC_POOLS: StoredPools = Map::new("known_usdc_pools"); pub const KNOWN_DENOMS: StoredDenoms = Map::new("known_denoms"); + +pub const SUBMSG_REPLY_ID: Item = Item::new("submsg_reply_id"); +pub const SUBMSG_DATA: Map<&u64, SubmsgData> = Map::new("submsg_data"); + +#[cw_serde] +pub enum SubmsgData { + MintCdt { + user_addr: Addr, + position_id: Uint128, + and_then: MembraneDepositCollateralAction, + }, + BondGamms { + pool_id: u64, + }, +} diff --git a/contracts/osmostake/src/tests/staking.rs b/contracts/osmostake/src/tests/staking.rs new file mode 100644 index 0000000..7df77d5 --- /dev/null +++ b/contracts/osmostake/src/tests/staking.rs @@ -0,0 +1,45 @@ +use osmosis_std::types::cosmos::staking::v1beta1::{ + MsgCreateValidator, MsgCreateValidatorResponse, MsgDelegate, MsgDelegateResponse, +}; + +use cw_orch::osmosis_test_tube::{ + cosmrs::proto::cosmos::staking::v1beta1::{ + QueryDelegationRequest, QueryDelegationResponse, QueryValidatorRequest, QueryValidatorResponse, + QueryValidatorsRequest, QueryValidatorsResponse, + }, + osmosis_test_tube::{fn_execute, Module, Runner}, +}; + +// Boilerplate code, copy and rename should just do the trick +pub struct Staking<'a, R: Runner<'a>> { + runner: &'a R, +} + +impl<'a, R: Runner<'a>> Module<'a, R> for Staking<'a, R> { + fn new(runner: &'a R) -> Self { + Self { runner } + } +} +// End Boilerplate code + +impl<'a, R> Staking<'a, R> +where + R: Runner<'a>, +{ + fn_execute! { + pub delegate: MsgDelegate => MsgDelegateResponse + } + fn_execute! { + pub create_validator: MsgCreateValidator => MsgCreateValidatorResponse + } + + // fn_query! { + // pub query_delegation ["/cosmos.staking.v1beta1.Query/Delegation"]: QueryDelegationRequest => QueryDelegationResponse + // } + // fn_query! { + // pub query_validator ["/cosmos.staking.v1beta1.Query/Validator"]: QueryValidatorRequest => QueryValidatorResponse + // } + // fn_query! { + // pub query_validators ["/cosmos.staking.v1beta1.Query/Validators"]: QueryValidatorsRequest => QueryValidatorsResponse + // } +} diff --git a/packages/membrane-helpers/Cargo.toml b/packages/membrane-helpers/Cargo.toml new file mode 100644 index 0000000..223c1ee --- /dev/null +++ b/packages/membrane-helpers/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "membrane-helpers" +version = { workspace = true } +authors = ["Marc "] +description = "Utility functions for Membrane Finance interaction used by Yieldmos outpost contracts" +edition = { workspace = true } + +[dependencies] +cw-storage-plus = { workspace = true } +cosmwasm-std = { workspace = true } +cosmwasm-schema = { workspace = true } +schemars = { workspace = true } +serde = { workspace = true } +thiserror = { workspace = true } +cosmos-sdk-proto = { workspace = true } +cw20 = { workspace = true } +outpost-utils = { workspace = true } +osmosis-std = { workspace = true } +osmosis-destinations = { workspace = true } +# swaprouter = { workspace = true } +cw-grant-spec = { workspace = true } +osmosis-helpers = { workspace = true } +membrane = { git = "https://github.com/MembraneFinance/membrane-core.git", branch = "oracle_pricing" } diff --git a/packages/membrane-helpers/README.md b/packages/membrane-helpers/README.md new file mode 100644 index 0000000..73cfdaa --- /dev/null +++ b/packages/membrane-helpers/README.md @@ -0,0 +1,5 @@ +# Yieldmos Membrane Finance Helpers + +This is a set of helper functions to be used by the outpost contracts when interacting with Membrane. The intent is to improve usage of interacting with collateral and debt positions. + +Both contain the runtime logic helpers as well as cw-grant-spec grant generators. diff --git a/packages/membrane-helpers/src/errors.rs b/packages/membrane-helpers/src/errors.rs new file mode 100644 index 0000000..19b1d00 --- /dev/null +++ b/packages/membrane-helpers/src/errors.rs @@ -0,0 +1,21 @@ +use cosmwasm_std::StdError; +use osmosis_destinations::errors::OsmosisDestinationError; +use osmosis_helpers::errors::OsmosisHelperError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum MembraneHelperError { + #[error("Outpost StdError: {0}")] + Std(#[from] StdError), + + #[error("Osmosis Destinations Error: &{0}")] + OsmosisDestinationError(#[from] OsmosisDestinationError), + + #[error("Osmosis Helpers Error: &{0}")] + OsmosisHelperError(#[from] OsmosisHelperError), + // #[error("Divide by zero error: {0}")] + // DivideByZeroError(#[from] DivideByZeroError), + + // #[error("Failed to convert int: {0}")] + // TryFromIntError(#[from] std::num::TryFromIntError), +} diff --git a/packages/membrane-helpers/src/grants.rs b/packages/membrane-helpers/src/grants.rs new file mode 100644 index 0000000..3621637 --- /dev/null +++ b/packages/membrane-helpers/src/grants.rs @@ -0,0 +1,85 @@ +use cosmos_sdk_proto::cosmos::base::v1beta1::Coin as CsdkCoin; +use cosmwasm_std::{Addr, Coin, Decimal, Uint128}; +use cw_grant_spec::grants::{GrantBase, GrantRequirement}; +use membrane::cdp; + +pub fn membrane_stake_grant( + base: GrantBase, + contract_addr: Addr, + mbrn_denom: &str, +) -> Vec { + vec![GrantRequirement::contract_exec_messages_auth( + base, + contract_addr, + vec![&membrane::staking::ExecuteMsg::Stake { user: None }], + Some(mbrn_denom), + )] +} + +pub fn membrane_deposit_into_stability_pool_grant( + base: GrantBase, + contract_addr: Addr, + cdt_denom: &str, +) -> Vec { + vec![GrantRequirement::contract_exec_messages_auth( + base, + contract_addr, + vec![&membrane::stability_pool::ExecuteMsg::Deposit { user: None }], + Some(cdt_denom), + )] +} + +pub fn membrane_deposit_grant( + base: GrantBase, + contract_addr: Addr, + position_id: Uint128, + deposits: Vec<&str>, +) -> Vec { + vec![GrantRequirement::contract_exec_messages_mutlidenoms_auth( + base, + contract_addr, + vec![&cdp::ExecuteMsg::Deposit { + position_id: Some(position_id), + position_owner: None, + }], + // We need to accept any basket denom, unsure if this will allow anythign or nothing + deposits, + )] +} + +pub fn membrane_mint_cdt_grant( + base: GrantBase, + contract_addr: Addr, + position_id: Uint128, + desired_ltv: Decimal, +) -> Vec { + vec![GrantRequirement::contract_exec_messages_auth( + base, + contract_addr, + vec![&membrane::cdp::ExecuteMsg::IncreaseDebt { + position_id, + amount: None, + LTV: Some(desired_ltv), + mint_to_addr: None, + }], + None, + )] +} + +pub fn membrane_repay_cdt_grant( + base: GrantBase, + contract_addr: Addr, + position_id: Uint128, +) -> Vec { + vec![GrantRequirement::contract_exec_messages_auth( + base, + contract_addr, + vec![&membrane::cdp::ExecuteMsg::Repay { + position_id, + position_owner: None, + send_excess_to: None, + }], + // this should allow any basket denom, unsure if this will allow anythign or nothing + None, + )] +} diff --git a/packages/membrane-helpers/src/lib.rs b/packages/membrane-helpers/src/lib.rs new file mode 100644 index 0000000..bea4fc4 --- /dev/null +++ b/packages/membrane-helpers/src/lib.rs @@ -0,0 +1,4 @@ +pub mod errors; +pub mod grants; +pub mod msg_gen; +pub mod utils; diff --git a/packages/membrane-helpers/src/msg_gen.rs b/packages/membrane-helpers/src/msg_gen.rs new file mode 100644 index 0000000..2010af7 --- /dev/null +++ b/packages/membrane-helpers/src/msg_gen.rs @@ -0,0 +1,157 @@ +use crate::errors::MembraneHelperError; +use cosmos_sdk_proto::cosmos::base::v1beta1::Coin as CsdkCoin; +use cosmwasm_std::{Addr, Coin, Decimal, Event, ReplyOn, Uint128}; +use membrane::{cdp, stability_pool}; +use outpost_utils::{ + helpers::DestProjectMsgs, + msg_gen::{create_exec_contract_msg, CosmosProtoMsg}, +}; + +pub type DestinationResult = Result; + +/// stake mbrn +pub fn stake_mbrn_msgs( + staker_addr: &Addr, + staking_contract_addr: &Addr, + mbrn_to_stake: Coin, +) -> DestinationResult { + Ok(DestProjectMsgs { + msgs: vec![CosmosProtoMsg::ExecuteContract(create_exec_contract_msg( + staking_contract_addr, + staker_addr, + &membrane::staking::ExecuteMsg::Stake { user: None }, + Some(vec![CsdkCoin { + denom: mbrn_to_stake.denom.to_string(), + amount: mbrn_to_stake.amount.to_string(), + }]), + )?)], + sub_msgs: vec![], + events: vec![Event::new("stake_mbrn").add_attribute("amount", mbrn_to_stake.to_string())], + }) +} + +/// deposit cdt into the stability pool +pub fn deposit_into_stability_pool_msgs( + depositor_addr: &Addr, + stability_pool_contract_addr: &Addr, + cdt_to_deposit: Coin, +) -> DestinationResult { + Ok(DestProjectMsgs { + msgs: vec![CosmosProtoMsg::ExecuteContract(create_exec_contract_msg( + stability_pool_contract_addr, + depositor_addr, + &stability_pool::ExecuteMsg::Deposit { user: None }, + Some(vec![CsdkCoin { + denom: cdt_to_deposit.denom.to_string(), + amount: cdt_to_deposit.amount.to_string(), + }]), + )?)], + sub_msgs: vec![], + events: vec![Event::new("deposit_into_stability_pool") + .add_attribute("amount", cdt_to_deposit.to_string())], + }) +} + +/// deposit basket assets into the user's CDP +pub fn deposit_into_cdp_msgs( + depositor_addr: &Addr, + cdp_contract_addr: &Addr, + position_id: Uint128, + deposits: &Vec, + as_submsg: Option<(u64, ReplyOn)>, +) -> DestinationResult { + let deposit_msg = CosmosProtoMsg::ExecuteContract(create_exec_contract_msg( + cdp_contract_addr, + depositor_addr, + &cdp::ExecuteMsg::Deposit { + position_id: Some(position_id), + position_owner: None, + }, + Some( + deposits + .into_iter() + .map(|coin| CsdkCoin { + denom: coin.denom.to_string(), + amount: coin.amount.to_string(), + }) + .collect::>(), + ), + )?); + + Ok(DestProjectMsgs { + msgs: if as_submsg.is_none() { + vec![] + } else { + vec![deposit_msg.clone()] + }, + sub_msgs: if let Some((id, reply)) = as_submsg { + vec![(id, vec![deposit_msg], reply)] + } else { + vec![] + }, + events: vec![Event::new("deposit_into_cdp") + .add_attribute( + "deposits", + deposits + .iter() + .map(|coin| coin.to_string()) + .collect::>() + .join(","), + ) + .add_attribute("position_id", position_id.to_string())], + }) +} + +/// Mint CDT +pub fn mint_cdt_msgs( + minter_addr: &Addr, + cdp_contract_addr: &Addr, + position_id: Uint128, + desired_ltv: &Decimal, +) -> DestinationResult { + Ok(DestProjectMsgs { + msgs: vec![CosmosProtoMsg::ExecuteContract(create_exec_contract_msg( + cdp_contract_addr, + minter_addr, + &cdp::ExecuteMsg::IncreaseDebt { + position_id, + amount: None, + LTV: Some(desired_ltv.clone()), + mint_to_addr: None, + }, + None, + )?)], + sub_msgs: vec![], + events: vec![Event::new("mint_cdt") + .add_attribute("desired_ltv", desired_ltv.to_string()) + .add_attribute("position_id", position_id.to_string())], + }) +} + +/// Repay CDT +pub fn repay_cdt_msgs( + repayer_addr: &Addr, + cdp_contract_addr: &Addr, + position_id: Uint128, + repay_amount: Coin, +) -> DestinationResult { + Ok(DestProjectMsgs { + msgs: vec![CosmosProtoMsg::ExecuteContract(create_exec_contract_msg( + cdp_contract_addr, + repayer_addr, + &cdp::ExecuteMsg::Repay { + position_id, + position_owner: None, + send_excess_to: None, + }, + Some(vec![CsdkCoin { + denom: repay_amount.denom.to_string(), + amount: repay_amount.amount.to_string(), + }]), + )?)], + sub_msgs: vec![], + events: vec![Event::new("repay_cdt") + .add_attribute("repay_amount", repay_amount.to_string()) + .add_attribute("position_id", position_id.to_string())], + }) +} diff --git a/packages/membrane-helpers/src/utils.rs b/packages/membrane-helpers/src/utils.rs new file mode 100644 index 0000000..49a9ec2 --- /dev/null +++ b/packages/membrane-helpers/src/utils.rs @@ -0,0 +1,200 @@ +use crate::{ + errors::MembraneHelperError, + msg_gen::{ + deposit_into_cdp_msgs, deposit_into_stability_pool_msgs, mint_cdt_msgs, DestinationResult, + }, +}; +use cosmwasm_std::{coin, Addr, Coin, QuerierWrapper, ReplyOn, Storage, Timestamp, Uint128}; +use cw_storage_plus::{Item, Map}; +use membrane::{ + cdp, + types::{Basket, UserInfo}, +}; +use osmosis_destinations::comp_prefs::{ + MembraneAddrs, MembraneDepositCollateralAction, OsmosisPoolSettings, RepayThreshold, +}; +use osmosis_helpers::osmosis_lp::{ + join_osmosis_cl_pool_single_side, join_osmosis_pool_single_side, +}; +use outpost_utils::{comp_prefs::store_submsg_data, msg_gen::CosmosProtoMsg}; +use serde::{de::DeserializeOwned, Serialize}; + +pub fn ltv_in_range( + querier: &QuerierWrapper, + cdp_contract_addr: &Addr, + user_addr: &Addr, + position_id: Uint128, + desired_ltv: Option, +) -> bool { + match desired_ltv { + Some(RepayThreshold { ltv_ratio, .. }) => { + // check if the user can mint enough CDT to hit the desired LTV + // this indirectly checks if the LTV is within the range + let est_mint: Result = querier.query_wasm_smart( + cdp_contract_addr, + &cdp::QueryMsg::SimulateMint { + position_info: UserInfo { + position_id, + position_owner: user_addr.to_string(), + }, + LTV: ltv_ratio, + }, + ); + + if let Ok(mintable_to_hit_ltv) = est_mint { + // if the user can mint any CDT to hit the desired LTV + // then we know the ltv is in range + mintable_to_hit_ltv.gt(&Uint128::zero()) + } else { + false + } + } + _ => true, + } +} + +/// filter out assets that are not in the CDP basket +pub fn basket_denoms_filter( + querier: &QuerierWrapper, + cdp_contract_addr: &Addr, + assets: &Vec, +) -> Result, MembraneHelperError> { + // check the currently allowed assets + let basket: Basket = + querier.query_wasm_smart(cdp_contract_addr, &cdp::QueryMsg::GetBasket {})?; + + Ok(assets + .into_iter() + // filter out assets that are not in the basket + .filter(|coin| { + basket + .collateral_types + .iter() + // very weird checking Asset against Coin.denom. might work might blow up + .any(|asset| asset.asset.info.to_string().eq(&coin.denom)) + }) + .cloned() + .collect()) +} + +/// Deposit collateral into a CDP and then do something else +/// This may spawn a submessage and if it does `membrane_mint_cdt` can be used to handle the followup +pub fn membrane_deposit_collateral_and_then( + store: &mut dyn Storage, + user_addr: &Addr, + cdp_addr: &Addr, + position_id: Uint128, + deposit_assets: &Vec, + and_then: &MembraneDepositCollateralAction, + submsg_data: T, + latest_reply_id_state: Item, + submsg_state: Map<&u64, T>, +) -> DestinationResult +where + T: Serialize + DeserializeOwned, +{ + // if the action is to mint CDT we dont need a submsg and we can + // just deposit the assets and mint however much CDT is needed + if let MembraneDepositCollateralAction::MintCdt { desired_ltv } = and_then { + let mut deposit_msg = + deposit_into_cdp_msgs(user_addr, &cdp_addr, position_id, deposit_assets, None)?; + + deposit_msg.concat_after(mint_cdt_msgs( + user_addr, + cdp_addr, + position_id, + desired_ltv, + )?); + + Ok(deposit_msg) + // if the action is not to mint CDT we need to store the submsg data + // so that we can query how much CDT to mint when the submsg returns + } else { + deposit_into_cdp_msgs( + user_addr, + &cdp_addr, + position_id, + deposit_assets, + Some(( + store_submsg_data(store, submsg_data, latest_reply_id_state, submsg_state)?, + ReplyOn::Success, + )), + ) + } +} + +/// Mint CDT and then do something else. +/// This function is intended to be able to rely on accurately querying how much CDT to mint +/// and thus cannot be used directly after adding collateral to a CDP. +pub fn membrane_mint_cdt( + querier: &QuerierWrapper, + membrane_addrs: &MembraneAddrs, + cdt_denom: &str, + user_addr: &Addr, + position_id: Uint128, + and_then: MembraneDepositCollateralAction, + current_timestamp: Timestamp, +) -> DestinationResult { + let desired_ltv = and_then.desired_ltv(); + let simulated_cdt: Uint128 = querier.query_wasm_smart( + membrane_addrs.cdp.clone(), + &cdp::QueryMsg::SimulateMint { + position_info: UserInfo { + position_id, + position_owner: user_addr.to_string(), + }, + LTV: desired_ltv, + }, + )?; + // the amount of cdt we can expect to have minted based off our simulation + let minted_cdt = coin(simulated_cdt.u128(), cdt_denom); + + let mut mint_cdt = mint_cdt_msgs(user_addr, &membrane_addrs.cdp, position_id, &desired_ltv)?; + + match and_then { + MembraneDepositCollateralAction::MintCdt { desired_ltv } => (), + MembraneDepositCollateralAction::EnterStabilityPool { .. } => { + mint_cdt.concat_after(deposit_into_stability_pool_msgs( + user_addr, + &membrane_addrs.stability_pool, + minted_cdt, + )?); + } + MembraneDepositCollateralAction::ProvideLiquidity { + pool_id, + pool_settings: OsmosisPoolSettings::Standard { bond_tokens }, + .. + } => { + let lp_msgs: Vec = + join_osmosis_pool_single_side(user_addr, pool_id, minted_cdt, bond_tokens)?; + + mint_cdt.append_msgs(lp_msgs); + } + MembraneDepositCollateralAction::ProvideLiquidity { + pool_id, + pool_settings: + OsmosisPoolSettings::ConcentratedLiquidity { + lower_tick, + upper_tick, + token_min_amount_0, + token_min_amount_1, + }, + .. + } => { + let lp_msgs: Vec = join_osmosis_cl_pool_single_side( + querier, + user_addr, + pool_id, + minted_cdt, + lower_tick, + upper_tick, + token_min_amount_0, + token_min_amount_1, + current_timestamp, + )?; + + mint_cdt.append_msgs(lp_msgs); + } + } + Ok(mint_cdt) +} diff --git a/packages/osmosis-destinations/Cargo.toml b/packages/osmosis-destinations/Cargo.toml index 152ccdd..ccc31a6 100644 --- a/packages/osmosis-destinations/Cargo.toml +++ b/packages/osmosis-destinations/Cargo.toml @@ -24,5 +24,4 @@ bond-router = { workspace = true, optional = true } cw-grant-spec = { workspace = true } osmosis-std = { workspace = true } sail-destinations = { workspace = true } -# membrane = { workspace = true } struct_iterable = { workspace = true } diff --git a/packages/osmosis-destinations/src/comp_prefs.rs b/packages/osmosis-destinations/src/comp_prefs.rs index cef3b98..2a69ce5 100644 --- a/packages/osmosis-destinations/src/comp_prefs.rs +++ b/packages/osmosis-destinations/src/comp_prefs.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, Api, Uint128}; +use cosmwasm_std::{Addr, Api, Decimal, Uint128}; use outpost_utils::comp_prefs::CompoundPrefs; use crate::{ @@ -55,22 +55,6 @@ pub enum OsmosisDestinationProject { // DaoDaoStake { // dao: OsmosisDao, // }, - MembraneStake {}, - // MembraneDeposit { - // position_id: Uint128, - // asset: String, - // }, - // MembraneRepay { - // asset: String, - // ltv_ratio_threshold: Decimal, - // }, - // MarginedRepay { - // asset: String, - // ltv_ratio_threshold: Decimal, - // }, - // NolusLendAsset { - // asset: String, - // }, // /// Pay back borrowed balance. Currently the first denom strings specified in the vector will be // /// paid back first. No order is guaranteed when no vector is passed in. @@ -145,7 +129,8 @@ pub enum OsmosisDestinationProject { MintLsd { lsd: OsmosisLsd, - }, // RedBankVault { + }, + // RedBankVault { // vault_address: String, // leverage_amount: u64, // }, @@ -155,9 +140,83 @@ pub enum OsmosisDestinationProject { // WhiteWhaleSatellite { // asset: String, // }, + + /// Stake as MBRN + MembraneStake {}, + + DepositCollateral { + /// swap the input asset(s) to the desired asset before depositing + as_asset: TargetAsset, + protocol: OsmosisDepositCollateral, + }, + + RepayDebt { + /// repayment conditional based on the ltv ratio + /// if None then repay the debt regardless of the ltv ratio + /// if the repay threshold is hit the WHOLE compounding amount will be used to repay the debt + ltv_ratio_threshold: Option, + protocol: OsmosisRepayDebt, + }, + Unallocated {}, } +#[cw_serde] +pub enum OsmosisDepositCollateral { + /// Deposit asset(s) into the CDP + Membrane { + position_id: Uint128, + and_then: Option, + }, + // RedBankDepositCollateral { + // account_id: String, + // deposit_denom: String, + // }, +} + +#[cw_serde] +pub enum OsmosisRepayDebt { + // RedBank, + // Margined { + // // asset to repay as + // asset: String, + // }, + /// repaid as CDT + Membrane { position_id: Uint128 }, +} + +#[cw_serde] +pub enum MembraneDepositCollateralAction { + /// mint cdt up to the desired_ltv then leave it liquid + MintCdt { desired_ltv: Decimal }, + + /// Mint CDT and deposit it into the stability pool contract + EnterStabilityPool { desired_ltv: Decimal }, + + /// Mint CDT and enter an osmosis pool single sided + ProvideLiquidity { + desired_ltv: Decimal, + pool_id: u64, + pool_settings: OsmosisPoolSettings, + }, +} + +impl MembraneDepositCollateralAction { + pub fn desired_ltv(&self) -> Decimal { + match self { + MembraneDepositCollateralAction::MintCdt { desired_ltv } => *desired_ltv, + MembraneDepositCollateralAction::EnterStabilityPool { desired_ltv } => *desired_ltv, + MembraneDepositCollateralAction::ProvideLiquidity { desired_ltv, .. } => *desired_ltv, + } + } +} + +#[cw_serde] +pub struct RepayThreshold { + pub ltv_ratio: Decimal, + pub otherwise: Box, +} + #[cw_serde] pub enum KnownPairedPoolAsset { OSMO, @@ -184,6 +243,7 @@ pub enum OsmosisLsd { Eris, // https://app.milkyway.zone/ MilkyWay, + // Stride } // #[cw_serde] @@ -234,12 +294,14 @@ impl OsmosisProjectAddresses { pub struct MembraneAddresses { pub cdp: String, pub staking: String, + pub stability_pool: String, } #[cw_serde] pub struct MembraneAddrs { pub cdp: Addr, pub staking: Addr, + pub stability_pool: Addr, } impl MembraneAddresses { @@ -247,6 +309,7 @@ impl MembraneAddresses { Ok(MembraneAddrs { cdp: api.addr_validate(&self.cdp)?, staking: api.addr_validate(&self.staking)?, + stability_pool: api.addr_validate(&self.stability_pool)?, }) } } diff --git a/packages/osmosis-destinations/src/dest_project_gen.rs b/packages/osmosis-destinations/src/dest_project_gen.rs index c0c334e..d272d54 100644 --- a/packages/osmosis-destinations/src/dest_project_gen.rs +++ b/packages/osmosis-destinations/src/dest_project_gen.rs @@ -2,10 +2,9 @@ use crate::{ errors::OsmosisDestinationError, mars_types::{RedBankAction, RedBankExecuteMsgs}, }; - use cosmos_sdk_proto::cosmos::base::v1beta1::Coin as CsdkCoin; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, Coin, Event, Uint128}; +use cosmwasm_std::{Addr, Coin, Decimal, Event, QuerierWrapper, ReplyOn, Uint128}; use outpost_utils::{ helpers::DestProjectMsgs, msg_gen::{create_exec_contract_msg, CosmosProtoMsg}, @@ -95,39 +94,3 @@ pub fn fund_red_bank_acct_msgs( .add_attribute("fund_and_lend", lend_asset.to_string())], }) } - -// stake mbrn -pub fn stake_mbrn_msgs( - staker_addr: &Addr, - membrane_staking_contract_addr: &Addr, - mbrn_to_stake: Coin, -) -> DestinationResult { - Ok(DestProjectMsgs { - msgs: vec![CosmosProtoMsg::ExecuteContract(create_exec_contract_msg( - membrane_staking_contract_addr, - staker_addr, - &MembraneExecuteMsg::Stake { user: None }, - Some(vec![cosmos_sdk_proto::cosmos::base::v1beta1::Coin { - denom: mbrn_to_stake.denom.to_string(), - amount: mbrn_to_stake.amount.to_string(), - }]), - )?)], - sub_msgs: vec![], - events: vec![Event::new("stake_mbrn").add_attribute("amount", mbrn_to_stake.to_string())], - }) -} - -#[cw_serde] -pub enum MembraneExecuteMsg { - Stake { - user: Option, - }, - CdpDeposit { - /// Position ID to deposit into. - /// If the user wants to create a new/separate position, no position id is passed. - position_id: Option, - /// Position owner. - /// Defaults to the sender. - position_owner: Option, - }, -} diff --git a/packages/osmosis-destinations/src/grants.rs b/packages/osmosis-destinations/src/grants.rs index f3c3ca6..be742c2 100644 --- a/packages/osmosis-destinations/src/grants.rs +++ b/packages/osmosis-destinations/src/grants.rs @@ -1,38 +1,8 @@ -use cosmwasm_std::{Addr, Uint128}; -use cw_grant_spec::grants::{GrantBase, GrantRequirement}; +use cosmos_sdk_proto::cosmos::base::v1beta1::Coin as CsdkCoin; +use cosmwasm_std::{Addr, Coin, Decimal, Uint128}; +use cw_grant_spec::grants::{ContractExecutionAuthorizationLimit, GrantBase, GrantRequirement}; -use crate::{dest_project_gen::MembraneExecuteMsg, mars_types::RedBankExecuteMsgs}; - -pub fn membrane_stake_grant( - base: GrantBase, - contract_addr: Addr, - mbrn_denom: &str, -) -> Vec { - vec![GrantRequirement::contract_exec_messages_auth( - base, - contract_addr, - vec![&MembraneExecuteMsg::Stake { user: None }], - Some(mbrn_denom), - )] -} - -pub fn membrane_deposit_grant( - base: GrantBase, - contract_addr: Addr, - position_id: Uint128, - _asset: String, - mbrn_denom: &str, -) -> Vec { - vec![GrantRequirement::contract_exec_messages_auth( - base, - contract_addr, - vec![&MembraneExecuteMsg::CdpDeposit { - position_id: Some(position_id), - position_owner: None, - }], - Some(mbrn_denom), - )] -} +use crate::mars_types::RedBankExecuteMsgs; pub fn mint_milk_tia_grant( base: GrantBase, diff --git a/packages/osmosis-helpers/src/osmosis_lp.rs b/packages/osmosis-helpers/src/osmosis_lp.rs index 3375ff9..5cd1398 100644 --- a/packages/osmosis-helpers/src/osmosis_lp.rs +++ b/packages/osmosis-helpers/src/osmosis_lp.rs @@ -1,26 +1,24 @@ +use crate::errors::OsmosisHelperError; +use crate::osmosis_swap::estimate_token_out_min_amount; +use crate::osmosis_swap::{ + generate_known_to_known_swap_and_sim_msg, osmosis_swap_grants, pool_swap_with_sim, + OsmosisRoutePools, +}; use cosmwasm_std::{ coin, Addr, Coin, Event, QuerierWrapper, StdError, Storage, Timestamp, Uint128, }; use cw_grant_spec::grants::{GrantBase, GrantRequirement}; - +use osmosis_destinations::comp_prefs::OsmosisPoolSettings; use osmosis_std::types::osmosis::concentratedliquidity::v1beta1::MsgCreatePosition; use osmosis_std::types::osmosis::gamm::v1beta1::MsgJoinSwapExternAmountIn; use osmosis_std::types::osmosis::gamm::v1beta1::Pool; use osmosis_std::types::osmosis::lockup::MsgLockTokens; - use osmosis_std::types::osmosis::poolmanager::v1beta1::SwapAmountInRoute; use osmosis_std::types::{ cosmos::base::v1beta1::Coin as OsmosisCoin, osmosis::poolmanager::v1beta1::PoolmanagerQuerier, }; use outpost_utils::{helpers::DestProjectMsgs, msg_gen::CosmosProtoMsg}; -use crate::errors::OsmosisHelperError; -use crate::osmosis_swap::estimate_token_out_min_amount; -use crate::osmosis_swap::{ - generate_known_to_known_swap_and_sim_msg, osmosis_swap_grants, pool_swap_with_sim, - OsmosisRoutePools, -}; - pub fn query_pool_info( pool_querier: PoolmanagerQuerier<'_, cosmwasm_std::Empty>, pool_id: u64, @@ -323,3 +321,15 @@ pub fn join_cl_pool_grants(base: GrantBase) -> Vec { ] .concat() } + +pub fn join_osmosis_pool_grants( + base: GrantBase, + pool_settings: OsmosisPoolSettings, +) -> Vec { + match pool_settings { + OsmosisPoolSettings::Standard { bond_tokens } => { + join_classic_pool_grants(base, bond_tokens) + } + OsmosisPoolSettings::ConcentratedLiquidity { .. } => join_cl_pool_grants(base), + } +} diff --git a/packages/osmosis-helpers/src/osmosis_swap.rs b/packages/osmosis-helpers/src/osmosis_swap.rs index 349d3a5..b71406d 100644 --- a/packages/osmosis-helpers/src/osmosis_swap.rs +++ b/packages/osmosis-helpers/src/osmosis_swap.rs @@ -88,10 +88,10 @@ pub fn generate_known_to_unknown_route( denom: to_denom, exit_pool_id, paired_asset, - }: TargetAsset, + }: &TargetAsset, ) -> Result, OsmosisHelperError> { // nothing to swap if from and to are the same denom - if from_denom.eq(&to_denom) { + if from_denom.eq(to_denom.as_str()) { return Ok(vec![]); } @@ -108,20 +108,20 @@ pub fn generate_known_to_unknown_route( match paired_asset { // if the target asset is paired with osmo and our from denom is osmo we have our route KnownPairedPoolAsset::OSMO if from_denom.eq(&denoms.osmo) => Ok(vec![SwapAmountInRoute { - pool_id: exit_pool_id, - token_out_denom: to_denom, + pool_id: exit_pool_id.clone(), + token_out_denom: to_denom.to_string(), }]), // if the target asset is paired with usdc and our from denom is usdc we have our route KnownPairedPoolAsset::USDC if from_denom.eq(&denoms.usdc) => Ok(vec![SwapAmountInRoute { - pool_id: exit_pool_id, - token_out_denom: to_denom, + pool_id: exit_pool_id.clone(), + token_out_denom: to_denom.to_string(), }]), KnownPairedPoolAsset::OSMO => { let mut to_osmo_route = generate_known_to_known_route(store, route_pools, from_denom, &denoms.osmo)?; to_osmo_route.push(SwapAmountInRoute { - pool_id: exit_pool_id, - token_out_denom: to_denom, + pool_id: exit_pool_id.clone(), + token_out_denom: to_denom.to_string(), }); Ok(to_osmo_route) } @@ -129,8 +129,8 @@ pub fn generate_known_to_unknown_route( let mut to_usdc_route = generate_known_to_known_route(store, route_pools, from_denom, &denoms.usdc)?; to_usdc_route.push(SwapAmountInRoute { - pool_id: exit_pool_id, - token_out_denom: to_denom, + pool_id: exit_pool_id.clone(), + token_out_denom: to_denom.to_string(), }); Ok(to_usdc_route) } @@ -328,7 +328,7 @@ pub fn generate_known_to_known_swap_and_sim_msg( querier, user_addr, from_asset, - to_denom.to_string(), + to_denom, generate_known_to_known_route(store, pool_routes, &from_asset.denom, to_denom)?, current_time, ) @@ -340,14 +340,14 @@ pub fn generate_known_to_unknown_swap_and_sim_msg( pool_routes: OsmosisRoutePools, user_addr: &Addr, from_asset: &CWCoin, - to_asset: TargetAsset, + to_asset: &TargetAsset, current_time: Timestamp, ) -> Result<(Uint128, Vec), OsmosisHelperError> { generate_swap_and_sim_msg( querier, user_addr, from_asset, - to_asset.denom.clone(), + &to_asset.denom, generate_known_to_unknown_route(store, pool_routes, &from_asset.denom, to_asset)?, current_time, ) @@ -358,7 +358,7 @@ pub fn generate_swap_and_sim_msg( querier: &QuerierWrapper, user_address: &Addr, from_asset: &CWCoin, - to_denom: String, + to_denom: &str, route: Vec, current_time: Timestamp, ) -> Result<(Uint128, Vec), OsmosisHelperError> { diff --git a/packages/utils/src/comp_prefs.rs b/packages/utils/src/comp_prefs.rs index 6175ebc..2bb7dc7 100644 --- a/packages/utils/src/comp_prefs.rs +++ b/packages/utils/src/comp_prefs.rs @@ -1,5 +1,7 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, Api, Decimal}; +use cosmwasm_std::{Addr, Api, Decimal, StdResult, Storage}; +use cw_storage_plus::{Item, Map}; +use serde::{de::DeserializeOwned, Serialize}; use crate::errors::OutpostError; @@ -41,3 +43,20 @@ impl TakeRate { }) } } + +/// Helper for storing submsg data +pub fn store_submsg_data( + store: &mut dyn Storage, + data: T, + latest_reply_id_state: Item, + submsg_data: Map<&u64, T>, +) -> StdResult +where + T: Serialize + DeserializeOwned, +{ + let next_reply_id = latest_reply_id_state.may_load(store)?.unwrap_or(0) + 1; + latest_reply_id_state.save(store, &next_reply_id)?; + submsg_data.save(store, &next_reply_id, &data)?; + + Ok(next_reply_id) +} diff --git a/packages/utils/src/helpers.rs b/packages/utils/src/helpers.rs index 79d0e0d..8a66cfe 100644 --- a/packages/utils/src/helpers.rs +++ b/packages/utils/src/helpers.rs @@ -1,12 +1,14 @@ use cosmos_sdk_proto::cosmos::{bank::v1beta1::MsgSend, base::v1beta1::Coin as CsdkCoin}; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, Coin, Decimal, Deps, Event, ReplyOn, Timestamp, Uint128}; +use cosmwasm_std::{ + Addr, Coin, Decimal, Deps, Event, ReplyOn, Response, SubMsg, Timestamp, Uint128, +}; use cw_storage_plus::Item; use crate::{ comp_prefs::{CompoundPrefs, DestinationAction}, errors::OutpostError, - msg_gen::CosmosProtoMsg, + msg_gen::{create_exec_msg, CosmosProtoMsg}, }; #[cw_serde] @@ -162,6 +164,75 @@ impl DestProjectMsgs { pub fn append_events(&mut self, events: Vec) { self.events.extend(events); } + + pub fn concat_after(&mut self, other: DestProjectMsgs) { + self.msgs.extend(other.msgs); + self.sub_msgs.extend(other.sub_msgs); + self.events.extend(other.events); + } +} + +impl DestProjectMsgs { + pub fn to_response(&self, grantee_addr: &Addr) -> Result { + Ok(Response::default() + .add_message(create_exec_msg(grantee_addr, self.msgs.clone())?) + .add_submessages( + self.sub_msgs + .clone() + .into_iter() + .filter_map(|sub_msg| { + if let (Ok(exec_msg), false) = ( + create_exec_msg(grantee_addr, sub_msg.1.clone()), + sub_msg.1.is_empty(), + ) { + Some((sub_msg.0, exec_msg, sub_msg.2)) + } else { + None + } + }) + .map(|(id, msg, reply_on)| SubMsg { + msg, + gas_limit: None, + id, + reply_on, + }) + .collect::>(), + ) + .add_events(self.events.clone())) + } +} + +/// Implementing FromIterator for DestProjectMsgs allows us to collect +impl FromIterator for DestProjectMsgs { + fn from_iter>(iter: I) -> Self { + let mut msgs = vec![]; + let mut sub_msgs = vec![]; + let mut events = vec![]; + + for item in iter { + msgs.extend(item.msgs); + sub_msgs.extend(item.sub_msgs); + events.extend(item.events); + } + + DestProjectMsgs { + msgs, + sub_msgs, + events, + } + } +} + +/// Combine multiple Responses into a single Response +pub fn combine_responses(responses: Vec) -> Response { + responses + .into_iter() + .fold(Response::default(), |mut acc, response| { + acc.messages.extend(response.messages); + acc.attributes.extend(response.attributes); + acc.events.extend(response.events); + acc + }) } /// Calculates the tax split for a given token amount and tax rate and the