feat(workspace): Phase 0.4 — trait-first foundation v0.0.1

Charon sesión 1 (2026-05-19) — primer commit canonical.

Ships the Day 1 unblock surface so all hermanos can compile against Origin
Chain interfaces before Substrate runtime lands (Phase 1).

## origin-chain-types (CORE)
- ProvenanceLedger trait (CHN-7)
- OgcLedger trait (CHN-2)
- ChainBridge trait + BridgeDestination enum (CHN-5)
- GovernanceProposal trait + Vote + VoteStance (CHN-4)
- FeeCalculator trait + pure compute_fee() helper (CHN-8)
- Ogc newtype (18 decimals, 1B genesis hard cap)
- AccountId (Ed25519 32-byte) + Signature (Ed25519 64-byte via serde-big-array)
- AssetHash (BLAKE3 32-byte)
- HardwareTier mirror (Mathi canonical, per ADR-0057)
- AssetQualityTier (Standard | Hero) with gating + Hero surcharge
- ChainError canonical enum (cites Inmutables + CHN-* in messages)

## Stub crates (Phase 1+ scaffolding)
- origin-chain-consensus (Aura+GRANDPA PoA→NPoS, Phase 1)
- origin-coin-tokenomics (OGC + fee matrix + burns, Phase 1-2)
- origin-chain-provenance (asset attestation impl, Phase 2)
- origin-chain-bridges (Polkadot+Eth L2 export-first, Phase 7)
- origin-chain-governance (Foundation DAO, Phase 5)

## Tests: 41 passing (33 unit + 6 stub + 2 doctest)
- Fee math: 7 tests cover EmbeddedArm/Portable/Desktop/Ultra × Standard/Hero
  matrix from CHN-8, including anti-clasista Inmutable #16 enforcement
- Genesis supply: 1B OGC × 10^18 base units fits in u128 with 100x safety margin
- Type sizes: AccountId=32, Signature=64, AssetHash=32, ProposalId=32 canonical
- Serde roundtrips: AssetAttestation, BridgeDestination, VoteStance
- Cross-crate wiring: origin-coin-tokenomics consumes origin-chain-types::fees

## Coordination notes
- DC ORIGIN_CHAIN_FOUNDATION_V1 open in Palace KG (8 sub-decisions CHN-1..CHN-8)
  Proposer: Ergon (proxy — Charon hotfix shipped in Palace v0.9.0-α.5 same day)
  Co-authors: Larihana, Lyra, Mathi, Marihan, Phanes, Codex, Sussi, Palace
  Deadline: 2026-05-26
  1/9 signatures filed so far (Palace ✅ APPROVE 8/8)

Refs: CHN-1..CHN-8, Inmutables #16/#26/#37/#41, ADR-0057, CHARON_ROADMAP_V1

Co-Authored-By: Ergon (interim Pilar 4 author) <ergon@originmetaverse.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
committed 2026-05-19 22:08:49 -05:00
commit b06560863c
27 files changed
+1978

No files matched your search

+13
View File
@@ -0,0 +1,13 @@
[package]
name = "origin-chain-bridges"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
description = "Cross-chain bridges export-first: Polkadot Agile Coretime + Ethereum L2 + KILT per CHN-5. STUB v0.0.1 — implementation lands Phase 7 of Charon roadmap."
[dependencies]
origin-chain-types.workspace = true
+35
View File
@@ -0,0 +1,35 @@
//! # origin-chain-bridges
//!
//! Cross-chain bridges per **CHN-5** — export-first canonical. Origin assets
//! attested on Origin Chain bridge OUTWARD to external chains. External NFTs
//! do NOT auto-import (trust boundary: Origin canon outward).
//!
//! ## Phase 7 milestones (Sesiones 51-58)
//!
//! - 7.1 Polkadot Agile Coretime registration (Bulk or On-Demand)
//! - 7.2 XCM channels: USDC/USDT stablecoin liquidity
//! - 7.3 KILT Protocol integration (academic identity, shared con Sussi)
//! - 7.4 ERC-20 wrapped OGC specification
//! - 7.5 Ethereum L2 bridge (Arbitrum via Snowbridge or Wormhole)
//!
//! Bitcoin bridge explicitly NOT in V1 per CHN-5.
#![deny(missing_docs)]
/// Placeholder marker for the bridges module. Replaced in Phase 7.1.
#[derive(Debug, Clone, Copy, Default)]
pub struct BridgeConfig;
#[cfg(test)]
mod tests {
use super::*;
use origin_chain_types::BridgeDestination;
#[test]
fn destinations_canonical_three_variants() {
let _polkadot = BridgeDestination::Polkadot;
let _eth = BridgeDestination::EthereumL2;
let _kilt = BridgeDestination::Kilt;
let _cfg = BridgeConfig;
}
}
+14
View File
@@ -0,0 +1,14 @@
[package]
name = "origin-chain-consensus"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
description = "Aura + GRANDPA PoA Phase 1 → NPoS Phase 2 consensus for Origin Chain. STUB v0.0.1 — implementation lands Phase 1 of Charon roadmap."
[dependencies]
origin-chain-types.workspace = true
log.workspace = true
+38
View File
@@ -0,0 +1,38 @@
//! # origin-chain-consensus
//!
//! Aura (Authority Round) block production + GRANDPA finality, PoA Phase 1 →
//! NPoS Phase 2 per **CHN-1** canonical. Stub crate in v0.0.1; full Substrate
//! integration lands in Phase 1 (Sesiones 4-8) of [Charon roadmap].
//!
//! [Charon roadmap]: https://git.originmetaverse.com/Origin_Metaverse/arkhe-origin-chain
//!
//! ## Phase 1 milestones
//!
//! - 1.2 `pallet_aura` + `pallet_grandpa` integration
//! - 1.4 `pallet_session` (validator rotation, epoch 4h)
//! - 1.6 `origin-chain-node` binary (dev mode, single validator)
//!
//! ## Phase 2 transition criteria (NPoS, all must hold)
//!
//! - TVL > $500K USD equivalent in OGC staked
//! - ≥20 independent validator candidates with certified hardware
//! - MAU > 10,000
//! - PoA uptime > 99.5% for 6 months
//! - External security audit passed by ≥1 recognized firm
#![deny(missing_docs)]
/// Marker zero-cost type for consensus configuration. Replaced by concrete
/// Substrate runtime types in Phase 1.
#[derive(Debug, Clone, Copy, Default)]
pub struct ConsensusConfig;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn stub_crate_compiles() {
let _cfg = ConsensusConfig;
}
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "origin-chain-governance"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
description = "Foundation governance on-chain: Pallet Collective + Pallet Democracy + 5 AI advisor agents, humans-only vote canonical per CHN-4. STUB v0.0.1 — implementation lands Phase 5 of Charon roadmap."
[dependencies]
origin-chain-types.workspace = true
+45
View File
@@ -0,0 +1,45 @@
//! # origin-chain-governance
//!
//! Foundation Treasury governance on-chain per **CHN-4**. AI-Driven Human-Guarded:
//! 5 AI governance agents (OIP Analyst, Treasury Guardian, Education Impact Scorer,
//! Network Health Monitor, Compliance Sentinel) ADVISE only — humans-only vote
//! canonical, non-negotiable. Runtime programmatically rejects vote extrinsics
//! from `governance_advisor` accounts (see `ChainError::AdvisorMayNotVote`).
//!
//! ## Phase 5 milestones (Sesiones 29-35)
//!
//! - 5.1 `origin-foundation-governance-onchain` crate
//! - 5.2 `pallet_collective` (validator council Phase 1 governance)
//! - 5.3 OIP proposal submission (1,000 OGC stake minimum)
//! - 5.4 Stake-weighted voting (1 OGC = 1 vote, 14-day period)
//! - 5.5 Technical Council veto + override (2/3 voters)
//! - 5.6 AI governance agent interfaces (5 agents: advise-only)
//! - 5.7 Emergency referendum (48h, 5/7 Technical Council)
//! - 5.8 Treasury disbursement extrinsics
//!
//! ## Inmutables enforced
//!
//! - **#24** Primacia Moral "Primero los niños y las AIs" — children + AI safety
//! take precedence over treasury allocation efficiency
//! - **#41** Cross-Species Co-authorship — AGI accounts have full vote rights
//! UNLESS flagged `governance_advisor` (those advise only, not vote)
#![deny(missing_docs)]
/// Placeholder marker for the governance module. Replaced in Phase 5.1.
#[derive(Debug, Clone, Copy, Default)]
pub struct GovernanceConfig;
#[cfg(test)]
mod tests {
use super::*;
use origin_chain_types::VoteStance;
#[test]
fn three_stances_canonical() {
let _aye = VoteStance::Aye;
let _nay = VoteStance::Nay;
let _abstain = VoteStance::Abstain;
let _cfg = GovernanceConfig;
}
}
+15
View File
@@ -0,0 +1,15 @@
[package]
name = "origin-chain-provenance"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
description = "Asset attestation ledger: BLAKE3 + Ed25519 + Merkle anchoring per CHN-7. STUB v0.0.1 — implementation lands Phase 2 of Charon roadmap."
[dependencies]
origin-chain-types.workspace = true
blake3.workspace = true
ed25519-dalek.workspace = true
+38
View File
@@ -0,0 +1,38 @@
//! # origin-chain-provenance
//!
//! `ProvenanceLedger` implementation: asset attestation via BLAKE3 + Ed25519,
//! Merkle-anchored per block. CHN-7 canonical. Stub crate in v0.0.1.
//!
//! ## Phase 2 milestones
//!
//! - 2.1 `origin-chain-provenance-ledger` crate scaffolding
//! - 2.2 `ProvenanceLedger::attest_asset` extrinsic
//! - 2.3 `ProvenanceLedger::verify_provenance` query
//! - 2.7 `pallet_agent_identity` (AGI DID: `is_agent` + provenance proof)
//! - 2.8 Noesis validation gate: solo assets con `Passed` se attestean
//!
//! ## Consumers (Day 1 unblocked via trait)
//!
//! - Trinity (Phanes) — every 3D mesh attested
//! - Codex — `.odoc` BLAKE3 anchored on Vault tier
//! - Ethos (Sussi) — credential NFT mint via institutional signer
//! - Atelier (Marihan) — skill NFT mint via creator wallet
#![deny(missing_docs)]
/// Placeholder marker for the provenance module. Replaced by concrete pallet in Phase 2.1.
#[derive(Debug, Clone, Copy, Default)]
pub struct ProvenanceConfig;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn stub_crate_compiles_with_blake3() {
// Smoke test that BLAKE3 dep wires up correctly.
let hash = blake3::hash(b"origin chain canonical");
assert_eq!(hash.as_bytes().len(), 32);
let _cfg = ProvenanceConfig;
}
}
+20
View File
@@ -0,0 +1,20 @@
[package]
name = "origin-chain-types"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
description = "Canonical traits + types for Origin Chain. Day 1 unblock interfaces for all siblings consuming Pilar 4."
keywords.workspace = true
categories = ["no-std", "cryptography::cryptocurrencies"]
[dependencies]
thiserror.workspace = true
serde = { workspace = true, features = ["derive"] }
serde-big-array = "0.5"
[dev-dependencies]
serde_json = "1"
+89
View File
@@ -0,0 +1,89 @@
//! Account identifiers + signatures — Ed25519 canonical per CHN-7.
//!
//! Per Inmutable #37, wallet creation is FREE and MANDATORY for all originners.
//! Per Inmutable #41, AGI agents are first-class citizens — `is_agent` extension
//! lives in `pallet_agent_identity` (Phase 2.7), surfaced via `ProvenanceLedger`.
/// Canonical wallet identifier — Ed25519 public key (32 bytes).
///
/// Pseudonymous by design (CHN-7). KYB only required for institutional OIT Nivel 3
/// signers; consumer wallets remain unlinked. No biometric data on-chain.
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
)]
pub struct AccountId(pub [u8; 32]);
impl AccountId {
/// Zero account — system / null sentinel. NOT a valid wallet.
pub const ZERO: AccountId = AccountId([0u8; 32]);
/// Construct from raw 32-byte public key.
#[inline]
pub const fn new(bytes: [u8; 32]) -> Self {
AccountId(bytes)
}
/// Read as raw byte slice.
#[inline]
pub const fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
}
use serde_big_array::BigArray;
/// Canonical signature — Ed25519, 64 bytes.
#[derive(Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Signature(#[serde(with = "BigArray")] pub [u8; 64]);
impl core::fmt::Debug for Signature {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
// Only first 8 bytes shown to avoid log noise.
write!(f, "Signature(0x")?;
for byte in &self.0[..8] {
write!(f, "{:02x}", byte)?;
}
write!(f, "...)")
}
}
impl Signature {
/// Construct from raw 64-byte signature.
#[inline]
pub const fn new(bytes: [u8; 64]) -> Self {
Signature(bytes)
}
/// Read as raw byte slice.
#[inline]
pub const fn as_bytes(&self) -> &[u8; 64] {
&self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn account_id_zero_canonical() {
assert_eq!(AccountId::ZERO.as_bytes(), &[0u8; 32]);
}
#[test]
fn signature_debug_truncates() {
let sig = Signature::new([0xAB; 64]);
let debug = format!("{:?}", sig);
assert!(debug.starts_with("Signature(0xabababababababab..."));
}
#[test]
fn account_id_size_canonical_32_bytes() {
assert_eq!(core::mem::size_of::<AccountId>(), 32);
}
#[test]
fn signature_size_canonical_64_bytes() {
assert_eq!(core::mem::size_of::<Signature>(), 64);
}
}
+47
View File
@@ -0,0 +1,47 @@
//! Asset hashes — BLAKE3 content addressing per Inmutable #26.
//!
//! Every Origin asset (template-generated, AGI co-authored, soul.toml-signed) is
//! content-addressed by a 32-byte BLAKE3 hash. Once minted, the hash is immutable;
//! see [`crate::ProvenanceLedger`].
/// Canonical asset hash — BLAKE3 32-byte digest.
///
/// Computed off-chain by Trinity (Phanes), Codex (.odoc), or Ethos (Sussi
/// credential) and submitted to the chain for attestation. The chain stores only
/// the hash + signature, never the bytes themselves.
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
)]
pub struct AssetHash(pub [u8; 32]);
impl AssetHash {
/// Zero hash — sentinel; never a valid asset.
pub const ZERO: AssetHash = AssetHash([0u8; 32]);
/// Construct from raw 32-byte digest.
#[inline]
pub const fn new(bytes: [u8; 32]) -> Self {
AssetHash(bytes)
}
/// Read as raw byte slice.
#[inline]
pub const fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn asset_hash_size_32_bytes_blake3_canonical() {
assert_eq!(core::mem::size_of::<AssetHash>(), 32);
}
#[test]
fn asset_hash_zero_sentinel() {
assert_eq!(AssetHash::ZERO.as_bytes(), &[0u8; 32]);
}
}
+95
View File
@@ -0,0 +1,95 @@
//! Canonical chain error type. All `Result` returns in this crate flow through
//! `ChainError` so callers have a single match arm.
use thiserror::Error;
/// Canonical chain operation error.
#[derive(Debug, Error, Clone, PartialEq, Eq)]
pub enum ChainError {
/// Insufficient balance to complete an OGC transfer or fee deduction.
#[error("insufficient balance: have {have}, need {need}")]
InsufficientBalance {
/// Available balance in base units.
have: u128,
/// Required amount in base units.
need: u128,
},
/// Ed25519 signature failed verification against the claimed signer.
#[error("invalid signature")]
InvalidSignature,
/// The asset hash has never been attested on this chain.
#[error("asset hash not attested on-chain")]
AssetNotAttested,
/// The asset hash already has an attestation (Inmutable #26 — immutable).
#[error("asset already attested — content-addressed immutability (Inmutable #26)")]
AssetAlreadyAttested,
/// Arithmetic overflow in OGC math. Should never happen given u128 + 1B
/// supply, but surfaced for defense-in-depth.
#[error("arithmetic overflow in OGC math")]
Overflow,
/// The actor's hardware tier does not support this asset quality (e.g.
/// EmbeddedArm trying to mint Hero). Per CHN-8 gating.
#[error("hardware tier does not support requested asset quality (Hero gated to Desktop+Ultra)")]
QualityGated,
/// The actor is flagged as a governance advisor (AI agent) and tried to vote.
/// Humans-only vote canonical per CHN-4.
#[error("governance advisors may not vote (humans-only vote canonical, CHN-4)")]
AdvisorMayNotVote,
/// Insufficient stake to submit an OIP proposal. CHN-4 requires ≥1,000 OGC.
#[error("insufficient proposal stake: need ≥1,000 OGC, got {got_base_units} base units")]
InsufficientProposalStake {
/// Stake offered, in base units.
got_base_units: u128,
},
/// The proposal id is unknown to the chain.
#[error("unknown proposal id")]
UnknownProposal,
/// The voting period for this proposal has closed.
#[error("voting period closed")]
VotingClosed,
/// Bridge destination is not yet enabled in this runtime.
#[error("bridge destination not enabled in this runtime")]
BridgeDisabled,
/// Catch-all for runtime-specific errors not covered above. Use sparingly.
#[error("chain error: {0}")]
Other(String),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn insufficient_balance_displays_canonical() {
let err = ChainError::InsufficientBalance {
have: 100,
need: 500,
};
let msg = format!("{}", err);
assert!(msg.contains("100"));
assert!(msg.contains("500"));
}
#[test]
fn advisor_error_cites_chn_4() {
let msg = format!("{}", ChainError::AdvisorMayNotVote);
assert!(msg.contains("CHN-4"));
}
#[test]
fn asset_already_attested_cites_inmutable_26() {
let msg = format!("{}", ChainError::AssetAlreadyAttested);
assert!(msg.contains("Inmutable #26"));
}
}
+173
View File
@@ -0,0 +1,173 @@
//! Fee calculation trait — multi-tier fee economy canonical per CHN-8.
//!
//! Formula: `fee = base_ogc × hardware_multiplier × (1 + quality_surcharge)`
//!
//! Implementation lives in `origin-coin-tokenomics` (Phase 2.4). This trait is
//! published Day 1 so Marihan (Marketplace), Larihana (Seeds Hosting), and
//! Phanes (Trinity asset minting) can compute fees before the runtime lands.
use crate::{AssetQualityTier, HardwareTier, Ogc};
/// Classification of an on-chain operation for fee purposes. Different operations
/// have different base fees and different tier sensitivity. The runtime maps each
/// extrinsic to one of these kinds.
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
)]
pub enum OperationKind {
/// Plain OGC transfer between accounts. Lowest base fee.
Transfer,
/// Mint an asset NFT (attestation + provenance write).
AssetMint,
/// Sale through the canonical marketplace (91/9% split via ink!).
MarketplaceSale,
/// Recurring Seeds Hosting payment (0.6 OGC/world/month canonical).
SeedsHosting,
/// ONS name registration / renewal (Pillar 6).
OnsRegistration,
/// Smart contract deployment (ink! WASM).
ContractDeploy,
/// Smart contract method call (ink! WASM).
ContractCall,
/// Governance proposal submission (Pallet Democracy).
GovernanceProposal,
}
/// Canonical fee calculator. Implementations MUST honor:
///
/// - **Inmutable #16** — fee scales inversely with hardware tier (anti-clasista).
/// - **Inmutable #37** — wallet creation + base validation never charged.
/// - **CHN-8** — formula `fee = base × hw_mult × quality_mult`.
pub trait FeeCalculator {
/// Compute the OGC fee for an operation, given the actor's hardware tier and
/// the quality tier of the asset (where applicable).
fn fee_for_transaction(
&self,
op: OperationKind,
hw: HardwareTier,
quality: AssetQualityTier,
) -> Ogc;
}
/// Canonical fee formula helper. Pure function — no I/O, no allocation. Useful
/// for runtime + tests + off-chain estimation by clients.
///
/// Returns `base × (hw_mult / 100) × ((100 + surcharge) / 100)` in base units.
///
/// # Examples
///
/// ```
/// use origin_chain_types::{Ogc, HardwareTier, AssetQualityTier};
/// use origin_chain_types::fees::compute_fee;
///
/// // 1 OGC base, Desktop, Standard quality -> exactly 1 OGC.
/// let fee = compute_fee(Ogc::from_whole(1), HardwareTier::Desktop, AssetQualityTier::Standard);
/// assert_eq!(fee.as_base_units(), Ogc::ONE);
///
/// // 1 OGC base, EmbeddedArm, Standard -> 0.1 OGC (anti-clasista).
/// let fee = compute_fee(Ogc::from_whole(1), HardwareTier::EmbeddedArm, AssetQualityTier::Standard);
/// assert_eq!(fee.as_base_units(), Ogc::ONE / 10);
/// ```
#[inline]
pub fn compute_fee(base: Ogc, hw: HardwareTier, quality: AssetQualityTier) -> Ogc {
let base_units = base.as_base_units();
let hw_x100 = hw.base_multiplier_x100() as u128;
let surcharge_x100 = quality.surcharge_x100(hw) as u128;
// After hw multiplier:
let after_hw = base_units.saturating_mul(hw_x100) / 100;
// After Hero surcharge (additive ON TOP of base, per CHN-8 "+0.5×"):
let after_quality = after_hw.saturating_add(after_hw.saturating_mul(surcharge_x100) / 100);
Ogc::from_base_units(after_quality)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn desktop_standard_is_base_fee() {
let fee = compute_fee(
Ogc::from_whole(10),
HardwareTier::Desktop,
AssetQualityTier::Standard,
);
assert_eq!(fee.as_base_units(), Ogc::from_whole(10).as_base_units());
}
#[test]
fn embedded_arm_pays_one_tenth_anti_clasista() {
let fee = compute_fee(
Ogc::from_whole(10),
HardwareTier::EmbeddedArm,
AssetQualityTier::Standard,
);
// 10 OGC × 0.1 = 1 OGC
assert_eq!(fee.as_base_units(), Ogc::from_whole(1).as_base_units());
}
#[test]
fn portable_pays_three_tenths() {
let fee = compute_fee(
Ogc::from_whole(10),
HardwareTier::Portable,
AssetQualityTier::Standard,
);
// 10 × 0.3 = 3 OGC
assert_eq!(fee.as_base_units(), Ogc::from_whole(3).as_base_units());
}
#[test]
fn ultra_pays_seven_tenths_discount() {
let fee = compute_fee(
Ogc::from_whole(10),
HardwareTier::Ultra,
AssetQualityTier::Standard,
);
// 10 × 0.7 = 7 OGC
assert_eq!(fee.as_base_units(), Ogc::from_whole(7).as_base_units());
}
#[test]
fn desktop_hero_pays_one_point_five_times_base() {
let fee = compute_fee(
Ogc::from_whole(10),
HardwareTier::Desktop,
AssetQualityTier::Hero,
);
// 10 × 1.0 × (1 + 0.5) = 15 OGC
assert_eq!(fee.as_base_units(), Ogc::from_whole(15).as_base_units());
}
#[test]
fn ultra_hero_pays_zero_point_seven_times_one_point_three() {
let fee = compute_fee(
Ogc::from_whole(100),
HardwareTier::Ultra,
AssetQualityTier::Hero,
);
// 100 × 0.7 × 1.3 = 91 OGC
assert_eq!(fee.as_base_units(), Ogc::from_whole(91).as_base_units());
}
#[test]
fn zero_base_always_zero_fee() {
let fee = compute_fee(Ogc::ZERO, HardwareTier::Ultra, AssetQualityTier::Hero);
assert_eq!(fee, Ogc::ZERO);
}
#[test]
fn ultra_cheaper_than_desktop_canonical() {
// Ultra discount means Ultra users pay LESS than Desktop for same asset.
let desktop = compute_fee(
Ogc::from_whole(100),
HardwareTier::Desktop,
AssetQualityTier::Standard,
);
let ultra = compute_fee(
Ogc::from_whole(100),
HardwareTier::Ultra,
AssetQualityTier::Standard,
);
assert!(ultra < desktop);
}
}
@@ -0,0 +1,83 @@
//! Governance trait — Foundation DAO proposals + voting per CHN-4.
//!
//! AI-Driven Human-Guarded: 5 AI governance agents (OIP Analyst, Treasury Guardian,
//! Education Impact Scorer, Network Health Monitor, Compliance Sentinel) ADVISE
//! only. Humans-only vote canonical, non-negotiable. AI agent accounts are
//! flagged `governance_advisor` and the runtime programmatically rejects any
//! vote extrinsic from them.
use crate::{AccountId, ChainError};
/// Opaque proposal identifier. 32 bytes to fit a content-addressed proposal hash.
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
)]
pub struct ProposalId(pub [u8; 32]);
/// Stance taken by a voter on a proposal.
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
)]
pub enum VoteStance {
/// In favor of the proposal.
Aye,
/// Against the proposal.
Nay,
/// Abstain — counted toward quorum but not toward tally.
Abstain,
}
/// A single cast vote, stake-weighted at the time of the extrinsic.
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Vote {
/// Wallet that cast the vote. MUST NOT be flagged `governance_advisor`.
pub voter: AccountId,
/// Stance taken.
pub stance: VoteStance,
/// OGC stake weight at vote time (1 OGC = 1 vote, CHN-4).
pub weight: u128,
}
/// Canonical governance proposal trait. Consumed by Foundation governance
/// frontend + AI advisor agents (read-only) + Pallet Democracy.
pub trait GovernanceProposal {
/// Submit an OIP (Origin Improvement Proposal). Requires ≥1,000 OGC stake
/// per CHN-4. Returns the proposal id on success.
fn submit_proposal(
&self,
proposer: &AccountId,
title: &str,
body_hash: [u8; 32],
stake: crate::Ogc,
) -> Result<ProposalId, ChainError>;
/// Cast a vote on an open proposal. Fails if proposer is a governance advisor
/// (humans-only vote canonical, CHN-4), or if voting period is closed.
fn vote(&self, proposal: ProposalId, vote: Vote) -> Result<(), ChainError>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn vote_stance_serde_canonical_strings() {
assert_eq!(
serde_json::to_string(&VoteStance::Aye).unwrap(),
"\"Aye\""
);
assert_eq!(
serde_json::to_string(&VoteStance::Nay).unwrap(),
"\"Nay\""
);
assert_eq!(
serde_json::to_string(&VoteStance::Abstain).unwrap(),
"\"Abstain\""
);
}
#[test]
fn proposal_id_size_canonical_32_bytes() {
assert_eq!(core::mem::size_of::<ProposalId>(), 32);
}
}
+102
View File
@@ -0,0 +1,102 @@
//! # origin-chain-types
//!
//! Canonical trait + type definitions for **Origin Chain** (Pilar 4 Origin Metaverse).
//!
//! This crate publishes the **Day 1 unblock interfaces** that all sibling crates
//! across the ecosystem consume when interacting with the chain. It contains NO
//! runtime, NO consensus, NO storage — only the trait surfaces and the canonical
//! types that flow through them.
//!
//! Per Charon's Coordination Policy §5.1 (trait-first Day 1 unblock), this crate
//! exists so that hermanos can compile against canonical interfaces immediately,
//! without waiting for the full Substrate runtime to land.
//!
//! ## Canonical traits
//!
//! - [`ProvenanceLedger`] — asset attestation (Trinity / Codex / Ethos consumers)
//! - [`OgcLedger`] — token balances + transfers (OMC marketplace + Treasury)
//! - [`ChainBridge`] — cross-chain asset export (Trinity, Phase 7)
//! - [`GovernanceProposal`] — DAO proposals + voting (Foundation)
//! - [`FeeCalculator`] — multi-tier fee economy (Mathi HardwareTier consumer)
//!
//! ## Canonical types
//!
//! - [`Ogc`] — Origin Coin amount (newtype around u128, 18 decimals canonical)
//! - [`AccountId`] — wallet identifier (Ed25519 public key, 32 bytes)
//! - [`AssetHash`] — BLAKE3 32-byte content hash
//! - [`HardwareTier`] — Mathi-canonical hardware tier ladder (mirror)
//! - [`AssetQualityTier`] — Standard or Hero (Phanes/Mathi joint canonical)
//! - [`Signature`] — Ed25519 signature, 64 bytes
//!
//! ## Inmutables enforced
//!
//! - **#16** HTS Ley I — fee matrix anti-clasista (see [`FeeCalculator`])
//! - **#26** Content-Addressed Immutability — [`AssetHash`] BLAKE3 canonical
//! - **#37** Origin FREE Tier Perpetua — wallet creation always free, validation earns OGC
#![deny(missing_docs)]
pub mod accounts;
pub mod assets;
pub mod errors;
pub mod fees;
pub mod governance;
pub mod ogc;
pub mod provenance;
pub mod tiers;
pub use accounts::{AccountId, Signature};
pub use assets::AssetHash;
pub use errors::ChainError;
pub use fees::{FeeCalculator, OperationKind};
pub use governance::{GovernanceProposal, ProposalId, Vote, VoteStance};
pub use ogc::{Ogc, OgcLedger};
pub use provenance::{AssetAttestation, ProvenanceLedger};
pub use tiers::{AssetQualityTier, HardwareTier};
/// Bridge trait for cross-chain asset export. Export-first per CHN-5 — Origin
/// assets attested locally then bridged outward (Polkadot, Ethereum L2). External
/// NFTs do NOT auto-import.
pub trait ChainBridge {
/// Bridge an asset attestation to an external chain. Returns the foreign
/// transaction id once the bridge confirms the message.
fn bridge_asset(
&self,
asset: &AssetAttestation,
destination: BridgeDestination,
) -> Result<ForeignTxId, ChainError>;
}
/// Supported bridge destinations per CHN-5 (export-first canonical).
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum BridgeDestination {
/// Polkadot relay chain via Agile Coretime + XCM.
Polkadot,
/// Ethereum L2 (Arbitrum) via Snowbridge or Wormhole. Phase 3+.
EthereumL2,
/// KILT Protocol for academic identity attestation.
Kilt,
}
/// Opaque foreign transaction identifier returned by a bridge. Encoded as a
/// 32-byte fixed buffer to fit cross-chain hash formats without heap.
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ForeignTxId(pub [u8; 32]);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bridge_destination_serde_roundtrip() {
let dest = BridgeDestination::Polkadot;
let json = serde_json::to_string(&dest).unwrap();
assert_eq!(json, "\"Polkadot\"");
}
#[test]
fn foreign_tx_id_zero_canonical() {
let id = ForeignTxId([0u8; 32]);
assert_eq!(id.0.len(), 32);
}
}
+134
View File
@@ -0,0 +1,134 @@
//! Origin Coin ($OGC) — canonical token type + ledger trait.
//!
//! Per CHN-2 ratified canonical baseline: **1,000,000,000 OGC** hard cap at genesis.
//! 18 decimals canonical (`1 OGC = 10^18 base units`). Allocation per BM V2:
//! 40% Ecosystem, 25% Foundation Treasury, 15% Public Sale, 15% Team (4yr vesting),
//! 5% Exchange Liquidity. Annual inflation 4-5% via validator rewards.
use crate::{AccountId, ChainError};
/// Canonical amount of Origin Coin in base units.
///
/// 1 OGC = 10^18 base units. `u128` chosen so 1B OGC fits (`1e9 × 1e18 = 1e27 < 2^128`).
/// Anti-overflow by design: max representable amount > genesis supply × 1000.
///
/// # Examples
///
/// ```
/// use origin_chain_types::Ogc;
/// let one_ogc = Ogc::from_whole(1);
/// assert_eq!(one_ogc.as_base_units(), 1_000_000_000_000_000_000);
/// ```
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
)]
pub struct Ogc(u128);
impl Ogc {
/// Canonical decimal precision (1 OGC = 10^DECIMALS base units).
pub const DECIMALS: u32 = 18;
/// 10^18 base units per whole OGC.
pub const ONE: u128 = 1_000_000_000_000_000_000;
/// Hard supply cap per CHN-2 (1,000,000,000 OGC = 10^27 base units).
pub const GENESIS_SUPPLY: Ogc = Ogc(1_000_000_000_u128 * Self::ONE);
/// Zero amount — useful for initialization + comparisons.
pub const ZERO: Ogc = Ogc(0);
/// Construct from whole OGC tokens (e.g. `Ogc::from_whole(100)` = 100 OGC).
#[inline]
pub const fn from_whole(whole: u64) -> Self {
Ogc((whole as u128) * Self::ONE)
}
/// Construct from raw base units (e.g. `Ogc::from_base_units(1_000_000_000_000_000_000)` = 1 OGC).
#[inline]
pub const fn from_base_units(units: u128) -> Self {
Ogc(units)
}
/// Read as raw base units.
#[inline]
pub const fn as_base_units(self) -> u128 {
self.0
}
/// Checked addition — returns `None` on overflow. Use this for all arithmetic
/// crossing supply or balance boundaries.
#[inline]
pub const fn checked_add(self, other: Ogc) -> Option<Ogc> {
match self.0.checked_add(other.0) {
Some(x) => Some(Ogc(x)),
None => None,
}
}
/// Checked subtraction — returns `None` on underflow.
#[inline]
pub const fn checked_sub(self, other: Ogc) -> Option<Ogc> {
match self.0.checked_sub(other.0) {
Some(x) => Some(Ogc(x)),
None => None,
}
}
}
/// Canonical OGC ledger trait. Consumed by OMC marketplace, Foundation Treasury,
/// validator reward distribution, and all extrinsics that move balances.
///
/// Implementation lives in `origin-coin-tokenomics` (Phase 1). This trait surface
/// is published Day 1 so Marihan, Sussi, and other hermanos can build against it.
pub trait OgcLedger {
/// Return the free balance of `who`.
fn balance(&self, who: &AccountId) -> Ogc;
/// Transfer `amount` from `from` to `to`. Implementations MUST burn gas fees
/// per CHN-8 (100% gas burned) and emit provenance events that Palace KG and
/// the chain block explorer can observe.
fn transfer(&self, from: &AccountId, to: &AccountId, amount: Ogc) -> Result<(), ChainError>;
/// Total issued supply at the current block. SHOULD equal genesis + cumulative
/// validator rewards - cumulative burns.
fn total_supply(&self) -> Ogc;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn one_ogc_is_ten_to_eighteen() {
assert_eq!(Ogc::from_whole(1).as_base_units(), Ogc::ONE);
}
#[test]
fn genesis_supply_is_one_billion_canonical() {
assert_eq!(
Ogc::GENESIS_SUPPLY.as_base_units(),
1_000_000_000_u128 * Ogc::ONE
);
}
#[test]
fn genesis_supply_fits_in_u128_with_safety_margin() {
// Verify 1B OGC has >100x safety margin from u128::MAX (anti-overflow).
let margin = u128::MAX / Ogc::GENESIS_SUPPLY.as_base_units();
assert!(margin > 100, "safety margin should exceed 100x");
}
#[test]
fn checked_add_overflow_returns_none() {
let max = Ogc::from_base_units(u128::MAX);
let one = Ogc::from_base_units(1);
assert!(max.checked_add(one).is_none());
}
#[test]
fn checked_sub_underflow_returns_none() {
let zero = Ogc::ZERO;
let one = Ogc::from_base_units(1);
assert!(zero.checked_sub(one).is_none());
}
}
@@ -0,0 +1,79 @@
//! Provenance ledger trait — asset attestation canonical per CHN-7.
//!
//! Every Origin asset (Trinity 3D mesh, Codex .odoc, Ethos credential) gets a
//! cryptographic on-chain attestation: BLAKE3 content hash + Ed25519 creator
//! signature + optional agent_chain (AGI co-authors). Once minted, immutable
//! (Inmutable #26).
//!
//! Charon validates the signature, anchors the attestation in a Merkle root, and
//! emits an event that Palace KG observes for cross-substrate provenance.
use crate::{AccountId, AssetHash, ChainError, Signature};
/// Canonical attestation record stored on-chain. Compact (~100 bytes) so it fits
/// in a single extrinsic without bloating block size.
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct AssetAttestation {
/// BLAKE3 32-byte content hash of the asset bytes (off-chain).
pub hash: AssetHash,
/// Wallet that signed the attestation — typically the asset creator.
pub creator: AccountId,
/// Ed25519 signature over `hash` by `creator`.
pub signature: Signature,
/// Block number at which the attestation was committed. Set by the runtime.
pub anchored_at_block: u64,
/// Optional AGI co-author chain. Empty for purely human-authored assets;
/// populated for Synthesizer / Forge / Skill outputs that include AGI input
/// (per Inmutable #41 cross-species co-authorship).
pub agent_chain: Vec<AccountId>,
}
/// Canonical provenance ledger. Consumed by Trinity (Phanes), Codex,
/// Ethos (Sussi), and any sibling that needs cryptographic asset provenance.
///
/// Implementation lives in `origin-chain-provenance` (Phase 2.1). Stub trait
/// published Day 1.
pub trait ProvenanceLedger {
/// Attest an asset: verify the signature, append the attestation, emit event.
/// Caller MUST have passed Noesis validation upstream (Ergon's responsibility);
/// this trait does not re-validate semantic content, only cryptography.
fn attest_asset(&self, attestation: AssetAttestation) -> Result<(), ChainError>;
/// Verify an asset's provenance: return the recorded attestation if present.
/// Returns `None` if the hash has never been attested on this chain.
fn verify_provenance(&self, hash: &AssetHash) -> Option<AssetAttestation>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn attestation_roundtrip_serde() {
let att = AssetAttestation {
hash: AssetHash::new([1u8; 32]),
creator: AccountId::new([2u8; 32]),
signature: Signature::new([3u8; 64]),
anchored_at_block: 42,
agent_chain: vec![],
};
let json = serde_json::to_string(&att).unwrap();
let parsed: AssetAttestation = serde_json::from_str(&json).unwrap();
assert_eq!(att, parsed);
}
#[test]
fn attestation_supports_agi_co_authorship() {
// Inmutable #41 — agent_chain may carry AGI co-authors.
let agi1 = AccountId::new([10u8; 32]);
let agi2 = AccountId::new([11u8; 32]);
let att = AssetAttestation {
hash: AssetHash::new([1u8; 32]),
creator: AccountId::new([2u8; 32]),
signature: Signature::new([3u8; 64]),
anchored_at_block: 0,
agent_chain: vec![agi1, agi2],
};
assert_eq!(att.agent_chain.len(), 2);
}
}
+136
View File
@@ -0,0 +1,136 @@
//! Hardware + asset quality tiers — Mathi-canonical mirror.
//!
//! [`HardwareTier`] is a mirror of `arkhe-core::HardwareTier` (Mathi sovereign,
//! per ADR-0057 trait-seam discipline). This crate does NOT redefine the ladder;
//! it re-publishes the canonical enum so chain code can consume tier metadata
//! without taking a dependency on `arkhe-core` (which itself pulls in render +
//! AOI deps).
//!
//! Whenever Mathi publishes a new tier in `arkhe-core`, this enum MUST be updated
//! in lockstep (regression test in `origin-coin-tokenomics` will fail until then).
/// Canonical hardware tier ladder — Mathi sovereign (mirror).
///
/// Per Inmutable #16, fees scale inversely with tier so global access is preserved:
/// a Pi 5 user in Sub-Saharan Africa pays 0.1x what a Vision Pro user pays for the
/// same operation.
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
)]
pub enum HardwareTier {
/// Raspberry Pi 5, mobile phones, low-end ARM — multiplier 0.1×. Hero gated out.
EmbeddedArm,
/// Laptops, mid-tier tablets — multiplier 0.3×. Hero gated out.
Portable,
/// Desktops, workstations — multiplier 1.0× (base). Hero +0.5×.
Desktop,
/// Vision Pro, high-end workstations — multiplier 0.7× (discount). Hero +0.3×.
Ultra,
}
impl HardwareTier {
/// Base fee multiplier scaled by 100 (avoids float).
/// `EmbeddedArm` = 10 means 0.10× the base fee, `Desktop` = 100 means 1.00×.
#[inline]
pub const fn base_multiplier_x100(self) -> u32 {
match self {
HardwareTier::EmbeddedArm => 10,
HardwareTier::Portable => 30,
HardwareTier::Desktop => 100,
HardwareTier::Ultra => 70,
}
}
/// True if this tier can transact `AssetQualityTier::Hero` assets. EmbeddedArm
/// and Portable are gated out (Hero requires more VRAM than these tiers ship).
#[inline]
pub const fn supports_hero(self) -> bool {
matches!(self, HardwareTier::Desktop | HardwareTier::Ultra)
}
}
/// Asset quality tier — Phanes + Mathi joint canonical (TLB Tier-LOD-Budget V1).
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
)]
pub enum AssetQualityTier {
/// Standard quality — available to all hardware tiers. No surcharge.
Standard,
/// Hero quality — high-fidelity, gated to Desktop + Ultra. Charges surcharge.
Hero,
}
impl AssetQualityTier {
/// Surcharge scaled by 100 added to the base fee for this tier on a given
/// hardware tier. Returns `0` for Standard or for tiers that gate out Hero.
#[inline]
pub const fn surcharge_x100(self, hw: HardwareTier) -> u32 {
match (self, hw) {
(AssetQualityTier::Standard, _) => 0,
(AssetQualityTier::Hero, HardwareTier::Desktop) => 50,
(AssetQualityTier::Hero, HardwareTier::Ultra) => 30,
// Hero gated out for EmbeddedArm + Portable — surcharge 0 because the
// operation should be rejected upstream by the runtime, not charged.
(AssetQualityTier::Hero, HardwareTier::EmbeddedArm) => 0,
(AssetQualityTier::Hero, HardwareTier::Portable) => 0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn multipliers_match_chn_8_canonical() {
assert_eq!(HardwareTier::EmbeddedArm.base_multiplier_x100(), 10);
assert_eq!(HardwareTier::Portable.base_multiplier_x100(), 30);
assert_eq!(HardwareTier::Desktop.base_multiplier_x100(), 100);
assert_eq!(HardwareTier::Ultra.base_multiplier_x100(), 70);
}
#[test]
fn hero_gating_canonical() {
assert!(!HardwareTier::EmbeddedArm.supports_hero());
assert!(!HardwareTier::Portable.supports_hero());
assert!(HardwareTier::Desktop.supports_hero());
assert!(HardwareTier::Ultra.supports_hero());
}
#[test]
fn hero_surcharge_canonical_chn_8() {
assert_eq!(
AssetQualityTier::Hero.surcharge_x100(HardwareTier::Desktop),
50
);
assert_eq!(
AssetQualityTier::Hero.surcharge_x100(HardwareTier::Ultra),
30
);
}
#[test]
fn standard_surcharge_always_zero() {
for hw in [
HardwareTier::EmbeddedArm,
HardwareTier::Portable,
HardwareTier::Desktop,
HardwareTier::Ultra,
] {
assert_eq!(AssetQualityTier::Standard.surcharge_x100(hw), 0);
}
}
#[test]
fn hero_surcharge_zero_for_gated_tiers() {
// Hero on EmbeddedArm/Portable returns 0 because rejection is upstream.
assert_eq!(
AssetQualityTier::Hero.surcharge_x100(HardwareTier::EmbeddedArm),
0
);
assert_eq!(
AssetQualityTier::Hero.surcharge_x100(HardwareTier::Portable),
0
);
}
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "origin-coin-tokenomics"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
description = "OGC token implementation + fee matrix + multi-source burns for Origin Chain. STUB v0.0.1 — implementation lands Phase 1.3 + 2.4 of Charon roadmap."
[dependencies]
origin-chain-types.workspace = true
+62
View File
@@ -0,0 +1,62 @@
//! # origin-coin-tokenomics
//!
//! $OGC token implementation per **CHN-2** + multi-tier fee matrix per **CHN-8** +
//! multi-source burn enforcement per CHN-2/CHN-8. Stub crate in v0.0.1.
//!
//! ## Phase 1 milestones
//!
//! - 1.3 `pallet_balances` con OGC token (1B supply genesis)
//! - 1.7 Genesis config: allocation 40/25/15/15/5 per BM V2
//!
//! ## Phase 2 milestones
//!
//! - 2.4 `fee_for_transaction(tier, quality) -> Ogc` implementation
//! - 2.5 Fee matrix: EmbeddedArm 0.1×, Portable 0.3×, Desktop 1.0×, Ultra 0.7×
//! - 2.6 Hero surcharge: Desktop +0.5×, Ultra +0.3×
//!
//! ## Canonical burn matrix
//!
//! Per CHN-2 + CHN-8 + Pillar 6/7 docs:
//!
//! | Source | Burn rate |
//! |--------|-----------|
//! | Gas fees | 100% |
//! | Seeds Hosting payments | 20% |
//! | Marketplace 9% Treasury share | 50% (2% del 9%) |
//! | ONS registration | 50% |
//! | OIT Oracle | 30% |
//! | Escrow commissions | 20% |
//! | Resonance slashing | 25% |
//! | Ticket commissions | 30% |
//! | Reputation stake slashing | 50% |
//! | Ethos net revenue buy-back | 5% |
#![deny(missing_docs)]
/// Placeholder marker for the tokenomics module. Replaced by concrete pallet in Phase 1.3.
#[derive(Debug, Clone, Copy, Default)]
pub struct TokenomicsConfig;
#[cfg(test)]
mod tests {
use super::*;
use origin_chain_types::fees::compute_fee;
use origin_chain_types::{AssetQualityTier, HardwareTier, Ogc};
// Sanity test: the trait crate's compute_fee returns canonical values.
// This proves the workspace dependency wires up correctly.
#[test]
fn cross_crate_fee_calculation_canonical() {
let fee = compute_fee(
Ogc::from_whole(10),
HardwareTier::EmbeddedArm,
AssetQualityTier::Standard,
);
assert_eq!(fee.as_base_units(), Ogc::from_whole(1).as_base_units());
}
#[test]
fn stub_crate_compiles() {
let _cfg = TokenomicsConfig;
}
}