dpp/tokens/token_payment_info/v1/
mod.rs1pub mod v1_accessors;
2
3use crate::balances::credits::TokenAmount;
4use crate::data_contract::TokenContractPosition;
5use crate::shielded::SerializedAction;
6use crate::tokens::gas_fees_paid_by::GasFeesPaidBy;
7use crate::tokens::token_payment_info::v0::v0_accessors::TokenPaymentInfoAccessorsV0;
8use crate::tokens::token_payment_info::v0::TokenPaymentInfoV0;
9use crate::ProtocolError;
10use bincode::{Decode, DecodeUntrusted, Encode};
11use platform_value::btreemap_extensions::{
12 BTreeValueRemoveFromMapHelper, BTreeValueRemoveInnerValueFromMapHelper,
13};
14use platform_value::{Identifier, Value};
15#[cfg(feature = "serde-conversion")]
16use serde::{Deserialize, Serialize};
17use std::collections::BTreeMap;
18use std::fmt;
19
20#[derive(Debug, Clone, Encode, Decode, PartialEq, DecodeUntrusted)]
26#[cfg_attr(feature = "json-conversion", crate::serialization::json_safe_fields)]
29#[cfg_attr(
30 feature = "serde-conversion",
31 derive(Serialize, Deserialize),
32 serde(rename_all = "camelCase")
33)]
34pub struct TokenShieldedPayment {
35 pub amount: TokenAmount,
38 pub actions: Vec<SerializedAction>,
40 pub anchor: [u8; 32],
42 pub proof: Vec<u8>,
44 #[cfg_attr(
48 all(feature = "serde-conversion", not(feature = "json-conversion")),
49 serde(with = "crate::serialization::serde_bytes")
50 )]
51 pub binding_signature: [u8; 64],
52}
53
54#[cfg(all(feature = "json-conversion", feature = "serde-conversion"))]
55impl crate::serialization::JsonConvertible for TokenShieldedPayment {}
56
57#[cfg(all(feature = "value-conversion", feature = "serde-conversion"))]
58impl crate::serialization::ValueConvertible for TokenShieldedPayment {}
59
60impl Default for TokenShieldedPayment {
61 fn default() -> Self {
62 Self {
63 amount: 0,
64 actions: vec![],
65 anchor: [0u8; 32],
66 proof: vec![],
67 binding_signature: [0u8; 64],
68 }
69 }
70}
71
72impl fmt::Display for TokenShieldedPayment {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 write!(
75 f,
76 "Shielded Payment: amount {}, actions {}",
77 self.amount,
78 self.actions.len()
79 )
80 }
81}
82
83impl TryFrom<BTreeMap<String, Value>> for TokenShieldedPayment {
84 type Error = ProtocolError;
85
86 fn try_from(mut map: BTreeMap<String, Value>) -> Result<Self, Self::Error> {
87 let actions = map
94 .remove_inner_value_array::<Vec<Value>>("actions")?
95 .into_iter()
96 .map(|value| {
97 let mut action = value.into_btree_string_map()?;
98 Ok(SerializedAction {
99 nullifier: action.remove_bytes_32("nullifier")?.to_buffer(),
100 rk: action.remove_bytes_32("rk")?.to_buffer(),
101 cmx: action.remove_bytes_32("cmx")?.to_buffer(),
102 encrypted_note: action.remove_binary_data("encryptedNote")?.0,
103 cv_net: action.remove_bytes_32("cvNet")?.to_buffer(),
104 spend_auth_sig: fixed_64(
105 action.remove_binary_data("spendAuthSig")?.0,
106 "spendAuthSig",
107 )?,
108 })
109 })
110 .collect::<Result<Vec<_>, ProtocolError>>()?;
111 Ok(TokenShieldedPayment {
112 amount: map.remove_integer("amount")?,
113 actions,
114 anchor: map.remove_bytes_32("anchor")?.to_buffer(),
115 proof: map.remove_binary_data("proof")?.0,
116 binding_signature: fixed_64(
117 map.remove_binary_data("bindingSignature")?.0,
118 "bindingSignature",
119 )?,
120 })
121 }
122}
123
124fn fixed_64(bytes: Vec<u8>, field: &str) -> Result<[u8; 64], ProtocolError> {
125 let len = bytes.len();
126 bytes
127 .try_into()
128 .map_err(|_| ProtocolError::DecodingError(format!("{field} must be 64 bytes, got {len}")))
129}
130
131#[derive(Debug, Clone, Encode, Decode, Default, PartialEq, DecodeUntrusted)]
136#[cfg_attr(feature = "json-conversion", crate::serialization::json_safe_fields)]
137#[cfg_attr(
138 feature = "serde-conversion",
139 derive(Serialize, Deserialize),
140 serde(rename_all = "camelCase")
141)]
142pub struct TokenPaymentInfoV1 {
143 pub payment_token_contract_id: Option<Identifier>,
146 pub token_contract_position: TokenContractPosition,
148 pub minimum_token_cost: Option<TokenAmount>,
150 pub maximum_token_cost: Option<TokenAmount>,
152 pub gas_fees_paid_by: GasFeesPaidBy,
154 pub shielded_payment: Box<TokenShieldedPayment>,
157}
158
159impl fmt::Display for TokenPaymentInfoV1 {
160 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161 write!(
162 f,
163 "Contract ID: {:?}, Token Position: {:?}, Min Cost: {:?}, Max Cost: {:?}, Gas Fees Paid By: {}, {}",
164 self.payment_token_contract_id,
165 self.token_contract_position,
166 self.minimum_token_cost,
167 self.maximum_token_cost,
168 self.gas_fees_paid_by,
169 self.shielded_payment
170 )
171 }
172}
173
174impl TokenPaymentInfoV1 {
175 pub fn from_v0(v0: TokenPaymentInfoV0, shielded_payment: TokenShieldedPayment) -> Self {
177 Self {
178 payment_token_contract_id: v0.payment_token_contract_id,
179 token_contract_position: v0.token_contract_position,
180 minimum_token_cost: v0.minimum_token_cost,
181 maximum_token_cost: v0.maximum_token_cost,
182 gas_fees_paid_by: v0.gas_fees_paid_by,
183 shielded_payment: Box::new(shielded_payment),
184 }
185 }
186
187 pub fn into_v0(self) -> TokenPaymentInfoV0 {
189 TokenPaymentInfoV0 {
190 payment_token_contract_id: self.payment_token_contract_id,
191 token_contract_position: self.token_contract_position,
192 minimum_token_cost: self.minimum_token_cost,
193 maximum_token_cost: self.maximum_token_cost,
194 gas_fees_paid_by: self.gas_fees_paid_by,
195 }
196 }
197
198 pub fn shielded_payment(&self) -> &TokenShieldedPayment {
200 &self.shielded_payment
201 }
202}
203
204impl TokenPaymentInfoAccessorsV0 for TokenPaymentInfoV1 {
205 fn payment_token_contract_id(&self) -> Option<Identifier> {
206 self.payment_token_contract_id
207 }
208
209 fn payment_token_contract_id_ref(&self) -> &Option<Identifier> {
210 &self.payment_token_contract_id
211 }
212
213 fn token_contract_position(&self) -> TokenContractPosition {
214 self.token_contract_position
215 }
216
217 fn minimum_token_cost(&self) -> Option<TokenAmount> {
218 self.minimum_token_cost
219 }
220
221 fn maximum_token_cost(&self) -> Option<TokenAmount> {
222 self.maximum_token_cost
223 }
224
225 fn set_payment_token_contract_id(&mut self, id: Option<Identifier>) {
226 self.payment_token_contract_id = id;
227 }
228
229 fn set_token_contract_position(&mut self, position: TokenContractPosition) {
230 self.token_contract_position = position;
231 }
232
233 fn set_minimum_token_cost(&mut self, cost: Option<TokenAmount>) {
234 self.minimum_token_cost = cost;
235 }
236
237 fn set_maximum_token_cost(&mut self, cost: Option<TokenAmount>) {
238 self.maximum_token_cost = cost;
239 }
240
241 fn gas_fees_paid_by(&self) -> GasFeesPaidBy {
242 self.gas_fees_paid_by
243 }
244
245 fn set_gas_fees_paid_by(&mut self, payer: GasFeesPaidBy) {
246 self.gas_fees_paid_by = payer;
247 }
248}
249
250impl TryFrom<BTreeMap<String, Value>> for TokenPaymentInfoV1 {
251 type Error = ProtocolError;
252
253 fn try_from(mut map: BTreeMap<String, Value>) -> Result<Self, Self::Error> {
254 let shielded_map: BTreeMap<String, Value> = map
255 .remove_map_as_btree_map_keep_values_as_platform_value::<String, Value>(
256 "shieldedPayment",
257 )?;
258 let shielded_payment: TokenShieldedPayment = shielded_map.try_into()?;
259 let v0: TokenPaymentInfoV0 = map.try_into()?;
260 Ok(TokenPaymentInfoV1::from_v0(v0, shielded_payment))
261 }
262}