@render-foundation/utils 0.0.248 → 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 +136 -138
- 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 +130 -133
- 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 +39 -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,59 +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) => {
|
|
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();
|
|
102
87
|
const sorted = [...events].sort((a, b) => a.t - b.t || (a.amt > b.amt ? -1 : 1)); // credits first on ties
|
|
103
|
-
|
|
104
|
-
|
|
88
|
+
const buckets = [];
|
|
89
|
+
const remaining = (b) => b.amount - b.reversed - b.drawnEmissions - b.drawnMisrouted;
|
|
90
|
+
let beyond = BigInt(0);
|
|
105
91
|
for (const e of sorted) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
92
|
+
if (e.src === 'grant') {
|
|
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
|
+
}
|
|
118
|
+
else {
|
|
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;
|
|
136
|
+
}
|
|
109
137
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
exports.foldTimeAware = foldTimeAware;
|
|
115
|
-
const foldGrantAllotment = (grants, attributedSpend, correctionSettled) => {
|
|
116
|
-
const granted = grants.reduce((s, g) => s + g.amountCredits, BigInt(0));
|
|
117
|
-
const consumed = (attributedSpend > BigInt(0) ? attributedSpend : BigInt(0)) +
|
|
118
|
-
(correctionSettled > BigInt(0) ? correctionSettled : BigInt(0));
|
|
119
|
-
const avail = granted > consumed ? granted - consumed : BigInt(0);
|
|
120
|
-
let drawn = consumed;
|
|
138
|
+
let avail = BigInt(0);
|
|
139
|
+
let expired = BigInt(0);
|
|
140
|
+
let usedEmissions = BigInt(0);
|
|
141
|
+
let usedMisrouted = BigInt(0);
|
|
121
142
|
let fifoGrantId;
|
|
122
|
-
for (const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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;
|
|
126
149
|
}
|
|
127
|
-
|
|
150
|
+
else {
|
|
151
|
+
b.left = rem;
|
|
152
|
+
avail += rem;
|
|
153
|
+
if (fifoGrantId === undefined && rem > BigInt(0) && b.grantId !== undefined)
|
|
154
|
+
fifoGrantId = b.grantId;
|
|
155
|
+
}
|
|
156
|
+
expired += b.expired;
|
|
128
157
|
}
|
|
129
|
-
return {
|
|
158
|
+
return {
|
|
159
|
+
avail,
|
|
160
|
+
beyondGrants: beyond,
|
|
161
|
+
jobCharged: usedEmissions + usedMisrouted,
|
|
162
|
+
usedEmissions,
|
|
163
|
+
usedMisrouted,
|
|
164
|
+
expired,
|
|
165
|
+
buckets,
|
|
166
|
+
fifoGrantId,
|
|
167
|
+
};
|
|
130
168
|
};
|
|
131
|
-
exports.
|
|
169
|
+
exports.foldGrantWindow = foldGrantWindow;
|
|
132
170
|
const base_2 = require("./base");
|
|
133
171
|
const moment_1 = __importDefault(require("moment"));
|
|
134
172
|
pg.types.setTypeParser(1114, (str) => moment_1.default.utc(str).toDate());
|
|
@@ -256,91 +294,54 @@ const pgClient = (config) => {
|
|
|
256
294
|
// Spendable grant per user, straight off otoy_grant (FIFO), net of what's already been spent or
|
|
257
295
|
// already settled by a burn correction. See GrantAllotment for the units + the double-spend guard.
|
|
258
296
|
const getGrantAllotments = (db, p, log = logger_1.consoleLogger) => __awaiter(void 0, void 0, void 0, function* () {
|
|
259
|
-
var _b;
|
|
297
|
+
var _b, _c;
|
|
260
298
|
const out = {};
|
|
261
299
|
if (!p.userIds.length)
|
|
262
300
|
return out;
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
const
|
|
285
|
-
.
|
|
286
|
-
.
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
.
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
.
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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;
|
|
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: [] });
|
|
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}`);
|
|
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.
|
|
303
|
+
const evRes = yield (0, kysely_1.sql) `
|
|
304
|
+
select user_id::text as u, coalesce(otoy_created_at, ${exports.GRANT_WINDOW_START}::timestamp) as t,
|
|
305
|
+
(amount_credits * 4 * 1e8)::numeric::text as amt, 'grant' as src, id as gid
|
|
306
|
+
from otoy_grant where user_id = any(${p.userIds}::uuid[])
|
|
307
|
+
union all
|
|
308
|
+
select user_id::text, ${exports.GRANT_WINDOW_START}::timestamp, sum(render_spent_delta)::text, 'grant', null
|
|
309
|
+
from user_grant_spend
|
|
310
|
+
where user_id = any(${p.userIds}::uuid[]) and render_spent_delta > 0 and otoy_grant_id is null
|
|
311
|
+
group by user_id
|
|
312
|
+
union all
|
|
313
|
+
select user_id::text, created_at, (-render_spent_delta)::text, 'grant', null
|
|
314
|
+
from user_grant_spend
|
|
315
|
+
where user_id = any(${p.userIds}::uuid[]) and render_spent_delta > 0 and otoy_grant_id is not null
|
|
316
|
+
union all
|
|
317
|
+
select user_id, completed_at, (-render_amt)::text, 'job', null
|
|
318
|
+
from job
|
|
319
|
+
where user_id = any(${p.userIds}::text[]) and completed_at >= ${exports.GRANT_WINDOW_START}::timestamp
|
|
320
|
+
`.execute(db); // db is always a Kysely trx at runtime
|
|
321
|
+
const byUser = new Map();
|
|
322
|
+
for (const r of evRes.rows) {
|
|
323
|
+
const arr = (_b = byUser.get(r.u)) !== null && _b !== void 0 ? _b : [];
|
|
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
|
+
});
|
|
330
|
+
byUser.set(r.u, arr);
|
|
331
|
+
}
|
|
332
|
+
for (const [u, events] of byUser) {
|
|
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
|
+
};
|
|
342
|
+
if (f.beyondGrants > BigInt(0)) {
|
|
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}`);
|
|
344
345
|
}
|
|
345
346
|
}
|
|
346
347
|
return out;
|
|
@@ -1209,7 +1210,7 @@ const pgClient = (config) => {
|
|
|
1209
1210
|
});
|
|
1210
1211
|
},
|
|
1211
1212
|
insertGrantSpend(db, p, log = logger_1.consoleLogger) {
|
|
1212
|
-
var _a, _b, _c;
|
|
1213
|
+
var _a, _b, _c, _d;
|
|
1213
1214
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1214
1215
|
const existing = yield getCurrentUserGrantSpend(db.trx, { userId: p.userId }, log);
|
|
1215
1216
|
const diff = {
|
|
@@ -1236,16 +1237,13 @@ const pgClient = (config) => {
|
|
|
1236
1237
|
return;
|
|
1237
1238
|
}
|
|
1238
1239
|
}
|
|
1239
|
-
// FIFO-attribute this spend to the user's oldest OTOY grant that still has room
|
|
1240
|
-
//
|
|
1241
|
-
// 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.
|
|
1242
1243
|
let otoyGrantId = p.otoyGrantId;
|
|
1243
1244
|
if (otoyGrantId === undefined && p.renderSpentDelta < BigInt(0)) {
|
|
1244
1245
|
const allots = yield getGrantAllotments(db.trx, { userIds: [p.userId] }, log);
|
|
1245
|
-
|
|
1246
|
-
if (a) {
|
|
1247
|
-
otoyGrantId = (0, exports.foldGrantAllotment)(a.grants, a.consumed, BigInt(0)).fifoGrantId;
|
|
1248
|
-
}
|
|
1246
|
+
otoyGrantId = (_c = allots[p.userId]) === null || _c === void 0 ? void 0 : _c.fifoGrantId;
|
|
1249
1247
|
}
|
|
1250
1248
|
const v = {
|
|
1251
1249
|
user_id: p.userId,
|
|
@@ -1271,7 +1269,7 @@ const pgClient = (config) => {
|
|
|
1271
1269
|
user_id: p.userId,
|
|
1272
1270
|
render_spent: p.renderSpentDelta,
|
|
1273
1271
|
description: p.description,
|
|
1274
|
-
perpetual: (
|
|
1272
|
+
perpetual: (_d = p.perpetual) !== null && _d !== void 0 ? _d : false,
|
|
1275
1273
|
})
|
|
1276
1274
|
.onConflict((oc) => oc.column('user_id').doUpdateSet(diff))
|
|
1277
1275
|
.returning((eb) => [
|