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(
|
|
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(
|
|
102
|
+
token = await manager.getAuthToken(amount);
|
|
101
103
|
} catch (error) {
|
|
102
104
|
if (error.name === "ChannelNotFound") {
|
|
103
|
-
await manager.deposit(
|
|
104
|
-
token = await manager.getAuthToken(
|
|
105
|
+
await manager.deposit(amount);
|
|
106
|
+
token = await manager.getAuthToken(amount);
|
|
105
107
|
} else {
|
|
106
108
|
throw error;
|
|
107
109
|
}
|
package/package.json
CHANGED
|
@@ -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 "
|
|
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
|
|
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
|
-
|
|
7
|
+
const { ChannelNotFound } = require("./utils");
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* A management tool for generating payment claims for use with Dhali APIs (XRPL).
|
package/src/dhali/utils.js
CHANGED
|
@@ -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
|
-
|
|
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
|
});
|