Skip to content

Commit

Permalink
native token pallet
Browse files Browse the repository at this point in the history
  • Loading branch information
AmbientTea committed Aug 21, 2024
1 parent 321efc4 commit 75c0ceb
Show file tree
Hide file tree
Showing 13 changed files with 499 additions and 15 deletions.
15 changes: 15 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ members = [
"primitives/session-manager",
"primitives/sidechain",
"partner-chains-cli",
"pallets/native-token-management",
"primitives/native-token-management"]
resolver = "2"

Expand Down Expand Up @@ -188,4 +189,5 @@ authority-selection-inherents = { path = "primitives/authority-selection-inheren
session-manager = { path = "primitives/session-manager", default-features = false }
sp-sidechain = { path = "primitives/sidechain", default-features = false }
chain-params = { path = "primitives/chain-params", default-features = false }
pallet-native-token-management = { path = "pallets/native-token-management", default-features = false }
sp-native-token-management = { path = "primitives/native-token-management", default-features = false }
5 changes: 5 additions & 0 deletions devnet/.envrc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ export COMMITTEE_CANDIDATE_ADDRESS=$(jq -r '.addresses.CommitteeCandidateValidat
export D_PARAMETER_POLICY_ID=$(jq -r '.mintingPolicies.DParameterPolicy' devnet/addresses.json)
export PERMISSIONED_CANDIDATES_POLICY_ID=$(jq -r '.mintingPolicies.PermissionedCandidatesPolicy' devnet/addresses.json)

# native token observability
export PARTNER_CHAIN_NATIVE_TOKEN_POLICY_ID='ada83ddd029614381f00e28de0922ab0dec6983ea9dd29ae20eef9b4'
export PARTNER_CHAIN_NATIVE_TOKEN_ASSET_NAME='5043546f6b656e44656d6f'
export PARTNER_CHAIN_ILLIQUID_SUPPLY_VALIDATOR_ADDRESS='addr_test1wrhvtvx3f0g9wv9rx8kfqc60jva3e07nqujk2cspekv4mqs9rjdvz'

# Preview parameters
export CARDANO_SECURITY_PARAMETER=432
export CARDANO_ACTIVE_SLOTS_COEFF=0.05
Expand Down
19 changes: 16 additions & 3 deletions node/src/staging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ use crate::chain_spec::get_account_id_from_seed;
use crate::chain_spec::*;
use chain_params::SidechainParams;
use sc_service::ChainType;
use sidechain_domain::{MainchainAddressHash, UtxoId};
use sidechain_domain::*;
use sidechain_runtime::{
AccountId, AuraConfig, BalancesConfig, GrandpaConfig, RuntimeGenesisConfig,
SessionCommitteeManagementConfig, SessionConfig, SidechainConfig, SudoConfig, SystemConfig,
AccountId, AuraConfig, BalancesConfig, GrandpaConfig, NativeTokenManagementConfig,
RuntimeGenesisConfig, SessionCommitteeManagementConfig, SessionConfig, SidechainConfig,
SudoConfig, SystemConfig,
};
use sp_core::bytes::from_hex;
use sp_core::{ed25519, sr25519};
Expand Down Expand Up @@ -164,6 +165,18 @@ pub fn staging_genesis(
.collect(),
main_chain_scripts: read_mainchain_scripts_from_env()?,
},
native_token_management: NativeTokenManagementConfig {
main_chain_scripts: sp_native_token_management::MainChainScripts {
native_token_policy: from_var::<PolicyId>("PARTNER_CHAIN_NATIVE_TOKEN_POLICY_ID")?,
native_token_asset_name: from_var::<AssetName>(
"PARTNER_CHAIN_NATIVE_TOKEN_ASSET_NAME",
)?,
illiquid_supply_address: from_var::<MainchainAddress>(
"PARTNER_CHAIN_ILLIQUID_SUPPLY_VALIDATOR_ADDRESS",
)?,
},
..Default::default()
},
};

Ok(serde_json::to_value(config).expect("Genesis config must be serialized correctly"))
Expand Down
16 changes: 14 additions & 2 deletions node/src/template_chain_spec.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::chain_spec::*;
use chain_params::SidechainParams;
use sc_service::ChainType;
use sidechain_domain::{MainchainAddressHash, UtxoId};
use sidechain_domain::{AssetName, MainchainAddress, MainchainAddressHash, PolicyId, UtxoId};
use sidechain_runtime::{
AuraConfig, BalancesConfig, GrandpaConfig, RuntimeGenesisConfig,
AuraConfig, BalancesConfig, GrandpaConfig, NativeTokenManagementConfig, RuntimeGenesisConfig,
SessionCommitteeManagementConfig, SessionConfig, SidechainConfig, SudoConfig, SystemConfig,
};

Expand Down Expand Up @@ -44,6 +44,18 @@ pub fn chain_spec() -> Result<ChainSpec, EnvVarReadError> {
initial_authorities: vec![],
main_chain_scripts: read_mainchain_scripts_from_env()?,
},
native_token_management: NativeTokenManagementConfig {
main_chain_scripts: sp_native_token_management::MainChainScripts {
native_token_policy: from_var::<PolicyId>("PARTNER_CHAIN_NATIVE_TOKEN_POLICY_ID")?,
native_token_asset_name: from_var::<AssetName>(
"PARTNER_CHAIN_NATIVE_TOKEN_ASSET_NAME",
)?,
illiquid_supply_address: from_var::<MainchainAddress>(
"PARTNER_CHAIN_ILLIQUID_SUPPLY_VALIDATOR_ADDRESS",
)?,
},
..Default::default()
},
};
let genesis_json = serde_json::to_value(runtime_genesis_config)
.expect("Genesis config must be serialized correctly");
Expand Down
19 changes: 16 additions & 3 deletions node/src/testnet.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use crate::chain_spec::*;
use chain_params::SidechainParams;
use sc_service::ChainType;
use sidechain_domain::{MainchainAddressHash, UtxoId};
use sidechain_domain::*;
use sidechain_runtime::{
AccountId, AuraConfig, BalancesConfig, GrandpaConfig, RuntimeGenesisConfig,
SessionCommitteeManagementConfig, SessionConfig, SidechainConfig, SudoConfig, SystemConfig,
AccountId, AuraConfig, BalancesConfig, GrandpaConfig, NativeTokenManagementConfig,
RuntimeGenesisConfig, SessionCommitteeManagementConfig, SessionConfig, SidechainConfig,
SudoConfig, SystemConfig,
};
use sidechain_slots::SlotsPerEpoch;
use sp_core::bytes::from_hex;
Expand Down Expand Up @@ -204,6 +205,18 @@ pub fn testnet_genesis(
.collect(),
main_chain_scripts: read_mainchain_scripts_from_env()?,
},
native_token_management: NativeTokenManagementConfig {
main_chain_scripts: sp_native_token_management::MainChainScripts {
native_token_policy: from_var::<PolicyId>("PARTNER_CHAIN_NATIVE_TOKEN_POLICY_ID")?,
native_token_asset_name: from_var::<AssetName>(
"PARTNER_CHAIN_NATIVE_TOKEN_ASSET_NAME",
)?,
illiquid_supply_address: from_var::<MainchainAddress>(
"PARTNER_CHAIN_ILLIQUID_SUPPLY_VALIDATOR_ADDRESS",
)?,
},
..Default::default()
},
};

Ok(serde_json::to_value(config).expect("Genesis config must be serialized correctly"))
Expand Down
29 changes: 29 additions & 0 deletions pallets/native-token-management/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[package]
name = "pallet-native-token-management"
version = "0.1.0"
edition = "2021"
description = "Pallet responsible for handling changes to the illiquid supply of the native token on main chain."

[dependencies]
frame-support = { workspace = true }
frame-system = { workspace = true }
log = { workspace = true }
parity-scale-codec = { workspace = true }
scale-info = { workspace = true }
sp-native-token-management = { workspace = true, features = ["serde"] }
sidechain-domain = { workspace = true }

[dev-dependencies]
sp-io = { workspace = true }

[features]
default = ["std"]
std = [
"frame-support/std",
"frame-system/std",
"log/std",
"scale-info/std",
"parity-scale-codec/std",
"scale-info/std",
"sp-native-token-management/std"
]
135 changes: 135 additions & 0 deletions pallets/native-token-management/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#![cfg_attr(not(feature = "std"), no_std)]

use frame_support::inherent::IsFatalError;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
pub use pallet::*;
use sidechain_domain::NativeTokenAmount;
use sp_native_token_management::*;

#[cfg(test)]
mod tests;

#[cfg(any(test, feature = "mock"))]
mod mock;

#[derive(Encode, Debug, PartialEq)]
pub enum InherentError {
TokenTransferNotHandled,
IncorrectTokenNumberTransfered,
UnexpectedTokenTransferInherent,
}

impl IsFatalError for InherentError {
fn is_fatal_error(&self) -> bool {
true
}
}

/// Interface for user-provided logic to handle native token transfers into the illiquid supply on the main chain.
///
/// The handler will be called with **the total sum** of transfers since the previous partner chain block.
pub trait TokenTransferHandler {
fn handle_token_transfer(token_amount: NativeTokenAmount) -> DispatchResult;
}

#[frame_support::pallet]
pub mod pallet {
use super::*;

#[pallet::pallet]
pub struct Pallet<T>(_);

#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;

type TokenTransferHandler: TokenTransferHandler;
}

#[pallet::event]
pub enum Event<T: Config> {
TokensTransfered(NativeTokenAmount),
}

#[pallet::storage]
pub type MainChainScripts<T: Config> =
StorageValue<_, sp_native_token_management::MainChainScripts, ValueQuery>;

#[pallet::genesis_config]
#[derive(frame_support::DefaultNoBound)]
pub struct GenesisConfig<T: Config> {
pub main_chain_scripts: sp_native_token_management::MainChainScripts,
pub _marker: PhantomData<T>,
}

#[pallet::genesis_build]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
fn build(&self) {
MainChainScripts::<T>::put(self.main_chain_scripts.clone());
}
}

#[pallet::inherent]
impl<T: Config> ProvideInherent for Pallet<T> {
type Call = Call<T>;
type Error = InherentError;
const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;

fn create_inherent(data: &InherentData) -> Option<Self::Call> {
Self::get_transfered_tokens_from_inherent_data(data)
.map(|data| Call::transfer_tokens { token_amount: data.token_amount })
}

fn check_inherent(call: &Self::Call, data: &InherentData) -> Result<(), Self::Error> {
let Some(token_transfer) = Self::get_transfered_tokens_from_inherent_data(data) else {
return Err(InherentError::UnexpectedTokenTransferInherent);
};
let Call::transfer_tokens { token_amount } = call else {
unreachable!("There is no other extrinsic in this pallet");
};
if token_transfer.token_amount != *token_amount {
return Err(InherentError::IncorrectTokenNumberTransfered);
}

Ok(())
}

fn is_inherent(call: &Self::Call) -> bool {
matches!(call, Call::transfer_tokens { .. })
}

fn is_inherent_required(data: &InherentData) -> Result<Option<Self::Error>, Self::Error> {
Ok(Self::get_transfered_tokens_from_inherent_data(data)
.map(|_| InherentError::TokenTransferNotHandled))
}
}

impl<T: Config> Pallet<T> {
fn get_transfered_tokens_from_inherent_data(
data: &InherentData,
) -> Option<TokenTransferData> {
data.get_data::<TokenTransferData>(&INHERENT_IDENTIFIER)
.expect("Token transfer data not correctly encoded")
}
}

#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight((0, DispatchClass::Mandatory))]
pub fn transfer_tokens(
origin: OriginFor<T>,
token_amount: NativeTokenAmount,
) -> DispatchResult {
ensure_none(origin)?;
T::TokenTransferHandler::handle_token_transfer(token_amount)
}
}

impl<T: Config> Pallet<T> {
pub fn get_main_chain_scripts() -> sp_native_token_management::MainChainScripts {
MainChainScripts::<T>::get()
}
}
}
Loading

0 comments on commit 75c0ceb

Please sign in to comment.