lpsignal 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/LICENSE +21 -0
- package/README.md +104 -0
- package/dist/client.d.ts +128 -0
- package/dist/client.js +195 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/stream.d.ts +146 -0
- package/dist/stream.js +310 -0
- package/dist/stream.js.map +1 -0
- package/dist/types.d.ts +344 -0
- package/dist/types.js +12 -0
- package/dist/types.js.map +1 -0
- package/dist/webhook.d.ts +22 -0
- package/dist/webhook.js +50 -0
- package/dist/webhook.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { WebhookEvent } from './types.js';
|
|
2
|
+
export type WebhookFailure = 'missing_headers' | 'bad_timestamp' | 'expired' | 'bad_signature' | 'bad_body';
|
|
3
|
+
export declare class WebhookVerificationError extends Error {
|
|
4
|
+
readonly reason: WebhookFailure;
|
|
5
|
+
constructor(reason: WebhookFailure);
|
|
6
|
+
}
|
|
7
|
+
/** A Fetch `Headers` object or a plain object such as Node's `req.headers` (any letter case). */
|
|
8
|
+
export type HeadersLike = Headers | Record<string, string | string[] | undefined>;
|
|
9
|
+
/**
|
|
10
|
+
* Verify a webhook delivery and return its parsed body.
|
|
11
|
+
*
|
|
12
|
+
* `x-lpsignal-signature` is `sha256=` + hex HMAC-SHA256 of `"<x-lpsignal-timestamp>.<raw body>"`, keyed with the
|
|
13
|
+
* secret returned when the webhook was set. Pass the raw request bytes, before any JSON parsing: re-serialised JSON
|
|
14
|
+
* does not match the signature. Deliveries older than `toleranceSec` (default 300) are rejected as replays; every
|
|
15
|
+
* retry is signed afresh, so a legitimate retry is never too old.
|
|
16
|
+
*
|
|
17
|
+
* Deliveries are at-least-once: skip `deliveryId`s you have already handled.
|
|
18
|
+
*/
|
|
19
|
+
export declare function verifyWebhook(rawBody: string | Uint8Array, headers: HeadersLike, secret: string, opts?: {
|
|
20
|
+
toleranceSec?: number;
|
|
21
|
+
nowSec?: number;
|
|
22
|
+
}): WebhookEvent;
|
package/dist/webhook.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
export class WebhookVerificationError extends Error {
|
|
3
|
+
reason;
|
|
4
|
+
constructor(reason) {
|
|
5
|
+
super(`LPSignal webhook rejected: ${reason}`);
|
|
6
|
+
this.reason = reason;
|
|
7
|
+
this.name = 'WebhookVerificationError';
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function header(h, name) {
|
|
11
|
+
if (typeof h.get === 'function')
|
|
12
|
+
return h.get(name) ?? undefined;
|
|
13
|
+
for (const [k, v] of Object.entries(h)) {
|
|
14
|
+
if (k.toLowerCase() === name)
|
|
15
|
+
return Array.isArray(v) ? v[0] : v;
|
|
16
|
+
}
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Verify a webhook delivery and return its parsed body.
|
|
21
|
+
*
|
|
22
|
+
* `x-lpsignal-signature` is `sha256=` + hex HMAC-SHA256 of `"<x-lpsignal-timestamp>.<raw body>"`, keyed with the
|
|
23
|
+
* secret returned when the webhook was set. Pass the raw request bytes, before any JSON parsing: re-serialised JSON
|
|
24
|
+
* does not match the signature. Deliveries older than `toleranceSec` (default 300) are rejected as replays; every
|
|
25
|
+
* retry is signed afresh, so a legitimate retry is never too old.
|
|
26
|
+
*
|
|
27
|
+
* Deliveries are at-least-once: skip `deliveryId`s you have already handled.
|
|
28
|
+
*/
|
|
29
|
+
export function verifyWebhook(rawBody, headers, secret, opts = {}) {
|
|
30
|
+
const ts = header(headers, 'x-lpsignal-timestamp');
|
|
31
|
+
const sig = header(headers, 'x-lpsignal-signature');
|
|
32
|
+
if (!ts || !sig || !secret)
|
|
33
|
+
throw new WebhookVerificationError('missing_headers');
|
|
34
|
+
if (!/^\d{1,12}$/.test(ts))
|
|
35
|
+
throw new WebhookVerificationError('bad_timestamp');
|
|
36
|
+
const now = opts.nowSec ?? Math.floor(Date.now() / 1000);
|
|
37
|
+
if (Math.abs(now - Number(ts)) > (opts.toleranceSec ?? 300))
|
|
38
|
+
throw new WebhookVerificationError('expired');
|
|
39
|
+
const want = Buffer.from('sha256=' + createHmac('sha256', secret).update(`${ts}.`).update(rawBody).digest('hex'));
|
|
40
|
+
const got = Buffer.from(sig);
|
|
41
|
+
if (got.length !== want.length || !timingSafeEqual(got, want))
|
|
42
|
+
throw new WebhookVerificationError('bad_signature');
|
|
43
|
+
try {
|
|
44
|
+
return JSON.parse(typeof rawBody === 'string' ? rawBody : Buffer.from(rawBody).toString('utf8'));
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
throw new WebhookVerificationError('bad_body');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=webhook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.js","sourceRoot":"","sources":["../src/webhook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAK1D,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IAC5B;IAArB,YAAqB,MAAsB;QACzC,KAAK,CAAC,8BAA8B,MAAM,EAAE,CAAC,CAAC;QAD3B,WAAM,GAAN,MAAM,CAAgB;QAEzC,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACzC,CAAC;CACF;AAKD,SAAS,MAAM,CAAC,CAAc,EAAE,IAAY;IAC1C,IAAI,OAAQ,CAAa,CAAC,GAAG,KAAK,UAAU;QAAE,OAAQ,CAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC;IAC3F,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAkD,CAAC,EAAE,CAAC;QACxF,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAC3B,OAA4B,EAC5B,OAAoB,EACpB,MAAc,EACd,OAAmD,EAAE;IAErD,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC;IACnD,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC;IACpD,IAAI,CAAC,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,wBAAwB,CAAC,iBAAiB,CAAC,CAAC;IAClF,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,MAAM,IAAI,wBAAwB,CAAC,eAAe,CAAC,CAAC;IAChF,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IACzD,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,IAAI,GAAG,CAAC;QAAE,MAAM,IAAI,wBAAwB,CAAC,SAAS,CAAC,CAAC;IAC3G,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAClH,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC;QAAE,MAAM,IAAI,wBAAwB,CAAC,eAAe,CAAC,CAAC;IACnH,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAiB,CAAC;IACnH,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,wBAAwB,CAAC,UAAU,CAAC,CAAC;IACjD,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lpsignal",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Official LPSignal SDK: net-of-IL APR signals for concentrated-liquidity pools, a WebSocket stream that never misses a signal, webhook verification and the REST API.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=20"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc -p tsconfig.json",
|
|
24
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"prepublishOnly": "npm run build",
|
|
27
|
+
"e2e": "node scripts/e2e.mjs"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"lpsignal",
|
|
31
|
+
"defi",
|
|
32
|
+
"uniswap",
|
|
33
|
+
"concentrated-liquidity",
|
|
34
|
+
"liquidity-provider",
|
|
35
|
+
"impermanent-loss",
|
|
36
|
+
"apr",
|
|
37
|
+
"pancakeswap",
|
|
38
|
+
"aerodrome"
|
|
39
|
+
],
|
|
40
|
+
"homepage": "https://github.com/Ligengxin96/lpsignal-sdk#readme",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/Ligengxin96/lpsignal-sdk.git",
|
|
44
|
+
"directory": "node"
|
|
45
|
+
},
|
|
46
|
+
"license": "MIT",
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"ws": "^8.18.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^22.0.0",
|
|
52
|
+
"@types/ws": "^8.5.12",
|
|
53
|
+
"typescript": "^5.6.0",
|
|
54
|
+
"vitest": "^3.2.0"
|
|
55
|
+
}
|
|
56
|
+
}
|