diff --git a/Cargo.lock b/Cargo.lock index ab8ee761..4afb2368 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4889,6 +4889,21 @@ dependencies = [ "libm 0.1.4", ] +[[package]] +name = "pallet-assets" +version = "4.0.0-dev" +source = "git+https://github.com/serai-dex/substrate#6dc38b0ba11dff62c1042ab0fa21d0b55a64bf6e" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-balances" version = "4.0.0-dev" @@ -7309,6 +7324,7 @@ dependencies = [ "frame-system", "frame-system-rpc-runtime-api", "hex-literal", + "pallet-assets", "pallet-balances", "pallet-session", "pallet-tendermint", @@ -7316,6 +7332,7 @@ dependencies = [ "pallet-transaction-payment-rpc-runtime-api", "parity-scale-codec", "scale-info", + "serai-primitives", "sp-api", "sp-application-crypto", "sp-block-builder", diff --git a/substrate/node/src/chain_spec.rs b/substrate/node/src/chain_spec.rs index bb7dd0e7..58aa9321 100644 --- a/substrate/node/src/chain_spec.rs +++ b/substrate/node/src/chain_spec.rs @@ -1,12 +1,14 @@ -use sp_core::{Pair as PairTrait, sr25519::Pair}; +use sp_core::{Decode, Pair as PairTrait, sr25519::Pair}; +use sp_runtime::traits::TrailingZeroInput; + use sc_service::ChainType; -use serai_primitives::{Amount, COIN, Coin}; +use serai_primitives::*; use pallet_tendermint::crypto::Public; use serai_runtime::{ WASM_BINARY, AccountId, opaque::SessionKeys, GenesisConfig, SystemConfig, BalancesConfig, - ValidatorSetsConfig, SessionConfig, + AssetsConfig, ValidatorSetsConfig, SessionConfig, }; pub type ChainSpec = sc_service::GenericChainSpec; @@ -29,11 +31,25 @@ fn testnet_genesis( (key, key, SessionKeys { tendermint: Public::from(key) }) }; + // TODO: Replace with a call to the pallet to ask for its account + let owner = AccountId::decode(&mut TrailingZeroInput::new(b"tokens")).unwrap(); + GenesisConfig { system: SystemConfig { code: wasm_binary.to_vec() }, balances: BalancesConfig { balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(), }, + assets: AssetsConfig { + assets: [BITCOIN, ETHER, DAI, MONERO].iter().map(|coin| (*coin, owner, true, 1)).collect(), + metadata: vec![ + (BITCOIN, b"Bitcoin".to_vec(), b"BTC".to_vec(), 8), + // Reduce to 8 decimals to feasibly fit within u64 (instead of its native u256) + (ETHER, b"Ether".to_vec(), b"ETH".to_vec(), 8), + (DAI, b"Dai Stablecoin".to_vec(), b"DAI".to_vec(), 8), + (MONERO, b"Monero".to_vec(), b"XMR".to_vec(), 12), + ], + accounts: vec![], + }, transaction_payment: Default::default(), validator_sets: ValidatorSetsConfig { diff --git a/substrate/runtime/Cargo.toml b/substrate/runtime/Cargo.toml index f964fa7a..78a5f32f 100644 --- a/substrate/runtime/Cargo.toml +++ b/substrate/runtime/Cargo.toml @@ -25,7 +25,7 @@ sp-inherents = { git = "https://github.com/serai-dex/substrate", default-feature sp-offchain = { git = "https://github.com/serai-dex/substrate", default-features = false } sp-session = { git = "https://github.com/serai-dex/substrate", default-features = false } sp-transaction-pool = { git = "https://github.com/serai-dex/substrate", default-features = false } -sp-block-builder = { git = "https://github.com/serai-dex/substrate", default-features = false} +sp-block-builder = { git = "https://github.com/serai-dex/substrate", default-features = false } sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false } sp-api = { git = "https://github.com/serai-dex/substrate", default-features = false } @@ -36,7 +36,10 @@ frame-support = { git = "https://github.com/serai-dex/substrate", default-featur frame-executive = { git = "https://github.com/serai-dex/substrate", default-features = false } frame-benchmarking = { git = "https://github.com/serai-dex/substrate", default-features = false, optional = true } +serai-primitives = { path = "../serai/primitives", default-features = false } + pallet-balances = { git = "https://github.com/serai-dex/substrate", default-features = false } +pallet-assets = { git = "https://github.com/serai-dex/substrate", default-features = false } pallet-transaction-payment = { git = "https://github.com/serai-dex/substrate", default-features = false } validator-sets-pallet = { path = "../validator-sets/pallet", default-features = false } @@ -72,7 +75,10 @@ std = [ "frame-support/std", "frame-executive/std", + "serai-primitives/std", + "pallet-balances/std", + "pallet-assets/std", "pallet-transaction-payment/std", "validator-sets-pallet/std", @@ -93,6 +99,7 @@ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "pallet-balances/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", "pallet-tendermint/runtime-benchmarks", ] diff --git a/substrate/runtime/src/lib.rs b/substrate/runtime/src/lib.rs index d4a40c37..3140fc03 100644 --- a/substrate/runtime/src/lib.rs +++ b/substrate/runtime/src/lib.rs @@ -28,7 +28,10 @@ use frame_support::{ }; pub use frame_system::Call as SystemCall; +use serai_primitives::Coin; + pub use pallet_balances::Call as BalancesCall; +pub use pallet_assets::Call as AssetsCall; use pallet_transaction_payment::CurrencyAdapter; use pallet_session::PeriodicSessions; @@ -40,6 +43,11 @@ pub type BlockNumber = u32; pub type AccountId = Public; /// Balance of an account. +// Distinct from serai-primitives Amount due to Substrate's requirements on this type. +// If Amount could be dropped in here, it would be. +// While Amount could have all the necessary traits implemented, not only are they many, yet it'd +// make Amount a larger type, providing more operations than desired. +// The current type's minimalism sets clear bounds on usage. pub type Balance = u64; /// Index of a transaction in the chain, for a given account. @@ -69,8 +77,7 @@ use opaque::SessionKeys; #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("serai"), - // TODO: "core"? - impl_name: create_runtime_str!("turoctocrab"), + impl_name: create_runtime_str!("core"), authoring_version: 1, // TODO: 1? Do we prefer some level of compatibility or our own path? spec_version: 100, @@ -97,14 +104,6 @@ pub fn native_version() -> NativeVersion { const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); -// Unit = the base number of indivisible units for balances -const UNIT: Balance = 1_000_000_000_000; -const MILLIUNIT: Balance = 1_000_000_000; - -const fn deposit(items: u32, bytes: u32) -> Balance { - (items as Balance * UNIT + (bytes as Balance) * (5 * MILLIUNIT / 100)) / 10 -} - parameter_types! { pub const BlockHashCount: BlockNumber = 2400; pub const Version: RuntimeVersion = VERSION; @@ -119,16 +118,6 @@ parameter_types! { Weight::from_ref_time(2u64 * WEIGHT_REF_TIME_PER_SECOND).set_proof_size(u64::MAX), NORMAL_DISPATCH_RATIO, ); - - pub const DepositPerItem: Balance = deposit(1, 0); - pub const DepositPerByte: Balance = deposit(0, 1); - pub const DeletionQueueDepth: u32 = 128; - // The lazy deletion runs inside on_initialize. - pub DeletionWeightLimit: Weight = BlockWeights::get() - .per_class - .get(DispatchClass::Normal) - .max_total - .unwrap_or(BlockWeights::get().max_block); } impl frame_system::Config for Runtime { @@ -173,6 +162,37 @@ impl pallet_balances::Config for Runtime { type WeightInfo = pallet_balances::weights::SubstrateWeight; } +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type Currency = Balances; + + type AssetId = Coin; + type AssetIdParameter = Coin; + type StringLimit = ConstU32<32>; + + // Don't allow anyone to create assets + type CreateOrigin = + frame_support::traits::AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; + + // Don't charge fees nor kill accounts + type RemoveItemsLimit = ConstU32<0>; + type AssetDeposit = ConstU64<0>; + type AssetAccountDeposit = ConstU64<0>; + type MetadataDepositBase = ConstU64<0>; + type MetadataDepositPerByte = ConstU64<0>; + type ApprovalDeposit = ConstU64<0>; + + // Unused hooks + type Freezer = (); + type Extra = (); + + type WeightInfo = pallet_assets::weights::SubstrateWeight; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); +} + impl pallet_transaction_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; type OnChargeTransaction = CurrencyAdapter; @@ -242,6 +262,7 @@ construct_runtime!( { System: frame_system, Balances: pallet_balances, + Assets: pallet_assets, TransactionPayment: pallet_transaction_payment, ValidatorSets: validator_sets_pallet, diff --git a/substrate/serai/primitives/src/amount.rs b/substrate/serai/primitives/src/amount.rs new file mode 100644 index 00000000..e2d3594f --- /dev/null +++ b/substrate/serai/primitives/src/amount.rs @@ -0,0 +1,31 @@ +use core::{ops::{Add, Mul}}; + +use scale::{Encode, Decode, MaxEncodedLen}; +use scale_info::TypeInfo; +#[cfg(feature = "std")] +use serde::{Serialize, Deserialize}; + +/// The type used for amounts. +#[derive( + Clone, Copy, PartialEq, Eq, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen, +)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub struct Amount(pub u64); + +/// One whole coin with eight decimals. +#[allow(clippy::inconsistent_digit_grouping)] +pub const COIN: Amount = Amount(1_000_000_00); + +impl Add for Amount { + type Output = Amount; + fn add(self, other: Amount) -> Amount { + Amount(self.0 + other.0) + } +} + +impl Mul for Amount { + type Output = Amount; + fn mul(self, other: Amount) -> Amount { + Amount(self.0 * other.0) + } +} diff --git a/substrate/serai/primitives/src/coins.rs b/substrate/serai/primitives/src/coins.rs new file mode 100644 index 00000000..a1a7af3c --- /dev/null +++ b/substrate/serai/primitives/src/coins.rs @@ -0,0 +1,19 @@ +use scale::{Encode, Decode, MaxEncodedLen}; +use scale_info::TypeInfo; +#[cfg(feature = "std")] +use serde::{Serialize, Deserialize}; + +/// The type used to identify coins. +#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub struct Coin(pub u32); +impl From for Coin { + fn from(coin: u32) -> Coin { + Coin(coin) + } +} + +pub const BITCOIN: Coin = Coin(0); +pub const ETHER: Coin = Coin(1); +pub const DAI: Coin = Coin(2); +pub const MONERO: Coin = Coin(3); diff --git a/substrate/serai/primitives/src/lib.rs b/substrate/serai/primitives/src/lib.rs index 829942d3..65e860b3 100644 --- a/substrate/serai/primitives/src/lib.rs +++ b/substrate/serai/primitives/src/lib.rs @@ -1,36 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -use core::ops::{Add, Mul}; +mod amount; +pub use amount::*; -use scale::{Encode, Decode, MaxEncodedLen}; -use scale_info::TypeInfo; -#[cfg(feature = "std")] -use serde::{Serialize, Deserialize}; - -/// The type used for amounts. -#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -pub struct Amount(pub u64); - -impl Add for Amount { - type Output = Amount; - fn add(self, other: Amount) -> Amount { - Amount(self.0 + other.0) - } -} - -impl Mul for Amount { - type Output = Amount; - fn mul(self, other: Amount) -> Amount { - Amount(self.0 * other.0) - } -} - -/// One whole coin with eight decimals. -#[allow(clippy::inconsistent_digit_grouping)] -pub const COIN: Amount = Amount(1_000_000_00); - -/// The type used to identify coins. -#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -pub struct Coin(pub u32); +mod coins; +pub use coins::*;