@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 +1 -42
- package/package.json +1 -1
- package/src/canton/index.js +89 -19
package/README.md
CHANGED
|
@@ -245,21 +245,7 @@ const result = await withdrawFunds({
|
|
|
245
245
|
});
|
|
246
246
|
```
|
|
247
247
|
|
|
248
|
-
### 7.
|
|
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
package/src/canton/index.js
CHANGED
|
@@ -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
|
|
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 = "
|
|
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 = "
|
|
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
|
-
}
|
|
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 = "
|
|
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 = `
|
|
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 = `
|
|
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 = `
|
|
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
|
-
* @
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
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 });
|