From 9f81b067ca03c9841e0cc33c529d6b5ab757e035 Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Mon, 29 Apr 2024 00:34:20 +0200 Subject: [PATCH 01/20] Upgrade Sylvia to 0.10, fix as many things as possible --- Cargo.lock | 253 ++++-------------- Cargo.toml | 4 +- contracts/consumer/converter/src/contract.rs | 28 +- .../src/multitest/virtual_staking_mock.rs | 18 +- .../remote-price-feed/src/bin/schema.rs | 2 +- .../remote-price-feed/src/contract.rs | 21 +- .../simple-price-feed/src/bin/schema.rs | 2 +- .../simple-price-feed/src/contract.rs | 20 +- .../osmosis-price-provider/src/contract.rs | 4 +- .../external-staking/src/bin/schema.rs | 2 +- .../provider/external-staking/src/contract.rs | 44 +-- .../external-staking/src/test_methods.rs | 24 +- .../external-staking/src/test_methods_impl.rs | 26 +- .../native-staking-proxy/src/bin/schema.rs | 2 +- .../native-staking-proxy/src/contract.rs | 24 +- .../native-staking-proxy/src/multitest.rs | 86 ++---- .../src/native_staking_callback.rs | 2 +- .../provider/native-staking/src/bin/schema.rs | 2 +- .../provider/native-staking/src/contract.rs | 90 +------ .../provider/native-staking/src/error.rs | 2 +- .../native-staking/src/local_staking_api.rs | 83 ++++-- .../provider/native-staking/src/multitest.rs | 27 +- .../src/native_staking_callback.rs | 7 - contracts/provider/vault/src/bin/schema.rs | 2 +- contracts/provider/vault/src/contract.rs | 44 ++- contracts/provider/vault/src/multitest.rs | 29 +- packages/apis/src/converter_api.rs | 6 +- packages/apis/src/cross_staking_api.rs | 12 +- packages/apis/src/local_staking_api.rs | 38 +-- packages/apis/src/price_feed_api.rs | 14 +- packages/apis/src/vault_api.rs | 24 +- packages/apis/src/virtual_staking_api.rs | 6 +- 32 files changed, 345 insertions(+), 603 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fc544368..c9f87aa4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,12 +25,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "base16ct" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" - [[package]] name = "base16ct" version = "0.2.0" @@ -134,9 +128,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6b4c3f9c4616d6413d4b5fc4c270a4cc32a374b9be08671e80e1a019f805d8f" dependencies = [ "digest 0.10.7", - "ecdsa 0.16.8", + "ecdsa", "ed25519-zebra", - "k256 0.13.1", + "k256", "rand_core 0.6.4", "thiserror", ] @@ -190,7 +184,7 @@ dependencies = [ "hex", "schemars", "serde", - "serde-json-wasm", + "serde-json-wasm 0.5.2", "sha2 0.10.8", "static_assertions", "thiserror", @@ -205,18 +199,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crypto-bigint" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - [[package]] name = "crypto-bigint" version = "0.5.3" @@ -254,20 +236,21 @@ dependencies = [ [[package]] name = "cw-multi-test" -version = "0.16.5" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "127c7bb95853b8e828bdab97065c81cb5ddc20f7339180b61b2300565aaa99d1" +checksum = "cc392a5cb7e778e3f90adbf7faa43c4db7f35b6623224b08886d796718edb875" dependencies = [ "anyhow", + "bech32", "cosmwasm-std", "cw-storage-plus", "cw-utils", "derivative", - "itertools 0.10.5", - "k256 0.11.6", - "prost 0.9.0", + "itertools 0.12.1", + "prost 0.12.4", "schemars", "serde", + "sha2 0.10.8", "thiserror", ] @@ -312,16 +295,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "der" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" -dependencies = [ - "const-oid", - "zeroize", -] - [[package]] name = "der" version = "0.7.8" @@ -370,30 +343,18 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbfc4744c1b8f2a09adc0e55242f60b1af195d88596bd8700be74418c056c555" -[[package]] -name = "ecdsa" -version = "0.14.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" -dependencies = [ - "der 0.6.1", - "elliptic-curve 0.12.3", - "rfc6979 0.3.1", - "signature 1.6.4", -] - [[package]] name = "ecdsa" version = "0.16.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" dependencies = [ - "der 0.7.8", + "der", "digest 0.10.7", - "elliptic-curve 0.13.6", - "rfc6979 0.4.0", - "signature 2.1.0", - "spki 0.7.2", + "elliptic-curve", + "rfc6979", + "signature", + "spki", ] [[package]] @@ -417,41 +378,21 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" -[[package]] -name = "elliptic-curve" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" -dependencies = [ - "base16ct 0.1.1", - "crypto-bigint 0.4.9", - "der 0.6.1", - "digest 0.10.7", - "ff 0.12.1", - "generic-array", - "group 0.12.1", - "pkcs8 0.9.0", - "rand_core 0.6.4", - "sec1 0.3.0", - "subtle", - "zeroize", -] - [[package]] name = "elliptic-curve" version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d97ca172ae9dc9f9b779a6e3a65d308f2af74e5b8c921299075bdb4a0370e914" dependencies = [ - "base16ct 0.2.0", - "crypto-bigint 0.5.3", + "base16ct", + "crypto-bigint", "digest 0.10.7", - "ff 0.13.0", + "ff", "generic-array", - "group 0.13.0", - "pkcs8 0.10.2", + "group", + "pkcs8", "rand_core 0.6.4", - "sec1 0.7.3", + "sec1", "subtle", "zeroize", ] @@ -462,16 +403,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "ff" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "ff" version = "0.13.0" @@ -510,24 +441,13 @@ dependencies = [ "wasi", ] -[[package]] -name = "group" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" -dependencies = [ - "ff 0.12.1", - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "group" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ - "ff 0.13.0", + "ff", "rand_core 0.6.4", "subtle", ] @@ -596,18 +516,6 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" -[[package]] -name = "k256" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" -dependencies = [ - "cfg-if", - "ecdsa 0.14.8", - "elliptic-curve 0.12.3", - "sha2 0.10.8", -] - [[package]] name = "k256" version = "0.13.1" @@ -615,11 +523,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ "cfg-if", - "ecdsa 0.16.8", - "elliptic-curve 0.13.6", + "ecdsa", + "elliptic-curve", "once_cell", "sha2 0.10.8", - "signature 2.1.0", + "signature", ] [[package]] @@ -960,24 +868,14 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "pkcs8" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" -dependencies = [ - "der 0.6.1", - "spki 0.6.0", -] - [[package]] name = "pkcs8" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.8", - "spki 0.7.2", + "der", + "spki", ] [[package]] @@ -1025,29 +923,29 @@ dependencies = [ [[package]] name = "prost" -version = "0.9.0" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes", - "prost-derive 0.9.0", + "prost-derive 0.11.9", ] [[package]] name = "prost" -version = "0.11.9" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" dependencies = [ "bytes", - "prost-derive 0.11.9", + "prost-derive 0.12.4", ] [[package]] name = "prost-derive" -version = "0.9.0" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools 0.10.5", @@ -1058,15 +956,15 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.11.9" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.12.1", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.60", ] [[package]] @@ -1102,17 +1000,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rfc6979" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" -dependencies = [ - "crypto-bigint 0.4.9", - "hmac", - "zeroize", -] - [[package]] name = "rfc6979" version = "0.4.0" @@ -1153,30 +1040,16 @@ dependencies = [ "syn 2.0.60", ] -[[package]] -name = "sec1" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" -dependencies = [ - "base16ct 0.1.1", - "der 0.6.1", - "generic-array", - "pkcs8 0.9.0", - "subtle", - "zeroize", -] - [[package]] name = "sec1" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ - "base16ct 0.2.0", - "der 0.7.8", + "base16ct", + "der", "generic-array", - "pkcs8 0.10.2", + "pkcs8", "subtle", "zeroize", ] @@ -1214,6 +1087,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_derive" version = "1.0.199" @@ -1271,16 +1153,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" -dependencies = [ - "digest 0.10.7", - "rand_core 0.6.4", -] - [[package]] name = "signature" version = "2.1.0" @@ -1291,16 +1163,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "spki" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" -dependencies = [ - "base64ct", - "der 0.6.1", -] - [[package]] name = "spki" version = "0.7.2" @@ -1308,7 +1170,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" dependencies = [ "base64ct", - "der 0.7.8", + "der", ] [[package]] @@ -1325,9 +1187,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "sylvia" -version = "0.8.1" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f33388920659b494dab887f3bb40ebb071c602750597575034bea7c63ab12800" +checksum = "e64c8e892525ec035e5bdbeceec19309b1534734da7ff7bc69e9f639b077c497" dependencies = [ "anyhow", "cosmwasm-schema", @@ -1338,22 +1200,23 @@ dependencies = [ "schemars", "serde", "serde-cw-value", - "serde-json-wasm", + "serde-json-wasm 1.0.1", "sylvia-derive", ] [[package]] name = "sylvia-derive" -version = "0.8.1" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8031f53dbfda341acd7bd321e10d0d684b673324145026e23705da4b6d5c4919" +checksum = "ae5e0f41752efba2c6895514fa4caae742f69a2a73b4790bb6e365400c563bc1" dependencies = [ "convert_case", + "itertools 0.12.1", "proc-macro-crate", "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.60", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 9a4b76ad..b15060e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ mesh-converter = { path = "./contracts/consumer/converter" } mesh-simple-price-feed = { path = "./contracts/consumer/simple-price-feed" } mesh-virtual-staking = { path = "./contracts/consumer/virtual-staking" } -sylvia = "0.8.1" +sylvia = "0.10.1" cosmwasm-schema = "1.5.4" cosmwasm-std = { version = "1.5.4", features = ["ibc3", "cosmwasm_1_3"] } cw-storage-plus = "1.2.0" @@ -39,7 +39,7 @@ itertools = "0.12.1" # dev deps anyhow = "1" -cw-multi-test = "0.16.5" +cw-multi-test = "0.20" derivative = "2" test-case = "3.3.1" diff --git a/contracts/consumer/converter/src/contract.rs b/contracts/consumer/converter/src/contract.rs index 226bafd6..db32327a 100644 --- a/contracts/consumer/converter/src/contract.rs +++ b/contracts/consumer/converter/src/contract.rs @@ -30,8 +30,8 @@ pub struct ConverterContract<'a> { #[cfg_attr(not(feature = "library"), sylvia::entry_points)] #[contract] -#[error(ContractError)] -#[messages(converter_api as ConverterApi)] +#[sv::error(ContractError)] +#[sv::messages(converter_api as ConverterApi)] impl ConverterContract<'_> { pub const fn new() -> Self { Self { @@ -47,7 +47,7 @@ impl ConverterContract<'_> { /// /// Discount is applied to foreign tokens after adjusting foreign/native price, /// such that 0.3 discount means foreign assets have 70% of their value - #[msg(instantiate)] + #[sv::msg(instantiate)] pub fn instantiate( &self, ctx: InstantiateCtx, @@ -92,7 +92,7 @@ impl ConverterContract<'_> { Ok(Response::new().add_submessage(init_msg)) } - #[msg(reply)] + #[sv::msg(reply)] fn reply(&self, ctx: ReplyCtx, reply: Reply) -> Result { match reply.id { REPLY_ID_INSTANTIATE => self.reply_init_callback(ctx.deps, reply.result.unwrap()), @@ -114,7 +114,7 @@ impl ConverterContract<'_> { /// This is only used for tests. /// Ideally we want conditional compilation of these whole methods and the enum variants - #[msg(exec)] + #[sv::msg(exec)] fn test_stake( &self, ctx: ExecCtx, @@ -135,7 +135,7 @@ impl ConverterContract<'_> { /// This is only used for tests. /// Ideally we want conditional compilation of these whole methods and the enum variants - #[msg(exec)] + #[sv::msg(exec)] fn test_unstake( &self, ctx: ExecCtx, @@ -156,7 +156,7 @@ impl ConverterContract<'_> { /// This is only used for tests. /// Ideally we want conditional compilation of these whole methods and the enum variants - #[msg(exec)] + #[sv::msg(exec)] fn test_burn( &self, ctx: ExecCtx, @@ -175,7 +175,7 @@ impl ConverterContract<'_> { } } - #[msg(query)] + #[sv::msg(query)] fn config(&self, ctx: QueryCtx) -> Result { let config = self.config.load(ctx.deps.storage)?; let virtual_staking = self.virtual_stake.load(ctx.deps.storage)?.into_string(); @@ -200,7 +200,7 @@ impl ConverterContract<'_> { .add_attribute("validator", &validator) .add_attribute("amount", amount.amount.to_string()); - let msg = virtual_staking_api::ExecMsg::Bond { validator, amount }; + let msg = virtual_staking_api::sv::ExecMsg::Bond { validator, amount }; let msg = WasmMsg::Execute { contract_addr: self.virtual_stake.load(deps.storage)?.into(), msg: to_json_binary(&msg)?, @@ -224,7 +224,7 @@ impl ConverterContract<'_> { .add_attribute("validator", &validator) .add_attribute("amount", amount.amount.to_string()); - let msg = virtual_staking_api::ExecMsg::Unbond { validator, amount }; + let msg = virtual_staking_api::sv::ExecMsg::Unbond { validator, amount }; let msg = WasmMsg::Execute { contract_addr: self.virtual_stake.load(deps.storage)?.into(), msg: to_json_binary(&msg)?, @@ -248,7 +248,7 @@ impl ConverterContract<'_> { .add_attribute("validators", validators.join(",")) .add_attribute("amount", amount.amount.to_string()); - let msg = virtual_staking_api::ExecMsg::Burn { + let msg = virtual_staking_api::sv::ExecMsg::Burn { validators: validators.to_vec(), amount, }; @@ -272,6 +272,7 @@ impl ConverterContract<'_> { } ); + // FIXME not sure how to get this to compile with latest sylvia // get the price value (usage is a bit clunky, need to use trait and cannot chain Remote::new() with .querier()) // also see https://github.com/CosmWasm/sylvia/issues/181 to just store Remote in state use price_feed_api::Querier; @@ -349,14 +350,11 @@ impl ConverterContract<'_> { } } -#[contract] -#[messages(converter_api as ConverterApi)] impl ConverterApi for ConverterContract<'_> { type Error = ContractError; /// Rewards tokens (in native staking denom) are sent alongside the message, and should be distributed to all /// stakers who staked on this validator. This is tracked on the provider, so we send an IBC packet there. - #[msg(exec)] fn distribute_reward( &self, mut ctx: ExecCtx, @@ -382,7 +380,6 @@ impl ConverterApi for ConverterContract<'_> { /// /// info.funds sent along with the message should be the sum of all rewards for all validators, /// in the native staking denom. - #[msg(exec)] fn distribute_rewards( &self, mut ctx: ExecCtx, @@ -422,7 +419,6 @@ impl ConverterApi for ConverterContract<'_> { /// /// Send validator set additions (entering the active validator set), jailings and tombstonings /// to the external staking contract on the Consumer via IBC. - #[msg(exec)] #[allow(clippy::too_many_arguments)] fn valset_update( &self, diff --git a/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs b/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs index 13552e60..b1155da9 100644 --- a/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs +++ b/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs @@ -42,7 +42,7 @@ pub struct VirtualStakingMock<'a> { #[contract] #[error(ContractError)] -#[messages(virtual_staking_api as VirtualStakingApi)] +#[sv::messages(virtual_staking_api as VirtualStakingApi)] impl VirtualStakingMock<'_> { pub const fn new() -> Self { Self { @@ -51,7 +51,7 @@ impl VirtualStakingMock<'_> { } } - #[msg(instantiate)] + #[sv::msg(instantiate)] pub fn instantiate(&self, ctx: InstantiateCtx) -> Result { nonpayable(&ctx.info)?; let denom = ctx.deps.querier.query_bonded_denom()?; @@ -63,7 +63,7 @@ impl VirtualStakingMock<'_> { Ok(Response::new()) } - #[msg(query)] + #[sv::msg(query)] fn config(&self, ctx: QueryCtx) -> Result { let cfg = self.config.load(ctx.deps.storage)?; let denom = cfg.denom; @@ -71,7 +71,7 @@ impl VirtualStakingMock<'_> { Ok(ConfigResponse { denom, converter }) } - #[msg(query)] + #[sv::msg(query)] fn stake(&self, ctx: QueryCtx, validator: String) -> Result { let stake = self .stake @@ -80,7 +80,7 @@ impl VirtualStakingMock<'_> { Ok(StakeResponse { stake }) } - #[msg(query)] + #[sv::msg(query)] fn all_stake(&self, ctx: QueryCtx) -> Result { let stakes = self .stake @@ -107,14 +107,14 @@ pub struct ConfigResponse { } #[contract] -#[messages(virtual_staking_api as VirtualStakingApi)] +#[sv::messages(virtual_staking_api as VirtualStakingApi)] impl VirtualStakingApi for VirtualStakingMock<'_> { type Error = ContractError; /// Requests to bond tokens to a validator. This will be actually handled at the next epoch. /// If the virtual staking module is over the max cap, it will trigger a rebalance. /// If the max cap is 0, then this will immediately return an error. - #[msg(exec)] + #[sv::msg(exec)] fn bond(&self, ctx: ExecCtx, validator: String, amount: Coin) -> Result { nonpayable(&ctx.info)?; let cfg = self.config.load(ctx.deps.storage)?; @@ -137,7 +137,7 @@ impl VirtualStakingApi for VirtualStakingMock<'_> { /// Requests to unbond tokens from a validator. This will be actually handled at the next epoch. /// If the virtual staking module is over the max cap, it will trigger a rebalance in addition to unbond. /// If the virtual staking contract doesn't have at least amount tokens staked to the given validator, this will return an error. - #[msg(exec)] + #[sv::msg(exec)] fn unbond( &self, ctx: ExecCtx, @@ -165,7 +165,7 @@ impl VirtualStakingApi for VirtualStakingMock<'_> { /// Requests to unbond and burn tokens from a lists of validators (one or more). This will be actually handled at the next epoch. /// If the virtual staking module is over the max cap, it will trigger a rebalance in addition to unbond. /// If the virtual staking contract doesn't have at least amount tokens staked over the given validators, this will return an error. - #[msg(exec)] + #[sv::msg(exec)] fn burn( &self, ctx: ExecCtx, diff --git a/contracts/consumer/remote-price-feed/src/bin/schema.rs b/contracts/consumer/remote-price-feed/src/bin/schema.rs index 1acd896c..830716f4 100644 --- a/contracts/consumer/remote-price-feed/src/bin/schema.rs +++ b/contracts/consumer/remote-price-feed/src/bin/schema.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::write_api; -use mesh_remote_price_feed::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; +use mesh_remote_price_feed::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; #[cfg(not(tarpaulin_include))] fn main() { diff --git a/contracts/consumer/remote-price-feed/src/contract.rs b/contracts/consumer/remote-price-feed/src/contract.rs index 7671b640..06e370fc 100644 --- a/contracts/consumer/remote-price-feed/src/contract.rs +++ b/contracts/consumer/remote-price-feed/src/contract.rs @@ -2,8 +2,7 @@ use cosmwasm_std::{entry_point, Decimal, DepsMut, Env, IbcChannel, Response, Tim use cw2::set_contract_version; use cw_storage_plus::Item; use cw_utils::nonpayable; -use mesh_apis::price_feed_api::SudoMsg; -use sylvia::types::{InstantiateCtx, QueryCtx}; +use sylvia::types::{InstantiateCtx, QueryCtx, SudoCtx}; use sylvia::{contract, schemars}; use mesh_apis::price_feed_api::{self, PriceFeedApi, PriceResponse}; @@ -33,8 +32,8 @@ impl Default for RemotePriceFeedContract { #[cfg_attr(not(feature = "library"), sylvia::entry_points)] #[contract] -#[error(ContractError)] -#[messages(price_feed_api as PriceFeedApi)] +#[sv::error(ContractError)] +#[sv::messages(price_feed_api as PriceFeedApi)] impl RemotePriceFeedContract { pub fn new() -> Self { Self { @@ -50,7 +49,7 @@ impl RemotePriceFeedContract { } } - #[msg(instantiate)] + #[sv::msg(instantiate)] pub fn instantiate( &self, mut ctx: InstantiateCtx, @@ -83,14 +82,11 @@ impl RemotePriceFeedContract { } } -#[contract] -#[messages(price_feed_api as PriceFeedApi)] impl PriceFeedApi for RemotePriceFeedContract { type Error = ContractError; /// Return the price of the foreign token. That is, how many native tokens /// are needed to buy one foreign token. - #[msg(query)] fn price(&self, ctx: QueryCtx) -> Result { Ok(self .price_keeper @@ -99,14 +95,9 @@ impl PriceFeedApi for RemotePriceFeedContract { native_per_foreign: rate, })?) } -} - -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn sudo(deps: DepsMut, env: Env, msg: SudoMsg) -> Result { - let contract = RemotePriceFeedContract::new(); - match msg { - SudoMsg::HandleEpoch {} => contract.scheduler.trigger(deps, &env), + fn handle_epoch(&self, ctx: SudoCtx) -> Result { + Ok(self.scheduler.trigger(ctx.deps, &ctx.env)?) } } diff --git a/contracts/consumer/simple-price-feed/src/bin/schema.rs b/contracts/consumer/simple-price-feed/src/bin/schema.rs index d6814af1..aba310dd 100644 --- a/contracts/consumer/simple-price-feed/src/bin/schema.rs +++ b/contracts/consumer/simple-price-feed/src/bin/schema.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::write_api; -use mesh_simple_price_feed::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; +use mesh_simple_price_feed::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; #[cfg(not(tarpaulin_include))] fn main() { diff --git a/contracts/consumer/simple-price-feed/src/contract.rs b/contracts/consumer/simple-price-feed/src/contract.rs index 6c4cb6e7..59896fc4 100644 --- a/contracts/consumer/simple-price-feed/src/contract.rs +++ b/contracts/consumer/simple-price-feed/src/contract.rs @@ -2,7 +2,7 @@ use cosmwasm_std::{ensure_eq, Decimal, Response}; use cw2::set_contract_version; use cw_storage_plus::Item; use cw_utils::nonpayable; -use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx}; +use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx, SudoCtx}; use sylvia::{contract, schemars}; use mesh_apis::price_feed_api::{self, PriceFeedApi, PriceResponse}; @@ -20,8 +20,8 @@ pub struct SimplePriceFeedContract<'a> { #[cfg_attr(not(feature = "library"), sylvia::entry_points)] #[contract] -#[error(ContractError)] -#[messages(price_feed_api as PriceFeedApi)] +#[sv::error(ContractError)] +#[sv::messages(price_feed_api as PriceFeedApi)] impl SimplePriceFeedContract<'_> { pub const fn new() -> Self { Self { @@ -31,7 +31,7 @@ impl SimplePriceFeedContract<'_> { /// Sets up the contract with an initial price. /// If the owner is not set in the message, it defaults to info.sender. - #[msg(instantiate)] + #[sv::msg(instantiate)] pub fn instantiate( &self, ctx: InstantiateCtx, @@ -53,7 +53,7 @@ impl SimplePriceFeedContract<'_> { Ok(Response::new()) } - #[msg(exec)] + #[sv::msg(exec)] fn update_price( &self, ctx: ExecCtx, @@ -75,7 +75,7 @@ impl SimplePriceFeedContract<'_> { Ok(Response::new()) } - #[msg(query)] + #[sv::msg(query)] fn config(&self, ctx: QueryCtx) -> Result { let config = self.config.load(ctx.deps.storage)?; Ok(ConfigResponse { @@ -85,18 +85,20 @@ impl SimplePriceFeedContract<'_> { } } -#[contract] -#[messages(price_feed_api as PriceFeedApi)] impl PriceFeedApi for SimplePriceFeedContract<'_> { type Error = ContractError; /// Return the price of the foreign token. That is, how many native tokens /// are needed to buy one foreign token. - #[msg(query)] fn price(&self, ctx: QueryCtx) -> Result { let config = self.config.load(ctx.deps.storage)?; Ok(PriceResponse { native_per_foreign: config.native_per_foreign, }) } + + /// Nothing needs to be done on the epoch + fn handle_epoch(&self, _ctx: SudoCtx) -> Result { + Ok(Response::new()) + } } diff --git a/contracts/osmosis-price-provider/src/contract.rs b/contracts/osmosis-price-provider/src/contract.rs index 10997c33..9c2ff9d2 100644 --- a/contracts/osmosis-price-provider/src/contract.rs +++ b/contracts/osmosis-price-provider/src/contract.rs @@ -21,7 +21,7 @@ pub struct OsmosisPriceProvider { #[cfg_attr(not(feature = "library"), sylvia::entry_points)] #[contract] -#[error(ContractError)] +#[sv::error(ContractError)] impl OsmosisPriceProvider { pub const fn new() -> Self { Self { @@ -30,7 +30,7 @@ impl OsmosisPriceProvider { } } - #[msg(instantiate)] + #[sv::msg(instantiate)] pub fn instantiate( &self, ctx: InstantiateCtx, diff --git a/contracts/provider/external-staking/src/bin/schema.rs b/contracts/provider/external-staking/src/bin/schema.rs index 33178722..a8681e08 100644 --- a/contracts/provider/external-staking/src/bin/schema.rs +++ b/contracts/provider/external-staking/src/bin/schema.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::write_api; -use mesh_external_staking::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; +use mesh_external_staking::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; #[cfg(not(tarpaulin_include))] fn main() { diff --git a/contracts/provider/external-staking/src/contract.rs b/contracts/provider/external-staking/src/contract.rs index 0ac1c833..400eebed 100644 --- a/contracts/provider/external-staking/src/contract.rs +++ b/contracts/provider/external-staking/src/contract.rs @@ -62,9 +62,9 @@ impl Default for ExternalStakingContract<'_> { #[cfg_attr(not(feature = "library"), sylvia::entry_points)] #[contract] -#[error(ContractError)] -#[messages(cross_staking_api as CrossStakingApi)] -#[messages(crate::test_methods as TestMethods)] +#[sv::error(ContractError)] +#[sv::messages(cross_staking_api as CrossStakingApi)] +#[sv::messages(crate::test_methods as TestMethods)] impl ExternalStakingContract<'_> { pub fn new() -> Self { Self { @@ -86,7 +86,7 @@ impl ExternalStakingContract<'_> { } #[allow(clippy::too_many_arguments)] - #[msg(instantiate)] + #[sv::msg(instantiate)] pub fn instantiate( &self, ctx: InstantiateCtx, @@ -245,7 +245,7 @@ impl ExternalStakingContract<'_> { /// Schedules tokens for release, adding them to the pending unbonds. After the unbonding period /// passes, funds are ready to be released through a `withdraw_unbonded` call by the user. - #[msg(exec)] + #[sv::msg(exec)] pub fn unstake( &self, ctx: ExecCtx, @@ -600,7 +600,7 @@ impl ExternalStakingContract<'_> { /// /// Tokens to be claimed have to be unbond before by calling the `unbond` message, and /// their unbonding period must have passed. - #[msg(exec)] + #[sv::msg(exec)] pub fn withdraw_unbonded(&self, ctx: ExecCtx) -> Result { nonpayable(&ctx.info)?; @@ -727,7 +727,7 @@ impl ExternalStakingContract<'_> { } /// Withdraw rewards from staking via given validator - #[msg(exec)] + #[sv::msg(exec)] pub fn withdraw_rewards( &self, ctx: ExecCtx, @@ -956,14 +956,14 @@ impl ExternalStakingContract<'_> { } /// Queries for contract configuration - #[msg(query)] + #[sv::msg(query)] pub fn config(&self, ctx: QueryCtx) -> Result { let resp = self.config.load(ctx.deps.storage)?.into(); Ok(resp) } /// Query for the endpoint that can connect - #[msg(query)] + #[sv::msg(query)] pub fn authorized_endpoint( &self, ctx: QueryCtx, @@ -973,14 +973,14 @@ impl ExternalStakingContract<'_> { } /// Query for the endpoint that can connect - #[msg(query)] + #[sv::msg(query)] pub fn ibc_channel(&self, ctx: QueryCtx) -> Result { let channel = IBC_CHANNEL.load(ctx.deps.storage)?; Ok(IbcChannelResponse { channel }) } /// Show all external validators that we know to be active (and can delegate to) - #[msg(query)] + #[sv::msg(query)] pub fn list_active_validators( &self, ctx: QueryCtx, @@ -995,7 +995,7 @@ impl ExternalStakingContract<'_> { } /// Show all external validators that we know about, along with their state. - #[msg(query)] + #[sv::msg(query)] pub fn list_validators( &self, ctx: QueryCtx, @@ -1020,7 +1020,7 @@ impl ExternalStakingContract<'_> { /// Queries for stake info /// /// If stake does not exist for (user, validator) pair, the zero-stake is returned - #[msg(query)] + #[sv::msg(query)] pub fn stake( &self, ctx: QueryCtx, @@ -1040,7 +1040,7 @@ impl ExternalStakingContract<'_> { /// Paginated list of user stakes. /// /// `start_after` is the last validator of previous page - #[msg(query)] + #[sv::msg(query)] pub fn stakes( &self, ctx: QueryCtx, @@ -1076,7 +1076,7 @@ impl ExternalStakingContract<'_> { } /// Queries a pending tx. - #[msg(query)] + #[sv::msg(query)] fn pending_tx(&self, ctx: QueryCtx, tx_id: u64) -> Result { let resp = self.pending_txs.load(ctx.deps.storage, tx_id)?; Ok(resp) @@ -1085,7 +1085,7 @@ impl ExternalStakingContract<'_> { /// Queries for all pending txs. /// Reports txs in descending order (newest first). /// `start_after` is the last tx id included in previous page - #[msg(query)] + #[sv::msg(query)] fn all_pending_txs_desc( &self, ctx: QueryCtx, @@ -1112,7 +1112,7 @@ impl ExternalStakingContract<'_> { /// Returns how much rewards are to be withdrawn by particular user, from the particular /// validator staking - #[msg(query)] + #[sv::msg(query)] pub fn pending_rewards( &self, ctx: QueryCtx, @@ -1142,7 +1142,7 @@ impl ExternalStakingContract<'_> { /// Returns how much rewards are to be withdrawn by particular user, iterating over all validators. /// This is like stakes is to stake query, but for rewards. - #[msg(query)] + #[sv::msg(query)] pub fn all_pending_rewards( &self, ctx: QueryCtx, @@ -1211,11 +1211,11 @@ pub mod cross_staking { use mesh_apis::{cross_staking_api::CrossStakingApi, local_staking_api::SlashRatioResponse}; #[contract(module=crate::contract)] - #[messages(mesh_apis::cross_staking_api as CrossStakingApi)] + #[sv::messages(mesh_apis::cross_staking_api as CrossStakingApi)] impl CrossStakingApi for ExternalStakingContract<'_> { type Error = ContractError; - #[msg(exec)] + #[sv::msg(exec)] fn receive_virtual_stake( &self, ctx: ExecCtx, @@ -1299,7 +1299,7 @@ pub mod cross_staking { Ok(resp) } - #[msg(exec)] + #[sv::msg(exec)] fn burn_virtual_stake( &self, ctx: ExecCtx, @@ -1433,7 +1433,7 @@ pub mod cross_staking { Ok(resp) } - #[msg(query)] + #[sv::msg(query)] fn max_slash(&self, ctx: QueryCtx) -> Result { let Config { slash_ratio, .. } = self.config.load(ctx.deps.storage)?; Ok(SlashRatioResponse { diff --git a/contracts/provider/external-staking/src/test_methods.rs b/contracts/provider/external-staking/src/test_methods.rs index d6b7e638..98deae7e 100644 --- a/contracts/provider/external-staking/src/test_methods.rs +++ b/contracts/provider/external-staking/src/test_methods.rs @@ -11,15 +11,15 @@ pub trait TestMethods { type Error: From; /// Commits a pending stake. - #[msg(exec)] + #[sv::msg(exec)] fn test_commit_stake(&self, ctx: ExecCtx, tx_id: u64) -> Result; /// Rollbacks a pending stake. - #[msg(exec)] + #[sv::msg(exec)] fn test_rollback_stake(&self, ctx: ExecCtx, tx_id: u64) -> Result; /// Updates the active validator set. - #[msg(exec)] + #[sv::msg(exec)] fn test_set_active_validator( &self, ctx: ExecCtx, @@ -29,7 +29,7 @@ pub trait TestMethods { ) -> Result; /// Sets validator as `unbonded`. - #[msg(exec)] + #[sv::msg(exec)] fn test_remove_validator( &self, ctx: ExecCtx, @@ -38,7 +38,7 @@ pub trait TestMethods { time: u64, ) -> Result; - #[msg(exec)] + #[sv::msg(exec)] fn test_tombstone_validator( &self, ctx: ExecCtx, @@ -48,15 +48,15 @@ pub trait TestMethods { ) -> Result; /// Commits a pending unstake. - #[msg(exec)] + #[sv::msg(exec)] fn test_commit_unstake(&self, ctx: ExecCtx, tx_id: u64) -> Result; /// Rollbacks a pending unstake. - #[msg(exec)] + #[sv::msg(exec)] fn test_rollback_unstake(&self, ctx: ExecCtx, tx_id: u64) -> Result; /// Distribute rewards. - #[msg(exec)] + #[sv::msg(exec)] fn test_distribute_rewards( &self, ctx: ExecCtx, @@ -65,7 +65,7 @@ pub trait TestMethods { ) -> Result; /// Batch distribute rewards. - #[msg(exec)] + #[sv::msg(exec)] fn test_distribute_rewards_batch( &self, ctx: ExecCtx, @@ -74,7 +74,7 @@ pub trait TestMethods { ) -> Result; /// Commits a withdraw rewards transaction. - #[msg(exec)] + #[sv::msg(exec)] fn test_commit_withdraw_rewards( &self, ctx: ExecCtx, @@ -82,7 +82,7 @@ pub trait TestMethods { ) -> Result; /// Rollbacks a withdraw rewards transaction. - #[msg(exec)] + #[sv::msg(exec)] fn test_rollback_withdraw_rewards( &self, ctx: ExecCtx, @@ -92,7 +92,7 @@ pub trait TestMethods { /// Slashes a validator. /// This will not perform any check on the validator's state in the validator set, which should /// be done before calling this function. - #[msg(exec)] + #[sv::msg(exec)] fn test_handle_slashing( &self, ctx: ExecCtx, diff --git a/contracts/provider/external-staking/src/test_methods_impl.rs b/contracts/provider/external-staking/src/test_methods_impl.rs index a9687631..c63927c1 100644 --- a/contracts/provider/external-staking/src/test_methods_impl.rs +++ b/contracts/provider/external-staking/src/test_methods_impl.rs @@ -10,12 +10,12 @@ use sylvia::types::ExecCtx; /// These methods are for test usage only #[contract(module=crate::contract)] -#[messages(crate::test_methods as TestMethods)] +#[sv::messages(crate::test_methods as TestMethods)] impl TestMethods for ExternalStakingContract<'_> { type Error = ContractError; /// Commits a pending stake. - #[msg(exec)] + #[sv::msg(exec)] fn test_commit_stake(&self, ctx: ExecCtx, tx_id: u64) -> Result { #[cfg(any(feature = "mt", test))] { @@ -30,7 +30,7 @@ impl TestMethods for ExternalStakingContract<'_> { } /// Rollbacks a pending stake. - #[msg(exec)] + #[sv::msg(exec)] fn test_rollback_stake(&self, ctx: ExecCtx, tx_id: u64) -> Result { #[cfg(any(test, feature = "mt"))] { @@ -45,7 +45,7 @@ impl TestMethods for ExternalStakingContract<'_> { } /// Updates the active validator set. - #[msg(exec)] + #[sv::msg(exec)] fn test_set_active_validator( &self, ctx: ExecCtx, @@ -68,7 +68,7 @@ impl TestMethods for ExternalStakingContract<'_> { } /// Sets validator as `unbonded`. - #[msg(exec)] + #[sv::msg(exec)] fn test_remove_validator( &self, ctx: ExecCtx, @@ -90,7 +90,7 @@ impl TestMethods for ExternalStakingContract<'_> { } /// Sets validator as `unbonded`. - #[msg(exec)] + #[sv::msg(exec)] fn test_tombstone_validator( &self, ctx: ExecCtx, @@ -112,7 +112,7 @@ impl TestMethods for ExternalStakingContract<'_> { } /// Commits a pending unstake. - #[msg(exec)] + #[sv::msg(exec)] fn test_commit_unstake(&self, ctx: ExecCtx, tx_id: u64) -> Result { #[cfg(any(test, feature = "mt"))] { @@ -127,7 +127,7 @@ impl TestMethods for ExternalStakingContract<'_> { } /// Rollbacks a pending unstake. - #[msg(exec)] + #[sv::msg(exec)] fn test_rollback_unstake(&self, ctx: ExecCtx, tx_id: u64) -> Result { #[cfg(any(test, feature = "mt"))] { @@ -142,7 +142,7 @@ impl TestMethods for ExternalStakingContract<'_> { } /// Distribute rewards. - #[msg(exec)] + #[sv::msg(exec)] fn test_distribute_rewards( &self, ctx: ExecCtx, @@ -162,7 +162,7 @@ impl TestMethods for ExternalStakingContract<'_> { } /// Batch distribute rewards. - #[msg(exec)] + #[sv::msg(exec)] fn test_distribute_rewards_batch( &self, ctx: ExecCtx, @@ -182,7 +182,7 @@ impl TestMethods for ExternalStakingContract<'_> { } /// Commits a withdraw rewards transaction. - #[msg(exec)] + #[sv::msg(exec)] fn test_commit_withdraw_rewards( &self, ctx: ExecCtx, @@ -201,7 +201,7 @@ impl TestMethods for ExternalStakingContract<'_> { } /// Rollbacks a withdraw rewards transaction. - #[msg(exec)] + #[sv::msg(exec)] fn test_rollback_withdraw_rewards( &self, ctx: ExecCtx, @@ -220,7 +220,7 @@ impl TestMethods for ExternalStakingContract<'_> { } /// Slashes a validator - #[msg(exec)] + #[sv::msg(exec)] fn test_handle_slashing( &self, ctx: ExecCtx, diff --git a/contracts/provider/native-staking-proxy/src/bin/schema.rs b/contracts/provider/native-staking-proxy/src/bin/schema.rs index e9246c00..c378ebbb 100644 --- a/contracts/provider/native-staking-proxy/src/bin/schema.rs +++ b/contracts/provider/native-staking-proxy/src/bin/schema.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::write_api; -use mesh_native_staking_proxy::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; +use mesh_native_staking_proxy::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; #[cfg(not(tarpaulin_include))] fn main() { diff --git a/contracts/provider/native-staking-proxy/src/contract.rs b/contracts/provider/native-staking-proxy/src/contract.rs index f19fa45a..89aa6240 100644 --- a/contracts/provider/native-staking-proxy/src/contract.rs +++ b/contracts/provider/native-staking-proxy/src/contract.rs @@ -25,7 +25,7 @@ pub struct NativeStakingProxyContract<'a> { #[cfg_attr(not(feature = "library"), sylvia::entry_points)] #[contract] -#[error(ContractError)] +#[sv::error(ContractError)] impl NativeStakingProxyContract<'_> { pub const fn new() -> Self { Self { @@ -36,7 +36,7 @@ impl NativeStakingProxyContract<'_> { /// The caller of the instantiation will be the native-staking contract. /// We stake `funds.info` on the given validator - #[msg(instantiate)] + #[sv::msg(instantiate)] pub fn instantiate( &self, ctx: InstantiateCtx, @@ -75,7 +75,7 @@ impl NativeStakingProxyContract<'_> { /// Stakes the tokens from `info.funds` to the given validator. /// Can only be called by the parent contract - #[msg(exec)] + #[sv::msg(exec)] fn stake(&self, ctx: ExecCtx, validator: String) -> Result { let cfg = self.config.load(ctx.deps.storage)?; ensure_eq!(cfg.parent, ctx.info.sender, ContractError::Unauthorized {}); @@ -91,7 +91,7 @@ impl NativeStakingProxyContract<'_> { /// Burn `amount` tokens from the given validator, if set. /// If `validator` is not set, undelegate evenly from all validators the user has stake. /// Can only be called by the parent contract - #[msg(exec)] + #[sv::msg(exec)] fn burn( &self, ctx: ExecCtx, @@ -179,7 +179,7 @@ impl NativeStakingProxyContract<'_> { /// Re-stakes the given amount from the one validator to another on behalf of the calling user. /// Returns an error if the user doesn't have such stake - #[msg(exec)] + #[sv::msg(exec)] fn restake( &self, ctx: ExecCtx, @@ -207,7 +207,7 @@ impl NativeStakingProxyContract<'_> { } /// Vote with the user's stake (over all delegations) - #[msg(exec)] + #[sv::msg(exec)] fn vote( &self, ctx: ExecCtx, @@ -224,7 +224,7 @@ impl NativeStakingProxyContract<'_> { } /// Vote with the user's stake (over all delegations) - #[msg(exec)] + #[sv::msg(exec)] fn vote_weighted( &self, ctx: ExecCtx, @@ -246,7 +246,7 @@ impl NativeStakingProxyContract<'_> { /// If the caller has any delegations, withdraw all rewards from those delegations and /// send the tokens to the caller. /// NOTE: must make sure not to release unbonded tokens - #[msg(exec)] + #[sv::msg(exec)] fn withdraw_rewards(&self, ctx: ExecCtx) -> Result { let cfg = self.config.load(ctx.deps.storage)?; ensure_eq!(cfg.owner, ctx.info.sender, ContractError::Unauthorized {}); @@ -270,7 +270,7 @@ impl NativeStakingProxyContract<'_> { /// Unstakes the given amount from the given validator on behalf of the calling user. /// Returns an error if the user doesn't have such stake. /// After the unbonding period, it will allow the user to claim the tokens (returning to vault) - #[msg(exec)] + #[sv::msg(exec)] fn unstake( &self, ctx: ExecCtx, @@ -295,7 +295,7 @@ impl NativeStakingProxyContract<'_> { /// Releases any tokens that have fully unbonded from a previous unstake. /// This will go back to the parent via `release_proxy_stake`. /// Errors if the proxy doesn't have any liquid tokens - #[msg(exec)] + #[sv::msg(exec)] fn release_unbonded(&self, ctx: ExecCtx) -> Result { let cfg = self.config.load(ctx.deps.storage)?; ensure_eq!(cfg.owner, ctx.info.sender, ContractError::Unauthorized {}); @@ -318,7 +318,7 @@ impl NativeStakingProxyContract<'_> { } // Send them to the parent contract via `release_proxy_stake` - let msg = to_json_binary(&native_staking_callback::ExecMsg::ReleaseProxyStake {})?; + let msg = to_json_binary(&native_staking_callback::sv::ExecMsg::ReleaseProxyStake {})?; let wasm_msg = Execute { contract_addr: cfg.parent.to_string(), @@ -328,7 +328,7 @@ impl NativeStakingProxyContract<'_> { Ok(Response::new().add_message(wasm_msg)) } - #[msg(query)] + #[sv::msg(query)] fn config(&self, ctx: QueryCtx) -> Result { Ok(self.config.load(ctx.deps.storage)?) } diff --git a/contracts/provider/native-staking-proxy/src/multitest.rs b/contracts/provider/native-staking-proxy/src/multitest.rs index a89fc034..f89a4788 100644 --- a/contracts/provider/native-staking-proxy/src/multitest.rs +++ b/contracts/provider/native-staking-proxy/src/multitest.rs @@ -7,7 +7,7 @@ use cw_multi_test::{App as MtApp, StakingInfo, StakingSudo, SudoMsg}; use sylvia::multitest::App; -use mesh_vault::contract::multitest_utils::VaultContractProxy; +use mesh_vault::contract::sv::mt::VaultContractProxy; use crate::contract; use crate::msg::ConfigResponse; @@ -57,15 +57,15 @@ fn setup<'app>( user: &str, validators: &[&str], ) -> AnyResult> { - let vault_code = mesh_vault::contract::multitest_utils::CodeId::store_code(app); - let staking_code = mesh_native_staking::contract::multitest_utils::CodeId::store_code(app); - let staking_proxy_code = contract::multitest_utils::CodeId::store_code(app); + let vault_code = mesh_vault::contract::sv::mt::CodeId::store_code(app); + let staking_code = mesh_native_staking::contract::sv::mt::CodeId::store_code(app); + let staking_proxy_code = contract::sv::mt::CodeId::store_code(app); // Instantiate vault msg let staking_init_info = mesh_vault::msg::StakingInitInfo { admin: None, code_id: staking_code.code_id(), - msg: to_json_binary(&mesh_native_staking::contract::InstantiateMsg { + msg: to_json_binary(&mesh_native_staking::contract::sv::InstantiateMsg { denom: OSMO.to_owned(), proxy_code_id: staking_proxy_code.code_id(), slash_ratio_dsign: Decimal::percent(5), @@ -120,10 +120,8 @@ fn instantiation() { setup(&app, owner, user, &[validator]).unwrap(); // Access staking proxy instance - let staking_proxy = contract::multitest_utils::NativeStakingProxyContractProxy::new( - Addr::unchecked(proxy_addr), - &app, - ); + let staking_proxy = + contract::sv::mt::NativeStakingProxyContractProxy::new(Addr::unchecked(proxy_addr), &app); // Check config let config = staking_proxy.config().unwrap(); @@ -166,10 +164,8 @@ fn staking() { let vault = setup(&app, owner, user, &[validator]).unwrap(); // Access staking proxy instance - let staking_proxy = contract::multitest_utils::NativeStakingProxyContractProxy::new( - Addr::unchecked(proxy_addr), - &app, - ); + let staking_proxy = + contract::sv::mt::NativeStakingProxyContractProxy::new(Addr::unchecked(proxy_addr), &app); // Stake some more vault @@ -214,10 +210,8 @@ fn restaking() { setup(&app, owner, user, &[validator]).unwrap(); // Access staking proxy instance - let staking_proxy = contract::multitest_utils::NativeStakingProxyContractProxy::new( - Addr::unchecked(proxy_addr), - &app, - ); + let staking_proxy = + contract::sv::mt::NativeStakingProxyContractProxy::new(Addr::unchecked(proxy_addr), &app); // Restake 30% to a different validator staking_proxy @@ -255,10 +249,8 @@ fn unstaking() { setup(&app, owner, user, &[validator]).unwrap(); // Access staking proxy instance - let staking_proxy = contract::multitest_utils::NativeStakingProxyContractProxy::new( - Addr::unchecked(proxy_addr), - &app, - ); + let staking_proxy = + contract::sv::mt::NativeStakingProxyContractProxy::new(Addr::unchecked(proxy_addr), &app); // Unstake 50% staking_proxy @@ -277,10 +269,6 @@ fn unstaking() { // And that they are now held, until the unbonding period // First, check that the contract has no funds - // Manually cause queue to get processed. TODO: Handle automatically in sylvia mt or cw-mt - app.app_mut() - .sudo(SudoMsg::Staking(StakingSudo::ProcessQueue {})) - .unwrap(); assert_eq!( app.app() .wrap() @@ -294,10 +282,6 @@ fn unstaking() { block.height += 1234; block.time = block.time.plus_seconds(UNBONDING_PERIOD); }); - // Manually cause queue to get processed. TODO: Handle automatically in sylvia mt or cw-mt - app.app_mut() - .sudo(SudoMsg::Staking(StakingSudo::ProcessQueue {})) - .unwrap(); // Check that the contract now has the funds assert_eq!( @@ -323,10 +307,8 @@ fn burning() { setup(&app, owner, user, &[validator]).unwrap(); // Access staking proxy instance - let staking_proxy = contract::multitest_utils::NativeStakingProxyContractProxy::new( - Addr::unchecked(proxy_addr), - &app, - ); + let staking_proxy = + contract::sv::mt::NativeStakingProxyContractProxy::new(Addr::unchecked(proxy_addr), &app); // Burn 10%, from validator staking_proxy @@ -345,10 +327,6 @@ fn burning() { // And that they are now held, until the unbonding period // First, check that the contract has no funds - // Manually cause queue to get processed. TODO: Handle automatically in sylvia mt or cw-mt - app.app_mut() - .sudo(SudoMsg::Staking(StakingSudo::ProcessQueue {})) - .unwrap(); assert_eq!( app.app() .wrap() @@ -362,10 +340,6 @@ fn burning() { block.height += 1234; block.time = block.time.plus_seconds(UNBONDING_PERIOD); }); - // Manually cause queue to get processed. TODO: Handle automatically in sylvia mt or cw-mt - app.app_mut() - .sudo(SudoMsg::Staking(StakingSudo::ProcessQueue {})) - .unwrap(); // Check that the contract now has the funds assert_eq!( @@ -403,10 +377,8 @@ fn burning_multiple_delegations() { setup(&app, owner, user, &validators).unwrap(); // Access staking proxy instance - let staking_proxy = contract::multitest_utils::NativeStakingProxyContractProxy::new( - Addr::unchecked(proxy_addr), - &app, - ); + let staking_proxy = + contract::sv::mt::NativeStakingProxyContractProxy::new(Addr::unchecked(proxy_addr), &app); // Burn 15%, no validator specified let burn_amount = 15; @@ -440,10 +412,6 @@ fn burning_multiple_delegations() { // And that they are now held, until the unbonding period // First, check that the contract has no funds - // Manually cause queue to get processed. TODO: Handle automatically in sylvia mt or cw-mt - app.app_mut() - .sudo(SudoMsg::Staking(StakingSudo::ProcessQueue {})) - .unwrap(); assert_eq!( app.app() .wrap() @@ -457,10 +425,6 @@ fn burning_multiple_delegations() { block.height += 1234; block.time = block.time.plus_seconds(UNBONDING_PERIOD); }); - // Manually cause queue to get processed. TODO: Handle automatically in sylvia mt or cw-mt - app.app_mut() - .sudo(SudoMsg::Staking(StakingSudo::ProcessQueue {})) - .unwrap(); // Check that the contract now has the funds assert_eq!( @@ -497,10 +461,8 @@ fn releasing_unbonded() { let vault = setup(&app, owner, user, &[validator]).unwrap(); // Access staking proxy instance - let staking_proxy = contract::multitest_utils::NativeStakingProxyContractProxy::new( - Addr::unchecked(proxy_addr), - &app, - ); + let staking_proxy = + contract::sv::mt::NativeStakingProxyContractProxy::new(Addr::unchecked(proxy_addr), &app); // Unstake 100% staking_proxy @@ -521,10 +483,6 @@ fn releasing_unbonded() { block.height += 12345; block.time = block.time.plus_seconds(UNBONDING_PERIOD + 1); }); - // Manually cause queue to get processed. TODO: Handle automatically in sylvia mt or cw-mt - app.app_mut() - .sudo(SudoMsg::Staking(StakingSudo::ProcessQueue {})) - .unwrap(); // Release the unbonded funds staking_proxy.release_unbonded().call(user).unwrap(); @@ -562,10 +520,8 @@ fn withdrawing_rewards() { let original_user_funds = app.app().wrap().query_balance(user, OSMO).unwrap(); // Access staking proxy instance - let staking_proxy = contract::multitest_utils::NativeStakingProxyContractProxy::new( - Addr::unchecked(proxy_addr), - &app, - ); + let staking_proxy = + contract::sv::mt::NativeStakingProxyContractProxy::new(Addr::unchecked(proxy_addr), &app); // Advance time enough for rewards to accrue app.update_block(|block| { diff --git a/contracts/provider/native-staking-proxy/src/native_staking_callback.rs b/contracts/provider/native-staking-proxy/src/native_staking_callback.rs index 25eca15f..53dc0241 100644 --- a/contracts/provider/native-staking-proxy/src/native_staking_callback.rs +++ b/contracts/provider/native-staking-proxy/src/native_staking_callback.rs @@ -10,6 +10,6 @@ pub trait NativeStakingCallback { /// This sends tokens back from the proxy to native-staking. (See info.funds) /// The native-staking contract can determine which user it belongs to via an internal Map. /// The native-staking contract will then send those tokens back to vault and release the claim. - #[msg(exec)] + #[sv::msg(exec)] fn release_proxy_stake(&self, _ctx: ExecCtx) -> Result; } diff --git a/contracts/provider/native-staking/src/bin/schema.rs b/contracts/provider/native-staking/src/bin/schema.rs index 9418954a..4f7cbbbc 100644 --- a/contracts/provider/native-staking/src/bin/schema.rs +++ b/contracts/provider/native-staking/src/bin/schema.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::write_api; -use mesh_native_staking::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; +use mesh_native_staking::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; #[cfg(not(tarpaulin_include))] fn main() { diff --git a/contracts/provider/native-staking/src/contract.rs b/contracts/provider/native-staking/src/contract.rs index 592928c4..8fa19bcc 100644 --- a/contracts/provider/native-staking/src/contract.rs +++ b/contracts/provider/native-staking/src/contract.rs @@ -3,8 +3,7 @@ use cosmwasm_std::entry_point; use cosmwasm_std::Order::Ascending; use cosmwasm_std::{ - from_json, Addr, Decimal, DepsMut, Env, Event, Reply, Response, StdResult, SubMsgResponse, - WasmMsg, + from_json, Addr, Decimal, DepsMut, Reply, Response, StdResult, SubMsgResponse, WasmMsg, }; use cw2::set_contract_version; use cw_storage_plus::{Item, Map}; @@ -13,7 +12,6 @@ use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx, ReplyCtx}; use sylvia::{contract, schemars}; use mesh_apis::local_staking_api; -use mesh_apis::local_staking_api::SudoMsg; use mesh_apis::vault_api::{SlashInfo, VaultApiHelper}; use mesh_native_staking_proxy::msg::OwnerMsg; use mesh_native_staking_proxy::native_staking_callback; @@ -45,9 +43,9 @@ pub(crate) enum SlashingReason { #[cfg_attr(not(feature = "library"), sylvia::entry_points)] #[contract] -#[error(ContractError)] -#[messages(local_staking_api as LocalStakingApi)] -#[messages(native_staking_callback as NativeStakingCallback)] +#[sv::error(ContractError)] +#[sv::messages(local_staking_api as LocalStakingApi)] +#[sv::messages(native_staking_callback as NativeStakingCallback)] impl NativeStakingContract<'_> { pub const fn new() -> Self { Self { @@ -59,7 +57,7 @@ impl NativeStakingContract<'_> { } /// The caller of the instantiation will be the vault contract - #[msg(instantiate)] + #[sv::msg(instantiate)] pub fn instantiate( &self, ctx: InstantiateCtx, @@ -84,44 +82,7 @@ impl NativeStakingContract<'_> { Ok(Response::new()) } - /// This is called every time there's a change of the active validator set that implies slashing. - /// In test code, this is called from `test_handle_jailing`. - /// In non-test code, this is called from `sudo`. - fn handle_jailing( - &self, - mut deps: DepsMut, - jailed: &[String], - tombstoned: &[String], - ) -> Result { - let cfg = self.config.load(deps.storage)?; - let mut msgs = vec![]; - for validator in tombstoned { - // Slash the validator (if bonded) - let slash_msg = - self.handle_slashing(&mut deps, &cfg, validator, SlashingReason::DoubleSign)?; - if let Some(msg) = slash_msg { - msgs.push(msg) - } - } - for validator in jailed { - // Slash the validator (if bonded) - let slash_msg = - self.handle_slashing(&mut deps, &cfg, validator, SlashingReason::Offline)?; - if let Some(msg) = slash_msg { - msgs.push(msg) - } - } - let mut evt = Event::new("jailing"); - if !jailed.is_empty() { - evt = evt.add_attribute("jailed", jailed.join(",")); - } - if !tombstoned.is_empty() { - evt = evt.add_attribute("tombstoned", tombstoned.join(",")); - } - Ok(Response::new().add_event(evt).add_messages(msgs)) - } - - fn handle_slashing( + pub(crate) fn handle_slashing( &self, deps: &mut DepsMut, config: &Config, @@ -177,12 +138,12 @@ impl NativeStakingContract<'_> { Ok(Some(msg)) } - #[msg(query)] + #[sv::msg(query)] fn config(&self, ctx: QueryCtx) -> Result { self.config.load(ctx.deps.storage).map_err(Into::into) } - #[msg(reply)] + #[sv::msg(reply)] fn reply(&self, ctx: ReplyCtx, reply: Reply) -> Result { match reply.id { REPLY_ID_INSTANTIATE => self.reply_init_callback(ctx.deps, reply.result.unwrap()), @@ -210,7 +171,7 @@ impl NativeStakingContract<'_> { Ok(Response::new()) } - #[msg(query)] + #[sv::msg(query)] fn proxy_by_owner( &self, ctx: QueryCtx, @@ -223,7 +184,7 @@ impl NativeStakingContract<'_> { }) } - #[msg(query)] + #[sv::msg(query)] fn owner_by_proxy( &self, ctx: QueryCtx, @@ -235,35 +196,4 @@ impl NativeStakingContract<'_> { owner: owner_addr.to_string(), }) } - - /// Jails validators temporarily or permanently. - /// Method used for test only. - #[msg(exec)] - fn test_handle_jailing( - &self, - ctx: ExecCtx, - jailed: Vec, - tombstoned: Vec, - ) -> Result { - #[cfg(any(feature = "mt", test))] - { - NativeStakingContract::new().handle_jailing(ctx.deps, &jailed, &tombstoned) - } - #[cfg(not(any(feature = "mt", test)))] - { - let _ = (ctx, jailed, tombstoned); - Err(ContractError::Unauthorized {}) - } - } -} - -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn sudo(deps: DepsMut, _env: Env, msg: SudoMsg) -> Result { - match msg { - SudoMsg::Jailing { jailed, tombstoned } => NativeStakingContract::new().handle_jailing( - deps, - &jailed.unwrap_or_default(), - &tombstoned.unwrap_or_default(), - ), - } } diff --git a/contracts/provider/native-staking/src/error.rs b/contracts/provider/native-staking/src/error.rs index 198619f4..e0d2e422 100644 --- a/contracts/provider/native-staking/src/error.rs +++ b/contracts/provider/native-staking/src/error.rs @@ -2,7 +2,7 @@ use cosmwasm_std::StdError; use cw_utils::{ParseReplyError, PaymentError}; use thiserror::Error; -#[derive(Error, Debug)] +#[derive(Error, Debug, PartialEq)] pub enum ContractError { #[error("{0}")] Std(#[from] StdError), diff --git a/contracts/provider/native-staking/src/local_staking_api.rs b/contracts/provider/native-staking/src/local_staking_api.rs index 41ae02ac..4045ed6d 100644 --- a/contracts/provider/native-staking/src/local_staking_api.rs +++ b/contracts/provider/native-staking/src/local_staking_api.rs @@ -1,28 +1,24 @@ -use cosmwasm_std::{ensure_eq, from_json, to_json_binary, Binary, Coin, Response, SubMsg, WasmMsg}; +use cosmwasm_std::{ + ensure_eq, from_json, to_json_binary, Binary, Coin, Event, Response, SubMsg, WasmMsg, +}; use cw_utils::{must_pay, nonpayable}; -use sylvia::types::QueryCtx; -use sylvia::{contract, types::ExecCtx}; +use sylvia::types::{ExecCtx, QueryCtx, SudoCtx}; #[allow(unused_imports)] use mesh_apis::local_staking_api::{self, LocalStakingApi, SlashRatioResponse}; -use crate::contract::{NativeStakingContract, REPLY_ID_INSTANTIATE}; +use crate::contract::{NativeStakingContract, SlashingReason, REPLY_ID_INSTANTIATE}; use crate::error::ContractError; use crate::msg::StakeMsg; -// FIXME: Move to sylvia contract macro -use crate::contract::BoundQuerier; use crate::state::Config; -#[contract] -#[messages(local_staking_api as LocalStakingApi)] impl LocalStakingApi for NativeStakingContract<'_> { type Error = ContractError; /// Receives stake (info.funds) from vault contract on behalf of owner and performs the action /// specified in msg with it. /// Msg is custom to each implementation of the staking contract and opaque to the vault - #[msg(exec)] fn receive_stake( &self, ctx: ExecCtx, @@ -52,11 +48,12 @@ impl LocalStakingApi for NativeStakingContract<'_> { { None => { // Instantiate proxy contract and send funds to stake, with reply handling on success - let msg = to_json_binary(&mesh_native_staking_proxy::contract::InstantiateMsg { - denom: cfg.denom, - owner: owner.clone(), - validator, - })?; + let msg = + to_json_binary(&mesh_native_staking_proxy::contract::sv::InstantiateMsg { + denom: cfg.denom, + owner: owner.clone(), + validator, + })?; let wasm_msg = WasmMsg::Instantiate { admin: Some(ctx.env.contract.address.into()), code_id: cfg.proxy_code_id, @@ -69,9 +66,10 @@ impl LocalStakingApi for NativeStakingContract<'_> { } Some(proxy_addr) => { // Send stake message with funds to the proxy contract - let msg = to_json_binary(&mesh_native_staking_proxy::contract::ExecMsg::Stake { - validator, - })?; + let msg = + to_json_binary(&mesh_native_staking_proxy::contract::sv::ExecMsg::Stake { + validator, + })?; let wasm_msg = WasmMsg::Execute { contract_addr: proxy_addr.into(), msg, @@ -86,7 +84,6 @@ impl LocalStakingApi for NativeStakingContract<'_> { /// propagation, the native staking contract needs to burn / discount the indicated slashing amount. /// If `validator` is set, undelegate preferentially from it first. /// If it is not set, undelegate evenly from all validators the user has stake in. - #[msg(exec)] fn burn_stake( &self, ctx: ExecCtx, @@ -110,10 +107,11 @@ impl LocalStakingApi for NativeStakingContract<'_> { None => Err(ContractError::NoProxy(owner)), Some(proxy_addr) => { // Send burn message to the proxy contract - let msg = to_json_binary(&mesh_native_staking_proxy::contract::ExecMsg::Burn { - validator, - amount, - })?; + let msg = + to_json_binary(&mesh_native_staking_proxy::contract::sv::ExecMsg::Burn { + validator, + amount, + })?; let wasm_msg = WasmMsg::Execute { contract_addr: proxy_addr.into(), msg, @@ -124,8 +122,47 @@ impl LocalStakingApi for NativeStakingContract<'_> { } } + /// This is called every time there's a change of the active validator set that implies slashing. + /// In test code, this is called from `test_handle_jailing`. + /// In non-test code, this is called from `sudo`. + fn jailing( + &self, + mut ctx: SudoCtx, + jailed: Option>, + tombstoned: Option>, + ) -> Result { + let jailed = &jailed.unwrap_or_default(); + let tombstoned = &tombstoned.unwrap_or_default(); + + let cfg = self.config.load(ctx.deps.storage)?; + let mut msgs = vec![]; + for validator in tombstoned { + // Slash the validator (if bonded) + let slash_msg = + self.handle_slashing(&mut ctx.deps, &cfg, validator, SlashingReason::DoubleSign)?; + if let Some(msg) = slash_msg { + msgs.push(msg) + } + } + for validator in jailed { + // Slash the validator (if bonded) + let slash_msg = + self.handle_slashing(&mut ctx.deps, &cfg, validator, SlashingReason::Offline)?; + if let Some(msg) = slash_msg { + msgs.push(msg) + } + } + let mut evt = Event::new("jailing"); + if !jailed.is_empty() { + evt = evt.add_attribute("jailed", jailed.join(",")); + } + if !tombstoned.is_empty() { + evt = evt.add_attribute("tombstoned", tombstoned.join(",")); + } + Ok(Response::new().add_event(evt).add_messages(msgs)) + } + /// Returns the maximum percentage that can be slashed - #[msg(query)] fn max_slash(&self, ctx: QueryCtx) -> Result { let Config { slash_ratio_dsign, diff --git a/contracts/provider/native-staking/src/multitest.rs b/contracts/provider/native-staking/src/multitest.rs index ff2781bb..51482ac6 100644 --- a/contracts/provider/native-staking/src/multitest.rs +++ b/contracts/provider/native-staking/src/multitest.rs @@ -5,14 +5,15 @@ use cosmwasm_std::{ use cw_multi_test::{App as MtApp, StakingInfo}; use sylvia::multitest::App; -use mesh_native_staking_proxy::contract::multitest_utils::{ +use mesh_apis::local_staking_api::sv::mt::LocalStakingApiProxy; +use mesh_native_staking_proxy::contract::sv::mt::{ CodeId as NativeStakingProxyCodeId, NativeStakingProxyContractProxy, }; use mesh_sync::ValueRange; - -use crate::local_staking_api::test_utils::LocalStakingApi; +use mesh_vault::contract::sv::mt::VaultContractProxy; use crate::contract; +use crate::contract::sv::mt::NativeStakingContractProxy; use crate::error::ContractError; use crate::msg; use crate::msg::{OwnerByProxyResponse, ProxyByOwnerResponse}; @@ -107,7 +108,7 @@ fn instantiation() { let owner = "vault"; // Owner of the staking contract (i. e. the vault contract) let staking_proxy_code = NativeStakingProxyCodeId::store_code(&app); - let staking_code = contract::multitest_utils::CodeId::store_code(&app); + let staking_code = contract::sv::mt::CodeId::store_code(&app); let staking = staking_code .instantiate( @@ -123,7 +124,7 @@ fn instantiation() { let config = staking.config().unwrap(); assert_eq!(config.denom, OSMO); - let res = staking.local_staking_api_proxy().max_slash().unwrap(); + let res = staking.max_slash().unwrap(); assert_eq!(res.slash_ratio_dsign, slashing_rate_dsign()); } @@ -141,7 +142,7 @@ fn receiving_stake() { // Contracts setup let staking_proxy_code = NativeStakingProxyCodeId::store_code(&app); - let staking_code = contract::multitest_utils::CodeId::store_code(&app); + let staking_code = contract::sv::mt::CodeId::store_code(&app); let staking = staking_code .instantiate( @@ -167,7 +168,6 @@ fn receiving_stake() { }) .unwrap(); staking - .local_staking_api_proxy() .receive_stake(user1.to_owned(), stake_msg) .with_funds(&coins(100, OSMO)) .call(owner) // called from vault @@ -191,7 +191,6 @@ fn receiving_stake() { }) .unwrap(); staking - .local_staking_api_proxy() .receive_stake(user1.to_owned(), stake_msg) .with_funds(&coins(50, OSMO)) .call(owner) // called from vault @@ -222,7 +221,6 @@ fn receiving_stake() { }) .unwrap(); staking - .local_staking_api_proxy() .receive_stake(user2.to_owned(), stake_msg) .with_funds(&coins(10, OSMO)) .call(owner) // called from vault @@ -256,15 +254,15 @@ fn releasing_proxy_stake() { let app = app(&[(user, (300, OSMO))], &[validator]); // Contracts setup - let vault_code = mesh_vault::contract::multitest_utils::CodeId::store_code(&app); - let staking_code = contract::multitest_utils::CodeId::store_code(&app); + let vault_code = mesh_vault::contract::sv::mt::CodeId::store_code(&app); + let staking_code = contract::sv::mt::CodeId::store_code(&app); let staking_proxy_code = NativeStakingProxyCodeId::store_code(&app); // Instantiate vault msg let staking_init_info = mesh_vault::msg::StakingInitInfo { admin: None, code_id: staking_code.code_id(), - msg: to_json_binary(&crate::contract::InstantiateMsg { + msg: to_json_binary(&crate::contract::sv::InstantiateMsg { denom: OSMO.to_owned(), proxy_code_id: staking_proxy_code.code_id(), slash_ratio_dsign: slashing_rate_dsign(), @@ -340,11 +338,6 @@ fn releasing_proxy_stake() { .unstake(validator.to_string(), coin(100, OSMO)) .call(user) .unwrap(); - app.app_mut() - .sudo(cw_multi_test::SudoMsg::Staking( - cw_multi_test::StakingSudo::ProcessQueue {}, - )) - .unwrap(); staking_proxy.release_unbonded().call(user).unwrap(); // Check that the vault has the funds again diff --git a/contracts/provider/native-staking/src/native_staking_callback.rs b/contracts/provider/native-staking/src/native_staking_callback.rs index c64184af..4996296f 100644 --- a/contracts/provider/native-staking/src/native_staking_callback.rs +++ b/contracts/provider/native-staking/src/native_staking_callback.rs @@ -1,6 +1,5 @@ use cosmwasm_std::Response; use cw_utils::must_pay; -use sylvia::contract; use sylvia::types::ExecCtx; #[allow(unused_imports)] @@ -9,18 +8,12 @@ use mesh_native_staking_proxy::native_staking_callback::{self, NativeStakingCall use crate::contract::NativeStakingContract; use crate::error::ContractError; -// FIXME: Move to sylvia contract macro -use crate::contract::BoundQuerier; - -#[contract] -#[messages(native_staking_callback as NativeStakingCallback)] impl NativeStakingCallback for NativeStakingContract<'_> { type Error = ContractError; /// This sends tokens back from the proxy to native-staking. (See info.funds) /// The native-staking contract can determine which user it belongs to via an internal Map. /// The native-staking contract will then send those tokens back to vault and release the claim. - #[msg(exec)] fn release_proxy_stake(&self, ctx: ExecCtx) -> Result { let cfg = self.config.load(ctx.deps.storage)?; diff --git a/contracts/provider/vault/src/bin/schema.rs b/contracts/provider/vault/src/bin/schema.rs index 580812f4..ab50e399 100644 --- a/contracts/provider/vault/src/bin/schema.rs +++ b/contracts/provider/vault/src/bin/schema.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::write_api; -use mesh_vault::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; +use mesh_vault::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; #[cfg(not(tarpaulin_include))] fn main() { diff --git a/contracts/provider/vault/src/contract.rs b/contracts/provider/vault/src/contract.rs index ee4009e7..4ce5003e 100644 --- a/contracts/provider/vault/src/contract.rs +++ b/contracts/provider/vault/src/contract.rs @@ -9,7 +9,7 @@ use std::cmp::min; use mesh_apis::cross_staking_api::CrossStakingApiHelper; use mesh_apis::local_staking_api::{ - LocalStakingApiHelper, LocalStakingApiQueryMsg, SlashRatioResponse, + sv::LocalStakingApiQueryMsg, LocalStakingApiHelper, SlashRatioResponse, }; use mesh_apis::vault_api::{self, SlashInfo, VaultApi}; use mesh_sync::Tx::InFlightStaking; @@ -64,8 +64,8 @@ pub struct VaultContract<'a> { #[cfg_attr(not(feature = "library"), sylvia::entry_points)] #[contract] -#[error(ContractError)] -#[messages(vault_api as VaultApi)] +#[sv::error(ContractError)] +#[sv::messages(vault_api as VaultApi)] impl VaultContract<'_> { pub fn new() -> Self { Self { @@ -85,7 +85,7 @@ impl VaultContract<'_> { Ok(id) } - #[msg(instantiate)] + #[sv::msg(instantiate)] pub fn instantiate( &self, ctx: InstantiateCtx, @@ -117,7 +117,7 @@ impl VaultContract<'_> { } } - #[msg(exec)] + #[sv::msg(exec)] fn bond(&self, ctx: ExecCtx) -> Result { let denom = self.config.load(ctx.deps.storage)?.denom; let amount = must_pay(&ctx.info, &denom)?; @@ -137,7 +137,7 @@ impl VaultContract<'_> { Ok(resp) } - #[msg(exec)] + #[sv::msg(exec)] fn unbond(&self, ctx: ExecCtx, amount: Coin) -> Result { nonpayable(&ctx.info)?; @@ -174,7 +174,7 @@ impl VaultContract<'_> { } /// This assigns a claim of amount tokens to the remote contract, which can take some action with it - #[msg(exec)] + #[sv::msg(exec)] fn stake_remote( &self, mut ctx: ExecCtx, @@ -223,7 +223,7 @@ impl VaultContract<'_> { } /// This sends actual tokens to the local staking contract - #[msg(exec)] + #[sv::msg(exec)] fn stake_local( &self, mut ctx: ExecCtx, @@ -263,7 +263,7 @@ impl VaultContract<'_> { } } - #[msg(query)] + #[sv::msg(query)] fn account(&self, ctx: QueryCtx, account: String) -> Result { let denom = self.config.load(ctx.deps.storage)?.denom; let account = ctx.deps.api.addr_validate(&account)?; @@ -279,7 +279,7 @@ impl VaultContract<'_> { }) } - #[msg(query)] + #[sv::msg(query)] fn account_details( &self, ctx: QueryCtx, @@ -301,7 +301,7 @@ impl VaultContract<'_> { }) } - #[msg(query)] + #[sv::msg(query)] fn config(&self, ctx: QueryCtx) -> Result { let config = self.config.load(ctx.deps.storage)?; let local_staking = self.local_staking.load(ctx.deps.storage)?; @@ -314,7 +314,7 @@ impl VaultContract<'_> { Ok(resp) } - #[msg(query)] + #[sv::msg(query)] fn active_external_staking( &self, ctx: QueryCtx, @@ -332,7 +332,7 @@ impl VaultContract<'_> { } /// Returns a single claim between the user and lienholder - #[msg(query)] + #[sv::msg(query)] fn claim( &self, ctx: QueryCtx, @@ -350,7 +350,7 @@ impl VaultContract<'_> { /// Returns paginated claims list for an user /// /// `start_after` is a last lienholder of the previous page, and it will not be included - #[msg(query)] + #[sv::msg(query)] fn account_claims( &self, ctx: QueryCtx, @@ -388,7 +388,7 @@ impl VaultContract<'_> { /// `start_after` is the last account included in previous page /// /// `with_collateral` flag filters out users with no collateral, defaulted to false - #[msg(query)] + #[sv::msg(query)] fn all_accounts( &self, ctx: QueryCtx, @@ -432,7 +432,7 @@ impl VaultContract<'_> { } /// Queries a pending tx. - #[msg(query)] + #[sv::msg(query)] fn pending_tx(&self, ctx: QueryCtx, tx_id: u64) -> Result { let resp = self.pending.txs.load(ctx.deps.storage, tx_id)?; Ok(resp) @@ -441,7 +441,7 @@ impl VaultContract<'_> { /// Queries for all pending txs. /// Reports txs in descending order (newest first). /// `start_after` is the last tx id included in previous page - #[msg(query)] + #[sv::msg(query)] fn all_pending_txs_desc( &self, ctx: QueryCtx, @@ -467,7 +467,7 @@ impl VaultContract<'_> { Ok(resp) } - #[msg(reply)] + #[sv::msg(reply)] fn reply(&self, ctx: ReplyCtx, reply: Reply) -> Result { match reply.id { REPLY_ID_INSTANTIATE => self.reply_init_callback(ctx.deps, reply.result.unwrap()), @@ -957,13 +957,10 @@ impl Default for VaultContract<'_> { } } -#[contract] -#[messages(vault_api as VaultApi)] impl VaultApi for VaultContract<'_> { type Error = ContractError; /// This must be called by the remote staking contract to release this claim - #[msg(exec)] fn release_cross_stake( &self, mut ctx: ExecCtx, @@ -987,7 +984,6 @@ impl VaultApi for VaultContract<'_> { /// This must be called by the local staking contract to release this claim /// Amount of tokens unstaked are those included in ctx.info.funds - #[msg(exec)] fn release_local_stake( &self, mut ctx: ExecCtx, @@ -1009,7 +1005,6 @@ impl VaultApi for VaultContract<'_> { } /// This must be called by the native staking contract to process a misbehaviour - #[msg(exec)] fn local_slash( &self, mut ctx: ExecCtx, @@ -1038,7 +1033,6 @@ impl VaultApi for VaultContract<'_> { } /// This must be called by the external staking contract to process a misbehaviour - #[msg(exec)] fn cross_slash( &self, mut ctx: ExecCtx, @@ -1066,7 +1060,6 @@ impl VaultApi for VaultContract<'_> { Ok(resp) } - #[msg(exec)] fn commit_tx(&self, mut ctx: ExecCtx, tx_id: u64) -> Result { self.commit_stake(&mut ctx, tx_id)?; @@ -1078,7 +1071,6 @@ impl VaultApi for VaultContract<'_> { Ok(resp) } - #[msg(exec)] fn rollback_tx(&self, mut ctx: ExecCtx, tx_id: u64) -> Result { self.rollback_stake(&mut ctx, tx_id)?; diff --git a/contracts/provider/vault/src/multitest.rs b/contracts/provider/vault/src/multitest.rs index 12a44466..cc8c73de 100644 --- a/contracts/provider/vault/src/multitest.rs +++ b/contracts/provider/vault/src/multitest.rs @@ -1,20 +1,18 @@ use cosmwasm_std::{coin, coins, to_json_binary, Addr, Decimal, Uint128, Validator}; use cw_multi_test::{App as MtApp, StakingInfo}; use mesh_apis::ibc::AddValidator; -use mesh_external_staking::contract::multitest_utils::ExternalStakingContractProxy; +use mesh_external_staking::contract::sv::mt::ExternalStakingContractProxy; use mesh_external_staking::msg::{AuthorizedEndpoint, ReceiveVirtualStake, StakeInfo}; use mesh_external_staking::state::SlashRatio; use mesh_external_staking::state::Stake; -use mesh_external_staking::test_methods_impl::test_utils::TestMethods; -use mesh_native_staking::contract::multitest_utils::NativeStakingContractProxy; -use mesh_native_staking_proxy::contract::multitest_utils::NativeStakingProxyContractProxy; +use mesh_native_staking::contract::sv::mt::NativeStakingContractProxy; +use mesh_native_staking_proxy::contract::sv::mt::NativeStakingProxyContractProxy; use mesh_sync::Tx::InFlightStaking; use mesh_sync::{Tx, ValueRange}; use sylvia::multitest::App; use crate::contract; -use crate::contract::multitest_utils::VaultContractProxy; -use crate::contract::test_utils::VaultApi; +use crate::contract::sv::mt::VaultContractProxy; use crate::error::ContractError; use crate::msg::{ AccountResponse, AllAccountsResponseItem, AllActiveExternalStakingResponse, LienResponse, @@ -31,7 +29,7 @@ const SLASHING_PERCENTAGE: u64 = 10; /// App initialization fn init_app(users: &[&str], amounts: &[u128]) -> App { - let mut app = MtApp::new(|router, _api, storage| { + let mut app = App::new(|router, _api, storage| { for (&user, amount) in std::iter::zip(users, amounts) { router .bank @@ -117,15 +115,14 @@ fn setup_inner<'app>( Option>, ExternalStakingContractProxy<'app, MtApp>, ) { - let vault_code = contract::multitest_utils::CodeId::store_code(app); + let vault_code = contract::sv::mt::CodeId::store_code(app); let staking_init_info = if local_staking { - let native_staking_code = - mesh_native_staking::contract::multitest_utils::CodeId::store_code(app); + let native_staking_code = mesh_native_staking::contract::sv::mt::CodeId::store_code(app); let native_staking_proxy_code = - mesh_native_staking_proxy::contract::multitest_utils::CodeId::store_code(app); + mesh_native_staking_proxy::contract::sv::mt::CodeId::store_code(app); - let native_staking_inst_msg = mesh_native_staking::contract::InstantiateMsg { + let native_staking_inst_msg = mesh_native_staking::contract::sv::InstantiateMsg { denom: OSMO.to_string(), slash_ratio_dsign: Decimal::percent(10), slash_ratio_offline: Decimal::percent(10), @@ -163,8 +160,7 @@ fn setup_cross_stake<'app>( unbond_period: u64, ) -> ExternalStakingContractProxy<'app, MtApp> { // FIXME: Code shouldn't be duplicated - let cross_staking_code = - mesh_external_staking::contract::multitest_utils::CodeId::store_code(app); + let cross_staking_code = mesh_external_staking::contract::sv::mt::CodeId::store_code(app); // FIXME: Connection endpoint should be unique let remote_contact = AuthorizedEndpoint::new("connection-2", "wasm-osmo1foobarbaz"); @@ -275,11 +271,6 @@ fn process_staking_unbondings(app: &App) { let mut block_info = app.block_info(); block_info.time = block_info.time.plus_seconds(61); app.set_block(block_info); - app.app_mut() - .sudo(cw_multi_test::SudoMsg::Staking( - cw_multi_test::StakingSudo::ProcessQueue {}, - )) - .unwrap(); } #[track_caller] diff --git a/packages/apis/src/converter_api.rs b/packages/apis/src/converter_api.rs index 77d0fb67..2a26474d 100644 --- a/packages/apis/src/converter_api.rs +++ b/packages/apis/src/converter_api.rs @@ -12,7 +12,7 @@ pub trait ConverterApi { /// Rewards tokens (in native staking denom) are sent alongside the message, and should be distributed to all /// stakers who staked on this validator. - #[msg(exec)] + #[sv::msg(exec)] fn distribute_reward(&self, ctx: ExecCtx, validator: String) -> Result; /// This is a batch for of distribute_reward, including the payment for multiple validators. @@ -20,7 +20,7 @@ pub trait ConverterApi { /// /// info.funds sent along with the message should be the sum of all rewards for all validators, /// in the native staking denom. - #[msg(exec)] + #[sv::msg(exec)] fn distribute_rewards( &self, ctx: ExecCtx, @@ -31,7 +31,7 @@ pub trait ConverterApi { /// /// TODO: pubkeys need to be part of the Validator struct (requires CosmWasm support). #[allow(clippy::too_many_arguments)] - #[msg(exec)] + #[sv::msg(exec)] fn valset_update( &self, ctx: ExecCtx, diff --git a/packages/apis/src/cross_staking_api.rs b/packages/apis/src/cross_staking_api.rs index c02fbbc1..f83e0dac 100644 --- a/packages/apis/src/cross_staking_api.rs +++ b/packages/apis/src/cross_staking_api.rs @@ -18,7 +18,7 @@ pub trait CrossStakingApi { /// Receives stake from vault contract on behalf of owner and performs the action /// specified in msg with it. /// Msg is custom to each implementation of the staking contract and opaque to the vault - #[msg(exec)] + #[sv::msg(exec)] fn receive_virtual_stake( &self, ctx: ExecCtx, @@ -34,7 +34,7 @@ pub trait CrossStakingApi { /// If it is not set, undelegate evenly from all validators the user has stake in. /// This should be transactional, but it's not. If the transaction fails there isn't much we can /// do, besides logging the failure. - #[msg(exec)] + #[sv::msg(exec)] fn burn_virtual_stake( &self, ctx: ExecCtx, @@ -44,7 +44,7 @@ pub trait CrossStakingApi { ) -> Result; /// Returns the maximum percentage that can be slashed - #[msg(query)] + #[sv::msg(query)] fn max_slash(&self, ctx: QueryCtx) -> Result; } @@ -64,7 +64,7 @@ impl CrossStakingApiHelper { msg: Binary, funds: Vec, ) -> Result { - let msg = CrossStakingApiExecMsg::ReceiveVirtualStake { + let msg = sv::CrossStakingApiExecMsg::ReceiveVirtualStake { owner, msg, amount, @@ -84,7 +84,7 @@ impl CrossStakingApiHelper { amount: Coin, validator: Option, ) -> Result { - let msg = CrossStakingApiExecMsg::BurnVirtualStake { + let msg = sv::CrossStakingApiExecMsg::BurnVirtualStake { owner: owner.to_string(), validator, amount, @@ -98,7 +98,7 @@ impl CrossStakingApiHelper { } pub fn max_slash(&self, deps: Deps) -> Result { - let query = CrossStakingApiQueryMsg::MaxSlash {}; + let query = sv::CrossStakingApiQueryMsg::MaxSlash {}; deps.querier.query_wasm_smart(&self.0, &query) } } diff --git a/packages/apis/src/local_staking_api.rs b/packages/apis/src/local_staking_api.rs index f2be8dd9..2212fba4 100644 --- a/packages/apis/src/local_staking_api.rs +++ b/packages/apis/src/local_staking_api.rs @@ -2,7 +2,7 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{ to_json_binary, Addr, Binary, Coin, Decimal, Deps, Response, StdError, WasmMsg, }; -use sylvia::types::{ExecCtx, QueryCtx}; +use sylvia::types::{ExecCtx, QueryCtx, SudoCtx}; use sylvia::{interface, schemars}; #[cw_serde] @@ -20,7 +20,7 @@ pub trait LocalStakingApi { /// Receives stake (info.funds) from vault contract on behalf of owner and performs the action /// specified in msg with it. /// Msg is custom to each implementation of the staking contract and opaque to the vault - #[msg(exec)] + #[sv::msg(exec)] fn receive_stake( &self, ctx: ExecCtx, @@ -37,7 +37,7 @@ pub trait LocalStakingApi { /// propagation, the native staking contract needs to burn / discount the indicated slashing amount. /// If `validator` is set, undelegate preferentially from it first. /// If it is not set, undelegate evenly from all validators the user has stake in. - #[msg(exec)] + #[sv::msg(exec)] fn burn_stake( &self, ctx: ExecCtx, @@ -47,8 +47,20 @@ pub trait LocalStakingApi { ) -> Result; /// Returns the maximum percentage that can be slashed - #[msg(query)] + #[sv::msg(query)] fn max_slash(&self, ctx: QueryCtx) -> Result; + + /// `SudoMsg::Jailing` should be called every time there's a validator set update that implies + /// slashing. + /// - Temporary removal of a validator from the active set due to jailing. + /// - Permanent removal (i.e. tombstoning) of a validator from the active set. + #[sv::msg(sudo)] + fn jailing( + &self, + ctx: SudoCtx, + jailed: Option>, + tombstoned: Option>, + ) -> Result; } #[cw_serde] @@ -68,7 +80,7 @@ impl LocalStakingApiHelper { // amount to stake on that contract funds: Vec, ) -> Result { - let msg = LocalStakingApiExecMsg::ReceiveStake { owner, msg }; + let msg = sv::LocalStakingApiExecMsg::ReceiveStake { owner, msg }; let wasm = WasmMsg::Execute { contract_addr: self.0.to_string(), msg: to_json_binary(&msg)?, @@ -83,7 +95,7 @@ impl LocalStakingApiHelper { amount: Coin, validator: Option, ) -> Result { - let msg = LocalStakingApiExecMsg::BurnStake { + let msg = sv::LocalStakingApiExecMsg::BurnStake { owner: owner.to_string(), validator, amount, @@ -97,19 +109,7 @@ impl LocalStakingApiHelper { } pub fn max_slash(&self, deps: Deps) -> Result { - let query = LocalStakingApiQueryMsg::MaxSlash {}; + let query = sv::LocalStakingApiQueryMsg::MaxSlash {}; deps.querier.query_wasm_smart(&self.0, &query) } } - -#[cw_serde] -pub enum SudoMsg { - /// `SudoMsg::Jailing` should be called every time there's a validator set update that implies - /// slashing. - /// - Temporary removal of a validator from the active set due to jailing. - /// - Permanent removal (i.e. tombstoning) of a validator from the active set. - Jailing { - jailed: Option>, - tombstoned: Option>, - }, -} diff --git a/packages/apis/src/price_feed_api.rs b/packages/apis/src/price_feed_api.rs index dbed0af1..49723bf1 100644 --- a/packages/apis/src/price_feed_api.rs +++ b/packages/apis/src/price_feed_api.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Decimal, StdError}; -use sylvia::types::QueryCtx; +use cosmwasm_std::{Decimal, Response, StdError}; +use sylvia::types::{QueryCtx, SudoCtx}; use sylvia::{interface, schemars}; /// This is a common interface to any price feed provider. @@ -15,16 +15,14 @@ pub trait PriceFeedApi { /// Return the price of the foreign token. That is, how many native tokens /// are needed to buy one foreign token. - #[msg(query)] + #[sv::msg(query)] fn price(&self, ctx: QueryCtx) -> Result; + + #[sv::msg(sudo)] + fn handle_epoch(&self, ctx: SudoCtx) -> Result; } #[cw_serde] pub struct PriceResponse { pub native_per_foreign: Decimal, } - -#[cw_serde] -pub enum SudoMsg { - HandleEpoch {}, -} diff --git a/packages/apis/src/vault_api.rs b/packages/apis/src/vault_api.rs index 56d25686..9d051627 100644 --- a/packages/apis/src/vault_api.rs +++ b/packages/apis/src/vault_api.rs @@ -10,7 +10,7 @@ pub trait VaultApi { type Error: From; /// This must be called by the remote staking contract to release this claim - #[msg(exec)] + #[sv::msg(exec)] fn release_cross_stake( &self, ctx: ExecCtx, @@ -22,7 +22,7 @@ pub trait VaultApi { /// This must be called by the local staking contract to release this claim /// Amount of tokens unstaked are those included in ctx.info.funds - #[msg(exec)] + #[sv::msg(exec)] fn release_local_stake( &self, ctx: ExecCtx, @@ -32,19 +32,19 @@ pub trait VaultApi { /// This must be called by the remote staking contract to commit the remote staking call on success. /// Transaction ID is used to identify the original (vault contract originated) transaction. - #[msg(exec)] + #[sv::msg(exec)] fn commit_tx(&self, ctx: ExecCtx, tx_id: u64) -> Result; /// This must be called by the remote staking contract to rollback the remote staking call on failure. /// Transaction ID is used to identify the original (vault contract originated) transaction. - #[msg(exec)] + #[sv::msg(exec)] fn rollback_tx(&self, ctx: ExecCtx, tx_id: u64) -> Result; /// This must be called by the native staking contract to process a slashing event /// because of a misbehaviour on the Provider chain. /// `validator` is the misbehaving validator address. Used during slashing propagation to /// preferentially burn stakes from this validator. - #[msg(exec)] + #[sv::msg(exec)] fn local_slash( &self, ctx: ExecCtx, @@ -56,7 +56,7 @@ pub trait VaultApi { /// because of a misbehaviour on the Consumer chain. /// `validator` is the misbehaving validator address. Used during slashing propagation to /// preferentially burn stakes from this validator. - #[msg(exec)] + #[sv::msg(exec)] fn cross_slash( &self, ctx: ExecCtx, @@ -87,7 +87,7 @@ impl VaultApiHelper { amount: Coin, funds: Vec, ) -> Result { - let msg = VaultApiExecMsg::ReleaseCrossStake { owner, amount }; + let msg = sv::VaultApiExecMsg::ReleaseCrossStake { owner, amount }; let wasm = WasmMsg::Execute { contract_addr: self.0.to_string(), msg: to_json_binary(&msg)?, @@ -103,7 +103,7 @@ impl VaultApiHelper { // tokens to send along with this funds: Vec, ) -> Result { - let msg = VaultApiExecMsg::ReleaseLocalStake { owner }; + let msg = sv::VaultApiExecMsg::ReleaseLocalStake { owner }; let wasm = WasmMsg::Execute { contract_addr: self.0.to_string(), msg: to_json_binary(&msg)?, @@ -117,7 +117,7 @@ impl VaultApiHelper { slashes: Vec, slashed_validator: &str, ) -> Result { - let msg = VaultApiExecMsg::LocalSlash { + let msg = sv::VaultApiExecMsg::LocalSlash { slashes, validator: slashed_validator.to_string(), }; @@ -134,7 +134,7 @@ impl VaultApiHelper { slashes: Vec, slashed_validator: &str, ) -> Result { - let msg = VaultApiExecMsg::CrossSlash { + let msg = sv::VaultApiExecMsg::CrossSlash { slashes, validator: slashed_validator.to_string(), }; @@ -147,7 +147,7 @@ impl VaultApiHelper { } pub fn commit_tx(&self, tx_id: u64) -> Result { - let msg = VaultApiExecMsg::CommitTx { tx_id }; + let msg = sv::VaultApiExecMsg::CommitTx { tx_id }; let wasm = WasmMsg::Execute { contract_addr: self.0.to_string(), msg: to_json_binary(&msg)?, @@ -157,7 +157,7 @@ impl VaultApiHelper { } pub fn rollback_tx(&self, tx_id: u64) -> Result { - let msg = VaultApiExecMsg::RollbackTx { tx_id }; + let msg = sv::VaultApiExecMsg::RollbackTx { tx_id }; let wasm = WasmMsg::Execute { contract_addr: self.0.to_string(), msg: to_json_binary(&msg)?, diff --git a/packages/apis/src/virtual_staking_api.rs b/packages/apis/src/virtual_staking_api.rs index e997b86a..8e1b6bb9 100644 --- a/packages/apis/src/virtual_staking_api.rs +++ b/packages/apis/src/virtual_staking_api.rs @@ -13,13 +13,13 @@ pub trait VirtualStakingApi { /// Requests to bond tokens to a validator. This will be actually handled at the next epoch. /// If the virtual staking module is over the max cap, it will trigger a rebalance. /// If the max cap is 0, then this will immediately return an error. - #[msg(exec)] + #[sv::msg(exec)] fn bond(&self, ctx: ExecCtx, validator: String, amount: Coin) -> Result; /// Requests to unbond tokens from a validator. This will be actually handled at the next epoch. /// If the virtual staking module is over the max cap, it will trigger a rebalance in addition to unbond. /// If the virtual staking contract doesn't have at least amount tokens staked to the given validator, this will return an error. - #[msg(exec)] + #[sv::msg(exec)] fn unbond( &self, ctx: ExecCtx, @@ -30,7 +30,7 @@ pub trait VirtualStakingApi { /// Burns stake. This is called when the user's collateral is slashed and, as part of slashing /// propagation, the virtual staking contract needs to burn / discount the indicated slashing amount. /// Undelegates evenly from all `validators`. - #[msg(exec)] + #[sv::msg(exec)] fn burn( &self, ctx: ExecCtx, From 94171cc1c90de8fce67ff5df9ccde3e437616abf Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Mon, 29 Apr 2024 00:38:07 +0200 Subject: [PATCH 02/20] Attempt to fix up virtual staking, still doesn't compile --- .../consumer/virtual-staking/src/contract.rs | 355 +++++++++--------- .../consumer/virtual-staking/src/multitest.rs | 16 +- packages/apis/Cargo.toml | 14 +- packages/apis/src/virtual_staking_api.rs | 16 +- 4 files changed, 190 insertions(+), 211 deletions(-) diff --git a/contracts/consumer/virtual-staking/src/contract.rs b/contracts/consumer/virtual-staking/src/contract.rs index 7d4b029b..cd4a55ad 100644 --- a/contracts/consumer/virtual-staking/src/contract.rs +++ b/contracts/consumer/virtual-staking/src/contract.rs @@ -13,10 +13,10 @@ use mesh_apis::converter_api::{self, RewardInfo, ValidatorSlashInfo}; use mesh_bindings::{ TokenQuerier, VirtualStakeCustomMsg, VirtualStakeCustomQuery, VirtualStakeMsg, }; -use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx, ReplyCtx}; +use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx, ReplyCtx, SudoCtx}; use sylvia::{contract, schemars}; -use mesh_apis::virtual_staking_api::{self, SudoMsg, ValidatorSlash, VirtualStakingApi}; +use mesh_apis::virtual_staking_api::{self, ValidatorSlash, VirtualStakingApi}; use crate::error::ContractError; use crate::msg::ConfigResponse; @@ -53,8 +53,10 @@ pub struct VirtualStakingContract<'a> { #[cfg_attr(not(feature = "library"), sylvia::entry_points)] #[contract] -#[error(ContractError)] -#[messages(virtual_staking_api as VirtualStakingApi)] +#[sv::error(ContractError)] +#[sv::messages(virtual_staking_api as VirtualStakingApi)] +// FIXME: how to handle custom messages for sudo? +#[sv::custom(query=VirtualStakeCustomQuery, msg=VirtualStakeCustomMsg)] // #[sv::override_entry_point(sudo=sudo(SudoMsg))] // Disabled because lack of custom query support impl VirtualStakingContract<'_> { pub const fn new() -> Self { @@ -69,7 +71,7 @@ impl VirtualStakingContract<'_> { } /// The caller of the instantiation will be the converter contract - #[msg(instantiate)] + #[sv::msg(instantiate)] pub fn instantiate(&self, ctx: InstantiateCtx) -> Result { nonpayable(&ctx.info)?; let denom = ctx.deps.querier.query_bonded_denom()?; @@ -88,92 +90,11 @@ impl VirtualStakingContract<'_> { Ok(Response::new()) } - #[msg(query)] + #[sv::msg(query)] fn config(&self, ctx: QueryCtx) -> Result { Ok(self.config.load(ctx.deps.storage)?.into()) } - /** - * This is called once per epoch to withdraw all rewards and rebalance the bonded tokens. - * Note: the current implementation may (repeatedly) fail if any validator was slashed or fell out - * of the active set. - * - * The basic logic for calculating rebalance is: - * 1. Get all bond requests - * 2. Sum the total amount - * 3. If the sum <= max_cap then use collected requests as is - * 4. If the sum > max_cap, - * a. calculate multiplier Decimal(max_cap / sum) - * b. multiply every element of the collected requests in place. - * 5. Find diff between collected (normalized) requests and last bonding amounts (which go up, which down). - * 6. Transform diff into unbond and bond requests, sorting so all unbond happen first - */ - fn handle_epoch( - &self, - mut deps: DepsMut, - env: Env, - ) -> Result, ContractError> { - // withdraw rewards - let bonded = self.bonded.load(deps.storage)?; - let inactive = self.inactive.load(deps.storage)?; - let withdraw = withdraw_reward_msgs(deps.branch(), &bonded, &inactive); - let mut resp = Response::new().add_submessages(withdraw); - - let bond = - TokenQuerier::new(&deps.querier).bond_status(env.contract.address.to_string())?; - let max_cap = bond.cap.amount; - // If 0 max cap, then we assume all tokens were force unbonded already, and just return the withdraw rewards - // call and set bonded to empty - // TODO: verify this behavior with SDK module (otherwise we send unbond message) - if max_cap.is_zero() { - self.bonded.save(deps.storage, &vec![])?; - return Ok(resp); - } - - let config = self.config.load(deps.storage)?; - // Make current bonded mutable - let mut current = bonded; - // Process slashes due to tombstoning (unbonded) or jailing, over bond_requests and current - let slash = self.slash_requests.load(deps.storage)?; - if !slash.is_empty() { - self.adjust_slashings(deps.branch(), &mut current, &slash)?; - // Update inactive list. Defensive, as it should already been updated in handle_valset_update, due to removals - self.inactive.update(deps.branch().storage, |mut old| { - old.extend_from_slice(&slash.iter().map(|v| v.address.clone()).collect::>()); - old.dedup(); - Ok::<_, ContractError>(old) - })?; - // Clear up slash requests - self.slash_requests.save(deps.storage, &vec![])?; - } - - // calculate what the delegations should be when we are done - let mut requests: Vec<(String, Uint128)> = self - .bond_requests - .range( - deps.as_ref().storage, - None, - None, - cosmwasm_std::Order::Ascending, - ) - .collect::>()?; - let total_requested: Uint128 = requests.iter().map(|(_, v)| v).sum(); - if total_requested > max_cap { - for (_, v) in requests.iter_mut() { - *v = (*v * max_cap) / total_requested; - } - } - - // Save the future values - self.bonded.save(deps.branch().storage, &requests)?; - - // Compare these two to make bond/unbond calls as needed - let rebalance = calculate_rebalance(current, requests, &config.denom); - resp = resp.add_messages(rebalance); - - Ok(resp) - } - fn adjust_slashings( &self, deps: DepsMut, @@ -203,73 +124,7 @@ impl VirtualStakingContract<'_> { Ok(()) } - /** - * This is called every time there's a change of the active validator set. - * - */ - #[allow(clippy::too_many_arguments)] - fn handle_valset_update( - &self, - deps: DepsMut, - additions: &[Validator], - removals: &[String], - updated: &[Validator], - jailed: &[String], - unjailed: &[String], - tombstoned: &[String], - slashed: &[ValidatorSlash], - ) -> Result, ContractError> { - // Account for slashed validators. Will be processed in handle_epoch - if !slashed.is_empty() { - self.slash_requests.update(deps.storage, |mut old| { - old.extend_from_slice(slashed); - Ok::<_, ContractError>(old) - })?; - } - - // Update inactive list. - // We ignore `unjailed` as it's not clear they make the validator active again or not. - if !removals.is_empty() || !additions.is_empty() { - self.inactive.update(deps.storage, |mut old| { - // Add removals - old.extend_from_slice(removals); - // Filter additions - old.retain(|v| !additions.iter().any(|a| a.address == *v)); - old.dedup(); - Ok::<_, ContractError>(old) - })?; - } - // Send all updates to the converter. - let cfg = self.config.load(deps.storage)?; - let msg = converter_api::ExecMsg::ValsetUpdate { - additions: additions.to_vec(), - removals: removals.to_vec(), - updated: updated.to_vec(), - jailed: jailed.to_vec(), - unjailed: unjailed.to_vec(), - tombstoned: tombstoned.to_vec(), - slashed: slashed - .iter() - .map(|s| ValidatorSlashInfo { - address: s.address.clone(), - infraction_height: s.infraction_height, - infraction_time: s.infraction_time, - power: s.power, - slash_amount: coin(s.slash_amount.u128(), cfg.denom.clone()), - slash_ratio: s.slash_ratio.clone(), - }) - .collect(), - }; - let msg = WasmMsg::Execute { - contract_addr: cfg.converter.to_string(), - msg: to_json_binary(&msg)?, - funds: vec![], - }; - let resp = Response::new().add_message(msg); - Ok(resp) - } - - #[msg(reply)] + #[sv::msg(reply)] fn reply(&self, ctx: ReplyCtx, reply: Reply) -> Result { match (reply.id, reply.result.into_result()) { (REPLY_REWARDS_ID, Ok(_)) => self.reply_rewards(ctx.deps, ctx.env), @@ -333,7 +188,7 @@ impl VirtualStakingContract<'_> { let all_rewards = all_rewards(deps.storage)?; BATCH.wipe(deps.storage)?; - let msg = converter_api::ExecMsg::DistributeRewards { + let msg = converter_api::sv::ExecMsg::DistributeRewards { payments: all_rewards, }; let msg = WasmMsg::Execute { @@ -481,15 +336,12 @@ fn withdraw_reward_msgs( .collect() } -#[contract] -#[messages(virtual_staking_api as VirtualStakingApi)] impl VirtualStakingApi for VirtualStakingContract<'_> { type Error = ContractError; /// Requests to bond tokens to a validator. This will be actually handled at the next epoch. /// If the virtual staking module is over the max cap, it will trigger a rebalance. /// If the max cap is 0, then this will immediately return an error. - #[msg(exec)] fn bond(&self, ctx: ExecCtx, validator: String, amount: Coin) -> Result { nonpayable(&ctx.info)?; let cfg = self.config.load(ctx.deps.storage)?; @@ -515,7 +367,6 @@ impl VirtualStakingApi for VirtualStakingContract<'_> { /// Requests to unbond tokens from a validator. This will be actually handled at the next epoch. /// If the virtual staking module is over the max cap, it will trigger a rebalance in addition to unbond. /// If the virtual staking contract doesn't have at least amount tokens staked to the given validator, this will return an error. - #[msg(exec)] fn unbond( &self, ctx: ExecCtx, @@ -545,7 +396,6 @@ impl VirtualStakingApi for VirtualStakingContract<'_> { /// Requests to unbond and burn tokens from a list of validators. Unbonding will be actually handled at the next epoch. /// If the virtual staking module is over the max cap, it will trigger a rebalance in addition to unbond. /// If the virtual staking contract doesn't have at least amount tokens staked over the given validators, this will return an error. - #[msg(exec)] fn burn( &self, ctx: ExecCtx, @@ -604,34 +454,165 @@ impl VirtualStakingApi for VirtualStakingContract<'_> { Ok(Response::new()) } -} -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn sudo( - deps: DepsMut, - env: Env, - msg: SudoMsg, -) -> Result, ContractError> { - match msg { - SudoMsg::HandleEpoch {} => VirtualStakingContract::new().handle_epoch(deps, env), - SudoMsg::ValsetUpdate { - additions, - removals, - updated, - jailed, - unjailed, - tombstoned, - slashed, - } => VirtualStakingContract::new().handle_valset_update( - deps, - &additions.unwrap_or_default(), - &removals.unwrap_or_default(), - &updated.unwrap_or_default(), - &jailed.unwrap_or_default(), - &unjailed.unwrap_or_default(), - &tombstoned.unwrap_or_default(), - &slashed.unwrap_or_default(), - ), + // FIXME: need to handle custom message types and queries + /** + * This is called once per epoch to withdraw all rewards and rebalance the bonded tokens. + * Note: the current implementation may (repeatedly) fail if any validator was slashed or fell out + * of the active set. + * + * The basic logic for calculating rebalance is: + * 1. Get all bond requests + * 2. Sum the total amount + * 3. If the sum <= max_cap then use collected requests as is + * 4. If the sum > max_cap, + * a. calculate multiplier Decimal(max_cap / sum) + * b. multiply every element of the collected requests in place. + * 5. Find diff between collected (normalized) requests and last bonding amounts (which go up, which down). + * 6. Transform diff into unbond and bond requests, sorting so all unbond happen first + */ + fn handle_epoch( + &self, + ctx: SudoCtx, + ) -> Result, ContractError> { + let SudoCtx { deps, env, .. } = ctx; + + // withdraw rewards + let bonded = self.bonded.load(deps.storage)?; + let inactive = self.inactive.load(deps.storage)?; + let withdraw = withdraw_reward_msgs(deps.branch(), &bonded, &inactive); + let mut resp = Response::new().add_submessages(withdraw); + + let bond = + TokenQuerier::new(&deps.querier).bond_status(env.contract.address.to_string())?; + let max_cap = bond.cap.amount; + // If 0 max cap, then we assume all tokens were force unbonded already, and just return the withdraw rewards + // call and set bonded to empty + // TODO: verify this behavior with SDK module (otherwise we send unbond message) + if max_cap.is_zero() { + self.bonded.save(deps.storage, &vec![])?; + return Ok(resp); + } + + let config = self.config.load(deps.storage)?; + // Make current bonded mutable + let mut current = bonded; + // Process slashes due to tombstoning (unbonded) or jailing, over bond_requests and current + let slash = self.slash_requests.load(deps.storage)?; + if !slash.is_empty() { + self.adjust_slashings(deps.branch(), &mut current, &slash)?; + // Update inactive list. Defensive, as it should already been updated in handle_valset_update, due to removals + self.inactive.update(deps.branch().storage, |mut old| { + old.extend_from_slice(&slash.iter().map(|v| v.address.clone()).collect::>()); + old.dedup(); + Ok::<_, ContractError>(old) + })?; + // Clear up slash requests + self.slash_requests.save(deps.storage, &vec![])?; + } + + // calculate what the delegations should be when we are done + let mut requests: Vec<(String, Uint128)> = self + .bond_requests + .range( + deps.as_ref().storage, + None, + None, + cosmwasm_std::Order::Ascending, + ) + .collect::>()?; + let total_requested: Uint128 = requests.iter().map(|(_, v)| v).sum(); + if total_requested > max_cap { + for (_, v) in requests.iter_mut() { + *v = (*v * max_cap) / total_requested; + } + } + + // Save the future values + self.bonded.save(deps.branch().storage, &requests)?; + + // Compare these two to make bond/unbond calls as needed + let rebalance = calculate_rebalance(current, requests, &config.denom); + resp = resp.add_messages(rebalance); + + Ok(resp) + } + + // FIXME: need to handle custom message types and queries + /** + * This is called every time there's a change of the active validator set. + * + */ + #[allow(clippy::too_many_arguments)] + fn handle_valset_update( + &self, + ctx: SudoCtx, + additions: Option>, + removals: Option>, + updated: Option>, + jailed: Option>, + unjailed: Option>, + tombstoned: Option>, + slashed: Option>, + ) -> Result, ContractError> { + let SudoCtx { deps, .. } = ctx; + + let additions = &additions.unwrap_or_default(); + let removals = &removals.unwrap_or_default(); + let updated = &updated.unwrap_or_default(); + let jailed = &jailed.unwrap_or_default(); + let unjailed = &unjailed.unwrap_or_default(); + let tombstoned = &tombstoned.unwrap_or_default(); + let slashed = &slashed.unwrap_or_default(); + + // Account for slashed validators. Will be processed in handle_epoch + if !slashed.is_empty() { + self.slash_requests.update(deps.storage, |mut old| { + old.extend_from_slice(slashed); + Ok::<_, ContractError>(old) + })?; + } + + // Update inactive list. + // We ignore `unjailed` as it's not clear they make the validator active again or not. + if !removals.is_empty() || !additions.is_empty() { + self.inactive.update(deps.storage, |mut old| { + // Add removals + old.extend_from_slice(removals); + // Filter additions + old.retain(|v| !additions.iter().any(|a| a.address == *v)); + old.dedup(); + Ok::<_, ContractError>(old) + })?; + } + // Send all updates to the converter. + let cfg = self.config.load(deps.storage)?; + let msg = converter_api::sv::ExecMsg::ValsetUpdate { + additions: additions.to_vec(), + removals: removals.to_vec(), + updated: updated.to_vec(), + jailed: jailed.to_vec(), + unjailed: unjailed.to_vec(), + tombstoned: tombstoned.to_vec(), + slashed: slashed + .iter() + .map(|s| ValidatorSlashInfo { + address: s.address.clone(), + infraction_height: s.infraction_height, + infraction_time: s.infraction_time, + power: s.power, + slash_amount: coin(s.slash_amount.u128(), cfg.denom.clone()), + slash_ratio: s.slash_ratio.clone(), + }) + .collect(), + }; + let msg = WasmMsg::Execute { + contract_addr: cfg.converter.to_string(), + msg: to_json_binary(&msg)?, + funds: vec![], + }; + let resp = Response::new().add_message(msg); + Ok(resp) } } diff --git a/contracts/consumer/virtual-staking/src/multitest.rs b/contracts/consumer/virtual-staking/src/multitest.rs index fed459b1..dde1db81 100644 --- a/contracts/consumer/virtual-staking/src/multitest.rs +++ b/contracts/consumer/virtual-staking/src/multitest.rs @@ -15,10 +15,9 @@ struct SetupArgs<'a> { } struct SetupResponse<'a> { - price_feed: - mesh_simple_price_feed::contract::multitest_utils::SimplePriceFeedContractProxy<'a, MtApp>, - converter: mesh_converter::contract::multitest_utils::ConverterContractProxy<'a, MtApp>, - virtual_staking: contract::multitest_utils::VirtualStakingContractProxy<'a, MtApp>, + price_feed: mesh_simple_price_feed::contract::sv::mt::SimplePriceFeedContractProxy<'a, MtApp>, + converter: mesh_converter::contract::sv::mt::ConverterContractProxy<'a, MtApp>, + virtual_staking: contract::sv::mt::VirtualStakingContractProxy<'a, MtApp>, } fn setup<'a>(app: &'a App, args: SetupArgs<'a>) -> SetupResponse<'a> { @@ -29,10 +28,9 @@ fn setup<'a>(app: &'a App, args: SetupArgs<'a>) -> SetupResponse<'a> { native_per_foreign, } = args; - let price_feed_code = - mesh_simple_price_feed::contract::multitest_utils::CodeId::store_code(app); - let virtual_staking_code = contract::multitest_utils::CodeId::store_code(app); - let converter_code = mesh_converter::contract::multitest_utils::CodeId::store_code(app); + let price_feed_code = mesh_simple_price_feed::contract::sv::mt::CodeId::store_code(app); + let virtual_staking_code = contract::sv::mt::CodeId::store_code(app); + let converter_code = mesh_converter::contract::sv::mt::CodeId::store_code(app); let price_feed = price_feed_code .instantiate(native_per_foreign, None) @@ -56,7 +54,7 @@ fn setup<'a>(app: &'a App, args: SetupArgs<'a>) -> SetupResponse<'a> { let config = converter.config().unwrap(); let virtual_staking_addr = Addr::unchecked(config.virtual_staking); let virtual_staking = - contract::multitest_utils::VirtualStakingContractProxy::new(virtual_staking_addr, app); + contract::sv::mt::VirtualStakingContractProxy::new(virtual_staking_addr, app); SetupResponse { price_feed, diff --git a/packages/apis/Cargo.toml b/packages/apis/Cargo.toml index 470587c3..1eb63b19 100644 --- a/packages/apis/Cargo.toml +++ b/packages/apis/Cargo.toml @@ -1,19 +1,17 @@ [package] -name = "mesh-apis" +name = "mesh-apis" version = { workspace = true } edition = { workspace = true } -license = { workspace = true } +license = { workspace = true } [features] mt = ["sylvia/mt"] [dependencies] -sylvia = { workspace = true } cosmwasm-std = { workspace = true } cosmwasm-schema = { workspace = true } - -thiserror = { workspace = true } +schemars = { workspace = true } +semver = { workspace = true } serde = { workspace = true } - -semver = { workspace = true } -schemars = { workspace = true } \ No newline at end of file +sylvia = { workspace = true } +thiserror = { workspace = true } diff --git a/packages/apis/src/virtual_staking_api.rs b/packages/apis/src/virtual_staking_api.rs index 8e1b6bb9..77d6ac7d 100644 --- a/packages/apis/src/virtual_staking_api.rs +++ b/packages/apis/src/virtual_staking_api.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{Coin, Response, StdError, Uint128, Validator}; -use sylvia::types::ExecCtx; +use sylvia::types::{ExecCtx, SudoCtx}; use sylvia::{interface, schemars}; /// The Virtual Staking API is called from the converter contract to bond and (instantly) unbond tokens. @@ -37,16 +37,15 @@ pub trait VirtualStakingApi { validators: Vec, amount: Coin, ) -> Result; -} -#[cw_serde] -pub enum SudoMsg { /// SudoMsg::HandleEpoch{} should be called once per epoch by the sdk (in EndBlock). /// It allows the virtual staking contract to bond or unbond any pending requests, as well /// as to perform a rebalance if needed (over the max cap). /// /// It should also withdraw all pending rewards here, and send them to the converter contract. - HandleEpoch {}, + #[sv::msg(sudo)] + fn handle_epoch(&self, ctx: SudoCtx) -> Result; + /// SudoMsg::ValsetUpdate{} should be called every time there's a validator set update: /// - Addition of a new validator to the active validator set. /// - Temporary removal of a validator from the active set. (i.e. `unbonded` state). @@ -54,7 +53,10 @@ pub enum SudoMsg { /// - Temporary removal of a validator from the active set due to jailing. Implies slashing. /// - Addition of an existing validator to the active validator set. /// - Permanent removal (i.e. tombstoning) of a validator from the active set. Implies slashing - ValsetUpdate { + #[sv::msg(sudo)] + fn handle_valset_update( + &self, + ctx: SudoCtx, additions: Option>, removals: Option>, updated: Option>, @@ -62,7 +64,7 @@ pub enum SudoMsg { unjailed: Option>, tombstoned: Option>, slashed: Option>, - }, + ) -> Result; } #[cw_serde] From 71dffdf517508341dfb89b5d1129842af6dc01c0 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 6 May 2024 11:08:33 +0200 Subject: [PATCH 03/20] Got consumer/converter to compile (but not tests) --- contracts/consumer/converter/src/bin/schema.rs | 2 +- contracts/consumer/converter/src/contract.rs | 17 +++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/contracts/consumer/converter/src/bin/schema.rs b/contracts/consumer/converter/src/bin/schema.rs index 09fccb17..ac23f122 100644 --- a/contracts/consumer/converter/src/bin/schema.rs +++ b/contracts/consumer/converter/src/bin/schema.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::write_api; -use mesh_converter::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; +use mesh_converter::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; #[cfg(not(tarpaulin_include))] fn main() { diff --git a/contracts/consumer/converter/src/contract.rs b/contracts/consumer/converter/src/contract.rs index db32327a..294adf4a 100644 --- a/contracts/consumer/converter/src/contract.rs +++ b/contracts/consumer/converter/src/contract.rs @@ -1,6 +1,5 @@ use cosmwasm_std::{ - ensure_eq, to_json_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, Deps, DepsMut, Event, - Fraction, MessageInfo, Reply, Response, SubMsg, SubMsgResponse, Uint128, Validator, WasmMsg, + ensure_eq, to_json_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, Deps, DepsMut, Event, Fraction, MessageInfo, Reply, Response, StdError, SubMsg, SubMsgResponse, Uint128, Validator, WasmMsg }; use cw2::set_contract_version; use cw_storage_plus::Item; @@ -275,8 +274,11 @@ impl ConverterContract<'_> { // FIXME not sure how to get this to compile with latest sylvia // get the price value (usage is a bit clunky, need to use trait and cannot chain Remote::new() with .querier()) // also see https://github.com/CosmWasm/sylvia/issues/181 to just store Remote in state - use price_feed_api::Querier; - let remote = price_feed_api::Remote::new(config.price_feed); + use price_feed_api::sv::Querier; + use sylvia::types::Remote; + // NOTE: Jan, this feels hacky... I'm not sure if this is the right way to do it + // Also, I am sticking in a random error type here, not what I will get (which is unknown) + let remote = Remote::<&dyn price_feed_api::PriceFeedApi>::new(config.price_feed); let price = remote.querier(&deps.querier).price()?.native_per_foreign; let converted = (amount.amount * price) * config.price_adjustment; @@ -299,8 +301,11 @@ impl ConverterContract<'_> { // get the price value (usage is a bit clunky, need to use trait and cannot chain Remote::new() with .querier()) // also see https://github.com/CosmWasm/sylvia/issues/181 to just store Remote in state - use price_feed_api::Querier; - let remote = price_feed_api::Remote::new(config.price_feed); + use price_feed_api::sv::Querier; + use sylvia::types::Remote; + // NOTE: Jan, this feels hacky... I'm not sure if this is the right way to do it + // Also, I am sticking in a random error type here, not what I will get (which is unknown) + let remote = Remote::<&dyn price_feed_api::PriceFeedApi>::new(config.price_feed); let price = remote.querier(&deps.querier).price()?.native_per_foreign; let converted = (amount.amount * price.inv().ok_or(ContractError::InvalidPrice {})?) * config From 437fcc4348a43cf41f4c815bf238824696019d66 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 6 May 2024 11:17:36 +0200 Subject: [PATCH 04/20] Remove codecov as we only get errors (seems it needs a paid API key?) --- .github/workflows/coverage.yml | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 .github/workflows/coverage.yml diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml deleted file mode 100644 index 19cbb145..00000000 --- a/.github/workflows/coverage.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Coverage - -on: [pull_request, push] - -jobs: - coverage: - runs-on: ubuntu-latest - env: - CARGO_TERM_COLOR: always - steps: - - uses: actions/checkout@v4 - - name: Install cargo-llvm-cov - uses: taiki-e/install-action@cargo-llvm-cov - - name: Generate code coverage - run: cargo llvm-cov --workspace --codecov --output-path codecov.json - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 - with: - # token: ${{ secrets.CODECOV_TOKEN }} # for private repos - files: codecov.json - fail_ci_if_error: true From 528c6e1bbf3ae7b2c206a619f8ad6365cfcd6baa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Mon, 6 May 2024 11:48:22 +0200 Subject: [PATCH 05/20] Use `sylvia::types::Proxy` in consumer converter tests --- contracts/consumer/converter/src/multitest.rs | 46 +++++++++---------- .../src/multitest/virtual_staking_mock.rs | 43 +++++++++++++---- 2 files changed, 56 insertions(+), 33 deletions(-) diff --git a/contracts/consumer/converter/src/multitest.rs b/contracts/consumer/converter/src/multitest.rs index 84b723e0..883b38c9 100644 --- a/contracts/consumer/converter/src/multitest.rs +++ b/contracts/consumer/converter/src/multitest.rs @@ -2,13 +2,20 @@ mod virtual_staking_mock; use cosmwasm_std::{coin, coins, Addr, Decimal, StdError, Uint128, Validator}; use cw_multi_test::App as MtApp; +use mesh_apis::converter_api::sv::mt::ConverterApiProxy; use mesh_apis::converter_api::RewardInfo; -use sylvia::multitest::App; - -use crate::contract; -use crate::contract::test_utils::ConverterApi; +use mesh_simple_price_feed::contract::sv::mt::CodeId as PriceFeedCodeId; +use mesh_simple_price_feed::contract::SimplePriceFeedContract; +use sylvia::multitest::{App, Proxy}; +use virtual_staking_mock::sv::mt::CodeId as VirtualStakingCodeId; +use virtual_staking_mock::VirtualStakingMock; + +use crate::contract::sv::mt::CodeId as ConverterCodeId; +use crate::contract::sv::mt::ConverterContractProxy; +use crate::contract::ConverterContract; use crate::error::ContractError; use crate::error::ContractError::Unauthorized; +use crate::multitest::virtual_staking_mock::sv::mt::VirtualStakingMockProxy; const JUNO: &str = "ujuno"; @@ -20,10 +27,9 @@ struct SetupArgs<'a> { } struct SetupResponse<'a> { - price_feed: - mesh_simple_price_feed::contract::multitest_utils::SimplePriceFeedContractProxy<'a, MtApp>, - converter: contract::multitest_utils::ConverterContractProxy<'a, MtApp>, - virtual_staking: virtual_staking_mock::multitest_utils::VirtualStakingMockProxy<'a, MtApp>, + price_feed: Proxy<'a, MtApp, SimplePriceFeedContract<'a>>, + converter: Proxy<'a, MtApp, ConverterContract<'a>>, + virtual_staking: Proxy<'a, MtApp, VirtualStakingMock<'a>>, } fn setup<'a>(app: &'a App, args: SetupArgs<'a>) -> SetupResponse<'a> { @@ -34,10 +40,9 @@ fn setup<'a>(app: &'a App, args: SetupArgs<'a>) -> SetupResponse<'a> { native_per_foreign, } = args; - let price_feed_code = - mesh_simple_price_feed::contract::multitest_utils::CodeId::store_code(app); - let virtual_staking_code = virtual_staking_mock::multitest_utils::CodeId::store_code(app); - let converter_code = contract::multitest_utils::CodeId::store_code(app); + let price_feed_code = PriceFeedCodeId::store_code(app); + let virtual_staking_code = VirtualStakingCodeId::store_code(app); + let converter_code = ConverterCodeId::store_code(app); let price_feed = price_feed_code .instantiate(native_per_foreign, None) @@ -60,10 +65,11 @@ fn setup<'a>(app: &'a App, args: SetupArgs<'a>) -> SetupResponse<'a> { let config = converter.config().unwrap(); let virtual_staking_addr = Addr::unchecked(config.virtual_staking); - let virtual_staking = virtual_staking_mock::multitest_utils::VirtualStakingMockProxy::new( - virtual_staking_addr, - app, - ); + // Ideally this should be initialized via `CodeId`. + // Consider bellow approach + // + // let virtual_staking = virtual_staking_code.instantiate().call(owner).unwrap(); + let virtual_staking = Proxy::new(virtual_staking_addr, app); SetupResponse { price_feed, @@ -328,13 +334,11 @@ fn valset_update_works() { // Check that only the virtual staking contract can call this handler let res = converter - .converter_api_proxy() .valset_update(vec![], vec![], vec![], vec![], vec![], vec![], vec![]) .call(owner); assert_eq!(res.unwrap_err(), Unauthorized {}); let res = converter - .converter_api_proxy() .valset_update( add_validators, rem_validators, @@ -374,7 +378,6 @@ fn unauthorized() { ); let err = converter - .converter_api_proxy() .distribute_rewards(vec![ RewardInfo { validator: "alice".to_string(), @@ -391,7 +394,6 @@ fn unauthorized() { assert_eq!(err, ContractError::Unauthorized); let err = converter - .converter_api_proxy() .distribute_reward("validator".to_string()) .call("mallory") .unwrap_err(); @@ -399,7 +401,6 @@ fn unauthorized() { assert_eq!(err, ContractError::Unauthorized); let err = converter - .converter_api_proxy() .valset_update(vec![], vec![], vec![], vec![], vec![], vec![], vec![]) .call("mallory") .unwrap_err(); @@ -442,7 +443,6 @@ fn distribute_rewards_invalid_amount_is_rejected() { }); let err = converter - .converter_api_proxy() .distribute_rewards(vec![ RewardInfo { validator: "alice".to_string(), @@ -466,7 +466,6 @@ fn distribute_rewards_invalid_amount_is_rejected() { ); let err = converter - .converter_api_proxy() .distribute_rewards(vec![ RewardInfo { validator: "alice".to_string(), @@ -526,7 +525,6 @@ fn distribute_rewards_valid_amount() { }); converter - .converter_api_proxy() .distribute_rewards(vec![ RewardInfo { validator: "alice".to_string(), diff --git a/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs b/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs index b1155da9..0be92e0f 100644 --- a/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs +++ b/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs @@ -1,11 +1,11 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{ensure_eq, Addr, Coin, Response, StdError, StdResult, Uint128}; +use cosmwasm_std::{ensure_eq, Addr, Coin, Response, StdError, StdResult, Uint128, Validator}; use cw_storage_plus::{Item, Map}; use cw_utils::{nonpayable, PaymentError}; -use mesh_apis::virtual_staking_api::{self, VirtualStakingApi}; +use mesh_apis::virtual_staking_api::{self, ValidatorSlash, VirtualStakingApi}; use sylvia::contract; -use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx}; +use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx, SudoCtx}; #[cw_serde] pub struct Config { @@ -41,7 +41,7 @@ pub struct VirtualStakingMock<'a> { } #[contract] -#[error(ContractError)] +#[sv::error(ContractError)] #[sv::messages(virtual_staking_api as VirtualStakingApi)] impl VirtualStakingMock<'_> { pub const fn new() -> Self { @@ -106,15 +106,12 @@ pub struct ConfigResponse { pub converter: String, } -#[contract] -#[sv::messages(virtual_staking_api as VirtualStakingApi)] impl VirtualStakingApi for VirtualStakingMock<'_> { type Error = ContractError; /// Requests to bond tokens to a validator. This will be actually handled at the next epoch. /// If the virtual staking module is over the max cap, it will trigger a rebalance. /// If the max cap is 0, then this will immediately return an error. - #[sv::msg(exec)] fn bond(&self, ctx: ExecCtx, validator: String, amount: Coin) -> Result { nonpayable(&ctx.info)?; let cfg = self.config.load(ctx.deps.storage)?; @@ -137,7 +134,6 @@ impl VirtualStakingApi for VirtualStakingMock<'_> { /// Requests to unbond tokens from a validator. This will be actually handled at the next epoch. /// If the virtual staking module is over the max cap, it will trigger a rebalance in addition to unbond. /// If the virtual staking contract doesn't have at least amount tokens staked to the given validator, this will return an error. - #[sv::msg(exec)] fn unbond( &self, ctx: ExecCtx, @@ -165,7 +161,6 @@ impl VirtualStakingApi for VirtualStakingMock<'_> { /// Requests to unbond and burn tokens from a lists of validators (one or more). This will be actually handled at the next epoch. /// If the virtual staking module is over the max cap, it will trigger a rebalance in addition to unbond. /// If the virtual staking contract doesn't have at least amount tokens staked over the given validators, this will return an error. - #[sv::msg(exec)] fn burn( &self, ctx: ExecCtx, @@ -222,4 +217,34 @@ impl VirtualStakingApi for VirtualStakingMock<'_> { Ok(Response::new()) } + + /// SudoMsg::HandleEpoch{} should be called once per epoch by the sdk (in EndBlock). + /// It allows the virtual staking contract to bond or unbond any pending requests, as well + /// as to perform a rebalance if needed (over the max cap). + /// + /// It should also withdraw all pending rewards here, and send them to the converter contract. + fn handle_epoch(&self, ctx: SudoCtx) -> Result { + unimplemented!() + } + + /// SudoMsg::ValsetUpdate{} should be called every time there's a validator set update: + /// - Addition of a new validator to the active validator set. + /// - Temporary removal of a validator from the active set. (i.e. `unbonded` state). + /// - Update of validator data. + /// - Temporary removal of a validator from the active set due to jailing. Implies slashing. + /// - Addition of an existing validator to the active validator set. + /// - Permanent removal (i.e. tombstoning) of a validator from the active set. Implies slashing + fn handle_valset_update( + &self, + ctx: SudoCtx, + additions: Option>, + removals: Option>, + updated: Option>, + jailed: Option>, + unjailed: Option>, + tombstoned: Option>, + slashed: Option>, + ) -> Result { + unimplemented!() + } } From a34baf21538b893e196dd67bd4beed30adc3e3f1 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 9 May 2024 12:02:45 +0200 Subject: [PATCH 06/20] Clippy also happy for converter contract --- .../src/multitest/virtual_staking_mock.rs | 18 +++++++++--------- .../consumer/remote-price-feed/src/contract.rs | 4 ++-- packages/apis/src/virtual_staking_api.rs | 1 + 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs b/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs index 0be92e0f..d2555b86 100644 --- a/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs +++ b/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs @@ -223,7 +223,7 @@ impl VirtualStakingApi for VirtualStakingMock<'_> { /// as to perform a rebalance if needed (over the max cap). /// /// It should also withdraw all pending rewards here, and send them to the converter contract. - fn handle_epoch(&self, ctx: SudoCtx) -> Result { + fn handle_epoch(&self, _ctx: SudoCtx) -> Result { unimplemented!() } @@ -236,14 +236,14 @@ impl VirtualStakingApi for VirtualStakingMock<'_> { /// - Permanent removal (i.e. tombstoning) of a validator from the active set. Implies slashing fn handle_valset_update( &self, - ctx: SudoCtx, - additions: Option>, - removals: Option>, - updated: Option>, - jailed: Option>, - unjailed: Option>, - tombstoned: Option>, - slashed: Option>, + _ctx: SudoCtx, + _additions: Option>, + _removals: Option>, + _updated: Option>, + _jailed: Option>, + _unjailed: Option>, + _tombstoned: Option>, + _slashed: Option>, ) -> Result { unimplemented!() } diff --git a/contracts/consumer/remote-price-feed/src/contract.rs b/contracts/consumer/remote-price-feed/src/contract.rs index 06e370fc..9640a096 100644 --- a/contracts/consumer/remote-price-feed/src/contract.rs +++ b/contracts/consumer/remote-price-feed/src/contract.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{entry_point, Decimal, DepsMut, Env, IbcChannel, Response, Timestamp}; +use cosmwasm_std::{Decimal, DepsMut, Env, IbcChannel, Response, Timestamp}; use cw2::set_contract_version; use cw_storage_plus::Item; use cw_utils::nonpayable; @@ -97,7 +97,7 @@ impl PriceFeedApi for RemotePriceFeedContract { } fn handle_epoch(&self, ctx: SudoCtx) -> Result { - Ok(self.scheduler.trigger(ctx.deps, &ctx.env)?) + self.scheduler.trigger(ctx.deps, &ctx.env) } } diff --git a/packages/apis/src/virtual_staking_api.rs b/packages/apis/src/virtual_staking_api.rs index 77d6ac7d..8e0afec8 100644 --- a/packages/apis/src/virtual_staking_api.rs +++ b/packages/apis/src/virtual_staking_api.rs @@ -53,6 +53,7 @@ pub trait VirtualStakingApi { /// - Temporary removal of a validator from the active set due to jailing. Implies slashing. /// - Addition of an existing validator to the active validator set. /// - Permanent removal (i.e. tombstoning) of a validator from the active set. Implies slashing + #[allow(clippy::too_many_arguments)] #[sv::msg(sudo)] fn handle_valset_update( &self, From 12605d3b70e46cbea27f63c5fa5169068660e879 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 9 May 2024 12:32:41 +0200 Subject: [PATCH 07/20] Virtual-staking compiles --- .../virtual-staking/src/bin/schema.rs | 2 +- .../consumer/virtual-staking/src/contract.rs | 53 +++++++++++++------ packages/apis/src/virtual_staking_api.rs | 21 +++++--- 3 files changed, 51 insertions(+), 25 deletions(-) diff --git a/contracts/consumer/virtual-staking/src/bin/schema.rs b/contracts/consumer/virtual-staking/src/bin/schema.rs index 4b389fc1..9ca6653b 100644 --- a/contracts/consumer/virtual-staking/src/bin/schema.rs +++ b/contracts/consumer/virtual-staking/src/bin/schema.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::write_api; -use mesh_virtual_staking::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; +use mesh_virtual_staking::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; #[cfg(not(tarpaulin_include))] fn main() { diff --git a/contracts/consumer/virtual-staking/src/contract.rs b/contracts/consumer/virtual-staking/src/contract.rs index cd4a55ad..c2c2a7fb 100644 --- a/contracts/consumer/virtual-staking/src/contract.rs +++ b/contracts/consumer/virtual-staking/src/contract.rs @@ -2,9 +2,8 @@ use std::cmp::Ordering; use std::collections::{BTreeMap, HashMap, HashSet}; use cosmwasm_std::{ - coin, ensure_eq, entry_point, to_json_binary, Coin, CosmosMsg, CustomQuery, DepsMut, - DistributionMsg, Env, Event, Reply, Response, StdResult, Storage, SubMsg, Uint128, Validator, - WasmMsg, + coin, ensure_eq, to_json_binary, Coin, CosmosMsg, CustomQuery, DepsMut, DistributionMsg, Env, + Event, Reply, Response, StdResult, Storage, SubMsg, Uint128, Validator, WasmMsg, }; use cw2::set_contract_version; use cw_storage_plus::{Item, Map}; @@ -72,7 +71,10 @@ impl VirtualStakingContract<'_> { /// The caller of the instantiation will be the converter contract #[sv::msg(instantiate)] - pub fn instantiate(&self, ctx: InstantiateCtx) -> Result { + pub fn instantiate( + &self, + ctx: InstantiateCtx, + ) -> Result, ContractError> { nonpayable(&ctx.info)?; let denom = ctx.deps.querier.query_bonded_denom()?; let config = Config { @@ -91,7 +93,10 @@ impl VirtualStakingContract<'_> { } #[sv::msg(query)] - fn config(&self, ctx: QueryCtx) -> Result { + fn config( + &self, + ctx: QueryCtx, + ) -> Result { Ok(self.config.load(ctx.deps.storage)?.into()) } @@ -125,7 +130,11 @@ impl VirtualStakingContract<'_> { } #[sv::msg(reply)] - fn reply(&self, ctx: ReplyCtx, reply: Reply) -> Result { + fn reply( + &self, + ctx: ReplyCtx, + reply: Reply, + ) -> Result, ContractError> { match (reply.id, reply.result.into_result()) { (REPLY_REWARDS_ID, Ok(_)) => self.reply_rewards(ctx.deps, ctx.env), (REPLY_REWARDS_ID, Err(e)) => { @@ -142,7 +151,11 @@ impl VirtualStakingContract<'_> { } /// This is called on each successful withdrawal - fn reply_rewards(&self, mut deps: DepsMut, env: Env) -> Result { + fn reply_rewards( + &self, + mut deps: DepsMut, + env: Env, + ) -> Result, ContractError> { const BATCH: ValidatorRewardsBatch = VALIDATOR_REWARDS_BATCH; // Find the validator to assign the new reward to @@ -212,7 +225,7 @@ impl VirtualStakingContract<'_> { /// Returns a tuple containing the reward target and a boolean value /// specifying if we've exhausted the list. -fn pop_target(deps: DepsMut) -> StdResult<(String, bool)> { +fn pop_target(deps: DepsMut) -> StdResult<(String, bool)> { let mut targets = REWARD_TARGETS.load(deps.storage)?; let target = targets.pop().unwrap(); REWARD_TARGETS.save(deps.storage, &targets)?; @@ -338,11 +351,19 @@ fn withdraw_reward_msgs( impl VirtualStakingApi for VirtualStakingContract<'_> { type Error = ContractError; + type QueryC = VirtualStakeCustomQuery; + type ExecC = VirtualStakeCustomMsg; /// Requests to bond tokens to a validator. This will be actually handled at the next epoch. /// If the virtual staking module is over the max cap, it will trigger a rebalance. /// If the max cap is 0, then this will immediately return an error. - fn bond(&self, ctx: ExecCtx, validator: String, amount: Coin) -> Result { + fn bond( + &self, + ctx: ExecCtx, + + validator: String, + amount: Coin, + ) -> Result, Self::Error> { nonpayable(&ctx.info)?; let cfg = self.config.load(ctx.deps.storage)?; ensure_eq!(ctx.info.sender, cfg.converter, ContractError::Unauthorized); // only the converter can call this @@ -369,10 +390,10 @@ impl VirtualStakingApi for VirtualStakingContract<'_> { /// If the virtual staking contract doesn't have at least amount tokens staked to the given validator, this will return an error. fn unbond( &self, - ctx: ExecCtx, + ctx: ExecCtx, validator: String, amount: Coin, - ) -> Result { + ) -> Result, Self::Error> { nonpayable(&ctx.info)?; let cfg = self.config.load(ctx.deps.storage)?; ensure_eq!(ctx.info.sender, cfg.converter, ContractError::Unauthorized); // only the converter can call this @@ -398,10 +419,10 @@ impl VirtualStakingApi for VirtualStakingContract<'_> { /// If the virtual staking contract doesn't have at least amount tokens staked over the given validators, this will return an error. fn burn( &self, - ctx: ExecCtx, + ctx: ExecCtx, validators: Vec, amount: Coin, - ) -> Result { + ) -> Result, Self::Error> { nonpayable(&ctx.info)?; let cfg = self.config.load(ctx.deps.storage)?; ensure_eq!(ctx.info.sender, cfg.converter, ContractError::Unauthorized); // only the converter can call this @@ -475,7 +496,7 @@ impl VirtualStakingApi for VirtualStakingContract<'_> { &self, ctx: SudoCtx, ) -> Result, ContractError> { - let SudoCtx { deps, env, .. } = ctx; + let SudoCtx { mut deps, env, .. } = ctx; // withdraw rewards let bonded = self.bonded.load(deps.storage)?; @@ -546,7 +567,7 @@ impl VirtualStakingApi for VirtualStakingContract<'_> { #[allow(clippy::too_many_arguments)] fn handle_valset_update( &self, - ctx: SudoCtx, + ctx: SudoCtx, additions: Option>, removals: Option>, updated: Option>, @@ -1495,7 +1516,7 @@ mod tests { msg: CosmosMsg::Wasm(WasmMsg::Execute { msg: bin_msg, .. }), .. }] => { - if let converter_api::ExecMsg::DistributeRewards { mut payments } = + if let converter_api::sv::ExecMsg::DistributeRewards { mut payments } = from_json(bin_msg).unwrap() { payments.sort(); diff --git a/packages/apis/src/virtual_staking_api.rs b/packages/apis/src/virtual_staking_api.rs index 8e0afec8..beb84742 100644 --- a/packages/apis/src/virtual_staking_api.rs +++ b/packages/apis/src/virtual_staking_api.rs @@ -1,20 +1,25 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{Coin, Response, StdError, Uint128, Validator}; +use sylvia::cw_std::{CustomMsg, CustomQuery}; use sylvia::types::{ExecCtx, SudoCtx}; use sylvia::{interface, schemars}; +// TODO: make these parameters of the trait? + /// The Virtual Staking API is called from the converter contract to bond and (instantly) unbond tokens. /// The Virtual Staking contract is responsible for interfacing with the native SDK module, while the converter /// manages the IBC connection. #[interface] pub trait VirtualStakingApi { type Error: From; + type ExecC: CustomMsg; + type QueryC: CustomQuery; /// Requests to bond tokens to a validator. This will be actually handled at the next epoch. /// If the virtual staking module is over the max cap, it will trigger a rebalance. /// If the max cap is 0, then this will immediately return an error. #[sv::msg(exec)] - fn bond(&self, ctx: ExecCtx, validator: String, amount: Coin) -> Result; + fn bond(&self, ctx: ExecCtx, validator: String, amount: Coin) -> Result, Self::Error>; /// Requests to unbond tokens from a validator. This will be actually handled at the next epoch. /// If the virtual staking module is over the max cap, it will trigger a rebalance in addition to unbond. @@ -22,10 +27,10 @@ pub trait VirtualStakingApi { #[sv::msg(exec)] fn unbond( &self, - ctx: ExecCtx, + ctx: ExecCtx, validator: String, amount: Coin, - ) -> Result; + ) -> Result, Self::Error>; /// Burns stake. This is called when the user's collateral is slashed and, as part of slashing /// propagation, the virtual staking contract needs to burn / discount the indicated slashing amount. @@ -33,10 +38,10 @@ pub trait VirtualStakingApi { #[sv::msg(exec)] fn burn( &self, - ctx: ExecCtx, + ctx: ExecCtx, validators: Vec, amount: Coin, - ) -> Result; + ) -> Result, Self::Error>; /// SudoMsg::HandleEpoch{} should be called once per epoch by the sdk (in EndBlock). /// It allows the virtual staking contract to bond or unbond any pending requests, as well @@ -44,7 +49,7 @@ pub trait VirtualStakingApi { /// /// It should also withdraw all pending rewards here, and send them to the converter contract. #[sv::msg(sudo)] - fn handle_epoch(&self, ctx: SudoCtx) -> Result; + fn handle_epoch(&self, ctx: SudoCtx) -> Result, Self::Error>; /// SudoMsg::ValsetUpdate{} should be called every time there's a validator set update: /// - Addition of a new validator to the active validator set. @@ -57,7 +62,7 @@ pub trait VirtualStakingApi { #[sv::msg(sudo)] fn handle_valset_update( &self, - ctx: SudoCtx, + ctx: SudoCtx, additions: Option>, removals: Option>, updated: Option>, @@ -65,7 +70,7 @@ pub trait VirtualStakingApi { unjailed: Option>, tombstoned: Option>, slashed: Option>, - ) -> Result; + ) -> Result, Self::Error>; } #[cw_serde] From 1d3772ee59b776f4dd97ae4610bdbc6900ef3862 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 9 May 2024 12:54:48 +0200 Subject: [PATCH 08/20] Lost in a message of custom msg/query types --- .../consumer/virtual-staking/src/multitest.rs | 48 +++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/contracts/consumer/virtual-staking/src/multitest.rs b/contracts/consumer/virtual-staking/src/multitest.rs index dde1db81..79c28351 100644 --- a/contracts/consumer/virtual-staking/src/multitest.rs +++ b/contracts/consumer/virtual-staking/src/multitest.rs @@ -1,12 +1,21 @@ use cosmwasm_std::{Addr, Decimal, Validator}; -use cw_multi_test::App as MtApp; -use mesh_apis::virtual_staking_api::SudoMsg; -use sylvia::multitest::App; +use sylvia::multitest::Proxy; + +use mesh_converter::contract::sv::mt::ConverterContractProxy; use crate::contract; +use crate::contract::sv::mt::VirtualStakingContractProxy; +use crate::contract::sv::ContractSudoMsg; const JUNO: &str = "ujuno"; +// Trying to figure out how to work with the generic types +type MtApp = cw_multi_test::BasicApp< + mesh_bindings::VirtualStakeCustomMsg, + mesh_bindings::VirtualStakeCustomQuery, +>; +type App = sylvia::multitest::App; + struct SetupArgs<'a> { owner: &'a str, admin: &'a str, @@ -15,12 +24,12 @@ struct SetupArgs<'a> { } struct SetupResponse<'a> { - price_feed: mesh_simple_price_feed::contract::sv::mt::SimplePriceFeedContractProxy<'a, MtApp>, - converter: mesh_converter::contract::sv::mt::ConverterContractProxy<'a, MtApp>, - virtual_staking: contract::sv::mt::VirtualStakingContractProxy<'a, MtApp>, + price_feed: Proxy<'a, MtApp, mesh_simple_price_feed::contract::SimplePriceFeedContract<'a>>, + converter: Proxy<'a, MtApp, mesh_converter::contract::ConverterContract<'a>>, + virtual_staking: Proxy<'a, MtApp, contract::VirtualStakingContract<'a>>, } -fn setup<'a>(app: &'a App, args: SetupArgs<'a>) -> SetupResponse<'a> { +fn setup<'a>(app: &'a App, args: SetupArgs<'a>) -> SetupResponse<'a> { let SetupArgs { owner, admin, @@ -53,8 +62,7 @@ fn setup<'a>(app: &'a App, args: SetupArgs<'a>) -> SetupResponse<'a> { let config = converter.config().unwrap(); let virtual_staking_addr = Addr::unchecked(config.virtual_staking); - let virtual_staking = - contract::sv::mt::VirtualStakingContractProxy::new(virtual_staking_addr, app); + let virtual_staking = Proxy::new(virtual_staking_addr, app); SetupResponse { price_feed, @@ -108,7 +116,7 @@ fn instantiation() { #[test] #[ignore] // FIXME: Enable / finish this test once custom query support is added to sylvia fn valset_update_sudo() { - let app = App::default(); + let app = App::::default(); let owner = "sunny"; // Owner of the staking contract (i. e. the vault contract) let admin = "theman"; @@ -146,15 +154,17 @@ fn valset_update_sudo() { ]; let rems = vec!["cosmosval2".to_string()]; let tombs = vec!["cosmosval3".to_string()]; - let msg = SudoMsg::ValsetUpdate { - additions: Some(adds), - removals: Some(rems), - updated: None, - jailed: None, - unjailed: None, - tombstoned: Some(tombs), - slashed: None, - }; + // See this as an example how we can make working directly with these genertaed enums nicer + let inner = mesh_apis::virtual_staking_api::sv::VirtualStakingApiSudoMsg::handle_valset_update( + Some(adds), + Some(rems), + None, + None, + None, + Some(tombs), + None, + ); + let msg = ContractSudoMsg::VirtualStakingApi(inner); let res = app .app_mut() From 632ee1bfff0147eebc9f33e57d7c9bd3166aeedf Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 9 May 2024 13:26:19 +0200 Subject: [PATCH 09/20] External and native staking compile, some tests fail --- .../provider/external-staking/src/contract.rs | 2 +- .../external-staking/src/multitest.rs | 77 ++++--------------- .../external-staking/src/multitest/utils.rs | 33 ++++---- .../native-staking-proxy/src/multitest.rs | 43 ++++++----- .../provider/native-staking/src/contract.rs | 5 +- .../provider/native-staking/src/multitest.rs | 6 +- 6 files changed, 62 insertions(+), 104 deletions(-) diff --git a/contracts/provider/external-staking/src/contract.rs b/contracts/provider/external-staking/src/contract.rs index 400eebed..25293bc3 100644 --- a/contracts/provider/external-staking/src/contract.rs +++ b/contracts/provider/external-staking/src/contract.rs @@ -1454,7 +1454,7 @@ mod tests { use crate::msg::{AuthorizedEndpoint, ReceiveVirtualStake, ValidatorState}; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; use mesh_apis::cross_staking_api::CrossStakingApi; - use mesh_apis::vault_api::VaultApiExecMsg::CrossSlash; + use mesh_apis::vault_api::sv::VaultApiExecMsg::CrossSlash; static OSMO: &str = "uosmo"; static CREATOR: &str = "creator"; diff --git a/contracts/provider/external-staking/src/multitest.rs b/contracts/provider/external-staking/src/multitest.rs index da895300..07c0b89a 100644 --- a/contracts/provider/external-staking/src/multitest.rs +++ b/contracts/provider/external-staking/src/multitest.rs @@ -3,23 +3,28 @@ mod utils; use anyhow::Result as AnyResult; use cosmwasm_std::{coin, coins, to_json_binary, Decimal, Uint128}; -use mesh_native_staking::contract::multitest_utils::CodeId as NativeStakingCodeId; -use mesh_native_staking::contract::InstantiateMsg as NativeStakingInstantiateMsg; -use mesh_native_staking_proxy::contract::multitest_utils::CodeId as NativeStakingProxyCodeId; -use mesh_vault::contract::multitest_utils::{CodeId as VaultCodeId, VaultContractProxy}; +use mesh_native_staking::contract::sv::mt::CodeId as NativeStakingCodeId; +use mesh_native_staking::contract::sv::InstantiateMsg as NativeStakingInstantiateMsg; +use mesh_native_staking_proxy::contract::sv::mt::CodeId as NativeStakingProxyCodeId; +use mesh_vault::contract::sv::mt::CodeId as VaultCodeId; +use mesh_vault::contract::VaultContract; use mesh_vault::msg::StakingInitInfo; use mesh_sync::ValueRange; use cw_multi_test::App as MtApp; -use sylvia::multitest::App; +use sylvia::multitest::{App, Proxy}; -use crate::contract::cross_staking::test_utils::CrossStakingApi; -use crate::contract::multitest_utils::{CodeId, ExternalStakingContractProxy}; +use crate::contract::sv::mt::ExternalStakingContractProxy; +use crate::test_methods::sv::mt::TestMethodsProxy; +use mesh_apis::cross_staking_api::sv::mt::CrossStakingApiProxy; +use mesh_vault::contract::sv::mt::VaultContractProxy; + +use crate::contract::sv::mt::CodeId; +use crate::contract::ExternalStakingContract; use crate::error::ContractError; use crate::msg::{AuthorizedEndpoint, ReceiveVirtualStake, StakeInfo, ValidatorPendingRewards}; use crate::state::{SlashRatio, Stake}; -use crate::test_methods_impl::test_utils::TestMethods; use utils::{ assert_rewards, get_last_external_staking_pending_tx_id, AppExt as _, ContractExt as _, VaultExt as _, @@ -39,11 +44,11 @@ const LOCAL_SLASHING_PERCENTAGE_OFFLINE: u64 = 5; // Returns vault and external staking proxies fn setup<'app>( app: &'app App, - owner: &str, + owner: &'app str, unbond_period: u64, ) -> AnyResult<( - VaultContractProxy<'app, MtApp>, - ExternalStakingContractProxy<'app, MtApp>, + Proxy<'app, MtApp, VaultContract<'app>>, + Proxy<'app, MtApp, ExternalStakingContract<'app>>, )> { let native_staking_proxy_code = NativeStakingProxyCodeId::store_code(app); let native_staking_code = NativeStakingCodeId::store_code(app); @@ -99,7 +104,7 @@ fn instantiate() { let stakes = contract.stakes(users[0].to_owned(), None, None).unwrap(); assert_eq!(stakes.stakes, []); - let max_slash = contract.cross_staking_api_proxy().max_slash().unwrap(); + let max_slash = contract.max_slash().unwrap(); assert_eq!( max_slash.slash_ratio_dsign, Decimal::percent(SLASHING_PERCENTAGE) @@ -250,7 +255,6 @@ fn unstaking() { .call(users[0]) .unwrap(); contract - .test_methods_proxy() .test_commit_unstake(get_last_external_staking_pending_tx_id(&contract).unwrap()) .call("test") .unwrap(); @@ -260,7 +264,6 @@ fn unstaking() { .call(users[0]) .unwrap(); contract - .test_methods_proxy() .test_commit_unstake(get_last_external_staking_pending_tx_id(&contract).unwrap()) .call("test") .unwrap(); @@ -270,7 +273,6 @@ fn unstaking() { .call(users[1]) .unwrap(); contract - .test_methods_proxy() .test_commit_unstake(get_last_external_staking_pending_tx_id(&contract).unwrap()) .call("test") .unwrap(); @@ -370,7 +372,6 @@ fn unstaking() { .call(users[0]) .unwrap(); contract - .test_methods_proxy() .test_commit_unstake(get_last_external_staking_pending_tx_id(&contract).unwrap()) .call("test") .unwrap(); @@ -380,7 +381,6 @@ fn unstaking() { .call(users[0]) .unwrap(); contract - .test_methods_proxy() .test_commit_unstake(get_last_external_staking_pending_tx_id(&contract).unwrap()) .call("test") .unwrap(); @@ -487,7 +487,6 @@ fn immediate_unstake_if_unbonded_validator() { .call(user) .unwrap(); contract - .test_methods_proxy() .test_commit_unstake(get_last_external_staking_pending_tx_id(&contract).unwrap()) .call("test") .unwrap(); @@ -527,7 +526,6 @@ fn immediate_unstake_if_tombstoned_validator() { .call(user) .unwrap(); contract - .test_methods_proxy() .test_commit_unstake(get_last_external_staking_pending_tx_id(&contract).unwrap()) .call("test") .unwrap(); @@ -581,7 +579,6 @@ fn distribution() { // 20 tokens for users[0] // 30 tokens for users[1] contract - .test_methods_proxy() .test_distribute_rewards(validators[0].to_owned(), coin(50, STAR)) .call(owner) .unwrap(); @@ -589,7 +586,6 @@ fn distribution() { // Only users[0] stakes on validators[1] // 30 tokens for users[0] contract - .test_methods_proxy() .test_distribute_rewards(validators[1].to_owned(), coin(30, STAR)) .call(owner) .unwrap(); @@ -640,14 +636,12 @@ fn distribution() { // 42 tokens for users[1] // 1 token is not distributed contract - .test_methods_proxy() .test_distribute_rewards(validators[0].to_owned(), coin(71, STAR)) .call(owner) .unwrap(); // Distribution in invalid coin should fail contract - .test_methods_proxy() .test_distribute_rewards(validators[1].to_owned(), coin(100, OSMO)) .call(owner) .unwrap_err(); @@ -685,7 +679,6 @@ fn distribution() { let tx_id = get_last_external_staking_pending_tx_id(&contract).unwrap(); contract - .test_methods_proxy() .test_commit_withdraw_rewards(tx_id) .call(users[0]) .unwrap(); @@ -696,7 +689,6 @@ fn distribution() { .unwrap(); let tx_id = get_last_external_staking_pending_tx_id(&contract).unwrap(); contract - .test_methods_proxy() .test_commit_withdraw_rewards(tx_id) .call(users[0]) .unwrap(); @@ -707,7 +699,6 @@ fn distribution() { .unwrap(); let tx_id = get_last_external_staking_pending_tx_id(&contract).unwrap(); contract - .test_methods_proxy() .test_commit_withdraw_rewards(tx_id) .call(users[1]) .unwrap(); @@ -774,7 +765,6 @@ fn distribution() { // // The additional 1 token is leftover after previous allocation contract - .test_methods_proxy() .test_distribute_rewards(validators[0].to_owned(), coin(9, STAR)) .call(owner) .unwrap(); @@ -797,7 +787,6 @@ fn distribution() { // 4 on users[0] (+ ~0.4) // 6 on users[1] (+ ~0.6) contract - .test_methods_proxy() .test_distribute_rewards(validators[0].to_owned(), coin(11, STAR)) .call(owner) .unwrap(); @@ -806,7 +795,6 @@ fn distribution() { // // 11 on users[0] contract - .test_methods_proxy() .test_distribute_rewards(validators[1].to_owned(), coin(11, STAR)) .call(owner) .unwrap(); @@ -821,7 +809,6 @@ fn distribution() { .call(users[1]) .unwrap(); contract - .test_methods_proxy() .test_commit_unstake(get_last_external_staking_pending_tx_id(&contract).unwrap()) .call("test") .unwrap(); @@ -844,7 +831,6 @@ fn distribution() { .call(users[1]) .unwrap(); contract - .test_methods_proxy() .test_commit_stake(get_last_external_staking_pending_tx_id(&contract).unwrap()) .call("test") .unwrap(); @@ -878,7 +864,6 @@ fn distribution() { // 10 on users[0] (~0.4 still not distributed) // 10 on users[1] (~0.6 still not distributed) contract - .test_methods_proxy() .test_distribute_rewards(validators[0].to_owned(), coin(20, STAR)) .call(owner) .unwrap(); @@ -887,7 +872,6 @@ fn distribution() { // 10 on users[1] // 30 on users[2] contract - .test_methods_proxy() .test_distribute_rewards(validators[1].to_owned(), coin(40, STAR)) .call(owner) .unwrap(); @@ -923,7 +907,6 @@ fn distribution() { // 3 for users[1] (+ ~0.5 from this distribution + ~0.6 accumulated -> ~1.1 tokens, we give one // back leaving ~0.1 accumulated) contract - .test_methods_proxy() .test_distribute_rewards(validators[0].to_owned(), coin(5, STAR)) .call(owner) .unwrap(); @@ -956,7 +939,6 @@ fn distribution() { .call(users[0]) .unwrap(); contract - .test_methods_proxy() .test_commit_unstake(get_last_external_staking_pending_tx_id(&contract).unwrap()) .call("test") .unwrap(); @@ -966,7 +948,6 @@ fn distribution() { .call(users[1]) .unwrap(); contract - .test_methods_proxy() .test_commit_unstake(get_last_external_staking_pending_tx_id(&contract).unwrap()) .call("test") .unwrap(); @@ -976,7 +957,6 @@ fn distribution() { // 7 + 1 = 8 to users[0] (~0.9 accumulated + ~0.2 = ~1.1 leftover, 1.0 payed back, ~0.1 accumulated) // 4 to users[0] (~0.1 accumulated + ~0.8 -> leaving at ~0.9) contract - .test_methods_proxy() .test_distribute_rewards(validators[0].to_owned(), coin(12, STAR)) .call(owner) .unwrap(); @@ -1000,7 +980,6 @@ fn distribution() { .unwrap(); let tx_id = get_last_external_staking_pending_tx_id(&contract).unwrap(); contract - .test_methods_proxy() .test_commit_withdraw_rewards(tx_id) .call(users[0]) .unwrap(); @@ -1011,7 +990,6 @@ fn distribution() { .unwrap(); let tx_id = get_last_external_staking_pending_tx_id(&contract).unwrap(); contract - .test_methods_proxy() .test_commit_withdraw_rewards(tx_id) .call(users[0]) .unwrap(); @@ -1023,7 +1001,6 @@ fn distribution() { .unwrap(); let tx_id = get_last_external_staking_pending_tx_id(&contract).unwrap(); contract - .test_methods_proxy() .test_rollback_withdraw_rewards(tx_id) .call(users[1]) .unwrap(); @@ -1080,13 +1057,11 @@ fn distribution() { // 2 tokens to users[0] via validators[1] (~0.5 leftover) // 7 tokens to users[1] via validators[1] (~0.5 lefover) contract - .test_methods_proxy() .test_distribute_rewards(validators[0].to_owned(), coin(10, STAR)) .call(owner) .unwrap(); contract - .test_methods_proxy() .test_distribute_rewards(validators[1].to_owned(), coin(10, STAR)) .call(owner) .unwrap(); @@ -1141,7 +1116,6 @@ fn distribution() { .unwrap(); let tx_id = get_last_external_staking_pending_tx_id(&contract).unwrap(); contract - .test_methods_proxy() .test_commit_withdraw_rewards(tx_id) .call(users[0]) .unwrap(); @@ -1152,7 +1126,6 @@ fn distribution() { .unwrap(); let tx_id = get_last_external_staking_pending_tx_id(&contract).unwrap(); contract - .test_methods_proxy() .test_commit_withdraw_rewards(tx_id) .call(users[0]) .unwrap(); @@ -1163,7 +1136,6 @@ fn distribution() { .unwrap(); let tx_id = get_last_external_staking_pending_tx_id(&contract).unwrap(); contract - .test_methods_proxy() .test_commit_withdraw_rewards(tx_id) .call(users[0]) .unwrap(); @@ -1174,7 +1146,6 @@ fn distribution() { .unwrap(); let tx_id = get_last_external_staking_pending_tx_id(&contract).unwrap(); contract - .test_methods_proxy() .test_commit_withdraw_rewards(tx_id) .call(users[0]) .unwrap(); @@ -1279,7 +1250,6 @@ fn slashing() { .call(user) .unwrap(); contract - .test_methods_proxy() .test_commit_unstake(get_last_external_staking_pending_tx_id(&contract).unwrap()) .call("test") .unwrap(); @@ -1323,7 +1293,6 @@ fn slashing() { .call(user) .unwrap(); contract - .test_methods_proxy() .test_commit_unstake(get_last_external_staking_pending_tx_id(&contract).unwrap()) .call("test") .unwrap(); @@ -1333,7 +1302,6 @@ fn slashing() { .call(user) .unwrap(); contract - .test_methods_proxy() .test_commit_unstake(get_last_external_staking_pending_tx_id(&contract).unwrap()) .call("test") .unwrap(); @@ -1358,7 +1326,6 @@ fn slashing() { // But now validators[0] slashing happens contract - .test_methods_proxy() .test_handle_slashing(validators[0].to_string(), Uint128::new(8)) .call("test") .unwrap(); @@ -1443,7 +1410,6 @@ fn slashing_pending_tx_partial_unbond() { // Now validators[0] slashing happens contract - .test_methods_proxy() .test_handle_slashing(validators[0].to_string(), Uint128::new(20)) .call("test") .unwrap(); @@ -1456,7 +1422,6 @@ fn slashing_pending_tx_partial_unbond() { // Now the unbond gets committed (i.e. successful) contract - .test_methods_proxy() .test_commit_unstake(get_last_external_staking_pending_tx_id(&contract).unwrap()) .call("test") .unwrap(); @@ -1526,7 +1491,6 @@ fn slashing_pending_tx_full_unbond() { // Now validators[0] slashing happens contract - .test_methods_proxy() .test_handle_slashing(validators[0].to_string(), Uint128::new(20)) .call("test") .unwrap(); @@ -1539,7 +1503,6 @@ fn slashing_pending_tx_full_unbond() { // Now the unbond gets committed (i.e. successful) contract - .test_methods_proxy() .test_commit_unstake(get_last_external_staking_pending_tx_id(&contract).unwrap()) .call("test") .unwrap(); @@ -1611,7 +1574,6 @@ fn slashing_pending_tx_full_unbond_rolled_back() { // Now validators[0] slashing happens contract - .test_methods_proxy() .test_handle_slashing(validators[0].to_string(), Uint128::new(20)) .call("test") .unwrap(); @@ -1624,7 +1586,6 @@ fn slashing_pending_tx_full_unbond_rolled_back() { // Now the unbond gets rolled back (i.e. failed) contract - .test_methods_proxy() .test_rollback_unstake(get_last_external_staking_pending_tx_id(&contract).unwrap()) .call("test") .unwrap(); @@ -1716,7 +1677,6 @@ fn slashing_pending_tx_bond() { // Now validators[0] slashing happens, over the amount included the pending bond contract - .test_methods_proxy() .test_handle_slashing(validators[0].to_string(), Uint128::new(25)) .call("test") .unwrap(); @@ -1732,7 +1692,6 @@ fn slashing_pending_tx_bond() { // Now the extra bond gets committed (i.e. successful) contract - .test_methods_proxy() .test_commit_stake(get_last_external_staking_pending_tx_id(&contract).unwrap()) .call("test") .unwrap(); @@ -1803,7 +1762,6 @@ fn slashing_pending_tx_bond_rolled_back() { // Now validators[0] slashing happens, but over the amount without the pending bond contract - .test_methods_proxy() .test_handle_slashing(validators[0].to_string(), Uint128::new(20)) .call("test") .unwrap(); @@ -1819,7 +1777,6 @@ fn slashing_pending_tx_bond_rolled_back() { // Now the extra bond gets rolled back (i.e. failed) contract - .test_methods_proxy() .test_rollback_stake(get_last_external_staking_pending_tx_id(&contract).unwrap()) .call("test") .unwrap(); diff --git a/contracts/provider/external-staking/src/multitest/utils.rs b/contracts/provider/external-staking/src/multitest/utils.rs index e7cc773d..038c1455 100644 --- a/contracts/provider/external-staking/src/multitest/utils.rs +++ b/contracts/provider/external-staking/src/multitest/utils.rs @@ -2,12 +2,16 @@ use cosmwasm_std::{to_json_binary, Addr, Coin}; use cw_multi_test::{App as MtApp, AppResponse}; use mesh_apis::{converter_api::RewardInfo, ibc::AddValidator}; use mesh_sync::Tx; -use mesh_vault::contract::multitest_utils::VaultContractProxy; -use sylvia::multitest::App; +use mesh_vault::contract::{sv::mt::VaultContractProxy, VaultContract}; +use sylvia::multitest::{App, Proxy}; use crate::{ - contract::multitest_utils::ExternalStakingContractProxy, error::ContractError, - msg::ReceiveVirtualStake, test_methods_impl::test_utils::TestMethods as _, + contract::sv::mt::ExternalStakingContractProxy, + contract::ExternalStakingContract, + error::ContractError, + msg::ReceiveVirtualStake, + test_methods::sv::mt::TestMethodsProxy, + //test_methods_impl::test_utils::TestMethods as _, }; macro_rules! assert_rewards { @@ -29,8 +33,8 @@ macro_rules! assert_rewards { pub(crate) use assert_rewards; #[track_caller] -pub(crate) fn get_last_external_staking_pending_tx_id( - contract: &ExternalStakingContractProxy, +pub(crate) fn get_last_external_staking_pending_tx_id<'a>( + contract: &Proxy<'a, MtApp, ExternalStakingContract<'a>>, ) -> Option { let txs = contract.all_pending_txs_desc(None, None).unwrap().txs; txs.first().map(Tx::id) @@ -55,8 +59,8 @@ impl AppExt for App { } } -type Vault<'app> = VaultContractProxy<'app, MtApp>; -type Contract<'app> = ExternalStakingContractProxy<'app, MtApp>; +type Vault<'app> = Proxy<'app, MtApp, VaultContract<'app>>; +type Contract<'app> = Proxy<'app, MtApp, ExternalStakingContract<'app>>; pub(crate) trait ContractExt { fn activate_validators( @@ -83,8 +87,7 @@ impl ContractExt for Contract<'_> { ) -> [&'static str; N] { for val in validators { let activate = AddValidator::mock(val); - self.test_methods_proxy() - .test_set_active_validator(activate, 100, 1234) + self.test_set_active_validator(activate, 100, 1234) .call("test") .unwrap(); } @@ -94,16 +97,14 @@ impl ContractExt for Contract<'_> { #[track_caller] fn remove_validator(&self, validator: &'static str) { - self.test_methods_proxy() - .test_remove_validator(validator.to_string(), 101, 1234) + self.test_remove_validator(validator.to_string(), 101, 1234) .call("test") .unwrap(); } #[track_caller] fn tombstone_validator(&self, validator: &'static str) { - self.test_methods_proxy() - .test_tombstone_validator(validator.to_string(), 101, 1234) + self.test_tombstone_validator(validator.to_string(), 101, 1234) .call("test") .unwrap(); } @@ -120,8 +121,7 @@ impl ContractExt for Contract<'_> { .map(|(validator, amount)| reward_info(*validator, *amount)) .collect(); - self.test_methods_proxy() - .test_distribute_rewards_batch(denom.into(), rewards) + self.test_distribute_rewards_batch(denom.into(), rewards) .call(caller.as_ref()) } } @@ -148,7 +148,6 @@ impl VaultExt for Vault<'_> { // This should be through `IbcPacketAckMsg` let last_external_staking_tx = get_last_external_staking_pending_tx_id(contract).unwrap(); contract - .test_methods_proxy() .test_commit_stake(last_external_staking_tx) .call("test") .unwrap(); diff --git a/contracts/provider/native-staking-proxy/src/multitest.rs b/contracts/provider/native-staking-proxy/src/multitest.rs index f89a4788..a7243a9d 100644 --- a/contracts/provider/native-staking-proxy/src/multitest.rs +++ b/contracts/provider/native-staking-proxy/src/multitest.rs @@ -3,13 +3,16 @@ use anyhow::Result as AnyResult; use cosmwasm_std::testing::mock_env; use cosmwasm_std::{coin, coins, to_json_binary, Addr, Decimal, Validator}; -use cw_multi_test::{App as MtApp, StakingInfo, StakingSudo, SudoMsg}; +use cw_multi_test::{App as MtApp, StakingInfo}; -use sylvia::multitest::App; +use sylvia::multitest::{App, Proxy}; use mesh_vault::contract::sv::mt::VaultContractProxy; +use mesh_vault::contract::VaultContract; use crate::contract; +use crate::contract::sv::mt::NativeStakingProxyContractProxy; +use crate::contract::NativeStakingProxyContract; use crate::msg::ConfigResponse; const OSMO: &str = "uosmo"; @@ -53,10 +56,10 @@ fn init_app(owner: &str, validators: &[&str]) -> App { fn setup<'app>( app: &'app App, - owner: &str, + owner: &'app str, user: &str, validators: &[&str], -) -> AnyResult> { +) -> AnyResult>> { let vault_code = mesh_vault::contract::sv::mt::CodeId::store_code(app); let staking_code = mesh_native_staking::contract::sv::mt::CodeId::store_code(app); let staking_proxy_code = contract::sv::mt::CodeId::store_code(app); @@ -120,8 +123,8 @@ fn instantiation() { setup(&app, owner, user, &[validator]).unwrap(); // Access staking proxy instance - let staking_proxy = - contract::sv::mt::NativeStakingProxyContractProxy::new(Addr::unchecked(proxy_addr), &app); + let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyContract<'_>> = + Proxy::new(Addr::unchecked(proxy_addr), &app); // Check config let config = staking_proxy.config().unwrap(); @@ -164,8 +167,8 @@ fn staking() { let vault = setup(&app, owner, user, &[validator]).unwrap(); // Access staking proxy instance - let staking_proxy = - contract::sv::mt::NativeStakingProxyContractProxy::new(Addr::unchecked(proxy_addr), &app); + let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyContract<'_>> = + Proxy::new(Addr::unchecked(proxy_addr), &app); // Stake some more vault @@ -210,8 +213,8 @@ fn restaking() { setup(&app, owner, user, &[validator]).unwrap(); // Access staking proxy instance - let staking_proxy = - contract::sv::mt::NativeStakingProxyContractProxy::new(Addr::unchecked(proxy_addr), &app); + let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyContract<'_>> = + Proxy::new(Addr::unchecked(proxy_addr), &app); // Restake 30% to a different validator staking_proxy @@ -249,8 +252,8 @@ fn unstaking() { setup(&app, owner, user, &[validator]).unwrap(); // Access staking proxy instance - let staking_proxy = - contract::sv::mt::NativeStakingProxyContractProxy::new(Addr::unchecked(proxy_addr), &app); + let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyContract<'_>> = + Proxy::new(Addr::unchecked(proxy_addr), &app); // Unstake 50% staking_proxy @@ -307,8 +310,8 @@ fn burning() { setup(&app, owner, user, &[validator]).unwrap(); // Access staking proxy instance - let staking_proxy = - contract::sv::mt::NativeStakingProxyContractProxy::new(Addr::unchecked(proxy_addr), &app); + let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyContract<'_>> = + Proxy::new(Addr::unchecked(proxy_addr), &app); // Burn 10%, from validator staking_proxy @@ -377,8 +380,8 @@ fn burning_multiple_delegations() { setup(&app, owner, user, &validators).unwrap(); // Access staking proxy instance - let staking_proxy = - contract::sv::mt::NativeStakingProxyContractProxy::new(Addr::unchecked(proxy_addr), &app); + let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyContract<'_>> = + Proxy::new(Addr::unchecked(proxy_addr), &app); // Burn 15%, no validator specified let burn_amount = 15; @@ -461,8 +464,8 @@ fn releasing_unbonded() { let vault = setup(&app, owner, user, &[validator]).unwrap(); // Access staking proxy instance - let staking_proxy = - contract::sv::mt::NativeStakingProxyContractProxy::new(Addr::unchecked(proxy_addr), &app); + let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyContract<'_>> = + Proxy::new(Addr::unchecked(proxy_addr), &app); // Unstake 100% staking_proxy @@ -520,8 +523,8 @@ fn withdrawing_rewards() { let original_user_funds = app.app().wrap().query_balance(user, OSMO).unwrap(); // Access staking proxy instance - let staking_proxy = - contract::sv::mt::NativeStakingProxyContractProxy::new(Addr::unchecked(proxy_addr), &app); + let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyContract<'_>> = + Proxy::new(Addr::unchecked(proxy_addr), &app); // Advance time enough for rewards to accrue app.update_block(|block| { diff --git a/contracts/provider/native-staking/src/contract.rs b/contracts/provider/native-staking/src/contract.rs index 8fa19bcc..cbebbd78 100644 --- a/contracts/provider/native-staking/src/contract.rs +++ b/contracts/provider/native-staking/src/contract.rs @@ -1,6 +1,3 @@ -#[cfg(not(feature = "library"))] -use cosmwasm_std::entry_point; - use cosmwasm_std::Order::Ascending; use cosmwasm_std::{ from_json, Addr, Decimal, DepsMut, Reply, Response, StdResult, SubMsgResponse, WasmMsg, @@ -8,7 +5,7 @@ use cosmwasm_std::{ use cw2::set_contract_version; use cw_storage_plus::{Item, Map}; use cw_utils::parse_instantiate_response_data; -use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx, ReplyCtx}; +use sylvia::types::{InstantiateCtx, QueryCtx, ReplyCtx}; use sylvia::{contract, schemars}; use mesh_apis::local_staking_api; diff --git a/contracts/provider/native-staking/src/multitest.rs b/contracts/provider/native-staking/src/multitest.rs index 51482ac6..140098cb 100644 --- a/contracts/provider/native-staking/src/multitest.rs +++ b/contracts/provider/native-staking/src/multitest.rs @@ -3,12 +3,13 @@ use cosmwasm_std::{ }; use cw_multi_test::{App as MtApp, StakingInfo}; -use sylvia::multitest::App; +use sylvia::multitest::{App, Proxy}; use mesh_apis::local_staking_api::sv::mt::LocalStakingApiProxy; use mesh_native_staking_proxy::contract::sv::mt::{ CodeId as NativeStakingProxyCodeId, NativeStakingProxyContractProxy, }; +use mesh_native_staking_proxy::contract::NativeStakingProxyContract; use mesh_sync::ValueRange; use mesh_vault::contract::sv::mt::VaultContractProxy; @@ -286,7 +287,8 @@ fn releasing_proxy_stake() { ); // Access staking instance - let staking_proxy = NativeStakingProxyContractProxy::new(Addr::unchecked(proxy_addr), &app); + let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyContract<'_>> = + Proxy::new(Addr::unchecked(proxy_addr), &app); // User bonds some funds to the vault vault From 9b2be1788d09d21e8b69577e018079e4562eedd8 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 9 May 2024 13:55:01 +0200 Subject: [PATCH 10/20] Everything runs minus virtual-staking, some tests fail --- contracts/provider/vault/src/multitest.rs | 92 +++++++++++------------ 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/contracts/provider/vault/src/multitest.rs b/contracts/provider/vault/src/multitest.rs index cc8c73de..eeebe524 100644 --- a/contracts/provider/vault/src/multitest.rs +++ b/contracts/provider/vault/src/multitest.rs @@ -2,17 +2,24 @@ use cosmwasm_std::{coin, coins, to_json_binary, Addr, Decimal, Uint128, Validato use cw_multi_test::{App as MtApp, StakingInfo}; use mesh_apis::ibc::AddValidator; use mesh_external_staking::contract::sv::mt::ExternalStakingContractProxy; +use mesh_external_staking::contract::ExternalStakingContract; use mesh_external_staking::msg::{AuthorizedEndpoint, ReceiveVirtualStake, StakeInfo}; use mesh_external_staking::state::SlashRatio; use mesh_external_staking::state::Stake; use mesh_native_staking::contract::sv::mt::NativeStakingContractProxy; +use mesh_native_staking::contract::NativeStakingContract; use mesh_native_staking_proxy::contract::sv::mt::NativeStakingProxyContractProxy; +use mesh_native_staking_proxy::contract::NativeStakingProxyContract; use mesh_sync::Tx::InFlightStaking; use mesh_sync::{Tx, ValueRange}; -use sylvia::multitest::App; +use sylvia::multitest::{App, Proxy}; + +use mesh_apis::vault_api::sv::mt::VaultApiProxy; +use mesh_external_staking::test_methods::sv::mt::TestMethodsProxy; use crate::contract; use crate::contract::sv::mt::VaultContractProxy; +use crate::contract::VaultContract; use crate::error::ContractError; use crate::msg::{ AccountResponse, AllAccountsResponseItem, AllActiveExternalStakingResponse, LienResponse, @@ -29,7 +36,7 @@ const SLASHING_PERCENTAGE: u64 = 10; /// App initialization fn init_app(users: &[&str], amounts: &[u128]) -> App { - let mut app = App::new(|router, _api, storage| { + let app = App::custom(|router, _api, storage| { for (&user, amount) in std::iter::zip(users, amounts) { router .bank @@ -38,9 +45,9 @@ fn init_app(users: &[&str], amounts: &[u128]) -> App { } }); - set_chain_native_denom(&mut app, OSMO); + set_chain_native_denom(&mut app.app_mut(), OSMO); - App::new(app) + app } fn add_local_validator(app: &mut App, validator: &str) { @@ -78,13 +85,13 @@ fn set_chain_native_denom(app: &mut MtApp, denom: &str) { /// Contracts setup fn setup<'app>( app: &'app App, - owner: &str, + owner: &'app str, slash_percent: u64, unbond_period: u64, ) -> ( - VaultContractProxy<'app, MtApp>, - NativeStakingContractProxy<'app, MtApp>, - ExternalStakingContractProxy<'app, MtApp>, + Proxy<'app, MtApp, VaultContract<'app>>, + Proxy<'app, MtApp, NativeStakingContract<'app>>, + Proxy<'app, MtApp, ExternalStakingContract<'app>>, ) { let (vault, native, external) = setup_inner(app, owner, slash_percent, unbond_period, true); (vault, native.unwrap(), external) @@ -93,12 +100,12 @@ fn setup<'app>( /// Contracts setup fn setup_without_local_staking<'app>( app: &'app App, - owner: &str, + owner: &'app str, slash_percent: u64, unbond_period: u64, ) -> ( - VaultContractProxy<'app, MtApp>, - ExternalStakingContractProxy<'app, MtApp>, + Proxy<'app, MtApp, VaultContract<'app>>, + Proxy<'app, MtApp, ExternalStakingContract<'app>>, ) { let (vault, _, external) = setup_inner(app, owner, slash_percent, unbond_period, false); (vault, external) @@ -106,14 +113,14 @@ fn setup_without_local_staking<'app>( fn setup_inner<'app>( app: &'app App, - owner: &str, + owner: &'app str, slash_percent: u64, unbond_period: u64, local_staking: bool, ) -> ( - VaultContractProxy<'app, MtApp>, - Option>, - ExternalStakingContractProxy<'app, MtApp>, + Proxy<'app, MtApp, VaultContract<'app>>, + Option>>, + Proxy<'app, MtApp, ExternalStakingContract<'app>>, ) { let vault_code = contract::sv::mt::CodeId::store_code(app); @@ -146,7 +153,7 @@ fn setup_inner<'app>( .unwrap(); let native_staking_addr = vault.config().unwrap().local_staking.map(Addr::unchecked); - let native_staking = native_staking_addr.map(|addr| NativeStakingContractProxy::new(addr, app)); + let native_staking = native_staking_addr.map(|addr| Proxy::new(addr, app)); let cross_staking = setup_cross_stake(app, owner, &vault, slash_percent, unbond_period); (vault, native_staking, cross_staking) @@ -154,11 +161,11 @@ fn setup_inner<'app>( fn setup_cross_stake<'app>( app: &'app App, - owner: &str, - vault: &VaultContractProxy<'app, MtApp>, + owner: &'app str, + vault: &Proxy<'app, MtApp, VaultContract<'app>>, slash_percent: u64, unbond_period: u64, -) -> ExternalStakingContractProxy<'app, MtApp> { +) -> Proxy<'app, MtApp, ExternalStakingContract<'app>> { // FIXME: Code shouldn't be duplicated let cross_staking_code = mesh_external_staking::contract::sv::mt::CodeId::store_code(app); // FIXME: Connection endpoint should be unique @@ -182,7 +189,7 @@ fn setup_cross_stake<'app>( /// Set some active validators fn set_active_validators( - cross_staking: &ExternalStakingContractProxy, + cross_staking: &Proxy<'_, MtApp, ExternalStakingContract<'_>>, validators: &[&str], ) -> (u64, u64) { let update_valset_height = 100; @@ -191,7 +198,6 @@ fn set_active_validators( for validator in validators { let activate = AddValidator::mock(validator); cross_staking - .test_methods_proxy() .test_set_active_validator(activate.clone(), update_valset_height, update_valset_time) .call("test") .unwrap(); @@ -200,7 +206,7 @@ fn set_active_validators( } /// Bond some tokens -fn bond(vault: &VaultContractProxy, user: &str, amount: u128) { +fn bond(vault: &Proxy<'_, MtApp, VaultContract<'_>>, user: &str, amount: u128) { vault .bond() .with_funds(&coins(amount, OSMO)) @@ -209,7 +215,7 @@ fn bond(vault: &VaultContractProxy, user: &str, amount: u128) { } fn stake_locally( - vault: &VaultContractProxy, + vault: &Proxy<'_, MtApp, VaultContract<'_>>, user: &str, stake: u128, validator: &str, @@ -224,8 +230,8 @@ fn stake_locally( } fn stake_remotely( - vault: &VaultContractProxy, - cross_staking: &ExternalStakingContractProxy, + vault: &Proxy<'_, MtApp, VaultContract<'_>>, + cross_staking: &Proxy<'_, MtApp, ExternalStakingContract<'_>>, user: &str, validators: &[&str], amounts: &[u128], @@ -248,7 +254,6 @@ fn stake_remotely( let last_external_staking_tx = get_last_external_staking_pending_tx_id(cross_staking).unwrap(); cross_staking - .test_methods_proxy() .test_commit_stake(last_external_staking_tx) .call("test") .unwrap(); @@ -256,15 +261,15 @@ fn stake_remotely( } fn proxy_for_user<'a>( - local_staking: &NativeStakingContractProxy, + local_staking: &Proxy<'_, MtApp, NativeStakingContract<'_>>, user: &str, app: &'a App, -) -> NativeStakingProxyContractProxy<'a, MtApp> { +) -> Proxy<'a, MtApp, NativeStakingProxyContract<'a>> { let proxy_addr = local_staking .proxy_by_owner(user.to_string()) .unwrap() .proxy; - NativeStakingProxyContractProxy::new(Addr::unchecked(proxy_addr), app) + Proxy::new(Addr::unchecked(proxy_addr), app) } fn process_staking_unbondings(app: &App) { @@ -274,14 +279,14 @@ fn process_staking_unbondings(app: &App) { } #[track_caller] -fn get_last_vault_pending_tx_id(contract: &VaultContractProxy) -> Option { +fn get_last_vault_pending_tx_id(contract: &Proxy<'_, MtApp, VaultContract<'_>>) -> Option { let txs = contract.all_pending_txs_desc(None, None).unwrap().txs; txs.first().map(Tx::id) } #[track_caller] fn get_last_external_staking_pending_tx_id( - contract: &ExternalStakingContractProxy, + contract: &Proxy<'_, MtApp, ExternalStakingContract<'_>>, ) -> Option { let txs = contract.all_pending_txs_desc(None, None).unwrap().txs; txs.first().map(Tx::id) @@ -745,7 +750,6 @@ fn stake_cross() { let last_external_staking_tx = get_last_external_staking_pending_tx_id(&cross_staking).unwrap(); println!("last_external_staking_tx: {:?}", last_external_staking_tx); cross_staking - .test_methods_proxy() .test_commit_stake(last_external_staking_tx) .call("test") .unwrap(); @@ -810,7 +814,6 @@ fn stake_cross() { let last_external_staking_tx = get_last_external_staking_pending_tx_id(&cross_staking).unwrap(); println!("last_external_staking_tx: {:?}", last_external_staking_tx); cross_staking - .test_methods_proxy() .test_commit_stake(last_external_staking_tx) .call("test") .unwrap(); @@ -924,7 +927,6 @@ fn stake_cross() { skip_time(&app, unbond_period); let tx_id = get_last_external_staking_pending_tx_id(&cross_staking).unwrap(); cross_staking - .test_methods_proxy() .test_commit_unstake(tx_id) .call("test") .unwrap(); @@ -992,7 +994,6 @@ fn stake_cross() { let tx_id = get_last_external_staking_pending_tx_id(&cross_staking).unwrap(); cross_staking - .test_methods_proxy() .test_commit_unstake(tx_id) .call("test") .unwrap(); @@ -1156,7 +1157,6 @@ fn stake_cross_txs() { // Last tx commit_tx call let last_tx = get_last_vault_pending_tx_id(&vault).unwrap(); vault - .vault_api_proxy() .commit_tx(last_tx) .call(cross_staking.contract_addr.as_str()) .unwrap(); @@ -1251,7 +1251,6 @@ fn stake_cross_txs() { // Commit first tx vault - .vault_api_proxy() .commit_tx(first_tx) .call(cross_staking.contract_addr.as_str()) .unwrap(); @@ -1332,7 +1331,6 @@ fn stake_cross_rollback_tx() { // Rollback tx let last_tx = get_last_vault_pending_tx_id(&vault).unwrap(); vault - .vault_api_proxy() .rollback_tx(last_tx) .call(cross_staking.contract_addr.as_str()) .unwrap(); @@ -1753,7 +1751,6 @@ fn cross_slash_scenario_1() { // Validator 1 is slashed cross_staking - .test_methods_proxy() .test_handle_slashing(validator1.to_string(), Uint128::new(10)) .call("test") .unwrap(); @@ -1865,7 +1862,6 @@ fn cross_slash_scenario_2() { // Validator 1 is slashed cross_staking - .test_methods_proxy() .test_handle_slashing(validator1.to_string(), Uint128::new(20)) .call("test") .unwrap(); @@ -1973,7 +1969,6 @@ fn cross_slash_scenario_3() { // Validator 1 is slashed cross_staking - .test_methods_proxy() .test_handle_slashing(validator1.to_string(), Uint128::new(15)) .call("test") .unwrap(); @@ -2106,7 +2101,6 @@ fn cross_slash_scenario_4() { // Validator 1 is slashed cross_staking_1 - .test_methods_proxy() .test_handle_slashing(validator1.to_string(), Uint128::new(14)) .call("test") .unwrap(); @@ -2283,7 +2277,6 @@ fn cross_slash_scenario_5() { // Validator 1 is slashed cross_staking_1 - .test_methods_proxy() .test_handle_slashing( validator1.to_string(), Uint128::new(180) * Decimal::percent(slashing_percentage), @@ -2426,7 +2419,6 @@ fn cross_slash_no_native_staking() { // Validator 1 is slashed cross_staking_1 - .test_methods_proxy() .test_handle_slashing(validator1.to_string(), Uint128::new(14)) .call("test") .unwrap(); @@ -2545,7 +2537,6 @@ fn cross_slash_pending_unbonding() { .call(user) .unwrap(); cross_staking - .test_methods_proxy() .test_commit_unstake(get_last_external_staking_pending_tx_id(&cross_staking).unwrap()) .call("test") .unwrap(); @@ -2558,7 +2549,6 @@ fn cross_slash_pending_unbonding() { // Validator 1 is slashed, over the current bond cross_staking - .test_methods_proxy() .test_handle_slashing(validator1.to_string(), Uint128::new(5)) .call("test") .unwrap(); @@ -2609,6 +2599,11 @@ fn cross_slash_pending_unbonding() { /// Scenario 7: /// Same as scenario 1 but with native slashing, because of double-signing. +/// +/// TODO: re-enable. It requires `test_handle_jailing` to be implemented, which is +/// also referenced in native-staking::local_staking_apis::jailing but doesn't seem to be +/// actually implemented anywhere. +#[cfg(feature = "todo")] #[test] fn native_slashing_tombstoning() { let owner = "owner"; @@ -2733,6 +2728,11 @@ fn native_slashing_tombstoning() { /// Scenario 8: /// Same as scenario 1 but with native slashing, because of offline. +/// +/// TODO: re-enable. It requires `test_handle_jailing` to be implemented, which is +/// also referenced in native-staking::local_staking_apis::jailing but doesn't seem to be +/// actually implemented anywhere. +#[cfg(feature = "todo")] #[test] fn native_slashing_jailing() { let owner = "owner"; From 180aa1e3354dfb02076147d9ae1bc52c19c5b3b6 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 9 May 2024 14:26:45 +0200 Subject: [PATCH 11/20] Clean up obvious errors in virtual-staking --- .../consumer/virtual-staking/src/contract.rs | 119 +++++++++++------- 1 file changed, 76 insertions(+), 43 deletions(-) diff --git a/contracts/consumer/virtual-staking/src/contract.rs b/contracts/consumer/virtual-staking/src/contract.rs index c2c2a7fb..41f24682 100644 --- a/contracts/consumer/virtual-staking/src/contract.rs +++ b/contracts/consumer/virtual-staking/src/contract.rs @@ -651,7 +651,6 @@ mod tests { Decimal, }; use mesh_bindings::{BondStatusResponse, SlashRatioResponse}; - use serde::de::DeserializeOwned; use super::*; @@ -1291,12 +1290,8 @@ mod tests { } trait VirtualStakingExt { - fn quick_inst(&self, deps: DepsMut); - fn push_rewards( - &self, - deps: &mut OwnedDeps, - amount: u128, - ) -> PushRewardsResult; + fn quick_inst(&self, deps: DepsMut); + fn push_rewards(&self, deps: &mut OwnedDeps, amount: u128) -> PushRewardsResult; fn hit_epoch(&self, deps: DepsMut) -> HitEpochResult; fn quick_bond(&self, deps: DepsMut, validator: &str, amount: u128); fn quick_unbond(&self, deps: DepsMut, validator: &str, amount: u128); @@ -1305,7 +1300,7 @@ mod tests { deps: DepsMut, validator: &[&str], amount: u128, - ) -> Result; + ) -> Result, ContractError>; fn jail( &self, deps: DepsMut, @@ -1326,20 +1321,16 @@ mod tests { } impl VirtualStakingExt for VirtualStakingContract<'_> { - fn quick_inst(&self, deps: DepsMut) { + fn quick_inst(&self, deps: DepsMut) { self.instantiate(InstantiateCtx { - deps: deps.into_empty(), + deps: deps, env: mock_env(), info: mock_info("me", &[]), }) .unwrap(); } - fn push_rewards( - &self, - deps: &mut OwnedDeps, - amount: u128, - ) -> PushRewardsResult { + fn push_rewards(&self, deps: &mut OwnedDeps, amount: u128) -> PushRewardsResult { let denom = self.config.load(&deps.storage).unwrap().denom; let old_amount = deps .as_ref() @@ -1354,7 +1345,7 @@ mod tests { )]); let result = PushRewardsResult::new( - self.reply_rewards(deps.as_mut().into_empty(), mock_env()) + self.reply_rewards(deps.as_mut(), mock_env()) .unwrap() .messages, ); @@ -1369,7 +1360,11 @@ mod tests { #[track_caller] fn hit_epoch(&self, deps: DepsMut) -> HitEpochResult { - HitEpochResult::new(self.handle_epoch(deps, mock_env()).unwrap()) + let deps = SudoCtx { + deps, + env: mock_env(), + }; + HitEpochResult::new(self.handle_epoch(deps).unwrap()) } fn quick_bond(&self, deps: DepsMut, validator: &str, amount: u128) { @@ -1377,7 +1372,7 @@ mod tests { self.bond( ExecCtx { - deps: deps.into_empty(), + deps: deps, env: mock_env(), info: mock_info("me", &[]), }, @@ -1392,7 +1387,7 @@ mod tests { self.unbond( ExecCtx { - deps: deps.into_empty(), + deps: deps, env: mock_env(), info: mock_info("me", &[]), }, @@ -1407,12 +1402,12 @@ mod tests { deps: DepsMut, validators: &[&str], amount: u128, - ) -> Result { + ) -> Result, ContractError> { let denom = self.config.load(deps.storage).unwrap().denom; self.burn( ExecCtx { - deps: deps.into_empty(), + deps: deps, env: mock_env(), info: mock_info("me", &[]), }, @@ -1428,16 +1423,20 @@ mod tests { nominal_slash_ratio: Decimal, slash_amount: Uint128, ) { + let deps = SudoCtx { + deps, + env: mock_env(), + }; // We sent a removal and a slash along with the jail, as this is what the blockchain does self.handle_valset_update( deps, - &[], - &[val.to_string()], - &[], - &[val.to_string()], - &[], - &[], - &[ValidatorSlash { + None, + Some(vec![val.to_string()]), + None, + Some(vec![val.to_string()]), + None, + None, + Some(vec![ValidatorSlash { address: val.to_string(), height: 0, time: 0, @@ -1446,14 +1445,27 @@ mod tests { power: 0, slash_amount, slash_ratio: nominal_slash_ratio.to_string(), - }], + }]), ) .unwrap(); } fn unjail(&self, deps: DepsMut, val: &str) { - self.handle_valset_update(deps, &[], &[], &[], &[], &[val.to_string()], &[], &[]) - .unwrap(); + let deps = SudoCtx { + deps, + env: mock_env(), + }; + self.handle_valset_update( + deps, + None, + None, + None, + None, + Some(vec![val.to_string()]), + None, + None, + ) + .unwrap(); } fn tombstone( @@ -1463,16 +1475,20 @@ mod tests { nominal_slash_ratio: Decimal, slash_amount: Uint128, ) { + let deps = SudoCtx { + deps, + env: mock_env(), + }; // We sent a slash along with the tombstone, as this is what the blockchain does self.handle_valset_update( deps, - &[], - &[], - &[], - &[], - &[], - &[val.to_string()], - &[ValidatorSlash { + None, + None, + None, + None, + None, + Some(vec![val.to_string()]), + Some(vec![ValidatorSlash { address: val.to_string(), height: 0, time: 0, @@ -1481,7 +1497,7 @@ mod tests { power: 0, slash_amount, slash_ratio: nominal_slash_ratio.to_string(), - }], + }]), ) .unwrap(); } @@ -1493,13 +1509,30 @@ mod tests { max_commission: Default::default(), max_change_rate: Default::default(), }; - self.handle_valset_update(deps, &[val], &[], &[], &[], &[], &[], &[]) + let deps = SudoCtx { + deps, + env: mock_env(), + }; + self.handle_valset_update(deps, Some(vec![val]), None, None, None, None, None, None) .unwrap(); } fn remove_val(&self, deps: DepsMut, val: &str) { - self.handle_valset_update(deps, &[], &[val.to_string()], &[], &[], &[], &[], &[]) - .unwrap(); + let deps = SudoCtx { + deps, + env: mock_env(), + }; + self.handle_valset_update( + deps, + None, + Some(vec![val.to_string()]), + None, + None, + None, + None, + None, + ) + .unwrap(); } } @@ -1509,7 +1542,7 @@ mod tests { } impl PushRewardsResult { - fn new(data: Vec) -> Self { + fn new(data: Vec>) -> Self { match &data[..] { [] => Self::Empty, [SubMsg { From 3fa51d78af6663c161a59dd7e852a6dff8fe5de1 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 9 May 2024 14:37:51 +0200 Subject: [PATCH 12/20] More virtual staking cleanup --- contracts/consumer/virtual-staking/src/multitest.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/contracts/consumer/virtual-staking/src/multitest.rs b/contracts/consumer/virtual-staking/src/multitest.rs index 79c28351..032c08eb 100644 --- a/contracts/consumer/virtual-staking/src/multitest.rs +++ b/contracts/consumer/virtual-staking/src/multitest.rs @@ -1,4 +1,5 @@ use cosmwasm_std::{Addr, Decimal, Validator}; +use cw_multi_test::no_init; use sylvia::multitest::Proxy; use mesh_converter::contract::sv::mt::ConverterContractProxy; @@ -16,6 +17,11 @@ type MtApp = cw_multi_test::BasicApp< >; type App = sylvia::multitest::App; +fn new_app() -> App { + // Ideally there is a shorter way to do this + App::new(cw_multi_test::custom_app(no_init)) +} + struct SetupArgs<'a> { owner: &'a str, admin: &'a str, @@ -73,7 +79,7 @@ fn setup<'a>(app: &'a App, args: SetupArgs<'a>) -> SetupResponse<'a> { #[test] fn instantiation() { - let app = App::default(); + let app = new_app(); let owner = "sunny"; // Owner of the staking contract (i. e. the vault contract) let admin = "theman"; @@ -116,7 +122,8 @@ fn instantiation() { #[test] #[ignore] // FIXME: Enable / finish this test once custom query support is added to sylvia fn valset_update_sudo() { - let app = App::::default(); + + let app = new_app(); let owner = "sunny"; // Owner of the staking contract (i. e. the vault contract) let admin = "theman"; From 19b17c2b5337b9d73b7186fd2c37fe7be2819235 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 10 May 2024 10:50:11 +0200 Subject: [PATCH 13/20] Restore original jailing logic --- contracts/consumer/converter/src/contract.rs | 10 ++- .../src/multitest/virtual_staking_mock.rs | 2 + .../consumer/virtual-staking/src/multitest.rs | 1 - .../provider/native-staking/src/contract.rs | 88 ++++++++++++++++++- .../native-staking/src/local_staking_api.rs | 48 +--------- contracts/provider/vault/src/multitest.rs | 10 --- packages/apis/src/local_staking_api.rs | 14 +-- packages/apis/src/virtual_staking_api.rs | 12 ++- 8 files changed, 109 insertions(+), 76 deletions(-) diff --git a/contracts/consumer/converter/src/contract.rs b/contracts/consumer/converter/src/contract.rs index 294adf4a..fc1d97cf 100644 --- a/contracts/consumer/converter/src/contract.rs +++ b/contracts/consumer/converter/src/contract.rs @@ -1,5 +1,7 @@ use cosmwasm_std::{ - ensure_eq, to_json_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, Deps, DepsMut, Event, Fraction, MessageInfo, Reply, Response, StdError, SubMsg, SubMsgResponse, Uint128, Validator, WasmMsg + ensure_eq, to_json_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, Deps, DepsMut, Event, + Fraction, MessageInfo, Reply, Response, StdError, SubMsg, SubMsgResponse, Uint128, Validator, + WasmMsg, }; use cw2::set_contract_version; use cw_storage_plus::Item; @@ -278,7 +280,8 @@ impl ConverterContract<'_> { use sylvia::types::Remote; // NOTE: Jan, this feels hacky... I'm not sure if this is the right way to do it // Also, I am sticking in a random error type here, not what I will get (which is unknown) - let remote = Remote::<&dyn price_feed_api::PriceFeedApi>::new(config.price_feed); + let remote = + Remote::<&dyn price_feed_api::PriceFeedApi>::new(config.price_feed); let price = remote.querier(&deps.querier).price()?.native_per_foreign; let converted = (amount.amount * price) * config.price_adjustment; @@ -305,7 +308,8 @@ impl ConverterContract<'_> { use sylvia::types::Remote; // NOTE: Jan, this feels hacky... I'm not sure if this is the right way to do it // Also, I am sticking in a random error type here, not what I will get (which is unknown) - let remote = Remote::<&dyn price_feed_api::PriceFeedApi>::new(config.price_feed); + let remote = + Remote::<&dyn price_feed_api::PriceFeedApi>::new(config.price_feed); let price = remote.querier(&deps.querier).price()?.native_per_foreign; let converted = (amount.amount * price.inv().ok_or(ContractError::InvalidPrice {})?) * config diff --git a/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs b/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs index d2555b86..ac313247 100644 --- a/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs +++ b/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs @@ -108,6 +108,8 @@ pub struct ConfigResponse { impl VirtualStakingApi for VirtualStakingMock<'_> { type Error = ContractError; + type ExecC = cosmwasm_std::Empty; + type QueryC = cosmwasm_std::Empty; /// Requests to bond tokens to a validator. This will be actually handled at the next epoch. /// If the virtual staking module is over the max cap, it will trigger a rebalance. diff --git a/contracts/consumer/virtual-staking/src/multitest.rs b/contracts/consumer/virtual-staking/src/multitest.rs index 032c08eb..4baeedb0 100644 --- a/contracts/consumer/virtual-staking/src/multitest.rs +++ b/contracts/consumer/virtual-staking/src/multitest.rs @@ -122,7 +122,6 @@ fn instantiation() { #[test] #[ignore] // FIXME: Enable / finish this test once custom query support is added to sylvia fn valset_update_sudo() { - let app = new_app(); let owner = "sunny"; // Owner of the staking contract (i. e. the vault contract) diff --git a/contracts/provider/native-staking/src/contract.rs b/contracts/provider/native-staking/src/contract.rs index cbebbd78..08ac3d95 100644 --- a/contracts/provider/native-staking/src/contract.rs +++ b/contracts/provider/native-staking/src/contract.rs @@ -1,11 +1,11 @@ use cosmwasm_std::Order::Ascending; use cosmwasm_std::{ - from_json, Addr, Decimal, DepsMut, Reply, Response, StdResult, SubMsgResponse, WasmMsg, + from_json, Addr, Decimal, DepsMut, Event, Reply, Response, StdResult, SubMsgResponse, WasmMsg, }; use cw2::set_contract_version; use cw_storage_plus::{Item, Map}; use cw_utils::parse_instantiate_response_data; -use sylvia::types::{InstantiateCtx, QueryCtx, ReplyCtx}; +use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx, ReplyCtx, SudoCtx}; use sylvia::{contract, schemars}; use mesh_apis::local_staking_api; @@ -79,6 +79,46 @@ impl NativeStakingContract<'_> { Ok(Response::new()) } + /// This is called every time there's a change of the active validator set that implies slashing. + /// In test code, this is called from `test_handle_jailing`. + /// In non-test code, this is called from `sudo`. + fn handle_jailing( + &self, + mut deps: DepsMut, + jailed: Option>, + tombstoned: Option>, + ) -> Result { + let jailed = &jailed.unwrap_or_default(); + let tombstoned = &tombstoned.unwrap_or_default(); + + let cfg = self.config.load(deps.storage)?; + let mut msgs = vec![]; + for validator in tombstoned { + // Slash the validator (if bonded) + let slash_msg = + self.handle_slashing(&mut deps, &cfg, validator, SlashingReason::DoubleSign)?; + if let Some(msg) = slash_msg { + msgs.push(msg) + } + } + for validator in jailed { + // Slash the validator (if bonded) + let slash_msg = + self.handle_slashing(&mut deps, &cfg, validator, SlashingReason::Offline)?; + if let Some(msg) = slash_msg { + msgs.push(msg) + } + } + let mut evt = Event::new("jailing"); + if !jailed.is_empty() { + evt = evt.add_attribute("jailed", jailed.join(",")); + } + if !tombstoned.is_empty() { + evt = evt.add_attribute("tombstoned", tombstoned.join(",")); + } + Ok(Response::new().add_event(evt).add_messages(msgs)) + } + pub(crate) fn handle_slashing( &self, deps: &mut DepsMut, @@ -193,4 +233,48 @@ impl NativeStakingContract<'_> { owner: owner_addr.to_string(), }) } + + /// Jails validators temporarily or permanently. + /// Method used for test only. + #[sv::msg(exec)] + fn test_handle_jailing( + &self, + ctx: ExecCtx, + jailed: Vec, + tombstoned: Vec, + ) -> Result { + #[cfg(any(feature = "mt", test))] + { + let jailed = if jailed.is_empty() { + None + } else { + Some(jailed) + }; + let tombstoned = if tombstoned.is_empty() { + None + } else { + Some(tombstoned) + }; + NativeStakingContract::new().handle_jailing(ctx.deps, jailed, tombstoned) + } + #[cfg(not(any(feature = "mt", test)))] + { + let _ = (ctx, jailed, tombstoned); + Err(ContractError::Unauthorized {}) + } + } + + /// `SudoMsg::Jailing` should be called every time there's a validator set update that implies + /// slashing. + /// - Temporary removal of a validator from the active set due to jailing. + /// - Permanent removal (i.e. tombstoning) of a validator from the active set. + #[sv::msg(sudo)] + fn jailing( + &self, + ctx: SudoCtx, + jailed: Option>, + tombstoned: Option>, + ) -> Result { + self.handle_jailing(ctx.deps, jailed, tombstoned) + } } diff --git a/contracts/provider/native-staking/src/local_staking_api.rs b/contracts/provider/native-staking/src/local_staking_api.rs index 4045ed6d..292f68a0 100644 --- a/contracts/provider/native-staking/src/local_staking_api.rs +++ b/contracts/provider/native-staking/src/local_staking_api.rs @@ -1,13 +1,11 @@ -use cosmwasm_std::{ - ensure_eq, from_json, to_json_binary, Binary, Coin, Event, Response, SubMsg, WasmMsg, -}; +use cosmwasm_std::{ensure_eq, from_json, to_json_binary, Binary, Coin, Response, SubMsg, WasmMsg}; use cw_utils::{must_pay, nonpayable}; -use sylvia::types::{ExecCtx, QueryCtx, SudoCtx}; +use sylvia::types::{ExecCtx, QueryCtx}; #[allow(unused_imports)] use mesh_apis::local_staking_api::{self, LocalStakingApi, SlashRatioResponse}; -use crate::contract::{NativeStakingContract, SlashingReason, REPLY_ID_INSTANTIATE}; +use crate::contract::{NativeStakingContract, REPLY_ID_INSTANTIATE}; use crate::error::ContractError; use crate::msg::StakeMsg; @@ -122,46 +120,6 @@ impl LocalStakingApi for NativeStakingContract<'_> { } } - /// This is called every time there's a change of the active validator set that implies slashing. - /// In test code, this is called from `test_handle_jailing`. - /// In non-test code, this is called from `sudo`. - fn jailing( - &self, - mut ctx: SudoCtx, - jailed: Option>, - tombstoned: Option>, - ) -> Result { - let jailed = &jailed.unwrap_or_default(); - let tombstoned = &tombstoned.unwrap_or_default(); - - let cfg = self.config.load(ctx.deps.storage)?; - let mut msgs = vec![]; - for validator in tombstoned { - // Slash the validator (if bonded) - let slash_msg = - self.handle_slashing(&mut ctx.deps, &cfg, validator, SlashingReason::DoubleSign)?; - if let Some(msg) = slash_msg { - msgs.push(msg) - } - } - for validator in jailed { - // Slash the validator (if bonded) - let slash_msg = - self.handle_slashing(&mut ctx.deps, &cfg, validator, SlashingReason::Offline)?; - if let Some(msg) = slash_msg { - msgs.push(msg) - } - } - let mut evt = Event::new("jailing"); - if !jailed.is_empty() { - evt = evt.add_attribute("jailed", jailed.join(",")); - } - if !tombstoned.is_empty() { - evt = evt.add_attribute("tombstoned", tombstoned.join(",")); - } - Ok(Response::new().add_event(evt).add_messages(msgs)) - } - /// Returns the maximum percentage that can be slashed fn max_slash(&self, ctx: QueryCtx) -> Result { let Config { diff --git a/contracts/provider/vault/src/multitest.rs b/contracts/provider/vault/src/multitest.rs index eeebe524..61657925 100644 --- a/contracts/provider/vault/src/multitest.rs +++ b/contracts/provider/vault/src/multitest.rs @@ -2599,11 +2599,6 @@ fn cross_slash_pending_unbonding() { /// Scenario 7: /// Same as scenario 1 but with native slashing, because of double-signing. -/// -/// TODO: re-enable. It requires `test_handle_jailing` to be implemented, which is -/// also referenced in native-staking::local_staking_apis::jailing but doesn't seem to be -/// actually implemented anywhere. -#[cfg(feature = "todo")] #[test] fn native_slashing_tombstoning() { let owner = "owner"; @@ -2728,11 +2723,6 @@ fn native_slashing_tombstoning() { /// Scenario 8: /// Same as scenario 1 but with native slashing, because of offline. -/// -/// TODO: re-enable. It requires `test_handle_jailing` to be implemented, which is -/// also referenced in native-staking::local_staking_apis::jailing but doesn't seem to be -/// actually implemented anywhere. -#[cfg(feature = "todo")] #[test] fn native_slashing_jailing() { let owner = "owner"; diff --git a/packages/apis/src/local_staking_api.rs b/packages/apis/src/local_staking_api.rs index 2212fba4..c319d894 100644 --- a/packages/apis/src/local_staking_api.rs +++ b/packages/apis/src/local_staking_api.rs @@ -2,7 +2,7 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{ to_json_binary, Addr, Binary, Coin, Decimal, Deps, Response, StdError, WasmMsg, }; -use sylvia::types::{ExecCtx, QueryCtx, SudoCtx}; +use sylvia::types::{ExecCtx, QueryCtx}; use sylvia::{interface, schemars}; #[cw_serde] @@ -49,18 +49,6 @@ pub trait LocalStakingApi { /// Returns the maximum percentage that can be slashed #[sv::msg(query)] fn max_slash(&self, ctx: QueryCtx) -> Result; - - /// `SudoMsg::Jailing` should be called every time there's a validator set update that implies - /// slashing. - /// - Temporary removal of a validator from the active set due to jailing. - /// - Permanent removal (i.e. tombstoning) of a validator from the active set. - #[sv::msg(sudo)] - fn jailing( - &self, - ctx: SudoCtx, - jailed: Option>, - tombstoned: Option>, - ) -> Result; } #[cw_serde] diff --git a/packages/apis/src/virtual_staking_api.rs b/packages/apis/src/virtual_staking_api.rs index beb84742..d820a668 100644 --- a/packages/apis/src/virtual_staking_api.rs +++ b/packages/apis/src/virtual_staking_api.rs @@ -19,7 +19,12 @@ pub trait VirtualStakingApi { /// If the virtual staking module is over the max cap, it will trigger a rebalance. /// If the max cap is 0, then this will immediately return an error. #[sv::msg(exec)] - fn bond(&self, ctx: ExecCtx, validator: String, amount: Coin) -> Result, Self::Error>; + fn bond( + &self, + ctx: ExecCtx, + validator: String, + amount: Coin, + ) -> Result, Self::Error>; /// Requests to unbond tokens from a validator. This will be actually handled at the next epoch. /// If the virtual staking module is over the max cap, it will trigger a rebalance in addition to unbond. @@ -49,7 +54,10 @@ pub trait VirtualStakingApi { /// /// It should also withdraw all pending rewards here, and send them to the converter contract. #[sv::msg(sudo)] - fn handle_epoch(&self, ctx: SudoCtx) -> Result, Self::Error>; + fn handle_epoch( + &self, + ctx: SudoCtx, + ) -> Result, Self::Error>; /// SudoMsg::ValsetUpdate{} should be called every time there's a validator set update: /// - Addition of a new validator to the active validator set. From c4c34f6e17a948b88f83c2487362a9c9dd84909e Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 10 May 2024 11:24:11 +0200 Subject: [PATCH 14/20] Native-staking* and vault tests pass --- .../native-staking-proxy/src/multitest.rs | 37 +++++++++++-------- .../provider/native-staking/src/multitest.rs | 10 +++++ contracts/provider/vault/src/multitest.rs | 13 +++++-- 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/contracts/provider/native-staking-proxy/src/multitest.rs b/contracts/provider/native-staking-proxy/src/multitest.rs index a7243a9d..410f067c 100644 --- a/contracts/provider/native-staking-proxy/src/multitest.rs +++ b/contracts/provider/native-staking-proxy/src/multitest.rs @@ -281,10 +281,7 @@ fn unstaking() { ); // Advance time until the unbonding period is over - app.update_block(|block| { - block.height += 1234; - block.time = block.time.plus_seconds(UNBONDING_PERIOD); - }); + process_staking_unbondings(&app); // Check that the contract now has the funds assert_eq!( @@ -296,6 +293,7 @@ fn unstaking() { ); } + #[test] fn burning() { let owner = "vault_admin"; @@ -338,11 +336,9 @@ fn burning() { coin(0, OSMO) ); + // Advance time until the unbonding period is over - app.update_block(|block| { - block.height += 1234; - block.time = block.time.plus_seconds(UNBONDING_PERIOD); - }); + process_staking_unbondings(&app); // Check that the contract now has the funds assert_eq!( @@ -424,10 +420,7 @@ fn burning_multiple_delegations() { ); // Advance time until the unbonding period is over - app.update_block(|block| { - block.height += 1234; - block.time = block.time.plus_seconds(UNBONDING_PERIOD); - }); + process_staking_unbondings(&app); // Check that the contract now has the funds assert_eq!( @@ -482,10 +475,7 @@ fn releasing_unbonded() { assert!(delegation.is_none()); // Advance time until the unbonding period is over - app.update_block(|block| { - block.height += 12345; - block.time = block.time.plus_seconds(UNBONDING_PERIOD + 1); - }); + process_staking_unbondings(&app); // Release the unbonded funds staking_proxy.release_unbonded().call(user).unwrap(); @@ -551,3 +541,18 @@ fn withdrawing_rewards() { .unwrap(); assert_eq!(original_vault_funds, vault_funds); } + +fn process_staking_unbondings(app: &App) { + // Advance unbonding period + app.app_mut().update_block(|block| { + block.time = block.time.plus_seconds(UNBONDING_PERIOD); + block.height += UNBONDING_PERIOD / 5; + }); + // This is deprecated as unneeded, but tests fail if it isn't here. What's up??? + app.app_mut() + .sudo(cw_multi_test::SudoMsg::Staking( + cw_multi_test::StakingSudo::ProcessQueue {}, + )) + .unwrap(); +} + diff --git a/contracts/provider/native-staking/src/multitest.rs b/contracts/provider/native-staking/src/multitest.rs index 140098cb..0bd18b4f 100644 --- a/contracts/provider/native-staking/src/multitest.rs +++ b/contracts/provider/native-staking/src/multitest.rs @@ -340,6 +340,8 @@ fn releasing_proxy_stake() { .unstake(validator.to_string(), coin(100, OSMO)) .call(user) .unwrap(); + // Important: we need to wait the unbonding period until this is released + app.update_block(advance_unbonding_period); staking_proxy.release_unbonded().call(user).unwrap(); // Check that the vault has the funds again @@ -351,3 +353,11 @@ fn releasing_proxy_stake() { let claims = vault.account_claims(user.to_owned(), None, None).unwrap(); assert_eq!(claims.claims, []); } + +pub fn advance_unbonding_period(block: &mut cosmwasm_std::BlockInfo) { + // Default unbonding time in cw_multi_test is 60, from looking at the code... + // Wish I could find this somewhere in this setup somewhere. + const UNBONDING_TIME: u64 = 60; + block.time = block.time.plus_seconds(5 * UNBONDING_TIME); + block.height += UNBONDING_TIME; +} diff --git a/contracts/provider/vault/src/multitest.rs b/contracts/provider/vault/src/multitest.rs index 61657925..7b448b89 100644 --- a/contracts/provider/vault/src/multitest.rs +++ b/contracts/provider/vault/src/multitest.rs @@ -273,9 +273,16 @@ fn proxy_for_user<'a>( } fn process_staking_unbondings(app: &App) { - let mut block_info = app.block_info(); - block_info.time = block_info.time.plus_seconds(61); - app.set_block(block_info); + app.app_mut().update_block(|block| { + block.time = block.time.plus_seconds(61); + block.height += 13; + }); + // This is deprecated as unneeded, but tests fail if it isn't here. What's up??? + app.app_mut() + .sudo(cw_multi_test::SudoMsg::Staking( + cw_multi_test::StakingSudo::ProcessQueue {}, + )) + .unwrap(); } #[track_caller] From 3db923e891b4bbed1df6ea70d310f354616034a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Fri, 10 May 2024 20:10:31 +0200 Subject: [PATCH 15/20] Workaround to enable communication between `Empty` and `Custom` contracts in mt --- Cargo.lock | 8 +- Cargo.toml | 85 ++++++++++--------- contracts/consumer/converter/Cargo.toml | 35 ++++---- contracts/consumer/converter/src/contract.rs | 62 ++++++++++---- .../consumer/simple-price-feed/Cargo.toml | 31 +++---- .../simple-price-feed/src/contract.rs | 26 ++++-- .../consumer/virtual-staking/src/contract.rs | 1 - 7 files changed, 149 insertions(+), 99 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c9f87aa4..7c98ea8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -607,6 +607,7 @@ dependencies = [ "cw2", "derivative", "mesh-apis", + "mesh-bindings", "mesh-burn", "mesh-simple-price-feed", "schemars", @@ -736,6 +737,7 @@ dependencies = [ "cw2", "derivative", "mesh-apis", + "mesh-bindings", "schemars", "serde", "sylvia", @@ -1188,8 +1190,7 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "sylvia" version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64c8e892525ec035e5bdbeceec19309b1534734da7ff7bc69e9f639b077c497" +source = "git+https://github.com/CosmWasm/sylvia?branch=jawoznia/cosmwasm1/map_err_on_into_response#a5d3546eb287e3e7f003251c529ff3401784e1bb" dependencies = [ "anyhow", "cosmwasm-schema", @@ -1207,8 +1208,7 @@ dependencies = [ [[package]] name = "sylvia-derive" version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae5e0f41752efba2c6895514fa4caae742f69a2a73b4790bb6e365400c563bc1" +source = "git+https://github.com/CosmWasm/sylvia?branch=jawoznia/cosmwasm1/map_err_on_into_response#a5d3546eb287e3e7f003251c529ff3401784e1bb" dependencies = [ "convert_case", "itertools 0.12.1", diff --git a/Cargo.toml b/Cargo.toml index b15060e5..e5384b60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,58 +1,65 @@ [workspace] -members = ["packages/*", "contracts/provider/*", "contracts/consumer/*", "contracts/osmosis-price-provider"] +members = [ + "packages/*", + "contracts/provider/*", + "contracts/consumer/*", + "contracts/osmosis-price-provider", +] resolver = "2" [workspace.package] -edition = "2021" -version = "0.10.0-alpha.1" -license = "MIT" -repository = "https://github.com/osmosis-labs/mesh-security" +edition = "2021" +version = "0.10.0-alpha.1" +license = "MIT" +repository = "https://github.com/osmosis-labs/mesh-security" [workspace.dependencies] -mesh-apis = { path = "./packages/apis" } -mesh-bindings = { path = "./packages/bindings" } -mesh-burn = { path = "./packages/burn" } -mesh-sync = { path = "./packages/sync" } +mesh-apis = { path = "./packages/apis" } +mesh-bindings = { path = "./packages/bindings" } +mesh-burn = { path = "./packages/burn" } +mesh-sync = { path = "./packages/sync" } mesh-virtual-staking-mock = { path = "./packages/virtual-staking-mock" } -mesh-vault = { path = "./contracts/provider/vault" } -mesh-external-staking = { path = "./contracts/provider/external-staking" } -mesh-native-staking = { path = "./contracts/provider/native-staking" } -mesh-native-staking-proxy = { path = "./contracts/provider/native-staking-proxy" } - -mesh-converter = { path = "./contracts/consumer/converter" } -mesh-simple-price-feed = { path = "./contracts/consumer/simple-price-feed" } -mesh-virtual-staking = { path = "./contracts/consumer/virtual-staking" } - -sylvia = "0.10.1" -cosmwasm-schema = "1.5.4" -cosmwasm-std = { version = "1.5.4", features = ["ibc3", "cosmwasm_1_3"] } -cw-storage-plus = "1.2.0" -cw-utils = "1.0.3" -cw2 = "1.1.2" -osmosis-std = "0.20.1" -schemars = "0.8.17" -serde = { version = "1.0.199", default-features = false, features = ["derive"] } -thiserror = "1.0.59" +mesh-vault = { path = "./contracts/provider/vault" } +mesh-external-staking = { path = "./contracts/provider/external-staking" } +mesh-native-staking = { path = "./contracts/provider/native-staking" } +mesh-native-staking-proxy = { path = "./contracts/provider/native-staking-proxy" } + +mesh-converter = { path = "./contracts/consumer/converter" } +mesh-simple-price-feed = { path = "./contracts/consumer/simple-price-feed" } +mesh-virtual-staking = { path = "./contracts/consumer/virtual-staking" } + +sylvia = { git = "https://github.com/CosmWasm/sylvia", branch = "jawoznia/cosmwasm1/map_err_on_into_response", features = [ + "mt", +] } +cosmwasm-schema = "1.5.4" +cosmwasm-std = { version = "1.5.4", features = ["ibc3", "cosmwasm_1_3"] } +cw-storage-plus = "1.2.0" +cw-utils = "1.0.3" +cw2 = "1.1.2" +osmosis-std = "0.20.1" +schemars = "0.8.17" +serde = { version = "1.0.199", default-features = false, features = ["derive"] } +thiserror = "1.0.59" semver = "1.0.22" itertools = "0.12.1" # dev deps -anyhow = "1" +anyhow = "1" cw-multi-test = "0.20" -derivative = "2" -test-case = "3.3.1" +derivative = "2" +test-case = "3.3.1" [profile.release] -codegen-units = 1 -debug = false +codegen-units = 1 +debug = false debug-assertions = false -lto = true -panic = 'abort' -rpath = false -opt-level = 3 -overflow-checks = true +lto = true +panic = 'abort' +rpath = false +opt-level = 3 +overflow-checks = true [profile.release.package.mesh-vault] codegen-units = 1 -incremental = false +incremental = false diff --git a/contracts/consumer/converter/Cargo.toml b/contracts/consumer/converter/Cargo.toml index 547f896d..c60f0af3 100644 --- a/contracts/consumer/converter/Cargo.toml +++ b/contracts/consumer/converter/Cargo.toml @@ -3,8 +3,8 @@ name = "mesh-converter" description = "IBC connector on the Consumer side, converting their IBC messages into native tokens for virtual staking" version = { workspace = true } edition = { workspace = true } -license = { workspace = true } -repository = { workspace = true } +license = { workspace = true } +repository = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] @@ -19,28 +19,29 @@ library = [] mt = ["library", "sylvia/mt"] [dependencies] -mesh-apis = { workspace = true } +mesh-apis = { workspace = true } +mesh-bindings = { workspace = true } -sylvia = { workspace = true } -cosmwasm-schema = { workspace = true } -cosmwasm-std = { workspace = true } -cw-storage-plus = { workspace = true } -cw2 = { workspace = true } -cw-utils = { workspace = true } +sylvia = { workspace = true } +cosmwasm-schema = { workspace = true } +cosmwasm-std = { workspace = true } +cw-storage-plus = { workspace = true } +cw2 = { workspace = true } +cw-utils = { workspace = true } -schemars = { workspace = true } -serde = { workspace = true } -thiserror = { workspace = true } +schemars = { workspace = true } +serde = { workspace = true } +thiserror = { workspace = true } [dev-dependencies] -mesh-burn = { workspace = true } +mesh-burn = { workspace = true } mesh-simple-price-feed = { workspace = true, features = ["mt"] } cw-multi-test = { workspace = true } -test-case = { workspace = true } -derivative = { workspace = true } -anyhow = { workspace = true } +test-case = { workspace = true } +derivative = { workspace = true } +anyhow = { workspace = true } [[bin]] name = "schema" -doc = false +doc = false diff --git a/contracts/consumer/converter/src/contract.rs b/contracts/consumer/converter/src/contract.rs index fc1d97cf..20203484 100644 --- a/contracts/consumer/converter/src/contract.rs +++ b/contracts/consumer/converter/src/contract.rs @@ -24,6 +24,16 @@ pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); const REPLY_ID_INSTANTIATE: u64 = 1; +#[cfg(not(any(test, feature = "mt")))] +pub type ConverterCustomMsg = cosmwasm_std::Empty; +#[cfg(any(test, feature = "mt"))] +pub type ConverterCustomMsg = mesh_bindings::VirtualStakeCustomMsg; + +#[cfg(not(any(test, feature = "mt")))] +pub type ConverterCustomQuery = cosmwasm_std::Empty; +#[cfg(any(test, feature = "mt"))] +pub type ConverterCustomQuery = mesh_bindings::VirtualStakeCustomQuery; + pub struct ConverterContract<'a> { pub config: Item<'a, Config>, pub virtual_stake: Item<'a, Addr>, @@ -33,6 +43,8 @@ pub struct ConverterContract<'a> { #[contract] #[sv::error(ContractError)] #[sv::messages(converter_api as ConverterApi)] +/// Workaround for lack of support in communication `Empty` <-> `Custom` Contracts. +#[sv::custom(query=ConverterCustomQuery, msg=ConverterCustomMsg)] impl ConverterContract<'_> { pub const fn new() -> Self { Self { @@ -51,7 +63,7 @@ impl ConverterContract<'_> { #[sv::msg(instantiate)] pub fn instantiate( &self, - ctx: InstantiateCtx, + ctx: InstantiateCtx, price_feed: String, discount: Decimal, remote_denom: String, @@ -94,7 +106,11 @@ impl ConverterContract<'_> { } #[sv::msg(reply)] - fn reply(&self, ctx: ReplyCtx, reply: Reply) -> Result { + fn reply( + &self, + ctx: ReplyCtx, + reply: Reply, + ) -> Result { match reply.id { REPLY_ID_INSTANTIATE => self.reply_init_callback(ctx.deps, reply.result.unwrap()), _ => Err(ContractError::InvalidReplyId(reply.id)), @@ -104,7 +120,7 @@ impl ConverterContract<'_> { /// Store virtual staking address fn reply_init_callback( &self, - deps: DepsMut, + deps: DepsMut, reply: SubMsgResponse, ) -> Result { let init_data = parse_instantiate_response_data(&reply.data.unwrap())?; @@ -118,7 +134,7 @@ impl ConverterContract<'_> { #[sv::msg(exec)] fn test_stake( &self, - ctx: ExecCtx, + ctx: ExecCtx, validator: String, stake: Coin, ) -> Result { @@ -139,7 +155,7 @@ impl ConverterContract<'_> { #[sv::msg(exec)] fn test_unstake( &self, - ctx: ExecCtx, + ctx: ExecCtx, validator: String, unstake: Coin, ) -> Result { @@ -160,7 +176,7 @@ impl ConverterContract<'_> { #[sv::msg(exec)] fn test_burn( &self, - ctx: ExecCtx, + ctx: ExecCtx, validators: Vec, burn: Coin, ) -> Result { @@ -177,7 +193,7 @@ impl ConverterContract<'_> { } #[sv::msg(query)] - fn config(&self, ctx: QueryCtx) -> Result { + fn config(&self, ctx: QueryCtx) -> Result { let config = self.config.load(ctx.deps.storage)?; let virtual_staking = self.virtual_stake.load(ctx.deps.storage)?.into_string(); Ok(ConfigResponse { @@ -191,7 +207,7 @@ impl ConverterContract<'_> { /// It is pulled out into a method, so it can also be called by test_stake for testing pub(crate) fn stake( &self, - deps: DepsMut, + deps: DepsMut, validator: String, stake: Coin, ) -> Result { @@ -215,7 +231,7 @@ impl ConverterContract<'_> { /// It is pulled out into a method, so it can also be called by test_unstake for testing pub(crate) fn unstake( &self, - deps: DepsMut, + deps: DepsMut, validator: String, unstake: Coin, ) -> Result { @@ -239,7 +255,7 @@ impl ConverterContract<'_> { /// It is pulled out into a method, so it can also be called by test_burn for testing pub(crate) fn burn( &self, - deps: DepsMut, + deps: DepsMut, validators: &[String], burn: Coin, ) -> Result { @@ -262,7 +278,11 @@ impl ConverterContract<'_> { Ok(Response::new().add_message(msg).add_event(event)) } - fn normalize_price(&self, deps: Deps, amount: Coin) -> Result { + fn normalize_price( + &self, + deps: Deps, + amount: Coin, + ) -> Result { let config = self.config.load(deps.storage)?; ensure_eq!( config.remote_denom, @@ -291,7 +311,11 @@ impl ConverterContract<'_> { }) } - fn invert_price(&self, deps: Deps, amount: Coin) -> Result { + fn invert_price( + &self, + deps: Deps, + amount: Coin, + ) -> Result { let config = self.config.load(deps.storage)?; ensure_eq!( config.local_denom, @@ -325,7 +349,7 @@ impl ConverterContract<'_> { pub(crate) fn transfer_rewards( &self, - deps: Deps, + deps: Deps, recipient: String, rewards: Coin, ) -> Result { @@ -351,7 +375,11 @@ impl ConverterContract<'_> { Ok(msg.into()) } - fn ensure_authorized(&self, deps: &DepsMut, info: &MessageInfo) -> Result<(), ContractError> { + fn ensure_authorized( + &self, + deps: &DepsMut, + info: &MessageInfo, + ) -> Result<(), ContractError> { let virtual_stake = self.virtual_stake.load(deps.storage)?; ensure_eq!(info.sender, virtual_stake, ContractError::Unauthorized {}); @@ -366,7 +394,7 @@ impl ConverterApi for ConverterContract<'_> { /// stakers who staked on this validator. This is tracked on the provider, so we send an IBC packet there. fn distribute_reward( &self, - mut ctx: ExecCtx, + mut ctx: ExecCtx, validator: String, ) -> Result { self.ensure_authorized(&ctx.deps, &ctx.info)?; @@ -391,7 +419,7 @@ impl ConverterApi for ConverterContract<'_> { /// in the native staking denom. fn distribute_rewards( &self, - mut ctx: ExecCtx, + mut ctx: ExecCtx, payments: Vec, ) -> Result { self.ensure_authorized(&ctx.deps, &ctx.info)?; @@ -431,7 +459,7 @@ impl ConverterApi for ConverterContract<'_> { #[allow(clippy::too_many_arguments)] fn valset_update( &self, - ctx: ExecCtx, + ctx: ExecCtx, additions: Vec, removals: Vec, updated: Vec, diff --git a/contracts/consumer/simple-price-feed/Cargo.toml b/contracts/consumer/simple-price-feed/Cargo.toml index d19c68b7..3122ac0d 100644 --- a/contracts/consumer/simple-price-feed/Cargo.toml +++ b/contracts/consumer/simple-price-feed/Cargo.toml @@ -3,8 +3,8 @@ name = "mesh-simple-price-feed" description = "Returns a fixed price for assets to the converter contract - for tests or gov-defined prices" version = { workspace = true } edition = { workspace = true } -license = { workspace = true } -repository = { workspace = true } +license = { workspace = true } +repository = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] @@ -19,25 +19,26 @@ library = [] mt = ["library", "sylvia/mt"] [dependencies] -mesh-apis = { workspace = true } +mesh-apis = { workspace = true } +mesh-bindings = { workspace = true } sylvia = { workspace = true } -cosmwasm-schema = { workspace = true } -cosmwasm-std = { workspace = true } -cw-storage-plus = { workspace = true } -cw2 = { workspace = true } -cw-utils = { workspace = true } +cosmwasm-schema = { workspace = true } +cosmwasm-std = { workspace = true } +cw-storage-plus = { workspace = true } +cw2 = { workspace = true } +cw-utils = { workspace = true } -schemars = { workspace = true } -serde = { workspace = true } -thiserror = { workspace = true } +schemars = { workspace = true } +serde = { workspace = true } +thiserror = { workspace = true } [dev-dependencies] cw-multi-test = { workspace = true } -test-case = { workspace = true } -derivative = { workspace = true } -anyhow = { workspace = true } +test-case = { workspace = true } +derivative = { workspace = true } +anyhow = { workspace = true } [[bin]] name = "schema" -doc = false +doc = false diff --git a/contracts/consumer/simple-price-feed/src/contract.rs b/contracts/consumer/simple-price-feed/src/contract.rs index 59896fc4..06aedf11 100644 --- a/contracts/consumer/simple-price-feed/src/contract.rs +++ b/contracts/consumer/simple-price-feed/src/contract.rs @@ -14,6 +14,16 @@ use crate::state::Config; pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +#[cfg(not(any(test, feature = "mt")))] +pub type PriceFeedCustomMsg = cosmwasm_std::Empty; +#[cfg(any(test, feature = "mt"))] +pub type PriceFeedCustomMsg = mesh_bindings::VirtualStakeCustomMsg; + +#[cfg(not(any(test, feature = "mt")))] +pub type PriceFeedCustomQuery = cosmwasm_std::Empty; +#[cfg(any(test, feature = "mt"))] +pub type PriceFeedCustomQuery = mesh_bindings::VirtualStakeCustomQuery; + pub struct SimplePriceFeedContract<'a> { pub config: Item<'a, Config>, } @@ -21,7 +31,11 @@ pub struct SimplePriceFeedContract<'a> { #[cfg_attr(not(feature = "library"), sylvia::entry_points)] #[contract] #[sv::error(ContractError)] -#[sv::messages(price_feed_api as PriceFeedApi)] +#[sv::messages(price_feed_api as PriceFeedApi: custom(msg, query))] +// #[cfg_attr(any(test, feature = "mt"), sv::messages(price_feed_api as PriceFeedApi: custom(msg, query)))] +// #[cfg_attr(not(any(test, feature = "mt")), sv::messages(price_feed_api as PriceFeedApi))] +/// Workaround for lack of support in communication `Empty` <-> `Custom` Contracts. +#[sv::custom(query=PriceFeedCustomQuery, msg=PriceFeedCustomMsg)] impl SimplePriceFeedContract<'_> { pub const fn new() -> Self { Self { @@ -34,10 +48,10 @@ impl SimplePriceFeedContract<'_> { #[sv::msg(instantiate)] pub fn instantiate( &self, - ctx: InstantiateCtx, + ctx: InstantiateCtx, native_per_foreign: Decimal, owner: Option, - ) -> Result { + ) -> Result, ContractError> { nonpayable(&ctx.info)?; let owner = match owner { Some(owner) => ctx.deps.api.addr_validate(&owner)?, @@ -56,9 +70,9 @@ impl SimplePriceFeedContract<'_> { #[sv::msg(exec)] fn update_price( &self, - ctx: ExecCtx, + ctx: ExecCtx, native_per_foreign: Decimal, - ) -> Result { + ) -> Result, ContractError> { nonpayable(&ctx.info)?; let mut config = self.config.load(ctx.deps.storage)?; @@ -76,7 +90,7 @@ impl SimplePriceFeedContract<'_> { } #[sv::msg(query)] - fn config(&self, ctx: QueryCtx) -> Result { + fn config(&self, ctx: QueryCtx) -> Result { let config = self.config.load(ctx.deps.storage)?; Ok(ConfigResponse { owner: config.owner.into_string(), diff --git a/contracts/consumer/virtual-staking/src/contract.rs b/contracts/consumer/virtual-staking/src/contract.rs index 41f24682..42a53555 100644 --- a/contracts/consumer/virtual-staking/src/contract.rs +++ b/contracts/consumer/virtual-staking/src/contract.rs @@ -360,7 +360,6 @@ impl VirtualStakingApi for VirtualStakingContract<'_> { fn bond( &self, ctx: ExecCtx, - validator: String, amount: Coin, ) -> Result, Self::Error> { From 07fb1ca429e979047dec1fb0634b897f1d341679 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 13 May 2024 15:52:30 +0200 Subject: [PATCH 16/20] More generic, simple-price and converter pass tests --- contracts/consumer/converter/Cargo.toml | 2 + contracts/consumer/converter/src/contract.rs | 111 ++++++++++-------- contracts/consumer/converter/src/ibc.rs | 11 +- contracts/consumer/converter/src/multitest.rs | 27 +++-- .../src/multitest/virtual_staking_mock.rs | 52 +++++--- .../consumer/simple-price-feed/Cargo.toml | 2 + .../simple-price-feed/src/contract.rs | 47 +++++--- packages/apis/src/converter_api.rs | 20 ++-- packages/apis/src/price_feed_api.rs | 11 +- 9 files changed, 179 insertions(+), 104 deletions(-) diff --git a/contracts/consumer/converter/Cargo.toml b/contracts/consumer/converter/Cargo.toml index c60f0af3..a152fbeb 100644 --- a/contracts/consumer/converter/Cargo.toml +++ b/contracts/consumer/converter/Cargo.toml @@ -17,6 +17,8 @@ backtraces = ["cosmwasm-std/backtraces"] library = [] # enables generation of mt utilities mt = ["library", "sylvia/mt"] +# enable this for multi-tests where you need custom messages for compatibility with virtual staking +fake-custom = [ "mesh-simple-price-feed/fake-custom" ] [dependencies] mesh-apis = { workspace = true } diff --git a/contracts/consumer/converter/src/contract.rs b/contracts/consumer/converter/src/contract.rs index 20203484..a45887f2 100644 --- a/contracts/consumer/converter/src/contract.rs +++ b/contracts/consumer/converter/src/contract.rs @@ -24,15 +24,18 @@ pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); const REPLY_ID_INSTANTIATE: u64 = 1; -#[cfg(not(any(test, feature = "mt")))] -pub type ConverterCustomMsg = cosmwasm_std::Empty; -#[cfg(any(test, feature = "mt"))] -pub type ConverterCustomMsg = mesh_bindings::VirtualStakeCustomMsg; - -#[cfg(not(any(test, feature = "mt")))] -pub type ConverterCustomQuery = cosmwasm_std::Empty; -#[cfg(any(test, feature = "mt"))] -pub type ConverterCustomQuery = mesh_bindings::VirtualStakeCustomQuery; +#[cfg(not(feature = "fake-custom"))] +pub mod custom { + pub type ConverterMsg = cosmwasm_std::Empty; + pub type ConverterQuery = cosmwasm_std::Empty; + pub type Response = cosmwasm_std::Response; +} +#[cfg(feature = "fake-custom")] +pub mod custom { + pub type ConverterMsg = mesh_bindings::VirtualStakeCustomMsg; + pub type ConverterQuery = mesh_bindings::VirtualStakeCustomQuery; + pub type Response = cosmwasm_std::Response; +} pub struct ConverterContract<'a> { pub config: Item<'a, Config>, @@ -44,7 +47,7 @@ pub struct ConverterContract<'a> { #[sv::error(ContractError)] #[sv::messages(converter_api as ConverterApi)] /// Workaround for lack of support in communication `Empty` <-> `Custom` Contracts. -#[sv::custom(query=ConverterCustomQuery, msg=ConverterCustomMsg)] +#[sv::custom(query=custom::ConverterQuery, msg=custom::ConverterMsg)] impl ConverterContract<'_> { pub const fn new() -> Self { Self { @@ -63,13 +66,13 @@ impl ConverterContract<'_> { #[sv::msg(instantiate)] pub fn instantiate( &self, - ctx: InstantiateCtx, + ctx: InstantiateCtx, price_feed: String, discount: Decimal, remote_denom: String, virtual_staking_code_id: u64, admin: Option, - ) -> Result { + ) -> Result { nonpayable(&ctx.info)?; // validate args if discount >= Decimal::one() { @@ -108,9 +111,9 @@ impl ConverterContract<'_> { #[sv::msg(reply)] fn reply( &self, - ctx: ReplyCtx, + ctx: ReplyCtx, reply: Reply, - ) -> Result { + ) -> Result { match reply.id { REPLY_ID_INSTANTIATE => self.reply_init_callback(ctx.deps, reply.result.unwrap()), _ => Err(ContractError::InvalidReplyId(reply.id)), @@ -120,9 +123,9 @@ impl ConverterContract<'_> { /// Store virtual staking address fn reply_init_callback( &self, - deps: DepsMut, + deps: DepsMut, reply: SubMsgResponse, - ) -> Result { + ) -> Result { let init_data = parse_instantiate_response_data(&reply.data.unwrap())?; let virtual_staking = Addr::unchecked(init_data.contract_address); self.virtual_stake.save(deps.storage, &virtual_staking)?; @@ -134,10 +137,10 @@ impl ConverterContract<'_> { #[sv::msg(exec)] fn test_stake( &self, - ctx: ExecCtx, + ctx: ExecCtx, validator: String, stake: Coin, - ) -> Result { + ) -> Result { #[cfg(any(test, feature = "mt"))] { // This can only ever be called in tests @@ -155,10 +158,10 @@ impl ConverterContract<'_> { #[sv::msg(exec)] fn test_unstake( &self, - ctx: ExecCtx, + ctx: ExecCtx, validator: String, unstake: Coin, - ) -> Result { + ) -> Result { #[cfg(any(test, feature = "mt"))] { // This can only ever be called in tests @@ -176,10 +179,10 @@ impl ConverterContract<'_> { #[sv::msg(exec)] fn test_burn( &self, - ctx: ExecCtx, + ctx: ExecCtx, validators: Vec, burn: Coin, - ) -> Result { + ) -> Result { #[cfg(any(test, feature = "mt"))] { // This can only ever be called in tests @@ -193,7 +196,10 @@ impl ConverterContract<'_> { } #[sv::msg(query)] - fn config(&self, ctx: QueryCtx) -> Result { + fn config( + &self, + ctx: QueryCtx, + ) -> Result { let config = self.config.load(ctx.deps.storage)?; let virtual_staking = self.virtual_stake.load(ctx.deps.storage)?.into_string(); Ok(ConfigResponse { @@ -207,10 +213,10 @@ impl ConverterContract<'_> { /// It is pulled out into a method, so it can also be called by test_stake for testing pub(crate) fn stake( &self, - deps: DepsMut, + deps: DepsMut, validator: String, stake: Coin, - ) -> Result { + ) -> Result { let amount = self.normalize_price(deps.as_ref(), stake)?; let event = Event::new("mesh-bond") @@ -231,10 +237,10 @@ impl ConverterContract<'_> { /// It is pulled out into a method, so it can also be called by test_unstake for testing pub(crate) fn unstake( &self, - deps: DepsMut, + deps: DepsMut, validator: String, unstake: Coin, - ) -> Result { + ) -> Result { let amount = self.normalize_price(deps.as_ref(), unstake)?; let event = Event::new("mesh-unbond") @@ -255,10 +261,10 @@ impl ConverterContract<'_> { /// It is pulled out into a method, so it can also be called by test_burn for testing pub(crate) fn burn( &self, - deps: DepsMut, + deps: DepsMut, validators: &[String], burn: Coin, - ) -> Result { + ) -> Result { let amount = self.normalize_price(deps.as_ref(), burn)?; let event = Event::new("mesh-burn") @@ -280,7 +286,7 @@ impl ConverterContract<'_> { fn normalize_price( &self, - deps: Deps, + deps: Deps, amount: Coin, ) -> Result { let config = self.config.load(deps.storage)?; @@ -298,10 +304,13 @@ impl ConverterContract<'_> { // also see https://github.com/CosmWasm/sylvia/issues/181 to just store Remote in state use price_feed_api::sv::Querier; use sylvia::types::Remote; - // NOTE: Jan, this feels hacky... I'm not sure if this is the right way to do it - // Also, I am sticking in a random error type here, not what I will get (which is unknown) - let remote = - Remote::<&dyn price_feed_api::PriceFeedApi>::new(config.price_feed); + let remote = Remote::< + dyn price_feed_api::PriceFeedApi< + Error = StdError, + ExecC = custom::ConverterMsg, + QueryC = custom::ConverterQuery, + >, + >::new(config.price_feed); let price = remote.querier(&deps.querier).price()?.native_per_foreign; let converted = (amount.amount * price) * config.price_adjustment; @@ -313,7 +322,7 @@ impl ConverterContract<'_> { fn invert_price( &self, - deps: Deps, + deps: Deps, amount: Coin, ) -> Result { let config = self.config.load(deps.storage)?; @@ -330,10 +339,14 @@ impl ConverterContract<'_> { // also see https://github.com/CosmWasm/sylvia/issues/181 to just store Remote in state use price_feed_api::sv::Querier; use sylvia::types::Remote; - // NOTE: Jan, this feels hacky... I'm not sure if this is the right way to do it - // Also, I am sticking in a random error type here, not what I will get (which is unknown) - let remote = - Remote::<&dyn price_feed_api::PriceFeedApi>::new(config.price_feed); + // Note: it doesn't seem to matter which error type goes here... + let remote = Remote::< + dyn price_feed_api::PriceFeedApi< + Error = StdError, + ExecC = custom::ConverterMsg, + QueryC = custom::ConverterQuery, + >, + >::new(config.price_feed); let price = remote.querier(&deps.querier).price()?.native_per_foreign; let converted = (amount.amount * price.inv().ok_or(ContractError::InvalidPrice {})?) * config @@ -349,10 +362,10 @@ impl ConverterContract<'_> { pub(crate) fn transfer_rewards( &self, - deps: Deps, + deps: Deps, recipient: String, rewards: Coin, - ) -> Result { + ) -> Result, ContractError> { // ensure the address is proper let recipient = deps.api.addr_validate(&recipient)?; @@ -377,7 +390,7 @@ impl ConverterContract<'_> { fn ensure_authorized( &self, - deps: &DepsMut, + deps: &DepsMut, info: &MessageInfo, ) -> Result<(), ContractError> { let virtual_stake = self.virtual_stake.load(deps.storage)?; @@ -389,14 +402,16 @@ impl ConverterContract<'_> { impl ConverterApi for ConverterContract<'_> { type Error = ContractError; + type ExecC = custom::ConverterMsg; + type QueryC = custom::ConverterQuery; /// Rewards tokens (in native staking denom) are sent alongside the message, and should be distributed to all /// stakers who staked on this validator. This is tracked on the provider, so we send an IBC packet there. fn distribute_reward( &self, - mut ctx: ExecCtx, + mut ctx: ExecCtx, validator: String, - ) -> Result { + ) -> Result { self.ensure_authorized(&ctx.deps, &ctx.info)?; let config = self.config.load(ctx.deps.storage)?; @@ -419,9 +434,9 @@ impl ConverterApi for ConverterContract<'_> { /// in the native staking denom. fn distribute_rewards( &self, - mut ctx: ExecCtx, + mut ctx: ExecCtx, payments: Vec, - ) -> Result { + ) -> Result { self.ensure_authorized(&ctx.deps, &ctx.info)?; let config = self.config.load(ctx.deps.storage)?; @@ -459,7 +474,7 @@ impl ConverterApi for ConverterContract<'_> { #[allow(clippy::too_many_arguments)] fn valset_update( &self, - ctx: ExecCtx, + ctx: ExecCtx, additions: Vec, removals: Vec, updated: Vec, @@ -467,7 +482,7 @@ impl ConverterApi for ConverterContract<'_> { unjailed: Vec, tombstoned: Vec, mut slashed: Vec, - ) -> Result { + ) -> Result { self.ensure_authorized(&ctx.deps, &ctx.info)?; // Send over IBC to the Consumer diff --git a/contracts/consumer/converter/src/ibc.rs b/contracts/consumer/converter/src/ibc.rs index d711a2a5..c54a19dd 100644 --- a/contracts/consumer/converter/src/ibc.rs +++ b/contracts/consumer/converter/src/ibc.rs @@ -16,7 +16,10 @@ use mesh_apis::ibc::{ }; use sylvia::types::ExecCtx; -use crate::{contract::ConverterContract, error::ContractError}; +use crate::{ + contract::{custom, ConverterContract}, + error::ContractError, +}; /// This is the maximum version of the Mesh Security protocol that we support const SUPPORTED_IBC_PROTOCOL_VERSION: &str = "0.11.0"; @@ -184,10 +187,10 @@ pub fn ibc_channel_close( /// We cannot return any meaningful response value as we do not know the response value /// of execution. We just return ok if we dispatched, error if we failed to dispatch pub fn ibc_packet_receive( - deps: DepsMut, + deps: DepsMut, _env: Env, msg: IbcPacketReceiveMsg, -) -> Result { +) -> Result, ContractError> { let packet: ProviderPacket = from_json(msg.packet.data)?; let contract = ConverterContract::new(); let res = match packet { @@ -280,7 +283,7 @@ pub fn ibc_packet_timeout( } pub(crate) fn make_ibc_packet( - ctx: &mut ExecCtx, + ctx: &mut ExecCtx, packet: ConsumerPacket, ) -> Result { let channel = IBC_CHANNEL.load(ctx.deps.storage)?; diff --git a/contracts/consumer/converter/src/multitest.rs b/contracts/consumer/converter/src/multitest.rs index 883b38c9..8f20b7ad 100644 --- a/contracts/consumer/converter/src/multitest.rs +++ b/contracts/consumer/converter/src/multitest.rs @@ -1,7 +1,8 @@ mod virtual_staking_mock; use cosmwasm_std::{coin, coins, Addr, Decimal, StdError, Uint128, Validator}; -use cw_multi_test::App as MtApp; +use cw_multi_test::no_init; +use cw_multi_test::AppBuilder; use mesh_apis::converter_api::sv::mt::ConverterApiProxy; use mesh_apis::converter_api::RewardInfo; use mesh_simple_price_feed::contract::sv::mt::CodeId as PriceFeedCodeId; @@ -12,13 +13,15 @@ use virtual_staking_mock::VirtualStakingMock; use crate::contract::sv::mt::CodeId as ConverterCodeId; use crate::contract::sv::mt::ConverterContractProxy; -use crate::contract::ConverterContract; +use crate::contract::{custom, ConverterContract}; use crate::error::ContractError; use crate::error::ContractError::Unauthorized; use crate::multitest::virtual_staking_mock::sv::mt::VirtualStakingMockProxy; const JUNO: &str = "ujuno"; +pub type MtApp = cw_multi_test::BasicApp; + struct SetupArgs<'a> { owner: &'a str, admin: &'a str, @@ -32,6 +35,10 @@ struct SetupResponse<'a> { virtual_staking: Proxy<'a, MtApp, VirtualStakingMock<'a>>, } +fn new_app() -> App { + App::new(AppBuilder::new_custom().build(no_init)) +} + fn setup<'a>(app: &'a App, args: SetupArgs<'a>) -> SetupResponse<'a> { let SetupArgs { owner, @@ -80,7 +87,7 @@ fn setup<'a>(app: &'a App, args: SetupArgs<'a>) -> SetupResponse<'a> { #[test] fn instantiation() { - let app = App::default(); + let app = new_app(); let owner = "sunny"; // Owner of the staking contract (i. e. the vault contract) let admin = "theman"; @@ -122,7 +129,7 @@ fn instantiation() { #[test] fn ibc_stake_and_unstake() { - let app = App::default(); + let app = new_app(); let owner = "sunny"; // Owner of the staking contract (i. e. the vault contract) let admin = "theman"; @@ -208,7 +215,7 @@ fn ibc_stake_and_unstake() { #[test] fn ibc_stake_and_burn() { - let app = App::default(); + let app = new_app(); let owner = "sunny"; // Owner of the staking contract (i. e. the vault contract) let admin = "theman"; @@ -294,7 +301,7 @@ fn ibc_stake_and_burn() { #[test] fn valset_update_works() { - let app = App::default(); + let app = new_app(); let owner = "sunny"; // Owner of the staking contract (i. e. the vault contract) let admin = "theman"; @@ -365,7 +372,7 @@ fn valset_update_works() { #[test] fn unauthorized() { - let app = App::default(); + let app = new_app(); let SetupResponse { converter, .. } = setup( &app, @@ -415,7 +422,7 @@ fn distribute_rewards_invalid_amount_is_rejected() { let discount = Decimal::percent(10); // 1 OSMO worth of JUNO should give 0.9 OSMO of stake let native_per_foreign = Decimal::percent(40); // 1 JUNO is worth 0.4 OSMO - let app = App::default(); + let app = new_app(); let SetupResponse { price_feed: _, @@ -490,14 +497,14 @@ fn distribute_rewards_invalid_amount_is_rejected() { } #[test] -#[ignore = "unsupported by Sylvia"] +#[ignore = "IBC unsupported by Sylvia"] fn distribute_rewards_valid_amount() { let owner = "sunny"; let admin = "theman"; let discount = Decimal::percent(10); // 1 OSMO worth of JUNO should give 0.9 OSMO of stake let native_per_foreign = Decimal::percent(40); // 1 JUNO is worth 0.4 OSMO - let app = App::default(); + let app = new_app(); let SetupResponse { price_feed: _, diff --git a/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs b/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs index ac313247..3faaedf8 100644 --- a/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs +++ b/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs @@ -7,6 +7,8 @@ use mesh_apis::virtual_staking_api::{self, ValidatorSlash, VirtualStakingApi}; use sylvia::contract; use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx, SudoCtx}; +use crate::contract::custom; + #[cw_serde] pub struct Config { /// The denom we accept for staking @@ -43,6 +45,7 @@ pub struct VirtualStakingMock<'a> { #[contract] #[sv::error(ContractError)] #[sv::messages(virtual_staking_api as VirtualStakingApi)] +#[sv::custom(query=custom::ConverterQuery, msg=custom::ConverterMsg)] impl VirtualStakingMock<'_> { pub const fn new() -> Self { Self { @@ -52,7 +55,10 @@ impl VirtualStakingMock<'_> { } #[sv::msg(instantiate)] - pub fn instantiate(&self, ctx: InstantiateCtx) -> Result { + pub fn instantiate( + &self, + ctx: InstantiateCtx, + ) -> Result { nonpayable(&ctx.info)?; let denom = ctx.deps.querier.query_bonded_denom()?; let config = Config { @@ -64,7 +70,10 @@ impl VirtualStakingMock<'_> { } #[sv::msg(query)] - fn config(&self, ctx: QueryCtx) -> Result { + fn config( + &self, + ctx: QueryCtx, + ) -> Result { let cfg = self.config.load(ctx.deps.storage)?; let denom = cfg.denom; let converter = cfg.converter.into_string(); @@ -72,7 +81,11 @@ impl VirtualStakingMock<'_> { } #[sv::msg(query)] - fn stake(&self, ctx: QueryCtx, validator: String) -> Result { + fn stake( + &self, + ctx: QueryCtx, + validator: String, + ) -> Result { let stake = self .stake .may_load(ctx.deps.storage, &validator)? @@ -81,7 +94,10 @@ impl VirtualStakingMock<'_> { } #[sv::msg(query)] - fn all_stake(&self, ctx: QueryCtx) -> Result { + fn all_stake( + &self, + ctx: QueryCtx, + ) -> Result { let stakes = self .stake .range(ctx.deps.storage, None, None, cosmwasm_std::Order::Ascending) @@ -108,13 +124,18 @@ pub struct ConfigResponse { impl VirtualStakingApi for VirtualStakingMock<'_> { type Error = ContractError; - type ExecC = cosmwasm_std::Empty; - type QueryC = cosmwasm_std::Empty; + type ExecC = custom::ConverterMsg; + type QueryC = custom::ConverterQuery; /// Requests to bond tokens to a validator. This will be actually handled at the next epoch. /// If the virtual staking module is over the max cap, it will trigger a rebalance. /// If the max cap is 0, then this will immediately return an error. - fn bond(&self, ctx: ExecCtx, validator: String, amount: Coin) -> Result { + fn bond( + &self, + ctx: ExecCtx, + validator: String, + amount: Coin, + ) -> Result, Self::Error> { nonpayable(&ctx.info)?; let cfg = self.config.load(ctx.deps.storage)?; ensure_eq!(ctx.info.sender, cfg.converter, ContractError::Unauthorized); // only the converter can call this @@ -138,10 +159,10 @@ impl VirtualStakingApi for VirtualStakingMock<'_> { /// If the virtual staking contract doesn't have at least amount tokens staked to the given validator, this will return an error. fn unbond( &self, - ctx: ExecCtx, + ctx: ExecCtx, validator: String, amount: Coin, - ) -> Result { + ) -> Result, Self::Error> { nonpayable(&ctx.info)?; let cfg = self.config.load(ctx.deps.storage)?; ensure_eq!(ctx.info.sender, cfg.converter, ContractError::Unauthorized); // only the converter can call this @@ -165,10 +186,10 @@ impl VirtualStakingApi for VirtualStakingMock<'_> { /// If the virtual staking contract doesn't have at least amount tokens staked over the given validators, this will return an error. fn burn( &self, - ctx: ExecCtx, + ctx: ExecCtx, validators: Vec, amount: Coin, - ) -> Result { + ) -> Result, Self::Error> { nonpayable(&ctx.info)?; let cfg = self.config.load(ctx.deps.storage)?; // only the converter can call this @@ -225,7 +246,10 @@ impl VirtualStakingApi for VirtualStakingMock<'_> { /// as to perform a rebalance if needed (over the max cap). /// /// It should also withdraw all pending rewards here, and send them to the converter contract. - fn handle_epoch(&self, _ctx: SudoCtx) -> Result { + fn handle_epoch( + &self, + _ctx: SudoCtx, + ) -> Result, Self::Error> { unimplemented!() } @@ -238,7 +262,7 @@ impl VirtualStakingApi for VirtualStakingMock<'_> { /// - Permanent removal (i.e. tombstoning) of a validator from the active set. Implies slashing fn handle_valset_update( &self, - _ctx: SudoCtx, + _ctx: SudoCtx, _additions: Option>, _removals: Option>, _updated: Option>, @@ -246,7 +270,7 @@ impl VirtualStakingApi for VirtualStakingMock<'_> { _unjailed: Option>, _tombstoned: Option>, _slashed: Option>, - ) -> Result { + ) -> Result, Self::Error> { unimplemented!() } } diff --git a/contracts/consumer/simple-price-feed/Cargo.toml b/contracts/consumer/simple-price-feed/Cargo.toml index 3122ac0d..b32fe2e4 100644 --- a/contracts/consumer/simple-price-feed/Cargo.toml +++ b/contracts/consumer/simple-price-feed/Cargo.toml @@ -17,6 +17,8 @@ backtraces = ["cosmwasm-std/backtraces"] library = [] # enables generation of mt utilities mt = ["library", "sylvia/mt"] +# enable this for multi-tests where you need custom messages for compatibility with virtual staking +fake-custom = [] [dependencies] mesh-apis = { workspace = true } diff --git a/contracts/consumer/simple-price-feed/src/contract.rs b/contracts/consumer/simple-price-feed/src/contract.rs index 06aedf11..b3a5e2d8 100644 --- a/contracts/consumer/simple-price-feed/src/contract.rs +++ b/contracts/consumer/simple-price-feed/src/contract.rs @@ -14,15 +14,18 @@ use crate::state::Config; pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); -#[cfg(not(any(test, feature = "mt")))] -pub type PriceFeedCustomMsg = cosmwasm_std::Empty; -#[cfg(any(test, feature = "mt"))] -pub type PriceFeedCustomMsg = mesh_bindings::VirtualStakeCustomMsg; - -#[cfg(not(any(test, feature = "mt")))] -pub type PriceFeedCustomQuery = cosmwasm_std::Empty; -#[cfg(any(test, feature = "mt"))] -pub type PriceFeedCustomQuery = mesh_bindings::VirtualStakeCustomQuery; +#[cfg(not(feature = "fake-custom"))] +pub mod custom { + pub type PriceFeedMsg = cosmwasm_std::Empty; + pub type PriceFeedQuery = cosmwasm_std::Empty; + pub type Response = cosmwasm_std::Response; +} +#[cfg(feature = "fake-custom")] +pub mod custom { + pub type PriceFeedMsg = mesh_bindings::VirtualStakeCustomMsg; + pub type PriceFeedQuery = mesh_bindings::VirtualStakeCustomQuery; + pub type Response = cosmwasm_std::Response; +} pub struct SimplePriceFeedContract<'a> { pub config: Item<'a, Config>, @@ -31,11 +34,11 @@ pub struct SimplePriceFeedContract<'a> { #[cfg_attr(not(feature = "library"), sylvia::entry_points)] #[contract] #[sv::error(ContractError)] -#[sv::messages(price_feed_api as PriceFeedApi: custom(msg, query))] +#[sv::messages(price_feed_api as PriceFeedApi)] // #[cfg_attr(any(test, feature = "mt"), sv::messages(price_feed_api as PriceFeedApi: custom(msg, query)))] // #[cfg_attr(not(any(test, feature = "mt")), sv::messages(price_feed_api as PriceFeedApi))] /// Workaround for lack of support in communication `Empty` <-> `Custom` Contracts. -#[sv::custom(query=PriceFeedCustomQuery, msg=PriceFeedCustomMsg)] +#[sv::custom(query=custom::PriceFeedQuery, msg=custom::PriceFeedMsg)] impl SimplePriceFeedContract<'_> { pub const fn new() -> Self { Self { @@ -48,10 +51,10 @@ impl SimplePriceFeedContract<'_> { #[sv::msg(instantiate)] pub fn instantiate( &self, - ctx: InstantiateCtx, + ctx: InstantiateCtx, native_per_foreign: Decimal, owner: Option, - ) -> Result, ContractError> { + ) -> Result { nonpayable(&ctx.info)?; let owner = match owner { Some(owner) => ctx.deps.api.addr_validate(&owner)?, @@ -70,9 +73,9 @@ impl SimplePriceFeedContract<'_> { #[sv::msg(exec)] fn update_price( &self, - ctx: ExecCtx, + ctx: ExecCtx, native_per_foreign: Decimal, - ) -> Result, ContractError> { + ) -> Result { nonpayable(&ctx.info)?; let mut config = self.config.load(ctx.deps.storage)?; @@ -90,7 +93,10 @@ impl SimplePriceFeedContract<'_> { } #[sv::msg(query)] - fn config(&self, ctx: QueryCtx) -> Result { + fn config( + &self, + ctx: QueryCtx, + ) -> Result { let config = self.config.load(ctx.deps.storage)?; Ok(ConfigResponse { owner: config.owner.into_string(), @@ -101,10 +107,12 @@ impl SimplePriceFeedContract<'_> { impl PriceFeedApi for SimplePriceFeedContract<'_> { type Error = ContractError; + type ExecC = custom::PriceFeedMsg; + type QueryC = custom::PriceFeedQuery; /// Return the price of the foreign token. That is, how many native tokens /// are needed to buy one foreign token. - fn price(&self, ctx: QueryCtx) -> Result { + fn price(&self, ctx: QueryCtx) -> Result { let config = self.config.load(ctx.deps.storage)?; Ok(PriceResponse { native_per_foreign: config.native_per_foreign, @@ -112,7 +120,10 @@ impl PriceFeedApi for SimplePriceFeedContract<'_> { } /// Nothing needs to be done on the epoch - fn handle_epoch(&self, _ctx: SudoCtx) -> Result { + fn handle_epoch( + &self, + _ctx: SudoCtx, + ) -> Result, Self::Error> { Ok(Response::new()) } } diff --git a/packages/apis/src/converter_api.rs b/packages/apis/src/converter_api.rs index 2a26474d..22fb1dfe 100644 --- a/packages/apis/src/converter_api.rs +++ b/packages/apis/src/converter_api.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Coin, Response, StdError, Uint128, Validator}; +use cosmwasm_std::{Coin, CustomMsg, CustomQuery, Response, StdError, Uint128, Validator}; use sylvia::types::ExecCtx; use sylvia::{interface, schemars}; @@ -9,11 +9,17 @@ use sylvia::{interface, schemars}; #[interface] pub trait ConverterApi { type Error: From; + type ExecC: CustomMsg; + type QueryC: CustomQuery; /// Rewards tokens (in native staking denom) are sent alongside the message, and should be distributed to all /// stakers who staked on this validator. #[sv::msg(exec)] - fn distribute_reward(&self, ctx: ExecCtx, validator: String) -> Result; + fn distribute_reward( + &self, + ctx: ExecCtx, + validator: String, + ) -> Result, Self::Error>; /// This is a batch for of distribute_reward, including the payment for multiple validators. /// This is more efficient than calling distribute_reward multiple times, but also more complex. @@ -23,18 +29,18 @@ pub trait ConverterApi { #[sv::msg(exec)] fn distribute_rewards( &self, - ctx: ExecCtx, + ctx: ExecCtx, payments: Vec, - ) -> Result; + ) -> Result, Self::Error>; /// Valset updates. /// /// TODO: pubkeys need to be part of the Validator struct (requires CosmWasm support). - #[allow(clippy::too_many_arguments)] #[sv::msg(exec)] + #[allow(clippy::too_many_arguments)] fn valset_update( &self, - ctx: ExecCtx, + ctx: ExecCtx, additions: Vec, removals: Vec, updated: Vec, @@ -42,7 +48,7 @@ pub trait ConverterApi { unjailed: Vec, tombstoned: Vec, slashed: Vec, - ) -> Result; + ) -> Result, Self::Error>; } #[cw_serde] diff --git a/packages/apis/src/price_feed_api.rs b/packages/apis/src/price_feed_api.rs index 49723bf1..92679320 100644 --- a/packages/apis/src/price_feed_api.rs +++ b/packages/apis/src/price_feed_api.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Decimal, Response, StdError}; +use cosmwasm_std::{CustomMsg, CustomQuery, Decimal, Response, StdError}; use sylvia::types::{QueryCtx, SudoCtx}; use sylvia::{interface, schemars}; @@ -12,14 +12,19 @@ use sylvia::{interface, schemars}; #[interface] pub trait PriceFeedApi { type Error: From; + type ExecC: CustomMsg; + type QueryC: CustomQuery; /// Return the price of the foreign token. That is, how many native tokens /// are needed to buy one foreign token. #[sv::msg(query)] - fn price(&self, ctx: QueryCtx) -> Result; + fn price(&self, ctx: QueryCtx) -> Result; #[sv::msg(sudo)] - fn handle_epoch(&self, ctx: SudoCtx) -> Result; + fn handle_epoch( + &self, + ctx: SudoCtx, + ) -> Result, Self::Error>; } #[cw_serde] From 1009590f5895b761d42bc803ddafea2ac5e2dea2 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 13 May 2024 16:18:56 +0200 Subject: [PATCH 17/20] virtual-staking tests work --- contracts/consumer/converter/src/multitest.rs | 3 +-- .../consumer/remote-price-feed/src/contract.rs | 3 +++ contracts/consumer/virtual-staking/Cargo.toml | 4 ++-- .../consumer/virtual-staking/src/multitest.rs | 17 ++++++----------- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/contracts/consumer/converter/src/multitest.rs b/contracts/consumer/converter/src/multitest.rs index 8f20b7ad..22169d99 100644 --- a/contracts/consumer/converter/src/multitest.rs +++ b/contracts/consumer/converter/src/multitest.rs @@ -1,8 +1,7 @@ mod virtual_staking_mock; use cosmwasm_std::{coin, coins, Addr, Decimal, StdError, Uint128, Validator}; -use cw_multi_test::no_init; -use cw_multi_test::AppBuilder; +use cw_multi_test::{no_init, AppBuilder}; use mesh_apis::converter_api::sv::mt::ConverterApiProxy; use mesh_apis::converter_api::RewardInfo; use mesh_simple_price_feed::contract::sv::mt::CodeId as PriceFeedCodeId; diff --git a/contracts/consumer/remote-price-feed/src/contract.rs b/contracts/consumer/remote-price-feed/src/contract.rs index 9640a096..745b1ea4 100644 --- a/contracts/consumer/remote-price-feed/src/contract.rs +++ b/contracts/consumer/remote-price-feed/src/contract.rs @@ -84,6 +84,9 @@ impl RemotePriceFeedContract { impl PriceFeedApi for RemotePriceFeedContract { type Error = ContractError; + // FIXME: make these under a feature flag if we need virtual-staking multitest compatibility + type ExecC = cosmwasm_std::Empty; + type QueryC = cosmwasm_std::Empty; /// Return the price of the foreign token. That is, how many native tokens /// are needed to buy one foreign token. diff --git a/contracts/consumer/virtual-staking/Cargo.toml b/contracts/consumer/virtual-staking/Cargo.toml index 47e84cad..2952a33b 100644 --- a/contracts/consumer/virtual-staking/Cargo.toml +++ b/contracts/consumer/virtual-staking/Cargo.toml @@ -35,8 +35,8 @@ serde = { workspace = true } thiserror = { workspace = true } [dev-dependencies] -mesh-simple-price-feed = { workspace = true, features = ["mt"] } -mesh-converter = { workspace = true, features = ["mt"] } +mesh-simple-price-feed = { workspace = true, features = ["mt", "fake-custom"] } +mesh-converter = { workspace = true, features = ["mt", "fake-custom"] } cw-multi-test = { workspace = true } test-case = { workspace = true } derivative = { workspace = true } diff --git a/contracts/consumer/virtual-staking/src/multitest.rs b/contracts/consumer/virtual-staking/src/multitest.rs index 4baeedb0..54be3760 100644 --- a/contracts/consumer/virtual-staking/src/multitest.rs +++ b/contracts/consumer/virtual-staking/src/multitest.rs @@ -1,12 +1,12 @@ use cosmwasm_std::{Addr, Decimal, Validator}; use cw_multi_test::no_init; +use mesh_apis::virtual_staking_api::sv::mt::VirtualStakingApiProxy; use sylvia::multitest::Proxy; use mesh_converter::contract::sv::mt::ConverterContractProxy; use crate::contract; use crate::contract::sv::mt::VirtualStakingContractProxy; -use crate::contract::sv::ContractSudoMsg; const JUNO: &str = "ujuno"; @@ -120,7 +120,8 @@ fn instantiation() { } #[test] -#[ignore] // FIXME: Enable / finish this test once custom query support is added to sylvia +// FIXME: Enable / finish this test once custom query support is added to sylvia +#[ignore = "IBC Messages not supported yet"] fn valset_update_sudo() { let app = new_app(); @@ -160,8 +161,8 @@ fn valset_update_sudo() { ]; let rems = vec!["cosmosval2".to_string()]; let tombs = vec!["cosmosval3".to_string()]; - // See this as an example how we can make working directly with these genertaed enums nicer - let inner = mesh_apis::virtual_staking_api::sv::VirtualStakingApiSudoMsg::handle_valset_update( + + let res = virtual_staking.handle_valset_update( Some(adds), Some(rems), None, @@ -170,12 +171,6 @@ fn valset_update_sudo() { Some(tombs), None, ); - let msg = ContractSudoMsg::VirtualStakingApi(inner); - - let res = app - .app_mut() - .wasm_sudo(virtual_staking.contract_addr, &msg) - .unwrap(); - println!("res: {:?}", res); + res.unwrap(); } From 493826569b74db2765384169a4659558f9e46f85 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 13 May 2024 16:29:43 +0200 Subject: [PATCH 18/20] Test pass, clippy happy :) --- contracts/provider/native-staking-proxy/src/multitest.rs | 4 +--- contracts/provider/vault/src/multitest.rs | 1 + packages/apis/src/converter_api.rs | 4 +++- packages/apis/src/virtual_staking_api.rs | 5 ++++- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/contracts/provider/native-staking-proxy/src/multitest.rs b/contracts/provider/native-staking-proxy/src/multitest.rs index 410f067c..02321a7d 100644 --- a/contracts/provider/native-staking-proxy/src/multitest.rs +++ b/contracts/provider/native-staking-proxy/src/multitest.rs @@ -293,7 +293,6 @@ fn unstaking() { ); } - #[test] fn burning() { let owner = "vault_admin"; @@ -336,7 +335,6 @@ fn burning() { coin(0, OSMO) ); - // Advance time until the unbonding period is over process_staking_unbondings(&app); @@ -551,8 +549,8 @@ fn process_staking_unbondings(app: &App) { // This is deprecated as unneeded, but tests fail if it isn't here. What's up??? app.app_mut() .sudo(cw_multi_test::SudoMsg::Staking( + #[allow(deprecated)] cw_multi_test::StakingSudo::ProcessQueue {}, )) .unwrap(); } - diff --git a/contracts/provider/vault/src/multitest.rs b/contracts/provider/vault/src/multitest.rs index 7b448b89..558bebf4 100644 --- a/contracts/provider/vault/src/multitest.rs +++ b/contracts/provider/vault/src/multitest.rs @@ -280,6 +280,7 @@ fn process_staking_unbondings(app: &App) { // This is deprecated as unneeded, but tests fail if it isn't here. What's up??? app.app_mut() .sudo(cw_multi_test::SudoMsg::Staking( + #[allow(deprecated)] cw_multi_test::StakingSudo::ProcessQueue {}, )) .unwrap(); diff --git a/packages/apis/src/converter_api.rs b/packages/apis/src/converter_api.rs index 22fb1dfe..8fc26412 100644 --- a/packages/apis/src/converter_api.rs +++ b/packages/apis/src/converter_api.rs @@ -1,3 +1,6 @@ +// only for valset_update but doesn't work for the autogenerated code +#![allow(clippy::too_many_arguments)] + use cosmwasm_schema::cw_serde; use cosmwasm_std::{Coin, CustomMsg, CustomQuery, Response, StdError, Uint128, Validator}; use sylvia::types::ExecCtx; @@ -37,7 +40,6 @@ pub trait ConverterApi { /// /// TODO: pubkeys need to be part of the Validator struct (requires CosmWasm support). #[sv::msg(exec)] - #[allow(clippy::too_many_arguments)] fn valset_update( &self, ctx: ExecCtx, diff --git a/packages/apis/src/virtual_staking_api.rs b/packages/apis/src/virtual_staking_api.rs index d820a668..d05013a2 100644 --- a/packages/apis/src/virtual_staking_api.rs +++ b/packages/apis/src/virtual_staking_api.rs @@ -1,3 +1,7 @@ +// only for handle_valset_update but doesn't work for the autogenerated code +#![allow(clippy::too_many_arguments)] + + use cosmwasm_schema::cw_serde; use cosmwasm_std::{Coin, Response, StdError, Uint128, Validator}; use sylvia::cw_std::{CustomMsg, CustomQuery}; @@ -66,7 +70,6 @@ pub trait VirtualStakingApi { /// - Temporary removal of a validator from the active set due to jailing. Implies slashing. /// - Addition of an existing validator to the active validator set. /// - Permanent removal (i.e. tombstoning) of a validator from the active set. Implies slashing - #[allow(clippy::too_many_arguments)] #[sv::msg(sudo)] fn handle_valset_update( &self, From 3092f958eefa4f6651b9e6771cdab917ac0558e1 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 13 May 2024 16:39:50 +0200 Subject: [PATCH 19/20] Wasm compilation proper again --- Cargo.lock | 6 ++++-- Cargo.toml | 5 ++--- contracts/consumer/converter/src/contract.rs | 4 ++-- packages/apis/src/virtual_staking_api.rs | 1 - scripts/optimizer.sh | 4 +++- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c98ea8f..0df67f14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1190,7 +1190,8 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "sylvia" version = "0.10.1" -source = "git+https://github.com/CosmWasm/sylvia?branch=jawoznia/cosmwasm1/map_err_on_into_response#a5d3546eb287e3e7f003251c529ff3401784e1bb" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64c8e892525ec035e5bdbeceec19309b1534734da7ff7bc69e9f639b077c497" dependencies = [ "anyhow", "cosmwasm-schema", @@ -1208,7 +1209,8 @@ dependencies = [ [[package]] name = "sylvia-derive" version = "0.10.1" -source = "git+https://github.com/CosmWasm/sylvia?branch=jawoznia/cosmwasm1/map_err_on_into_response#a5d3546eb287e3e7f003251c529ff3401784e1bb" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae5e0f41752efba2c6895514fa4caae742f69a2a73b4790bb6e365400c563bc1" dependencies = [ "convert_case", "itertools 0.12.1", diff --git a/Cargo.toml b/Cargo.toml index e5384b60..3937a620 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,9 +29,8 @@ mesh-converter = { path = "./contracts/consumer/converter" } mesh-simple-price-feed = { path = "./contracts/consumer/simple-price-feed" } mesh-virtual-staking = { path = "./contracts/consumer/virtual-staking" } -sylvia = { git = "https://github.com/CosmWasm/sylvia", branch = "jawoznia/cosmwasm1/map_err_on_into_response", features = [ - "mt", -] } +sylvia = "0.10.1" + cosmwasm-schema = "1.5.4" cosmwasm-std = { version = "1.5.4", features = ["ibc3", "cosmwasm_1_3"] } cw-storage-plus = "1.2.0" diff --git a/contracts/consumer/converter/src/contract.rs b/contracts/consumer/converter/src/contract.rs index a45887f2..caedb92d 100644 --- a/contracts/consumer/converter/src/contract.rs +++ b/contracts/consumer/converter/src/contract.rs @@ -305,7 +305,7 @@ impl ConverterContract<'_> { use price_feed_api::sv::Querier; use sylvia::types::Remote; let remote = Remote::< - dyn price_feed_api::PriceFeedApi< + &dyn price_feed_api::PriceFeedApi< Error = StdError, ExecC = custom::ConverterMsg, QueryC = custom::ConverterQuery, @@ -341,7 +341,7 @@ impl ConverterContract<'_> { use sylvia::types::Remote; // Note: it doesn't seem to matter which error type goes here... let remote = Remote::< - dyn price_feed_api::PriceFeedApi< + &dyn price_feed_api::PriceFeedApi< Error = StdError, ExecC = custom::ConverterMsg, QueryC = custom::ConverterQuery, diff --git a/packages/apis/src/virtual_staking_api.rs b/packages/apis/src/virtual_staking_api.rs index d05013a2..982855ce 100644 --- a/packages/apis/src/virtual_staking_api.rs +++ b/packages/apis/src/virtual_staking_api.rs @@ -1,7 +1,6 @@ // only for handle_valset_update but doesn't work for the autogenerated code #![allow(clippy::too_many_arguments)] - use cosmwasm_schema::cw_serde; use cosmwasm_std::{Coin, Response, StdError, Uint128, Validator}; use sylvia::cw_std::{CustomMsg, CustomQuery}; diff --git a/scripts/optimizer.sh b/scripts/optimizer.sh index 2d196567..12d84b87 100755 --- a/scripts/optimizer.sh +++ b/scripts/optimizer.sh @@ -1,4 +1,6 @@ -: +#!/bin/bash + +set -x U="cosmwasm" V="0.15.0" From c5bbcc4c3ad9175c7cf750fa555b655b9fbc724b Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 13 May 2024 16:46:25 +0200 Subject: [PATCH 20/20] More clippy fixes --- contracts/consumer/virtual-staking/src/contract.rs | 8 ++++---- contracts/provider/vault/src/multitest.rs | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/contracts/consumer/virtual-staking/src/contract.rs b/contracts/consumer/virtual-staking/src/contract.rs index 42a53555..52af7d8b 100644 --- a/contracts/consumer/virtual-staking/src/contract.rs +++ b/contracts/consumer/virtual-staking/src/contract.rs @@ -1322,7 +1322,7 @@ mod tests { impl VirtualStakingExt for VirtualStakingContract<'_> { fn quick_inst(&self, deps: DepsMut) { self.instantiate(InstantiateCtx { - deps: deps, + deps, env: mock_env(), info: mock_info("me", &[]), }) @@ -1371,7 +1371,7 @@ mod tests { self.bond( ExecCtx { - deps: deps, + deps, env: mock_env(), info: mock_info("me", &[]), }, @@ -1386,7 +1386,7 @@ mod tests { self.unbond( ExecCtx { - deps: deps, + deps, env: mock_env(), info: mock_info("me", &[]), }, @@ -1406,7 +1406,7 @@ mod tests { self.burn( ExecCtx { - deps: deps, + deps, env: mock_env(), info: mock_info("me", &[]), }, diff --git a/contracts/provider/vault/src/multitest.rs b/contracts/provider/vault/src/multitest.rs index 558bebf4..49893402 100644 --- a/contracts/provider/vault/src/multitest.rs +++ b/contracts/provider/vault/src/multitest.rs @@ -111,6 +111,7 @@ fn setup_without_local_staking<'app>( (vault, external) } +#[allow(clippy::type_complexity)] fn setup_inner<'app>( app: &'app App, owner: &'app str,