miracle-api 0.6.0

Miracle is a pay2e protocol for sovereign individuals living in Mirascape Horizon.
Documentation
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
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
use const_crypto::ed25519;
use solana_program::{pubkey, pubkey::Pubkey};

use crate::calculations::{
    calculate_customer_reward, calculate_merchant_reward, calculate_smooth_community_decay,
    calculate_time_decay,
};

// Project Start Time -- Devnet
// Date and time (UTC): Monday, August 15, 2025 12:00:00 AM UTC
// This will be finalized once go-live date is confirmed
pub const START_AT: i64 = 1755216000;

// // Project Start Time -- Mainnet
// // Date and time (UTC): Monday, September 1, 2025 12:00:00 AM UTC
// // This will be finalized once go-live date is confirmed
// pub const START_AT: i64 = 1756684800;

/// Project End Time - The unix timestamp after which mining will stop.
// Date and time (GMT): Wednesday, September 1, 2075 12:00:00 AM UTC
// 50 years from start date for long-term sustainability
pub const END_AT: i64 = 3334521600;

// Test initializer (mac::id.json)
// pub const INITIALIZER_ADDRESS: Pubkey = pubkey!("Ac2ev4ofDx61tuSJCgq9ToSBfVwHD2a1FVJf6p7TAqiB");
// Test initializer (mac&hp::test-id.json)
pub const INITIALIZER_ADDRESS: Pubkey = pubkey!("4ALL9EAVHpv7ioJF95ktDLtrHUEJezPuFxTFFMy3fpSy");

/// The authority allowed to initialize the program.
// pub const INITIALIZER_ADDRESS: Pubkey = pubkey!("staryJacbXodPh4WfwVtgA5jkJhvsMHERtkdttnLEHc");

/// The decimal precision of the Miracle token.
/// There are 10^6 indivisible units per Miracle (called "glows").
pub const TOKEN_DECIMALS: u8 = 6;

/// One Miracle token, denominated in indivisible units.
pub const ONE_MIRACLE: u64 = 10u64.pow(TOKEN_DECIMALS as u32);

/// Ten MIRACLE token
pub const TEN_MIRACLE: u64 = ONE_MIRACLE * 10;

/// The maximum token supply (300 million).
pub const MAX_SUPPLY: u64 = ONE_MIRACLE * 300_000_000;

/// Pool limits for reward distribution (based on MAX_SUPPLY).
/// These ensure proper allocation and prevent over-minting.
pub const PAYMENT_POOL_LIMIT: u64 = MAX_SUPPLY.saturating_mul(67).saturating_div(100); // 201M (67%)
pub const SOCIAL_POOL_LIMIT: u64 = MAX_SUPPLY.saturating_mul(3).saturating_div(100); // 9M (3%)

/// The base daily rewards amount for the payment-based system (before community decay).
/// This is the maximum daily rewards when community health is struggling.
pub const BASE_DAILY_REWARDS: u64 = ONE_MIRACLE * 120_000; // 120k MIRACLE per day

/// The seed of the config account PDA.
pub const CONFIG: &[u8] = b"config";

/// The seed of the metadata account PDA.
pub const METADATA: &[u8] = b"metadata";

/// The seed of the mint account PDA.
pub const MINT: &[u8] = b"mint";

/// The seed of proof account PDAs.
pub const PROOF: &[u8] = b"proof";

/// The seed of the treasury account PDA.
pub const TREASURY: &[u8] = b"treasury";

/// Noise for deriving the mint pda.
// mint noise: Signer-Payer, big-endian: [83, 105, 103, 110, 101, 114, 45, 80, 97, 121, 101, 114]
// #1 MINT ADDRESS: MirakQE19pNhjP71wR7Lr4JoKhbCrqcy5nz9eZpSdqX
// nonce: 2932254387, big-endian: [174, 198, 166, 179]
// #2 MINT ADDRESS: miraBBC187B9GKzL56W1P9v5MiuSnprPwBpFQLQ4WF2
// Nonce: 308643398 (big-endian: [18, 101, 134, 70])
// `Signer-Payer` concat(+) nonce
/// Noise for deriving the mint pda.
pub const MINT_NOISE: [u8; 16] = [
    83, 105, 103, 110, 101, 114, 45, 80, 97, 121, 101, 114, 174, 198, 166, 179,
];

/// The name for token metadata.
pub const METADATA_NAME: &str = "Miracle";

/// The ticker symbol for token metadata.
pub const METADATA_SYMBOL: &str = "MIRACLE";

/// The uri for token metdata.
pub const METADATA_URI: &str =
    "https://github.com/miraland-labs/resources/blob/main/metadata/miracle.json";

/// Genesis hash for epoch 0 (hash of "MIRACLE_EPOCH_0_EXTREME_WAYS")
/// This is the foundation of the entire epoch hash chain
pub const fn miracle_epoch_0_genesis() -> [u8; 32] {
    // Use const_crypto for compile-time hash computation
    // This makes the genesis hash self-documenting and verifiable
    const_crypto::sha2::Sha256::new()
        .update(b"MIRACLE_EPOCH_0_EXTREME_WAYS")
        .finalize()
}

/// Program id for const pda derivations
const PROGRAM_ID: [u8; 32] = unsafe { *(&crate::id() as *const Pubkey as *const [u8; 32]) };

/// The address of the config account.
pub const CONFIG_ADDRESS: Pubkey =
    Pubkey::new_from_array(ed25519::derive_program_address(&[CONFIG], &PROGRAM_ID).0);

/// The address of the mint metadata account.
pub const METADATA_ADDRESS: Pubkey = Pubkey::new_from_array(
    ed25519::derive_program_address(
        &[
            METADATA,
            unsafe { &*(&mpl_token_metadata::ID as *const Pubkey as *const [u8; 32]) },
            unsafe { &*(&MINT_ADDRESS as *const Pubkey as *const [u8; 32]) },
        ],
        unsafe { &*(&mpl_token_metadata::ID as *const Pubkey as *const [u8; 32]) },
    )
    .0,
);

// MI
// MIRACLE MINT ADDRESS: MirakQE19pNhjP71wR7Lr4JoKhbCrqcy5nz9eZpSdqX
/// The address of the mint account.
pub const MINT_ADDRESS: Pubkey =
    Pubkey::new_from_array(ed25519::derive_program_address(&[MINT, &MINT_NOISE], &PROGRAM_ID).0);

/// The bump of the mint account.
pub const MINT_BUMP: u8 = ed25519::derive_program_address(&[MINT, &MINT_NOISE], &PROGRAM_ID).1;

/// The address of the treasury account.
pub const TREASURY_ADDRESS: Pubkey =
    Pubkey::new_from_array(ed25519::derive_program_address(&[TREASURY], &PROGRAM_ID).0);

/// The bump of the treasury account, for cpis.
pub const TREASURY_BUMP: u8 = ed25519::derive_program_address(&[TREASURY], &PROGRAM_ID).1;

/// The address of the treasury token account.
pub const TREASURY_TOKENS_ADDRESS: Pubkey = Pubkey::new_from_array(
    ed25519::derive_program_address(
        &[
            unsafe { &*(&TREASURY_ADDRESS as *const Pubkey as *const [u8; 32]) },
            unsafe { &*(&spl_token::id() as *const Pubkey as *const [u8; 32]) },
            unsafe { &*(&MINT_ADDRESS as *const Pubkey as *const [u8; 32]) },
        ],
        unsafe { &*(&spl_associated_token_account::id() as *const Pubkey as *const [u8; 32]) },
    )
    .0,
);

// ===== PAYMENT-BASED REWARDS SYSTEM CONSTANTS =====

/// Epoch 0 start date: Same as START_AT for consistency
pub const EPOCH_0_START: i64 = START_AT;

/// The duration of a daily epoch, in seconds (24 hours).
pub const EPOCH_DURATION: i64 = 24 * 60 * 60;

/// Maximum merkle proof length for payment proofs (supports ~33M participants).
/// This prevents DoS attacks through extremely long proofs.
pub const MAX_PAYMENT_PROOF_LENGTH: u8 = 25;

/// Maximum merkle proof length for seal proofs (supports ~1M epochs).
/// This prevents DoS attacks through extremely long proofs.
/// Each epoch = 1 day, so this supports ~2,700 years of history.
pub const MAX_SEAL_PROOF_LENGTH: u8 = 20;

/// The seed of the snapshot account PDA.
pub const SNAPSHOT: &[u8] = b"snapshot";

/// The seed of the community metrics account PDA.
pub const METRICS: &[u8] = b"metrics";

/// Basis points for percentage calculations (1 basis point = 0.01%).
pub const BASIS_POINTS: u64 = 10_000;

/// The address of the snapshot account.
pub const SNAPSHOT_ADDRESS: Pubkey =
    Pubkey::new_from_array(ed25519::derive_program_address(&[SNAPSHOT], &PROGRAM_ID).0);

/// The bump of the snapshot account.
pub const SNAPSHOT_BUMP: u8 = ed25519::derive_program_address(&[SNAPSHOT], &PROGRAM_ID).1;

/// The address of the community metrics account.
pub const METRICS_ADDRESS: Pubkey =
    Pubkey::new_from_array(ed25519::derive_program_address(&[METRICS], &PROGRAM_ID).0);

/// The bump of the metrics account.
pub const METRICS_BUMP: u8 = ed25519::derive_program_address(&[METRICS], &PROGRAM_ID).1;

/// Common stablecoin mint addresses.
pub const USDT_MINT: Pubkey = pubkey!("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB");
pub const USDC_MINT: Pubkey = pubkey!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
pub const WSOL_MINT: Pubkey = pubkey!("So11111111111111111111111111111111111111112");

// /// The address of the CU-optimized Solana noop program.
// pub const NOOP_PROGRAM_ID: Pubkey = pubkey!("noop8ytexvkpCuqbf6FB89BSuNemHtPRqaNC31GWivW");

// ===== COMMUNITY-DRIVEN DECAY SYSTEM CONSTANTS =====

/// Community Health Targets (now configurable via UpdateTargets instruction)
/// These constants have been moved to Config.community_targets
/// Launch defaults: target_weekly_users = 500, target_weekly_activity = 5_000 (2,500 payments × 2), target_retention_rate = 5000

/// Calculate daily rewards with smooth community decay and time decay.
///
/// ## Formula
/// daily_rewards = BASE_DAILY_REWARDS * community_decay * time_decay / 100_000_000
///
/// ## Parameters
/// - `community_score`: Community health score (0-10000 basis points)
/// - `years_since_launch`: Number of years since project launch (for time decay)
/// - Used here for time decay (reduces rewards over time)
///
/// ## Returns
/// - Daily rewards amount in smallest units
/// - Smooth transition from 100% to 60% of base rewards (community decay)
/// - Time decay reduces rewards over 5 years to 10% minimum
///
/// ## Benefits
/// - Replaces binary threshold with smooth function
/// - Eliminates gaming incentives at 80% threshold
/// - Provides fair rewards for all community health levels
/// - Implements time-based decay for sustainable tokenomics
pub fn calculate_daily_rewards(community_score: u16, years_since_launch: u32) -> u64 {
    let community_decay = calculate_smooth_community_decay(community_score) as u128;
    let time_decay = calculate_time_decay(years_since_launch) as u128;
    let base = BASE_DAILY_REWARDS as u128;

    let result = base
        .saturating_mul(community_decay)
        .saturating_mul(time_decay)
        .saturating_div(100_000_000u128);
    result as u64
}

/// Calculate daily rewards split between customers and merchants.
/// This function extends the base daily rewards calculation with customer/merchant allocation.
///
/// ## Formula
/// total_rewards = calculate_daily_rewards(community_score, years_since_launch)
/// customer_pool = total_rewards * customer_reward_share / 10000
/// merchant_pool = total_rewards * merchant_reward_share / 10000
///
/// ## Parameters
/// - `community_score`: Community health score (0-10000 basis points)
/// - `years_since_launch`: Number of years since project launch (for time decay)
/// - `customer_reward_share`: Customer reward percentage in basis points (0-10000)
/// - `merchant_reward_share`: Merchant reward percentage in basis points (0-10000)
///
/// - `years_since_launch`: Used here for time decay (reduces rewards over time)
///
/// ## Returns
/// - Tuple of (customer_pool, merchant_pool) in smallest token units
///
/// ## Benefits
/// - Fair allocation between customers and merchants
/// - Oracle-configurable split ratios
/// - Maintains total reward pool integrity
/// - Supports platform evolution with dynamic splits
pub fn calculate_daily_rewards_split(
    community_score: u16,
    years_since_launch: u32,
    customer_reward_share: u16,
    merchant_reward_share: u16,
) -> (u64, u64) {
    // Calculate total daily rewards
    let total_rewards = calculate_daily_rewards(community_score, years_since_launch);

    // Calculate customer pool
    let customer_pool = (total_rewards as u128)
        .saturating_mul(customer_reward_share as u128)
        .saturating_div(BASIS_POINTS as u128) as u64;

    // Calculate merchant pool
    let merchant_pool = (total_rewards as u128)
        .saturating_mul(merchant_reward_share as u128)
        .saturating_div(BASIS_POINTS as u128) as u64;

    (customer_pool, merchant_pool)
}

/// Calculate time decay factor based on years since launch.
/// This is used for time decay calculations.
///
/// ## Formula
/// time_decay = max(1000, 10000 - years_since_launch * 1800)
///
/// ## Purpose
/// Reduces total rewards over time to ensure sustainable tokenomics.
/// - 0 years: 100% rewards
/// - 5 years: 10% rewards (minimum)
///
/// - `years_since_launch`: Used here for time decay (reduces rewards over time)

/// Complete transparent calculation chain for customer rewards.
/// This function combines all steps of the reward calculation for transparency.
///
/// ## Formula Chain
/// 1. daily_rewards = calculate_daily_rewards(community_score, years_since_launch)
/// 2. customer_pool = daily_rewards * customer_reward_share / 10000
/// 3. customer_reward = (activity_count * customer_pool) / total_customer_activity
///
/// ## Parameters
/// - `activity_count`: Number of activities performed by the customer
/// - `community_score`: Community health score (0-10000 basis points)
/// - `years_since_launch`: Number of years since project launch (for time decay)
/// - `customer_reward_share`: Customer reward percentage in basis points (0-10000)
/// - `total_customer_activity`: Total activity count across all customers
///
/// ## Returns
/// - Individual customer reward amount in smallest token units
///
/// ## Benefits
/// - Complete transparency from community metrics to individual rewards
/// - Can be used off-chain for reward estimation
/// - Deterministic calculation for verification
/// - Public formula for user understanding
///
/// ## Note
/// This function does NOT apply activity capping. Use `calculate_customer_reward_transparent_with_cap`
/// for calculations that match on-chain behavior.
pub fn calculate_customer_reward_transparent(
    activity_count: u32,
    community_score: u16,
    years_since_launch: u32,
    customer_reward_share: u16,
    total_customer_activity: u32,
) -> u64 {
    // Step 1: Calculate daily rewards
    let daily_rewards = calculate_daily_rewards(community_score, years_since_launch);

    // Step 2: Calculate customer pool
    let customer_pool = (daily_rewards as u128)
        .saturating_mul(customer_reward_share as u128)
        .saturating_div(BASIS_POINTS as u128) as u64;

    // Step 3: Calculate individual reward
    calculate_customer_reward(activity_count, customer_pool, total_customer_activity)
}

/// Complete transparent calculation chain for customer rewards WITH activity capping.
/// This function matches the on-chain calculation behavior exactly.
///
/// ## Formula Chain
/// 1. daily_rewards = calculate_daily_rewards(community_score, years_since_launch)
/// 2. customer_pool = daily_rewards * customer_reward_share / 10000
/// 3. capped_activity = min(activity_count, max_customer_activity_per_epoch)
/// 4. customer_reward = (capped_activity * customer_pool) / total_customer_activity
///
/// ## Parameters
/// - `activity_count`: Number of activities performed by the customer
/// - `community_score`: Community health score (0-10000 basis points)
/// - `years_since_launch`: Number of years since project launch (for time decay)
/// - `customer_reward_share`: Customer reward percentage in basis points (0-10000)
/// - `total_customer_activity`: Total activity count across all customers
/// - `max_customer_activity_per_epoch`: Maximum allowed customer activity per epoch
///
/// ## Returns
/// - Individual customer reward amount in smallest token units (with activity capping)
///
/// ## Benefits
/// - Matches on-chain calculation exactly
/// - Includes activity capping to prevent gaming
/// - Complete transparency for off-chain estimation
/// - Deterministic calculation for verification
pub fn calculate_customer_reward_transparent_with_cap(
    activity_count: u32,
    community_score: u16,
    years_since_launch: u32,
    customer_reward_share: u16,
    total_customer_activity: u32,
    max_customer_activity_per_epoch: u32,
) -> u64 {
    // Step 1: Calculate daily rewards
    let daily_rewards = calculate_daily_rewards(community_score, years_since_launch);

    // Step 2: Calculate customer pool
    let customer_pool = (daily_rewards as u128)
        .saturating_mul(customer_reward_share as u128)
        .saturating_div(BASIS_POINTS as u128) as u64;

    // Step 3: Apply activity capping
    let capped_activity = activity_count.min(max_customer_activity_per_epoch);

    // Step 4: Calculate individual reward with capped activity
    calculate_customer_reward(capped_activity, customer_pool, total_customer_activity)
}

/// Complete transparent calculation chain for merchant rewards.
/// This function combines all steps of the reward calculation for transparency.
///
/// ## Formula Chain
/// 1. daily_rewards = calculate_daily_rewards(community_score, years_since_launch)
/// 2. merchant_pool = daily_rewards * merchant_reward_share / 10000
/// 3. merchant_reward = (activity_count * merchant_pool) / total_merchant_activity
///
/// ## Parameters
/// - `activity_count`: Number of activities performed by the merchant
/// - `community_score`: Community health score (0-10000 basis points)
/// - `years_since_launch`: Number of years since project launch (for time decay)
/// - `merchant_reward_share`: Merchant reward percentage in basis points (0-10000)
/// - `total_merchant_activity`: Total activity count across all merchants
///
/// ## Returns
/// - Individual merchant reward amount in smallest token units
///
/// ## Benefits
/// - Complete transparency from community metrics to individual rewards
/// - Can be used off-chain for reward estimation
/// - Deterministic calculation for verification
/// - Public formula for user understanding
///
/// ## Note
/// This function does NOT apply activity capping. Use `calculate_merchant_reward_transparent_with_cap`
/// for calculations that match on-chain behavior.
pub fn calculate_merchant_reward_transparent(
    activity_count: u32,
    community_score: u16,
    years_since_launch: u32,
    merchant_reward_share: u16,
    total_merchant_activity: u32,
) -> u64 {
    // Step 1: Calculate daily rewards
    let daily_rewards = calculate_daily_rewards(community_score, years_since_launch);

    // Step 2: Calculate merchant pool
    let merchant_pool = (daily_rewards as u128)
        .saturating_mul(merchant_reward_share as u128)
        .saturating_div(BASIS_POINTS as u128) as u64;

    // Step 3: Calculate individual reward
    calculate_merchant_reward(activity_count, merchant_pool, total_merchant_activity)
}

/// Complete transparent calculation chain for merchant rewards WITH activity capping.
/// This function matches the on-chain calculation behavior exactly.
///
/// ## Formula Chain
/// 1. daily_rewards = calculate_daily_rewards(community_score, years_since_launch)
/// 2. merchant_pool = daily_rewards * merchant_reward_share / 10000
/// 3. capped_activity = min(activity_count, max_merchant_activity_per_epoch)
/// 4. merchant_reward = (capped_activity * merchant_pool) / total_merchant_activity
///
/// ## Parameters
/// - `activity_count`: Number of activities performed by the merchant
/// - `community_score`: Community health score (0-10000 basis points)
/// - `years_since_launch`: Number of years since project launch (for time decay)
/// - `merchant_reward_share`: Merchant reward percentage in basis points (0-10000)
/// - `total_merchant_activity`: Total activity count across all merchants
/// - `max_merchant_activity_per_epoch`: Maximum allowed merchant activity per epoch
///
/// ## Returns
/// - Individual merchant reward amount in smallest token units (with activity capping)
///
/// ## Benefits
/// - Matches on-chain calculation exactly
/// - Includes activity capping to prevent gaming
/// - Complete transparency for off-chain estimation
/// - Deterministic calculation for verification
pub fn calculate_merchant_reward_transparent_with_cap(
    activity_count: u32,
    community_score: u16,
    years_since_launch: u32,
    merchant_reward_share: u16,
    total_merchant_activity: u32,
    max_merchant_activity_per_epoch: u32,
) -> u64 {
    // Step 1: Calculate daily rewards
    let daily_rewards = calculate_daily_rewards(community_score, years_since_launch);

    // Step 2: Calculate merchant pool
    let merchant_pool = (daily_rewards as u128)
        .saturating_mul(merchant_reward_share as u128)
        .saturating_div(BASIS_POINTS as u128) as u64;

    // Step 3: Apply activity capping
    let capped_activity = activity_count.min(max_merchant_activity_per_epoch);

    // Step 4: Calculate individual reward with capped activity
    calculate_merchant_reward(capped_activity, merchant_pool, total_merchant_activity)
}