use crate::asset::{CandidCryptoAsset, CryptoAsset};
use crate::{AssetAmount, AssetId, CandidAssetAmount, CandidAssetId, CandidDepositId, DepositId};
use candid::CandidType;
use ex3_crypto::sha256;
use ex3_serde::bincode;
use ex3_timestamp::TimeInNs;
use ic_stable_structures::{storable::Bound, Storable};
use serde::{Deserialize, Serialize};
use std::fmt::Display;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, PartialOrd, Ord)]
pub struct DepositIdentifier {
pub asset_id: AssetId,
pub block_ref: Option<String>,
pub tx_ref: Option<String>,
}
impl Display for DepositIdentifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}:{}:{}",
self.asset_id,
self.block_ref.clone().unwrap_or("".to_string()),
self.tx_ref.clone().unwrap_or("".to_string())
)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, CandidType, Ord, PartialOrd)]
pub struct CandidDepositIdentifier {
pub asset_id: CandidAssetId,
pub block_ref: Option<String>,
pub tx_ref: Option<String>,
}
impl From<DepositIdentifier> for CandidDepositIdentifier {
fn from(deposit_identifier: DepositIdentifier) -> Self {
Self {
asset_id: deposit_identifier.asset_id.into(),
block_ref: deposit_identifier.block_ref,
tx_ref: deposit_identifier.tx_ref,
}
}
}
impl From<CandidDepositIdentifier> for DepositIdentifier {
fn from(deposit_identifier: CandidDepositIdentifier) -> Self {
Self {
asset_id: deposit_identifier.asset_id.into(),
block_ref: deposit_identifier.block_ref,
tx_ref: deposit_identifier.tx_ref,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, Hash, Ord, PartialOrd)]
pub struct OriginalDeposit {
pub from: String,
pub asset: CryptoAsset,
pub amount: AssetAmount,
pub block_ref: Option<String>,
pub tx_ref: Option<String>,
}
impl OriginalDeposit {
pub fn hash(&self) -> [u8; 32] {
sha256(&bincode::serialize(&self).unwrap())
}
}
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, CandidType, Hash, Ord, PartialOrd)]
pub struct OriginalCandidDeposit {
pub from: String,
pub asset: CandidCryptoAsset,
pub amount: CandidAssetAmount,
pub block_ref: Option<String>,
pub tx_ref: Option<String>,
}
impl Storable for OriginalDeposit {
fn to_bytes(&self) -> std::borrow::Cow<[u8]> {
bincode::serialize(&self).unwrap().into()
}
fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self {
bincode::deserialize(bytes.as_ref()).unwrap()
}
const BOUND: Bound = Bound::Bounded {
max_size: 888,
is_fixed_size: false,
};
}
impl From<OriginalDeposit> for OriginalCandidDeposit {
fn from(deposit: OriginalDeposit) -> Self {
Self {
asset: deposit.asset.into(),
block_ref: deposit.block_ref,
tx_ref: deposit.tx_ref,
from: deposit.from,
amount: deposit.amount.into(),
}
}
}
impl From<OriginalCandidDeposit> for OriginalDeposit {
fn from(deposit: OriginalCandidDeposit) -> Self {
Self {
asset: deposit.asset.into(),
block_ref: deposit.block_ref,
tx_ref: deposit.tx_ref,
from: deposit.from,
amount: deposit.amount.into(),
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct VerifiedDeposit {
pub seq_id: DepositId,
pub deposit: OriginalDeposit,
pub verified_at: TimeInNs,
}
#[derive(Deserialize, CandidType, Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct CandidVerifiedDeposit {
pub seq_id: CandidDepositId,
pub deposit: OriginalCandidDeposit,
pub confirmed_at: TimeInNs,
}
impl Storable for DepositIdentifier {
fn to_bytes(&self) -> std::borrow::Cow<[u8]> {
bincode::serialize(&self).unwrap().into()
}
fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self {
bincode::deserialize(bytes.as_ref()).unwrap()
}
const BOUND: Bound = Bound::Bounded {
max_size: 512,
is_fixed_size: false,
};
}
impl Storable for VerifiedDeposit {
fn to_bytes(&self) -> std::borrow::Cow<[u8]> {
bincode::serialize(&self).unwrap().into()
}
fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self {
bincode::deserialize(bytes.as_ref()).unwrap()
}
const BOUND: Bound = Bound::Bounded {
max_size: 304,
is_fixed_size: false,
};
}
impl From<VerifiedDeposit> for CandidVerifiedDeposit {
fn from(deposit: VerifiedDeposit) -> Self {
Self {
seq_id: deposit.seq_id.into(),
deposit: deposit.deposit.into(),
confirmed_at: deposit.verified_at,
}
}
}
impl From<CandidVerifiedDeposit> for VerifiedDeposit {
fn from(deposit: CandidVerifiedDeposit) -> Self {
Self {
seq_id: deposit.seq_id.into(),
deposit: deposit.deposit.into(),
verified_at: deposit.confirmed_at,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::asset::TokenType;
use crate::chain::{Chain, ChainType};
#[test]
fn test_storable_for_deposit_identifier() {
let tx_id: String = std::iter::repeat('1').take(128).collect();
let deposit_identifier = DepositIdentifier {
asset_id: u128::MAX.into(),
block_ref: Some(u128::MAX.to_string()),
tx_ref: Some(tx_id),
};
let bytes = deposit_identifier.to_bytes();
assert!(bytes.len() <= 512, "bytes.len() = {}", bytes.len());
let deposit_identifier2 = DepositIdentifier::from_bytes(bytes);
assert_eq!(deposit_identifier, deposit_identifier2);
}
#[test]
fn test_storable_for_original_deposit() {
let original_deposit = OriginalDeposit {
asset: CryptoAsset {
chain: Chain {
r#type: ChainType::Bitcoin,
network: 1u8.into(),
},
r#type: TokenType::Native,
address: "0x1234567890".to_string(),
},
block_ref: Some(u128::MAX.to_string()),
tx_ref: Some(
"0xf2e0565df1a88e98a0c506287b4d408bf19b59f7b9f6f2a0a54375e28038489d".to_string(),
),
from: "0x1234".to_string(),
amount: u128::MAX.into(),
};
let bytes = original_deposit.to_bytes();
assert!(bytes.len() <= 888, "bytes.len() = {}", bytes.len());
let original_deposit2 = OriginalDeposit::from_bytes(bytes);
assert_eq!(original_deposit, original_deposit2);
}
}