@render-foundation/utils 0.0.236 → 0.0.238

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 (76) hide show
  1. package/lib/cjs/assertSecrets.js +2 -1
  2. package/lib/cjs/assertSecrets.js.map +1 -1
  3. package/lib/cjs/backfiller.js +16 -16
  4. package/lib/cjs/backfiller.js.map +1 -1
  5. package/lib/cjs/burn/burnCalculations.js +140 -0
  6. package/lib/cjs/burn/burnCalculations.js.map +1 -0
  7. package/lib/cjs/client/dispersed.js +4 -4
  8. package/lib/cjs/client/dispersed.js.map +1 -1
  9. package/lib/cjs/client/onchain.js +3 -3
  10. package/lib/cjs/client/onchain.js.map +1 -1
  11. package/lib/cjs/client/pg/base.js +7 -7
  12. package/lib/cjs/client/pg/base.js.map +1 -1
  13. package/lib/cjs/client/pg/v1.js +31 -31
  14. package/lib/cjs/client/pg/v1.js.map +1 -1
  15. package/lib/cjs/client/pg/v2Client.js +344 -148
  16. package/lib/cjs/client/pg/v2Client.js.map +1 -1
  17. package/lib/cjs/client/pg/viewClient.js +6 -6
  18. package/lib/cjs/client/pg/viewClient.js.map +1 -1
  19. package/lib/cjs/client/price.js +2 -2
  20. package/lib/cjs/client/price.js.map +1 -1
  21. package/lib/cjs/client/render.js +10 -10
  22. package/lib/cjs/client/render.js.map +1 -1
  23. package/lib/cjs/client/squads.js +10 -20
  24. package/lib/cjs/client/squads.js.map +1 -1
  25. package/lib/cjs/client/trade.js +12 -15
  26. package/lib/cjs/client/trade.js.map +1 -1
  27. package/lib/cjs/client/wormhole.js +9 -19
  28. package/lib/cjs/client/wormhole.js.map +1 -1
  29. package/lib/cjs/errorReporter.js +1 -1
  30. package/lib/cjs/errorReporter.js.map +1 -1
  31. package/lib/cjs/index.js +10 -18
  32. package/lib/cjs/index.js.map +1 -1
  33. package/lib/esm/src/assertSecrets.js.map +1 -1
  34. package/lib/esm/src/backfiller.js.map +1 -1
  35. package/lib/esm/src/burn/burnCalculations.js +124 -0
  36. package/lib/esm/src/burn/burnCalculations.js.map +1 -0
  37. package/lib/esm/src/client/dispersed.js.map +1 -1
  38. package/lib/esm/src/client/onchain.js.map +1 -1
  39. package/lib/esm/src/client/pg/base.js.map +1 -1
  40. package/lib/esm/src/client/pg/v1.js.map +1 -1
  41. package/lib/esm/src/client/pg/v2Client.js +218 -49
  42. package/lib/esm/src/client/pg/v2Client.js.map +1 -1
  43. package/lib/esm/src/client/pg/viewClient.js.map +1 -1
  44. package/lib/esm/src/client/price.js.map +1 -1
  45. package/lib/esm/src/client/render.js.map +1 -1
  46. package/lib/esm/src/client/squads.js.map +1 -1
  47. package/lib/esm/src/client/trade.js +2 -2
  48. package/lib/esm/src/client/trade.js.map +1 -1
  49. package/lib/esm/src/client/wormhole.js.map +1 -1
  50. package/lib/esm/src/errorReporter.js.map +1 -1
  51. package/lib/esm/src/index.js +1 -0
  52. package/lib/esm/src/index.js.map +1 -1
  53. package/lib/esm/tsconfig.esm.tsbuildinfo +1 -1
  54. package/lib/types/src/backfiller.d.ts.map +1 -1
  55. package/lib/types/src/burn/burnCalculations.d.ts +52 -0
  56. package/lib/types/src/burn/burnCalculations.d.ts.map +1 -0
  57. package/lib/types/src/client/dispersed.d.ts.map +1 -1
  58. package/lib/types/src/client/onchain.d.ts +1 -2
  59. package/lib/types/src/client/onchain.d.ts.map +1 -1
  60. package/lib/types/src/client/pg/base.d.ts.map +1 -1
  61. package/lib/types/src/client/pg/v1.d.ts.map +1 -1
  62. package/lib/types/src/client/pg/v2Client.d.ts +42 -1
  63. package/lib/types/src/client/pg/v2Client.d.ts.map +1 -1
  64. package/lib/types/src/client/pg/viewClient.d.ts.map +1 -1
  65. package/lib/types/src/client/render.d.ts.map +1 -1
  66. package/lib/types/src/client/squads.d.ts.map +1 -1
  67. package/lib/types/src/client/trade.d.ts.map +1 -1
  68. package/lib/types/src/client/wormhole.d.ts +1 -1
  69. package/lib/types/src/client/wormhole.d.ts.map +1 -1
  70. package/lib/types/src/dbTypesV2.d.ts +1 -0
  71. package/lib/types/src/dbTypesV2.d.ts.map +1 -1
  72. package/lib/types/src/errorReporter.d.ts.map +1 -1
  73. package/lib/types/src/index.d.ts +2 -1
  74. package/lib/types/src/index.d.ts.map +1 -1
  75. package/package.json +1 -1
  76. package/lib/cjs/tsconfig.cjs.tsbuildinfo +0 -1
@@ -15,23 +15,13 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
15
15
  }) : function(o, v) {
16
16
  o["default"] = v;
17
17
  });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
35
25
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
26
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
27
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -45,7 +35,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
45
35
  return (mod && mod.__esModule) ? mod : { "default": mod };
46
36
  };
47
37
  Object.defineProperty(exports, "__esModule", { value: true });
48
- 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_CORRECTION_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;
38
+ exports.pgClient = exports.batchFinishedAtMedian = exports.foldGrantAllotment = exports.CREDIT_DECIMALS = exports.EUR_TO_CREDITS = exports.DISPERSED_BURN_CORRECTION_TABLE = exports.DISPERSED_SETTLEMENT_TABLE = exports.GRANT_BURN_TABLE = exports.GRANT_BURN_ID_TABLE = exports.OTOY_GRANT_TABLE = exports.USER_GRANT_SPEND_TABLE = exports.CURRENT_USER_GRANT_SPEND_TABLE = exports.POLYGON_UPGRADE_TABLE = exports.ENTITY_EPOCH_INFO_TABLE = exports.BURN_CORRECTION_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;
49
39
  const kysely_1 = require("kysely");
50
40
  const logger_1 = require("../../logger");
51
41
  const pg_1 = require("pg");
@@ -69,12 +59,48 @@ exports.ENTITY_EPOCH_INFO_TABLE = 'entity_epoch_info';
69
59
  exports.POLYGON_UPGRADE_TABLE = 'polygon_upgrade';
70
60
  exports.CURRENT_USER_GRANT_SPEND_TABLE = 'current_user_grant_spend';
71
61
  exports.USER_GRANT_SPEND_TABLE = 'user_grant_spend';
62
+ exports.OTOY_GRANT_TABLE = 'otoy_grant';
72
63
  exports.GRANT_BURN_ID_TABLE = 'grant_burn_id';
73
64
  exports.GRANT_BURN_TABLE = 'grant_burn';
74
65
  exports.DISPERSED_SETTLEMENT_TABLE = 'dispersed_settlement';
66
+ exports.DISPERSED_BURN_CORRECTION_TABLE = 'dispersed_burn_correction';
75
67
  BigInt.prototype.toJSON = function () {
76
68
  return this.toString();
77
69
  };
70
+ // EUR -> render credits. OTOY issues grants in EUR (otoy_grant.amount_credits holds the raw OTOY
71
+ // confirmed_amount = EUR, despite the column name); 1 render credit = EUR 0.25, so credits = EUR x 4.
72
+ // job.render_amt / user_grant_spend.render_spent_delta are credits x 1e8.
73
+ exports.EUR_TO_CREDITS = BigInt(4);
74
+ exports.CREDIT_DECIMALS = BigInt(100000000); // 1e8
75
+ /**
76
+ * Pure core of the grant allotment: fold a user's OTOY grants against what's already been spent.
77
+ *
78
+ * consumed = attributedSpend (spend already stamped with an otoy_grant_id)
79
+ * + correctionSettled (jobs whose grant is already being settled by a burn_correction)
80
+ *
81
+ * The correctionSettled term is the DOUBLE-SPEND GUARD: an escrow_credit correction means we're
82
+ * recovering that job's dollars from escrow, so its grant is already committed. Counting it here stops
83
+ * the same grant from also funding a future emissions burn.
84
+ *
85
+ * fifoGrantId = the oldest grant still holding room, i.e. the one the next spend draws from.
86
+ */
87
+ const foldGrantAllotment = (grants, attributedSpend, correctionSettled) => {
88
+ const granted = grants.reduce((s, g) => s + g.amountCredits, BigInt(0));
89
+ const consumed = (attributedSpend > BigInt(0) ? attributedSpend : BigInt(0)) +
90
+ (correctionSettled > BigInt(0) ? correctionSettled : BigInt(0));
91
+ const avail = granted > consumed ? granted - consumed : BigInt(0);
92
+ let drawn = consumed;
93
+ let fifoGrantId;
94
+ for (const g of grants) {
95
+ if (drawn < g.amountCredits) {
96
+ fifoGrantId = g.id;
97
+ break;
98
+ }
99
+ drawn -= g.amountCredits;
100
+ }
101
+ return { granted, consumed, avail, fifoGrantId };
102
+ };
103
+ exports.foldGrantAllotment = foldGrantAllotment;
78
104
  const base_2 = require("./base");
79
105
  const moment_1 = __importDefault(require("moment"));
80
106
  pg.types.setTypeParser(1114, (str) => moment_1.default.utc(str).toDate());
@@ -92,6 +118,11 @@ const batchFinishedAtMedian = (jobs) => {
92
118
  return pricedAt;
93
119
  };
94
120
  exports.batchFinishedAtMedian = batchFinishedAtMedian;
121
+ // Thrown+caught inside insertDispersedOutbox to roll back the txn when a correction draw would push
122
+ // consumed_render past amount_render (a concurrent draw won the room). Not exported — the caller sees
123
+ // { inserted: false } instead.
124
+ class DispersedCorrectionRejected extends Error {
125
+ }
95
126
  const pgClient = (config) => {
96
127
  // Single pg pool that we can also use for ad-hoc performance tests outside Kysely
97
128
  const pgPool = new pg_1.Pool(config);
@@ -105,7 +136,7 @@ const pgClient = (config) => {
105
136
  const db = new kysely_1.Kysely({
106
137
  dialect,
107
138
  });
108
- const getOrInsertEntity = (db_1, p_1, ...args_1) => __awaiter(void 0, [db_1, p_1, ...args_1], void 0, function* (db, p, log = logger_1.consoleLogger) {
139
+ const getOrInsertEntity = (db, p, log = logger_1.consoleLogger) => __awaiter(void 0, void 0, void 0, function* () {
109
140
  const res = (yield db
110
141
  .selectFrom(exports.ENTITY_TABLE)
111
142
  .select('id')
@@ -126,7 +157,7 @@ const pgClient = (config) => {
126
157
  log.debug(`inserted entity ${id}`);
127
158
  return id;
128
159
  });
129
- const getOrInsertTx = (db_1, p_1, ...args_1) => __awaiter(void 0, [db_1, p_1, ...args_1], void 0, function* (db, p, log = logger_1.consoleLogger) {
160
+ const getOrInsertTx = (db, p, log = logger_1.consoleLogger) => __awaiter(void 0, void 0, void 0, function* () {
130
161
  const res = yield db
131
162
  .selectFrom(exports.SOL_TX_TABLE)
132
163
  .select('id')
@@ -148,7 +179,7 @@ const pgClient = (config) => {
148
179
  log.debug(`inserted tx ${id}`);
149
180
  return id;
150
181
  });
151
- const getOrInsertEpoch = (db_1, p_1, ...args_1) => __awaiter(void 0, [db_1, p_1, ...args_1], void 0, function* (db, p, log = logger_1.consoleLogger) {
182
+ const getOrInsertEpoch = (db, p, log = logger_1.consoleLogger) => __awaiter(void 0, void 0, void 0, function* () {
152
183
  const res = yield db
153
184
  .selectFrom(exports.EPOCH_TABLE)
154
185
  .select('id')
@@ -176,7 +207,7 @@ const pgClient = (config) => {
176
207
  log.debug(`inserted epoch ${id}`);
177
208
  return id;
178
209
  });
179
- const getCurrentUserGrantSpend = (db_1, p_1, ...args_1) => __awaiter(void 0, [db_1, p_1, ...args_1], void 0, function* (db, p, log = logger_1.consoleLogger) {
210
+ const getCurrentUserGrantSpend = (db, p, log = logger_1.consoleLogger) => __awaiter(void 0, void 0, void 0, function* () {
180
211
  var _a;
181
212
  const res = yield db
182
213
  .selectFrom(exports.CURRENT_USER_GRANT_SPEND_TABLE)
@@ -194,6 +225,73 @@ const pgClient = (config) => {
194
225
  }
195
226
  : undefined;
196
227
  });
228
+ // Spendable grant per user, straight off otoy_grant (FIFO), net of what's already been spent or
229
+ // already settled by a burn correction. See GrantAllotment for the units + the double-spend guard.
230
+ const getGrantAllotments = (db, p, log = logger_1.consoleLogger) => __awaiter(void 0, void 0, void 0, function* () {
231
+ var _b, _c, _d;
232
+ const out = {};
233
+ if (!p.userIds.length)
234
+ return out;
235
+ const grants = yield db
236
+ .selectFrom(exports.OTOY_GRANT_TABLE)
237
+ .select(['id', 'user_id', 'amount_credits', 'otoy_created_at'])
238
+ .where('user_id', 'in', p.userIds)
239
+ .orderBy('otoy_created_at', 'asc')
240
+ .orderBy('id', 'asc')
241
+ .execute();
242
+ if (!grants.length)
243
+ return out;
244
+ // Anything already booked against a pool grant, in EITHER direction:
245
+ // - negative delta = spend drawn from the grant (this mechanism)
246
+ // - positive delta = the grant was CREDITED into current_user_grant_spend (the one-off canary
247
+ // route). That balance is already handed to the caller separately, so leaving it in the pool too
248
+ // would hand out the same grant twice.
249
+ // Hence abs(): both directions retire pool capacity.
250
+ const spent = yield db
251
+ .selectFrom(exports.USER_GRANT_SPEND_TABLE)
252
+ .select((eb) => [
253
+ 'user_id',
254
+ eb.fn.sum((0, kysely_1.sql) `abs(render_spent_delta)`).as('net'),
255
+ ])
256
+ .where('user_id', 'in', p.userIds)
257
+ .where('otoy_grant_id', 'is not', null)
258
+ .groupBy('user_id')
259
+ .execute();
260
+ // jobs already settled by a burn correction — their grant is spoken for (double-spend guard)
261
+ const corrected = yield db
262
+ .selectFrom(`${exports.JOB_TABLE} as j`)
263
+ .innerJoin(`${exports.BURN_CORRECTION_TABLE} as bc`, 'bc.job_id', 'j.id')
264
+ .select((eb) => ['j.user_id as user_id', eb.fn.sum('j.render_amt').as('amt')])
265
+ .where('j.user_id', 'in', p.userIds)
266
+ .groupBy('j.user_id')
267
+ .execute();
268
+ const spentBy = new Map(spent.map((r) => { var _a; return [r.user_id, BigInt((_a = r.net) !== null && _a !== void 0 ? _a : 0)]; }));
269
+ const corrBy = new Map(corrected.map((r) => { var _a; return [r.user_id, BigInt((_a = r.amt) !== null && _a !== void 0 ? _a : 0)]; }));
270
+ for (const g of grants) {
271
+ const u = g.user_id;
272
+ const cur = (_b = out[u]) !== null && _b !== void 0 ? _b : (out[u] = { userId: u, granted: BigInt(0), consumed: BigInt(0), avail: BigInt(0), grants: [] });
273
+ // amount_credits is numeric EUR — truncate to whole base units after the x4 conversion
274
+ const credits = (BigInt(Math.round(Number(g.amount_credits) * 1e8)) * exports.EUR_TO_CREDITS);
275
+ cur.granted += credits;
276
+ cur.grants.push({
277
+ id: Number(g.id),
278
+ amountCredits: credits,
279
+ createdAt: g.otoy_created_at ? new Date(g.otoy_created_at) : null,
280
+ });
281
+ }
282
+ for (const u of Object.keys(out)) {
283
+ const attributed = (_c = spentBy.get(u)) !== null && _c !== void 0 ? _c : BigInt(0); // abs() — spend drawn or grant already credited
284
+ const viaCorrection = (_d = corrBy.get(u)) !== null && _d !== void 0 ? _d : BigInt(0);
285
+ const f = (0, exports.foldGrantAllotment)(out[u].grants, attributed, viaCorrection);
286
+ out[u].granted = f.granted;
287
+ out[u].consumed = f.consumed;
288
+ out[u].avail = f.avail;
289
+ if (viaCorrection > BigInt(0)) {
290
+ log.info(`grant allotment ${u}: granted ${f.granted} - attributed ${attributed} - correction-settled ${viaCorrection} = ${f.avail}`);
291
+ }
292
+ }
293
+ return out;
294
+ });
197
295
  const updateTotalRndr = (db, p, totalRndr, savedAt, log) => __awaiter(void 0, void 0, void 0, function* () {
198
296
  const res = (yield db
199
297
  .updateTable(exports.EPOCH_TABLE)
@@ -206,7 +304,7 @@ const pgClient = (config) => {
206
304
  .executeTakeFirst());
207
305
  log.info(`set ${res.numUpdatedRows} rows. channel: ${p.channel}, ui: ${p.uiEpochId} -> totalRndr ${totalRndr} `);
208
306
  });
209
- const fetchJobs1 = (db_1, f_1, ...args_1) => __awaiter(void 0, [db_1, f_1, ...args_1], void 0, function* (db, f, log = logger_1.consoleLogger) {
307
+ const fetchJobs1 = (db, f, log = logger_1.consoleLogger) => __awaiter(void 0, void 0, void 0, function* () {
210
308
  let q = db.selectFrom('job').selectAll();
211
309
  //.where('job.burn_id', 'is not', null)
212
310
  if (f.completedAtAfter) {
@@ -236,7 +334,7 @@ const pgClient = (config) => {
236
334
  insertedAt: r.inserted_at,
237
335
  }));
238
336
  });
239
- const fetchJobs2 = (db_1, f_1, ...args_1) => __awaiter(void 0, [db_1, f_1, ...args_1], void 0, function* (db, f, log = logger_1.consoleLogger) {
337
+ const fetchJobs2 = (db, f, log = logger_1.consoleLogger) => __awaiter(void 0, void 0, void 0, function* () {
240
338
  let q = db.selectFrom('job').selectAll();
241
339
  //.where('job.burn_id', 'is not', null)
242
340
  if (f.completedAtAfter) {
@@ -270,7 +368,7 @@ const pgClient = (config) => {
270
368
  });
271
369
  });
272
370
  });
273
- const createBridgeTransfers = (db_1, bts_1, ...args_1) => __awaiter(void 0, [db_1, bts_1, ...args_1], void 0, function* (db, bts, log = logger_1.consoleLogger) {
371
+ const createBridgeTransfers = (db, bts, log = logger_1.consoleLogger) => __awaiter(void 0, void 0, void 0, function* () {
274
372
  const sequenceNumbers = [];
275
373
  const dbEpochIds = [];
276
374
  for (const bt of bts) {
@@ -314,7 +412,7 @@ const pgClient = (config) => {
314
412
  }
315
413
  return { sequenceNumbers: sequenceNumbers };
316
414
  });
317
- const createPayments = (db_1, ps_1, ...args_1) => __awaiter(void 0, [db_1, ps_1, ...args_1], void 0, function* (db, ps, log = logger_1.consoleLogger) {
415
+ const createPayments = (db, ps, log = logger_1.consoleLogger) => __awaiter(void 0, void 0, void 0, function* () {
318
416
  const ids = [];
319
417
  const epochDbIds = [];
320
418
  for (const p of ps) {
@@ -411,18 +509,18 @@ const pgClient = (config) => {
411
509
  rollback: () => { },
412
510
  trx: db,
413
511
  },
414
- startTransaction() {
415
- return __awaiter(this, arguments, void 0, function* (log = logger_1.consoleLogger) {
512
+ startTransaction(log = logger_1.consoleLogger) {
513
+ return __awaiter(this, void 0, void 0, function* () {
416
514
  return (0, base_2.begin)(db, log);
417
515
  });
418
516
  },
419
- createPayments(db_1, ps_1) {
420
- return __awaiter(this, arguments, void 0, function* (db, ps, log = logger_1.consoleLogger) {
517
+ createPayments(db, ps, log = logger_1.consoleLogger) {
518
+ return __awaiter(this, void 0, void 0, function* () {
421
519
  return createPayments(db.trx, ps, log);
422
520
  });
423
521
  },
424
- fetchTransfers(db_1, f_1) {
425
- return __awaiter(this, arguments, void 0, function* (db, f, log = logger_1.consoleLogger) {
522
+ fetchTransfers(db, f, log = logger_1.consoleLogger) {
523
+ return __awaiter(this, void 0, void 0, function* () {
426
524
  let lastId = undefined;
427
525
  if (f.cursor) {
428
526
  lastId = atob(f.cursor);
@@ -478,8 +576,8 @@ const pgClient = (config) => {
478
576
  };
479
577
  });
480
578
  },
481
- fetchEpochs(db_1, f_1) {
482
- return __awaiter(this, arguments, void 0, function* (db, f, log = logger_1.consoleLogger) {
579
+ fetchEpochs(db, f, log = logger_1.consoleLogger) {
580
+ return __awaiter(this, void 0, void 0, function* () {
483
581
  let q = db.trx.selectFrom(exports.EPOCH_TABLE).selectAll();
484
582
  if (f.channels) {
485
583
  q = q.where('channel', 'in', f.channels);
@@ -502,9 +600,9 @@ const pgClient = (config) => {
502
600
  }));
503
601
  });
504
602
  },
505
- fetchLiabilities(db_1, f_1) {
506
- return __awaiter(this, arguments, void 0, function* (db, f, log = logger_1.consoleLogger) {
507
- var _a;
603
+ fetchLiabilities(db, f, log = logger_1.consoleLogger) {
604
+ var _a;
605
+ return __awaiter(this, void 0, void 0, function* () {
508
606
  let offset = 0;
509
607
  let orderBy = (_a = f.orderBy) !== null && _a !== void 0 ? _a : 'epoch.ui,liability.id';
510
608
  if (f.cursor) {
@@ -571,8 +669,8 @@ const pgClient = (config) => {
571
669
  };
572
670
  });
573
671
  },
574
- getLiabilitiesAndTransfers(f_1) {
575
- return __awaiter(this, arguments, void 0, function* (f, log = logger_1.consoleLogger) {
672
+ getLiabilitiesAndTransfers(f, log = logger_1.consoleLogger) {
673
+ return __awaiter(this, void 0, void 0, function* () {
576
674
  let lq = db
577
675
  .selectFrom(exports.LIABILITY_TABLE)
578
676
  .selectAll()
@@ -633,9 +731,9 @@ const pgClient = (config) => {
633
731
  return { transfers, liabilities, epochs: Object.values(epochs) };
634
732
  });
635
733
  },
636
- getOutstanding(f_1) {
637
- return __awaiter(this, arguments, void 0, function* (f, log = logger_1.consoleLogger) {
638
- var _a, _b, _c, _d, _e;
734
+ getOutstanding(f, log = logger_1.consoleLogger) {
735
+ var _a, _b, _c, _d, _e;
736
+ return __awaiter(this, void 0, void 0, function* () {
639
737
  log.info(`getting outstanding balances for ${(_a = f.channels) !== null && _a !== void 0 ? _a : '(no channels specified)'} epoch: ${(_b = f.startEpochId.id) !== null && _b !== void 0 ? _b : '(no epoch specified)'}`);
640
738
  let epochUi;
641
739
  let epochEndTime;
@@ -771,8 +869,8 @@ const pgClient = (config) => {
771
869
  return out;
772
870
  });
773
871
  },
774
- addLiabilityAdjustments(batch_1, rows_1) {
775
- return __awaiter(this, arguments, void 0, function* (batch, rows, log = logger_1.consoleLogger) {
872
+ addLiabilityAdjustments(batch, rows, log = logger_1.consoleLogger) {
873
+ return __awaiter(this, void 0, void 0, function* () {
776
874
  return yield db.transaction().execute((trx) => __awaiter(this, void 0, void 0, function* () {
777
875
  var _a;
778
876
  // Upsert the batch by its label (idempotent re-runs; label is the run identity).
@@ -824,9 +922,9 @@ const pgClient = (config) => {
824
922
  }));
825
923
  });
826
924
  },
827
- fetchNodeOperatorEpoch(epochId_1) {
828
- return __awaiter(this, arguments, void 0, function* (epochId, log = logger_1.consoleLogger) {
829
- var _a;
925
+ fetchNodeOperatorEpoch(epochId, log = logger_1.consoleLogger) {
926
+ var _a;
927
+ return __awaiter(this, void 0, void 0, function* () {
830
928
  const q = db
831
929
  .selectFrom(exports.JOB_ID_TABLE)
832
930
  .innerJoin(exports.EPOCH_TABLE, 'epoch.id', 'job_id.epoch_id')
@@ -876,14 +974,14 @@ const pgClient = (config) => {
876
974
  };
877
975
  });
878
976
  },
879
- insertEpoch(db_1, epoch_1) {
880
- return __awaiter(this, arguments, void 0, function* (db, epoch, log = logger_1.consoleLogger) {
977
+ insertEpoch(db, epoch, log = logger_1.consoleLogger) {
978
+ return __awaiter(this, void 0, void 0, function* () {
881
979
  yield getOrInsertEpoch(db.trx, Object.assign(Object.assign({}, epoch), { uiEpochId: epoch.ui }), log);
882
980
  });
883
981
  },
884
- getEpoch(f_1) {
885
- return __awaiter(this, arguments, void 0, function* (f, log = logger_1.consoleLogger) {
886
- var _a;
982
+ getEpoch(f, log = logger_1.consoleLogger) {
983
+ var _a;
984
+ return __awaiter(this, void 0, void 0, function* () {
887
985
  const now = new Date(Date.now());
888
986
  try {
889
987
  let q = db
@@ -921,9 +1019,9 @@ const pgClient = (config) => {
921
1019
  }
922
1020
  });
923
1021
  },
924
- getLatestNodeOperatorEpoch(f_1) {
925
- return __awaiter(this, arguments, void 0, function* (f, log = logger_1.consoleLogger) {
926
- var _a;
1022
+ getLatestNodeOperatorEpoch(f, log = logger_1.consoleLogger) {
1023
+ var _a;
1024
+ return __awaiter(this, void 0, void 0, function* () {
927
1025
  const now = new Date(Date.now());
928
1026
  try {
929
1027
  let q = db
@@ -963,8 +1061,8 @@ const pgClient = (config) => {
963
1061
  }
964
1062
  });
965
1063
  },
966
- saveNodeOperatorEpoch(epochId_1, liabilities_1, jobIds_1, totalRndr_1) {
967
- return __awaiter(this, arguments, void 0, function* (epochId, liabilities, jobIds, totalRndr, log = logger_1.consoleLogger) {
1064
+ saveNodeOperatorEpoch(epochId, liabilities, jobIds, totalRndr, log = logger_1.consoleLogger) {
1065
+ return __awaiter(this, void 0, void 0, function* () {
968
1066
  yield db
969
1067
  .transaction()
970
1068
  .setIsolationLevel('repeatable read')
@@ -1000,8 +1098,8 @@ const pgClient = (config) => {
1000
1098
  return;
1001
1099
  });
1002
1100
  },
1003
- produceBurnedJobs(jobs_1) {
1004
- return __awaiter(this, arguments, void 0, function* (jobs, log = logger_1.consoleLogger) {
1101
+ produceBurnedJobs(jobs, log = logger_1.consoleLogger) {
1102
+ return __awaiter(this, void 0, void 0, function* () {
1005
1103
  yield db.transaction().execute((trx) => __awaiter(this, void 0, void 0, function* () {
1006
1104
  var _a;
1007
1105
  let num = BigInt(0);
@@ -1024,9 +1122,9 @@ const pgClient = (config) => {
1024
1122
  }));
1025
1123
  });
1026
1124
  },
1027
- markJobsBurntWithGrant(db_1, p_1) {
1028
- return __awaiter(this, arguments, void 0, function* (db, p, log = logger_1.consoleLogger) {
1029
- var _a;
1125
+ markJobsBurntWithGrant(db, p, log = logger_1.consoleLogger) {
1126
+ var _a;
1127
+ return __awaiter(this, void 0, void 0, function* () {
1030
1128
  const solTxId = yield getOrInsertTx(db.trx, { sig: p.tx.sig, executedAt: p.tx.executedAt }, log);
1031
1129
  const { id } = yield db.trx
1032
1130
  .insertInto(exports.GRANT_BURN_TABLE)
@@ -1057,9 +1155,9 @@ const pgClient = (config) => {
1057
1155
  return;
1058
1156
  });
1059
1157
  },
1060
- insertGrantSpend(db_1, p_1) {
1061
- return __awaiter(this, arguments, void 0, function* (db, p, log = logger_1.consoleLogger) {
1062
- var _a, _b, _c;
1158
+ insertGrantSpend(db, p, log = logger_1.consoleLogger) {
1159
+ var _a, _b, _c;
1160
+ return __awaiter(this, void 0, void 0, function* () {
1063
1161
  const existing = yield getCurrentUserGrantSpend(db.trx, { userId: p.userId }, log);
1064
1162
  const diff = {
1065
1163
  render_spent: ((_a = existing === null || existing === void 0 ? void 0 : existing.renderSpent) !== null && _a !== void 0 ? _a : BigInt(0)) + p.renderSpentDelta,
@@ -1085,11 +1183,23 @@ const pgClient = (config) => {
1085
1183
  return;
1086
1184
  }
1087
1185
  }
1186
+ // FIFO-attribute this spend to the user's oldest OTOY grant that still has room. Stamping
1187
+ // otoy_grant_id is what makes getGrantAllotments count it as consumed — an unattributed row
1188
+ // would leave the grant looking unspent and let it fund a second burn.
1189
+ let otoyGrantId = p.otoyGrantId;
1190
+ if (otoyGrantId === undefined && p.renderSpentDelta < BigInt(0)) {
1191
+ const allots = yield getGrantAllotments(db.trx, { userIds: [p.userId] }, log);
1192
+ const a = allots[p.userId];
1193
+ if (a) {
1194
+ otoyGrantId = (0, exports.foldGrantAllotment)(a.grants, a.consumed, BigInt(0)).fifoGrantId;
1195
+ }
1196
+ }
1088
1197
  const v = {
1089
1198
  user_id: p.userId,
1090
1199
  render_spent_delta: p.renderSpentDelta,
1091
1200
  grant_burn_id: p.grantBurnId,
1092
1201
  job_id: p.jobId,
1202
+ otoy_grant_id: otoyGrantId,
1093
1203
  };
1094
1204
  const res = yield db.trx
1095
1205
  .insertInto(exports.USER_GRANT_SPEND_TABLE)
@@ -1124,9 +1234,9 @@ const pgClient = (config) => {
1124
1234
  }
1125
1235
  });
1126
1236
  },
1127
- markJobsBurnt(trx_1, p_1) {
1128
- return __awaiter(this, arguments, void 0, function* (trx, p, log = logger_1.consoleLogger) {
1129
- var _a, _b;
1237
+ markJobsBurnt(trx, p, log = logger_1.consoleLogger) {
1238
+ var _a, _b;
1239
+ return __awaiter(this, void 0, void 0, function* () {
1130
1240
  if (!p.buyBurn && !p.grantBurn) {
1131
1241
  throw new Error('no buyBurn or grantBurn');
1132
1242
  }
@@ -1227,8 +1337,8 @@ const pgClient = (config) => {
1227
1337
  return { grantBurnId, buyBurnId };
1228
1338
  });
1229
1339
  },
1230
- markJobsBurned(trx_1, p_1) {
1231
- return __awaiter(this, arguments, void 0, function* (trx, p, log = logger_1.consoleLogger) {
1340
+ markJobsBurned(trx, p, log = logger_1.consoleLogger) {
1341
+ return __awaiter(this, void 0, void 0, function* () {
1232
1342
  let { markedAt, solTx } = p;
1233
1343
  let res = yield trx.trx
1234
1344
  .updateTable(exports.BURN_TABLE)
@@ -1252,8 +1362,8 @@ const pgClient = (config) => {
1252
1362
  }
1253
1363
  });
1254
1364
  },
1255
- consumeAndLockBurnJobs(limit_1, lowerRndrThreshold_1, upperRndrThreshold_1) {
1256
- return __awaiter(this, arguments, void 0, function* (limit, lowerRndrThreshold, upperRndrThreshold, log = logger_1.consoleLogger) {
1365
+ consumeAndLockBurnJobs(limit, lowerRndrThreshold, upperRndrThreshold, log = logger_1.consoleLogger) {
1366
+ return __awaiter(this, void 0, void 0, function* () {
1257
1367
  const trx = yield (0, base_2.begin)(db, log);
1258
1368
  const q = trx.trx
1259
1369
  .selectFrom(trx.trx
@@ -1360,9 +1470,9 @@ const pgClient = (config) => {
1360
1470
  };
1361
1471
  });
1362
1472
  },
1363
- incrBuyBurnMetrics() {
1364
- return __awaiter(this, arguments, void 0, function* (log = logger_1.consoleLogger) {
1365
- var _a;
1473
+ incrBuyBurnMetrics(log = logger_1.consoleLogger) {
1474
+ var _a;
1475
+ return __awaiter(this, void 0, void 0, function* () {
1366
1476
  const res = yield db
1367
1477
  .selectFrom(exports.JOB_TABLE)
1368
1478
  .select('inserted_at')
@@ -1379,9 +1489,9 @@ const pgClient = (config) => {
1379
1489
  };
1380
1490
  });
1381
1491
  },
1382
- fetchBurns(db_1, f_1) {
1383
- return __awaiter(this, arguments, void 0, function* (db, f, log = logger_1.consoleLogger) {
1384
- var _a, _b;
1492
+ fetchBurns(db, f, log = logger_1.consoleLogger) {
1493
+ var _a, _b;
1494
+ return __awaiter(this, void 0, void 0, function* () {
1385
1495
  let q = db.trx
1386
1496
  .selectFrom('burn')
1387
1497
  .selectAll('burn')
@@ -1450,9 +1560,9 @@ const pgClient = (config) => {
1450
1560
  return burns;
1451
1561
  });
1452
1562
  },
1453
- fetchBurnsAndGrants(db_1, f_1) {
1454
- return __awaiter(this, arguments, void 0, function* (db, f, log = logger_1.consoleLogger) {
1455
- var _a, _b;
1563
+ fetchBurnsAndGrants(db, f, log = logger_1.consoleLogger) {
1564
+ var _a, _b;
1565
+ return __awaiter(this, void 0, void 0, function* () {
1456
1566
  // Build a unified query to get all transactions with both buy burns and grant burns
1457
1567
  let baseQuery = db.trx
1458
1568
  .selectFrom('sol_tx')
@@ -1590,13 +1700,13 @@ const pgClient = (config) => {
1590
1700
  return groupedBurns;
1591
1701
  });
1592
1702
  },
1593
- createBridgeTransfers(db_1, bts_1) {
1594
- return __awaiter(this, arguments, void 0, function* (db, bts, log = logger_1.consoleLogger) {
1703
+ createBridgeTransfers(db, bts, log = logger_1.consoleLogger) {
1704
+ return __awaiter(this, void 0, void 0, function* () {
1595
1705
  return createBridgeTransfers(db.trx, bts, log);
1596
1706
  });
1597
1707
  },
1598
- getBridgeTransfers(_a) {
1599
- return __awaiter(this, arguments, void 0, function* ({ solKey, page, }, log = logger_1.consoleLogger) {
1708
+ getBridgeTransfers({ solKey, page, }, log = logger_1.consoleLogger) {
1709
+ return __awaiter(this, void 0, void 0, function* () {
1600
1710
  let q = db
1601
1711
  .selectFrom(exports.BRIDGE_TRANSFER_TABLE)
1602
1712
  .innerJoin(exports.EPOCH_TABLE, 'epoch.id', 'bridge_transfer.epoch_id')
@@ -1673,8 +1783,8 @@ const pgClient = (config) => {
1673
1783
  });
1674
1784
  },
1675
1785
  getTotalBurned(db) {
1786
+ var _a, _b;
1676
1787
  return __awaiter(this, void 0, void 0, function* () {
1677
- var _a, _b;
1678
1788
  const total = yield db.trx
1679
1789
  .selectFrom('burn')
1680
1790
  .select(({ fn }) => fn.sum('burned').as('total_burned'))
@@ -1682,8 +1792,8 @@ const pgClient = (config) => {
1682
1792
  return BigInt((_b = (_a = total[0]) === null || _a === void 0 ? void 0 : _a.total_burned) !== null && _b !== void 0 ? _b : 0);
1683
1793
  });
1684
1794
  },
1685
- insertBurn(p_1) {
1686
- return __awaiter(this, arguments, void 0, function* (p, log = logger_1.consoleLogger) {
1795
+ insertBurn(p, log = logger_1.consoleLogger) {
1796
+ return __awaiter(this, void 0, void 0, function* () {
1687
1797
  yield db.transaction().execute((trx) => __awaiter(this, void 0, void 0, function* () {
1688
1798
  var _a;
1689
1799
  const solTxId = yield getOrInsertTx(trx, { sig: p.solTx, executedAt: p.executedAt }, log);
@@ -1720,29 +1830,86 @@ const pgClient = (config) => {
1720
1830
  }));
1721
1831
  });
1722
1832
  },
1723
- insertDispersedOutbox(p_1) {
1724
- return __awaiter(this, arguments, void 0, function* (p, log = logger_1.consoleLogger) {
1725
- var _a, _b, _c;
1726
- const res = yield db
1727
- .insertInto(exports.DISPERSED_SETTLEMENT_TABLE)
1728
- .values({
1729
- tx_signature: p.txSignature,
1730
- claim_uuid: p.claimUuid,
1731
- obligation_uuids: p.obligationUuids,
1732
- settled_amount: p.settledAmount,
1733
- burned_amount: p.burnedAmount,
1734
- executed_at: p.executedAt,
1735
- eur_render: (_a = p.eurRender) !== null && _a !== void 0 ? _a : null,
1736
- last_valid_block_height: (_b = p.lastValidBlockHeight) !== null && _b !== void 0 ? _b : null,
1737
- usdc_spent: (_c = p.usdcSpent) !== null && _c !== void 0 ? _c : null,
1738
- })
1739
- .onConflict((oc) => oc.column('tx_signature').doNothing())
1833
+ insertDispersedOutbox(p, log = logger_1.consoleLogger) {
1834
+ return __awaiter(this, void 0, void 0, function* () {
1835
+ // The outbox row is the durable, crash-safe anchor persisted BEFORE broadcast. When it carries a
1836
+ // burn-correction draw, we increment consumed_render in the SAME transaction as the insert, bounded
1837
+ // so it can never exceed amount_render. Row + counter are therefore atomic and inseparable: no
1838
+ // reserve/insert gap to orphan on process death, and the counter reflects exactly the durable rows
1839
+ // (decremented again by voidDispersedOutbox). If the bounded increment can't fit (a concurrent draw
1840
+ // took the room), the whole txn rolls back and we return {inserted:false} — the caller must NOT
1841
+ // broadcast (no row, no counter change, no burn).
1842
+ return yield db.transaction().execute((trx) => __awaiter(this, void 0, void 0, function* () {
1843
+ var _a, _b, _c, _d, _e, _f, _g;
1844
+ const res = yield trx
1845
+ .insertInto(exports.DISPERSED_SETTLEMENT_TABLE)
1846
+ .values({
1847
+ tx_signature: p.txSignature,
1848
+ claim_uuid: p.claimUuid,
1849
+ obligation_uuids: p.obligationUuids,
1850
+ settled_amount: p.settledAmount,
1851
+ burned_amount: p.burnedAmount,
1852
+ executed_at: p.executedAt,
1853
+ eur_render: (_a = p.eurRender) !== null && _a !== void 0 ? _a : null,
1854
+ last_valid_block_height: (_b = p.lastValidBlockHeight) !== null && _b !== void 0 ? _b : null,
1855
+ usdc_spent: (_c = p.usdcSpent) !== null && _c !== void 0 ? _c : null,
1856
+ correction_id: (_d = p.correctionId) !== null && _d !== void 0 ? _d : null,
1857
+ correction_render: (_e = p.correctionRender) !== null && _e !== void 0 ? _e : null,
1858
+ })
1859
+ .onConflict((oc) => oc.column('tx_signature').doNothing())
1860
+ .executeTakeFirst();
1861
+ const inserted = ((_f = res.numInsertedOrUpdatedRows) !== null && _f !== void 0 ? _f : BigInt(0)) > BigInt(0);
1862
+ // Only reserve when the row was actually inserted (a tx_signature conflict is a no-op → no double
1863
+ // count) and it drew a correction.
1864
+ if (inserted &&
1865
+ p.correctionId != null &&
1866
+ p.correctionRender != null &&
1867
+ Number(p.correctionRender) > 0) {
1868
+ const upd = yield trx
1869
+ .updateTable(exports.DISPERSED_BURN_CORRECTION_TABLE)
1870
+ .set({
1871
+ consumed_render: (0, kysely_1.sql) `consumed_render + ${p.correctionRender}::numeric`,
1872
+ })
1873
+ .where('id', '=', String(p.correctionId))
1874
+ .where((eb) => eb((0, kysely_1.sql) `consumed_render + ${p.correctionRender}::numeric`, '<=', eb.ref('amount_render')))
1875
+ .executeTakeFirst();
1876
+ if (((_g = upd.numUpdatedRows) !== null && _g !== void 0 ? _g : BigInt(0)) === BigInt(0)) {
1877
+ // Would exceed amount_render (concurrent draw won the room) — roll back the whole txn.
1878
+ log.warn(`dispersed correction ${p.correctionId}: draw ${p.correctionRender} would over-consume; rejecting outbox insert for tx ${p.txSignature}`);
1879
+ throw new DispersedCorrectionRejected();
1880
+ }
1881
+ }
1882
+ log.debug(`dispersed outbox tx ${p.txSignature}: ${res.numInsertedOrUpdatedRows} row(s)`);
1883
+ return { inserted };
1884
+ })).catch((e) => {
1885
+ if (e instanceof DispersedCorrectionRejected)
1886
+ return { inserted: false };
1887
+ throw e;
1888
+ });
1889
+ });
1890
+ },
1891
+ getOutstandingDispersedCorrection(log = logger_1.consoleLogger) {
1892
+ return __awaiter(this, void 0, void 0, function* () {
1893
+ // consumed_render is the atomic source of truth; outstanding = amount_render - consumed_render.
1894
+ const c = yield db
1895
+ .selectFrom(exports.DISPERSED_BURN_CORRECTION_TABLE)
1896
+ .select(['id', 'amount_render', 'consumed_render'])
1897
+ .where((eb) => eb('consumed_render', '<', eb.ref('amount_render')))
1898
+ .orderBy('id', 'asc')
1899
+ .limit(1)
1740
1900
  .executeTakeFirst();
1741
- log.debug(`dispersed outbox tx ${p.txSignature}: ${res.numInsertedOrUpdatedRows} row(s)`);
1901
+ if (!c)
1902
+ return null;
1903
+ log.debug(`dispersed correction ${c.id}: outstanding ${Number(c.amount_render) - Number(c.consumed_render)} RENDER`);
1904
+ return {
1905
+ id: Number(c.id),
1906
+ amountRender: c.amount_render,
1907
+ consumed: c.consumed_render,
1908
+ };
1742
1909
  });
1743
1910
  },
1744
- markDispersedSettled(txSignature_1, p_1) {
1745
- return __awaiter(this, arguments, void 0, function* (txSignature, p, log = logger_1.consoleLogger) {
1911
+ markDispersedSettled(txSignature, p, log = logger_1.consoleLogger) {
1912
+ return __awaiter(this, void 0, void 0, function* () {
1746
1913
  yield db
1747
1914
  .updateTable(exports.DISPERSED_SETTLEMENT_TABLE)
1748
1915
  .set({ settled_at: new Date(), idempotent_replay: p.idempotentReplay })
@@ -1751,8 +1918,8 @@ const pgClient = (config) => {
1751
1918
  log.debug(`dispersed settled tx ${txSignature}`);
1752
1919
  });
1753
1920
  },
1754
- getUnsettledDispersed() {
1755
- return __awaiter(this, arguments, void 0, function* (log = logger_1.consoleLogger) {
1921
+ getUnsettledDispersed(log = logger_1.consoleLogger) {
1922
+ return __awaiter(this, void 0, void 0, function* () {
1756
1923
  const rows = yield db
1757
1924
  .selectFrom(exports.DISPERSED_SETTLEMENT_TABLE)
1758
1925
  .selectAll()
@@ -1778,20 +1945,44 @@ const pgClient = (config) => {
1778
1945
  });
1779
1946
  });
1780
1947
  },
1781
- voidDispersedOutbox(txSignature_1) {
1782
- return __awaiter(this, arguments, void 0, function* (txSignature, log = logger_1.consoleLogger) {
1783
- const res = yield db
1784
- .deleteFrom(exports.DISPERSED_SETTLEMENT_TABLE)
1785
- .where('tx_signature', '=', txSignature)
1786
- // Never delete a row we already settled — void only applies to pending (un-landed) rows.
1787
- .where('settled_at', 'is', null)
1788
- .executeTakeFirst();
1789
- log.debug(`dispersed outbox void tx ${txSignature}: ${res.numDeletedRows} row(s)`);
1948
+ voidDispersedOutbox(txSignature, log = logger_1.consoleLogger) {
1949
+ return __awaiter(this, void 0, void 0, function* () {
1950
+ // Void = the burn provably never landed. If the row drew a correction, give that draw back
1951
+ // (decrement consumed_render) in the SAME txn as the delete, so the correction only counts draws
1952
+ // whose burns actually landed. This is how a released reservation is reconciled from chain state.
1953
+ yield db.transaction().execute((trx) => __awaiter(this, void 0, void 0, function* () {
1954
+ var _a;
1955
+ const row = yield trx
1956
+ .selectFrom(exports.DISPERSED_SETTLEMENT_TABLE)
1957
+ .select(['correction_id', 'correction_render'])
1958
+ .where('tx_signature', '=', txSignature)
1959
+ .where('settled_at', 'is', null)
1960
+ .executeTakeFirst();
1961
+ const res = yield trx
1962
+ .deleteFrom(exports.DISPERSED_SETTLEMENT_TABLE)
1963
+ .where('tx_signature', '=', txSignature)
1964
+ // Never delete a row we already settled — void only applies to pending (un-landed) rows.
1965
+ .where('settled_at', 'is', null)
1966
+ .executeTakeFirst();
1967
+ if (((_a = res.numDeletedRows) !== null && _a !== void 0 ? _a : BigInt(0)) > BigInt(0) &&
1968
+ (row === null || row === void 0 ? void 0 : row.correction_id) != null &&
1969
+ row.correction_render != null) {
1970
+ yield trx
1971
+ .updateTable(exports.DISPERSED_BURN_CORRECTION_TABLE)
1972
+ .set({
1973
+ consumed_render: (0, kysely_1.sql) `GREATEST(0, consumed_render - ${row.correction_render}::numeric)`,
1974
+ })
1975
+ .where('id', '=', row.correction_id)
1976
+ .execute();
1977
+ log.debug(`dispersed void tx ${txSignature}: released ${row.correction_render} RENDER to correction ${row.correction_id}`);
1978
+ }
1979
+ log.debug(`dispersed outbox void tx ${txSignature}: ${res.numDeletedRows} row(s)`);
1980
+ }));
1790
1981
  });
1791
1982
  },
1792
- fetchDispersedSettlements(f_1) {
1793
- return __awaiter(this, arguments, void 0, function* (f, _log = logger_1.consoleLogger) {
1794
- var _a;
1983
+ fetchDispersedSettlements(f, _log = logger_1.consoleLogger) {
1984
+ var _a;
1985
+ return __awaiter(this, void 0, void 0, function* () {
1795
1986
  let q = db
1796
1987
  .selectFrom(exports.DISPERSED_SETTLEMENT_TABLE)
1797
1988
  // Settled burns only — pending (un-landed) outbox rows aren't real, completed burns.
@@ -1831,8 +2022,8 @@ const pgClient = (config) => {
1831
2022
  });
1832
2023
  });
1833
2024
  },
1834
- fetchDispersedMonthlyStats(f_1) {
1835
- return __awaiter(this, arguments, void 0, function* (f, _log = logger_1.consoleLogger) {
2025
+ fetchDispersedMonthlyStats(f, _log = logger_1.consoleLogger) {
2026
+ return __awaiter(this, void 0, void 0, function* () {
1836
2027
  let q = db
1837
2028
  .selectFrom(exports.DISPERSED_SETTLEMENT_TABLE)
1838
2029
  .where('settled_at', 'is not', null);
@@ -1862,18 +2053,18 @@ const pgClient = (config) => {
1862
2053
  }));
1863
2054
  });
1864
2055
  },
1865
- fetchJobs(db_1, f_1) {
1866
- return __awaiter(this, arguments, void 0, function* (db, f, log = logger_1.consoleLogger) {
2056
+ fetchJobs(db, f, log = logger_1.consoleLogger) {
2057
+ return __awaiter(this, void 0, void 0, function* () {
1867
2058
  return yield fetchJobs1(db.trx, f, log);
1868
2059
  });
1869
2060
  },
1870
- fetchJobsWithGrantValues(db_1, f_1) {
1871
- return __awaiter(this, arguments, void 0, function* (db, f, log = logger_1.consoleLogger) {
2061
+ fetchJobsWithGrantValues(db, f, log = logger_1.consoleLogger) {
2062
+ return __awaiter(this, void 0, void 0, function* () {
1872
2063
  return yield fetchJobs2(db.trx, f, log);
1873
2064
  });
1874
2065
  },
1875
- fetchLiabilityEpochs(db_1) {
1876
- return __awaiter(this, arguments, void 0, function* (db, log = logger_1.consoleLogger) {
2066
+ fetchLiabilityEpochs(db, log = logger_1.consoleLogger) {
2067
+ return __awaiter(this, void 0, void 0, function* () {
1877
2068
  const q = db.trx
1878
2069
  .selectFrom(exports.LIABILITY_TABLE)
1879
2070
  .select(({ fn }) => [
@@ -1931,8 +2122,8 @@ const pgClient = (config) => {
1931
2122
  }));
1932
2123
  });
1933
2124
  },
1934
- createBurnCorrection(trx_1, p_1) {
1935
- return __awaiter(this, arguments, void 0, function* (trx, p, log = logger_1.consoleLogger) {
2125
+ createBurnCorrection(trx, p, log = logger_1.consoleLogger) {
2126
+ return __awaiter(this, void 0, void 0, function* () {
1936
2127
  // Anchor + unit invariants are also DB CHECKs; validate here for a clearer error.
1937
2128
  if ((p.jobId == null) === (p.solTxId == null))
1938
2129
  throw new Error('burn_correction needs exactly one anchor (jobId XOR solTxId)');
@@ -1962,8 +2153,8 @@ const pgClient = (config) => {
1962
2153
  return Number(row.id);
1963
2154
  });
1964
2155
  },
1965
- getBurnCorrections(trx_1, ps_1) {
1966
- return __awaiter(this, arguments, void 0, function* (trx, ps, log = logger_1.consoleLogger) {
2156
+ getBurnCorrections(trx, ps, log = logger_1.consoleLogger) {
2157
+ return __awaiter(this, void 0, void 0, function* () {
1967
2158
  if (!ps.toFill)
1968
2159
  return [];
1969
2160
  // fill column depends on kind: down_adjust consumes (to_burn - burned); escrow_credit consumes
@@ -2006,8 +2197,8 @@ const pgClient = (config) => {
2006
2197
  });
2007
2198
  });
2008
2199
  },
2009
- insertPolygonUpgrade(db_1, p_1) {
2010
- return __awaiter(this, arguments, void 0, function* (db, p, log = logger_1.consoleLogger) {
2200
+ insertPolygonUpgrade(db, p, log = logger_1.consoleLogger) {
2201
+ return __awaiter(this, void 0, void 0, function* () {
2011
2202
  const solTxId = yield getOrInsertTx(db.trx, {
2012
2203
  sig: p.solTx.sig,
2013
2204
  executedAt: p.solTx.executedAt,
@@ -2025,8 +2216,8 @@ const pgClient = (config) => {
2025
2216
  return;
2026
2217
  });
2027
2218
  },
2028
- fetchPolygonUpgrades(db_1, f_1) {
2029
- return __awaiter(this, arguments, void 0, function* (db, f, log = logger_1.consoleLogger) {
2219
+ fetchPolygonUpgrades(db, f, log = logger_1.consoleLogger) {
2220
+ return __awaiter(this, void 0, void 0, function* () {
2030
2221
  let query = db.trx
2031
2222
  .selectFrom('polygon_upgrade')
2032
2223
  .selectAll('polygon_upgrade')
@@ -2052,13 +2243,18 @@ const pgClient = (config) => {
2052
2243
  }));
2053
2244
  });
2054
2245
  },
2055
- getCurrentUserGrantSpend(db_1, f_1) {
2056
- return __awaiter(this, arguments, void 0, function* (db, f, log = logger_1.consoleLogger) {
2246
+ getCurrentUserGrantSpend(db, f, log = logger_1.consoleLogger) {
2247
+ return __awaiter(this, void 0, void 0, function* () {
2057
2248
  return yield getCurrentUserGrantSpend(db.trx, f, log);
2058
2249
  });
2059
2250
  },
2060
- deleteAll() {
2061
- return __awaiter(this, arguments, void 0, function* (log = logger_1.consoleLogger) {
2251
+ getGrantAllotments(db, f, log = logger_1.consoleLogger) {
2252
+ return __awaiter(this, void 0, void 0, function* () {
2253
+ return yield getGrantAllotments(db.trx, f, log);
2254
+ });
2255
+ },
2256
+ deleteAll(log = logger_1.consoleLogger) {
2257
+ return __awaiter(this, void 0, void 0, function* () {
2062
2258
  if (config.database.includes('render')) {
2063
2259
  throw new Error('cannot nuke from deployed database. call from tests only');
2064
2260
  }
@@ -2104,8 +2300,8 @@ const pgClient = (config) => {
2104
2300
  }
2105
2301
  });
2106
2302
  },
2107
- checkPaymentsForDate(targetDate_1) {
2108
- return __awaiter(this, arguments, void 0, function* (targetDate, channel = 'both', log = logger_1.consoleLogger) {
2303
+ checkPaymentsForDate(targetDate, channel = 'both', log = logger_1.consoleLogger) {
2304
+ return __awaiter(this, void 0, void 0, function* () {
2109
2305
  const startOfDay = new Date(targetDate);
2110
2306
  startOfDay.setHours(0, 0, 0, 0);
2111
2307
  const endOfDay = new Date(targetDate);
@@ -2156,8 +2352,8 @@ const pgClient = (config) => {
2156
2352
  }
2157
2353
  });
2158
2354
  },
2159
- checkEpochProcessed(epochId_1, channel_1) {
2160
- return __awaiter(this, arguments, void 0, function* (epochId, channel, log = logger_1.consoleLogger) {
2355
+ checkEpochProcessed(epochId, channel, log = logger_1.consoleLogger) {
2356
+ return __awaiter(this, void 0, void 0, function* () {
2161
2357
  try {
2162
2358
  const result = yield db
2163
2359
  .selectFrom('epoch')
@@ -2177,8 +2373,8 @@ const pgClient = (config) => {
2177
2373
  }
2178
2374
  });
2179
2375
  },
2180
- checkRecentPaymentActivity() {
2181
- return __awaiter(this, arguments, void 0, function* (lookbackHours = 2, channel = 'both', log = logger_1.consoleLogger) {
2376
+ checkRecentPaymentActivity(lookbackHours = 2, channel = 'both', log = logger_1.consoleLogger) {
2377
+ return __awaiter(this, void 0, void 0, function* () {
2182
2378
  const lookbackTime = new Date(Date.now() - lookbackHours * 60 * 60 * 1000);
2183
2379
  const now = new Date();
2184
2380
  try {