DailyParticipantData

Struct DailyParticipantData 

Source
#[repr(C)]
pub struct DailyParticipantData { pub participant_id: [u8; 32], pub first_payment_timestamp: i64, pub last_payment_timestamp: i64, pub first_payment_tx_sig_short: [u8; 8], pub last_payment_tx_sig_short: [u8; 8], pub payment_count: u32, pub participant_type: u8, pub _padding: [u8; 3], }
Expand description

DailyParticipantData represents aggregated payment data for a single participant in a daily epoch.

This structure is used off-chain by the oracle to build Merkle trees for daily payment snapshots. It aggregates all payments for a single participant (customer or merchant) within a 24-hour epoch.

§Key Features

  • Aggregation: Combines multiple payments per participant per day
  • Fair Rewards: Uses transaction count and total amount for reward calculation
  • Efficient Storage: Off-chain only, reduces on-chain storage costs
  • Flexible: Supports both customer and merchant participants
  • Simple Design: Only necessary fields included (Simple is Best)
  • Transaction Verification: Includes short transaction signatures for on-chain verification

§Data Structure

  • participant_id: Unique identifier for the participant (customer/merchant)
  • first_payment_timestamp: Timestamp of first payment in the day
  • last_payment_timestamp: Timestamp of last payment in the day
  • first_payment_tx_sig_short: First 8 characters of first payment transaction signature
  • last_payment_tx_sig_short: First 8 characters of last payment transaction signature
  • first_payment_tx_sig_short: First 8 characters of first payment transaction signature
  • last_payment_tx_sig_short: First 8 characters of last payment transaction signature
  • payment_count: Number of transactions for the day
  • participant_type: Whether this is a customer (0) or merchant (1)

§Merkle Leaf Construction

The Merkle leaf hash is computed as:

use miracle_api::prelude::DailyParticipantData;

let participant_data = DailyParticipantData::new(
    [0u8; 32],           // participant_id
    1723680000,         // first_payment_timestamp
    1723766400,         // last_payment_timestamp
    [0u8; 8],           // first_payment_tx_sig_short
    [0u8; 8],           // last_payment_tx_sig_short
    5,                  // payment_count
    0,                   // participant_type (customer)
);
let leaf_hash = participant_data.compute_leaf_hash();

§Security Features

  • Timestamp Range: Provides audit trail and prevents stale data
  • Participant Type: Distinguishes between customers and merchants for fair allocation
  • Aggregation: Reduces Merkle tree size while maintaining fairness
  • Uniqueness: participant_id + daily timestamps provide natural uniqueness
  • Deployment Isolation: Handled by program ID, not data structure
  • Transaction Verification: Short signatures enable on-chain payment verification
  • Transaction Verification: Short signatures enable on-chain verification of payment authenticity

Fields§

§participant_id: [u8; 32]

Unique identifier for the participant (customer or merchant). This should be a deterministic identifier that can be reproduced by both the oracle and the claiming user.

§first_payment_timestamp: i64

Timestamp of the first payment in the daily epoch (Unix timestamp). Used for audit trail and to prevent stale data replay.

§last_payment_timestamp: i64

Timestamp of the last payment in the daily epoch (Unix timestamp). Used for audit trail and to ensure data freshness.

§first_payment_tx_sig_short: [u8; 8]

First 8 characters of the first payment transaction signature. Used for on-chain verification of payment authenticity. Provides cost-effective verification without storing full signatures.

§last_payment_tx_sig_short: [u8; 8]

First 8 characters of the last payment transaction signature. Used for on-chain verification of payment authenticity. Provides cost-effective verification without storing full signatures.

§payment_count: u32

Number of transactions for this participant in the daily epoch. Used for activity-based reward calculation and community metrics.

§participant_type: u8

Participant type: 0 for customer, 1 for merchant. Used to determine reward allocation between customer and merchant pools.

§_padding: [u8; 3]

Padding for future extensibility.

Implementations§

Source§

impl DailyParticipantData

Source

pub fn new( participant_id: [u8; 32], first_payment_timestamp: i64, last_payment_timestamp: i64, first_payment_tx_sig_short: [u8; 8], last_payment_tx_sig_short: [u8; 8], payment_count: u32, participant_type: u8, ) -> Self

Create a new DailyParticipantData instance.

§Parameters
  • participant_id: Unique identifier for the participant
  • first_payment_timestamp: Timestamp of first payment
  • last_payment_timestamp: Timestamp of last payment
  • first_payment_tx_sig_short: First 8 characters of first payment transaction signature
  • last_payment_tx_sig_short: First 8 characters of last payment transaction signature
  • payment_count: Number of transactions
  • participant_type: 0 for customer, 1 for merchant
§Returns
  • New DailyParticipantData instance
Examples found in repository?
examples/claim_instruction_example.rs (lines 130-138)
14fn main() -> Result<(), Box<dyn std::error::Error>> {
15    println!("🔍 Miracle API Utils Example: Building Claim Instructions");
16    println!("========================================================");
17
18    // ===== SCENARIO 1: Converting Database Hex Strings =====
19    println!("\n📊 Scenario 1: Converting Database Hex Strings");
20    println!("------------------------------------------------");
21
22    // Simulate database query output (hex strings from SQL function)
23    let db_customer_reward_pool = "0x1234567890abcdef";
24    let db_merchant_reward_pool = "0xfedcba0987654321";
25    let db_total_customer_activity = "0x0000000000000064"; // 100 in decimal
26    let db_total_merchant_activity = "0x00000000000000c8"; // 200 in decimal
27
28    println!("Database hex strings:");
29    println!("  customer_reward_pool: {}", db_customer_reward_pool);
30    println!("  merchant_reward_pool: {}", db_merchant_reward_pool);
31    println!("  total_customer_activity: {}", db_total_customer_activity);
32    println!("  total_merchant_activity: {}", db_total_merchant_activity);
33
34    // Convert hex strings to EpochClaimData using utils
35    let epoch_data = claim::epoch_claim_data_from_hex(
36        db_customer_reward_pool,
37        db_merchant_reward_pool,
38        db_total_customer_activity,
39        db_total_merchant_activity,
40    )?;
41
42    println!("\n✅ Converted to EpochClaimData:");
43    println!(
44        "  customer_reward_pool bytes: {:?}",
45        epoch_data.customer_reward_pool
46    );
47    println!(
48        "  merchant_reward_pool bytes: {:?}",
49        epoch_data.merchant_reward_pool
50    );
51    println!(
52        "  total_customer_activity bytes: {:?}",
53        epoch_data.total_customer_activity
54    );
55    println!(
56        "  total_merchant_activity bytes: {:?}",
57        epoch_data.total_merchant_activity
58    );
59
60    // ===== SCENARIO 2: Converting Native u64 Values =====
61    println!("\n📊 Scenario 2: Converting Native u64 Values");
62    println!("---------------------------------------------");
63
64    let native_customer_reward_pool = 1234567890u64;
65    let native_merchant_reward_pool = 9876543210u64;
66    let native_total_customer_activity = 100u64;
67    let native_total_merchant_activity = 200u64;
68
69    println!("Native u64 values:");
70    println!("  customer_reward_pool: {}", native_customer_reward_pool);
71    println!("  merchant_reward_pool: {}", native_merchant_reward_pool);
72    println!(
73        "  total_customer_activity: {}",
74        native_total_customer_activity
75    );
76    println!(
77        "  total_merchant_activity: {}",
78        native_total_merchant_activity
79    );
80
81    // Convert u64 values to EpochClaimData using utils
82    let epoch_data_u64 = claim::epoch_claim_data_from_u64(
83        native_customer_reward_pool,
84        native_merchant_reward_pool,
85        native_total_customer_activity,
86        native_total_merchant_activity,
87    );
88
89    println!("\n✅ Converted to EpochClaimData:");
90    println!(
91        "  customer_reward_pool bytes: {:?}",
92        epoch_data_u64.customer_reward_pool
93    );
94    println!(
95        "  merchant_reward_pool bytes: {:?}",
96        epoch_data_u64.merchant_reward_pool
97    );
98    println!(
99        "  total_customer_activity bytes: {:?}",
100        epoch_data_u64.total_customer_activity
101    );
102    println!(
103        "  total_merchant_activity bytes: {:?}",
104        epoch_data_u64.total_merchant_activity
105    );
106
107    // ===== SCENARIO 3: Individual Hex Conversions =====
108    println!("\n📊 Scenario 3: Individual Hex Conversions");
109    println!("-------------------------------------------");
110
111    // Convert individual hex strings to byte arrays
112    let reward_pool_bytes = hex::hex_string_to_u64_bytes("0x1234567890abcdef")?;
113    let hash_bytes = hex::hex_string_to_hash_bytes(
114        "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
115    )?;
116
117    println!("Individual conversions:");
118    println!("  reward_pool (8 bytes): {:?}", reward_pool_bytes);
119    println!("  hash (32 bytes): {:?}", hash_bytes);
120
121    // Convert bytes back to hex strings
122    let reward_pool_hex = hex::bytes_to_hex_string(&reward_pool_bytes);
123    println!("  reward_pool back to hex: {}", reward_pool_hex);
124
125    // ===== SCENARIO 4: Building Claim Instruction =====
126    println!("\n📊 Scenario 4: Building Claim Instruction");
127    println!("------------------------------------------");
128
129    // Create sample participant data
130    let participant_data = DailyParticipantData::new(
131        [1u8; 32],  // participant_id
132        1723680000, // first_payment_timestamp
133        1723766400, // last_payment_timestamp
134        [1u8; 8],   // first_payment_tx_sig_short
135        [2u8; 8],   // last_payment_tx_sig_short
136        5,          // payment_count
137        0,          // participant_type (customer)
138    );
139
140    // Create payment proof (empty for this example)
141    let payment_proof = vec![[3u8; 32]];
142    let payment_indices = vec![true];
143
144    // Create seal proof (empty for this example)
145    let seal_proof = vec![[4u8; 32]];
146    let seal_indices = vec![false];
147
148    // Payment root (32 bytes)
149    let payment_root = [5u8; 32];
150
151    // Build claim instruction using the converted epoch_data
152    let claim_instruction = claim(
153        Pubkey::new_from_array([6u8; 32]), // signer (placeholder)
154        Pubkey::new_from_array([7u8; 32]), // beneficiary (placeholder)
155        0,                                 // epoch
156        participant_data,
157        payment_proof,
158        payment_indices,
159        seal_proof,
160        seal_indices,
161        payment_root,
162        epoch_data, // Using the converted epoch_data
163        0,          // participant_type
164        None,       // social_data
165    );
166
167    println!("✅ Claim instruction built successfully!");
168    println!(
169        "  Instruction data length: {} bytes",
170        claim_instruction.data.len()
171    );
172    println!("  Number of accounts: {}", claim_instruction.accounts.len());
173
174    // ===== SUMMARY =====
175    println!("\n🎯 Summary");
176    println!("===========");
177    println!("✅ Successfully converted database hex strings to EpochClaimData");
178    println!("✅ Successfully converted native u64 values to EpochClaimData");
179    println!("✅ Successfully built claim instruction with converted data");
180    println!("✅ All data type conversions working correctly");
181
182    println!("\n🚀 The utils module successfully bridges the gap between:");
183    println!("   - Database hex string output (from SQL functions)");
184    println!("   - On-chain byte array requirements (for EpochClaimData)");
185    println!("   - Claim instruction construction");
186
187    Ok(())
188}
Source

pub fn compute_leaf_hash(&self) -> [u8; 32]

Compute the Merkle leaf hash for this participant data.

§Returns
  • 32-byte hash that will be used as a leaf in the Merkle tree
§Security

This hash includes all fields to ensure data integrity and prevent replay attacks across different epochs. The short transaction signatures are included to enable on-chain verification of payment authenticity.

§Implementation

Uses Solana’s standard hashv function with SHA-256 for cryptographic security. All fields are included in the hash to prevent any data manipulation.

Source

pub fn validate(&self) -> Result<(), &'static str>

Validate the participant data for consistency.

§Returns
  • Ok(()) if data is valid
  • Err(&str) with error message if validation fails
§Validation Rules
  • Participant type must be 0 or 1
  • Payment count must be greater than 0
  • First timestamp must be before or equal to last timestamp
  • Short signatures must not be all zeros (basic validation)
Source

pub fn is_customer(&self) -> bool

Check if this participant is a customer.

§Returns
  • true if participant_type is 0 (customer)
  • false if participant_type is 1 (merchant)
Source

pub fn is_merchant(&self) -> bool

Check if this participant is a merchant.

§Returns
  • true if participant_type is 1 (merchant)
  • false if participant_type is 0 (customer)
Source

pub fn from_bytes(data: &[u8]) -> Result<Self, &'static str>

Deserialize from bytes manually

Trait Implementations§

Source§

impl Clone for DailyParticipantData

Source§

fn clone(&self) -> DailyParticipantData

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for DailyParticipantData

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for DailyParticipantData

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl PartialEq for DailyParticipantData

Source§

fn eq(&self, other: &DailyParticipantData) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Serialize for DailyParticipantData

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl Zeroable for DailyParticipantData

Source§

fn zeroed() -> Self

Source§

impl Copy for DailyParticipantData

Source§

impl Pod for DailyParticipantData

Source§

impl StructuralPartialEq for DailyParticipantData

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Az for T

Source§

fn az<Dst>(self) -> Dst
where T: Cast<Dst>,

Casts the value.
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<Src, Dst> CastFrom<Src> for Dst
where Src: Cast<Dst>,

Source§

fn cast_from(src: Src) -> Dst

Casts the value.
Source§

impl<T> CheckedAs for T

Source§

fn checked_as<Dst>(self) -> Option<Dst>
where T: CheckedCast<Dst>,

Casts the value.
Source§

impl<T> CheckedBitPattern for T
where T: AnyBitPattern,

Source§

type Bits = T

Self must have the same layout as the specified Bits except for the possible invalid bit patterns being checked during is_valid_bit_pattern.
Source§

fn is_valid_bit_pattern(_bits: &T) -> bool

If this function returns true, then it must be valid to reinterpret bits as &Self.
Source§

impl<Src, Dst> CheckedCastFrom<Src> for Dst
where Src: CheckedCast<Dst>,

Source§

fn checked_cast_from(src: Src) -> Option<Dst>

Casts the value.
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<Src, Dst> LosslessTryInto<Dst> for Src
where Dst: LosslessTryFrom<Src>,

Source§

fn lossless_try_into(self) -> Option<Dst>

Performs the conversion.
Source§

impl<Src, Dst> LossyInto<Dst> for Src
where Dst: LossyFrom<Src>,

Source§

fn lossy_into(self) -> Dst

Performs the conversion.
Source§

impl<T> OverflowingAs for T

Source§

fn overflowing_as<Dst>(self) -> (Dst, bool)
where T: OverflowingCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> OverflowingCastFrom<Src> for Dst
where Src: OverflowingCast<Dst>,

Source§

fn overflowing_cast_from(src: Src) -> (Dst, bool)

Casts the value.
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> SaturatingAs for T

Source§

fn saturating_as<Dst>(self) -> Dst
where T: SaturatingCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> SaturatingCastFrom<Src> for Dst
where Src: SaturatingCast<Dst>,

Source§

fn saturating_cast_from(src: Src) -> Dst

Casts the value.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> UnwrappedAs for T

Source§

fn unwrapped_as<Dst>(self) -> Dst
where T: UnwrappedCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> UnwrappedCastFrom<Src> for Dst
where Src: UnwrappedCast<Dst>,

Source§

fn unwrapped_cast_from(src: Src) -> Dst

Casts the value.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WrappingAs for T

Source§

fn wrapping_as<Dst>(self) -> Dst
where T: WrappingCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> WrappingCastFrom<Src> for Dst
where Src: WrappingCast<Dst>,

Source§

fn wrapping_cast_from(src: Src) -> Dst

Casts the value.
Source§

impl<T> AnyBitPattern for T
where T: Pod,

Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> NoUninit for T
where T: Pod,