use packable::Packable;
use crate::types::block::{
address::Address, payload::milestone::option::receipt::TailTransactionHash, protocol::ProtocolParameters, Error,
};
#[derive(Clone, Debug, Eq, PartialEq, Packable)]
#[packable(unpack_visitor = ProtocolParameters)]
pub struct MigratedFundsEntry {
tail_transaction_hash: TailTransactionHash,
address: Address,
#[packable(verify_with = verify_amount_packable)]
amount: u64,
}
impl MigratedFundsEntry {
pub const AMOUNT_MIN: u64 = 1_000_000;
pub fn new(
tail_transaction_hash: TailTransactionHash,
address: Address,
amount: u64,
token_supply: u64,
) -> Result<Self, Error> {
verify_amount::<true>(&amount, &token_supply)?;
Ok(Self {
tail_transaction_hash,
address,
amount,
})
}
#[inline(always)]
pub fn tail_transaction_hash(&self) -> &TailTransactionHash {
&self.tail_transaction_hash
}
#[inline(always)]
pub fn address(&self) -> &Address {
&self.address
}
#[inline(always)]
pub fn amount(&self) -> u64 {
self.amount
}
}
fn verify_amount<const VERIFY: bool>(amount: &u64, token_supply: &u64) -> Result<(), Error> {
if VERIFY && (*amount < MigratedFundsEntry::AMOUNT_MIN || amount > token_supply) {
Err(Error::InvalidMigratedFundsEntryAmount(*amount))
} else {
Ok(())
}
}
fn verify_amount_packable<const VERIFY: bool>(
amount: &u64,
protocol_parameters: &ProtocolParameters,
) -> Result<(), Error> {
verify_amount::<VERIFY>(amount, &protocol_parameters.token_supply())
}
#[cfg(feature = "serde")]
pub(crate) mod dto {
use alloc::string::String;
use serde::{Deserialize, Serialize};
use super::*;
use crate::types::{
block::{address::dto::AddressDto, Error},
TryFromDto, ValidationParams,
};
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MigratedFundsEntryDto {
pub tail_transaction_hash: String,
pub address: AddressDto,
pub deposit: u64,
}
impl From<&MigratedFundsEntry> for MigratedFundsEntryDto {
fn from(value: &MigratedFundsEntry) -> Self {
Self {
tail_transaction_hash: prefix_hex::encode(value.tail_transaction_hash().as_ref()),
address: value.address().into(),
deposit: value.amount(),
}
}
}
impl TryFromDto for MigratedFundsEntry {
type Dto = MigratedFundsEntryDto;
type Error = Error;
fn try_from_dto_with_params_inner(dto: Self::Dto, params: ValidationParams<'_>) -> Result<Self, Self::Error> {
let tail_transaction_hash = prefix_hex::decode(&dto.tail_transaction_hash)
.map_err(|_| Error::InvalidField("tailTransactionHash"))?;
Ok(if let Some(token_supply) = params.token_supply() {
Self::new(
TailTransactionHash::new(tail_transaction_hash)?,
dto.address.try_into()?,
dto.deposit,
token_supply,
)?
} else {
Self {
tail_transaction_hash: TailTransactionHash::new(tail_transaction_hash)?,
amount: dto.deposit,
address: dto.address.try_into()?,
}
})
}
}
}