diff --git a/Cargo.lock b/Cargo.lock index a95235e8..7bcc9a80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1296,7 +1296,7 @@ dependencies = [ [[package]] name = "dfn_candid" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-05-29_23-02-base#b9a0f18dd5d6019e3241f205de797bca0d9cc3f8" +source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-10-17_03-07-base#e54d3fa34ded227c885d04e64505fa4b5d564743" dependencies = [ "candid", "dfn_core", @@ -1308,7 +1308,7 @@ dependencies = [ [[package]] name = "dfn_core" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-05-29_23-02-base#b9a0f18dd5d6019e3241f205de797bca0d9cc3f8" +source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-10-17_03-07-base#e54d3fa34ded227c885d04e64505fa4b5d564743" dependencies = [ "ic-base-types", "on_wire", @@ -1317,10 +1317,10 @@ dependencies = [ [[package]] name = "dfn_protobuf" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-05-29_23-02-base#b9a0f18dd5d6019e3241f205de797bca0d9cc3f8" +source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-10-17_03-07-base#e54d3fa34ded227c885d04e64505fa4b5d564743" dependencies = [ "on_wire", - "prost 0.12.6", + "prost", ] [[package]] @@ -1826,7 +1826,7 @@ dependencies = [ "hyper 1.4.1", "jsonwebtoken", "once_cell", - "prost 0.13.3", + "prost", "prost-types", "reqwest 0.12.7", "secret-vault-value", @@ -2848,7 +2848,7 @@ dependencies = [ "once_cell", "openidconnect", "priority-queue", - "prost 0.13.3", + "prost", "pulldown-cmark", "rand_chacha", "redb", @@ -3221,23 +3221,27 @@ dependencies = [ [[package]] name = "ic-base-types" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-05-29_23-02-base#b9a0f18dd5d6019e3241f205de797bca0d9cc3f8" +source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-10-17_03-07-base#e54d3fa34ded227c885d04e64505fa4b5d564743" dependencies = [ "byte-unit", "bytes", "candid", "comparable", + "hex", "ic-crypto-sha2", "ic-protobuf", "phantom_newtype", - "prost 0.12.6", + "prost", "serde", + "strum", + "strum_macros", ] [[package]] name = "ic-btc-interface" -version = "0.2.0" -source = "git+https://github.com/dfinity/bitcoin-canister?rev=62a71e47c491fb842ccc257b1c675651501f4b82#62a71e47c491fb842ccc257b1c675651501f4b82" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0152e14e697b0e988dbfdcb3f7e352d1c76a65b7d2d75c5d76bad22c3aca10d" dependencies = [ "candid", "serde", @@ -3245,9 +3249,9 @@ dependencies = [ ] [[package]] -name = "ic-btc-types-internal" +name = "ic-btc-replica-types" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-05-29_23-02-base#b9a0f18dd5d6019e3241f205de797bca0d9cc3f8" +source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-10-17_03-07-base#e54d3fa34ded227c885d04e64505fa4b5d564743" dependencies = [ "candid", "ic-btc-interface", @@ -3260,16 +3264,16 @@ dependencies = [ [[package]] name = "ic-canister-log" version = "0.2.0" -source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-05-29_23-02-base#b9a0f18dd5d6019e3241f205de797bca0d9cc3f8" +source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-10-17_03-07-base#e54d3fa34ded227c885d04e64505fa4b5d564743" dependencies = [ "serde", ] [[package]] name = "ic-cdk" -version = "0.13.5" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b1da6a25b045f9da3c9459c0cb2b0700ac368ee16382975a17185a23b9c18ab" +checksum = "dd8ecacd682fa05a985253592963306cb9799622d7b1cce4b1edb89c6ec85be1" dependencies = [ "candid", "ic-cdk-macros", @@ -3280,16 +3284,16 @@ dependencies = [ [[package]] name = "ic-cdk-macros" -version = "0.13.2" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a45800053d80a6df839a71aaea5797e723188c0b992618208ca3b941350c7355" +checksum = "0d4d857135deef20cc7ea8f3869a30cd9cfeb1392b3a81043790b2cd82adc3e0" dependencies = [ "candid", "proc-macro2", "quote", "serde", "serde_tokenstream", - "syn 1.0.109", + "syn 2.0.77", ] [[package]] @@ -3304,15 +3308,10 @@ dependencies = [ "sha2 0.10.8", ] -[[package]] -name = "ic-constants" -version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-05-29_23-02-base#b9a0f18dd5d6019e3241f205de797bca0d9cc3f8" - [[package]] name = "ic-crypto-internal-sha2" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-05-29_23-02-base#b9a0f18dd5d6019e3241f205de797bca0d9cc3f8" +source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-10-17_03-07-base#e54d3fa34ded227c885d04e64505fa4b5d564743" dependencies = [ "sha2 0.10.8", ] @@ -3320,7 +3319,7 @@ dependencies = [ [[package]] name = "ic-crypto-sha2" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-05-29_23-02-base#b9a0f18dd5d6019e3241f205de797bca0d9cc3f8" +source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-10-17_03-07-base#e54d3fa34ded227c885d04e64505fa4b5d564743" dependencies = [ "ic-crypto-internal-sha2", ] @@ -3328,7 +3327,7 @@ dependencies = [ [[package]] name = "ic-error-types" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-05-29_23-02-base#b9a0f18dd5d6019e3241f205de797bca0d9cc3f8" +source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-10-17_03-07-base#e54d3fa34ded227c885d04e64505fa4b5d564743" dependencies = [ "ic-protobuf", "ic-utils", @@ -3340,15 +3339,15 @@ dependencies = [ [[package]] name = "ic-ledger-canister-core" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-05-29_23-02-base#b9a0f18dd5d6019e3241f205de797bca0d9cc3f8" +source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-10-17_03-07-base#e54d3fa34ded227c885d04e64505fa4b5d564743" dependencies = [ "async-trait", "candid", "ic-base-types", "ic-canister-log", - "ic-constants", "ic-ledger-core", "ic-ledger-hash-of", + "ic-limits", "ic-management-canister-types", "ic-utils", "num-traits", @@ -3358,7 +3357,7 @@ dependencies = [ [[package]] name = "ic-ledger-core" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-05-29_23-02-base#b9a0f18dd5d6019e3241f205de797bca0d9cc3f8" +source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-10-17_03-07-base#e54d3fa34ded227c885d04e64505fa4b5d564743" dependencies = [ "candid", "ic-ledger-hash-of", @@ -3370,24 +3369,30 @@ dependencies = [ [[package]] name = "ic-ledger-hash-of" version = "0.1.0" -source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-05-29_23-02-base#b9a0f18dd5d6019e3241f205de797bca0d9cc3f8" +source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-10-17_03-07-base#e54d3fa34ded227c885d04e64505fa4b5d564743" dependencies = [ "candid", "hex", "serde", ] +[[package]] +name = "ic-limits" +version = "0.9.0" +source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-10-17_03-07-base#e54d3fa34ded227c885d04e64505fa4b5d564743" + [[package]] name = "ic-management-canister-types" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-05-29_23-02-base#b9a0f18dd5d6019e3241f205de797bca0d9cc3f8" +source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-10-17_03-07-base#e54d3fa34ded227c885d04e64505fa4b5d564743" dependencies = [ "candid", "ic-base-types", "ic-btc-interface", - "ic-btc-types-internal", + "ic-btc-replica-types", "ic-error-types", "ic-protobuf", + "ic-utils", "num-traits", "serde", "serde_bytes", @@ -3399,13 +3404,12 @@ dependencies = [ [[package]] name = "ic-protobuf" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-05-29_23-02-base#b9a0f18dd5d6019e3241f205de797bca0d9cc3f8" +source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-10-17_03-07-base#e54d3fa34ded227c885d04e64505fa4b5d564743" dependencies = [ "bincode", "candid", "erased-serde", - "maplit", - "prost 0.12.6", + "prost", "serde", "serde_json", "slog", @@ -3431,10 +3435,9 @@ dependencies = [ [[package]] name = "ic-utils" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-05-29_23-02-base#b9a0f18dd5d6019e3241f205de797bca0d9cc3f8" +source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-10-17_03-07-base#e54d3fa34ded227c885d04e64505fa4b5d564743" dependencies = [ "hex", - "prost 0.12.6", "scoped_threadpool", "serde", "serde_bytes", @@ -3456,9 +3459,9 @@ dependencies = [ [[package]] name = "ic0" -version = "0.21.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a54b5297861c651551676e8c43df805dad175cc33bc97dbd992edbbb85dcbcdf" +checksum = "8de254dd67bbd58073e23dc1c8553ba12fa1dc610a19de94ad2bbcd0460c067f" [[package]] name = "ic_bls12_381" @@ -3686,7 +3689,7 @@ dependencies = [ [[package]] name = "icp-ledger" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-05-29_23-02-base#b9a0f18dd5d6019e3241f205de797bca0d9cc3f8" +source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-10-17_03-07-base#e54d3fa34ded227c885d04e64505fa4b5d564743" dependencies = [ "candid", "comparable", @@ -3705,8 +3708,7 @@ dependencies = [ "lazy_static", "num-traits", "on_wire", - "prost 0.12.6", - "prost-derive 0.12.6", + "prost", "serde", "serde_bytes", "serde_cbor", @@ -3716,8 +3718,8 @@ dependencies = [ [[package]] name = "icrc-ledger-types" -version = "0.1.5" -source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-05-29_23-02-base#b9a0f18dd5d6019e3241f205de797bca0d9cc3f8" +version = "0.1.6" +source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-10-17_03-07-base#e54d3fa34ded227c885d04e64505fa4b5d564743" dependencies = [ "base32", "candid", @@ -3729,6 +3731,9 @@ dependencies = [ "serde", "serde_bytes", "sha2 0.10.8", + "strum", + "strum_macros", + "time", ] [[package]] @@ -4364,12 +4369,6 @@ dependencies = [ "quote", ] -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - [[package]] name = "match_cfg" version = "0.1.0" @@ -4611,7 +4610,7 @@ dependencies = [ [[package]] name = "on_wire" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-05-29_23-02-base#b9a0f18dd5d6019e3241f205de797bca0d9cc3f8" +source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-10-17_03-07-base#e54d3fa34ded227c885d04e64505fa4b5d564743" [[package]] name = "once_cell" @@ -4859,9 +4858,10 @@ dependencies = [ [[package]] name = "phantom_newtype" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-05-29_23-02-base#b9a0f18dd5d6019e3241f205de797bca0d9cc3f8" +source = "git+https://github.com/dfinity/ic?rev=tags/release-2024-10-17_03-07-base#e54d3fa34ded227c885d04e64505fa4b5d564743" dependencies = [ "candid", + "num-traits", "serde", "slog", ] @@ -5168,16 +5168,6 @@ version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "744a264d26b88a6a7e37cbad97953fa233b94d585236310bcbc88474b4092d79" -[[package]] -name = "prost" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" -dependencies = [ - "bytes", - "prost-derive 0.12.6", -] - [[package]] name = "prost" version = "0.13.3" @@ -5185,7 +5175,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" dependencies = [ "bytes", - "prost-derive 0.13.3", + "prost-derive", ] [[package]] @@ -5202,26 +5192,13 @@ dependencies = [ "once_cell", "petgraph", "prettyplease", - "prost 0.13.3", + "prost", "prost-types", "regex", "syn 2.0.77", "tempfile", ] -[[package]] -name = "prost-derive" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" -dependencies = [ - "anyhow", - "itertools 0.12.1", - "proc-macro2", - "quote", - "syn 2.0.77", -] - [[package]] name = "prost-derive" version = "0.13.3" @@ -5241,7 +5218,7 @@ version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4759aa0d3a6232fb8dbdb97b61de2c20047c68aca932c7ed76da9d788508d670" dependencies = [ - "prost 0.13.3", + "prost", ] [[package]] @@ -5993,7 +5970,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc32a777b53b3433b974c9c26b6d502a50037f8da94e46cb8ce2ced2cfdfaea0" dependencies = [ - "prost 0.13.3", + "prost", "prost-types", "serde", "serde_json", @@ -6188,13 +6165,14 @@ dependencies = [ [[package]] name = "serde_tokenstream" -version = "0.1.7" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "797ba1d80299b264f3aac68ab5d12e5825a561749db4df7cd7c8083900c5d4e9" +checksum = "64060d864397305347a78851c51588fd283767e7e7589829e8121d65512340f1" dependencies = [ "proc-macro2", + "quote", "serde", - "syn 1.0.109", + "syn 2.0.77", ] [[package]] @@ -6983,7 +6961,7 @@ dependencies = [ "hyper-util", "percent-encoding", "pin-project 1.1.5", - "prost 0.13.3", + "prost", "rustls-pemfile 2.1.3", "socket2", "tokio", @@ -7744,7 +7722,7 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yral-canisters-client" version = "0.1.0" -source = "git+https://github.com/yral-dapp/yral-common.git?rev=2e432882052a69d5a625e7188cd69ad8aa5bcec7#2e432882052a69d5a625e7188cd69ad8aa5bcec7" +source = "git+https://github.com/yral-dapp/yral-common.git?rev=0247d886ad95747deee4618c3fd42524ba78d5fe#0247d886ad95747deee4618c3fd42524ba78d5fe" dependencies = [ "anyhow", "candid", @@ -7794,7 +7772,7 @@ dependencies = [ [[package]] name = "yral-qstash-types" version = "0.1.0" -source = "git+https://github.com/yral-dapp/yral-common.git?rev=2e432882052a69d5a625e7188cd69ad8aa5bcec7#2e432882052a69d5a625e7188cd69ad8aa5bcec7" +source = "git+https://github.com/yral-dapp/yral-common.git?rev=0247d886ad95747deee4618c3fd42524ba78d5fe#0247d886ad95747deee4618c3fd42524ba78d5fe" dependencies = [ "candid", "serde", @@ -7815,7 +7793,7 @@ dependencies = [ [[package]] name = "yral-types" version = "0.1.0" -source = "git+https://github.com/yral-dapp/yral-common.git?rev=2e432882052a69d5a625e7188cd69ad8aa5bcec7#2e432882052a69d5a625e7188cd69ad8aa5bcec7" +source = "git+https://github.com/yral-dapp/yral-common.git?rev=0247d886ad95747deee4618c3fd42524ba78d5fe#0247d886ad95747deee4618c3fd42524ba78d5fe" dependencies = [ "ic-agent", "k256", diff --git a/sns-validation/src/config.rs b/sns-validation/src/config.rs index 8371b206..fa7c7648 100644 --- a/sns-validation/src/config.rs +++ b/sns-validation/src/config.rs @@ -5,12 +5,10 @@ use crate::{ humanize, pbs::{ gov_pb::CreateServiceNervousSystem, - nns_pb::{self, GlobalTimeOfDay, Image}, + nns_pb::{self, Image}, sns_pb::SnsInitPayload, - ExecutedCreateServiceNervousSystemProposal, }, }; -use web_time::{SystemTime, UNIX_EPOCH}; // Alias CreateServiceNervousSystem-related types, but since we have many // related types in this module, put these aliases in their own module to avoid @@ -32,7 +30,7 @@ mod nns_governance_pb { // the format that we are trying to implement here. // // (Thanks to the magic of serde, all the code here is declarative.) -#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq, Eq)] +#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize)] #[serde(deny_unknown_fields)] pub struct SnsConfigurationFile { pub name: String, @@ -68,7 +66,7 @@ pub struct SnsConfigurationFile { pub nns_proposal: NnsProposal, } -#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq, Eq)] +#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)] #[serde(deny_unknown_fields)] pub struct PrincipalAlias { id: String, // PrincipalId @@ -76,7 +74,7 @@ pub struct PrincipalAlias { email: Option, } -#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq, Eq)] +#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)] #[serde(deny_unknown_fields)] pub struct Token { pub name: String, @@ -86,7 +84,7 @@ pub struct Token { pub logo_b64: String, } -#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq, Eq, Clone)] +#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)] #[serde(deny_unknown_fields)] pub struct Proposals { #[serde(with = "humanize::ser_de::tokens")] @@ -99,14 +97,14 @@ pub struct Proposals { pub maximum_wait_for_quiet_deadline_extension: nns_pb::Duration, } -#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq, Eq, Clone)] +#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)] #[serde(deny_unknown_fields)] pub struct Neurons { #[serde(with = "humanize::ser_de::tokens")] pub minimum_creation_stake: nns_pb::Tokens, } -#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq, Eq, Clone)] +#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)] #[serde(deny_unknown_fields)] pub struct Voting { #[serde(with = "humanize::ser_de::duration")] @@ -119,7 +117,7 @@ pub struct Voting { pub reward_rate: RewardRate, } -#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq, Eq, Clone)] +#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)] #[serde(deny_unknown_fields)] pub struct MaximumVotingPowerBonuses { #[serde(rename = "DissolveDelay")] @@ -129,7 +127,7 @@ pub struct MaximumVotingPowerBonuses { pub age: Bonus, } -#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq, Eq, Clone)] +#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)] #[serde(deny_unknown_fields)] pub struct Bonus { #[serde(with = "humanize::ser_de::duration")] @@ -139,7 +137,7 @@ pub struct Bonus { pub bonus: nns_pb::Percentage, } -#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq, Eq, Clone)] +#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)] #[serde(deny_unknown_fields)] pub struct RewardRate { #[serde(with = "humanize::ser_de::percentage")] @@ -152,7 +150,7 @@ pub struct RewardRate { pub transition_duration: nns_pb::Duration, } -#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq, Eq, Clone)] +#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)] #[serde(deny_unknown_fields)] pub struct Swap { pub minimum_participants: u64, @@ -196,7 +194,7 @@ pub struct Swap { pub neurons_fund_participation: Option, } -#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq, Eq, Clone)] +#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)] #[serde(deny_unknown_fields)] pub struct VestingSchedule { pub events: u64, @@ -205,7 +203,7 @@ pub struct VestingSchedule { pub interval: nns_pb::Duration, } -#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq, Eq, Clone)] +#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)] #[serde(deny_unknown_fields)] pub struct Distribution { #[serde(rename = "Neurons")] @@ -218,7 +216,7 @@ pub struct Distribution { pub total: nns_pb::Tokens, } -#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq, Eq, Clone)] +#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)] #[serde(deny_unknown_fields)] pub struct Neuron { pub principal: String, // Principal (alias) @@ -236,7 +234,7 @@ pub struct Neuron { pub vesting_period: nns_pb::Duration, } -#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq, Eq, Clone)] +#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)] #[serde(deny_unknown_fields)] pub struct InitialBalances { #[serde(with = "humanize::ser_de::tokens")] @@ -246,7 +244,7 @@ pub struct InitialBalances { pub swap: nns_pb::Tokens, } -#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq, Eq, Clone)] +#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct NnsProposal { pub title: String, pub summary: String, @@ -257,7 +255,7 @@ struct AliasToPrincipalId<'a> { #[allow(unused)] source: &'a Vec, /* TODO - #[derive(Debug, PartialEq, Eq, Hash)] + #[derive(Eq, PartialEq, Hash, Debug)] enum Key { // TODO: This name is just a placeholder. Name(String), Email(String), @@ -410,24 +408,19 @@ impl SnsConfigurationFile { Ok(result) } - pub fn try_convert_to_executed_sns_init(&self) -> Result { - let create_sns = self.try_convert_to_create_service_nervous_system()?; - let executed_create_sns = ExecutedCreateServiceNervousSystemProposal { - current_timestamp_seconds: SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs(), - create_service_nervous_system: create_sns, - random_swap_start_time: GlobalTimeOfDay { - seconds_after_utc_midnight: Some(0), - }, - neurons_fund_participation_constraints: None, - // `proposal_id` only exists to be exposed to the user for audit purposes, which don't apply here. - // But it's required, so we can just use any arbitrary value. - proposal_id: 10, - }; - - SnsInitPayload::try_from(executed_create_sns) + pub fn try_convert_to_sns_init_payload(&self) -> Result { + let create_nervous_system = self.try_convert_to_create_service_nervous_system()?; + let now = web_time::SystemTime::now() + .duration_since(web_time::UNIX_EPOCH) + .unwrap() + .as_secs(); + let mut sns_init = SnsInitPayload::try_from(create_nervous_system)?; + sns_init.nns_proposal_id = Some(1); + sns_init.swap_start_timestamp_seconds = Some(now - 1000); + sns_init.swap_due_timestamp_seconds = Some(now + 300); + sns_init.validate_post_execution()?; + + Ok(sns_init) } } @@ -580,14 +573,12 @@ impl Token { let token_symbol = Some(symbol.clone()); let transaction_fee = Some(*transaction_fee); - let token_logo = logo_b64.clone(); - nns_governance_pb::LedgerParameters { token_name, token_symbol, transaction_fee, token_logo: Some(Image { - base64_encoding: Some(token_logo), + base64_encoding: Some(logo_b64.clone()), }), } } diff --git a/sns-validation/src/pbs/gov_pb.rs b/sns-validation/src/pbs/gov_pb.rs index d6581f65..35bc75c6 100644 --- a/sns-validation/src/pbs/gov_pb.rs +++ b/sns-validation/src/pbs/gov_pb.rs @@ -8,48 +8,131 @@ pub struct NeuronId { pub id: Vec, } -#[derive(candid::CandidType, candid::Deserialize)] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq)] +/// The nervous system's parameters, which are parameters that can be changed, via proposals, +/// by each nervous system community. +/// For some of the values there are specified minimum values (floor) or maximum values +/// (ceiling). The motivation for this is a) to prevent that the nervous system accidentally +/// chooses parameters that result in an un-upgradable (and thus stuck) governance canister +/// and b) to prevent the canister from growing too big (which could harm the other canisters +/// on the subnet). +/// +/// Required invariant: the canister code assumes that all system parameters are always set. +#[derive(candid::CandidType, candid::Deserialize, Clone, PartialEq)] pub struct NervousSystemParameters { + /// The number of e8s (10E-8 of a token) that a rejected + /// proposal costs the proposer. pub reject_cost_e8s: ::core::option::Option, - + /// The minimum number of e8s (10E-8 of a token) that can be staked in a neuron. + /// + /// To ensure that staking and disbursing of the neuron work, the chosen value + /// must be larger than the transaction_fee_e8s. pub neuron_minimum_stake_e8s: ::core::option::Option, - + /// The transaction fee that must be paid for ledger transactions (except + /// minting and burning governance tokens). pub transaction_fee_e8s: ::core::option::Option, - + /// The maximum number of proposals to keep, per action. When the + /// total number of proposals for a given action is greater than this + /// number, the oldest proposals that have reached final decision state + /// (rejected, executed, or failed) and final rewards status state + /// (settled) may be deleted. + /// + /// The number must be larger than zero and at most be as large as the + /// defined ceiling MAX_PROPOSALS_TO_KEEP_PER_ACTION_CEILING. pub max_proposals_to_keep_per_action: ::core::option::Option, - + /// The initial voting period of a newly created proposal. + /// A proposal's voting period may then be further increased during + /// a proposal's lifecycle due to the wait-for-quiet algorithm. + /// + /// The voting period must be between (inclusive) the defined floor + /// INITIAL_VOTING_PERIOD_SECONDS_FLOOR and ceiling + /// INITIAL_VOTING_PERIOD_SECONDS_CEILING. pub initial_voting_period_seconds: ::core::option::Option, - + /// The wait for quiet algorithm extends the voting period of a proposal when + /// there is a flip in the majority vote during the proposal's voting period. + /// This parameter determines the maximum time period that the voting period + /// may be extended after a flip. If there is a flip at the very end of the + /// original proposal deadline, the remaining time will be set to this parameter. + /// If there is a flip before or after the original deadline, the deadline will + /// extended by somewhat less than this parameter. + /// The maximum total voting period extension is 2 * wait_for_quiet_deadline_increase_seconds. + /// For more information, see the wiki page on the wait-for-quiet algorithm: + /// pub wait_for_quiet_deadline_increase_seconds: ::core::option::Option, - + /// TODO NNS1-2169: This field currently has no effect. + /// TODO NNS1-2169: Design and implement this feature. + /// + /// The set of default followees that every newly created neuron will follow + /// per function. This is specified as a mapping of proposal functions to followees. + /// + /// If unset, neurons will have no followees by default. + /// The set of followees for each function can be at most of size + /// max_followees_per_function. pub default_followees: ::core::option::Option, - + /// The maximum number of allowed neurons. When this maximum is reached, no new + /// neurons will be created until some are removed. + /// + /// This number must be larger than zero and at most as large as the defined + /// ceiling MAX_NUMBER_OF_NEURONS_CEILING. pub max_number_of_neurons: ::core::option::Option, - + /// The minimum dissolve delay a neuron must have to be eligible to vote. + /// + /// The chosen value must be smaller than max_dissolve_delay_seconds. pub neuron_minimum_dissolve_delay_to_vote_seconds: ::core::option::Option, - + /// The maximum number of followees each neuron can establish for each nervous system function. + /// + /// This number can be at most as large as the defined ceiling + /// MAX_FOLLOWEES_PER_FUNCTION_CEILING. pub max_followees_per_function: ::core::option::Option, - + /// The maximum dissolve delay that a neuron can have. That is, the maximum + /// that a neuron's dissolve delay can be increased to. The maximum is also enforced + /// when saturating the dissolve delay bonus in the voting power computation. pub max_dissolve_delay_seconds: ::core::option::Option, - + /// The age of a neuron that saturates the age bonus for the voting power computation. pub max_neuron_age_for_age_bonus: ::core::option::Option, - + /// The max number of proposals for which ballots are still stored, i.e., + /// unsettled proposals. If this number of proposals is reached, new proposals + /// can only be added in exceptional cases (for few proposals it is defined + /// that they are allowed even if resources are low to guarantee that the relevant + /// canisters can be upgraded). + /// + /// This number must be larger than zero and at most as large as the defined + /// ceiling MAX_NUMBER_OF_PROPOSALS_WITH_BALLOTS_CEILING. pub max_number_of_proposals_with_ballots: ::core::option::Option, - + /// The default set of neuron permissions granted to the principal claiming a neuron. pub neuron_claimer_permissions: ::core::option::Option, - + /// The superset of neuron permissions a principal with permission + /// `NeuronPermissionType::ManagePrincipals` for a given neuron can grant to another + /// principal for this same neuron. + /// If this set changes via a ManageNervousSystemParameters proposal, previous + /// neurons' permissions will be unchanged and only newly granted permissions will be affected. pub neuron_grantable_permissions: ::core::option::Option, - + /// The maximum number of principals that can have permissions for a neuron pub max_number_of_principals_per_neuron: ::core::option::Option, - + /// When this field is not populated, voting rewards are "disabled". Once this + /// is set, it probably should not be changed, because the results would + /// probably be pretty confusing. pub voting_rewards_parameters: ::core::option::Option, - + /// E.g. if a large dissolve delay can double the voting power of a neuron, + /// then this field would have a value of 100, indicating a maximum of + /// 100% additional voting power. + /// + /// For no bonus, this should be set to 0. + /// + /// To achieve functionality equivalent to NNS, this should be set to 100. pub max_dissolve_delay_bonus_percentage: ::core::option::Option, - + /// Analogous to the previous field (see the previous comment), + /// but this one relates to neuron age instead of dissolve delay. + /// + /// To achieve functionality equivalent to NNS, this should be set to 25. pub max_age_bonus_percentage: ::core::option::Option, - + /// By default, maturity modulation is enabled; however, an SNS can use this + /// field to disable it. When disabled, this canister will still poll the + /// Cycles Minting Canister (CMC), and store the value received therefrom. + /// However, the fetched value does not get used when this is set to true. + /// + /// The reason we call this "disabled" instead of (positive) "enabled" is so + /// that the PB default (bool fields are false) and our application default + /// (enabled) agree. pub maturity_modulation_disabled: ::core::option::Option, } diff --git a/sns-validation/src/pbs/mod.rs b/sns-validation/src/pbs/mod.rs index 2a416da6..ddedc13b 100644 --- a/sns-validation/src/pbs/mod.rs +++ b/sns-validation/src/pbs/mod.rs @@ -1,7 +1,6 @@ use gov_pb::{create_sns, CreateServiceNervousSystem}; use nns_pb::{Duration, GlobalTimeOfDay}; use sns_pb::{sns_init_payload, SnsInitPayload}; -use sns_swap_pb::NeuronsFundParticipationConstraints; use crate::consts::ONE_DAY_SECONDS; @@ -34,6 +33,7 @@ fn divide_perfectly(field_name: &str, dividend: u64, divisor: u64) -> Result for SnsInitPayload { type Error = String; + // This validation should just be put into separate function fn try_from(src: CreateServiceNervousSystem) -> Result { let CreateServiceNervousSystem { name, @@ -261,9 +261,8 @@ impl TryFrom for SnsInitPayload { neurons_fund_participation, // These are not known from only the CreateServiceNervousSystem - // proposal. See TryFrom + // proposal. See `Governance::make_sns_init_payload`. nns_proposal_id: None, - neurons_fund_participants: None, swap_start_timestamp_seconds: None, swap_due_timestamp_seconds: None, neurons_fund_participation_constraints: None, @@ -453,65 +452,6 @@ impl TryFrom, -} - -impl TryFrom for SnsInitPayload { - type Error = String; - - fn try_from(src: ExecutedCreateServiceNervousSystemProposal) -> Result { - let mut defects = vec![]; - - let current_timestamp_seconds = src.current_timestamp_seconds; - let nns_proposal_id = Some(src.proposal_id); - let neurons_fund_participation_constraints = src.neurons_fund_participation_constraints; - let start_time = src - .create_service_nervous_system - .swap_parameters - .as_ref() - .and_then(|swap_parameters| swap_parameters.start_time); - let duration = src - .create_service_nervous_system - .swap_parameters - .as_ref() - .and_then(|swap_parameters| swap_parameters.duration); - - let (swap_start_timestamp_seconds, swap_due_timestamp_seconds) = - match CreateServiceNervousSystem::swap_start_and_due_timestamps( - start_time.unwrap_or(src.random_swap_start_time), - duration.unwrap_or_default(), - current_timestamp_seconds, - ) { - Ok((swap_start_timestamp_seconds, swap_due_timestamp_seconds)) => ( - Some(swap_start_timestamp_seconds), - Some(swap_due_timestamp_seconds), - ), - Err(err) => { - defects.push(err); - (None, None) - } - }; - - let mut result = SnsInitPayload::try_from(src.create_service_nervous_system)?; - - result.nns_proposal_id = nns_proposal_id; - result.swap_start_timestamp_seconds = swap_start_timestamp_seconds; - result.swap_due_timestamp_seconds = swap_due_timestamp_seconds; - result.neurons_fund_participation_constraints = neurons_fund_participation_constraints; - - result.validate_post_execution()?; - - Ok(result) - } -} - impl CreateServiceNervousSystem { pub fn sns_token_e8s(&self) -> Option { self.initial_token_distribution diff --git a/sns-validation/src/pbs/sns_pb.rs b/sns-validation/src/pbs/sns_pb.rs index 77f96cfb..ef569b33 100644 --- a/sns-validation/src/pbs/sns_pb.rs +++ b/sns-validation/src/pbs/sns_pb.rs @@ -1,84 +1,176 @@ -#[derive(candid::CandidType, candid::Deserialize, serde::Serialize, Eq)] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, Debug)] +#[derive( + candid::CandidType, candid::Deserialize, serde::Serialize, Debug, Eq, Clone, PartialEq, +)] pub struct SnsInitPayload { + /// Fee of a transaction. pub transaction_fee_e8s: ::core::option::Option, - + /// The name of the token issued by an SNS Ledger. + /// This field has no default, a value must be provided by the user. + /// Must be a string length between {} and {} characters + /// + /// Example: Bitcoin pub token_name: ::core::option::Option, - + /// The symbol of the token issued by an SNS Ledger. This field has no + /// default, a value must be provided by the user. Must be a string length + /// between 3 and 10 characters pub token_symbol: ::core::option::Option, - + /// Cost of making a proposal that doesnt pass. pub proposal_reject_cost_e8s: ::core::option::Option, - + /// The minimum amount of SNS Token e8s an SNS Ledger account must have to stake a neuron. pub neuron_minimum_stake_e8s: ::core::option::Option, - + /// If the swap fails, control of the dapp canister(s) will be set to these + /// principal IDs. In most use-cases, this would be the same as the original + /// set of controller(s). Must not be empty. pub fallback_controller_principal_ids: Vec, - + /// The logo for the SNS project represented as a base64 encoded string. pub logo: ::core::option::Option, - + /// Url to the dapp controlled by the SNS project. pub url: ::core::option::Option, - + /// Name of the SNS project. This may differ from the name of the associated token. pub name: ::core::option::Option, - + /// Description of the SNS project. pub description: ::core::option::Option, - + /// The minimum dissolve_delay in seconds a neuron must have to be able to cast votes on proposals. pub neuron_minimum_dissolve_delay_to_vote_seconds: ::core::option::Option, - + /// The amount of rewards is proportional to token_supply * current_rate. In + /// turn, current_rate is somewhere between these two values. In the first + /// reward period, it is the initial growth rate, and after the growth rate + /// transition period has elapsed, the growth rate becomes the final growth + /// rate, and remains at that value for the rest of time. The transition + /// between the initial and final growth rates is quadratic, and levels out at + /// the end of the growth rate transition period. + /// + /// (A basis point is one in ten thousand.) pub initial_reward_rate_basis_points: ::core::option::Option, - pub final_reward_rate_basis_points: ::core::option::Option, + pub final_reward_rate_basis_points: ::core::option::Option, + /// The amount of time that the growth rate changes (presumably, decreases) + /// from the initial growth rate to the final growth rate. (See the two + /// *_reward_rate_basis_points fields bellow.) The transition is quadratic, and + /// levels out at the end of the growth rate transition period. pub reward_rate_transition_duration_seconds: ::core::option::Option, - + /// The maximum dissolve delay that a neuron can have. That is, the maximum + /// that a neuron's dissolve delay can be increased to. The maximum is also enforced + /// when saturating the dissolve delay bonus in the voting power computation. pub max_dissolve_delay_seconds: ::core::option::Option, - + /// The age of a neuron that saturates the age bonus for the voting power computation. pub max_neuron_age_seconds_for_age_bonus: ::core::option::Option, - + /// E.g. if a large dissolve delay can double the voting power of a neuron, + /// then this field would have a value of 2.0. + /// + /// For no bonus, this should be set to 1. + /// + /// To achieve functionality equivalent to NNS, this should be set to 2. pub max_dissolve_delay_bonus_percentage: ::core::option::Option, - + /// Analogous to the previous field (see the previous comment), + /// but this one relates to neuron age instead of dissolve delay. + /// + /// To achieve functionality equivalent to NNS, this should be set to 1.25. pub max_age_bonus_percentage: ::core::option::Option, - + /// The initial voting period of a newly created proposal. + /// A proposal's voting period may then be further increased during + /// a proposal's lifecycle due to the wait-for-quiet algorithm. + /// + /// The voting period must be between (inclusive) the defined floor + /// INITIAL_VOTING_PERIOD_SECONDS_FLOOR and ceiling + /// INITIAL_VOTING_PERIOD_SECONDS_CEILING. pub initial_voting_period_seconds: ::core::option::Option, - + /// The wait for quiet algorithm extends the voting period of a proposal when + /// there is a flip in the majority vote during the proposal's voting period. + /// This parameter determines the maximum time period that the voting period + /// may be extended after a flip. If there is a flip at the very end of the + /// original proposal deadline, the remaining time will be set to this parameter. + /// If there is a flip before or after the original deadline, the deadline will + /// extended by somewhat less than this parameter. + /// The maximum total voting period extension is 2 * wait_for_quiet_deadline_increase_seconds. + /// For more information, see the wiki page on the wait-for-quiet algorithm: + /// pub wait_for_quiet_deadline_increase_seconds: ::core::option::Option, - + /// An optional text that swap participants should confirm before they may + /// participate in the swap. If the field is set, its value should be plain text + /// with at least 1 and at most 1,000 characters. pub confirmation_text: ::core::option::Option, - - pub restricted_countries: ::core::option::Option, - + /// An optional set of countries that should not participate in the swap. + pub restricted_countries: ::core::option::Option, + /// / Canisters that will be transferred to an SNS. pub dapp_canisters: ::core::option::Option, - + /// The minimum number of buyers that must participate for the swap + /// to take place. Must be greater than zero. pub min_participants: ::core::option::Option, - + /// The total number of ICP that is required for this token swap to + /// take place. This number divided by the number of SNS tokens being + /// offered gives the seller's reserve price for the swap, i.e., the + /// minimum number of ICP per SNS tokens that the seller of SNS + /// tokens is willing to accept. If this amount is not achieved, the + /// swap will be aborted (instead of committed) when the due date/time + /// occurs. Must be smaller than or equal to `max_icp_e8s`. pub min_icp_e8s: ::core::option::Option, - + /// The number of ICP that is "targeted" by this token swap. If this + /// amount is achieved with sufficient participation, the swap will be + /// triggered immediately, without waiting for the due date + /// (`end_timestamp_seconds`). This means that an investor knows the minimum + /// number of SNS tokens received per invested ICP. If this amount is achieved + /// without reaching sufficient_participation, the swap will abort without + /// waiting for the due date. Must be at least + /// `min_participants * min_participant_icp_e8s`. pub max_icp_e8s: ::core::option::Option, - + /// The amount of ICP that is required to be directly contributed for this + /// token swap to take place. This number + the minimum NF contribution divided + /// by the number of SNS tokens being offered gives the seller's reserve price + /// for the swap, i.e., the minimum number of ICP per SNS tokens that the + /// seller of SNS tokens is willing to accept. If this amount is not achieved, + /// the swap will be aborted (instead of committed) when the due date/time + /// occurs. Must be smaller than or equal to `max_icp_e8s`. pub min_direct_participation_icp_e8s: ::core::option::Option, - + /// The amount of ICP that this token swap is "targeting" for direct + /// contribution. If this amount is achieved with sufficient participation, the + /// swap will be triggered immediately, without waiting for the due date + /// (`end_timestamp_seconds`). This means that an investor knows the minimum + /// number of SNS tokens received per invested ICP. If this amount is achieved + /// without reaching sufficient_participation, the swap will abort without + /// waiting for the due date. Must be at least + /// `min_participants * min_participant_icp_e8s`. pub max_direct_participation_icp_e8s: ::core::option::Option, - + /// The minimum amount of ICP that each buyer must contribute to + /// participate. Must be greater than zero. pub min_participant_icp_e8s: ::core::option::Option, - + /// The maximum amount of ICP that each buyer can contribute. Must be + /// greater than or equal to `min_participant_icp_e8s` and less than + /// or equal to `max_icp_e8s`. Can effectively be disabled by + /// setting it to `max_icp_e8s`. pub max_participant_icp_e8s: ::core::option::Option, - + /// The date/time when the swap should start. pub swap_start_timestamp_seconds: ::core::option::Option, - + /// The date/time when the swap is due, i.e., it will automatically + /// end and commit or abort depending on whether the parameters have + /// been fulfilled. pub swap_due_timestamp_seconds: ::core::option::Option, - + /// The construction parameters for the basket of neurons created for all + /// investors in the decentralization swap. Each investor, whether via + /// the Neurons' Fund or direct, will receive `count` Neurons with + /// increasing dissolve delays. The total number of Tokens swapped for + /// by the investor will be evenly distributed across the basket. This is + /// effectively a vesting schedule to ensure there is a gradual release of + /// SNS Tokens available to all investors instead of being liquid immediately. + /// See `NeuronBasketConstructionParameters` for more details on how + /// the basket is configured. pub neuron_basket_construction_parameters: ::core::option::Option, - + /// The ID of the NNS proposal submitted to launch this SNS decentralization + /// swap. pub nns_proposal_id: ::core::option::Option, - + /// Whether or not the neurons' fund is participating pub neurons_fund_participation: ::core::option::Option, - - pub neurons_fund_participants: ::core::option::Option, - + /// The token_logo for the SNS project represented as a base64 encoded string. pub token_logo: ::core::option::Option, - + /// Constraints for the Neurons' Fund participation in this swap. These constraints passed from + /// the NNS Governance (via SNS-W) to an SNS Swap to determine the Neurons' Fund participation + /// amount as a function of the direct participation amount. pub neurons_fund_participation_constraints: ::core::option::Option, - + /// The initial tokens and neurons available at genesis will be distributed according + /// to the strategy and configuration picked via the initial_token_distribution + /// parameter. pub initial_token_distribution: ::core::option::Option, } @@ -157,9 +249,3 @@ pub struct NeuronDistribution { pub struct DappCanisters { pub canisters: Vec, } -#[derive(candid::CandidType, candid::Deserialize, serde::Serialize, Eq)] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, Debug)] -pub struct NeuronsFundParticipants { - pub participants: Vec, -} diff --git a/sns-validation/src/validation/sns_init.rs b/sns-validation/src/validation/sns_init.rs index 7272cac9..0120dbb4 100644 --- a/sns-validation/src/validation/sns_init.rs +++ b/sns-validation/src/validation/sns_init.rs @@ -31,8 +31,6 @@ pub const MAX_FALLBACK_CONTROLLER_PRINCIPAL_IDS_COUNT: usize = 15; pub const MAX_DIRECT_ICP_CONTRIBUTION_TO_SWAP: u64 = 1_000_000_000 * E8; -pub const MAX_NEURONS_FOR_DIRECT_PARTICIPANTS: u64 = 100_000; - pub const MIN_SNS_NEURONS_PER_BASKET: u64 = 2; pub const MAX_SNS_NEURONS_PER_BASKET: u64 = 10; @@ -509,6 +507,77 @@ impl FractionalDeveloperVotingPower { } impl SnsInitPayload { + /// Due to conflict with the prost derived macros on the generated Rust structs, this method + /// acts like `SnsInitPayload::default()` except that it will provide default "real" values + /// for default-able parameters. + pub fn with_default_values() -> Self { + let nervous_system_parameters_default = NervousSystemParameters::with_default_values(); + let voting_rewards_parameters = nervous_system_parameters_default + .voting_rewards_parameters + .as_ref() + .unwrap(); + Self { + transaction_fee_e8s: nervous_system_parameters_default.transaction_fee_e8s, + reward_rate_transition_duration_seconds: voting_rewards_parameters + .reward_rate_transition_duration_seconds, + initial_reward_rate_basis_points: voting_rewards_parameters + .initial_reward_rate_basis_points, + final_reward_rate_basis_points: voting_rewards_parameters + .final_reward_rate_basis_points, + token_name: None, + token_symbol: None, + token_logo: None, + proposal_reject_cost_e8s: nervous_system_parameters_default.reject_cost_e8s, + neuron_minimum_stake_e8s: nervous_system_parameters_default.neuron_minimum_stake_e8s, + neuron_minimum_dissolve_delay_to_vote_seconds: nervous_system_parameters_default + .neuron_minimum_dissolve_delay_to_vote_seconds, + initial_token_distribution: None, + fallback_controller_principal_ids: vec![], + logo: None, + url: None, + name: None, + description: None, + max_dissolve_delay_seconds: nervous_system_parameters_default + .max_dissolve_delay_seconds, + max_neuron_age_seconds_for_age_bonus: nervous_system_parameters_default + .max_neuron_age_for_age_bonus, + max_dissolve_delay_bonus_percentage: nervous_system_parameters_default + .max_dissolve_delay_bonus_percentage, + max_age_bonus_percentage: nervous_system_parameters_default.max_age_bonus_percentage, + initial_voting_period_seconds: nervous_system_parameters_default + .initial_voting_period_seconds, + wait_for_quiet_deadline_increase_seconds: nervous_system_parameters_default + .wait_for_quiet_deadline_increase_seconds, + dapp_canisters: None, + min_participants: None, + min_icp_e8s: None, + max_icp_e8s: None, + min_direct_participation_icp_e8s: None, + max_direct_participation_icp_e8s: None, + min_participant_icp_e8s: None, + max_participant_icp_e8s: None, + swap_start_timestamp_seconds: None, + swap_due_timestamp_seconds: None, + neuron_basket_construction_parameters: None, + confirmation_text: None, + restricted_countries: None, + nns_proposal_id: None, + neurons_fund_participation_constraints: None, + neurons_fund_participation: None, + } + } + + fn get_swap_distribution(&self) -> Result<&SwapDistribution, String> { + match &self.initial_token_distribution { + None => Err("Error: initial-token-distribution must be specified".to_string()), + Some(InitialTokenDistribution::FractionalDeveloperVotingPower(f)) => { + f.swap_distribution() + } + } + } + + /// Returns a complete NervousSystemParameter struct with its corresponding SnsInitPayload + /// fields filled out. fn get_nervous_system_parameters(&self) -> NervousSystemParameters { let nervous_system_parameters = NervousSystemParameters::with_default_values(); let all_permissions = NeuronPermissionList { @@ -551,7 +620,6 @@ impl SnsInitPayload { swap_due_timestamp_seconds: _, neuron_basket_construction_parameters: _, nns_proposal_id: _, - neurons_fund_participants: _, token_logo: _, neurons_fund_participation_constraints: _, neurons_fund_participation: _, @@ -582,15 +650,9 @@ impl SnsInitPayload { } } - fn get_swap_distribution(&self) -> Result<&SwapDistribution, String> { - match &self.initial_token_distribution { - None => Err("Error: initial-token-distribution must be specified".to_string()), - Some(InitialTokenDistribution::FractionalDeveloperVotingPower(f)) => { - f.swap_distribution() - } - } - } - + /// Validates all the fields that are shared with CreateServiceNervousSystem. + /// For use in e.g. the SNS CLI or in NNS Governance before the proposal has + /// been executed. pub fn validate_pre_execution(&self) -> Result { let validation_fns = [ self.validate_token_symbol(), @@ -623,7 +685,6 @@ impl SnsInitPayload { // Ensure that the values that can only be known after the execution // of the CreateServiceNervousSystem proposal are not set. self.validate_nns_proposal_id_pre_execution(), - self.validate_neurons_fund_participants_pre_execution(), self.validate_swap_start_timestamp_seconds_pre_execution(), self.validate_swap_due_timestamp_seconds_pre_execution(), self.validate_neurons_fund_participation_constraints(true), @@ -642,8 +703,10 @@ impl SnsInitPayload { self.validate_token_name(), self.validate_token_logo(), self.validate_token_distribution(), + self.validate_participation_constraints(), self.validate_neuron_minimum_stake_e8s(), self.validate_neuron_minimum_dissolve_delay_to_vote_seconds(), + self.validate_neuron_basket_construction_params(), self.validate_proposal_reject_cost_e8s(), self.validate_transaction_fee_e8s(), self.validate_fallback_controller_principal_ids(), @@ -664,20 +727,14 @@ impl SnsInitPayload { self.validate_confirmation_text(), self.validate_restricted_countries(), self.validate_all_post_execution_swap_parameters_are_set(), - self.validate_neuron_basket_construction_params(), - self.validate_min_participants(), - self.validate_min_icp_e8s(), - self.validate_max_icp_e8s(), - self.validate_min_direct_participation_icp_e8s(), - self.validate_max_direct_participation_icp_e8s(), - self.validate_min_participant_icp_e8s(), - self.validate_max_participant_icp_e8s(), self.validate_nns_proposal_id(), - self.validate_neurons_fund_participants(), self.validate_swap_start_timestamp_seconds(), self.validate_swap_due_timestamp_seconds(), self.validate_neurons_fund_participation_constraints(false), self.validate_neurons_fund_participation(), + // Obsolete fields are not set + self.validate_min_icp_e8s(), + self.validate_max_icp_e8s(), ]; self.join_validation_results(&validation_fns) @@ -1340,7 +1397,7 @@ impl SnsInitPayload { // SNS_WASM_CANISTER_ID, // EXCHANGE_RATE_CANISTER_ID, // ] - // .map(Principal::from); + // .map(PrincipalId::from); // let nns_canisters_listed_as_dapp = dapp_canisters // .canisters @@ -1466,38 +1523,6 @@ impl SnsInitPayload { Ok(()) } - fn validate_min_participants(&self) -> Result<(), String> { - let min_participants = self - .min_participants - .ok_or("Error: min_participants must be specified")?; - - if min_participants == 0 { - return Err("Error: min_participants must be > 0".to_string()); - } - - // Needed as the SwapInit min_participants field is a u32 - if min_participants > (u32::MAX as u64) { - return Err(format!( - "Error: min_participants cannot be greater than {}", - u32::MAX - )); - } - - Ok(()) - } - - fn validate_min_direct_participation_icp_e8s(&self) -> Result<(), String> { - let min_direct_participation_icp_e8s = self - .min_direct_participation_icp_e8s - .ok_or("Error: min_direct_participation_icp_e8s must be specified")?; - - if min_direct_participation_icp_e8s == 0 { - return Err("Error: min_direct_participation_icp_e8s must be > 0".to_string()); - } - - Ok(()) - } - fn validate_max_icp_e8s(&self) -> Result<(), String> { if self.max_icp_e8s.is_some() { return Err( @@ -1520,141 +1545,36 @@ impl SnsInitPayload { Ok(()) } - fn validate_max_direct_participation_icp_e8s(&self) -> Result<(), String> { - let max_direct_participation_icp_e8s = self - .max_direct_participation_icp_e8s - .ok_or("Error: max_direct_participation_icp_e8s must be specified")?; - - let min_direct_participation_icp_e8s = self - .min_direct_participation_icp_e8s - .ok_or("Error: min_direct_participation_icp_e8s must be specified")?; - - if max_direct_participation_icp_e8s < min_direct_participation_icp_e8s { - return Err(format!( - "max_direct_participation_icp_e8s ({}) must be >= min_direct_participation_icp_e8s ({})", - max_direct_participation_icp_e8s, min_direct_participation_icp_e8s - )); - } - - if max_direct_participation_icp_e8s > MAX_DIRECT_ICP_CONTRIBUTION_TO_SWAP { - return Err(format!( - "Error: max_direct_participation_icp_e8s ({}) can be at most {} ICP E8s", - max_direct_participation_icp_e8s, MAX_DIRECT_ICP_CONTRIBUTION_TO_SWAP - )); - } - - let min_participants = self - .min_participants - .ok_or("Error: min_participants must be specified")?; - - let min_participant_icp_e8s = self - .min_participant_icp_e8s - .ok_or("Error: min_participant_icp_e8s must be specified")?; - - if max_direct_participation_icp_e8s - < min_participants.saturating_mul(min_participant_icp_e8s) - { - return Err(format!( - "Error: max_direct_participation_icp_e8s ({}) must be >= min_participants ({}) * min_participant_icp_e8s ({})", - max_direct_participation_icp_e8s, min_participants, min_participant_icp_e8s - )); - } - - Ok(()) - } - - fn validate_min_participant_icp_e8s(&self) -> Result<(), String> { - let min_participant_icp_e8s = self - .min_participant_icp_e8s - .ok_or("Error: min_participant_icp_e8s must be specified")?; - - let max_direct_participation_icp_e8s = self - .max_direct_participation_icp_e8s - .ok_or("Error: max_direct_participation_icp_e8s must be specified")?; - - let sns_transaction_fee_e8s = self - .transaction_fee_e8s - .ok_or("Error: transaction_fee_e8s must be specified")?; - - let neuron_minimum_stake_e8s = self - .neuron_minimum_stake_e8s - .ok_or("Error: neuron_minimum_stake_e8s must be specified")?; - - let neuron_basket_construction_parameters_count = self - .neuron_basket_construction_parameters - .as_ref() - .ok_or("Error: neuron_basket_construction_parameters must be specified")? - .count; - - let sns_tokens_e8s = self - .get_swap_distribution() - .map_err(|_| "Error: the SwapDistribution must be specified")? - .initial_swap_amount_e8s; - - let min_participant_sns_e8s = min_participant_icp_e8s as u128 * sns_tokens_e8s as u128 - / max_direct_participation_icp_e8s as u128; - - if neuron_minimum_stake_e8s <= sns_transaction_fee_e8s { - return Err(format!( - "Error: neuron_minimum_stake_e8s={} is too small. It needs to be \ - greater than the transaction fee ({} e8s)", - neuron_minimum_stake_e8s, sns_transaction_fee_e8s - )); - } - - let min_participant_icp_e8s_big_enough = min_participant_sns_e8s - >= neuron_basket_construction_parameters_count as u128 - * (neuron_minimum_stake_e8s + sns_transaction_fee_e8s) as u128; - - if !min_participant_icp_e8s_big_enough { - return Err(format!( - "Error: min_participant_icp_e8s={} is too small. It needs to be \ - large enough to ensure that participants will end up with \ - enough SNS tokens to form {} SNS neurons, each of which \ - require at least {} SNS e8s, plus {} e8s in transaction \ - fees. More precisely, the following inequality must hold: \ - min_participant_icp_e8s >= neuron_basket_count * \ - (neuron_minimum_stake_e8s + transaction_fee_e8s) * max_icp_e8s / sns_tokens_e8s", - min_participant_icp_e8s, - neuron_basket_construction_parameters_count, - neuron_minimum_stake_e8s, - sns_transaction_fee_e8s, - )); - } - - Ok(()) - } - - fn validate_max_participant_icp_e8s(&self) -> Result<(), String> { - let max_participant_icp_e8s = self - .max_participant_icp_e8s - .ok_or("Error: max_participant_icp_e8s must be specified")?; - - let min_participant_icp_e8s = self - .min_participant_icp_e8s - .ok_or("Error: min_participant_icp_e8s must be specified")?; - - if max_participant_icp_e8s < min_participant_icp_e8s { - return Err(format!( - "Error: max_participant_icp_e8s ({}) must be >= min_participant_icp_e8s ({})", - max_participant_icp_e8s, min_participant_icp_e8s - )); - } - - let max_direct_participation_icp_e8s = self - .max_direct_participation_icp_e8s - .ok_or("Error: max_direct_participation_icp_e8s must be specified")?; - - if max_participant_icp_e8s > max_direct_participation_icp_e8s { - return Err(format!( - "max_participant_icp_e8s ({}) must be <= max_direct_participation_icp_e8s ({})", - max_participant_icp_e8s, max_direct_participation_icp_e8s - )); - } - - Ok(()) - } - + /// Validates that swap participation-related parameters* pass the following checks: + /// (1) All participation-related parameters are set. + /// (2) All participation-related parameters are within expected constant lower/upper bounds. + /// (3) Minimum is less than or equal to maximum for the same parameter. + /// (4) One participation cannot exceed the maximum ICP amount that the swap can obtain. + /// (5) No more than `MAX_DIRECT_ICP_CONTRIBUTION_TO_SWAP` may be collected from direct swap + /// participants. + /// (6) If the minimum required number of participants participate each with the minimum + /// required amount of ICP, the maximum ICP amount that the swap can obtain is not exceeded. + /// (7) Determines the smallest SNS neuron size is greated than the SNS ledger transaction fee. + /// (8) Required ICP participation amount is big enough to ensure that all participants will + /// end up with enough SNS tokens to form the right number of SNS neurons (after paying for + /// the SNS ledger transaction fee to create each such SNS neuron). + /// + /// * -- In the context of this function, swap participation-related parameters include: + /// - `min_direct_participation_icp_e8s` - Required ICP amount for the swap to succeed. + /// - `max_direct_participation_icp_e8s` - Maximum ICP amount that the swap can obtain. + /// - `min_participant_icp_e8s` - Required ICP participation amount. + /// - `max_participant_icp_e8s` - Maximum ICP amount from one participant. + /// - `min_participants` - Required number of *direct* participants for the swap + /// to succeed. This does not restrict the number of + /// *Neurons' Fund* participants. + /// - `initial_token_distribution.swap_distribution.initial_swap_amount_e8s` + /// - How many SNS tokens will be distributed amoung all + /// the swap participants if the swap succeeds. + /// - `neuron_basket_construction_parameters` + /// - How many SNS neurons will be created per participant. + /// - `neuron_minimum_stake_e8s` - Determines the smallest SNS neuron size. + /// - `sns_transaction_fee_e8s` - SNS ledger transaction fee, in particular, charged + /// for SNS neuron creation at swap finalization. fn validate_participation_constraints(&self) -> Result<(), String> { // (1) let min_direct_participation_icp_e8s = self @@ -1792,33 +1712,6 @@ impl SnsInitPayload { )); } - // (9) - // Conceptually, we want to calculate the following value: - // ``` - // let max_sns_neurons_for_direct_participants = { - // let max_participants = max_direct_participation_icp_e8s / min_participant_icp_e8s; - // max_participants * neuron_basket_construction_parameters_count; - // }; - // ``` - // To minimize rounding errors related to integer division, we first do `*` and then `/`. - let max_sns_neurons_for_direct_participants = max_direct_participation_icp_e8s as u128 - * neuron_basket_construction_parameters_count as u128 - / min_participant_icp_e8s as u128; - if max_sns_neurons_for_direct_participants > MAX_NEURONS_FOR_DIRECT_PARTICIPANTS as u128 { - return Err(format!( - "Error: The number of SNS neurons created for direct participants of a successful \ - swap ((max_direct_participation_icp_e8s={}) \ - * (neuron_basket_construction_parameters_count={}) \ - / (min_participant_icp_e8s={}) = {}) must not exceed \ - (MAX_NEURONS_FOR_DIRECT_PARTICIPANTS={}).", - max_direct_participation_icp_e8s, - neuron_basket_construction_parameters_count, - min_participant_icp_e8s, - max_sns_neurons_for_direct_participants, - MAX_NEURONS_FOR_DIRECT_PARTICIPANTS - )); - } - Ok(()) } @@ -1840,28 +1733,6 @@ impl SnsInitPayload { } } - fn validate_neurons_fund_participants_pre_execution(&self) -> Result<(), String> { - if self.neurons_fund_participants.is_none() { - Ok(()) - } else { - Err(format!( - "Error: neurons_fund_participants cannot be specified pre_execution, but was {:?}", - self.neurons_fund_participants - )) - } - } - - fn validate_neurons_fund_participants(&self) -> Result<(), String> { - if self.neurons_fund_participants.is_none() { - Ok(()) - } else { - Err(format!( - "Error: neurons_fund_participants can be set only by Swap; was initialized to {:?}", - self.neurons_fund_participants - )) - } - } - fn validate_swap_start_timestamp_seconds_pre_execution(&self) -> Result<(), String> { if self.swap_start_timestamp_seconds.is_none() { Ok(()) @@ -2033,6 +1904,7 @@ impl SnsInitPayload { }) } + /// Checks that all parameters whose values can only be known after the CreateServiceNervousSystem proposal is executed are present. pub fn validate_all_post_execution_swap_parameters_are_set(&self) -> Result<(), String> { let mut missing_one_proposal_fields = vec![]; if self.nns_proposal_id.is_none() { @@ -2063,6 +1935,7 @@ impl SnsInitPayload { } } + /// Checks that all parameters used by the one-proposal flow are present, except for those whose values can't be known before the CreateServiceNervousSystem proposal is executed. pub fn validate_all_non_legacy_pre_execution_swap_parameters_are_set( &self, ) -> Result<(), String> { diff --git a/ssr/Cargo.toml b/ssr/Cargo.toml index c4a01cd5..ba6e515d 100644 --- a/ssr/Cargo.toml +++ b/ssr/Cargo.toml @@ -30,8 +30,8 @@ http = "1.1.0" serde.workspace = true candid.workspace = true ic-agent = { version = "0.38.1", default-features = false, features = ["pem"] } -ic-base-types = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-05-29_23-02-base" } -icp-ledger = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-05-29_23-02-base" } +ic-base-types = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-10-17_03-07-base" } +icp-ledger = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-10-17_03-07-base" } serde-wasm-bindgen = "0.6.5" futures = "0.3.30" leptos-use = "0.12.0" @@ -107,9 +107,9 @@ firestore = { version = "0.43.1", default-features = false, features = [ ], optional = true } speedate = { version = "0.14.4", optional = true } urlencoding = "2.1.3" -yral-types = { git = "https://github.com/yral-dapp/yral-common.git", rev = "2e432882052a69d5a625e7188cd69ad8aa5bcec7" } -yral-qstash-types = { git = "https://github.com/yral-dapp/yral-common.git", rev = "2e432882052a69d5a625e7188cd69ad8aa5bcec7" } -yral-canisters-client = { git = "https://github.com/yral-dapp/yral-common.git", rev = "2e432882052a69d5a625e7188cd69ad8aa5bcec7", features = ["full"] } +yral-types = { git = "https://github.com/yral-dapp/yral-common.git", rev = "0247d886ad95747deee4618c3fd42524ba78d5fe" } +yral-qstash-types = { git = "https://github.com/yral-dapp/yral-common.git", rev = "0247d886ad95747deee4618c3fd42524ba78d5fe" } +yral-canisters-client = { git = "https://github.com/yral-dapp/yral-common.git", rev = "0247d886ad95747deee4618c3fd42524ba78d5fe", features = ["full"] } pulldown-cmark = "0.12.1" ic-certification = "2.6.0" ciborium = "0.2.2" diff --git a/ssr/src/app.rs b/ssr/src/app.rs index e05b9282..ddb04adc 100644 --- a/ssr/src/app.rs +++ b/ssr/src/app.rs @@ -1,7 +1,6 @@ use crate::page::icpump::ai::ICPumpAi; use crate::page::icpump::ICPumpLanding; -use crate::page::token::create::{CreateToken, CreateTokenSettings}; // use crate::page::wallet::TestIndex; use crate::{ component::{base_route::BaseRoute, nav::NavBar}, @@ -20,8 +19,7 @@ use crate::{ settings::Settings, terms::TermsOfService, token::{ - //create::{CreateToken, CreateTokenCtx, CreateTokenSettings}, - create::CreateTokenCtx, + create::{CreateToken, CreateTokenCtx, CreateTokenSettings}, create_token_faq::CreateTokenFAQ, info::TokenInfo, transfer::TokenTransfer, diff --git a/ssr/src/component/auth_providers/mod.rs b/ssr/src/component/auth_providers/mod.rs index 392b2631..743134ab 100644 --- a/ssr/src/component/auth_providers/mod.rs +++ b/ssr/src/component/auth_providers/mod.rs @@ -271,14 +271,14 @@ mod server_fn_impl { ) -> Result { use crate::state::admin_canisters::admin_canisters; use yral_canisters_client::individual_user_template::{ - Result12, Result23, SessionType, + Result13, Result25, SessionType, }; let admin_cans = admin_canisters(); let user = admin_cans.individual_user_for(user_canister).await; if matches!( user.get_session_type().await?, - Result12::Ok(SessionType::RegisteredSession) + Result13::Ok(SessionType::RegisteredSession) ) { return Ok(false); } @@ -286,8 +286,8 @@ mod server_fn_impl { .await .map_err(ServerFnError::from) .and_then(|res| match res { - Result23::Ok(_) => Ok(()), - Result23::Err(e) => Err(ServerFnError::new(format!( + Result25::Ok(_) => Ok(()), + Result25::Err(e) => Err(ServerFnError::new(format!( "failed to mark user as registered {e}" ))), })?; diff --git a/ssr/src/consts/local.rs b/ssr/src/consts/local.rs index 594a0227..b45b175d 100644 --- a/ssr/src/consts/local.rs +++ b/ssr/src/consts/local.rs @@ -6,5 +6,5 @@ pub static METADATA_API_BASE: Lazy = pub const AGENT_URL: &str = "http://localhost:4943"; -pub const YRAL_BACKEND_CONTAINER_TAG: &str = "692e419da7e96c9a7d0e20fe89287460df795cea"; +pub const YRAL_BACKEND_CONTAINER_TAG: &str = "04b53277579d9370c13312a2833ca0b855cdad72"; pub const YRAL_METADATA_CONTAINER_TAG: &str = "a4879e2e711c17beeb12ed6987ba315c110be9e5"; diff --git a/ssr/src/consts/mod.rs b/ssr/src/consts/mod.rs index f46475d3..e0a04880 100644 --- a/ssr/src/consts/mod.rs +++ b/ssr/src/consts/mod.rs @@ -45,7 +45,9 @@ pub const ICP_LEDGER_CANISTER_ID: &str = "ryjl3-tyaaa-aaaaa-aaaba-cai"; pub const ICPUMP_LISTING_PAGE_SIZE: usize = 12; -pub const CDAO_SWAP_TIME_SECS: u64 = 90; +pub const CDAO_SWAP_PRE_READY_TIME_SECS: u64 = 150; + +pub const CDAO_SWAP_TIME_SECS: u64 = CDAO_SWAP_PRE_READY_TIME_SECS + 150; pub const ICPUMP_SEARCH_GRPC_URL: &str = "https://yral-icpumpsearch.fly.dev:443"; pub const NSFW_SERVER_URL: &str = "https://prod-yral-nsfw-classification.fly.dev:443"; diff --git a/ssr/src/page/profile/profile_iter.rs b/ssr/src/page/profile/profile_iter.rs index 458dc0c1..6398f598 100644 --- a/ssr/src/page/profile/profile_iter.rs +++ b/ssr/src/page/profile/profile_iter.rs @@ -1,6 +1,6 @@ use candid::Principal; use futures::stream::{FuturesOrdered, StreamExt, TryStreamExt}; -use yral_canisters_client::individual_user_template::{GetPostsOfUserProfileError, Result11}; +use yral_canisters_client::individual_user_template::{GetPostsOfUserProfileError, Result12}; use crate::{ state::canisters::Canisters, @@ -70,7 +70,7 @@ impl ProfVideoStream for ProfileVideoStream { .get_posts_of_this_user_profile_with_pagination_cursor(cursor.start, cursor.limit) .await?; match posts { - Result11::Ok(v) => { + Result12::Ok(v) => { let end = v.len() < LIMIT as usize; let posts = v .into_iter() @@ -78,7 +78,7 @@ impl ProfVideoStream for ProfileVideoStream { .collect::>(); Ok(PostsRes { posts, end }) } - Result11::Err(GetPostsOfUserProfileError::ReachedEndOfItemsList) => Ok(PostsRes { + Result12::Err(GetPostsOfUserProfileError::ReachedEndOfItemsList) => Ok(PostsRes { posts: vec![], end: true, }), diff --git a/ssr/src/page/refer_earn/history.rs b/ssr/src/page/refer_earn/history.rs index 0b236abb..451cd5d7 100644 --- a/ssr/src/page/refer_earn/history.rs +++ b/ssr/src/page/refer_earn/history.rs @@ -114,7 +114,7 @@ mod history_provider { ) -> Result, AgentError> { use crate::utils::route::failure_redirect; use yral_canisters_client::individual_user_template::{ - MintEvent, Result15, TokenEvent, + MintEvent, Result16, TokenEvent, }; let individual = self.0.authenticated_user().await; let history = individual @@ -124,8 +124,8 @@ mod history_provider { ) .await?; let history = match history { - Result15::Ok(history) => history, - Result15::Err(_) => { + Result16::Ok(history) => history, + Result16::Err(_) => { failure_redirect("failed to get posts"); return Ok(PageEntry { data: vec![], diff --git a/ssr/src/page/token/create/mod.rs b/ssr/src/page/token/create/mod.rs index 54af37a2..31de7ceb 100644 --- a/ssr/src/page/token/create/mod.rs +++ b/ssr/src/page/token/create/mod.rs @@ -328,7 +328,7 @@ pub fn CreateToken() -> impl IntoView { let sns_form = ctx.form_state.get_untracked(); let sns_config = sns_form.try_into_config(&cans)?; - let create_sns = sns_config.try_convert_to_executed_sns_init()?; + let create_sns = sns_config.try_convert_to_sns_init_payload()?; let server_available = is_server_available().await.map_err(|e| e.to_string())?; log::debug!( "Server details: {}, {}", diff --git a/ssr/src/page/token/create/server_impl.rs b/ssr/src/page/token/create/server_impl.rs index 4de6d629..00eb17db 100644 --- a/ssr/src/page/token/create/server_impl.rs +++ b/ssr/src/page/token/create/server_impl.rs @@ -6,7 +6,7 @@ pub use real_impl::{deploy_cdao_canisters, is_server_available}; #[cfg(all(feature = "backend-admin", feature = "qstash"))] mod qstash_claim { use leptos::{expect_context, ServerFnError}; - use yral_qstash_types::ClaimTokensRequest; + use yral_qstash_types::{ClaimTokensRequest, ParticipateInSwapRequest}; pub async fn enqueue_claim_token(req: ClaimTokensRequest) -> Result<(), ServerFnError> { use crate::utils::qstash::QStashClient; @@ -15,14 +15,26 @@ mod qstash_claim { Ok(()) } + + pub async fn enqueue_participate_in_swap( + req: ParticipateInSwapRequest, + ) -> Result<(), ServerFnError> { + use crate::utils::qstash::QStashClient; + let client: QStashClient = expect_context(); + client.enqueue_participate_in_swap(req).await?; + + Ok(()) + } } #[cfg(all(feature = "backend-admin", not(feature = "qstash")))] mod local_claim { use web_time::Duration; + use candid::{Decode, Encode}; use candid::{Nat, Principal}; use ic_agent::{identity::DelegatedIdentity, Identity}; + use ic_base_types::PrincipalId; use leptos::ServerFnError; use yral_canisters_client::{ sns_governance::{ @@ -31,10 +43,19 @@ mod local_claim { }, sns_ledger::{Account as LedgerAccount, SnsLedger, TransferArg, TransferResult}, sns_root::{ListSnsCanistersArg, SnsRoot}, + sns_swap::{NewSaleTicketRequest, RefreshBuyerTokensRequest, Result2}, }; - use yral_qstash_types::ClaimTokensRequest; + use yral_qstash_types::{ClaimTokensRequest, ParticipateInSwapRequest}; - use crate::{consts::CDAO_SWAP_TIME_SECS, utils::ic::AgentWrapper}; + use crate::{ + consts::{CDAO_SWAP_PRE_READY_TIME_SECS, CDAO_SWAP_TIME_SECS, ICP_LEDGER_CANISTER_ID}, + state::{ + admin_canisters::{admin_canisters, AdminCanisters}, + canisters::{unauth_canisters, Canisters}, + }, + utils::ic::AgentWrapper, + }; + use std::str::FromStr; async fn get_neurons( governance: &SnsGovernance<'_>, @@ -51,7 +72,10 @@ mod local_claim { Ok(neurons.neurons) } - async fn claim_tokens(req: ClaimTokensRequest) -> Result<(), ServerFnError> { + async fn claim_tokens( + cans: Canisters, + req: ClaimTokensRequest, + ) -> Result<(), ServerFnError> { let identity: DelegatedIdentity = req.identity.try_into()?; let user_principal = identity .sender() @@ -59,6 +83,10 @@ mod local_claim { let agent_w = AgentWrapper::build(|b| b.with_identity(identity)); let agent = agent_w.get_agent().await; + let user_canister = cans + .get_individual_canister_by_user_principal(user_principal) + .await? + .ok_or_else(|| ServerFnError::new("unable to get user canister"))?; let root_canister = SnsRoot(req.token_root, agent); let token_cans = root_canister @@ -130,7 +158,6 @@ mod local_claim { } // Transfer to canister - let user_canister = req.user_canister; let ledger_can = SnsLedger(ledger, agent); // User has 50% of the overall amount // 20% of this 50% is 10% of the overall amount @@ -165,11 +192,84 @@ mod local_claim { Ok(()) } + async fn participate_in_swap( + admin_cans: AdminCanisters, + req: ParticipateInSwapRequest, + ) -> Result<(), ServerFnError> { + use crate::page::token::types::{Recipient, Transaction, TransferResult}; + use icp_ledger::Subaccount; + + let admin_principal = admin_cans.principal(); + let agent = admin_cans.get_agent().await; + + let root = SnsRoot(req.token_root, agent); + let token_cans = root.list_sns_canisters(ListSnsCanistersArg {}).await?; + let Some(swap_canister) = token_cans.swap else { + log::warn!("No swap canister found for token. Ignoring..."); + return Ok(()); + }; + + let swap = admin_cans.sns_swap(swap_canister).await; + + let new_sale_ticket = swap + .new_sale_ticket(NewSaleTicketRequest { + amount_icp_e8s: 100_000, + subaccount: None, + }) + .await?; + match new_sale_ticket.result { + Some(Result2::Ok(_)) => (), + None => return Err(ServerFnError::new("failed to perform swap new_sale_ticket")), + Some(Result2::Err(e)) => { + return Err(ServerFnError::new(format!( + "failed to perform swap new_sale_ticket {e:?}" + ))) + } + }; + + // transfer icp + let subaccount = Subaccount::from(&PrincipalId(admin_principal)); + let transfer_args = Transaction { + memo: Some(vec![0]), + amount: Nat::from(1000000_u64), + fee: None, + from_subaccount: None, + to: Recipient { + owner: swap_canister, + subaccount: Some(subaccount.to_vec()), + }, + created_at_time: None, + }; + let res: Vec = agent + .update( + &Principal::from_str(ICP_LEDGER_CANISTER_ID).unwrap(), + "icrc1_transfer", + ) + .with_arg(Encode!(&transfer_args).unwrap()) + .call_and_wait() + .await?; + let transfer_result: TransferResult = Decode!(&res, TransferResult).unwrap(); + if let TransferResult::Err(e) = transfer_result { + return Err(ServerFnError::new(format!( + "failed to perform swap icrc1_transfer {e:?}" + ))); + } + + swap.refresh_buyer_tokens(RefreshBuyerTokensRequest { + buyer: admin_principal.to_string(), + confirmation_text: None, + }) + .await?; + + Ok(()) + } + pub async fn enqueue_claim_token(req: ClaimTokensRequest) -> Result<(), ServerFnError> { + let cans = unauth_canisters(); tokio::spawn(async move { log::info!("started claiming job"); tokio::time::sleep(Duration::from_secs(CDAO_SWAP_TIME_SECS)).await; - if let Err(e) = claim_tokens(req).await { + if let Err(e) = claim_tokens(cans, req).await { log::error!("claim job failed: {e:?}"); } log::info!("claiming completed") @@ -177,6 +277,22 @@ mod local_claim { Ok(()) } + + pub async fn enqueue_participate_in_swap( + req: ParticipateInSwapRequest, + ) -> Result<(), ServerFnError> { + let admin_cans = admin_canisters(); + tokio::spawn(async move { + log::info!("started participate in swap job"); + tokio::time::sleep(Duration::from_secs(CDAO_SWAP_PRE_READY_TIME_SECS)).await; + if let Err(e) = participate_in_swap(admin_cans, req).await { + log::error!("participate in swap job failed: {e:?}"); + } + log::info!("participate in swap completed") + }); + + Ok(()) + } } #[cfg(feature = "backend-admin")] @@ -187,32 +303,27 @@ mod real_impl { use crate::page::token::create::DeployedCdaoCanistersRes; use crate::utils::token::nsfw::NSFWInfo; use yral_canisters_client::individual_user_template::Result7; - use yral_canisters_client::sns_swap::{ - NewSaleTicketRequest, RefreshBuyerTokensRequest, Result2, - }; use crate::consts::ICP_LEDGER_CANISTER_ID; use crate::utils::token::nsfw; - use candid::{Decode, Encode, Nat, Principal}; + use candid::{Decode, Nat, Principal}; use ic_base_types::PrincipalId; - use icp_ledger::{AccountIdentifier, Subaccount}; + use icp_ledger::AccountIdentifier; use leptos::ServerFnError; use sns_validation::pbs::sns_pb::SnsInitPayload; - use yral_qstash_types::ClaimTokensRequest; + use yral_qstash_types::{ClaimTokensRequest, ParticipateInSwapRequest}; - use crate::page::token::types::{Icrc1BalanceOfArg, Recipient, Transaction, TransferResult}; + use crate::page::token::types::Icrc1BalanceOfArg; use crate::state::admin_canisters::admin_canisters; use crate::state::canisters::CanistersAuthWire; - #[cfg(all(feature = "backend-admin", not(feature = "qstash")))] - use super::local_claim::enqueue_claim_token; - #[cfg(all(feature = "backend-admin", feature = "qstash"))] - use super::qstash_claim::enqueue_claim_token; + #[cfg(not(feature = "qstash"))] + use super::local_claim::{enqueue_claim_token, enqueue_participate_in_swap}; + #[cfg(feature = "qstash")] + use super::qstash_claim::{enqueue_claim_token, enqueue_participate_in_swap}; - #[allow(dead_code)] const ICP_TX_FEE: u64 = 10000; - #[allow(unused_variables)] pub async fn is_server_available() -> Result<(bool, AccountIdentifier), ServerFnError> { let admin_cans = admin_canisters(); let admin_principal = admin_cans.principal(); @@ -234,69 +345,12 @@ mod real_impl { .await?; let balance: Nat = Decode!(&balance_res, Nat).unwrap(); let acc_id = AccountIdentifier::new(PrincipalId(admin_principal), None); - // if balance >= (1000000 + ICP_TX_FEE) { // amount we participate + icp tx fee - // Ok((true, acc_id)) - // } else { - Ok((false, acc_id)) - // } - } - - async fn participate_in_swap(swap_canister: Principal) -> Result<(), ServerFnError> { - let admin_cans = admin_canisters(); - let admin_principal = admin_cans.principal(); - let agent = admin_cans.get_agent().await; - - let swap = admin_cans.sns_swap(swap_canister).await; - - let new_sale_ticket = swap - .new_sale_ticket(NewSaleTicketRequest { - amount_icp_e8s: 100_000, - subaccount: None, - }) - .await?; - match new_sale_ticket.result { - Some(Result2::Ok(_)) => (), - None | Some(Result2::Err(_)) => { - return Err(ServerFnError::new("failed to perform swap new_sale_ticket")) - } - }; - - // transfer icp - let subaccount = Subaccount::from(&PrincipalId(admin_principal)); - let transfer_args = Transaction { - memo: Some(vec![0]), - amount: Nat::from(1000000_u64), - fee: None, - from_subaccount: None, - to: Recipient { - owner: swap_canister, - subaccount: Some(subaccount.to_vec()), - }, - created_at_time: None, - }; - let res: Vec = agent - .update( - &Principal::from_str(ICP_LEDGER_CANISTER_ID).unwrap(), - "icrc1_transfer", - ) - .with_arg(Encode!(&transfer_args).unwrap()) - .call_and_wait() - .await?; - let transfer_result: TransferResult = Decode!(&res, TransferResult).unwrap(); - if let TransferResult::Err(e) = transfer_result { - return Err(ServerFnError::new(format!( - "failed to perform swap icrc1_transfer {e:?}" - ))); + if balance >= (1000000 + ICP_TX_FEE) { + Ok((true, acc_id)) + } else { + Ok((false, acc_id)) } - - swap.refresh_buyer_tokens(RefreshBuyerTokensRequest { - buyer: admin_principal.to_string(), - confirmation_text: None, - }) - .await?; - - Ok(()) } pub async fn deploy_cdao_canisters( @@ -326,12 +380,15 @@ mod real_impl { Result7::Err(e) => return Err(ServerFnError::new(format!("{e:?}"))), }; - participate_in_swap(deployed_cans.swap).await?; + let participate_in_swap_req = ParticipateInSwapRequest { + user_principal: cans.user_principal(), + token_root: deployed_cans.root, + }; + enqueue_participate_in_swap(participate_in_swap_req).await?; let temp_id = delegate_short_lived_identity(cans.identity()); let claim_req = ClaimTokensRequest { identity: temp_id, - user_canister: cans.user_canister(), token_root: deployed_cans.root, }; enqueue_claim_token(claim_req).await?; diff --git a/ssr/src/page/wallet/tokens.rs b/ssr/src/page/wallet/tokens.rs index 1d5a57a1..3235da47 100644 --- a/ssr/src/page/wallet/tokens.rs +++ b/ssr/src/page/wallet/tokens.rs @@ -17,7 +17,7 @@ use crate::{ }; use futures::stream::{self, StreamExt}; use leptos::*; -use yral_canisters_client::individual_user_template::Result14; +use yral_canisters_client::individual_user_template::Result15; use yral_canisters_client::sns_ledger::{Account, SnsLedger}; #[derive(Clone)] pub struct TokenRootList { @@ -65,11 +65,11 @@ impl CursoredDataProvider for TokenRootList { .get_token_roots_of_this_user_with_pagination_cursor(start as u64, end as u64) .await?; let mut tokens: Vec = match tokens { - Result14::Ok(v) => v + Result15::Ok(v) => v .into_iter() .map(|t| RootType::from_str(&t.to_text()).unwrap()) .collect(), - Result14::Err(_) => vec![], + Result15::Err(_) => vec![], }; let list_end = tokens.len() < (end - start); if start == 0 { diff --git a/ssr/src/state/canisters.rs b/ssr/src/state/canisters.rs index 384893fc..ad3a8788 100644 --- a/ssr/src/state/canisters.rs +++ b/ssr/src/state/canisters.rs @@ -9,7 +9,7 @@ use yral_metadata_client::MetadataClient; use yral_metadata_types::UserMetadata; use yral_canisters_client::{ - individual_user_template::{IndividualUserTemplate, Result23, Result7, UserCanisterDetails}, + individual_user_template::{IndividualUserTemplate, Result25, Result7, UserCanisterDetails}, platform_orchestrator::PlatformOrchestrator, post_cache::PostCache, sns_governance::SnsGovernance, @@ -310,8 +310,8 @@ pub async fn do_canister_auth( .await .map_err(|e| e.to_string()) { - Ok(Result23::Ok(_)) => (), - Err(e) | Ok(Result23::Err(e)) => log::warn!("Failed to update last access time: {}", e), + Ok(Result25::Ok(_)) => (), + Err(e) | Ok(Result25::Err(e)) => log::warn!("Failed to update last access time: {}", e), } let profile_details = user.get_profile_details().await?.into(); diff --git a/ssr/src/utils/profile.rs b/ssr/src/utils/profile.rs index 3252a0c9..67f29766 100644 --- a/ssr/src/utils/profile.rs +++ b/ssr/src/utils/profile.rs @@ -6,7 +6,7 @@ use leptos::{RwSignal, SignalUpdateUntracked}; use serde::{Deserialize, Serialize}; use yral_canisters_client::individual_user_template::{ - BetDirection, BetOutcomeForBetMaker, PlacedBetDetail, Result11, UserProfileDetailsForFrontend, + BetDirection, BetOutcomeForBetMaker, PlacedBetDetail, Result12, UserProfileDetailsForFrontend, }; use crate::{ @@ -226,8 +226,8 @@ impl CursoredDataProvider for PostsProvider { .get_posts_of_this_user_profile_with_pagination_cursor(start as u64, limit as u64) .await?; let posts = match posts { - Result11::Ok(v) => v, - Result11::Err(_) => { + Result12::Ok(v) => v, + Result12::Err(_) => { log::warn!("failed to get posts"); return Ok(PageEntry { data: vec![], diff --git a/ssr/src/utils/qstash.rs b/ssr/src/utils/qstash.rs index 471ae48a..1ef1e519 100644 --- a/ssr/src/utils/qstash.rs +++ b/ssr/src/utils/qstash.rs @@ -5,9 +5,9 @@ use http::{ HeaderMap, HeaderValue, }; use reqwest::{Client, Url}; -use yral_qstash_types::ClaimTokensRequest; +use yral_qstash_types::{ClaimTokensRequest, ParticipateInSwapRequest}; -use crate::consts::{CDAO_SWAP_TIME_SECS, OFF_CHAIN_AGENT_URL}; +use crate::consts::{CDAO_SWAP_PRE_READY_TIME_SECS, CDAO_SWAP_TIME_SECS, OFF_CHAIN_AGENT_URL}; #[derive(Clone, Debug)] pub struct QStashClient { @@ -52,4 +52,25 @@ impl QStashClient { .await?; Ok(()) } + + pub async fn enqueue_participate_in_swap( + &self, + req: ParticipateInSwapRequest, + ) -> Result<(), reqwest::Error> { + let off_chain_ep = OFF_CHAIN_AGENT_URL + .join("qstash/participate_in_swap") + .unwrap(); + let path = format!("publish/{off_chain_ep}"); + let ep = self.base_url.join(&path).unwrap(); + + self.client + .post(ep) + .json(&req) + .header(CONTENT_TYPE, "application/json") + .header("upstash-method", "POST") + .header("upstash-delay", format!("{CDAO_SWAP_PRE_READY_TIME_SECS}s")) + .send() + .await?; + Ok(()) + } }