@temple-digital-group/temple-canton-js 2.0.0-beta.9 → 2.0.1
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 +455 -457
- package/dist/canton/deposits.js +4 -1
- package/dist/canton/withdrawals.d.ts +20 -0
- package/dist/canton/withdrawals.js +284 -3
- package/index.js +15 -15
- package/package.json +49 -49
- package/src/api/config.d.ts +20 -20
- package/src/api/index.ts +322 -322
- package/src/api/tokenStore.ts +30 -30
- package/src/api/types.ts +196 -196
- package/src/auth0/index.d.ts +1 -1
- package/src/auth0/index.js +50 -50
- package/src/canton/deposits.ts +563 -559
- package/src/canton/helpers.ts +266 -266
- package/src/canton/index.d.ts +41 -41
- package/src/canton/index.js +3116 -3294
- package/src/canton/instrumentCatalog.d.ts +6 -6
- package/src/canton/instrumentCatalog.js +183 -183
- package/src/canton/request_schemas/cancel_orders_amulet.json +77 -77
- package/src/canton/request_schemas/cancel_orders_utility.json +68 -68
- package/src/canton/request_schemas/create_order_proposal_amulet.json +94 -94
- package/src/canton/request_schemas/create_order_proposal_utility.json +121 -121
- package/src/canton/request_schemas/create_utility_credential.json +31 -31
- package/src/canton/request_schemas/execute_transfer_factory.json +43 -43
- package/src/canton/request_schemas/get_allocation_factory.json +21 -21
- package/src/canton/request_schemas/get_amulet_holdings.json +21 -21
- package/src/canton/request_schemas/get_instrument_configurations.json +21 -21
- package/src/canton/request_schemas/get_locked_amulet_holdings.json +21 -21
- package/src/canton/request_schemas/get_order_proposals.json +21 -21
- package/src/canton/request_schemas/get_orders.json +21 -21
- package/src/canton/request_schemas/get_sender_credentials.json +22 -22
- package/src/canton/request_schemas/get_transfer_factory.json +28 -28
- package/src/canton/request_schemas/get_utility_holdings.json +21 -21
- package/src/canton/request_schemas/unlock_amulet.json +38 -38
- package/src/canton/walletAdapter.d.ts +7 -6
- package/src/canton/walletAdapter.js +112 -89
- package/src/canton/withdrawals.ts +810 -489
- package/src/config/index.d.ts +63 -63
- package/src/config/index.js +188 -188
- package/src/websocket/index.ts +341 -341
- package/src/websocket/ws.d.ts +24 -24
package/dist/canton/deposits.js
CHANGED
|
@@ -2,7 +2,7 @@ import config from "../../src/config/index.js";
|
|
|
2
2
|
import axios from "axios";
|
|
3
3
|
import { getDisclosures } from "../api/index.js";
|
|
4
4
|
import { getUserId } from "../api/tokenStore.js";
|
|
5
|
-
import { getAdapterPartyId, getWalletAdapter, submitCommand } from "../../src/canton/walletAdapter.js";
|
|
5
|
+
import { getAdapterPartyId, getWalletAdapter, submitCommand, payDueGasIfAny } from "../../src/canton/walletAdapter.js";
|
|
6
6
|
import { normalizeAssetId, instrumentCatalog } from "../../src/canton/instrumentCatalog.js";
|
|
7
7
|
import { randomUUID, shouldUseLedgerForMetadata, normalizeContractId, resolveInstrumentDefinition, getInstrumentRegistrar, resolveProvider, dedupeDisclosedContracts, buildHeaders, DEFAULT_UTILITY_CONTEXT_KEYS, } from "./helpers.js";
|
|
8
8
|
import { resolveAmuletContext, resolveUtilityInstrumentConfiguration, resolveUtilityAllocationFactory, getAmuletHoldingsForParty, getUtilityHoldingsForParty, getUtxoCount, } from "../../src/canton/index.js";
|
|
@@ -114,6 +114,8 @@ export async function deposit(amount, symbol) {
|
|
|
114
114
|
if (!party) {
|
|
115
115
|
return { error: "deposit: could not resolve party ID from wallet adapter." };
|
|
116
116
|
}
|
|
117
|
+
// Pay any outstanding network gas first so the balance check below is accurate.
|
|
118
|
+
await payDueGasIfAny();
|
|
117
119
|
const assetId = normalizeAssetId(symbol);
|
|
118
120
|
if (!instrumentCatalog[assetId]) {
|
|
119
121
|
return { error: `deposit: unsupported symbol "${symbol}"` };
|
|
@@ -442,6 +444,7 @@ export async function depositFunds(opts, returnCommand = false) {
|
|
|
442
444
|
// Auto-submit via wallet adapter if available
|
|
443
445
|
if (getWalletAdapter()) {
|
|
444
446
|
try {
|
|
447
|
+
await payDueGasIfAny();
|
|
445
448
|
return (await submitCommand(command));
|
|
446
449
|
}
|
|
447
450
|
catch (error) {
|
|
@@ -17,6 +17,13 @@ export interface FinalizeWithdrawOpts {
|
|
|
17
17
|
};
|
|
18
18
|
userId?: string;
|
|
19
19
|
}
|
|
20
|
+
export interface BulkWithdrawFundsOpts {
|
|
21
|
+
allocationIds: string[];
|
|
22
|
+
sender?: string;
|
|
23
|
+
assetId: string;
|
|
24
|
+
disclosures?: FinalizeWithdrawOpts["disclosures"];
|
|
25
|
+
userId?: string;
|
|
26
|
+
}
|
|
20
27
|
export interface WithdrawFundsOpts {
|
|
21
28
|
asset_id: string;
|
|
22
29
|
amount: string | number;
|
|
@@ -32,6 +39,19 @@ export interface WithdrawFundsOpts {
|
|
|
32
39
|
* 3. Remote: Scan API / Registry API
|
|
33
40
|
*/
|
|
34
41
|
export declare function finalizeWithdrawFunds(opts: FinalizeWithdrawOpts, returnCommand?: boolean): Promise<Record<string, unknown>>;
|
|
42
|
+
/**
|
|
43
|
+
* Finalize multiple withdrawals in a single signed transaction.
|
|
44
|
+
*
|
|
45
|
+
* Bundles N Allocation_Withdraw exercises into one ledger submission so the user
|
|
46
|
+
* signs once for the whole batch. All allocations must belong to the same sender
|
|
47
|
+
* and the same asset (caller's responsibility — pass one assetId).
|
|
48
|
+
*
|
|
49
|
+
* Choice-context fetching:
|
|
50
|
+
* - Localhost ledger / Amulet via /api/amulet/disclosures: one shared context for all N.
|
|
51
|
+
* - Scan API (Amulet) / Registry API (utility): one fetch per allocation, then disclosed
|
|
52
|
+
* contracts merged and deduped.
|
|
53
|
+
*/
|
|
54
|
+
export declare function bulkWithdrawFunds(opts: BulkWithdrawFundsOpts, returnCommand?: boolean): Promise<Record<string, unknown>>;
|
|
35
55
|
/**
|
|
36
56
|
* High-level withdrawal flow:
|
|
37
57
|
* 1. Creates a withdrawal request via the backend API.
|
|
@@ -2,10 +2,12 @@ import config from "../../src/config/index.js";
|
|
|
2
2
|
import axios from "axios";
|
|
3
3
|
import { getDisclosures, createWithdrawalRequest, getWithdrawalRequestStatus, getDelegation, } from "../api/index.js";
|
|
4
4
|
import { getUserId } from "../api/tokenStore.js";
|
|
5
|
-
import { getAdapterPartyId, getWalletAdapter, submitCommand } from "../../src/canton/walletAdapter.js";
|
|
6
|
-
import { randomUUID, shouldUseLedgerForMetadata, normalizeContractId, resolveInstrumentDefinition, getInstrumentRegistrar, dedupeDisclosedContracts, buildHeaders, DEFAULT_AMULET_CONTEXT_KEYS, DEFAULT_UTILITY_CONTEXT_KEYS, } from "./helpers.js";
|
|
7
|
-
import { resolveAmuletContext, resolveUtilityInstrumentConfiguration, resolveUtilityAllocationFactory, } from "../../src/canton/index.js";
|
|
5
|
+
import { getAdapterPartyId, getWalletAdapter, submitCommand, payDueGasIfAny } from "../../src/canton/walletAdapter.js";
|
|
6
|
+
import { randomUUID, shouldUseLedgerForMetadata, normalizeContractId, resolveInstrumentDefinition, getInstrumentRegistrar, resolveProvider, dedupeDisclosedContracts, buildHeaders, DEFAULT_AMULET_CONTEXT_KEYS, DEFAULT_UTILITY_CONTEXT_KEYS, } from "./helpers.js";
|
|
7
|
+
import { resolveAmuletContext, resolveUtilityInstrumentConfiguration, resolveUtilityAllocationFactory, getUtxoCount, } from "../../src/canton/index.js";
|
|
8
8
|
import { normalizeAssetId } from "../../src/canton/instrumentCatalog.js";
|
|
9
|
+
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
10
|
+
const CC_FEE_RESERVE = 10;
|
|
9
11
|
// ─── Withdrawal Functions ────────────────────────────────────────────────────
|
|
10
12
|
/**
|
|
11
13
|
* Finalize a withdrawal by exercising the Allocation_Withdraw choice.
|
|
@@ -233,6 +235,7 @@ export async function finalizeWithdrawFunds(opts, returnCommand = false) {
|
|
|
233
235
|
// Auto-submit via wallet adapter if available
|
|
234
236
|
if (getWalletAdapter()) {
|
|
235
237
|
try {
|
|
238
|
+
await payDueGasIfAny();
|
|
236
239
|
return (await submitCommand(command));
|
|
237
240
|
}
|
|
238
241
|
catch (error) {
|
|
@@ -255,6 +258,271 @@ export async function finalizeWithdrawFunds(opts, returnCommand = false) {
|
|
|
255
258
|
return { error: msg };
|
|
256
259
|
}
|
|
257
260
|
}
|
|
261
|
+
/**
|
|
262
|
+
* Finalize multiple withdrawals in a single signed transaction.
|
|
263
|
+
*
|
|
264
|
+
* Bundles N Allocation_Withdraw exercises into one ledger submission so the user
|
|
265
|
+
* signs once for the whole batch. All allocations must belong to the same sender
|
|
266
|
+
* and the same asset (caller's responsibility — pass one assetId).
|
|
267
|
+
*
|
|
268
|
+
* Choice-context fetching:
|
|
269
|
+
* - Localhost ledger / Amulet via /api/amulet/disclosures: one shared context for all N.
|
|
270
|
+
* - Scan API (Amulet) / Registry API (utility): one fetch per allocation, then disclosed
|
|
271
|
+
* contracts merged and deduped.
|
|
272
|
+
*/
|
|
273
|
+
export async function bulkWithdrawFunds(opts, returnCommand = false) {
|
|
274
|
+
const { allocationIds, sender: senderOpt, assetId: rawAssetId, disclosures, userId } = opts;
|
|
275
|
+
const assetId = normalizeAssetId(rawAssetId);
|
|
276
|
+
const sender = getAdapterPartyId() ?? senderOpt ?? config.VALIDATOR_USER_PARTY_ID;
|
|
277
|
+
if (!Array.isArray(allocationIds) || allocationIds.length === 0) {
|
|
278
|
+
const msg = "bulkWithdrawFunds: allocationIds must be a non-empty array";
|
|
279
|
+
console.error(msg);
|
|
280
|
+
return { error: msg };
|
|
281
|
+
}
|
|
282
|
+
if (!sender || !assetId) {
|
|
283
|
+
const msg = "bulkWithdrawFunds: sender and assetId are required";
|
|
284
|
+
console.error(msg);
|
|
285
|
+
return { error: msg };
|
|
286
|
+
}
|
|
287
|
+
const isAmulet = assetId === "Amulet";
|
|
288
|
+
const allocationCids = allocationIds.map((cid) => normalizeContractId(cid));
|
|
289
|
+
// Per-allocation choice context (one entry per allocation, in the same order).
|
|
290
|
+
// Shared-context paths fill all N entries with the same object.
|
|
291
|
+
let perAllocContexts = [];
|
|
292
|
+
let mergedDisclosedContracts = [];
|
|
293
|
+
if (shouldUseLedgerForMetadata()) {
|
|
294
|
+
// Localhost: resolve once, reuse for all allocations.
|
|
295
|
+
let sharedContext = {};
|
|
296
|
+
const sharedDisclosed = [];
|
|
297
|
+
if (isAmulet) {
|
|
298
|
+
const amuletCtx = await resolveAmuletContext({ investor: sender, holdingIds: [], transferAmount: 0 });
|
|
299
|
+
if (!amuletCtx) {
|
|
300
|
+
const msg = "bulkWithdrawFunds: failed to resolve Amulet context from ledger";
|
|
301
|
+
console.error(msg);
|
|
302
|
+
return { error: msg };
|
|
303
|
+
}
|
|
304
|
+
const ctx = amuletCtx;
|
|
305
|
+
const contextKeys = ctx.contextKeys;
|
|
306
|
+
const amuletRules = ctx.amuletRules;
|
|
307
|
+
const openMiningRound = ctx.openMiningRound;
|
|
308
|
+
const featuredAppRight = ctx.featuredAppRight;
|
|
309
|
+
const externalAmuletRules = ctx.externalAmuletRules;
|
|
310
|
+
sharedContext = {
|
|
311
|
+
values: {
|
|
312
|
+
[contextKeys.amuletRules]: { tag: "AV_ContractId", value: amuletRules.contractCid },
|
|
313
|
+
[contextKeys.openRound]: { tag: "AV_ContractId", value: openMiningRound.contractCid },
|
|
314
|
+
[contextKeys.featuredAppRight]: { tag: "AV_ContractId", value: featuredAppRight.contractCid },
|
|
315
|
+
[contextKeys.expireLock]: { tag: "AV_Bool", value: true },
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
sharedDisclosed.push({
|
|
319
|
+
templateId: amuletRules.templateId ?? null,
|
|
320
|
+
contractId: amuletRules.contractCid,
|
|
321
|
+
createdEventBlob: amuletRules.disclosureCid,
|
|
322
|
+
synchronizerId: amuletRules.synchronizerId,
|
|
323
|
+
}, {
|
|
324
|
+
templateId: openMiningRound.templateId ?? null,
|
|
325
|
+
contractId: openMiningRound.contractCid,
|
|
326
|
+
createdEventBlob: openMiningRound.disclosureCid,
|
|
327
|
+
synchronizerId: openMiningRound.synchronizerId,
|
|
328
|
+
}, {
|
|
329
|
+
templateId: externalAmuletRules.templateId ?? null,
|
|
330
|
+
contractId: externalAmuletRules.contractCid,
|
|
331
|
+
createdEventBlob: externalAmuletRules.disclosureCid,
|
|
332
|
+
synchronizerId: externalAmuletRules.synchronizerId,
|
|
333
|
+
});
|
|
334
|
+
if (featuredAppRight?.contractCid && featuredAppRight?.disclosureCid) {
|
|
335
|
+
sharedDisclosed.push({
|
|
336
|
+
templateId: featuredAppRight.templateId ?? null,
|
|
337
|
+
contractId: featuredAppRight.contractCid,
|
|
338
|
+
createdEventBlob: featuredAppRight.disclosureCid,
|
|
339
|
+
synchronizerId: featuredAppRight.synchronizerId,
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
const instrumentDef = resolveInstrumentDefinition(assetId);
|
|
345
|
+
const networkDef = instrumentDef?.[config.NETWORK];
|
|
346
|
+
const registrar = networkDef?.registrar || getInstrumentRegistrar(assetId) || config.VALIDATOR_REGISTRAR_PARTY_ID;
|
|
347
|
+
const [allocFactory, instConfig] = await Promise.all([
|
|
348
|
+
resolveUtilityAllocationFactory(registrar, sender),
|
|
349
|
+
resolveUtilityInstrumentConfiguration(assetId, registrar),
|
|
350
|
+
]);
|
|
351
|
+
if (instConfig) {
|
|
352
|
+
sharedContext = {
|
|
353
|
+
values: {
|
|
354
|
+
[DEFAULT_UTILITY_CONTEXT_KEYS.instrumentConfiguration]: {
|
|
355
|
+
tag: "AV_ContractId",
|
|
356
|
+
value: instConfig.contractCid,
|
|
357
|
+
},
|
|
358
|
+
[DEFAULT_UTILITY_CONTEXT_KEYS.instrumentConfigurationPrefixed]: {
|
|
359
|
+
tag: "AV_ContractId",
|
|
360
|
+
value: instConfig.contractCid,
|
|
361
|
+
},
|
|
362
|
+
[DEFAULT_UTILITY_CONTEXT_KEYS.senderCredentials]: { tag: "AV_List", value: [] },
|
|
363
|
+
[DEFAULT_UTILITY_CONTEXT_KEYS.senderCredentialsPrefixed]: { tag: "AV_List", value: [] },
|
|
364
|
+
[DEFAULT_UTILITY_CONTEXT_KEYS.receiverCredentials]: { tag: "AV_List", value: [] },
|
|
365
|
+
[DEFAULT_UTILITY_CONTEXT_KEYS.receiverCredentialsPrefixed]: { tag: "AV_List", value: [] },
|
|
366
|
+
[DEFAULT_AMULET_CONTEXT_KEYS.expireLock]: { tag: "AV_Bool", value: true },
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
sharedDisclosed.push({
|
|
370
|
+
templateId: null,
|
|
371
|
+
contractId: instConfig.contractCid,
|
|
372
|
+
createdEventBlob: instConfig.disclosureCid,
|
|
373
|
+
synchronizerId: instConfig.synchronizerId,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
if (allocFactory) {
|
|
377
|
+
sharedDisclosed.push({
|
|
378
|
+
templateId: null,
|
|
379
|
+
contractId: allocFactory.contractCid,
|
|
380
|
+
createdEventBlob: allocFactory.disclosureCid,
|
|
381
|
+
synchronizerId: allocFactory.synchronizerId,
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
perAllocContexts = allocationCids.map(() => sharedContext);
|
|
386
|
+
mergedDisclosedContracts = sharedDisclosed;
|
|
387
|
+
}
|
|
388
|
+
else if (isAmulet && !config.VALIDATOR_SCAN_API_URL) {
|
|
389
|
+
// Amulet via /api/amulet/disclosures: one fetch shared across all allocations.
|
|
390
|
+
const factoryData = disclosures?.disclosures || disclosures || null;
|
|
391
|
+
let resolvedData = factoryData;
|
|
392
|
+
if (!resolvedData?.choiceContext) {
|
|
393
|
+
const disclosuresResult = (await getDisclosures(sender));
|
|
394
|
+
resolvedData = disclosuresResult?.disclosures;
|
|
395
|
+
if (disclosuresResult?.error || !resolvedData?.choiceContext) {
|
|
396
|
+
const detail = disclosuresResult?.message ||
|
|
397
|
+
disclosuresResult?.error ||
|
|
398
|
+
"missing choiceContext in response";
|
|
399
|
+
const msg = `bulkWithdrawFunds: failed to resolve Amulet disclosures: ${detail}`;
|
|
400
|
+
console.error(msg);
|
|
401
|
+
return { error: msg };
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
const choiceContext = resolvedData.choiceContext;
|
|
405
|
+
const sharedContext = choiceContext.choiceContextData || {};
|
|
406
|
+
const values = sharedContext.values;
|
|
407
|
+
if (values && !values[DEFAULT_AMULET_CONTEXT_KEYS.expireLock]) {
|
|
408
|
+
values[DEFAULT_AMULET_CONTEXT_KEYS.expireLock] = { tag: "AV_Bool", value: true };
|
|
409
|
+
}
|
|
410
|
+
const sharedDisclosed = (choiceContext.disclosedContracts || []).map((dc) => ({
|
|
411
|
+
templateId: dc.templateId,
|
|
412
|
+
contractId: dc.contractId,
|
|
413
|
+
createdEventBlob: dc.createdEventBlob,
|
|
414
|
+
synchronizerId: dc.synchronizerId,
|
|
415
|
+
}));
|
|
416
|
+
perAllocContexts = allocationCids.map(() => sharedContext);
|
|
417
|
+
mergedDisclosedContracts = sharedDisclosed;
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
// Per-allocation context: Scan API (Amulet) or Registry API (utility).
|
|
421
|
+
let baseUrl;
|
|
422
|
+
let contextHeaders = {};
|
|
423
|
+
if (isAmulet) {
|
|
424
|
+
if (!config.VALIDATOR_SCAN_API_URL) {
|
|
425
|
+
const msg = "bulkWithdrawFunds: VALIDATOR_SCAN_API_URL is required for Amulet allocations";
|
|
426
|
+
console.error(msg);
|
|
427
|
+
return { error: msg };
|
|
428
|
+
}
|
|
429
|
+
baseUrl = `${config.VALIDATOR_SCAN_API_URL}/registry`;
|
|
430
|
+
contextHeaders = await buildHeaders();
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
const instrumentDef = resolveInstrumentDefinition(assetId);
|
|
434
|
+
const networkContracts = instrumentDef?.[config.NETWORK];
|
|
435
|
+
const registryAPI = networkContracts?.registryAPI;
|
|
436
|
+
if (!registryAPI) {
|
|
437
|
+
const msg = `bulkWithdrawFunds: no registryAPI defined for ${assetId} on ${config.NETWORK}`;
|
|
438
|
+
console.error(msg);
|
|
439
|
+
return { error: msg };
|
|
440
|
+
}
|
|
441
|
+
baseUrl = registryAPI;
|
|
442
|
+
}
|
|
443
|
+
const requestConfig = Object.keys(contextHeaders).length > 0 ? { headers: contextHeaders } : undefined;
|
|
444
|
+
try {
|
|
445
|
+
const contextResults = await Promise.all(allocationCids.map(async (cid) => {
|
|
446
|
+
const url = `${baseUrl}/allocations/v1/${encodeURIComponent(cid)}/choice-contexts/withdraw`;
|
|
447
|
+
try {
|
|
448
|
+
const resp = await axios.post(url, { meta: {}, excludeDebugFields: true }, requestConfig);
|
|
449
|
+
return resp.data;
|
|
450
|
+
}
|
|
451
|
+
catch (error) {
|
|
452
|
+
const axiosErr = error;
|
|
453
|
+
const detail = axiosErr.response?.data ? JSON.stringify(axiosErr.response.data) : axiosErr.message;
|
|
454
|
+
throw new Error(`bulkWithdrawFunds: error fetching withdraw context for ${cid} from ${isAmulet ? "Scan API" : "Registry API"}: ${detail}`);
|
|
455
|
+
}
|
|
456
|
+
}));
|
|
457
|
+
for (const data of contextResults) {
|
|
458
|
+
perAllocContexts.push(data?.choiceContextData || {});
|
|
459
|
+
const disclosed = (data?.disclosedContracts || []).map((dc) => ({
|
|
460
|
+
templateId: dc.templateId,
|
|
461
|
+
contractId: dc.contractId,
|
|
462
|
+
createdEventBlob: dc.createdEventBlob,
|
|
463
|
+
synchronizerId: dc.synchronizerId,
|
|
464
|
+
}));
|
|
465
|
+
mergedDisclosedContracts.push(...disclosed);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
catch (error) {
|
|
469
|
+
const msg = error.message;
|
|
470
|
+
console.error(msg);
|
|
471
|
+
return { error: msg };
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
// Build one ExerciseCommand per allocation; Canton signs the whole submission atomically.
|
|
475
|
+
const exerciseCommands = allocationCids.map((cid, idx) => ({
|
|
476
|
+
ExerciseCommand: {
|
|
477
|
+
templateId: "#splice-api-token-allocation-v1:Splice.Api.Token.AllocationV1:Allocation",
|
|
478
|
+
contractId: cid,
|
|
479
|
+
choice: "Allocation_Withdraw",
|
|
480
|
+
choiceArgument: {
|
|
481
|
+
extraArgs: {
|
|
482
|
+
context: perAllocContexts[idx],
|
|
483
|
+
meta: { values: {} },
|
|
484
|
+
},
|
|
485
|
+
},
|
|
486
|
+
},
|
|
487
|
+
}));
|
|
488
|
+
const command = {
|
|
489
|
+
commands: exerciseCommands,
|
|
490
|
+
commandId: randomUUID(),
|
|
491
|
+
userId: userId || getUserId() || config.AUTH0_USER_ID || "temple",
|
|
492
|
+
applicationId: "temple",
|
|
493
|
+
actAs: [sender],
|
|
494
|
+
disclosedContracts: mergedDisclosedContracts,
|
|
495
|
+
};
|
|
496
|
+
dedupeDisclosedContracts(command);
|
|
497
|
+
const endpoint = `${config.VALIDATOR_API_URL}/v2/commands/submit-and-wait`;
|
|
498
|
+
if (returnCommand) {
|
|
499
|
+
return { command, endpoint };
|
|
500
|
+
}
|
|
501
|
+
if (getWalletAdapter()) {
|
|
502
|
+
try {
|
|
503
|
+
await payDueGasIfAny();
|
|
504
|
+
return (await submitCommand(command));
|
|
505
|
+
}
|
|
506
|
+
catch (error) {
|
|
507
|
+
const msg = `bulkWithdrawFunds: wallet adapter submission failed: ${error.message}`;
|
|
508
|
+
console.error(msg);
|
|
509
|
+
return { error: msg };
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
const headers = await buildHeaders();
|
|
513
|
+
try {
|
|
514
|
+
const response = await axios.post(endpoint, command, { headers });
|
|
515
|
+
return response.data;
|
|
516
|
+
}
|
|
517
|
+
catch (error) {
|
|
518
|
+
const axiosErr = error;
|
|
519
|
+
const errorData = axiosErr?.response?.data;
|
|
520
|
+
const errorDetail = errorData ? JSON.stringify(errorData, null, 2) : axiosErr.message;
|
|
521
|
+
const msg = `bulkWithdrawFunds: error submitting command: ${errorDetail}`;
|
|
522
|
+
console.error(msg);
|
|
523
|
+
return { error: msg };
|
|
524
|
+
}
|
|
525
|
+
}
|
|
258
526
|
/**
|
|
259
527
|
* High-level withdrawal flow:
|
|
260
528
|
* 1. Creates a withdrawal request via the backend API.
|
|
@@ -270,6 +538,18 @@ export async function withdrawFunds(opts, returnCommand = false) {
|
|
|
270
538
|
console.error(msg);
|
|
271
539
|
return { error: msg };
|
|
272
540
|
}
|
|
541
|
+
// Pay any outstanding network gas first so the fee-reserve check below is accurate.
|
|
542
|
+
await payDueGasIfAny();
|
|
543
|
+
// Ensure the user has enough CC unlocked to pay gas for the eventual Allocation_Withdraw.
|
|
544
|
+
const provider = resolveProvider(null);
|
|
545
|
+
const ccBalance = await getUtxoCount(sender, "Amulet", provider);
|
|
546
|
+
const availableCC = ccBalance.unlockedBalance || 0;
|
|
547
|
+
if (availableCC < CC_FEE_RESERVE) {
|
|
548
|
+
return {
|
|
549
|
+
error: `withdrawFunds: insufficient CC for fees. You have ${availableCC} CC but need at least ${CC_FEE_RESERVE} CC to cover transaction fees.`,
|
|
550
|
+
data: { ccBalance: availableCC, feeReserve: CC_FEE_RESERVE },
|
|
551
|
+
};
|
|
552
|
+
}
|
|
273
553
|
// 1. Submit the withdrawal request
|
|
274
554
|
const createResult = (await createWithdrawalRequest(asset_id, amount));
|
|
275
555
|
if (createResult?.error) {
|
|
@@ -368,6 +648,7 @@ export async function withdrawDelegation(delegationId = null, user = null, retur
|
|
|
368
648
|
// Auto-submit via wallet adapter if available
|
|
369
649
|
if (getWalletAdapter()) {
|
|
370
650
|
try {
|
|
651
|
+
await payDueGasIfAny();
|
|
371
652
|
return (await submitCommand(command));
|
|
372
653
|
}
|
|
373
654
|
catch (error) {
|
package/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
// Export Canton client functions
|
|
2
|
-
export * from './src/canton/index.js';
|
|
3
|
-
|
|
4
|
-
// Export Auth0 utilities
|
|
5
|
-
export { getJWTToken } from './src/auth0/index.js';
|
|
6
|
-
|
|
7
|
-
// Export configuration
|
|
8
|
-
export * from './src/config/index.js';
|
|
9
|
-
|
|
10
|
-
// Export Temple REST API functions
|
|
11
|
-
export * from './dist/api/index.js';
|
|
12
|
-
|
|
13
|
-
// Export WebSocket client
|
|
14
|
-
export * from './dist/websocket/index.js';
|
|
15
|
-
|
|
1
|
+
// Export Canton client functions
|
|
2
|
+
export * from './src/canton/index.js';
|
|
3
|
+
|
|
4
|
+
// Export Auth0 utilities
|
|
5
|
+
export { getJWTToken } from './src/auth0/index.js';
|
|
6
|
+
|
|
7
|
+
// Export configuration
|
|
8
|
+
export * from './src/config/index.js';
|
|
9
|
+
|
|
10
|
+
// Export Temple REST API functions
|
|
11
|
+
export * from './dist/api/index.js';
|
|
12
|
+
|
|
13
|
+
// Export WebSocket client
|
|
14
|
+
export * from './dist/websocket/index.js';
|
|
15
|
+
|
package/package.json
CHANGED
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@temple-digital-group/temple-canton-js",
|
|
3
|
-
"version": "2.0.
|
|
4
|
-
"description": "JavaScript library for interacting with Temple Canton blockchain",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "index.js",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": "./index.js"
|
|
9
|
-
},
|
|
10
|
-
"files": [
|
|
11
|
-
"index.js",
|
|
12
|
-
"src/",
|
|
13
|
-
"dist/"
|
|
14
|
-
],
|
|
15
|
-
"repository": {
|
|
16
|
-
"type": "git",
|
|
17
|
-
"url": "git+https://github.com/Temple-Digital-Group/temple-canton-js.git"
|
|
18
|
-
},
|
|
19
|
-
"publishConfig": {
|
|
20
|
-
"access": "public"
|
|
21
|
-
},
|
|
22
|
-
"author": "kakashigr <augegr@gmail.com>",
|
|
23
|
-
"keywords": [
|
|
24
|
-
"canton",
|
|
25
|
-
"blockchain",
|
|
26
|
-
"daml",
|
|
27
|
-
"splice",
|
|
28
|
-
"amulet",
|
|
29
|
-
"ledger",
|
|
30
|
-
"trading",
|
|
31
|
-
"temple"
|
|
32
|
-
],
|
|
33
|
-
"license": "MIT",
|
|
34
|
-
"scripts": {
|
|
35
|
-
"start": "node index.js",
|
|
36
|
-
"build": "tsc",
|
|
37
|
-
"prepublishOnly": "npm run build"
|
|
38
|
-
},
|
|
39
|
-
"dependencies": {
|
|
40
|
-
"axios": "^1.13.1",
|
|
41
|
-
"dotenv": "^17.2.3",
|
|
42
|
-
"ws": "^8.19.0"
|
|
43
|
-
},
|
|
44
|
-
"devDependencies": {
|
|
45
|
-
"@types/node": "^25.3.3",
|
|
46
|
-
"@types/ws": "^8.18.0",
|
|
47
|
-
"typescript": "^5.9.3"
|
|
48
|
-
}
|
|
49
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@temple-digital-group/temple-canton-js",
|
|
3
|
+
"version": "2.0.1",
|
|
4
|
+
"description": "JavaScript library for interacting with Temple Canton blockchain",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"index.js",
|
|
12
|
+
"src/",
|
|
13
|
+
"dist/"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/Temple-Digital-Group/temple-canton-js.git"
|
|
18
|
+
},
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"author": "kakashigr <augegr@gmail.com>",
|
|
23
|
+
"keywords": [
|
|
24
|
+
"canton",
|
|
25
|
+
"blockchain",
|
|
26
|
+
"daml",
|
|
27
|
+
"splice",
|
|
28
|
+
"amulet",
|
|
29
|
+
"ledger",
|
|
30
|
+
"trading",
|
|
31
|
+
"temple"
|
|
32
|
+
],
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"scripts": {
|
|
35
|
+
"start": "node index.js",
|
|
36
|
+
"build": "tsc",
|
|
37
|
+
"prepublishOnly": "npm run build"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"axios": "^1.13.1",
|
|
41
|
+
"dotenv": "^17.2.3",
|
|
42
|
+
"ws": "^8.19.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/node": "^25.3.3",
|
|
46
|
+
"@types/ws": "^8.18.0",
|
|
47
|
+
"typescript": "^5.9.3"
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/api/config.d.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
declare module "../../src/config/index.js" {
|
|
2
|
-
const config: {
|
|
3
|
-
readonly API_BASE_URL: string;
|
|
4
|
-
readonly API_EMAIL?: string;
|
|
5
|
-
readonly API_PASSWORD?: string;
|
|
6
|
-
readonly API_KEY?: string;
|
|
7
|
-
readonly NETWORK: string;
|
|
8
|
-
readonly VALIDATOR_API_URL: string;
|
|
9
|
-
readonly VALIDATOR_SCAN_API_URL: string;
|
|
10
|
-
readonly JWT_TOKEN: string;
|
|
11
|
-
[key: string]: unknown;
|
|
12
|
-
};
|
|
13
|
-
export default config;
|
|
14
|
-
export function initializeConfig(config: Record<string, unknown>): void;
|
|
15
|
-
export function setJWTToken(token: string): void;
|
|
16
|
-
export function getConfigValue(key: string): string | undefined;
|
|
17
|
-
export function setWalletAdapter(adapter: unknown): void;
|
|
18
|
-
export function getWalletAdapter(): unknown;
|
|
19
|
-
export function getAdapterProvider(): unknown;
|
|
20
|
-
}
|
|
1
|
+
declare module "../../src/config/index.js" {
|
|
2
|
+
const config: {
|
|
3
|
+
readonly API_BASE_URL: string;
|
|
4
|
+
readonly API_EMAIL?: string;
|
|
5
|
+
readonly API_PASSWORD?: string;
|
|
6
|
+
readonly API_KEY?: string;
|
|
7
|
+
readonly NETWORK: string;
|
|
8
|
+
readonly VALIDATOR_API_URL: string;
|
|
9
|
+
readonly VALIDATOR_SCAN_API_URL: string;
|
|
10
|
+
readonly JWT_TOKEN: string;
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
};
|
|
13
|
+
export default config;
|
|
14
|
+
export function initializeConfig(config: Record<string, unknown>): void;
|
|
15
|
+
export function setJWTToken(token: string): void;
|
|
16
|
+
export function getConfigValue(key: string): string | undefined;
|
|
17
|
+
export function setWalletAdapter(adapter: unknown): void;
|
|
18
|
+
export function getWalletAdapter(): unknown;
|
|
19
|
+
export function getAdapterProvider(): unknown;
|
|
20
|
+
}
|