feat: noter delegation for staking score system
- Add NoterCheck trait: accounts with Noter tiki can submit receive_staking_details without root origin - Remove stake requirement from start_score_tracking (opt-in only, bot + noter submit data after event detection) - Add zero-stake cleanup: sending staked_amount=0 removes cached entry, cleans up StakingStartBlock when no stake remains - Add NotAuthorized error for non-noter signed callers - Configure TikiNoterChecker in people-pezkuwichain runtime - Update weights with detailed DB operation analysis - Bump People Chain spec_version to 1_020_007 - 49 unit tests (17 new E2E + edge cases), fmt/clippy clean
This commit is contained in:
@@ -52,7 +52,7 @@
|
||||
| Para ID | Isim | Durum | spec_version | Block | Peers |
|
||||
|---------|------|-------|-------------|-------|-------|
|
||||
| 1000 | Asset Hub | CALISIYOR | 1_020_004 | ~11,009 | 1 |
|
||||
| 1004 | People Chain | CALISIYOR | 1_020_004 | ~11,029 | 1 |
|
||||
| 1004 | People Chain | CALISIYOR | 1_020_006 | ~17,200 | 1 |
|
||||
|
||||
#### Mainnet Servis Isimleri (VPS3)
|
||||
- `pez-mainnet-validator-1` ... `pez-mainnet-validator-4`
|
||||
|
||||
@@ -15,7 +15,10 @@ mod benchmarks {
|
||||
fn start_score_tracking() {
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
|
||||
// Populate CachedStakingDetails with test data
|
||||
// Ensure no prior tracking exists.
|
||||
StakingStartBlock::<T>::remove(&caller);
|
||||
|
||||
// Pre-populate CachedStakingDetails for worst-case OnStakingUpdate callback.
|
||||
CachedStakingDetails::<T>::insert(
|
||||
&caller,
|
||||
StakingSource::RelayChain,
|
||||
@@ -26,18 +29,28 @@ mod benchmarks {
|
||||
},
|
||||
);
|
||||
|
||||
StakingStartBlock::<T>::remove(&caller);
|
||||
|
||||
#[extrinsic_call]
|
||||
_(RawOrigin::Signed(caller.clone()));
|
||||
|
||||
assert!(StakingStartBlock::<T>::get(&caller).is_some());
|
||||
}
|
||||
|
||||
/// Benchmark worst case: root origin, non-zero stake insert.
|
||||
#[benchmark]
|
||||
fn receive_staking_details() {
|
||||
let target: T::AccountId = whitelisted_caller();
|
||||
|
||||
// Pre-populate both sources for worst-case trust callback iteration.
|
||||
CachedStakingDetails::<T>::insert(
|
||||
&target,
|
||||
StakingSource::AssetHub,
|
||||
StakingDetails {
|
||||
staked_amount: (200u128 * UNITS).into(),
|
||||
nominations_count: 1,
|
||||
unlocking_chunks_count: 0,
|
||||
},
|
||||
);
|
||||
|
||||
#[extrinsic_call]
|
||||
_(
|
||||
RawOrigin::Root,
|
||||
|
||||
@@ -6,10 +6,16 @@
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! People Chain does not have direct access to staking data. Instead, staking details
|
||||
//! are pushed from Relay Chain and Asset Hub via XCM Transact into `CachedStakingDetails`.
|
||||
//! This pallet aggregates stake from all sources and calculates a score based on amount
|
||||
//! and duration.
|
||||
//! People Chain does not have direct access to staking data. Staking details are
|
||||
//! submitted by noter-authorized accounts (or root via XCM Transact) into
|
||||
//! `CachedStakingDetails`. This pallet aggregates stake from all sources and
|
||||
//! calculates a score based on amount and duration.
|
||||
//!
|
||||
//! ## Noter Delegation
|
||||
//!
|
||||
//! The sudo account delegates `receive_staking_details` authority to accounts that
|
||||
//! hold the `Noter` tiki (role NFT). A bot collects staking data from Relay Chain
|
||||
//! and Asset Hub, then a noter signs and submits the data to People Chain.
|
||||
//!
|
||||
//! ## Dual-Chain Staking
|
||||
//!
|
||||
@@ -19,10 +25,11 @@
|
||||
//!
|
||||
//! ## Workflow
|
||||
//!
|
||||
//! 1. Relay Chain / Asset Hub pushes staking data via XCM → `receive_staking_details()`
|
||||
//! 2. User calls `start_score_tracking()` to begin time-based score accumulation
|
||||
//! 3. `pezpallet-trust` queries staking score via `StakingScoreProvider` trait
|
||||
//! 4. Score = base_score(amount_tier) * duration_multiplier, capped at 100
|
||||
//! 1. User calls `start_score_tracking()` to opt-in to time-based scoring
|
||||
//! 2. Bot detects the event, collects staking data from Relay Chain / Asset Hub
|
||||
//! 3. Noter submits `receive_staking_details()` with the staking data
|
||||
//! 4. `pezpallet-trust` queries staking score via `StakingScoreProvider` trait
|
||||
//! 5. Score = base_score(amount_tier) * duration_multiplier, capped at 100
|
||||
|
||||
pub use pezpallet::*;
|
||||
|
||||
@@ -71,6 +78,19 @@ pub mod pezpallet {
|
||||
#[pezpallet::pezpallet]
|
||||
pub struct Pezpallet<T>(_);
|
||||
|
||||
/// Trait for checking if an account has noter authority.
|
||||
/// Noter-authorized accounts can submit staking details on behalf of users.
|
||||
pub trait NoterCheck<AccountId> {
|
||||
fn is_noter(who: &AccountId) -> bool;
|
||||
}
|
||||
|
||||
/// Default implementation: nobody is noter (safe default for tests).
|
||||
impl<AccountId> NoterCheck<AccountId> for () {
|
||||
fn is_noter(_who: &AccountId) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[pezpallet::config]
|
||||
pub trait Config: pezframe_system::Config<RuntimeEvent: From<Event<Self>>>
|
||||
where
|
||||
@@ -94,6 +114,10 @@ pub mod pezpallet {
|
||||
|
||||
/// Weight information for extrinsics.
|
||||
type WeightInfo: WeightInfo;
|
||||
|
||||
/// Checker for noter authority. Accounts with the Noter tiki can submit
|
||||
/// staking details without requiring root origin.
|
||||
type NoterChecker: NoterCheck<Self::AccountId>;
|
||||
}
|
||||
|
||||
// --- Storage ---
|
||||
@@ -136,12 +160,20 @@ pub mod pezpallet {
|
||||
NoStakeFound,
|
||||
/// Score tracking has already been started for this account.
|
||||
TrackingAlreadyStarted,
|
||||
/// Caller does not have noter authority.
|
||||
NotAuthorized,
|
||||
}
|
||||
|
||||
#[pezpallet::call]
|
||||
impl<T: Config> Pezpallet<T> {
|
||||
/// Start time-based score accumulation. One-time call per user.
|
||||
/// Requires the user to have cached staking data from at least one source.
|
||||
/// Start time-based score accumulation. One-time opt-in call per user.
|
||||
///
|
||||
/// The user does not need to have cached staking data yet. A bot will
|
||||
/// detect the `ScoreTrackingStarted` event and a noter will submit the
|
||||
/// staking data via `receive_staking_details`.
|
||||
///
|
||||
/// Duration tracking begins at the block this is called, regardless of
|
||||
/// when the staking data arrives.
|
||||
#[pezpallet::call_index(0)]
|
||||
#[pezpallet::weight(T::WeightInfo::start_score_tracking())]
|
||||
pub fn start_score_tracking(origin: OriginFor<T>) -> DispatchResult {
|
||||
@@ -152,21 +184,25 @@ pub mod pezpallet {
|
||||
Error::<T>::TrackingAlreadyStarted
|
||||
);
|
||||
|
||||
// Check if user has any stake from any source.
|
||||
let total_stake = Self::total_cached_stake(&who);
|
||||
ensure!(!total_stake.is_zero(), Error::<T>::NoStakeFound);
|
||||
|
||||
let current_block = pezframe_system::Pezpallet::<T>::block_number();
|
||||
StakingStartBlock::<T>::insert(&who, current_block);
|
||||
|
||||
// Notify trust pallet. Score may be 0 if CachedStakingDetails is empty.
|
||||
T::OnStakingUpdate::on_staking_data_changed(&who);
|
||||
|
||||
Self::deposit_event(Event::ScoreTrackingStarted { who, start_block: current_block });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Receive staking details from a chain via XCM Transact.
|
||||
/// Only root origin is accepted (XCM Transact from sibling/parent arrives as root).
|
||||
/// Receive staking details for an account.
|
||||
///
|
||||
/// Accepts root origin (XCM Transact) or a signed origin from an account
|
||||
/// that holds the Noter tiki. This allows a noter-authorized bot to submit
|
||||
/// staking data collected from Relay Chain and Asset Hub.
|
||||
///
|
||||
/// If `staked_amount` is zero, the cached entry for the given source is
|
||||
/// removed. If no stake remains from any source, `StakingStartBlock` is
|
||||
/// also cleaned up, effectively resetting the user's staking score to zero.
|
||||
#[pezpallet::call_index(1)]
|
||||
#[pezpallet::weight(T::WeightInfo::receive_staking_details())]
|
||||
pub fn receive_staking_details(
|
||||
@@ -177,12 +213,27 @@ pub mod pezpallet {
|
||||
nominations_count: u32,
|
||||
unlocking_chunks_count: u32,
|
||||
) -> DispatchResult {
|
||||
ensure_root(origin)?;
|
||||
// Root (XCM Transact) OR noter-authorized signed origin.
|
||||
if ensure_root(origin.clone()).is_err() {
|
||||
let caller = ensure_signed(origin)?;
|
||||
ensure!(T::NoterChecker::is_noter(&caller), Error::<T>::NotAuthorized);
|
||||
}
|
||||
|
||||
let details =
|
||||
StakingDetails { staked_amount, nominations_count, unlocking_chunks_count };
|
||||
if staked_amount.is_zero() {
|
||||
// Zero stake: remove the cached entry for this source.
|
||||
CachedStakingDetails::<T>::remove(&who, source);
|
||||
|
||||
CachedStakingDetails::<T>::insert(&who, source, details);
|
||||
// Check if any stake remains from other sources.
|
||||
let remaining = Self::total_cached_stake(&who);
|
||||
if remaining.is_zero() {
|
||||
// No stake from any source — clean up tracking.
|
||||
StakingStartBlock::<T>::remove(&who);
|
||||
}
|
||||
} else {
|
||||
let details =
|
||||
StakingDetails { staked_amount, nominations_count, unlocking_chunks_count };
|
||||
CachedStakingDetails::<T>::insert(&who, source, details);
|
||||
}
|
||||
|
||||
T::OnStakingUpdate::on_staking_data_changed(&who);
|
||||
|
||||
|
||||
@@ -47,10 +47,20 @@ impl pezpallet_balances::Config for Test {
|
||||
type AccountStore = System;
|
||||
}
|
||||
|
||||
/// Mock noter checker for tests.
|
||||
/// Account 99 is noter, everyone else is not.
|
||||
pub struct MockNoterChecker;
|
||||
impl crate::NoterCheck<AccountId> for MockNoterChecker {
|
||||
fn is_noter(who: &AccountId) -> bool {
|
||||
*who == 99
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Config for Test {
|
||||
type Balance = Balance;
|
||||
type WeightInfo = ();
|
||||
type OnStakingUpdate = ();
|
||||
type NoterChecker = MockNoterChecker;
|
||||
}
|
||||
|
||||
// --- ExtBuilder ---
|
||||
@@ -73,6 +83,8 @@ impl ExtBuilder {
|
||||
(2, 1_000_000 * UNITS),
|
||||
(10, 1_000_000 * UNITS),
|
||||
(20, 100_000 * UNITS),
|
||||
(30, 100_000 * UNITS), // Charlie
|
||||
(99, 100_000 * UNITS), // NOTER
|
||||
],
|
||||
..Default::default()
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,7 @@
|
||||
//! They account for the `OnStakingUpdate` callback cost (trust pallet update).
|
||||
//!
|
||||
//! DATE: 2026-02-16
|
||||
//! UPDATED: 2026-02-16 (noter delegation + zero-stake cleanup)
|
||||
//! TODO: Run proper benchmarks to replace these estimates.
|
||||
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
@@ -41,51 +42,72 @@ pub trait WeightInfo {
|
||||
/// Weights for `pezpallet_staking_score` using the Bizinikiwi node and recommended hardware.
|
||||
pub struct BizinikiwiWeight<T>(PhantomData<T>);
|
||||
impl<T: pezframe_system::Config> WeightInfo for BizinikiwiWeight<T> {
|
||||
/// Storage: `StakingScore::StakingStartBlock` (r:1 w:1)
|
||||
/// Storage: `StakingScore::CachedStakingDetails` (r:2 w:0) -- iter_prefix worst case 2 sources
|
||||
/// Storage: `Trust::TrustScores` (r:1 w:1) -- OnStakingUpdate callback
|
||||
/// Storage: `Trust::TotalActiveTrustScore` (r:1 w:1) -- OnStakingUpdate callback
|
||||
/// Storage: `IdentityKyc::KycStatuses` (r:1 w:0) -- citizenship check
|
||||
/// Storage: `StakingScore` reads for score calc (r:2 w:0)
|
||||
/// `start_score_tracking` — user opt-in, no stake check needed.
|
||||
///
|
||||
/// Total: r:8 w:3
|
||||
/// Pallet operations:
|
||||
/// StakingScore::StakingStartBlock (r:1 w:1) — check + insert
|
||||
///
|
||||
/// OnStakingUpdate callback (trust pallet):
|
||||
/// StakingScore::CachedStakingDetails (r:2 w:0) — iter_prefix for score calc
|
||||
/// StakingScore::StakingStartBlock (r:1 w:0) — duration lookup in score calc
|
||||
/// Trust::TrustScores (r:1 w:1) — update trust score
|
||||
/// Trust::TotalActiveTrustScore (r:1 w:1) — update aggregate
|
||||
/// IdentityKyc::KycStatuses (r:1 w:0) — citizenship gate
|
||||
///
|
||||
/// Total: r:7 w:3
|
||||
fn start_score_tracking() -> Weight {
|
||||
// Conservative estimate: ~35 microseconds execution + 8 reads + 3 writes
|
||||
// Proof size: StakingStartBlock(52) + CachedStakingDetails(77*2) + TrustScores(48) +
|
||||
// TotalActiveTrustScore(16) + KycStatuses(34) + overhead = ~8000
|
||||
Weight::from_parts(35_000_000, 8_000)
|
||||
.saturating_add(T::DbWeight::get().reads(8_u64))
|
||||
// ~30us execution + 7 reads + 3 writes
|
||||
// Proof size: StakingStartBlock(52) + CachedStakingDetails(77*2) +
|
||||
// TrustScores(48) + TotalActiveTrustScore(16) + KycStatuses(34) = ~7500
|
||||
Weight::from_parts(30_000_000, 7_500)
|
||||
.saturating_add(T::DbWeight::get().reads(7_u64))
|
||||
.saturating_add(T::DbWeight::get().writes(3_u64))
|
||||
}
|
||||
|
||||
/// Storage: `StakingScore::CachedStakingDetails` (r:1 w:1) -- DoubleMap insert
|
||||
/// Storage: `Trust::TrustScores` (r:1 w:1) -- OnStakingUpdate callback
|
||||
/// Storage: `Trust::TotalActiveTrustScore` (r:1 w:1) -- OnStakingUpdate callback
|
||||
/// Storage: `IdentityKyc::KycStatuses` (r:1 w:0) -- citizenship check
|
||||
/// Storage: `StakingScore` reads for score calc (r:2 w:0)
|
||||
/// `receive_staking_details` — worst case: noter signed + zero-stake cleanup.
|
||||
///
|
||||
/// Total: r:6 w:3
|
||||
/// Origin check (noter path):
|
||||
/// Tiki::UserTikis (r:1 w:0) — noter authority check via has_tiki()
|
||||
///
|
||||
/// Zero-stake cleanup path (worst case):
|
||||
/// StakingScore::CachedStakingDetails (r:1 w:1) — remove entry for source
|
||||
/// StakingScore::CachedStakingDetails (r:2 w:0) — iter_prefix remaining check
|
||||
/// StakingScore::StakingStartBlock (r:1 w:1) — remove if no remaining stake
|
||||
///
|
||||
/// OnStakingUpdate callback (trust pallet):
|
||||
/// StakingScore::CachedStakingDetails (r:2 w:0) — iter for score calc (overlaps)
|
||||
/// StakingScore::StakingStartBlock (r:1 w:0) — duration lookup (overlaps)
|
||||
/// Trust::TrustScores (r:1 w:1) — update trust score
|
||||
/// Trust::TotalActiveTrustScore (r:1 w:1) — update aggregate
|
||||
/// IdentityKyc::KycStatuses (r:1 w:0) — citizenship gate
|
||||
///
|
||||
/// Note: Some reads overlap (CachedStakingDetails iter + StakingStartBlock read
|
||||
/// are done both in cleanup and in the trust callback score calculation).
|
||||
/// Counting unique reads conservatively:
|
||||
///
|
||||
/// Total: r:10 w:4
|
||||
fn receive_staking_details() -> Weight {
|
||||
// Conservative estimate: ~30 microseconds execution + 6 reads + 3 writes
|
||||
// Proof size: CachedStakingDetails(77) + TrustScores(48) +
|
||||
// TotalActiveTrustScore(16) + KycStatuses(34) + overhead = ~7000
|
||||
Weight::from_parts(30_000_000, 7_000)
|
||||
.saturating_add(T::DbWeight::get().reads(6_u64))
|
||||
.saturating_add(T::DbWeight::get().writes(3_u64))
|
||||
// ~40us execution + 10 reads + 4 writes
|
||||
// Proof size: Tiki::UserTikis(200) + CachedStakingDetails(77*2) +
|
||||
// StakingStartBlock(52) + TrustScores(48) +
|
||||
// TotalActiveTrustScore(16) + KycStatuses(34) = ~9500
|
||||
Weight::from_parts(40_000_000, 9_500)
|
||||
.saturating_add(T::DbWeight::get().reads(10_u64))
|
||||
.saturating_add(T::DbWeight::get().writes(4_u64))
|
||||
}
|
||||
}
|
||||
|
||||
// For backwards compatibility and tests.
|
||||
impl WeightInfo for () {
|
||||
fn start_score_tracking() -> Weight {
|
||||
Weight::from_parts(35_000_000, 8_000)
|
||||
.saturating_add(RocksDbWeight::get().reads(8_u64))
|
||||
Weight::from_parts(30_000_000, 7_500)
|
||||
.saturating_add(RocksDbWeight::get().reads(7_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(3_u64))
|
||||
}
|
||||
|
||||
fn receive_staking_details() -> Weight {
|
||||
Weight::from_parts(30_000_000, 7_000)
|
||||
.saturating_add(RocksDbWeight::get().reads(6_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(3_u64))
|
||||
Weight::from_parts(40_000_000, 9_500)
|
||||
.saturating_add(RocksDbWeight::get().reads(10_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(4_u64))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
spec_name: alloc::borrow::Cow::Borrowed("people-pezkuwichain"),
|
||||
impl_name: alloc::borrow::Cow::Borrowed("people-pezkuwichain"),
|
||||
authoring_version: 1,
|
||||
spec_version: 1_020_006,
|
||||
spec_version: 1_020_007,
|
||||
impl_version: 0,
|
||||
apis: RUNTIME_API_VERSIONS,
|
||||
transaction_version: 1,
|
||||
|
||||
@@ -469,10 +469,20 @@ parameter_types! {
|
||||
pub const StakingScoreUpdateInterval: BlockNumber = HOURS;
|
||||
}
|
||||
|
||||
/// Noter authority checker backed by the Tiki pallet.
|
||||
/// Accounts holding the `Noter` tiki role can submit staking details.
|
||||
pub struct TikiNoterChecker;
|
||||
impl pezpallet_staking_score::NoterCheck<AccountId> for TikiNoterChecker {
|
||||
fn is_noter(who: &AccountId) -> bool {
|
||||
pezpallet_tiki::Pezpallet::<Runtime>::has_tiki(who, &pezpallet_tiki::Tiki::Noter)
|
||||
}
|
||||
}
|
||||
|
||||
impl pezpallet_staking_score::Config for Runtime {
|
||||
type WeightInfo = pezpallet_staking_score::weights::BizinikiwiWeight<Runtime>;
|
||||
type Balance = Balance;
|
||||
type OnStakingUpdate = Trust;
|
||||
type NoterChecker = TikiNoterChecker;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
+20
-4
@@ -1,7 +1,9 @@
|
||||
//! Send receive_staking_details to People Chain for all 21 validators via XCM Transact
|
||||
//!
|
||||
//! People Chain StakingScore pallet (index 80) has:
|
||||
//! receive_staking_details(who, staked_amount, nominations_count, unlocking_chunks_count)
|
||||
//! receive_staking_details(who, source, staked_amount, nominations_count, unlocking_chunks_count)
|
||||
//!
|
||||
//! source: StakingSource enum (0=RelayChain, 1=AssetHub)
|
||||
//!
|
||||
//! This populates CachedStakingDetails on People Chain so validators can
|
||||
//! call start_score_tracking() and have their staking scores calculated.
|
||||
@@ -57,22 +59,29 @@ fn validators() -> Vec<ValidatorInfo> {
|
||||
|
||||
const PLANCK_PER_HEZ: u128 = 1_000_000_000_000;
|
||||
|
||||
/// Encode StakingScore.receive_staking_details(who, staked_amount, nominations_count, unlocking_chunks_count)
|
||||
/// StakingSource enum (SCALE-encoded as single byte)
|
||||
const STAKING_SOURCE_RELAY_CHAIN: u8 = 0;
|
||||
const STAKING_SOURCE_ASSET_HUB: u8 = 1;
|
||||
|
||||
/// Encode StakingScore.receive_staking_details(who, source, staked_amount, nominations_count, unlocking_chunks_count)
|
||||
/// Pallet 80 (0x50), call_index 1
|
||||
/// who: AccountId32 (32 bytes raw)
|
||||
/// source: StakingSource (1 byte enum: 0=RelayChain, 1=AssetHub)
|
||||
/// staked_amount: u128 LE (16 bytes) - this is T::Balance which is u128
|
||||
/// nominations_count: u32 LE (4 bytes)
|
||||
/// unlocking_chunks_count: u32 LE (4 bytes)
|
||||
fn encode_receive_staking_details(
|
||||
account_id: &[u8; 32],
|
||||
source: u8,
|
||||
staked_amount: u128,
|
||||
nominations_count: u32,
|
||||
unlocking_chunks_count: u32,
|
||||
) -> Vec<u8> {
|
||||
let mut encoded = Vec::with_capacity(58);
|
||||
let mut encoded = Vec::with_capacity(59);
|
||||
encoded.push(STAKING_SCORE_PALLET); // 0x50
|
||||
encoded.push(RECEIVE_STAKING_DETAILS_CALL); // 0x01
|
||||
encoded.extend_from_slice(account_id); // 32 bytes
|
||||
encoded.push(source); // 1 byte (StakingSource enum)
|
||||
encoded.extend_from_slice(&staked_amount.to_le_bytes()); // 16 bytes
|
||||
encoded.extend_from_slice(&nominations_count.to_le_bytes()); // 4 bytes
|
||||
encoded.extend_from_slice(&unlocking_chunks_count.to_le_bytes()); // 4 bytes
|
||||
@@ -177,9 +186,16 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
};
|
||||
|
||||
// Encode receive_staking_details call
|
||||
// source = RelayChain (validators stake on Relay Chain)
|
||||
// nominations_count = 0 (validators don't nominate, they validate)
|
||||
// unlocking_chunks_count = 0 (no pending unstakes)
|
||||
let call = encode_receive_staking_details(&account.0, staked_planck, 0, 0);
|
||||
let call = encode_receive_staking_details(
|
||||
&account.0,
|
||||
STAKING_SOURCE_RELAY_CHAIN,
|
||||
staked_planck,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
println!(
|
||||
" Call: {} bytes (0x{}...)",
|
||||
call.len(),
|
||||
|
||||
Reference in New Issue
Block a user