Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions Cargo.lock

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

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,7 @@ members = [
"substrate/frame/tx-pause",
"substrate/frame/uniques",
"substrate/frame/utility",
"substrate/frame/vaults",
"substrate/frame/verify-signature",
"substrate/frame/vesting",
"substrate/frame/whitelist",
Expand Down Expand Up @@ -517,6 +518,7 @@ members = [
"substrate/primitives/npos-elections/fuzzer",
"substrate/primitives/offchain",
"substrate/primitives/panic-handler",
"substrate/primitives/pusd",
"substrate/primitives/rpc",
"substrate/primitives/runtime",
"substrate/primitives/runtime-interface",
Expand Down Expand Up @@ -1079,6 +1081,7 @@ pallet-treasury = { path = "substrate/frame/treasury", default-features = false
pallet-tx-pause = { default-features = false, path = "substrate/frame/tx-pause" }
pallet-uniques = { path = "substrate/frame/uniques", default-features = false }
pallet-utility = { path = "substrate/frame/utility", default-features = false }
pallet-vaults = { path = "substrate/frame/vaults", default-features = false }
pallet-verify-signature = { path = "substrate/frame/verify-signature", default-features = false }
pallet-vesting = { path = "substrate/frame/vesting", default-features = false }
pallet-whitelist = { path = "substrate/frame/whitelist", default-features = false }
Expand Down Expand Up @@ -1356,6 +1359,7 @@ sp-mmr-primitives = { path = "substrate/primitives/merkle-mountain-range", defau
sp-npos-elections = { path = "substrate/primitives/npos-elections", default-features = false }
sp-offchain = { path = "substrate/primitives/offchain", default-features = false }
sp-panic-handler = { path = "substrate/primitives/panic-handler", default-features = false }
sp-pusd = { path = "substrate/primitives/pusd", default-features = false }
sp-rpc = { path = "substrate/primitives/rpc", default-features = false }
sp-runtime = { path = "substrate/primitives/runtime", default-features = false }
sp-runtime-interface = { path = "substrate/primitives/runtime-interface", default-features = false }
Expand Down
135 changes: 134 additions & 1 deletion substrate/bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use pallet_treasury::ArgumentsFactory as PalletTreasuryArgumentsFactory;
#[cfg(feature = "runtime-benchmarks")]
use polkadot_sdk::sp_core::crypto::FromEntropy;

use polkadot_sdk::*;
use polkadot_sdk::{cumulus_primitives_core::Location, *};

use alloc::{vec, vec::Vec};
use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
Expand Down Expand Up @@ -2865,6 +2865,9 @@ mod runtime {
#[runtime::pallet_index(85)]
pub type Oracle = pallet_oracle::Pallet<Runtime>;

#[runtime::pallet_index(86)]
pub type Vaults = pallet_vaults::Pallet<Runtime>;

#[runtime::pallet_index(89)]
pub type MetaTx = pallet_meta_tx::Pallet<Runtime>;

Expand Down Expand Up @@ -3040,6 +3043,136 @@ impl pallet_oracle::Config for Runtime {
type BenchmarkHelper = OracleBenchmarkingHelper;
}

parameter_types! {
/// The pUSD stablecoin asset ID.
pub const VaultsStablecoinAssetId: u32 = 1;
/// Minimum collateral deposit to create a vault (1 DOT = 10^10 planck).
pub const VaultsMinimumDeposit: Balance = 10_000_000_000;
/// Minimum mint amount: 5 pUSD (pUSD has 6 decimals, so 5_000_000).
pub const VaultsMinimumMint: Balance = 5_000_000;
/// Duration (milliseconds) before a vault is considered stale for on_idle fee accrual.
/// 4 hours = 4 × 60 × 60 × 1000 = 14,400,000 ms
pub const VaultsStaleThreshold: u64 = 14_400_000;
/// Maximum age (milliseconds) of oracle price before operations are paused.
/// 1 hour = 60 × 60 × 1000 = 3,600,000 ms
pub const VaultsOracleStalenessThreshold: u64 = 3_600_000;
/// DOT collateral location.
pub VaultsCollateralLocation: Location = Location::here();
}

/// Insurance fund account that receives protocol revenue (interest and penalties).
pub struct InsuranceFundAccount;
impl frame_support::traits::Get<AccountId> for InsuranceFundAccount {
fn get() -> AccountId {
// Use a deterministic insurance fund account
sp_runtime::traits::AccountIdConversion::<AccountId>::into_account_truncating(
&frame_support::PalletId(*b"py/insur"),
)
}
}

/// Mock oracle adapter that provides a fixed price for noe.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

noe = now?

///
/// This adapter implements `ProvidePrice` with a hardcoded DOT price.
/// For production, replace this with a real oracle integration.
///
/// **Price format (normalized):** smallest_pUSD_units / smallest_collateral_unit
///
/// Example calculation for DOT at $4.21:
/// - DOT has 10 decimals, pUSD has 6 decimals
/// - 1 DOT = 4.21 pUSD
/// - Normalized price = 4.21 × 10^6 / 10^10 = 0.000421
/// - As FixedU128 (18 decimals): 0.000421 × 10^18 = 421_000_000_000_000
pub struct MockOracleAdapter;
impl pallet_vaults::ProvidePrice for MockOracleAdapter {
type Price = FixedU128;
type Moment = u64;

fn get_price(asset: &Location) -> Option<(Self::Price, Self::Moment)> {
// Only support DOT (native asset) for now
if *asset != Location::here() {
return None;
}

// Fixed DOT price: $4.21 normalized for DOT (10 decimals) and pUSD (6 decimals)
// Price = 4.21 * 10^6 / 10^10 = 0.000421
// As FixedU128: 421_000_000_000_000 (0.000421 * 10^18)
let price = FixedU128::from_inner(421_000_000_000_000);

// Use current timestamp from the Timestamp pallet
let now = <Timestamp as frame_support::traits::Time>::now();

Some((price, now))
}
}

/// Stub implementation for the Auctions handler.
///
/// This is a placeholder until a proper Auctions pallet is implemented.
/// Currently, liquidations will fail with `Unimplemented` error.
///
/// TODO: Replace with actual pallet_auctions integration when available.
pub struct AuctionAdapter;
impl pallet_vaults::AuctionsHandler<AccountId, Balance> for AuctionAdapter {
fn start_auction(
_vault_owner: &AccountId,
_collateral_amount: Balance,
_principal: Balance,
_accrued_interest: Balance,
_penalty: Balance,
_keeper: &AccountId,
) -> Result<u32, frame_support::pallet_prelude::DispatchError> {
// TODO: Implement actual auction logic when pallet_auctions is available
// For now, liquidations are disabled
Err(frame_support::pallet_prelude::DispatchError::Other("Auctions not yet implemented"))
}
}

/// EnsureOrigin implementation for vaults management that supports privilege levels.
///
/// - Root origin → `VaultsManagerLevel::Full` (can modify all parameters)
///
/// TODO: In the future, this can be extended to support Emergency privilege level
/// via a (new) specific governance origin that can only lower the debt ceiling.
pub struct EnsureVaultsManager;
impl frame_support::traits::EnsureOrigin<RuntimeOrigin> for EnsureVaultsManager {
type Success = pallet_vaults::VaultsManagerLevel;

fn try_origin(o: RuntimeOrigin) -> Result<Self::Success, RuntimeOrigin> {
use frame_system::RawOrigin;

match o.clone().into() {
Ok(RawOrigin::Root) => Ok(pallet_vaults::VaultsManagerLevel::Full),
_ => Err(o),
}
}

#[cfg(feature = "runtime-benchmarks")]
fn try_successful_origin() -> Result<RuntimeOrigin, ()> {
Ok(RuntimeOrigin::root())
}
}

/// Configure the Vaults pallet.
impl pallet_vaults::Config for Runtime {
type Currency = Balances;
type RuntimeHoldReason = RuntimeHoldReason;
type Asset = Assets;
type AssetId = u32;
type StablecoinAssetId = VaultsStablecoinAssetId;
type InsuranceFund = InsuranceFundAccount;
type MinimumDeposit = VaultsMinimumDeposit;
type MinimumMint = VaultsMinimumMint;
type TimeProvider = Timestamp;
type ManagerOrigin = EnsureVaultsManager;
type StaleVaultThreshold = VaultsStaleThreshold;
type OracleStalenessThreshold = VaultsOracleStalenessThreshold;
type Oracle = MockOracleAdapter;
type CollateralLocation = VaultsCollateralLocation;
type AuctionsHandler = AuctionAdapter;
type WeightInfo = pallet_vaults::weights::SubstrateWeight<Runtime>;
}

/// MMR helper types.
mod mmr {
use super::*;
Expand Down
65 changes: 65 additions & 0 deletions substrate/frame/vaults/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
[package]
name = "pallet-vaults"
version = "0.1.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "FRAME pallet for Vaults."
readme = "README.md"
include = ["README.md", "src/**/*"]

[lints]
workspace = true

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

[dependencies]
codec = { features = ["derive"], workspace = true }
frame-benchmarking = { workspace = true, optional = true }
frame-support = { workspace = true }
frame-system = { workspace = true }
log = { workspace = true }
scale-info = { features = ["derive"], workspace = true }
sp-pusd = { workspace = true }
sp-runtime = { workspace = true }
xcm = { workspace = true }

[dev-dependencies]
pallet-assets = { workspace = true, default-features = true }
pallet-balances = { workspace = true, default-features = true }
sp-io = { workspace = true, default-features = true }

[features]
default = ["std"]
std = [
"codec/std",
"frame-benchmarking?/std",
"frame-support/std",
"frame-system/std",
"log/std",
"scale-info/std",
"sp-pusd/std",
"sp-runtime/std",
"xcm/std",
]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"pallet-assets/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
"sp-pusd/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
"xcm/runtime-benchmarks",
]
try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
"pallet-assets/try-runtime",
"pallet-balances/try-runtime",
"sp-pusd/try-runtime",
"sp-runtime/try-runtime",
]
Loading
Loading