@render-foundation/utils 0.0.243 → 0.0.244

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.
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.calculateBurnAmounts = exports.batchFinishedAtMedian = void 0;
15
+ const spl_utils_1 = require("@render-foundation/spl-utils");
16
+ const logger_1 = require("../logger");
17
+ // Utility function copied from cron-server
18
+ const batchFinishedAtMedian = (jobs) => {
19
+ const pricedAt = jobs.length % 2 == 0
20
+ ? new Date(Math.trunc((Date.parse(jobs[jobs.length / 2].finishedAt) +
21
+ Date.parse(jobs[jobs.length / 2 - 1].finishedAt)) /
22
+ 2))
23
+ : new Date(Date.parse(jobs[Math.floor(jobs.length / 2)].finishedAt));
24
+ return pricedAt;
25
+ };
26
+ exports.batchFinishedAtMedian = batchFinishedAtMedian;
27
+ /**
28
+ * Pure function that calculates burn amounts without side effects
29
+ * This is the core logic extracted from consumeIncrBuyAndBurnRenderPG
30
+ */
31
+ const calculateBurnAmounts = (input, opts = {
32
+ perpetual: true,
33
+ }, log = logger_1.consoleLogger) => {
34
+ const { jobs, userGrantSpends, burnAdjustments, params, decimals, priceAtDate, eurToUSDC, } = input;
35
+ // Apply grant/buy split logic to jobs
36
+ const processedJobs = jobs.map((j) => {
37
+ const { buyRenderAmt, grantRenderAmt } = j, rest = __rest(j, ["buyRenderAmt", "grantRenderAmt"]);
38
+ return rest;
39
+ }); // Deep copy to avoid mutations
40
+ for (const j of processedJobs) {
41
+ const userGrantSpend = userGrantSpends[j.userId];
42
+ if (userGrantSpend) {
43
+ if (opts.perpetual ||
44
+ userGrantSpend.userGrantSpend.perpetual ||
45
+ j.rndrUsed <= userGrantSpend.currAllot) {
46
+ j.grantRenderAmt = Number(j.rndrUsed);
47
+ log.debug(`job ${j.id} grant split: ${j.rndrUsed}`);
48
+ userGrantSpend.currAllot -= BigInt(j.rndrUsed);
49
+ }
50
+ else if (userGrantSpend.currAllot <= BigInt(0)) {
51
+ log.debug(`job ${j.id} buy split: ${j.rndrUsed}`);
52
+ j.buyRenderAmt = Number(j.rndrUsed);
53
+ }
54
+ else {
55
+ log.debug(`job ${j.id} buy + grant split: ${j.rndrUsed} grant: ${Number(userGrantSpend.currAllot)} buy: ${Number(j.rndrUsed) - Number(userGrantSpend.currAllot)}`);
56
+ j.grantRenderAmt = Number(userGrantSpend.currAllot);
57
+ j.buyRenderAmt = Number(j.rndrUsed) - Number(userGrantSpend.currAllot);
58
+ userGrantSpend.currAllot = BigInt(0);
59
+ }
60
+ }
61
+ else {
62
+ log.debug(`job ${j.id} no grant split: ${j.rndrUsed}`);
63
+ j.buyRenderAmt = Number(j.rndrUsed);
64
+ }
65
+ }
66
+ const totalRenderUsed = processedJobs.reduce((tot, job) => tot + Number(job.rndrUsed.toString()), 0);
67
+ log.debug(`jobs buy + grant split: ${JSON.stringify(processedJobs)}`);
68
+ // Check minimum threshold
69
+ if (totalRenderUsed < (0, spl_utils_1.toBN)(params.minTotalRndrThreshold, decimals).toNumber()) {
70
+ throw new Error(`totalRenderUsed ${totalRenderUsed / Math.pow(10, decimals)} < total threshold ${params.minTotalRndrThreshold}`);
71
+ }
72
+ const totalRenderBuyAndBurn = processedJobs.reduce((tot, job) => { var _a; return tot + Number((_a = job.buyRenderAmt) !== null && _a !== void 0 ? _a : 0); }, 0);
73
+ const totalRender = processedJobs.reduce((tot, job) => tot + Number(job.rndrUsed.toString()), 0);
74
+ log.debug(`Burn calculation inputs:`);
75
+ log.debug(` totalRenderUsed: ${totalRenderUsed}`);
76
+ log.debug(` totalRenderBuyAndBurn: ${totalRenderBuyAndBurn}`);
77
+ log.debug(` totalRender: ${totalRender}`);
78
+ log.debug(` priceAtDate: ${priceAtDate}`);
79
+ log.debug(` eurToUSDC: ${eurToUSDC}`);
80
+ const pricedAt = (0, exports.batchFinishedAtMedian)(processedJobs.map((job) => ({
81
+ finishedAt: job.completedAt.toISOString(),
82
+ jobId: job.id,
83
+ rndrUsed: Number(job.rndrUsed.toString()),
84
+ })));
85
+ // Core burn calculation logic
86
+ const toBurn = Math.trunc((totalRender / priceAtDate / 4) * 0.95 * eurToUSDC);
87
+ let renderToBuyAndBurn = Math.trunc((totalRenderBuyAndBurn / totalRender) * toBurn);
88
+ const origRenderToBuyAndBurn = renderToBuyAndBurn;
89
+ log.debug(`Core burn calculation:`);
90
+ log.debug(` toBurn: ${toBurn}`);
91
+ log.debug(` renderToBuyAndBurn: ${renderToBuyAndBurn}`);
92
+ log.debug(` origRenderToBuyAndBurn: ${origRenderToBuyAndBurn}`);
93
+ let burnAdjustmentId = undefined;
94
+ // Apply burn adjustments
95
+ if (burnAdjustments.length > 0) {
96
+ const adjustment = burnAdjustments[0];
97
+ burnAdjustmentId = adjustment.id;
98
+ const adjLeft = adjustment.downAdjToBurn - adjustment.adjusted;
99
+ const minBurnRndrThreshold = (0, spl_utils_1.toBN)(params.minBurnRndrThreshold, decimals).toNumber();
100
+ const available = renderToBuyAndBurn - minBurnRndrThreshold;
101
+ if (adjLeft > available) {
102
+ renderToBuyAndBurn = minBurnRndrThreshold;
103
+ }
104
+ else {
105
+ renderToBuyAndBurn -= adjLeft;
106
+ }
107
+ }
108
+ // Validate burn thresholds
109
+ const toBuyAndBurnUI = renderToBuyAndBurn / Math.pow(10, decimals);
110
+ if (renderToBuyAndBurn != 0) {
111
+ if (toBuyAndBurnUI > params.maxBurnRndrThreshold) {
112
+ throw new Error(`buy and burn RENDER ${toBuyAndBurnUI} too high`);
113
+ }
114
+ if (toBuyAndBurnUI < params.minBurnRndrThreshold) {
115
+ throw new Error(`buy and burn RENDER ${toBuyAndBurnUI} too low < ${params.minBurnRndrThreshold}`);
116
+ }
117
+ }
118
+ const renderToBurnFromEmissions = toBurn - origRenderToBuyAndBurn;
119
+ const totalToBurn = renderToBuyAndBurn + renderToBurnFromEmissions;
120
+ log.debug(`Final burn calculation results:`);
121
+ log.debug(` renderToBuyAndBurn: ${renderToBuyAndBurn}`);
122
+ log.debug(` renderToBurnFromEmissions: ${renderToBurnFromEmissions}`);
123
+ log.debug(` totalToBurn: ${totalToBurn}`);
124
+ log.debug(` toBurn: ${toBurn}`);
125
+ return {
126
+ totalRenderUsed,
127
+ totalRenderBuyAndBurn,
128
+ totalRender,
129
+ renderToBuyAndBurn,
130
+ renderToBurnFromEmissions,
131
+ totalToBurn,
132
+ toBurn,
133
+ origRenderToBuyAndBurn,
134
+ burnAdjustmentId,
135
+ pricedAt,
136
+ processedJobs,
137
+ };
138
+ };
139
+ exports.calculateBurnAmounts = calculateBurnAmounts;
140
+ //# sourceMappingURL=burnCalculations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"burnCalculations.js","sourceRoot":"","sources":["../../../src/burn/burnCalculations.ts"],"names":[],"mappings":"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"}
@@ -35,7 +35,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
35
35
  return (mod && mod.__esModule) ? mod : { "default": mod };
36
36
  };
37
37
  Object.defineProperty(exports, "__esModule", { value: true });
38
- exports.pgClient = exports.batchFinishedAtMedian = exports.foldGrantAllotment = exports.CREDIT_DECIMALS = exports.EUR_TO_CREDITS = exports.DISPERSED_BURN_CORRECTION_TABLE = exports.DISPERSED_SETTLEMENT_TABLE = exports.GRANT_BURN_TABLE = exports.GRANT_BURN_ID_TABLE = exports.OTOY_GRANT_TABLE = exports.USER_GRANT_SPEND_TABLE = exports.CURRENT_USER_GRANT_SPEND_TABLE = exports.POLYGON_UPGRADE_TABLE = exports.ENTITY_EPOCH_INFO_TABLE = exports.BURN_CORRECTION_TABLE = exports.NETWORK_REVENUE_TABLE = exports.BRIDGE_TRANSFER_TABLE = exports.BURN_TABLE = exports.JOB_TABLE = exports.JOB_ID_TABLE = exports.LIABILITY_ADJUSTMENT_BATCH_TABLE = exports.LIABILITY_ADJUSTMENT_TABLE = exports.LIABILITY_TABLE = exports.SOL_TRANSFER_TABLE = exports.ENTITY_TABLE = exports.MANUAL_BURN_TABLE = exports.EPOCH_TABLE = exports.SOL_TX_TABLE = void 0;
38
+ exports.pgClient = exports.batchFinishedAtMedian = exports.foldGrantAllotment = exports.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");
@@ -71,6 +71,10 @@ BigInt.prototype.toJSON = function () {
71
71
  // confirmed_amount = EUR, despite the column name); 1 render credit = EUR 0.25, so credits = EUR x 4.
72
72
  // job.render_amt / user_grant_spend.render_spent_delta are credits x 1e8.
73
73
  exports.EUR_TO_CREDITS = BigInt(4);
74
+ // Grant accounting window: otoy_grant mirrors OTOY RFG issuances from this date. Consumption against the
75
+ // window counts EVERY job the user ran in it — buy-and-burn jobs consumed OTOY credits exactly like
76
+ // emissions-funded ones (counting only emissions-attributed spend over-issued ~2.58M credits, 2026-08-04).
77
+ exports.GRANT_WINDOW_START = '2025-05-01';
74
78
  exports.CREDIT_DECIMALS = BigInt(100000000); // 1e8
75
79
  /**
76
80
  * Pure core of the grant allotment: fold a user's OTOY grants against what's already been spent.
@@ -228,7 +232,7 @@ const pgClient = (config) => {
228
232
  // Spendable grant per user, straight off otoy_grant (FIFO), net of what's already been spent or
229
233
  // already settled by a burn correction. See GrantAllotment for the units + the double-spend guard.
230
234
  const getGrantAllotments = (db, p, log = logger_1.consoleLogger) => __awaiter(void 0, void 0, void 0, function* () {
231
- var _b, _c, _d;
235
+ var _b, _c, _d, _e;
232
236
  const out = {};
233
237
  if (!p.userIds.length)
234
238
  return out;
@@ -266,16 +270,30 @@ const pgClient = (config) => {
266
270
  .where('user_id', 'in', p.userIds)
267
271
  .groupBy('user_id')
268
272
  .execute();
269
- // jobs already settled by a burn correction their grant is spoken for (double-spend guard)
270
- const corrected = yield db
273
+ // ALL in-window job spend the true deduction ledger. Every render job flows through us, and every
274
+ // job consumed the user's OTOY credits regardless of HOW we funded the burn (emissions or USDC
275
+ // buy-and-burn). This supersedes the old correction-settled term (those jobs are in-window) and
276
+ // closes the pre-go-live gap where buy-and-burn jobs didn't decrement grants.
277
+ const jobspend = yield db
271
278
  .selectFrom(`${exports.JOB_TABLE} as j`)
272
- .innerJoin(`${exports.BURN_CORRECTION_TABLE} as bc`, 'bc.job_id', 'j.id')
273
279
  .select((eb) => ['j.user_id as user_id', eb.fn.sum('j.render_amt').as('amt')])
274
280
  .where('j.user_id', 'in', p.userIds)
281
+ .where('j.completed_at', '>=', new Date(exports.GRANT_WINDOW_START))
275
282
  .groupBy('j.user_id')
276
283
  .execute();
284
+ // legacy CSV balance (pre-cutoff OTOY grants): still a granted source, folded in here so the
285
+ // consumer no longer seeds it separately.
286
+ const legacy = yield db
287
+ .selectFrom(exports.CURRENT_USER_GRANT_SPEND_TABLE)
288
+ .select(['user_id', 'render_spent'])
289
+ .where('user_id', 'in', p.userIds)
290
+ .execute();
277
291
  const spentBy = new Map(spent.map((r) => { var _a; return [r.user_id, BigInt((_a = r.net) !== null && _a !== void 0 ? _a : 0)]; }));
278
- const corrBy = new Map(corrected.map((r) => { var _a; return [r.user_id, BigInt((_a = r.amt) !== null && _a !== void 0 ? _a : 0)]; }));
292
+ const jobBy = new Map(jobspend.map((r) => { var _a; return [r.user_id, BigInt((_a = r.amt) !== null && _a !== void 0 ? _a : 0)]; }));
293
+ const legBy = new Map(legacy.map((r) => {
294
+ const v = BigInt(r.render_spent);
295
+ return [r.user_id, v > BigInt(0) ? v : BigInt(0)];
296
+ }));
279
297
  for (const g of grants) {
280
298
  const u = g.user_id;
281
299
  const cur = (_b = out[u]) !== null && _b !== void 0 ? _b : (out[u] = { userId: u, granted: BigInt(0), consumed: BigInt(0), avail: BigInt(0), grants: [] });
@@ -288,15 +306,25 @@ const pgClient = (config) => {
288
306
  createdAt: g.otoy_created_at ? new Date(g.otoy_created_at) : null,
289
307
  });
290
308
  }
309
+ // legacy-only users (no mirrored grant) still get an entry — their CSV balance is a granted source
310
+ for (const [u, v] of legBy) {
311
+ if (v > BigInt(0) && !out[u])
312
+ out[u] = { userId: u, granted: BigInt(0), consumed: BigInt(0), avail: BigInt(0), grants: [] };
313
+ }
291
314
  for (const u of Object.keys(out)) {
292
- const attributed = (_c = spentBy.get(u)) !== null && _c !== void 0 ? _c : BigInt(0); // abs() spend drawn or grant already credited
293
- const viaCorrection = (_d = corrBy.get(u)) !== null && _d !== void 0 ? _d : BigInt(0);
294
- const f = (0, exports.foldGrantAllotment)(out[u].grants, attributed, viaCorrection);
295
- out[u].granted = f.granted;
296
- out[u].consumed = f.consumed;
297
- out[u].avail = f.avail;
298
- if (viaCorrection > BigInt(0)) {
299
- log.info(`grant allotment ${u}: granted ${f.granted} - attributed ${attributed} - correction-settled ${viaCorrection} = ${f.avail}`);
315
+ const attributed = (_c = spentBy.get(u)) !== null && _c !== void 0 ? _c : BigInt(0); // ledger-counted (burns + pool-tied credits)
316
+ const spend = (_d = jobBy.get(u)) !== null && _d !== void 0 ? _d : BigInt(0); // ALL in-window job spend
317
+ const leg = (_e = legBy.get(u)) !== null && _e !== void 0 ? _e : BigInt(0);
318
+ // consumed = the larger of the two accountings — the ledger can exceed job spend (pool-tied
319
+ // credits retiring capacity), job spend can exceed the ledger (pre-go-live buy-and-burn jobs).
320
+ const consumed = attributed > spend ? attributed : spend;
321
+ const f = (0, exports.foldGrantAllotment)(out[u].grants, consumed, BigInt(0));
322
+ out[u].granted = f.granted + leg;
323
+ out[u].consumed = consumed;
324
+ out[u].avail =
325
+ f.granted + leg > consumed ? f.granted + leg - consumed : BigInt(0);
326
+ if (spend > attributed) {
327
+ log.info(`grant allotment ${u}: in-window job spend ${spend} > ledger-counted ${attributed} — strict consumption applied (granted ${f.granted} + legacy ${leg} => avail ${out[u].avail})`);
300
328
  }
301
329
  }
302
330
  return out;
@@ -1573,21 +1601,11 @@ const pgClient = (config) => {
1573
1601
  var _a;
1574
1602
  return __awaiter(this, void 0, void 0, function* () {
1575
1603
  const lastId = f.cursor ? atob(f.cursor) : undefined;
1576
- // grant_burn holds the portion of the SAME transaction burned from emissions rather than bought with
1577
- // USDC: markJobsBurnt writes buyBurn -> burn and grantBurn -> grant_burn, keyed on the same sol_tx_id.
1578
- // So burn.burned alone UNDERSTATES what the tx actually burned. Measured on prod: 2,053 grant_burn rows
1579
- // back to 2025-09, up to 23.9% of a month's total (Oct 2025) and ~1% recently.
1580
- //
1581
- // A correlated subquery, not a LEFT JOIN. Today there is at most one grant_burn per sol_tx (verified:
1582
- // max 1, zero sol_tx with more), but nothing enforces that, and a join that started multiplying rows
1583
- // would silently corrupt the limit and the cursor — the same failure the jobId filter avoids.
1584
- const grantBurned = (0, kysely_1.sql) `coalesce((select sum(gb.burned) from grant_burn gb where gb.sol_tx_id = burn.sol_tx_id), 0)`;
1585
1604
  let q = db.trx
1586
1605
  .selectFrom('burn')
1587
1606
  .selectAll('burn')
1588
1607
  .innerJoin('sol_tx', 'sol_tx.id', 'burn.sol_tx_id')
1589
- .select(['sol_tx.executed_at', 'sol_tx.sig'])
1590
- .select(grantBurned.as('grant_burned'));
1608
+ .select(['sol_tx.executed_at', 'sol_tx.sig']);
1591
1609
  if (!f.inclManual) {
1592
1610
  q = q.where('burn.usdc_spent', '>', String(0));
1593
1611
  }
@@ -1618,19 +1636,16 @@ const pgClient = (config) => {
1618
1636
  const comp = q.compile();
1619
1637
  const { rows } = yield (0, base_1.timedQuery)(pgPool, comp.sql, comp.parameters, 'fetchBurnsPage', log);
1620
1638
  const burns = rows.map((r) => {
1621
- var _a, _b, _c;
1639
+ var _a, _b;
1622
1640
  return ({
1623
1641
  jobs: [],
1624
- // The emissions-burned portion of the same tx. Callers wanting "RENDER burned by this transaction"
1625
- // must add this to `burned`.
1626
- grantBurned: BigInt((_a = r.grant_burned) !== null && _a !== void 0 ? _a : 0),
1627
1642
  id: Number(r.id),
1628
1643
  renderToUsdc: r.render_to_usdc,
1629
1644
  solTx: r.sig,
1630
1645
  executedAt: r.executed_at,
1631
1646
  pricedAt: r.priced_at,
1632
- usdcSpent: BigInt((_b = r.usdc_spent) !== null && _b !== void 0 ? _b : 0),
1633
- quoteAmt: BigInt((_c = r.quote_amt) !== null && _c !== void 0 ? _c : 0),
1647
+ usdcSpent: BigInt((_a = r.usdc_spent) !== null && _a !== void 0 ? _a : 0),
1648
+ quoteAmt: BigInt((_b = r.quote_amt) !== null && _b !== void 0 ? _b : 0),
1634
1649
  burned: BigInt(r.burned),
1635
1650
  eurToUsdc: r.eur_to_usdc,
1636
1651
  markedBurnedAt: r.marked_burned_at,
@@ -1671,16 +1686,12 @@ const pgClient = (config) => {
1671
1686
  // Truncating the naive column is pure arithmetic and identical under every session timezone, and
1672
1687
  // to_char hands back a plain 'YYYY-MM-DD' so node-postgres has no Date to decode in local time.
1673
1688
  const dayExpr = (0, kysely_1.sql) `to_char(date_trunc('day', sol_tx.executed_at), 'YYYY-MM-DD')`;
1674
- // Same emissions gap as fetchBurnsPage: summing burn.burned alone understates the daily total, so the
1675
- // charts drawn from this were short by the emissions portion too.
1676
- const grantBurnedDay = (0, kysely_1.sql) `coalesce(sum((select sum(gb.burned) from grant_burn gb where gb.sol_tx_id = burn.sol_tx_id)), 0)`;
1677
1689
  let q = db.trx
1678
1690
  .selectFrom('burn')
1679
1691
  .innerJoin('sol_tx', 'sol_tx.id', 'burn.sol_tx_id')
1680
1692
  .select(({ fn }) => [
1681
1693
  dayExpr.as('day'),
1682
1694
  fn.sum('burn.burned').as('render_burned'),
1683
- grantBurnedDay.as('grant_burned'),
1684
1695
  fn.sum('burn.usdc_spent').as('usdc_spent'),
1685
1696
  fn.count('burn.id').as('burn_count'),
1686
1697
  ])
@@ -1699,13 +1710,12 @@ const pgClient = (config) => {
1699
1710
  const { rows } = yield (0, base_1.timedQuery)(pgPool, comp.sql, comp.parameters, 'fetchDailyBurnStats', log);
1700
1711
  log.info(`fetchDailyBurnStats len ${rows.length}`);
1701
1712
  return rows.map((r) => {
1702
- var _a, _b, _c, _d;
1713
+ var _a, _b, _c;
1703
1714
  return ({
1704
1715
  day: r.day,
1705
1716
  renderBurned: BigInt((_a = r.render_burned) !== null && _a !== void 0 ? _a : 0),
1706
1717
  usdcSpent: BigInt((_b = r.usdc_spent) !== null && _b !== void 0 ? _b : 0),
1707
- grantBurned: BigInt((_c = r.grant_burned) !== null && _c !== void 0 ? _c : 0),
1708
- burnCount: Number((_d = r.burn_count) !== null && _d !== void 0 ? _d : 0),
1718
+ burnCount: Number((_c = r.burn_count) !== null && _c !== void 0 ? _c : 0),
1709
1719
  });
1710
1720
  });
1711
1721
  });