use alloc::collections::BTreeMap;
use miden_protocol::Word;
use miden_protocol::account::delta::AssetDeltaOperation;
use miden_protocol::account::{
AccountVaultDelta,
AccountVaultPatch,
FungibleAssetDelta,
NonFungibleAssetDelta,
NonFungibleDeltaAction,
};
use miden_protocol::asset::{Asset, AssetId};
use crate::TransactionKernelError;
use crate::host::tx_event::{AssetDelta, AssetPatch};
#[derive(Debug, Clone, Default)]
pub(crate) struct VaultUpdateTracker {
delta: BTreeMap<AssetId, AssetDelta>,
entries: BTreeMap<AssetId, (Word, Word)>,
}
impl VaultUpdateTracker {
pub fn update_patch(&mut self, patch: AssetPatch) -> Result<(), TransactionKernelError> {
self.entries
.entry(patch.asset_id)
.and_modify(|(_, r#final)| *r#final = patch.final_vault_value)
.or_insert((patch.initial_vault_value, patch.final_vault_value));
Ok(())
}
pub fn update_delta(&mut self, delta: AssetDelta) {
self.delta.insert(delta.asset.id(), delta);
}
pub fn reset_delta(&mut self) {
self.delta.clear();
}
pub fn into_delta(self) -> AccountVaultDelta {
self.build_delta()
}
pub fn into_patch(self) -> AccountVaultPatch {
let normalized = self
.entries
.into_iter()
.filter_map(|(key, (initial_value, final_value))| {
if final_value == initial_value {
None
} else {
Some((key, final_value))
}
})
.collect();
AccountVaultPatch::new(normalized).expect("tx kernel should only emit valid assets")
}
fn build_delta(&self) -> AccountVaultDelta {
let mut fungible: BTreeMap<AssetId, i64> = BTreeMap::new();
let mut non_fungible: BTreeMap<AssetId, (_, NonFungibleDeltaAction)> = BTreeMap::new();
for (&asset_id, asset_delta) in &self.delta {
match asset_delta.asset {
Asset::Fungible(fungible_asset) => {
let amount = fungible_asset.amount().as_i64();
let signed_amount = match asset_delta.delta_op {
AssetDeltaOperation::Add => amount,
AssetDeltaOperation::Remove => -amount,
};
fungible.insert(asset_id, signed_amount);
},
Asset::NonFungible(non_fungible_asset) => {
let action = match asset_delta.delta_op {
AssetDeltaOperation::Add => NonFungibleDeltaAction::Add,
AssetDeltaOperation::Remove => NonFungibleDeltaAction::Remove,
};
non_fungible.insert(asset_id, (non_fungible_asset, action));
},
}
}
let fungible = FungibleAssetDelta::new(fungible)
.expect("tx kernel should only emit valid fungible asset deltas");
let non_fungible = NonFungibleAssetDelta::new(non_fungible);
AccountVaultDelta::new(fungible, non_fungible)
}
}