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

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.
Files changed (28) hide show
  1. package/README.md +361 -361
  2. package/index.js +12 -12
  3. package/package.json +47 -47
  4. package/src/api/config.d.ts +20 -20
  5. package/src/api/index.ts +355 -355
  6. package/src/api/tokenStore.ts +87 -87
  7. package/src/api/types.ts +149 -149
  8. package/src/auth0/index.js +50 -50
  9. package/src/canton/index.js +3825 -3790
  10. package/src/canton/instrumentCatalog.js +174 -174
  11. package/src/canton/request_schemas/cancel_orders_amulet.json +77 -77
  12. package/src/canton/request_schemas/cancel_orders_utility.json +68 -68
  13. package/src/canton/request_schemas/create_order_proposal_amulet.json +94 -94
  14. package/src/canton/request_schemas/create_order_proposal_utility.json +121 -121
  15. package/src/canton/request_schemas/create_utility_credential.json +31 -31
  16. package/src/canton/request_schemas/execute_transfer_factory.json +43 -43
  17. package/src/canton/request_schemas/get_allocation_factory.json +21 -21
  18. package/src/canton/request_schemas/get_amulet_holdings.json +21 -21
  19. package/src/canton/request_schemas/get_instrument_configurations.json +21 -21
  20. package/src/canton/request_schemas/get_locked_amulet_holdings.json +21 -21
  21. package/src/canton/request_schemas/get_order_proposals.json +21 -21
  22. package/src/canton/request_schemas/get_orders.json +21 -21
  23. package/src/canton/request_schemas/get_sender_credentials.json +22 -22
  24. package/src/canton/request_schemas/get_transfer_factory.json +28 -28
  25. package/src/canton/request_schemas/get_utility_holdings.json +21 -21
  26. package/src/canton/request_schemas/unlock_amulet.json +38 -38
  27. package/src/canton/walletAdapter.js +89 -89
  28. package/src/config/index.js +154 -154
package/README.md CHANGED
@@ -1,361 +1,361 @@
1
- # Temple Canton JS
2
-
3
- JavaScript SDK for interacting with the Temple Canton blockchain exchange. Supports CC, USDCx, and CBTC tokens on the Canton network.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install @temple-digital-group/temple-canton-js
9
- ```
10
-
11
- ## Configuration
12
-
13
- Call `initialize()` before using any SDK functions. It sets up the config and optionally authenticates with the Temple REST API.
14
-
15
- ### Custom Validator
16
-
17
- For apps connecting to your own validator (browser or server-side):
18
-
19
- ```javascript
20
- import { initialize } from "@temple-digital-group/temple-canton-js";
21
-
22
- await initialize({
23
- NETWORK: "mainnet",
24
- VALIDATOR_API_URL: "https://your-validator-url",
25
- VALIDATOR_SCAN_API_URL: "https://your-scan-api-url",
26
- VALIDATOR_USER_PARTY_ID: "your-user-party-id",
27
- AUTH0_TOKEN_URL: "https://your-auth0-domain/oauth/token",
28
- AUTH0_USER_ID: "your-auth0-user-id",
29
- AUTH0_CLIENT_ID: "your-client-id",
30
- AUTH0_CLIENT_SECRET: "your-client-secret",
31
- AUTH0_AUDIENCE: "your-audience",
32
- });
33
- ```
34
-
35
- ### Custom Validator + REST API
36
-
37
- To also authenticate with the Temple REST API at init time, pass `API_EMAIL`/`API_PASSWORD`:
38
-
39
- ```javascript
40
- import { initialize } from "@temple-digital-group/temple-canton-js";
41
-
42
- await initialize({
43
- NETWORK: "mainnet",
44
- VALIDATOR_API_URL: "https://your-validator-url",
45
- VALIDATOR_SCAN_API_URL: "https://your-scan-api-url",
46
- VALIDATOR_USER_PARTY_ID: "your-user-party-id",
47
- AUTH0_TOKEN_URL: "https://your-auth0-domain/oauth/token",
48
- AUTH0_USER_ID: "your-auth0-user-id",
49
- AUTH0_CLIENT_ID: "your-client-id",
50
- AUTH0_CLIENT_SECRET: "your-client-secret",
51
- AUTH0_AUDIENCE: "your-audience",
52
- API_EMAIL: "user@example.com", // REST API login (optional)
53
- API_PASSWORD: "password",
54
- });
55
- ```
56
-
57
- | Key | Required | Description |
58
- | ------------------------- | -------- | ---------------------------------------------------- |
59
- | `NETWORK` | Yes | `mainnet`, `testnet`, or `localhost` |
60
- | `VALIDATOR_API_URL` | Yes | Base URL of the Canton validator ledger API |
61
- | `VALIDATOR_SCAN_API_URL` | Yes | Base URL of the Canton Scan API |
62
- | `VALIDATOR_USER_PARTY_ID` | Yes | The user's party ID on the network |
63
- | `JWT_TOKEN` | Yes\* | Pre-authenticated JWT token for ledger API calls |
64
- | `AUTH0_TOKEN_URL` | Yes\* | Auth0 OAuth token endpoint |
65
- | `AUTH0_CLIENT_ID` | Yes\* | Auth0 application client ID |
66
- | `AUTH0_CLIENT_SECRET` | Yes\* | Auth0 application client secret |
67
- | `AUTH0_AUDIENCE` | Yes\* | Auth0 API audience identifier |
68
- | `AUTH0_USER_ID` | Yes\* | Auth0 user ID for the service account |
69
- | `API_EMAIL` | No | Temple REST API email (triggers login at init) |
70
- | `API_PASSWORD` | No | Temple REST API password (required with `API_EMAIL`) |
71
-
72
- \* Provide either `JWT_TOKEN` (browser) or the `AUTH0_*` fields (server-side). The SDK fetches and caches JWT tokens automatically when Auth0 credentials are provided.
73
-
74
- To update the JWT token later without re-initializing:
75
-
76
- ```javascript
77
- import { setJWTToken } from "@temple-digital-group/temple-canton-js";
78
-
79
- setJWTToken(newToken);
80
- ```
81
-
82
- ### Wallet Adapter
83
-
84
- For apps using Loop Wallet. Pass the Loop SDK instance as `WALLET_ADAPTER` — the SDK auto-detects server vs client mode.
85
-
86
- - **Server-side:** uses `loop.executeTransaction()` (signs locally with private key)
87
- - **Client-side:** uses `loop.provider.submitTransaction()` (delegates signing to Loop Wallet via WebSocket)
88
-
89
- ```javascript
90
- import { initialize, createOrderProposal } from "@temple-digital-group/temple-canton-js";
91
-
92
- await initialize({
93
- NETWORK: "mainnet",
94
- WALLET_ADAPTER: loop, // Pass the Loop SDK instance
95
- });
96
-
97
- // Auto-submit: SDK handles signing and submission
98
- const result = await createOrderProposal(orderArgs);
99
-
100
- // Or get the raw command back for manual submission
101
- const command = await createOrderProposal(orderArgs, true);
102
- ```
103
-
104
- You can also set or change the wallet adapter after init:
105
-
106
- ```javascript
107
- import { setWalletAdapter } from "@temple-digital-group/temple-canton-js";
108
-
109
- setWalletAdapter(loop);
110
- ```
111
-
112
- | Key | Required | Description |
113
- | ---------------- | -------- | --------------------------------------------------------------- |
114
- | `NETWORK` | Yes | `mainnet`, `testnet`, or `localhost` |
115
- | `WALLET_ADAPTER` | No | Loop SDK instance — auto-detects server/client mode for signing |
116
-
117
- > You can optionally pass `API_EMAIL`/`API_PASSWORD` to any `initialize()` call to also authenticate with the REST API at init time.
118
-
119
- > **Legacy:** `initializeConfig()` is still available as a sync-only alternative (no REST API auth). `initialize()` is the recommended approach.
120
-
121
- ## Supported Instruments
122
-
123
- | Asset | Type | Networks |
124
- | ----- | ----------- | ---------------- |
125
- | CC | Canton Coin | testnet, mainnet |
126
- | USDCx | Utility | testnet, mainnet |
127
- | CBTC | Utility | testnet, mainnet |
128
-
129
- ## Supported Trading Pairs
130
-
131
- - `CC/USDCx`
132
- - `CBTC/USDCx`
133
-
134
- ## Usage
135
-
136
- ### Get User Balances
137
-
138
- ```javascript
139
- import { getUserBalances } from "@temple-digital-group/temple-canton-js";
140
-
141
- // Both params are optional — falls back to wallet adapter / config
142
- const balances = await getUserBalances();
143
-
144
- // Or pass explicitly
145
- const balances = await getUserBalances(partyId);
146
- const balances = await getUserBalances(partyId, walletProvider);
147
- ```
148
-
149
- Each entry in the returned array contains:
150
-
151
- ```javascript
152
- {
153
- asset: 'USDCx', // Catalog symbol
154
- total_balance: 170.5, // Unlocked + locked combined
155
- available_balance: 150.5, // Unlocked balance (usable for orders)
156
- locked_balance: 20.0, // Locked balance
157
- dso: null, // DSO party (CC only)
158
- registrar: '...', // Registrar party (utility tokens)
159
- operator: '...', // Operator party
160
- provider: '...', // Provider party
161
- merge_warning: true, // true if multiple unlocked holdings exist
162
- holdings: [...], // Unlocked holding contracts
163
- locked_holdings: [...], // Locked holding contracts
164
- utilityContext: { ... } // CIP-56 context (null when using wallet provider)
165
- }
166
- ```
167
-
168
- > **Note:** `utilityContext` is only resolved when using a custom validator (ledger API access). When using a wallet provider, it will be `null`.
169
-
170
- ### Create an Order
171
-
172
- ```javascript
173
- import { createOrderProposal } from "@temple-digital-group/temple-canton-js";
174
-
175
- const result = await createOrderProposal({
176
- party: partyId,
177
- symbol: "CC/USDCx",
178
- side: "Buy",
179
- quantity: "100",
180
- pricePerUnit: "1.5",
181
- expiration: new Date(Date.now() + 3600000).toISOString(),
182
- userId: auth0UserId, // Optional if authenticated via REST API — auto-resolved from login
183
- orderType: "limit",
184
- });
185
- ```
186
-
187
- #### Handling Stale Holdings (Wallet Provider)
188
-
189
- When a holding is used in an order, the contract is archived on-ledger and a new one is created. If the wallet provider hasn't caught up yet, it may still return the old (archived) holding, causing the next order to fail.
190
-
191
- **Auto-submit (`returnCommand = false`):** The SDK handles this automatically. If a stale holding error is detected, it waits for the provider to sync and retries up to 3 times.
192
-
193
- **Manual submit (`returnCommand = true`):** You are responsible for handling stale holdings. `createOrderProposal` returns a `holdingContractId` — the contract ID of the holding that was selected. You can poll until it disappears from the provider's active contracts:
194
-
195
- ```javascript
196
- import { createOrderProposal, getAmuletHoldingsForParty } from "@temple-digital-group/temple-canton-js";
197
-
198
- const { command, holdingContractId } = await createOrderProposal(orderArgs, true);
199
- const result = await walletProvider.submitTransaction(command);
200
-
201
- // Poll until the provider catches up before creating next order
202
- async function waitForProviderSync(contractId, party, provider, interval = 500, maxAttempts = 20) {
203
- for (let i = 0; i < maxAttempts; i++) {
204
- const holdings = await getAmuletHoldingsForParty(party, false, provider);
205
- const stillActive = holdings?.some((h) => h.contractEntry?.JsActiveContract?.createdEvent?.contractId === contractId);
206
- if (!stillActive) return true; // Provider caught up
207
- await new Promise((r) => setTimeout(r, interval));
208
- }
209
- return false; // Timed out
210
- }
211
-
212
- await waitForProviderSync(holdingContractId, party, walletProvider);
213
- // Safe to create next order
214
- ```
215
-
216
- > `holdingContractId` is included in success responses, error responses, and `returnCommand` responses.
217
-
218
- ### Get Orders and Proposals
219
-
220
- ```javascript
221
- import { getOrdersForParty, getOrderProposalsForParty } from "@temple-digital-group/temple-canton-js";
222
-
223
- const orders = await getOrdersForParty(partyId);
224
- const proposals = await getOrderProposalsForParty(partyId);
225
- ```
226
-
227
- ### REST API (Optional) — Market Data and Orders
228
-
229
- The REST API functions are optional. If you pass `API_EMAIL`/`API_PASSWORD` in `initialize()`, the SDK authenticates at init time. You can also call `login()` directly.
230
-
231
- ```javascript
232
- import { initialize, getTicker, getOrderBook, getActiveOrders, cancelOrder } from "@temple-digital-group/temple-canton-js";
233
-
234
- await initialize({
235
- NETWORK: "mainnet",
236
- API_EMAIL: "user@example.com",
237
- API_PASSWORD: "password",
238
- });
239
-
240
- // Market data
241
- const ticker = await getTicker("CC/USDCx");
242
- const book = await getOrderBook("CC/USDCx", { levels: 10 });
243
-
244
- // Orders
245
- const orders = await getActiveOrders({ symbol: "CC/USDCx" });
246
- const result = await cancelOrder("order-id-123");
247
-
248
- // All functions return { error: true, status, code, message } on failure
249
- if (result.error) {
250
- console.log(`Failed: ${result.message}`);
251
- }
252
- ```
253
-
254
- ### Merge Holdings
255
-
256
- ```javascript
257
- import { mergeAmuletHoldingsForParty, mergeUtilityHoldingsForParty, getAmuletDisclosures, getUtxoCount } from "@temple-digital-group/temple-canton-js";
258
-
259
- // Custom Validator (server-side)
260
- await mergeAmuletHoldingsForParty(partyId);
261
- await mergeUtilityHoldingsForParty(partyId, "USDCx");
262
-
263
- // Wallet Provider — merge up to 5 smallest USDCx UTXOs
264
- const command = await mergeUtilityHoldingsForParty(partyId, "USDCx", true, walletProvider, 5);
265
- const result = await walletProvider.submitTransaction(command);
266
-
267
- // Wallet Provider — merge CC (requires disclosures)
268
- const disclosures = await getAmuletDisclosures(partyId);
269
- const cmd = await mergeAmuletHoldingsForParty(partyId, true, walletProvider, 5, disclosures);
270
- const res = await walletProvider.submitTransaction(cmd);
271
-
272
- // Check UTXO status after merge — confirm the order amount is now covered
273
- const counts = await getUtxoCount(partyId, "USDCx", walletProvider);
274
- // { total: 5, unlocked: 1, locked: 4, largestUnlocked: 27.26, unlockedBalance: 27.26 }
275
- if (counts.largestUnlocked >= requiredAmount) {
276
- // Safe to retry createOrderProposal
277
- }
278
- ```
279
-
280
- ## API Reference
281
-
282
- > Functions marked with **W** support Loop Wallet via the wallet adapter. All other functions require a custom validator (ledger access via `VALIDATOR_API_URL` and `JWT_TOKEN`).
283
-
284
- ### Configuration
285
-
286
- | Function | Description |
287
- | --------------------------- | ----------------------------------------------------------------------------- |
288
- | `initialize(config)` | Initialize the SDK, set config, and optionally authenticate with the REST API |
289
- | `setJWTToken(token)` | Update the JWT token without re-initializing the config |
290
- | `setWalletAdapter(adapter)` | Set or update the Loop SDK instance for all wallet-aware functions |
291
-
292
- ### Instrument Catalog
293
-
294
- | Function | Description |
295
- | ---------------------------- | --------------------------------------- |
296
- | `getSupportedTradingPairs()` | Get the list of supported trading pairs |
297
- | `getInstrumentCatalog()` | Get the full instrument catalog |
298
-
299
- ### Holdings
300
-
301
- | Function | Provider | Description |
302
- | ----------------------------------------------------------------- | -------- | -------------------------------------------------------------- |
303
- | `getUserBalances(party?, provider?)` | **W** | Get all balances grouped by asset (CC, locked CC, and utility) |
304
- | `getAmuletHoldingsForParty(party, returnCommand, provider)` | **W** | Get CC holdings |
305
- | `getLockedAmuletHoldingsForParty(party, returnCommand, provider)`| **W** | Get locked CC holdings |
306
- | `getUtilityHoldingsForParty(party, returnCommand, provider)` | **W** | Get utility token holdings |
307
- | `getUtxoCount(party, assetId, provider)` | **W** | Get UTXO summary: counts, largest unlocked amount, and total unlocked balance |
308
-
309
- ### Orders
310
-
311
- | Function | Provider | Description |
312
- | ------------------------------------- | -------- | --------------------------- |
313
- | `createOrderProposal(orderArguments)` | **W** | Create an order proposal |
314
- | `getOrderProposalsForParty(party)` | | Get pending order proposals |
315
- | `getOrdersForParty(party)` | | Get active orders |
316
-
317
- ### Holding Operations
318
-
319
- | Function | Provider | Description |
320
- | ------------------------------------------------------------------------------------------ | -------- | --------------------------- |
321
- | `mergeAmuletHoldingsForParty(party, returnCommand, provider, maxUtxos, amuletDisclosures)` | **W** | Merge CC holdings into one |
322
- | `mergeUtilityHoldingsForParty(party, utilityAsset, returnCommand, provider, maxUtxos)` | **W** | Merge utility holdings into one |
323
- | `splitAmuletHoldingForParty(party, outputQuantity)` | | Split a CC holding |
324
- | `unlockLockedAmulets(party)` | | Unlock locked CC holdings |
325
-
326
- ### Temple REST API (Optional)
327
-
328
- > These functions call the Temple public REST API and are entirely optional. Pass `API_EMAIL`/`API_PASSWORD` in `initialize()`, or call `login()` directly — tokens are stored and auto-refreshed.
329
-
330
- #### Auth
331
-
332
- | Function | Description |
333
- | ----------------------------------- | --------------------------------------------------- |
334
- | `login(email, password)` | Login and store access/refresh tokens and user profile |
335
- | `refreshAccessToken(refreshToken?)` | Refresh the access token (auto-called when expired) |
336
- | `logout()` | Clear stored tokens |
337
- | `getUserId()` | Get the stored user ID from login (used automatically by `createOrderProposal`) |
338
-
339
- #### Market Data
340
-
341
- | Function | Description |
342
- | ----------------------------------- | --------------------------------------------------------- |
343
- | `getTicker(symbol?)` | Get ticker data for one or all trading pairs |
344
- | `getOrderBook(symbol, options?)` | Get the order book (options: `levels`, `precision`) |
345
- | `getSymbolConfig(symbol)` | Get symbol configuration (paused, decimals, min quantity) |
346
- | `getOpenInterest(symbol)` | Get open interest for a trading pair |
347
- | `getRecentTrades(symbol, options?)` | Get recent trades (options: `limit`, max 500) |
348
-
349
- #### Orders
350
-
351
- | Function | Description |
352
- | --------------------------- | ---------------------------------------------- |
353
- | `getActiveOrders(options?)` | Get active orders (options: `symbol`, `limit`) |
354
- | `cancelOrder(orderId)` | Cancel a specific order |
355
- | `cancelAllOrders(options?)` | Cancel all orders (options: `symbol` filter) |
356
-
357
- #### Disclosures
358
-
359
- | Function | Description |
360
- | ------------------------- | ------------------------------------------------------------------------------- |
361
- | `getDisclosures(partyId)` | Get CC disclosure data (amulet rules, open mining rounds, external party rules) |
1
+ # Temple Canton JS
2
+
3
+ JavaScript SDK for interacting with the Temple Canton blockchain exchange. Supports CC, USDCx, and CBTC tokens on the Canton network.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @temple-digital-group/temple-canton-js
9
+ ```
10
+
11
+ ## Configuration
12
+
13
+ Call `initialize()` before using any SDK functions. It sets up the config and optionally authenticates with the Temple REST API.
14
+
15
+ ### Custom Validator
16
+
17
+ For apps connecting to your own validator (browser or server-side):
18
+
19
+ ```javascript
20
+ import { initialize } from "@temple-digital-group/temple-canton-js";
21
+
22
+ await initialize({
23
+ NETWORK: "mainnet",
24
+ VALIDATOR_API_URL: "https://your-validator-url",
25
+ VALIDATOR_SCAN_API_URL: "https://your-scan-api-url",
26
+ VALIDATOR_USER_PARTY_ID: "your-user-party-id",
27
+ AUTH0_TOKEN_URL: "https://your-auth0-domain/oauth/token",
28
+ AUTH0_USER_ID: "your-auth0-user-id",
29
+ AUTH0_CLIENT_ID: "your-client-id",
30
+ AUTH0_CLIENT_SECRET: "your-client-secret",
31
+ AUTH0_AUDIENCE: "your-audience",
32
+ });
33
+ ```
34
+
35
+ ### Custom Validator + REST API
36
+
37
+ To also authenticate with the Temple REST API at init time, pass `API_EMAIL`/`API_PASSWORD`:
38
+
39
+ ```javascript
40
+ import { initialize } from "@temple-digital-group/temple-canton-js";
41
+
42
+ await initialize({
43
+ NETWORK: "mainnet",
44
+ VALIDATOR_API_URL: "https://your-validator-url",
45
+ VALIDATOR_SCAN_API_URL: "https://your-scan-api-url",
46
+ VALIDATOR_USER_PARTY_ID: "your-user-party-id",
47
+ AUTH0_TOKEN_URL: "https://your-auth0-domain/oauth/token",
48
+ AUTH0_USER_ID: "your-auth0-user-id",
49
+ AUTH0_CLIENT_ID: "your-client-id",
50
+ AUTH0_CLIENT_SECRET: "your-client-secret",
51
+ AUTH0_AUDIENCE: "your-audience",
52
+ API_EMAIL: "user@example.com", // REST API login (optional)
53
+ API_PASSWORD: "password",
54
+ });
55
+ ```
56
+
57
+ | Key | Required | Description |
58
+ | ------------------------- | -------- | ---------------------------------------------------- |
59
+ | `NETWORK` | Yes | `mainnet`, `testnet`, or `localhost` |
60
+ | `VALIDATOR_API_URL` | Yes | Base URL of the Canton validator ledger API |
61
+ | `VALIDATOR_SCAN_API_URL` | Yes | Base URL of the Canton Scan API |
62
+ | `VALIDATOR_USER_PARTY_ID` | Yes | The user's party ID on the network |
63
+ | `JWT_TOKEN` | Yes\* | Pre-authenticated JWT token for ledger API calls |
64
+ | `AUTH0_TOKEN_URL` | Yes\* | Auth0 OAuth token endpoint |
65
+ | `AUTH0_CLIENT_ID` | Yes\* | Auth0 application client ID |
66
+ | `AUTH0_CLIENT_SECRET` | Yes\* | Auth0 application client secret |
67
+ | `AUTH0_AUDIENCE` | Yes\* | Auth0 API audience identifier |
68
+ | `AUTH0_USER_ID` | Yes\* | Auth0 user ID for the service account |
69
+ | `API_EMAIL` | No | Temple REST API email (triggers login at init) |
70
+ | `API_PASSWORD` | No | Temple REST API password (required with `API_EMAIL`) |
71
+
72
+ \* Provide either `JWT_TOKEN` (browser) or the `AUTH0_*` fields (server-side). The SDK fetches and caches JWT tokens automatically when Auth0 credentials are provided.
73
+
74
+ To update the JWT token later without re-initializing:
75
+
76
+ ```javascript
77
+ import { setJWTToken } from "@temple-digital-group/temple-canton-js";
78
+
79
+ setJWTToken(newToken);
80
+ ```
81
+
82
+ ### Wallet Adapter
83
+
84
+ For apps using Loop Wallet. Pass the Loop SDK instance as `WALLET_ADAPTER` — the SDK auto-detects server vs client mode.
85
+
86
+ - **Server-side:** uses `loop.executeTransaction()` (signs locally with private key)
87
+ - **Client-side:** uses `loop.provider.submitTransaction()` (delegates signing to Loop Wallet via WebSocket)
88
+
89
+ ```javascript
90
+ import { initialize, createOrderProposal } from "@temple-digital-group/temple-canton-js";
91
+
92
+ await initialize({
93
+ NETWORK: "mainnet",
94
+ WALLET_ADAPTER: loop, // Pass the Loop SDK instance
95
+ });
96
+
97
+ // Auto-submit: SDK handles signing and submission
98
+ const result = await createOrderProposal(orderArgs);
99
+
100
+ // Or get the raw command back for manual submission
101
+ const command = await createOrderProposal(orderArgs, true);
102
+ ```
103
+
104
+ You can also set or change the wallet adapter after init:
105
+
106
+ ```javascript
107
+ import { setWalletAdapter } from "@temple-digital-group/temple-canton-js";
108
+
109
+ setWalletAdapter(loop);
110
+ ```
111
+
112
+ | Key | Required | Description |
113
+ | ---------------- | -------- | --------------------------------------------------------------- |
114
+ | `NETWORK` | Yes | `mainnet`, `testnet`, or `localhost` |
115
+ | `WALLET_ADAPTER` | No | Loop SDK instance — auto-detects server/client mode for signing |
116
+
117
+ > You can optionally pass `API_EMAIL`/`API_PASSWORD` to any `initialize()` call to also authenticate with the REST API at init time.
118
+
119
+ > **Legacy:** `initializeConfig()` is still available as a sync-only alternative (no REST API auth). `initialize()` is the recommended approach.
120
+
121
+ ## Supported Instruments
122
+
123
+ | Asset | Type | Networks |
124
+ | ----- | ----------- | ---------------- |
125
+ | CC | Canton Coin | testnet, mainnet |
126
+ | USDCx | Utility | testnet, mainnet |
127
+ | CBTC | Utility | testnet, mainnet |
128
+
129
+ ## Supported Trading Pairs
130
+
131
+ - `CC/USDCx`
132
+ - `CBTC/USDCx`
133
+
134
+ ## Usage
135
+
136
+ ### Get User Balances
137
+
138
+ ```javascript
139
+ import { getUserBalances } from "@temple-digital-group/temple-canton-js";
140
+
141
+ // Both params are optional — falls back to wallet adapter / config
142
+ const balances = await getUserBalances();
143
+
144
+ // Or pass explicitly
145
+ const balances = await getUserBalances(partyId);
146
+ const balances = await getUserBalances(partyId, walletProvider);
147
+ ```
148
+
149
+ Each entry in the returned array contains:
150
+
151
+ ```javascript
152
+ {
153
+ asset: 'USDCx', // Catalog symbol
154
+ total_balance: 170.5, // Unlocked + locked combined
155
+ available_balance: 150.5, // Unlocked balance (usable for orders)
156
+ locked_balance: 20.0, // Locked balance
157
+ dso: null, // DSO party (CC only)
158
+ registrar: '...', // Registrar party (utility tokens)
159
+ operator: '...', // Operator party
160
+ provider: '...', // Provider party
161
+ merge_warning: true, // true if multiple unlocked holdings exist
162
+ holdings: [...], // Unlocked holding contracts
163
+ locked_holdings: [...], // Locked holding contracts
164
+ utilityContext: { ... } // CIP-56 context (null when using wallet provider)
165
+ }
166
+ ```
167
+
168
+ > **Note:** `utilityContext` is only resolved when using a custom validator (ledger API access). When using a wallet provider, it will be `null`.
169
+
170
+ ### Create an Order
171
+
172
+ ```javascript
173
+ import { createOrderProposal } from "@temple-digital-group/temple-canton-js";
174
+
175
+ const result = await createOrderProposal({
176
+ party: partyId,
177
+ symbol: "CC/USDCx",
178
+ side: "Buy",
179
+ quantity: "100",
180
+ pricePerUnit: "1.5",
181
+ expiration: new Date(Date.now() + 3600000).toISOString(),
182
+ userId: auth0UserId, // Optional if authenticated via REST API — auto-resolved from login
183
+ orderType: "limit",
184
+ });
185
+ ```
186
+
187
+ #### Handling Stale Holdings (Wallet Provider)
188
+
189
+ When a holding is used in an order, the contract is archived on-ledger and a new one is created. If the wallet provider hasn't caught up yet, it may still return the old (archived) holding, causing the next order to fail.
190
+
191
+ **Auto-submit (`returnCommand = false`):** The SDK handles this automatically. If a stale holding error is detected, it waits for the provider to sync and retries up to 3 times.
192
+
193
+ **Manual submit (`returnCommand = true`):** You are responsible for handling stale holdings. `createOrderProposal` returns a `holdingContractId` — the contract ID of the holding that was selected. You can poll until it disappears from the provider's active contracts:
194
+
195
+ ```javascript
196
+ import { createOrderProposal, getAmuletHoldingsForParty } from "@temple-digital-group/temple-canton-js";
197
+
198
+ const { command, holdingContractId } = await createOrderProposal(orderArgs, true);
199
+ const result = await walletProvider.submitTransaction(command);
200
+
201
+ // Poll until the provider catches up before creating next order
202
+ async function waitForProviderSync(contractId, party, provider, interval = 500, maxAttempts = 20) {
203
+ for (let i = 0; i < maxAttempts; i++) {
204
+ const holdings = await getAmuletHoldingsForParty(party, false, provider);
205
+ const stillActive = holdings?.some((h) => h.contractEntry?.JsActiveContract?.createdEvent?.contractId === contractId);
206
+ if (!stillActive) return true; // Provider caught up
207
+ await new Promise((r) => setTimeout(r, interval));
208
+ }
209
+ return false; // Timed out
210
+ }
211
+
212
+ await waitForProviderSync(holdingContractId, party, walletProvider);
213
+ // Safe to create next order
214
+ ```
215
+
216
+ > `holdingContractId` is included in success responses, error responses, and `returnCommand` responses.
217
+
218
+ ### Get Orders and Proposals
219
+
220
+ ```javascript
221
+ import { getOrdersForParty, getOrderProposalsForParty } from "@temple-digital-group/temple-canton-js";
222
+
223
+ const orders = await getOrdersForParty(partyId);
224
+ const proposals = await getOrderProposalsForParty(partyId);
225
+ ```
226
+
227
+ ### REST API (Optional) — Market Data and Orders
228
+
229
+ The REST API functions are optional. If you pass `API_EMAIL`/`API_PASSWORD` in `initialize()`, the SDK authenticates at init time. You can also call `login()` directly.
230
+
231
+ ```javascript
232
+ import { initialize, getTicker, getOrderBook, getActiveOrders, cancelOrder } from "@temple-digital-group/temple-canton-js";
233
+
234
+ await initialize({
235
+ NETWORK: "mainnet",
236
+ API_EMAIL: "user@example.com",
237
+ API_PASSWORD: "password",
238
+ });
239
+
240
+ // Market data
241
+ const ticker = await getTicker("CC/USDCx");
242
+ const book = await getOrderBook("CC/USDCx", { levels: 10 });
243
+
244
+ // Orders
245
+ const orders = await getActiveOrders({ symbol: "CC/USDCx" });
246
+ const result = await cancelOrder("order-id-123");
247
+
248
+ // All functions return { error: true, status, code, message } on failure
249
+ if (result.error) {
250
+ console.log(`Failed: ${result.message}`);
251
+ }
252
+ ```
253
+
254
+ ### Merge Holdings
255
+
256
+ ```javascript
257
+ import { mergeAmuletHoldingsForParty, mergeUtilityHoldingsForParty, getAmuletDisclosures, getUtxoCount } from "@temple-digital-group/temple-canton-js";
258
+
259
+ // Custom Validator (server-side)
260
+ await mergeAmuletHoldingsForParty(partyId);
261
+ await mergeUtilityHoldingsForParty(partyId, "USDCx");
262
+
263
+ // Wallet Provider — merge up to 5 smallest USDCx UTXOs
264
+ const command = await mergeUtilityHoldingsForParty(partyId, "USDCx", true, walletProvider, 5);
265
+ const result = await walletProvider.submitTransaction(command);
266
+
267
+ // Wallet Provider — merge CC (requires disclosures)
268
+ const disclosures = await getAmuletDisclosures(partyId);
269
+ const cmd = await mergeAmuletHoldingsForParty(partyId, true, walletProvider, 5, disclosures);
270
+ const res = await walletProvider.submitTransaction(cmd);
271
+
272
+ // Check UTXO status after merge — confirm the order amount is now covered
273
+ const counts = await getUtxoCount(partyId, "USDCx", walletProvider);
274
+ // { total: 5, unlocked: 1, locked: 4, largestUnlocked: 27.26, unlockedBalance: 27.26 }
275
+ if (counts.largestUnlocked >= requiredAmount) {
276
+ // Safe to retry createOrderProposal
277
+ }
278
+ ```
279
+
280
+ ## API Reference
281
+
282
+ > Functions marked with **W** support Loop Wallet via the wallet adapter. All other functions require a custom validator (ledger access via `VALIDATOR_API_URL` and `JWT_TOKEN`).
283
+
284
+ ### Configuration
285
+
286
+ | Function | Description |
287
+ | --------------------------- | ----------------------------------------------------------------------------- |
288
+ | `initialize(config)` | Initialize the SDK, set config, and optionally authenticate with the REST API |
289
+ | `setJWTToken(token)` | Update the JWT token without re-initializing the config |
290
+ | `setWalletAdapter(adapter)` | Set or update the Loop SDK instance for all wallet-aware functions |
291
+
292
+ ### Instrument Catalog
293
+
294
+ | Function | Description |
295
+ | ---------------------------- | --------------------------------------- |
296
+ | `getSupportedTradingPairs()` | Get the list of supported trading pairs |
297
+ | `getInstrumentCatalog()` | Get the full instrument catalog |
298
+
299
+ ### Holdings
300
+
301
+ | Function | Provider | Description |
302
+ | ----------------------------------------------------------------- | -------- | -------------------------------------------------------------- |
303
+ | `getUserBalances(party?, provider?)` | **W** | Get all balances grouped by asset (CC, locked CC, and utility) |
304
+ | `getAmuletHoldingsForParty(party, returnCommand, provider)` | **W** | Get CC holdings |
305
+ | `getLockedAmuletHoldingsForParty(party, returnCommand, provider)`| **W** | Get locked CC holdings |
306
+ | `getUtilityHoldingsForParty(party, returnCommand, provider)` | **W** | Get utility token holdings |
307
+ | `getUtxoCount(party, assetId, provider)` | **W** | Get UTXO summary: counts, largest unlocked amount, and total unlocked balance |
308
+
309
+ ### Orders
310
+
311
+ | Function | Provider | Description |
312
+ | ------------------------------------- | -------- | --------------------------- |
313
+ | `createOrderProposal(orderArguments)` | **W** | Create an order proposal |
314
+ | `getOrderProposalsForParty(party)` | | Get pending order proposals |
315
+ | `getOrdersForParty(party)` | | Get active orders |
316
+
317
+ ### Holding Operations
318
+
319
+ | Function | Provider | Description |
320
+ | ------------------------------------------------------------------------------------------ | -------- | --------------------------- |
321
+ | `mergeAmuletHoldingsForParty(party, returnCommand, provider, maxUtxos, amuletDisclosures)` | **W** | Merge CC holdings into one |
322
+ | `mergeUtilityHoldingsForParty(party, utilityAsset, returnCommand, provider, maxUtxos)` | **W** | Merge utility holdings into one |
323
+ | `splitAmuletHoldingForParty(party, outputQuantity)` | | Split a CC holding |
324
+ | `unlockLockedAmulets(party)` | | Unlock locked CC holdings |
325
+
326
+ ### Temple REST API (Optional)
327
+
328
+ > These functions call the Temple public REST API and are entirely optional. Pass `API_EMAIL`/`API_PASSWORD` in `initialize()`, or call `login()` directly — tokens are stored and auto-refreshed.
329
+
330
+ #### Auth
331
+
332
+ | Function | Description |
333
+ | ----------------------------------- | --------------------------------------------------- |
334
+ | `login(email, password)` | Login and store access/refresh tokens and user profile |
335
+ | `refreshAccessToken(refreshToken?)` | Refresh the access token (auto-called when expired) |
336
+ | `logout()` | Clear stored tokens |
337
+ | `getUserId()` | Get the stored user ID from login (used automatically by `createOrderProposal`) |
338
+
339
+ #### Market Data
340
+
341
+ | Function | Description |
342
+ | ----------------------------------- | --------------------------------------------------------- |
343
+ | `getTicker(symbol?)` | Get ticker data for one or all trading pairs |
344
+ | `getOrderBook(symbol, options?)` | Get the order book (options: `levels`, `precision`) |
345
+ | `getSymbolConfig(symbol)` | Get symbol configuration (paused, decimals, min quantity) |
346
+ | `getOpenInterest(symbol)` | Get open interest for a trading pair |
347
+ | `getRecentTrades(symbol, options?)` | Get recent trades (options: `limit`, max 500) |
348
+
349
+ #### Orders
350
+
351
+ | Function | Description |
352
+ | --------------------------- | ---------------------------------------------- |
353
+ | `getActiveOrders(options?)` | Get active orders (options: `symbol`, `limit`) |
354
+ | `cancelOrder(orderId)` | Cancel a specific order |
355
+ | `cancelAllOrders(options?)` | Cancel all orders (options: `symbol` filter) |
356
+
357
+ #### Disclosures
358
+
359
+ | Function | Description |
360
+ | ------------------------- | ------------------------------------------------------------------------------- |
361
+ | `getDisclosures(partyId)` | Get CC disclosure data (amulet rules, open mining rounds, external party rules) |