dpp/shielded/builder/
token_direct_purchase_to_pool.rs1use grovedb_commitment_tree::OutgoingViewingKey;
2
3use crate::address_funds::OrchardAddress;
4use crate::balances::credits::TokenAmount;
5use crate::fee::Credits;
6use crate::identity::signer::Signer;
7use crate::identity::IdentityPublicKey;
8use crate::prelude::{Identifier, IdentityNonce, UserFeeIncrease};
9use crate::shielded::{token_pool_output_only_extra_sighash_data, OrchardBundleParams};
10use crate::state_transition::batch_transition::batched_transition::token_transition_action_type::TokenTransitionActionType;
11use crate::state_transition::batch_transition::methods::v1::DocumentsBatchTransitionMethodsV1;
12use crate::state_transition::batch_transition::BatchTransition;
13use crate::state_transition::StateTransition;
14use crate::ProtocolError;
15use platform_version::version::PlatformVersion;
16
17use super::{build_output_only_bundle, serialize_authorized_bundle, OrchardProver};
18
19#[allow(clippy::too_many_arguments)]
24pub async fn build_token_direct_purchase_to_pool_transition<
25 S: Signer<IdentityPublicKey>,
26 P: OrchardProver,
27>(
28 token_id: Identifier,
29 owner_id: Identifier,
30 data_contract_id: Identifier,
31 token_contract_position: u16,
32 recipient: &OrchardAddress,
33 token_count: TokenAmount,
34 total_agreed_price: Credits,
35 memo: [u8; 36],
36 sender_ovk: Option<OutgoingViewingKey>,
37 identity_public_key: &IdentityPublicKey,
38 identity_contract_nonce: IdentityNonce,
39 user_fee_increase: UserFeeIncrease,
40 signer: &S,
41 prover: &P,
42 platform_version: &PlatformVersion,
43) -> Result<StateTransition, ProtocolError> {
44 if token_count == 0 {
45 return Err(ProtocolError::ShieldedBuildError(
46 "token direct purchase to pool count must be greater than zero".to_string(),
47 ));
48 }
49 if token_count > i64::MAX as u64 {
50 return Err(ProtocolError::ShieldedBuildError(format!(
51 "token direct purchase to pool count {} exceeds maximum allowed value {}",
52 token_count,
53 i64::MAX as u64
54 )));
55 }
56
57 let extra_sighash_data = token_pool_output_only_extra_sighash_data(
58 TokenTransitionActionType::DirectPurchaseToPool,
59 token_id.as_bytes(),
60 owner_id.as_bytes(),
61 platform_version,
62 )?;
63 let bundle = build_output_only_bundle(
64 recipient,
65 token_count,
66 memo,
67 sender_ovk,
68 0,
69 &extra_sighash_data,
70 prover,
71 )?;
72 let sb = serialize_authorized_bundle(&bundle);
73
74 if sb.value_balance != -(token_count as i64) {
75 return Err(ProtocolError::ShieldedBuildError(format!(
76 "token direct purchase to pool count bundle value balance {} does not equal -{}",
77 sb.value_balance, token_count
78 )));
79 }
80
81 BatchTransition::new_token_direct_purchase_to_pool_transition(
82 token_id,
83 owner_id,
84 data_contract_id,
85 token_contract_position,
86 token_count,
87 total_agreed_price,
88 OrchardBundleParams {
89 actions: sb.actions,
90 anchor: sb.anchor,
91 proof: sb.proof,
92 binding_signature: sb.binding_signature,
93 },
94 identity_public_key,
95 identity_contract_nonce,
96 user_fee_increase,
97 signer,
98 platform_version,
99 None,
100 )
101 .await
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 use crate::shielded::builder::test_helpers::{
108 test_identity_key, test_orchard_address, DummyIdentitySigner, TestProver,
109 };
110
111 #[tokio::test]
112 async fn rejects_zero_count() {
113 let key = test_identity_key();
114 let err = build_token_direct_purchase_to_pool_transition(
115 Identifier::from([1u8; 32]),
116 Identifier::from([2u8; 32]),
117 Identifier::from([3u8; 32]),
118 0,
119 &test_orchard_address(),
120 0,
121 1_000,
122 [0u8; 36],
123 None,
124 &key,
125 1,
126 0,
127 &DummyIdentitySigner,
128 &TestProver,
129 PlatformVersion::latest(),
130 )
131 .await
132 .expect_err("zero count must be rejected")
133 .to_string();
134 assert!(err.contains("greater than zero"), "unexpected error: {err}");
135 }
136}