From f917001c0144302d0934aa50014a0123a553a1b9 Mon Sep 17 00:00:00 2001 From: Gregory Hill Date: Wed, 5 Jan 2022 10:27:32 +0000 Subject: [PATCH] feat: add parachain testnet runtime w/ sudo Signed-off-by: Gregory Hill --- Cargo.lock | 96 ++ parachain/Cargo.toml | 1 + parachain/runtime/testnet/Cargo.toml | 246 ++++ parachain/runtime/testnet/build.rs | 9 + parachain/runtime/testnet/src/lib.rs | 1650 ++++++++++++++++++++++++++ parachain/src/chain_spec.rs | 106 +- parachain/src/command.rs | 37 +- parachain/src/service.rs | 47 +- 8 files changed, 2124 insertions(+), 68 deletions(-) create mode 100644 parachain/runtime/testnet/Cargo.toml create mode 100644 parachain/runtime/testnet/build.rs create mode 100644 parachain/runtime/testnet/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index fa5a74b2c3..6da8eafe27 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3288,6 +3288,7 @@ dependencies = [ "substrate-build-script-utils", "substrate-frame-rpc-system", "substrate-prometheus-endpoint", + "testnet-runtime-parachain", ] [[package]] @@ -11430,6 +11431,101 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "testnet-runtime-parachain" +version = "1.2.0" +dependencies = [ + "annuity", + "bitcoin", + "btc-relay", + "cumulus-pallet-aura-ext", + "cumulus-pallet-dmp-queue", + "cumulus-pallet-parachain-system", + "cumulus-pallet-xcm", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-timestamp", + "cumulus-primitives-utility", + "currency", + "democracy", + "escrow", + "fee", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "hex", + "hex-literal 0.3.3", + "interbtc-primitives", + "issue", + "mocktopus", + "module-btc-relay-rpc-runtime-api", + "module-issue-rpc-runtime-api", + "module-oracle-rpc-runtime-api", + "module-redeem-rpc-runtime-api", + "module-refund-rpc-runtime-api", + "module-relay-rpc-runtime-api", + "module-replace-rpc-runtime-api", + "module-vault-registry-rpc-runtime-api", + "nomination", + "oracle", + "orml-tokens", + "orml-traits", + "orml-unknown-tokens", + "orml-vesting", + "orml-xcm-support", + "orml-xtokens", + "pallet-aura", + "pallet-authorship", + "pallet-balances", + "pallet-collective", + "pallet-membership", + "pallet-multisig", + "pallet-scheduler", + "pallet-society", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-treasury", + "pallet-utility", + "pallet-xcm", + "parachain-info", + "parity-scale-codec", + "polkadot-parachain", + "redeem", + "refund", + "relay", + "replace", + "reward", + "scale-info", + "security", + "serde", + "serde_json", + "sp-api", + "sp-arithmetic", + "sp-block-builder", + "sp-consensus-aura", + "sp-core", + "sp-inherents", + "sp-io", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std", + "sp-transaction-pool", + "sp-version", + "staking", + "substrate-wasm-builder", + "supply", + "vault-registry", + "xcm", + "xcm-builder", + "xcm-executor", +] + [[package]] name = "textwrap" version = "0.11.0" diff --git a/parachain/Cargo.toml b/parachain/Cargo.toml index 2879ee576a..c0264cf2b7 100644 --- a/parachain/Cargo.toml +++ b/parachain/Cargo.toml @@ -24,6 +24,7 @@ hex-literal = "0.2.1" # Parachain dependencies interlay-runtime = { package = "interlay-runtime-parachain", path = "./runtime/interlay" } kintsugi-runtime = { package = "kintsugi-runtime-parachain", path = "./runtime/kintsugi" } +testnet-runtime = { package = "testnet-runtime-parachain", path = "./runtime/testnet" } interbtc-rpc = { path = "../rpc" } bitcoin = { path = "../crates/bitcoin" } primitives = { package = "interbtc-primitives", path = "../primitives" } diff --git a/parachain/runtime/testnet/Cargo.toml b/parachain/runtime/testnet/Cargo.toml new file mode 100644 index 0000000000..0104614ad9 --- /dev/null +++ b/parachain/runtime/testnet/Cargo.toml @@ -0,0 +1,246 @@ +[package] +authors = ["Interlay Ltd"] +edition = "2018" +name = 'testnet-runtime-parachain' +version = "1.2.0" + +[package.metadata.docs.rs] +targets = ['x86_64-unknown-linux-gnu'] + +[dependencies] +serde = { version = "1.0.130", default-features = false, features = ["derive"], optional = true } +codec = { package = "parity-scale-codec", version = "2.2.0", default-features = false, features = ["derive", "max-encoded-len"] } +hex-literal = { version = "0.3.1" } +scale-info = { version = "1.0.0", default-features = false, features = ["derive"] } + +# Substrate dependencies +sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +sp-version = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +sp-session = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +sp-offchain = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +sp-block-builder = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +sp-transaction-pool = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +frame-executive = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false, optional = true } +frame-system-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false, optional = true } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +pallet-sudo = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +pallet-utility = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +pallet-treasury = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +pallet-scheduler = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +pallet-multisig = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } + +frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } + +## Governance +pallet-collective = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +pallet-membership = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +pallet-society = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } + +# Aura dependencies +pallet-authorship = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +pallet-aura = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +sp-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } + +# Cumulus dependencies +cumulus-pallet-aura-ext = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.12", default-features = false } +cumulus-pallet-parachain-system = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.12", default-features = false } +cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.12", default-features = false } +cumulus-primitives-timestamp = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.12", default-features = false } +cumulus-primitives-utility = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.12", default-features = false } +cumulus-pallet-dmp-queue = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.12", default-features = false } +cumulus-pallet-xcmp-queue = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.12", default-features = false } +cumulus-pallet-xcm = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.12", default-features = false } + +# Polkadot dependencies +polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.12", default-features = false } +xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.12", default-features = false } +xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.12", default-features = false } +xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.12", default-features = false } +pallet-xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.12", default-features = false } + +# Parachain dependencies +btc-relay = { path = "../../../crates/btc-relay", default-features = false } +currency = { path = "../../../crates/currency", default-features = false } +security = { path = "../../../crates/security", default-features = false } +relay = { path = "../../../crates/relay", default-features = false } +vault-registry = { path = "../../../crates/vault-registry", default-features = false } +oracle = { path = "../../../crates/oracle", default-features = false } +fee = { path = "../../../crates/fee", default-features = false } +issue = { path = "../../../crates/issue", default-features = false } +redeem = { path = "../../../crates/redeem", default-features = false } +replace = { path = "../../../crates/replace", default-features = false } +refund = { path = "../../../crates/refund", default-features = false } +nomination = { path = "../../../crates/nomination", default-features = false } +reward = { path = "../../../crates/reward", default-features = false } +staking = { path = "../../../crates/staking", default-features = false } +escrow = { path = "../../../crates/escrow", default-features = false } +democracy = { path = "../../../crates/democracy", default-features = false } +annuity = { path = "../../../crates/annuity", default-features = false } +supply = { path = "../../../crates/supply", default-features = false } + +primitives = { package = "interbtc-primitives", path = "../../../primitives", default-features = false } + +parachain-info = { path = "../../../crates/parachain-info", default-features = false } + +module-btc-relay-rpc-runtime-api = { path = "../../../crates/btc-relay/rpc/runtime-api", default-features = false } +module-oracle-rpc-runtime-api = { path = "../../../crates/oracle/rpc/runtime-api", default-features = false } +module-relay-rpc-runtime-api = { path = "../../../crates/relay/rpc/runtime-api", default-features = false } +module-vault-registry-rpc-runtime-api = { path = "../../../crates/vault-registry/rpc/runtime-api", default-features = false } +module-issue-rpc-runtime-api = { path = "../../../crates/issue/rpc/runtime-api", default-features = false } +module-redeem-rpc-runtime-api = { path = "../../../crates/redeem/rpc/runtime-api", default-features = false } +module-replace-rpc-runtime-api = { path = "../../../crates/replace/rpc/runtime-api", default-features = false } +module-refund-rpc-runtime-api = { path = "../../../crates/refund/rpc/runtime-api", default-features = false } + +# Orml dependencies +orml-tokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "36f9395fc449b7b0902b369c69d12c858eba25fd", default-features = false } +orml-traits = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "36f9395fc449b7b0902b369c69d12c858eba25fd", default-features = false } +orml-vesting = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "36f9395fc449b7b0902b369c69d12c858eba25fd", default-features = false } + +orml-xtokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "36f9395fc449b7b0902b369c69d12c858eba25fd", default-features = false } +orml-xcm-support = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "36f9395fc449b7b0902b369c69d12c858eba25fd", default-features = false } +orml-unknown-tokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "36f9395fc449b7b0902b369c69d12c858eba25fd", default-features = false } + +[dev-dependencies] +hex = '0.4.2' +mocktopus = '0.7.0' +serde_json = "1.0" + +bitcoin = { path = "../../../crates/bitcoin", default-features = false } + +[build-dependencies] +substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12" } + +# TODO: enable weak dependency activation when available +# https://github.com/rust-lang/cargo/issues/8832 +# https://github.com/paritytech/substrate/issues/8161 + +[features] +default = ["std"] +std = [ + "serde", + "codec/std", + + "sp-std/std", + "sp-api/std", + "sp-io/std", + "sp-version/std", + "sp-runtime/std", + "sp-core/std", + "sp-session/std", + "sp-offchain/std", + "sp-block-builder/std", + "sp-transaction-pool/std", + "sp-inherents/std", + "sp-arithmetic/std", + + "frame-support/std", + "frame-executive/std", + "frame-system/std", + "frame-benchmarking/std", + "frame-system-benchmarking/std", + "pallet-balances/std", + "pallet-timestamp/std", + "pallet-sudo/std", + "pallet-utility/std", + "pallet-transaction-payment/std", + "pallet-treasury/std", + "pallet-scheduler/std", + "pallet-multisig/std", + + "frame-system-rpc-runtime-api/std", + "pallet-transaction-payment-rpc-runtime-api/std", + + "pallet-collective/std", + "pallet-membership/std", + "pallet-society/std", + + "pallet-authorship/std", + "pallet-aura/std", + "sp-consensus-aura/std", + + "cumulus-pallet-aura-ext/std", + "cumulus-pallet-dmp-queue/std", + "cumulus-pallet-parachain-system/std", + "cumulus-pallet-xcmp-queue/std", + "cumulus-pallet-xcm/std", + "cumulus-primitives-core/std", + "cumulus-primitives-timestamp/std", + "cumulus-primitives-utility/std", + + "polkadot-parachain/std", + "xcm/std", + "xcm-builder/std", + "xcm-executor/std", + "pallet-xcm/std", + + "btc-relay/std", + "currency/std", + "security/std", + "relay/std", + "vault-registry/std", + "oracle/std", + "fee/std", + "refund/std", + "issue/std", + "redeem/std", + "replace/std", + "nomination/std", + "reward/std", + "staking/std", + "escrow/std", + "democracy/std", + "annuity/std", + "supply/std", + + "primitives/std", + + "parachain-info/std", + + "module-btc-relay-rpc-runtime-api/std", + "module-oracle-rpc-runtime-api/std", + "module-relay-rpc-runtime-api/std", + "module-vault-registry-rpc-runtime-api/std", + "module-issue-rpc-runtime-api/std", + "module-redeem-rpc-runtime-api/std", + "module-replace-rpc-runtime-api/std", + "module-refund-rpc-runtime-api/std", + + "orml-tokens/std", + "orml-traits/std", + "orml-vesting/std", + + "orml-xtokens/std", + "orml-xcm-support/std", + "orml-unknown-tokens/std", +] +runtime-benchmarks = [ + "frame-benchmarking", + "frame-system-benchmarking", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-society/runtime-benchmarks", + "pallet-collective/runtime-benchmarks", + + "btc-relay/runtime-benchmarks", + "oracle/runtime-benchmarks", + "issue/runtime-benchmarks", + "redeem/runtime-benchmarks", + "replace/runtime-benchmarks", + "relay/runtime-benchmarks", + "vault-registry/runtime-benchmarks", + "fee/runtime-benchmarks", +] +disable-runtime-api = [] diff --git a/parachain/runtime/testnet/build.rs b/parachain/runtime/testnet/build.rs new file mode 100644 index 0000000000..18a76f7893 --- /dev/null +++ b/parachain/runtime/testnet/build.rs @@ -0,0 +1,9 @@ +use substrate_wasm_builder::WasmBuilder; + +fn main() { + WasmBuilder::new() + .with_current_project() + .export_heap_base() + .import_memory() + .build() +} diff --git a/parachain/runtime/testnet/src/lib.rs b/parachain/runtime/testnet/src/lib.rs new file mode 100644 index 0000000000..e2d85eabd2 --- /dev/null +++ b/parachain/runtime/testnet/src/lib.rs @@ -0,0 +1,1650 @@ +//! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm. + +#![cfg_attr(not(feature = "std"), no_std)] +// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. +#![recursion_limit = "256"] + +// Make the WASM binary available. +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +use bitcoin::types::H256Le; +use currency::Amount; +use frame_support::{ + dispatch::{DispatchError, DispatchResult}, + traits::{ + Contains, Currency as PalletCurrency, EnsureOrigin, ExistenceRequirement, FindAuthor, Imbalance, OnUnbalanced, + }, + PalletId, +}; +use frame_system::{EnsureOneOf, EnsureRoot, RawOrigin}; +use orml_traits::{parameter_type_with_key, MultiCurrency}; +use sp_api::impl_runtime_apis; +use sp_core::{u32_trait::_1, OpaqueMetadata, H256}; +use sp_runtime::{ + create_runtime_str, generic, impl_opaque_keys, + traits::{AccountIdConversion, BlakeTwo256, Block as BlockT, IdentityLookup, Zero}, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, +}; +use sp_std::{marker::PhantomData, prelude::*}; +#[cfg(feature = "std")] +use sp_version::NativeVersion; +use sp_version::RuntimeVersion; + +use codec::Encode; +// A few exports that help ease life for downstream crates. +pub use frame_support::{ + construct_runtime, parameter_types, + traits::{Everything, Get, KeyOwnerProofSystem, LockIdentifier, Nothing}, + weights::{ + constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND}, + DispatchClass, IdentityFee, Weight, + }, + StorageValue, +}; +use frame_system::limits::{BlockLength, BlockWeights}; +pub use pallet_timestamp::Call as TimestampCall; +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; +pub use sp_runtime::{Perbill, Permill}; + +// interBTC exports +pub use btc_relay::{bitcoin, Call as RelayCall, TARGET_SPACING}; +pub use module_oracle_rpc_runtime_api::BalanceWrapper; +pub use security::StatusCode; + +pub use primitives::{ + self, AccountId, Balance, BlockNumber, CurrencyId, CurrencyId::Token, CurrencyInfo, Hash, Moment, Nonce, Signature, + SignedFixedPoint, SignedInner, UnsignedFixedPoint, UnsignedInner, KBTC, KINT, KSM, +}; + +// XCM imports +use cumulus_primitives_core::ParaId; +use orml_xcm_support::{IsNativeConcrete, MultiCurrencyAdapter, MultiNativeAsset}; +use pallet_transaction_payment::{Multiplier, TargetedFeeAdjustment}; +use pallet_xcm::XcmPassthrough; +use polkadot_parachain::primitives::Sibling; +use sp_runtime::{traits::Convert, FixedPointNumber, Perquintill}; +use xcm::{ + v1::{prelude::*, MultiAsset, MultiLocation, NetworkId}, + AlwaysV1, +}; +use xcm_builder::{ + AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, + EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, LocationInverter, NativeAsset, ParentAsSuperuser, + ParentIsDefault, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeRevenue, TakeWeightCredit, +}; +use xcm_executor::{Config, XcmExecutor}; + +pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; + +type VaultId = primitives::VaultId; + +impl_opaque_keys! { + pub struct SessionKeys { + pub aura: Aura, + } +} + +/// This runtime version. +#[sp_version::runtime_version] +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("testnet-parachain"), + impl_name: create_runtime_str!("testnet-parachain"), + authoring_version: 1, + spec_version: 0, + impl_version: 1, + transaction_version: 0, + apis: RUNTIME_API_VERSIONS, +}; + +// The relay chain is limited to 12s to include parachain blocks. +pub const MILLISECS_PER_BLOCK: u64 = 12000; + +pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; + +// These time units are defined in number of blocks. +pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); +pub const HOURS: BlockNumber = MINUTES * 60; +pub const DAYS: BlockNumber = HOURS * 24; +pub const WEEKS: BlockNumber = DAYS * 7; +pub const YEARS: BlockNumber = DAYS * 365; + +pub const BITCOIN_SPACING_MS: u32 = TARGET_SPACING * 1000; +pub const BITCOIN_BLOCK_SPACING: BlockNumber = BITCOIN_SPACING_MS / MILLISECS_PER_BLOCK as BlockNumber; + +pub mod token_distribution { + use super::*; + + // 10 million KINT distributed over 4 years + // KINT has 12 decimal places, same as KSM + // See: https://wiki.polkadot.network/docs/learn-DOT#kusama + pub const INITIAL_ALLOCATION: Balance = 10_000_000_u128 * UNITS; + + // multiplication is non-overflow by default + pub const ESCROW_INFLATION_REWARDS: Permill = Permill::from_parts(67000); // 6.7% + pub const TREASURY_INFLATION_REWARDS: Permill = Permill::from_parts(533000); // 53.3% + pub const VAULT_INFLATION_REWARDS: Permill = Permill::from_percent(40); // 40% +} + +/// The version information used to identify this runtime when compiled natively. +#[cfg(feature = "std")] +pub fn native_version() -> NativeVersion { + NativeVersion { + runtime_version: VERSION, + can_author_with: Default::default(), + } +} + +/// We assume that ~10% of the block weight is consumed by `on_initalize` handlers. +/// This is used to limit the maximal weight of a single extrinsic. +const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10); +/// We allow `Normal` extrinsics to fill up the block up to 75%, the rest can be used +/// by Operational extrinsics. +const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); +/// We allow for 2 seconds of compute with a 12 second average block time. +const MAXIMUM_BLOCK_WEIGHT: Weight = 2 * WEIGHT_PER_SECOND; + +parameter_types! { + pub const BlockHashCount: BlockNumber = 250; + pub const Version: RuntimeVersion = VERSION; + pub RuntimeBlockLength: BlockLength = + BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); + pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder() + .base_block(BlockExecutionWeight::get()) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = 0; // TODO: this is 0 so that we can do runtime upgrade without fees. Restore value afterwards! + }) + .for_class(DispatchClass::Normal, |weights| { + weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT); + }) + .for_class(DispatchClass::Operational, |weights| { + weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT); + // Operational transactions have some extra reserved space, so that they + // are included even if block reached `MAXIMUM_BLOCK_WEIGHT`. + weights.reserved = Some( + MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT + ); + }) + .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) + .build_or_panic(); + pub const SS58Prefix: u16 = 42; +} + +pub struct BaseCallFilter; + +impl Contains for BaseCallFilter { + fn contains(call: &Call) -> bool { + if matches!( + call, + Call::System(_) + | Call::Authorship(_) + | Call::Timestamp(_) + | Call::ParachainSystem(_) + | Call::Sudo(_) + | Call::Democracy(_) + | Call::Escrow(_) + | Call::TechnicalCommittee(_) + ) { + // always allow core calls + true + } else { + // disallow everything if shutdown + !security::Pallet::::is_parachain_shutdown() + } + } +} + +impl frame_system::Config for Runtime { + /// The identifier used to distinguish between accounts. + type AccountId = AccountId; + /// The aggregated dispatch type that is available for extrinsics. + type Call = Call; + /// The lookup mechanism to get account ID from whatever is passed in dispatchers. + type Lookup = IdentityLookup; + /// The index type for storing how many extrinsics an account has signed. + type Index = Nonce; + /// The index type for blocks. + type BlockNumber = BlockNumber; + /// The type for hashing blocks and tries. + type Hash = Hash; + /// The hashing algorithm used. + type Hashing = BlakeTwo256; + /// The header type. + type Header = generic::Header; + /// The ubiquitous event type. + type Event = Event; + /// The ubiquitous origin type. + type Origin = Origin; + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). + type BlockHashCount = BlockHashCount; + /// Runtime version. + type Version = Version; + /// Converts a module to an index of this module in the runtime. + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = BaseCallFilter; + type SystemWeightInfo = (); + type BlockWeights = RuntimeBlockWeights; + type BlockLength = RuntimeBlockLength; + type SS58Prefix = SS58Prefix; + type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; +} + +parameter_types! { + pub const UncleGenerations: u32 = 0; +} + +impl pallet_authorship::Config for Runtime { + type FindAuthor = AuraAccountAdapter; + type UncleGenerations = UncleGenerations; + type FilterUncle = (); + type EventHandler = (); +} + +pub struct AuraAccountAdapter; + +impl FindAuthor for AuraAccountAdapter { + fn find_author<'a, I>(digests: I) -> Option + where + I: 'a + IntoIterator, + { + use sp_std::convert::TryFrom; + pallet_aura::AuraAuthorId::::find_author(digests).and_then(|k| AccountId::try_from(k.as_ref()).ok()) + } +} + +parameter_types! { + pub const MaxAuthorities: u32 = 32; +} + +impl pallet_aura::Config for Runtime { + type AuthorityId = AuraId; + type DisabledValidators = (); + type MaxAuthorities = MaxAuthorities; +} + +parameter_types! { + pub const MinimumPeriod: u64 = SLOT_DURATION / 2; +} + +impl pallet_timestamp::Config for Runtime { + /// A timestamp: milliseconds since the unix epoch. + type Moment = Moment; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +pub type SlowAdjustingFeeUpdate = + TargetedFeeAdjustment; + +parameter_types! { + pub const TransactionByteFee: Balance = 0; // TODO: this is 0 so that we can do runtime upgrade without fees. Restore value afterwards! + pub OperationalFeeMultiplier: u8 = 5; + /// The portion of the `NORMAL_DISPATCH_RATIO` that we adjust the fees with. Blocks filled less + /// than this will decrease the weight and more will increase. + pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25); + /// The adjustment variable of the runtime. Higher values will cause `TargetBlockFullness` to + /// change the fees more rapidly. + pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(3, 100_000); + /// Minimum amount of the multiplier. This value cannot be too low. A test case should ensure + /// that combined with `AdjustmentVariable`, we can recover from the minimum. + /// See `multiplier_can_grow_from_zero`. + pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1u128, 1_000_000u128); +} + +type NegativeImbalance = as PalletCurrency< + ::AccountId, +>>::NegativeImbalance; + +pub struct DealWithFees(PhantomData<(T, GetCurrencyId)>); + +impl OnUnbalanced> for DealWithFees +where + T: pallet_authorship::Config + orml_tokens::Config, + GetCurrencyId: Get, +{ + fn on_unbalanceds(mut fees_then_tips: impl Iterator>) { + if let Some(mut fees) = fees_then_tips.next() { + if let Some(tips) = fees_then_tips.next() { + tips.merge_into(&mut fees); + } + let author = pallet_authorship::Pallet::::author(); + orml_tokens::CurrencyAdapter::::resolve_creating(&author, fees); + } + } +} + +impl pallet_transaction_payment::Config for Runtime { + type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter< + orml_tokens::CurrencyAdapter, + DealWithFees, + >; + type TransactionByteFee = TransactionByteFee; + type WeightToFee = IdentityFee; + type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type OperationalFeeMultiplier = OperationalFeeMultiplier; +} + +impl pallet_sudo::Config for Runtime { + type Call = Call; + type Event = Event; +} + +impl pallet_utility::Config for Runtime { + type Call = Call; + type Event = Event; + type WeightInfo = (); +} + +parameter_types! { + pub MinVestedTransfer: Balance = 0; + // NOTE: per account, airdrop only needs one + pub const MaxVestingSchedules: u32 = 1; +} + +parameter_types! { + pub KintsugiLabsAccounts: Vec = vec![ + // 5Fhn5mX4JGeDxikaxkJZYRYjxxbZ7DjxS5f9hsAVAzGXUNyG + hex_literal::hex!["a0fb017d4b777bc2be8ad9e9dfe7bdf0a3db060644de499685adacd19f84df71"].into(), + // 5GgS9vsF77Y7p2wZLEW1CW7vZpq8DSoXCf2sTdBoB51jpuan + hex_literal::hex!["cc30e8cd03a20ce00f7dab8451a1d43047a43f50cdd0bc9b14dbaa78ed66bd1e"].into(), + // 5GDzXqLxGiJV6A7mDp1SGRV6DB8xnnwauMEwR7PL4PW122FM + hex_literal::hex!["b80646c2c305d0e8f1e3df9cf515a3cf1f5fc7e24a8205202fce65dfb8198345"].into(), + // 5FgimgwW2s4V14NniQ6Nt145Sksb83xohW5LkMXYnMw3Racp + hex_literal::hex!["a02c9cba51b7ec7c1717cdf0fd9044fa5228d9e8217a5a904871ce47627d8743"].into(), + ]; +} + +pub struct EnsureKintsugiLabs; +impl EnsureOrigin for EnsureKintsugiLabs { + type Success = AccountId; + + fn try_origin(o: Origin) -> Result { + Into::, Origin>>::into(o).and_then(|o| match o { + RawOrigin::Signed(caller) => { + if KintsugiLabsAccounts::get().contains(&caller) { + Ok(caller) + } else { + Err(Origin::from(Some(caller))) + } + } + r => Err(Origin::from(r)), + }) + } + + #[cfg(feature = "runtime-benchmarks")] + fn successful_origin() -> Origin { + Origin::from(RawOrigin::Signed(Default::default())) + } +} + +impl orml_vesting::Config for Runtime { + type Event = Event; + type Currency = NativeCurrency; + type MinVestedTransfer = MinVestedTransfer; + type VestedTransferOrigin = EnsureKintsugiLabs; + type WeightInfo = (); + type MaxVestingSchedules = MaxVestingSchedules; + type BlockNumberProvider = System; +} + +parameter_types! { + pub MaximumSchedulerWeight: Weight = Perbill::from_percent(10) * RuntimeBlockWeights::get().max_block; + pub const MaxScheduledPerBlock: u32 = 30; +} + +impl pallet_scheduler::Config for Runtime { + type Event = Event; + type Origin = Origin; + type PalletsOrigin = OriginCaller; + type Call = Call; + type MaximumWeight = MaximumSchedulerWeight; + type ScheduleOrigin = EnsureRoot; + type MaxScheduledPerBlock = MaxScheduledPerBlock; + type WeightInfo = (); +} + +// https://github.com/paritytech/polkadot/blob/c4ee9d463adccfa3bf436433e3e26d0de5a4abbc/runtime/kusama/src/constants.rs#L18 +pub const UNITS: Balance = 1_000_000_000_000; +pub const CENTS: Balance = UNITS / 30_000; +pub const GRAND: Balance = CENTS * 100_000; +pub const MILLICENTS: Balance = CENTS / 1_000; + +pub const fn deposit(items: u32, bytes: u32) -> Balance { + items as Balance * 2_000 * CENTS + (bytes as Balance) * 100 * MILLICENTS +} + +type EnsureRootOrAllTechnicalCommittee = EnsureOneOf< + AccountId, + EnsureRoot, + pallet_collective::EnsureProportionAtLeast<_1, _1, AccountId, TechnicalCommitteeInstance>, +>; + +parameter_types! { + pub const LaunchPeriod: BlockNumber = 7 * DAYS; + pub const VotingPeriod: BlockNumber = 7 * DAYS; + pub const FastTrackVotingPeriod: BlockNumber = 3 * HOURS; + // Require 5 vKINT to make a proposal. Given the crowdloan airdrop, this qualifies about 3500 + // accounts to make a governance proposal. Only 2300 can do two proposals, + // and 700 accounts can do ten or more proposals. + pub MinimumDeposit: Balance = 5 * UNITS; + pub const EnactmentPeriod: BlockNumber = DAYS; + pub PreimageByteDeposit: Balance = 10 * MILLICENTS; + pub const MaxVotes: u32 = 100; + pub const MaxProposals: u32 = 100; +} + +impl democracy::Config for Runtime { + type Proposal = Call; + type Event = Event; + type Currency = Escrow; + type EnactmentPeriod = EnactmentPeriod; + type LaunchPeriod = LaunchPeriod; + type VotingPeriod = VotingPeriod; + type MinimumDeposit = MinimumDeposit; + /// The technical committee can have any proposal be tabled immediately + /// with a shorter voting period. + type FastTrackOrigin = EnsureRootOrAllTechnicalCommittee; + type FastTrackVotingPeriod = FastTrackVotingPeriod; + type PreimageByteDeposit = PreimageByteDeposit; + type Slash = Treasury; + type Scheduler = Scheduler; + type PalletsOrigin = OriginCaller; + type MaxVotes = MaxVotes; + type WeightInfo = (); + type MaxProposals = MaxProposals; +} + +parameter_types! { + // One storage item; key size is 32; value is size 4+4+16+32 bytes = 56 bytes. + pub const GetDepositBase: Balance = deposit(1, 88); + // Additional storage item size of 32 bytes. + pub const GetDepositFactor: Balance = deposit(0, 32); + pub GetMaxSignatories: u16 = 100; // multisig of at most 100 accounts +} + +impl pallet_multisig::Config for Runtime { + type Event = Event; + type Call = Call; + type Currency = orml_tokens::CurrencyAdapter; // pay for execution in INTR/KINT + type DepositBase = GetDepositBase; + type DepositFactor = GetDepositFactor; + type MaxSignatories = GetMaxSignatories; + type WeightInfo = (); +} + +parameter_types! { + pub const ProposalBond: Permill = Permill::from_percent(5); + pub ProposalBondMinimum: Balance = 5; + pub const SpendPeriod: BlockNumber = 7 * DAYS; + pub const Burn: Permill = Permill::from_percent(0); + pub const MaxApprovals: u32 = 100; +} + +impl pallet_treasury::Config for Runtime { + type PalletId = TreasuryPalletId; + type Currency = orml_tokens::CurrencyAdapter; + type ApproveOrigin = EnsureRoot; + type RejectOrigin = EnsureRoot; + type Event = Event; + type OnSlash = Treasury; + type ProposalBond = ProposalBond; + type ProposalBondMinimum = ProposalBondMinimum; + type SpendPeriod = SpendPeriod; + type Burn = Burn; + type BurnDestination = (); + type SpendFunds = (); + type WeightInfo = (); + type MaxApprovals = MaxApprovals; +} + +parameter_types! { + pub const TechnicalCommitteeMotionDuration: BlockNumber = 3 * DAYS; + pub const TechnicalCommitteeMaxProposals: u32 = 100; + pub const TechnicalCommitteeMaxMembers: u32 = 100; +} + +type TechnicalCommitteeInstance = pallet_collective::Instance1; + +impl pallet_collective::Config for Runtime { + type Origin = Origin; + type Proposal = Call; + type Event = Event; + type MotionDuration = TechnicalCommitteeMotionDuration; + type MaxProposals = TechnicalCommitteeMaxProposals; + type MaxMembers = TechnicalCommitteeMaxMembers; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type WeightInfo = (); +} + +impl pallet_membership::Config for Runtime { + type Event = Event; + type AddOrigin = EnsureRoot; + type RemoveOrigin = EnsureRoot; + type SwapOrigin = EnsureRoot; + type ResetOrigin = EnsureRoot; + type PrimeOrigin = EnsureRoot; + type MembershipInitialized = TechnicalCommittee; + type MembershipChanged = TechnicalCommittee; + type MaxMembers = TechnicalCommitteeMaxMembers; + type WeightInfo = (); +} + +parameter_types! { + pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 4; + pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 4; +} + +impl cumulus_pallet_parachain_system::Config for Runtime { + type Event = Event; + type OnValidationData = (); + type SelfParaId = parachain_info::Pallet; + type OutboundXcmpMessageSource = XcmpQueue; + type DmpMessageHandler = DmpQueue; + type ReservedDmpWeight = ReservedDmpWeight; + type XcmpMessageHandler = XcmpQueue; + type ReservedXcmpWeight = ReservedXcmpWeight; +} + +impl parachain_info::Config for Runtime {} + +impl cumulus_pallet_aura_ext::Config for Runtime {} + +parameter_types! { + pub const ParentLocation: MultiLocation = MultiLocation::parent(); + pub const ParentNetwork: NetworkId = NetworkId::Kusama; + pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); + pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); +} + +/// Means for transacting assets on this chain. +type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the default `AccountId`. + ParentIsDefault, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // Straight up local `AccountId32` origins just alias directly to `AccountId`. + AccountId32Aliases, +); + +/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, +/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can +/// biases the kind of local `Origin` it will become. +pub type XcmOriginToTransactDispatchOrigin = ( + // Sovereign account converter; this attempts to derive an `AccountId` from the origin location + // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for + // foreign chains who want to have a local sovereign account on this chain which they control. + SovereignSignedViaLocation, + // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when + // recognised. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognised. + SiblingParachainAsNative, + // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a + // transaction from the Root origin. + ParentAsSuperuser, + // Native signed account converter; this just converts an `AccountId32` origin into a normal + // `Origin::Signed` origin of the same 32-byte value. + SignedAccountId32AsNative, + // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. + XcmPassthrough, +); + +parameter_types! { + // One XCM operation is 1_000_000 weight - almost certainly a conservative estimate. + pub UnitWeightCost: Weight = 1_000_000; +} + +pub type Barrier = ( + TakeWeightCredit, + AllowTopLevelPaidExecutionFrom, + AllowKnownQueryResponses, + AllowSubscriptionsFrom, +); // required for others to keep track of our xcm version + +parameter_types! { + pub const MaxInstructions: u32 = 100; +} +pub struct XcmConfig; + +// the ksm cost to to execute a no-op extrinsic +fn base_tx_in_ksm() -> Balance { + KSM.one() / 50_000 +} +pub fn ksm_per_second() -> u128 { + let base_weight = Balance::from(ExtrinsicBaseWeight::get()); + let base_tx_per_second = (WEIGHT_PER_SECOND as u128) / base_weight; + base_tx_per_second * base_tx_in_ksm() +} + +parameter_types! { + pub KsmPerSecond: (AssetId, u128) = (MultiLocation::parent().into(), ksm_per_second()); + pub KintPerSecond: (AssetId, u128) = ( + MultiLocation::new( + 1, + X2(Parachain(2092), GeneralKey(Token(KINT).encode())), + ).into(), + // KINT:KSM = 4:3 + (ksm_per_second() * 4) / 3 + ); + pub KbtcPerSecond: (AssetId, u128) = ( + MultiLocation::new( + 1, + X2(Parachain(2092), GeneralKey(Token(KBTC).encode())), + ).into(), + // KBTC:KSM = 1:150 & Satoshi:Planck = 1:10_000 + ksm_per_second() / 1_500_000 + ); +} + +parameter_types! { + pub KintsugiTreasuryAccount: AccountId = TreasuryPalletId::get().into_account(); +} + +pub struct ToTreasury; +impl TakeRevenue for ToTreasury { + fn take_revenue(revenue: MultiAsset) { + if let MultiAsset { + id: Concrete(location), + fun: Fungible(amount), + } = revenue + { + if let Some(currency_id) = CurrencyIdConvert::convert(location) { + // Note: we should ensure that treasury account has existenial deposit for all of the cross-chain asset. + // Ignore the result. + let _ = Tokens::deposit(currency_id, &KintsugiTreasuryAccount::get(), amount); + } + } + } +} + +pub type Trader = ( + FixedRateOfFungible, + FixedRateOfFungible, + FixedRateOfFungible, +); + +impl Config for XcmConfig { + type Call = Call; + type XcmSender = XcmRouter; + // How to withdraw and deposit an asset. + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + type IsReserve = MultiNativeAsset; + type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation + type LocationInverter = LocationInverter; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = Trader; + type ResponseHandler = (); // Don't handle responses for now. + type SubscriptionService = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetClaims = PolkadotXcm; +} + +/// No local origins on this chain are allowed to dispatch XCM sends/executions. +pub type LocalOriginToLocation = (SignedToAccountId32,); + +/// The means for routing XCM messages which are not for local execution into the right message +/// queues. +pub type XcmRouter = ( + // Two routers - use UMP to communicate with the relay chain: + cumulus_primitives_utility::ParentAsUmp, + // ..and XCMP to communicate with the sibling chains. + XcmpQueue, +); + +impl pallet_xcm::Config for Runtime { + type Event = Event; + type Call = Call; + type Origin = Origin; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Everything; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type LocationInverter = LocationInverter; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; +} + +impl cumulus_pallet_xcm::Config for Runtime { + type Event = Event; + type XcmExecutor = XcmExecutor; +} + +impl cumulus_pallet_xcmp_queue::Config for Runtime { + type Event = Event; + type XcmExecutor = XcmExecutor; + type ChannelInfo = ParachainSystem; + type VersionWrapper = AlwaysV1; +} + +impl cumulus_pallet_dmp_queue::Config for Runtime { + type Event = Event; + type XcmExecutor = XcmExecutor; + type ExecuteOverweightOrigin = frame_system::EnsureRoot; +} + +pub type LocalAssetTransactor = MultiCurrencyAdapter< + Tokens, + UnknownTokens, + IsNativeConcrete, + AccountId, + LocationToAccountId, + CurrencyId, + CurrencyIdConvert, +>; + +pub use currency_id_convert::CurrencyIdConvert; + +mod currency_id_convert { + use super::*; + use codec::{Decode, Encode}; + + fn native_currency_location(id: CurrencyId) -> MultiLocation { + MultiLocation::new(1, X2(Parachain(ParachainInfo::get().into()), GeneralKey(id.encode()))) + } + + pub struct CurrencyIdConvert; + + impl Convert> for CurrencyIdConvert { + fn convert(id: CurrencyId) -> Option { + match id { + RELAY_CHAIN_CURRENCY_ID => Some(MultiLocation::parent()), + WRAPPED_CURRENCY_ID => Some(native_currency_location(id)), + NATIVE_CURRENCY_ID => Some(native_currency_location(id)), + _ => None, + } + } + } + + impl Convert> for CurrencyIdConvert { + fn convert(location: MultiLocation) -> Option { + match location { + x if x == MultiLocation::parent() => Some(RELAY_CHAIN_CURRENCY_ID), + MultiLocation { + parents: 1, + interior: X2(Parachain(id), GeneralKey(key)), + } if ParaId::from(id) == ParachainInfo::get() => { + // decode the general key + if let Ok(currency_id) = CurrencyId::decode(&mut &key[..]) { + // check `currency_id` is cross-chain asset + match currency_id { + WRAPPED_CURRENCY_ID => Some(currency_id), + NATIVE_CURRENCY_ID => Some(currency_id), + _ => None, + } + } else { + None + } + } + _ => None, + } + } + } + + impl Convert> for CurrencyIdConvert { + fn convert(asset: MultiAsset) -> Option { + if let MultiAsset { + id: Concrete(location), .. + } = asset + { + Self::convert(location) + } else { + None + } + } + } +} + +parameter_types! { + pub SelfLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(ParachainInfo::get().into()))); +} + +pub struct AccountIdToMultiLocation; + +impl Convert for AccountIdToMultiLocation { + fn convert(account: AccountId) -> MultiLocation { + X1(AccountId32 { + network: NetworkId::Any, + id: account.into(), + }) + .into() + } +} + +impl orml_xtokens::Config for Runtime { + type Event = Event; + type Balance = Balance; + type CurrencyId = CurrencyId; + type CurrencyIdConvert = CurrencyIdConvert; + type AccountIdToMultiLocation = AccountIdToMultiLocation; + type SelfLocation = SelfLocation; + type XcmExecutor = XcmExecutor; + type Weigher = FixedWeightBounds; + type BaseXcmWeight = UnitWeightCost; + type LocationInverter = ::LocationInverter; +} + +impl orml_unknown_tokens::Config for Runtime { + type Event = Event; +} + +parameter_types! { + pub const ParachainBlocksPerBitcoinBlock: BlockNumber = BITCOIN_BLOCK_SPACING; +} + +impl btc_relay::Config for Runtime { + type Event = Event; + type WeightInfo = (); + type ParachainBlocksPerBitcoinBlock = ParachainBlocksPerBitcoinBlock; +} + +const RELAY_CHAIN_CURRENCY_ID: CurrencyId = Token(KSM); +const WRAPPED_CURRENCY_ID: CurrencyId = Token(KBTC); +const NATIVE_CURRENCY_ID: CurrencyId = Token(KINT); + +parameter_types! { + pub const GetCollateralCurrencyId: CurrencyId = RELAY_CHAIN_CURRENCY_ID; + pub const GetWrappedCurrencyId: CurrencyId = WRAPPED_CURRENCY_ID; + pub const GetNativeCurrencyId: CurrencyId = NATIVE_CURRENCY_ID; +} + +type NativeCurrency = orml_tokens::CurrencyAdapter; + +// Pallet accounts +parameter_types! { + pub const FeePalletId: PalletId = PalletId(*b"mod/fees"); + pub const SupplyPalletId: PalletId = PalletId(*b"mod/supl"); + pub const EscrowAnnuityPalletId: PalletId = PalletId(*b"esc/annu"); + pub const VaultAnnuityPalletId: PalletId = PalletId(*b"vlt/annu"); + pub const TreasuryPalletId: PalletId = PalletId(*b"mod/trsy"); + pub const VaultRegistryPalletId: PalletId = PalletId(*b"mod/vreg"); +} + +parameter_types! { + // 5EYCAe5i8QbRr5WN1PvaAVqPbfXsqazk9ocaxuzcTjgXPM1e + pub FeeAccount: AccountId = FeePalletId::get().into_account(); + // 5EYCAe5i8QbRrUhwETaRvgif6H3HA84YQri7wjgLtKzRJCML + pub SupplyAccount: AccountId = SupplyPalletId::get().into_account(); + // 5EYCAe5gXcgF6fT7oVsD7E4bfnRZeovzMUD2wkdyvCHrYQQE + pub EscrowAnnuityAccount: AccountId = EscrowAnnuityPalletId::get().into_account(); + // 5EYCAe5jvsMTc6NLhunLTPVjJg5cZNweWKjNXKqz9RUqQJDY + pub VaultAnnuityAccount: AccountId = VaultAnnuityPalletId::get().into_account(); + // 5EYCAe5i8QbRrWTk2CHjZA79gSf1piYSGm2LQfxaw6id3M88 + pub TreasuryAccount: AccountId = TreasuryPalletId::get().into_account(); + // 5EYCAe5i8QbRra1jndPz1WAuf1q1KHQNfu2cW1EXJ231emTd + pub VaultRegistryAccount: AccountId = VaultRegistryPalletId::get().into_account(); +} + +pub fn get_all_module_accounts() -> Vec { + vec![ + FeeAccount::get(), + SupplyAccount::get(), + EscrowAnnuityAccount::get(), + VaultAnnuityAccount::get(), + TreasuryAccount::get(), + VaultRegistryAccount::get(), + ] +} + +pub struct DustRemovalWhitelist; +impl Contains for DustRemovalWhitelist { + fn contains(a: &AccountId) -> bool { + get_all_module_accounts().contains(a) + } +} + +parameter_types! { + pub const MaxLocks: u32 = 50; +} + +parameter_type_with_key! { + pub ExistentialDeposits: |_currency_id: CurrencyId| -> Balance { + Zero::zero() + }; +} + +impl orml_tokens::Config for Runtime { + type Event = Event; + type Balance = Balance; + type Amount = primitives::Amount; + type CurrencyId = CurrencyId; + type WeightInfo = (); + type ExistentialDeposits = ExistentialDeposits; + type OnDust = orml_tokens::TransferDust; + type MaxLocks = MaxLocks; + type DustRemovalWhitelist = DustRemovalWhitelist; +} + +parameter_types! { + pub const InflationPeriod: BlockNumber = YEARS; +} + +pub struct DealWithRewards; + +impl supply::OnInflation for DealWithRewards { + type Currency = NativeCurrency; + // transfer will only fail if balance is too low + // existential deposit is not required due to whitelist + fn on_inflation(from: &AccountId, amount: Balance) { + let vault_inflation = token_distribution::VAULT_INFLATION_REWARDS * amount; + let escrow_inflation = token_distribution::ESCROW_INFLATION_REWARDS * amount; + + // vault block rewards + let _ = Self::Currency::transfer( + from, + &VaultAnnuityAccount::get(), + vault_inflation, + ExistenceRequirement::KeepAlive, + ); + VaultAnnuity::update_reward_per_block(); + + // stake-to-vote block rewards + let _ = Self::Currency::transfer( + from, + &EscrowAnnuityAccount::get(), + escrow_inflation, + ExistenceRequirement::KeepAlive, + ); + EscrowAnnuity::update_reward_per_block(); + + // remainder goes to treasury + let _ = Self::Currency::transfer( + from, + &TreasuryAccount::get(), + amount.saturating_sub(vault_inflation).saturating_sub(escrow_inflation), + ExistenceRequirement::KeepAlive, + ); + } +} + +impl supply::Config for Runtime { + type SupplyPalletId = SupplyPalletId; + type Event = Event; + type UnsignedFixedPoint = UnsignedFixedPoint; + type Currency = NativeCurrency; + type InflationPeriod = InflationPeriod; + type OnInflation = DealWithRewards; +} + +parameter_types! { + pub const EmissionPeriod: BlockNumber = YEARS; +} + +pub struct EscrowBlockRewardProvider; + +impl annuity::BlockRewardProvider for EscrowBlockRewardProvider { + type Currency = NativeCurrency; + fn distribute_block_reward(_from: &AccountId, amount: Balance) -> DispatchResult { + >::distribute_reward( + amount, + GetNativeCurrencyId::get(), + ) + } + fn withdraw_reward(who: &AccountId) -> Result { + >::withdraw_reward( + who, + GetNativeCurrencyId::get(), + ) + } +} + +type EscrowAnnuityInstance = annuity::Instance1; + +impl annuity::Config for Runtime { + type AnnuityPalletId = EscrowAnnuityPalletId; + type Event = Event; + type Currency = NativeCurrency; + type BlockRewardProvider = EscrowBlockRewardProvider; + type BlockNumberToBalance = BlockNumberToBalance; + type EmissionPeriod = EmissionPeriod; +} + +pub struct VaultBlockRewardProvider; + +impl annuity::BlockRewardProvider for VaultBlockRewardProvider { + type Currency = NativeCurrency; + fn distribute_block_reward(from: &AccountId, amount: Balance) -> DispatchResult { + // TODO: remove fee pallet? + Self::Currency::transfer(from, &FeeAccount::get(), amount, ExistenceRequirement::KeepAlive)?; + >::distribute_reward( + amount, + GetNativeCurrencyId::get(), + ) + } + fn withdraw_reward(_: &AccountId) -> Result { + Ok(Zero::zero()) + } +} + +type VaultAnnuityInstance = annuity::Instance2; + +impl annuity::Config for Runtime { + type AnnuityPalletId = VaultAnnuityPalletId; + type Event = Event; + type Currency = NativeCurrency; + type BlockRewardProvider = VaultBlockRewardProvider; + type BlockNumberToBalance = BlockNumberToBalance; + type EmissionPeriod = EmissionPeriod; +} + +type EscrowRewardsInstance = reward::Instance1; + +impl reward::Config for Runtime { + type Event = Event; + type SignedFixedPoint = SignedFixedPoint; + type RewardId = AccountId; + type CurrencyId = CurrencyId; + type GetNativeCurrencyId = GetNativeCurrencyId; + type GetWrappedCurrencyId = GetWrappedCurrencyId; +} + +type VaultRewardsInstance = reward::Instance2; + +impl reward::Config for Runtime { + type Event = Event; + type SignedFixedPoint = SignedFixedPoint; + type RewardId = VaultId; + type CurrencyId = CurrencyId; + type GetNativeCurrencyId = GetNativeCurrencyId; + type GetWrappedCurrencyId = GetWrappedCurrencyId; +} + +impl security::Config for Runtime { + type Event = Event; +} + +pub use relay::Event as RelayEvent; + +pub struct CurrencyConvert; +impl currency::CurrencyConversion, CurrencyId> for CurrencyConvert { + fn convert(amount: ¤cy::Amount, to: CurrencyId) -> Result, DispatchError> { + Oracle::convert(amount, to) + } +} + +impl currency::Config for Runtime { + type SignedInner = SignedInner; + type SignedFixedPoint = SignedFixedPoint; + type UnsignedFixedPoint = UnsignedFixedPoint; + type Balance = Balance; + type GetWrappedCurrencyId = GetWrappedCurrencyId; + type CurrencyConversion = CurrencyConvert; +} + +impl relay::Config for Runtime { + type Event = Event; + type WeightInfo = (); +} + +impl staking::Config for Runtime { + type Event = Event; + type SignedFixedPoint = SignedFixedPoint; + type SignedInner = SignedInner; + type CurrencyId = CurrencyId; + type GetNativeCurrencyId = GetNativeCurrencyId; +} + +parameter_types! { + pub const Span: BlockNumber = WEEKS; + pub const MaxPeriod: BlockNumber = WEEKS * 96; +} + +pub struct BlockNumberToBalance; + +impl Convert for BlockNumberToBalance { + fn convert(a: BlockNumber) -> Balance { + a.into() + } +} + +impl escrow::Config for Runtime { + type Event = Event; + type BlockNumberToBalance = BlockNumberToBalance; + type Currency = orml_tokens::CurrencyAdapter; + type Span = Span; + type MaxPeriod = MaxPeriod; + type EscrowRewards = EscrowRewards; + type WeightInfo = (); +} + +impl vault_registry::Config for Runtime { + type PalletId = VaultRegistryPalletId; + type Event = Event; + type Balance = Balance; + type WeightInfo = (); + type GetGriefingCollateralCurrencyId = GetNativeCurrencyId; +} + +impl frame_system::offchain::SendTransactionTypes for Runtime +where + Call: From, +{ + type OverarchingCall = Call; + type Extrinsic = UncheckedExtrinsic; +} + +impl oracle::Config for Runtime { + type Event = Event; + type WeightInfo = (); +} + +impl fee::Config for Runtime { + type FeePalletId = FeePalletId; + type WeightInfo = (); + type SignedFixedPoint = SignedFixedPoint; + type SignedInner = SignedInner; + type UnsignedFixedPoint = UnsignedFixedPoint; + type UnsignedInner = UnsignedInner; + type VaultRewards = VaultRewards; + type VaultStaking = VaultStaking; + type GetNativeCurrencyId = GetNativeCurrencyId; + type OnSweep = currency::SweepFunds; +} + +pub use refund::{Event as RefundEvent, RefundRequest}; + +impl refund::Config for Runtime { + type Event = Event; + type WeightInfo = (); +} + +pub use issue::{Event as IssueEvent, IssueRequest}; + +impl issue::Config for Runtime { + type Event = Event; + type WeightInfo = (); +} + +pub use redeem::{Event as RedeemEvent, RedeemRequest}; + +impl redeem::Config for Runtime { + type Event = Event; + type WeightInfo = (); +} + +pub use replace::{Event as ReplaceEvent, ReplaceRequest}; + +impl replace::Config for Runtime { + type Event = Event; + type WeightInfo = (); +} + +pub use nomination::Event as NominationEvent; + +impl nomination::Config for Runtime { + type Event = Event; + type WeightInfo = (); +} + +construct_runtime! { + pub enum Runtime where + Block = Block, + NodeBlock = primitives::Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: frame_system::{Pallet, Call, Storage, Config, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + Sudo: pallet_sudo::{Pallet, Call, Storage, Config, Event}, + Utility: pallet_utility::{Pallet, Call, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage}, + Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event}, + MultiSig: pallet_multisig::{Pallet, Call, Storage, Event}, + + // Tokens & Balances + Currency: currency::{Pallet}, + Tokens: orml_tokens::{Pallet, Call, Storage, Config, Event}, + Escrow: escrow::{Pallet, Call, Storage, Event}, + Vesting: orml_vesting::{Pallet, Storage, Call, Event, Config}, + + EscrowAnnuity: annuity::::{Pallet, Call, Storage, Event}, + EscrowRewards: reward::::{Pallet, Storage, Event}, + + VaultAnnuity: annuity::::{Pallet, Storage, Event}, + VaultRewards: reward::::{Pallet, Storage, Event}, + VaultStaking: staking::{Pallet, Storage, Event}, + + Supply: supply::{Pallet, Storage, Call, Event, Config}, + + // Bitcoin SPV + BTCRelay: btc_relay::{Pallet, Call, Config, Storage, Event}, + Relay: relay::{Pallet, Call, Storage, Event}, + + // Operational + Security: security::{Pallet, Call, Config, Storage, Event}, + VaultRegistry: vault_registry::{Pallet, Call, Config, Storage, Event, ValidateUnsigned}, + Oracle: oracle::{Pallet, Call, Config, Storage, Event}, + Issue: issue::{Pallet, Call, Config, Storage, Event}, + Redeem: redeem::{Pallet, Call, Config, Storage, Event}, + Replace: replace::{Pallet, Call, Config, Storage, Event}, + Fee: fee::{Pallet, Call, Config, Storage}, + Refund: refund::{Pallet, Call, Config, Storage, Event}, + Nomination: nomination::{Pallet, Call, Config, Storage, Event}, + + // Governance + Democracy: democracy::{Pallet, Call, Storage, Config, Event}, + TechnicalCommittee: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config}, + TechnicalMembership: pallet_membership::{Pallet, Call, Storage, Event, Config}, + Treasury: pallet_treasury::{Pallet, Call, Storage, Config, Event}, + + ParachainSystem: cumulus_pallet_parachain_system::{Pallet, Call, Config, Storage, Inherent, Event}, + ParachainInfo: parachain_info::{Pallet, Storage, Config}, + + Authorship: pallet_authorship::{Pallet, Call, Storage}, + Aura: pallet_aura::{Pallet, Storage, Config}, + AuraExt: cumulus_pallet_aura_ext::{Pallet, Storage, Config}, + + // XCM helpers. + XcmpQueue: cumulus_pallet_xcmp_queue::{Pallet, Call, Storage, Event}, + PolkadotXcm: pallet_xcm::{Pallet, Call, Event, Origin}, + CumulusXcm: cumulus_pallet_xcm::{Pallet, Call, Event, Origin}, + DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event}, + + XTokens: orml_xtokens::{Pallet, Storage, Call, Event}, + UnknownTokens: orml_unknown_tokens::{Pallet, Storage, Event}, + } +} + +/// The address format for describing accounts. +pub type Address = AccountId; +/// Block header type as expected by this runtime. +pub type Header = generic::Header; +/// Block type as expected by this runtime. +pub type Block = generic::Block; +/// A Block signed with a Justification +pub type SignedBlock = generic::SignedBlock; +/// BlockId type as expected by this runtime. +pub type BlockId = generic::BlockId; +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = ( + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, +); +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +/// Extrinsic type that has already been checked. +pub type CheckedExtrinsic = generic::CheckedExtrinsic; +/// Executive: handles dispatch to the various modules. +pub type Executive = + frame_executive::Executive, Runtime, AllPallets>; + +#[cfg(not(feature = "disable-runtime-api"))] +impl_runtime_apis! { + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block) + } + + fn initialize_block(header: &::Header) { + Executive::initialize_block(header) + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + } + + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { + Executive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> ::Header { + Executive::finalize_block() + } + + fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> { + data.create_extrinsics() + } + + fn check_inherents( + block: Block, + data: sp_inherents::InherentData, + ) -> sp_inherents::CheckInherentsResult { + data.check_extrinsics(&block) + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + tx: ::Extrinsic, + block_hash: ::Hash, + ) -> TransactionValidity { + Executive::validate_transaction(source, tx, block_hash) + } + } + + impl sp_offchain::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &::Header) { + Executive::offchain_worker(header) + } + } + + impl sp_session::SessionKeys for Runtime { + fn decode_session_keys( + encoded: Vec, + ) -> Option, sp_core::crypto::KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } + + fn generate_session_keys(seed: Option>) -> Vec { + SessionKeys::generate(seed) + } + } + + impl sp_consensus_aura::AuraApi for Runtime { + fn slot_duration() -> sp_consensus_aura::SlotDuration { + sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) + } + + fn authorities() -> Vec { + Aura::authorities().into_inner() + } + } + + impl cumulus_primitives_core::CollectCollationInfo for Runtime { + fn collect_collation_info() -> cumulus_primitives_core::CollationInfo { + ParachainSystem::collect_collation_info() + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Nonce { + System::account_nonce(account) + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi for Runtime { + fn query_info( + uxt: ::Extrinsic, + len: u32, + ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { + TransactionPayment::query_info(uxt, len) + } + + fn query_fee_details( + uxt: ::Extrinsic, + len: u32, + ) -> pallet_transaction_payment_rpc_runtime_api::FeeDetails { + TransactionPayment::query_fee_details(uxt, len) + } + } + + #[cfg(feature = "runtime-benchmarks")] + impl frame_benchmarking::Benchmark for Runtime { + fn benchmark_metadata(extra: bool) -> ( + Vec, + Vec, + ) { + use frame_benchmarking::{list_benchmark, Benchmarking, BenchmarkList}; + use frame_support::traits::StorageInfoTrait; + + let mut list = Vec::::new(); + + list_benchmark!(list, extra, btc_relay, BTCRelay); + list_benchmark!(list, extra, fee, Fee); + list_benchmark!(list, extra, issue, Issue); + list_benchmark!(list, extra, nomination, Nomination); + list_benchmark!(list, extra, oracle, Oracle); + list_benchmark!(list, extra, redeem, Redeem); + list_benchmark!(list, extra, refund, Refund); + list_benchmark!(list, extra, relay, Relay); + list_benchmark!(list, extra, replace, Replace); + list_benchmark!(list, extra, vault_registry, VaultRegistry); + + let storage_info = AllPalletsWithSystem::storage_info(); + + return (list, storage_info) + } + + fn dispatch_benchmark( + config: frame_benchmarking::BenchmarkConfig + ) -> Result, sp_runtime::RuntimeString> { + use frame_benchmarking::{Benchmarking, BenchmarkBatch, add_benchmark, TrackedStorageKey}; + + let whitelist: Vec = vec![ + // Block Number + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(), + // Total Issuance + hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80").to_vec().into(), + // Execution Phase + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec().into(), + // Event Count + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec().into(), + // System Events + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec().into(), + ]; + + let mut batches = Vec::::new(); + let params = (&config, &whitelist); + + add_benchmark!(params, batches, btc_relay, BTCRelay); + add_benchmark!(params, batches, fee, Fee); + add_benchmark!(params, batches, issue, Issue); + add_benchmark!(params, batches, nomination, Nomination); + add_benchmark!(params, batches, oracle, Oracle); + add_benchmark!(params, batches, redeem, Redeem); + add_benchmark!(params, batches, refund, Refund); + add_benchmark!(params, batches, relay, Relay); + add_benchmark!(params, batches, replace, Replace); + add_benchmark!(params, batches, vault_registry, VaultRegistry); + + if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } + Ok(batches) + } + } + + impl module_btc_relay_rpc_runtime_api::BtcRelayApi< + Block, + H256Le, + > for Runtime { + fn verify_block_header_inclusion(block_hash: H256Le) -> Result<(), DispatchError> { + BTCRelay::verify_block_header_inclusion(block_hash, None).map(|_| ()) + } + } + + impl module_oracle_rpc_runtime_api::OracleApi< + Block, + Balance, + CurrencyId + > for Runtime { + fn wrapped_to_collateral( amount: BalanceWrapper, currency_id: CurrencyId) -> Result, DispatchError> { + let result = Oracle::wrapped_to_collateral(amount.amount, currency_id)?; + Ok(BalanceWrapper{amount:result}) + } + + fn collateral_to_wrapped(amount: BalanceWrapper, currency_id: CurrencyId) -> Result, DispatchError> { + let result = Oracle::collateral_to_wrapped(amount.amount, currency_id)?; + Ok(BalanceWrapper{amount:result}) + } + } + + impl module_relay_rpc_runtime_api::RelayApi< + Block, + VaultId, + > for Runtime { + fn is_transaction_invalid(vault_id: VaultId, raw_tx: Vec) -> DispatchResult { + Relay::is_transaction_invalid(&vault_id, raw_tx) + } + } + + impl module_vault_registry_rpc_runtime_api::VaultRegistryApi< + Block, + VaultId, + Balance, + UnsignedFixedPoint, + CurrencyId, + AccountId, + > for Runtime { + fn get_vault_collateral(vault_id: VaultId) -> Result, DispatchError> { + let result = VaultRegistry::compute_collateral(&vault_id)?; + Ok(BalanceWrapper{amount:result.amount()}) + } + + fn get_vaults_by_account_id(account_id: AccountId) -> Result, DispatchError> { + VaultRegistry::get_vaults_by_account_id(account_id) + } + + fn get_vault_total_collateral(vault_id: VaultId) -> Result, DispatchError> { + let result = VaultRegistry::get_backing_collateral(&vault_id)?; + Ok(BalanceWrapper{amount:result.amount()}) + } + + fn get_premium_redeem_vaults() -> Result)>, DispatchError> { + let result = VaultRegistry::get_premium_redeem_vaults()?; + Ok(result.iter().map(|v| (v.0.clone(), BalanceWrapper{amount:v.1.amount()})).collect()) + } + + fn get_vaults_with_issuable_tokens() -> Result)>, DispatchError> { + let result = VaultRegistry::get_vaults_with_issuable_tokens()?; + Ok(result.into_iter().map(|v| (v.0, BalanceWrapper{amount:v.1.amount()})).collect()) + } + + fn get_vaults_with_redeemable_tokens() -> Result)>, DispatchError> { + let result = VaultRegistry::get_vaults_with_redeemable_tokens()?; + Ok(result.into_iter().map(|v| (v.0, BalanceWrapper{amount:v.1.amount()})).collect()) + } + + fn get_issuable_tokens_from_vault(vault: VaultId) -> Result, DispatchError> { + let result = VaultRegistry::get_issuable_tokens_from_vault(&vault)?; + Ok(BalanceWrapper{amount:result.amount()}) + } + + fn get_collateralization_from_vault(vault: VaultId, only_issued: bool) -> Result { + VaultRegistry::get_collateralization_from_vault(vault, only_issued) + } + + fn get_collateralization_from_vault_and_collateral(vault: VaultId, collateral: BalanceWrapper, only_issued: bool) -> Result { + let amount = Amount::new(collateral.amount, vault.collateral_currency()); + VaultRegistry::get_collateralization_from_vault_and_collateral(vault, &amount, only_issued) + } + + fn get_required_collateral_for_wrapped(amount_btc: BalanceWrapper, currency_id: CurrencyId) -> Result, DispatchError> { + let amount_btc = Amount::new(amount_btc.amount, GetWrappedCurrencyId::get()); + let result = VaultRegistry::get_required_collateral_for_wrapped(&amount_btc, currency_id)?; + Ok(BalanceWrapper{amount:result.amount()}) + } + + fn get_required_collateral_for_vault(vault_id: VaultId) -> Result, DispatchError> { + let result = VaultRegistry::get_required_collateral_for_vault(vault_id)?; + Ok(BalanceWrapper{amount:result.amount()}) + } + } + + impl module_issue_rpc_runtime_api::IssueApi< + Block, + AccountId, + H256, + IssueRequest + > for Runtime { + fn get_issue_requests(account_id: AccountId) -> Vec { + Issue::get_issue_requests_for_account(account_id) + } + + fn get_vault_issue_requests(vault_id: AccountId) -> Vec { + Issue::get_issue_requests_for_vault(vault_id) + } + } + + impl module_redeem_rpc_runtime_api::RedeemApi< + Block, + AccountId, + H256, + RedeemRequest + > for Runtime { + fn get_redeem_requests(account_id: AccountId) -> Vec { + Redeem::get_redeem_requests_for_account(account_id) + } + + fn get_vault_redeem_requests(account_id: AccountId) -> Vec { + Redeem::get_redeem_requests_for_vault(account_id) + } + } + + impl module_refund_rpc_runtime_api::RefundApi< + Block, + AccountId, + H256, + RefundRequest + > for Runtime { + fn get_refund_requests(account_id: AccountId) -> Vec { + Refund::get_refund_requests_for_account(account_id) + } + + fn get_refund_requests_by_issue_id(issue_id: H256) -> Option { + Refund::get_refund_requests_by_issue_id(issue_id) + } + + fn get_vault_refund_requests(vault_id: AccountId) -> Vec { + Refund::get_refund_requests_for_vault(vault_id) + } + } + + impl module_replace_rpc_runtime_api::ReplaceApi< + Block, + AccountId, + H256, + ReplaceRequest + > for Runtime { + fn get_old_vault_replace_requests(vault_id: AccountId) -> Vec { + Replace::get_replace_requests_for_old_vault(vault_id) + } + + fn get_new_vault_replace_requests(vault_id: AccountId) -> Vec { + Replace::get_replace_requests_for_new_vault(vault_id) + } + } +} + +struct CheckInherents; + +impl cumulus_pallet_parachain_system::CheckInherents for CheckInherents { + fn check_inherents( + block: &Block, + relay_state_proof: &cumulus_pallet_parachain_system::RelayChainStateProof, + ) -> sp_inherents::CheckInherentsResult { + let relay_chain_slot = relay_state_proof + .read_slot() + .expect("Could not read the relay chain slot from the proof"); + + let inherent_data = cumulus_primitives_timestamp::InherentDataProvider::from_relay_chain_slot_and_duration( + relay_chain_slot, + sp_std::time::Duration::from_secs(6), + ) + .create_inherent_data() + .expect("Could not create the timestamp inherent data"); + + inherent_data.check_extrinsics(&block) + } +} + +cumulus_pallet_parachain_system::register_validate_block! { + Runtime = Runtime, + BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, + CheckInherents = CheckInherents, +} diff --git a/parachain/src/chain_spec.rs b/parachain/src/chain_spec.rs index 1989eb0e4e..9713981330 100644 --- a/parachain/src/chain_spec.rs +++ b/parachain/src/chain_spec.rs @@ -26,6 +26,9 @@ pub type InterlayChainSpec = sc_service::GenericChainSpec; +/// Specialized `ChainSpec` for the testnet parachain runtime. +pub type TestnetChainSpec = sc_service::GenericChainSpec; + /// The extensions for the [`ChainSpec`]. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecExtension, ChainSpecGroup)] #[serde(deny_unknown_fields)] @@ -84,6 +87,20 @@ const DEFAULT_DUST_VALUE: Balance = 1000; const DEFAULT_BITCOIN_CONFIRMATIONS: u32 = 1; const SECURE_BITCOIN_CONFIRMATIONS: u32 = 6; +fn testnet_properties() -> Map { + let mut properties = Map::new(); + let mut token_symbol: Vec = vec![]; + let mut token_decimals: Vec = vec![]; + [KINT, KBTC, KSM, INTR, INTERBTC, DOT].iter().for_each(|token| { + token_symbol.push(token.symbol().to_string()); + token_decimals.push(token.decimals() as u32); + }); + properties.insert("tokenSymbol".into(), token_symbol.into()); + properties.insert("tokenDecimals".into(), token_decimals.into()); + properties.insert("ss58Format".into(), testnet_runtime::SS58Prefix::get().into()); + properties +} + fn kintsugi_properties() -> Map { let mut properties = Map::new(); let mut token_symbol: Vec = vec![]; @@ -127,13 +144,14 @@ fn expected_transaction_size() -> u32 { ) } -pub fn local_config(id: ParaId) -> KintsugiChainSpec { - KintsugiChainSpec::from_genesis( +pub fn local_config(id: ParaId) -> TestnetChainSpec { + TestnetChainSpec::from_genesis( "interBTC", "local_testnet", ChainType::Local, move || { testnet_genesis( + get_account_id_from_seed::("Alice"), vec![get_from_seed::("Alice")], vec![ get_account_id_from_seed::("Alice"), @@ -161,7 +179,7 @@ pub fn local_config(id: ParaId) -> KintsugiChainSpec { vec![], None, None, - Some(kintsugi_properties()), + Some(testnet_properties()), Extensions { relay_chain: "local".into(), para_id: id.into(), @@ -169,13 +187,14 @@ pub fn local_config(id: ParaId) -> KintsugiChainSpec { ) } -pub fn development_config(id: ParaId) -> KintsugiChainSpec { - KintsugiChainSpec::from_genesis( +pub fn development_config(id: ParaId) -> TestnetChainSpec { + TestnetChainSpec::from_genesis( "interBTC", "dev_testnet", ChainType::Development, move || { testnet_genesis( + get_account_id_from_seed::("Alice"), vec![get_from_seed::("Alice")], vec![ get_account_id_from_seed::("Alice"), @@ -213,7 +232,7 @@ pub fn development_config(id: ParaId) -> KintsugiChainSpec { Vec::new(), None, None, - Some(kintsugi_properties()), + Some(testnet_properties()), Extensions { relay_chain: "dev".into(), para_id: id.into(), @@ -221,13 +240,14 @@ pub fn development_config(id: ParaId) -> KintsugiChainSpec { ) } -pub fn rococo_testnet_config(id: ParaId) -> KintsugiChainSpec { - KintsugiChainSpec::from_genesis( +pub fn rococo_testnet_config(id: ParaId) -> TestnetChainSpec { + TestnetChainSpec::from_genesis( "interBTC", "rococo_testnet", ChainType::Live, move || { testnet_genesis( + get_account_id_from_string("5HeVGqvfpabwFqzV1DhiQmjaLQiFcTSmq2sH6f7atsXkgvtt"), vec![ // 5DJ3wbdicFSFFudXndYBuvZKjucTsyxtJX5WPzQM8HysSkFY hex!["366a092a27b4b28199a588b0155a2c9f3f0513d92481de4ee2138273926fa91c"].unchecked_into(), @@ -268,7 +288,7 @@ pub fn rococo_testnet_config(id: ParaId) -> KintsugiChainSpec { Vec::new(), None, None, - Some(kintsugi_properties()), + Some(testnet_properties()), Extensions { relay_chain: "rococo".into(), para_id: id.into(), @@ -276,17 +296,18 @@ pub fn rococo_testnet_config(id: ParaId) -> KintsugiChainSpec { ) } -pub fn rococo_local_testnet_config(id: ParaId) -> KintsugiChainSpec { +pub fn rococo_local_testnet_config(id: ParaId) -> TestnetChainSpec { development_config(id) } -pub fn westend_testnet_config(id: ParaId) -> KintsugiChainSpec { - KintsugiChainSpec::from_genesis( +pub fn westend_testnet_config(id: ParaId) -> TestnetChainSpec { + TestnetChainSpec::from_genesis( "interBTC", "westend_testnet", ChainType::Live, move || { testnet_genesis( + get_account_id_from_string("5DUupBJSyBDcqQudgPR4gttFie3cLPRw3HwaUfq9H2D2mKiA"), vec![ // 5H75GkhA6TnyCW7fM4H8LyoTqmPJWf3JuZZPFR9Bpv26LGHA (//authority/0) hex!["defbbf8f70964f6a4952bc168b6c1489b502e05d6b5ef57f8767589cf3813705"].unchecked_into(), @@ -341,7 +362,7 @@ pub fn westend_testnet_config(id: ParaId) -> KintsugiChainSpec { Vec::new(), None, None, - Some(kintsugi_properties()), + Some(testnet_properties()), Extensions { relay_chain: "westend".into(), para_id: id.into(), @@ -364,34 +385,39 @@ fn default_pair_kintsugi(currency_id: CurrencyId) -> VaultCurrencyPair, endowed_accounts: Vec, authorized_oracles: Vec<(AccountId, Vec)>, id: ParaId, bitcoin_confirmations: u32, start_shutdown: bool, -) -> kintsugi_runtime::GenesisConfig { - kintsugi_runtime::GenesisConfig { - system: kintsugi_runtime::SystemConfig { - code: kintsugi_runtime::WASM_BINARY +) -> testnet_runtime::GenesisConfig { + testnet_runtime::GenesisConfig { + system: testnet_runtime::SystemConfig { + code: testnet_runtime::WASM_BINARY .expect("WASM binary was not build, please build it!") .to_vec(), changes_trie_config: Default::default(), }, - aura: kintsugi_runtime::AuraConfig { + aura: testnet_runtime::AuraConfig { authorities: initial_authorities, }, aura_ext: Default::default(), parachain_system: Default::default(), - parachain_info: kintsugi_runtime::ParachainInfoConfig { parachain_id: id }, - security: kintsugi_runtime::SecurityConfig { + parachain_info: testnet_runtime::ParachainInfoConfig { parachain_id: id }, + security: testnet_runtime::SecurityConfig { initial_status: if start_shutdown { - kintsugi_runtime::StatusCode::Shutdown + testnet_runtime::StatusCode::Shutdown } else { - kintsugi_runtime::StatusCode::Error + testnet_runtime::StatusCode::Error }, }, - tokens: kintsugi_runtime::TokensConfig { + sudo: testnet_runtime::SudoConfig { + // Assign network admin rights. + key: root_key.clone(), + }, + tokens: testnet_runtime::TokensConfig { balances: endowed_accounts .iter() .flat_map(|k| { @@ -405,32 +431,32 @@ fn testnet_genesis( .collect(), }, vesting: Default::default(), - oracle: kintsugi_runtime::OracleConfig { + oracle: testnet_runtime::OracleConfig { authorized_oracles, max_delay: DEFAULT_MAX_DELAY_MS, }, - btc_relay: kintsugi_runtime::BTCRelayConfig { + btc_relay: testnet_runtime::BTCRelayConfig { bitcoin_confirmations, - parachain_confirmations: bitcoin_confirmations.saturating_mul(kintsugi_runtime::BITCOIN_BLOCK_SPACING), + parachain_confirmations: bitcoin_confirmations.saturating_mul(testnet_runtime::BITCOIN_BLOCK_SPACING), disable_difficulty_check: true, disable_inclusion_check: false, }, - issue: kintsugi_runtime::IssueConfig { - issue_period: kintsugi_runtime::DAYS, + issue: testnet_runtime::IssueConfig { + issue_period: testnet_runtime::DAYS, issue_btc_dust_value: DEFAULT_DUST_VALUE, }, - redeem: kintsugi_runtime::RedeemConfig { + redeem: testnet_runtime::RedeemConfig { redeem_transaction_size: expected_transaction_size(), - redeem_period: kintsugi_runtime::DAYS, + redeem_period: testnet_runtime::DAYS, redeem_btc_dust_value: DEFAULT_DUST_VALUE, }, - replace: kintsugi_runtime::ReplaceConfig { - replace_period: kintsugi_runtime::DAYS, + replace: testnet_runtime::ReplaceConfig { + replace_period: testnet_runtime::DAYS, replace_btc_dust_value: DEFAULT_DUST_VALUE, }, - vault_registry: kintsugi_runtime::VaultRegistryConfig { + vault_registry: testnet_runtime::VaultRegistryConfig { minimum_collateral_vault: vec![(Token(KSM), 0)], - punishment_delay: kintsugi_runtime::DAYS, + punishment_delay: testnet_runtime::DAYS, system_collateral_ceiling: vec![(default_pair_kintsugi(Token(KSM)), 1000 * KSM.one())], secure_collateral_threshold: vec![( default_pair_kintsugi(Token(KSM)), @@ -445,7 +471,7 @@ fn testnet_genesis( FixedU128::checked_from_rational(110, 100).unwrap(), )], /* 110% */ }, - fee: kintsugi_runtime::FeeConfig { + fee: testnet_runtime::FeeConfig { issue_fee: FixedU128::checked_from_rational(5, 1000).unwrap(), // 0.5% issue_griefing_collateral: FixedU128::checked_from_rational(5, 100000).unwrap(), // 0.005% refund_fee: FixedU128::checked_from_rational(5, 1000).unwrap(), // 0.5% @@ -456,21 +482,21 @@ fn testnet_genesis( theft_fee: FixedU128::checked_from_rational(5, 100).unwrap(), // 5% theft_fee_max: 10000000, // 0.1 BTC }, - refund: kintsugi_runtime::RefundConfig { + refund: testnet_runtime::RefundConfig { refund_btc_dust_value: DEFAULT_DUST_VALUE, refund_transaction_size: expected_transaction_size(), }, - nomination: kintsugi_runtime::NominationConfig { + nomination: testnet_runtime::NominationConfig { is_nomination_enabled: false, }, technical_committee: Default::default(), technical_membership: Default::default(), treasury: Default::default(), democracy: Default::default(), - supply: kintsugi_runtime::SupplyConfig { - initial_supply: kintsugi_runtime::token_distribution::INITIAL_ALLOCATION, + supply: testnet_runtime::SupplyConfig { + initial_supply: testnet_runtime::token_distribution::INITIAL_ALLOCATION, // start of year 5 - start_height: kintsugi_runtime::YEARS * 4, + start_height: testnet_runtime::YEARS * 4, inflation: FixedU128::checked_from_rational(2, 100).unwrap(), // 2% }, } diff --git a/parachain/src/command.rs b/parachain/src/command.rs index b6bb570f5c..fbf0e91473 100644 --- a/parachain/src/command.rs +++ b/parachain/src/command.rs @@ -17,7 +17,7 @@ use crate::{ chain_spec, cli::{Cli, Subcommand}, - service::{new_partial, InterlayRuntimeExecutor, KintsugiRuntimeExecutor}, + service::{new_partial, InterlayRuntimeExecutor, KintsugiRuntimeExecutor, TestnetRuntimeExecutor}, }; use primitives::Block; use sc_cli::{ChainSpec, Result, RuntimeVersion, SubstrateCli}; @@ -40,6 +40,7 @@ const DEFAULT_PARA_ID: u32 = 2121; trait IdentifyChain { fn is_interlay(&self) -> bool; fn is_kintsugi(&self) -> bool; + fn is_testnet(&self) -> bool; } impl IdentifyChain for dyn sc_service::ChainSpec { @@ -49,6 +50,9 @@ impl IdentifyChain for dyn sc_service::ChainSpec { fn is_kintsugi(&self) -> bool { self.id().starts_with("kintsugi") } + fn is_testnet(&self) -> bool { + self.id().starts_with("testnet") + } } impl IdentifyChain for T { @@ -58,6 +62,9 @@ impl IdentifyChain for T { fn is_kintsugi(&self) -> bool { ::is_kintsugi(self) } + fn is_testnet(&self) -> bool { + ::is_testnet(self) + } } fn load_spec(id: &str, para_id: ParaId) -> std::result::Result, String> { @@ -81,6 +88,8 @@ fn load_spec(id: &str, para_id: ParaId) -> std::result::Result) -> &'static RuntimeVersion { if chain_spec.is_interlay() { &interlay_runtime::VERSION - } else { + } else if chain_spec.is_kintsugi() { &kintsugi_runtime::VERSION + } else { + &testnet_runtime::VERSION } } } @@ -186,7 +197,7 @@ macro_rules! construct_async_run { let task_manager = $components.task_manager; { $( $code )* }.map(|v| (v, task_manager)) }) - } else { + } else if runner.config().chain_spec.is_kintsugi() { runner.async_run(|$config| { let $components = new_partial::< kintsugi_runtime::RuntimeApi, @@ -197,6 +208,17 @@ macro_rules! construct_async_run { let task_manager = $components.task_manager; { $( $code )* }.map(|v| (v, task_manager)) }) + } else { + runner.async_run(|$config| { + let $components = new_partial::< + testnet_runtime::RuntimeApi, + TestnetRuntimeExecutor, + >( + &$config, + )?; + let task_manager = $components.task_manager; + { $( $code )* }.map(|v| (v, task_manager)) + }) } }} } @@ -266,6 +288,8 @@ pub fn run() -> Result<()> { runner.sync_run(|config| cmd.run::(config)) } else if runner.config().chain_spec.is_kintsugi() { runner.sync_run(|config| cmd.run::(config)) + } else if runner.config().chain_spec.is_testnet() { + runner.sync_run(|config| cmd.run::(config)) } else { Err("Chain doesn't support benchmarking".into()) } @@ -363,11 +387,16 @@ async fn start_node(cli: Cli, config: Configuration) -> sc_service::error::Resul .await .map(|r| r.0) .map_err(Into::into) - } else { + } else if config.chain_spec.is_kintsugi() { crate::service::start_node::(config, polkadot_config, id) .await .map(|r| r.0) .map_err(Into::into) + } else { + crate::service::start_node::(config, polkadot_config, id) + .await + .map(|r| r.0) + .map_err(Into::into) } } diff --git a/parachain/src/service.rs b/parachain/src/service.rs index 2945349373..6eee7306eb 100644 --- a/parachain/src/service.rs +++ b/parachain/src/service.rs @@ -20,35 +20,34 @@ use sp_runtime::traits::BlakeTwo256; use std::sync::Arc; use substrate_prometheus_endpoint::Registry; -// Native kintsugi executor instance. -pub struct KintsugiRuntimeExecutor; - -impl sc_executor::NativeExecutionDispatch for KintsugiRuntimeExecutor { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; - - fn dispatch(method: &str, data: &[u8]) -> Option> { - kintsugi_runtime::api::dispatch(method, data) - } - - fn native_version() -> sc_executor::NativeVersion { - kintsugi_runtime::native_version() - } +macro_rules! new_runtime_executor { + ($name:ident,$runtime:ident) => { + + pub struct $name; + + impl sc_executor::NativeExecutionDispatch for $name { + type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; + + fn dispatch(method: &str, data: &[u8]) -> Option> { + $runtime::api::dispatch(method, data) + } + + fn native_version() -> sc_executor::NativeVersion { + $runtime::native_version() + } + } + + }; } // Native interlay executor instance. -pub struct InterlayRuntimeExecutor; - -impl sc_executor::NativeExecutionDispatch for InterlayRuntimeExecutor { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; +new_runtime_executor!(InterlayRuntimeExecutor, interlay_runtime); - fn dispatch(method: &str, data: &[u8]) -> Option> { - interlay_runtime::api::dispatch(method, data) - } +// Native kintsugi executor instance. +new_runtime_executor!(KintsugiRuntimeExecutor, kintsugi_runtime); - fn native_version() -> sc_executor::NativeVersion { - interlay_runtime::native_version() - } -} +// Native testnet executor instance. +new_runtime_executor!(TestnetRuntimeExecutor, testnet_runtime); pub trait RuntimeApiCollection: sp_transaction_pool::runtime_api::TaggedTransactionQueue