dhali-js 1.0.3 → 2.0.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.
@@ -27,3 +27,6 @@ jobs:
27
27
 
28
28
  - name: Run tests
29
29
  run: npm test
30
+
31
+ - name: Run lint
32
+ run: npx tsc src/dhali/*.js --allowJs --checkJs --noEmit --moduleResolution node --module commonjs --target esnext
package/README.md CHANGED
@@ -1,7 +1,12 @@
1
+ [![Package Tests](https://github.com/Dhali-org/Dhali-js/actions/workflows/tests.yaml/badge.svg)](https://github.com/Dhali-org/Dhali-js/actions/workflows/tests.yaml)
2
+ [![Release](https://github.com/Dhali-org/Dhali-js/actions/workflows/publish.yaml/badge.svg)](https://github.com/Dhali-org/Dhali-js/actions/workflows/publish.yaml)
3
+
4
+
1
5
  # dhali-js
2
6
 
3
- A JavaScript library for managing XRPL payment channels and generating auth tokens for use with [Dhali](https://dhali.io) APIs.
4
- Leverages [xrpl.js](https://github.com/XRPLF/xrpl.js) and **only ever performs local signing**—your private key never leaves your environment.
7
+ A JavaScript library for managing payment channels (XRPL & Ethereum) and generating auth tokens for use with [Dhali](https://dhali.io) APIs.
8
+
9
+ Includes support for **Machine-to-Machine (M2M) payments** using seamless off-chain claims.
5
10
 
6
11
  ---
7
12
 
@@ -9,87 +14,116 @@ Leverages [xrpl.js](https://github.com/XRPLF/xrpl.js) and **only ever performs l
9
14
 
10
15
  ```bash
11
16
  npm install dhali-js
12
- ````
17
+ ```
13
18
 
14
19
  ---
15
20
 
16
- ## Quick Start
21
+ ## Quick Start: Machine-to-Machine Payments
22
+
23
+ ### 1. XRPL
24
+
25
+ Uses `xrpl.js` for local signing.
17
26
 
18
27
  ```js
19
- const { Wallet } = require('xrpl')
20
- const { DhaliChannelManager, ChannelNotFound } = require('dhali-js')
21
-
22
- const seed = "sXXX"
23
-
24
- ;(async () => {
25
- const wallet = Wallet.fromSeed(seed)
26
- const manager = new DhaliChannelManager(wallet)
27
-
28
- let token
29
- try {
30
- token = await manager.getAuthToken()
31
- } catch (err) {
32
- if (err instanceof ChannelNotFound) {
33
- await manager.deposit(1_000_000)
34
- token = await manager.getAuthToken()
28
+ const { Client, Wallet } = require('xrpl')
29
+ const { DhaliChannelManager, ChannelNotFound, Currency } = require('dhali-js')
30
+
31
+ const seed = "sXXX..."
32
+ const wallet = Wallet.fromSeed(seed)
33
+ const client = new Client("wss://s.altnet.rippletest.net:51233")
34
+ await client.connect()
35
+
36
+ const currency = new Currency("XRP", 6)
37
+
38
+ // Use Factory
39
+ const manager = DhaliChannelManager.xrpl(wallet, client, "XRPL.TESTNET", currency)
40
+
41
+ // Generate Claim
42
+ let token;
43
+ try {
44
+ token = await manager.getAuthToken();
45
+ } catch (error) {
46
+ if (error.name === "ChannelNotFound") {
47
+ await manager.deposit(1000000); // Deposit 1 XRP
48
+ token = await manager.getAuthToken();
35
49
  } else {
36
- console.error(err)
37
- process.exit(1)
50
+ throw error;
38
51
  }
39
- }
40
-
41
- const url = `https://xrplcluster.dhali.io?payment-claim=${token}`
42
- const resp = await fetch(url, {
43
- method: 'POST',
44
- headers: { 'Content-Type': 'application/json' },
45
- body: JSON.stringify({
46
- method: 'account_info',
47
- params: [{ account: wallet.classicAddress, ledger_index: 'validated' }],
48
- id: 1,
49
- }),
50
- })
51
- const result = await resp.json()
52
- console.log(result)
53
- })()
54
-
52
+ }
53
+ console.log('XRPL Token:', token);
55
54
  ```
56
55
 
57
- ---
58
-
59
- ## API
56
+ ### 2. Ethereum (EVM)
60
57
 
61
- ### `new DhaliChannelManager(wallet: xrpl.Wallet)`
62
-
63
- * **wallet**: an `xrpl.js` `Wallet` instance (e.g. `Wallet.fromSeed`).
64
-
65
- ---
58
+ Uses `ethers` (v6) for EIP-712 signing.
66
59
 
67
- ### `async deposit(amountDrops: number) → Promise<object>`
68
-
69
- * **amountDrops**: Number of XRP drops (e.g. `1_000_000` = 1 XRP).
70
- * **Returns**: The JSON result of the `PaymentChannelCreate` or `PaymentChannelFund` transaction.
60
+ ```js
61
+ const { ethers } = require('ethers')
62
+ const { DhaliChannelManager, getAvailableDhaliCurrencies } = require('dhali-js')
63
+
64
+ // 1. Setup Signer
65
+ const provider = new ethers.JsonRpcProvider("https://rpc.ankr.com/eth_sepolia")
66
+ const signer = new ethers.Wallet("0x...", provider)
67
+
68
+ // 2. Fetch Available Currencies
69
+ const configs = await getAvailableDhaliCurrencies()
70
+ const sepoliaUsdc = configs["SEPOLIA"]["USDC"]
71
+
72
+ // 3. Instantiate Manager with Dynamic Config
73
+ const manager = DhaliChannelManager.evm(
74
+ signer,
75
+ provider,
76
+ "SEPOLIA",
77
+ sepoliaUsdc.currency
78
+ )
79
+
80
+ // 4. Generate Claim
81
+ // 4. Generate Claim
82
+ let token;
83
+ try {
84
+ token = await manager.getAuthToken(1000000); // 1.00 USDC
85
+ } catch (error) {
86
+ if (error.name === "ChannelNotFound") {
87
+ await manager.deposit(1000000); // Deposit 1.00 USDC
88
+ token = await manager.getAuthToken(1000000);
89
+ } else {
90
+ throw error;
91
+ }
92
+ }
93
+ console.log('EVM Token:', token);
94
+ ```
71
95
 
72
96
  ---
73
97
 
74
- ### `async getAuthToken(amountDrops?: number) → Promise<string>`
98
+ ## Integration
75
99
 
76
- * **amountDrops** (optional): How many drops to authorize; defaults to full channel balance.
77
- * **Returns**: A base64-encoded JSON string containing your signed claim.
78
- * **Throws**:
100
+ Pass the token in your API calls to Dhali-enabled services.
79
101
 
80
- * `ChannelNotFound` if there is no open channel.
81
- * `Error` if `amountDrops` exceeds channel capacity.
102
+ ```js
103
+ const url = `https://xrplcluster.dhali.io?payment-claim=${token}`
104
+ const response = await fetch(url, { method: 'POST', body: ... })
105
+ ```
82
106
 
83
107
  ---
84
108
 
85
- ## Errors
109
+ ## API Reference
86
110
 
87
- * **ChannelNotFound**
88
- Thrown when `getAuthToken` finds no channel from your wallet to Dhali’s receiver.
111
+ ### `DhaliChannelManager`
89
112
 
90
- ---
113
+ * `.xrpl(wallet, client, protocol, currency)`: Returns `DhaliXrplChannelManager`.
114
+ * `.evm(signer, provider, protocol, currency)`: Returns `DhaliEthChannelManager`.
115
+
116
+ ### `getAvailableDhaliCurrencies()`
91
117
 
92
- ## Security
118
+ Returns a Promise resolving to:
119
+ ```js
120
+ {
121
+ "SEPOLIA": {
122
+ "USDC": { currency: ..., destinationAddress: ... },
123
+ ...
124
+ },
125
+ ...
126
+ }
127
+ ```
93
128
 
94
- All XRPL interactions and claim-signatures are done locally via `xrpl.js` + `ripple-keypairs`.
95
- Your private key never leaves your machine.
129
+ ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dhali-js",
3
- "version": "1.0.3",
3
+ "version": "2.0.0",
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",
@@ -16,11 +16,13 @@
16
16
  ],
17
17
  "license": "MIT",
18
18
  "dependencies": {
19
+ "ethers": "^6.0.0",
19
20
  "ripple-keypairs": "^2.0.0",
20
21
  "xrpl": "^4.0.0"
21
22
  },
22
23
  "devDependencies": {
23
- "jest": "^29.0.0",
24
- "prettier": "^3.5.3"
24
+ "jest": "^29.7.0",
25
+ "prettier": "^3.5.3",
26
+ "typescript": "^5.9.3"
25
27
  }
26
28
  }
@@ -0,0 +1,9 @@
1
+ class Currency {
2
+ constructor(code, scale, tokenAddress = null) {
3
+ this.code = code;
4
+ this.scale = scale;
5
+ this.tokenAddress = tokenAddress;
6
+ }
7
+ }
8
+
9
+ module.exports = Currency;
@@ -1,110 +1,39 @@
1
- const { Client } = require("xrpl");
2
- const { buildPaychanAuthHexStringToBeSigned } = require("./createSignedClaim");
3
- const { sign: signClaim } = require("ripple-keypairs");
4
-
5
- class ChannelNotFound extends Error {}
6
-
7
- /**
8
- * A management tool for generating payment claims for use with Dhali APIs.
9
- */
10
- class DhaliChannelManager {
11
- /**
12
- * @param {xrpl.Wallet} wallet
13
- */
14
- constructor(wallet) {
15
- this.client = new Client("wss://s1.ripple.com:51234/");
16
- this.ready = this.client.connect();
17
- this.wallet = wallet;
18
- this.destination = "rLggTEwmTe3eJgyQbCSk4wQazow2TeKrtR";
19
- this.protocol = "XRPL.MAINNET";
20
- }
21
-
22
- async _findChannel() {
23
- await this.ready;
24
- const resp = await this.client.request({
25
- command: "account_channels",
26
- account: this.wallet.classicAddress,
27
- destination_account: this.destination,
28
- ledger_index: "validated",
29
- });
30
- const channels = resp.result.channels || [];
31
- if (channels.length === 0) {
32
- throw new ChannelNotFound(
33
- `No open payment channel from ${this.wallet.classicAddress} to ${this.destination}`,
34
- );
35
- }
36
- return channels[0];
37
- }
1
+ const { DhaliXrplChannelManager } = require("./DhaliXrplChannelManager");
2
+ const { DhaliEthChannelManager } = require("./DhaliEthChannelManager");
38
3
 
4
+ const DhaliChannelManager = {
39
5
  /**
40
- * Create or fund a payment channel.
41
- * @param {number} amountDrops
42
- * @returns {Promise<object>}
6
+ * @param {import("xrpl").Wallet} wallet
7
+ * @param {import("xrpl").Client} client
8
+ * @param {string} protocol
9
+ * @param {import("./Currency")} currency
10
+ * @param {typeof fetch} [httpClient] - Injected HTTP client
11
+ * @param {object} [publicConfig]
12
+ * @returns {DhaliXrplChannelManager}
43
13
  */
44
- async deposit(amountDrops) {
45
- await this.ready;
46
- let tx;
47
- try {
48
- const ch = await this._findChannel();
49
- tx = {
50
- TransactionType: "PaymentChannelFund",
51
- Account: this.wallet.classicAddress,
52
- Channel: ch.channel_id,
53
- Amount: amountDrops.toString(),
54
- };
55
- } catch (err) {
56
- if (!(err instanceof ChannelNotFound)) throw err;
57
- tx = {
58
- TransactionType: "PaymentChannelCreate",
59
- Account: this.wallet.classicAddress,
60
- Destination: this.destination,
61
- Amount: amountDrops.toString(),
62
- SettleDelay: 86400 * 14,
63
- PublicKey: this.wallet.publicKey,
64
- };
65
- }
66
- // autofill sequence, fee, etc.
67
- const prepared = await this.client.autofill(tx);
68
- // sign
69
- const signed = this.wallet.sign(prepared);
70
- const txBlob = signed.tx_blob || signed.signedTransaction;
71
- // submit & wait
72
- const result = await this.client.submitAndWait(txBlob);
73
- return result.result;
74
- }
14
+ xrpl: (wallet, client, protocol, currency, httpClient, publicConfig) => {
15
+ return new DhaliXrplChannelManager(wallet, client, protocol, currency, httpClient, publicConfig);
16
+ },
75
17
 
76
18
  /**
77
- * Generate a base64-encoded payment claim.
78
- * @param {number=} amountDrops
79
- * @returns {Promise<string>}
19
+ * @param {import("ethers").Signer} signer
20
+ * @param {import("ethers").Provider} provider
21
+ * @param {string} protocol
22
+ * @param {import("./Currency")} currency
23
+ * @param {typeof fetch} [httpClient] - Injected HTTP client
24
+ * @param {object} [publicConfig]
25
+ * @returns {DhaliEthChannelManager}
80
26
  */
81
- async getAuthToken(amountDrops) {
82
- await this.ready;
83
- const ch = await this._findChannel();
84
- const total = BigInt(ch.amount);
85
- const allowed = amountDrops != null ? BigInt(amountDrops) : total;
86
- if (allowed > total) {
87
- throw new Error(
88
- `Requested auth ${allowed} exceeds channel capacity ${total}`,
89
- );
90
- }
91
- const claimHex = buildPaychanAuthHexStringToBeSigned(
92
- ch.channel_id,
93
- allowed.toString(),
27
+ evm: (signer, provider, protocol, currency, httpClient, publicConfig) => {
28
+ return new DhaliEthChannelManager(
29
+ signer,
30
+ provider,
31
+ protocol,
32
+ currency,
33
+ httpClient,
34
+ publicConfig
94
35
  );
95
- const signature = signClaim(claimHex, this.wallet.privateKey);
96
- const claim = {
97
- version: "2",
98
- account: this.wallet.classicAddress,
99
- protocol: this.protocol,
100
- currency: { code: "XRP", scale: 6 },
101
- destination_account: this.destination,
102
- authorized_to_claim: allowed.toString(),
103
- channel_id: ch.channel_id,
104
- signature,
105
- };
106
- return Buffer.from(JSON.stringify(claim)).toString("base64");
107
36
  }
108
- }
37
+ };
109
38
 
110
- module.exports = { DhaliChannelManager, ChannelNotFound };
39
+ module.exports = { DhaliChannelManager };