@secondlayer/sdk 6.0.0 → 6.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/dist/index.d.ts +230 -125
- package/dist/index.js +376 -404
- package/dist/index.js.map +17 -15
- package/dist/streams/index.d.ts +62 -2
- package/dist/streams/index.js +310 -226
- package/dist/streams/index.js.map +12 -9
- package/dist/subgraphs/index.d.ts +208 -1
- package/dist/subgraphs/index.js +311 -141
- package/dist/subgraphs/index.js.map +13 -10
- package/package.json +2 -2
package/dist/streams/index.js
CHANGED
|
@@ -1,7 +1,205 @@
|
|
|
1
1
|
// src/streams/client.ts
|
|
2
2
|
import { ed25519 } from "@secondlayer/shared";
|
|
3
3
|
|
|
4
|
+
// src/errors.ts
|
|
5
|
+
class ApiError extends Error {
|
|
6
|
+
status;
|
|
7
|
+
body;
|
|
8
|
+
code;
|
|
9
|
+
constructor(status, message, body, code) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.status = status;
|
|
12
|
+
this.body = body;
|
|
13
|
+
this.code = code;
|
|
14
|
+
this.name = "ApiError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class VersionConflictError extends ApiError {
|
|
19
|
+
currentVersion;
|
|
20
|
+
expectedVersion;
|
|
21
|
+
constructor(currentVersion, expectedVersion, message = `Version conflict: expected ${expectedVersion}, current ${currentVersion}`) {
|
|
22
|
+
super(409, message, { currentVersion, expectedVersion });
|
|
23
|
+
this.currentVersion = currentVersion;
|
|
24
|
+
this.expectedVersion = expectedVersion;
|
|
25
|
+
this.name = "VersionConflictError";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// src/base.ts
|
|
30
|
+
var DEFAULT_BASE_URL = "https://api.secondlayer.tools";
|
|
31
|
+
function buildQuery(params) {
|
|
32
|
+
const search = new URLSearchParams;
|
|
33
|
+
for (const [name, value] of Object.entries(params)) {
|
|
34
|
+
if (value === undefined || value === null)
|
|
35
|
+
continue;
|
|
36
|
+
const serialized = Array.isArray(value) ? value.join(",") : String(value);
|
|
37
|
+
if (serialized.length === 0)
|
|
38
|
+
continue;
|
|
39
|
+
search.set(name, serialized);
|
|
40
|
+
}
|
|
41
|
+
const query = search.toString();
|
|
42
|
+
return query ? `?${query}` : "";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
class BaseClient {
|
|
46
|
+
baseUrl;
|
|
47
|
+
apiKey;
|
|
48
|
+
origin;
|
|
49
|
+
constructor(options = {}) {
|
|
50
|
+
this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
51
|
+
this.apiKey = options.apiKey;
|
|
52
|
+
this.origin = options.origin ?? "cli";
|
|
53
|
+
}
|
|
54
|
+
static authHeaders(apiKey) {
|
|
55
|
+
const headers = {
|
|
56
|
+
"Content-Type": "application/json"
|
|
57
|
+
};
|
|
58
|
+
if (apiKey) {
|
|
59
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
60
|
+
}
|
|
61
|
+
return headers;
|
|
62
|
+
}
|
|
63
|
+
async request(method, path, body) {
|
|
64
|
+
const response = await this.fetchResponse(method, path, body);
|
|
65
|
+
if (response.status === 204) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
return response.json();
|
|
69
|
+
}
|
|
70
|
+
async requestText(method, path, body) {
|
|
71
|
+
const response = await this.fetchResponse(method, path, body);
|
|
72
|
+
return response.text();
|
|
73
|
+
}
|
|
74
|
+
async fetchResponse(method, path, body) {
|
|
75
|
+
const url = `${this.baseUrl}${path}`;
|
|
76
|
+
const headers = BaseClient.authHeaders(this.apiKey);
|
|
77
|
+
headers["x-sl-origin"] = this.origin;
|
|
78
|
+
let serializedBody;
|
|
79
|
+
if (body !== undefined && body !== null) {
|
|
80
|
+
try {
|
|
81
|
+
serializedBody = JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value);
|
|
82
|
+
} catch (err) {
|
|
83
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
84
|
+
throw new ApiError(0, `Failed to serialize request body: ${detail}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
let response;
|
|
88
|
+
try {
|
|
89
|
+
response = await fetch(url, {
|
|
90
|
+
method,
|
|
91
|
+
headers,
|
|
92
|
+
body: serializedBody
|
|
93
|
+
});
|
|
94
|
+
} catch {
|
|
95
|
+
throw new ApiError(0, `Cannot reach API at ${this.baseUrl}. Check your connection or try again.`);
|
|
96
|
+
}
|
|
97
|
+
if (!response.ok) {
|
|
98
|
+
if (response.status === 401) {
|
|
99
|
+
throw new ApiError(401, "API key invalid or expired.");
|
|
100
|
+
}
|
|
101
|
+
if (response.status === 429) {
|
|
102
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
103
|
+
const msg = retryAfter ? `Rate limited. Wait ${retryAfter} seconds.` : "Rate limited. Try again later.";
|
|
104
|
+
throw new ApiError(429, msg);
|
|
105
|
+
}
|
|
106
|
+
if (response.status >= 500) {
|
|
107
|
+
throw new ApiError(response.status, `Server error. Try again or check status at ${this.baseUrl}/health`);
|
|
108
|
+
}
|
|
109
|
+
const errorBody = await response.text();
|
|
110
|
+
let message = `HTTP ${response.status}`;
|
|
111
|
+
let parsedBody = errorBody;
|
|
112
|
+
let code;
|
|
113
|
+
try {
|
|
114
|
+
const json = JSON.parse(errorBody);
|
|
115
|
+
parsedBody = json;
|
|
116
|
+
const err = json.error ?? json.message;
|
|
117
|
+
if (typeof err === "string") {
|
|
118
|
+
message = err;
|
|
119
|
+
} else if (err && typeof err === "object") {
|
|
120
|
+
message = JSON.stringify(err);
|
|
121
|
+
}
|
|
122
|
+
if (typeof json.code === "string") {
|
|
123
|
+
code = json.code;
|
|
124
|
+
}
|
|
125
|
+
} catch {
|
|
126
|
+
if (errorBody)
|
|
127
|
+
message = errorBody;
|
|
128
|
+
}
|
|
129
|
+
throw new ApiError(response.status, message, parsedBody, code);
|
|
130
|
+
}
|
|
131
|
+
return response;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/streams/errors.ts
|
|
136
|
+
class AuthError extends Error {
|
|
137
|
+
status = 401;
|
|
138
|
+
constructor(message = "API key invalid or expired.") {
|
|
139
|
+
super(message);
|
|
140
|
+
this.name = "AuthError";
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
class RateLimitError extends Error {
|
|
145
|
+
retryAfter;
|
|
146
|
+
status = 429;
|
|
147
|
+
constructor(message = "Rate limited. Try again later.", retryAfter) {
|
|
148
|
+
super(message);
|
|
149
|
+
this.retryAfter = retryAfter;
|
|
150
|
+
this.name = "RateLimitError";
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
class ValidationError extends Error {
|
|
155
|
+
status;
|
|
156
|
+
body;
|
|
157
|
+
constructor(message, status, body) {
|
|
158
|
+
super(message);
|
|
159
|
+
this.status = status;
|
|
160
|
+
this.body = body;
|
|
161
|
+
this.name = "ValidationError";
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
class StreamsServerError extends Error {
|
|
166
|
+
status;
|
|
167
|
+
body;
|
|
168
|
+
constructor(message, status, body) {
|
|
169
|
+
super(message);
|
|
170
|
+
this.status = status;
|
|
171
|
+
this.body = body;
|
|
172
|
+
this.name = "StreamsServerError";
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
class StreamsSignatureError extends Error {
|
|
177
|
+
constructor(message = "Streams response signature verification failed.") {
|
|
178
|
+
super(message);
|
|
179
|
+
this.name = "StreamsSignatureError";
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// src/streams/cursor.ts
|
|
184
|
+
var Cursor = {
|
|
185
|
+
atHeight(height) {
|
|
186
|
+
return `${height}:0`;
|
|
187
|
+
},
|
|
188
|
+
parse(cursor) {
|
|
189
|
+
const parts = cursor.split(":");
|
|
190
|
+
const blockHeight = Number(parts[0]);
|
|
191
|
+
const eventIndex = Number(parts[1]);
|
|
192
|
+
if (parts.length !== 2 || !Number.isInteger(blockHeight) || !Number.isInteger(eventIndex)) {
|
|
193
|
+
throw new ValidationError(`Invalid stream cursor "${cursor}"; expected "<block>:<index>" (e.g. "951475:3").`, 400);
|
|
194
|
+
}
|
|
195
|
+
return { blockHeight, eventIndex };
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
4
199
|
// src/streams/consumer.ts
|
|
200
|
+
function reorgKey(reorg) {
|
|
201
|
+
return `${reorg.detected_at}|${reorg.fork_point_height}|${reorg.new_canonical_tip}`;
|
|
202
|
+
}
|
|
5
203
|
async function defaultSleep(ms, signal) {
|
|
6
204
|
if (signal?.aborted)
|
|
7
205
|
return;
|
|
@@ -18,10 +216,12 @@ async function defaultSleep(ms, signal) {
|
|
|
18
216
|
async function consumeStreamsEvents(opts) {
|
|
19
217
|
const sleep = opts.sleep ?? defaultSleep;
|
|
20
218
|
const mode = opts.mode ?? "tail";
|
|
219
|
+
const finalizedOnly = opts.finalizedOnly ?? false;
|
|
21
220
|
const emptyBackoffMs = opts.emptyBackoffMs ?? 500;
|
|
22
221
|
const maxPages = opts.maxPages ?? Number.POSITIVE_INFINITY;
|
|
23
222
|
const maxEmptyPolls = opts.maxEmptyPolls ?? Number.POSITIVE_INFINITY;
|
|
24
223
|
let cursor = opts.fromCursor ?? null;
|
|
224
|
+
const handledReorgs = new Set;
|
|
25
225
|
let pages = 0;
|
|
26
226
|
let emptyPolls = 0;
|
|
27
227
|
while (pages < maxPages && emptyPolls < maxEmptyPolls && !opts.signal?.aborted) {
|
|
@@ -36,14 +236,32 @@ async function consumeStreamsEvents(opts) {
|
|
|
36
236
|
assetIdentifier: opts.assetIdentifier
|
|
37
237
|
});
|
|
38
238
|
pages++;
|
|
39
|
-
|
|
40
|
-
|
|
239
|
+
if (!finalizedOnly && opts.onReorg) {
|
|
240
|
+
const fresh = envelope.reorgs.filter((reorg) => !handledReorgs.has(reorgKey(reorg))).sort((a, b) => a.fork_point_height - b.fork_point_height);
|
|
241
|
+
if (fresh.length > 0) {
|
|
242
|
+
const forkPoint = Math.min(...fresh.map((reorg) => reorg.fork_point_height));
|
|
243
|
+
const rewind = Cursor.atHeight(forkPoint);
|
|
244
|
+
for (const reorg of fresh) {
|
|
245
|
+
await opts.onReorg(reorg, { cursor: rewind });
|
|
246
|
+
handledReorgs.add(reorgKey(reorg));
|
|
247
|
+
}
|
|
248
|
+
cursor = rewind;
|
|
249
|
+
emptyPolls = 0;
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
const emitted = finalizedOnly ? envelope.events.filter((event) => event.finalized) : envelope.events;
|
|
254
|
+
const checkpoint = finalizedOnly ? emitted.at(-1)?.cursor ?? cursor : envelope.next_cursor;
|
|
255
|
+
const returnedCursor = await opts.onBatch(emitted, envelope, {
|
|
256
|
+
cursor: checkpoint
|
|
257
|
+
});
|
|
258
|
+
const nextCursor = returnedCursor ?? checkpoint;
|
|
41
259
|
if (nextCursor && nextCursor !== cursor) {
|
|
42
260
|
cursor = nextCursor;
|
|
43
261
|
emptyPolls = 0;
|
|
44
262
|
continue;
|
|
45
263
|
}
|
|
46
|
-
if (
|
|
264
|
+
if (emitted.length === 0) {
|
|
47
265
|
emptyPolls++;
|
|
48
266
|
if (mode === "bounded") {
|
|
49
267
|
return { cursor, pages, emptyPolls };
|
|
@@ -99,56 +317,6 @@ async function* streamStreamsEvents(opts) {
|
|
|
99
317
|
|
|
100
318
|
// src/streams/dumps.ts
|
|
101
319
|
import { createHash } from "node:crypto";
|
|
102
|
-
|
|
103
|
-
// src/streams/errors.ts
|
|
104
|
-
class AuthError extends Error {
|
|
105
|
-
status = 401;
|
|
106
|
-
constructor(message = "API key invalid or expired.") {
|
|
107
|
-
super(message);
|
|
108
|
-
this.name = "AuthError";
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
class RateLimitError extends Error {
|
|
113
|
-
retryAfter;
|
|
114
|
-
status = 429;
|
|
115
|
-
constructor(message = "Rate limited. Try again later.", retryAfter) {
|
|
116
|
-
super(message);
|
|
117
|
-
this.retryAfter = retryAfter;
|
|
118
|
-
this.name = "RateLimitError";
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
class ValidationError extends Error {
|
|
123
|
-
status;
|
|
124
|
-
body;
|
|
125
|
-
constructor(message, status, body) {
|
|
126
|
-
super(message);
|
|
127
|
-
this.status = status;
|
|
128
|
-
this.body = body;
|
|
129
|
-
this.name = "ValidationError";
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
class StreamsServerError extends Error {
|
|
134
|
-
status;
|
|
135
|
-
body;
|
|
136
|
-
constructor(message, status, body) {
|
|
137
|
-
super(message);
|
|
138
|
-
this.status = status;
|
|
139
|
-
this.body = body;
|
|
140
|
-
this.name = "StreamsServerError";
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
class StreamsSignatureError extends Error {
|
|
145
|
-
constructor(message = "Streams response signature verification failed.") {
|
|
146
|
-
super(message);
|
|
147
|
-
this.name = "StreamsSignatureError";
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// src/streams/dumps.ts
|
|
152
320
|
function createStreamsDumps(opts) {
|
|
153
321
|
const baseUrl = opts.baseUrl?.replace(/\/+$/, "");
|
|
154
322
|
function requireBaseUrl() {
|
|
@@ -187,8 +355,12 @@ function createStreamsDumps(opts) {
|
|
|
187
355
|
function cursorTuple(cursor) {
|
|
188
356
|
if (!cursor)
|
|
189
357
|
return [-1, -1];
|
|
190
|
-
const
|
|
191
|
-
|
|
358
|
+
const parts = cursor.split(":");
|
|
359
|
+
const [block, index] = parts.map(Number);
|
|
360
|
+
if (parts.length !== 2 || !Number.isInteger(block) || !Number.isInteger(index)) {
|
|
361
|
+
throw new ValidationError(`Invalid stream cursor "${cursor}"; expected "<block>:<index>" (e.g. "951475:3").`, 400);
|
|
362
|
+
}
|
|
363
|
+
return [block, index];
|
|
192
364
|
}
|
|
193
365
|
function maxCursor(a, b) {
|
|
194
366
|
const [ah, ai] = cursorTuple(a);
|
|
@@ -199,18 +371,6 @@ var DEFAULT_STREAMS_BASE_URL = "https://api.secondlayer.tools";
|
|
|
199
371
|
function normalizeBaseUrl(baseUrl) {
|
|
200
372
|
return baseUrl.replace(/\/+$/, "");
|
|
201
373
|
}
|
|
202
|
-
function appendSearchParam(params, name, value) {
|
|
203
|
-
if (value === undefined || value === null)
|
|
204
|
-
return;
|
|
205
|
-
params.set(name, String(value));
|
|
206
|
-
}
|
|
207
|
-
function appendListParam(params, name, value) {
|
|
208
|
-
if (value === undefined || value === null)
|
|
209
|
-
return;
|
|
210
|
-
const joined = Array.isArray(value) ? value.join(",") : value;
|
|
211
|
-
if (joined.length > 0)
|
|
212
|
-
params.set(name, joined);
|
|
213
|
-
}
|
|
214
374
|
async function responseBody(response) {
|
|
215
375
|
const text = await response.text();
|
|
216
376
|
if (text.length === 0)
|
|
@@ -332,23 +492,18 @@ function createStreamsClient(options) {
|
|
|
332
492
|
});
|
|
333
493
|
};
|
|
334
494
|
async function listEvents(params = {}) {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
}
|
|
347
|
-
if (params.notTypes?.length) {
|
|
348
|
-
searchParams.set("not_types", params.notTypes.join(","));
|
|
349
|
-
}
|
|
350
|
-
const query = searchParams.toString();
|
|
351
|
-
return request(`/v1/streams/events${query ? `?${query}` : ""}`);
|
|
495
|
+
return request(`/v1/streams/events${buildQuery({
|
|
496
|
+
cursor: params.cursor,
|
|
497
|
+
from_height: params.fromHeight,
|
|
498
|
+
to_height: params.toHeight,
|
|
499
|
+
limit: params.limit,
|
|
500
|
+
contract_id: params.contractId,
|
|
501
|
+
sender: params.sender,
|
|
502
|
+
recipient: params.recipient,
|
|
503
|
+
asset_identifier: params.assetIdentifier,
|
|
504
|
+
types: params.types,
|
|
505
|
+
not_types: params.notTypes
|
|
506
|
+
})}`);
|
|
352
507
|
}
|
|
353
508
|
return {
|
|
354
509
|
events: {
|
|
@@ -360,6 +515,7 @@ function createStreamsClient(options) {
|
|
|
360
515
|
return consumeStreamsEvents({
|
|
361
516
|
fromCursor: params.fromCursor,
|
|
362
517
|
mode: params.mode,
|
|
518
|
+
finalizedOnly: params.finalizedOnly,
|
|
363
519
|
types: params.types,
|
|
364
520
|
notTypes: params.notTypes,
|
|
365
521
|
contractId: params.contractId,
|
|
@@ -369,6 +525,7 @@ function createStreamsClient(options) {
|
|
|
369
525
|
batchSize: params.batchSize ?? 100,
|
|
370
526
|
fetchEvents,
|
|
371
527
|
onBatch: params.onBatch,
|
|
528
|
+
onReorg: params.onReorg,
|
|
372
529
|
emptyBackoffMs: params.emptyBackoffMs,
|
|
373
530
|
maxPages: params.maxPages,
|
|
374
531
|
maxEmptyPolls: params.maxEmptyPolls,
|
|
@@ -423,11 +580,10 @@ function createStreamsClient(options) {
|
|
|
423
580
|
},
|
|
424
581
|
reorgs: {
|
|
425
582
|
list(params) {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
return request(`/v1/streams/reorgs${query ? `?${query}` : ""}`);
|
|
583
|
+
return request(`/v1/streams/reorgs${buildQuery({
|
|
584
|
+
since: params.since,
|
|
585
|
+
limit: params.limit
|
|
586
|
+
})}`);
|
|
431
587
|
}
|
|
432
588
|
},
|
|
433
589
|
dumps,
|
|
@@ -439,127 +595,8 @@ function createStreamsClient(options) {
|
|
|
439
595
|
}
|
|
440
596
|
};
|
|
441
597
|
}
|
|
442
|
-
// src/streams/ft-transfer.ts
|
|
443
|
-
function requireString(payload, field) {
|
|
444
|
-
const value = payload[field];
|
|
445
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
446
|
-
throw new Error(`ft_transfer payload missing ${field}`);
|
|
447
|
-
}
|
|
448
|
-
return value;
|
|
449
|
-
}
|
|
450
|
-
function parseAssetIdentifier(assetIdentifier) {
|
|
451
|
-
const [contractId, tokenName] = assetIdentifier.split("::");
|
|
452
|
-
if (!contractId) {
|
|
453
|
-
throw new Error("ft_transfer payload has malformed asset_identifier");
|
|
454
|
-
}
|
|
455
|
-
return {
|
|
456
|
-
contract_id: contractId,
|
|
457
|
-
token_name: tokenName && tokenName.length > 0 ? tokenName : null
|
|
458
|
-
};
|
|
459
|
-
}
|
|
460
|
-
function isFtTransfer(event) {
|
|
461
|
-
return event.event_type === "ft_transfer";
|
|
462
|
-
}
|
|
463
|
-
function decodeFtTransfer(event) {
|
|
464
|
-
if (!isFtTransfer(event)) {
|
|
465
|
-
throw new Error(`Expected ft_transfer event, got ${event.event_type}`);
|
|
466
|
-
}
|
|
467
|
-
const payload = event.payload;
|
|
468
|
-
const assetIdentifier = requireString(payload, "asset_identifier");
|
|
469
|
-
const sender = requireString(payload, "sender");
|
|
470
|
-
const recipient = requireString(payload, "recipient");
|
|
471
|
-
const amount = requireString(payload, "amount");
|
|
472
|
-
if (!/^(0|[1-9]\d*)$/.test(amount)) {
|
|
473
|
-
throw new Error("ft_transfer payload has malformed amount");
|
|
474
|
-
}
|
|
475
|
-
const { contract_id, token_name } = parseAssetIdentifier(assetIdentifier);
|
|
476
|
-
return {
|
|
477
|
-
cursor: event.cursor,
|
|
478
|
-
block_height: event.block_height,
|
|
479
|
-
tx_id: event.tx_id,
|
|
480
|
-
tx_index: event.tx_index,
|
|
481
|
-
event_index: event.event_index,
|
|
482
|
-
event_type: event.event_type,
|
|
483
|
-
decoded_payload: {
|
|
484
|
-
asset_identifier: assetIdentifier,
|
|
485
|
-
contract_id: event.contract_id ?? contract_id,
|
|
486
|
-
token_name,
|
|
487
|
-
sender,
|
|
488
|
-
recipient,
|
|
489
|
-
amount
|
|
490
|
-
},
|
|
491
|
-
source_cursor: event.cursor
|
|
492
|
-
};
|
|
493
|
-
}
|
|
494
|
-
// src/streams/nft-transfer.ts
|
|
495
|
-
function requireString2(payload, field) {
|
|
496
|
-
const value = payload[field];
|
|
497
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
498
|
-
throw new Error(`nft_transfer payload missing ${field}`);
|
|
499
|
-
}
|
|
500
|
-
return value;
|
|
501
|
-
}
|
|
502
|
-
function requireHexValue(payload) {
|
|
503
|
-
const rawValue = payload.raw_value;
|
|
504
|
-
if (typeof rawValue === "string") {
|
|
505
|
-
if (!/^0x[0-9a-fA-F]*$/.test(rawValue)) {
|
|
506
|
-
throw new Error("nft_transfer payload has malformed value");
|
|
507
|
-
}
|
|
508
|
-
return rawValue;
|
|
509
|
-
}
|
|
510
|
-
const value = payload.value;
|
|
511
|
-
const hex = typeof value === "string" ? value : value && typeof value === "object" && typeof value.hex === "string" ? value.hex : null;
|
|
512
|
-
if (!hex) {
|
|
513
|
-
throw new Error("nft_transfer payload missing value");
|
|
514
|
-
}
|
|
515
|
-
if (!/^0x[0-9a-fA-F]*$/.test(hex)) {
|
|
516
|
-
throw new Error("nft_transfer payload has malformed value");
|
|
517
|
-
}
|
|
518
|
-
return hex;
|
|
519
|
-
}
|
|
520
|
-
function parseAssetIdentifier2(assetIdentifier) {
|
|
521
|
-
const [contractId, tokenName] = assetIdentifier.split("::");
|
|
522
|
-
if (!contractId) {
|
|
523
|
-
throw new Error("nft_transfer payload has malformed asset_identifier");
|
|
524
|
-
}
|
|
525
|
-
return {
|
|
526
|
-
contract_id: contractId,
|
|
527
|
-
token_name: tokenName && tokenName.length > 0 ? tokenName : null
|
|
528
|
-
};
|
|
529
|
-
}
|
|
530
|
-
function isNftTransfer(event) {
|
|
531
|
-
return event.event_type === "nft_transfer";
|
|
532
|
-
}
|
|
533
|
-
function decodeNftTransfer(event) {
|
|
534
|
-
if (!isNftTransfer(event)) {
|
|
535
|
-
throw new Error(`Expected nft_transfer event, got ${event.event_type}`);
|
|
536
|
-
}
|
|
537
|
-
const payload = event.payload;
|
|
538
|
-
const assetIdentifier = requireString2(payload, "asset_identifier");
|
|
539
|
-
const sender = requireString2(payload, "sender");
|
|
540
|
-
const recipient = requireString2(payload, "recipient");
|
|
541
|
-
const value = requireHexValue(payload);
|
|
542
|
-
const { contract_id, token_name } = parseAssetIdentifier2(assetIdentifier);
|
|
543
|
-
return {
|
|
544
|
-
cursor: event.cursor,
|
|
545
|
-
block_height: event.block_height,
|
|
546
|
-
tx_id: event.tx_id,
|
|
547
|
-
tx_index: event.tx_index,
|
|
548
|
-
event_index: event.event_index,
|
|
549
|
-
event_type: event.event_type,
|
|
550
|
-
decoded_payload: {
|
|
551
|
-
asset_identifier: assetIdentifier,
|
|
552
|
-
contract_id: event.contract_id ?? contract_id,
|
|
553
|
-
token_name,
|
|
554
|
-
sender,
|
|
555
|
-
recipient,
|
|
556
|
-
value
|
|
557
|
-
},
|
|
558
|
-
source_cursor: event.cursor
|
|
559
|
-
};
|
|
560
|
-
}
|
|
561
598
|
// src/streams/_payload.ts
|
|
562
|
-
function
|
|
599
|
+
function requireString(payload, field, eventType) {
|
|
563
600
|
const value = payload[field];
|
|
564
601
|
if (typeof value !== "string" || value.length === 0) {
|
|
565
602
|
throw new Error(`${eventType} payload missing ${field}`);
|
|
@@ -570,7 +607,7 @@ function optionalString(value) {
|
|
|
570
607
|
return typeof value === "string" && value.length > 0 ? value : null;
|
|
571
608
|
}
|
|
572
609
|
function requireAmountField(payload, field, eventType) {
|
|
573
|
-
const amount =
|
|
610
|
+
const amount = requireString(payload, field, eventType);
|
|
574
611
|
if (!/^(0|[1-9]\d*)$/.test(amount)) {
|
|
575
612
|
throw new Error(`${eventType} payload has malformed ${field}`);
|
|
576
613
|
}
|
|
@@ -579,7 +616,7 @@ function requireAmountField(payload, field, eventType) {
|
|
|
579
616
|
function requireAmount(payload, eventType) {
|
|
580
617
|
return requireAmountField(payload, "amount", eventType);
|
|
581
618
|
}
|
|
582
|
-
function
|
|
619
|
+
function parseAssetIdentifier(assetIdentifier, eventType) {
|
|
583
620
|
const [contractId, tokenName] = assetIdentifier.split("::");
|
|
584
621
|
if (!contractId) {
|
|
585
622
|
throw new Error(`${eventType} payload has malformed asset_identifier`);
|
|
@@ -589,7 +626,7 @@ function parseAssetIdentifier3(assetIdentifier, eventType) {
|
|
|
589
626
|
token_name: tokenName && tokenName.length > 0 ? tokenName : null
|
|
590
627
|
};
|
|
591
628
|
}
|
|
592
|
-
function
|
|
629
|
+
function requireHexValue(payload, eventType) {
|
|
593
630
|
const rawValue = payload.raw_value;
|
|
594
631
|
if (typeof rawValue === "string") {
|
|
595
632
|
if (!/^0x[0-9a-fA-F]*$/.test(rawValue)) {
|
|
@@ -620,6 +657,52 @@ function decodedRow(event, eventType, decoded_payload) {
|
|
|
620
657
|
};
|
|
621
658
|
}
|
|
622
659
|
|
|
660
|
+
// src/streams/ft-transfer.ts
|
|
661
|
+
function isFtTransfer(event) {
|
|
662
|
+
return event.event_type === "ft_transfer";
|
|
663
|
+
}
|
|
664
|
+
function decodeFtTransfer(event) {
|
|
665
|
+
if (!isFtTransfer(event)) {
|
|
666
|
+
throw new Error(`Expected ft_transfer event, got ${event.event_type}`);
|
|
667
|
+
}
|
|
668
|
+
const payload = event.payload;
|
|
669
|
+
const assetIdentifier = requireString(payload, "asset_identifier", "ft_transfer");
|
|
670
|
+
const sender = requireString(payload, "sender", "ft_transfer");
|
|
671
|
+
const recipient = requireString(payload, "recipient", "ft_transfer");
|
|
672
|
+
const amount = requireAmount(payload, "ft_transfer");
|
|
673
|
+
const { contract_id, token_name } = parseAssetIdentifier(assetIdentifier, "ft_transfer");
|
|
674
|
+
return decodedRow(event, "ft_transfer", {
|
|
675
|
+
asset_identifier: assetIdentifier,
|
|
676
|
+
contract_id: event.contract_id ?? contract_id,
|
|
677
|
+
token_name,
|
|
678
|
+
sender,
|
|
679
|
+
recipient,
|
|
680
|
+
amount
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
// src/streams/nft-transfer.ts
|
|
684
|
+
function isNftTransfer(event) {
|
|
685
|
+
return event.event_type === "nft_transfer";
|
|
686
|
+
}
|
|
687
|
+
function decodeNftTransfer(event) {
|
|
688
|
+
if (!isNftTransfer(event)) {
|
|
689
|
+
throw new Error(`Expected nft_transfer event, got ${event.event_type}`);
|
|
690
|
+
}
|
|
691
|
+
const payload = event.payload;
|
|
692
|
+
const assetIdentifier = requireString(payload, "asset_identifier", "nft_transfer");
|
|
693
|
+
const sender = requireString(payload, "sender", "nft_transfer");
|
|
694
|
+
const recipient = requireString(payload, "recipient", "nft_transfer");
|
|
695
|
+
const value = requireHexValue(payload, "nft_transfer");
|
|
696
|
+
const { contract_id, token_name } = parseAssetIdentifier(assetIdentifier, "nft_transfer");
|
|
697
|
+
return decodedRow(event, "nft_transfer", {
|
|
698
|
+
asset_identifier: assetIdentifier,
|
|
699
|
+
contract_id: event.contract_id ?? contract_id,
|
|
700
|
+
token_name,
|
|
701
|
+
sender,
|
|
702
|
+
recipient,
|
|
703
|
+
value
|
|
704
|
+
});
|
|
705
|
+
}
|
|
623
706
|
// src/streams/stx-events.ts
|
|
624
707
|
function isStxTransfer(event) {
|
|
625
708
|
return event.event_type === "stx_transfer";
|
|
@@ -630,8 +713,8 @@ function decodeStxTransfer(event) {
|
|
|
630
713
|
}
|
|
631
714
|
const payload = event.payload;
|
|
632
715
|
return decodedRow(event, "stx_transfer", {
|
|
633
|
-
sender:
|
|
634
|
-
recipient:
|
|
716
|
+
sender: requireString(payload, "sender", "stx_transfer"),
|
|
717
|
+
recipient: requireString(payload, "recipient", "stx_transfer"),
|
|
635
718
|
amount: requireAmount(payload, "stx_transfer"),
|
|
636
719
|
memo: optionalString(payload.memo)
|
|
637
720
|
});
|
|
@@ -645,7 +728,7 @@ function decodeStxMint(event) {
|
|
|
645
728
|
}
|
|
646
729
|
const payload = event.payload;
|
|
647
730
|
return decodedRow(event, "stx_mint", {
|
|
648
|
-
recipient:
|
|
731
|
+
recipient: requireString(payload, "recipient", "stx_mint"),
|
|
649
732
|
amount: requireAmount(payload, "stx_mint")
|
|
650
733
|
});
|
|
651
734
|
}
|
|
@@ -658,7 +741,7 @@ function decodeStxBurn(event) {
|
|
|
658
741
|
}
|
|
659
742
|
const payload = event.payload;
|
|
660
743
|
return decodedRow(event, "stx_burn", {
|
|
661
|
-
sender:
|
|
744
|
+
sender: requireString(payload, "sender", "stx_burn"),
|
|
662
745
|
amount: requireAmount(payload, "stx_burn")
|
|
663
746
|
});
|
|
664
747
|
}
|
|
@@ -671,15 +754,15 @@ function decodeStxLock(event) {
|
|
|
671
754
|
}
|
|
672
755
|
const payload = event.payload;
|
|
673
756
|
return decodedRow(event, "stx_lock", {
|
|
674
|
-
sender:
|
|
757
|
+
sender: requireString(payload, "locked_address", "stx_lock"),
|
|
675
758
|
amount: requireAmountField(payload, "locked_amount", "stx_lock"),
|
|
676
759
|
payload: { unlock_height: optionalString(payload.unlock_height) }
|
|
677
760
|
});
|
|
678
761
|
}
|
|
679
762
|
// src/streams/token-mint-burn.ts
|
|
680
763
|
function assetFields(event, eventType) {
|
|
681
|
-
const assetIdentifier =
|
|
682
|
-
const { contract_id, token_name } =
|
|
764
|
+
const assetIdentifier = requireString(event.payload, "asset_identifier", eventType);
|
|
765
|
+
const { contract_id, token_name } = parseAssetIdentifier(assetIdentifier, eventType);
|
|
683
766
|
return {
|
|
684
767
|
asset_identifier: assetIdentifier,
|
|
685
768
|
contract_id: event.contract_id ?? contract_id,
|
|
@@ -695,7 +778,7 @@ function decodeFtMint(event) {
|
|
|
695
778
|
}
|
|
696
779
|
return decodedRow(event, "ft_mint", {
|
|
697
780
|
...assetFields(event, "ft_mint"),
|
|
698
|
-
recipient:
|
|
781
|
+
recipient: requireString(event.payload, "recipient", "ft_mint"),
|
|
699
782
|
amount: requireAmount(event.payload, "ft_mint")
|
|
700
783
|
});
|
|
701
784
|
}
|
|
@@ -708,7 +791,7 @@ function decodeFtBurn(event) {
|
|
|
708
791
|
}
|
|
709
792
|
return decodedRow(event, "ft_burn", {
|
|
710
793
|
...assetFields(event, "ft_burn"),
|
|
711
|
-
sender:
|
|
794
|
+
sender: requireString(event.payload, "sender", "ft_burn"),
|
|
712
795
|
amount: requireAmount(event.payload, "ft_burn")
|
|
713
796
|
});
|
|
714
797
|
}
|
|
@@ -721,8 +804,8 @@ function decodeNftMint(event) {
|
|
|
721
804
|
}
|
|
722
805
|
return decodedRow(event, "nft_mint", {
|
|
723
806
|
...assetFields(event, "nft_mint"),
|
|
724
|
-
recipient:
|
|
725
|
-
value:
|
|
807
|
+
recipient: requireString(event.payload, "recipient", "nft_mint"),
|
|
808
|
+
value: requireHexValue(event.payload, "nft_mint")
|
|
726
809
|
});
|
|
727
810
|
}
|
|
728
811
|
function isNftBurn(event) {
|
|
@@ -734,8 +817,8 @@ function decodeNftBurn(event) {
|
|
|
734
817
|
}
|
|
735
818
|
return decodedRow(event, "nft_burn", {
|
|
736
819
|
...assetFields(event, "nft_burn"),
|
|
737
|
-
sender:
|
|
738
|
-
value:
|
|
820
|
+
sender: requireString(event.payload, "sender", "nft_burn"),
|
|
821
|
+
value: requireHexValue(event.payload, "nft_burn")
|
|
739
822
|
});
|
|
740
823
|
}
|
|
741
824
|
// src/clarity.ts
|
|
@@ -834,8 +917,9 @@ export {
|
|
|
834
917
|
StreamsServerError,
|
|
835
918
|
STREAMS_EVENT_TYPES,
|
|
836
919
|
RateLimitError,
|
|
920
|
+
Cursor,
|
|
837
921
|
AuthError
|
|
838
922
|
};
|
|
839
923
|
|
|
840
|
-
//# debugId=
|
|
924
|
+
//# debugId=CA2A679BA194AF3B64756E2164756E21
|
|
841
925
|
//# sourceMappingURL=index.js.map
|