miden_client/rpc/domain/
account_vault.rs1use alloc::string::ToString;
2use alloc::vec::Vec;
3
4use miden_protocol::Word;
5use miden_protocol::account::AccountVaultPatch;
6use miden_protocol::asset::{Asset, AssetId};
7use miden_protocol::block::BlockNumber;
8
9use crate::rpc::domain::MissingFieldHelper;
10use crate::rpc::{RpcConversionError, RpcError, generated as proto};
11
12impl TryFrom<proto::primitives::Asset> for Asset {
16 type Error = RpcConversionError;
17
18 fn try_from(value: proto::primitives::Asset) -> Result<Self, Self::Error> {
19 let key_word: Word = value
20 .key
21 .ok_or(proto::primitives::Asset::missing_field(stringify!(key)))?
22 .try_into()?;
23 let value_word: Word = value
24 .value
25 .ok_or(proto::primitives::Asset::missing_field(stringify!(value)))?
26 .try_into()?;
27 Asset::from_id_and_value_words(key_word, value_word)
28 .map_err(|e| RpcConversionError::InvalidField(e.to_string()))
29 }
30}
31
32pub struct AccountVaultInfo {
41 pub chain_tip: BlockNumber,
43 pub block_number: BlockNumber,
45 pub vault_patch: AccountVaultPatch,
47}
48
49impl TryFrom<proto::rpc::SyncAccountVaultResponse> for AccountVaultInfo {
53 type Error = RpcError;
54
55 fn try_from(value: proto::rpc::SyncAccountVaultResponse) -> Result<Self, Self::Error> {
56 let pagination_info =
57 value
58 .pagination_info
59 .ok_or(proto::rpc::SyncAccountVaultResponse::missing_field(stringify!(
60 pagination_info
61 )))?;
62
63 let mut updates = value
64 .updates
65 .into_iter()
66 .map(vault_update_from_proto)
67 .collect::<Result<Vec<_>, _>>()?;
68
69 updates.sort_by_key(|(block_num, ..)| *block_num);
73 let mut vault_patch = AccountVaultPatch::default();
74 for (_, asset_id, asset) in updates {
75 match asset {
76 Some(asset) => vault_patch.insert_asset(asset),
77 None => vault_patch.remove_asset(asset_id),
78 }
79 }
80
81 Ok(Self {
82 chain_tip: pagination_info.chain_tip.into(),
83 block_number: pagination_info.block_num.into(),
84 vault_patch,
85 })
86 }
87}
88
89fn vault_update_from_proto(
95 value: proto::rpc::AccountVaultUpdate,
96) -> Result<(BlockNumber, AssetId, Option<Asset>), RpcError> {
97 let block_num = BlockNumber::from(value.block_num);
98
99 let vault_id: Word = value
100 .vault_key
101 .ok_or(proto::rpc::SyncAccountVaultResponse::missing_field(stringify!(vault_id)))?
102 .try_into()?;
103 let asset_id =
104 AssetId::try_from(vault_id).map_err(|e| RpcError::InvalidResponse(e.to_string()))?;
105
106 let asset = value.asset.map(Asset::try_from).transpose()?;
107
108 if let Some(ref asset) = asset
109 && asset.id() != asset_id
110 {
111 return Err(RpcError::InvalidResponse(
112 "account vault update returned mismatched asset key".to_string(),
113 ));
114 }
115
116 Ok((block_num, asset_id, asset))
117}