@kaleidorg/wallet-engine 1.0.0-beta.36 → 1.0.0-beta.38

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 (41) hide show
  1. package/dist/adapters/RgbAdapter.d.ts +70 -28
  2. package/dist/adapters/RgbAdapter.d.ts.map +1 -1
  3. package/dist/adapters/RgbAdapter.js +463 -371
  4. package/dist/adapters/RgbAdapter.js.map +1 -1
  5. package/dist/adapters/flashnet.d.ts +14 -0
  6. package/dist/adapters/flashnet.d.ts.map +1 -0
  7. package/dist/adapters/flashnet.js +14 -0
  8. package/dist/adapters/flashnet.js.map +1 -0
  9. package/dist/adapters/rgb.d.ts +11 -0
  10. package/dist/adapters/rgb.d.ts.map +1 -0
  11. package/dist/adapters/rgb.js +11 -0
  12. package/dist/adapters/rgb.js.map +1 -0
  13. package/dist/lib/flashnet-client-manager.d.ts +25 -8
  14. package/dist/lib/flashnet-client-manager.d.ts.map +1 -1
  15. package/dist/lib/flashnet-client-manager.js +97 -13
  16. package/dist/lib/flashnet-client-manager.js.map +1 -1
  17. package/dist/lib/kaleido-client-manager.d.ts +38 -3
  18. package/dist/lib/kaleido-client-manager.d.ts.map +1 -1
  19. package/dist/lib/kaleido-client-manager.js +79 -10
  20. package/dist/lib/kaleido-client-manager.js.map +1 -1
  21. package/dist/lib/orchestra-client.d.ts +149 -0
  22. package/dist/lib/orchestra-client.d.ts.map +1 -0
  23. package/dist/lib/orchestra-client.js +178 -0
  24. package/dist/lib/orchestra-client.js.map +1 -0
  25. package/dist/lib/rgb-converters.d.ts +62 -0
  26. package/dist/lib/rgb-converters.d.ts.map +1 -0
  27. package/dist/lib/rgb-converters.js +179 -0
  28. package/dist/lib/rgb-converters.js.map +1 -0
  29. package/dist/lib/rgb-fee-policy.d.ts +41 -0
  30. package/dist/lib/rgb-fee-policy.d.ts.map +1 -0
  31. package/dist/lib/rgb-fee-policy.js +52 -0
  32. package/dist/lib/rgb-fee-policy.js.map +1 -0
  33. package/dist/lib/rgb-helpers.d.ts +54 -0
  34. package/dist/lib/rgb-helpers.d.ts.map +1 -0
  35. package/dist/lib/rgb-helpers.js +89 -0
  36. package/dist/lib/rgb-helpers.js.map +1 -0
  37. package/dist/types/flashnet.d.ts +20 -0
  38. package/dist/types/flashnet.d.ts.map +1 -1
  39. package/dist/types/flashnet.js +34 -6
  40. package/dist/types/flashnet.js.map +1 -1
  41. package/package.json +9 -1
@@ -1,18 +1,24 @@
1
1
  /**
2
2
  * RGB Protocol Adapter
3
- * Uses kaleido-sdk to implement the protocol adapter interface.
4
- * Ported from rate-extension/src/protocols/adapters/RgbAdapter.ts
3
+ * Uses Kaleido SDK to implement the protocol adapter interface
4
+ */
5
+ import { log } from "../lib/log.js";
6
+ import { kaleidoClientManager } from "../lib/kaleido-client-manager.js";
7
+ import { KaleidoError, APIError, NetworkError, NodeNotConfiguredError, QuoteExpiredError, InsufficientBalanceError as SdkInsufficientBalanceError, Layer as SdkLayer, } from "kaleido-sdk";
8
+ import { ProtocolError, ConnectionError, InsufficientBalanceError, } from "../types/base.js";
9
+ import { PROTOCOL_OPERATIONS } from "../capabilities/operations.js";
10
+ import { resolveRgbFeeRatePolicy } from "../lib/rgb-fee-policy.js";
11
+ import { mapPaymentStatus, mapSwapStatus } from "../lib/rgb-helpers.js";
12
+ import { convertBtcBalance, convertNodeAssetToUnified, convertPaymentToTransaction, convertSdkBalance, convertSwapToTransaction, convertTransferToTransaction, } from "../lib/rgb-converters.js";
13
+ /**
14
+ * RGB Protocol Adapter Implementation using Kaleido SDK
5
15
  */
6
- import { kaleidoClientManager } from '../lib/kaleido-client-manager.js';
7
- import { KaleidoError, APIError, NetworkError, NodeNotConfiguredError, QuoteExpiredError, InsufficientBalanceError as SdkInsufficientBalanceError, Layer as SdkLayer, } from 'kaleido-sdk';
8
- import { ProtocolError, ConnectionError, InsufficientBalanceError, } from '../types/base.js';
9
- import { PROTOCOL_OPERATIONS } from '../capabilities/operations.js';
10
16
  export class RgbAdapter {
11
17
  constructor() {
12
- this.protocolName = 'RGB_LN';
18
+ this.protocolName = "RGB_LN";
19
+ this.supportedLayers = ["RGB_L1", "RGB_LN", "BTC_L1", "BTC_LN"];
20
+ this.version = "1.0.0";
13
21
  this.capabilities = PROTOCOL_OPERATIONS.RGB_LN;
14
- this.supportedLayers = ['RGB_L1', 'RGB_LN', 'BTC_L1', 'BTC_LN'];
15
- this.version = '1.0.0';
16
22
  this.connected = false;
17
23
  this.config = null;
18
24
  this.swapAccessTokens = new Map();
@@ -22,66 +28,99 @@ export class RgbAdapter {
22
28
  // ========================================================================
23
29
  async connect(config) {
24
30
  const rgbConfig = config;
25
- if (!rgbConfig.nodeUrl) {
26
- throw new ConnectionError('Node URL is required', 'RGB_LN');
31
+ const transport = rgbConfig.transport ?? "http";
32
+ // For HTTP a node URL is required; for NWC the connection string is. Maker
33
+ // is optional in both cases.
34
+ if (transport === "nwc") {
35
+ if (!rgbConfig.nwcUri) {
36
+ throw new ConnectionError("NWC connection string is required", "RGB_LN");
37
+ }
38
+ }
39
+ else if (!rgbConfig.nodeUrl) {
40
+ throw new ConnectionError("Node URL is required", "RGB_LN");
27
41
  }
42
+ log.info(`[RgbAdapter] connect() — transport=${transport} ${transport === "nwc" ? "nwc=(string)" : `nodeUrl=${rgbConfig.nodeUrl}`} makerUrl=${rgbConfig.makerUrl || "(none)"} hasApiKey=${!!rgbConfig.apiKey}`);
28
43
  try {
44
+ // Initialize Kaleido SDK client (maker URL is optional, transport-independent)
29
45
  kaleidoClientManager.initialize({
30
- baseUrl: rgbConfig.makerUrl || '',
46
+ baseUrl: rgbConfig.makerUrl || "",
31
47
  nodeUrl: rgbConfig.nodeUrl,
32
48
  apiKey: rgbConfig.apiKey,
49
+ transport,
50
+ nwcUri: rgbConfig.nwcUri,
33
51
  });
34
52
  const client = kaleidoClientManager.getClient();
35
- await client.rln.getNodeInfo();
53
+ log.info(`[RgbAdapter] Calling client.rln.getNodeInfo() → ${rgbConfig.nodeUrl}`);
54
+ const t0 = Date.now();
55
+ let nodeInfo;
56
+ try {
57
+ nodeInfo = await client.rln.getNodeInfo();
58
+ log.info(`[RgbAdapter] getNodeInfo() OK in ${Date.now() - t0}ms:`, nodeInfo);
59
+ }
60
+ catch (httpErr) {
61
+ const msg = httpErr instanceof Error ? httpErr.message : String(httpErr);
62
+ log.error(`[RgbAdapter] getNodeInfo() FAILED after ${Date.now() - t0}ms: ${msg}`, httpErr);
63
+ throw httpErr;
64
+ }
36
65
  this.config = rgbConfig;
37
66
  this.connected = true;
38
- console.log('[RgbAdapter] Connected to RGB node via kaleido-sdk');
39
- // Test maker connection (non-blocking)
67
+ log.info("[RgbAdapter] Connected to RGB node successfully via SDK");
68
+ // Optionally test maker connection (non-blocking)
40
69
  if (rgbConfig.makerUrl) {
41
70
  try {
71
+ log.info(`[RgbAdapter] Testing maker API → ${rgbConfig.makerUrl}`);
42
72
  await client.maker.listAssets();
43
- console.log('[RgbAdapter] Maker API accessible');
73
+ log.info("[RgbAdapter] Maker API accessible ✓");
44
74
  }
45
75
  catch (error) {
46
76
  const msg = error instanceof Error ? error.message : String(error);
47
- console.warn('[RgbAdapter] Maker API not accessible (swaps will show error):', msg);
77
+ log.warn("[RgbAdapter] Maker API not accessible (swaps will show error):", msg);
78
+ // Don't throw - maker is optional, only needed for swaps
48
79
  }
49
80
  }
81
+ else {
82
+ log.info("[RgbAdapter] No maker URL provided (swaps disabled)");
83
+ }
50
84
  }
51
85
  catch (error) {
52
86
  const msg = error instanceof Error ? error.message : String(error);
53
- throw new ConnectionError(`Failed to connect to RGB node: ${msg}`, 'RGB_LN', error);
87
+ log.error(`[RgbAdapter] connect() failed: ${msg}`);
88
+ throw new ConnectionError(`Failed to connect to RGB node: ${msg}`, "RGB_LN", error instanceof Error ? error : undefined);
54
89
  }
55
90
  }
56
91
  async disconnect() {
57
92
  kaleidoClientManager.reset();
58
93
  this.connected = false;
59
94
  this.config = null;
60
- console.log('[RgbAdapter] Disconnected');
95
+ log.info("[RgbAdapter] Disconnected");
61
96
  }
62
97
  isConnected() {
63
98
  return this.connected && kaleidoClientManager.isInitialized();
64
99
  }
65
100
  async getConnectionInfo() {
66
101
  if (!this.isConnected()) {
67
- throw new ProtocolError('Not connected', 'RGB_LN', 'NOT_CONNECTED');
102
+ throw new ProtocolError("Not connected", "RGB_LN", "NOT_CONNECTED");
68
103
  }
69
104
  const info = {
70
- protocol: 'RGB_LN',
105
+ protocol: "RGB_LN",
71
106
  connected: true,
72
- network: this.config?.network || 'regtest',
107
+ network: this.config?.network || "regtest",
73
108
  };
109
+ // Try to get node info if node is configured
74
110
  if (kaleidoClientManager.hasNode()) {
75
111
  try {
76
112
  const client = kaleidoClientManager.getClient();
77
113
  const nodeInfo = await client.rln.getNodeInfo();
78
114
  const networkInfo = await client.rln.getNetworkInfo();
79
- info.nodeId = nodeInfo.pubkey || '';
115
+ info.nodeId = nodeInfo.pubkey || "";
80
116
  info.blockHeight = networkInfo.height || 0;
81
- info.syncStatus = { synced: true, progress: 100 };
117
+ info.syncStatus = {
118
+ synced: true,
119
+ progress: 100,
120
+ };
82
121
  }
83
122
  catch (error) {
84
- console.warn('[RgbAdapter] Could not get node info:', error);
123
+ log.warn("[RgbAdapter] Could not get node info:", error);
85
124
  }
86
125
  }
87
126
  return info;
@@ -91,91 +130,134 @@ export class RgbAdapter {
91
130
  // ========================================================================
92
131
  async listAssets() {
93
132
  if (!this.isConnected()) {
94
- throw new ProtocolError('Not connected', 'RGB_LN', 'NOT_CONNECTED');
133
+ throw new ProtocolError("Not connected", "RGB_LN", "NOT_CONNECTED");
95
134
  }
96
135
  const client = kaleidoClientManager.getClient();
136
+ // Get node assets (always works if node is connected)
97
137
  let nodeAssetsArray = [];
98
138
  if (kaleidoClientManager.hasNode()) {
99
139
  try {
100
140
  const nodeAssets = await client.rln.listAssets();
101
- const response = nodeAssets;
141
+ // ListAssetsResponse is an object with nia, uda, cfa arrays
142
+ const nodeAssetsResponse = nodeAssets;
102
143
  nodeAssetsArray = [
103
- ...(response.nia || []),
104
- ...(response.uda || []),
105
- ...(response.cfa || []),
144
+ ...(nodeAssetsResponse.nia || []),
145
+ ...(nodeAssetsResponse.uda || []),
146
+ ...(nodeAssetsResponse.cfa || []),
106
147
  ];
148
+ log.info("[RgbAdapter] Got assets from node via SDK:", nodeAssetsArray.length);
107
149
  }
108
150
  catch (error) {
109
- console.warn('[RgbAdapter] Could not get node assets:', error);
151
+ log.warn("[RgbAdapter] Could not get node assets:", error);
110
152
  }
111
153
  }
154
+ // Wallet asset lists must reflect wallet-owned node assets only.
155
+ // Maker-listed assets belong to market discovery and should be queried
156
+ // through the dedicated maker APIs used by swap flows.
112
157
  if (nodeAssetsArray.length === 0) {
113
- // Return at least BTC if no RGB assets
114
- try {
115
- const btcBalance = await client.rln.getBtcBalance();
116
- return [this.createBtcAsset(btcBalance)];
117
- }
118
- catch {
119
- return [];
120
- }
121
- }
122
- // Add BTC as first asset
123
- const assets = [];
124
- try {
125
- const btcBalance = await client.rln.getBtcBalance();
126
- assets.push(this.createBtcAsset(btcBalance));
158
+ throw new ProtocolError("No wallet assets available from node", "RGB_LN", "NO_ASSETS_AVAILABLE");
127
159
  }
128
- catch {
129
- // Skip BTC if balance check fails
130
- }
131
- assets.push(...nodeAssetsArray.map(a => this.convertNodeAssetToUnified(a)));
132
- return assets;
160
+ return nodeAssetsArray.map((asset) => convertNodeAssetToUnified(asset));
133
161
  }
134
162
  async getAsset(assetId) {
135
163
  const assets = await this.listAssets();
136
- const asset = assets.find(a => a.id === assetId || a.ticker === assetId);
164
+ const asset = assets.find((a) => a.id === assetId || a.ticker === assetId);
137
165
  if (!asset) {
138
- throw new ProtocolError(`Asset not found: ${assetId}`, 'RGB_LN', 'ASSET_NOT_FOUND');
166
+ throw new ProtocolError(`Asset not found: ${assetId}`, "RGB_LN", "ASSET_NOT_FOUND");
139
167
  }
140
168
  return asset;
141
169
  }
142
170
  async getAssetBalance(assetId) {
143
171
  if (!kaleidoClientManager.hasNode()) {
144
- throw new ProtocolError('Node not configured', 'RGB_LN', 'NODE_NOT_CONFIGURED');
172
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
173
+ }
174
+ if (!assetId || !assetId.trim()) {
175
+ throw new ProtocolError("Asset ID is required", "RGB_LN", "INVALID_ASSET_ID");
145
176
  }
146
177
  try {
147
178
  const client = kaleidoClientManager.getClient();
148
- if (assetId === 'BTC' || assetId.toLowerCase() === 'btc') {
179
+ // Check if requesting BTC balance
180
+ if (assetId === "BTC" || assetId.toLowerCase() === "btc") {
149
181
  const btcBalance = await client.rln.getBtcBalance();
150
- return this.convertBtcBalance(btcBalance);
182
+ return convertBtcBalance(btcBalance);
183
+ }
184
+ // Get RGB asset balance
185
+ const balanceData = await client.rln.getAssetBalance({
186
+ asset_id: assetId,
187
+ });
188
+ return convertSdkBalance(balanceData);
189
+ }
190
+ catch (error) {
191
+ throw this.handleSdkError(error, "Failed to get asset balance");
192
+ }
193
+ }
194
+ async refreshBalances() {
195
+ if (!kaleidoClientManager.hasNode())
196
+ return;
197
+ try {
198
+ const client = kaleidoClientManager.getClient();
199
+ const refreshTransfers = client.rln?.refreshTransfers?.bind(client.rln) ?? client.refreshTransfers?.bind(client);
200
+ if (refreshTransfers) {
201
+ await refreshTransfers({ skip_sync: false });
151
202
  }
152
- const balanceData = await client.rln.getAssetBalance({ asset_id: assetId });
153
- return this.convertSdkBalance(balanceData);
154
203
  }
155
204
  catch (error) {
156
- throw this.handleSdkError(error, 'Failed to get asset balance');
205
+ log.warn("[RgbAdapter] Could not refresh transfers:", error);
157
206
  }
158
207
  }
159
- async refreshBalances() { }
160
208
  // ========================================================================
161
209
  // Transaction Operations
162
210
  // ========================================================================
163
211
  async listTransactions(filter) {
164
212
  if (!kaleidoClientManager.hasNode()) {
165
- throw new ProtocolError('Node not configured', 'RGB_LN', 'NODE_NOT_CONFIGURED');
213
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
166
214
  }
167
215
  try {
168
216
  const client = kaleidoClientManager.getClient();
217
+ // listTransfers requires asset_id for RGB on-chain transfers
169
218
  if (!filter?.asset) {
170
- throw new ProtocolError('Asset ID is required for listing RGB transfers', 'RGB_LN', 'ASSET_ID_REQUIRED');
219
+ throw new ProtocolError("Asset ID is required for listing RGB transfers", "RGB_LN", "ASSET_ID_REQUIRED");
171
220
  }
172
- const response = await client.rln.listTransfers({ asset_id: filter.asset });
173
- const transfers = response.transfers || [];
174
- return transfers
175
- .map(t => this.convertTransferToTransaction(t))
176
- .filter(tx => {
177
- if (!filter)
178
- return true;
221
+ // Fetch on-chain transfers, Lightning payments AND swaps in parallel.
222
+ // RGB assets can move via all three rails; the asset card needs to
223
+ // surface all of them. listPayments() and listSwaps() return ALL
224
+ // entries — filter client-side by asset_id.
225
+ const [transfersResponse, paymentsResponse, swapsResponse] = await Promise.all([
226
+ client.rln.listTransfers({ asset_id: filter.asset }),
227
+ client.rln.listPayments().catch(() => ({ payments: [] })),
228
+ client.rln.listSwaps().catch(() => ({ maker: [], taker: [] })),
229
+ ]);
230
+ const transferTxs = (transfersResponse.transfers ?? []).map((transfer) => convertTransferToTransaction(transfer));
231
+ const paymentTxs = (paymentsResponse.payments ?? [])
232
+ .filter((payment) => {
233
+ const paymentAssetId = payment.asset_id;
234
+ // Match BTC payments to BTC, RGB payments to their asset_id.
235
+ if (filter.asset === "BTC" || filter.asset?.toLowerCase() === "btc") {
236
+ return !paymentAssetId;
237
+ }
238
+ return paymentAssetId === filter.asset;
239
+ })
240
+ .map((payment) => convertPaymentToTransaction(payment));
241
+ const isAssetBtc = filter.asset === "BTC" || filter.asset?.toLowerCase() === "btc";
242
+ const matchesSwapAsset = (swap) => {
243
+ const fromAsset = swap.from_asset ?? null;
244
+ const toAsset = swap.to_asset ?? null;
245
+ // BTC side of a swap is encoded as a missing asset_id.
246
+ if (isAssetBtc)
247
+ return fromAsset === null || toAsset === null;
248
+ return fromAsset === filter.asset || toAsset === filter.asset;
249
+ };
250
+ const swapTxs = [
251
+ ...(swapsResponse.maker ?? [])
252
+ .filter(matchesSwapAsset)
253
+ .map((swap) => convertSwapToTransaction(swap, "maker")),
254
+ ...(swapsResponse.taker ?? [])
255
+ .filter(matchesSwapAsset)
256
+ .map((swap) => convertSwapToTransaction(swap, "taker")),
257
+ ];
258
+ const merged = [...transferTxs, ...paymentTxs, ...swapTxs];
259
+ return merged
260
+ .filter((tx) => {
179
261
  if (filter.type && tx.type !== filter.type)
180
262
  return false;
181
263
  if (filter.status && tx.status !== filter.status)
@@ -186,20 +268,21 @@ export class RgbAdapter {
186
268
  return false;
187
269
  return true;
188
270
  })
271
+ .sort((a, b) => b.timestamp - a.timestamp)
189
272
  .slice(filter?.offset || 0, (filter?.offset || 0) + (filter?.limit || 100));
190
273
  }
191
274
  catch (error) {
192
- throw this.handleSdkError(error, 'Failed to list transactions');
275
+ throw this.handleSdkError(error, "Failed to list transactions");
193
276
  }
194
277
  }
195
278
  async getTransaction(txId, assetId) {
196
279
  if (!assetId) {
197
- throw new ProtocolError('Asset ID is required to look up an RGB transaction', 'RGB_LN', 'ASSET_ID_REQUIRED');
280
+ throw new ProtocolError("Asset ID is required to look up an RGB transaction", "RGB_LN", "ASSET_ID_REQUIRED");
198
281
  }
199
282
  const transactions = await this.listTransactions({ asset: assetId });
200
- const tx = transactions.find(t => t.id === txId);
283
+ const tx = transactions.find((t) => t.id === txId);
201
284
  if (!tx) {
202
- throw new ProtocolError(`Transaction not found: ${txId}`, 'RGB_LN', 'TX_NOT_FOUND');
285
+ throw new ProtocolError(`Transaction not found: ${txId}`, "RGB_LN", "TX_NOT_FOUND");
203
286
  }
204
287
  return tx;
205
288
  }
@@ -208,57 +291,62 @@ export class RgbAdapter {
208
291
  // ========================================================================
209
292
  async createInvoice(request) {
210
293
  if (!kaleidoClientManager.hasNode()) {
211
- throw new ProtocolError('Node not configured', 'RGB_LN', 'NODE_NOT_CONFIGURED');
294
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
212
295
  }
213
296
  try {
214
297
  const client = kaleidoClientManager.getClient();
298
+ // Create Lightning invoice (BTC or RGB over Lightning)
299
+ // Build params - always include expiry_sec
215
300
  const lnInvoiceParams = {
216
- expiry_sec: request.expirySeconds || 3600,
301
+ expiry_sec: request.expirySeconds || 3600, // Default to 1 hour
217
302
  };
218
- const isRgbInvoice = request.asset && request.asset !== 'BTC' && request.asset !== 'btc';
303
+ // Include asset fields if provided (for RGB Lightning invoices)
304
+ const isRgbInvoice = request.asset && request.asset !== "BTC" && request.asset !== "btc";
219
305
  if (isRgbInvoice) {
220
306
  lnInvoiceParams.asset_id = request.asset;
221
307
  if (request.assetAmount && request.assetAmount > 0) {
222
308
  lnInvoiceParams.asset_amount = request.assetAmount;
223
309
  }
224
- // RGB HTLC requires minimum 3000 sats in msats
225
- const RGB_HTLC_MIN_MSAT = 3000000;
310
+ // The node requires amt_msat >= 3000000 for ANY RGB Lightning invoice,
311
+ // even zero-amount ones where asset_amount is omitted.
312
+ const RGB_HTLC_MIN_MSAT = 3000000; // 3000 sats in msats
226
313
  const requestedMsat = request.amount && request.amount > 0 ? request.amount * 1000 : 0;
227
314
  lnInvoiceParams.amt_msat = Math.max(requestedMsat, RGB_HTLC_MIN_MSAT);
228
315
  }
229
316
  else {
317
+ // BTC Lightning invoice — only include amt_msat if amount is provided
230
318
  if (request.amount && request.amount > 0) {
231
319
  lnInvoiceParams.amt_msat = request.amount * 1000;
232
320
  }
233
321
  }
234
- const lnInvoice = await client.rln.createLNInvoice(lnInvoiceParams);
322
+ const lnInvoice = (await client.rln.createLNInvoice(lnInvoiceParams));
235
323
  return {
236
- invoice: lnInvoice.invoice ?? '',
237
- paymentHash: lnInvoice.payment_hash ?? '',
324
+ invoice: lnInvoice.invoice ?? "",
325
+ paymentHash: lnInvoice.payment_hash ?? "",
238
326
  amount: request.amount,
239
327
  expiresAt: Date.now() + (request.expirySeconds || 3600) * 1000,
240
328
  description: request.description,
241
329
  };
242
330
  }
243
331
  catch (error) {
244
- throw this.handleSdkError(error, 'Failed to create invoice');
332
+ throw this.handleSdkError(error, "Failed to create invoice");
245
333
  }
246
334
  }
247
335
  async decodeInvoice(invoice) {
248
336
  if (!kaleidoClientManager.hasNode()) {
249
- throw new ProtocolError('Node not configured', 'RGB_LN', 'NODE_NOT_CONFIGURED');
337
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
250
338
  }
251
339
  try {
252
340
  const client = kaleidoClientManager.getClient();
253
- const decoded = await client.rln.decodeLNInvoice({ invoice });
341
+ const decoded = (await client.rln.decodeLNInvoice({ invoice }));
254
342
  const amtMsat = decoded.amt_msat;
255
343
  return {
256
- paymentHash: decoded.payment_hash ?? '',
344
+ paymentHash: decoded.payment_hash ?? "",
257
345
  amount: amtMsat != null ? amtMsat / 1000 : undefined,
258
346
  amountMsat: amtMsat ?? undefined,
259
347
  description: decoded.description,
260
348
  expiresAt: decoded.expiry_sec ? Date.now() + decoded.expiry_sec * 1000 : 0,
261
- destination: decoded.payee_pubkey || '',
349
+ destination: decoded.payee_pubkey || "",
262
350
  asset_id: decoded.asset_id ?? undefined,
263
351
  asset_amount: decoded.asset_amount ?? undefined,
264
352
  payment_hash: decoded.payment_hash,
@@ -266,51 +354,95 @@ export class RgbAdapter {
266
354
  };
267
355
  }
268
356
  catch (error) {
269
- throw this.handleSdkError(error, 'Failed to decode invoice');
357
+ throw this.handleSdkError(error, "Failed to decode invoice");
270
358
  }
271
359
  }
272
360
  async sendPayment(request) {
273
361
  if (!kaleidoClientManager.hasNode()) {
274
- throw new ProtocolError('Node not configured', 'RGB_LN', 'NODE_NOT_CONFIGURED');
362
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
275
363
  }
276
364
  try {
277
365
  const client = kaleidoClientManager.getClient();
278
- const sendParams = { invoice: request.invoice };
279
- if (request.amount) {
280
- sendParams.amt_msat = request.amount * 1000;
366
+ const sendParams = {
367
+ invoice: request.invoice,
368
+ };
369
+ // Forward `amt_msat` only for amountless invoices. Previously this
370
+ // truthy-check forwarded any positive `request.amount`, silently
371
+ // re-amounting amount-bearing invoices.
372
+ if (request.amount && request.amount > 0) {
373
+ let invoiceIsAmountless = false;
374
+ try {
375
+ const decoded = await this.decodeInvoice(request.invoice);
376
+ invoiceIsAmountless = !decoded.amount_msat && !decoded.amountMsat && !decoded.amount;
377
+ }
378
+ catch {
379
+ // If decode fails, err on the side of not overriding.
380
+ invoiceIsAmountless = false;
381
+ }
382
+ if (invoiceIsAmountless) {
383
+ sendParams.amt_msat = request.amount * 1000;
384
+ }
281
385
  }
282
- const result = await client.rln.sendPayment(sendParams);
386
+ const result = (await client.rln.sendPayment(sendParams));
283
387
  return {
284
- paymentHash: result.payment_hash ?? '',
388
+ paymentHash: result.payment_hash ?? "",
285
389
  preimage: result.payment_preimage,
286
390
  amount: result.amount_msat ? result.amount_msat / 1000 : 0,
287
391
  fee: result.fee_msat ? result.fee_msat / 1000 : 0,
288
- status: this.mapPaymentStatus(result.status),
392
+ status: mapPaymentStatus(result.status),
393
+ timestamp: Date.now(),
394
+ };
395
+ }
396
+ catch (error) {
397
+ throw this.handleSdkError(error, "Failed to send payment");
398
+ }
399
+ }
400
+ async payKeysend(request) {
401
+ if (!kaleidoClientManager.hasNode()) {
402
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
403
+ }
404
+ try {
405
+ const client = kaleidoClientManager.getClient();
406
+ const result = (await client.rln.keysend({
407
+ dest_pubkey: request.pubkey,
408
+ amt_msat: request.amount,
409
+ asset_id: request.assetId,
410
+ asset_amount: request.assetAmount,
411
+ }));
412
+ return {
413
+ paymentHash: result.payment_hash ?? "",
414
+ preimage: result.payment_preimage,
415
+ amount: request.amount / 1000,
416
+ fee: 0,
417
+ status: mapPaymentStatus(result.status),
289
418
  timestamp: Date.now(),
290
419
  };
291
420
  }
292
421
  catch (error) {
293
- throw this.handleSdkError(error, 'Failed to send payment');
422
+ throw this.handleSdkError(error, "Failed to send keysend payment");
294
423
  }
295
424
  }
296
425
  async getPaymentStatus(paymentHash) {
297
426
  if (!kaleidoClientManager.hasNode()) {
298
- throw new ProtocolError('Node not configured', 'RGB_LN', 'NODE_NOT_CONFIGURED');
427
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
299
428
  }
300
429
  try {
301
430
  const client = kaleidoClientManager.getClient();
302
- const response = await client.rln.getPayment({ payment_hash: paymentHash });
431
+ const response = (await client.rln.getPayment({
432
+ payment_hash: paymentHash,
433
+ }));
434
+ // The response may be the payment directly or wrapped in a { payment } object
303
435
  const payment = (response.payment ?? response);
304
436
  return {
305
437
  paymentHash,
306
- status: this.mapPaymentStatus(payment.status),
438
+ status: mapPaymentStatus(payment.status),
307
439
  amount: payment.amount_msat ? payment.amount_msat / 1000 : undefined,
308
440
  fee: payment.fee_msat ? payment.fee_msat / 1000 : undefined,
309
441
  timestamp: payment.created_at,
310
442
  };
311
443
  }
312
444
  catch (error) {
313
- throw this.handleSdkError(error, 'Failed to get payment status');
445
+ throw this.handleSdkError(error, "Failed to get payment status");
314
446
  }
315
447
  }
316
448
  // ========================================================================
@@ -318,19 +450,19 @@ export class RgbAdapter {
318
450
  // ========================================================================
319
451
  async getReceiveAddress(assetId) {
320
452
  if (!kaleidoClientManager.hasNode()) {
321
- throw new ProtocolError('Node not configured', 'RGB_LN', 'NODE_NOT_CONFIGURED');
453
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
322
454
  }
323
455
  try {
324
456
  const client = kaleidoClientManager.getClient();
325
- const addressData = await client.rln.getAddress();
457
+ const addressData = (await client.rln.getAddress());
326
458
  return {
327
- address: addressData.address ?? '',
328
- format: 'BTC_ADDRESS',
459
+ address: addressData.address ?? "",
460
+ format: "BTC_ADDRESS",
329
461
  asset: assetId,
330
462
  };
331
463
  }
332
464
  catch (error) {
333
- throw this.handleSdkError(error, 'Failed to get receive address');
465
+ throw this.handleSdkError(error, "Failed to get receive address");
334
466
  }
335
467
  }
336
468
  // ========================================================================
@@ -338,68 +470,83 @@ export class RgbAdapter {
338
470
  // ========================================================================
339
471
  async getNodeInfo() {
340
472
  if (!kaleidoClientManager.hasNode()) {
341
- throw new ProtocolError('Node not configured', 'RGB_LN', 'NODE_NOT_CONFIGURED');
473
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
342
474
  }
343
475
  try {
344
- return await kaleidoClientManager.getClient().rln.getNodeInfo();
476
+ const client = kaleidoClientManager.getClient();
477
+ return await client.rln.getNodeInfo();
345
478
  }
346
479
  catch (error) {
347
- throw this.handleSdkError(error, 'Failed to get node info');
480
+ throw this.handleSdkError(error, "Failed to get node info");
348
481
  }
349
482
  }
350
483
  async getBtcBalance() {
351
484
  if (!kaleidoClientManager.hasNode()) {
352
- throw new ProtocolError('Node not configured', 'RGB_LN', 'NODE_NOT_CONFIGURED');
485
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
353
486
  }
354
487
  try {
355
488
  const client = kaleidoClientManager.getClient();
356
489
  const btcBalance = await client.rln.getBtcBalance();
357
490
  const vanilla = btcBalance?.vanilla || {};
358
491
  const colored = btcBalance?.colored || {};
359
- const confirmed = (vanilla.spendable || 0) + (colored.spendable || 0);
360
- const futureTotal = (vanilla.future || 0) + (colored.future || 0);
492
+ const spendableVanilla = vanilla.spendable || 0;
493
+ const spendableColored = colored.spendable || 0;
494
+ const futureVanilla = vanilla.future || 0;
495
+ const futureColored = colored.future || 0;
496
+ const confirmed = spendableVanilla + spendableColored;
497
+ // `future` is the expected balance after all pending txs settle.
498
+ // Pending incoming = amount above spendable; pending outgoing reduces future below spendable.
499
+ const futureTotal = futureVanilla + futureColored;
361
500
  const unconfirmed = Math.max(futureTotal - confirmed, 0);
362
501
  return { confirmed, unconfirmed, total: futureTotal };
363
502
  }
364
503
  catch (error) {
365
- throw this.handleSdkError(error, 'Failed to get BTC balance');
504
+ throw this.handleSdkError(error, "Failed to get BTC balance");
366
505
  }
367
506
  }
368
507
  async listChannels() {
369
508
  if (!kaleidoClientManager.hasNode()) {
370
- throw new ProtocolError('Node not configured', 'RGB_LN', 'NODE_NOT_CONFIGURED');
509
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
371
510
  }
372
511
  try {
373
- const response = await kaleidoClientManager.getClient().rln.listChannels();
374
- return response?.channels || response || [];
512
+ const client = kaleidoClientManager.getClient();
513
+ const response = (await client.rln.listChannels());
514
+ if (Array.isArray(response))
515
+ return response;
516
+ if (response && "channels" in response && Array.isArray(response.channels)) {
517
+ return response.channels;
518
+ }
519
+ return [];
375
520
  }
376
521
  catch (error) {
377
- throw this.handleSdkError(error, 'Failed to list channels');
522
+ throw this.handleSdkError(error, "Failed to list channels");
378
523
  }
379
524
  }
380
525
  async listPayments() {
381
526
  if (!kaleidoClientManager.hasNode()) {
382
- throw new ProtocolError('Node not configured', 'RGB_LN', 'NODE_NOT_CONFIGURED');
527
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
383
528
  }
384
529
  try {
385
- return await kaleidoClientManager.getClient().rln.listPayments();
530
+ const client = kaleidoClientManager.getClient();
531
+ return await client.rln.listPayments();
386
532
  }
387
533
  catch (error) {
388
- throw this.handleSdkError(error, 'Failed to list payments');
534
+ throw this.handleSdkError(error, "Failed to list payments");
389
535
  }
390
536
  }
391
537
  async listTransfers(options) {
392
538
  if (!kaleidoClientManager.hasNode()) {
393
- throw new ProtocolError('Node not configured', 'RGB_LN', 'NODE_NOT_CONFIGURED');
539
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
394
540
  }
395
541
  try {
542
+ const client = kaleidoClientManager.getClient();
396
543
  if (!options?.asset_id) {
397
544
  return { transfers: [] };
398
545
  }
399
- return await kaleidoClientManager.getClient().rln.listTransfers({ asset_id: options.asset_id });
546
+ return await client.rln.listTransfers({ asset_id: options.asset_id });
400
547
  }
401
548
  catch (error) {
402
- throw this.handleSdkError(error, 'Failed to list transfers');
549
+ throw this.handleSdkError(error, "Failed to list transfers");
403
550
  }
404
551
  }
405
552
  // ========================================================================
@@ -407,107 +554,218 @@ export class RgbAdapter {
407
554
  // ========================================================================
408
555
  async createRgbInvoice(params) {
409
556
  if (!kaleidoClientManager.hasNode()) {
410
- throw new ProtocolError('Node not configured', 'RGB_LN', 'NODE_NOT_CONFIGURED');
557
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
411
558
  }
412
559
  try {
413
- const durationSec = params.durationSeconds || params.duration_seconds || 3600;
414
- return await kaleidoClientManager.getClient().rln.createRgbInvoice({
415
- asset_id: params.assetId || params.asset_id,
416
- expiration_timestamp: Math.floor(Date.now() / 1000) + durationSec,
417
- min_confirmations: params.minConfirmations || params.min_confirmations || 1,
418
- witness: params.witness ?? true,
560
+ const client = kaleidoClientManager.getClient();
561
+ const durationSeconds = (params.durationSeconds ||
562
+ params.duration_seconds ||
563
+ 3600);
564
+ const invoiceReq = {
565
+ asset_id: (params.assetId || params.asset_id),
566
+ expiration_timestamp: Math.floor(Date.now() / 1000) + durationSeconds,
567
+ min_confirmations: (params.minConfirmations ||
568
+ params.min_confirmations ||
569
+ 1),
570
+ witness: (params.witness ?? true),
419
571
  ...(params.assignment ? { assignment: params.assignment } : {}),
572
+ };
573
+ return await client.rln.createRgbInvoice(invoiceReq);
574
+ }
575
+ catch (error) {
576
+ throw this.handleSdkError(error, "Failed to create RGB invoice");
577
+ }
578
+ }
579
+ async createRgbUtxos(params = {}) {
580
+ if (!kaleidoClientManager.hasNode()) {
581
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
582
+ }
583
+ try {
584
+ const client = kaleidoClientManager.getClient();
585
+ await client.rln.createUtxos({
586
+ up_to: params.upTo ?? false,
587
+ num: params.num ?? 3,
588
+ size: params.size ?? 3000,
589
+ fee_rate: await this.resolveFeeRate(params.feeRate, "normal"),
590
+ skip_sync: false,
420
591
  });
592
+ return { success: true };
421
593
  }
422
594
  catch (error) {
423
- throw this.handleSdkError(error, 'Failed to create RGB invoice');
595
+ throw this.handleSdkError(error, "Failed to create RGB UTXOs");
596
+ }
597
+ }
598
+ async listRgbUnspents() {
599
+ if (!kaleidoClientManager.hasNode()) {
600
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
601
+ }
602
+ try {
603
+ const client = kaleidoClientManager.getClient();
604
+ const response = await client.rln.listUnspents();
605
+ return response;
606
+ }
607
+ catch (error) {
608
+ throw this.handleSdkError(error, "Failed to list unspent outputs");
609
+ }
610
+ }
611
+ /**
612
+ * Resolve a sat/vB fee rate to use for an RGB on-chain operation.
613
+ *
614
+ * Thin wrapper around {@link resolveRgbFeeRatePolicy} that provides the
615
+ * `estimateFn` and `network` from the live adapter state. The pure
616
+ * policy lives outside the class so it can be unit-tested without
617
+ * spinning up a kaleido client.
618
+ *
619
+ * Closes [GL #26] for the RGB adapter — previously every RGB on-chain
620
+ * spend used a hardcoded `1` (createUtxos) or `5` (sendAsset, sendBtc)
621
+ * which is a regtest-era default. On a busy mainnet mempool that's
622
+ * effectively "never confirms".
623
+ */
624
+ async resolveFeeRate(provided, urgency = "normal") {
625
+ return resolveRgbFeeRatePolicy({
626
+ provided,
627
+ urgency,
628
+ network: this.config?.network ?? null,
629
+ estimateFn: async (blocks) => {
630
+ try {
631
+ const { fee_rate } = await this.estimateRgbFee(blocks);
632
+ return fee_rate;
633
+ }
634
+ catch (err) {
635
+ log.warn(`[RgbAdapter] fee estimation failed (urgency=${urgency}, blocks=${blocks}):`, err);
636
+ return null;
637
+ }
638
+ },
639
+ });
640
+ }
641
+ async estimateRgbFee(blocks) {
642
+ if (!kaleidoClientManager.hasNode()) {
643
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
644
+ }
645
+ try {
646
+ const client = kaleidoClientManager.getClient();
647
+ const response = await client.rln.estimateFee({ blocks });
648
+ return { fee_rate: response?.fee_rate ?? 1 };
649
+ }
650
+ catch (error) {
651
+ throw this.handleSdkError(error, "Failed to estimate fee");
652
+ }
653
+ }
654
+ async getRgbDetailedBalance() {
655
+ if (!kaleidoClientManager.hasNode()) {
656
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
657
+ }
658
+ try {
659
+ const client = kaleidoClientManager.getClient();
660
+ const balance = await client.rln.getBtcBalance();
661
+ const empty = { settled: 0, future: 0, spendable: 0 };
662
+ return {
663
+ vanilla: balance?.vanilla ?? empty,
664
+ colored: balance?.colored ?? empty,
665
+ };
666
+ }
667
+ catch (error) {
668
+ throw this.handleSdkError(error, "Failed to get detailed BTC balance");
424
669
  }
425
670
  }
426
671
  async decodeRgbInvoice(params) {
427
672
  if (!kaleidoClientManager.hasNode()) {
428
- throw new ProtocolError('Node not configured', 'RGB_LN', 'NODE_NOT_CONFIGURED');
673
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
429
674
  }
430
675
  try {
431
- return await kaleidoClientManager.getClient().rln.decodeRgbInvoice({
676
+ const client = kaleidoClientManager.getClient();
677
+ return await client.rln.decodeRgbInvoice({
432
678
  invoice: params.invoice || params,
433
679
  });
434
680
  }
435
681
  catch (error) {
436
- throw this.handleSdkError(error, 'Failed to decode RGB invoice');
682
+ throw this.handleSdkError(error, "Failed to decode RGB invoice");
437
683
  }
438
684
  }
439
685
  async getInvoiceStatus(params) {
440
686
  if (!kaleidoClientManager.hasNode()) {
441
- throw new ProtocolError('Node not configured', 'RGB_LN', 'NODE_NOT_CONFIGURED');
687
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
442
688
  }
443
689
  try {
444
- return await kaleidoClientManager.getClient().rln.getInvoiceStatus(params);
690
+ const client = kaleidoClientManager.getClient();
691
+ return await client.rln.getInvoiceStatus(params);
445
692
  }
446
693
  catch (error) {
447
- throw this.handleSdkError(error, 'Failed to get invoice status');
694
+ throw this.handleSdkError(error, "Failed to get invoice status");
448
695
  }
449
696
  }
450
697
  async sendAsset(params) {
451
698
  if (!kaleidoClientManager.hasNode()) {
452
- throw new ProtocolError('Node not configured', 'RGB_LN', 'NODE_NOT_CONFIGURED');
699
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
453
700
  }
454
701
  try {
455
702
  const client = kaleidoClientManager.getClient();
456
- const assetId = params.assetId || params.asset_id;
457
- const amount = params.amount ?? params.assignment?.value;
458
- const assignment = params.assignment ?? (amount != null ? { type: 'Fungible', value: amount } : undefined);
459
- return await client.rln.sendRgb({
703
+ const assetId = (params.assetId || params.asset_id);
704
+ const assignmentObj = params.assignment;
705
+ const amount = (params.amount ?? assignmentObj?.value);
706
+ // The SDK always requires an assignment; derive it from amount when not explicitly provided
707
+ const assignment = params.assignment ?? (amount != null ? { type: "Fungible", value: amount } : undefined);
708
+ const sendReq = {
460
709
  donation: params.donation || false,
461
- fee_rate: params.feeRate || params.fee_rate || 5,
710
+ fee_rate: await this.resolveFeeRate((params.feeRate ?? params.fee_rate), "normal"),
462
711
  min_confirmations: 1,
463
712
  recipient_map: {
464
- [assetId]: [{
465
- recipient_id: params.recipientId || params.recipient_id,
713
+ [assetId]: [
714
+ {
715
+ recipient_id: (params.recipientId ||
716
+ params.recipient_id),
466
717
  assignment,
467
- transport_endpoints: params.transportEndpoints || params.transport_endpoints || [],
718
+ transport_endpoints: (params.transportEndpoints ||
719
+ params.transport_endpoints ||
720
+ []),
468
721
  ...(params.witness_data ? { witness_data: params.witness_data } : {}),
469
- }],
722
+ },
723
+ ],
470
724
  },
471
- // skip_sync is a valid runtime param; cast bridges kaleido-sdk type drift.
472
725
  skip_sync: false,
473
- });
726
+ };
727
+ return await client.rln.sendRgb(sendReq);
474
728
  }
475
729
  catch (error) {
476
- throw this.handleSdkError(error, 'Failed to send RGB asset');
730
+ throw this.handleSdkError(error, "Failed to send RGB asset");
477
731
  }
478
732
  }
479
733
  async sendBtcOnchain(params) {
480
734
  if (!kaleidoClientManager.hasNode()) {
481
- throw new ProtocolError('Node not configured', 'RGB_LN', 'NODE_NOT_CONFIGURED');
735
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
482
736
  }
483
737
  try {
484
- return await kaleidoClientManager.getClient().rln.sendBtc({
738
+ const client = kaleidoClientManager.getClient();
739
+ return await client.rln.sendBtc({
485
740
  address: params.address,
486
741
  amount: params.amount,
487
- fee_rate: params.feeRate || 5,
742
+ fee_rate: await this.resolveFeeRate(params.feeRate, "normal"),
488
743
  skip_sync: false,
489
744
  });
490
745
  }
491
746
  catch (error) {
492
- throw this.handleSdkError(error, 'Failed to send BTC on-chain');
747
+ throw this.handleSdkError(error, "Failed to send BTC on-chain");
493
748
  }
494
749
  }
495
750
  // ========================================================================
496
- // Swap Operations (via KaleidoSwap Maker API)
751
+ // Swap Operations
497
752
  // ========================================================================
498
753
  supportsSwaps() {
499
- return true;
754
+ // Swaps via Kaleidoswap require a configured maker URL — without one
755
+ // every quote request errors. The UI must reflect that.
756
+ return !!this.config?.makerUrl;
500
757
  }
501
758
  async getSwapQuote(request) {
502
759
  if (!this.isConnected()) {
503
- throw new ProtocolError('Not connected', 'RGB_LN', 'NOT_CONNECTED');
760
+ throw new ProtocolError("Not connected", "RGB_LN", "NOT_CONNECTED");
504
761
  }
762
+ // Check if maker is configured
505
763
  if (!this.config?.makerUrl) {
506
- throw new ProtocolError('Maker API not configured. Swaps not available in node-only mode.', 'RGB_LN', 'MAKER_NOT_CONFIGURED');
764
+ throw new ProtocolError("Maker API not configured. Swaps are not available in node-only mode.", "RGB_LN", "MAKER_NOT_CONFIGURED");
507
765
  }
508
766
  try {
509
767
  const client = kaleidoClientManager.getClient();
510
- const quoteResponse = await client.maker.getQuote({
768
+ const quoteResponse = (await client.maker.getQuote({
511
769
  from_asset: {
512
770
  asset_id: request.fromAsset,
513
771
  layer: SdkLayer.RGB_LN,
@@ -518,7 +776,7 @@ export class RgbAdapter {
518
776
  layer: SdkLayer.RGB_LN,
519
777
  amount: request.toAmount,
520
778
  },
521
- });
779
+ }));
522
780
  return {
523
781
  id: quoteResponse.rfq_id,
524
782
  fromAsset: quoteResponse.from_asset.asset_id,
@@ -536,285 +794,119 @@ export class RgbAdapter {
536
794
  },
537
795
  },
538
796
  expiresAt: quoteResponse.expires_at,
539
- provider: 'Kaleidoswap',
797
+ provider: "Kaleidoswap",
540
798
  };
541
799
  }
542
800
  catch (error) {
543
- throw this.handleSdkError(error, 'Maker API connection failed');
801
+ throw this.handleSdkError(error, "Maker API connection failed");
544
802
  }
545
803
  }
546
804
  async executeSwap(quote) {
547
805
  if (!this.isConnected()) {
548
- throw new ProtocolError('Not connected', 'RGB_LN', 'NOT_CONNECTED');
806
+ throw new ProtocolError("Not connected", "RGB_LN", "NOT_CONNECTED");
549
807
  }
808
+ // Check if maker is configured
550
809
  if (!this.config?.makerUrl) {
551
- throw new ProtocolError('Maker API not configured.', 'RGB_LN', 'MAKER_NOT_CONFIGURED');
810
+ throw new ProtocolError("Maker API not configured. Swaps are not available in node-only mode.", "RGB_LN", "MAKER_NOT_CONFIGURED");
552
811
  }
553
812
  try {
554
813
  const client = kaleidoClientManager.getClient();
555
814
  const quoteAny = quote;
556
- const orderRequest = {
557
- rfq_id: quoteAny.rfqId || quote.id || '',
558
- from_asset: { asset_id: quote.fromAsset, amount: quote.fromAmount, layer: quoteAny.fromLayer || 'RGB_LN' },
559
- to_asset: { asset_id: quote.toAsset, amount: quote.toAmount, layer: quoteAny.toLayer || 'RGB_LN' },
560
- receiver_address: { address: quoteAny.receiverAddress || '', format: 'BTC_ADDRESS' },
815
+ const quote_request = {
816
+ rfq_id: quoteAny.rfqId || quote.id || "",
817
+ from_asset: {
818
+ asset_id: quote.fromAsset,
819
+ amount: quote.fromAmount,
820
+ layer: quoteAny.fromLayer || "RGB_LN",
821
+ },
822
+ to_asset: {
823
+ asset_id: quote.toAsset,
824
+ amount: quote.toAmount,
825
+ layer: quoteAny.toLayer || "RGB_LN",
826
+ },
827
+ receiver_address: {
828
+ address: quoteAny.receiverAddress || "",
829
+ format: "BTC_ADDRESS",
830
+ },
561
831
  min_onchain_conf: 1,
562
- refund_address: '',
563
- email: '',
832
+ refund_address: "",
833
+ email: "",
564
834
  };
565
- const result = await client.maker.createSwapOrder(orderRequest);
835
+ const result = await client.maker.createSwapOrder(quote_request);
566
836
  const swapResult = result;
567
- const swapId = (swapResult.order_id ?? swapResult.id ?? '');
837
+ const swapId = (swapResult.order_id ?? swapResult.id ?? "");
568
838
  if (swapId && swapResult.access_token) {
569
839
  this.swapAccessTokens.set(swapId, swapResult.access_token);
570
840
  }
571
841
  return {
572
842
  swapId,
573
- paymentHash: (swapResult.payment_hash ?? ''),
574
- status: this.mapSwapStatus(swapResult.status),
843
+ paymentHash: (swapResult.payment_hash ?? ""),
844
+ status: mapSwapStatus(swapResult.status),
575
845
  quote,
576
846
  timestamp: Date.now(),
577
847
  };
578
848
  }
579
849
  catch (error) {
580
- throw this.handleSdkError(error, 'Failed to execute swap');
850
+ throw this.handleSdkError(error, "Failed to execute swap");
581
851
  }
582
852
  }
583
853
  async getSwapStatus(swapId) {
584
854
  if (!this.isConnected()) {
585
- throw new ProtocolError('Not connected', 'RGB_LN', 'NOT_CONNECTED');
855
+ throw new ProtocolError("Not connected", "RGB_LN", "NOT_CONNECTED");
586
856
  }
587
857
  try {
588
858
  const client = kaleidoClientManager.getClient();
589
859
  const accessToken = this.swapAccessTokens.get(swapId);
590
860
  if (!accessToken) {
591
- throw new ProtocolError('Missing swap access token for status lookup', 'RGB_LN', 'SWAP_ACCESS_TOKEN_MISSING');
861
+ throw new ProtocolError("Missing swap access token for status lookup", "RGB_LN", "SWAP_ACCESS_TOKEN_MISSING");
592
862
  }
593
- const status = await client.maker.getSwapOrderStatus({
863
+ const status = (await client.maker.getSwapOrderStatus({
594
864
  order_id: swapId,
595
865
  access_token: accessToken,
596
- });
866
+ }));
597
867
  const order = (status.order ?? status);
598
868
  return {
599
869
  swapId,
600
- status: this.mapSwapStatus(order.status),
601
- quote: {},
870
+ status: mapSwapStatus(order.status),
871
+ quote: {}, // Would need to store original quote
602
872
  timestamp: order.created_at || Date.now(),
603
873
  };
604
874
  }
605
875
  catch (error) {
606
- throw this.handleSdkError(error, 'Failed to get swap status');
607
- }
608
- }
609
- // ========================================================================
610
- // Protocol-Specific Operations (escape hatch)
611
- // ========================================================================
612
- async executeProtocolOperation(operation, params) {
613
- if (!kaleidoClientManager.hasNode()) {
614
- throw new ProtocolError('Node not configured', 'RGB_LN', 'NODE_NOT_CONFIGURED');
615
- }
616
- const client = kaleidoClientManager.getClient();
617
- switch (operation) {
618
- case 'initNode':
619
- return client.rln.initWallet(params);
620
- case 'unlockNode':
621
- return client.rln.unlockWallet(params);
622
- case 'lockNode':
623
- return client.rln.lockWallet();
624
- case 'createUtxos':
625
- return client.rln.createUtxos(params);
626
- case 'issueAssetNIA':
627
- return client.rln.issueAssetNIA(params);
628
- case 'issueAssetCFA':
629
- return client.rln.issueAssetCFA(params);
630
- case 'estimateFee':
631
- return client.rln.estimateFee(params);
632
- case 'failTransfers':
633
- return client.rln.failTransfers(params);
634
- case 'refreshTransfers':
635
- return client.rln.refreshTransfers(params);
636
- case 'sync':
637
- return client.rln.syncRgbWallet();
638
- case 'connectPeer':
639
- return client.rln.connectPeer(params);
640
- case 'disconnectPeer':
641
- return client.rln.disconnectPeer(params);
642
- case 'listPeers':
643
- return client.rln.listPeers();
644
- case 'openChannel':
645
- return client.rln.openChannel(params);
646
- case 'closeChannel':
647
- return client.rln.closeChannel(params);
648
- case 'getAssetMetadata':
649
- return client.rln.getAssetMetadata(params);
650
- case 'getTakerPubkey':
651
- return client.rln.getTakerPubkey();
652
- case 'whitelistSwap':
653
- return client.rln.whitelistSwap(params);
654
- case 'initSwap':
655
- return client.maker.initSwap(params);
656
- case 'confirmSwap':
657
- return client.maker.executeSwap(params);
658
- case 'listSwaps':
659
- return client.rln.listSwaps();
660
- case 'getSwap':
661
- return client.rln.getSwap(params);
662
- case 'getLspInfo':
663
- return client.maker.getLspInfo();
664
- case 'createLspOrder':
665
- return client.maker.createLspOrder(params);
666
- case 'getLspOrder':
667
- return client.maker.getLspOrder(params);
668
- case 'estimateLspFees':
669
- return client.maker.estimateLspFees(params);
670
- default:
671
- throw new ProtocolError(`Unknown operation: ${operation}`, 'RGB_LN', 'UNKNOWN_OPERATION');
876
+ throw this.handleSdkError(error, "Failed to get swap status");
672
877
  }
673
878
  }
674
879
  // ========================================================================
675
- // Private Helpers
880
+ // SDK ↔ unified-shape converters moved to ./converters.ts (this-free;
881
+ // covered by tests/unit/rgb-converters.test.ts).
676
882
  // ========================================================================
677
- createBtcAsset(btcBalance) {
678
- const balance = this.convertBtcBalance(btcBalance);
679
- return {
680
- id: 'BTC',
681
- name: 'Bitcoin (RGB Node)',
682
- ticker: 'BTC',
683
- precision: 8,
684
- protocol: 'RGB_LN',
685
- layer: 'BTC_L1',
686
- balance,
687
- capabilities: {
688
- canSend: true, canReceive: true, canSwap: true,
689
- supportsLightning: true, supportsOnchain: true,
690
- },
691
- };
692
- }
693
- convertNodeAssetToUnified(asset) {
694
- return {
695
- id: asset.asset_id,
696
- name: asset.name,
697
- ticker: asset.ticker,
698
- precision: asset.precision || 8,
699
- protocol: 'RGB_LN',
700
- layer: 'RGB_LN',
701
- balance: this.convertNodeBalance(asset.balance),
702
- capabilities: {
703
- canSend: true, canReceive: true, canSwap: false,
704
- supportsLightning: true, supportsOnchain: true,
705
- },
706
- };
707
- }
708
- convertBtcBalance(btcBalance) {
709
- const vanilla = btcBalance.vanilla ?? { settled: 0, future: 0, spendable: 0 };
710
- return {
711
- total: vanilla.settled || 0,
712
- available: vanilla.spendable || 0,
713
- pending: vanilla.future || 0,
714
- totalDisplay: this.formatAmount(vanilla.settled || 0, 8),
715
- availableDisplay: this.formatAmount(vanilla.spendable || 0, 8),
716
- };
717
- }
718
- convertSdkBalance(balance) {
719
- return {
720
- total: balance.settled || 0,
721
- available: balance.spendable || 0,
722
- pending: balance.future || 0,
723
- locked: balance.offchain_outbound || 0,
724
- totalDisplay: this.formatAmount(balance.settled || 0, 8),
725
- availableDisplay: this.formatAmount(balance.spendable || 0, 8),
726
- };
727
- }
728
- convertNodeBalance(balance) {
729
- const total = balance?.settled || 0;
730
- const available = balance?.spendable || 0;
731
- const pending = balance?.future || 0;
732
- return {
733
- total, available, pending,
734
- locked: balance?.offchain_outbound || 0,
735
- totalDisplay: this.formatAmount(total, 8),
736
- availableDisplay: this.formatAmount(available, 8),
737
- };
738
- }
739
- convertTransferToTransaction(transfer) {
740
- return {
741
- id: transfer.txid || `tx_${Date.now()}`,
742
- type: this.mapTransferType(transfer.kind),
743
- status: this.mapTransferStatus(transfer.status),
744
- timestamp: transfer.created_at || Date.now(),
745
- amount: transfer.amount || 0,
746
- amountDisplay: this.formatAmount(transfer.amount || 0, 8),
747
- fee: transfer.fee,
748
- feeDisplay: this.formatAmount(transfer.fee || 0, 8),
749
- asset: {},
750
- from: transfer.sender,
751
- to: transfer.recipient,
752
- protocolData: transfer,
753
- };
754
- }
755
- mapTransferType(kind) {
756
- if (!kind)
757
- return 'send';
758
- if (kind.includes('receive') || kind.includes('ReceiveAsset'))
759
- return 'receive';
760
- if (kind.includes('send') || kind.includes('SendAsset'))
761
- return 'send';
762
- return 'send';
763
- }
764
- mapTransferStatus(status) {
765
- if (!status)
766
- return 'pending';
767
- if (status === 'Settled' || status === 'settled')
768
- return 'confirmed';
769
- if (status === 'Failed' || status === 'failed')
770
- return 'failed';
771
- return 'pending';
772
- }
773
- mapPaymentStatus(status) {
774
- if (!status)
775
- return 'pending';
776
- if (status === 'succeeded' || status === 'success' || status === 'Succeeded')
777
- return 'confirmed';
778
- if (status === 'failed' || status === 'Failed')
779
- return 'failed';
780
- return 'pending';
781
- }
782
- mapSwapStatus(status) {
783
- if (!status)
784
- return 'pending';
785
- if (status === 'completed' || status === 'success' || status === 'Completed')
786
- return 'confirmed';
787
- if (status === 'failed' || status === 'error' || status === 'Failed')
788
- return 'failed';
789
- return 'pending';
790
- }
791
- formatAmount(amount, precision) {
792
- return (amount / Math.pow(10, precision)).toFixed(precision);
793
- }
883
+ // Pure mappers + formatAmount moved to ./helpers.ts (this-free; covered
884
+ // by tests/unit/rgb-helpers.test.ts).
794
885
  // ========================================================================
795
886
  // Error Handling
796
887
  // ========================================================================
797
888
  handleSdkError(error, context) {
798
889
  if (error instanceof NodeNotConfiguredError) {
799
- throw new ProtocolError('Node not configured', 'RGB_LN', 'NODE_NOT_CONFIGURED');
890
+ throw new ProtocolError("Node not configured", "RGB_LN", "NODE_NOT_CONFIGURED");
800
891
  }
801
892
  else if (error instanceof QuoteExpiredError) {
802
- throw new ProtocolError('Quote expired', 'RGB_LN', 'QUOTE_EXPIRED');
893
+ throw new ProtocolError("Quote expired", "RGB_LN", "QUOTE_EXPIRED");
803
894
  }
804
895
  else if (error instanceof SdkInsufficientBalanceError) {
805
- throw new InsufficientBalanceError('Insufficient balance', 'RGB_LN', 0, 0);
896
+ throw new InsufficientBalanceError("Insufficient balance", "RGB_LN", 0, 0);
806
897
  }
807
898
  else if (error instanceof APIError) {
808
- throw new ProtocolError(`${context}: ${error.message}`, 'RGB_LN', 'API_ERROR', error);
899
+ throw new ProtocolError(`${context}: ${error.message}`, "RGB_LN", "API_ERROR", error);
809
900
  }
810
901
  else if (error instanceof NetworkError) {
811
- throw new ConnectionError(`${context}: Network error - ${error.message}`, 'RGB_LN', error);
902
+ throw new ConnectionError(`${context}: Network error - ${error.message}`, "RGB_LN", error);
812
903
  }
813
904
  else if (error instanceof KaleidoError) {
814
- throw new ProtocolError(`${context}: ${error.message}`, 'RGB_LN', 'SDK_ERROR', error);
905
+ throw new ProtocolError(`${context}: ${error.message}`, "RGB_LN", "SDK_ERROR", error);
815
906
  }
816
- const msg = error instanceof Error ? error.message : 'Unknown error';
817
- throw new ProtocolError(`${context}: ${msg}`, 'RGB_LN', 'UNKNOWN_ERROR', error instanceof Error ? error : undefined);
907
+ // Default error handling
908
+ const msg = error instanceof Error ? error.message : "Unknown error";
909
+ throw new ProtocolError(`${context}: ${msg}`, "RGB_LN", "UNKNOWN_ERROR", error instanceof Error ? error : undefined);
818
910
  }
819
911
  }
820
912
  //# sourceMappingURL=RgbAdapter.js.map