use crate::cache::{CacheCollectionCompleteness, CacheValidationStatus};
use serde::{Deserialize as SerdeDeserialize, Serialize};
use serde_json::Value as JsonValue;
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct IcrcAccountTransactionPageData {
pub index_canister_id: String,
pub balance: String,
pub oldest_transaction_id: Option<String>,
pub next_start: Option<String>,
pub token_symbol: String,
pub decimals: u8,
pub transactions: Vec<IcrcAccountTransactionRow>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct IcrcAccountTransactionPageReport {
#[serde(flatten)]
pub provenance: super::IcrcReportProvenance,
pub account_owner: String,
pub subaccount_hex: Option<String>,
pub requested_start: Option<String>,
pub requested_limit: u32,
#[serde(flatten)]
pub page: IcrcAccountTransactionPageData,
}
#[derive(Clone, Debug, Eq, PartialEq, SerdeDeserialize, Serialize)]
pub struct IcrcAccountTransactionContext {
pub ledger_canister_id: String,
pub index_canister_id: String,
pub account_owner: String,
pub subaccount_hex: Option<String>,
pub source_endpoint: String,
pub collection_started_at: String,
pub collection_completed_at: String,
pub fetched_by: String,
}
#[derive(Clone, Debug, Eq, PartialEq, SerdeDeserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct IcrcAccountTransactionSnapshot {
#[serde(flatten)]
pub context: IcrcAccountTransactionContext,
pub schema_version: u32,
pub balance: String,
pub token_symbol: String,
pub decimals: u8,
pub completeness: CacheCollectionCompleteness,
pub transactions: Vec<IcrcAccountTransactionRow>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct IcrcAccountTransactionRefreshReport {
#[serde(flatten)]
pub context: IcrcAccountTransactionContext,
pub schema_version: u32,
pub transaction_count: usize,
pub newest_transaction_id: Option<String>,
pub oldest_transaction_id: Option<String>,
pub page_size: u32,
pub page_count: u32,
pub point_in_time_guaranteed: bool,
pub replaced_existing_cache: bool,
pub attempt_finalization_error: Option<String>,
pub cache_path: String,
pub refresh_attempt_path: String,
pub refresh_lock_path: String,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct IcrcAccountTransactionListReport {
#[serde(flatten)]
pub context: IcrcAccountTransactionContext,
pub schema_version: u32,
pub requested_limit: u32,
pub sort: String,
pub total_transaction_count: usize,
pub newest_transaction_id: Option<String>,
pub oldest_transaction_id: Option<String>,
pub balance: String,
pub token_symbol: String,
pub decimals: u8,
pub complete: bool,
pub point_in_time_guaranteed: bool,
pub page_size: u32,
pub page_count: u32,
pub cache_path: String,
pub transactions: Vec<IcrcAccountTransactionRow>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct IcrcAccountTransactionCacheStatusReport {
pub schema_version: u32,
pub ledger_canister_id: String,
pub account_owner: String,
pub subaccount_hex: Option<String>,
pub source_endpoint: String,
pub found: bool,
pub cache: Option<IcrcAccountTransactionCacheSummary>,
pub expected_cache_path: String,
pub refresh_attempt_path: String,
pub refresh_lock_path: String,
pub latest_attempt: Option<IcrcAccountTransactionRefreshAttemptStatus>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[serde(tag = "cache_status", rename_all = "snake_case")]
pub enum IcrcAccountTransactionCacheSummary {
Valid {
#[serde(flatten)]
data: IcrcAccountTransactionValidCacheSummary,
},
Invalid {
cache_path: String,
cache_error: String,
},
}
impl IcrcAccountTransactionCacheSummary {
#[must_use]
pub const fn cache_status(&self) -> CacheValidationStatus {
match self {
Self::Valid { .. } => CacheValidationStatus::Valid,
Self::Invalid { .. } => CacheValidationStatus::Invalid,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct IcrcAccountTransactionValidCacheSummary {
pub index_canister_id: String,
pub transaction_count: usize,
pub newest_transaction_id: Option<String>,
pub oldest_transaction_id: Option<String>,
pub page_size: u32,
pub page_count: u32,
pub point_in_time_guaranteed: bool,
pub collection_started_at: String,
pub collection_completed_at: String,
pub cache_path: String,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct IcrcAccountTransactionRefreshAttemptStatus {
pub index_canister_id: Option<String>,
#[serde(flatten)]
pub lifecycle: crate::cache::SnapshotRefreshAttemptStatus,
}
#[derive(Clone, Debug, Eq, PartialEq, SerdeDeserialize, Serialize)]
#[serde(tag = "account_kind", rename_all = "snake_case", deny_unknown_fields)]
pub enum IcrcAccountRow {
Structured {
owner: String,
subaccount_hex: Option<String>,
},
Legacy {
account_identifier: String,
},
}
#[derive(Clone, Debug, Eq, PartialEq, SerdeDeserialize, Serialize)]
pub struct IcrcAccountTransactionRow {
pub id: String,
pub kind: String,
pub timestamp_unix_nanos: Option<String>,
pub amount_base_units: Option<String>,
pub fee_base_units: Option<String>,
pub from: Option<IcrcAccountRow>,
pub to: Option<IcrcAccountRow>,
pub spender: Option<IcrcAccountRow>,
pub memo_hex: Option<String>,
pub created_at_time_unix_nanos: Option<String>,
pub expires_at_unix_nanos: Option<String>,
pub expected_allowance_base_units: Option<String>,
pub raw_transaction: JsonValue,
}
#[cfg(test)]
mod account_tests {
use super::IcrcAccountRow;
use serde_json::json;
#[test]
fn account_forms_round_trip_and_refuse_ambiguous_or_former_shapes() {
for account in [
IcrcAccountRow::Structured {
owner: "aaaaa-aa".to_string(),
subaccount_hex: None,
},
IcrcAccountRow::Legacy {
account_identifier: "original-index-identifier".to_string(),
},
] {
let value = serde_json::to_value(&account).unwrap();
assert_eq!(
serde_json::from_value::<IcrcAccountRow>(value).unwrap(),
account
);
}
for value in [
json!({"owner":"aaaaa-aa", "subaccount_hex":null, "account_identifier":null}),
json!({"account_kind":"legacy", "account_identifier":"id", "owner":"aaaaa-aa"}),
json!({"account_kind":"structured", "subaccount_hex":null}),
json!({"account_kind":"structured", "owner":"aaaaa-aa", "account_identifier":"id"}),
] {
assert!(serde_json::from_value::<IcrcAccountRow>(value).is_err());
}
}
}