dhali-js 3.0.1 → 3.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -43,12 +43,13 @@ async function main() {
43
43
  const manager = DhaliChannelManager.xrpl(wallet, client, currency)
44
44
 
45
45
  // Generate Claim
46
+ const amount = Math.floor(1.0 * Math.pow(10, currency.scale)); // 1 XRP
46
47
  let token;
47
48
  try {
48
49
  token = await manager.getAuthToken();
49
50
  } catch (error) {
50
51
  if (error.name === "ChannelNotFound") {
51
- await manager.deposit(1000000); // Deposit 1 XRP
52
+ await manager.deposit(amount);
52
53
  token = await manager.getAuthToken();
53
54
  } else {
54
55
  throw error;
@@ -95,13 +96,14 @@ async function main() {
95
96
  )
96
97
 
97
98
  // 4. Generate Claim
99
+ const amount = Math.floor(0.1 * Math.pow(10, sepoliaUsdc.scale)); // 0.10 USDC
98
100
  let token;
99
101
  try {
100
- token = await manager.getAuthToken(1000000); // 1.00 USDC
102
+ token = await manager.getAuthToken(amount);
101
103
  } catch (error) {
102
104
  if (error.name === "ChannelNotFound") {
103
- await manager.deposit(1000000); // Deposit 1.00 USDC
104
- token = await manager.getAuthToken(1000000);
105
+ await manager.deposit(amount);
106
+ token = await manager.getAuthToken(amount);
105
107
  } else {
106
108
  throw error;
107
109
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dhali-js",
3
- "version": "3.0.1",
3
+ "version": "3.0.4",
4
4
  "description": "A JavaScript library for managing XRPL payment channels and generating auth tokens for Dhali APIs",
5
5
  "main": "src/index.js",
6
6
  "type": "commonjs",
@@ -8,6 +8,7 @@ const {
8
8
  const crypto = require("crypto");
9
9
 
10
10
  const { fetchPublicConfig, notifyAdminGateway, retrieveChannelIdFromFirestoreRest } = require("./configUtils");
11
+ const { ChannelNotFound } = require("./utils");
11
12
 
12
13
  class DhaliEthChannelManager {
13
14
  /**
@@ -29,11 +30,11 @@ class DhaliEthChannelManager {
29
30
  }
30
31
 
31
32
  _getChainIdFromProtocol(protocol) {
32
- switch (protocol) {
33
+ switch (protocol.toUpperCase()) {
33
34
  case "ETHEREUM": return 1;
34
35
  case "SEPOLIA": return 11155111;
35
36
  case "HOLESKY": return 17000;
36
- case "LOCALHOST": return 31337;
37
+ case "HARDHAT": return 31337;
37
38
  default: throw new Error(`Unsupported protocol: ${protocol}`);
38
39
  }
39
40
  }
@@ -282,7 +283,7 @@ class DhaliEthChannelManager {
282
283
  // Poll Firestore if not found (setupBalanceListener simulation)
283
284
  const channelIdRaw = await this._retrieveChannelIdFromFirestoreWithPolling(10);
284
285
  if (!channelIdRaw) {
285
- throw new Error("No open payment channel found in Firestore. Please deposit first.");
286
+ throw new ChannelNotFound("No open payment channel found in Firestore. Please deposit first.");
286
287
  }
287
288
 
288
289
  let channelId = channelIdRaw;
@@ -324,7 +325,7 @@ class DhaliEthChannelManager {
324
325
 
325
326
  const claim = {
326
327
  version: "2",
327
- account: accountString,
328
+ account: accountString.toLowerCase(),
328
329
  protocol: this.currency.network,
329
330
  currency: {
330
331
  code: this.currency.code,
@@ -4,7 +4,7 @@ const { sign: signClaim } = require("ripple-keypairs");
4
4
 
5
5
  const { fetchPublicConfig, notifyAdminGateway, retrieveChannelIdFromFirestoreRest } = require("./configUtils");
6
6
 
7
- class ChannelNotFound extends Error { }
7
+ const { ChannelNotFound } = require("./utils");
8
8
 
9
9
  /**
10
10
  * A management tool for generating payment claims for use with Dhali APIs (XRPL).
@@ -39,6 +39,14 @@ function wrapAsX402PaymentPayload(claimBase64, paymentRequirementBase64) {
39
39
  return Buffer.from(JSON.stringify(x402Payload)).toString("base64");
40
40
  }
41
41
 
42
+ class ChannelNotFound extends Error {
43
+ constructor(message = "No open payment channel found.") {
44
+ super(message);
45
+ this.name = "ChannelNotFound";
46
+ }
47
+ }
48
+
42
49
  module.exports = {
43
50
  wrapAsX402PaymentPayload,
51
+ ChannelNotFound
44
52
  };
@@ -147,4 +147,26 @@ describe("DhaliChannelManager", () => {
147
147
  );
148
148
  });
149
149
  });
150
+
151
+ describe("Casing", () => {
152
+ test("queries Firestore with original XRPL address casing", async () => {
153
+ const mockHttp = jest.fn();
154
+ wallet.address = "rMixedCaseAddress";
155
+ manager = new DhaliXrplChannelManager(
156
+ wallet,
157
+ mockClient,
158
+ currency,
159
+ mockHttp,
160
+ );
161
+
162
+ await manager._retrieveChannelIdFromFirestore();
163
+
164
+ expect(configUtils.retrieveChannelIdFromFirestoreRest).toHaveBeenCalledWith(
165
+ "XRPL.MAINNET",
166
+ currency,
167
+ "rMixedCaseAddress", // Should NOT be lower-cased
168
+ mockHttp,
169
+ );
170
+ });
171
+ });
150
172
  });
@@ -2,6 +2,7 @@ const { DhaliEthChannelManager } = require("../src/dhali/DhaliEthChannelManager"
2
2
  const Currency = require("../src/dhali/Currency");
3
3
  const { keccak256, encodeAbiParameters, parseAbiParameters } = require("viem");
4
4
  const configUtils = require("../src/dhali/configUtils");
5
+ const { ChannelNotFound } = require("../src/dhali/utils");
5
6
 
6
7
  jest.mock("../src/dhali/configUtils");
7
8
 
@@ -138,7 +139,9 @@ describe("DhaliEthChannelManager", () => {
138
139
  const originalTimeout = global.setTimeout;
139
140
  global.setTimeout = (cb) => cb();
140
141
 
141
- await expect(manager.getAuthToken(100)).rejects.toThrow(/No open payment channel found in Firestore/);
142
+ const promise = manager.getAuthToken(100);
143
+ await expect(promise).rejects.toThrow(ChannelNotFound);
144
+ await expect(promise).rejects.toThrow(/No open payment channel found in Firestore/);
142
145
 
143
146
  global.setTimeout = originalTimeout;
144
147
  });
@@ -255,4 +258,68 @@ describe("DhaliEthChannelManager", () => {
255
258
  fetch
256
259
  );
257
260
  });
261
+
262
+ test("performs lower-casing on EVM addresses consistently", async () => {
263
+ const mixedAddr = "0x71C7656EC7ab88b098defB751B7401B5f6d8976F";
264
+ const lowerAddr = mixedAddr.toLowerCase();
265
+
266
+ const mockHttp = jest.fn();
267
+ const manager = new DhaliEthChannelManager(mockWalletClient, mockPublicClient, currency, mockHttp, publicConfig);
268
+ mockWalletClient.getAddresses.mockResolvedValue([mixedAddr]);
269
+
270
+ // 1. Check firestore retrieval
271
+ configUtils.retrieveChannelIdFromFirestoreRest.mockResolvedValue("0xid");
272
+ await manager._retrieveChannelIdFromFirestore();
273
+ expect(configUtils.retrieveChannelIdFromFirestoreRest).toHaveBeenCalledWith(
274
+ expect.anything(),
275
+ expect.anything(),
276
+ lowerAddr,
277
+ mockHttp
278
+ );
279
+
280
+ // 2. Check gateway notification during deposit (Open Channel path)
281
+ configUtils.retrieveChannelIdFromFirestoreRest.mockResolvedValue(null);
282
+ manager._generateNonce = jest.fn().mockReturnValue(1n);
283
+ manager._calculateChannelId = jest.fn().mockReturnValue("0xid");
284
+ mockWalletClient.sendTransaction.mockResolvedValue("0xhash");
285
+ mockPublicClient.waitForTransactionReceipt.mockResolvedValue({ status: "success" });
286
+ manager._retrieveChannelIdFromFirestoreWithPolling = jest.fn().mockResolvedValue("0xid");
287
+
288
+ await manager.deposit(100);
289
+ expect(configUtils.notifyAdminGateway).toHaveBeenCalledWith(
290
+ expect.anything(),
291
+ expect.anything(),
292
+ lowerAddr,
293
+ expect.anything(),
294
+ mockHttp
295
+ );
296
+ });
297
+
298
+ test("getAuthToken preserves destination_account casing", async () => {
299
+ const mixedCaseDestination = "0x71C7656EC7ab88b098defB751B7401B5f6d8976F";
300
+ const mixedCaseConfig = {
301
+ DHALI_PUBLIC_ADDRESSES: {
302
+ ETHEREUM: {
303
+ ETH: { wallet_id: mixedCaseDestination }
304
+ }
305
+ },
306
+ CONTRACTS: {
307
+ ETHEREUM: { contract_address: "0x0000000000000000000000000000000000000003" }
308
+ }
309
+ };
310
+
311
+ const localManager = new DhaliEthChannelManager(mockWalletClient, mockPublicClient, currency, null, mixedCaseConfig);
312
+
313
+ // Mock required dependencies for getAuthToken
314
+ configUtils.retrieveChannelIdFromFirestoreRest.mockResolvedValue("0xChannelId");
315
+ const amountHex = BigInt(1000).toString(16).padStart(64, '0');
316
+ mockPublicClient.call = jest.fn().mockResolvedValue({ data: "0x" + "0".repeat(64 * 4) + amountHex });
317
+ mockWalletClient.signTypedData.mockResolvedValue("0xSignature");
318
+
319
+ const token = await localManager.getAuthToken(100);
320
+ const decoded = JSON.parse(Buffer.from(token, "base64").toString("utf8"));
321
+
322
+ expect(decoded.destination_account).toBe(mixedCaseDestination);
323
+ expect(decoded.destination_account).not.toBe(mixedCaseDestination.toLowerCase());
324
+ });
258
325
  });