Skip to content

Commit c7cb8e6

Browse files
feat(wasm-sdk): add token-paid document support (#3599)
1 parent b9ac843 commit c7cb8e6

3 files changed

Lines changed: 542 additions & 10 deletions

File tree

packages/js-evo-sdk/tests/unit/facades/documents.spec.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ describe('DocumentsFacade', () => {
88
let document: wasmSDKPackage.Document;
99
let identityKey: wasmSDKPackage.IdentityPublicKey;
1010
let signer: wasmSDKPackage.IdentitySigner;
11+
const tokenPaymentInfo = {
12+
paymentTokenContractId: 'BpJvvpPiR2obh7ueZixjtYXsmWQdgJhiZtQJWjD7Ruus',
13+
tokenContractPosition: 0,
14+
minimumTokenCost: BigInt(10),
15+
maximumTokenCost: BigInt(25),
16+
gasFeesPaidBy: 'PreferContractOwner',
17+
};
1118

1219
// Stub references for type-safe assertions
1320
let getDocumentsStub: SinonStub;
@@ -116,6 +123,7 @@ describe('DocumentsFacade', () => {
116123
document,
117124
identityKey,
118125
signer,
126+
tokenPaymentInfo,
119127
};
120128

121129
await client.documents.create(options);
@@ -130,6 +138,7 @@ describe('DocumentsFacade', () => {
130138
document,
131139
identityKey,
132140
signer,
141+
tokenPaymentInfo,
133142
settings: { retries: 3 },
134143
};
135144

@@ -145,6 +154,7 @@ describe('DocumentsFacade', () => {
145154
document,
146155
identityKey,
147156
signer,
157+
tokenPaymentInfo,
148158
};
149159

150160
await client.documents.delete(options);
@@ -162,6 +172,7 @@ describe('DocumentsFacade', () => {
162172
},
163173
identityKey,
164174
signer,
175+
tokenPaymentInfo,
165176
};
166177

167178
await client.documents.delete(options);
@@ -178,6 +189,7 @@ describe('DocumentsFacade', () => {
178189
recipientId,
179190
identityKey,
180191
signer,
192+
tokenPaymentInfo,
181193
};
182194

183195
await client.documents.transfer(options);
@@ -195,6 +207,7 @@ describe('DocumentsFacade', () => {
195207
price: BigInt(1000000), // 1M credits
196208
identityKey,
197209
signer,
210+
tokenPaymentInfo,
198211
};
199212

200213
await client.documents.purchase(options);
@@ -210,6 +223,7 @@ describe('DocumentsFacade', () => {
210223
price: BigInt(5000000), // 5M credits
211224
identityKey,
212225
signer,
226+
tokenPaymentInfo,
213227
};
214228

215229
await client.documents.setPrice(options);

packages/wasm-sdk/src/state_transitions/document.rs

Lines changed: 110 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,84 @@ use dash_sdk::dpp::document::{Document, DocumentV0Getters};
1111
use dash_sdk::dpp::fee::Credits;
1212
use dash_sdk::dpp::identity::IdentityPublicKey;
1313
use dash_sdk::dpp::platform_value::Identifier;
14+
use dash_sdk::dpp::tokens::token_payment_info::TokenPaymentInfo;
1415
use dash_sdk::platform::documents::transitions::DocumentDeleteTransitionBuilder;
1516
use dash_sdk::platform::transition::purchase_document::PurchaseDocument;
1617
use dash_sdk::platform::transition::put_document::PutDocument;
1718
use dash_sdk::platform::transition::transfer_document::TransferDocument;
1819
use dash_sdk::platform::transition::update_price_of_document::UpdatePriceOfDocument;
20+
use js_sys::Reflect;
1921
use std::sync::Arc;
20-
use wasm_bindgen::prelude::*;
22+
use wasm_bindgen::{prelude::*, JsCast};
2123
use wasm_dpp2::data_contract::document::DocumentWasm;
2224
use wasm_dpp2::identifier::IdentifierWasm;
2325
use wasm_dpp2::identity::IdentityPublicKeyWasm;
26+
use wasm_dpp2::state_transitions::batch::token_payment_info::{
27+
TokenPaymentInfoOptionsJs, TokenPaymentInfoWasm,
28+
};
2429
use wasm_dpp2::utils::{
2530
get_class_type, try_from_options_optional, try_from_options_with, try_to_string, try_to_u64,
2631
IntoWasm,
2732
};
2833
use wasm_dpp2::IdentitySignerWasm;
2934

35+
#[wasm_bindgen(typescript_custom_section)]
36+
const TOKEN_PAYMENT_INFO_TS: &str = r#"
37+
/**
38+
* Token-based payment metadata for document actions that require token cost agreement.
39+
*/
40+
export interface DocumentTokenPaymentInfo {
41+
/**
42+
* Optional external token contract ID.
43+
* If omitted, the token is expected to come from the current document contract.
44+
*/
45+
paymentTokenContractId?: IdentifierLike;
46+
47+
/**
48+
* Token position within the token contract.
49+
*/
50+
tokenContractPosition: number;
51+
52+
/**
53+
* Optional minimum token amount the payer agrees to spend.
54+
*/
55+
minimumTokenCost?: bigint;
56+
57+
/**
58+
* Optional maximum token amount the payer agrees to spend.
59+
*/
60+
maximumTokenCost?: bigint;
61+
62+
/**
63+
* Which party covers gas fees for the document action.
64+
*/
65+
gasFeesPaidBy?: GasFeesPaidByLike;
66+
}
67+
"#;
68+
69+
fn try_from_options_optional_token_payment_info(
70+
options: &JsValue,
71+
) -> Result<Option<TokenPaymentInfo>, WasmSdkError> {
72+
let token_payment_info_value = Reflect::get(options, &JsValue::from_str("tokenPaymentInfo"))
73+
.map_err(|err| {
74+
WasmSdkError::invalid_argument(format!(
75+
"Failed to read tokenPaymentInfo option: {:?}",
76+
err
77+
))
78+
})?;
79+
80+
if token_payment_info_value.is_null() || token_payment_info_value.is_undefined() {
81+
return Ok(None);
82+
}
83+
84+
let token_payment_info = TokenPaymentInfoWasm::constructor(
85+
token_payment_info_value.unchecked_into::<TokenPaymentInfoOptionsJs>(),
86+
)
87+
.map_err(|err| WasmSdkError::invalid_argument(err.to_string()))?;
88+
89+
Ok(Some(token_payment_info.into()))
90+
}
91+
3092
// ============================================================================
3193
// Document Create
3294
// ============================================================================
@@ -57,6 +119,11 @@ export interface DocumentCreateOptions {
57119
*/
58120
signer: IdentitySigner;
59121
122+
/**
123+
* Optional token payment agreement for document types with tokenCost.create.
124+
*/
125+
tokenPaymentInfo?: DocumentTokenPaymentInfo;
126+
60127
/**
61128
* Optional settings for the broadcast operation.
62129
* Includes retries, timeouts, userFeeIncrease, etc.
@@ -126,6 +193,7 @@ impl WasmSdk {
126193
// Extract settings from options
127194
let settings =
128195
try_from_options_optional::<PutSettingsInput>(&options, "settings")?.map(Into::into);
196+
let token_payment_info = try_from_options_optional_token_payment_info(&options)?;
129197

130198
// Use PutDocument trait for creation
131199
document
@@ -134,7 +202,7 @@ impl WasmSdk {
134202
document_type,
135203
Some(entropy_array),
136204
identity_key,
137-
None, // token_payment_info
205+
token_payment_info,
138206
&signer,
139207
settings,
140208
)
@@ -174,6 +242,11 @@ export interface DocumentReplaceOptions {
174242
*/
175243
signer: IdentitySigner;
176244
245+
/**
246+
* Optional token payment agreement for document types with tokenCost.replace.
247+
*/
248+
tokenPaymentInfo?: DocumentTokenPaymentInfo;
249+
177250
/**
178251
* Optional settings for the broadcast operation.
179252
* Includes retries, timeouts, userFeeIncrease, etc.
@@ -229,6 +302,7 @@ impl WasmSdk {
229302
// Extract settings from options
230303
let settings =
231304
try_from_options_optional::<PutSettingsInput>(&options, "settings")?.map(Into::into);
305+
let token_payment_info = try_from_options_optional_token_payment_info(&options)?;
232306

233307
// Use PutDocument trait for replacement (revision > INITIAL_REVISION triggers replace)
234308
document
@@ -237,7 +311,7 @@ impl WasmSdk {
237311
document_type,
238312
None, // entropy not needed for replace
239313
identity_key,
240-
None, // token_payment_info
314+
token_payment_info,
241315
&signer,
242316
settings,
243317
)
@@ -287,6 +361,11 @@ export interface DocumentDeleteOptions {
287361
*/
288362
signer: IdentitySigner;
289363
364+
/**
365+
* Optional token payment agreement for document types with tokenCost.delete.
366+
*/
367+
tokenPaymentInfo?: DocumentTokenPaymentInfo;
368+
290369
/**
291370
* Optional settings for the broadcast operation.
292371
* Includes retries, timeouts, userFeeIncrease, etc.
@@ -368,6 +447,7 @@ impl WasmSdk {
368447
// Extract settings from options
369448
let settings =
370449
try_from_options_optional::<PutSettingsInput>(&options, "settings")?.map(Into::into);
450+
let token_payment_info = try_from_options_optional_token_payment_info(&options)?;
371451

372452
// Build and execute delete transition using DocumentDeleteTransitionBuilder
373453
let builder = DocumentDeleteTransitionBuilder::new(
@@ -377,6 +457,12 @@ impl WasmSdk {
377457
owner_id,
378458
);
379459

460+
let builder = if let Some(token_payment_info) = token_payment_info {
461+
builder.with_token_payment_info(token_payment_info)
462+
} else {
463+
builder
464+
};
465+
380466
let builder = if let Some(s) = settings {
381467
builder.with_settings(s)
382468
} else {
@@ -425,6 +511,11 @@ export interface DocumentTransferOptions {
425511
*/
426512
signer: IdentitySigner;
427513
514+
/**
515+
* Optional token payment agreement for document types with tokenCost.transfer.
516+
*/
517+
tokenPaymentInfo?: DocumentTokenPaymentInfo;
518+
428519
/**
429520
* Optional settings for the broadcast operation.
430521
* Includes retries, timeouts, userFeeIncrease, etc.
@@ -491,6 +582,7 @@ impl WasmSdk {
491582
// Extract settings from options
492583
let settings =
493584
try_from_options_optional::<PutSettingsInput>(&options, "settings")?.map(Into::into);
585+
let token_payment_info = try_from_options_optional_token_payment_info(&options)?;
494586

495587
// Use TransferDocument trait
496588
document
@@ -499,7 +591,7 @@ impl WasmSdk {
499591
self.inner_sdk(),
500592
document_type,
501593
identity_key,
502-
None, // token_payment_info
594+
token_payment_info,
503595
&signer,
504596
settings,
505597
)
@@ -549,6 +641,11 @@ export interface DocumentPurchaseOptions {
549641
*/
550642
signer: IdentitySigner;
551643
644+
/**
645+
* Optional token payment agreement for document types with tokenCost.purchase.
646+
*/
647+
tokenPaymentInfo?: DocumentTokenPaymentInfo;
648+
552649
/**
553650
* Optional settings for the broadcast operation.
554651
* Includes retries, timeouts, userFeeIncrease, etc.
@@ -609,6 +706,7 @@ impl WasmSdk {
609706
// Extract settings from options
610707
let settings =
611708
try_from_options_optional::<PutSettingsInput>(&options, "settings")?.map(Into::into);
709+
let token_payment_info = try_from_options_optional_token_payment_info(&options)?;
612710

613711
// Use PurchaseDocument trait
614712
document
@@ -618,7 +716,7 @@ impl WasmSdk {
618716
document_type,
619717
buyer_id,
620718
identity_key,
621-
None, // token_payment_info
719+
token_payment_info,
622720
&signer,
623721
settings,
624722
)
@@ -663,6 +761,11 @@ export interface DocumentSetPriceOptions {
663761
*/
664762
signer: IdentitySigner;
665763
764+
/**
765+
* Optional token payment agreement for document types with tokenCost.update_price.
766+
*/
767+
tokenPaymentInfo?: DocumentTokenPaymentInfo;
768+
666769
/**
667770
* Optional settings for the broadcast operation.
668771
* Includes retries, timeouts, userFeeIncrease, etc.
@@ -720,6 +823,7 @@ impl WasmSdk {
720823
// Extract settings from options
721824
let settings =
722825
try_from_options_optional::<PutSettingsInput>(&options, "settings")?.map(Into::into);
826+
let token_payment_info = try_from_options_optional_token_payment_info(&options)?;
723827

724828
// Use UpdatePriceOfDocument trait
725829
document
@@ -728,7 +832,7 @@ impl WasmSdk {
728832
self.inner_sdk(),
729833
document_type,
730834
identity_key,
731-
None, // token_payment_info
835+
token_payment_info,
732836
&signer,
733837
settings,
734838
)

0 commit comments

Comments
 (0)