From 8164265775649ab46a5a79336266112092e09171 Mon Sep 17 00:00:00 2001 From: pauldelucia Date: Mon, 18 Aug 2025 23:20:04 +0700 Subject: [PATCH 1/3] feat(wasm-sdk): implement four missing token transitions --- packages/wasm-sdk/AI_REFERENCE.md | 84 ++---- packages/wasm-sdk/api-definitions.json | 56 +++- packages/wasm-sdk/docs.html | 116 ++++---- packages/wasm-sdk/docs_manifest.json | 2 +- packages/wasm-sdk/index.html | 45 +++ .../src/state_transitions/tokens/mod.rs | 278 +++++++++++++++++- 6 files changed, 444 insertions(+), 137 deletions(-) diff --git a/packages/wasm-sdk/AI_REFERENCE.md b/packages/wasm-sdk/AI_REFERENCE.md index 85c7a81f74..57df8f64ce 100644 --- a/packages/wasm-sdk/AI_REFERENCE.md +++ b/packages/wasm-sdk/AI_REFERENCE.md @@ -757,55 +757,29 @@ const result = await sdk.{transition_name}(identityHex, ...params, privateKeyHex **Identity Create** - `identityCreate` *Create a new identity with initial credits* -Parameters: -- `assetLockProof` (string, required) - Asset lock proof (hex-encoded JSON) -- `assetLockProofPrivateKey` (string, required) - Private key for the asset lock proof (WIF format) -- `publicKeys` (string, required) - JSON array of public keys to add to the identity - -Example: -```javascript -// Asset lock proof is a hex-encoded JSON object -const assetLockProof = "a9147d3b... (hex-encoded)"; -const assetLockProofPrivateKey = "XFfpaSbZq52HPy3WWwe1dXsZMiU1bQn8vQd34HNXkSZThevBWRn1"; // WIF format - -// Public keys array with proper key types -const publicKeys = JSON.stringify([ - { - id: 0, - type: 0, // ECDSA_SECP256K1 = 0, BLS12_381 = 1, ECDSA_HASH160 = 2 - purpose: 0, // AUTHENTICATION = 0, ENCRYPTION = 1, DECRYPTION = 2, TRANSFER = 3, etc. - securityLevel: 0, // MASTER = 0, CRITICAL = 1, HIGH = 2, MEDIUM = 3 - data: "A5GzYHPIolbHkFrp5l+s9IvF2lWMuuuSu3oWZB8vWHNJ", // Base64-encoded public key - readOnly: false - }, - { - id: 1, - type: 0, - purpose: 0, - securityLevel: 2, - data: "AnotherBase64EncodedPublicKeyHere", // Base64-encoded public key - readOnly: false - } -]); +Parameters (in addition to identity/key): +- `seedPhrase` (textarea, required) - Seed Phrase + - Example: `Enter seed phrase (12-24 words) or click Generate` +- `generateSeedButton` (button, optional) - Generate New Seed +- `identityIndex` (number, required) - Identity Index +- `keySelectionMode` (select, required) - Key Selection Mode +- `keyPreview` (keyPreview, optional) - Keys to be added -const result = await sdk.identityCreate(assetLockProof, assetLockProofPrivateKey, publicKeys); +Example: +```javascript +const result = await sdk.identityCreate(identityHex, /* params */, privateKeyHex); ``` **Identity Top Up** - `identityTopUp` *Add credits to an existing identity* -Parameters: -- `identityId` (string, required) - The identity ID to top up (base58 format) -- `assetLockProof` (string, required) - Asset lock proof (hex-encoded JSON) -- `assetLockProofPrivateKey` (string, required) - Private key for the asset lock proof (WIF format) +Parameters (in addition to identity/key): +- `identityId` (text, required) - Identity ID + - Example: `Enter the identity ID to top up (base58)` Example: ```javascript -const identityId = "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk"; // base58 -const assetLockProof = "a9147d3b... (hex-encoded)"; -const assetLockProofPrivateKey = "XFfpaSbZq52HPy3WWwe1dXsZMiU1bQn8vQd34HNXkSZThevBWRn1"; // WIF format - -const result = await sdk.identityTopUp(identityId, assetLockProof, assetLockProofPrivateKey); +const result = await sdk.identityTopUp(identityHex, /* params */, privateKeyHex); ``` **Identity Update** - `identityUpdate` @@ -813,7 +787,7 @@ const result = await sdk.identityTopUp(identityId, assetLockProof, assetLockProo Parameters (in addition to identity/key): - `addPublicKeys` (textarea, optional) - Keys to Add (JSON array) - - Example: `[{"type":0,"purpose":0,"securityLevel":2,"data":"base64_encoded_public_key","readOnly":false}]` + - Example: `[{"keyType":"ECDSA_HASH160","purpose":"AUTHENTICATION","data":"base64_key_data"}]` - `disablePublicKeys` (text, optional) - Key IDs to Disable (comma-separated) - Example: `2,3,5` @@ -1114,13 +1088,14 @@ const result = await sdk.tokenConfigUpdate(identityHex, /* params */, privateKey ``` **Token Transfer** - `tokenTransfer` -*Transfer tokens to another identity* +*Transfer tokens between identities* Parameters (in addition to identity/key): - `contractId` (text, required) - Data Contract ID -- `tokenId` (text, required) - Token Contract Position -- `amount` (number, required) - Amount to Transfer +- `tokenPosition` (number, required) - Token Contract Position +- `amount` (text, required) - Amount to Transfer - `recipientId` (text, required) - Recipient Identity ID +- `publicNote` (text, optional) - Public Note Example: ```javascript @@ -1135,12 +1110,13 @@ const result = await sdk.token_transfer( ``` **Token Freeze** - `tokenFreeze` -*Freeze tokens for an identity* +*Freeze tokens for a specific identity* Parameters (in addition to identity/key): - `contractId` (text, required) - Data Contract ID -- `tokenId` (text, required) - Token Contract Position -- `identityId` (text, required) - Identity ID to Freeze +- `tokenPosition` (number, required) - Token Contract Position +- `identityToFreeze` (text, required) - Identity ID to Freeze +- `publicNote` (text, optional) - Public Note Example: ```javascript @@ -1148,25 +1124,27 @@ const result = await sdk.tokenFreeze(identityHex, /* params */, privateKeyHex); ``` **Token Unfreeze** - `tokenUnfreeze` -*Unfreeze tokens for an identity* +*Unfreeze tokens for a specific identity* Parameters (in addition to identity/key): - `contractId` (text, required) - Data Contract ID -- `tokenId` (text, required) - Token Contract Position -- `identityId` (text, required) - Identity ID to Unfreeze +- `tokenPosition` (number, required) - Token Contract Position +- `identityToUnfreeze` (text, required) - Identity ID to Unfreeze +- `publicNote` (text, optional) - Public Note Example: ```javascript const result = await sdk.tokenUnfreeze(identityHex, /* params */, privateKeyHex); ``` -**Token Destroy Frozen Funds** - `tokenDestroyFrozen` +**Token Destroy Frozen** - `tokenDestroyFrozen` *Destroy frozen tokens* Parameters (in addition to identity/key): - `contractId` (text, required) - Data Contract ID -- `tokenId` (text, required) - Token Contract Position -- `identityId` (text, required) - Identity ID +- `tokenPosition` (number, required) - Token Contract Position +- `identityId` (text, required) - Identity ID whose frozen tokens to destroy +- `publicNote` (text, optional) - Public Note Example: ```javascript diff --git a/packages/wasm-sdk/api-definitions.json b/packages/wasm-sdk/api-definitions.json index ed1be49b62..a2b91d2e6e 100644 --- a/packages/wasm-sdk/api-definitions.json +++ b/packages/wasm-sdk/api-definitions.json @@ -1915,7 +1915,7 @@ }, "tokenTransfer": { "label": "Token Transfer", - "description": "Transfer tokens to another identity", + "description": "Transfer tokens between identities", "inputs": [ { "name": "contractId", @@ -1924,14 +1924,14 @@ "required": true }, { - "name": "tokenId", - "type": "text", + "name": "tokenPosition", + "type": "number", "label": "Token Contract Position", "required": true }, { "name": "amount", - "type": "number", + "type": "text", "label": "Amount to Transfer", "required": true }, @@ -1940,12 +1940,18 @@ "type": "text", "label": "Recipient Identity ID", "required": true + }, + { + "name": "publicNote", + "type": "text", + "label": "Public Note", + "required": false } ] }, "tokenFreeze": { "label": "Token Freeze", - "description": "Freeze tokens for an identity", + "description": "Freeze tokens for a specific identity", "inputs": [ { "name": "contractId", @@ -1954,22 +1960,28 @@ "required": true }, { - "name": "tokenId", - "type": "text", + "name": "tokenPosition", + "type": "number", "label": "Token Contract Position", "required": true }, { - "name": "identityId", + "name": "identityToFreeze", "type": "text", "label": "Identity ID to Freeze", "required": true + }, + { + "name": "publicNote", + "type": "text", + "label": "Public Note", + "required": false } ] }, "tokenUnfreeze": { "label": "Token Unfreeze", - "description": "Unfreeze tokens for an identity", + "description": "Unfreeze tokens for a specific identity", "inputs": [ { "name": "contractId", @@ -1978,21 +1990,27 @@ "required": true }, { - "name": "tokenId", - "type": "text", + "name": "tokenPosition", + "type": "number", "label": "Token Contract Position", "required": true }, { - "name": "identityId", + "name": "identityToUnfreeze", "type": "text", "label": "Identity ID to Unfreeze", "required": true + }, + { + "name": "publicNote", + "type": "text", + "label": "Public Note", + "required": false } ] }, "tokenDestroyFrozen": { - "label": "Token Destroy Frozen Funds", + "label": "Token Destroy Frozen", "description": "Destroy frozen tokens", "inputs": [ { @@ -2002,16 +2020,22 @@ "required": true }, { - "name": "tokenId", - "type": "text", + "name": "tokenPosition", + "type": "number", "label": "Token Contract Position", "required": true }, { "name": "identityId", "type": "text", - "label": "Identity ID", + "label": "Identity ID whose frozen tokens to destroy", "required": true + }, + { + "name": "publicNote", + "type": "text", + "label": "Public Note", + "required": false } ] } diff --git a/packages/wasm-sdk/docs.html b/packages/wasm-sdk/docs.html index 36b873d146..4eeaaba1ea 100644 --- a/packages/wasm-sdk/docs.html +++ b/packages/wasm-sdk/docs.html @@ -465,7 +465,7 @@

Table of Contents

  • Token Transfer
  • Token Freeze
  • Token Unfreeze
  • -
  • Token Destroy Frozen Funds
  • +
  • Token Destroy Frozen
  • Voting Transitions
  • DPNS Username
  • Contested Resource
  • @@ -1952,52 +1952,37 @@

    Identity Create

    Parameters:
    - Asset Lock Proof - string + Seed Phrase + textarea (required) -
    Hex-encoded JSON asset lock proof +
    Example: Enter seed phrase (12-24 words) or click Generate
    - Asset Lock Proof Private Key - string + Generate New Seed + button + (optional) +
    +
    + Identity Index + number (required) -
    WIF format private key
    - Public Keys - string + Key Selection Mode + select (required) -
    JSON array of public keys +
    Options: Default (Recommended), Advanced +
    +
    + Keys to be added + keyPreview + (optional)
    Example
    -
    // Asset lock proof is a hex-encoded JSON object -const assetLockProof = "a9147d3b... (hex-encoded)"; -const assetLockProofPrivateKey = "XFfpaSbZq52HPy3WWwe1dXsZMiU1bQn8vQd34HNXkSZThevBWRn1"; // WIF format - -// Public keys array with proper key types -const publicKeys = JSON.stringify([ - { - id: 0, - type: 0, // ECDSA_SECP256K1 = 0, BLS12_381 = 1, ECDSA_HASH160 = 2 - purpose: 0, // AUTHENTICATION = 0, ENCRYPTION = 1, DECRYPTION = 2, TRANSFER = 3, etc. - securityLevel: 0, // MASTER = 0, CRITICAL = 1, HIGH = 2, MEDIUM = 3 - data: "A5GzYHPIolbHkFrp5l+s9IvF2lWMuuuSu3oWZB8vWHNJ", // Base64-encoded public key - readOnly: false - }, - { - id: 1, - type: 0, - purpose: 0, - securityLevel: 2, - data: "AnotherBase64EncodedPublicKeyHere", // Base64-encoded public key - readOnly: false - } -]); - -const result = await sdk.identityCreate(assetLockProof, assetLockProofPrivateKey, publicKeys);
    +
    const result = await sdk.identityCreate(identityHex, /* params */, privateKeyHex);

    Identity Top Up

    @@ -2007,30 +1992,15 @@

    Identity Top Up

    Parameters:
    Identity ID - string - (required) -
    Base58 format identity ID -
    -
    - Asset Lock Proof - string + text (required) -
    Hex-encoded JSON asset lock proof +
    Example: Enter the identity ID to top up (base58)
    -
    - Asset Lock Proof Private Key - string - (required) -
    WIF format private key
    Example
    -
    const identityId = "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk"; // base58 -const assetLockProof = "a9147d3b... (hex-encoded)"; -const assetLockProofPrivateKey = "XFfpaSbZq52HPy3WWwe1dXsZMiU1bQn8vQd34HNXkSZThevBWRn1"; // WIF format - -const result = await sdk.identityTopUp(identityId, assetLockProof, assetLockProofPrivateKey);
    +
    const result = await sdk.identityTopUp(identityHex, /* params */, privateKeyHex);

    Identity Update

    @@ -2689,7 +2659,7 @@
    Example

    Token Transfer

    -

    Transfer tokens to another identity

    +

    Transfer tokens between identities

    Parameters:
    @@ -2700,12 +2670,12 @@
    Parameters:
    Token Contract Position - text + number (required)
    Amount to Transfer - number + text (required)
    @@ -2713,6 +2683,11 @@
    Parameters:
    text (required)
    +
    + Public Note + text + (optional) +
    @@ -2728,7 +2703,7 @@
    Example

    Token Freeze

    -

    Freeze tokens for an identity

    +

    Freeze tokens for a specific identity

    Parameters:
    @@ -2739,7 +2714,7 @@
    Parameters:
    Token Contract Position - text + number (required)
    @@ -2747,6 +2722,11 @@
    Parameters:
    text (required)
    +
    + Public Note + text + (optional) +
    @@ -2755,7 +2735,7 @@
    Example

    Token Unfreeze

    -

    Unfreeze tokens for an identity

    +

    Unfreeze tokens for a specific identity

    Parameters:
    @@ -2766,7 +2746,7 @@
    Parameters:
    Token Contract Position - text + number (required)
    @@ -2774,6 +2754,11 @@
    Parameters:
    text (required)
    +
    + Public Note + text + (optional) +
    @@ -2781,7 +2766,7 @@
    Example
    const result = await sdk.tokenUnfreeze(identityHex, /* params */, privateKeyHex);
    -

    Token Destroy Frozen Funds

    +

    Token Destroy Frozen

    Destroy frozen tokens

    @@ -2793,14 +2778,19 @@
    Parameters:
    Token Contract Position - text + number (required)
    - Identity ID + Identity ID whose frozen tokens to destroy text (required)
    +
    + Public Note + text + (optional) +
    diff --git a/packages/wasm-sdk/docs_manifest.json b/packages/wasm-sdk/docs_manifest.json index c3a4999d63..4de9028611 100644 --- a/packages/wasm-sdk/docs_manifest.json +++ b/packages/wasm-sdk/docs_manifest.json @@ -1,5 +1,5 @@ { - "generated_at": "2025-08-14T18:48:19.291132+00:00", + "generated_at": "2025-08-18T16:09:12.996174+00:00", "queries": { "getIdentity": { "category": "identity", diff --git a/packages/wasm-sdk/index.html b/packages/wasm-sdk/index.html index 514f34892f..93f8083832 100644 --- a/packages/wasm-sdk/index.html +++ b/packages/wasm-sdk/index.html @@ -3206,6 +3206,51 @@

    Results

    ); displayResult(JSON.stringify(result, null, 2)); updateStatusWithTime('Token configuration updated successfully', 'success', startTime); + } else if (transitionType === 'tokenTransfer') { + result = await sdk.tokenTransfer( + values.contractId, + Number(values.tokenPosition), + values.amount, + identityId, // sender ID + values.recipientId, + privateKey, + values.publicNote || null + ); + displayResult(JSON.stringify(result, null, 2)); + updateStatusWithTime('Tokens transferred successfully', 'success', startTime); + } else if (transitionType === 'tokenFreeze') { + result = await sdk.tokenFreeze( + values.contractId, + Number(values.tokenPosition), + values.identityToFreeze, + identityId, // freezer ID + privateKey, + values.publicNote || null + ); + displayResult(JSON.stringify(result, null, 2)); + updateStatusWithTime('Tokens frozen successfully', 'success', startTime); + } else if (transitionType === 'tokenUnfreeze') { + result = await sdk.tokenUnfreeze( + values.contractId, + Number(values.tokenPosition), + values.identityToUnfreeze, + identityId, // unfreezer ID + privateKey, + values.publicNote || null + ); + displayResult(JSON.stringify(result, null, 2)); + updateStatusWithTime('Tokens unfrozen successfully', 'success', startTime); + } else if (transitionType === 'tokenDestroyFrozen') { + result = await sdk.tokenDestroyFrozen( + values.contractId, + Number(values.tokenPosition), + values.identityId, // identity whose frozen tokens to destroy + identityId, // destroyer ID + privateKey, + values.publicNote || null + ); + displayResult(JSON.stringify(result, null, 2)); + updateStatusWithTime('Frozen tokens destroyed successfully', 'success', startTime); } else if (transitionType === 'documentCreate') { // Collect document fields from dynamic inputs const documentData = collectDocumentFields(); diff --git a/packages/wasm-sdk/src/state_transitions/tokens/mod.rs b/packages/wasm-sdk/src/state_transitions/tokens/mod.rs index ec9992689c..e5b1509c51 100644 --- a/packages/wasm-sdk/src/state_transitions/tokens/mod.rs +++ b/packages/wasm-sdk/src/state_transitions/tokens/mod.rs @@ -317,6 +317,7 @@ impl WasmSdk { /// * `sender_id` - The identity ID of the sender /// * `recipient_id` - The identity ID of the recipient /// * `private_key_wif` - The private key in WIF format for signing + /// * `public_note` - Optional public note for the transfer /// /// # Returns /// @@ -330,8 +331,76 @@ impl WasmSdk { sender_id: String, recipient_id: String, private_key_wif: String, + public_note: Option, ) -> Result { - Err(JsValue::from_str("Token transfer not yet implemented - similar pattern to mint/burn")) + let sdk = self.inner_clone(); + + // Parse and validate parameters + let (contract_id, sender_identifier, token_amount, _) = self.parse_token_params( + &data_contract_id, + &sender_id, + &amount, + None, + ).await?; + + // Parse recipient ID + let recipient_identifier = Identifier::from_string(&recipient_id, Encoding::Base58) + .map_err(|e| JsValue::from_str(&format!("Invalid recipient ID: {}", e)))?; + + // Fetch and cache the data contract + let _data_contract = self.fetch_and_cache_token_contract(contract_id).await?; + + // Get identity to find matching authentication key + let identity = dash_sdk::platform::Identity::fetch(&sdk, sender_identifier) + .await + .map_err(|e| JsValue::from_str(&format!("Failed to fetch identity: {}", e)))? + .ok_or_else(|| JsValue::from_str("Identity not found"))?; + + // Get identity contract nonce + let identity_contract_nonce = sdk + .get_identity_contract_nonce(sender_identifier, contract_id, true, None) + .await + .map_err(|e| JsValue::from_str(&format!("Failed to fetch nonce: {}", e)))?; + + // Find matching authentication key and create signer + let (_, matching_key) = crate::sdk::WasmSdk::find_authentication_key(&identity, &private_key_wif)?; + let signer = crate::sdk::WasmSdk::create_signer_from_wif(&private_key_wif, sdk.network)?; + let public_key = matching_key.clone(); + + // Calculate token ID + let token_id = Identifier::from(calculate_token_id( + contract_id.as_bytes(), + token_position, + )); + + // Create the state transition + let platform_version = sdk.version(); + let state_transition = BatchTransition::new_token_transfer_transition( + token_id, + sender_identifier, + contract_id, + token_position, + token_amount, + recipient_identifier, + public_note, + None, // shared_encrypted_note + None, // private_encrypted_note + &public_key, + identity_contract_nonce, + UserFeeIncrease::default(), + &signer, + platform_version, + None, // state_transition_creation_options + ).map_err(|e| JsValue::from_str(&format!("Failed to create transfer transition: {}", e)))?; + + // Broadcast the transition + let proof_result = state_transition + .broadcast_and_wait::(&sdk, None) + .await + .map_err(|e| JsValue::from_str(&format!("Failed to broadcast transition: {}", e)))?; + + // Format and return result + self.format_token_result(proof_result) } /// Freeze tokens for a specific identity. @@ -343,6 +412,7 @@ impl WasmSdk { /// * `identity_to_freeze` - The identity ID whose tokens to freeze /// * `freezer_id` - The identity ID of the freezer (must have permission) /// * `private_key_wif` - The private key in WIF format for signing + /// * `public_note` - Optional public note for the freeze operation /// /// # Returns /// @@ -355,8 +425,74 @@ impl WasmSdk { identity_to_freeze: String, freezer_id: String, private_key_wif: String, + public_note: Option, ) -> Result { - Err(JsValue::from_str("Token freeze not yet implemented")) + let sdk = self.inner_clone(); + + // Parse and validate parameters + let (contract_id, freezer_identifier, _, _) = self.parse_token_params( + &data_contract_id, + &freezer_id, + "0", // Amount not needed for freeze + None, + ).await?; + + // Parse identity to freeze + let frozen_identity_id = Identifier::from_string(&identity_to_freeze, Encoding::Base58) + .map_err(|e| JsValue::from_str(&format!("Invalid identity to freeze: {}", e)))?; + + // Fetch and cache the data contract + let _data_contract = self.fetch_and_cache_token_contract(contract_id).await?; + + // Get identity to find matching authentication key + let identity = dash_sdk::platform::Identity::fetch(&sdk, freezer_identifier) + .await + .map_err(|e| JsValue::from_str(&format!("Failed to fetch identity: {}", e)))? + .ok_or_else(|| JsValue::from_str("Identity not found"))?; + + // Get identity contract nonce + let identity_contract_nonce = sdk + .get_identity_contract_nonce(freezer_identifier, contract_id, true, None) + .await + .map_err(|e| JsValue::from_str(&format!("Failed to fetch nonce: {}", e)))?; + + // Find matching authentication key and create signer + let (_, matching_key) = crate::sdk::WasmSdk::find_authentication_key(&identity, &private_key_wif)?; + let signer = crate::sdk::WasmSdk::create_signer_from_wif(&private_key_wif, sdk.network)?; + let public_key = matching_key.clone(); + + // Calculate token ID + let token_id = Identifier::from(calculate_token_id( + contract_id.as_bytes(), + token_position, + )); + + // Create the state transition + let platform_version = sdk.version(); + let state_transition = BatchTransition::new_token_freeze_transition( + token_id, + freezer_identifier, + contract_id, + token_position, + frozen_identity_id, + public_note, + None, // using_group_info + &public_key, + identity_contract_nonce, + UserFeeIncrease::default(), + &signer, + platform_version, + None, // state_transition_creation_options + ).map_err(|e| JsValue::from_str(&format!("Failed to create freeze transition: {}", e)))?; + + // Broadcast the transition + let proof_result = state_transition + .broadcast_and_wait::(&sdk, None) + .await + .map_err(|e| JsValue::from_str(&format!("Failed to broadcast transition: {}", e)))?; + + // Format and return result + self.format_token_result(proof_result) } /// Unfreeze tokens for a specific identity. @@ -368,6 +504,7 @@ impl WasmSdk { /// * `identity_to_unfreeze` - The identity ID whose tokens to unfreeze /// * `unfreezer_id` - The identity ID of the unfreezer (must have permission) /// * `private_key_wif` - The private key in WIF format for signing + /// * `public_note` - Optional public note for the unfreeze operation /// /// # Returns /// @@ -380,8 +517,74 @@ impl WasmSdk { identity_to_unfreeze: String, unfreezer_id: String, private_key_wif: String, + public_note: Option, ) -> Result { - Err(JsValue::from_str("Token unfreeze not yet implemented")) + let sdk = self.inner_clone(); + + // Parse and validate parameters + let (contract_id, unfreezer_identifier, _, _) = self.parse_token_params( + &data_contract_id, + &unfreezer_id, + "0", // Amount not needed for unfreeze + None, + ).await?; + + // Parse identity to unfreeze + let frozen_identity_id = Identifier::from_string(&identity_to_unfreeze, Encoding::Base58) + .map_err(|e| JsValue::from_str(&format!("Invalid identity to unfreeze: {}", e)))?; + + // Fetch and cache the data contract + let _data_contract = self.fetch_and_cache_token_contract(contract_id).await?; + + // Get identity to find matching authentication key + let identity = dash_sdk::platform::Identity::fetch(&sdk, unfreezer_identifier) + .await + .map_err(|e| JsValue::from_str(&format!("Failed to fetch identity: {}", e)))? + .ok_or_else(|| JsValue::from_str("Identity not found"))?; + + // Get identity contract nonce + let identity_contract_nonce = sdk + .get_identity_contract_nonce(unfreezer_identifier, contract_id, true, None) + .await + .map_err(|e| JsValue::from_str(&format!("Failed to fetch nonce: {}", e)))?; + + // Find matching authentication key and create signer + let (_, matching_key) = crate::sdk::WasmSdk::find_authentication_key(&identity, &private_key_wif)?; + let signer = crate::sdk::WasmSdk::create_signer_from_wif(&private_key_wif, sdk.network)?; + let public_key = matching_key.clone(); + + // Calculate token ID + let token_id = Identifier::from(calculate_token_id( + contract_id.as_bytes(), + token_position, + )); + + // Create the state transition + let platform_version = sdk.version(); + let state_transition = BatchTransition::new_token_unfreeze_transition( + token_id, + unfreezer_identifier, + contract_id, + token_position, + frozen_identity_id, + public_note, + None, // using_group_info + &public_key, + identity_contract_nonce, + UserFeeIncrease::default(), + &signer, + platform_version, + None, // state_transition_creation_options + ).map_err(|e| JsValue::from_str(&format!("Failed to create unfreeze transition: {}", e)))?; + + // Broadcast the transition + let proof_result = state_transition + .broadcast_and_wait::(&sdk, None) + .await + .map_err(|e| JsValue::from_str(&format!("Failed to broadcast transition: {}", e)))?; + + // Format and return result + self.format_token_result(proof_result) } /// Destroy frozen tokens. @@ -393,6 +596,7 @@ impl WasmSdk { /// * `identity_id` - The identity ID whose frozen tokens to destroy /// * `destroyer_id` - The identity ID of the destroyer (must have permission) /// * `private_key_wif` - The private key in WIF format for signing + /// * `public_note` - Optional public note for the destroy operation /// /// # Returns /// @@ -405,8 +609,74 @@ impl WasmSdk { identity_id: String, destroyer_id: String, private_key_wif: String, + public_note: Option, ) -> Result { - Err(JsValue::from_str("Token destroy frozen not yet implemented")) + let sdk = self.inner_clone(); + + // Parse and validate parameters + let (contract_id, destroyer_identifier, _, _) = self.parse_token_params( + &data_contract_id, + &destroyer_id, + "0", // Amount not needed for destroy frozen + None, + ).await?; + + // Parse identity whose frozen tokens to destroy + let frozen_identity_id = Identifier::from_string(&identity_id, Encoding::Base58) + .map_err(|e| JsValue::from_str(&format!("Invalid identity to destroy frozen funds: {}", e)))?; + + // Fetch and cache the data contract + let _data_contract = self.fetch_and_cache_token_contract(contract_id).await?; + + // Get identity to find matching authentication key + let identity = dash_sdk::platform::Identity::fetch(&sdk, destroyer_identifier) + .await + .map_err(|e| JsValue::from_str(&format!("Failed to fetch identity: {}", e)))? + .ok_or_else(|| JsValue::from_str("Identity not found"))?; + + // Get identity contract nonce + let identity_contract_nonce = sdk + .get_identity_contract_nonce(destroyer_identifier, contract_id, true, None) + .await + .map_err(|e| JsValue::from_str(&format!("Failed to fetch nonce: {}", e)))?; + + // Find matching authentication key and create signer + let (_, matching_key) = crate::sdk::WasmSdk::find_authentication_key(&identity, &private_key_wif)?; + let signer = crate::sdk::WasmSdk::create_signer_from_wif(&private_key_wif, sdk.network)?; + let public_key = matching_key.clone(); + + // Calculate token ID + let token_id = Identifier::from(calculate_token_id( + contract_id.as_bytes(), + token_position, + )); + + // Create the state transition + let platform_version = sdk.version(); + let state_transition = BatchTransition::new_token_destroy_frozen_funds_transition( + token_id, + destroyer_identifier, + contract_id, + token_position, + frozen_identity_id, + public_note, + None, // using_group_info + &public_key, + identity_contract_nonce, + UserFeeIncrease::default(), + &signer, + platform_version, + None, // state_transition_creation_options + ).map_err(|e| JsValue::from_str(&format!("Failed to create destroy frozen transition: {}", e)))?; + + // Broadcast the transition + let proof_result = state_transition + .broadcast_and_wait::(&sdk, None) + .await + .map_err(|e| JsValue::from_str(&format!("Failed to broadcast transition: {}", e)))?; + + // Format and return result + self.format_token_result(proof_result) } /// Set or update the price for direct token purchases. From a1ee9cf5394f75e5250d05ed374dc0ba1fdd1979 Mon Sep 17 00:00:00 2001 From: pauldelucia Date: Mon, 18 Aug 2025 23:38:48 +0700 Subject: [PATCH 2/3] feat: add tests for new token transitions --- packages/wasm-sdk/test/README_TOKEN_TESTS.md | 64 ++++ .../wasm-sdk/test/token-transitions.test.mjs | 354 ++++++++++++++++++ 2 files changed, 418 insertions(+) create mode 100644 packages/wasm-sdk/test/README_TOKEN_TESTS.md create mode 100644 packages/wasm-sdk/test/token-transitions.test.mjs diff --git a/packages/wasm-sdk/test/README_TOKEN_TESTS.md b/packages/wasm-sdk/test/README_TOKEN_TESTS.md new file mode 100644 index 0000000000..b53572bc3c --- /dev/null +++ b/packages/wasm-sdk/test/README_TOKEN_TESTS.md @@ -0,0 +1,64 @@ +# Token Transition Tests + +## Overview +This directory contains tests for the token state transitions in the WASM SDK. + +## New Token Transitions (Implemented) +The following token transitions have been implemented and added to the SDK: + +1. **tokenTransfer** - Transfer tokens between identities +2. **tokenFreeze** - Freeze tokens for a specific identity +3. **tokenUnfreeze** - Unfreeze tokens for a specific identity +4. **tokenDestroyFrozen** - Destroy frozen tokens + +## Test Files + +### token-transitions.test.mjs +New test file that tests the four newly implemented token transitions: +- Tests parameter validation +- Tests error handling for invalid inputs +- Tests permission requirements +- Verifies all methods are available on the SDK instance + +### state-transitions.test.mjs (Needs Update) +The existing state transitions test file contains an outdated test for `token_transfer` (line 307-325) that uses the old function signature: +```javascript +// OLD (no longer exists) +await wasmSdk.token_transfer(sdk, mnemonic, identity, contract, recipient, amount, keyIndex) + +// NEW (implemented) +await sdk.tokenTransfer(contractId, position, amount, senderId, recipientId, privateKey, publicNote) +``` + +This test should be updated or removed since the old function no longer exists. + +## Running Tests + +To run the token transition tests: + +1. First build the WASM SDK: + ```bash + ./build.sh + ``` + +2. Then run the tests: + ```bash + node test/token-transitions.test.mjs + ``` + +## Expected Results + +Most tests will fail with permission/identity errors, which is expected behavior since we're testing without real funded identities. The important validations are: + +1. All methods are available on the SDK instance +2. Parameter validation works correctly +3. Invalid inputs are rejected with appropriate errors +4. The methods attempt to connect to the network (even if they fail due to permissions) + +## Integration with UI + +The token transitions are also exposed in the HTML UI (index.html) and defined in api-definitions.json, allowing users to: +- Execute token transfers through the web interface +- Freeze and unfreeze tokens +- Destroy frozen tokens +- All with optional public notes for transparency \ No newline at end of file diff --git a/packages/wasm-sdk/test/token-transitions.test.mjs b/packages/wasm-sdk/test/token-transitions.test.mjs new file mode 100644 index 0000000000..4d6d51ceaf --- /dev/null +++ b/packages/wasm-sdk/test/token-transitions.test.mjs @@ -0,0 +1,354 @@ +#!/usr/bin/env node +// token-transitions.test.mjs - Tests for new token state transition functions + +import { readFileSync } from 'fs'; +import { fileURLToPath } from 'url'; +import { dirname, join } from 'path'; +import { webcrypto } from 'crypto'; + +// Get directory paths +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +// Set up globals for WASM +if (!global.crypto) { + Object.defineProperty(global, 'crypto', { + value: webcrypto, + writable: true, + configurable: true + }); +} + +// Import WASM SDK +import init, * as wasmSdk from '../pkg/wasm_sdk.js'; + +// Initialize WASM +console.log('Initializing WASM SDK...'); +const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm'); +const wasmBuffer = readFileSync(wasmPath); +await init(wasmBuffer); + +// Test utilities +let passed = 0; +let failed = 0; + +async function test(name, fn) { + try { + await fn(); + console.log(`✅ ${name}`); + passed++; + } catch (error) { + console.log(`❌ ${name}`); + console.log(` ${error.message}`); + failed++; + } +} + +function describe(name) { + console.log(`\n${name}`); +} + +console.log('\nToken State Transition Tests\n'); + +// Initialize SDK - use trusted builder for WASM +console.log('Prefetching trusted quorums...'); +try { + await wasmSdk.prefetch_trusted_quorums_testnet(); + console.log('Quorums prefetched successfully'); +} catch (error) { + console.log('Warning: Could not prefetch quorums:', error.message); +} + +const builder = wasmSdk.WasmSdkBuilder.new_testnet_trusted(); +const sdk = await builder.build(); + +// Test values +const TEST_CONTRACT_ID = 'Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv'; +const TEST_TOKEN_POSITION = 0; +const TEST_IDENTITY_ID = '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk'; +const TEST_RECIPIENT_ID = '3mFKtDYspCMd8YmXNTB3qzKmbY3Azf4Kx3x8e36V8Gho'; +const TEST_PRIVATE_KEY = 'KycRvJNvCVapwvvpRLWz76qXFAbXFfAqhG9FouVjUmDVZ6UtZfGa'; // Dummy key for testing + +// Token Transfer Tests +describe('Token Transfer State Transition'); + +await test('tokenTransfer - should validate parameters', async () => { + try { + // Test with invalid contract ID + await sdk.tokenTransfer( + 'invalid-contract-id', + TEST_TOKEN_POSITION, + '1000', + TEST_IDENTITY_ID, + TEST_RECIPIENT_ID, + TEST_PRIVATE_KEY, + 'Test transfer' + ); + throw new Error('Should fail with invalid contract ID'); + } catch (error) { + if (error && error.message && error.message.includes('Should fail')) { + throw error; + } + console.log(' Expected error with invalid contract ID'); + } +}); + +await test('tokenTransfer - should validate amount', async () => { + try { + // Test with invalid amount + await sdk.tokenTransfer( + TEST_CONTRACT_ID, + TEST_TOKEN_POSITION, + 'invalid-amount', + TEST_IDENTITY_ID, + TEST_RECIPIENT_ID, + TEST_PRIVATE_KEY, + null + ); + throw new Error('Should fail with invalid amount'); + } catch (error) { + if (error && error.message && error.message.includes('Should fail')) { + throw error; + } + console.log(' Expected error with invalid amount'); + } +}); + +await test('tokenTransfer - should require valid identity', async () => { + try { + // This will fail because the identity doesn't exist or we don't have the right key + await sdk.tokenTransfer( + TEST_CONTRACT_ID, + TEST_TOKEN_POSITION, + '1000', + TEST_IDENTITY_ID, + TEST_RECIPIENT_ID, + TEST_PRIVATE_KEY, + 'Test transfer' + ); + throw new Error('Should fail without valid identity/key'); + } catch (error) { + if (error && error.message && error.message.includes('Should fail')) { + throw error; + } + console.log(' Expected error without valid identity/key'); + } +}); + +// Token Freeze Tests +describe('Token Freeze State Transition'); + +await test('tokenFreeze - should validate parameters', async () => { + try { + // Test with invalid contract ID + await sdk.tokenFreeze( + 'invalid-contract-id', + TEST_TOKEN_POSITION, + TEST_RECIPIENT_ID, + TEST_IDENTITY_ID, + TEST_PRIVATE_KEY, + 'Freezing tokens' + ); + throw new Error('Should fail with invalid contract ID'); + } catch (error) { + if (error && error.message && error.message.includes('Should fail')) { + throw error; + } + console.log(' Expected error with invalid contract ID'); + } +}); + +await test('tokenFreeze - should validate identity to freeze', async () => { + try { + // Test with invalid identity ID + await sdk.tokenFreeze( + TEST_CONTRACT_ID, + TEST_TOKEN_POSITION, + 'invalid-identity', + TEST_IDENTITY_ID, + TEST_PRIVATE_KEY, + null + ); + throw new Error('Should fail with invalid identity to freeze'); + } catch (error) { + if (error && error.message && error.message.includes('Should fail')) { + throw error; + } + console.log(' Expected error with invalid identity to freeze'); + } +}); + +await test('tokenFreeze - should require freezer permissions', async () => { + try { + // This will fail because the identity doesn't have freeze permissions + await sdk.tokenFreeze( + TEST_CONTRACT_ID, + TEST_TOKEN_POSITION, + TEST_RECIPIENT_ID, + TEST_IDENTITY_ID, + TEST_PRIVATE_KEY, + 'Test freeze' + ); + throw new Error('Should fail without freeze permissions'); + } catch (error) { + if (error && error.message && error.message.includes('Should fail')) { + throw error; + } + console.log(' Expected error without freeze permissions'); + } +}); + +// Token Unfreeze Tests +describe('Token Unfreeze State Transition'); + +await test('tokenUnfreeze - should validate parameters', async () => { + try { + // Test with invalid contract ID + await sdk.tokenUnfreeze( + 'invalid-contract-id', + TEST_TOKEN_POSITION, + TEST_RECIPIENT_ID, + TEST_IDENTITY_ID, + TEST_PRIVATE_KEY, + 'Unfreezing tokens' + ); + throw new Error('Should fail with invalid contract ID'); + } catch (error) { + if (error && error.message && error.message.includes('Should fail')) { + throw error; + } + console.log(' Expected error with invalid contract ID'); + } +}); + +await test('tokenUnfreeze - should validate identity to unfreeze', async () => { + try { + // Test with invalid identity ID + await sdk.tokenUnfreeze( + TEST_CONTRACT_ID, + TEST_TOKEN_POSITION, + 'invalid-identity', + TEST_IDENTITY_ID, + TEST_PRIVATE_KEY, + null + ); + throw new Error('Should fail with invalid identity to unfreeze'); + } catch (error) { + if (error && error.message && error.message.includes('Should fail')) { + throw error; + } + console.log(' Expected error with invalid identity to unfreeze'); + } +}); + +await test('tokenUnfreeze - should require unfreezer permissions', async () => { + try { + // This will fail because the identity doesn't have unfreeze permissions + await sdk.tokenUnfreeze( + TEST_CONTRACT_ID, + TEST_TOKEN_POSITION, + TEST_RECIPIENT_ID, + TEST_IDENTITY_ID, + TEST_PRIVATE_KEY, + 'Test unfreeze' + ); + throw new Error('Should fail without unfreeze permissions'); + } catch (error) { + if (error && error.message && error.message.includes('Should fail')) { + throw error; + } + console.log(' Expected error without unfreeze permissions'); + } +}); + +// Token Destroy Frozen Tests +describe('Token Destroy Frozen State Transition'); + +await test('tokenDestroyFrozen - should validate parameters', async () => { + try { + // Test with invalid contract ID + await sdk.tokenDestroyFrozen( + 'invalid-contract-id', + TEST_TOKEN_POSITION, + TEST_RECIPIENT_ID, + TEST_IDENTITY_ID, + TEST_PRIVATE_KEY, + 'Destroying frozen tokens' + ); + throw new Error('Should fail with invalid contract ID'); + } catch (error) { + if (error && error.message && error.message.includes('Should fail')) { + throw error; + } + console.log(' Expected error with invalid contract ID'); + } +}); + +await test('tokenDestroyFrozen - should validate identity', async () => { + try { + // Test with invalid identity ID + await sdk.tokenDestroyFrozen( + TEST_CONTRACT_ID, + TEST_TOKEN_POSITION, + 'invalid-identity', + TEST_IDENTITY_ID, + TEST_PRIVATE_KEY, + null + ); + throw new Error('Should fail with invalid identity'); + } catch (error) { + if (error && error.message && error.message.includes('Should fail')) { + throw error; + } + console.log(' Expected error with invalid identity'); + } +}); + +await test('tokenDestroyFrozen - should require destroyer permissions', async () => { + try { + // This will fail because the identity doesn't have destroy permissions + await sdk.tokenDestroyFrozen( + TEST_CONTRACT_ID, + TEST_TOKEN_POSITION, + TEST_RECIPIENT_ID, + TEST_IDENTITY_ID, + TEST_PRIVATE_KEY, + 'Test destroy frozen' + ); + throw new Error('Should fail without destroy permissions'); + } catch (error) { + if (error && error.message && error.message.includes('Should fail')) { + throw error; + } + console.log(' Expected error without destroy permissions'); + } +}); + +// Method Availability Tests +describe('Token Transition Methods Availability'); + +await test('All new token transition methods should be available on SDK', async () => { + if (typeof sdk.tokenTransfer !== 'function') { + throw new Error('tokenTransfer method not found on SDK instance'); + } + if (typeof sdk.tokenFreeze !== 'function') { + throw new Error('tokenFreeze method not found on SDK instance'); + } + if (typeof sdk.tokenUnfreeze !== 'function') { + throw new Error('tokenUnfreeze method not found on SDK instance'); + } + if (typeof sdk.tokenDestroyFrozen !== 'function') { + throw new Error('tokenDestroyFrozen method not found on SDK instance'); + } + console.log(' All token transition methods are available'); +}); + +// Summary +console.log('\n=== Test Summary ==='); +console.log(`Passed: ${passed}`); +console.log(`Failed: ${failed}`); +console.log('\nNote: Most tests are expected to fail with permission/identity errors'); +console.log('This is normal as we are testing parameter validation without real funded identities.'); +console.log('The important thing is that the methods are available and validate parameters correctly.\n'); + +process.exit(failed > 0 ? 1 : 0); \ No newline at end of file From b8a7ba574ce791dc72866ab6fb31cf9e46009d4d Mon Sep 17 00:00:00 2001 From: thephez Date: Mon, 18 Aug 2025 21:20:01 -0400 Subject: [PATCH 3/3] fix(sdk): fix generate docs (#2730) Co-authored-by: Claude --- packages/wasm-sdk/AI_REFERENCE.md | 60 ++++++++++++++++----- packages/wasm-sdk/api-definitions.json | 52 +++++++++++++++++- packages/wasm-sdk/docs.css | 2 + packages/wasm-sdk/docs.html | 75 ++++++++++++++++++-------- packages/wasm-sdk/docs_manifest.json | 2 +- packages/wasm-sdk/generate_docs.py | 45 ++++++++++++---- 6 files changed, 186 insertions(+), 50 deletions(-) diff --git a/packages/wasm-sdk/AI_REFERENCE.md b/packages/wasm-sdk/AI_REFERENCE.md index 57df8f64ce..dfd9f2eb94 100644 --- a/packages/wasm-sdk/AI_REFERENCE.md +++ b/packages/wasm-sdk/AI_REFERENCE.md @@ -757,29 +757,61 @@ const result = await sdk.{transition_name}(identityHex, ...params, privateKeyHex **Identity Create** - `identityCreate` *Create a new identity with initial credits* -Parameters (in addition to identity/key): -- `seedPhrase` (textarea, required) - Seed Phrase - - Example: `Enter seed phrase (12-24 words) or click Generate` -- `generateSeedButton` (button, optional) - Generate New Seed -- `identityIndex` (number, required) - Identity Index -- `keySelectionMode` (select, required) - Key Selection Mode -- `keyPreview` (keyPreview, optional) - Keys to be added +Parameters: +- `assetLockProof` (string, required) - Asset Lock Proof + - Hex-encoded JSON asset lock proof +- `assetLockProofPrivateKey` (string, required) - Asset Lock Proof Private Key + - WIF format private key +- `publicKeys` (string, required) - Public Keys + - JSON array of public keys + +Example: +```javascript +// Asset lock proof is a hex-encoded JSON object +const assetLockProof = "a9147d3b... (hex-encoded)"; +const assetLockProofPrivateKey = "XFfpaSbZq52HPy3WWwe1dXsZMiU1bQn8vQd34HNXkSZThevBWRn1"; // WIF format + +// Public keys array with proper key types +const publicKeys = JSON.stringify([ + { + id: 0, + type: 0, // ECDSA_SECP256K1 = 0, BLS12_381 = 1, ECDSA_HASH160 = 2 + purpose: 0, // AUTHENTICATION = 0, ENCRYPTION = 1, DECRYPTION = 2, TRANSFER = 3, etc. + securityLevel: 0, // MASTER = 0, CRITICAL = 1, HIGH = 2, MEDIUM = 3 + data: "A5GzYHPIolbHkFrp5l+s9IvF2lWMuuuSu3oWZB8vWHNJ", // Base64-encoded public key + readOnly: false + }, + { + id: 1, + type: 0, + purpose: 0, + securityLevel: 2, + data: "AnotherBase64EncodedPublicKeyHere", // Base64-encoded public key + readOnly: false + } +]); -Example: -```javascript -const result = await sdk.identityCreate(identityHex, /* params */, privateKeyHex); +const result = await sdk.identityCreate(assetLockProof, assetLockProofPrivateKey, publicKeys); ``` **Identity Top Up** - `identityTopUp` *Add credits to an existing identity* -Parameters (in addition to identity/key): -- `identityId` (text, required) - Identity ID - - Example: `Enter the identity ID to top up (base58)` +Parameters: +- `identityId` (string, required) - Identity ID + - Base58 format identity ID +- `assetLockProof` (string, required) - Asset Lock Proof + - Hex-encoded JSON asset lock proof +- `assetLockProofPrivateKey` (string, required) - Asset Lock Proof Private Key + - WIF format private key Example: ```javascript -const result = await sdk.identityTopUp(identityHex, /* params */, privateKeyHex); +const identityId = "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk"; // base58 +const assetLockProof = "a9147d3b... (hex-encoded)"; +const assetLockProofPrivateKey = "XFfpaSbZq52HPy3WWve1dXsZMiU1bQn8vQd34HNXkSZThevBWRn1"; // WIF format + +const result = await sdk.identityTopUp(identityId, assetLockProof, assetLockProofPrivateKey); ``` **Identity Update** - `identityUpdate` diff --git a/packages/wasm-sdk/api-definitions.json b/packages/wasm-sdk/api-definitions.json index a2b91d2e6e..67e39c11d6 100644 --- a/packages/wasm-sdk/api-definitions.json +++ b/packages/wasm-sdk/api-definitions.json @@ -1235,7 +1235,31 @@ "label": "Keys to be added", "help": "These keys will be added to your new identity" } - ] + ], + "sdk_params": [ + { + "name": "assetLockProof", + "type": "string", + "label": "Asset Lock Proof", + "required": true, + "description": "Hex-encoded JSON asset lock proof" + }, + { + "name": "assetLockProofPrivateKey", + "type": "string", + "label": "Asset Lock Proof Private Key", + "required": true, + "description": "WIF format private key" + }, + { + "name": "publicKeys", + "type": "string", + "label": "Public Keys", + "required": true, + "description": "JSON array of public keys" + } + ], + "sdk_example": "// Asset lock proof is a hex-encoded JSON object\nconst assetLockProof = \"a9147d3b... (hex-encoded)\";\nconst assetLockProofPrivateKey = \"XFfpaSbZq52HPy3WWwe1dXsZMiU1bQn8vQd34HNXkSZThevBWRn1\"; // WIF format\n\n// Public keys array with proper key types\nconst publicKeys = JSON.stringify([\n {\n id: 0,\n type: 0, // ECDSA_SECP256K1 = 0, BLS12_381 = 1, ECDSA_HASH160 = 2\n purpose: 0, // AUTHENTICATION = 0, ENCRYPTION = 1, DECRYPTION = 2, TRANSFER = 3, etc.\n securityLevel: 0, // MASTER = 0, CRITICAL = 1, HIGH = 2, MEDIUM = 3\n data: \"A5GzYHPIolbHkFrp5l+s9IvF2lWMuuuSu3oWZB8vWHNJ\", // Base64-encoded public key\n readOnly: false\n },\n {\n id: 1,\n type: 0,\n purpose: 0,\n securityLevel: 2,\n data: \"AnotherBase64EncodedPublicKeyHere\", // Base64-encoded public key\n readOnly: false\n }\n]);\n\nconst result = await sdk.identityCreate(assetLockProof, assetLockProofPrivateKey, publicKeys);" }, "identityTopUp": { "label": "Identity Top Up", @@ -1249,7 +1273,31 @@ "placeholder": "Enter the identity ID to top up (base58)", "help": "The identity ID that will receive the credits from the asset lock proof" } - ] + ], + "sdk_params": [ + { + "name": "identityId", + "type": "string", + "label": "Identity ID", + "required": true, + "description": "Base58 format identity ID" + }, + { + "name": "assetLockProof", + "type": "string", + "label": "Asset Lock Proof", + "required": true, + "description": "Hex-encoded JSON asset lock proof" + }, + { + "name": "assetLockProofPrivateKey", + "type": "string", + "label": "Asset Lock Proof Private Key", + "required": true, + "description": "WIF format private key" + } + ], + "sdk_example": "const identityId = \"5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk\"; // base58\nconst assetLockProof = \"a9147d3b... (hex-encoded)\";\nconst assetLockProofPrivateKey = \"XFfpaSbZq52HPy3WWve1dXsZMiU1bQn8vQd34HNXkSZThevBWRn1\"; // WIF format\n\nconst result = await sdk.identityTopUp(identityId, assetLockProof, assetLockProofPrivateKey);" }, "identityUpdate": { "label": "Identity Update", diff --git a/packages/wasm-sdk/docs.css b/packages/wasm-sdk/docs.css index 35302b49ea..4af6d24309 100644 --- a/packages/wasm-sdk/docs.css +++ b/packages/wasm-sdk/docs.css @@ -270,6 +270,8 @@ h3 { font-size: 0.9em; margin-bottom: 10px; position: relative; + white-space: pre-wrap; + overflow-x: auto; } .run-button { diff --git a/packages/wasm-sdk/docs.html b/packages/wasm-sdk/docs.html index 4eeaaba1ea..167b857b71 100644 --- a/packages/wasm-sdk/docs.html +++ b/packages/wasm-sdk/docs.html @@ -1952,37 +1952,52 @@

    Identity Create

    Parameters:
    - Seed Phrase - textarea + Asset Lock Proof + string (required) -
    Example: Enter seed phrase (12-24 words) or click Generate -
    -
    - Generate New Seed - button - (optional) +
    Hex-encoded JSON asset lock proof
    - Identity Index - number + Asset Lock Proof Private Key + string (required) +
    WIF format private key
    - Key Selection Mode - select + Public Keys + string (required) -
    Options: Default (Recommended), Advanced -
    -
    - Keys to be added - keyPreview - (optional) +
    JSON array of public keys
    Example
    -
    const result = await sdk.identityCreate(identityHex, /* params */, privateKeyHex);
    +
    // Asset lock proof is a hex-encoded JSON object +const assetLockProof = "a9147d3b... (hex-encoded)"; +const assetLockProofPrivateKey = "XFfpaSbZq52HPy3WWwe1dXsZMiU1bQn8vQd34HNXkSZThevBWRn1"; // WIF format + +// Public keys array with proper key types +const publicKeys = JSON.stringify([ + { + id: 0, + type: 0, // ECDSA_SECP256K1 = 0, BLS12_381 = 1, ECDSA_HASH160 = 2 + purpose: 0, // AUTHENTICATION = 0, ENCRYPTION = 1, DECRYPTION = 2, TRANSFER = 3, etc. + securityLevel: 0, // MASTER = 0, CRITICAL = 1, HIGH = 2, MEDIUM = 3 + data: "A5GzYHPIolbHkFrp5l+s9IvF2lWMuuuSu3oWZB8vWHNJ", // Base64-encoded public key + readOnly: false + }, + { + id: 1, + type: 0, + purpose: 0, + securityLevel: 2, + data: "AnotherBase64EncodedPublicKeyHere", // Base64-encoded public key + readOnly: false + } +]); + +const result = await sdk.identityCreate(assetLockProof, assetLockProofPrivateKey, publicKeys);

    Identity Top Up

    @@ -1992,15 +2007,31 @@

    Identity Top Up

    Parameters:
    Identity ID - text + string + (required) +
    Base58 format identity ID +
    +
    + Asset Lock Proof + string (required) -
    Example: Enter the identity ID to top up (base58) +
    Hex-encoded JSON asset lock proof +
    +
    + Asset Lock Proof Private Key + string + (required) +
    WIF format private key
    Example
    -
    const result = await sdk.identityTopUp(identityHex, /* params */, privateKeyHex);
    +
    const identityId = "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk"; // base58 +const assetLockProof = "a9147d3b... (hex-encoded)"; +const assetLockProofPrivateKey = "XFfpaSbZq52HPy3WWve1dXsZMiU1bQn8vQd34HNXkSZThevBWRn1"; // WIF format + +const result = await sdk.identityTopUp(identityId, assetLockProof, assetLockProofPrivateKey);

    Identity Update

    diff --git a/packages/wasm-sdk/docs_manifest.json b/packages/wasm-sdk/docs_manifest.json index 4de9028611..252a50417e 100644 --- a/packages/wasm-sdk/docs_manifest.json +++ b/packages/wasm-sdk/docs_manifest.json @@ -1,5 +1,5 @@ { - "generated_at": "2025-08-18T16:09:12.996174+00:00", + "generated_at": "2025-08-18T19:21:21.062910+00:00", "queries": { "getIdentity": { "category": "identity", diff --git a/packages/wasm-sdk/generate_docs.py b/packages/wasm-sdk/generate_docs.py index ea8b42ae40..5411cd2ab6 100755 --- a/packages/wasm-sdk/generate_docs.py +++ b/packages/wasm-sdk/generate_docs.py @@ -245,11 +245,15 @@ def generate_operation_entry(operation_key, operation, type_prefix):
    Parameters:
    ''' + # Use sdk_params if available (for state transitions), otherwise use inputs + sdk_params = operation.get('sdk_params', []) inputs = operation.get('inputs', []) - if not inputs: + params_to_use = sdk_params if sdk_params else inputs + + if not params_to_use: html_content += '

    No parameters required

    ' else: - for param in inputs: + for param in params_to_use: html_content += generate_parameter_entry(param) html_content += '''
    @@ -297,7 +301,7 @@ def generate_operation_entry(operation_key, operation, type_prefix): html_content += f'\n
    ' else: # State transitions don't have run buttons - html_content += f'
    {generate_transition_example(operation_key)}
    ' + html_content += f'
    {generate_transition_example(operation_key, operation)}
    ' html_content += ''' @@ -312,7 +316,9 @@ def generate_parameter_entry(param): {param.get('type', 'text')} {required_text} ''' - if param.get('placeholder'): + if param.get('description'): + html_content += f'
    {html_lib.escape(param.get("description"))}\n' + elif param.get('placeholder'): html_content += f'
    Example: {html_lib.escape(param.get("placeholder"))}\n' elif param.get('name') == 'limit' and not param.get('required', False): html_content += '
    Default: 100 (maximum items returned if not specified)\n' @@ -324,8 +330,12 @@ def generate_parameter_entry(param): html_content += ' \n' return html_content -def generate_transition_example(trans_key): +def generate_transition_example(trans_key, transition=None): """Generate example code for state transitions""" + # Check if there's a custom sdk_example + if transition and transition.get('sdk_example'): + return transition.get('sdk_example') + if trans_key == 'documentCreate': return '''const result = await sdk.document_create( identityHex, @@ -1670,18 +1680,28 @@ def generate_ai_reference_md(query_defs, transition_defs): md_content += f"\n**{transition.get('label', trans_key)}** - `{trans_key}`\n" md_content += f"*{transition.get('description', 'No description')}*\n\n" - # Parameters + # Parameters - use sdk_params if available, otherwise fall back to inputs + sdk_params = transition.get('sdk_params', []) inputs = transition.get('inputs', []) - if inputs: + params_to_use = sdk_params if sdk_params else inputs + + # Adjust parameter section header based on whether we're using SDK params + if sdk_params: + md_content += "Parameters:\n" + elif inputs: md_content += "Parameters (in addition to identity/key):\n" - for param in inputs: + + if params_to_use: + for param in params_to_use: req = "required" if param.get('required', False) else "optional" md_content += f"- `{param.get('name', 'unknown')}` ({param.get('type', 'text')}, {req})" if param.get('label') and param.get('label') != param.get('name'): md_content += f" - {param.get('label')}" - if param.get('placeholder'): + if param.get('description'): + md_content += f"\n - {param.get('description')}" + elif param.get('placeholder'): md_content += f"\n - Example: `{param.get('placeholder')}`" md_content += "\n" @@ -1689,8 +1709,11 @@ def generate_ai_reference_md(query_defs, transition_defs): # Example md_content += f"\nExample:\n```javascript\n" - # Generate specific examples - if trans_key == 'documentCreate': + # Check if there's a custom sdk_example + sdk_example = transition.get('sdk_example') + if sdk_example: + md_content += sdk_example + elif trans_key == 'documentCreate': md_content += '''const result = await sdk.document_create( identityHex, contractId,