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.
- package/.github/workflows/tests.yaml +3 -0
- package/README.md +97 -63
- 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
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
[](https://github.com/Dhali-org/Dhali-js/actions/workflows/tests.yaml)
|
|
2
|
+
[](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
|
|
4
|
-
|
|
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
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
98
|
+
## Integration
|
|
75
99
|
|
|
76
|
-
|
|
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
|
-
|
|
81
|
-
|
|
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
|
-
##
|
|
109
|
+
## API Reference
|
|
86
110
|
|
|
87
|
-
|
|
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
|
-
|
|
118
|
+
Returns a Promise resolving to:
|
|
119
|
+
```js
|
|
120
|
+
{
|
|
121
|
+
"SEPOLIA": {
|
|
122
|
+
"USDC": { currency: ..., destinationAddress: ... },
|
|
123
|
+
...
|
|
124
|
+
},
|
|
125
|
+
...
|
|
126
|
+
}
|
|
127
|
+
```
|
|
93
128
|
|
|
94
|
-
|
|
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": "
|
|
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 };
|