@render-foundation/utils 0.0.180 → 0.0.182
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/backfiller.js +895 -0
- package/lib/cjs/backfiller.js.map +1 -0
- package/lib/cjs/client/onchain.js +132 -0
- package/lib/cjs/client/onchain.js.map +1 -0
- package/lib/cjs/client/pg/base.js +198 -0
- package/lib/cjs/client/pg/base.js.map +1 -0
- package/lib/cjs/client/pg/v1.js +623 -0
- package/lib/cjs/client/pg/v1.js.map +1 -0
- package/lib/cjs/client/pg/v2Client.js +1584 -0
- package/lib/cjs/client/pg/v2Client.js.map +1 -0
- package/lib/cjs/client/pg/viewClient.js +107 -0
- package/lib/cjs/client/pg/viewClient.js.map +1 -0
- package/lib/cjs/client/price.js +63 -0
- package/lib/cjs/client/price.js.map +1 -0
- package/lib/cjs/client/render.js +173 -0
- package/lib/cjs/client/render.js.map +1 -0
- package/lib/cjs/client/squads.js +204 -0
- package/lib/cjs/client/squads.js.map +1 -0
- package/lib/cjs/client/trade.js +235 -0
- package/lib/cjs/client/trade.js.map +1 -0
- package/lib/cjs/client/wormhole.js +69 -0
- package/lib/cjs/client/wormhole.js.map +1 -0
- package/lib/cjs/constants.js +46 -0
- package/lib/cjs/constants.js.map +1 -0
- package/lib/cjs/dbTypes.js +3 -0
- package/lib/cjs/dbTypes.js.map +1 -0
- package/lib/cjs/dbTypesV2.js +3 -0
- package/lib/cjs/dbTypesV2.js.map +1 -0
- package/lib/cjs/dbViewTypes.js +3 -0
- package/lib/cjs/dbViewTypes.js.map +1 -0
- package/lib/cjs/errorReporter.js +126 -0
- package/lib/cjs/errorReporter.js.map +1 -0
- package/lib/cjs/index.js +44 -0
- package/lib/cjs/index.js.map +1 -0
- package/lib/cjs/logger.js +21 -0
- package/lib/cjs/logger.js.map +1 -0
- package/lib/esm/src/backfiller.js +850 -0
- package/lib/esm/src/backfiller.js.map +1 -0
- package/lib/esm/src/client/onchain.js +122 -0
- package/lib/esm/src/client/onchain.js.map +1 -0
- package/lib/esm/src/client/pg/base.js +162 -0
- package/lib/esm/src/client/pg/base.js.map +1 -0
- package/lib/esm/src/client/pg/v1.js +559 -0
- package/lib/esm/src/client/pg/v1.js.map +1 -0
- package/lib/esm/src/client/pg/v2Client.js +1465 -0
- package/lib/esm/src/client/pg/v2Client.js.map +1 -0
- package/lib/esm/src/client/pg/viewClient.js +91 -0
- package/lib/esm/src/client/pg/viewClient.js.map +1 -0
- package/lib/esm/src/client/price.js +41 -0
- package/lib/esm/src/client/price.js.map +1 -0
- package/lib/esm/src/client/render.js +147 -0
- package/lib/esm/src/client/render.js.map +1 -0
- package/lib/esm/src/client/squads.js +163 -0
- package/lib/esm/src/client/squads.js.map +1 -0
- package/lib/esm/src/client/trade.js +219 -0
- package/lib/esm/src/client/trade.js.map +1 -0
- package/lib/esm/src/client/wormhole.js +33 -0
- package/lib/esm/src/client/wormhole.js.map +1 -0
- package/lib/esm/src/constants.js +43 -0
- package/lib/esm/src/constants.js.map +1 -0
- package/lib/esm/src/dbTypes.js +2 -0
- package/lib/esm/src/dbTypes.js.map +1 -0
- package/lib/esm/src/dbTypesV2.js +2 -0
- package/lib/esm/src/dbTypesV2.js.map +1 -0
- package/lib/esm/src/dbViewTypes.js +2 -0
- package/lib/esm/src/dbViewTypes.js.map +1 -0
- package/lib/esm/src/errorReporter.js +117 -0
- package/lib/esm/src/errorReporter.js.map +1 -0
- package/lib/esm/src/index.js +15 -0
- package/lib/esm/src/index.js.map +1 -0
- package/lib/esm/src/logger.js +18 -0
- package/lib/esm/src/logger.js.map +1 -0
- package/lib/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/lib/types/src/backfiller.d.ts +62 -0
- package/lib/types/src/backfiller.d.ts.map +1 -0
- package/lib/types/src/client/onchain.d.ts +24 -0
- package/lib/types/src/client/onchain.d.ts.map +1 -0
- package/lib/types/src/client/pg/base.d.ts +82 -0
- package/lib/types/src/client/pg/base.d.ts.map +1 -0
- package/lib/types/src/client/pg/v1.d.ts +137 -0
- package/lib/types/src/client/pg/v1.d.ts.map +1 -0
- package/lib/types/src/client/pg/v2Client.d.ts +309 -0
- package/lib/types/src/client/pg/v2Client.d.ts.map +1 -0
- package/lib/types/src/client/pg/viewClient.d.ts +55 -0
- package/lib/types/src/client/pg/viewClient.d.ts.map +1 -0
- package/lib/types/src/client/price.d.ts +12 -0
- package/lib/types/src/client/price.d.ts.map +1 -0
- package/lib/types/src/client/render.d.ts +24 -0
- package/lib/types/src/client/render.d.ts.map +1 -0
- package/lib/types/src/client/squads.d.ts +17 -0
- package/lib/types/src/client/squads.d.ts.map +1 -0
- package/lib/types/src/client/trade.d.ts +24 -0
- package/lib/types/src/client/trade.d.ts.map +1 -0
- package/lib/types/src/client/wormhole.d.ts +9 -0
- package/lib/types/src/client/wormhole.d.ts.map +1 -0
- package/lib/types/src/constants.d.ts +41 -0
- package/lib/types/src/constants.d.ts.map +1 -0
- package/lib/types/src/dbTypes.d.ts +123 -0
- package/lib/types/src/dbTypes.d.ts.map +1 -0
- package/lib/types/src/dbTypesV2.d.ts +193 -0
- package/lib/types/src/dbTypesV2.d.ts.map +1 -0
- package/lib/types/src/dbViewTypes.d.ts +35 -0
- package/lib/types/src/dbViewTypes.d.ts.map +1 -0
- package/lib/types/src/errorReporter.d.ts +13 -0
- package/lib/types/src/errorReporter.d.ts.map +1 -0
- package/lib/types/src/index.d.ts +15 -0
- package/lib/types/src/index.d.ts.map +1 -0
- package/lib/types/src/logger.d.ts +16 -0
- package/lib/types/src/logger.d.ts.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1465 @@
|
|
|
1
|
+
import { Kysely, NoResultError, PostgresDialect, sql, } from 'kysely';
|
|
2
|
+
import { consoleLogger } from '../../logger';
|
|
3
|
+
import { Pool } from 'pg';
|
|
4
|
+
import * as pg from 'pg';
|
|
5
|
+
import { timedQuery } from './base';
|
|
6
|
+
export const SOL_TX_TABLE = 'sol_tx';
|
|
7
|
+
export const EPOCH_TABLE = 'epoch';
|
|
8
|
+
export const MANUAL_BURN_TABLE = 'manual_burn';
|
|
9
|
+
export const ENTITY_TABLE = 'entity';
|
|
10
|
+
export const SOL_TRANSFER_TABLE = 'sol_transfer1';
|
|
11
|
+
export const LIABILITY_TABLE = 'liability';
|
|
12
|
+
export const JOB_ID_TABLE = 'job_id';
|
|
13
|
+
export const JOB_TABLE = 'job';
|
|
14
|
+
export const BURN_TABLE = 'burn';
|
|
15
|
+
export const BRIDGE_TRANSFER_TABLE = 'bridge_transfer';
|
|
16
|
+
export const NETWORK_REVENUE_TABLE = 'network_revenue';
|
|
17
|
+
export const BURN_ADJUSTMENT_TABLE = 'burn_adjustment';
|
|
18
|
+
export const ENTITY_EPOCH_INFO_TABLE = 'entity_epoch_info';
|
|
19
|
+
export const POLYGON_UPGRADE_TABLE = 'polygon_upgrade';
|
|
20
|
+
export const CURRENT_USER_GRANT_SPEND_TABLE = 'current_user_grant_spend';
|
|
21
|
+
export const USER_GRANT_SPEND_TABLE = 'user_grant_spend';
|
|
22
|
+
export const GRANT_BURN_ID_TABLE = 'grant_burn_id';
|
|
23
|
+
export const GRANT_BURN_TABLE = 'grant_burn';
|
|
24
|
+
BigInt.prototype.toJSON = function () {
|
|
25
|
+
return this.toString();
|
|
26
|
+
};
|
|
27
|
+
import { begin } from './base';
|
|
28
|
+
import moment from 'moment';
|
|
29
|
+
pg.types.setTypeParser(1114, (str) => moment.utc(str).toDate());
|
|
30
|
+
pg.defaults.parseInputDatesAsUTC = true;
|
|
31
|
+
/* ;(Date.prototype as any).toPostgres = function (prepareValue: (v: any) => any) {
|
|
32
|
+
return this.toISOString()
|
|
33
|
+
}
|
|
34
|
+
*/
|
|
35
|
+
export const batchFinishedAtMedian = (jobs) => {
|
|
36
|
+
const pricedAt = jobs.length % 2 == 0
|
|
37
|
+
? new Date(Math.trunc((Date.parse(jobs[jobs.length / 2].finishedAt) +
|
|
38
|
+
Date.parse(jobs[jobs.length / 2 - 1].finishedAt)) /
|
|
39
|
+
2))
|
|
40
|
+
: new Date(Date.parse(jobs[Math.floor(jobs.length / 2)].finishedAt));
|
|
41
|
+
return pricedAt;
|
|
42
|
+
};
|
|
43
|
+
export const pgClient = (config) => {
|
|
44
|
+
// Single pg pool that we can also use for ad-hoc performance tests outside Kysely
|
|
45
|
+
const pgPool = new Pool(config);
|
|
46
|
+
pgPool.on('connect', (c) => console.log('new pg socket created'));
|
|
47
|
+
pgPool.on('error', (err) => console.log('idle pg client died and was replaced:', err));
|
|
48
|
+
const dialect = new PostgresDialect({ pool: pgPool });
|
|
49
|
+
// Database interface is passed to Kysely's constructor, and from now on, Kysely
|
|
50
|
+
// knows your database structure.
|
|
51
|
+
// Dialect is passed to Kysely's constructor, and from now on, Kysely knows how
|
|
52
|
+
// to communicate with your database.
|
|
53
|
+
const db = new Kysely({
|
|
54
|
+
dialect,
|
|
55
|
+
});
|
|
56
|
+
const getOrInsertEntity = async (db, p, log = consoleLogger) => {
|
|
57
|
+
const res = (await db
|
|
58
|
+
.selectFrom(ENTITY_TABLE)
|
|
59
|
+
.select('id')
|
|
60
|
+
.where('sol_key', '=', p.solKey)
|
|
61
|
+
.executeTakeFirst());
|
|
62
|
+
if (res) {
|
|
63
|
+
log.debug(`already found entity ${res.id}`);
|
|
64
|
+
return res.id;
|
|
65
|
+
}
|
|
66
|
+
const { id } = (await db
|
|
67
|
+
.insertInto(ENTITY_TABLE)
|
|
68
|
+
.values({
|
|
69
|
+
sol_key: p.solKey,
|
|
70
|
+
})
|
|
71
|
+
.onConflict((oc) => oc.column('sol_key').doNothing())
|
|
72
|
+
.returning((eb) => ['id as id'])
|
|
73
|
+
.executeTakeFirst());
|
|
74
|
+
log.debug(`inserted entity ${id}`);
|
|
75
|
+
return id;
|
|
76
|
+
};
|
|
77
|
+
const getOrInsertTx = async (db, p, log = consoleLogger) => {
|
|
78
|
+
const res = await db
|
|
79
|
+
.selectFrom(SOL_TX_TABLE)
|
|
80
|
+
.select('id')
|
|
81
|
+
.where('sig', '=', p.sig)
|
|
82
|
+
.executeTakeFirst();
|
|
83
|
+
if (res) {
|
|
84
|
+
log.debug(`already found tx ${res.id}`);
|
|
85
|
+
return res.id;
|
|
86
|
+
}
|
|
87
|
+
const { id } = (await db
|
|
88
|
+
.insertInto(SOL_TX_TABLE)
|
|
89
|
+
.values({
|
|
90
|
+
sig: p.sig,
|
|
91
|
+
executed_at: p.executedAt,
|
|
92
|
+
})
|
|
93
|
+
.onConflict((oc) => oc.column('sig').doNothing())
|
|
94
|
+
.returning((eb) => ['id as id'])
|
|
95
|
+
.executeTakeFirst());
|
|
96
|
+
log.debug(`inserted tx ${id}`);
|
|
97
|
+
return id;
|
|
98
|
+
};
|
|
99
|
+
const getOrInsertEpoch = async (db, p, log = consoleLogger) => {
|
|
100
|
+
const res = await db
|
|
101
|
+
.selectFrom(EPOCH_TABLE)
|
|
102
|
+
.select('id')
|
|
103
|
+
.where('ui', '=', String(p.uiEpochId))
|
|
104
|
+
.where('channel', '=', p.channel)
|
|
105
|
+
.executeTakeFirst();
|
|
106
|
+
if (res) {
|
|
107
|
+
log.debug(`already found epoch ${res.id}`);
|
|
108
|
+
return res.id;
|
|
109
|
+
}
|
|
110
|
+
const { id } = (await db
|
|
111
|
+
.insertInto(EPOCH_TABLE)
|
|
112
|
+
.values({
|
|
113
|
+
ui: p.uiEpochId,
|
|
114
|
+
channel: p.channel,
|
|
115
|
+
start_time: p.startTime,
|
|
116
|
+
end_time: p.endTime,
|
|
117
|
+
saved_at: p.channel == 'node_operator' || p.channel == 'availability'
|
|
118
|
+
? new Date()
|
|
119
|
+
: undefined,
|
|
120
|
+
})
|
|
121
|
+
.onConflict((oc) => oc.columns(['ui', 'channel']).doNothing())
|
|
122
|
+
.returning((eb) => ['id as id'])
|
|
123
|
+
.executeTakeFirst());
|
|
124
|
+
log.debug(`inserted epoch ${id}`);
|
|
125
|
+
return id;
|
|
126
|
+
};
|
|
127
|
+
const getCurrentUserGrantSpend = async (db, p, log = consoleLogger) => {
|
|
128
|
+
const res = await db
|
|
129
|
+
.selectFrom(CURRENT_USER_GRANT_SPEND_TABLE)
|
|
130
|
+
.selectAll()
|
|
131
|
+
.where('user_id', '=', p.userId)
|
|
132
|
+
.forUpdate()
|
|
133
|
+
.executeTakeFirst();
|
|
134
|
+
return res
|
|
135
|
+
? {
|
|
136
|
+
id: BigInt(res.id),
|
|
137
|
+
user: res.user_id,
|
|
138
|
+
renderSpent: BigInt(res.render_spent),
|
|
139
|
+
perpetual: res.perpetual ?? false,
|
|
140
|
+
description: res.description,
|
|
141
|
+
}
|
|
142
|
+
: undefined;
|
|
143
|
+
};
|
|
144
|
+
const updateTotalRndr = async (db, p, totalRndr, savedAt, log) => {
|
|
145
|
+
const res = (await db
|
|
146
|
+
.updateTable(EPOCH_TABLE)
|
|
147
|
+
.set({
|
|
148
|
+
total_rndr: totalRndr,
|
|
149
|
+
saved_at: savedAt,
|
|
150
|
+
})
|
|
151
|
+
.where('channel', '=', p.channel)
|
|
152
|
+
.where('ui', '=', String(p.uiEpochId))
|
|
153
|
+
.executeTakeFirst());
|
|
154
|
+
log.info(`set ${res.numUpdatedRows} rows. channel: ${p.channel}, ui: ${p.uiEpochId} -> totalRndr ${totalRndr} `);
|
|
155
|
+
};
|
|
156
|
+
const fetchJobs1 = async (db, f, log = consoleLogger) => {
|
|
157
|
+
let q = db.selectFrom('job').selectAll();
|
|
158
|
+
//.where('job.burn_id', 'is not', null)
|
|
159
|
+
if (f.completedAtAfter) {
|
|
160
|
+
q = q.where('completed_at', '>=', f.completedAtAfter);
|
|
161
|
+
}
|
|
162
|
+
if (f.completedAtBefore) {
|
|
163
|
+
q = q.where('completed_at', '<=', f.completedAtBefore);
|
|
164
|
+
}
|
|
165
|
+
if (f.ids) {
|
|
166
|
+
q = q.where('id', 'in', f.ids);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
if (f.burned == false) {
|
|
170
|
+
q = q.where('burn_id', 'is', null);
|
|
171
|
+
}
|
|
172
|
+
else if (f.burned == true) {
|
|
173
|
+
q = q.where('burn_id', 'is not', null);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
q = q.orderBy('completed_at asc');
|
|
177
|
+
log.info(`fetchJobs q ${q.compile().sql} ${JSON.stringify(f)}`);
|
|
178
|
+
const rows = await q.execute();
|
|
179
|
+
return rows.map((r) => ({
|
|
180
|
+
id: Number(r.id),
|
|
181
|
+
completedAt: r.completed_at,
|
|
182
|
+
rndrUsed: BigInt(r.render_amt),
|
|
183
|
+
insertedAt: r.inserted_at,
|
|
184
|
+
}));
|
|
185
|
+
};
|
|
186
|
+
const createBridgeTransfers = async (db, bts, log = consoleLogger) => {
|
|
187
|
+
const sequenceNumbers = [];
|
|
188
|
+
const dbEpochIds = [];
|
|
189
|
+
for (const bt of bts) {
|
|
190
|
+
let { solKey: toSolKey, seq, createdAt, amount, upgradeBracket, priorSupply, points, epochId, } = bt;
|
|
191
|
+
let entityId;
|
|
192
|
+
if (toSolKey && toSolKey != '') {
|
|
193
|
+
entityId = await getOrInsertEntity(db, { solKey: toSolKey });
|
|
194
|
+
}
|
|
195
|
+
const dbEpochId = await getOrInsertEpoch(db, {
|
|
196
|
+
channel: 'upgrade',
|
|
197
|
+
uiEpochId: epochId,
|
|
198
|
+
}, log);
|
|
199
|
+
dbEpochIds.push(BigInt(dbEpochId));
|
|
200
|
+
const q = db
|
|
201
|
+
.insertInto(BRIDGE_TRANSFER_TABLE)
|
|
202
|
+
.values({
|
|
203
|
+
entity_id: entityId,
|
|
204
|
+
sequence: seq,
|
|
205
|
+
created_at: createdAt,
|
|
206
|
+
amount: amount,
|
|
207
|
+
upgrade_bracket: upgradeBracket,
|
|
208
|
+
prior_supply: priorSupply,
|
|
209
|
+
points: points,
|
|
210
|
+
epoch_id: dbEpochId,
|
|
211
|
+
})
|
|
212
|
+
.onConflict((oc) => oc.column('sequence').doUpdateSet({
|
|
213
|
+
created_at: createdAt,
|
|
214
|
+
amount: amount,
|
|
215
|
+
upgrade_bracket: upgradeBracket,
|
|
216
|
+
prior_supply: priorSupply,
|
|
217
|
+
points: points,
|
|
218
|
+
epoch_id: dbEpochId,
|
|
219
|
+
entity_id: entityId,
|
|
220
|
+
}))
|
|
221
|
+
.returning('sequence');
|
|
222
|
+
const c = q.compile();
|
|
223
|
+
log.debug(`inserting bridge transfer ${JSON.stringify(bt)} -> ${c.sql} ps ${c.parameters}`);
|
|
224
|
+
const { sequence: seqNum } = await q.executeTakeFirst();
|
|
225
|
+
log.debug(`inserted bridge transfer sequence ${seq}`);
|
|
226
|
+
sequenceNumbers.push(BigInt(seqNum));
|
|
227
|
+
}
|
|
228
|
+
return { sequenceNumbers: sequenceNumbers };
|
|
229
|
+
};
|
|
230
|
+
const createPayments = async (db, ps, log = consoleLogger) => {
|
|
231
|
+
const ids = [];
|
|
232
|
+
const epochDbIds = [];
|
|
233
|
+
for (const p of ps) {
|
|
234
|
+
let liabilityId;
|
|
235
|
+
let transferId;
|
|
236
|
+
if (p.liability) {
|
|
237
|
+
let { id: dbId, solKey, channel, epochId, createdAt, amountDue, deviceId, points, obhUsed, } = p.liability;
|
|
238
|
+
try {
|
|
239
|
+
let entityId;
|
|
240
|
+
if (solKey && solKey != '') {
|
|
241
|
+
entityId = await getOrInsertEntity(db, { solKey });
|
|
242
|
+
}
|
|
243
|
+
const dbEpochId = await getOrInsertEpoch(db, {
|
|
244
|
+
channel,
|
|
245
|
+
uiEpochId: epochId,
|
|
246
|
+
}, log);
|
|
247
|
+
epochDbIds.push(BigInt(dbEpochId));
|
|
248
|
+
log.debug(`inserting liability ${JSON.stringify(p.liability)}`);
|
|
249
|
+
const { id } = await db
|
|
250
|
+
.insertInto(LIABILITY_TABLE)
|
|
251
|
+
.values({
|
|
252
|
+
id: dbId,
|
|
253
|
+
entity_id: entityId,
|
|
254
|
+
epoch_id: dbEpochId,
|
|
255
|
+
channel,
|
|
256
|
+
created_at: createdAt ?? new Date(),
|
|
257
|
+
amount_due: amountDue,
|
|
258
|
+
device_id: deviceId,
|
|
259
|
+
points,
|
|
260
|
+
obh_used: obhUsed,
|
|
261
|
+
})
|
|
262
|
+
.returning((eb) => ['id as id'])
|
|
263
|
+
.executeTakeFirstOrThrow();
|
|
264
|
+
log.debug(`inserted liability ${id}`);
|
|
265
|
+
liabilityId = BigInt(id);
|
|
266
|
+
}
|
|
267
|
+
catch (e) {
|
|
268
|
+
throw new Error(`failed inserting solKey ${solKey} epochId ${epochId} ${JSON.stringify(p.liability)}: ${e}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (p.transfer) {
|
|
272
|
+
let { executedAt, symbol, amountPayed, solTx, channel, fromSolKey, toSolKey, id: dbId, } = p.transfer;
|
|
273
|
+
if (!fromSolKey || !toSolKey || !solTx) {
|
|
274
|
+
log.debug(`skipping transfer. one of undefined: fromKey ${fromSolKey} toKey ${toSolKey} tx ${solTx}`);
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
log.debug(`inserting transfer ${JSON.stringify(p.transfer)}`);
|
|
278
|
+
const fromEntityId = await getOrInsertEntity(db, { solKey: fromSolKey });
|
|
279
|
+
const toEntityId = await getOrInsertEntity(db, { solKey: toSolKey });
|
|
280
|
+
const solTxId = await getOrInsertTx(db, {
|
|
281
|
+
sig: solTx,
|
|
282
|
+
executedAt,
|
|
283
|
+
}, log);
|
|
284
|
+
const { id } = await db
|
|
285
|
+
.insertInto(SOL_TRANSFER_TABLE)
|
|
286
|
+
.values({
|
|
287
|
+
id: dbId,
|
|
288
|
+
sol_tx_id: solTxId,
|
|
289
|
+
from_entity_id: fromEntityId,
|
|
290
|
+
to_entity_id: toEntityId,
|
|
291
|
+
channel,
|
|
292
|
+
symbol,
|
|
293
|
+
amount_payed: amountPayed,
|
|
294
|
+
})
|
|
295
|
+
.returning((eb) => ['id as id'])
|
|
296
|
+
.executeTakeFirstOrThrow();
|
|
297
|
+
transferId = BigInt(id);
|
|
298
|
+
}
|
|
299
|
+
ids.push({ transferId, liabilityId });
|
|
300
|
+
}
|
|
301
|
+
return { epochDbIds, inserted: ids };
|
|
302
|
+
};
|
|
303
|
+
return {
|
|
304
|
+
db: {
|
|
305
|
+
commit: () => { },
|
|
306
|
+
rollback: () => { },
|
|
307
|
+
trx: db,
|
|
308
|
+
},
|
|
309
|
+
async startTransaction(log = consoleLogger) {
|
|
310
|
+
return begin(db, log);
|
|
311
|
+
},
|
|
312
|
+
async createPayments(db, ps, log = consoleLogger) {
|
|
313
|
+
return createPayments(db.trx, ps, log);
|
|
314
|
+
},
|
|
315
|
+
async fetchTransfers(db, f, log = consoleLogger) {
|
|
316
|
+
let lastId = undefined;
|
|
317
|
+
if (f.cursor) {
|
|
318
|
+
lastId = atob(f.cursor);
|
|
319
|
+
}
|
|
320
|
+
let q = db.trx
|
|
321
|
+
.selectFrom(SOL_TRANSFER_TABLE)
|
|
322
|
+
.select([
|
|
323
|
+
'sol_transfer1.id',
|
|
324
|
+
'sol_transfer1.channel',
|
|
325
|
+
'sol_transfer1.symbol',
|
|
326
|
+
'sol_transfer1.amount_payed',
|
|
327
|
+
])
|
|
328
|
+
.innerJoin('sol_tx', 'sol_tx.id', 'sol_transfer1.sol_tx_id')
|
|
329
|
+
.select(['sol_tx.sig', 'sol_tx.executed_at'])
|
|
330
|
+
.innerJoin('entity as f', 'f.id', 'sol_transfer1.from_entity_id')
|
|
331
|
+
.select(['f.sol_key as from_sol_key'])
|
|
332
|
+
.innerJoin('entity as t', 't.id', 'sol_transfer1.to_entity_id')
|
|
333
|
+
.select(['t.sol_key as to_sol_key']);
|
|
334
|
+
if (f.executedAfter) {
|
|
335
|
+
q = q.where('sol_tx.executed_at', '>=', f.executedAfter);
|
|
336
|
+
}
|
|
337
|
+
if (f.executedBefore) {
|
|
338
|
+
q = q.where('sol_tx.executed_at', '<=', f.executedBefore);
|
|
339
|
+
}
|
|
340
|
+
if (f.channels) {
|
|
341
|
+
q = q.where('sol_transfer1.channel', 'in', f.channels);
|
|
342
|
+
}
|
|
343
|
+
if (lastId) {
|
|
344
|
+
q = q.where('sol_transfer1.id', '>', lastId);
|
|
345
|
+
}
|
|
346
|
+
let limit = 10000;
|
|
347
|
+
if (f.limit) {
|
|
348
|
+
limit = f.limit;
|
|
349
|
+
}
|
|
350
|
+
q = q.limit(limit).orderBy(`${SOL_TRANSFER_TABLE}.id`);
|
|
351
|
+
const comp = q.compile();
|
|
352
|
+
const { rows } = await timedQuery(pgPool, comp.sql, comp.parameters, 'fetchTransfers', log);
|
|
353
|
+
const trs = rows.map((r) => ({
|
|
354
|
+
id: BigInt(r.id),
|
|
355
|
+
channel: r.channel,
|
|
356
|
+
symbol: r.symbol,
|
|
357
|
+
amountPayed: BigInt(r.amount_payed),
|
|
358
|
+
solTx: r.sig,
|
|
359
|
+
executedAt: r.executed_at,
|
|
360
|
+
fromSolKey: r.from_sol_key,
|
|
361
|
+
toSolKey: r.to_sol_key,
|
|
362
|
+
}));
|
|
363
|
+
return {
|
|
364
|
+
transfers: trs,
|
|
365
|
+
cursor: trs.length === limit
|
|
366
|
+
? btoa(trs[trs.length - 1].id.toString())
|
|
367
|
+
: undefined,
|
|
368
|
+
};
|
|
369
|
+
},
|
|
370
|
+
async fetchEpochs(db, f, log = consoleLogger) {
|
|
371
|
+
let q = db.trx.selectFrom(EPOCH_TABLE).selectAll();
|
|
372
|
+
if (f.channels) {
|
|
373
|
+
q = q.where('channel', 'in', f.channels);
|
|
374
|
+
}
|
|
375
|
+
if (f.startTimeAfter) {
|
|
376
|
+
q = q.where('epoch.start_time', '>', f.startTimeAfter);
|
|
377
|
+
}
|
|
378
|
+
if (f.endTimeBefore) {
|
|
379
|
+
q = q.where('epoch.end_time', '<', f.endTimeBefore);
|
|
380
|
+
}
|
|
381
|
+
q = q.orderBy('epoch.start_time');
|
|
382
|
+
const comp = q.compile();
|
|
383
|
+
const { rows } = await timedQuery(pgPool, comp.sql, comp.parameters, 'fetchEpochs', log);
|
|
384
|
+
return rows.map((e) => ({
|
|
385
|
+
channel: e.channel,
|
|
386
|
+
startTime: e.start_time,
|
|
387
|
+
endTime: e.end_time,
|
|
388
|
+
ui: Number(e.ui),
|
|
389
|
+
totalRndr: Number(e.total_rndr),
|
|
390
|
+
}));
|
|
391
|
+
},
|
|
392
|
+
async fetchLiabilities(db, f, log = consoleLogger) {
|
|
393
|
+
let offset = 0;
|
|
394
|
+
let orderBy = f.orderBy ?? 'epoch.ui,liability.id';
|
|
395
|
+
if (f.cursor) {
|
|
396
|
+
const c = JSON.parse(atob(f.cursor));
|
|
397
|
+
if (c.offset) {
|
|
398
|
+
offset = c.offset;
|
|
399
|
+
}
|
|
400
|
+
if (c.orderBy) {
|
|
401
|
+
orderBy = c.orderBy;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
let q = db.trx
|
|
405
|
+
.selectFrom(LIABILITY_TABLE)
|
|
406
|
+
.selectAll()
|
|
407
|
+
.innerJoin(ENTITY_TABLE, `${LIABILITY_TABLE}.entity_id`, 'entity.id')
|
|
408
|
+
.select(['entity.sol_key'])
|
|
409
|
+
.innerJoin(EPOCH_TABLE, `${EPOCH_TABLE}.id`, 'liability.epoch_id')
|
|
410
|
+
.select(['epoch.ui', 'epoch.start_time', 'epoch.end_time']);
|
|
411
|
+
if (f.epochEndedAtAfter) {
|
|
412
|
+
q = q.where('epoch.end_time', '>=', f.epochEndedAtAfter);
|
|
413
|
+
}
|
|
414
|
+
if (f.epochEndedAtBefore) {
|
|
415
|
+
q = q.where('epoch.end_time', '<=', f.epochEndedAtBefore);
|
|
416
|
+
}
|
|
417
|
+
if (f.epochId) {
|
|
418
|
+
q = q.where('epoch.ui', '=', f.epochId.toString());
|
|
419
|
+
}
|
|
420
|
+
if (f.channels) {
|
|
421
|
+
q = q.where('liability.channel', 'in', f.channels);
|
|
422
|
+
}
|
|
423
|
+
let limit = 10000;
|
|
424
|
+
if (f.limit) {
|
|
425
|
+
limit = f.limit;
|
|
426
|
+
}
|
|
427
|
+
// @ts-ignore
|
|
428
|
+
q = q.limit(limit).orderBy(orderBy.split(','));
|
|
429
|
+
if (offset) {
|
|
430
|
+
q = q.offset(offset);
|
|
431
|
+
}
|
|
432
|
+
const comp = q.compile();
|
|
433
|
+
const { rows } = await timedQuery(pgPool, comp.sql, comp.parameters, 'fetchLiabilities', log);
|
|
434
|
+
const ls = [];
|
|
435
|
+
for (const l of rows) {
|
|
436
|
+
ls.push({
|
|
437
|
+
id: BigInt(l.id),
|
|
438
|
+
solKey: l.sol_key,
|
|
439
|
+
epochId: Number(l.ui),
|
|
440
|
+
channel: l.channel,
|
|
441
|
+
createdAt: l.created_at,
|
|
442
|
+
amountDue: BigInt(l.amount_due),
|
|
443
|
+
deviceId: l.device_id,
|
|
444
|
+
obhUsed: l.obh_used,
|
|
445
|
+
points: l.points ? BigInt(l.points) : undefined,
|
|
446
|
+
startTime: l.start_time,
|
|
447
|
+
endTime: l.end_time,
|
|
448
|
+
ui: Number(l.ui),
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
return {
|
|
452
|
+
liabilities: ls,
|
|
453
|
+
cursor: ls.length == limit
|
|
454
|
+
? btoa(JSON.stringify({ offset: ls.length + offset, orderBy }))
|
|
455
|
+
: undefined,
|
|
456
|
+
};
|
|
457
|
+
},
|
|
458
|
+
async getLiabilitiesAndTransfers(f, log = consoleLogger) {
|
|
459
|
+
let lq = db
|
|
460
|
+
.selectFrom(LIABILITY_TABLE)
|
|
461
|
+
.selectAll()
|
|
462
|
+
.innerJoin(ENTITY_TABLE, `${LIABILITY_TABLE}.entity_id`, 'entity.id')
|
|
463
|
+
.select(['entity.sol_key'])
|
|
464
|
+
.innerJoin(EPOCH_TABLE, `${EPOCH_TABLE}.id`, 'liability.epoch_id')
|
|
465
|
+
.select(['epoch.ui', 'epoch.start_time', 'epoch.end_time'])
|
|
466
|
+
.where('entity.sol_key', '=', f.user);
|
|
467
|
+
let tq = db
|
|
468
|
+
.selectFrom(SOL_TRANSFER_TABLE)
|
|
469
|
+
.selectAll()
|
|
470
|
+
.innerJoin(SOL_TX_TABLE, 'sol_tx.id', 'sol_transfer1.sol_tx_id')
|
|
471
|
+
.select(['sol_tx.sig', 'sol_tx.executed_at'])
|
|
472
|
+
.innerJoin(`${ENTITY_TABLE} as t`, `${SOL_TRANSFER_TABLE}.to_entity_id`, 't.id')
|
|
473
|
+
.select(['t.sol_key as to_sol_key'])
|
|
474
|
+
.innerJoin(`${ENTITY_TABLE} as f`, `${SOL_TRANSFER_TABLE}.from_entity_id`, 'f.id')
|
|
475
|
+
.select(['f.sol_key as from_sol_key'])
|
|
476
|
+
.where('t.sol_key', '=', f.user);
|
|
477
|
+
log.info(`fetch liabilities ${lq.compile().sql} ps ${JSON.stringify(f)}`);
|
|
478
|
+
log.info(`fetch transfers ${tq.compile().sql} ps ${JSON.stringify(f)}`);
|
|
479
|
+
const lc = lq.compile();
|
|
480
|
+
const tc = tq.compile();
|
|
481
|
+
const [liabRes, transRes] = await Promise.all([
|
|
482
|
+
timedQuery(pgPool, lc.sql, lc.parameters, 'getLiabilitiesAndTransfers->liabilities', log),
|
|
483
|
+
timedQuery(pgPool, tc.sql, tc.parameters, 'getLiabilitiesAndTransfers->transfers', log),
|
|
484
|
+
]);
|
|
485
|
+
const rows = [liabRes.rows, transRes.rows];
|
|
486
|
+
const epochs = {};
|
|
487
|
+
const liabilities = [];
|
|
488
|
+
for (const l of rows[0]) {
|
|
489
|
+
epochs[`${l.ui}-${l.channel}`] = {
|
|
490
|
+
startTime: l.start_time,
|
|
491
|
+
endTime: l.end_time,
|
|
492
|
+
channel: l.channel,
|
|
493
|
+
ui: Number(l.ui),
|
|
494
|
+
};
|
|
495
|
+
liabilities.push({
|
|
496
|
+
id: BigInt(l.id),
|
|
497
|
+
solKey: l.sol_key,
|
|
498
|
+
epochId: Number(l.ui),
|
|
499
|
+
channel: l.channel,
|
|
500
|
+
createdAt: l.created_at,
|
|
501
|
+
amountDue: BigInt(l.amount_due),
|
|
502
|
+
deviceId: l.device_id,
|
|
503
|
+
points: l.points ? BigInt(l.points) : undefined,
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
const transfers = rows[1].map((r) => ({
|
|
507
|
+
id: BigInt(r.id),
|
|
508
|
+
fromSolKey: r.from_sol_key,
|
|
509
|
+
toSolKey: r.to_sol_key,
|
|
510
|
+
symbol: r.symbol,
|
|
511
|
+
channel: r.channel,
|
|
512
|
+
solTx: r.sig,
|
|
513
|
+
executedAt: r.executed_at,
|
|
514
|
+
amountPayed: BigInt(r.amount_payed),
|
|
515
|
+
}));
|
|
516
|
+
return { transfers, liabilities, epochs: Object.values(epochs) };
|
|
517
|
+
},
|
|
518
|
+
async getOutstanding(f, log = consoleLogger) {
|
|
519
|
+
let epochUi;
|
|
520
|
+
let epochEndTime;
|
|
521
|
+
if (f.startEpochId.id) {
|
|
522
|
+
if (f.channels?.includes('node_operator') ||
|
|
523
|
+
f.channels?.includes('availability')) {
|
|
524
|
+
let qb = db
|
|
525
|
+
.selectFrom(EPOCH_TABLE)
|
|
526
|
+
.selectAll(EPOCH_TABLE)
|
|
527
|
+
.where('ui', '>', f.startEpochId.id.toString())
|
|
528
|
+
.where('channel', 'in', f.channels);
|
|
529
|
+
if (f.startEpochId.savedAtNotNull) {
|
|
530
|
+
qb = qb.where('saved_at', 'is not', null);
|
|
531
|
+
}
|
|
532
|
+
const e = await qb
|
|
533
|
+
.orderBy('ui', 'desc')
|
|
534
|
+
.limit(1)
|
|
535
|
+
.executeTakeFirstOrThrow();
|
|
536
|
+
epochEndTime = e.end_time;
|
|
537
|
+
epochUi = e.ui;
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
epochUi = f.startEpochId.id.toString();
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
let q = db
|
|
544
|
+
.selectFrom((eb) => eb
|
|
545
|
+
.selectFrom((eb) => {
|
|
546
|
+
let b = eb
|
|
547
|
+
.selectFrom(SOL_TRANSFER_TABLE)
|
|
548
|
+
.select(({ fn }) => fn.sum(`amount_payed`).as('payed'))
|
|
549
|
+
.innerJoin(ENTITY_TABLE, `${SOL_TRANSFER_TABLE}.to_entity_id`, 'entity.id')
|
|
550
|
+
.select(['entity.sol_key'])
|
|
551
|
+
.groupBy('entity.sol_key');
|
|
552
|
+
if (f.banned) {
|
|
553
|
+
b = b.where('entity.banned', '=', true);
|
|
554
|
+
}
|
|
555
|
+
else {
|
|
556
|
+
b = b.where('entity.banned', '=', false);
|
|
557
|
+
}
|
|
558
|
+
if (epochEndTime) {
|
|
559
|
+
b = b
|
|
560
|
+
.innerJoin(SOL_TX_TABLE, `${SOL_TX_TABLE}.id`, `${SOL_TRANSFER_TABLE}.sol_tx_id`)
|
|
561
|
+
.where(`${SOL_TX_TABLE}.executed_at`, '>', epochEndTime);
|
|
562
|
+
}
|
|
563
|
+
if (f.channels && f.channels.length > 0) {
|
|
564
|
+
b = b.where((eb) => eb.or(f.channels.map((c) => eb('sol_transfer1.channel', '=', c))));
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
b = b.where((eb) => eb.or([
|
|
568
|
+
eb('sol_transfer1.channel', '=', 'node_operator'),
|
|
569
|
+
eb('sol_transfer1.channel', '=', 'availability'),
|
|
570
|
+
]));
|
|
571
|
+
}
|
|
572
|
+
return b.as('y');
|
|
573
|
+
})
|
|
574
|
+
.fullJoin((eb) => {
|
|
575
|
+
let b = eb
|
|
576
|
+
.selectFrom(LIABILITY_TABLE)
|
|
577
|
+
.select(({ fn }) => fn.sum(`amount_due`).as('due'))
|
|
578
|
+
.innerJoin(ENTITY_TABLE, `${LIABILITY_TABLE}.entity_id`, 'entity.id')
|
|
579
|
+
.select(['entity.sol_key'])
|
|
580
|
+
.groupBy('entity.sol_key');
|
|
581
|
+
if (f.banned) {
|
|
582
|
+
b = b.where('entity.banned', '=', true);
|
|
583
|
+
}
|
|
584
|
+
else {
|
|
585
|
+
b = b.where('entity.banned', '=', false);
|
|
586
|
+
}
|
|
587
|
+
if (epochUi) {
|
|
588
|
+
b = b
|
|
589
|
+
.innerJoin(EPOCH_TABLE, `${LIABILITY_TABLE}.epoch_id`, `${EPOCH_TABLE}.id`)
|
|
590
|
+
.where(`${EPOCH_TABLE}.ui`, '>=', epochUi);
|
|
591
|
+
}
|
|
592
|
+
if (f.channels && f.channels.length > 0) {
|
|
593
|
+
b = b.where((eb) => eb.or(f.channels.map((c) => eb('liability.channel', '=', c))));
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
b = b.where((eb) => eb.or([
|
|
597
|
+
eb('liability.channel', '=', 'node_operator'),
|
|
598
|
+
eb('liability.channel', '=', 'availability'),
|
|
599
|
+
]));
|
|
600
|
+
}
|
|
601
|
+
return b.as('x');
|
|
602
|
+
}, (join) => join.onRef('y.sol_key', '=', 'x.sol_key'))
|
|
603
|
+
.select(({ fn }) => [
|
|
604
|
+
fn.coalesce('y.sol_key', 'x.sol_key').as('sol_key'),
|
|
605
|
+
sql `${fn.coalesce(`x.due`, sql `0`)} -
|
|
606
|
+
${fn.coalesce(`y.payed`, sql `0`)}`.as('out'),
|
|
607
|
+
])
|
|
608
|
+
.as('z'))
|
|
609
|
+
.selectAll();
|
|
610
|
+
if (f.due == 'overpayed') {
|
|
611
|
+
q = q.where('out', '<', '0');
|
|
612
|
+
}
|
|
613
|
+
else if (f.due == 'underpayed') {
|
|
614
|
+
q = q.where('out', '>', '0');
|
|
615
|
+
}
|
|
616
|
+
log.info(`${q.compile().sql} ps ${JSON.stringify({
|
|
617
|
+
...f,
|
|
618
|
+
epochEndTime,
|
|
619
|
+
epochUi,
|
|
620
|
+
})}`);
|
|
621
|
+
const res = await q.execute();
|
|
622
|
+
const out = {};
|
|
623
|
+
for (const r of res) {
|
|
624
|
+
out[r.sol_key] = BigInt(r.out);
|
|
625
|
+
}
|
|
626
|
+
return out;
|
|
627
|
+
},
|
|
628
|
+
async fetchNodeOperatorEpoch(epochId, log = consoleLogger) {
|
|
629
|
+
const q = db
|
|
630
|
+
.selectFrom(JOB_ID_TABLE)
|
|
631
|
+
.innerJoin(EPOCH_TABLE, 'epoch.id', 'job_id.epoch_id')
|
|
632
|
+
.leftJoin(MANUAL_BURN_TABLE, 'manual_burn.epoch_id', 'epoch.id')
|
|
633
|
+
.where('epoch.ui', '=', epochId.toString())
|
|
634
|
+
.where('epoch.channel', '=', 'node_operator')
|
|
635
|
+
.select([
|
|
636
|
+
'job_id.id',
|
|
637
|
+
'epoch.total_rndr',
|
|
638
|
+
'epoch.start_time',
|
|
639
|
+
'epoch.end_time',
|
|
640
|
+
'manual_burn.burned',
|
|
641
|
+
'manual_burn.tx_sig',
|
|
642
|
+
])
|
|
643
|
+
.orderBy('job_id.id asc');
|
|
644
|
+
log.debug(`fetch epoch info q ${q.compile().sql} epochId ${epochId}`);
|
|
645
|
+
const res = await q.execute();
|
|
646
|
+
if (res.length == 0) {
|
|
647
|
+
throw new Error(`epoch ${epochId} not found`);
|
|
648
|
+
}
|
|
649
|
+
log.debug(`fetchNodeOperatorEpoch -> fetching liablities`);
|
|
650
|
+
const ks = await db
|
|
651
|
+
.selectFrom(LIABILITY_TABLE)
|
|
652
|
+
.innerJoin(ENTITY_TABLE, 'liability.entity_id', 'entity.id')
|
|
653
|
+
.select(({ fn }) => [
|
|
654
|
+
sql `distinct
|
|
655
|
+
(entity.sol_key)`.as('sol_key'),
|
|
656
|
+
])
|
|
657
|
+
.innerJoin(EPOCH_TABLE, 'liability.epoch_id', 'epoch.id')
|
|
658
|
+
.where('epoch.ui', '=', epochId.toString())
|
|
659
|
+
.where('epoch.channel', '=', 'node_operator')
|
|
660
|
+
.where('liability.channel', '=', 'node_operator')
|
|
661
|
+
.execute();
|
|
662
|
+
log.debug(`fetchNodeOperatorEpoch -> fetched liablities`);
|
|
663
|
+
const solKeys = ks.reduce((r, k) => {
|
|
664
|
+
r[k.sol_key] = {};
|
|
665
|
+
return r;
|
|
666
|
+
}, {});
|
|
667
|
+
return {
|
|
668
|
+
jobIds: res.map((r) => Number(r.id)),
|
|
669
|
+
totalRndr: BigInt(res[0].total_rndr),
|
|
670
|
+
startTime: res[0].start_time,
|
|
671
|
+
endTime: res[0].end_time,
|
|
672
|
+
burned: BigInt(res[0].burned ?? 0),
|
|
673
|
+
burnTx: res[0].tx_sig,
|
|
674
|
+
solKeys,
|
|
675
|
+
};
|
|
676
|
+
},
|
|
677
|
+
async insertEpoch(db, epoch, log = consoleLogger) {
|
|
678
|
+
await getOrInsertEpoch(db.trx, { ...epoch, uiEpochId: epoch.ui }, log);
|
|
679
|
+
},
|
|
680
|
+
async getEpoch(f, log = consoleLogger) {
|
|
681
|
+
const now = new Date(Date.now());
|
|
682
|
+
try {
|
|
683
|
+
let q = db
|
|
684
|
+
.selectFrom(EPOCH_TABLE)
|
|
685
|
+
.selectAll()
|
|
686
|
+
.where('channel', '=', f.channel);
|
|
687
|
+
if (f.epochId) {
|
|
688
|
+
q = q.where('ui', '=', f.epochId.toString());
|
|
689
|
+
}
|
|
690
|
+
else {
|
|
691
|
+
q = q.where('end_time', '<=', now);
|
|
692
|
+
if (!f.savedAtNotNull) {
|
|
693
|
+
q = q.where('saved_at', 'is', null);
|
|
694
|
+
}
|
|
695
|
+
else {
|
|
696
|
+
q = q.where('saved_at', 'is not', null);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
q = q.orderBy('end_time desc').limit(1);
|
|
700
|
+
const res = await q.executeTakeFirstOrThrow();
|
|
701
|
+
return {
|
|
702
|
+
ui: Number(res.ui),
|
|
703
|
+
channel: f.channel,
|
|
704
|
+
startTime: res.start_time,
|
|
705
|
+
endTime: res.end_time,
|
|
706
|
+
savedAt: res.saved_at ?? undefined,
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
catch (e) {
|
|
710
|
+
if (e instanceof NoResultError) {
|
|
711
|
+
log.info(`no node operator epoch found`);
|
|
712
|
+
return undefined;
|
|
713
|
+
}
|
|
714
|
+
throw e;
|
|
715
|
+
}
|
|
716
|
+
},
|
|
717
|
+
async getLatestNodeOperatorEpoch(f, log = consoleLogger) {
|
|
718
|
+
const now = new Date(Date.now());
|
|
719
|
+
try {
|
|
720
|
+
let q = db
|
|
721
|
+
.selectFrom(EPOCH_TABLE)
|
|
722
|
+
.selectAll()
|
|
723
|
+
.where('channel', '=', 'node_operator')
|
|
724
|
+
.where('end_time', '<=', now);
|
|
725
|
+
if (!f.latestDistributed) {
|
|
726
|
+
q = q.where('saved_at', 'is', null).orderBy('end_time asc');
|
|
727
|
+
}
|
|
728
|
+
else {
|
|
729
|
+
q = q.orderBy('end_time desc');
|
|
730
|
+
}
|
|
731
|
+
q = q.limit(1);
|
|
732
|
+
const res = await q.executeTakeFirstOrThrow();
|
|
733
|
+
return {
|
|
734
|
+
epochId: BigInt(res.ui),
|
|
735
|
+
startedAt: res.start_time,
|
|
736
|
+
endedAt: res.end_time,
|
|
737
|
+
savedAt: res.saved_at ?? undefined,
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
catch (e) {
|
|
741
|
+
if (e instanceof NoResultError) {
|
|
742
|
+
log.info(`no node operator epoch found`);
|
|
743
|
+
return undefined;
|
|
744
|
+
}
|
|
745
|
+
throw e;
|
|
746
|
+
}
|
|
747
|
+
},
|
|
748
|
+
async insertEpochEntityInfo(db, eeis, log) {
|
|
749
|
+
for (const eei of eeis) {
|
|
750
|
+
await db.trx.insertInto(ENTITY_EPOCH_INFO_TABLE).values(eei).execute();
|
|
751
|
+
log.debug(`inserted eei ${JSON.stringify(eei)}`);
|
|
752
|
+
}
|
|
753
|
+
},
|
|
754
|
+
async saveNodeOperatorEpoch(epochId, liabilities, jobIds, totalRndr, log = consoleLogger) {
|
|
755
|
+
await db
|
|
756
|
+
.transaction()
|
|
757
|
+
.setIsolationLevel('repeatable read')
|
|
758
|
+
.execute(async (trx) => {
|
|
759
|
+
const { epochDbIds } = await createPayments(trx, liabilities.map((l) => ({
|
|
760
|
+
liability: l,
|
|
761
|
+
})), log);
|
|
762
|
+
log.info(`inserted liabilities`);
|
|
763
|
+
for (const id of jobIds) {
|
|
764
|
+
await trx
|
|
765
|
+
.insertInto(JOB_ID_TABLE)
|
|
766
|
+
.values({
|
|
767
|
+
id: BigInt(id),
|
|
768
|
+
epoch_id: epochDbIds[0],
|
|
769
|
+
})
|
|
770
|
+
.executeTakeFirstOrThrow();
|
|
771
|
+
}
|
|
772
|
+
const now = new Date();
|
|
773
|
+
await updateTotalRndr(db, { uiEpochId: epochId, channel: 'node_operator' }, totalRndr, now, log);
|
|
774
|
+
log.debug(`saved epoch`);
|
|
775
|
+
/*const now = new Date(Date.now())
|
|
776
|
+
const res = await trx
|
|
777
|
+
.updateTable(EPOCH_TABLE)
|
|
778
|
+
.set({
|
|
779
|
+
saved_at: now,
|
|
780
|
+
total_rndr: totalRndr,
|
|
781
|
+
})
|
|
782
|
+
.where('channel')
|
|
783
|
+
.where('ui', '=', epochId.toString())
|
|
784
|
+
.where('', '=')
|
|
785
|
+
.executeTakeFirstOrThrow()*/
|
|
786
|
+
});
|
|
787
|
+
return;
|
|
788
|
+
},
|
|
789
|
+
async produceBurnedJobs(jobs, log = consoleLogger) {
|
|
790
|
+
await db.transaction().execute(async (trx) => {
|
|
791
|
+
let num = BigInt(0);
|
|
792
|
+
for (const { amount, obhUsed, completedAt, jobId, userId } of jobs) {
|
|
793
|
+
const res = await trx
|
|
794
|
+
.insertInto(JOB_TABLE)
|
|
795
|
+
.values({
|
|
796
|
+
id: jobId,
|
|
797
|
+
render_amt: amount,
|
|
798
|
+
completed_at: completedAt,
|
|
799
|
+
inserted_at: new Date(Date.now()),
|
|
800
|
+
obh_used: obhUsed,
|
|
801
|
+
user_id: userId,
|
|
802
|
+
})
|
|
803
|
+
.onConflict((oc) => oc.column('id').doNothing())
|
|
804
|
+
.executeTakeFirstOrThrow();
|
|
805
|
+
num += res.numInsertedOrUpdatedRows ?? BigInt(0);
|
|
806
|
+
}
|
|
807
|
+
log.debug(`inserted ${num} of ${jobs.length} burn jobs`);
|
|
808
|
+
});
|
|
809
|
+
},
|
|
810
|
+
async markJobsBurntWithGrant(db, p, log = consoleLogger) {
|
|
811
|
+
const solTxId = await getOrInsertTx(db.trx, { sig: p.tx.sig, executedAt: p.tx.executedAt }, log);
|
|
812
|
+
const { id } = await db.trx
|
|
813
|
+
.insertInto(GRANT_BURN_TABLE)
|
|
814
|
+
.values({
|
|
815
|
+
sol_tx_id: solTxId,
|
|
816
|
+
burned: p.burned,
|
|
817
|
+
tags: p.tags?.join(','),
|
|
818
|
+
})
|
|
819
|
+
.returning((eb) => ['id as id'])
|
|
820
|
+
.executeTakeFirstOrThrow();
|
|
821
|
+
const jobIdsStr = p.jobsIds.map(String);
|
|
822
|
+
const res = await db.trx
|
|
823
|
+
.updateTable(JOB_TABLE)
|
|
824
|
+
.where('id', 'in', jobIdsStr)
|
|
825
|
+
.set('grant_burn_id', id)
|
|
826
|
+
.executeTakeFirstOrThrow();
|
|
827
|
+
log.debug(`set grant_burn_id on ${res.numUpdatedRows} jobs`);
|
|
828
|
+
for (const [userId, grantSpendDelta] of Object.entries(p.userIdToGrantSpendDelta)) {
|
|
829
|
+
await db.trx
|
|
830
|
+
.insertInto(USER_GRANT_SPEND_TABLE)
|
|
831
|
+
.values({
|
|
832
|
+
user_id: userId,
|
|
833
|
+
grant_burn_id: id,
|
|
834
|
+
render_spent_delta: -grantSpendDelta,
|
|
835
|
+
})
|
|
836
|
+
.executeTakeFirstOrThrow();
|
|
837
|
+
}
|
|
838
|
+
return;
|
|
839
|
+
},
|
|
840
|
+
async insertGrantSpend(db, p, log = consoleLogger) {
|
|
841
|
+
const existing = await getCurrentUserGrantSpend(db.trx, { userId: p.userId }, log);
|
|
842
|
+
const diff = {
|
|
843
|
+
render_spent: (existing?.renderSpent ?? BigInt(0)) + p.renderSpentDelta,
|
|
844
|
+
description: p.description ? p.description : existing?.description,
|
|
845
|
+
perpetual: p.perpetual ? p.perpetual : existing?.perpetual ?? false,
|
|
846
|
+
updated_at: new Date(),
|
|
847
|
+
};
|
|
848
|
+
log.debug(`insertGrantSpend currUserGrantSpend: ${JSON.stringify(existing)} delta: ${JSON.stringify(p)}`);
|
|
849
|
+
const res = await db.trx
|
|
850
|
+
.insertInto(CURRENT_USER_GRANT_SPEND_TABLE)
|
|
851
|
+
.values({
|
|
852
|
+
user_id: p.userId,
|
|
853
|
+
render_spent: p.renderSpentDelta,
|
|
854
|
+
description: p.description,
|
|
855
|
+
perpetual: p.perpetual ?? false,
|
|
856
|
+
})
|
|
857
|
+
.onConflict((oc) => oc.column('user_id').doUpdateSet(diff))
|
|
858
|
+
.returning((eb) => [
|
|
859
|
+
'user_id as user_id',
|
|
860
|
+
'render_spent as render_spent',
|
|
861
|
+
'description as description',
|
|
862
|
+
'perpetual as perpetual',
|
|
863
|
+
'updated_at as updated_at',
|
|
864
|
+
])
|
|
865
|
+
.executeTakeFirstOrThrow();
|
|
866
|
+
if (res) {
|
|
867
|
+
log.debug(`inserted current_user_grant_spend row ${JSON.stringify(res)}`);
|
|
868
|
+
}
|
|
869
|
+
if (p.renderSpentDelta > BigInt(0)) {
|
|
870
|
+
const v = {
|
|
871
|
+
user_id: p.userId,
|
|
872
|
+
render_spent_delta: p.renderSpentDelta,
|
|
873
|
+
grant_burn_id: p.grantBurnId,
|
|
874
|
+
};
|
|
875
|
+
const res = await db.trx
|
|
876
|
+
.insertInto(USER_GRANT_SPEND_TABLE)
|
|
877
|
+
.values(v)
|
|
878
|
+
.executeTakeFirstOrThrow();
|
|
879
|
+
log.debug(`inserted ${res.numInsertedOrUpdatedRows} user_grant_spend rows ${JSON.stringify(v)}`);
|
|
880
|
+
}
|
|
881
|
+
},
|
|
882
|
+
async markJobsBurnt(trx, p, log = consoleLogger) {
|
|
883
|
+
if (!p.buyBurn && !p.grantBurn) {
|
|
884
|
+
throw new Error('no buyBurn or grantBurn');
|
|
885
|
+
}
|
|
886
|
+
const solTxId = await getOrInsertTx(trx, { sig: p.solTx.sig, executedAt: p.solTx.executedAt }, log);
|
|
887
|
+
let buyBurnId;
|
|
888
|
+
let grantBurnId;
|
|
889
|
+
if (p.buyBurn) {
|
|
890
|
+
const { burned, usdcSpent, eurToUsdc, renderToUsdc, quoteAmt, tags, burnAdjustmentId, toBurn, } = p.buyBurn;
|
|
891
|
+
let pricedAt = p.buyBurn.pricedAt;
|
|
892
|
+
if (!pricedAt) {
|
|
893
|
+
const jobs = await fetchJobs1(trx, { ids: p.jobs.map((j) => String(j.id)) }, log);
|
|
894
|
+
pricedAt = batchFinishedAtMedian(jobs.map((j) => ({
|
|
895
|
+
finishedAt: j.completedAt.toISOString(),
|
|
896
|
+
jobId: j.id,
|
|
897
|
+
rndrUsed: Number(j.rndrUsed.toString()),
|
|
898
|
+
})));
|
|
899
|
+
}
|
|
900
|
+
const { id } = await trx
|
|
901
|
+
.insertInto(BURN_TABLE)
|
|
902
|
+
.values({
|
|
903
|
+
sol_tx_id: solTxId,
|
|
904
|
+
priced_at: pricedAt,
|
|
905
|
+
eur_to_usdc: eurToUsdc,
|
|
906
|
+
render_to_usdc: renderToUsdc,
|
|
907
|
+
usdc_spent: usdcSpent,
|
|
908
|
+
quote_amt: quoteAmt,
|
|
909
|
+
burned: burned,
|
|
910
|
+
tags: tags.join(','),
|
|
911
|
+
burn_adjustment_id: burnAdjustmentId,
|
|
912
|
+
to_burn: toBurn,
|
|
913
|
+
})
|
|
914
|
+
//.onConflict((oc) => oc.column('sol_tx').doNothing())
|
|
915
|
+
.returning((eb) => ['id as id'])
|
|
916
|
+
.executeTakeFirstOrThrow();
|
|
917
|
+
buyBurnId = Number(id);
|
|
918
|
+
}
|
|
919
|
+
if (p.grantBurn) {
|
|
920
|
+
const { burned, tags } = p.grantBurn;
|
|
921
|
+
const { id } = await trx
|
|
922
|
+
.insertInto(GRANT_BURN_TABLE)
|
|
923
|
+
.values({
|
|
924
|
+
sol_tx_id: solTxId,
|
|
925
|
+
burned: burned,
|
|
926
|
+
tags: tags?.join(','),
|
|
927
|
+
})
|
|
928
|
+
.returning((eb) => ['id as id'])
|
|
929
|
+
.executeTakeFirstOrThrow();
|
|
930
|
+
grantBurnId = Number(id);
|
|
931
|
+
}
|
|
932
|
+
for (const job of p.jobs) {
|
|
933
|
+
const res = await trx
|
|
934
|
+
.updateTable(JOB_TABLE)
|
|
935
|
+
.where('id', '=', String(job.id))
|
|
936
|
+
.set('burn_id', job.buyRenderAmt != undefined ? buyBurnId : null)
|
|
937
|
+
.set('grant_burn_id', job.grantRenderAmt != undefined ? grantBurnId : null)
|
|
938
|
+
.set('buy_render_amt', job.buyRenderAmt ?? 0)
|
|
939
|
+
.set('grant_render_amt', job.grantRenderAmt ?? 0)
|
|
940
|
+
.executeTakeFirstOrThrow();
|
|
941
|
+
log.debug(`set grant_burn_id ${grantBurnId} burn_id ${buyBurnId} buy_render_amt ${job.buyRenderAmt} grant_render_amt ${job.grantRenderAmt} on ${res.numUpdatedRows} jobs id ${job.id}`);
|
|
942
|
+
}
|
|
943
|
+
return { grantBurnId, buyBurnId };
|
|
944
|
+
},
|
|
945
|
+
async markJobsBurned(trx, p, log = consoleLogger) {
|
|
946
|
+
let { markedAt, solTx } = p;
|
|
947
|
+
let res = await trx.trx
|
|
948
|
+
.updateTable(BURN_TABLE)
|
|
949
|
+
.set({ marked_burned_at: markedAt })
|
|
950
|
+
.from(SOL_TX_TABLE)
|
|
951
|
+
.whereRef('burn.sol_tx_id', '=', 'sol_tx.id')
|
|
952
|
+
.where('sol_tx.sig', '=', solTx)
|
|
953
|
+
.executeTakeFirst();
|
|
954
|
+
if (res) {
|
|
955
|
+
log.debug(`buy burn set ${res.numUpdatedRows} ${solTx} -> marked_burned_at ${markedAt.toISOString()}`);
|
|
956
|
+
}
|
|
957
|
+
let res1 = await trx.trx
|
|
958
|
+
.updateTable(GRANT_BURN_TABLE)
|
|
959
|
+
.set({ marked_burned_at: markedAt })
|
|
960
|
+
.from(SOL_TX_TABLE)
|
|
961
|
+
.whereRef('grant_burn.sol_tx_id', '=', 'sol_tx.id')
|
|
962
|
+
.where('sol_tx.sig', '=', solTx)
|
|
963
|
+
.executeTakeFirst();
|
|
964
|
+
if (res1) {
|
|
965
|
+
log.debug(`grant burn set ${res1.numUpdatedRows} ${solTx} -> marked_burned_at ${markedAt.toISOString()}`);
|
|
966
|
+
}
|
|
967
|
+
},
|
|
968
|
+
async consumeAndLockBurnJobs(limit, lowerRndrThreshold, upperRndrThreshold, log = consoleLogger) {
|
|
969
|
+
const trx = await begin(db, log);
|
|
970
|
+
const q = trx.trx
|
|
971
|
+
.selectFrom(trx.trx
|
|
972
|
+
.selectFrom(JOB_TABLE)
|
|
973
|
+
.select(({ fn, val, ref }) => [
|
|
974
|
+
'id',
|
|
975
|
+
fn
|
|
976
|
+
.sum('render_amt')
|
|
977
|
+
.over((ob) => ob.orderBy(`completed_at`, `asc`))
|
|
978
|
+
.as('total'),
|
|
979
|
+
])
|
|
980
|
+
.where('burn_id', 'is', null)
|
|
981
|
+
.where('grant_burn_id', 'is', null)
|
|
982
|
+
.as('t'))
|
|
983
|
+
.select('id')
|
|
984
|
+
.where('total', '>=', lowerRndrThreshold)
|
|
985
|
+
.where('total', '<=', upperRndrThreshold);
|
|
986
|
+
log.debug(`outstanding jobs q: ${q.compile().sql} ${JSON.stringify({
|
|
987
|
+
limit,
|
|
988
|
+
lowerRndrThreshold,
|
|
989
|
+
upperRndrThreshold,
|
|
990
|
+
})}`);
|
|
991
|
+
const idRs = await q.execute();
|
|
992
|
+
const ids = idRs.map((x) => x.id);
|
|
993
|
+
log.debug(`${ids.length} candidate jobs for burn. locking up to ${limit} jobs`);
|
|
994
|
+
if (ids.length == 0) {
|
|
995
|
+
return { trx, jobs: [] };
|
|
996
|
+
}
|
|
997
|
+
const rows = await trx.trx
|
|
998
|
+
.selectFrom(JOB_TABLE)
|
|
999
|
+
.selectAll()
|
|
1000
|
+
.where('id', 'in', ids)
|
|
1001
|
+
.limit(limit)
|
|
1002
|
+
.forUpdate()
|
|
1003
|
+
.skipLocked()
|
|
1004
|
+
.execute();
|
|
1005
|
+
log.debug(`locked ${rows.length} jobs`);
|
|
1006
|
+
const jobs = [];
|
|
1007
|
+
for (const r of rows) {
|
|
1008
|
+
jobs.push({
|
|
1009
|
+
id: Number(r.id),
|
|
1010
|
+
completedAt: r.completed_at,
|
|
1011
|
+
userId: r.user_id,
|
|
1012
|
+
rndrUsed: BigInt(r.render_amt),
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
return { trx, jobs };
|
|
1016
|
+
},
|
|
1017
|
+
async paymentMetrics(log) {
|
|
1018
|
+
// Get the latest epochs for each channel
|
|
1019
|
+
const latestEpochs = await db
|
|
1020
|
+
.selectFrom(EPOCH_TABLE)
|
|
1021
|
+
.select(['id', 'ui', 'channel', 'start_time', 'end_time'])
|
|
1022
|
+
.where('channel', 'in', [
|
|
1023
|
+
'availability',
|
|
1024
|
+
'node_operator',
|
|
1025
|
+
'render_compute_network',
|
|
1026
|
+
])
|
|
1027
|
+
.distinctOn('channel')
|
|
1028
|
+
.where('saved_at', 'is not', null)
|
|
1029
|
+
.orderBy('channel')
|
|
1030
|
+
.orderBy('ui', 'desc')
|
|
1031
|
+
.execute();
|
|
1032
|
+
// Calculate distributed rewards for each channel
|
|
1033
|
+
let availabilityRewards = 0;
|
|
1034
|
+
let nodeOperatorRewards = 0;
|
|
1035
|
+
let renderComputeNetworkRewards = 0;
|
|
1036
|
+
let latestEpochId = 0;
|
|
1037
|
+
for (const epoch of latestEpochs) {
|
|
1038
|
+
if (!epoch.end_time) {
|
|
1039
|
+
log?.info(`Skipping epoch ${epoch.ui} for channel ${epoch.channel} - no end_time`);
|
|
1040
|
+
continue;
|
|
1041
|
+
}
|
|
1042
|
+
// Get total distributed rewards for this channel after epoch ended
|
|
1043
|
+
const transfersResult = await db
|
|
1044
|
+
.selectFrom(SOL_TRANSFER_TABLE)
|
|
1045
|
+
.innerJoin('sol_tx', 'sol_tx.id', `${SOL_TRANSFER_TABLE}.sol_tx_id`)
|
|
1046
|
+
.select([(eb) => eb.fn.sum('amount_payed').as('total_distributed')])
|
|
1047
|
+
.where(`${SOL_TRANSFER_TABLE}.channel`, '=', epoch.channel)
|
|
1048
|
+
.where('sol_tx.executed_at', '>', epoch.end_time)
|
|
1049
|
+
.executeTakeFirst();
|
|
1050
|
+
const totalDistributed = Number(transfersResult?.total_distributed || 0);
|
|
1051
|
+
switch (epoch.channel) {
|
|
1052
|
+
case 'availability':
|
|
1053
|
+
availabilityRewards = totalDistributed;
|
|
1054
|
+
break;
|
|
1055
|
+
case 'node_operator':
|
|
1056
|
+
nodeOperatorRewards = totalDistributed;
|
|
1057
|
+
latestEpochId = Number(epoch.ui);
|
|
1058
|
+
break;
|
|
1059
|
+
case 'render_compute_network':
|
|
1060
|
+
renderComputeNetworkRewards = totalDistributed;
|
|
1061
|
+
break;
|
|
1062
|
+
}
|
|
1063
|
+
log?.info(`Channel ${epoch.channel}: ${totalDistributed} distributed after epoch ${epoch.ui}`);
|
|
1064
|
+
}
|
|
1065
|
+
return {
|
|
1066
|
+
latestEpochId,
|
|
1067
|
+
availabilityRewards,
|
|
1068
|
+
nodeOperatorRewards,
|
|
1069
|
+
renderComputeNetworkRewards,
|
|
1070
|
+
};
|
|
1071
|
+
},
|
|
1072
|
+
async incrBuyBurnMetrics(log = consoleLogger) {
|
|
1073
|
+
const res = await db
|
|
1074
|
+
.selectFrom(JOB_TABLE)
|
|
1075
|
+
.select('inserted_at')
|
|
1076
|
+
.orderBy('completed_at asc')
|
|
1077
|
+
.where('burn_id', 'is', null)
|
|
1078
|
+
.execute();
|
|
1079
|
+
if (res.length == 0) {
|
|
1080
|
+
return { outstandingJobs: 0 };
|
|
1081
|
+
}
|
|
1082
|
+
return {
|
|
1083
|
+
outstandingJobs: res.length,
|
|
1084
|
+
oldestJobInsertedAt: res[0].inserted_at ?? undefined,
|
|
1085
|
+
};
|
|
1086
|
+
},
|
|
1087
|
+
async fetchBurns(db, f, log = consoleLogger) {
|
|
1088
|
+
let q = db.trx
|
|
1089
|
+
.selectFrom('burn')
|
|
1090
|
+
.selectAll('burn')
|
|
1091
|
+
.innerJoin('sol_tx', 'sol_tx.id', 'burn.sol_tx_id')
|
|
1092
|
+
.select(['sol_tx.executed_at', 'sol_tx.sig']);
|
|
1093
|
+
if (!f.inclManual) {
|
|
1094
|
+
q = q.where('burn.usdc_spent', '>', String(0));
|
|
1095
|
+
}
|
|
1096
|
+
if (f.executedAfter) {
|
|
1097
|
+
q = q.where('sol_tx.executed_at', '>=', f.executedAfter);
|
|
1098
|
+
}
|
|
1099
|
+
if (f.executedBefore) {
|
|
1100
|
+
q = q.where('sol_tx.executed_at', '<=', f.executedBefore);
|
|
1101
|
+
}
|
|
1102
|
+
if (f.pricedAtBefore) {
|
|
1103
|
+
q = q.where('burn.priced_at', '<=', f.pricedAtBefore);
|
|
1104
|
+
}
|
|
1105
|
+
if (f.pricedAtAfter) {
|
|
1106
|
+
q = q.where('burn.priced_at', '>=', f.pricedAtAfter);
|
|
1107
|
+
}
|
|
1108
|
+
if (f.orderBy == 'pricedAt') {
|
|
1109
|
+
q = q.orderBy(['burn.priced_at']);
|
|
1110
|
+
}
|
|
1111
|
+
else if (f.orderBy == 'executedAt') {
|
|
1112
|
+
q = q.orderBy(['sol_tx.executed_at']);
|
|
1113
|
+
}
|
|
1114
|
+
log.info(`fetchBurns q ${q.compile().sql} ${JSON.stringify(f)}`);
|
|
1115
|
+
const burns = [];
|
|
1116
|
+
const burnIdToIdx = {};
|
|
1117
|
+
const comp = q.compile();
|
|
1118
|
+
const { rows } = await timedQuery(pgPool, comp.sql, comp.parameters, 'fetchBurns', log);
|
|
1119
|
+
log.info(`fetchBurns len ${rows.length}`);
|
|
1120
|
+
for (const r of rows) {
|
|
1121
|
+
burns.push({
|
|
1122
|
+
jobs: [],
|
|
1123
|
+
id: Number(r.id),
|
|
1124
|
+
renderToUsdc: r.render_to_usdc,
|
|
1125
|
+
solTx: r.sig,
|
|
1126
|
+
executedAt: r.executed_at,
|
|
1127
|
+
pricedAt: r.priced_at,
|
|
1128
|
+
usdcSpent: BigInt(r.usdc_spent ?? 0),
|
|
1129
|
+
quoteAmt: BigInt(r.quote_amt ?? 0),
|
|
1130
|
+
burned: BigInt(r.burned),
|
|
1131
|
+
eurToUsdc: r.eur_to_usdc,
|
|
1132
|
+
markedBurnedAt: r.marked_burned_at,
|
|
1133
|
+
tags: r.tags ? r.tags.split(',') : [],
|
|
1134
|
+
});
|
|
1135
|
+
burnIdToIdx[r.id] = burns.length - 1;
|
|
1136
|
+
}
|
|
1137
|
+
if (f.inclJobs) {
|
|
1138
|
+
const jobRows = await db.trx
|
|
1139
|
+
.selectFrom('job')
|
|
1140
|
+
.selectAll('job')
|
|
1141
|
+
.where('burn_id', 'in', rows.map((r) => r.id))
|
|
1142
|
+
.execute();
|
|
1143
|
+
log.info(`fetchBurns jobRows len ${jobRows.length}`);
|
|
1144
|
+
for (const r of jobRows) {
|
|
1145
|
+
burns[burnIdToIdx[r.burn_id]].jobs.push({
|
|
1146
|
+
id: Number(r.id),
|
|
1147
|
+
completedAt: r.completed_at,
|
|
1148
|
+
rndrUsed: BigInt(r.render_amt),
|
|
1149
|
+
obhUsed: r.obh_used ? Number(r.obh_used) : undefined,
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
return burns;
|
|
1154
|
+
},
|
|
1155
|
+
async createBridgeTransfers(db, bts, log = consoleLogger) {
|
|
1156
|
+
return createBridgeTransfers(db.trx, bts, log);
|
|
1157
|
+
},
|
|
1158
|
+
async getBridgeTransfers({ solKey, page, }, log = consoleLogger) {
|
|
1159
|
+
let q = db
|
|
1160
|
+
.selectFrom(BRIDGE_TRANSFER_TABLE)
|
|
1161
|
+
.innerJoin(EPOCH_TABLE, 'epoch.id', 'bridge_transfer.epoch_id')
|
|
1162
|
+
.innerJoin(ENTITY_TABLE, 'entity.id', 'bridge_transfer.entity_id')
|
|
1163
|
+
.select([
|
|
1164
|
+
'entity.sol_key',
|
|
1165
|
+
'bridge_transfer.sequence',
|
|
1166
|
+
'bridge_transfer.created_at',
|
|
1167
|
+
'bridge_transfer.amount',
|
|
1168
|
+
'bridge_transfer.upgrade_bracket',
|
|
1169
|
+
'bridge_transfer.prior_supply',
|
|
1170
|
+
'bridge_transfer.points',
|
|
1171
|
+
'epoch.ui',
|
|
1172
|
+
]);
|
|
1173
|
+
let limit = 10000;
|
|
1174
|
+
if (page && page.limit) {
|
|
1175
|
+
limit = page.limit;
|
|
1176
|
+
}
|
|
1177
|
+
q = q.limit(page.limit).orderBy(`${BRIDGE_TRANSFER_TABLE}.id`);
|
|
1178
|
+
let offset = 0;
|
|
1179
|
+
if (page && page.cursor) {
|
|
1180
|
+
offset = Number(atob(page.cursor));
|
|
1181
|
+
q = q.offset(offset);
|
|
1182
|
+
}
|
|
1183
|
+
if (solKey) {
|
|
1184
|
+
q = q.where('entity.sol_key', '=', solKey);
|
|
1185
|
+
}
|
|
1186
|
+
const c = q.compile();
|
|
1187
|
+
log.debug(`fetch bridge transfers ${c.sql} params ${c.parameters}`);
|
|
1188
|
+
const res = await q.execute();
|
|
1189
|
+
log.info(`bridge transfers ${res.length}`);
|
|
1190
|
+
return {
|
|
1191
|
+
bts: res.map((row) => {
|
|
1192
|
+
return {
|
|
1193
|
+
solKey: row.sol_key,
|
|
1194
|
+
seq: BigInt(row.sequence),
|
|
1195
|
+
createdAt: row.created_at,
|
|
1196
|
+
amount: BigInt(row.amount),
|
|
1197
|
+
upgradeBracket: row.upgrade_bracket
|
|
1198
|
+
? Number(row.upgrade_bracket)
|
|
1199
|
+
: undefined,
|
|
1200
|
+
priorSupply: BigInt(row.prior_supply),
|
|
1201
|
+
points: row.points ? BigInt(row.points) : undefined,
|
|
1202
|
+
epochId: Number(row.ui),
|
|
1203
|
+
};
|
|
1204
|
+
}),
|
|
1205
|
+
cursor: res.length == limit ? btoa(`${res.length + offset}`) : undefined,
|
|
1206
|
+
};
|
|
1207
|
+
},
|
|
1208
|
+
async getLiabilitySolKeys(f) {
|
|
1209
|
+
let q = db
|
|
1210
|
+
.selectFrom(LIABILITY_TABLE)
|
|
1211
|
+
.innerJoin(ENTITY_TABLE, `${LIABILITY_TABLE}.entity_id`, 'entity.id')
|
|
1212
|
+
.select(({ fn }) => [
|
|
1213
|
+
sql `distinct
|
|
1214
|
+
(entity.sol_key)`.as('sol_key'),
|
|
1215
|
+
])
|
|
1216
|
+
.where(`${LIABILITY_TABLE}.channel`, '=', f.channel);
|
|
1217
|
+
if (f.epoch) {
|
|
1218
|
+
q = q.where('liability.epoch_id', '=', db
|
|
1219
|
+
.selectFrom('epoch')
|
|
1220
|
+
.select('id')
|
|
1221
|
+
.where('channel', '=', f.channel)
|
|
1222
|
+
.where('ui', '=', String(f.epoch)));
|
|
1223
|
+
}
|
|
1224
|
+
const ks = await q.execute();
|
|
1225
|
+
const solKeys = {};
|
|
1226
|
+
for (const k of ks) {
|
|
1227
|
+
solKeys[k.sol_key] = {};
|
|
1228
|
+
}
|
|
1229
|
+
return solKeys;
|
|
1230
|
+
},
|
|
1231
|
+
async getTotalBurned(db) {
|
|
1232
|
+
const total = await db.trx
|
|
1233
|
+
.selectFrom('burn')
|
|
1234
|
+
.select(({ fn }) => fn.sum('burned').as('total_burned'))
|
|
1235
|
+
.execute();
|
|
1236
|
+
return BigInt(total[0]?.total_burned ?? 0);
|
|
1237
|
+
},
|
|
1238
|
+
async insertBurn(p, log = consoleLogger) {
|
|
1239
|
+
await db.transaction().execute(async (trx) => {
|
|
1240
|
+
const solTxId = await getOrInsertTx(trx, { sig: p.solTx, executedAt: p.executedAt }, log);
|
|
1241
|
+
const { id } = await trx
|
|
1242
|
+
.insertInto(BURN_TABLE)
|
|
1243
|
+
.values({
|
|
1244
|
+
sol_tx_id: solTxId,
|
|
1245
|
+
priced_at: p.pricedAt,
|
|
1246
|
+
usdc_spent: p.usdcSpent,
|
|
1247
|
+
quote_amt: p.quoteAmt,
|
|
1248
|
+
burned: p.burned,
|
|
1249
|
+
marked_burned_at: p.markedBurnedAt,
|
|
1250
|
+
})
|
|
1251
|
+
//.onConflict((oc) => oc.column('sol_tx').doNothing())
|
|
1252
|
+
.returning((eb) => ['id as id'])
|
|
1253
|
+
.executeTakeFirstOrThrow();
|
|
1254
|
+
let num = BigInt(0);
|
|
1255
|
+
for (const { rndrUsed, completedAt, id: jobId, insertedAt, userId, } of p.jobs) {
|
|
1256
|
+
const res = await trx
|
|
1257
|
+
.insertInto(JOB_TABLE)
|
|
1258
|
+
.values({
|
|
1259
|
+
id: jobId,
|
|
1260
|
+
render_amt: rndrUsed,
|
|
1261
|
+
burn_id: id,
|
|
1262
|
+
completed_at: completedAt,
|
|
1263
|
+
inserted_at: insertedAt ?? new Date(),
|
|
1264
|
+
user_id: userId,
|
|
1265
|
+
})
|
|
1266
|
+
.onConflict((oc) => oc.column('id').doNothing())
|
|
1267
|
+
.executeTakeFirstOrThrow();
|
|
1268
|
+
num += res.numInsertedOrUpdatedRows ?? BigInt(0);
|
|
1269
|
+
}
|
|
1270
|
+
log.debug(`burn id ${id} inserted ${num} of ${p.jobs.length} burn jobs`);
|
|
1271
|
+
});
|
|
1272
|
+
},
|
|
1273
|
+
async fetchJobs(db, f, log = consoleLogger) {
|
|
1274
|
+
return await fetchJobs1(db.trx, f, log);
|
|
1275
|
+
},
|
|
1276
|
+
async fetchLiabilityEpochs(db, log = consoleLogger) {
|
|
1277
|
+
const q = db.trx
|
|
1278
|
+
.selectFrom(LIABILITY_TABLE)
|
|
1279
|
+
.select(({ fn }) => [
|
|
1280
|
+
`${LIABILITY_TABLE}.channel`,
|
|
1281
|
+
fn.sum(`${LIABILITY_TABLE}.amount_due`).as('amount_due'),
|
|
1282
|
+
])
|
|
1283
|
+
.innerJoin(EPOCH_TABLE, `${EPOCH_TABLE}.id`, `${LIABILITY_TABLE}.epoch_id`)
|
|
1284
|
+
.select(['ui', 'start_time', 'end_time'])
|
|
1285
|
+
.groupBy([
|
|
1286
|
+
'liability.channel',
|
|
1287
|
+
'epoch.ui',
|
|
1288
|
+
'epoch.start_time',
|
|
1289
|
+
'epoch.end_time',
|
|
1290
|
+
])
|
|
1291
|
+
.orderBy(['epoch.start_time desc']);
|
|
1292
|
+
// Fetch node_operator SOL key metadata separately
|
|
1293
|
+
const nodeOpMetadataQuery = db.trx
|
|
1294
|
+
.selectFrom(LIABILITY_TABLE)
|
|
1295
|
+
.select([`${LIABILITY_TABLE}.obh_used`])
|
|
1296
|
+
.innerJoin(EPOCH_TABLE, `${EPOCH_TABLE}.id`, `${LIABILITY_TABLE}.epoch_id`)
|
|
1297
|
+
.select([`${EPOCH_TABLE}.ui as epoch_ui`])
|
|
1298
|
+
.innerJoin('entity', 'entity.id', `${LIABILITY_TABLE}.entity_id`)
|
|
1299
|
+
.select(['entity.sol_key'])
|
|
1300
|
+
.where(`${LIABILITY_TABLE}.channel`, '=', 'node_operator')
|
|
1301
|
+
.where('entity.sol_key', 'is not', null)
|
|
1302
|
+
.where(`${LIABILITY_TABLE}.obh_used`, 'is not', null);
|
|
1303
|
+
log.debug(`fetchLiabilityEpochs ${q.compile().sql}`);
|
|
1304
|
+
log.debug(`nodeOpMetadata ${nodeOpMetadataQuery.compile().sql}`);
|
|
1305
|
+
const [rs, nodeOpMetadata] = await Promise.all([
|
|
1306
|
+
q.execute(),
|
|
1307
|
+
nodeOpMetadataQuery.execute(),
|
|
1308
|
+
]);
|
|
1309
|
+
// Group node operator metadata by epoch
|
|
1310
|
+
const metadataByEpoch = {};
|
|
1311
|
+
nodeOpMetadata.forEach((row) => {
|
|
1312
|
+
const epochId = Number(row.epoch_ui);
|
|
1313
|
+
if (!metadataByEpoch[epochId]) {
|
|
1314
|
+
metadataByEpoch[epochId] = {};
|
|
1315
|
+
}
|
|
1316
|
+
if (row.sol_key && row.obh_used !== null) {
|
|
1317
|
+
metadataByEpoch[epochId][row.sol_key] = {
|
|
1318
|
+
obhUsed: Number(row.obh_used),
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
});
|
|
1322
|
+
return rs.map((r) => ({
|
|
1323
|
+
epochId: Number(r.ui),
|
|
1324
|
+
channel: r.channel,
|
|
1325
|
+
start: r.start_time,
|
|
1326
|
+
end: r.end_time,
|
|
1327
|
+
amountDue: Number(r.amount_due),
|
|
1328
|
+
metadataBySolKey: r.channel === 'node_operator'
|
|
1329
|
+
? metadataByEpoch[Number(r.ui)] || {}
|
|
1330
|
+
: {},
|
|
1331
|
+
}));
|
|
1332
|
+
},
|
|
1333
|
+
async getBurnAdjustments(trx, ps, log = consoleLogger) {
|
|
1334
|
+
if (ps.toFill) {
|
|
1335
|
+
const q = trx
|
|
1336
|
+
.selectFrom(BURN_ADJUSTMENT_TABLE)
|
|
1337
|
+
.selectAll(BURN_ADJUSTMENT_TABLE)
|
|
1338
|
+
.innerJoin(JOB_TABLE, `${JOB_TABLE}.id`, `${BURN_ADJUSTMENT_TABLE}.job_id`)
|
|
1339
|
+
.select([`${JOB_TABLE}.completed_at`, `${JOB_TABLE}.render_amt`])
|
|
1340
|
+
.leftJoin(BURN_TABLE, `${BURN_TABLE}.burn_adjustment_id`, `${BURN_ADJUSTMENT_TABLE}.id`)
|
|
1341
|
+
.select(({ fn, lit }) => [
|
|
1342
|
+
fn
|
|
1343
|
+
.sum(fn.coalesce(`${BURN_TABLE}.to_burn`, lit(0)))
|
|
1344
|
+
.as('tot_to_burn'),
|
|
1345
|
+
fn
|
|
1346
|
+
.sum(fn.coalesce(`${BURN_TABLE}.burned`, lit(0)))
|
|
1347
|
+
.as('tot_burned'),
|
|
1348
|
+
])
|
|
1349
|
+
.groupBy([
|
|
1350
|
+
`${BURN_ADJUSTMENT_TABLE}.id`,
|
|
1351
|
+
`${JOB_TABLE}.completed_at`,
|
|
1352
|
+
`${JOB_TABLE}.render_amt`,
|
|
1353
|
+
])
|
|
1354
|
+
.havingRef(sql `SUM(COALESCE("burn"."to_burn", 0)) - SUM(COALESCE("burn"."burned", 0))`, '<', `${BURN_ADJUSTMENT_TABLE}.down_adj_to_burn`);
|
|
1355
|
+
log.info(`getBurnAdjustments ${q.compile().sql}`);
|
|
1356
|
+
const res = await q.execute();
|
|
1357
|
+
return res.map((r) => ({
|
|
1358
|
+
id: Number(r.id),
|
|
1359
|
+
createdAt: r.created_at,
|
|
1360
|
+
jobId: Number(r.job_id),
|
|
1361
|
+
downAdjRndrUsed: Number(r.down_adj_rndr_used),
|
|
1362
|
+
downAdjToBurn: Number(r.down_adj_to_burn),
|
|
1363
|
+
adjusted: Number(r.tot_to_burn) - Number(r.tot_burned),
|
|
1364
|
+
job: {
|
|
1365
|
+
id: Number(r.job_id),
|
|
1366
|
+
completedAt: r.completed_at,
|
|
1367
|
+
rndrUsed: BigInt(r.render_amt),
|
|
1368
|
+
},
|
|
1369
|
+
}));
|
|
1370
|
+
}
|
|
1371
|
+
return [];
|
|
1372
|
+
},
|
|
1373
|
+
async insertPolygonUpgrade(db, p, log = consoleLogger) {
|
|
1374
|
+
const solTxId = await getOrInsertTx(db.trx, {
|
|
1375
|
+
sig: p.solTx.sig,
|
|
1376
|
+
executedAt: p.solTx.executedAt,
|
|
1377
|
+
});
|
|
1378
|
+
await db.trx
|
|
1379
|
+
.insertInto(POLYGON_UPGRADE_TABLE)
|
|
1380
|
+
.values({
|
|
1381
|
+
eth_addr: p.ethAddr,
|
|
1382
|
+
sol_addr: p.solAddr,
|
|
1383
|
+
amount: p.amount,
|
|
1384
|
+
sol_tx_id: solTxId,
|
|
1385
|
+
})
|
|
1386
|
+
.execute();
|
|
1387
|
+
log.info(`insertPolygonUpgrade ${JSON.stringify(p)}`);
|
|
1388
|
+
return;
|
|
1389
|
+
},
|
|
1390
|
+
async fetchPolygonUpgrades(db, f, log = consoleLogger) {
|
|
1391
|
+
let query = db.trx
|
|
1392
|
+
.selectFrom('polygon_upgrade')
|
|
1393
|
+
.selectAll('polygon_upgrade')
|
|
1394
|
+
.innerJoin('sol_tx', 'sol_tx.id', 'polygon_upgrade.sol_tx_id')
|
|
1395
|
+
.select(['sol_tx.sig', 'sol_tx.executed_at'])
|
|
1396
|
+
.orderBy('sol_tx.executed_at', 'desc');
|
|
1397
|
+
if (f.executedAfter) {
|
|
1398
|
+
query = query.where('sol_tx.executed_at', '>=', f.executedAfter);
|
|
1399
|
+
}
|
|
1400
|
+
if (f.executedBefore) {
|
|
1401
|
+
query = query.where('sol_tx.executed_at', '<=', f.executedBefore);
|
|
1402
|
+
}
|
|
1403
|
+
const results = await query.execute();
|
|
1404
|
+
log.info(`fetchPolygonUpgrades returned ${results.length} records`);
|
|
1405
|
+
return results.map((row) => ({
|
|
1406
|
+
ethAddr: row.eth_addr,
|
|
1407
|
+
solAddr: row.sol_addr,
|
|
1408
|
+
amount: BigInt(row.amount.toString()),
|
|
1409
|
+
solTx: {
|
|
1410
|
+
sig: row.sig,
|
|
1411
|
+
executedAt: row.executed_at,
|
|
1412
|
+
},
|
|
1413
|
+
}));
|
|
1414
|
+
},
|
|
1415
|
+
async getCurrentUserGrantSpend(db, f, log = consoleLogger) {
|
|
1416
|
+
return await getCurrentUserGrantSpend(db.trx, f, log);
|
|
1417
|
+
},
|
|
1418
|
+
async deleteAll(log = consoleLogger) {
|
|
1419
|
+
if (config.database.includes('render')) {
|
|
1420
|
+
throw new Error('cannot nuke from deployed database. call from tests only');
|
|
1421
|
+
}
|
|
1422
|
+
const tx = await begin(db, log);
|
|
1423
|
+
try {
|
|
1424
|
+
const res = await Promise.all([
|
|
1425
|
+
tx.trx
|
|
1426
|
+
.deleteFrom(SOL_TX_TABLE)
|
|
1427
|
+
.where('id', 'is not', null)
|
|
1428
|
+
.executeTakeFirst(),
|
|
1429
|
+
tx.trx.deleteFrom(LIABILITY_TABLE).executeTakeFirstOrThrow(),
|
|
1430
|
+
tx.trx.deleteFrom(SOL_TRANSFER_TABLE).executeTakeFirstOrThrow(),
|
|
1431
|
+
tx.trx
|
|
1432
|
+
.deleteFrom(EPOCH_TABLE)
|
|
1433
|
+
.where('id', 'is not', null)
|
|
1434
|
+
.executeTakeFirst(),
|
|
1435
|
+
tx.trx
|
|
1436
|
+
.deleteFrom(ENTITY_TABLE)
|
|
1437
|
+
.where('id', 'is not', null)
|
|
1438
|
+
.executeTakeFirst(),
|
|
1439
|
+
tx.trx
|
|
1440
|
+
.deleteFrom(MANUAL_BURN_TABLE)
|
|
1441
|
+
.where('epoch_id', 'is not', null)
|
|
1442
|
+
.executeTakeFirst(),
|
|
1443
|
+
tx.trx
|
|
1444
|
+
.deleteFrom(JOB_ID_TABLE)
|
|
1445
|
+
.where('id', 'is not', null)
|
|
1446
|
+
.executeTakeFirst(),
|
|
1447
|
+
tx.trx
|
|
1448
|
+
.deleteFrom(JOB_TABLE)
|
|
1449
|
+
.where('id', 'is not', null)
|
|
1450
|
+
.executeTakeFirst(),
|
|
1451
|
+
tx.trx
|
|
1452
|
+
.deleteFrom(BURN_TABLE)
|
|
1453
|
+
.where('id', 'is not', null)
|
|
1454
|
+
.executeTakeFirst(),
|
|
1455
|
+
]);
|
|
1456
|
+
tx.commit();
|
|
1457
|
+
}
|
|
1458
|
+
catch (e) {
|
|
1459
|
+
log.error(`caught ${e}`);
|
|
1460
|
+
tx.rollback();
|
|
1461
|
+
}
|
|
1462
|
+
},
|
|
1463
|
+
};
|
|
1464
|
+
};
|
|
1465
|
+
//# sourceMappingURL=v2Client.js.map
|