1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
//! Type definitions for the IP pallet.
//!
//! This module contains all core types used in the IP pallet, including NFTs,
//! offers, contracts, and payment structures.
use crate::pallet::Config;
use frame_support::{pallet_prelude::*, traits::Currency};
use frame_system::pallet_prelude::*;
use sp_std::prelude::*;
pub type BalanceOf<T> =
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
/// Represents an offer for either licensing or purchasing an NFT
///
/// This is the initial offer structure created by NFT owners before
/// a contract is formed. See [`Contract`] for the activated version.
#[derive(Clone, Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen, Debug)]
#[scale_info(skip_type_params(T))]
pub enum Offer<T: Config> {
/// A license offer for temporary IP rights
License(LicenseOffer<T>),
/// A purchase offer for permanent IP transfer
Purchase(PurchaseOffer<T>),
}
/// Distinguishes between license and purchase contracts
///
/// Used for type-level distinction of contract variants.
#[derive(Clone, Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen, Debug)]
pub enum ContractType {
/// Temporary rights transfer through licensing
License,
/// Permanent rights transfer through purchase
Purchase,
}
/// An active contract created from an accepted offer
///
/// Created when a [`LicenseOffer`] or [`PurchaseOffer`] is accepted by a counterparty.
/// Contains all necessary information for managing the agreement including payment schedules.
#[derive(Clone, Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen, Debug)]
#[scale_info(skip_type_params(T))]
pub enum Contract<T: Config> {
/// An active license agreement
License(License<T>),
/// An active purchase agreement
Purchase(PurchaseContract<T>),
}
/// Defines payment terms for a contract
///
/// Supports both one-time payments and periodic payment schedules.
/// Used in both offers and active contracts.
#[derive(Clone, Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen)]
#[scale_info(skip_type_params(T))]
pub enum PaymentType<T: Config> {
/// Single upfront payment
///
/// # Parameters
/// * `BalanceOf<T>` - The full payment amount
OneTime(BalanceOf<T>),
/// Multiple payments over time
///
/// # Parameters
/// * `amount_per_payment` - Amount due for each installment
/// * `total_payments` - Number of installments required
/// * `frequency` - Blocks between payments
Periodic {
/// Amount to be paid in each installment
amount_per_payment: BalanceOf<T>,
/// Total number of payments to complete the contract
total_payments: T::Index,
/// Number of blocks between payments
frequency: BlockNumberFor<T>,
},
}
impl<T: Config> core::fmt::Debug for PaymentType<T>
where
BalanceOf<T>: core::fmt::Debug,
T::Index: core::fmt::Debug,
BlockNumberFor<T>: core::fmt::Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
PaymentType::OneTime(amount) => f.debug_tuple("OneTime").field(amount).finish(),
PaymentType::Periodic {
amount_per_payment,
total_payments,
frequency,
} => f
.debug_struct("Periodic")
.field("amount_per_payment", amount_per_payment)
.field("total_payments", total_payments)
.field("frequency", frequency)
.finish(),
}
}
}
/// Represents an intellectual property NFT
///
/// The core asset type of the pallet, representing registered intellectual property
/// with associated metadata and ownership information.
#[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
#[scale_info(skip_type_params(T))]
pub struct NFT<T: Config> {
/// Unique identifier for this NFT
pub id: T::NFTId,
/// Current owner's account
pub owner: T::AccountId,
/// Name of the intellectual property
///
/// Limited by [`Config::MaxNameLength`]
pub name: BoundedVec<u8, T::MaxNameLength>,
/// Detailed description of the intellectual property
///
/// Limited by [`Config::MaxDescriptionLength`]
pub description: BoundedVec<u8, T::MaxDescriptionLength>,
/// Date when the IP was filed
///
/// Format should be consistent (e.g., "YYYY-MM-DD")
pub filing_date: BoundedVec<u8, T::MaxNameLength>,
/// Legal jurisdiction where the IP is registered
///
/// Country or region code (e.g., "US", "EU")
pub jurisdiction: BoundedVec<u8, T::MaxNameLength>,
}
/// An offer to license an NFT
///
/// Created by NFT owners to specify license terms before a contract is formed.
/// See [`License`] for the activated version after acceptance.
#[derive(Clone, Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen, Debug)]
#[scale_info(skip_type_params(T))]
pub struct LicenseOffer<T: Config> {
/// ID of the NFT being licensed
pub nft_id: T::NFTId,
/// Account offering the license (must be NFT owner)
pub licensor: T::AccountId,
/// Payment terms for the license
pub payment_type: PaymentType<T>,
/// Whether this is an exclusive license
pub is_exclusive: bool,
/// How long the license lasts (in blocks)
pub duration: BlockNumberFor<T>,
}
/// An offer to purchase an NFT
///
/// Created by NFT owners to specify sale terms before a contract is formed.
/// See [`PurchaseContract`] for the activated version after acceptance.
#[derive(Clone, Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen, Debug)]
#[scale_info(skip_type_params(T))]
pub struct PurchaseOffer<T: Config> {
/// ID of the NFT being sold
pub nft_id: T::NFTId,
/// Account selling the NFT (must be owner)
pub seller: T::AccountId,
/// Payment terms for the purchase
pub payment_type: PaymentType<T>,
}
/// An active license contract
///
/// Created when a [`LicenseOffer`] is accepted. Tracks the ongoing
/// license relationship including payments and duration.
#[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
#[scale_info(skip_type_params(T))]
pub struct License<T: Config> {
/// ID of the licensed NFT
pub nft_id: T::NFTId,
/// Account that owns and licensed the NFT
pub licensor: T::AccountId,
/// Account that accepted the license
pub licensee: T::AccountId,
/// How long the license lasts (in blocks)
pub duration: BlockNumberFor<T>,
/// When the license period began
pub start_block: BlockNumberFor<T>,
/// Payment terms for the license
pub payment_type: PaymentType<T>,
/// Payment tracking for periodic payments
pub payment_schedule: Option<PaymentSchedule<T>>,
/// Whether this is an exclusive license
pub is_exclusive: bool,
}
/// An active purchase contract
///
/// Created when a [`PurchaseOffer`] is accepted. Tracks the purchase
/// agreement including any ongoing payments.
#[derive(Clone, Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen, Debug)]
#[scale_info(skip_type_params(T))]
pub struct PurchaseContract<T: Config> {
/// ID of the NFT being purchased
pub nft_id: T::NFTId,
/// Account selling the NFT
pub seller: T::AccountId,
/// Account buying the NFT
pub buyer: T::AccountId,
/// Payment terms for the purchase
pub payment_type: PaymentType<T>,
/// Payment tracking for periodic payments
pub payment_schedule: Option<PaymentSchedule<T>>,
}
/// Tracks progress of periodic payments
///
/// Used by both license and purchase contracts to manage
/// installment payments and handle missed payments.
#[derive(Clone, Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen)]
#[scale_info(skip_type_params(T))]
pub struct PaymentSchedule<T: Config> {
/// When the payment schedule began
pub start_block: BlockNumberFor<T>,
/// When the next payment is due
pub next_payment_block: BlockNumberFor<T>,
/// Number of payments completed
pub payments_made: T::Index,
/// Number of payments remaining
pub payments_due: T::Index,
/// Track missed payments for penalties
pub missed_payments: Option<T::Index>,
/// Current penalty amount (if any)
pub penalty_amount: Option<BalanceOf<T>>,
/// Blocks between payments
pub frequency: BlockNumberFor<T>,
}
impl<T: Config> PaymentSchedule<T> {
pub fn increment(&mut self) {
self.next_payment_block = self.next_payment_block + self.frequency;
self.payments_made = self.payments_made + 1u32.into();
self.payments_due = self.payments_due - 1u32.into();
}
}
impl<T: Config> core::fmt::Debug for PaymentSchedule<T>
where
T::Index: core::fmt::Debug,
BlockNumberFor<T>: core::fmt::Debug,
BalanceOf<T>: core::fmt::Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PaymentSchedule")
.field("start_block", &self.start_block)
.field("next_payment_block", &self.next_payment_block)
.field("payments_made", &self.payments_made)
.field("payments_due", &self.payments_due)
.field("missed_payments", &self.missed_payments)
.field("penalty_amount", &self.penalty_amount)
.field("frequency", &self.frequency)
.finish()
}
}
impl<T: Config> LicenseOffer<T> {
pub fn init(self, licensee: T::AccountId) -> License<T> {
let start_block = frame_system::Pallet::<T>::block_number();
// Create payment schedule based on payment type
let payment_schedule = match &self.payment_type {
PaymentType::Periodic {
amount_per_payment: _,
total_payments,
frequency,
} => Some(PaymentSchedule {
start_block,
next_payment_block: start_block,
payments_made: T::Index::default(),
payments_due: *total_payments,
missed_payments: None,
penalty_amount: None,
frequency: *frequency,
}),
PaymentType::OneTime(_) => None,
};
License {
nft_id: self.nft_id,
licensor: self.licensor,
licensee,
duration: self.duration,
start_block,
payment_type: self.payment_type,
payment_schedule,
is_exclusive: self.is_exclusive,
}
}
}
impl<T: Config> PurchaseOffer<T> {
pub fn init(self, buyer: T::AccountId) -> PurchaseContract<T> {
let start_block = frame_system::Pallet::<T>::block_number();
// Create payment schedule based on payment type
let payment_schedule = match &self.payment_type {
PaymentType::Periodic {
amount_per_payment: _,
total_payments,
frequency,
} => Some(PaymentSchedule {
start_block,
next_payment_block: start_block,
payments_made: T::Index::default(),
payments_due: *total_payments,
missed_payments: None,
penalty_amount: None,
frequency: *frequency,
}),
PaymentType::OneTime(_) => None,
};
PurchaseContract {
nft_id: self.nft_id,
seller: self.seller,
buyer,
payment_type: self.payment_type,
payment_schedule,
}
}
}