@v3finance/monitoring-sdk 0.0.0-stage → 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/CHANGELOG.md +16 -0
- package/LICENSE +21 -0
- package/README.md +247 -2
- package/dist/cjs/client.d.ts +65 -0
- package/dist/cjs/client.d.ts.map +1 -0
- package/dist/cjs/client.js +89 -0
- package/dist/cjs/client.js.map +1 -0
- package/dist/cjs/core/errors.d.ts +64 -0
- package/dist/cjs/core/errors.d.ts.map +1 -0
- package/dist/cjs/core/errors.js +95 -0
- package/dist/cjs/core/errors.js.map +1 -0
- package/dist/cjs/core/http.d.ts +62 -0
- package/dist/cjs/core/http.d.ts.map +1 -0
- package/dist/cjs/core/http.js +151 -0
- package/dist/cjs/core/http.js.map +1 -0
- package/dist/cjs/core/pagination.d.ts +13 -0
- package/dist/cjs/core/pagination.d.ts.map +1 -0
- package/dist/cjs/core/pagination.js +15 -0
- package/dist/cjs/core/pagination.js.map +1 -0
- package/dist/cjs/generated/types.d.ts +324 -0
- package/dist/cjs/generated/types.d.ts.map +1 -0
- package/dist/cjs/generated/types.js +5 -0
- package/dist/cjs/generated/types.js.map +1 -0
- package/dist/cjs/index.d.ts +23 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +50 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/resources/account.d.ts +61 -0
- package/dist/cjs/resources/account.d.ts.map +1 -0
- package/dist/cjs/resources/account.js +75 -0
- package/dist/cjs/resources/account.js.map +1 -0
- package/dist/cjs/resources/addresses.d.ts +109 -0
- package/dist/cjs/resources/addresses.d.ts.map +1 -0
- package/dist/cjs/resources/addresses.js +137 -0
- package/dist/cjs/resources/addresses.js.map +1 -0
- package/dist/cjs/resources/catalog.d.ts +30 -0
- package/dist/cjs/resources/catalog.d.ts.map +1 -0
- package/dist/cjs/resources/catalog.js +46 -0
- package/dist/cjs/resources/catalog.js.map +1 -0
- package/dist/cjs/resources/clients.d.ts +84 -0
- package/dist/cjs/resources/clients.d.ts.map +1 -0
- package/dist/cjs/resources/clients.js +102 -0
- package/dist/cjs/resources/clients.js.map +1 -0
- package/dist/cjs/resources/deliveries.d.ts +50 -0
- package/dist/cjs/resources/deliveries.d.ts.map +1 -0
- package/dist/cjs/resources/deliveries.js +43 -0
- package/dist/cjs/resources/deliveries.js.map +1 -0
- package/dist/cjs/resources/transactions.d.ts +28 -0
- package/dist/cjs/resources/transactions.d.ts.map +1 -0
- package/dist/cjs/resources/transactions.js +35 -0
- package/dist/cjs/resources/transactions.js.map +1 -0
- package/dist/cjs/units.d.ts +29 -0
- package/dist/cjs/units.d.ts.map +1 -0
- package/dist/cjs/units.js +62 -0
- package/dist/cjs/units.js.map +1 -0
- package/dist/cjs/version.d.ts +5 -0
- package/dist/cjs/version.d.ts.map +1 -0
- package/dist/cjs/version.js +8 -0
- package/dist/cjs/version.js.map +1 -0
- package/dist/cjs/webhooks.d.ts +77 -0
- package/dist/cjs/webhooks.d.ts.map +1 -0
- package/dist/cjs/webhooks.js +128 -0
- package/dist/cjs/webhooks.js.map +1 -0
- package/dist/esm/client.d.ts +65 -0
- package/dist/esm/client.d.ts.map +1 -0
- package/dist/esm/client.js +85 -0
- package/dist/esm/client.js.map +1 -0
- package/dist/esm/core/errors.d.ts +64 -0
- package/dist/esm/core/errors.d.ts.map +1 -0
- package/dist/esm/core/errors.js +86 -0
- package/dist/esm/core/errors.js.map +1 -0
- package/dist/esm/core/http.d.ts +62 -0
- package/dist/esm/core/http.d.ts.map +1 -0
- package/dist/esm/core/http.js +146 -0
- package/dist/esm/core/http.js.map +1 -0
- package/dist/esm/core/pagination.d.ts +13 -0
- package/dist/esm/core/pagination.d.ts.map +1 -0
- package/dist/esm/core/pagination.js +12 -0
- package/dist/esm/core/pagination.js.map +1 -0
- package/dist/esm/generated/types.d.ts +324 -0
- package/dist/esm/generated/types.d.ts.map +1 -0
- package/dist/esm/generated/types.js +4 -0
- package/dist/esm/generated/types.js.map +1 -0
- package/dist/esm/index.d.ts +23 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +20 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/resources/account.d.ts +61 -0
- package/dist/esm/resources/account.d.ts.map +1 -0
- package/dist/esm/resources/account.js +69 -0
- package/dist/esm/resources/account.js.map +1 -0
- package/dist/esm/resources/addresses.d.ts +109 -0
- package/dist/esm/resources/addresses.d.ts.map +1 -0
- package/dist/esm/resources/addresses.js +133 -0
- package/dist/esm/resources/addresses.js.map +1 -0
- package/dist/esm/resources/catalog.d.ts +30 -0
- package/dist/esm/resources/catalog.d.ts.map +1 -0
- package/dist/esm/resources/catalog.js +41 -0
- package/dist/esm/resources/catalog.js.map +1 -0
- package/dist/esm/resources/clients.d.ts +84 -0
- package/dist/esm/resources/clients.d.ts.map +1 -0
- package/dist/esm/resources/clients.js +97 -0
- package/dist/esm/resources/clients.js.map +1 -0
- package/dist/esm/resources/deliveries.d.ts +50 -0
- package/dist/esm/resources/deliveries.d.ts.map +1 -0
- package/dist/esm/resources/deliveries.js +39 -0
- package/dist/esm/resources/deliveries.js.map +1 -0
- package/dist/esm/resources/transactions.d.ts +28 -0
- package/dist/esm/resources/transactions.d.ts.map +1 -0
- package/dist/esm/resources/transactions.js +31 -0
- package/dist/esm/resources/transactions.js.map +1 -0
- package/dist/esm/units.d.ts +29 -0
- package/dist/esm/units.d.ts.map +1 -0
- package/dist/esm/units.js +58 -0
- package/dist/esm/units.js.map +1 -0
- package/dist/esm/version.d.ts +5 -0
- package/dist/esm/version.d.ts.map +1 -0
- package/dist/esm/version.js +5 -0
- package/dist/esm/version.js.map +1 -0
- package/dist/esm/webhooks.d.ts +77 -0
- package/dist/esm/webhooks.d.ts.map +1 -0
- package/dist/esm/webhooks.js +120 -0
- package/dist/esm/webhooks.js.map +1 -0
- package/package.json +66 -4
- package/src/client.ts +94 -0
- package/src/core/errors.ts +99 -0
- package/src/core/http.ts +193 -0
- package/src/core/pagination.ts +21 -0
- package/src/generated/types.ts +355 -0
- package/src/index.ts +60 -0
- package/src/resources/account.ts +87 -0
- package/src/resources/addresses.ts +190 -0
- package/src/resources/catalog.ts +47 -0
- package/src/resources/clients.ts +135 -0
- package/src/resources/deliveries.ts +69 -0
- package/src/resources/transactions.ts +33 -0
- package/src/units.ts +56 -0
- package/src/version.ts +5 -0
- package/src/webhooks.ts +133 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type HttpClient, type RequestOptions } from '../core/http.js';
|
|
2
|
+
import { type Page } from '../core/pagination.js';
|
|
3
|
+
import type { Delivery, DeliveryDetail, DeliveryStatus } from '../generated/types.js';
|
|
4
|
+
/** Фильтры истории; все необязательны и сочетаются */
|
|
5
|
+
export interface DeliveryFilter {
|
|
6
|
+
/** `delivered` — вы ответили 2xx, `retrying` — будет повтор, `failed` — повторы исчерпаны */
|
|
7
|
+
status?: DeliveryStatus;
|
|
8
|
+
/** Сеть: slug или CAIP-2 */
|
|
9
|
+
network?: string;
|
|
10
|
+
/** Символ актива */
|
|
11
|
+
asset?: string;
|
|
12
|
+
/** Хеш транзакции */
|
|
13
|
+
tx?: string;
|
|
14
|
+
/** Отправитель или получатель */
|
|
15
|
+
address?: string;
|
|
16
|
+
/** Первая попытка не раньше (дата или время) */
|
|
17
|
+
since?: Date | string;
|
|
18
|
+
/** Первая попытка раньше (не включая) */
|
|
19
|
+
until?: Date | string;
|
|
20
|
+
}
|
|
21
|
+
export interface ListDeliveriesParams extends DeliveryFilter {
|
|
22
|
+
/** Размер страницы, до 500; по умолчанию 100 */
|
|
23
|
+
limit?: number;
|
|
24
|
+
/** Курсор: `next` прошлой страницы */
|
|
25
|
+
before?: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* История вебхуков клиента: что, когда и куда отправлено, сколько было попыток и что вы ответили.
|
|
29
|
+
* Повторы одного вебхука — одна запись. Хранится 90 дней.
|
|
30
|
+
*/
|
|
31
|
+
export declare class DeliveriesResource {
|
|
32
|
+
#private;
|
|
33
|
+
constructor(http: HttpClient);
|
|
34
|
+
/**
|
|
35
|
+
* Страница истории, новые сверху.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* const { items } = await monitoring.deliveries.list(clientId, { status: 'failed', since: new Date(Date.now() - 86_400_000) })
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
list(clientId: string, params?: ListDeliveriesParams, options?: RequestOptions): Promise<Page<Delivery, number>>;
|
|
43
|
+
/** Вся история по фильтру — от новых к старым, страница за страницей */
|
|
44
|
+
iterate(clientId: string, filter?: DeliveryFilter & {
|
|
45
|
+
limit?: number;
|
|
46
|
+
}, options?: RequestOptions): AsyncGenerator<Delivery, void, undefined>;
|
|
47
|
+
/** Одна запись с URL последней попытки и точным телом запроса */
|
|
48
|
+
get(clientId: string, deliveryId: number, options?: RequestOptions): Promise<DeliveryDetail>;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=deliveries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deliveries.d.ts","sourceRoot":"","sources":["../../../src/resources/deliveries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAC3E,OAAO,EAAY,KAAK,IAAI,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAErF,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC7B,6FAA6F;IAC7F,MAAM,CAAC,EAAE,cAAc,CAAA;IACvB,4BAA4B;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,oBAAoB;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qBAAqB;IACrB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,gDAAgD;IAChD,KAAK,CAAC,EAAE,IAAI,GAAG,MAAM,CAAA;IACrB,yCAAyC;IACzC,KAAK,CAAC,EAAE,IAAI,GAAG,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,oBAAqB,SAAQ,cAAc;IAC1D,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAID;;;GAGG;AACH,qBAAa,kBAAkB;;IAE7B,YAAY,IAAI,EAAE,UAAU,EAE3B;IAED;;;;;;;OAOG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,oBAAyB,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAQnH;IAED,wEAAwE;IACxE,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,cAAc,GAAG;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,cAAc,CAAC,QAAQ,EAAE,IAAI,EAAE,SAAS,CAAC,CAE/I;IAED,iEAAiE;IACjE,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAE3F;CACF"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { seg } from '../core/http.js';
|
|
2
|
+
import { paginate } from '../core/pagination.js';
|
|
3
|
+
const iso = (v) => (v instanceof Date ? v.toISOString() : v);
|
|
4
|
+
/**
|
|
5
|
+
* История вебхуков клиента: что, когда и куда отправлено, сколько было попыток и что вы ответили.
|
|
6
|
+
* Повторы одного вебхука — одна запись. Хранится 90 дней.
|
|
7
|
+
*/
|
|
8
|
+
export class DeliveriesResource {
|
|
9
|
+
#http;
|
|
10
|
+
constructor(http) {
|
|
11
|
+
this.#http = http;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Страница истории, новые сверху.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const { items } = await monitoring.deliveries.list(clientId, { status: 'failed', since: new Date(Date.now() - 86_400_000) })
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
list(clientId, params = {}, options) {
|
|
22
|
+
return this.#http.request({
|
|
23
|
+
method: 'GET',
|
|
24
|
+
path: `/v1/clients/${seg(clientId)}/deliveries`,
|
|
25
|
+
query: { ...params, since: iso(params.since), until: iso(params.until) },
|
|
26
|
+
idempotent: true,
|
|
27
|
+
options,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/** Вся история по фильтру — от новых к старым, страница за страницей */
|
|
31
|
+
iterate(clientId, filter = {}, options) {
|
|
32
|
+
return paginate((before) => this.list(clientId, { ...filter, before }, options));
|
|
33
|
+
}
|
|
34
|
+
/** Одна запись с URL последней попытки и точным телом запроса */
|
|
35
|
+
get(clientId, deliveryId, options) {
|
|
36
|
+
return this.#http.request({ method: 'GET', path: `/v1/clients/${seg(clientId)}/deliveries/${seg(deliveryId)}`, idempotent: true, options });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=deliveries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deliveries.js","sourceRoot":"","sources":["../../../src/resources/deliveries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAwC,MAAM,iBAAiB,CAAA;AAC3E,OAAO,EAAE,QAAQ,EAAa,MAAM,uBAAuB,CAAA;AA4B3D,MAAM,GAAG,GAAG,CAAC,CAA4B,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAEvF;;;GAGG;AACH,MAAM,OAAO,kBAAkB;IACpB,KAAK,CAAY;IAC1B,YAAY,IAAgB;QAC1B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;IACnB,CAAC;IAED;;;;;;;OAOG;IACH,IAAI,CAAC,QAAgB,EAAE,MAAM,GAAyB,EAAE,EAAE,OAAwB;QAChF,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC;YACxB,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,eAAe,GAAG,CAAC,QAAQ,CAAC,aAAa;YAC/C,KAAK,EAAE,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;YACxE,UAAU,EAAE,IAAI;YAChB,OAAO;SACR,CAAC,CAAA;IACJ,CAAC;IAED,wEAAwE;IACxE,OAAO,CAAC,QAAgB,EAAE,MAAM,GAAwC,EAAE,EAAE,OAAwB;QAClG,OAAO,QAAQ,CAAmB,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,CAAC,CAAA;IACpG,CAAC;IAED,iEAAiE;IACjE,GAAG,CAAC,QAAgB,EAAE,UAAkB,EAAE,OAAwB;QAChE,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,eAAe,GAAG,CAAC,QAAQ,CAAC,eAAe,GAAG,CAAC,UAAU,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;IAC7I,CAAC;CACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type HttpClient, type RequestOptions } from '../core/http.js';
|
|
2
|
+
import type { Transaction } from '../generated/types.js';
|
|
3
|
+
/** Любая транзакция сети по хешу — не только те, о которых пришёл вебхук */
|
|
4
|
+
export declare class TransactionsResource {
|
|
5
|
+
#private;
|
|
6
|
+
constructor(http: HttpClient);
|
|
7
|
+
/**
|
|
8
|
+
* Статус, блок, подтверждения, финальность, комиссия и все переводы внутри транзакции; для ваших адресов —
|
|
9
|
+
* направление и вебхуки, которые мы по ней отправили.
|
|
10
|
+
*
|
|
11
|
+
* `final: true` — транзакцию уже не отменить: в TRON блок солидифицирован (около минуты), в EVM —
|
|
12
|
+
* финализирован (Ethereum — около 13 минут). Крупные суммы зачисляйте после `final`.
|
|
13
|
+
*
|
|
14
|
+
* Не больше 60 запросов в минуту на ключ; при превышении SDK подождёт и повторит.
|
|
15
|
+
*
|
|
16
|
+
* @param network - slug (`tron`, `ethereum`) или CAIP-2
|
|
17
|
+
* @param hash - EVM — `0x` + 64 hex; TRON — 64 hex (можно с `0x`)
|
|
18
|
+
* @throws {@link MonitoringApiError} 404 — такой транзакции в сети нет
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* const tx = await monitoring.transactions.get('tron', webhook.tx.id)
|
|
23
|
+
* if (tx.status === 'success' && tx.final) credit(webhook)
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
get(network: string, hash: string, options?: RequestOptions): Promise<Transaction>;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=transactions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transactions.d.ts","sourceRoot":"","sources":["../../../src/resources/transactions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAC3E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAExD,4EAA4E;AAC5E,qBAAa,oBAAoB;;IAE/B,YAAY,IAAI,EAAE,UAAU,EAE3B;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACH,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC,CAEjF;CACF"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { seg } from '../core/http.js';
|
|
2
|
+
/** Любая транзакция сети по хешу — не только те, о которых пришёл вебхук */
|
|
3
|
+
export class TransactionsResource {
|
|
4
|
+
#http;
|
|
5
|
+
constructor(http) {
|
|
6
|
+
this.#http = http;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Статус, блок, подтверждения, финальность, комиссия и все переводы внутри транзакции; для ваших адресов —
|
|
10
|
+
* направление и вебхуки, которые мы по ней отправили.
|
|
11
|
+
*
|
|
12
|
+
* `final: true` — транзакцию уже не отменить: в TRON блок солидифицирован (около минуты), в EVM —
|
|
13
|
+
* финализирован (Ethereum — около 13 минут). Крупные суммы зачисляйте после `final`.
|
|
14
|
+
*
|
|
15
|
+
* Не больше 60 запросов в минуту на ключ; при превышении SDK подождёт и повторит.
|
|
16
|
+
*
|
|
17
|
+
* @param network - slug (`tron`, `ethereum`) или CAIP-2
|
|
18
|
+
* @param hash - EVM — `0x` + 64 hex; TRON — 64 hex (можно с `0x`)
|
|
19
|
+
* @throws {@link MonitoringApiError} 404 — такой транзакции в сети нет
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const tx = await monitoring.transactions.get('tron', webhook.tx.id)
|
|
24
|
+
* if (tx.status === 'success' && tx.final) credit(webhook)
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
get(network, hash, options) {
|
|
28
|
+
return this.#http.request({ method: 'GET', path: `/v1/networks/${seg(network)}/transactions/${seg(hash)}`, idempotent: true, options });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=transactions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transactions.js","sourceRoot":"","sources":["../../../src/resources/transactions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAwC,MAAM,iBAAiB,CAAA;AAG3E,4EAA4E;AAC5E,MAAM,OAAO,oBAAoB;IACtB,KAAK,CAAY;IAC1B,YAAY,IAAgB;QAC1B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;IACnB,CAAC;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACH,GAAG,CAAC,OAAe,EAAE,IAAY,EAAE,OAAwB;QACzD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,gBAAgB,GAAG,CAAC,OAAO,CAAC,iBAAiB,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;IACzI,CAAC;CACF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Суммы в API и вебхуках — строки в минимальных единицах (sun, wei; у USDT 6 знаков после запятой): uint256 не
|
|
3
|
+
* помещается в `number`. Эти функции переводят их в привычный вид и обратно без потери точности.
|
|
4
|
+
* Знаки после запятой актива — `decimals` из `monitoring.assets.list()`.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Минимальные единицы → строка в монетах.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* formatUnits('100000000', 6) // '100' — 100 USDT
|
|
12
|
+
* formatUnits('1500000', 6) // '1.5'
|
|
13
|
+
* formatUnits('10090000000000000', 18) // '0.01009' — ETH
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export declare function formatUnits(value: string | bigint, decimals: number): string;
|
|
17
|
+
/**
|
|
18
|
+
* Строка в монетах → минимальные единицы.
|
|
19
|
+
*
|
|
20
|
+
* @throws RangeError — больше знаков после запятой, чем у актива, или не число
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* parseUnits('1.5', 6) // '1500000'
|
|
25
|
+
* parseUnits('100', 6) // '100000000'
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function parseUnits(amount: string, decimals: number): string;
|
|
29
|
+
//# sourceMappingURL=units.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"units.d.ts","sourceRoot":"","sources":["../../src/units.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAY5E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOnE"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Суммы в API и вебхуках — строки в минимальных единицах (sun, wei; у USDT 6 знаков после запятой): uint256 не
|
|
3
|
+
* помещается в `number`. Эти функции переводят их в привычный вид и обратно без потери точности.
|
|
4
|
+
* Знаки после запятой актива — `decimals` из `monitoring.assets.list()`.
|
|
5
|
+
*/
|
|
6
|
+
const INTEGER = /^-?\d+$/;
|
|
7
|
+
const DECIMAL = /^(\d+)(?:\.(\d+))?$/;
|
|
8
|
+
/**
|
|
9
|
+
* Минимальные единицы → строка в монетах.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* formatUnits('100000000', 6) // '100' — 100 USDT
|
|
14
|
+
* formatUnits('1500000', 6) // '1.5'
|
|
15
|
+
* formatUnits('10090000000000000', 18) // '0.01009' — ETH
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export function formatUnits(value, decimals) {
|
|
19
|
+
assertDecimals(decimals);
|
|
20
|
+
const raw = typeof value === 'bigint' ? value.toString() : value.trim();
|
|
21
|
+
if (!INTEGER.test(raw))
|
|
22
|
+
throw new RangeError(`formatUnits: integer in minimal units expected, got ${JSON.stringify(value)}`);
|
|
23
|
+
const negative = raw.startsWith('-');
|
|
24
|
+
const digits = (negative ? raw.slice(1) : raw).replace(/^0+(?=\d)/, '');
|
|
25
|
+
if (decimals === 0)
|
|
26
|
+
return (negative && digits !== '0' ? '-' : '') + digits;
|
|
27
|
+
const padded = digits.padStart(decimals + 1, '0');
|
|
28
|
+
const whole = padded.slice(0, -decimals);
|
|
29
|
+
const fraction = padded.slice(-decimals).replace(/0+$/, '');
|
|
30
|
+
const sign = negative && (whole !== '0' || fraction) ? '-' : '';
|
|
31
|
+
return `${sign}${whole}${fraction ? `.${fraction}` : ''}`;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Строка в монетах → минимальные единицы.
|
|
35
|
+
*
|
|
36
|
+
* @throws RangeError — больше знаков после запятой, чем у актива, или не число
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* parseUnits('1.5', 6) // '1500000'
|
|
41
|
+
* parseUnits('100', 6) // '100000000'
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export function parseUnits(amount, decimals) {
|
|
45
|
+
assertDecimals(decimals);
|
|
46
|
+
const m = DECIMAL.exec(amount.trim());
|
|
47
|
+
if (!m)
|
|
48
|
+
throw new RangeError(`parseUnits: non-negative decimal expected, got ${JSON.stringify(amount)}`);
|
|
49
|
+
const fraction = m[2] ?? '';
|
|
50
|
+
if (fraction.length > decimals)
|
|
51
|
+
throw new RangeError(`parseUnits: ${amount} has more than ${decimals} decimal places`);
|
|
52
|
+
return BigInt(m[1] + fraction.padEnd(decimals, '0')).toString();
|
|
53
|
+
}
|
|
54
|
+
function assertDecimals(decimals) {
|
|
55
|
+
if (!Number.isInteger(decimals) || decimals < 0 || decimals > 255)
|
|
56
|
+
throw new RangeError(`decimals: integer 0..255 expected, got ${decimals}`);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=units.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"units.js","sourceRoot":"","sources":["../../src/units.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,OAAO,GAAG,SAAS,CAAA;AACzB,MAAM,OAAO,GAAG,qBAAqB,CAAA;AAErC;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CAAC,KAAsB,EAAE,QAAgB;IAClE,cAAc,CAAC,QAAQ,CAAC,CAAA;IACxB,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAA;IACvE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,uDAAuD,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAC5H,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;IACpC,MAAM,MAAM,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;IACvE,IAAI,QAAQ,KAAK,CAAC;QAAE,OAAO,CAAC,QAAQ,IAAI,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAA;IAC3E,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,GAAG,CAAC,EAAE,GAAG,CAAC,CAAA;IACjD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAA;IACxC,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IAC3D,MAAM,IAAI,GAAG,QAAQ,IAAI,CAAC,KAAK,KAAK,GAAG,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;IAC/D,OAAO,GAAG,IAAI,GAAG,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;AAC3D,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,UAAU,CAAC,MAAc,EAAE,QAAgB;IACzD,cAAc,CAAC,QAAQ,CAAC,CAAA;IACxB,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;IACrC,IAAI,CAAC,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,kDAAkD,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IACxG,MAAM,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAC3B,IAAI,QAAQ,CAAC,MAAM,GAAG,QAAQ;QAAE,MAAM,IAAI,UAAU,CAAC,eAAe,MAAM,kBAAkB,QAAQ,iBAAiB,CAAC,CAAA;IACtH,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,CAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAA;AAClE,CAAC;AAED,SAAS,cAAc,CAAC,QAAgB;IACtC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,CAAC,IAAI,QAAQ,GAAG,GAAG;QAAE,MAAM,IAAI,UAAU,CAAC,0CAA0C,QAAQ,EAAE,CAAC,CAAA;AAC/I,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Версия SDK: уходит в заголовке `user-agent`. Тест сверяет её с package.json */
|
|
2
|
+
export declare const SDK_VERSION = "0.1.0";
|
|
3
|
+
/** Адрес API по умолчанию */
|
|
4
|
+
export declare const DEFAULT_BASE_URL = "https://monitoring.v3.finance";
|
|
5
|
+
//# sourceMappingURL=version.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,eAAO,MAAM,WAAW,UAAU,CAAA;AAElC,6BAA6B;AAC7B,eAAO,MAAM,gBAAgB,kCAAkC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAA;AAElC,6BAA6B;AAC7B,MAAM,CAAC,MAAM,gBAAgB,GAAG,+BAA+B,CAAA"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Приём вебхуков: проверка подписи и разбор тела. Работает без клиента API и без сети — нужен только секрет
|
|
3
|
+
* подписи клиента. Использует WebCrypto: Node.js 20+, Bun, Deno, Cloudflare Workers.
|
|
4
|
+
*/
|
|
5
|
+
import type { WebhookPayload } from './generated/types.js';
|
|
6
|
+
/** Заголовки вебхука */
|
|
7
|
+
export declare const WEBHOOK_HEADERS: {
|
|
8
|
+
/** HMAC-SHA256 сырого тела секретом клиента, hex в нижнем регистре */
|
|
9
|
+
readonly signature: 'x-signature';
|
|
10
|
+
/** id клиента */
|
|
11
|
+
readonly clientId: 'x-client-id';
|
|
12
|
+
/** `1` у тестового вебхука — такой не зачислять */
|
|
13
|
+
readonly test: 'x-webhook-test';
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Тело вебхука ровно как пришло — сырые байты или строка. Не `JSON.parse` + `JSON.stringify`: подпись считается
|
|
17
|
+
* по исходным байтам, и любое переформатирование её ломает.
|
|
18
|
+
*/
|
|
19
|
+
export type WebhookBody = string | Uint8Array | ArrayBuffer;
|
|
20
|
+
/**
|
|
21
|
+
* Откуда взять подпись: значение заголовка `x-signature` или все заголовки запроса — Fetch `Headers`
|
|
22
|
+
* (Hono, Next.js, Workers) или объект Node.js (`req.headers` в Express, Fastify).
|
|
23
|
+
*/
|
|
24
|
+
export type SignatureSource = string | null | undefined | Headers | Record<string, string | string[] | undefined>;
|
|
25
|
+
/**
|
|
26
|
+
* Подпись тела секретом клиента — как её считает сервис. Нужна в тестах вашего обработчика вебхуков.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* const body = JSON.stringify({ tx: { …, id: 'test' } })
|
|
31
|
+
* await fetch('http://localhost:3000/webhooks', { method: 'POST', body, headers: { 'x-signature': await signWebhookBody(body, secret) } })
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare function signWebhookBody(body: WebhookBody, secret: string): Promise<string>;
|
|
35
|
+
/**
|
|
36
|
+
* Проверяет подпись вебхука: `true` — тело пришло от сервиса и не изменено.
|
|
37
|
+
*
|
|
38
|
+
* @param body - сырое тело запроса
|
|
39
|
+
* @param signature - заголовок `x-signature` или все заголовки запроса
|
|
40
|
+
* @param secret - секрет подписи клиента
|
|
41
|
+
*/
|
|
42
|
+
export declare function verifyWebhookSignature(body: WebhookBody, signature: SignatureSource, secret: string): Promise<boolean>;
|
|
43
|
+
/**
|
|
44
|
+
* Проверяет подпись и возвращает разобранный вебхук.
|
|
45
|
+
*
|
|
46
|
+
* Дальше — по правилам приёма: тестовый вебхук ({@link isTestWebhook}) не зачислять; один и тот же вебхук может
|
|
47
|
+
* прийти повторно — обрабатывайте идемпотентно по {@link webhookIdempotencyKey}; ответьте 2xx в течение 15 секунд.
|
|
48
|
+
* Вебхук уходит, как только перевод попал в блок, — для крупных сумм дождитесь финальности
|
|
49
|
+
* (`monitoring.transactions.get(...).final`).
|
|
50
|
+
*
|
|
51
|
+
* @throws {@link WebhookSignatureError} подпись не совпала — ответьте 401 и ничего не зачисляйте
|
|
52
|
+
*
|
|
53
|
+
* @example Express — нужен сырой body
|
|
54
|
+
* ```ts
|
|
55
|
+
* app.post('/webhooks/monitoring', express.raw({ type: 'application/json' }), async (req, res) => {
|
|
56
|
+
* const event = await parseWebhook(req.body, req.headers, process.env.MONITORING_WEBHOOK_SECRET!)
|
|
57
|
+
* if (!isTestWebhook(event)) await creditOnce(webhookIdempotencyKey(event), event.tx)
|
|
58
|
+
* res.sendStatus(200)
|
|
59
|
+
* })
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @example Hono, Next.js route handler, Cloudflare Workers
|
|
63
|
+
* ```ts
|
|
64
|
+
* const event = await parseWebhook(await request.text(), request.headers, env.MONITORING_WEBHOOK_SECRET)
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare function parseWebhook(body: WebhookBody, signature: SignatureSource, secret: string): Promise<WebhookPayload>;
|
|
68
|
+
/** Тестовый вебхук (из панели или `clients.sendTestWebhook`) — такой не зачислять */
|
|
69
|
+
export declare function isTestWebhook(event: WebhookPayload): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Ключ идемпотентности вебхука: сеть, транзакция и номер лога. Один перевод — один ключ, даже если вебхук пришёл
|
|
72
|
+
* несколько раз. В одной транзакции EVM может быть несколько переводов токенов — у них разный `logIndex`.
|
|
73
|
+
*
|
|
74
|
+
* @example `"TRON:1249cb…a5fc45:"`, `"ETHEREUM:0x3fbc…1d57:7"`
|
|
75
|
+
*/
|
|
76
|
+
export declare function webhookIdempotencyKey(event: WebhookPayload): string;
|
|
77
|
+
//# sourceMappingURL=webhooks.d.ts.map
|
|
@@ -0,0 +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,wBAAwB;AACxB,eAAO,MAAM,eAAe;IAC1B,sEAAsE;aACtE,SAAS,EAAE,aAAa;IACxB,iBAAiB;aACjB,QAAQ,EAAE,aAAa;IACvB,mDAAmD;aACnD,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;;;;;;;;GAQG;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,qFAAqF;AACrF,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAE5D;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAEnE"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Приём вебхуков: проверка подписи и разбор тела. Работает без клиента API и без сети — нужен только секрет
|
|
3
|
+
* подписи клиента. Использует WebCrypto: Node.js 20+, Bun, Deno, Cloudflare Workers.
|
|
4
|
+
*/
|
|
5
|
+
import { MonitoringError, WebhookSignatureError } from './core/errors.js';
|
|
6
|
+
/** Заголовки вебхука */
|
|
7
|
+
export const WEBHOOK_HEADERS = {
|
|
8
|
+
/** HMAC-SHA256 сырого тела секретом клиента, hex в нижнем регистре */
|
|
9
|
+
signature: 'x-signature',
|
|
10
|
+
/** id клиента */
|
|
11
|
+
clientId: 'x-client-id',
|
|
12
|
+
/** `1` у тестового вебхука — такой не зачислять */
|
|
13
|
+
test: 'x-webhook-test',
|
|
14
|
+
};
|
|
15
|
+
const encoder = new TextEncoder();
|
|
16
|
+
function bytesOf(body) {
|
|
17
|
+
if (typeof body === 'string')
|
|
18
|
+
return encoder.encode(body);
|
|
19
|
+
// копия: WebCrypto не принимает view над SharedArrayBuffer (Buffer в Node.js — тоже Uint8Array)
|
|
20
|
+
return body instanceof Uint8Array ? new Uint8Array(body) : new Uint8Array(body);
|
|
21
|
+
}
|
|
22
|
+
function signatureOf(source) {
|
|
23
|
+
if (source === null || source === undefined)
|
|
24
|
+
return undefined;
|
|
25
|
+
if (typeof source === 'string')
|
|
26
|
+
return source;
|
|
27
|
+
if (typeof source.get === 'function')
|
|
28
|
+
return source.get(WEBHOOK_HEADERS.signature) ?? undefined;
|
|
29
|
+
const value = source[WEBHOOK_HEADERS.signature];
|
|
30
|
+
return Array.isArray(value) ? value[0] : value;
|
|
31
|
+
}
|
|
32
|
+
/** Сравнение без утечки по времени: время не зависит от того, где строки расходятся */
|
|
33
|
+
function safeEqual(a, b) {
|
|
34
|
+
if (a.length !== b.length)
|
|
35
|
+
return false;
|
|
36
|
+
let diff = 0;
|
|
37
|
+
for (let i = 0; i < a.length; i++)
|
|
38
|
+
diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
39
|
+
return diff === 0;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Подпись тела секретом клиента — как её считает сервис. Нужна в тестах вашего обработчика вебхуков.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* const body = JSON.stringify({ tx: { …, id: 'test' } })
|
|
47
|
+
* await fetch('http://localhost:3000/webhooks', { method: 'POST', body, headers: { 'x-signature': await signWebhookBody(body, secret) } })
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export async function signWebhookBody(body, secret) {
|
|
51
|
+
const key = await crypto.subtle.importKey('raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
52
|
+
const mac = new Uint8Array(await crypto.subtle.sign('HMAC', key, bytesOf(body)));
|
|
53
|
+
return Array.from(mac, (b) => b.toString(16).padStart(2, '0')).join('');
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Проверяет подпись вебхука: `true` — тело пришло от сервиса и не изменено.
|
|
57
|
+
*
|
|
58
|
+
* @param body - сырое тело запроса
|
|
59
|
+
* @param signature - заголовок `x-signature` или все заголовки запроса
|
|
60
|
+
* @param secret - секрет подписи клиента
|
|
61
|
+
*/
|
|
62
|
+
export async function verifyWebhookSignature(body, signature, secret) {
|
|
63
|
+
const given = signatureOf(signature)?.trim().toLowerCase();
|
|
64
|
+
if (!given)
|
|
65
|
+
return false;
|
|
66
|
+
return safeEqual(await signWebhookBody(body, secret), given);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Проверяет подпись и возвращает разобранный вебхук.
|
|
70
|
+
*
|
|
71
|
+
* Дальше — по правилам приёма: тестовый вебхук ({@link isTestWebhook}) не зачислять; один и тот же вебхук может
|
|
72
|
+
* прийти повторно — обрабатывайте идемпотентно по {@link webhookIdempotencyKey}; ответьте 2xx в течение 15 секунд.
|
|
73
|
+
* Вебхук уходит, как только перевод попал в блок, — для крупных сумм дождитесь финальности
|
|
74
|
+
* (`monitoring.transactions.get(...).final`).
|
|
75
|
+
*
|
|
76
|
+
* @throws {@link WebhookSignatureError} подпись не совпала — ответьте 401 и ничего не зачисляйте
|
|
77
|
+
*
|
|
78
|
+
* @example Express — нужен сырой body
|
|
79
|
+
* ```ts
|
|
80
|
+
* app.post('/webhooks/monitoring', express.raw({ type: 'application/json' }), async (req, res) => {
|
|
81
|
+
* const event = await parseWebhook(req.body, req.headers, process.env.MONITORING_WEBHOOK_SECRET!)
|
|
82
|
+
* if (!isTestWebhook(event)) await creditOnce(webhookIdempotencyKey(event), event.tx)
|
|
83
|
+
* res.sendStatus(200)
|
|
84
|
+
* })
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* @example Hono, Next.js route handler, Cloudflare Workers
|
|
88
|
+
* ```ts
|
|
89
|
+
* const event = await parseWebhook(await request.text(), request.headers, env.MONITORING_WEBHOOK_SECRET)
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export async function parseWebhook(body, signature, secret) {
|
|
93
|
+
if (!(await verifyWebhookSignature(body, signature, secret)))
|
|
94
|
+
throw new WebhookSignatureError();
|
|
95
|
+
let payload;
|
|
96
|
+
try {
|
|
97
|
+
payload = JSON.parse(typeof body === 'string' ? body : new TextDecoder().decode(bytesOf(body)));
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
throw new MonitoringError('webhook body is not valid JSON', { cause: e });
|
|
101
|
+
}
|
|
102
|
+
const tx = payload?.tx;
|
|
103
|
+
if (typeof tx !== 'object' || tx === null || typeof tx.id !== 'string')
|
|
104
|
+
throw new MonitoringError('webhook body has no tx');
|
|
105
|
+
return payload;
|
|
106
|
+
}
|
|
107
|
+
/** Тестовый вебхук (из панели или `clients.sendTestWebhook`) — такой не зачислять */
|
|
108
|
+
export function isTestWebhook(event) {
|
|
109
|
+
return event.test === true;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Ключ идемпотентности вебхука: сеть, транзакция и номер лога. Один перевод — один ключ, даже если вебхук пришёл
|
|
113
|
+
* несколько раз. В одной транзакции EVM может быть несколько переводов токенов — у них разный `logIndex`.
|
|
114
|
+
*
|
|
115
|
+
* @example `"TRON:1249cb…a5fc45:"`, `"ETHEREUM:0x3fbc…1d57:7"`
|
|
116
|
+
*/
|
|
117
|
+
export function webhookIdempotencyKey(event) {
|
|
118
|
+
return `${event.tx.network}:${event.tx.id}:${event.tx.logIndex ?? ''}`;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=webhooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/webhooks.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,68 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@v3finance/monitoring-sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Official TypeScript SDK for the V3 Finance address monitoring API: TRON and EVM addresses, signed webhooks, delivery history, transaction finality",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"tron",
|
|
7
|
+
"ethereum",
|
|
8
|
+
"evm",
|
|
9
|
+
"usdt",
|
|
10
|
+
"trc20",
|
|
11
|
+
"erc20",
|
|
12
|
+
"webhooks",
|
|
13
|
+
"blockchain",
|
|
14
|
+
"monitoring",
|
|
15
|
+
"payments",
|
|
16
|
+
"sdk",
|
|
17
|
+
"v3finance"
|
|
18
|
+
],
|
|
19
|
+
"homepage": "https://monitoring.v3.finance/docs",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/v3-finance/monitoring-sdk.git"
|
|
23
|
+
},
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"author": "V3 Finance",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=20"
|
|
30
|
+
},
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"import": {
|
|
34
|
+
"types": "./dist/esm/index.d.ts",
|
|
35
|
+
"default": "./dist/esm/index.js"
|
|
36
|
+
},
|
|
37
|
+
"require": {
|
|
38
|
+
"types": "./dist/cjs/index.d.ts",
|
|
39
|
+
"default": "./dist/cjs/index.js"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"./package.json": "./package.json"
|
|
43
|
+
},
|
|
44
|
+
"main": "./dist/cjs/index.js",
|
|
45
|
+
"module": "./dist/esm/index.js",
|
|
46
|
+
"types": "./dist/cjs/index.d.ts",
|
|
47
|
+
"files": [
|
|
48
|
+
"dist",
|
|
49
|
+
"src",
|
|
50
|
+
"CHANGELOG.md"
|
|
51
|
+
],
|
|
52
|
+
"scripts": {
|
|
53
|
+
"spec:update": "node scripts/fetch-spec.ts",
|
|
54
|
+
"generate": "node scripts/generate.ts",
|
|
55
|
+
"typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.build.json --noEmit",
|
|
56
|
+
"test": "vitest run",
|
|
57
|
+
"build": "node scripts/build.ts",
|
|
58
|
+
"prepublishOnly": "npm run typecheck && npm test && npm run build"
|
|
59
|
+
},
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"access": "public"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@types/node": "22.20.5",
|
|
65
|
+
"typescript": "7.0.2",
|
|
66
|
+
"vitest": "4.1.11"
|
|
67
|
+
}
|
|
68
|
+
}
|