@render-foundation/utils 0.0.246 → 0.0.248
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/client/pg/v2Client.js +77 -68
- package/lib/cjs/client/pg/v2Client.js.map +1 -1
- package/lib/cjs/index.js +2 -1
- package/lib/cjs/index.js.map +1 -1
- package/lib/esm/src/client/pg/v2Client.js +71 -62
- package/lib/esm/src/client/pg/v2Client.js.map +1 -1
- package/lib/esm/src/index.js +1 -1
- package/lib/esm/src/index.js.map +1 -1
- package/lib/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/lib/types/src/client/pg/v2Client.d.ts +21 -0
- package/lib/types/src/client/pg/v2Client.d.ts.map +1 -1
- package/lib/types/src/dbTypesV2.d.ts +2 -0
- package/lib/types/src/dbTypesV2.d.ts.map +1 -1
- package/lib/types/src/index.d.ts +1 -1
- package/lib/types/src/index.d.ts.map +1 -1
- package/package.json +1 -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.foldGrantAllotment = exports.CREDIT_DECIMALS = exports.GRANT_WINDOW_START = 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;
|
|
38
|
+
exports.pgClient = exports.batchFinishedAtMedian = exports.foldGrantAllotment = exports.foldTimeAware = exports.CREDIT_DECIMALS = exports.GRANT_WINDOW_START = 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;
|
|
39
39
|
const kysely_1 = require("kysely");
|
|
40
40
|
const logger_1 = require("../../logger");
|
|
41
41
|
const pg_1 = require("pg");
|
|
@@ -88,6 +88,30 @@ exports.CREDIT_DECIMALS = BigInt(100000000); // 1e8
|
|
|
88
88
|
*
|
|
89
89
|
* fifoGrantId = the oldest grant still holding room, i.e. the one the next spend draws from.
|
|
90
90
|
*/
|
|
91
|
+
/**
|
|
92
|
+
* Time-aware reservoir fold. Events = grant issuances (+, at issue time) and job spend (−, at job time),
|
|
93
|
+
* processed chronologically: a job may only draw entitlement that existed WHEN IT RAN. Spend beyond the
|
|
94
|
+
* balance at that moment is purchase-paid forever — a later grant must not retroactively absorb it
|
|
95
|
+
* (the aggregate `granted − allSpend` formula did exactly that, under-issuing 256 users / 44,243 credits;
|
|
96
|
+
* it also let the UI attribute jobs to grants issued days after they ran).
|
|
97
|
+
*
|
|
98
|
+
* Identity used everywhere (SQL + client): with net = Σamt and overshoot = max prefix deficit,
|
|
99
|
+
* avail = max(net + overshoot, 0); beyondGrants = overshoot.
|
|
100
|
+
*/
|
|
101
|
+
const foldTimeAware = (events) => {
|
|
102
|
+
const sorted = [...events].sort((a, b) => a.t - b.t || (a.amt > b.amt ? -1 : 1)); // credits first on ties
|
|
103
|
+
let run = BigInt(0);
|
|
104
|
+
let minRun = BigInt(0);
|
|
105
|
+
for (const e of sorted) {
|
|
106
|
+
run += e.amt;
|
|
107
|
+
if (run < minRun)
|
|
108
|
+
minRun = run;
|
|
109
|
+
}
|
|
110
|
+
const overshoot = minRun < BigInt(0) ? -minRun : BigInt(0);
|
|
111
|
+
const avail = run + overshoot; // ≥ 0 by construction
|
|
112
|
+
return { avail, beyondGrants: overshoot };
|
|
113
|
+
};
|
|
114
|
+
exports.foldTimeAware = foldTimeAware;
|
|
91
115
|
const foldGrantAllotment = (grants, attributedSpend, correctionSettled) => {
|
|
92
116
|
const granted = grants.reduce((s, g) => s + g.amountCredits, BigInt(0));
|
|
93
117
|
const consumed = (attributedSpend > BigInt(0) ? attributedSpend : BigInt(0)) +
|
|
@@ -232,7 +256,7 @@ const pgClient = (config) => {
|
|
|
232
256
|
// Spendable grant per user, straight off otoy_grant (FIFO), net of what's already been spent or
|
|
233
257
|
// already settled by a burn correction. See GrantAllotment for the units + the double-spend guard.
|
|
234
258
|
const getGrantAllotments = (db, p, log = logger_1.consoleLogger) => __awaiter(void 0, void 0, void 0, function* () {
|
|
235
|
-
var _b
|
|
259
|
+
var _b;
|
|
236
260
|
const out = {};
|
|
237
261
|
if (!p.userIds.length)
|
|
238
262
|
return out;
|
|
@@ -270,70 +294,53 @@ const pgClient = (config) => {
|
|
|
270
294
|
.where('user_id', 'in', p.userIds)
|
|
271
295
|
.groupBy('user_id')
|
|
272
296
|
.execute();
|
|
273
|
-
//
|
|
274
|
-
//
|
|
275
|
-
//
|
|
276
|
-
//
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
297
|
+
// Time-aware fold (see foldTimeAware): one pass over the user's event stream — grant issuances
|
|
298
|
+
// (+, at issue time; the legacy CSV credit at window start; pool-stamped credits as capacity moved
|
|
299
|
+
// out at their created_at) and job spend (−, at completion). A job can only draw entitlement that
|
|
300
|
+
// existed when it ran; overshoot is purchase-paid forever.
|
|
301
|
+
const fold = yield (0, kysely_1.sql) `
|
|
302
|
+
with ev as (
|
|
303
|
+
select user_id::text as u, coalesce(otoy_created_at, ${exports.GRANT_WINDOW_START}::timestamp) as t,
|
|
304
|
+
(amount_credits * 4 * 1e8)::numeric as amt
|
|
305
|
+
from otoy_grant where user_id = any(${p.userIds}::uuid[])
|
|
306
|
+
union all
|
|
307
|
+
select user_id::text, ${exports.GRANT_WINDOW_START}::timestamp, sum(render_spent_delta)::numeric
|
|
308
|
+
from user_grant_spend
|
|
309
|
+
where user_id = any(${p.userIds}::uuid[]) and render_spent_delta > 0 and otoy_grant_id is null
|
|
310
|
+
group by user_id
|
|
311
|
+
union all
|
|
312
|
+
select user_id::text, created_at, -render_spent_delta::numeric
|
|
313
|
+
from user_grant_spend
|
|
314
|
+
where user_id = any(${p.userIds}::uuid[]) and render_spent_delta > 0 and otoy_grant_id is not null
|
|
315
|
+
union all
|
|
316
|
+
select user_id, completed_at, -render_amt::numeric
|
|
317
|
+
from job
|
|
318
|
+
where user_id = any(${p.userIds}::text[]) and completed_at >= ${exports.GRANT_WINDOW_START}::timestamp
|
|
319
|
+
),
|
|
320
|
+
r as (
|
|
321
|
+
select u, amt,
|
|
322
|
+
sum(amt) over (partition by u order by t asc, amt desc rows unbounded preceding) as run
|
|
323
|
+
from ev
|
|
324
|
+
)
|
|
325
|
+
select u as user_id,
|
|
326
|
+
sum(case when amt > 0 then amt else 0 end)::text as granted_total,
|
|
327
|
+
sum(amt)::text as net,
|
|
328
|
+
greatest(-min(run), 0)::text as overshoot
|
|
329
|
+
from r group by u
|
|
330
|
+
`.execute(db); // db is always a Kysely trx at runtime; QueryCreator's type just lacks getExecutor
|
|
331
|
+
const toB = (v) => BigInt(Math.round(Number(v !== null && v !== void 0 ? v : 0)));
|
|
332
|
+
for (const r of fold.rows) {
|
|
333
|
+
const u = r.user_id;
|
|
308
334
|
const cur = (_b = out[u]) !== null && _b !== void 0 ? _b : (out[u] = { userId: u, granted: BigInt(0), consumed: BigInt(0), avail: BigInt(0), grants: [] });
|
|
309
|
-
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
// legacy-only users (no mirrored grant) still get an entry — their CSV balance is a granted source
|
|
319
|
-
for (const [u, v] of legBy) {
|
|
320
|
-
if (v > BigInt(0) && !out[u])
|
|
321
|
-
out[u] = { userId: u, granted: BigInt(0), consumed: BigInt(0), avail: BigInt(0), grants: [] };
|
|
322
|
-
}
|
|
323
|
-
for (const u of Object.keys(out)) {
|
|
324
|
-
const attributed = (_c = spentBy.get(u)) !== null && _c !== void 0 ? _c : BigInt(0); // ledger-counted (burns + pool-tied credits)
|
|
325
|
-
const spend = (_d = jobBy.get(u)) !== null && _d !== void 0 ? _d : BigInt(0); // ALL in-window job spend
|
|
326
|
-
const leg = (_e = legBy.get(u)) !== null && _e !== void 0 ? _e : BigInt(0);
|
|
327
|
-
// consumed = the larger of the two accountings — the ledger can exceed job spend (pool-tied
|
|
328
|
-
// credits retiring capacity), job spend can exceed the ledger (pre-go-live buy-and-burn jobs).
|
|
329
|
-
const consumed = attributed > spend ? attributed : spend;
|
|
330
|
-
const f = (0, exports.foldGrantAllotment)(out[u].grants, consumed, BigInt(0));
|
|
331
|
-
out[u].granted = f.granted + leg;
|
|
332
|
-
out[u].consumed = consumed;
|
|
333
|
-
out[u].avail =
|
|
334
|
-
f.granted + leg > consumed ? f.granted + leg - consumed : BigInt(0);
|
|
335
|
-
if (spend > attributed) {
|
|
336
|
-
log.info(`grant allotment ${u}: in-window job spend ${spend} > ledger-counted ${attributed} — strict consumption applied (granted ${f.granted} + legacy ${leg} => avail ${out[u].avail})`);
|
|
335
|
+
const granted = toB(r.granted_total);
|
|
336
|
+
const net = toB(r.net);
|
|
337
|
+
const overshoot = toB(r.overshoot);
|
|
338
|
+
const avail = net + overshoot > BigInt(0) ? net + overshoot : BigInt(0);
|
|
339
|
+
cur.granted = granted;
|
|
340
|
+
cur.consumed = granted - avail; // grant capacity actually charged
|
|
341
|
+
cur.avail = avail;
|
|
342
|
+
if (overshoot > BigInt(0)) {
|
|
343
|
+
log.info(`grant allotment ${u}: time-aware fold — overshoot ${overshoot} is purchase-paid; granted ${granted} => avail ${avail}`);
|
|
337
344
|
}
|
|
338
345
|
}
|
|
339
346
|
return out;
|
|
@@ -2009,7 +2016,7 @@ const pgClient = (config) => {
|
|
|
2009
2016
|
// took the room), the whole txn rolls back and we return {inserted:false} — the caller must NOT
|
|
2010
2017
|
// broadcast (no row, no counter change, no burn).
|
|
2011
2018
|
return yield db.transaction().execute((trx) => __awaiter(this, void 0, void 0, function* () {
|
|
2012
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
2019
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
2013
2020
|
const res = yield trx
|
|
2014
2021
|
.insertInto(exports.DISPERSED_SETTLEMENT_TABLE)
|
|
2015
2022
|
.values({
|
|
@@ -2024,10 +2031,12 @@ const pgClient = (config) => {
|
|
|
2024
2031
|
usdc_spent: (_c = p.usdcSpent) !== null && _c !== void 0 ? _c : null,
|
|
2025
2032
|
correction_id: (_d = p.correctionId) !== null && _d !== void 0 ? _d : null,
|
|
2026
2033
|
correction_render: (_e = p.correctionRender) !== null && _e !== void 0 ? _e : null,
|
|
2034
|
+
usage_from: (_f = p.usageFrom) !== null && _f !== void 0 ? _f : null,
|
|
2035
|
+
usage_to: (_g = p.usageTo) !== null && _g !== void 0 ? _g : null,
|
|
2027
2036
|
})
|
|
2028
2037
|
.onConflict((oc) => oc.column('tx_signature').doNothing())
|
|
2029
2038
|
.executeTakeFirst();
|
|
2030
|
-
const inserted = ((
|
|
2039
|
+
const inserted = ((_h = res.numInsertedOrUpdatedRows) !== null && _h !== void 0 ? _h : BigInt(0)) > BigInt(0);
|
|
2031
2040
|
// Only reserve when the row was actually inserted (a tx_signature conflict is a no-op → no double
|
|
2032
2041
|
// count) and it drew a correction.
|
|
2033
2042
|
if (inserted &&
|
|
@@ -2042,7 +2051,7 @@ const pgClient = (config) => {
|
|
|
2042
2051
|
.where('id', '=', String(p.correctionId))
|
|
2043
2052
|
.where((eb) => eb((0, kysely_1.sql) `consumed_render + ${p.correctionRender}::numeric`, '<=', eb.ref('amount_render')))
|
|
2044
2053
|
.executeTakeFirst();
|
|
2045
|
-
if (((
|
|
2054
|
+
if (((_j = upd.numUpdatedRows) !== null && _j !== void 0 ? _j : BigInt(0)) === BigInt(0)) {
|
|
2046
2055
|
// Would exceed amount_render (concurrent draw won the room) — roll back the whole txn.
|
|
2047
2056
|
log.warn(`dispersed correction ${p.correctionId}: draw ${p.correctionRender} would over-consume; rejecting outbox insert for tx ${p.txSignature}`);
|
|
2048
2057
|
throw new DispersedCorrectionRejected();
|