dhali-js 1.0.4 → 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.
- package/.github/workflows/tests.yaml +3 -0
- package/README.md +83 -75
- package/package.json +5 -3
- package/src/dhali/Currency.js +9 -0
- package/src/dhali/DhaliChannelManager.js +30 -101
- package/src/dhali/DhaliEthChannelManager.js +331 -0
- package/src/dhali/DhaliXrplChannelManager.js +199 -0
- package/src/dhali/configUtils.js +203 -0
- package/src/dhali/createSignedClaim.js +34 -0
- package/src/index.js +9 -10
- package/tests/DhaliChannelManager.test.js +76 -146
- package/tests/DhaliEthChannelManager.test.js +231 -0
package/README.md
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
# dhali-js
|
|
6
6
|
|
|
7
|
-
A JavaScript library for managing
|
|
8
|
-
|
|
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.
|
|
9
10
|
|
|
10
11
|
---
|
|
11
12
|
|
|
@@ -17,105 +18,112 @@ npm install dhali-js
|
|
|
17
18
|
|
|
18
19
|
---
|
|
19
20
|
|
|
20
|
-
## Quick Start
|
|
21
|
+
## Quick Start: Machine-to-Machine Payments
|
|
22
|
+
|
|
23
|
+
### 1. XRPL
|
|
24
|
+
|
|
25
|
+
Uses `xrpl.js` for local signing.
|
|
21
26
|
|
|
22
27
|
```js
|
|
23
|
-
|
|
24
|
-
const {
|
|
25
|
-
const { DhaliChannelManager, ChannelNotFound } = require('dhali-js')
|
|
28
|
+
const { Client, Wallet } = require('xrpl')
|
|
29
|
+
const { DhaliChannelManager, ChannelNotFound, Currency } = require('dhali-js')
|
|
26
30
|
|
|
27
|
-
const seed = "sXXX"
|
|
31
|
+
const seed = "sXXX..."
|
|
28
32
|
const wallet = Wallet.fromSeed(seed)
|
|
29
|
-
const
|
|
30
|
-
|
|
33
|
+
const client = new Client("wss://s.altnet.rippletest.net:51233")
|
|
34
|
+
await client.connect()
|
|
31
35
|
|
|
36
|
+
const currency = new Currency("XRP", 6)
|
|
32
37
|
|
|
33
|
-
|
|
38
|
+
// Use Factory
|
|
39
|
+
const manager = DhaliChannelManager.xrpl(wallet, client, "XRPL.TESTNET", currency)
|
|
34
40
|
|
|
35
|
-
|
|
36
|
-
let token
|
|
41
|
+
// Generate Claim
|
|
42
|
+
let token;
|
|
37
43
|
try {
|
|
38
|
-
|
|
39
|
-
} catch (
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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();
|
|
49
|
+
} else {
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
44
52
|
}
|
|
45
|
-
console.log('
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
### 2. Top Up Later (and Regenerate)
|
|
51
|
-
|
|
52
|
-
```js
|
|
53
|
-
await manager.deposit(2_000_000) // add 2 XRP
|
|
54
|
-
const updatedToken = await manager.getAuthToken()
|
|
55
|
-
console.log('Updated token:', updatedToken)
|
|
53
|
+
console.log('XRPL Token:', token);
|
|
56
54
|
```
|
|
57
55
|
|
|
58
|
-
|
|
56
|
+
### 2. Ethereum (EVM)
|
|
59
57
|
|
|
60
|
-
|
|
58
|
+
Uses `ethers` (v6) for EIP-712 signing.
|
|
61
59
|
|
|
62
60
|
```js
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
+
}
|
|
75
92
|
}
|
|
76
|
-
|
|
77
|
-
;(async () => {
|
|
78
|
-
const result = await fetchWithClaim()
|
|
79
|
-
console.log(result)
|
|
80
|
-
})()
|
|
93
|
+
console.log('EVM Token:', token);
|
|
81
94
|
```
|
|
82
95
|
|
|
83
96
|
---
|
|
84
97
|
|
|
85
|
-
##
|
|
98
|
+
## Integration
|
|
86
99
|
|
|
87
|
-
|
|
100
|
+
Pass the token in your API calls to Dhali-enabled services.
|
|
88
101
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
### `async deposit(amountDrops: number) → Promise<object>`
|
|
94
|
-
|
|
95
|
-
* **amountDrops**: Number of XRP drops (e.g. `1_000_000` = 1 XRP).
|
|
96
|
-
* **Returns**: The JSON result of the `PaymentChannelCreate` or `PaymentChannelFund` transaction.
|
|
102
|
+
```js
|
|
103
|
+
const url = `https://xrplcluster.dhali.io?payment-claim=${token}`
|
|
104
|
+
const response = await fetch(url, { method: 'POST', body: ... })
|
|
105
|
+
```
|
|
97
106
|
|
|
98
107
|
---
|
|
99
108
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
* **amountDrops** (optional): How many drops to authorize; defaults to full channel balance.
|
|
103
|
-
* **Returns**: A base64-encoded JSON string containing your signed claim.
|
|
104
|
-
* **Throws**:
|
|
109
|
+
## API Reference
|
|
105
110
|
|
|
106
|
-
|
|
107
|
-
* `Error` if `amountDrops` exceeds channel capacity.
|
|
111
|
+
### `DhaliChannelManager`
|
|
108
112
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
## Errors
|
|
113
|
+
* `.xrpl(wallet, client, protocol, currency)`: Returns `DhaliXrplChannelManager`.
|
|
114
|
+
* `.evm(signer, provider, protocol, currency)`: Returns `DhaliEthChannelManager`.
|
|
112
115
|
|
|
113
|
-
|
|
114
|
-
Thrown when `getAuthToken` finds no channel from your wallet to Dhali’s receiver.
|
|
115
|
-
|
|
116
|
-
---
|
|
116
|
+
### `getAvailableDhaliCurrencies()`
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
Returns a Promise resolving to:
|
|
119
|
+
```js
|
|
120
|
+
{
|
|
121
|
+
"SEPOLIA": {
|
|
122
|
+
"USDC": { currency: ..., destinationAddress: ... },
|
|
123
|
+
...
|
|
124
|
+
},
|
|
125
|
+
...
|
|
126
|
+
}
|
|
127
|
+
```
|
|
119
128
|
|
|
120
|
-
|
|
121
|
-
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": "
|
|
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.
|
|
24
|
-
"prettier": "^3.5.3"
|
|
24
|
+
"jest": "^29.7.0",
|
|
25
|
+
"prettier": "^3.5.3",
|
|
26
|
+
"typescript": "^5.9.3"
|
|
25
27
|
}
|
|
26
28
|
}
|
|
@@ -1,110 +1,39 @@
|
|
|
1
|
-
const {
|
|
2
|
-
const {
|
|
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
|
-
*
|
|
41
|
-
* @param {
|
|
42
|
-
* @
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
*
|
|
78
|
-
* @param {
|
|
79
|
-
* @
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
|
39
|
+
module.exports = { DhaliChannelManager };
|