@xrplrequest/sdk 0.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.
- package/README.md +234 -0
- package/dist/index.d.mts +117 -0
- package/dist/index.d.ts +117 -0
- package/dist/index.js +171 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +166 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# @xrplrequest/sdk
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for [XRPL Request](https://xrplre.quest) — wallet-agnostic XRPL transaction signing.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @xrplrequest/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { XRPLRequest } from '@xrplrequest/sdk';
|
|
15
|
+
|
|
16
|
+
const client = new XRPLRequest({ apiKey: 'xrplr_live_...' });
|
|
17
|
+
|
|
18
|
+
const payload = await client.payloads.create({
|
|
19
|
+
type: 'signAndSubmit',
|
|
20
|
+
transaction: {
|
|
21
|
+
TransactionType: 'Payment',
|
|
22
|
+
Destination: 'rXXX...',
|
|
23
|
+
Amount: '1000000', // 1 XRP in drops
|
|
24
|
+
},
|
|
25
|
+
options: { expiresIn: 300 },
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
console.log(payload.signingUrl); // share with user
|
|
29
|
+
// https://xrplre.quest/sign/{uuid}
|
|
30
|
+
|
|
31
|
+
const result = await client.payloads.poll(payload.uuid, { timeout: 300_000 });
|
|
32
|
+
|
|
33
|
+
if (result.status === 'signed') {
|
|
34
|
+
console.log('tx hash:', result.txHash);
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## API
|
|
39
|
+
|
|
40
|
+
### `new XRPLRequest(config)`
|
|
41
|
+
|
|
42
|
+
| Option | Type | Required | Description |
|
|
43
|
+
|---|---|---|---|
|
|
44
|
+
| `apiKey` | `string` | ✓ | Your project API key (`xrplr_live_...`) |
|
|
45
|
+
| `baseUrl` | `string` | | Override the API base URL (default: `https://xrplre.quest`) |
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
### `client.payloads.create(options)`
|
|
50
|
+
|
|
51
|
+
Create a new signing payload. Returns a `Payload` with a `signingUrl` to share with the user.
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
const payload = await client.payloads.create({
|
|
55
|
+
type: 'signAndSubmit', // 'connect' | 'sign' | 'signAndSubmit' | 'signMessage'
|
|
56
|
+
transaction: { ... }, // required for sign / signAndSubmit
|
|
57
|
+
message: 'Hello XRPL', // required for signMessage
|
|
58
|
+
options: {
|
|
59
|
+
expiresIn: 300, // seconds (default 300, max depends on plan)
|
|
60
|
+
returnUrl: 'https://...', // optional redirect after signing
|
|
61
|
+
customInstructions: '...', // shown to user on sign page (max 200 chars)
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
payload.uuid // unique identifier
|
|
66
|
+
payload.signingUrl // https://xrplre.quest/sign/{uuid}
|
|
67
|
+
payload.status // 'pending'
|
|
68
|
+
payload.expiresAt // ISO timestamp
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### `client.payloads.get(uuid)`
|
|
72
|
+
|
|
73
|
+
Fetch the current status of a payload.
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
const payload = await client.payloads.get(uuid);
|
|
77
|
+
// payload.status: 'pending' | 'signed' | 'rejected' | 'expired'
|
|
78
|
+
// payload.txHash — populated when signed + submitted
|
|
79
|
+
// payload.signerAddress — populated when signed
|
|
80
|
+
// payload.walletAdapter — which wallet was used
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### `client.payloads.poll(uuid, options?)`
|
|
84
|
+
|
|
85
|
+
Poll until the payload leaves `pending` state or the timeout is reached.
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
const result = await client.payloads.poll(uuid, {
|
|
89
|
+
timeout: 300_000, // max wait in ms (default 300,000)
|
|
90
|
+
interval: 2_000, // poll interval in ms (default 2,000; min 3s on Free plan)
|
|
91
|
+
onUpdate: (payload) => {
|
|
92
|
+
console.log('still waiting:', payload.status);
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### `client.payloads.cancel(uuid)`
|
|
98
|
+
|
|
99
|
+
Cancel a pending payload.
|
|
100
|
+
|
|
101
|
+
### `client.payloads.list(options?)`
|
|
102
|
+
|
|
103
|
+
List payloads for your project.
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
const { payloads } = await client.payloads.list({ limit: 20, offset: 0 });
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
### `client.webhooks.create(options)`
|
|
112
|
+
|
|
113
|
+
Register a webhook URL. The returned `secret` is used to verify signatures — save it, it won't be shown again.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
const webhook = await client.webhooks.create({
|
|
117
|
+
url: 'https://yourapp.com/xrplrequest-webhook',
|
|
118
|
+
events: ['payload.signed', 'payload.rejected', 'payload.expired'],
|
|
119
|
+
});
|
|
120
|
+
console.log(webhook.secret); // save this
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### `client.webhooks.list()`
|
|
124
|
+
|
|
125
|
+
List webhooks for your project.
|
|
126
|
+
|
|
127
|
+
### `client.webhooks.delete(id)`
|
|
128
|
+
|
|
129
|
+
Remove a webhook.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### `verifyWebhookSignature(secret, rawBody, signature)`
|
|
134
|
+
|
|
135
|
+
Standalone function to verify an incoming webhook request. Also available as `WebhooksClient.verify`.
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
import { verifyWebhookSignature } from '@xrplrequest/sdk';
|
|
139
|
+
|
|
140
|
+
// Express example
|
|
141
|
+
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
|
|
142
|
+
const sig = req.headers['x-xrpl-request-signature'] as string;
|
|
143
|
+
const valid = verifyWebhookSignature(process.env.WEBHOOK_SECRET!, req.body.toString(), sig);
|
|
144
|
+
|
|
145
|
+
if (!valid) return res.status(401).send('Invalid signature');
|
|
146
|
+
|
|
147
|
+
const { event, payload } = JSON.parse(req.body.toString());
|
|
148
|
+
console.log(event, payload.txHash);
|
|
149
|
+
res.sendStatus(200);
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Payload types
|
|
156
|
+
|
|
157
|
+
| Type | Description |
|
|
158
|
+
|---|---|
|
|
159
|
+
| `connect` | Request wallet address / identity verification. No transaction. |
|
|
160
|
+
| `sign` | Sign a transaction without submitting to the ledger. |
|
|
161
|
+
| `signAndSubmit` | Sign and broadcast to the XRP Ledger. Returns `txHash`. |
|
|
162
|
+
| `signMessage` | Sign an arbitrary UTF-8 message string. |
|
|
163
|
+
|
|
164
|
+
## Webhook events
|
|
165
|
+
|
|
166
|
+
| Event | When |
|
|
167
|
+
|---|---|
|
|
168
|
+
| `payload.signed` | User signed (and optionally submitted) the transaction |
|
|
169
|
+
| `payload.rejected` | User rejected in their wallet |
|
|
170
|
+
| `payload.expired` | TTL elapsed without resolution |
|
|
171
|
+
|
|
172
|
+
Webhook POST body:
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"event": "payload.signed",
|
|
176
|
+
"payload": {
|
|
177
|
+
"uuid": "...",
|
|
178
|
+
"type": "signAndSubmit",
|
|
179
|
+
"status": "signed",
|
|
180
|
+
"signerAddress": "rXXX...",
|
|
181
|
+
"txHash": "ABC123...",
|
|
182
|
+
"walletAdapter": "crossmark",
|
|
183
|
+
"createdAt": "2026-05-08T12:00:00.000Z",
|
|
184
|
+
"resolvedAt": "2026-05-08T12:01:23.000Z"
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Signature header: `X-XRPL-Request-Signature: sha256=<hmac>`
|
|
190
|
+
|
|
191
|
+
## Discord bot example
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
import { Client, GatewayIntentBits } from 'discord.js';
|
|
195
|
+
import { XRPLRequest } from '@xrplrequest/sdk';
|
|
196
|
+
|
|
197
|
+
const xrpl = new XRPLRequest({ apiKey: process.env.XRPL_REQUEST_API_KEY! });
|
|
198
|
+
const discord = new Client({ intents: [GatewayIntentBits.Guilds] });
|
|
199
|
+
|
|
200
|
+
discord.on('interactionCreate', async (interaction) => {
|
|
201
|
+
if (!interaction.isChatInputCommand() || interaction.commandName !== 'tip') return;
|
|
202
|
+
|
|
203
|
+
const amount = interaction.options.getInteger('amount', true);
|
|
204
|
+
const destination = interaction.options.getString('destination', true);
|
|
205
|
+
|
|
206
|
+
const payload = await xrpl.payloads.create({
|
|
207
|
+
type: 'signAndSubmit',
|
|
208
|
+
transaction: {
|
|
209
|
+
TransactionType: 'Payment',
|
|
210
|
+
Destination: destination,
|
|
211
|
+
Amount: String(amount * 1_000_000), // XRP → drops
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
await interaction.reply({
|
|
216
|
+
content: `Sign this transaction to send ${amount} XRP:\n${payload.signingUrl}`,
|
|
217
|
+
ephemeral: true,
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
const result = await xrpl.payloads.poll(payload.uuid, { timeout: 300_000 });
|
|
221
|
+
|
|
222
|
+
if (result.status === 'signed') {
|
|
223
|
+
await interaction.editReply(`✅ Sent! Tx: \`${result.txHash}\``);
|
|
224
|
+
} else if (result.status === 'rejected') {
|
|
225
|
+
await interaction.editReply('❌ Transaction rejected.');
|
|
226
|
+
} else {
|
|
227
|
+
await interaction.editReply('⏱ Request expired.');
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## License
|
|
233
|
+
|
|
234
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
type PayloadType = "connect" | "sign" | "signAndSubmit" | "signMessage";
|
|
2
|
+
type PayloadStatus = "pending" | "signed" | "rejected" | "expired";
|
|
3
|
+
interface CreatePayloadOptions {
|
|
4
|
+
type: PayloadType;
|
|
5
|
+
transaction?: Record<string, unknown>;
|
|
6
|
+
message?: string;
|
|
7
|
+
options?: {
|
|
8
|
+
expiresIn?: number;
|
|
9
|
+
returnUrl?: string;
|
|
10
|
+
customInstructions?: string;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
interface Payload {
|
|
14
|
+
uuid: string;
|
|
15
|
+
type: PayloadType;
|
|
16
|
+
status: PayloadStatus;
|
|
17
|
+
network: "mainnet" | "testnet";
|
|
18
|
+
signingUrl: string;
|
|
19
|
+
expiresAt: string;
|
|
20
|
+
createdAt: string;
|
|
21
|
+
signerAddress?: string | null;
|
|
22
|
+
txHash?: string | null;
|
|
23
|
+
walletAdapter?: string | null;
|
|
24
|
+
resolvedAt?: string | null;
|
|
25
|
+
}
|
|
26
|
+
interface PollOptions {
|
|
27
|
+
timeout?: number;
|
|
28
|
+
interval?: number;
|
|
29
|
+
onUpdate?: (payload: Payload) => void;
|
|
30
|
+
}
|
|
31
|
+
interface XRPLRequestConfig {
|
|
32
|
+
apiKey: string;
|
|
33
|
+
baseUrl?: string;
|
|
34
|
+
}
|
|
35
|
+
interface CreateWebhookOptions {
|
|
36
|
+
url: string;
|
|
37
|
+
events?: Array<"payload.signed" | "payload.rejected" | "payload.expired">;
|
|
38
|
+
}
|
|
39
|
+
interface Webhook {
|
|
40
|
+
id: string;
|
|
41
|
+
url: string;
|
|
42
|
+
events: string[];
|
|
43
|
+
active: boolean;
|
|
44
|
+
createdAt: string;
|
|
45
|
+
}
|
|
46
|
+
interface Analytics {
|
|
47
|
+
period: string;
|
|
48
|
+
totals: {
|
|
49
|
+
total: number;
|
|
50
|
+
signed: number;
|
|
51
|
+
rejected: number;
|
|
52
|
+
expired: number;
|
|
53
|
+
pending: number;
|
|
54
|
+
};
|
|
55
|
+
walletBreakdown: Array<{
|
|
56
|
+
walletAdapter: string | null;
|
|
57
|
+
count: number;
|
|
58
|
+
}>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
declare class PayloadsClient {
|
|
62
|
+
private readonly apiKey;
|
|
63
|
+
private readonly baseUrl;
|
|
64
|
+
constructor(apiKey: string, baseUrl: string);
|
|
65
|
+
private get headers();
|
|
66
|
+
create(options: CreatePayloadOptions): Promise<Payload>;
|
|
67
|
+
get(uuid: string): Promise<Payload>;
|
|
68
|
+
cancel(uuid: string): Promise<Payload>;
|
|
69
|
+
list(options?: {
|
|
70
|
+
limit?: number;
|
|
71
|
+
offset?: number;
|
|
72
|
+
}): Promise<{
|
|
73
|
+
payloads: Payload[];
|
|
74
|
+
}>;
|
|
75
|
+
poll(uuid: string, options?: PollOptions): Promise<Payload>;
|
|
76
|
+
}
|
|
77
|
+
declare class XRPLRequestError extends Error {
|
|
78
|
+
readonly statusCode: number;
|
|
79
|
+
constructor(message: string, statusCode: number);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Verify an incoming webhook signature from XRPL Request.
|
|
84
|
+
* Use this in your webhook endpoint to confirm the request is authentic.
|
|
85
|
+
*
|
|
86
|
+
* @param secret - The webhook secret shown when the webhook was created
|
|
87
|
+
* @param rawBody - The raw request body string (before JSON.parse)
|
|
88
|
+
* @param signature - The value of the X-XRPL-Request-Signature header
|
|
89
|
+
*/
|
|
90
|
+
declare function verifyWebhookSignature(secret: string, rawBody: string, signature: string): boolean;
|
|
91
|
+
|
|
92
|
+
declare class WebhooksClient {
|
|
93
|
+
private readonly apiKey;
|
|
94
|
+
private readonly baseUrl;
|
|
95
|
+
constructor(apiKey: string, baseUrl: string);
|
|
96
|
+
private get headers();
|
|
97
|
+
create(options: CreateWebhookOptions): Promise<Webhook & {
|
|
98
|
+
secret: string;
|
|
99
|
+
}>;
|
|
100
|
+
list(): Promise<{
|
|
101
|
+
webhooks: Webhook[];
|
|
102
|
+
}>;
|
|
103
|
+
delete(id: string): Promise<{
|
|
104
|
+
id: string;
|
|
105
|
+
deleted: boolean;
|
|
106
|
+
}>;
|
|
107
|
+
/** Verify an incoming webhook signature. Convenience alias for `verifyWebhookSignature`. */
|
|
108
|
+
static verify: typeof verifyWebhookSignature;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
declare class XRPLRequest {
|
|
112
|
+
readonly payloads: PayloadsClient;
|
|
113
|
+
readonly webhooks: WebhooksClient;
|
|
114
|
+
constructor(config: XRPLRequestConfig);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export { type Analytics, type CreatePayloadOptions, type CreateWebhookOptions, type Payload, type PayloadStatus, type PayloadType, type PollOptions, type Webhook, WebhooksClient, XRPLRequest, type XRPLRequestConfig, XRPLRequestError, verifyWebhookSignature };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
type PayloadType = "connect" | "sign" | "signAndSubmit" | "signMessage";
|
|
2
|
+
type PayloadStatus = "pending" | "signed" | "rejected" | "expired";
|
|
3
|
+
interface CreatePayloadOptions {
|
|
4
|
+
type: PayloadType;
|
|
5
|
+
transaction?: Record<string, unknown>;
|
|
6
|
+
message?: string;
|
|
7
|
+
options?: {
|
|
8
|
+
expiresIn?: number;
|
|
9
|
+
returnUrl?: string;
|
|
10
|
+
customInstructions?: string;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
interface Payload {
|
|
14
|
+
uuid: string;
|
|
15
|
+
type: PayloadType;
|
|
16
|
+
status: PayloadStatus;
|
|
17
|
+
network: "mainnet" | "testnet";
|
|
18
|
+
signingUrl: string;
|
|
19
|
+
expiresAt: string;
|
|
20
|
+
createdAt: string;
|
|
21
|
+
signerAddress?: string | null;
|
|
22
|
+
txHash?: string | null;
|
|
23
|
+
walletAdapter?: string | null;
|
|
24
|
+
resolvedAt?: string | null;
|
|
25
|
+
}
|
|
26
|
+
interface PollOptions {
|
|
27
|
+
timeout?: number;
|
|
28
|
+
interval?: number;
|
|
29
|
+
onUpdate?: (payload: Payload) => void;
|
|
30
|
+
}
|
|
31
|
+
interface XRPLRequestConfig {
|
|
32
|
+
apiKey: string;
|
|
33
|
+
baseUrl?: string;
|
|
34
|
+
}
|
|
35
|
+
interface CreateWebhookOptions {
|
|
36
|
+
url: string;
|
|
37
|
+
events?: Array<"payload.signed" | "payload.rejected" | "payload.expired">;
|
|
38
|
+
}
|
|
39
|
+
interface Webhook {
|
|
40
|
+
id: string;
|
|
41
|
+
url: string;
|
|
42
|
+
events: string[];
|
|
43
|
+
active: boolean;
|
|
44
|
+
createdAt: string;
|
|
45
|
+
}
|
|
46
|
+
interface Analytics {
|
|
47
|
+
period: string;
|
|
48
|
+
totals: {
|
|
49
|
+
total: number;
|
|
50
|
+
signed: number;
|
|
51
|
+
rejected: number;
|
|
52
|
+
expired: number;
|
|
53
|
+
pending: number;
|
|
54
|
+
};
|
|
55
|
+
walletBreakdown: Array<{
|
|
56
|
+
walletAdapter: string | null;
|
|
57
|
+
count: number;
|
|
58
|
+
}>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
declare class PayloadsClient {
|
|
62
|
+
private readonly apiKey;
|
|
63
|
+
private readonly baseUrl;
|
|
64
|
+
constructor(apiKey: string, baseUrl: string);
|
|
65
|
+
private get headers();
|
|
66
|
+
create(options: CreatePayloadOptions): Promise<Payload>;
|
|
67
|
+
get(uuid: string): Promise<Payload>;
|
|
68
|
+
cancel(uuid: string): Promise<Payload>;
|
|
69
|
+
list(options?: {
|
|
70
|
+
limit?: number;
|
|
71
|
+
offset?: number;
|
|
72
|
+
}): Promise<{
|
|
73
|
+
payloads: Payload[];
|
|
74
|
+
}>;
|
|
75
|
+
poll(uuid: string, options?: PollOptions): Promise<Payload>;
|
|
76
|
+
}
|
|
77
|
+
declare class XRPLRequestError extends Error {
|
|
78
|
+
readonly statusCode: number;
|
|
79
|
+
constructor(message: string, statusCode: number);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Verify an incoming webhook signature from XRPL Request.
|
|
84
|
+
* Use this in your webhook endpoint to confirm the request is authentic.
|
|
85
|
+
*
|
|
86
|
+
* @param secret - The webhook secret shown when the webhook was created
|
|
87
|
+
* @param rawBody - The raw request body string (before JSON.parse)
|
|
88
|
+
* @param signature - The value of the X-XRPL-Request-Signature header
|
|
89
|
+
*/
|
|
90
|
+
declare function verifyWebhookSignature(secret: string, rawBody: string, signature: string): boolean;
|
|
91
|
+
|
|
92
|
+
declare class WebhooksClient {
|
|
93
|
+
private readonly apiKey;
|
|
94
|
+
private readonly baseUrl;
|
|
95
|
+
constructor(apiKey: string, baseUrl: string);
|
|
96
|
+
private get headers();
|
|
97
|
+
create(options: CreateWebhookOptions): Promise<Webhook & {
|
|
98
|
+
secret: string;
|
|
99
|
+
}>;
|
|
100
|
+
list(): Promise<{
|
|
101
|
+
webhooks: Webhook[];
|
|
102
|
+
}>;
|
|
103
|
+
delete(id: string): Promise<{
|
|
104
|
+
id: string;
|
|
105
|
+
deleted: boolean;
|
|
106
|
+
}>;
|
|
107
|
+
/** Verify an incoming webhook signature. Convenience alias for `verifyWebhookSignature`. */
|
|
108
|
+
static verify: typeof verifyWebhookSignature;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
declare class XRPLRequest {
|
|
112
|
+
readonly payloads: PayloadsClient;
|
|
113
|
+
readonly webhooks: WebhooksClient;
|
|
114
|
+
constructor(config: XRPLRequestConfig);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export { type Analytics, type CreatePayloadOptions, type CreateWebhookOptions, type Payload, type PayloadStatus, type PayloadType, type PollOptions, type Webhook, WebhooksClient, XRPLRequest, type XRPLRequestConfig, XRPLRequestError, verifyWebhookSignature };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var crypto = require('crypto');
|
|
4
|
+
|
|
5
|
+
// src/api-error.ts
|
|
6
|
+
function apiErrorMessage(body, fallback) {
|
|
7
|
+
if (typeof body === "object" && body !== null && "error" in body && typeof body.error === "string") {
|
|
8
|
+
return body.error;
|
|
9
|
+
}
|
|
10
|
+
return fallback;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// src/payloads.ts
|
|
14
|
+
var DEFAULT_POLL_INTERVAL = 2e3;
|
|
15
|
+
var DEFAULT_POLL_TIMEOUT = 3e5;
|
|
16
|
+
var PayloadsClient = class {
|
|
17
|
+
constructor(apiKey, baseUrl) {
|
|
18
|
+
this.apiKey = apiKey;
|
|
19
|
+
this.baseUrl = baseUrl;
|
|
20
|
+
}
|
|
21
|
+
get headers() {
|
|
22
|
+
return {
|
|
23
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
24
|
+
"Content-Type": "application/json"
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
async create(options) {
|
|
28
|
+
const res = await fetch(`${this.baseUrl}/api/v1/payloads`, {
|
|
29
|
+
method: "POST",
|
|
30
|
+
headers: this.headers,
|
|
31
|
+
body: JSON.stringify(options)
|
|
32
|
+
});
|
|
33
|
+
if (!res.ok) {
|
|
34
|
+
const body = await res.json().catch(() => null);
|
|
35
|
+
throw new XRPLRequestError(apiErrorMessage(body, "Failed to create payload"), res.status);
|
|
36
|
+
}
|
|
37
|
+
const data = await res.json();
|
|
38
|
+
return data;
|
|
39
|
+
}
|
|
40
|
+
async get(uuid) {
|
|
41
|
+
const res = await fetch(`${this.baseUrl}/api/v1/payloads/${uuid}`, {
|
|
42
|
+
headers: this.headers
|
|
43
|
+
});
|
|
44
|
+
if (!res.ok) {
|
|
45
|
+
const body = await res.json().catch(() => null);
|
|
46
|
+
throw new XRPLRequestError(apiErrorMessage(body, "Failed to get payload"), res.status);
|
|
47
|
+
}
|
|
48
|
+
return res.json();
|
|
49
|
+
}
|
|
50
|
+
async cancel(uuid) {
|
|
51
|
+
const res = await fetch(`${this.baseUrl}/api/v1/payloads/${uuid}`, {
|
|
52
|
+
method: "DELETE",
|
|
53
|
+
headers: this.headers
|
|
54
|
+
});
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
const body = await res.json().catch(() => null);
|
|
57
|
+
throw new XRPLRequestError(apiErrorMessage(body, "Failed to cancel payload"), res.status);
|
|
58
|
+
}
|
|
59
|
+
return res.json();
|
|
60
|
+
}
|
|
61
|
+
async list(options) {
|
|
62
|
+
const url = new URL(`${this.baseUrl}/api/v1/payloads`);
|
|
63
|
+
if (options?.limit) url.searchParams.set("limit", String(options.limit));
|
|
64
|
+
if (options?.offset) url.searchParams.set("offset", String(options.offset));
|
|
65
|
+
const res = await fetch(url.toString(), { headers: this.headers });
|
|
66
|
+
if (!res.ok) {
|
|
67
|
+
const body = await res.json().catch(() => null);
|
|
68
|
+
throw new XRPLRequestError(apiErrorMessage(body, "Failed to list payloads"), res.status);
|
|
69
|
+
}
|
|
70
|
+
return res.json();
|
|
71
|
+
}
|
|
72
|
+
async poll(uuid, options) {
|
|
73
|
+
const timeout = options?.timeout ?? DEFAULT_POLL_TIMEOUT;
|
|
74
|
+
const interval = options?.interval ?? DEFAULT_POLL_INTERVAL;
|
|
75
|
+
const deadline = Date.now() + timeout;
|
|
76
|
+
while (Date.now() < deadline) {
|
|
77
|
+
const payload = await this.get(uuid);
|
|
78
|
+
if (options?.onUpdate) options.onUpdate(payload);
|
|
79
|
+
if (payload.status !== "pending") return payload;
|
|
80
|
+
const remaining = deadline - Date.now();
|
|
81
|
+
if (remaining <= 0) break;
|
|
82
|
+
await sleep(Math.min(interval, remaining));
|
|
83
|
+
}
|
|
84
|
+
return this.get(uuid);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
function sleep(ms) {
|
|
88
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
89
|
+
}
|
|
90
|
+
var XRPLRequestError = class extends Error {
|
|
91
|
+
constructor(message, statusCode) {
|
|
92
|
+
super(message);
|
|
93
|
+
this.statusCode = statusCode;
|
|
94
|
+
this.name = "XRPLRequestError";
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
function verifyWebhookSignature(secret, rawBody, signature) {
|
|
98
|
+
if (typeof signature !== "string" || !signature.startsWith("sha256=")) return false;
|
|
99
|
+
const expected = `sha256=${crypto.createHmac("sha256", secret).update(rawBody).digest("hex")}`;
|
|
100
|
+
if (expected.length !== signature.length) return false;
|
|
101
|
+
let diff = 0;
|
|
102
|
+
for (let i = 0; i < expected.length; i++) {
|
|
103
|
+
diff |= expected.charCodeAt(i) ^ signature.charCodeAt(i);
|
|
104
|
+
}
|
|
105
|
+
return diff === 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/webhooks.ts
|
|
109
|
+
var WebhooksClient = class {
|
|
110
|
+
constructor(apiKey, baseUrl) {
|
|
111
|
+
this.apiKey = apiKey;
|
|
112
|
+
this.baseUrl = baseUrl;
|
|
113
|
+
}
|
|
114
|
+
get headers() {
|
|
115
|
+
return {
|
|
116
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
117
|
+
"Content-Type": "application/json"
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
async create(options) {
|
|
121
|
+
const res = await fetch(`${this.baseUrl}/api/v1/webhooks`, {
|
|
122
|
+
method: "POST",
|
|
123
|
+
headers: this.headers,
|
|
124
|
+
body: JSON.stringify(options)
|
|
125
|
+
});
|
|
126
|
+
if (!res.ok) {
|
|
127
|
+
const body = await res.json().catch(() => null);
|
|
128
|
+
throw new XRPLRequestError(apiErrorMessage(body, "Failed to create webhook"), res.status);
|
|
129
|
+
}
|
|
130
|
+
return res.json();
|
|
131
|
+
}
|
|
132
|
+
async list() {
|
|
133
|
+
const res = await fetch(`${this.baseUrl}/api/v1/webhooks`, { headers: this.headers });
|
|
134
|
+
if (!res.ok) {
|
|
135
|
+
const body = await res.json().catch(() => null);
|
|
136
|
+
throw new XRPLRequestError(apiErrorMessage(body, "Failed to list webhooks"), res.status);
|
|
137
|
+
}
|
|
138
|
+
return res.json();
|
|
139
|
+
}
|
|
140
|
+
async delete(id) {
|
|
141
|
+
const res = await fetch(`${this.baseUrl}/api/v1/webhooks/${id}`, {
|
|
142
|
+
method: "DELETE",
|
|
143
|
+
headers: this.headers
|
|
144
|
+
});
|
|
145
|
+
if (!res.ok) {
|
|
146
|
+
const body = await res.json().catch(() => null);
|
|
147
|
+
throw new XRPLRequestError(apiErrorMessage(body, "Failed to delete webhook"), res.status);
|
|
148
|
+
}
|
|
149
|
+
return res.json();
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
/** Verify an incoming webhook signature. Convenience alias for `verifyWebhookSignature`. */
|
|
153
|
+
WebhooksClient.verify = verifyWebhookSignature;
|
|
154
|
+
|
|
155
|
+
// src/index.ts
|
|
156
|
+
var DEFAULT_BASE_URL = "https://xrplre.quest";
|
|
157
|
+
var XRPLRequest = class {
|
|
158
|
+
constructor(config) {
|
|
159
|
+
if (!config.apiKey) throw new Error("apiKey is required");
|
|
160
|
+
const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
161
|
+
this.payloads = new PayloadsClient(config.apiKey, baseUrl);
|
|
162
|
+
this.webhooks = new WebhooksClient(config.apiKey, baseUrl);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
exports.WebhooksClient = WebhooksClient;
|
|
167
|
+
exports.XRPLRequest = XRPLRequest;
|
|
168
|
+
exports.XRPLRequestError = XRPLRequestError;
|
|
169
|
+
exports.verifyWebhookSignature = verifyWebhookSignature;
|
|
170
|
+
//# sourceMappingURL=index.js.map
|
|
171
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api-error.ts","../src/payloads.ts","../src/utils.ts","../src/webhooks.ts","../src/index.ts"],"names":["createHmac"],"mappings":"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","file":"index.js","sourcesContent":["export function apiErrorMessage(body: unknown, fallback: string): string {\n if (\n typeof body === \"object\" &&\n body !== null &&\n \"error\" in body &&\n typeof (body as { error: unknown }).error === \"string\"\n ) {\n return (body as { error: string }).error;\n }\n return fallback;\n}\n","import { apiErrorMessage } from \"./api-error\";\nimport type { CreatePayloadOptions, Payload, PollOptions } from \"./types\";\n\nconst DEFAULT_BASE_URL = \"https://xrplre.quest\";\nconst DEFAULT_POLL_INTERVAL = 2000;\nconst DEFAULT_POLL_TIMEOUT = 300_000;\n\nexport class PayloadsClient {\n constructor(\n private readonly apiKey: string,\n private readonly baseUrl: string\n ) {}\n\n private get headers() {\n return {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n };\n }\n\n async create(options: CreatePayloadOptions): Promise<Payload> {\n const res = await fetch(`${this.baseUrl}/api/v1/payloads`, {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify(options),\n });\n\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to create payload\"), res.status);\n }\n\n const data = await res.json();\n return data as Payload;\n }\n\n async get(uuid: string): Promise<Payload> {\n const res = await fetch(`${this.baseUrl}/api/v1/payloads/${uuid}`, {\n headers: this.headers,\n });\n\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to get payload\"), res.status);\n }\n\n return res.json() as Promise<Payload>;\n }\n\n async cancel(uuid: string): Promise<Payload> {\n const res = await fetch(`${this.baseUrl}/api/v1/payloads/${uuid}`, {\n method: \"DELETE\",\n headers: this.headers,\n });\n\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to cancel payload\"), res.status);\n }\n\n return res.json() as Promise<Payload>;\n }\n\n async list(options?: { limit?: number; offset?: number }): Promise<{ payloads: Payload[] }> {\n const url = new URL(`${this.baseUrl}/api/v1/payloads`);\n if (options?.limit) url.searchParams.set(\"limit\", String(options.limit));\n if (options?.offset) url.searchParams.set(\"offset\", String(options.offset));\n\n const res = await fetch(url.toString(), { headers: this.headers });\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to list payloads\"), res.status);\n }\n\n return res.json() as Promise<{ payloads: Payload[] }>;\n }\n\n async poll(uuid: string, options?: PollOptions): Promise<Payload> {\n const timeout = options?.timeout ?? DEFAULT_POLL_TIMEOUT;\n const interval = options?.interval ?? DEFAULT_POLL_INTERVAL;\n const deadline = Date.now() + timeout;\n\n while (Date.now() < deadline) {\n const payload = await this.get(uuid);\n\n if (options?.onUpdate) options.onUpdate(payload);\n\n if (payload.status !== \"pending\") return payload;\n\n const remaining = deadline - Date.now();\n if (remaining <= 0) break;\n\n await sleep(Math.min(interval, remaining));\n }\n\n // Final check\n return this.get(uuid);\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport class XRPLRequestError extends Error {\n constructor(message: string, public readonly statusCode: number) {\n super(message);\n this.name = \"XRPLRequestError\";\n }\n}\n","import { createHmac } from \"crypto\";\n\n/**\n * Verify an incoming webhook signature from XRPL Request.\n * Use this in your webhook endpoint to confirm the request is authentic.\n *\n * @param secret - The webhook secret shown when the webhook was created\n * @param rawBody - The raw request body string (before JSON.parse)\n * @param signature - The value of the X-XRPL-Request-Signature header\n */\nexport function verifyWebhookSignature(\n secret: string,\n rawBody: string,\n signature: string,\n): boolean {\n if (typeof signature !== \"string\" || !signature.startsWith(\"sha256=\")) return false;\n const expected = `sha256=${createHmac(\"sha256\", secret).update(rawBody).digest(\"hex\")}`;\n if (expected.length !== signature.length) return false;\n // Constant-time comparison\n let diff = 0;\n for (let i = 0; i < expected.length; i++) {\n diff |= expected.charCodeAt(i) ^ signature.charCodeAt(i);\n }\n return diff === 0;\n}\n","import { apiErrorMessage } from \"./api-error\";\nimport type { CreateWebhookOptions, Webhook } from \"./types\";\nimport { XRPLRequestError } from \"./payloads\";\nimport { verifyWebhookSignature } from \"./utils\";\n\nexport class WebhooksClient {\n constructor(\n private readonly apiKey: string,\n private readonly baseUrl: string,\n ) {}\n\n private get headers() {\n return {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n };\n }\n\n async create(options: CreateWebhookOptions): Promise<Webhook & { secret: string }> {\n const res = await fetch(`${this.baseUrl}/api/v1/webhooks`, {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify(options),\n });\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to create webhook\"), res.status);\n }\n return res.json() as Promise<Webhook & { secret: string }>;\n }\n\n async list(): Promise<{ webhooks: Webhook[] }> {\n const res = await fetch(`${this.baseUrl}/api/v1/webhooks`, { headers: this.headers });\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to list webhooks\"), res.status);\n }\n return res.json() as Promise<{ webhooks: Webhook[] }>;\n }\n\n async delete(id: string): Promise<{ id: string; deleted: boolean }> {\n const res = await fetch(`${this.baseUrl}/api/v1/webhooks/${id}`, {\n method: \"DELETE\",\n headers: this.headers,\n });\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to delete webhook\"), res.status);\n }\n return res.json() as Promise<{ id: string; deleted: boolean }>;\n }\n\n /** Verify an incoming webhook signature. Convenience alias for `verifyWebhookSignature`. */\n static verify = verifyWebhookSignature;\n}\n","import { PayloadsClient, XRPLRequestError } from \"./payloads\";\nimport { WebhooksClient } from \"./webhooks\";\nimport type { XRPLRequestConfig } from \"./types\";\n\nexport * from \"./types\";\nexport { XRPLRequestError } from \"./payloads\";\nexport { WebhooksClient } from \"./webhooks\";\nexport { verifyWebhookSignature } from \"./utils\";\n\nconst DEFAULT_BASE_URL = \"https://xrplre.quest\";\n\nexport class XRPLRequest {\n public readonly payloads: PayloadsClient;\n public readonly webhooks: WebhooksClient;\n\n constructor(config: XRPLRequestConfig) {\n if (!config.apiKey) throw new Error(\"apiKey is required\");\n const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, \"\");\n this.payloads = new PayloadsClient(config.apiKey, baseUrl);\n this.webhooks = new WebhooksClient(config.apiKey, baseUrl);\n }\n}\n"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { createHmac } from 'crypto';
|
|
2
|
+
|
|
3
|
+
// src/api-error.ts
|
|
4
|
+
function apiErrorMessage(body, fallback) {
|
|
5
|
+
if (typeof body === "object" && body !== null && "error" in body && typeof body.error === "string") {
|
|
6
|
+
return body.error;
|
|
7
|
+
}
|
|
8
|
+
return fallback;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// src/payloads.ts
|
|
12
|
+
var DEFAULT_POLL_INTERVAL = 2e3;
|
|
13
|
+
var DEFAULT_POLL_TIMEOUT = 3e5;
|
|
14
|
+
var PayloadsClient = class {
|
|
15
|
+
constructor(apiKey, baseUrl) {
|
|
16
|
+
this.apiKey = apiKey;
|
|
17
|
+
this.baseUrl = baseUrl;
|
|
18
|
+
}
|
|
19
|
+
get headers() {
|
|
20
|
+
return {
|
|
21
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
22
|
+
"Content-Type": "application/json"
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
async create(options) {
|
|
26
|
+
const res = await fetch(`${this.baseUrl}/api/v1/payloads`, {
|
|
27
|
+
method: "POST",
|
|
28
|
+
headers: this.headers,
|
|
29
|
+
body: JSON.stringify(options)
|
|
30
|
+
});
|
|
31
|
+
if (!res.ok) {
|
|
32
|
+
const body = await res.json().catch(() => null);
|
|
33
|
+
throw new XRPLRequestError(apiErrorMessage(body, "Failed to create payload"), res.status);
|
|
34
|
+
}
|
|
35
|
+
const data = await res.json();
|
|
36
|
+
return data;
|
|
37
|
+
}
|
|
38
|
+
async get(uuid) {
|
|
39
|
+
const res = await fetch(`${this.baseUrl}/api/v1/payloads/${uuid}`, {
|
|
40
|
+
headers: this.headers
|
|
41
|
+
});
|
|
42
|
+
if (!res.ok) {
|
|
43
|
+
const body = await res.json().catch(() => null);
|
|
44
|
+
throw new XRPLRequestError(apiErrorMessage(body, "Failed to get payload"), res.status);
|
|
45
|
+
}
|
|
46
|
+
return res.json();
|
|
47
|
+
}
|
|
48
|
+
async cancel(uuid) {
|
|
49
|
+
const res = await fetch(`${this.baseUrl}/api/v1/payloads/${uuid}`, {
|
|
50
|
+
method: "DELETE",
|
|
51
|
+
headers: this.headers
|
|
52
|
+
});
|
|
53
|
+
if (!res.ok) {
|
|
54
|
+
const body = await res.json().catch(() => null);
|
|
55
|
+
throw new XRPLRequestError(apiErrorMessage(body, "Failed to cancel payload"), res.status);
|
|
56
|
+
}
|
|
57
|
+
return res.json();
|
|
58
|
+
}
|
|
59
|
+
async list(options) {
|
|
60
|
+
const url = new URL(`${this.baseUrl}/api/v1/payloads`);
|
|
61
|
+
if (options?.limit) url.searchParams.set("limit", String(options.limit));
|
|
62
|
+
if (options?.offset) url.searchParams.set("offset", String(options.offset));
|
|
63
|
+
const res = await fetch(url.toString(), { headers: this.headers });
|
|
64
|
+
if (!res.ok) {
|
|
65
|
+
const body = await res.json().catch(() => null);
|
|
66
|
+
throw new XRPLRequestError(apiErrorMessage(body, "Failed to list payloads"), res.status);
|
|
67
|
+
}
|
|
68
|
+
return res.json();
|
|
69
|
+
}
|
|
70
|
+
async poll(uuid, options) {
|
|
71
|
+
const timeout = options?.timeout ?? DEFAULT_POLL_TIMEOUT;
|
|
72
|
+
const interval = options?.interval ?? DEFAULT_POLL_INTERVAL;
|
|
73
|
+
const deadline = Date.now() + timeout;
|
|
74
|
+
while (Date.now() < deadline) {
|
|
75
|
+
const payload = await this.get(uuid);
|
|
76
|
+
if (options?.onUpdate) options.onUpdate(payload);
|
|
77
|
+
if (payload.status !== "pending") return payload;
|
|
78
|
+
const remaining = deadline - Date.now();
|
|
79
|
+
if (remaining <= 0) break;
|
|
80
|
+
await sleep(Math.min(interval, remaining));
|
|
81
|
+
}
|
|
82
|
+
return this.get(uuid);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
function sleep(ms) {
|
|
86
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
87
|
+
}
|
|
88
|
+
var XRPLRequestError = class extends Error {
|
|
89
|
+
constructor(message, statusCode) {
|
|
90
|
+
super(message);
|
|
91
|
+
this.statusCode = statusCode;
|
|
92
|
+
this.name = "XRPLRequestError";
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
function verifyWebhookSignature(secret, rawBody, signature) {
|
|
96
|
+
if (typeof signature !== "string" || !signature.startsWith("sha256=")) return false;
|
|
97
|
+
const expected = `sha256=${createHmac("sha256", secret).update(rawBody).digest("hex")}`;
|
|
98
|
+
if (expected.length !== signature.length) return false;
|
|
99
|
+
let diff = 0;
|
|
100
|
+
for (let i = 0; i < expected.length; i++) {
|
|
101
|
+
diff |= expected.charCodeAt(i) ^ signature.charCodeAt(i);
|
|
102
|
+
}
|
|
103
|
+
return diff === 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/webhooks.ts
|
|
107
|
+
var WebhooksClient = class {
|
|
108
|
+
constructor(apiKey, baseUrl) {
|
|
109
|
+
this.apiKey = apiKey;
|
|
110
|
+
this.baseUrl = baseUrl;
|
|
111
|
+
}
|
|
112
|
+
get headers() {
|
|
113
|
+
return {
|
|
114
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
115
|
+
"Content-Type": "application/json"
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
async create(options) {
|
|
119
|
+
const res = await fetch(`${this.baseUrl}/api/v1/webhooks`, {
|
|
120
|
+
method: "POST",
|
|
121
|
+
headers: this.headers,
|
|
122
|
+
body: JSON.stringify(options)
|
|
123
|
+
});
|
|
124
|
+
if (!res.ok) {
|
|
125
|
+
const body = await res.json().catch(() => null);
|
|
126
|
+
throw new XRPLRequestError(apiErrorMessage(body, "Failed to create webhook"), res.status);
|
|
127
|
+
}
|
|
128
|
+
return res.json();
|
|
129
|
+
}
|
|
130
|
+
async list() {
|
|
131
|
+
const res = await fetch(`${this.baseUrl}/api/v1/webhooks`, { headers: this.headers });
|
|
132
|
+
if (!res.ok) {
|
|
133
|
+
const body = await res.json().catch(() => null);
|
|
134
|
+
throw new XRPLRequestError(apiErrorMessage(body, "Failed to list webhooks"), res.status);
|
|
135
|
+
}
|
|
136
|
+
return res.json();
|
|
137
|
+
}
|
|
138
|
+
async delete(id) {
|
|
139
|
+
const res = await fetch(`${this.baseUrl}/api/v1/webhooks/${id}`, {
|
|
140
|
+
method: "DELETE",
|
|
141
|
+
headers: this.headers
|
|
142
|
+
});
|
|
143
|
+
if (!res.ok) {
|
|
144
|
+
const body = await res.json().catch(() => null);
|
|
145
|
+
throw new XRPLRequestError(apiErrorMessage(body, "Failed to delete webhook"), res.status);
|
|
146
|
+
}
|
|
147
|
+
return res.json();
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
/** Verify an incoming webhook signature. Convenience alias for `verifyWebhookSignature`. */
|
|
151
|
+
WebhooksClient.verify = verifyWebhookSignature;
|
|
152
|
+
|
|
153
|
+
// src/index.ts
|
|
154
|
+
var DEFAULT_BASE_URL = "https://xrplre.quest";
|
|
155
|
+
var XRPLRequest = class {
|
|
156
|
+
constructor(config) {
|
|
157
|
+
if (!config.apiKey) throw new Error("apiKey is required");
|
|
158
|
+
const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
159
|
+
this.payloads = new PayloadsClient(config.apiKey, baseUrl);
|
|
160
|
+
this.webhooks = new WebhooksClient(config.apiKey, baseUrl);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export { WebhooksClient, XRPLRequest, XRPLRequestError, verifyWebhookSignature };
|
|
165
|
+
//# sourceMappingURL=index.mjs.map
|
|
166
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api-error.ts","../src/payloads.ts","../src/utils.ts","../src/webhooks.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["export function apiErrorMessage(body: unknown, fallback: string): string {\n if (\n typeof body === \"object\" &&\n body !== null &&\n \"error\" in body &&\n typeof (body as { error: unknown }).error === \"string\"\n ) {\n return (body as { error: string }).error;\n }\n return fallback;\n}\n","import { apiErrorMessage } from \"./api-error\";\nimport type { CreatePayloadOptions, Payload, PollOptions } from \"./types\";\n\nconst DEFAULT_BASE_URL = \"https://xrplre.quest\";\nconst DEFAULT_POLL_INTERVAL = 2000;\nconst DEFAULT_POLL_TIMEOUT = 300_000;\n\nexport class PayloadsClient {\n constructor(\n private readonly apiKey: string,\n private readonly baseUrl: string\n ) {}\n\n private get headers() {\n return {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n };\n }\n\n async create(options: CreatePayloadOptions): Promise<Payload> {\n const res = await fetch(`${this.baseUrl}/api/v1/payloads`, {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify(options),\n });\n\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to create payload\"), res.status);\n }\n\n const data = await res.json();\n return data as Payload;\n }\n\n async get(uuid: string): Promise<Payload> {\n const res = await fetch(`${this.baseUrl}/api/v1/payloads/${uuid}`, {\n headers: this.headers,\n });\n\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to get payload\"), res.status);\n }\n\n return res.json() as Promise<Payload>;\n }\n\n async cancel(uuid: string): Promise<Payload> {\n const res = await fetch(`${this.baseUrl}/api/v1/payloads/${uuid}`, {\n method: \"DELETE\",\n headers: this.headers,\n });\n\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to cancel payload\"), res.status);\n }\n\n return res.json() as Promise<Payload>;\n }\n\n async list(options?: { limit?: number; offset?: number }): Promise<{ payloads: Payload[] }> {\n const url = new URL(`${this.baseUrl}/api/v1/payloads`);\n if (options?.limit) url.searchParams.set(\"limit\", String(options.limit));\n if (options?.offset) url.searchParams.set(\"offset\", String(options.offset));\n\n const res = await fetch(url.toString(), { headers: this.headers });\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to list payloads\"), res.status);\n }\n\n return res.json() as Promise<{ payloads: Payload[] }>;\n }\n\n async poll(uuid: string, options?: PollOptions): Promise<Payload> {\n const timeout = options?.timeout ?? DEFAULT_POLL_TIMEOUT;\n const interval = options?.interval ?? DEFAULT_POLL_INTERVAL;\n const deadline = Date.now() + timeout;\n\n while (Date.now() < deadline) {\n const payload = await this.get(uuid);\n\n if (options?.onUpdate) options.onUpdate(payload);\n\n if (payload.status !== \"pending\") return payload;\n\n const remaining = deadline - Date.now();\n if (remaining <= 0) break;\n\n await sleep(Math.min(interval, remaining));\n }\n\n // Final check\n return this.get(uuid);\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport class XRPLRequestError extends Error {\n constructor(message: string, public readonly statusCode: number) {\n super(message);\n this.name = \"XRPLRequestError\";\n }\n}\n","import { createHmac } from \"crypto\";\n\n/**\n * Verify an incoming webhook signature from XRPL Request.\n * Use this in your webhook endpoint to confirm the request is authentic.\n *\n * @param secret - The webhook secret shown when the webhook was created\n * @param rawBody - The raw request body string (before JSON.parse)\n * @param signature - The value of the X-XRPL-Request-Signature header\n */\nexport function verifyWebhookSignature(\n secret: string,\n rawBody: string,\n signature: string,\n): boolean {\n if (typeof signature !== \"string\" || !signature.startsWith(\"sha256=\")) return false;\n const expected = `sha256=${createHmac(\"sha256\", secret).update(rawBody).digest(\"hex\")}`;\n if (expected.length !== signature.length) return false;\n // Constant-time comparison\n let diff = 0;\n for (let i = 0; i < expected.length; i++) {\n diff |= expected.charCodeAt(i) ^ signature.charCodeAt(i);\n }\n return diff === 0;\n}\n","import { apiErrorMessage } from \"./api-error\";\nimport type { CreateWebhookOptions, Webhook } from \"./types\";\nimport { XRPLRequestError } from \"./payloads\";\nimport { verifyWebhookSignature } from \"./utils\";\n\nexport class WebhooksClient {\n constructor(\n private readonly apiKey: string,\n private readonly baseUrl: string,\n ) {}\n\n private get headers() {\n return {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n };\n }\n\n async create(options: CreateWebhookOptions): Promise<Webhook & { secret: string }> {\n const res = await fetch(`${this.baseUrl}/api/v1/webhooks`, {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify(options),\n });\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to create webhook\"), res.status);\n }\n return res.json() as Promise<Webhook & { secret: string }>;\n }\n\n async list(): Promise<{ webhooks: Webhook[] }> {\n const res = await fetch(`${this.baseUrl}/api/v1/webhooks`, { headers: this.headers });\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to list webhooks\"), res.status);\n }\n return res.json() as Promise<{ webhooks: Webhook[] }>;\n }\n\n async delete(id: string): Promise<{ id: string; deleted: boolean }> {\n const res = await fetch(`${this.baseUrl}/api/v1/webhooks/${id}`, {\n method: \"DELETE\",\n headers: this.headers,\n });\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to delete webhook\"), res.status);\n }\n return res.json() as Promise<{ id: string; deleted: boolean }>;\n }\n\n /** Verify an incoming webhook signature. Convenience alias for `verifyWebhookSignature`. */\n static verify = verifyWebhookSignature;\n}\n","import { PayloadsClient, XRPLRequestError } from \"./payloads\";\nimport { WebhooksClient } from \"./webhooks\";\nimport type { XRPLRequestConfig } from \"./types\";\n\nexport * from \"./types\";\nexport { XRPLRequestError } from \"./payloads\";\nexport { WebhooksClient } from \"./webhooks\";\nexport { verifyWebhookSignature } from \"./utils\";\n\nconst DEFAULT_BASE_URL = \"https://xrplre.quest\";\n\nexport class XRPLRequest {\n public readonly payloads: PayloadsClient;\n public readonly webhooks: WebhooksClient;\n\n constructor(config: XRPLRequestConfig) {\n if (!config.apiKey) throw new Error(\"apiKey is required\");\n const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, \"\");\n this.payloads = new PayloadsClient(config.apiKey, baseUrl);\n this.webhooks = new WebhooksClient(config.apiKey, baseUrl);\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xrplrequest/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TypeScript SDK for XRPL Request — wallet-agnostic XRPL transaction signing",
|
|
5
|
+
"author": "LedgerCraft <hello@xrplre.quest>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"homepage": "https://docs.xrplre.quest/sdk",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/LedgerCraft/XRPL-Request.git",
|
|
11
|
+
"directory": "packages/sdk"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/LedgerCraft/XRPL-Request/issues"
|
|
15
|
+
},
|
|
16
|
+
"main": "./dist/index.js",
|
|
17
|
+
"module": "./dist/index.mjs",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": "./dist/index.mjs",
|
|
23
|
+
"require": "./dist/index.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"README.md"
|
|
29
|
+
],
|
|
30
|
+
"sideEffects": false,
|
|
31
|
+
"keywords": [
|
|
32
|
+
"xrpl",
|
|
33
|
+
"xrp",
|
|
34
|
+
"ripple",
|
|
35
|
+
"signing",
|
|
36
|
+
"wallet",
|
|
37
|
+
"xaman",
|
|
38
|
+
"crossmark",
|
|
39
|
+
"discord",
|
|
40
|
+
"bot",
|
|
41
|
+
"webhook",
|
|
42
|
+
"transaction"
|
|
43
|
+
],
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18"
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public",
|
|
49
|
+
"registry": "https://registry.npmjs.org"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^22.15.0",
|
|
54
|
+
"tsup": "^8.4.0",
|
|
55
|
+
"typescript": "^5.8.3"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
59
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
60
|
+
"type-check": "tsc --noEmit"
|
|
61
|
+
}
|
|
62
|
+
}
|