@imtbl/minting-backend 2.0.0-alpha.7 → 2.0.0-alpha.8

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.
Files changed (3) hide show
  1. package/dist/index.cjs +10 -572
  2. package/dist/index.js +10 -572
  3. package/package.json +6 -6
package/dist/index.cjs CHANGED
@@ -1,86 +1,7 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/index.ts
2
- var _blockchaindata = require('@imtbl/blockchain-data');
3
- var _webhook = require('@imtbl/webhook');
4
- var _metrics = require('@imtbl/metrics');
5
-
6
- // src/analytics/index.ts
7
-
8
- var moduleName = "minting_backend_sdk";
9
- var trackInitializePersistencePG = () => {
10
- try {
11
- _metrics.track.call(void 0, moduleName, "initializePersistencePG");
12
- } catch (e3) {
13
- }
14
- };
15
- var trackInitializePersistencePrismaSqlite = () => {
16
- try {
17
- _metrics.track.call(void 0, moduleName, "initializePersistencePrismaSqlite");
18
- } catch (e4) {
19
- }
20
- };
21
- var trackSubmitMintingRequests = () => {
22
- try {
23
- _metrics.track.call(void 0, moduleName, "submitMintingRequests");
24
- } catch (e5) {
25
- }
26
- };
27
- var trackProcessMint = () => {
28
- try {
29
- _metrics.track.call(void 0, moduleName, "processMint");
30
- } catch (e6) {
31
- }
32
- };
33
- var trackRecordMint = () => {
34
- try {
35
- _metrics.track.call(void 0, moduleName, "recordMint");
36
- } catch (e7) {
37
- }
38
- };
39
- var trackError = (error) => {
40
- try {
41
- _metrics.track.call(void 0, moduleName, "error", {
42
- name: error.name,
43
- message: error.message
44
- });
45
- } catch (e8) {
46
- }
47
- };
48
- var trackUncaughtException = (error, origin) => {
49
- try {
50
- _metrics.track.call(void 0, moduleName, "error", {
51
- name: error.name,
52
- message: error.message,
53
- origin
54
- });
55
- } catch (e9) {
56
- }
57
- };
58
-
59
- // src/persistence/pg/postgres.ts
60
- var mintingPersistence = (client) => {
61
- trackInitializePersistencePG();
62
- return {
63
- recordMint: async (request) => {
64
- const r = await client.query(
65
- `
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } async function _asyncOptionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = await fn(value); } else if (op === 'call' || op === 'optionalCall') { value = await fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _blockchaindata = require('@imtbl/blockchain-data');var _webhook = require('@imtbl/webhook');var _metrics = require('@imtbl/metrics');var u="minting_backend_sdk",M=()=>{try{_metrics.track.call(void 0, u,"initializePersistencePG")}catch (e2){}},h=()=>{try{_metrics.track.call(void 0, u,"initializePersistencePrismaSqlite")}catch (e3){}},I=()=>{try{_metrics.track.call(void 0, u,"submitMintingRequests")}catch (e4){}},A=()=>{try{_metrics.track.call(void 0, u,"processMint")}catch (e5){}},S=()=>{try{_metrics.track.call(void 0, u,"recordMint")}catch (e6){}},w=e=>{try{_metrics.track.call(void 0, u,"error",{name:e.name,message:e.message})}catch (e7){}},E=(e,t)=>{try{_metrics.track.call(void 0, u,"error",{name:e.name,message:e.message,origin:t})}catch (e8){}};var q=e=>(M(),{recordMint:async t=>{if((await e.query(`
66
2
  INSERT INTO im_assets (asset_id, contract_address, owner_address, metadata, amount, token_id)
67
3
  VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (asset_id, contract_address) DO NOTHING;
68
- `,
69
- [
70
- request.asset_id,
71
- request.contract_address,
72
- request.owner_address,
73
- request.metadata,
74
- request.amount,
75
- request.token_id
76
- ]
77
- );
78
- if (r.rowCount === 0) {
79
- throw new Error("Duplicated mint");
80
- }
81
- },
82
- getNextBatchForSubmission: async (limit) => {
83
- const res = await client.query(`
4
+ `,[t.asset_id,t.contract_address,t.owner_address,t.metadata,t.amount,t.token_id])).rowCount===0)throw new Error("Duplicated mint")},getNextBatchForSubmission:async t=>(await e.query(`
84
5
  WITH limited_assets AS (
85
6
  SELECT id
86
7
  FROM im_assets
@@ -93,16 +14,9 @@ var mintingPersistence = (client) => {
93
14
  WHERE minting_status IS NULL
94
15
  AND id IN (SELECT id FROM limited_assets)
95
16
  RETURNING *;
96
- `, [limit]);
97
- return res.rows;
98
- },
99
- updateMintingStatusToSubmitted: async (ids) => {
100
- await client.query(`
17
+ `,[t])).rows,updateMintingStatusToSubmitted:async t=>{await e.query(`
101
18
  UPDATE im_assets SET minting_status = $2 WHERE id = ANY($1);
102
- `, [ids, "submitted"]);
103
- },
104
- syncMintingStatus: async (submittedMintRequest) => {
105
- await client.query(`
19
+ `,[t,"submitted"])},syncMintingStatus:async t=>{await e.query(`
106
20
  INSERT INTO im_assets (
107
21
  asset_id,
108
22
  contract_address,
@@ -125,494 +39,18 @@ var mintingPersistence = (client) => {
125
39
  im_assets.last_imtbl_zkevm_mint_request_updated_id < $7 OR
126
40
  im_assets.last_imtbl_zkevm_mint_request_updated_id is null
127
41
  );
128
- `, [
129
- submittedMintRequest.assetId,
130
- submittedMintRequest.contractAddress,
131
- submittedMintRequest.ownerAddress,
132
- submittedMintRequest.tokenId,
133
- submittedMintRequest.status,
134
- submittedMintRequest.metadataId,
135
- submittedMintRequest.imtblZkevmMintRequestUpdatedId,
136
- submittedMintRequest.error,
137
- submittedMintRequest.amount
138
- ]);
139
- },
140
- markAsConflict: async (assetIds, contractAddress) => {
141
- await client.query(`
42
+ `,[t.assetId,t.contractAddress,t.ownerAddress,t.tokenId,t.status,t.metadataId,t.imtblZkevmMintRequestUpdatedId,t.error,t.amount])},markAsConflict:async(t,s)=>{await e.query(`
142
43
  UPDATE im_assets
143
44
  SET minting_status = 'conflicting'
144
45
  WHERE asset_id = ANY($1)
145
46
  AND contract_address = $2;
146
- `, [assetIds, contractAddress]);
147
- },
148
- resetMintingStatus: async (ids) => {
149
- await client.query(`
47
+ `,[t,s])},resetMintingStatus:async t=>{await e.query(`
150
48
  UPDATE im_assets SET minting_status = null WHERE id = ANY($1);
151
- `, [ids]);
152
- },
153
- markForRetry: async (ids) => {
154
- await client.query(`
49
+ `,[t])},markForRetry:async t=>{await e.query(`
155
50
  UPDATE im_assets
156
51
  SET minting_status = null, tried_count = tried_count + 1 WHERE id = ANY($1);
157
- `, [ids]);
158
- },
159
- updateMintingStatusToSubmissionFailed: async (ids) => {
160
- await client.query(`
52
+ `,[t])},updateMintingStatusToSubmissionFailed:async t=>{await e.query(`
161
53
  UPDATE im_assets SET minting_status = 'submission_failed' WHERE id = ANY($1);
162
- `, [ids]);
163
- },
164
- getMintingRequest: async (contractAddress, referenceId) => {
165
- const res = await client.query(`
54
+ `,[t])},getMintingRequest:async(t,s)=>(await e.query(`
166
55
  SELECT * FROM im_assets WHERE contract_address = $1 and asset_id = $2;
167
- `, [contractAddress, referenceId]);
168
- return res.rows[0] || null;
169
- }
170
- };
171
- };
172
-
173
- // src/persistence/prismaSqlite/sqlite.ts
174
- var mintingPersistence2 = (client) => {
175
- trackInitializePersistencePrismaSqlite();
176
- return {
177
- recordMint: async (request) => {
178
- const result = await client.imAssets.upsert({
179
- where: {
180
- im_assets_uindex: {
181
- assetId: request.asset_id,
182
- contractAddress: request.contract_address
183
- }
184
- },
185
- update: {},
186
- // Do nothing on conflict
187
- create: {
188
- assetId: request.asset_id,
189
- contractAddress: request.contract_address,
190
- ownerAddress: request.owner_address,
191
- metadata: JSON.stringify(request.metadata),
192
- // Serialize JSON metadata
193
- amount: request.amount || null,
194
- tokenId: request.token_id || null
195
- }
196
- });
197
- if (!result) {
198
- throw new Error("Duplicated mint");
199
- }
200
- },
201
- // WARNING: this is NOT concurrency safe. Please only call this method one at a time.
202
- getNextBatchForSubmission: async (limit) => {
203
- const assets = await client.imAssets.findMany({
204
- where: {
205
- mintingStatus: null
206
- },
207
- take: limit
208
- });
209
- const assetIds = assets.map((asset) => asset.id);
210
- await client.imAssets.updateMany({
211
- where: {
212
- id: {
213
- in: assetIds
214
- }
215
- },
216
- data: {
217
- mintingStatus: "submitting"
218
- }
219
- });
220
- const updatedAssets = await client.imAssets.findMany({
221
- where: {
222
- id: {
223
- in: assetIds
224
- }
225
- }
226
- });
227
- return updatedAssets.map((asset) => ({
228
- id: asset.id,
229
- contract_address: asset.contractAddress,
230
- wallet_address: asset.ownerAddress,
231
- asset_id: asset.assetId,
232
- metadata: asset.metadata ? JSON.parse(asset.metadata) : null,
233
- owner_address: asset.ownerAddress,
234
- tried_count: asset.triedCount,
235
- amount: asset.amount || null,
236
- token_id: asset.tokenId || null
237
- }));
238
- },
239
- updateMintingStatusToSubmitted: async (ids) => {
240
- await client.imAssets.updateMany({
241
- where: {
242
- id: {
243
- in: ids
244
- }
245
- },
246
- data: {
247
- mintingStatus: "submitted"
248
- }
249
- });
250
- },
251
- syncMintingStatus: async (submittedMintRequest) => {
252
- const existingAsset = await client.imAssets.findUnique({
253
- where: {
254
- im_assets_uindex: {
255
- assetId: submittedMintRequest.assetId,
256
- contractAddress: submittedMintRequest.contractAddress
257
- }
258
- }
259
- });
260
- if (existingAsset && (existingAsset.lastImtblZkevmMintRequestUpdatedId === null || existingAsset.lastImtblZkevmMintRequestUpdatedId < submittedMintRequest.imtblZkevmMintRequestUpdatedId)) {
261
- await client.imAssets.update({
262
- where: {
263
- im_assets_uindex: {
264
- assetId: submittedMintRequest.assetId,
265
- contractAddress: submittedMintRequest.contractAddress
266
- }
267
- },
268
- data: {
269
- ownerAddress: submittedMintRequest.ownerAddress,
270
- tokenId: submittedMintRequest.tokenId,
271
- mintingStatus: submittedMintRequest.status,
272
- metadataId: submittedMintRequest.metadataId,
273
- lastImtblZkevmMintRequestUpdatedId: submittedMintRequest.imtblZkevmMintRequestUpdatedId,
274
- error: submittedMintRequest.error,
275
- amount: submittedMintRequest.amount || null
276
- }
277
- });
278
- } else if (!existingAsset) {
279
- await client.imAssets.create({
280
- data: {
281
- assetId: submittedMintRequest.assetId,
282
- contractAddress: submittedMintRequest.contractAddress,
283
- ownerAddress: submittedMintRequest.ownerAddress,
284
- tokenId: submittedMintRequest.tokenId,
285
- mintingStatus: submittedMintRequest.status,
286
- metadataId: submittedMintRequest.metadataId,
287
- lastImtblZkevmMintRequestUpdatedId: submittedMintRequest.imtblZkevmMintRequestUpdatedId,
288
- error: submittedMintRequest.error,
289
- amount: submittedMintRequest.amount || null
290
- }
291
- });
292
- }
293
- },
294
- updateMintingStatusToSubmissionFailed: async (ids) => {
295
- await client.imAssets.updateMany({
296
- where: {
297
- id: {
298
- in: ids
299
- }
300
- },
301
- data: {
302
- mintingStatus: "submission_failed"
303
- }
304
- });
305
- },
306
- markAsConflict: async (assetIds, contractAddress) => {
307
- await client.imAssets.updateMany({
308
- where: {
309
- assetId: {
310
- in: assetIds
311
- // Targets assets where assetId is in the provided list
312
- },
313
- contractAddress
314
- // Additional condition for contract address
315
- },
316
- data: {
317
- mintingStatus: "conflicting"
318
- // Set the new status
319
- }
320
- });
321
- },
322
- resetMintingStatus: async (ids) => {
323
- await client.imAssets.updateMany({
324
- where: {
325
- id: {
326
- in: ids
327
- // Condition to match ids
328
- }
329
- },
330
- data: {
331
- mintingStatus: null
332
- // Setting minting_status to null
333
- }
334
- });
335
- },
336
- // this method is not concurrency safe
337
- markForRetry: async (ids) => {
338
- const assets = await client.imAssets.findMany({
339
- where: {
340
- id: {
341
- in: ids
342
- }
343
- },
344
- select: {
345
- id: true,
346
- triedCount: true
347
- // Assuming the field is named triedCount
348
- }
349
- });
350
- for (const asset of assets) {
351
- await client.imAssets.update({
352
- where: {
353
- id: asset.id
354
- },
355
- data: {
356
- mintingStatus: null,
357
- triedCount: asset.triedCount + 1
358
- }
359
- });
360
- }
361
- },
362
- getMintingRequest: async (contractAddress, referenceId) => {
363
- const asset = await client.imAssets.findFirst({
364
- where: {
365
- contractAddress,
366
- assetId: referenceId
367
- }
368
- });
369
- if (!asset) {
370
- return null;
371
- }
372
- return {
373
- asset_id: asset.assetId,
374
- contract_address: asset.contractAddress,
375
- id: asset.id,
376
- metadata: asset.metadata ? JSON.parse(asset.metadata) : null,
377
- owner_address: asset.ownerAddress,
378
- tried_count: asset.triedCount,
379
- wallet_address: asset.ownerAddress,
380
- amount: asset.amount || null
381
- };
382
- }
383
- };
384
- };
385
-
386
- // src/minting.ts
387
- var recordMint = async (mintingPersistence3, mintRequest) => {
388
- trackRecordMint();
389
- mintingPersistence3.recordMint(mintRequest);
390
- };
391
- var defaultMintingDelay = 1e3;
392
- var submitMintingRequests = async (mintingPersistence3, blockchainDataSDKClient, {
393
- defaultBatchSize = 1e3,
394
- chainName = "imtbl-zkevm-testnet",
395
- maxNumberOfTries = 3
396
- }, logger = console, maxLoops = Infinity) => {
397
- trackSubmitMintingRequests();
398
- let mintingResponse;
399
- let numberOfLoops = 0;
400
- while (numberOfLoops++ < maxLoops) {
401
- await new Promise((resolve) => {
402
- setTimeout(resolve, defaultMintingDelay);
403
- });
404
- let batchSize = Math.min(
405
- _optionalChain([mintingResponse, 'optionalAccess', _ => _.imx_remaining_mint_requests]) ? parseInt(mintingResponse.imx_remaining_mint_requests, 10) : defaultBatchSize,
406
- defaultBatchSize
407
- );
408
- if (batchSize === 0 && mintingResponse && new Date(mintingResponse.imx_mint_requests_limit_reset) > /* @__PURE__ */ new Date()) {
409
- logger.info(
410
- `minting limit reached, waiting for reset at ${_optionalChain([mintingResponse, 'optionalAccess', _2 => _2.imx_mint_requests_limit_reset])}`
411
- );
412
- continue;
413
- }
414
- if (batchSize === 0) {
415
- logger.info(
416
- `minting limit has been reset, use default batch size: ${defaultBatchSize}`
417
- );
418
- mintingResponse = void 0;
419
- batchSize = defaultBatchSize;
420
- }
421
- const pendingMints = await mintingPersistence3.getNextBatchForSubmission(
422
- batchSize
423
- );
424
- if (pendingMints.length === 0) {
425
- logger.info("no assets to mint");
426
- continue;
427
- }
428
- const chunkedAssets = pendingMints.sort(
429
- (a, b) => a.contract_address > b.contract_address ? 1 : -1
430
- ).reduce((acc, row) => {
431
- if (acc.length === 0) {
432
- return [{ contractAddress: row.contract_address, assets: [row] }];
433
- }
434
- const lastBatch = acc[acc.length - 1];
435
- if (lastBatch.contractAddress === row.contract_address && lastBatch.assets.length < 100) {
436
- return [...acc.slice(0, -1), { ...lastBatch, assets: [...lastBatch.assets, row] }];
437
- }
438
- return [...acc, { contractAddress: row.contract_address, assets: [row] }];
439
- }, []);
440
- const mintingResults = await Promise.allSettled(
441
- chunkedAssets.map(
442
- async ({ contractAddress, assets }) => {
443
- const mintingRequest = {
444
- chainName,
445
- contractAddress,
446
- createMintRequestRequest: {
447
- assets: assets.map((row) => ({
448
- reference_id: row.asset_id,
449
- owner_address: row.owner_address,
450
- metadata: row.metadata,
451
- token_id: row.token_id,
452
- amount: row.amount ? `${row.amount}` : null
453
- }))
454
- }
455
- };
456
- try {
457
- const response = await blockchainDataSDKClient.createMintRequest(
458
- mintingRequest
459
- );
460
- logger.info(
461
- `mintingResponse: ${JSON.stringify(response, null, 2)}`
462
- );
463
- await mintingPersistence3.updateMintingStatusToSubmitted(
464
- assets.map(({ id }) => id)
465
- );
466
- return response;
467
- } catch (e) {
468
- logger.error(e);
469
- trackError(e);
470
- if (e.code === "CONFLICT_ERROR" && _optionalChain([e, 'access', _3 => _3.details, 'optionalAccess', _4 => _4.id]) === "reference_id") {
471
- try {
472
- await mintingPersistence3.markAsConflict(
473
- e.details.values,
474
- contractAddress
475
- );
476
- await mintingPersistence3.resetMintingStatus(
477
- assets.map(({ id }) => id).filter((id) => !e.details.values.includes(id))
478
- );
479
- } catch (e2) {
480
- logger.error(e2);
481
- trackError(e);
482
- }
483
- } else {
484
- const { assetsToRetry, assetsExceededMaxNumberOfTries } = assets.reduce(
485
- (acc, { tried_count = 0, id }) => {
486
- if (tried_count < maxNumberOfTries) {
487
- acc.assetsToRetry.push(id);
488
- } else {
489
- acc.assetsExceededMaxNumberOfTries.push(id);
490
- }
491
- return acc;
492
- },
493
- {
494
- assetsToRetry: [],
495
- assetsExceededMaxNumberOfTries: []
496
- }
497
- );
498
- await mintingPersistence3.markForRetry(
499
- assetsToRetry
500
- );
501
- await mintingPersistence3.updateMintingStatusToSubmissionFailed(
502
- assetsExceededMaxNumberOfTries
503
- );
504
- }
505
- return e;
506
- }
507
- }
508
- )
509
- );
510
- mintingResponse = _optionalChain([mintingResults, 'access', _5 => _5.reverse, 'call', _6 => _6(), 'access', _7 => _7.find, 'call', _8 => _8(
511
- (r) => r.status === "fulfilled"
512
- ), 'optionalAccess', _9 => _9.value]);
513
- }
514
- };
515
- var processMint = async (mintingPersistence3, event, logger = console) => {
516
- trackProcessMint();
517
- if (event.event_name !== "imtbl_zkevm_mint_request_updated") {
518
- logger.info(
519
- `${event.event_name} is not imtbl_zkevm_mint_request_updated, skip.`
520
- );
521
- return;
522
- }
523
- const referenceId = event.data.reference_id;
524
- if (!referenceId) {
525
- throw new Error("reference_id not found in webhook event");
526
- }
527
- const contractAddress = event.data.contract_address;
528
- if (!contractAddress) {
529
- throw new Error("contract_address not found in webhook event");
530
- }
531
- if (event.data.status === "failed") {
532
- logger.error(`mint failed: ${JSON.stringify(event.data, null, 2)}`);
533
- }
534
- const mintReq = await mintingPersistence3.getMintingRequest(
535
- contractAddress,
536
- referenceId
537
- );
538
- if (!mintReq) {
539
- logger.info(
540
- `minting request not found in the database, ${JSON.stringify(event.data)}`
541
- );
542
- }
543
- const ownerAddress = _optionalChain([mintReq, 'optionalAccess', _10 => _10.wallet_address]) || event.data.owner_address;
544
- if (!ownerAddress) {
545
- logger.error("owner_address missing");
546
- throw new Error("owner_address missing");
547
- }
548
- await mintingPersistence3.syncMintingStatus({
549
- tokenId: event.data.token_id,
550
- status: event.data.status,
551
- assetId: referenceId,
552
- contractAddress,
553
- ownerAddress,
554
- metadataId: event.data.metadata_id,
555
- imtblZkevmMintRequestUpdatedId: event.event_id,
556
- error: event.data.error ? JSON.stringify(event.data.error) : null,
557
- amount: event.data.amount || null
558
- });
559
- };
560
-
561
- // src/index.ts
562
- var noopHandlers = {
563
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
564
- zkevmMintRequestUpdated: async (event) => {
565
- }
566
- };
567
- var MintingBackendModule = class {
568
-
569
-
570
-
571
-
572
- constructor(config) {
573
- this.baseConfig = config.baseConfig;
574
- this.persistence = config.persistence;
575
- this.logger = config.logger || console;
576
- this.blockchainDataClient = new (0, _blockchaindata.BlockchainData)({
577
- baseConfig: config.baseConfig
578
- });
579
- _metrics.setEnvironment.call(void 0, this.baseConfig.environment);
580
- if (this.baseConfig.publishableKey) {
581
- _metrics.setPublishableApiKey.call(void 0, this.baseConfig.publishableKey);
582
- }
583
- }
584
- async recordMint(mintRequest) {
585
- await recordMint(this.persistence, mintRequest);
586
- }
587
- async submitMintingRequests(config) {
588
- await submitMintingRequests(
589
- this.persistence,
590
- this.blockchainDataClient,
591
- config
592
- );
593
- }
594
- async processMint(body, otherHandlers = noopHandlers) {
595
- await _webhook.handle.call(void 0, body, this.baseConfig.environment, {
596
- zkevmMintRequestUpdated: async (event) => {
597
- await processMint(this.persistence, event, this.logger);
598
- if (otherHandlers.zkevmMintRequestUpdated) {
599
- otherHandlers.zkevmMintRequestUpdated(event);
600
- }
601
- }
602
- });
603
- }
604
- };
605
- if (typeof process !== "undefined" && process.on) {
606
- try {
607
- process.on("uncaughtExceptionMonitor", trackUncaughtException);
608
- } catch (e10) {
609
- }
610
- }
611
-
612
-
613
-
614
-
615
-
616
-
617
-
618
- exports.MintingBackendModule = MintingBackendModule; exports.mintingPersistencePg = mintingPersistence; exports.mintingPersistencePrismaSqlite = mintingPersistence2; exports.processMint = processMint; exports.recordMint = recordMint; exports.submitMintingRequests = submitMintingRequests;
56
+ `,[t,s])).rows[0]||null});var U=e=>(h(),{recordMint:async t=>{if(!await e.imAssets.upsert({where:{im_assets_uindex:{assetId:t.asset_id,contractAddress:t.contract_address}},update:{},create:{assetId:t.asset_id,contractAddress:t.contract_address,ownerAddress:t.owner_address,metadata:JSON.stringify(t.metadata),amount:t.amount||null,tokenId:t.token_id||null}}))throw new Error("Duplicated mint")},getNextBatchForSubmission:async t=>{let a=(await e.imAssets.findMany({where:{mintingStatus:null},take:t})).map(n=>n.id);return await e.imAssets.updateMany({where:{id:{in:a}},data:{mintingStatus:"submitting"}}),(await e.imAssets.findMany({where:{id:{in:a}}})).map(n=>({id:n.id,contract_address:n.contractAddress,wallet_address:n.ownerAddress,asset_id:n.assetId,metadata:n.metadata?JSON.parse(n.metadata):null,owner_address:n.ownerAddress,tried_count:n.triedCount,amount:n.amount||null,token_id:n.tokenId||null}))},updateMintingStatusToSubmitted:async t=>{await e.imAssets.updateMany({where:{id:{in:t}},data:{mintingStatus:"submitted"}})},syncMintingStatus:async t=>{let s=await e.imAssets.findUnique({where:{im_assets_uindex:{assetId:t.assetId,contractAddress:t.contractAddress}}});s&&(s.lastImtblZkevmMintRequestUpdatedId===null||s.lastImtblZkevmMintRequestUpdatedId<t.imtblZkevmMintRequestUpdatedId)?await e.imAssets.update({where:{im_assets_uindex:{assetId:t.assetId,contractAddress:t.contractAddress}},data:{ownerAddress:t.ownerAddress,tokenId:t.tokenId,mintingStatus:t.status,metadataId:t.metadataId,lastImtblZkevmMintRequestUpdatedId:t.imtblZkevmMintRequestUpdatedId,error:t.error,amount:t.amount||null}}):s||await e.imAssets.create({data:{assetId:t.assetId,contractAddress:t.contractAddress,ownerAddress:t.ownerAddress,tokenId:t.tokenId,mintingStatus:t.status,metadataId:t.metadataId,lastImtblZkevmMintRequestUpdatedId:t.imtblZkevmMintRequestUpdatedId,error:t.error,amount:t.amount||null}})},updateMintingStatusToSubmissionFailed:async t=>{await e.imAssets.updateMany({where:{id:{in:t}},data:{mintingStatus:"submission_failed"}})},markAsConflict:async(t,s)=>{await e.imAssets.updateMany({where:{assetId:{in:t},contractAddress:s},data:{mintingStatus:"conflicting"}})},resetMintingStatus:async t=>{await e.imAssets.updateMany({where:{id:{in:t}},data:{mintingStatus:null}})},markForRetry:async t=>{let s=await e.imAssets.findMany({where:{id:{in:t}},select:{id:!0,triedCount:!0}});for(let a of s)await e.imAssets.update({where:{id:a.id},data:{mintingStatus:null,triedCount:a.triedCount+1}})},getMintingRequest:async(t,s)=>{let a=await e.imAssets.findFirst({where:{contractAddress:t,assetId:s}});return a?{asset_id:a.assetId,contract_address:a.contractAddress,id:a.id,metadata:a.metadata?JSON.parse(a.metadata):null,owner_address:a.ownerAddress,tried_count:a.triedCount,wallet_address:a.ownerAddress,amount:a.amount||null}:null}});var R=async(e,t)=>{S(),e.recordMint(t)},O=1e3,b= exports.submitMintingRequests =async(e,t,{defaultBatchSize:s=1e3,chainName:a="imtbl-zkevm-testnet",maxNumberOfTries:l=3},n=console,g=1/0)=>{I();let m,P=0;for(;P++<g;){await new Promise(r=>{setTimeout(r,O)});let p=Math.min(_optionalChain([m, 'optionalAccess', _2 => _2.imx_remaining_mint_requests])?parseInt(m.imx_remaining_mint_requests,10):s,s);if(p===0&&m&&new Date(m.imx_mint_requests_limit_reset)>new Date){n.info(`minting limit reached, waiting for reset at ${_optionalChain([m, 'optionalAccess', _3 => _3.imx_mint_requests_limit_reset])}`);continue}p===0&&(n.info(`minting limit has been reset, use default batch size: ${s}`),m=void 0,p=s);let y=await e.getNextBatchForSubmission(p);if(y.length===0){n.info("no assets to mint");continue}let N=y.sort((r,d)=>r.contract_address>d.contract_address?1:-1).reduce((r,d)=>{if(r.length===0)return[{contractAddress:d.contract_address,assets:[d]}];let _=r[r.length-1];return _.contractAddress===d.contract_address&&_.assets.length<100?[...r.slice(0,-1),{..._,assets:[..._.assets,d]}]:[...r,{contractAddress:d.contract_address,assets:[d]}]},[]);m=await _asyncOptionalChain([(await Promise.allSettled(N.map(async({contractAddress:r,assets:d})=>{let _={chainName:a,contractAddress:r,createMintRequestRequest:{assets:d.map(i=>({reference_id:i.asset_id,owner_address:i.owner_address,metadata:i.metadata,token_id:i.token_id,amount:i.amount?`${i.amount}`:null}))}};try{let i=await t.createMintRequest(_);return n.info(`mintingResponse: ${JSON.stringify(i,null,2)}`),await e.updateMintingStatusToSubmitted(d.map(({id:o})=>o)),i}catch(i){if(n.error(i),w(i),i.code==="CONFLICT_ERROR"&&_optionalChain([i, 'access', _4 => _4.details, 'optionalAccess', _5 => _5.id])==="reference_id")try{await e.markAsConflict(i.details.values,r),await e.resetMintingStatus(d.map(({id:o})=>o).filter(o=>!i.details.values.includes(o)))}catch(o){n.error(o),w(i)}else{let{assetsToRetry:o,assetsExceededMaxNumberOfTries:x}=d.reduce((f,{tried_count:$=0,id:k})=>($<l?f.assetsToRetry.push(k):f.assetsExceededMaxNumberOfTries.push(k),f),{assetsToRetry:[],assetsExceededMaxNumberOfTries:[]});await e.markForRetry(o),await e.updateMintingStatusToSubmissionFailed(x)}return i}}))), 'access', async _6 => _6.reverse, 'call', async _7 => _7(), 'access', async _8 => _8.find, 'call', async _9 => _9(r=>r.status==="fulfilled"), 'optionalAccess', async _10 => _10.value])}},T= exports.processMint =async(e,t,s=console)=>{if(A(),t.event_name!=="imtbl_zkevm_mint_request_updated"){s.info(`${t.event_name} is not imtbl_zkevm_mint_request_updated, skip.`);return}let a=t.data.reference_id;if(!a)throw new Error("reference_id not found in webhook event");let l=t.data.contract_address;if(!l)throw new Error("contract_address not found in webhook event");t.data.status==="failed"&&s.error(`mint failed: ${JSON.stringify(t.data,null,2)}`);let n=await e.getMintingRequest(l,a);n||s.info(`minting request not found in the database, ${JSON.stringify(t.data)}`);let g=_optionalChain([n, 'optionalAccess', _11 => _11.wallet_address])||t.data.owner_address;if(!g)throw s.error("owner_address missing"),new Error("owner_address missing");await e.syncMintingStatus({tokenId:t.data.token_id,status:t.data.status,assetId:a,contractAddress:l,ownerAddress:g,metadataId:t.data.metadata_id,imtblZkevmMintRequestUpdatedId:t.event_id,error:t.data.error?JSON.stringify(t.data.error):null,amount:t.data.amount||null})};var z={zkevmMintRequestUpdated:async e=>{}},C= exports.MintingBackendModule =class{constructor(t){this.baseConfig=t.baseConfig,this.persistence=t.persistence,this.logger=t.logger||console,this.blockchainDataClient=new (0, _blockchaindata.BlockchainData)({baseConfig:t.baseConfig}),_metrics.setEnvironment.call(void 0, this.baseConfig.environment),this.baseConfig.publishableKey&&_metrics.setPublishableApiKey.call(void 0, this.baseConfig.publishableKey)}async recordMint(t){await R(this.persistence,t)}async submitMintingRequests(t){await b(this.persistence,this.blockchainDataClient,t)}async processMint(t,s=z){await _webhook.handle.call(void 0, t,this.baseConfig.environment,{zkevmMintRequestUpdated:async a=>{await T(this.persistence,a,this.logger),s.zkevmMintRequestUpdated&&s.zkevmMintRequestUpdated(a)}})}};if(typeof process<"u"&&process.on)try{process.on("uncaughtExceptionMonitor",E)}catch (e9){}exports.MintingBackendModule = C; exports.mintingPersistencePg = q; exports.mintingPersistencePrismaSqlite = U; exports.processMint = T; exports.recordMint = R; exports.submitMintingRequests = b;
package/dist/index.js CHANGED
@@ -1,86 +1,7 @@
1
- // src/index.ts
2
- import { BlockchainData } from "@imtbl/blockchain-data";
3
- import { handle } from "@imtbl/webhook";
4
- import { setEnvironment, setPublishableApiKey } from "@imtbl/metrics";
5
-
6
- // src/analytics/index.ts
7
- import { track } from "@imtbl/metrics";
8
- var moduleName = "minting_backend_sdk";
9
- var trackInitializePersistencePG = () => {
10
- try {
11
- track(moduleName, "initializePersistencePG");
12
- } catch {
13
- }
14
- };
15
- var trackInitializePersistencePrismaSqlite = () => {
16
- try {
17
- track(moduleName, "initializePersistencePrismaSqlite");
18
- } catch {
19
- }
20
- };
21
- var trackSubmitMintingRequests = () => {
22
- try {
23
- track(moduleName, "submitMintingRequests");
24
- } catch {
25
- }
26
- };
27
- var trackProcessMint = () => {
28
- try {
29
- track(moduleName, "processMint");
30
- } catch {
31
- }
32
- };
33
- var trackRecordMint = () => {
34
- try {
35
- track(moduleName, "recordMint");
36
- } catch {
37
- }
38
- };
39
- var trackError = (error) => {
40
- try {
41
- track(moduleName, "error", {
42
- name: error.name,
43
- message: error.message
44
- });
45
- } catch {
46
- }
47
- };
48
- var trackUncaughtException = (error, origin) => {
49
- try {
50
- track(moduleName, "error", {
51
- name: error.name,
52
- message: error.message,
53
- origin
54
- });
55
- } catch {
56
- }
57
- };
58
-
59
- // src/persistence/pg/postgres.ts
60
- var mintingPersistence = (client) => {
61
- trackInitializePersistencePG();
62
- return {
63
- recordMint: async (request) => {
64
- const r = await client.query(
65
- `
1
+ import{BlockchainData as v}from"@imtbl/blockchain-data";import{handle as D}from"@imtbl/webhook";import{setEnvironment as L,setPublishableApiKey as F}from"@imtbl/metrics";import{track as c}from"@imtbl/metrics";var u="minting_backend_sdk",M=()=>{try{c(u,"initializePersistencePG")}catch{}},h=()=>{try{c(u,"initializePersistencePrismaSqlite")}catch{}},I=()=>{try{c(u,"submitMintingRequests")}catch{}},A=()=>{try{c(u,"processMint")}catch{}},S=()=>{try{c(u,"recordMint")}catch{}},w=e=>{try{c(u,"error",{name:e.name,message:e.message})}catch{}},E=(e,t)=>{try{c(u,"error",{name:e.name,message:e.message,origin:t})}catch{}};var q=e=>(M(),{recordMint:async t=>{if((await e.query(`
66
2
  INSERT INTO im_assets (asset_id, contract_address, owner_address, metadata, amount, token_id)
67
3
  VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (asset_id, contract_address) DO NOTHING;
68
- `,
69
- [
70
- request.asset_id,
71
- request.contract_address,
72
- request.owner_address,
73
- request.metadata,
74
- request.amount,
75
- request.token_id
76
- ]
77
- );
78
- if (r.rowCount === 0) {
79
- throw new Error("Duplicated mint");
80
- }
81
- },
82
- getNextBatchForSubmission: async (limit) => {
83
- const res = await client.query(`
4
+ `,[t.asset_id,t.contract_address,t.owner_address,t.metadata,t.amount,t.token_id])).rowCount===0)throw new Error("Duplicated mint")},getNextBatchForSubmission:async t=>(await e.query(`
84
5
  WITH limited_assets AS (
85
6
  SELECT id
86
7
  FROM im_assets
@@ -93,16 +14,9 @@ var mintingPersistence = (client) => {
93
14
  WHERE minting_status IS NULL
94
15
  AND id IN (SELECT id FROM limited_assets)
95
16
  RETURNING *;
96
- `, [limit]);
97
- return res.rows;
98
- },
99
- updateMintingStatusToSubmitted: async (ids) => {
100
- await client.query(`
17
+ `,[t])).rows,updateMintingStatusToSubmitted:async t=>{await e.query(`
101
18
  UPDATE im_assets SET minting_status = $2 WHERE id = ANY($1);
102
- `, [ids, "submitted"]);
103
- },
104
- syncMintingStatus: async (submittedMintRequest) => {
105
- await client.query(`
19
+ `,[t,"submitted"])},syncMintingStatus:async t=>{await e.query(`
106
20
  INSERT INTO im_assets (
107
21
  asset_id,
108
22
  contract_address,
@@ -125,494 +39,18 @@ var mintingPersistence = (client) => {
125
39
  im_assets.last_imtbl_zkevm_mint_request_updated_id < $7 OR
126
40
  im_assets.last_imtbl_zkevm_mint_request_updated_id is null
127
41
  );
128
- `, [
129
- submittedMintRequest.assetId,
130
- submittedMintRequest.contractAddress,
131
- submittedMintRequest.ownerAddress,
132
- submittedMintRequest.tokenId,
133
- submittedMintRequest.status,
134
- submittedMintRequest.metadataId,
135
- submittedMintRequest.imtblZkevmMintRequestUpdatedId,
136
- submittedMintRequest.error,
137
- submittedMintRequest.amount
138
- ]);
139
- },
140
- markAsConflict: async (assetIds, contractAddress) => {
141
- await client.query(`
42
+ `,[t.assetId,t.contractAddress,t.ownerAddress,t.tokenId,t.status,t.metadataId,t.imtblZkevmMintRequestUpdatedId,t.error,t.amount])},markAsConflict:async(t,s)=>{await e.query(`
142
43
  UPDATE im_assets
143
44
  SET minting_status = 'conflicting'
144
45
  WHERE asset_id = ANY($1)
145
46
  AND contract_address = $2;
146
- `, [assetIds, contractAddress]);
147
- },
148
- resetMintingStatus: async (ids) => {
149
- await client.query(`
47
+ `,[t,s])},resetMintingStatus:async t=>{await e.query(`
150
48
  UPDATE im_assets SET minting_status = null WHERE id = ANY($1);
151
- `, [ids]);
152
- },
153
- markForRetry: async (ids) => {
154
- await client.query(`
49
+ `,[t])},markForRetry:async t=>{await e.query(`
155
50
  UPDATE im_assets
156
51
  SET minting_status = null, tried_count = tried_count + 1 WHERE id = ANY($1);
157
- `, [ids]);
158
- },
159
- updateMintingStatusToSubmissionFailed: async (ids) => {
160
- await client.query(`
52
+ `,[t])},updateMintingStatusToSubmissionFailed:async t=>{await e.query(`
161
53
  UPDATE im_assets SET minting_status = 'submission_failed' WHERE id = ANY($1);
162
- `, [ids]);
163
- },
164
- getMintingRequest: async (contractAddress, referenceId) => {
165
- const res = await client.query(`
54
+ `,[t])},getMintingRequest:async(t,s)=>(await e.query(`
166
55
  SELECT * FROM im_assets WHERE contract_address = $1 and asset_id = $2;
167
- `, [contractAddress, referenceId]);
168
- return res.rows[0] || null;
169
- }
170
- };
171
- };
172
-
173
- // src/persistence/prismaSqlite/sqlite.ts
174
- var mintingPersistence2 = (client) => {
175
- trackInitializePersistencePrismaSqlite();
176
- return {
177
- recordMint: async (request) => {
178
- const result = await client.imAssets.upsert({
179
- where: {
180
- im_assets_uindex: {
181
- assetId: request.asset_id,
182
- contractAddress: request.contract_address
183
- }
184
- },
185
- update: {},
186
- // Do nothing on conflict
187
- create: {
188
- assetId: request.asset_id,
189
- contractAddress: request.contract_address,
190
- ownerAddress: request.owner_address,
191
- metadata: JSON.stringify(request.metadata),
192
- // Serialize JSON metadata
193
- amount: request.amount || null,
194
- tokenId: request.token_id || null
195
- }
196
- });
197
- if (!result) {
198
- throw new Error("Duplicated mint");
199
- }
200
- },
201
- // WARNING: this is NOT concurrency safe. Please only call this method one at a time.
202
- getNextBatchForSubmission: async (limit) => {
203
- const assets = await client.imAssets.findMany({
204
- where: {
205
- mintingStatus: null
206
- },
207
- take: limit
208
- });
209
- const assetIds = assets.map((asset) => asset.id);
210
- await client.imAssets.updateMany({
211
- where: {
212
- id: {
213
- in: assetIds
214
- }
215
- },
216
- data: {
217
- mintingStatus: "submitting"
218
- }
219
- });
220
- const updatedAssets = await client.imAssets.findMany({
221
- where: {
222
- id: {
223
- in: assetIds
224
- }
225
- }
226
- });
227
- return updatedAssets.map((asset) => ({
228
- id: asset.id,
229
- contract_address: asset.contractAddress,
230
- wallet_address: asset.ownerAddress,
231
- asset_id: asset.assetId,
232
- metadata: asset.metadata ? JSON.parse(asset.metadata) : null,
233
- owner_address: asset.ownerAddress,
234
- tried_count: asset.triedCount,
235
- amount: asset.amount || null,
236
- token_id: asset.tokenId || null
237
- }));
238
- },
239
- updateMintingStatusToSubmitted: async (ids) => {
240
- await client.imAssets.updateMany({
241
- where: {
242
- id: {
243
- in: ids
244
- }
245
- },
246
- data: {
247
- mintingStatus: "submitted"
248
- }
249
- });
250
- },
251
- syncMintingStatus: async (submittedMintRequest) => {
252
- const existingAsset = await client.imAssets.findUnique({
253
- where: {
254
- im_assets_uindex: {
255
- assetId: submittedMintRequest.assetId,
256
- contractAddress: submittedMintRequest.contractAddress
257
- }
258
- }
259
- });
260
- if (existingAsset && (existingAsset.lastImtblZkevmMintRequestUpdatedId === null || existingAsset.lastImtblZkevmMintRequestUpdatedId < submittedMintRequest.imtblZkevmMintRequestUpdatedId)) {
261
- await client.imAssets.update({
262
- where: {
263
- im_assets_uindex: {
264
- assetId: submittedMintRequest.assetId,
265
- contractAddress: submittedMintRequest.contractAddress
266
- }
267
- },
268
- data: {
269
- ownerAddress: submittedMintRequest.ownerAddress,
270
- tokenId: submittedMintRequest.tokenId,
271
- mintingStatus: submittedMintRequest.status,
272
- metadataId: submittedMintRequest.metadataId,
273
- lastImtblZkevmMintRequestUpdatedId: submittedMintRequest.imtblZkevmMintRequestUpdatedId,
274
- error: submittedMintRequest.error,
275
- amount: submittedMintRequest.amount || null
276
- }
277
- });
278
- } else if (!existingAsset) {
279
- await client.imAssets.create({
280
- data: {
281
- assetId: submittedMintRequest.assetId,
282
- contractAddress: submittedMintRequest.contractAddress,
283
- ownerAddress: submittedMintRequest.ownerAddress,
284
- tokenId: submittedMintRequest.tokenId,
285
- mintingStatus: submittedMintRequest.status,
286
- metadataId: submittedMintRequest.metadataId,
287
- lastImtblZkevmMintRequestUpdatedId: submittedMintRequest.imtblZkevmMintRequestUpdatedId,
288
- error: submittedMintRequest.error,
289
- amount: submittedMintRequest.amount || null
290
- }
291
- });
292
- }
293
- },
294
- updateMintingStatusToSubmissionFailed: async (ids) => {
295
- await client.imAssets.updateMany({
296
- where: {
297
- id: {
298
- in: ids
299
- }
300
- },
301
- data: {
302
- mintingStatus: "submission_failed"
303
- }
304
- });
305
- },
306
- markAsConflict: async (assetIds, contractAddress) => {
307
- await client.imAssets.updateMany({
308
- where: {
309
- assetId: {
310
- in: assetIds
311
- // Targets assets where assetId is in the provided list
312
- },
313
- contractAddress
314
- // Additional condition for contract address
315
- },
316
- data: {
317
- mintingStatus: "conflicting"
318
- // Set the new status
319
- }
320
- });
321
- },
322
- resetMintingStatus: async (ids) => {
323
- await client.imAssets.updateMany({
324
- where: {
325
- id: {
326
- in: ids
327
- // Condition to match ids
328
- }
329
- },
330
- data: {
331
- mintingStatus: null
332
- // Setting minting_status to null
333
- }
334
- });
335
- },
336
- // this method is not concurrency safe
337
- markForRetry: async (ids) => {
338
- const assets = await client.imAssets.findMany({
339
- where: {
340
- id: {
341
- in: ids
342
- }
343
- },
344
- select: {
345
- id: true,
346
- triedCount: true
347
- // Assuming the field is named triedCount
348
- }
349
- });
350
- for (const asset of assets) {
351
- await client.imAssets.update({
352
- where: {
353
- id: asset.id
354
- },
355
- data: {
356
- mintingStatus: null,
357
- triedCount: asset.triedCount + 1
358
- }
359
- });
360
- }
361
- },
362
- getMintingRequest: async (contractAddress, referenceId) => {
363
- const asset = await client.imAssets.findFirst({
364
- where: {
365
- contractAddress,
366
- assetId: referenceId
367
- }
368
- });
369
- if (!asset) {
370
- return null;
371
- }
372
- return {
373
- asset_id: asset.assetId,
374
- contract_address: asset.contractAddress,
375
- id: asset.id,
376
- metadata: asset.metadata ? JSON.parse(asset.metadata) : null,
377
- owner_address: asset.ownerAddress,
378
- tried_count: asset.triedCount,
379
- wallet_address: asset.ownerAddress,
380
- amount: asset.amount || null
381
- };
382
- }
383
- };
384
- };
385
-
386
- // src/minting.ts
387
- var recordMint = async (mintingPersistence3, mintRequest) => {
388
- trackRecordMint();
389
- mintingPersistence3.recordMint(mintRequest);
390
- };
391
- var defaultMintingDelay = 1e3;
392
- var submitMintingRequests = async (mintingPersistence3, blockchainDataSDKClient, {
393
- defaultBatchSize = 1e3,
394
- chainName = "imtbl-zkevm-testnet",
395
- maxNumberOfTries = 3
396
- }, logger = console, maxLoops = Infinity) => {
397
- trackSubmitMintingRequests();
398
- let mintingResponse;
399
- let numberOfLoops = 0;
400
- while (numberOfLoops++ < maxLoops) {
401
- await new Promise((resolve) => {
402
- setTimeout(resolve, defaultMintingDelay);
403
- });
404
- let batchSize = Math.min(
405
- mintingResponse?.imx_remaining_mint_requests ? parseInt(mintingResponse.imx_remaining_mint_requests, 10) : defaultBatchSize,
406
- defaultBatchSize
407
- );
408
- if (batchSize === 0 && mintingResponse && new Date(mintingResponse.imx_mint_requests_limit_reset) > /* @__PURE__ */ new Date()) {
409
- logger.info(
410
- `minting limit reached, waiting for reset at ${mintingResponse?.imx_mint_requests_limit_reset}`
411
- );
412
- continue;
413
- }
414
- if (batchSize === 0) {
415
- logger.info(
416
- `minting limit has been reset, use default batch size: ${defaultBatchSize}`
417
- );
418
- mintingResponse = void 0;
419
- batchSize = defaultBatchSize;
420
- }
421
- const pendingMints = await mintingPersistence3.getNextBatchForSubmission(
422
- batchSize
423
- );
424
- if (pendingMints.length === 0) {
425
- logger.info("no assets to mint");
426
- continue;
427
- }
428
- const chunkedAssets = pendingMints.sort(
429
- (a, b) => a.contract_address > b.contract_address ? 1 : -1
430
- ).reduce((acc, row) => {
431
- if (acc.length === 0) {
432
- return [{ contractAddress: row.contract_address, assets: [row] }];
433
- }
434
- const lastBatch = acc[acc.length - 1];
435
- if (lastBatch.contractAddress === row.contract_address && lastBatch.assets.length < 100) {
436
- return [...acc.slice(0, -1), { ...lastBatch, assets: [...lastBatch.assets, row] }];
437
- }
438
- return [...acc, { contractAddress: row.contract_address, assets: [row] }];
439
- }, []);
440
- const mintingResults = await Promise.allSettled(
441
- chunkedAssets.map(
442
- async ({ contractAddress, assets }) => {
443
- const mintingRequest = {
444
- chainName,
445
- contractAddress,
446
- createMintRequestRequest: {
447
- assets: assets.map((row) => ({
448
- reference_id: row.asset_id,
449
- owner_address: row.owner_address,
450
- metadata: row.metadata,
451
- token_id: row.token_id,
452
- amount: row.amount ? `${row.amount}` : null
453
- }))
454
- }
455
- };
456
- try {
457
- const response = await blockchainDataSDKClient.createMintRequest(
458
- mintingRequest
459
- );
460
- logger.info(
461
- `mintingResponse: ${JSON.stringify(response, null, 2)}`
462
- );
463
- await mintingPersistence3.updateMintingStatusToSubmitted(
464
- assets.map(({ id }) => id)
465
- );
466
- return response;
467
- } catch (e) {
468
- logger.error(e);
469
- trackError(e);
470
- if (e.code === "CONFLICT_ERROR" && e.details?.id === "reference_id") {
471
- try {
472
- await mintingPersistence3.markAsConflict(
473
- e.details.values,
474
- contractAddress
475
- );
476
- await mintingPersistence3.resetMintingStatus(
477
- assets.map(({ id }) => id).filter((id) => !e.details.values.includes(id))
478
- );
479
- } catch (e2) {
480
- logger.error(e2);
481
- trackError(e);
482
- }
483
- } else {
484
- const { assetsToRetry, assetsExceededMaxNumberOfTries } = assets.reduce(
485
- (acc, { tried_count = 0, id }) => {
486
- if (tried_count < maxNumberOfTries) {
487
- acc.assetsToRetry.push(id);
488
- } else {
489
- acc.assetsExceededMaxNumberOfTries.push(id);
490
- }
491
- return acc;
492
- },
493
- {
494
- assetsToRetry: [],
495
- assetsExceededMaxNumberOfTries: []
496
- }
497
- );
498
- await mintingPersistence3.markForRetry(
499
- assetsToRetry
500
- );
501
- await mintingPersistence3.updateMintingStatusToSubmissionFailed(
502
- assetsExceededMaxNumberOfTries
503
- );
504
- }
505
- return e;
506
- }
507
- }
508
- )
509
- );
510
- mintingResponse = mintingResults.reverse().find(
511
- (r) => r.status === "fulfilled"
512
- )?.value;
513
- }
514
- };
515
- var processMint = async (mintingPersistence3, event, logger = console) => {
516
- trackProcessMint();
517
- if (event.event_name !== "imtbl_zkevm_mint_request_updated") {
518
- logger.info(
519
- `${event.event_name} is not imtbl_zkevm_mint_request_updated, skip.`
520
- );
521
- return;
522
- }
523
- const referenceId = event.data.reference_id;
524
- if (!referenceId) {
525
- throw new Error("reference_id not found in webhook event");
526
- }
527
- const contractAddress = event.data.contract_address;
528
- if (!contractAddress) {
529
- throw new Error("contract_address not found in webhook event");
530
- }
531
- if (event.data.status === "failed") {
532
- logger.error(`mint failed: ${JSON.stringify(event.data, null, 2)}`);
533
- }
534
- const mintReq = await mintingPersistence3.getMintingRequest(
535
- contractAddress,
536
- referenceId
537
- );
538
- if (!mintReq) {
539
- logger.info(
540
- `minting request not found in the database, ${JSON.stringify(event.data)}`
541
- );
542
- }
543
- const ownerAddress = mintReq?.wallet_address || event.data.owner_address;
544
- if (!ownerAddress) {
545
- logger.error("owner_address missing");
546
- throw new Error("owner_address missing");
547
- }
548
- await mintingPersistence3.syncMintingStatus({
549
- tokenId: event.data.token_id,
550
- status: event.data.status,
551
- assetId: referenceId,
552
- contractAddress,
553
- ownerAddress,
554
- metadataId: event.data.metadata_id,
555
- imtblZkevmMintRequestUpdatedId: event.event_id,
556
- error: event.data.error ? JSON.stringify(event.data.error) : null,
557
- amount: event.data.amount || null
558
- });
559
- };
560
-
561
- // src/index.ts
562
- var noopHandlers = {
563
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
564
- zkevmMintRequestUpdated: async (event) => {
565
- }
566
- };
567
- var MintingBackendModule = class {
568
- baseConfig;
569
- persistence;
570
- blockchainDataClient;
571
- logger;
572
- constructor(config) {
573
- this.baseConfig = config.baseConfig;
574
- this.persistence = config.persistence;
575
- this.logger = config.logger || console;
576
- this.blockchainDataClient = new BlockchainData({
577
- baseConfig: config.baseConfig
578
- });
579
- setEnvironment(this.baseConfig.environment);
580
- if (this.baseConfig.publishableKey) {
581
- setPublishableApiKey(this.baseConfig.publishableKey);
582
- }
583
- }
584
- async recordMint(mintRequest) {
585
- await recordMint(this.persistence, mintRequest);
586
- }
587
- async submitMintingRequests(config) {
588
- await submitMintingRequests(
589
- this.persistence,
590
- this.blockchainDataClient,
591
- config
592
- );
593
- }
594
- async processMint(body, otherHandlers = noopHandlers) {
595
- await handle(body, this.baseConfig.environment, {
596
- zkevmMintRequestUpdated: async (event) => {
597
- await processMint(this.persistence, event, this.logger);
598
- if (otherHandlers.zkevmMintRequestUpdated) {
599
- otherHandlers.zkevmMintRequestUpdated(event);
600
- }
601
- }
602
- });
603
- }
604
- };
605
- if (typeof process !== "undefined" && process.on) {
606
- try {
607
- process.on("uncaughtExceptionMonitor", trackUncaughtException);
608
- } catch {
609
- }
610
- }
611
- export {
612
- MintingBackendModule,
613
- mintingPersistence as mintingPersistencePg,
614
- mintingPersistence2 as mintingPersistencePrismaSqlite,
615
- processMint,
616
- recordMint,
617
- submitMintingRequests
618
- };
56
+ `,[t,s])).rows[0]||null});var U=e=>(h(),{recordMint:async t=>{if(!await e.imAssets.upsert({where:{im_assets_uindex:{assetId:t.asset_id,contractAddress:t.contract_address}},update:{},create:{assetId:t.asset_id,contractAddress:t.contract_address,ownerAddress:t.owner_address,metadata:JSON.stringify(t.metadata),amount:t.amount||null,tokenId:t.token_id||null}}))throw new Error("Duplicated mint")},getNextBatchForSubmission:async t=>{let a=(await e.imAssets.findMany({where:{mintingStatus:null},take:t})).map(n=>n.id);return await e.imAssets.updateMany({where:{id:{in:a}},data:{mintingStatus:"submitting"}}),(await e.imAssets.findMany({where:{id:{in:a}}})).map(n=>({id:n.id,contract_address:n.contractAddress,wallet_address:n.ownerAddress,asset_id:n.assetId,metadata:n.metadata?JSON.parse(n.metadata):null,owner_address:n.ownerAddress,tried_count:n.triedCount,amount:n.amount||null,token_id:n.tokenId||null}))},updateMintingStatusToSubmitted:async t=>{await e.imAssets.updateMany({where:{id:{in:t}},data:{mintingStatus:"submitted"}})},syncMintingStatus:async t=>{let s=await e.imAssets.findUnique({where:{im_assets_uindex:{assetId:t.assetId,contractAddress:t.contractAddress}}});s&&(s.lastImtblZkevmMintRequestUpdatedId===null||s.lastImtblZkevmMintRequestUpdatedId<t.imtblZkevmMintRequestUpdatedId)?await e.imAssets.update({where:{im_assets_uindex:{assetId:t.assetId,contractAddress:t.contractAddress}},data:{ownerAddress:t.ownerAddress,tokenId:t.tokenId,mintingStatus:t.status,metadataId:t.metadataId,lastImtblZkevmMintRequestUpdatedId:t.imtblZkevmMintRequestUpdatedId,error:t.error,amount:t.amount||null}}):s||await e.imAssets.create({data:{assetId:t.assetId,contractAddress:t.contractAddress,ownerAddress:t.ownerAddress,tokenId:t.tokenId,mintingStatus:t.status,metadataId:t.metadataId,lastImtblZkevmMintRequestUpdatedId:t.imtblZkevmMintRequestUpdatedId,error:t.error,amount:t.amount||null}})},updateMintingStatusToSubmissionFailed:async t=>{await e.imAssets.updateMany({where:{id:{in:t}},data:{mintingStatus:"submission_failed"}})},markAsConflict:async(t,s)=>{await e.imAssets.updateMany({where:{assetId:{in:t},contractAddress:s},data:{mintingStatus:"conflicting"}})},resetMintingStatus:async t=>{await e.imAssets.updateMany({where:{id:{in:t}},data:{mintingStatus:null}})},markForRetry:async t=>{let s=await e.imAssets.findMany({where:{id:{in:t}},select:{id:!0,triedCount:!0}});for(let a of s)await e.imAssets.update({where:{id:a.id},data:{mintingStatus:null,triedCount:a.triedCount+1}})},getMintingRequest:async(t,s)=>{let a=await e.imAssets.findFirst({where:{contractAddress:t,assetId:s}});return a?{asset_id:a.assetId,contract_address:a.contractAddress,id:a.id,metadata:a.metadata?JSON.parse(a.metadata):null,owner_address:a.ownerAddress,tried_count:a.triedCount,wallet_address:a.ownerAddress,amount:a.amount||null}:null}});var R=async(e,t)=>{S(),e.recordMint(t)},O=1e3,b=async(e,t,{defaultBatchSize:s=1e3,chainName:a="imtbl-zkevm-testnet",maxNumberOfTries:l=3},n=console,g=1/0)=>{I();let m,P=0;for(;P++<g;){await new Promise(r=>{setTimeout(r,O)});let p=Math.min(m?.imx_remaining_mint_requests?parseInt(m.imx_remaining_mint_requests,10):s,s);if(p===0&&m&&new Date(m.imx_mint_requests_limit_reset)>new Date){n.info(`minting limit reached, waiting for reset at ${m?.imx_mint_requests_limit_reset}`);continue}p===0&&(n.info(`minting limit has been reset, use default batch size: ${s}`),m=void 0,p=s);let y=await e.getNextBatchForSubmission(p);if(y.length===0){n.info("no assets to mint");continue}let N=y.sort((r,d)=>r.contract_address>d.contract_address?1:-1).reduce((r,d)=>{if(r.length===0)return[{contractAddress:d.contract_address,assets:[d]}];let _=r[r.length-1];return _.contractAddress===d.contract_address&&_.assets.length<100?[...r.slice(0,-1),{..._,assets:[..._.assets,d]}]:[...r,{contractAddress:d.contract_address,assets:[d]}]},[]);m=(await Promise.allSettled(N.map(async({contractAddress:r,assets:d})=>{let _={chainName:a,contractAddress:r,createMintRequestRequest:{assets:d.map(i=>({reference_id:i.asset_id,owner_address:i.owner_address,metadata:i.metadata,token_id:i.token_id,amount:i.amount?`${i.amount}`:null}))}};try{let i=await t.createMintRequest(_);return n.info(`mintingResponse: ${JSON.stringify(i,null,2)}`),await e.updateMintingStatusToSubmitted(d.map(({id:o})=>o)),i}catch(i){if(n.error(i),w(i),i.code==="CONFLICT_ERROR"&&i.details?.id==="reference_id")try{await e.markAsConflict(i.details.values,r),await e.resetMintingStatus(d.map(({id:o})=>o).filter(o=>!i.details.values.includes(o)))}catch(o){n.error(o),w(i)}else{let{assetsToRetry:o,assetsExceededMaxNumberOfTries:x}=d.reduce((f,{tried_count:$=0,id:k})=>($<l?f.assetsToRetry.push(k):f.assetsExceededMaxNumberOfTries.push(k),f),{assetsToRetry:[],assetsExceededMaxNumberOfTries:[]});await e.markForRetry(o),await e.updateMintingStatusToSubmissionFailed(x)}return i}}))).reverse().find(r=>r.status==="fulfilled")?.value}},T=async(e,t,s=console)=>{if(A(),t.event_name!=="imtbl_zkevm_mint_request_updated"){s.info(`${t.event_name} is not imtbl_zkevm_mint_request_updated, skip.`);return}let a=t.data.reference_id;if(!a)throw new Error("reference_id not found in webhook event");let l=t.data.contract_address;if(!l)throw new Error("contract_address not found in webhook event");t.data.status==="failed"&&s.error(`mint failed: ${JSON.stringify(t.data,null,2)}`);let n=await e.getMintingRequest(l,a);n||s.info(`minting request not found in the database, ${JSON.stringify(t.data)}`);let g=n?.wallet_address||t.data.owner_address;if(!g)throw s.error("owner_address missing"),new Error("owner_address missing");await e.syncMintingStatus({tokenId:t.data.token_id,status:t.data.status,assetId:a,contractAddress:l,ownerAddress:g,metadataId:t.data.metadata_id,imtblZkevmMintRequestUpdatedId:t.event_id,error:t.data.error?JSON.stringify(t.data.error):null,amount:t.data.amount||null})};var z={zkevmMintRequestUpdated:async e=>{}},C=class{baseConfig;persistence;blockchainDataClient;logger;constructor(t){this.baseConfig=t.baseConfig,this.persistence=t.persistence,this.logger=t.logger||console,this.blockchainDataClient=new v({baseConfig:t.baseConfig}),L(this.baseConfig.environment),this.baseConfig.publishableKey&&F(this.baseConfig.publishableKey)}async recordMint(t){await R(this.persistence,t)}async submitMintingRequests(t){await b(this.persistence,this.blockchainDataClient,t)}async processMint(t,s=z){await D(t,this.baseConfig.environment,{zkevmMintRequestUpdated:async a=>{await T(this.persistence,a,this.logger),s.zkevmMintRequestUpdated&&s.zkevmMintRequestUpdated(a)}})}};if(typeof process<"u"&&process.on)try{process.on("uncaughtExceptionMonitor",E)}catch{}export{C as MintingBackendModule,q as mintingPersistencePg,U as mintingPersistencePrismaSqlite,T as processMint,R as recordMint,b as submitMintingRequests};
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@imtbl/minting-backend",
3
3
  "description": "minting backend utilising Immutable Minting API",
4
- "version": "2.0.0-alpha.7",
4
+ "version": "2.0.0-alpha.8",
5
5
  "author": "Immutable",
6
6
  "bugs": "https://github.com/immutable/ts-immutable-sdk/issues",
7
7
  "dependencies": {
8
- "@imtbl/blockchain-data": "2.0.0-alpha.7",
9
- "@imtbl/config": "2.0.0-alpha.7",
10
- "@imtbl/generated-clients": "2.0.0-alpha.7",
11
- "@imtbl/metrics": "2.0.0-alpha.7",
12
- "@imtbl/webhook": "2.0.0-alpha.7",
8
+ "@imtbl/blockchain-data": "2.0.0-alpha.8",
9
+ "@imtbl/config": "2.0.0-alpha.8",
10
+ "@imtbl/generated-clients": "2.0.0-alpha.8",
11
+ "@imtbl/metrics": "2.0.0-alpha.8",
12
+ "@imtbl/webhook": "2.0.0-alpha.8",
13
13
  "uuid": "^8.3.2"
14
14
  },
15
15
  "devDependencies": {