feat(emission): FIX-6 Stage 1 — emision que paga validadores (V2-CA2) + remover APR falso
Build + tests verde: emission pallet 5 + runtime 28 (incl. emission_pallet_index_13)
+ staking contract 4. Verificacion adversarial ultracode (2 skeptics): correctitud
+ seguridad CONFIRMADAS (split exacto, sin overflow, autor solo-desde-Aura sin
input de usuario, mint-None-seguro, sin dispatchable explotable, APR removal
honesto). 'partial' solo por cobertura de tests en ramas triviales (None /
reward==0), NO por vulnerabilidad.
Cierra V2-CA2 (la cadena no tenia emision -> los validadores ganaban 0 OGC):
NUEVO pallet origin-chain-emission (Index 13):
- on_initialize cada bloque: identifica al autor Aura (FindAuthor =
pallet_session::FindAccountFromAuthorIndex<Runtime, Aura>), mintea un
BlockReward (10 OGC, ~5.26% inflacion anual) y lo reparte:
- 70% al autor (el validador que produjo el bloque).
- 20% al treasury (og/gvtry — la MISMA cuenta-vault de governance FIX-6).
- 10% al subsidy pool (og/subsy — proof-of-equity que financia el subsidio
de hardware anti-clasista #16).
- El residual del split va al subsidy (no pierde unidades por redondeo de bps).
- TotalEmitted trackea la inflacion acumulada on-chain. mint_into (fungible::
Mutate) = emision real (aumenta total issuance).
- El autor viene SOLO de FindAuthor/Aura (no de input de usuario) -> nadie puede
mintear a una cuenta arbitraria. Si FindAuthor devuelve None, no mintea. Sin
overflow (saturating_mul/div, 10^19 << u128::MAX).
sub_mult ORTOGONAL (David 2026-06-10): el subsidy pool financia el subsidio de
hardware SIN importar la suscripcion premium. Esta emision lo alimenta; el
OnChargeTransaction de Stage 2 lo consumira.
Remover APR falso (V1/V2 fake APR): staking contract default_chn3 reward rates
8/5/3% -> 0. Era enganoso (no habia motor que los pagara; los campos
reward_rate_bps se almacenaban pero NUNCA se leian, rewards_accumulated jamas se
acreditaba). Los validadores ahora cobran via la emision real; las tasas de
staking reales las define Stage 2 (NPoS por stake).
FIX-6 Stage 1 COMPLETO (con v0.0.56 treasury real): emision + treasury real +
APR removido. Stage 2: NPoS (pallet_staking + EraPayout) + fee unificado
(OnChargeTransaction con sub_mult ortogonal).
FOLLOW-UPS (low, documentados): anadir tests de cobertura para las ramas
FindAuthor==None (no mintea) y reward==0 (requieren un 2o mock runtime);
BlockReward fijo (decay schedule = Stage 2); el subsidy pool acumula pero no se
consume hasta el OnChargeTransaction de Stage 2; genesis mainnet del treasury
vault (pre-launch).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
1a9e37021d
commit
e6a803b54f
8 files changed
+413
-3
No files matched your search
Generated
+15
@@ -6379,6 +6379,20 @@ dependencies = [
|
||||
"origin-chain-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "origin-chain-emission"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"frame-support",
|
||||
"frame-system",
|
||||
"pallet-balances",
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"sp-core",
|
||||
"sp-io 43.0.0",
|
||||
"sp-runtime 44.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "origin-chain-fees"
|
||||
version = "0.0.1"
|
||||
@@ -6477,6 +6491,7 @@ dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
"log",
|
||||
"origin-chain-agent-identity",
|
||||
"origin-chain-emission",
|
||||
"origin-chain-fees",
|
||||
"origin-chain-governance",
|
||||
"origin-chain-provenance",
|
||||
|
||||
@@ -9,6 +9,7 @@ members = [
|
||||
"crates/origin-chain-agent-identity",
|
||||
"crates/origin-chain-bridges",
|
||||
"crates/origin-chain-governance",
|
||||
"crates/origin-chain-emission",
|
||||
"crates/origin-chain-runtime",
|
||||
"crates/origin-chain-node",
|
||||
]
|
||||
@@ -201,6 +202,7 @@ origin-chain-fees = { path = "crates/origin-chain-fees", version = "0.0.1", defa
|
||||
origin-chain-agent-identity = { path = "crates/origin-chain-agent-identity", version = "0.0.1", default-features = false }
|
||||
origin-chain-bridges = { path = "crates/origin-chain-bridges", version = "0.0.1", default-features = false }
|
||||
origin-chain-governance = { path = "crates/origin-chain-governance", version = "0.0.1", default-features = false }
|
||||
origin-chain-emission = { path = "crates/origin-chain-emission", version = "0.0.1", default-features = false }
|
||||
origin-chain-runtime = { path = "crates/origin-chain-runtime", version = "0.0.1", default-features = false }
|
||||
origin-chain-node = { path = "crates/origin-chain-node", version = "0.0.1" }
|
||||
|
||||
|
||||
@@ -221,9 +221,14 @@ mod origin_staking {
|
||||
403_200, // 28 days lockup validator
|
||||
100_800, // 7 days lockup beacon
|
||||
201_600, // 14 days lockup reputation
|
||||
800, // 8% APR validator
|
||||
500, // 5% APR beacon
|
||||
300, // 3% APR reputation
|
||||
// FIX-6 (V1/V2 fake APR): reward rates en 0 — NO hay rewards
|
||||
// falsos en este contrato. Los validadores cobran vía el
|
||||
// pallet-origin-emission (block rewards REALES en el runtime); las
|
||||
// tasas reales de staking las define Stage 2 (NPoS por stake).
|
||||
// Mostrar 8/5/3% sin un motor que los pagara era engañoso.
|
||||
0, // validator reward rate (era 8% APR ficticio)
|
||||
0, // beacon reward rate (era 5% APR ficticio)
|
||||
0, // reputation reward rate (era 3% APR ficticio)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
[package]
|
||||
name = "origin-chain-emission"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
description = "Origin Chain emission pallet — FIX-6 Stage 1 (audit V2-CA2). Mintea un block reward por bloque y lo reparte autor (validador Aura) + treasury + subsidy pool (proof-of-equity, Inmutable #16 anti-clasista). Cierra 'los validadores ganan 0 OGC'."
|
||||
|
||||
[dependencies]
|
||||
# FRAME canonical
|
||||
frame-support = { workspace = true }
|
||||
frame-system = { workspace = true }
|
||||
|
||||
# Substrate primitives canonical (AccountIdConversion para PalletId)
|
||||
sp-runtime = { workspace = true }
|
||||
|
||||
# SCALE codec canonical
|
||||
parity-scale-codec = { workspace = true }
|
||||
scale-info = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
# Mock runtime canonical — pallet_balances para testear el minteo real.
|
||||
pallet-balances = { workspace = true, default-features = true }
|
||||
sp-core = { workspace = true, default-features = true }
|
||||
sp-io = { workspace = true, default-features = true }
|
||||
sp-runtime = { workspace = true, default-features = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"frame-support/std",
|
||||
"frame-system/std",
|
||||
"sp-runtime/std",
|
||||
"parity-scale-codec/std",
|
||||
"scale-info/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"frame-support/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
]
|
||||
try-runtime = [
|
||||
"frame-support/try-runtime",
|
||||
"frame-system/try-runtime",
|
||||
]
|
||||
@@ -0,0 +1,282 @@
|
||||
//! # pallet-origin-emission — FIX-6 Stage 1: emisión que paga a los validadores
|
||||
//!
|
||||
//! Cierra el crítico V2-CA2 del audit: la cadena NO tenía emisión (Aura PoA
|
||||
//! mintea 0), así que los validadores ganaban **0 OGC**. Ahora, en cada bloque,
|
||||
//! se mintea un `BlockReward` y se reparte:
|
||||
//!
|
||||
//! - **Autor** (el validador Aura que produjo el bloque) — el grueso.
|
||||
//! - **Treasury** (la cuenta-tesorería de governance, og/gvtry) — financia DAO.
|
||||
//! - **Subsidy pool** (proof-of-equity, og/subsy) — financia el subsidio de
|
||||
//! hardware anti-clasista (Inmutable #16).
|
||||
//!
|
||||
//! La inflación resultante (~5% anual con el reward calibrado) materializa la
|
||||
//! emisión del BM V2 (40% Ecosystem distribuido en 10-15 años).
|
||||
//!
|
||||
//! ## sub_mult ORTOGONAL (ratificado David 2026-06-10)
|
||||
//!
|
||||
//! El subsidy pool financia el subsidio de hardware **sin importar la
|
||||
//! suscripción premium** (Pro/Ultra) — son dimensiones ortogonales (V2-CA3). Esta
|
||||
//! emisión alimenta el pool; el OnChargeTransaction de Stage 2 lo consumirá.
|
||||
//!
|
||||
//! ## NPoS diferido a Stage 2
|
||||
//!
|
||||
//! Esto es la emisión simple Phase 1 PoA (paga al autor Aura directo). Stage 2
|
||||
//! introduce pallet_staking + EraPayout (nominators + slashing + selección por
|
||||
//! stake). El reward por bloque aquí es el substrate sobre el que Stage 2 hereda.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
pub use pallet::*;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_support::traits::fungible::Mutate;
|
||||
use frame_support::traits::FindAuthor;
|
||||
use frame_support::PalletId;
|
||||
use frame_system::pallet_prelude::*;
|
||||
use sp_runtime::traits::AccountIdConversion;
|
||||
|
||||
/// StorageVersion en código (FIX-2 lifecycle): arranca en 0 (genesis).
|
||||
const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
|
||||
|
||||
/// Configuración del pallet de emisión.
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {
|
||||
/// Runtime event type.
|
||||
type RuntimeEvent: From<Event<Self>>
|
||||
+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
||||
|
||||
/// Currency que se mintea (la emisión aumenta el total issuance).
|
||||
type Currency: Mutate<Self::AccountId, Balance = u128>;
|
||||
|
||||
/// Cómo encontrar el autor del bloque. En el runtime se cablea con la
|
||||
/// FindAuthor de Aura (pallet_session::FindAccountFromAuthorIndex).
|
||||
type FindAuthor: FindAuthor<Self::AccountId>;
|
||||
|
||||
/// Recompensa por bloque en base units OGC (calibra la inflación ~5%/año).
|
||||
#[pallet::constant]
|
||||
type BlockReward: Get<u128>;
|
||||
|
||||
/// Share del autor en basis points (10_000 = 100%). E.g. 7_000 = 70%.
|
||||
#[pallet::constant]
|
||||
type AuthorShareBps: Get<u16>;
|
||||
|
||||
/// Share del treasury en basis points. E.g. 2_000 = 20%. El residual va al
|
||||
/// subsidy pool.
|
||||
#[pallet::constant]
|
||||
type TreasuryShareBps: Get<u16>;
|
||||
|
||||
/// PalletId de la cuenta-tesorería (la MISMA de governance, og/gvtry).
|
||||
#[pallet::constant]
|
||||
type TreasuryPalletId: Get<PalletId>;
|
||||
|
||||
/// PalletId del subsidy pool (proof-of-equity, Inmutable #16 anti-clasista).
|
||||
#[pallet::constant]
|
||||
type SubsidyPalletId: Get<PalletId>;
|
||||
}
|
||||
|
||||
/// Pallet definition.
|
||||
#[pallet::pallet]
|
||||
#[pallet::storage_version(STORAGE_VERSION)]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
/// Total OGC emitido acumulado (tracking de inflación on-chain).
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn total_emitted)]
|
||||
pub type TotalEmitted<T> = StorageValue<_, u128, ValueQuery>;
|
||||
|
||||
/// Eventos del pallet.
|
||||
#[pallet::event]
|
||||
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
/// Block reward minteado y repartido autor/treasury/subsidy.
|
||||
BlockRewardMinted {
|
||||
/// El validador Aura autor del bloque.
|
||||
author: T::AccountId,
|
||||
/// Monto minteado al autor.
|
||||
author_amount: u128,
|
||||
/// Monto minteado al treasury.
|
||||
treasury_amount: u128,
|
||||
/// Monto minteado al subsidy pool.
|
||||
subsidy_amount: u128,
|
||||
},
|
||||
}
|
||||
|
||||
#[pallet::hooks]
|
||||
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
|
||||
/// Cada bloque: identifica al autor Aura (desde los pre-runtime digests),
|
||||
/// mintea el BlockReward y lo reparte. Si no hay autor (e.g. bloque de
|
||||
/// inherentes sin pre-digest), no mintea.
|
||||
fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
|
||||
let digest = <frame_system::Pallet<T>>::digest();
|
||||
let pre_runtime = digest.logs.iter().filter_map(|d| d.as_pre_runtime());
|
||||
if let Some(author) = T::FindAuthor::find_author(pre_runtime) {
|
||||
let reward = T::BlockReward::get();
|
||||
if reward > 0 {
|
||||
let author_amount =
|
||||
reward.saturating_mul(T::AuthorShareBps::get() as u128) / 10_000;
|
||||
let treasury_amount =
|
||||
reward.saturating_mul(T::TreasuryShareBps::get() as u128) / 10_000;
|
||||
// El residual evita pérdida por redondeo de bps.
|
||||
let subsidy_amount = reward
|
||||
.saturating_sub(author_amount)
|
||||
.saturating_sub(treasury_amount);
|
||||
|
||||
// mint_into aumenta el total issuance (emisión real).
|
||||
let _ = T::Currency::mint_into(&author, author_amount);
|
||||
let _ = T::Currency::mint_into(&Self::treasury_account(), treasury_amount);
|
||||
let _ = T::Currency::mint_into(&Self::subsidy_account(), subsidy_amount);
|
||||
|
||||
TotalEmitted::<T>::mutate(|t| *t = t.saturating_add(reward));
|
||||
Self::deposit_event(Event::BlockRewardMinted {
|
||||
author,
|
||||
author_amount,
|
||||
treasury_amount,
|
||||
subsidy_amount,
|
||||
});
|
||||
}
|
||||
}
|
||||
Weight::from_parts(30_000, 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// Cuenta-tesorería (vault) — la MISMA que governance (og/gvtry).
|
||||
pub fn treasury_account() -> T::AccountId {
|
||||
T::TreasuryPalletId::get().into_account_truncating()
|
||||
}
|
||||
|
||||
/// Cuenta del subsidy pool (proof-of-equity, financia el subsidio #16).
|
||||
pub fn subsidy_account() -> T::AccountId {
|
||||
T::SubsidyPalletId::get().into_account_truncating()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate as origin_chain_emission;
|
||||
use frame_support::traits::fungible::Inspect;
|
||||
use frame_support::traits::{FindAuthor, Hooks};
|
||||
use frame_support::{construct_runtime, derive_impl, parameter_types, PalletId};
|
||||
use sp_runtime::{AccountId32, BuildStorage};
|
||||
|
||||
type Block = frame_system::mocking::MockBlock<Test>;
|
||||
|
||||
construct_runtime!(
|
||||
pub enum Test {
|
||||
System: frame_system,
|
||||
Balances: pallet_balances,
|
||||
Emission: origin_chain_emission,
|
||||
}
|
||||
);
|
||||
|
||||
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
|
||||
impl frame_system::Config for Test {
|
||||
type Block = Block;
|
||||
type AccountId = AccountId32;
|
||||
type Lookup = sp_runtime::traits::IdentityLookup<Self::AccountId>;
|
||||
type AccountData = pallet_balances::AccountData<u128>;
|
||||
}
|
||||
|
||||
#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
|
||||
impl pallet_balances::Config for Test {
|
||||
type AccountStore = System;
|
||||
type Balance = u128;
|
||||
}
|
||||
|
||||
/// FindAuthor de prueba: devuelve siempre ALICE como autor del bloque.
|
||||
pub struct MockFindAuthor;
|
||||
impl FindAuthor<AccountId32> for MockFindAuthor {
|
||||
fn find_author<'a, I>(_digests: I) -> Option<AccountId32>
|
||||
where
|
||||
I: 'a + IntoIterator<Item = (sp_runtime::ConsensusEngineId, &'a [u8])>,
|
||||
{
|
||||
Some(AccountId32::new([1u8; 32]))
|
||||
}
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const BlockRewardCfg: u128 = 1_000;
|
||||
pub const AuthorShareBpsCfg: u16 = 7_000; // 70%
|
||||
pub const TreasuryShareBpsCfg: u16 = 2_000; // 20% (subsidy = 10% residual)
|
||||
pub const TreasuryPid: PalletId = PalletId(*b"og/gvtry");
|
||||
pub const SubsidyPid: PalletId = PalletId(*b"og/subsy");
|
||||
}
|
||||
|
||||
impl crate::Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type Currency = Balances;
|
||||
type FindAuthor = MockFindAuthor;
|
||||
type BlockReward = BlockRewardCfg;
|
||||
type AuthorShareBps = AuthorShareBpsCfg;
|
||||
type TreasuryShareBps = TreasuryShareBpsCfg;
|
||||
type TreasuryPalletId = TreasuryPid;
|
||||
type SubsidyPalletId = SubsidyPid;
|
||||
}
|
||||
|
||||
fn new_test_ext() -> sp_io::TestExternalities {
|
||||
let storage = frame_system::GenesisConfig::<Test>::default()
|
||||
.build_storage()
|
||||
.expect("genesis builds");
|
||||
let mut ext: sp_io::TestExternalities = storage.into();
|
||||
ext.execute_with(|| System::set_block_number(1));
|
||||
ext
|
||||
}
|
||||
|
||||
fn author() -> AccountId32 {
|
||||
AccountId32::new([1u8; 32])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_reward_minted_and_split_correctly() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let treasury = Emission::treasury_account();
|
||||
let subsidy = Emission::subsidy_account();
|
||||
|
||||
// Pre: autor/treasury/subsidy en 0 (no genesis-funded).
|
||||
assert_eq!(<Balances as Inspect<_>>::balance(&author()), 0);
|
||||
assert_eq!(Emission::total_emitted(), 0);
|
||||
|
||||
// Un bloque: mintea 1000 → autor 700, treasury 200, subsidy 100.
|
||||
<Emission as Hooks<u64>>::on_initialize(1);
|
||||
|
||||
assert_eq!(<Balances as Inspect<_>>::balance(&author()), 700);
|
||||
assert_eq!(<Balances as Inspect<_>>::balance(&treasury), 200);
|
||||
assert_eq!(<Balances as Inspect<_>>::balance(&subsidy), 100);
|
||||
assert_eq!(Emission::total_emitted(), 1000);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn emission_accumulates_over_blocks() {
|
||||
new_test_ext().execute_with(|| {
|
||||
<Emission as Hooks<u64>>::on_initialize(1);
|
||||
<Emission as Hooks<u64>>::on_initialize(2);
|
||||
<Emission as Hooks<u64>>::on_initialize(3);
|
||||
|
||||
// 3 bloques × 1000 = 3000 emitido; autor acumula 3 × 700 = 2100.
|
||||
assert_eq!(Emission::total_emitted(), 3_000);
|
||||
assert_eq!(<Balances as Inspect<_>>::balance(&author()), 2_100);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_no_pierde_por_redondeo() {
|
||||
new_test_ext().execute_with(|| {
|
||||
// author + treasury + subsidy == reward exacto (el residual al subsidy).
|
||||
<Emission as Hooks<u64>>::on_initialize(1);
|
||||
let treasury = Emission::treasury_account();
|
||||
let subsidy = Emission::subsidy_account();
|
||||
let total = <Balances as Inspect<_>>::balance(&author())
|
||||
+ <Balances as Inspect<_>>::balance(&treasury)
|
||||
+ <Balances as Inspect<_>>::balance(&subsidy);
|
||||
assert_eq!(total, 1_000, "el reparto no pierde unidades");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,10 @@ getrandom_03 = { workspace = true }
|
||||
# (CHN-4 Foundation Treasury Governance, AI-Driven Human-Guarded canonical)
|
||||
origin-chain-governance.workspace = true
|
||||
|
||||
# FIX-6 Stage 1: origin-chain-emission wired Index 13 — paga a los validadores
|
||||
# Aura (mintea block reward + split treasury/subsidy). Cierra V2-CA2.
|
||||
origin-chain-emission.workspace = true
|
||||
|
||||
# Phase 3 Slice 3.1 v0.0.39: pallet-revive wired runtime composition Index 11
|
||||
# (SBS-5 ratificado supersedes CHN-6 v1.0 → PolkaVM RISC-V + ink! v6 + Solidity).
|
||||
# Smart contracts deployable canonical post Slice 3.1 SCAFFOLD COMPLETE (v0.0.41).
|
||||
@@ -131,6 +135,7 @@ std = [
|
||||
"origin-chain-fees/std",
|
||||
"origin-chain-agent-identity/std",
|
||||
"origin-chain-governance/std",
|
||||
"origin-chain-emission/std",
|
||||
"pallet-revive/std",
|
||||
"sp-offchain/std",
|
||||
"sp-genesis-builder/std",
|
||||
|
||||
@@ -416,6 +416,44 @@ impl origin_chain_governance::Config for Runtime {
|
||||
type TreasuryPalletId = GovernanceTreasuryPalletId;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// origin_chain_emission::Config — FIX-6 Stage 1 (audit V2-CA2)
|
||||
// ============================================================
|
||||
//
|
||||
// En cada bloque mintea EmissionBlockReward y lo reparte: 70% al autor (el
|
||||
// validador Aura), 20% al treasury (og/gvtry, la MISMA cuenta de governance),
|
||||
// 10% al subsidy pool (og/subsy, proof-of-equity que financia el subsidio de
|
||||
// hardware #16 — ortogonal a la suscripción, David 2026-06-10). Cierra "los
|
||||
// validadores ganan 0 OGC". FindAuthor = el mapeo Aura→validador de session.
|
||||
//
|
||||
// BlockReward 10 OGC × ~5.256M bloques/año ≈ 52.6M OGC/año ≈ 5.26% inflación
|
||||
// sobre el genesis de 1B (materializa la emisión del 40% Ecosystem BM V2).
|
||||
// Stage 2 introduce pallet_staking NPoS sobre este substrate.
|
||||
parameter_types! {
|
||||
/// Recompensa por bloque: 10 OGC (~5% inflación anual).
|
||||
pub const EmissionBlockReward: Balance = 10 * OGC;
|
||||
/// 70% al autor (validador Aura).
|
||||
pub const EmissionAuthorShareBps: u16 = 7_000;
|
||||
/// 20% al treasury (el residual 10% va al subsidy pool).
|
||||
pub const EmissionTreasuryShareBps: u16 = 2_000;
|
||||
/// PalletId del subsidy pool (proof-of-equity, #16 anti-clasista).
|
||||
pub const EmissionSubsidyPalletId: frame_support::PalletId =
|
||||
frame_support::PalletId(*b"og/subsy");
|
||||
}
|
||||
|
||||
impl origin_chain_emission::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type Currency = crate::Balances;
|
||||
/// Aura → validador account vía pallet_session (FindAccountFromAuthorIndex).
|
||||
type FindAuthor = pallet_session::FindAccountFromAuthorIndex<Runtime, crate::Aura>;
|
||||
type BlockReward = EmissionBlockReward;
|
||||
type AuthorShareBps = EmissionAuthorShareBps;
|
||||
type TreasuryShareBps = EmissionTreasuryShareBps;
|
||||
// Reusa el MISMO vault de governance (og/gvtry) para el treasury.
|
||||
type TreasuryPalletId = GovernanceTreasuryPalletId;
|
||||
type SubsidyPalletId = EmissionSubsidyPalletId;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// pallet_revive::Config — Phase 3 Slice 3.1 v0.0.39 wired
|
||||
// ============================================================
|
||||
|
||||
@@ -376,6 +376,14 @@ mod runtime {
|
||||
#[runtime::pallet_index(12)]
|
||||
pub type Sudo = pallet_sudo::Pallet<Runtime>;
|
||||
|
||||
/// Emission pallet (FIX-6 Stage 1 per audit V2-CA2). En cada bloque mintea un
|
||||
/// block reward y lo reparte: autor Aura (validador) + treasury (og/gvtry) +
|
||||
/// subsidy pool (og/subsy, proof-of-equity #16). Cierra "los validadores
|
||||
/// ganan 0 OGC". sub_mult ortogonal (David 2026-06-10): el subsidy financia
|
||||
/// el subsidio de hardware sin importar la suscripción.
|
||||
#[runtime::pallet_index(13)]
|
||||
pub type Emission = origin_chain_emission::Pallet<Runtime>;
|
||||
|
||||
// Substrate pallet-identity DEFERRED canonical (decision empírica
|
||||
// 2026-05-21 documentada en configs.rs). pallet-identity 41.0.0 ReservableCurrency
|
||||
// compat work canonical hereda al custom `pallet_agent_identity` Phase 2.7
|
||||
@@ -951,6 +959,14 @@ mod tests {
|
||||
assert_eq!(<crate::Sudo as PalletInfoAccess>::name(), "Sudo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn emission_pallet_index_13_canonical() {
|
||||
// FIX-6 Stage 1: Emission=13 (post Sudo=12). Paga validadores Aura (V2-CA2).
|
||||
use frame_support::traits::PalletInfoAccess;
|
||||
assert_eq!(<crate::Emission as PalletInfoAccess>::index(), 13);
|
||||
assert_eq!(<crate::Emission as PalletInfoAccess>::name(), "Emission");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn revive_address_mapper_account32_returns_h160_canonical() {
|
||||
// AccountId32Mapper canonical: AccountId32 → H160 mapping deterministic.
|
||||
|
||||
Reference in new issue
Block a user