diff --git a/Cargo.lock b/Cargo.lock index 3aba7b2c..bed90168 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7329,6 +7329,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "serai-coins-primitives", + "serai-genesis-liquidity-primitives", "serai-in-instructions-primitives", "serai-primitives", "serai-signals-primitives", @@ -7527,6 +7528,9 @@ dependencies = [ "serai-dex-pallet", "serai-genesis-liquidity-primitives", "serai-primitives", + "serai-validator-sets-primitives", + "sp-application-crypto", + "sp-core", "sp-std", ] @@ -7534,7 +7538,14 @@ dependencies = [ name = "serai-genesis-liquidity-primitives" version = "0.1.0" dependencies = [ + "borsh", + "parity-scale-codec", + "scale-info", "serai-primitives", + "serai-validator-sets-primitives", + "serde", + "sp-std", + "zeroize", ] [[package]] diff --git a/substrate/abi/Cargo.toml b/substrate/abi/Cargo.toml index 04350486..45e809b5 100644 --- a/substrate/abi/Cargo.toml +++ b/substrate/abi/Cargo.toml @@ -31,6 +31,7 @@ sp-consensus-grandpa = { git = "https://github.com/serai-dex/substrate" } serai-primitives = { path = "../primitives", version = "0.1" } serai-coins-primitives = { path = "../coins/primitives", version = "0.1" } serai-validator-sets-primitives = { path = "../validator-sets/primitives", version = "0.1" } +serai-genesis-liquidity-primitives = { path = "../genesis-liquidity/primitives", version = "0.1" } serai-in-instructions-primitives = { path = "../in-instructions/primitives", version = "0.1" } serai-signals-primitives = { path = "../signals/primitives", version = "0.1" } @@ -42,6 +43,7 @@ borsh = [ "serai-primitives/borsh", "serai-coins-primitives/borsh", "serai-validator-sets-primitives/borsh", + "serai-genesis-liquidity-primitives/borsh", "serai-in-instructions-primitives/borsh", "serai-signals-primitives/borsh", ] @@ -50,6 +52,7 @@ serde = [ "serai-primitives/serde", "serai-coins-primitives/serde", "serai-validator-sets-primitives/serde", + "serai-genesis-liquidity-primitives/serde", "serai-in-instructions-primitives/serde", "serai-signals-primitives/serde", ] diff --git a/substrate/abi/src/genesis_liquidity.rs b/substrate/abi/src/genesis_liquidity.rs index dda30023..7837c2b1 100644 --- a/substrate/abi/src/genesis_liquidity.rs +++ b/substrate/abi/src/genesis_liquidity.rs @@ -1,10 +1,12 @@ use serai_primitives::*; +use serai_genesis_liquidity_primitives::*; #[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)] #[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum Call { remove_coin_liquidity { balance: Balance }, + set_initial_price { prices: Prices }, } #[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)] diff --git a/substrate/genesis-liquidity/pallet/Cargo.toml b/substrate/genesis-liquidity/pallet/Cargo.toml index 873261d9..456a3850 100644 --- a/substrate/genesis-liquidity/pallet/Cargo.toml +++ b/substrate/genesis-liquidity/pallet/Cargo.toml @@ -27,12 +27,15 @@ frame-system = { git = "https://github.com/serai-dex/substrate", default-feature frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false } sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false } +sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false } +sp-application-crypto = { git = "https://github.com/serai-dex/substrate", default-features = false } dex-pallet = { package = "serai-dex-pallet", path = "../../dex/pallet", default-features = false } coins-pallet = { package = "serai-coins-pallet", path = "../../coins/pallet", default-features = false } serai-primitives = { path = "../../primitives", default-features = false } genesis-liquidity-primitives = { package = "serai-genesis-liquidity-primitives", path = "../primitives", default-features = false } +validator-sets-primitives = { package = "serai-validator-sets-primitives", path = "../../validator-sets/primitives", default-features = false } [features] std = [ @@ -43,12 +46,15 @@ std = [ "frame-support/std", "sp-std/std", + "sp-core/std", + "sp-application-crypto/std", "coins-pallet/std", "dex-pallet/std", "serai-primitives/std", "genesis-liquidity-primitives/std", + "validator-sets-primitives/std", ] -default = ["std"] \ No newline at end of file +default = ["std"] diff --git a/substrate/genesis-liquidity/pallet/src/lib.rs b/substrate/genesis-liquidity/pallet/src/lib.rs index 0ee8514c..64207127 100644 --- a/substrate/genesis-liquidity/pallet/src/lib.rs +++ b/substrate/genesis-liquidity/pallet/src/lib.rs @@ -5,14 +5,20 @@ pub mod pallet { use super::*; use frame_system::{pallet_prelude::*, RawOrigin}; - use frame_support::{pallet_prelude::*, sp_runtime::SaturatedConversion}; + use frame_support::{ + pallet_prelude::*, + sp_runtime::{self, SaturatedConversion}, + }; - use sp_std::{vec, collections::btree_map::BTreeMap}; + use sp_std::{vec, vec::Vec, collections::btree_map::BTreeMap}; + use sp_core::sr25519::Signature; + use sp_application_crypto::RuntimePublic; use dex_pallet::{Pallet as Dex, Config as DexConfig}; use coins_pallet::{Config as CoinsConfig, Pallet as Coins, AllowMint}; use serai_primitives::*; + use validator_sets_primitives::{ValidatorSet, Session, musig_key}; pub use genesis_liquidity_primitives as primitives; use primitives::*; @@ -26,6 +32,19 @@ pub mod pallet { type RuntimeEvent: From> + IsType<::RuntimeEvent>; } + #[pallet::genesis_config] + #[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)] + pub struct GenesisConfig { + /// List of participants to place in the initial validator sets. + pub participants: Vec, + } + + impl Default for GenesisConfig { + fn default() -> Self { + GenesisConfig { participants: Default::default() } + } + } + #[pallet::error] pub enum Error { GenesisPeriodEnded, @@ -58,6 +77,20 @@ pub mod pallet { pub(crate) type EconomicSecurityReached = StorageMap<_, Identity, NetworkId, BlockNumberFor, ValueQuery>; + #[pallet::storage] + pub(crate) type Participants = + StorageMap<_, Identity, NetworkId, BoundedVec>, ValueQuery>; + + #[pallet::storage] + pub(crate) type Oracle = StorageMap<_, Identity, Coin, u64, ValueQuery>; + + #[pallet::genesis_build] + impl BuildGenesisConfig for GenesisConfig { + fn build(&self) { + Participants::::set(NetworkId::Serai, self.participants.clone().try_into().unwrap()); + } + } + #[pallet::hooks] impl Hooks> for Pallet { fn on_finalize(n: BlockNumberFor) { @@ -75,9 +108,14 @@ pub mod pallet { let mut pool_values = BTreeMap::new(); let mut total_value: u64 = 0; for coin in COINS { - // TODO: following line is just a place holder till we get the actual coin value - // in terms of btc. - let value = Dex::::security_oracle_value(coin).unwrap_or(Amount(0)).0; + if coin == Coin::Serai { + continue; + } + + // initial coin value in terms of btc + let value = Oracle::::get(coin); + + // get the pool & individual address values account_values.insert(coin, vec![]); let mut pool_amount: u64 = 0; for (account, amount) in Liquidity::::iter_prefix(coin) { @@ -265,6 +303,50 @@ pub mod pallet { Self::deposit_event(Event::GenesisLiquidityRemoved { by: account.into(), balance }); Ok(()) } + + /// A call to submit the initial coi values. + #[pallet::call_index(1)] + #[pallet::weight((0, DispatchClass::Operational))] // TODO + pub fn set_initial_price( + origin: OriginFor, + prices: Prices, + _signature: Signature, + ) -> DispatchResult { + ensure_none(origin)?; + + // set the prices + Oracle::::set(Coin::Bitcoin, prices.bitcoin); + Oracle::::set(Coin::Monero, prices.monero); + Oracle::::set(Coin::Ether, prices.ethereum); + Oracle::::set(Coin::Dai, prices.dai); + Ok(()) + } + } + + #[pallet::validate_unsigned] + impl ValidateUnsigned for Pallet { + type Call = Call; + + fn validate_unsigned(_: TransactionSource, call: &Self::Call) -> TransactionValidity { + match call { + Call::set_initial_price { ref prices, ref signature } => { + let set = ValidatorSet { network: NetworkId::Serai, session: Session(0) }; + let signers = Participants::::get(NetworkId::Serai); + + if !musig_key(set, &signers).verify(&set_initial_price_message(&set, prices), signature) { + Err(InvalidTransaction::BadProof)?; + } + + ValidTransaction::with_tag_prefix("GenesisLiquidity") + .and_provides((0, set)) + .longevity(u64::MAX) + .propagate(true) + .build() + } + Call::remove_coin_liquidity { .. } => Err(InvalidTransaction::Call)?, + Call::__Ignore(_, _) => unreachable!(), + } + } } } diff --git a/substrate/genesis-liquidity/primitives/Cargo.toml b/substrate/genesis-liquidity/primitives/Cargo.toml index 1e10e840..a3911b81 100644 --- a/substrate/genesis-liquidity/primitives/Cargo.toml +++ b/substrate/genesis-liquidity/primitives/Cargo.toml @@ -16,10 +16,19 @@ rustdoc-args = ["--cfg", "docsrs"] workspace = true [dependencies] +zeroize = { version = "^1.5", features = ["derive"], optional = true } + +borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"], optional = true } +serde = { version = "1", default-features = false, features = ["derive", "alloc"], optional = true } + +scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } +scale-info = { version = "2", default-features = false, features = ["derive"] } + +sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false } + serai-primitives = { path = "../../primitives", default-features = false } +validator-sets-primitives = { package = "serai-validator-sets-primitives", path = "../../validator-sets/primitives", default-features = false } [features] -std = [ - "serai-primitives/std", -] +std = ["serai-primitives/std", "validator-sets-primitives/std", "sp-std/std"] default = ["std"] diff --git a/substrate/genesis-liquidity/primitives/src/lib.rs b/substrate/genesis-liquidity/primitives/src/lib.rs index 0ce75824..9846c113 100644 --- a/substrate/genesis-liquidity/primitives/src/lib.rs +++ b/substrate/genesis-liquidity/primitives/src/lib.rs @@ -2,7 +2,21 @@ #![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(feature = "std")] +use zeroize::Zeroize; + +#[cfg(feature = "borsh")] +use borsh::{BorshSerialize, BorshDeserialize}; +#[cfg(feature = "serde")] +use serde::{Serialize, Deserialize}; + +use sp_std::vec::Vec; + +use scale::{Encode, Decode, MaxEncodedLen}; +use scale_info::TypeInfo; + use serai_primitives::*; +use validator_sets_primitives::ValidatorSet; // amount of blocks in 30 days for 6s per block. pub const BLOCKS_PER_MONTH: u32 = 10 * 60 * 24 * 30; @@ -15,3 +29,19 @@ pub const GENESIS_SRI: u64 = 100_000_000 * 10_u64.pow(8); // This is the account to hold and manage the genesis liquidity. pub const GENESIS_LIQUIDITY_ACCOUNT: SeraiAddress = system_address(b"Genesis-liquidity-account"); + +#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)] +#[cfg_attr(feature = "std", derive(Zeroize))] +#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Prices { + pub bitcoin: u64, + pub monero: u64, + pub ethereum: u64, + pub dai: u64, +} + +/// The message for the set_initial_price signature. +pub fn set_initial_price_message(set: &ValidatorSet, prices: &Prices) -> Vec { + (b"GenesisLiquidity-set_initial_price", set, prices).encode() +} diff --git a/substrate/node/src/chain_spec.rs b/substrate/node/src/chain_spec.rs index 6fa8d6c3..428f929c 100644 --- a/substrate/node/src/chain_spec.rs +++ b/substrate/node/src/chain_spec.rs @@ -8,6 +8,7 @@ use sc_service::ChainType; use serai_runtime::{ primitives::*, WASM_BINARY, BABE_GENESIS_EPOCH_CONFIG, RuntimeGenesisConfig, SystemConfig, CoinsConfig, DexConfig, ValidatorSetsConfig, SignalsConfig, BabeConfig, GrandpaConfig, + GenesisLiquidityConfig, }; pub type ChainSpec = sc_service::GenericChainSpec; @@ -60,6 +61,7 @@ fn devnet_genesis( .collect(), participants: validators.clone(), }, + genesis_liquidity: GenesisLiquidityConfig { participants: validators.clone() }, signals: SignalsConfig::default(), babe: BabeConfig { authorities: validators.iter().map(|validator| ((*validator).into(), 1)).collect(), @@ -111,6 +113,7 @@ fn testnet_genesis(wasm_binary: &[u8], validators: Vec<&'static str>) -> Runtime .collect(), participants: validators.clone(), }, + genesis_liquidity: GenesisLiquidityConfig { participants: validators.clone() }, signals: SignalsConfig::default(), babe: BabeConfig { authorities: validators.iter().map(|validator| ((*validator).into(), 1)).collect(),