dhali-js 1.0.4 → 2.1.0

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.
@@ -0,0 +1,203 @@
1
+ const Currency = require("./Currency");
2
+
3
+ /**
4
+ * @typedef {Object} NetworkCurrencyConfig
5
+ * @property {Currency} currency
6
+ * @property {string} destinationAddress
7
+ */
8
+
9
+ /**
10
+ * Fetches and parses available Dhali currencies and configurations.
11
+ * @returns {Promise<Object.<string, Object.<string, NetworkCurrencyConfig>>>}
12
+ */
13
+ /**
14
+ * @param {typeof fetch} [httpClient]
15
+ * @returns {Promise<Object>}
16
+ */
17
+ async function getAvailableDhaliCurrencies(httpClient = fetch) {
18
+ const url = "https://raw.githubusercontent.com/Dhali-org/Dhali-config/master/public.prod.json";
19
+ let data;
20
+ try {
21
+ const response = await httpClient(url);
22
+ if (!response.ok) {
23
+ throw new Error(`HTTP error! status: ${response.status}`);
24
+ }
25
+ data = await response.json();
26
+ } catch (e) {
27
+ throw new Error(`Failed to fetch Dhali configuration: ${e.message}`);
28
+ }
29
+
30
+ const publicAddresses = data.DHALI_PUBLIC_ADDRESSES || {};
31
+ /** @type {Object.<string, Object.<string, NetworkCurrencyConfig>>} */
32
+ const result = {};
33
+
34
+ for (const [network, currencies] of Object.entries(publicAddresses)) {
35
+ result[network] = {};
36
+ for (const [code, details] of Object.entries(currencies)) {
37
+ const tokenAddress = details.issuer || null;
38
+ const scale = details.scale || 6;
39
+ const destination = details.wallet_id;
40
+
41
+ if (!destination) continue;
42
+
43
+ const curr = new Currency(code, scale, tokenAddress);
44
+
45
+ result[network][code] = {
46
+ currency: curr,
47
+ destinationAddress: destination
48
+ };
49
+ }
50
+ }
51
+ return result;
52
+ }
53
+
54
+ /**
55
+ * Fetches the raw Dhali public configuration JSON.
56
+ * @returns {Promise<Object>}
57
+ */
58
+ async function fetchPublicConfig(httpClient = fetch) {
59
+ const url = "https://raw.githubusercontent.com/Dhali-org/Dhali-config/master/public.prod.json";
60
+ try {
61
+ const response = await httpClient(url);
62
+ if (!response.ok) {
63
+ throw new Error(`HTTP error! status: ${response.status}`);
64
+ }
65
+ return await response.json();
66
+ } catch (e) {
67
+ throw new Error(`Failed to fetch Dhali configuration: ${e.message}`);
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Proactively notifies the Dhali Admin Gateway about a new payment channel.
73
+ * @param {string} protocol
74
+ * @param {string} currencyIdentifier
75
+ * @param {string} accountAddress
76
+ * @param {string} channelId
77
+ * @param {typeof fetch} [httpClient]
78
+ */
79
+ async function notifyAdminGateway(protocol, currencyIdentifier, accountAddress, channelId, httpClient = fetch) {
80
+ const config = await fetchPublicConfig(httpClient);
81
+ const rootUrl = config.ROOT_API_ADMIN_URL;
82
+ if (!rootUrl) return;
83
+
84
+ const httpRootUrl = rootUrl.replace("wss://", "https://").replace("ws://", "http://");
85
+ const url = `${httpRootUrl}/public_claim_info/${protocol}/${currencyIdentifier}`;
86
+
87
+ if (!channelId.startsWith("0x")) {
88
+ channelId = "0x" + channelId;
89
+ }
90
+
91
+ const payload = {
92
+ account: accountAddress,
93
+ channel_id: channelId
94
+ };
95
+
96
+ try {
97
+ await httpClient(url, {
98
+ method: "PUT",
99
+ headers: {
100
+ "Content-Type": "application/json"
101
+ },
102
+ body: JSON.stringify(payload)
103
+ });
104
+ } catch (e) {
105
+ // Best effort notification
106
+ }
107
+ }
108
+
109
+
110
+
111
+ const FIREBASE_CONFIG = {
112
+ apiKey: "AIzaSyBro8QN3zyJwyo92lYUMPwsyRVPLLGOTcs",
113
+ authDomain: "dhali-prod.firebaseapp.com",
114
+ projectId: "dhali-prod",
115
+ storageBucket: "dhali-prod.firebasestorage.app",
116
+ messagingSenderId: "1042340549063",
117
+ appId: "1:1042340549063:web:3dc69cffe6d3c0746189e2",
118
+ measurementId: "G-6TPZFK7NQ6",
119
+ };
120
+
121
+ /**
122
+ * Queries Firestore via REST API using the public API key.
123
+ * @param {string} protocol
124
+ * @param {import("./Currency")} currency
125
+ * @param {string} accountAddress
126
+ * @param {typeof fetch} [httpClient]
127
+ * @returns {Promise<string|null>}
128
+ */
129
+ async function retrieveChannelIdFromFirestoreRest(protocol, currency, accountAddress, httpClient = fetch) {
130
+ let currencyIdentifier = currency.code;
131
+ if (currency.tokenAddress) {
132
+ currencyIdentifier = `${currency.code}.${currency.tokenAddress}`;
133
+ }
134
+
135
+ const projectId = FIREBASE_CONFIG.projectId;
136
+ const apiKey = FIREBASE_CONFIG.apiKey;
137
+ const url = `https://firestore.googleapis.com/v1/projects/${projectId}/databases/(default)/documents/public_claim_info/${protocol}:runQuery?key=${apiKey}`;
138
+
139
+ const query = {
140
+ structuredQuery: {
141
+ from: [{ collectionId: currencyIdentifier }],
142
+ where: {
143
+ compositeFilter: {
144
+ op: "AND",
145
+ filters: [
146
+ {
147
+ fieldFilter: {
148
+ field: { fieldPath: "account" },
149
+ op: "EQUAL",
150
+ value: { stringValue: accountAddress },
151
+ }
152
+ },
153
+ {
154
+ fieldFilter: {
155
+ field: { fieldPath: "closed" },
156
+ op: "NOT_EQUAL",
157
+ value: { booleanValue: true },
158
+ }
159
+ },
160
+ ],
161
+ }
162
+ },
163
+ }
164
+ };
165
+
166
+ try {
167
+ const response = await httpClient(url, {
168
+ method: "POST",
169
+ headers: {
170
+ "Content-Type": "application/json"
171
+ },
172
+ body: JSON.stringify(query)
173
+ });
174
+
175
+ if (!response.ok) return null;
176
+
177
+ const results = await response.json();
178
+
179
+ for (const result of results) {
180
+ const doc = result.document;
181
+ if (!doc) continue;
182
+
183
+ const fields = doc.fields || {};
184
+ const closing = fields.closing ? fields.closing.booleanValue : false;
185
+ const closed = fields.closed ? fields.closed.booleanValue : false;
186
+
187
+ if (closing || closed) continue;
188
+
189
+ const channelId = fields.channel_id ? fields.channel_id.stringValue : null;
190
+ if (channelId) return channelId;
191
+ }
192
+ } catch (e) {
193
+ return null;
194
+ }
195
+ return null;
196
+ }
197
+
198
+ module.exports = {
199
+ getAvailableDhaliCurrencies,
200
+ fetchPublicConfig,
201
+ notifyAdminGateway,
202
+ retrieveChannelIdFromFirestoreRest
203
+ };
@@ -59,8 +59,42 @@ function buildPaychanAuthHexStringToBeSigned(channelIdHex, amountStr) {
59
59
  return msg.toString("hex").toUpperCase();
60
60
  }
61
61
 
62
+ /**
63
+ * Get Typed Data structure for EIP-712 signing
64
+ */
65
+ function getEthereumClaimTypedData(channelId, tokenAddress, maxAmount, chainId, contractAddress) {
66
+ const domain = {
67
+ name: "DhaliPaymentChannel",
68
+ version: "2.1.0",
69
+ chainId: chainId,
70
+ verifyingContract: contractAddress,
71
+ };
72
+
73
+ const types = {
74
+ DhaliClaim: [
75
+ { name: "channelId", type: "bytes32" },
76
+ { name: "token", type: "address" },
77
+ { name: "maxAmount", type: "uint256" },
78
+ ],
79
+ };
80
+
81
+ if (typeof channelId === "string" && !channelId.startsWith("0x")) {
82
+ channelId = "0x" + channelId;
83
+ }
84
+
85
+ const value = {
86
+ channelId: channelId,
87
+ token: tokenAddress,
88
+ maxAmount: maxAmount
89
+ };
90
+
91
+ return { domain, types, value };
92
+ }
93
+
94
+
62
95
  module.exports = {
63
96
  buildPaychanAuthHexStringToBeSigned,
64
97
  // exposed for testing
65
98
  _serializePaychanAuthorization,
99
+ getEthereumClaimTypedData
66
100
  };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Wraps a base64 encoded claim and a base64 encoded requirement into an x402 compliant payload.
3
+ *
4
+ * @param {string} claimBase64 - The base64 encoded claim object.
5
+ * @param {string} paymentRequirementBase64 - The base64 encoded payment requirement object.
6
+ * @returns {string} The base64 encoded x402 payment payload.
7
+ */
8
+ function wrapAsX402PaymentPayload(claimBase64, paymentRequirementBase64) {
9
+ const decodedClaim = JSON.parse(
10
+ Buffer.from(claimBase64, "base64").toString("utf-8")
11
+ );
12
+ let req = JSON.parse(
13
+ Buffer.from(paymentRequirementBase64, "base64").toString("utf-8")
14
+ );
15
+
16
+ if (req.accepts) {
17
+ req = Array.isArray(req.accepts) ? req.accepts[0] : req.accepts;
18
+ }
19
+
20
+ // Normalize fields to match Dhali-wallet's PaymentRequirements defaults (camelCase)
21
+ const normalizedReq = {
22
+ scheme: req.scheme || "",
23
+ network: req.network || "",
24
+ asset: req.asset || (req.price ? req.price.asset : "") || "",
25
+ amount: String(req.amount || (req.price ? req.price.amount : "0")),
26
+ payTo: req.payTo || req.pay_to || "",
27
+ maxTimeoutSeconds: parseInt(
28
+ req.maxTimeoutSeconds || req.max_timeout_seconds || 1209600
29
+ ),
30
+ extra: req.extra || {},
31
+ };
32
+
33
+ const x402Payload = {
34
+ x402Version: 2,
35
+ payload: decodedClaim,
36
+ accepted: normalizedReq,
37
+ };
38
+
39
+ return Buffer.from(JSON.stringify(x402Payload)).toString("base64");
40
+ }
41
+
42
+ module.exports = {
43
+ wrapAsX402PaymentPayload,
44
+ };
package/src/index.js CHANGED
@@ -1,15 +1,16 @@
1
- const {
2
- buildPaychanAuthHexStringToBeSigned,
3
- _serializePaychanAuthorization,
4
- } = require("./dhali/createSignedClaim");
5
- const {
6
- DhaliChannelManager,
7
- ChannelNotFound,
8
- } = require("./dhali/DhaliChannelManager");
1
+ const { DhaliChannelManager } = require("./dhali/DhaliChannelManager");
2
+ const { DhaliXrplChannelManager, ChannelNotFound } = require("./dhali/DhaliXrplChannelManager");
3
+ const { DhaliEthChannelManager } = require("./dhali/DhaliEthChannelManager");
4
+ const { Currency } = require("./dhali/Currency");
5
+ const { getAvailableDhaliCurrencies } = require("./dhali/configUtils");
6
+ const { wrapAsX402PaymentPayload } = require("./dhali/utils");
9
7
 
10
8
  module.exports = {
11
- buildPaychanAuthHexStringToBeSigned,
12
- _serializePaychanAuthorization,
13
9
  DhaliChannelManager,
10
+ DhaliXrplChannelManager,
11
+ DhaliEthChannelManager,
14
12
  ChannelNotFound,
13
+ Currency,
14
+ getAvailableDhaliCurrencies,
15
+ wrapAsX402PaymentPayload
15
16
  };
@@ -1,193 +1,128 @@
1
- // tests/DhaliChannelManager.test.js
1
+ const {
2
+ DhaliXrplChannelManager,
3
+ ChannelNotFound,
4
+ } = require("../src/dhali/DhaliXrplChannelManager");
5
+ const createSignedClaim = require("../src/dhali/createSignedClaim");
6
+ const rippleKeypairs = require("ripple-keypairs");
7
+ const Currency = require("../src/dhali/Currency");
2
8
 
3
- // 1) MOCK xrpl.js BEFORE importing any code that instantiates Client
4
9
  jest.mock("xrpl", () => {
5
- // Return an object with the Client class stubbed
6
10
  return {
7
- Client: jest.fn().mockImplementation(() => {
8
- return {
9
- // connect() returns a resolved Promise so await this.ready never hangs
10
- connect: () => Promise.resolve(),
11
- // stubbed methods your code calls:
12
- request: jest.fn(),
13
- autofill: jest.fn(),
14
- submitAndWait: jest.fn(),
15
- disconnect: jest.fn(),
16
- // preserve .url for the constructor test
17
- url: "wss://s1.ripple.com:51234/",
18
- };
19
- }),
11
+ Client: jest.fn().mockImplementation(() => ({
12
+ connect: jest.fn().mockResolvedValue(),
13
+ request: jest.fn(),
14
+ submitAndWait: jest.fn(),
15
+ autofill: jest.fn().mockResolvedValue({}),
16
+ })),
17
+ Wallet: {
18
+ fromSeed: jest.fn(),
19
+ },
20
20
  };
21
21
  });
22
22
 
23
- // 2) MOCK ripple-keypairs BEFORE importing DhaliChannelManager
24
23
  jest.mock("ripple-keypairs", () => ({
25
- // We'll override this mock's behavior inside individual tests
26
24
  sign: jest.fn(),
27
25
  }));
28
26
 
29
- jest.mock("../src/dhali/createSignedClaim", () => ({
30
- buildPaychanAuthHexStringToBeSigned: jest.fn(),
31
- // If you need to expose _serializePaychanAuthorization, add it here too:
32
- // _serializePaychanAuthorization: jest.fn(),
33
- }));
34
27
 
35
- // 3) Now import everything under test
36
- const createSignedClaim = require("../src/dhali/createSignedClaim");
37
- const { sign: mockRippleSign } = require("ripple-keypairs");
38
- const {
39
- DhaliChannelManager,
40
- ChannelNotFound,
41
- } = require("../src/dhali/DhaliChannelManager");
28
+
29
+ const configUtils = require("../src/dhali/configUtils");
30
+ jest.mock("../src/dhali/configUtils", () => ({
31
+ fetchPublicConfig: jest.fn().mockResolvedValue({}),
32
+ retrieveChannelIdFromFirestoreRest: jest.fn(),
33
+ }));
42
34
 
43
35
  describe("DhaliChannelManager", () => {
44
- const CHANNEL_ID = "AB".repeat(32);
45
- let wallet;
46
36
  let manager;
37
+ let mockClient;
38
+ let wallet;
39
+ let currency;
40
+ const CHANNEL_ID = "0000000000000000000000000000000000000000000000000000000000000000";
47
41
 
48
42
  beforeEach(() => {
49
- // 4) Create a minimal fake wallet
50
43
  wallet = {
51
- classicAddress: "rTESTADDRESS",
52
- publicKey: "PUBKEY",
53
- privateKey: "PRIVKEY",
54
- // sign() is used for transaction signing
55
- sign: jest.fn().mockReturnValue({ signedTransaction: "TX_BLOB" }),
44
+ address: "rTestAddress",
45
+ classicAddress: "rTestAddress",
46
+ publicKey: "TEST_PUB_KEY",
47
+ privateKey: "TEST_PRIV_KEY",
48
+ sign: jest.fn().mockReturnValue({ tx_blob: "BLOB" }),
56
49
  };
57
- // 5) Instantiate the manager. client.connect() is already stubbed.
58
- manager = new DhaliChannelManager(wallet);
59
- // 6) Short-circuit any `await this.ready` if your constructor awaits client.connect()
60
- manager.ready = Promise.resolve();
61
- });
50
+ mockClient = {
51
+ connect: jest.fn().mockResolvedValue(),
52
+ request: jest.fn(),
53
+ submitAndWait: jest.fn(),
54
+ autofill: jest.fn().mockResolvedValue({}),
55
+ };
56
+ currency = new Currency("XRP", 6);
62
57
 
63
- afterEach(() => {
64
- // 7) Restore any spies on createSignedClaim so tests remain isolated
65
- if (createSignedClaim.buildPaychanAuthHexStringToBeSigned.mockRestore) {
66
- createSignedClaim.buildPaychanAuthHexStringToBeSigned.mockRestore();
67
- }
58
+ const mockHttp = jest.fn();
59
+ configUtils.retrieveChannelIdFromFirestoreRest.mockResolvedValue("CHAN123");
60
+ manager = new DhaliXrplChannelManager(wallet, mockClient, "XRPL.MAINNET", currency, mockHttp);
68
61
  });
69
62
 
70
- test("constructor sets defaults", () => {
71
- expect(manager.wallet).toBe(wallet);
72
- expect(manager.protocol).toBe("XRPL.MAINNET");
73
- expect(manager.destination).toBe("rLggTEwmTe3eJgyQbCSk4wQazow2TeKrtR");
74
- // client.url comes from our mock above
75
- expect(manager.client.url).toBe("wss://s1.ripple.com:51234/");
63
+ afterEach(() => {
64
+ jest.clearAllMocks();
76
65
  });
77
66
 
78
- describe("_findChannel", () => {
79
- test("returns first channel when present", async () => {
80
- const fakeChannel = { channel_id: "CHAN123", amount: "1000" };
81
- manager.client.request.mockResolvedValue({
82
- result: { channels: [fakeChannel] },
67
+ describe("deposit", () => {
68
+ test("funds existing channel if found", async () => {
69
+ mockClient.rpc_client = { request: jest.fn() }; // not really how it works but let's see
70
+ mockClient.request.mockResolvedValue({
71
+ result: {
72
+ channels: [{ channel_id: CHANNEL_ID }],
73
+ },
83
74
  });
75
+ mockClient.submitAndWait.mockResolvedValue({ result: "SUCCESS" });
76
+ mockClient.autofill = jest.fn().mockResolvedValue({});
84
77
 
85
- const ch = await manager._findChannel();
86
- expect(ch).toBe(fakeChannel);
87
- // verify the exact request payload
88
- expect(manager.client.request).toHaveBeenCalledWith({
89
- command: "account_channels",
90
- account: wallet.classicAddress,
91
- destination_account: manager.destination,
92
- ledger_index: "validated",
93
- });
78
+ const result = await manager.deposit(100);
79
+ expect(result).toBe("SUCCESS");
94
80
  });
95
81
 
96
- test("throws ChannelNotFound when none", async () => {
97
- manager.client.request.mockResolvedValue({ result: { channels: [] } });
98
-
99
- await expect(manager._findChannel()).rejects.toThrow(ChannelNotFound);
100
- await expect(manager._findChannel()).rejects.toThrow(
82
+ test("throws ChannelNotFound if firestore returns null", async () => {
83
+ const mockHttp = jest.fn();
84
+ configUtils.retrieveChannelIdFromFirestoreRest.mockResolvedValue(null);
85
+ manager = new DhaliXrplChannelManager(wallet, mockClient, "XRPL.MAINNET", currency, mockHttp);
86
+ await expect(manager.getAuthToken(100)).rejects.toThrow(ChannelNotFound);
87
+ await expect(manager.getAuthToken(100)).rejects.toThrow(/No open payment channel from/);
88
+ expect(configUtils.retrieveChannelIdFromFirestoreRest).toHaveBeenCalledWith(
89
+ "XRPL.MAINNET",
90
+ currency,
101
91
  wallet.classicAddress,
92
+ mockHttp
102
93
  );
103
- await expect(manager._findChannel()).rejects.toThrow(manager.destination);
104
94
  });
105
- });
106
95
 
107
- describe("deposit", () => {
108
- test("funds existing channel", async () => {
109
- // _findChannel resolves => fund path
110
- const fakeChannel = { channel_id: "CHANID", amount: "500" };
111
- manager._findChannel = jest.fn().mockResolvedValue(fakeChannel);
112
-
113
- // stub autofill + submitAndWait from our Client mock
114
- manager.client.autofill.mockResolvedValue({ foo: "bar" });
115
- manager.client.submitAndWait.mockResolvedValue({
116
- result: { status: "funded" },
96
+ test("throws ChannelNotFound if firestore ID does not match on-chain channels", async () => {
97
+ configUtils.retrieveChannelIdFromFirestoreRest.mockResolvedValue("FIRESTORE_ID");
98
+ mockClient.request.mockResolvedValue({
99
+ result: {
100
+ channels: [{ channel_id: "XRPL_ID", amount: "1000" }],
101
+ },
117
102
  });
118
103
 
119
- const res = await manager.deposit(100);
120
- expect(res).toEqual({ status: "funded" });
121
-
122
- // ensure autofill got the correct PaymentChannelFund payload
123
- expect(manager.client.autofill).toHaveBeenCalledWith({
124
- TransactionType: "PaymentChannelFund",
125
- Account: wallet.classicAddress,
126
- Channel: fakeChannel.channel_id,
127
- Amount: "100",
128
- });
129
- // ensure we submitted the signed blob returned by wallet.sign()
130
- expect(manager.client.submitAndWait).toHaveBeenCalledWith("TX_BLOB");
131
- });
132
-
133
- test("creates channel if none exists", async () => {
134
- // _findChannel rejects with ChannelNotFound => create path
135
- manager._findChannel = jest
136
- .fn()
137
- .mockRejectedValue(new ChannelNotFound("nope"));
138
-
139
- manager.client.autofill.mockResolvedValue({ baz: "qux" });
140
- manager.client.submitAndWait.mockResolvedValue({
141
- result: { status: "created" },
142
- });
143
-
144
- const res = await manager.deposit(200);
145
- expect(res).toEqual({ status: "created" });
146
-
147
- expect(manager.client.autofill).toHaveBeenCalledWith({
148
- TransactionType: "PaymentChannelCreate",
149
- Account: wallet.classicAddress,
150
- Destination: manager.destination,
151
- Amount: "200",
152
- SettleDelay: 86400 * 14,
153
- PublicKey: wallet.publicKey,
154
- });
155
- expect(manager.client.submitAndWait).toHaveBeenCalledWith("TX_BLOB");
104
+ await expect(manager.getAuthToken(100)).rejects.toThrow(ChannelNotFound);
105
+ await expect(manager.getAuthToken(100)).rejects.toThrow(/FIRESTORE_ID not found on-chain/);
156
106
  });
157
107
  });
158
108
 
159
109
  describe("getAuthToken", () => {
160
110
  test("success with default amount", async () => {
161
- // stub channel lookup
162
111
  manager._findChannel = jest.fn().mockResolvedValue({
163
112
  channel_id: CHANNEL_ID,
164
113
  amount: "1001",
165
114
  });
166
115
 
167
- // 8) SPY on the hex-builder instead of reassigning it
168
- const claimSpy = jest
169
- .spyOn(createSignedClaim, "buildPaychanAuthHexStringToBeSigned")
170
- .mockReturnValue("CLAIMHEX");
171
-
172
- // stub signature
173
- mockRippleSign.mockReturnValue("SIGVALUE");
116
+ rippleKeypairs.sign.mockReturnValue("SIG");
174
117
 
175
118
  const token = await manager.getAuthToken();
176
119
  const decoded = JSON.parse(Buffer.from(token, "base64").toString("utf8"));
177
120
 
178
121
  expect(decoded).toMatchObject({
179
- version: "2",
180
- account: wallet.classicAddress,
181
- protocol: manager.protocol,
182
- currency: { code: "XRP", scale: 6 },
183
- destination_account: manager.destination,
184
- authorized_to_claim: "1001",
185
122
  channel_id: CHANNEL_ID,
186
- signature: "SIGVALUE",
123
+ authorized_to_claim: "1001",
124
+ signature: "SIG",
187
125
  });
188
-
189
- // ensure our spy was called with correct args
190
- expect(claimSpy).toHaveBeenCalledWith(CHANNEL_ID, "1001");
191
126
  });
192
127
 
193
128
  test("success with specific amount", async () => {
@@ -195,16 +130,11 @@ describe("DhaliChannelManager", () => {
195
130
  channel_id: CHANNEL_ID,
196
131
  amount: "500",
197
132
  });
198
-
199
- const claimSpy = jest
200
- .spyOn(createSignedClaim, "buildPaychanAuthHexStringToBeSigned")
201
- .mockReturnValue("CLAIMHEX2");
202
- mockRippleSign.mockReturnValue("SIG2");
133
+ rippleKeypairs.sign.mockReturnValue("SIG2");
203
134
 
204
135
  const token = await manager.getAuthToken(200);
205
136
  const decoded = JSON.parse(Buffer.from(token, "base64").toString("utf8"));
206
137
  expect(decoded.authorized_to_claim).toBe("200");
207
- expect(claimSpy).toHaveBeenCalledWith(CHANNEL_ID, "200");
208
138
  });
209
139
 
210
140
  test("throws if amount exceeds capacity", async () => {