Add Substrate "assets" pallet

While over-engineered for our purposes, it's still usable.

Also cleans the runtime a bit.
This commit is contained in:
Luke Parker 2023-01-05 19:36:49 -05:00
parent daa88a051f
commit f760c09006
No known key found for this signature in database
7 changed files with 139 additions and 57 deletions
Cargo.lock
substrate
node/src
runtime
serai/primitives/src

17
Cargo.lock generated
View file

@ -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",

View file

@ -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<GenesisConfig>;
@ -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 {

View file

@ -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",
]

View file

@ -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<Runtime>;
}
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<frame_system::EnsureNever<AccountId>>;
type ForceOrigin = frame_system::EnsureRoot<AccountId>;
// 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<Runtime>;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
}
impl pallet_transaction_payment::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type OnChargeTransaction = CurrencyAdapter<Balances, ()>;
@ -242,6 +262,7 @@ construct_runtime!(
{
System: frame_system,
Balances: pallet_balances,
Assets: pallet_assets,
TransactionPayment: pallet_transaction_payment,
ValidatorSets: validator_sets_pallet,

View file

@ -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)
}
}

View file

@ -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<u32> 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);

View file

@ -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<Amount> for Amount {
type Output = Amount;
fn add(self, other: Amount) -> Amount {
Amount(self.0 + other.0)
}
}
impl Mul<Amount> 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::*;