@render-foundation/utils 0.0.249 → 0.0.250
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 +109 -112
- package/lib/cjs/client/pg/v2Client.js.map +1 -1
- package/lib/cjs/index.js +4 -3
- package/lib/cjs/index.js.map +1 -1
- package/lib/esm/src/client/pg/v2Client.js +104 -108
- 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 +37 -22
- package/lib/types/src/client/pg/v2Client.d.ts.map +1 -1
- package/lib/types/src/index.d.ts +2 -2
- 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.
|
|
38
|
+
exports.pgClient = exports.batchFinishedAtMedian = exports.foldGrantWindow = exports.GRANT_LOOKBACK_MS = exports.GRANT_LOOKBACK_DAYS = 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");
|
|
@@ -76,73 +76,97 @@ exports.EUR_TO_CREDITS = BigInt(4);
|
|
|
76
76
|
// emissions-funded ones (counting only emissions-attributed spend over-issued ~2.58M credits, 2026-08-04).
|
|
77
77
|
exports.GRANT_WINDOW_START = '2025-05-01';
|
|
78
78
|
exports.CREDIT_DECIMALS = BigInt(100000000); // 1e8
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
* the same grant from also funding a future emissions burn.
|
|
88
|
-
*
|
|
89
|
-
* fifoGrantId = the oldest grant still holding room, i.e. the one the next spend draws from.
|
|
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
|
-
// True reservoir semantics, chronological:
|
|
103
|
-
// grant + -> capacity += amt
|
|
104
|
-
// grant − -> capacity = max(capacity + amt, 0) (a REVERSAL claws back capacity; it is not spend
|
|
105
|
-
// and can never create "beyond grants" — the linear min-run identity got this wrong:
|
|
106
|
-
// a reversal ordered before its covering positives produced phantom overshoot)
|
|
107
|
-
// job -> draw min(need, capacity); the un-drawable remainder is purchase-paid forever
|
|
79
|
+
// A grant is consumable for this long after issuance; whatever is left after that EXPIRES (2026-08-05
|
|
80
|
+
// model change). A job can only draw grants issued within the lookback window before it ran.
|
|
81
|
+
exports.GRANT_LOOKBACK_DAYS = 60;
|
|
82
|
+
exports.GRANT_LOOKBACK_MS = exports.GRANT_LOOKBACK_DAYS * 24 * 3600 * 1000;
|
|
83
|
+
const foldGrantWindow = (events, opts = {}) => {
|
|
84
|
+
var _a, _b, _c;
|
|
85
|
+
const lookback = (_a = opts.lookbackMs) !== null && _a !== void 0 ? _a : exports.GRANT_LOOKBACK_MS;
|
|
86
|
+
const now = (_b = opts.now) !== null && _b !== void 0 ? _b : Date.now();
|
|
108
87
|
const sorted = [...events].sort((a, b) => a.t - b.t || (a.amt > b.amt ? -1 : 1)); // credits first on ties
|
|
109
|
-
|
|
88
|
+
const buckets = [];
|
|
89
|
+
const remaining = (b) => b.amount - b.reversed - b.drawnEmissions - b.drawnMisrouted;
|
|
110
90
|
let beyond = BigInt(0);
|
|
111
|
-
let charged = BigInt(0);
|
|
112
91
|
for (const e of sorted) {
|
|
113
92
|
if (e.src === 'grant') {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
93
|
+
if (e.amt > BigInt(0)) {
|
|
94
|
+
buckets.push({
|
|
95
|
+
grantId: e.grantId,
|
|
96
|
+
t: e.t,
|
|
97
|
+
amount: e.amt,
|
|
98
|
+
reversed: BigInt(0),
|
|
99
|
+
drawnEmissions: BigInt(0),
|
|
100
|
+
drawnMisrouted: BigInt(0),
|
|
101
|
+
expired: BigInt(0),
|
|
102
|
+
left: BigInt(0),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
let claw = -e.amt; // clawback hits remaining capacity oldest-first; excess is dropped
|
|
107
|
+
for (const b of buckets) {
|
|
108
|
+
if (claw <= BigInt(0))
|
|
109
|
+
break;
|
|
110
|
+
const take = remaining(b) < claw ? remaining(b) : claw;
|
|
111
|
+
if (take > BigInt(0)) {
|
|
112
|
+
b.reversed += take;
|
|
113
|
+
claw -= take;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
117
|
}
|
|
118
118
|
else {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
119
|
+
let need = -e.amt;
|
|
120
|
+
let emissionsLeft = (_c = e.emissions) !== null && _c !== void 0 ? _c : need; // no split provided -> count it all as emissions
|
|
121
|
+
for (const b of buckets) {
|
|
122
|
+
if (need <= BigInt(0))
|
|
123
|
+
break;
|
|
124
|
+
if (b.t + lookback < e.t)
|
|
125
|
+
continue; // grant expired before this job ran
|
|
126
|
+
const take = remaining(b) < need ? remaining(b) : need;
|
|
127
|
+
if (take <= BigInt(0))
|
|
128
|
+
continue;
|
|
129
|
+
const em = emissionsLeft < take ? emissionsLeft : take;
|
|
130
|
+
b.drawnEmissions += em;
|
|
131
|
+
b.drawnMisrouted += take - em;
|
|
132
|
+
emissionsLeft -= em;
|
|
133
|
+
need -= take;
|
|
134
|
+
}
|
|
135
|
+
beyond += need;
|
|
124
136
|
}
|
|
125
137
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const granted = grants.reduce((s, g) => s + g.amountCredits, BigInt(0));
|
|
131
|
-
const consumed = (attributedSpend > BigInt(0) ? attributedSpend : BigInt(0)) +
|
|
132
|
-
(correctionSettled > BigInt(0) ? correctionSettled : BigInt(0));
|
|
133
|
-
const avail = granted > consumed ? granted - consumed : BigInt(0);
|
|
134
|
-
let drawn = consumed;
|
|
138
|
+
let avail = BigInt(0);
|
|
139
|
+
let expired = BigInt(0);
|
|
140
|
+
let usedEmissions = BigInt(0);
|
|
141
|
+
let usedMisrouted = BigInt(0);
|
|
135
142
|
let fifoGrantId;
|
|
136
|
-
for (const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
143
|
+
for (const b of buckets) {
|
|
144
|
+
usedEmissions += b.drawnEmissions;
|
|
145
|
+
usedMisrouted += b.drawnMisrouted;
|
|
146
|
+
const rem = remaining(b);
|
|
147
|
+
if (b.t + lookback < now) {
|
|
148
|
+
b.expired = rem;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
b.left = rem;
|
|
152
|
+
avail += rem;
|
|
153
|
+
if (fifoGrantId === undefined && rem > BigInt(0) && b.grantId !== undefined)
|
|
154
|
+
fifoGrantId = b.grantId;
|
|
140
155
|
}
|
|
141
|
-
|
|
156
|
+
expired += b.expired;
|
|
142
157
|
}
|
|
143
|
-
return {
|
|
158
|
+
return {
|
|
159
|
+
avail,
|
|
160
|
+
beyondGrants: beyond,
|
|
161
|
+
jobCharged: usedEmissions + usedMisrouted,
|
|
162
|
+
usedEmissions,
|
|
163
|
+
usedMisrouted,
|
|
164
|
+
expired,
|
|
165
|
+
buckets,
|
|
166
|
+
fifoGrantId,
|
|
167
|
+
};
|
|
144
168
|
};
|
|
145
|
-
exports.
|
|
169
|
+
exports.foldGrantWindow = foldGrantWindow;
|
|
146
170
|
const base_2 = require("./base");
|
|
147
171
|
const moment_1 = __importDefault(require("moment"));
|
|
148
172
|
pg.types.setTypeParser(1114, (str) => moment_1.default.utc(str).toDate());
|
|
@@ -274,74 +298,50 @@ const pgClient = (config) => {
|
|
|
274
298
|
const out = {};
|
|
275
299
|
if (!p.userIds.length)
|
|
276
300
|
return out;
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
.select(['id', 'user_id', 'amount_credits', 'otoy_created_at'])
|
|
280
|
-
.where('user_id', 'in', p.userIds)
|
|
281
|
-
.orderBy('otoy_created_at', 'asc')
|
|
282
|
-
.orderBy('id', 'asc')
|
|
283
|
-
.execute();
|
|
284
|
-
if (!grants.length)
|
|
285
|
-
return out;
|
|
286
|
-
// EVERY grant-ledger row retires pool capacity, in either direction:
|
|
287
|
-
// - negative delta = an emissions burn. ALL of them count, not just ones stamped with an
|
|
288
|
-
// otoy_grant_id. Historical grants are now backfilled to cover the Sep/Oct-2025 burns, so those
|
|
289
|
-
// burns must net against them; counting only stamped rows would re-issue ~1.06M credits of
|
|
290
|
-
// already-spent grant.
|
|
291
|
-
// - positive delta = the grant was CREDITED into current_user_grant_spend (the one-off canary
|
|
292
|
-
// route). That balance reaches the split separately, so leaving it in the pool double-issues it.
|
|
293
|
-
// Hence abs() over the whole ledger.
|
|
294
|
-
// NOT abs() over the whole ledger — that would charge the Sep/Oct-2025 CSV *credits* as if they
|
|
295
|
-
// were spend, double-penalising those users (464e8e33: 376,749 burned + 79,304 credited = 456,053).
|
|
296
|
-
// A positive only retires capacity when it is tied to a pool grant (the canary route, where the
|
|
297
|
-
// grant was moved into current_user_grant_spend and reaches the split from there).
|
|
298
|
-
const spent = yield db
|
|
299
|
-
.selectFrom(exports.USER_GRANT_SPEND_TABLE)
|
|
300
|
-
.select((eb) => [
|
|
301
|
-
'user_id',
|
|
302
|
-
eb.fn
|
|
303
|
-
.sum((0, kysely_1.sql) `case when render_spent_delta < 0 then -render_spent_delta
|
|
304
|
-
when otoy_grant_id is not null then render_spent_delta
|
|
305
|
-
else 0 end`)
|
|
306
|
-
.as('net'),
|
|
307
|
-
])
|
|
308
|
-
.where('user_id', 'in', p.userIds)
|
|
309
|
-
.groupBy('user_id')
|
|
310
|
-
.execute();
|
|
311
|
-
// Fetch the raw event stream and fold in code (foldTimeAware — reservoir semantics; reversals
|
|
312
|
-
// clamp capacity, only jobs create beyond-grants). SQL window identities can't express the clamp.
|
|
301
|
+
// Raw event stream, folded in code (foldGrantWindow — per-grant buckets, 60-day consumable window,
|
|
302
|
+
// reversals clamp capacity, only jobs create beyond-grants). SQL can't express the bucketed clamp.
|
|
313
303
|
const evRes = yield (0, kysely_1.sql) `
|
|
314
304
|
select user_id::text as u, coalesce(otoy_created_at, ${exports.GRANT_WINDOW_START}::timestamp) as t,
|
|
315
|
-
(amount_credits * 4 * 1e8)::numeric::text as amt, 'grant' as src
|
|
305
|
+
(amount_credits * 4 * 1e8)::numeric::text as amt, 'grant' as src, id as gid
|
|
316
306
|
from otoy_grant where user_id = any(${p.userIds}::uuid[])
|
|
317
307
|
union all
|
|
318
|
-
select user_id::text, ${exports.GRANT_WINDOW_START}::timestamp, sum(render_spent_delta)::text, 'grant'
|
|
308
|
+
select user_id::text, ${exports.GRANT_WINDOW_START}::timestamp, sum(render_spent_delta)::text, 'grant', null
|
|
319
309
|
from user_grant_spend
|
|
320
310
|
where user_id = any(${p.userIds}::uuid[]) and render_spent_delta > 0 and otoy_grant_id is null
|
|
321
311
|
group by user_id
|
|
322
312
|
union all
|
|
323
|
-
select user_id::text, created_at, (-render_spent_delta)::text, 'grant'
|
|
313
|
+
select user_id::text, created_at, (-render_spent_delta)::text, 'grant', null
|
|
324
314
|
from user_grant_spend
|
|
325
315
|
where user_id = any(${p.userIds}::uuid[]) and render_spent_delta > 0 and otoy_grant_id is not null
|
|
326
316
|
union all
|
|
327
|
-
select user_id, completed_at, (-render_amt)::text, 'job'
|
|
317
|
+
select user_id, completed_at, (-render_amt)::text, 'job', null
|
|
328
318
|
from job
|
|
329
319
|
where user_id = any(${p.userIds}::text[]) and completed_at >= ${exports.GRANT_WINDOW_START}::timestamp
|
|
330
320
|
`.execute(db); // db is always a Kysely trx at runtime
|
|
331
321
|
const byUser = new Map();
|
|
332
322
|
for (const r of evRes.rows) {
|
|
333
323
|
const arr = (_b = byUser.get(r.u)) !== null && _b !== void 0 ? _b : [];
|
|
334
|
-
arr.push({
|
|
324
|
+
arr.push({
|
|
325
|
+
t: new Date(r.t).getTime(),
|
|
326
|
+
amt: BigInt(Math.round(Number(r.amt))),
|
|
327
|
+
src: r.src,
|
|
328
|
+
grantId: (_c = r.gid) !== null && _c !== void 0 ? _c : undefined,
|
|
329
|
+
});
|
|
335
330
|
byUser.set(r.u, arr);
|
|
336
331
|
}
|
|
337
332
|
for (const [u, events] of byUser) {
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
333
|
+
const f = (0, exports.foldGrantWindow)(events);
|
|
334
|
+
out[u] = {
|
|
335
|
+
userId: u,
|
|
336
|
+
granted: events.reduce((a, e) => (e.src === 'grant' && e.amt > BigInt(0) ? a + e.amt : a), BigInt(0)),
|
|
337
|
+
consumed: f.jobCharged,
|
|
338
|
+
avail: f.avail,
|
|
339
|
+
expired: f.expired,
|
|
340
|
+
fifoGrantId: f.fifoGrantId,
|
|
341
|
+
};
|
|
343
342
|
if (f.beyondGrants > BigInt(0)) {
|
|
344
|
-
log.info(`grant allotment ${u}: ${f.beyondGrants} beyond grants (purchase-paid at the time)
|
|
343
|
+
log.info(`grant allotment ${u}: ${f.beyondGrants} beyond grants (purchase-paid at the time), ` +
|
|
344
|
+
`${f.expired} expired past the ${exports.GRANT_LOOKBACK_DAYS}d window — avail ${f.avail}`);
|
|
345
345
|
}
|
|
346
346
|
}
|
|
347
347
|
return out;
|
|
@@ -1210,7 +1210,7 @@ const pgClient = (config) => {
|
|
|
1210
1210
|
});
|
|
1211
1211
|
},
|
|
1212
1212
|
insertGrantSpend(db, p, log = logger_1.consoleLogger) {
|
|
1213
|
-
var _a, _b, _c;
|
|
1213
|
+
var _a, _b, _c, _d;
|
|
1214
1214
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1215
1215
|
const existing = yield getCurrentUserGrantSpend(db.trx, { userId: p.userId }, log);
|
|
1216
1216
|
const diff = {
|
|
@@ -1237,16 +1237,13 @@ const pgClient = (config) => {
|
|
|
1237
1237
|
return;
|
|
1238
1238
|
}
|
|
1239
1239
|
}
|
|
1240
|
-
// FIFO-attribute this spend to the user's oldest OTOY grant that still has room
|
|
1241
|
-
//
|
|
1242
|
-
// would leave the grant looking unspent
|
|
1240
|
+
// FIFO-attribute this spend to the user's oldest LIVE OTOY grant that still has room (the fold
|
|
1241
|
+
// already applies the lookback window). Stamping otoy_grant_id is what shows the draw in the
|
|
1242
|
+
// admin drill-down — an unattributed row would leave the grant looking unspent there.
|
|
1243
1243
|
let otoyGrantId = p.otoyGrantId;
|
|
1244
1244
|
if (otoyGrantId === undefined && p.renderSpentDelta < BigInt(0)) {
|
|
1245
1245
|
const allots = yield getGrantAllotments(db.trx, { userIds: [p.userId] }, log);
|
|
1246
|
-
|
|
1247
|
-
if (a) {
|
|
1248
|
-
otoyGrantId = (0, exports.foldGrantAllotment)(a.grants, a.consumed, BigInt(0)).fifoGrantId;
|
|
1249
|
-
}
|
|
1246
|
+
otoyGrantId = (_c = allots[p.userId]) === null || _c === void 0 ? void 0 : _c.fifoGrantId;
|
|
1250
1247
|
}
|
|
1251
1248
|
const v = {
|
|
1252
1249
|
user_id: p.userId,
|
|
@@ -1272,7 +1269,7 @@ const pgClient = (config) => {
|
|
|
1272
1269
|
user_id: p.userId,
|
|
1273
1270
|
render_spent: p.renderSpentDelta,
|
|
1274
1271
|
description: p.description,
|
|
1275
|
-
perpetual: (
|
|
1272
|
+
perpetual: (_d = p.perpetual) !== null && _d !== void 0 ? _d : false,
|
|
1276
1273
|
})
|
|
1277
1274
|
.onConflict((oc) => oc.column('user_id').doUpdateSet(diff))
|
|
1278
1275
|
.returning((eb) => [
|