@temple-digital-group/temple-canton-js 1.0.39-beta.2 → 1.0.39-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -245,21 +245,7 @@ const result = await withdrawFunds({
245
245
  });
246
246
  ```
247
247
 
248
- ### 7. Emergency Withdraw
249
-
250
- Cancels ALL orders, rolls back pending settlements, and withdraws everything immediately.
251
-
252
- ```javascript
253
- import { emergencyWithdrawFunds } from "@temple-digital-group/temple-canton-js";
254
-
255
- const result = await emergencyWithdrawFunds({
256
- allocationId: "allocation-contract-id",
257
- sender: partyId,
258
- assetId: "USDCx",
259
- });
260
- ```
261
-
262
- ### 8. Withdraw Delegation
248
+ ### 7. Withdraw Delegation
263
249
 
264
250
  Archives the user's delegation contract. The user must re-onboard to trade again.
265
251
 
@@ -273,8 +259,6 @@ await withdrawDelegation();
273
259
  await withdrawDelegation(delegationContractId, partyId);
274
260
  ```
275
261
 
276
- ## Wallet Balances
277
-
278
262
  ### Get User Balances
279
263
 
280
264
  ```javascript
@@ -307,24 +291,7 @@ Each entry in the returned array contains:
307
291
  }
308
292
  ```
309
293
 
310
- ## Legacy Order Creation
311
294
 
312
- > `createOrderProposal` is the legacy on-ledger order creation method. For v2 trading, use `createOrderRequest` instead.
313
-
314
- ```javascript
315
- import { createOrderProposal } from "@temple-digital-group/temple-canton-js";
316
-
317
- const result = await createOrderProposal({
318
- party: partyId,
319
- symbol: "CC/USDCx",
320
- side: "Buy",
321
- quantity: "100",
322
- pricePerUnit: "1.5",
323
- expiration: new Date(Date.now() + 3600000).toISOString(),
324
- userId: auth0UserId,
325
- orderType: "limit",
326
- });
327
- ```
328
295
 
329
296
  ### Merge Holdings
330
297
 
@@ -403,14 +370,6 @@ const counts = await getUtxoCount(partyId, "USDCx", walletProvider);
403
370
  | `splitAmuletHoldingForParty(party, outputQuantity)` | | Split an Amulet holding |
404
371
  | `unlockLockedAmulets(party)` | | Unlock locked Amulet holdings |
405
372
 
406
- ### Legacy Orders (On-Ledger)
407
-
408
- | Function | Provider | Description |
409
- | ------------------------------------- | -------- | --------------------------- |
410
- | `createOrderProposal(orderArguments)` | **W** | Create an order proposal |
411
- | `getOrderProposalsForParty(party)` | | Get pending order proposals |
412
- | `getOrdersForParty(party)` | | Get active orders |
413
-
414
373
  ### Temple REST API
415
374
 
416
375
  > These functions call the Temple REST API. Pass `API_EMAIL`/`API_PASSWORD` in `initialize()`, or call `login()` directly — tokens are stored and auto-refreshed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@temple-digital-group/temple-canton-js",
3
- "version": "1.0.39-beta.2",
3
+ "version": "1.0.39-beta.4",
4
4
  "description": "JavaScript library for interacting with Temple Canton blockchain",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -3264,14 +3264,17 @@ export async function depositFunds(opts, returnCommand = false) {
3264
3264
  * @param {string} opts.allocationId - The contract ID of the allocation to withdraw.
3265
3265
  * @param {string} opts.sender - The party that created the allocation (controller of Withdraw).
3266
3266
  * @param {string} opts.assetId - Instrument symbol (e.g. "USDCx", "Amulet") – used to resolve the registry/scan URL.
3267
+ * @param {object} [opts.disclosures] - Pre-fetched Amulet disclosures (from /api/amulet/disclosures). Required for Amulet when no Scan API or ledger access.
3268
+ * @param {string} [opts.userId] - Temple user ID. Falls back to wallet adapter → AUTH0_USER_ID → "temple".
3267
3269
  * @param {boolean} [returnCommand=false] - If true, returns { command, endpoint } instead of executing.
3268
3270
  * @returns {Promise<Object|null>} Ledger API response (or { error } on failure).
3269
3271
  */
3270
- export async function emergencyWithdrawFunds(opts, returnCommand = false) {
3271
- const { allocationId, sender, assetId } = opts;
3272
+ export async function finalizeWithdrawFunds(opts, returnCommand = false) {
3273
+ const { allocationId, sender: senderOpt, assetId, disclosures, userId } = opts;
3274
+ const sender = getAdapterPartyId() ?? senderOpt ?? config.VALIDATOR_USER_PARTY_ID;
3272
3275
 
3273
3276
  if (!allocationId || !sender || !assetId) {
3274
- const msg = "emergencyWithdrawFunds: allocationId, sender, and assetId are required";
3277
+ const msg = "finalizeWithdrawFunds: allocationId, sender, and assetId are required";
3275
3278
  console.error(msg);
3276
3279
  return { error: msg };
3277
3280
  }
@@ -3287,7 +3290,7 @@ export async function emergencyWithdrawFunds(opts, returnCommand = false) {
3287
3290
  if (isAmulet) {
3288
3291
  const amuletCtx = await resolveAmuletContext({ investor: sender, holdingIds: [], transferAmount: 0 });
3289
3292
  if (!amuletCtx) {
3290
- const msg = "emergencyWithdrawFunds: failed to resolve Amulet context from ledger";
3293
+ const msg = "finalizeWithdrawFunds: failed to resolve Amulet context from ledger";
3291
3294
  console.error(msg);
3292
3295
  return { error: msg };
3293
3296
  }
@@ -3349,14 +3352,44 @@ export async function emergencyWithdrawFunds(opts, returnCommand = false) {
3349
3352
  disclosedContracts.push({ templateId: null, contractId: allocFactory.contractCid, createdEventBlob: allocFactory.disclosureCid, synchronizerId: allocFactory.synchronizerId });
3350
3353
  }
3351
3354
  }
3352
- } else {
3355
+ }
3356
+ else if (isAmulet && !config.VALIDATOR_SCAN_API_URL) {
3357
+ // Amulet via disclosures (FE/proxy path — no Scan API access)
3358
+ // Use pre-fetched disclosures if provided, otherwise fetch from API
3359
+ const factoryData = disclosures?.disclosures || disclosures || null;
3360
+ let resolvedData = factoryData;
3361
+
3362
+ if (!resolvedData?.choiceContext) {
3363
+ const disclosuresResult = await getDisclosures(sender);
3364
+ resolvedData = disclosuresResult?.disclosures;
3365
+ if (disclosuresResult?.error || !resolvedData?.choiceContext) {
3366
+ const detail = disclosuresResult?.message || disclosuresResult?.error || "missing choiceContext in response";
3367
+ const msg = `finalizeWithdrawFunds: failed to resolve Amulet disclosures: ${detail}`;
3368
+ console.error(msg);
3369
+ return { error: msg };
3370
+ }
3371
+ }
3372
+
3373
+ choiceContextData = resolvedData.choiceContext.choiceContextData || {};
3374
+ // Ensure expire-lock is present (required by Allocation_Withdraw but not always in API response)
3375
+ if (choiceContextData.values && !choiceContextData.values[DEFAULT_AMULET_CONTEXT_KEYS.expireLock]) {
3376
+ choiceContextData.values[DEFAULT_AMULET_CONTEXT_KEYS.expireLock] = { tag: "AV_Bool", value: true };
3377
+ }
3378
+ disclosedContracts = (resolvedData.choiceContext.disclosedContracts || []).map((dc) => ({
3379
+ templateId: dc.templateId,
3380
+ contractId: dc.contractId,
3381
+ createdEventBlob: dc.createdEventBlob,
3382
+ synchronizerId: dc.synchronizerId,
3383
+ }));
3384
+ }
3385
+ else {
3353
3386
  // Remote: fetch choice context from Scan API / Registry API
3354
3387
  let baseUrl;
3355
3388
  let contextHeaders = {};
3356
3389
 
3357
3390
  if (isAmulet) {
3358
3391
  if (!config.VALIDATOR_SCAN_API_URL) {
3359
- const msg = "emergencyWithdrawFunds: VALIDATOR_SCAN_API_URL is required for Amulet allocations";
3392
+ const msg = "finalizeWithdrawFunds: VALIDATOR_SCAN_API_URL is required for Amulet allocations";
3360
3393
  console.error(msg);
3361
3394
  return { error: msg };
3362
3395
  }
@@ -3368,7 +3401,7 @@ export async function emergencyWithdrawFunds(opts, returnCommand = false) {
3368
3401
  const registryAPI = networkContracts?.registryAPI;
3369
3402
 
3370
3403
  if (!registryAPI) {
3371
- const msg = `emergencyWithdrawFunds: no registryAPI defined for ${assetId} on ${config.NETWORK}`;
3404
+ const msg = `finalizeWithdrawFunds: no registryAPI defined for ${assetId} on ${config.NETWORK}`;
3372
3405
  console.error(msg);
3373
3406
  return { error: msg };
3374
3407
  }
@@ -3389,7 +3422,7 @@ export async function emergencyWithdrawFunds(opts, returnCommand = false) {
3389
3422
  contextData = contextResponse.data;
3390
3423
  } catch (error) {
3391
3424
  const detail = error.response?.data ? JSON.stringify(error.response.data) : error.message;
3392
- const msg = `emergencyWithdrawFunds: error fetching withdraw context from ${isAmulet ? "Scan API" : "Registry API"}: ${detail}`;
3425
+ const msg = `finalizeWithdrawFunds: error fetching withdraw context from ${isAmulet ? "Scan API" : "Registry API"}: ${detail}`;
3393
3426
  console.error(msg);
3394
3427
  return { error: msg };
3395
3428
  }
@@ -3421,7 +3454,7 @@ export async function emergencyWithdrawFunds(opts, returnCommand = false) {
3421
3454
  },
3422
3455
  ],
3423
3456
  commandId: randomUUID(),
3424
- userId: config.AUTH0_USER_ID || "temple",
3457
+ userId: userId || getUserId() || config.AUTH0_USER_ID || "temple",
3425
3458
  applicationId: "temple",
3426
3459
  actAs: [sender],
3427
3460
  disclosedContracts: disclosedContracts,
@@ -3435,6 +3468,17 @@ export async function emergencyWithdrawFunds(opts, returnCommand = false) {
3435
3468
  return { command, endpoint };
3436
3469
  }
3437
3470
 
3471
+ // Auto-submit via wallet adapter if available
3472
+ if (getWalletAdapter()) {
3473
+ try {
3474
+ return await submitCommand(command);
3475
+ } catch (error) {
3476
+ const msg = `finalizeWithdrawFunds: wallet adapter submission failed: ${error.message}`;
3477
+ console.error(msg);
3478
+ return { error: msg };
3479
+ }
3480
+ }
3481
+
3438
3482
  const headers = await buildHeaders();
3439
3483
  try {
3440
3484
  const response = await axios.post(endpoint, command, { headers });
@@ -3442,7 +3486,7 @@ export async function emergencyWithdrawFunds(opts, returnCommand = false) {
3442
3486
  } catch (error) {
3443
3487
  const errorData = error?.response?.data;
3444
3488
  const errorDetail = errorData ? JSON.stringify(errorData, null, 2) : error.message;
3445
- const msg = `emergencyWithdrawFunds: error submitting command: ${errorDetail}`;
3489
+ const msg = `finalizeWithdrawFunds: error submitting command: ${errorDetail}`;
3446
3490
  console.error(msg);
3447
3491
  return { error: msg };
3448
3492
  }
@@ -3454,17 +3498,26 @@ export async function emergencyWithdrawFunds(opts, returnCommand = false) {
3454
3498
  * High-level withdrawal flow:
3455
3499
  * 1. Creates a withdrawal request via the backend API.
3456
3500
  * 2. Polls the request status until it is no longer "pending".
3501
+ * 3. Exercises Allocation_Withdraw on the allocation contract to release holdings back to the user.
3457
3502
  *
3458
3503
  * @param {Object} opts
3459
3504
  * @param {string} opts.asset_id - Asset identifier (e.g. "USDCx", "Amulet").
3460
3505
  * @param {string|number} opts.amount - Amount to withdraw.
3461
3506
  * @param {number} [opts.pollIntervalMs=2000] - How often to poll (ms).
3462
3507
  * @param {number} [opts.maxPollAttempts=30] - Max polling attempts before giving up.
3463
- * @returns {Promise<Object>} Final withdrawal status, or { error }.
3508
+ * @param {boolean} [returnCommand=false] - If true, returns { command, endpoint } for the Allocation_Withdraw step instead of executing.
3509
+ * @returns {Promise<Object>} Allocation_Withdraw result, or { error }.
3464
3510
  */
3465
- export async function withdrawFunds(opts) {
3511
+ export async function withdrawFunds(opts, returnCommand = false) {
3466
3512
  const { asset_id, amount, pollIntervalMs = 2000, maxPollAttempts = 30 } = opts || {};
3467
3513
 
3514
+ const sender = getAdapterPartyId() ?? config.VALIDATOR_USER_PARTY_ID;
3515
+ if (!sender) {
3516
+ const msg = "withdrawFunds: sender party is required. Connect a wallet adapter or configure VALIDATOR_USER_PARTY_ID.";
3517
+ console.error(msg);
3518
+ return { error: msg };
3519
+ }
3520
+
3468
3521
  // 1. Submit the withdrawal request
3469
3522
  const createResult = await createWithdrawalRequest(asset_id, amount);
3470
3523
  if (createResult?.error) {
@@ -3481,6 +3534,7 @@ export async function withdrawFunds(opts) {
3481
3534
  console.log(`withdrawFunds: withdrawal request ${requestId} submitted, polling for status...`);
3482
3535
 
3483
3536
  // 2. Poll until status is "ready" and allocation_cid is available
3537
+ let allocationCid = null;
3484
3538
  for (let attempt = 1; attempt <= maxPollAttempts; attempt++) {
3485
3539
  await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
3486
3540
 
@@ -3495,10 +3549,11 @@ export async function withdrawFunds(opts) {
3495
3549
  return status;
3496
3550
  }
3497
3551
 
3498
- // Ready with an allocation_cid the FE can act on
3552
+ // Ready with an allocation_cid proceed to withdraw
3499
3553
  if (status.status === "ready" && status.allocation_cid) {
3500
3554
  console.log(`withdrawFunds: request ${requestId} ready — allocation_cid: ${status.allocation_cid}`);
3501
- return status;
3555
+ allocationCid = status.allocation_cid;
3556
+ break;
3502
3557
  }
3503
3558
 
3504
3559
  if (attempt % 5 === 0) {
@@ -3506,9 +3561,24 @@ export async function withdrawFunds(opts) {
3506
3561
  }
3507
3562
  }
3508
3563
 
3509
- const msg = `withdrawFunds: request ${requestId} not ready after ${maxPollAttempts} attempts`;
3510
- console.error(msg);
3511
- return { error: msg, request_id: requestId };
3564
+ if (!allocationCid) {
3565
+ const msg = `withdrawFunds: request ${requestId} not ready after ${maxPollAttempts} attempts`;
3566
+ console.error(msg);
3567
+ return { error: msg, request_id: requestId };
3568
+ }
3569
+
3570
+ // 3. Exercise Allocation_Withdraw to release held funds back to the user
3571
+ console.log(`withdrawFunds: exercising Allocation_Withdraw on ${allocationCid}...`);
3572
+ const assetId = asset_id === "CC" ? "Amulet" : asset_id;
3573
+ const withdrawResult = await finalizeWithdrawFunds({ allocationId: allocationCid, sender, assetId }, returnCommand);
3574
+
3575
+ if (withdrawResult?.error) {
3576
+ console.error(`withdrawFunds: Allocation_Withdraw failed for request ${requestId}: ${withdrawResult.error}`);
3577
+ return { error: withdrawResult.error, request_id: requestId, allocation_cid: allocationCid };
3578
+ }
3579
+
3580
+ console.log(`withdrawFunds: withdrawal complete for request ${requestId}`);
3581
+ return { ...withdrawResult, request_id: requestId, allocation_cid: allocationCid };
3512
3582
  }
3513
3583
 
3514
3584
  // ─── Onboarding & Delegation ─────────────────────────────────────────────────
@@ -3561,6 +3631,8 @@ export async function onboardUser(user = {}, returnCommand = false) {
3561
3631
  actAs: [user.partyId],
3562
3632
  };
3563
3633
 
3634
+ const endpoint = `${config.VALIDATOR_API_URL}/v2/commands/submit-and-wait`;
3635
+
3564
3636
  if (returnCommand) {
3565
3637
  return { command, endpoint };
3566
3638
  }
@@ -3575,8 +3647,6 @@ export async function onboardUser(user = {}, returnCommand = false) {
3575
3647
  return { error: msg };
3576
3648
  }
3577
3649
  }
3578
-
3579
- const endpoint = `${config.VALIDATOR_API_URL}/v2/commands/submit-and-wait`;
3580
3650
  const headers = await buildHeaders();
3581
3651
  try {
3582
3652
  const response = await axios.post(endpoint, command, { headers });