@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.
- package/README.md +361 -361
- package/index.js +12 -12
- package/package.json +47 -47
- package/src/api/config.d.ts +20 -20
- package/src/api/index.ts +355 -355
- package/src/api/tokenStore.ts +87 -87
- package/src/api/types.ts +149 -149
- package/src/auth0/index.js +50 -50
- package/src/canton/index.js +3825 -3790
- package/src/canton/instrumentCatalog.js +174 -174
- 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.js +89 -89
- 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) |
|