@v3finance/monitoring-sdk 0.1.0 → 0.2.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/CHANGELOG.md +21 -11
- package/README.md +223 -148
- package/dist/cjs/client.d.ts +23 -42
- package/dist/cjs/client.d.ts.map +1 -1
- package/dist/cjs/client.js +44 -53
- package/dist/cjs/client.js.map +1 -1
- package/dist/cjs/core/errors.d.ts +18 -18
- package/dist/cjs/core/errors.d.ts.map +1 -1
- package/dist/cjs/core/errors.js +17 -17
- package/dist/cjs/core/errors.js.map +1 -1
- package/dist/cjs/core/http.d.ts +22 -26
- package/dist/cjs/core/http.d.ts.map +1 -1
- package/dist/cjs/core/http.js +28 -36
- package/dist/cjs/core/http.js.map +1 -1
- package/dist/cjs/core/pagination.d.ts +6 -7
- package/dist/cjs/core/pagination.d.ts.map +1 -1
- package/dist/cjs/core/pagination.js +1 -1
- package/dist/cjs/core/pagination.js.map +1 -1
- package/dist/cjs/core/scope.d.ts +8 -0
- package/dist/cjs/core/scope.d.ts.map +1 -0
- package/dist/cjs/core/scope.js +3 -0
- package/dist/cjs/core/scope.js.map +1 -0
- package/dist/cjs/generated/types.d.ts +129 -117
- package/dist/cjs/generated/types.d.ts.map +1 -1
- package/dist/cjs/generated/types.js +1 -1
- package/dist/cjs/generated/types.js.map +1 -1
- package/dist/cjs/index.d.ts +6 -6
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +13 -16
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/resources/account.d.ts +12 -51
- package/dist/cjs/resources/account.d.ts.map +1 -1
- package/dist/cjs/resources/account.js +17 -61
- package/dist/cjs/resources/account.js.map +1 -1
- package/dist/cjs/resources/addresses.d.ts +49 -65
- package/dist/cjs/resources/addresses.d.ts.map +1 -1
- package/dist/cjs/resources/addresses.js +60 -69
- package/dist/cjs/resources/addresses.js.map +1 -1
- package/dist/cjs/resources/catalog.d.ts +17 -10
- package/dist/cjs/resources/catalog.d.ts.map +1 -1
- package/dist/cjs/resources/catalog.js +23 -17
- package/dist/cjs/resources/catalog.js.map +1 -1
- package/dist/cjs/resources/deliveries.d.ts +34 -23
- package/dist/cjs/resources/deliveries.d.ts.map +1 -1
- package/dist/cjs/resources/deliveries.js +27 -17
- package/dist/cjs/resources/deliveries.js.map +1 -1
- package/dist/cjs/resources/transactions.d.ts +14 -14
- package/dist/cjs/resources/transactions.d.ts.map +1 -1
- package/dist/cjs/resources/transactions.js +15 -16
- package/dist/cjs/resources/transactions.js.map +1 -1
- package/dist/cjs/resources/webhooks.d.ts +22 -0
- package/dist/cjs/resources/webhooks.d.ts.map +1 -0
- package/dist/cjs/resources/webhooks.js +35 -0
- package/dist/cjs/resources/webhooks.js.map +1 -0
- package/dist/cjs/units.d.ts +11 -11
- package/dist/cjs/units.js +14 -14
- package/dist/cjs/units.js.map +1 -1
- package/dist/cjs/version.d.ts +3 -3
- package/dist/cjs/version.d.ts.map +1 -1
- package/dist/cjs/version.js +3 -3
- package/dist/cjs/version.js.map +1 -1
- package/dist/cjs/webhooks.d.ts +33 -28
- package/dist/cjs/webhooks.d.ts.map +1 -1
- package/dist/cjs/webhooks.js +33 -28
- package/dist/cjs/webhooks.js.map +1 -1
- package/dist/esm/client.d.ts +23 -42
- package/dist/esm/client.d.ts.map +1 -1
- package/dist/esm/client.js +46 -55
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/core/errors.d.ts +18 -18
- package/dist/esm/core/errors.d.ts.map +1 -1
- package/dist/esm/core/errors.js +17 -17
- package/dist/esm/core/errors.js.map +1 -1
- package/dist/esm/core/http.d.ts +22 -26
- package/dist/esm/core/http.d.ts.map +1 -1
- package/dist/esm/core/http.js +28 -36
- package/dist/esm/core/http.js.map +1 -1
- package/dist/esm/core/pagination.d.ts +6 -7
- package/dist/esm/core/pagination.d.ts.map +1 -1
- package/dist/esm/core/pagination.js +1 -1
- package/dist/esm/core/pagination.js.map +1 -1
- package/dist/esm/core/scope.d.ts +8 -0
- package/dist/esm/core/scope.d.ts.map +1 -0
- package/dist/esm/core/scope.js +2 -0
- package/dist/esm/core/scope.js.map +1 -0
- package/dist/esm/generated/types.d.ts +129 -117
- package/dist/esm/generated/types.d.ts.map +1 -1
- package/dist/esm/generated/types.js +1 -1
- package/dist/esm/generated/types.js.map +1 -1
- package/dist/esm/index.d.ts +6 -6
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +6 -6
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/resources/account.d.ts +12 -51
- package/dist/esm/resources/account.d.ts.map +1 -1
- package/dist/esm/resources/account.js +15 -57
- package/dist/esm/resources/account.js.map +1 -1
- package/dist/esm/resources/addresses.d.ts +49 -65
- package/dist/esm/resources/addresses.d.ts.map +1 -1
- package/dist/esm/resources/addresses.js +60 -69
- package/dist/esm/resources/addresses.js.map +1 -1
- package/dist/esm/resources/catalog.d.ts +17 -10
- package/dist/esm/resources/catalog.d.ts.map +1 -1
- package/dist/esm/resources/catalog.js +23 -17
- package/dist/esm/resources/catalog.js.map +1 -1
- package/dist/esm/resources/deliveries.d.ts +34 -23
- package/dist/esm/resources/deliveries.d.ts.map +1 -1
- package/dist/esm/resources/deliveries.js +27 -17
- package/dist/esm/resources/deliveries.js.map +1 -1
- package/dist/esm/resources/transactions.d.ts +14 -14
- package/dist/esm/resources/transactions.d.ts.map +1 -1
- package/dist/esm/resources/transactions.js +15 -16
- package/dist/esm/resources/transactions.js.map +1 -1
- package/dist/esm/resources/webhooks.d.ts +22 -0
- package/dist/esm/resources/webhooks.d.ts.map +1 -0
- package/dist/esm/resources/webhooks.js +31 -0
- package/dist/esm/resources/webhooks.js.map +1 -0
- package/dist/esm/units.d.ts +11 -11
- package/dist/esm/units.js +14 -14
- package/dist/esm/units.js.map +1 -1
- package/dist/esm/version.d.ts +3 -3
- package/dist/esm/version.d.ts.map +1 -1
- package/dist/esm/version.js +3 -3
- package/dist/esm/version.js.map +1 -1
- package/dist/esm/webhooks.d.ts +33 -28
- package/dist/esm/webhooks.d.ts.map +1 -1
- package/dist/esm/webhooks.js +33 -28
- package/dist/esm/webhooks.js.map +1 -1
- package/package.json +8 -5
- package/src/client.ts +50 -61
- package/src/core/errors.ts +18 -18
- package/src/core/http.ts +45 -55
- package/src/core/pagination.ts +7 -8
- package/src/core/scope.ts +8 -0
- package/src/generated/types.ts +139 -125
- package/src/index.ts +7 -24
- package/src/resources/account.ts +18 -72
- package/src/resources/addresses.ts +84 -109
- package/src/resources/catalog.ts +28 -19
- package/src/resources/deliveries.ts +43 -30
- package/src/resources/transactions.ts +18 -17
- package/src/resources/webhooks.ts +36 -0
- package/src/units.ts +14 -14
- package/src/version.ts +3 -3
- package/src/webhooks.ts +37 -32
- package/dist/cjs/resources/clients.d.ts +0 -84
- package/dist/cjs/resources/clients.d.ts.map +0 -1
- package/dist/cjs/resources/clients.js +0 -102
- package/dist/cjs/resources/clients.js.map +0 -1
- package/dist/esm/resources/clients.d.ts +0 -84
- package/dist/esm/resources/clients.d.ts.map +0 -1
- package/dist/esm/resources/clients.js +0 -97
- package/dist/esm/resources/clients.js.map +0 -1
- package/src/resources/clients.ts +0 -135
package/dist/esm/webhooks.d.ts
CHANGED
|
@@ -1,56 +1,60 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Receiving webhooks: signature verification and parsing. No API key or network access is needed — only your
|
|
3
|
+
* webhook secret. Uses Web Crypto, so it runs on Node.js 20+, Bun, Deno and Cloudflare Workers.
|
|
4
4
|
*/
|
|
5
5
|
import type { WebhookPayload } from './generated/types.js';
|
|
6
|
-
/**
|
|
6
|
+
/** Headers sent with every webhook. */
|
|
7
7
|
export declare const WEBHOOK_HEADERS: {
|
|
8
|
-
/** HMAC-SHA256
|
|
8
|
+
/** HMAC-SHA256 of the raw body keyed with your webhook secret, hex-encoded. */
|
|
9
9
|
readonly signature: 'x-signature';
|
|
10
|
-
/**
|
|
10
|
+
/** Your client ID. */
|
|
11
11
|
readonly clientId: 'x-client-id';
|
|
12
|
-
/** `1`
|
|
12
|
+
/** `1` for test webhooks. */
|
|
13
13
|
readonly test: 'x-webhook-test';
|
|
14
14
|
};
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* The raw request body, exactly as received. Do not parse and re-serialize the JSON: the signature is computed over
|
|
17
|
+
* the original bytes.
|
|
18
18
|
*/
|
|
19
19
|
export type WebhookBody = string | Uint8Array | ArrayBuffer;
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
22
|
-
* (
|
|
21
|
+
* Where to read the signature from: the value of the `x-signature` header, or all request headers — a Fetch
|
|
22
|
+
* `Headers` object (Next.js, Hono, Cloudflare Workers) or a Node.js headers object (`req.headers` in Express, Fastify).
|
|
23
23
|
*/
|
|
24
24
|
export type SignatureSource = string | null | undefined | Headers | Record<string, string | string[] | undefined>;
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
26
|
+
* Computes the signature of a body the way the API does. Useful for testing your webhook handler.
|
|
27
27
|
*
|
|
28
28
|
* @example
|
|
29
29
|
* ```ts
|
|
30
|
-
* const body = JSON.stringify({ tx: {
|
|
31
|
-
* await fetch('http://localhost:3000/webhooks', {
|
|
30
|
+
* const body = JSON.stringify({ tx: { network: 'TRON', asset: 'USDT', … } })
|
|
31
|
+
* await fetch('http://localhost:3000/webhooks/monitoring', {
|
|
32
|
+
* method: 'POST',
|
|
33
|
+
* body,
|
|
34
|
+
* headers: { 'content-type': 'application/json', 'x-signature': await signWebhookBody(body, secret) },
|
|
35
|
+
* })
|
|
32
36
|
* ```
|
|
33
37
|
*/
|
|
34
38
|
export declare function signWebhookBody(body: WebhookBody, secret: string): Promise<string>;
|
|
35
39
|
/**
|
|
36
|
-
*
|
|
40
|
+
* Verifies a webhook signature. Returns `true` when the body was sent by the API and was not modified.
|
|
37
41
|
*
|
|
38
|
-
* @param body -
|
|
39
|
-
* @param signature -
|
|
40
|
-
* @param secret -
|
|
42
|
+
* @param body - Raw request body.
|
|
43
|
+
* @param signature - The `x-signature` header, or all request headers.
|
|
44
|
+
* @param secret - Your webhook secret.
|
|
41
45
|
*/
|
|
42
46
|
export declare function verifyWebhookSignature(body: WebhookBody, signature: SignatureSource, secret: string): Promise<boolean>;
|
|
43
47
|
/**
|
|
44
|
-
*
|
|
48
|
+
* Verifies the signature and returns the parsed webhook.
|
|
45
49
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
+
* Respond with a 2xx status within 15 seconds. Process webhooks idempotently by {@link webhookIdempotencyKey} — the
|
|
51
|
+
* same webhook may arrive more than once — and do not credit test webhooks ({@link isTestWebhook}). Webhooks are sent
|
|
52
|
+
* as soon as a transfer is included in a block; before crediting large amounts, check that the transaction is final
|
|
53
|
+
* with `monitoring.transactions.get()`.
|
|
50
54
|
*
|
|
51
|
-
* @throws {@link WebhookSignatureError}
|
|
55
|
+
* @throws {@link WebhookSignatureError} when the signature does not match. Respond with `401` and do not process it.
|
|
52
56
|
*
|
|
53
|
-
* @example Express
|
|
57
|
+
* @example Express (use the raw body)
|
|
54
58
|
* ```ts
|
|
55
59
|
* app.post('/webhooks/monitoring', express.raw({ type: 'application/json' }), async (req, res) => {
|
|
56
60
|
* const event = await parseWebhook(req.body, req.headers, process.env.MONITORING_WEBHOOK_SECRET!)
|
|
@@ -59,17 +63,18 @@ export declare function verifyWebhookSignature(body: WebhookBody, signature: Sig
|
|
|
59
63
|
* })
|
|
60
64
|
* ```
|
|
61
65
|
*
|
|
62
|
-
* @example
|
|
66
|
+
* @example Next.js route handlers, Hono, Cloudflare Workers
|
|
63
67
|
* ```ts
|
|
64
68
|
* const event = await parseWebhook(await request.text(), request.headers, env.MONITORING_WEBHOOK_SECRET)
|
|
65
69
|
* ```
|
|
66
70
|
*/
|
|
67
71
|
export declare function parseWebhook(body: WebhookBody, signature: SignatureSource, secret: string): Promise<WebhookPayload>;
|
|
68
|
-
/**
|
|
72
|
+
/** `true` for test webhooks. Do not credit them. */
|
|
69
73
|
export declare function isTestWebhook(event: WebhookPayload): boolean;
|
|
70
74
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
75
|
+
* Idempotency key of a webhook: network, transaction hash and log index. A transfer always has the same key, however
|
|
76
|
+
* many times its webhook is delivered. An EVM transaction can contain several token transfers; they differ by
|
|
77
|
+
* `logIndex`.
|
|
73
78
|
*
|
|
74
79
|
* @example `"TRON:1249cb…a5fc45:"`, `"ETHEREUM:0x3fbc…1d57:7"`
|
|
75
80
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../src/webhooks.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAE1D,
|
|
1
|
+
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../src/webhooks.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAE1D,uCAAuC;AACvC,eAAO,MAAM,eAAe;IAC1B,+EAA+E;aAC/E,SAAS,EAAE,aAAa;IACxB,sBAAsB;aACtB,QAAQ,EAAE,aAAa;IACvB,6BAA6B;aAC7B,IAAI,EAAE,gBAAgB;CACd,CAAA;AAEV;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,UAAU,GAAG,WAAW,CAAA;AAE3D;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;AA0BjH;;;;;;;;;;;;GAYG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAIxF;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAI5H;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAWzH;AAED,oDAAoD;AACpD,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAE5D;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAEnE"}
|
package/dist/esm/webhooks.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Receiving webhooks: signature verification and parsing. No API key or network access is needed — only your
|
|
3
|
+
* webhook secret. Uses Web Crypto, so it runs on Node.js 20+, Bun, Deno and Cloudflare Workers.
|
|
4
4
|
*/
|
|
5
5
|
import { MonitoringError, WebhookSignatureError } from './core/errors.js';
|
|
6
|
-
/**
|
|
6
|
+
/** Headers sent with every webhook. */
|
|
7
7
|
export const WEBHOOK_HEADERS = {
|
|
8
|
-
/** HMAC-SHA256
|
|
8
|
+
/** HMAC-SHA256 of the raw body keyed with your webhook secret, hex-encoded. */
|
|
9
9
|
signature: 'x-signature',
|
|
10
|
-
/**
|
|
10
|
+
/** Your client ID. */
|
|
11
11
|
clientId: 'x-client-id',
|
|
12
|
-
/** `1`
|
|
12
|
+
/** `1` for test webhooks. */
|
|
13
13
|
test: 'x-webhook-test',
|
|
14
14
|
};
|
|
15
15
|
const encoder = new TextEncoder();
|
|
16
16
|
function bytesOf(body) {
|
|
17
17
|
if (typeof body === 'string')
|
|
18
18
|
return encoder.encode(body);
|
|
19
|
-
//
|
|
19
|
+
// copy: Web Crypto does not accept views over a SharedArrayBuffer (a Node.js Buffer is a Uint8Array too)
|
|
20
20
|
return body instanceof Uint8Array ? new Uint8Array(body) : new Uint8Array(body);
|
|
21
21
|
}
|
|
22
22
|
function signatureOf(source) {
|
|
@@ -29,7 +29,7 @@ function signatureOf(source) {
|
|
|
29
29
|
const value = source[WEBHOOK_HEADERS.signature];
|
|
30
30
|
return Array.isArray(value) ? value[0] : value;
|
|
31
31
|
}
|
|
32
|
-
/**
|
|
32
|
+
/** Constant-time comparison. */
|
|
33
33
|
function safeEqual(a, b) {
|
|
34
34
|
if (a.length !== b.length)
|
|
35
35
|
return false;
|
|
@@ -39,12 +39,16 @@ function safeEqual(a, b) {
|
|
|
39
39
|
return diff === 0;
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
|
-
*
|
|
42
|
+
* Computes the signature of a body the way the API does. Useful for testing your webhook handler.
|
|
43
43
|
*
|
|
44
44
|
* @example
|
|
45
45
|
* ```ts
|
|
46
|
-
* const body = JSON.stringify({ tx: {
|
|
47
|
-
* await fetch('http://localhost:3000/webhooks', {
|
|
46
|
+
* const body = JSON.stringify({ tx: { network: 'TRON', asset: 'USDT', … } })
|
|
47
|
+
* await fetch('http://localhost:3000/webhooks/monitoring', {
|
|
48
|
+
* method: 'POST',
|
|
49
|
+
* body,
|
|
50
|
+
* headers: { 'content-type': 'application/json', 'x-signature': await signWebhookBody(body, secret) },
|
|
51
|
+
* })
|
|
48
52
|
* ```
|
|
49
53
|
*/
|
|
50
54
|
export async function signWebhookBody(body, secret) {
|
|
@@ -53,11 +57,11 @@ export async function signWebhookBody(body, secret) {
|
|
|
53
57
|
return Array.from(mac, (b) => b.toString(16).padStart(2, '0')).join('');
|
|
54
58
|
}
|
|
55
59
|
/**
|
|
56
|
-
*
|
|
60
|
+
* Verifies a webhook signature. Returns `true` when the body was sent by the API and was not modified.
|
|
57
61
|
*
|
|
58
|
-
* @param body -
|
|
59
|
-
* @param signature -
|
|
60
|
-
* @param secret -
|
|
62
|
+
* @param body - Raw request body.
|
|
63
|
+
* @param signature - The `x-signature` header, or all request headers.
|
|
64
|
+
* @param secret - Your webhook secret.
|
|
61
65
|
*/
|
|
62
66
|
export async function verifyWebhookSignature(body, signature, secret) {
|
|
63
67
|
const given = signatureOf(signature)?.trim().toLowerCase();
|
|
@@ -66,16 +70,16 @@ export async function verifyWebhookSignature(body, signature, secret) {
|
|
|
66
70
|
return safeEqual(await signWebhookBody(body, secret), given);
|
|
67
71
|
}
|
|
68
72
|
/**
|
|
69
|
-
*
|
|
73
|
+
* Verifies the signature and returns the parsed webhook.
|
|
70
74
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
+
* Respond with a 2xx status within 15 seconds. Process webhooks idempotently by {@link webhookIdempotencyKey} — the
|
|
76
|
+
* same webhook may arrive more than once — and do not credit test webhooks ({@link isTestWebhook}). Webhooks are sent
|
|
77
|
+
* as soon as a transfer is included in a block; before crediting large amounts, check that the transaction is final
|
|
78
|
+
* with `monitoring.transactions.get()`.
|
|
75
79
|
*
|
|
76
|
-
* @throws {@link WebhookSignatureError}
|
|
80
|
+
* @throws {@link WebhookSignatureError} when the signature does not match. Respond with `401` and do not process it.
|
|
77
81
|
*
|
|
78
|
-
* @example Express
|
|
82
|
+
* @example Express (use the raw body)
|
|
79
83
|
* ```ts
|
|
80
84
|
* app.post('/webhooks/monitoring', express.raw({ type: 'application/json' }), async (req, res) => {
|
|
81
85
|
* const event = await parseWebhook(req.body, req.headers, process.env.MONITORING_WEBHOOK_SECRET!)
|
|
@@ -84,7 +88,7 @@ export async function verifyWebhookSignature(body, signature, secret) {
|
|
|
84
88
|
* })
|
|
85
89
|
* ```
|
|
86
90
|
*
|
|
87
|
-
* @example
|
|
91
|
+
* @example Next.js route handlers, Hono, Cloudflare Workers
|
|
88
92
|
* ```ts
|
|
89
93
|
* const event = await parseWebhook(await request.text(), request.headers, env.MONITORING_WEBHOOK_SECRET)
|
|
90
94
|
* ```
|
|
@@ -96,21 +100,22 @@ export async function parseWebhook(body, signature, secret) {
|
|
|
96
100
|
try {
|
|
97
101
|
payload = JSON.parse(typeof body === 'string' ? body : new TextDecoder().decode(bytesOf(body)));
|
|
98
102
|
}
|
|
99
|
-
catch (
|
|
100
|
-
throw new MonitoringError('webhook body is not valid JSON', { cause:
|
|
103
|
+
catch (error) {
|
|
104
|
+
throw new MonitoringError('webhook body is not valid JSON', { cause: error });
|
|
101
105
|
}
|
|
102
106
|
const tx = payload?.tx;
|
|
103
107
|
if (typeof tx !== 'object' || tx === null || typeof tx.id !== 'string')
|
|
104
108
|
throw new MonitoringError('webhook body has no tx');
|
|
105
109
|
return payload;
|
|
106
110
|
}
|
|
107
|
-
/**
|
|
111
|
+
/** `true` for test webhooks. Do not credit them. */
|
|
108
112
|
export function isTestWebhook(event) {
|
|
109
113
|
return event.test === true;
|
|
110
114
|
}
|
|
111
115
|
/**
|
|
112
|
-
*
|
|
113
|
-
*
|
|
116
|
+
* Idempotency key of a webhook: network, transaction hash and log index. A transfer always has the same key, however
|
|
117
|
+
* many times its webhook is delivered. An EVM transaction can contain several token transfers; they differ by
|
|
118
|
+
* `logIndex`.
|
|
114
119
|
*
|
|
115
120
|
* @example `"TRON:1249cb…a5fc45:"`, `"ETHEREUM:0x3fbc…1d57:7"`
|
|
116
121
|
*/
|
package/dist/esm/webhooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/webhooks.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AAGzE,
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/webhooks.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@v3finance/monitoring-sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Official TypeScript SDK for the V3 Finance
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Official TypeScript SDK for the V3 Finance Monitoring API — monitor TRON and EVM addresses and receive signed webhooks for every transfer.",
|
|
5
5
|
"keywords": [
|
|
6
|
+
"v3finance",
|
|
7
|
+
"monitoring",
|
|
8
|
+
"webhooks",
|
|
6
9
|
"tron",
|
|
7
10
|
"ethereum",
|
|
8
11
|
"evm",
|
|
9
12
|
"usdt",
|
|
13
|
+
"usdc",
|
|
10
14
|
"trc20",
|
|
11
15
|
"erc20",
|
|
12
|
-
"webhooks",
|
|
13
16
|
"blockchain",
|
|
14
|
-
"monitoring",
|
|
15
17
|
"payments",
|
|
18
|
+
"crypto",
|
|
16
19
|
"sdk",
|
|
17
|
-
"
|
|
20
|
+
"typescript"
|
|
18
21
|
],
|
|
19
22
|
"homepage": "https://monitoring.v3.finance/docs",
|
|
20
23
|
"repository": {
|
package/src/client.ts
CHANGED
|
@@ -1,94 +1,83 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type
|
|
3
|
-
import {
|
|
1
|
+
import { MonitoringError } from './core/errors.js'
|
|
2
|
+
import { HttpClient, seg, type ClientOptions } from './core/http.js'
|
|
3
|
+
import type { Scope } from './core/scope.js'
|
|
4
|
+
import type { Identity } from './generated/types.js'
|
|
5
|
+
import { AccountResource } from './resources/account.js'
|
|
4
6
|
import { AddressesResource } from './resources/addresses.js'
|
|
5
7
|
import { AssetsResource, NetworksResource } from './resources/catalog.js'
|
|
6
|
-
import { ClientsResource } from './resources/clients.js'
|
|
7
8
|
import { DeliveriesResource } from './resources/deliveries.js'
|
|
8
9
|
import { TransactionsResource } from './resources/transactions.js'
|
|
10
|
+
import { WebhooksResource } from './resources/webhooks.js'
|
|
9
11
|
import { DEFAULT_BASE_URL } from './version.js'
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
|
-
*
|
|
13
|
-
* Только для сервера: ключ в браузере увидит любой.
|
|
14
|
+
* Client for the V3 Finance Monitoring API.
|
|
14
15
|
*
|
|
15
|
-
*
|
|
16
|
+
* Use it on the server only: an API key in a browser is visible to anyone.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
16
19
|
* ```ts
|
|
17
20
|
* import { MonitoringClient } from '@v3finance/monitoring-sdk'
|
|
18
21
|
*
|
|
19
|
-
* const monitoring = new MonitoringClient({ apiKey: process.env.MONITORING_API_KEY })
|
|
20
|
-
* await monitoring.addresses.add(clientId, [{ network: 'tron', address: 'T…' }], { assets: ['USDT'], direction: 'in' })
|
|
21
|
-
* ```
|
|
22
|
+
* const monitoring = new MonitoringClient({ apiKey: process.env.MONITORING_API_KEY! })
|
|
22
23
|
*
|
|
23
|
-
*
|
|
24
|
-
* ```ts
|
|
25
|
-
* const { client } = await MonitoringClient.signIn({ email, password })
|
|
26
|
-
* const { key } = await client.account.keys.create({ name: 'ci' })
|
|
24
|
+
* await monitoring.addresses.add([{ network: 'tron', address: 'TA8Hg…' }], { assets: ['USDT'], direction: 'in' })
|
|
27
25
|
* ```
|
|
28
26
|
*/
|
|
29
27
|
export class MonitoringClient {
|
|
30
|
-
/**
|
|
28
|
+
/** Your client: the account your API key belongs to. */
|
|
31
29
|
readonly account: AccountResource
|
|
32
|
-
/**
|
|
33
|
-
readonly auth: AuthResource
|
|
34
|
-
/** Сети */
|
|
35
|
-
readonly networks: NetworksResource
|
|
36
|
-
/** Нативные монеты и токены */
|
|
37
|
-
readonly assets: AssetsResource
|
|
38
|
-
/** Ваш клиент: URL вебхука, секрет, ключи, участники, тестовый вебхук */
|
|
39
|
-
readonly clients: ClientsResource
|
|
40
|
-
/** Адреса на мониторинге и подписка */
|
|
30
|
+
/** Addresses you monitor and their subscriptions. */
|
|
41
31
|
readonly addresses: AddressesResource
|
|
42
|
-
/**
|
|
32
|
+
/** Test your webhook endpoint. */
|
|
33
|
+
readonly webhooks: WebhooksResource
|
|
34
|
+
/** History of webhooks sent to you. */
|
|
43
35
|
readonly deliveries: DeliveriesResource
|
|
44
|
-
/**
|
|
36
|
+
/** Any transaction by hash, with confirmations and finality. */
|
|
45
37
|
readonly transactions: TransactionsResource
|
|
38
|
+
/** Supported networks. */
|
|
39
|
+
readonly networks: NetworksResource
|
|
40
|
+
/** Supported native coins and tokens. */
|
|
41
|
+
readonly assets: AssetsResource
|
|
46
42
|
|
|
47
43
|
readonly #http: HttpClient
|
|
44
|
+
#clientId: Promise<string> | undefined
|
|
48
45
|
|
|
49
46
|
/**
|
|
50
|
-
* @param options -
|
|
51
|
-
* @throws {@link MonitoringError}
|
|
47
|
+
* @param options - API key and optional settings.
|
|
48
|
+
* @throws {@link MonitoringError} when `apiKey` is missing or `fetch` is not available.
|
|
52
49
|
*/
|
|
53
|
-
constructor(options: ClientOptions
|
|
50
|
+
constructor(options: ClientOptions) {
|
|
54
51
|
this.#http = new HttpClient(options, DEFAULT_BASE_URL)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
this.
|
|
58
|
-
this.
|
|
59
|
-
this.
|
|
60
|
-
this.
|
|
61
|
-
this.
|
|
62
|
-
this.
|
|
52
|
+
if (options.clientId) this.#clientId = Promise.resolve(options.clientId)
|
|
53
|
+
const scope: Scope = { http: this.#http, clientPath: async () => `/v1/clients/${seg(await this.#resolveClientId())}` }
|
|
54
|
+
this.account = new AccountResource(scope, () => this.#resolveClientId())
|
|
55
|
+
this.addresses = new AddressesResource(scope)
|
|
56
|
+
this.webhooks = new WebhooksResource(scope)
|
|
57
|
+
this.deliveries = new DeliveriesResource(scope)
|
|
58
|
+
this.transactions = new TransactionsResource(scope)
|
|
59
|
+
this.networks = new NetworksResource(scope)
|
|
60
|
+
this.assets = new AssetsResource(scope)
|
|
63
61
|
}
|
|
64
62
|
|
|
65
|
-
/**
|
|
63
|
+
/** API base URL this client sends requests to. */
|
|
66
64
|
get baseUrl(): string {
|
|
67
65
|
return this.#http.baseUrl
|
|
68
66
|
}
|
|
69
67
|
|
|
70
|
-
/**
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
method: 'POST',
|
|
85
|
-
path: '/api/auth/sign-in/email',
|
|
86
|
-
body: credentials,
|
|
87
|
-
idempotent: false,
|
|
88
|
-
auth: false,
|
|
89
|
-
// повтор при отказе 429 только сожжёт попытки входа
|
|
90
|
-
options: { maxRetries: 0, ...requestOptions },
|
|
91
|
-
})
|
|
92
|
-
return { token: r.token, user: r.user, client: new MonitoringClient({ ...options, sessionToken: r.token }) }
|
|
68
|
+
/** Client ID of the API key: from the options, or resolved once from the API and cached. */
|
|
69
|
+
#resolveClientId(): Promise<string> {
|
|
70
|
+
this.#clientId ??= this.#http.request<Identity>({ method: 'GET', path: '/v1/me', idempotent: true }).then(
|
|
71
|
+
(identity) => {
|
|
72
|
+
if (!identity.actor.clientId) throw new MonitoringError('this API key is not bound to a client: pass clientId in the options')
|
|
73
|
+
return identity.actor.clientId
|
|
74
|
+
},
|
|
75
|
+
(error: unknown) => {
|
|
76
|
+
// a failed lookup is not cached: the next call tries again
|
|
77
|
+
this.#clientId = undefined
|
|
78
|
+
throw error
|
|
79
|
+
},
|
|
80
|
+
)
|
|
81
|
+
return this.#clientId
|
|
93
82
|
}
|
|
94
83
|
}
|
package/src/core/errors.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import type { ApiErrorBody } from '../generated/types.js'
|
|
2
2
|
|
|
3
|
-
/**
|
|
3
|
+
/** A per-item problem of a batch request: the item's index in your request and the reason. */
|
|
4
4
|
export type ErrorDetail = NonNullable<ApiErrorBody['error']['details']>[number]
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Base class of every error thrown by the SDK.
|
|
8
8
|
*
|
|
9
9
|
* @example
|
|
10
10
|
* ```ts
|
|
11
11
|
* try {
|
|
12
|
-
* await monitoring.addresses.add(
|
|
13
|
-
* } catch (
|
|
14
|
-
* if (
|
|
15
|
-
* else throw
|
|
12
|
+
* await monitoring.addresses.add(items)
|
|
13
|
+
* } catch (error) {
|
|
14
|
+
* if (error instanceof MonitoringApiError && error.status === 400) console.log(error.details)
|
|
15
|
+
* else throw error
|
|
16
16
|
* }
|
|
17
17
|
* ```
|
|
18
18
|
*/
|
|
@@ -23,20 +23,20 @@ export class MonitoringError extends Error {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
/** API
|
|
26
|
+
/** The API responded with an error status (4xx or 5xx). */
|
|
27
27
|
export class MonitoringApiError extends MonitoringError {
|
|
28
|
-
/** HTTP
|
|
28
|
+
/** HTTP status code. */
|
|
29
29
|
readonly status: number
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
32
|
-
* `rate_limited`, `upstream_error`, `upstream_rate_limited
|
|
31
|
+
* Machine-readable error code, e.g. `invalid_request`, `unauthorized`, `forbidden`, `not_found`,
|
|
32
|
+
* `rate_limited`, `upstream_error`, `upstream_rate_limited`.
|
|
33
33
|
*/
|
|
34
34
|
readonly code: string
|
|
35
|
-
/**
|
|
35
|
+
/** Per-item problems of a batch request, such as invalid addresses. Empty when there are none. */
|
|
36
36
|
readonly details: ErrorDetail[]
|
|
37
|
-
/**
|
|
37
|
+
/** Request ID. Include it when contacting support. */
|
|
38
38
|
readonly requestId: string | undefined
|
|
39
|
-
/**
|
|
39
|
+
/** Raw response body. */
|
|
40
40
|
readonly body: unknown
|
|
41
41
|
|
|
42
42
|
constructor(init: { status: number; code: string; message: string; details?: ErrorDetail[]; requestId?: string; body?: unknown }) {
|
|
@@ -49,13 +49,13 @@ export class MonitoringApiError extends MonitoringError {
|
|
|
49
49
|
this.body = init.body
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
/**
|
|
52
|
+
/** `true` for transient errors — rate limits and unavailable upstream providers — that can be retried later. */
|
|
53
53
|
get retryable(): boolean {
|
|
54
54
|
return this.status === 429 || this.status === 503
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
/**
|
|
58
|
+
/** The request did not get a response because of a network or connection failure. */
|
|
59
59
|
export class MonitoringConnectionError extends MonitoringError {
|
|
60
60
|
constructor(message: string, options?: { cause?: unknown }) {
|
|
61
61
|
super(message, options)
|
|
@@ -63,7 +63,7 @@ export class MonitoringConnectionError extends MonitoringError {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
/**
|
|
66
|
+
/** The request did not complete within `timeoutMs`. */
|
|
67
67
|
export class MonitoringTimeoutError extends MonitoringConnectionError {
|
|
68
68
|
constructor(timeoutMs: number) {
|
|
69
69
|
super(`request timed out after ${timeoutMs} ms`)
|
|
@@ -71,7 +71,7 @@ export class MonitoringTimeoutError extends MonitoringConnectionError {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
/**
|
|
74
|
+
/** The webhook signature does not match: the body was altered or is verified with the wrong secret. */
|
|
75
75
|
export class WebhookSignatureError extends MonitoringError {
|
|
76
76
|
constructor(message = 'webhook signature mismatch') {
|
|
77
77
|
super(message)
|
|
@@ -79,7 +79,7 @@ export class WebhookSignatureError extends MonitoringError {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
/**
|
|
82
|
+
/** Builds an error from any error body the API returns. */
|
|
83
83
|
export function apiErrorFrom(status: number, body: unknown, requestId: string | undefined): MonitoringApiError {
|
|
84
84
|
const b = (body ?? {}) as Record<string, unknown>
|
|
85
85
|
const v1 = typeof b.error === 'object' && b.error !== null ? (b.error as Record<string, unknown>) : undefined
|