@render-foundation/utils 0.0.232-beta.0 → 0.0.232

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 (34) hide show
  1. package/lib/cjs/burn/burnAnalysis.js +101 -0
  2. package/lib/cjs/burn/burnAnalysis.js.map +1 -0
  3. package/lib/cjs/burn/burnCalculations.js +140 -0
  4. package/lib/cjs/burn/burnCalculations.js.map +1 -0
  5. package/lib/cjs/client/pg/v2Client.js +222 -122
  6. package/lib/cjs/client/pg/v2Client.js.map +1 -1
  7. package/lib/cjs/index.js +2 -5
  8. package/lib/cjs/index.js.map +1 -1
  9. package/lib/esm/src/burn/burnAnalysis.js +90 -0
  10. package/lib/esm/src/burn/burnAnalysis.js.map +1 -0
  11. package/lib/esm/src/burn/burnCalculations.js +124 -0
  12. package/lib/esm/src/burn/burnCalculations.js.map +1 -0
  13. package/lib/esm/src/client/pg/v2Client.js +214 -109
  14. package/lib/esm/src/client/pg/v2Client.js.map +1 -1
  15. package/lib/esm/src/index.js +2 -5
  16. package/lib/esm/src/index.js.map +1 -1
  17. package/lib/esm/tsconfig.esm.tsbuildinfo +1 -1
  18. package/lib/types/src/burn/burnAnalysis.d.ts +51 -0
  19. package/lib/types/src/burn/burnAnalysis.d.ts.map +1 -0
  20. package/lib/types/src/burn/burnCalculations.d.ts +52 -0
  21. package/lib/types/src/burn/burnCalculations.d.ts.map +1 -0
  22. package/lib/types/src/client/pg/v2Client.d.ts +41 -31
  23. package/lib/types/src/client/pg/v2Client.d.ts.map +1 -1
  24. package/lib/types/src/dbTypesV2.d.ts +40 -16
  25. package/lib/types/src/dbTypesV2.d.ts.map +1 -1
  26. package/lib/types/src/index.d.ts +2 -2
  27. package/lib/types/src/index.d.ts.map +1 -1
  28. package/package.json +1 -1
  29. package/lib/cjs/client/dispersed.js +0 -123
  30. package/lib/cjs/client/dispersed.js.map +0 -1
  31. package/lib/esm/src/client/dispersed.js +0 -108
  32. package/lib/esm/src/client/dispersed.js.map +0 -1
  33. package/lib/types/src/client/dispersed.d.ts +0 -91
  34. package/lib/types/src/client/dispersed.d.ts.map +0 -1
@@ -35,7 +35,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
35
35
  return (mod && mod.__esModule) ? mod : { "default": mod };
36
36
  };
37
37
  Object.defineProperty(exports, "__esModule", { value: true });
38
- exports.pgClient = exports.batchFinishedAtMedian = exports.DISPERSED_SETTLEMENT_TABLE = exports.GRANT_BURN_TABLE = exports.GRANT_BURN_ID_TABLE = exports.USER_GRANT_SPEND_TABLE = exports.CURRENT_USER_GRANT_SPEND_TABLE = exports.POLYGON_UPGRADE_TABLE = exports.ENTITY_EPOCH_INFO_TABLE = exports.BURN_ADJUSTMENT_TABLE = exports.NETWORK_REVENUE_TABLE = exports.BRIDGE_TRANSFER_TABLE = exports.BURN_TABLE = exports.JOB_TABLE = exports.JOB_ID_TABLE = exports.LIABILITY_TABLE = exports.SOL_TRANSFER_TABLE = exports.ENTITY_TABLE = exports.MANUAL_BURN_TABLE = exports.EPOCH_TABLE = exports.SOL_TX_TABLE = void 0;
38
+ exports.pgClient = exports.batchFinishedAtMedian = exports.GRANT_BURN_TABLE = exports.GRANT_BURN_ID_TABLE = exports.USER_GRANT_SPEND_TABLE = exports.CURRENT_USER_GRANT_SPEND_TABLE = exports.POLYGON_UPGRADE_TABLE = exports.ENTITY_EPOCH_INFO_TABLE = exports.TX_BURN_ADJUSTMENT_TABLE = exports.JOB_BURN_ADJUSTMENT_TABLE = exports.NETWORK_REVENUE_TABLE = exports.BRIDGE_TRANSFER_TABLE = exports.BURN_TABLE = exports.JOB_TABLE = exports.JOB_ID_TABLE = exports.LIABILITY_ADJUSTMENT_BATCH_TABLE = exports.LIABILITY_ADJUSTMENT_TABLE = exports.LIABILITY_TABLE = exports.SOL_TRANSFER_TABLE = exports.ENTITY_TABLE = exports.MANUAL_BURN_TABLE = exports.EPOCH_TABLE = exports.SOL_TX_TABLE = void 0;
39
39
  const kysely_1 = require("kysely");
40
40
  const logger_1 = require("../../logger");
41
41
  const pg_1 = require("pg");
@@ -47,19 +47,21 @@ exports.MANUAL_BURN_TABLE = 'manual_burn';
47
47
  exports.ENTITY_TABLE = 'entity';
48
48
  exports.SOL_TRANSFER_TABLE = 'sol_transfer1';
49
49
  exports.LIABILITY_TABLE = 'liability';
50
+ exports.LIABILITY_ADJUSTMENT_TABLE = 'liability_adjustment';
51
+ exports.LIABILITY_ADJUSTMENT_BATCH_TABLE = 'liability_adjustment_batch';
50
52
  exports.JOB_ID_TABLE = 'job_id';
51
53
  exports.JOB_TABLE = 'job';
52
54
  exports.BURN_TABLE = 'burn';
53
55
  exports.BRIDGE_TRANSFER_TABLE = 'bridge_transfer';
54
56
  exports.NETWORK_REVENUE_TABLE = 'network_revenue';
55
- exports.BURN_ADJUSTMENT_TABLE = 'burn_adjustment';
57
+ exports.JOB_BURN_ADJUSTMENT_TABLE = 'job_burn_adjustment';
58
+ exports.TX_BURN_ADJUSTMENT_TABLE = 'tx_burn_adjustment';
56
59
  exports.ENTITY_EPOCH_INFO_TABLE = 'entity_epoch_info';
57
60
  exports.POLYGON_UPGRADE_TABLE = 'polygon_upgrade';
58
61
  exports.CURRENT_USER_GRANT_SPEND_TABLE = 'current_user_grant_spend';
59
62
  exports.USER_GRANT_SPEND_TABLE = 'user_grant_spend';
60
63
  exports.GRANT_BURN_ID_TABLE = 'grant_burn_id';
61
64
  exports.GRANT_BURN_TABLE = 'grant_burn';
62
- exports.DISPERSED_SETTLEMENT_TABLE = 'dispersed_settlement';
63
65
  BigInt.prototype.toJSON = function () {
64
66
  return this.toString();
65
67
  };
@@ -356,7 +358,11 @@ const pgClient = (config) => {
356
358
  sig: solTx,
357
359
  executedAt,
358
360
  }, log);
359
- const { id } = yield db
361
+ // sol_transfer1 already has UNIQUE(sol_tx_id, from_entity_id,
362
+ // to_entity_id, symbol). Without ON CONFLICT a retry of the record step
363
+ // throws a unique violation and wedges the BullMQ job; make it an
364
+ // idempotent no-op and re-fetch the existing id instead.
365
+ let transferRow = yield db
360
366
  .insertInto(exports.SOL_TRANSFER_TABLE)
361
367
  .values({
362
368
  id: dbId,
@@ -367,9 +373,23 @@ const pgClient = (config) => {
367
373
  symbol,
368
374
  amount_payed: amountPayed,
369
375
  })
376
+ .onConflict((oc) => oc
377
+ .columns(['sol_tx_id', 'from_entity_id', 'to_entity_id', 'symbol'])
378
+ .doNothing())
370
379
  .returning((eb) => ['id as id'])
371
- .executeTakeFirstOrThrow();
372
- transferId = BigInt(id);
380
+ .executeTakeFirst();
381
+ if (!transferRow) {
382
+ log.warn(`sol_transfer for sol_tx_id ${solTxId} ${fromEntityId}->${toEntityId} ${symbol} already exists; reusing existing id (idempotent retry)`);
383
+ transferRow = yield db
384
+ .selectFrom(exports.SOL_TRANSFER_TABLE)
385
+ .select((eb) => ['id as id'])
386
+ .where('sol_tx_id', '=', solTxId)
387
+ .where('from_entity_id', '=', fromEntityId)
388
+ .where('to_entity_id', '=', toEntityId)
389
+ .where('symbol', '=', symbol)
390
+ .executeTakeFirstOrThrow();
391
+ }
392
+ transferId = BigInt(transferRow.id);
373
393
  }
374
394
  ids.push({ transferId, liabilityId });
375
395
  }
@@ -691,9 +711,36 @@ const pgClient = (config) => {
691
711
  }
692
712
  return b.as('x');
693
713
  }, (join) => join.onRef('y.sol_key', '=', 'x.sol_key'))
714
+ // Standing per-wallet/channel corrections (migration 36). NO epoch filter — an adjustment is
715
+ // a permanent delta on outstanding, not tied to a payout window; payments net it via `payed`.
716
+ .fullJoin((eb) => {
717
+ let b = eb
718
+ .selectFrom(exports.LIABILITY_ADJUSTMENT_TABLE)
719
+ .select(({ fn }) => fn.sum(`amount_adjustment`).as('adj'))
720
+ .innerJoin(exports.ENTITY_TABLE, `${exports.LIABILITY_ADJUSTMENT_TABLE}.entity_id`, 'entity.id')
721
+ .select(['entity.sol_key'])
722
+ .groupBy('entity.sol_key');
723
+ if (f.banned) {
724
+ b = b.where('entity.banned', '=', true);
725
+ }
726
+ else {
727
+ b = b.where('entity.banned', '=', false);
728
+ }
729
+ if (f.channels && f.channels.length > 0) {
730
+ b = b.where((eb) => eb.or(f.channels.map((c) => eb('liability_adjustment.channel', '=', c))));
731
+ }
732
+ else {
733
+ b = b.where((eb) => eb.or([
734
+ eb('liability_adjustment.channel', '=', 'node_operator'),
735
+ eb('liability_adjustment.channel', '=', 'availability'),
736
+ ]));
737
+ }
738
+ return b.as('a');
739
+ }, (join) => join.on((0, kysely_1.sql) `"a"."sol_key" = coalesce("y"."sol_key", "x"."sol_key")`))
694
740
  .select(({ fn }) => [
695
- fn.coalesce('y.sol_key', 'x.sol_key').as('sol_key'),
696
- (0, kysely_1.sql) `${fn.coalesce(`x.due`, (0, kysely_1.sql) `0`)} -
741
+ fn.coalesce('y.sol_key', 'x.sol_key', 'a.sol_key').as('sol_key'),
742
+ (0, kysely_1.sql) `${fn.coalesce(`x.due`, (0, kysely_1.sql) `0`)} +
743
+ ${fn.coalesce(`a.adj`, (0, kysely_1.sql) `0`)} -
697
744
  ${fn.coalesce(`y.payed`, (0, kysely_1.sql) `0`)}`.as('out'),
698
745
  ])
699
746
  .as('z'))
@@ -714,6 +761,59 @@ const pgClient = (config) => {
714
761
  return out;
715
762
  });
716
763
  },
764
+ addLiabilityAdjustments(batch, rows, log = logger_1.consoleLogger) {
765
+ return __awaiter(this, void 0, void 0, function* () {
766
+ return yield db.transaction().execute((trx) => __awaiter(this, void 0, void 0, function* () {
767
+ var _a;
768
+ // Upsert the batch by its label (idempotent re-runs; label is the run identity).
769
+ const b = yield trx
770
+ .insertInto(exports.LIABILITY_ADJUSTMENT_BATCH_TABLE)
771
+ .values({
772
+ label: batch.label,
773
+ description: batch.description,
774
+ created_by: batch.createdBy,
775
+ })
776
+ .onConflict((oc) => oc.column('label').doUpdateSet({
777
+ description: batch.description,
778
+ created_by: batch.createdBy,
779
+ }))
780
+ .returning('id')
781
+ .executeTakeFirstOrThrow();
782
+ const batchId = BigInt(b.id);
783
+ let inserted = 0;
784
+ let skipped = 0;
785
+ let net = BigInt(0);
786
+ let positive = BigInt(0);
787
+ let negative = BigInt(0);
788
+ for (const r of rows) {
789
+ const entityId = yield getOrInsertEntity(trx, { solKey: r.solKey }, log);
790
+ const res = yield trx
791
+ .insertInto(exports.LIABILITY_ADJUSTMENT_TABLE)
792
+ .values({
793
+ entity_id: entityId,
794
+ channel: r.channel,
795
+ amount_adjustment: r.amountAdjustment,
796
+ batch_id: batchId,
797
+ })
798
+ // unique (entity_id, channel, batch_id) → a re-run of the same batch is a no-op
799
+ .onConflict((oc) => oc.columns(['entity_id', 'channel', 'batch_id']).doNothing())
800
+ .executeTakeFirst();
801
+ if (((_a = res.numInsertedOrUpdatedRows) !== null && _a !== void 0 ? _a : BigInt(0)) > BigInt(0))
802
+ inserted++;
803
+ else
804
+ skipped++;
805
+ net += r.amountAdjustment;
806
+ if (r.amountAdjustment > BigInt(0))
807
+ positive += r.amountAdjustment;
808
+ else
809
+ negative += r.amountAdjustment;
810
+ }
811
+ log.info(`liability_adjustment batch '${batch.label}' (id ${batchId}): ` +
812
+ `inserted ${inserted}, skipped ${skipped}, net ${net} (+${positive} / ${negative})`);
813
+ return { batchId, inserted, skipped, net, positive, negative };
814
+ }));
815
+ });
816
+ },
717
817
  fetchNodeOperatorEpoch(epochId, log = logger_1.consoleLogger) {
718
818
  var _a;
719
819
  return __awaiter(this, void 0, void 0, function* () {
@@ -1024,7 +1124,7 @@ const pgClient = (config) => {
1024
1124
  let buyBurnId;
1025
1125
  let grantBurnId;
1026
1126
  if (p.buyBurn) {
1027
- const { burned, usdcSpent, eurToUsdc, renderToUsdc, quoteAmt, tags, burnAdjustmentId, toBurn, } = p.buyBurn;
1127
+ const { burned, usdcSpent, eurToUsdc, renderToUsdc, quoteAmt, tags, jobBurnAdjustmentId, txBurnAdjustmentId, toBurn, fromEscrowUsdc, } = p.buyBurn;
1028
1128
  let pricedAt = p.buyBurn.pricedAt;
1029
1129
  if (!pricedAt) {
1030
1130
  const jobs = yield fetchJobs1(trx.trx, { ids: p.jobs.map((j) => String(j.id)) }, log);
@@ -1043,17 +1143,30 @@ const pgClient = (config) => {
1043
1143
  quote_amt: quoteAmt,
1044
1144
  burned: burned,
1045
1145
  tags: tags.join(','),
1046
- burn_adjustment_id: burnAdjustmentId,
1146
+ job_burn_adjustment_id: jobBurnAdjustmentId,
1147
+ tx_burn_adjustment_id: txBurnAdjustmentId,
1047
1148
  to_burn: toBurn,
1149
+ from_escrow_usdc: fromEscrowUsdc !== null && fromEscrowUsdc !== void 0 ? fromEscrowUsdc : 0,
1048
1150
  };
1049
1151
  log.debug(`inserting burn ${JSON.stringify(v)}`);
1050
- const { id } = yield trx.trx
1152
+ // Idempotent on sol_tx_id (uniq_burn_sol_tx_id, migration 27): a retry of
1153
+ // the record step for the same on-chain burn is a safe no-op. On conflict
1154
+ // executeTakeFirst() returns undefined, so re-fetch the existing id.
1155
+ let burnRow = yield trx.trx
1051
1156
  .insertInto(exports.BURN_TABLE)
1052
1157
  .values(v)
1053
- //.onConflict((oc) => oc.column('sol_tx').doNothing())
1158
+ .onConflict((oc) => oc.column('sol_tx_id').doNothing())
1054
1159
  .returning((eb) => ['id as id'])
1055
- .executeTakeFirstOrThrow();
1056
- buyBurnId = Number(id);
1160
+ .executeTakeFirst();
1161
+ if (!burnRow) {
1162
+ log.warn(`burn for sol_tx_id ${solTxId} already exists; reusing existing id (idempotent retry)`);
1163
+ burnRow = yield trx.trx
1164
+ .selectFrom(exports.BURN_TABLE)
1165
+ .select((eb) => ['id as id'])
1166
+ .where('sol_tx_id', '=', solTxId)
1167
+ .executeTakeFirstOrThrow();
1168
+ }
1169
+ buyBurnId = Number(burnRow.id);
1057
1170
  }
1058
1171
  if (p.grantBurn) {
1059
1172
  const { burned, tags } = p.grantBurn;
@@ -1063,12 +1176,22 @@ const pgClient = (config) => {
1063
1176
  tags: tags === null || tags === void 0 ? void 0 : tags.join(','),
1064
1177
  };
1065
1178
  log.debug(`inserting grant burn ${JSON.stringify(v)}`);
1066
- const { id } = yield trx.trx
1179
+ // Idempotent on sol_tx_id (uniq_grant_burn_sol_tx_id, migration 27).
1180
+ let grantBurnRow = yield trx.trx
1067
1181
  .insertInto(exports.GRANT_BURN_TABLE)
1068
1182
  .values(v)
1183
+ .onConflict((oc) => oc.column('sol_tx_id').doNothing())
1069
1184
  .returning((eb) => ['id as id'])
1070
- .executeTakeFirstOrThrow();
1071
- grantBurnId = Number(id);
1185
+ .executeTakeFirst();
1186
+ if (!grantBurnRow) {
1187
+ log.warn(`grant_burn for sol_tx_id ${solTxId} already exists; reusing existing id (idempotent retry)`);
1188
+ grantBurnRow = yield trx.trx
1189
+ .selectFrom(exports.GRANT_BURN_TABLE)
1190
+ .select((eb) => ['id as id'])
1191
+ .where('sol_tx_id', '=', solTxId)
1192
+ .executeTakeFirstOrThrow();
1193
+ }
1194
+ grantBurnId = Number(grantBurnRow.id);
1072
1195
  }
1073
1196
  const burnId = buyBurnId;
1074
1197
  const grantId = grantBurnId !== null && grantBurnId !== void 0 ? grantBurnId : buyBurnId;
@@ -1587,73 +1710,6 @@ const pgClient = (config) => {
1587
1710
  }));
1588
1711
  });
1589
1712
  },
1590
- insertDispersedOutbox(p, log = logger_1.consoleLogger) {
1591
- var _a, _b;
1592
- return __awaiter(this, void 0, void 0, function* () {
1593
- const res = yield db
1594
- .insertInto(exports.DISPERSED_SETTLEMENT_TABLE)
1595
- .values({
1596
- tx_signature: p.txSignature,
1597
- claim_uuid: p.claimUuid,
1598
- obligation_uuids: p.obligationUuids,
1599
- settled_amount: p.settledAmount,
1600
- burned_amount: p.burnedAmount,
1601
- executed_at: p.executedAt,
1602
- eur_render: (_a = p.eurRender) !== null && _a !== void 0 ? _a : null,
1603
- last_valid_block_height: (_b = p.lastValidBlockHeight) !== null && _b !== void 0 ? _b : null,
1604
- })
1605
- .onConflict((oc) => oc.column('tx_signature').doNothing())
1606
- .executeTakeFirst();
1607
- log.debug(`dispersed outbox tx ${p.txSignature}: ${res.numInsertedOrUpdatedRows} row(s)`);
1608
- });
1609
- },
1610
- markDispersedSettled(txSignature, p, log = logger_1.consoleLogger) {
1611
- return __awaiter(this, void 0, void 0, function* () {
1612
- yield db
1613
- .updateTable(exports.DISPERSED_SETTLEMENT_TABLE)
1614
- .set({ settled_at: new Date(), idempotent_replay: p.idempotentReplay })
1615
- .where('tx_signature', '=', txSignature)
1616
- .execute();
1617
- log.debug(`dispersed settled tx ${txSignature}`);
1618
- });
1619
- },
1620
- getUnsettledDispersed(log = logger_1.consoleLogger) {
1621
- return __awaiter(this, void 0, void 0, function* () {
1622
- const rows = yield db
1623
- .selectFrom(exports.DISPERSED_SETTLEMENT_TABLE)
1624
- .selectAll()
1625
- .where('settled_at', 'is', null)
1626
- .execute();
1627
- log.debug(`dispersed outbox: ${rows.length} unsettled`);
1628
- return rows.map((r) => {
1629
- var _a;
1630
- return ({
1631
- txSignature: r.tx_signature,
1632
- claimUuid: r.claim_uuid,
1633
- obligationUuids: r.obligation_uuids,
1634
- settledAmount: r.settled_amount,
1635
- burnedAmount: r.burned_amount,
1636
- executedAt: r.executed_at,
1637
- eurRender: (_a = r.eur_render) !== null && _a !== void 0 ? _a : undefined,
1638
- // int8 comes back as a string from pg; block heights are well within JS safe-integer range.
1639
- lastValidBlockHeight: r.last_valid_block_height != null
1640
- ? Number(r.last_valid_block_height)
1641
- : undefined,
1642
- });
1643
- });
1644
- });
1645
- },
1646
- voidDispersedOutbox(txSignature, log = logger_1.consoleLogger) {
1647
- return __awaiter(this, void 0, void 0, function* () {
1648
- const res = yield db
1649
- .deleteFrom(exports.DISPERSED_SETTLEMENT_TABLE)
1650
- .where('tx_signature', '=', txSignature)
1651
- // Never delete a row we already settled — void only applies to pending (un-landed) rows.
1652
- .where('settled_at', 'is', null)
1653
- .executeTakeFirst();
1654
- log.debug(`dispersed outbox void tx ${txSignature}: ${res.numDeletedRows} row(s)`);
1655
- });
1656
- },
1657
1713
  fetchJobs(db, f, log = logger_1.consoleLogger) {
1658
1714
  return __awaiter(this, void 0, void 0, function* () {
1659
1715
  return yield fetchJobs1(db.trx, f, log);
@@ -1723,46 +1779,90 @@ const pgClient = (config) => {
1723
1779
  }));
1724
1780
  });
1725
1781
  },
1726
- getBurnAdjustments(trx, ps, log = logger_1.consoleLogger) {
1782
+ createTxBurnAdjustment(trx, p, log = logger_1.consoleLogger) {
1727
1783
  return __awaiter(this, void 0, void 0, function* () {
1728
- if (ps.toFill) {
1729
- const q = trx.trx
1730
- .selectFrom(exports.BURN_ADJUSTMENT_TABLE)
1731
- .selectAll(exports.BURN_ADJUSTMENT_TABLE)
1732
- .innerJoin(exports.JOB_TABLE, `${exports.JOB_TABLE}.id`, `${exports.BURN_ADJUSTMENT_TABLE}.job_id`)
1733
- .select([`${exports.JOB_TABLE}.completed_at`, `${exports.JOB_TABLE}.render_amt`])
1734
- .leftJoin(exports.BURN_TABLE, `${exports.BURN_TABLE}.burn_adjustment_id`, `${exports.BURN_ADJUSTMENT_TABLE}.id`)
1735
- .select(({ fn, lit }) => [
1736
- fn
1737
- .sum(fn.coalesce(`${exports.BURN_TABLE}.to_burn`, lit(0)))
1738
- .as('tot_to_burn'),
1739
- fn
1740
- .sum(fn.coalesce(`${exports.BURN_TABLE}.burned`, lit(0)))
1741
- .as('tot_burned'),
1742
- ])
1743
- .groupBy([
1744
- `${exports.BURN_ADJUSTMENT_TABLE}.id`,
1745
- `${exports.JOB_TABLE}.completed_at`,
1746
- `${exports.JOB_TABLE}.render_amt`,
1747
- ])
1748
- .havingRef((0, kysely_1.sql) `SUM(COALESCE("burn"."to_burn", 0)) - SUM(COALESCE("burn"."burned", 0))`, '<', `${exports.BURN_ADJUSTMENT_TABLE}.down_adj_to_burn`);
1749
- log.info(`getBurnAdjustments ${q.compile().sql}`);
1750
- const res = yield q.execute();
1751
- return res.map((r) => ({
1752
- id: Number(r.id),
1753
- createdAt: r.created_at,
1754
- jobId: Number(r.job_id),
1755
- downAdjRndrUsed: Number(r.down_adj_rndr_used),
1756
- downAdjToBurn: Number(r.down_adj_to_burn),
1757
- adjusted: Number(r.tot_to_burn) - Number(r.tot_burned),
1758
- job: {
1759
- id: Number(r.job_id),
1760
- completedAt: r.completed_at,
1761
- rndrUsed: BigInt(r.render_amt),
1762
- },
1763
- }));
1784
+ // Idempotent per sol_tx_id (unique) — a re-run for the same bad tx is a no-op.
1785
+ const row = yield trx.trx
1786
+ .insertInto(exports.TX_BURN_ADJUSTMENT_TABLE)
1787
+ .values({
1788
+ sol_tx_id: p.solTxId,
1789
+ surplus_usdc: p.surplusUsdc,
1790
+ description: p.description,
1791
+ })
1792
+ .onConflict((oc) => oc.column('sol_tx_id').doNothing())
1793
+ .returning('id')
1794
+ .executeTakeFirst();
1795
+ if (!row) {
1796
+ log.info(`tx_burn_adjustment for sol_tx ${p.solTxId} already exists — skipped`);
1797
+ return undefined;
1764
1798
  }
1765
- return [];
1799
+ log.info(`created tx_burn_adjustment ${row.id}: ${p.surplusUsdc} USDC over-spent (sol_tx ${p.solTxId})`);
1800
+ return Number(row.id);
1801
+ });
1802
+ },
1803
+ getJobBurnAdjustments(trx, ps, log = logger_1.consoleLogger) {
1804
+ return __awaiter(this, void 0, void 0, function* () {
1805
+ if (!ps.toFill)
1806
+ return [];
1807
+ const q = trx.trx
1808
+ .selectFrom(exports.JOB_BURN_ADJUSTMENT_TABLE)
1809
+ .selectAll(exports.JOB_BURN_ADJUSTMENT_TABLE)
1810
+ .innerJoin(exports.JOB_TABLE, `${exports.JOB_TABLE}.id`, `${exports.JOB_BURN_ADJUSTMENT_TABLE}.job_id`)
1811
+ .select([`${exports.JOB_TABLE}.completed_at`, `${exports.JOB_TABLE}.render_amt`])
1812
+ .leftJoin(exports.BURN_TABLE, `${exports.BURN_TABLE}.job_burn_adjustment_id`, `${exports.JOB_BURN_ADJUSTMENT_TABLE}.id`)
1813
+ .select(({ fn, lit }) => [
1814
+ fn.sum(fn.coalesce(`${exports.BURN_TABLE}.to_burn`, lit(0))).as('tot_to_burn'),
1815
+ fn.sum(fn.coalesce(`${exports.BURN_TABLE}.burned`, lit(0))).as('tot_burned'),
1816
+ ])
1817
+ .groupBy([
1818
+ `${exports.JOB_BURN_ADJUSTMENT_TABLE}.id`,
1819
+ `${exports.JOB_TABLE}.completed_at`,
1820
+ `${exports.JOB_TABLE}.render_amt`,
1821
+ ]);
1822
+ log.info(`getJobBurnAdjustments ${q.compile().sql}`);
1823
+ const res = yield q.execute();
1824
+ return res
1825
+ .map((r) => ({
1826
+ id: Number(r.id),
1827
+ createdAt: r.created_at,
1828
+ jobId: Number(r.job_id),
1829
+ downAdjRndrUsed: Number(r.down_adj_rndr_used),
1830
+ downAdjToBurn: Number(r.down_adj_to_burn),
1831
+ adjusted: Number(r.tot_to_burn) - Number(r.tot_burned),
1832
+ job: {
1833
+ id: Number(r.job_id),
1834
+ completedAt: r.completed_at,
1835
+ rndrUsed: BigInt(r.render_amt),
1836
+ },
1837
+ }))
1838
+ .filter((a) => a.adjusted < a.downAdjToBurn);
1839
+ });
1840
+ },
1841
+ getTxBurnAdjustments(trx, ps, log = logger_1.consoleLogger) {
1842
+ return __awaiter(this, void 0, void 0, function* () {
1843
+ if (!ps.toFill)
1844
+ return [];
1845
+ const q = trx.trx
1846
+ .selectFrom(exports.TX_BURN_ADJUSTMENT_TABLE)
1847
+ .selectAll(exports.TX_BURN_ADJUSTMENT_TABLE)
1848
+ .leftJoin(exports.BURN_TABLE, `${exports.BURN_TABLE}.tx_burn_adjustment_id`, `${exports.TX_BURN_ADJUSTMENT_TABLE}.id`)
1849
+ .select(({ fn, lit }) => [
1850
+ fn
1851
+ .sum(fn.coalesce(`${exports.BURN_TABLE}.from_escrow_usdc`, lit(0)))
1852
+ .as('tot_from_escrow'),
1853
+ ])
1854
+ .groupBy(`${exports.TX_BURN_ADJUSTMENT_TABLE}.id`);
1855
+ log.info(`getTxBurnAdjustments ${q.compile().sql}`);
1856
+ const res = yield q.execute();
1857
+ return res
1858
+ .map((r) => ({
1859
+ id: Number(r.id),
1860
+ createdAt: r.created_at,
1861
+ solTxId: Number(r.sol_tx_id),
1862
+ surplusUsdc: Number(r.surplus_usdc),
1863
+ consumed: Number(r.tot_from_escrow),
1864
+ }))
1865
+ .filter((a) => a.consumed < a.surplusUsdc);
1766
1866
  });
1767
1867
  },
1768
1868
  insertPolygonUpgrade(db, p, log = logger_1.consoleLogger) {