@render-foundation/utils 0.0.232 → 0.0.233
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/dispersed.js +123 -0
- package/lib/cjs/client/dispersed.js.map +1 -0
- package/lib/cjs/client/pg/v2Client.js +115 -59
- package/lib/cjs/client/pg/v2Client.js.map +1 -1
- package/lib/cjs/client/price.js +41 -17
- package/lib/cjs/client/price.js.map +1 -1
- package/lib/cjs/client/trade.js +2 -0
- package/lib/cjs/client/trade.js.map +1 -1
- package/lib/cjs/index.js +5 -2
- package/lib/cjs/index.js.map +1 -1
- package/lib/esm/src/client/dispersed.js +108 -0
- package/lib/esm/src/client/dispersed.js.map +1 -0
- package/lib/esm/src/client/pg/v2Client.js +99 -56
- package/lib/esm/src/client/pg/v2Client.js.map +1 -1
- package/lib/esm/src/client/price.js +40 -18
- package/lib/esm/src/client/price.js.map +1 -1
- package/lib/esm/src/client/trade.js +2 -0
- package/lib/esm/src/client/trade.js.map +1 -1
- package/lib/esm/src/index.js +5 -2
- package/lib/esm/src/index.js.map +1 -1
- package/lib/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/lib/types/src/client/dispersed.d.ts +91 -0
- package/lib/types/src/client/dispersed.d.ts.map +1 -0
- package/lib/types/src/client/pg/v2Client.d.ts +45 -24
- package/lib/types/src/client/pg/v2Client.d.ts.map +1 -1
- package/lib/types/src/client/price.d.ts.map +1 -1
- package/lib/types/src/client/trade.d.ts +2 -0
- package/lib/types/src/client/trade.d.ts.map +1 -1
- package/lib/types/src/dbTypesV2.d.ts +24 -16
- package/lib/types/src/dbTypesV2.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
- package/lib/cjs/burn/burnAnalysis.js +0 -101
- package/lib/cjs/burn/burnAnalysis.js.map +0 -1
- package/lib/esm/src/burn/burnAnalysis.js +0 -90
- package/lib/esm/src/burn/burnAnalysis.js.map +0 -1
- package/lib/types/src/burn/burnAnalysis.d.ts +0 -51
- package/lib/types/src/burn/burnAnalysis.d.ts.map +0 -1
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { consoleLogger } from '../logger';
|
|
2
|
+
import fetch from 'node-fetch';
|
|
3
|
+
/**
|
|
4
|
+
* Dispersed Token Burn API client (M2M OAuth2 client_credentials).
|
|
5
|
+
*
|
|
6
|
+
* Thin transport only: it mints/caches the bearer token and posts the two burn
|
|
7
|
+
* endpoints (claim, settle). It does NOT price RENDER, sum obligations, or do
|
|
8
|
+
* any burn math — the consumer owns that (incl. the `settled_amount == Σ
|
|
9
|
+
* obligation amounts` invariant). Amounts are passed through as decimal strings.
|
|
10
|
+
*
|
|
11
|
+
* Contract: see docs/dispersed-burn.md. Authoritative spec at
|
|
12
|
+
* `{apiBaseUrl}/openapi.json` (tag "Burn Ledger").
|
|
13
|
+
*/
|
|
14
|
+
// Both scopes, space-delimited, requested on every token mint.
|
|
15
|
+
const SCOPES = 'burn_ledger_claims.create.all burn_ledger_settlements.create.all';
|
|
16
|
+
// Re-mint a little before the token actually expires.
|
|
17
|
+
const TOKEN_REFRESH_MARGIN_MS = 60_000;
|
|
18
|
+
/**
|
|
19
|
+
* Thrown on a 409 from settle: the claim is missing/expired/released/already
|
|
20
|
+
* settled, an obligation mismatch, an amount mismatch, or a tx_signature reused
|
|
21
|
+
* with a different payload. Nothing was written. Do NOT re-burn — reconcile.
|
|
22
|
+
*/
|
|
23
|
+
export class DispersedConflictError extends Error {
|
|
24
|
+
body;
|
|
25
|
+
status = 409;
|
|
26
|
+
constructor(body) {
|
|
27
|
+
super(`dispersed 409 conflict: ${body}`);
|
|
28
|
+
this.body = body;
|
|
29
|
+
this.name = 'DispersedConflictError';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export const dispersedClient = (cfg, fetchImpl = fetch) => {
|
|
33
|
+
let cached;
|
|
34
|
+
const mintToken = async () => {
|
|
35
|
+
const res = await fetchImpl(`${cfg.authBaseUrl}/token`, {
|
|
36
|
+
method: 'POST',
|
|
37
|
+
headers: { 'Content-Type': 'application/json' },
|
|
38
|
+
body: JSON.stringify({
|
|
39
|
+
grant_type: 'client_credentials',
|
|
40
|
+
client_id: cfg.clientId,
|
|
41
|
+
client_secret: cfg.clientSecret,
|
|
42
|
+
scope: SCOPES,
|
|
43
|
+
}),
|
|
44
|
+
});
|
|
45
|
+
if (res.status !== 200) {
|
|
46
|
+
throw new Error(`dispersed token mint failed. code: ${res.status} body: ${await res.text()}`);
|
|
47
|
+
}
|
|
48
|
+
const json = (await res.json());
|
|
49
|
+
cached = {
|
|
50
|
+
token: json.access_token,
|
|
51
|
+
expiresAt: Date.now() + json.expires_in * 1000,
|
|
52
|
+
};
|
|
53
|
+
return cached.token;
|
|
54
|
+
};
|
|
55
|
+
const getToken = async () => {
|
|
56
|
+
if (cached && Date.now() < cached.expiresAt - TOKEN_REFRESH_MARGIN_MS) {
|
|
57
|
+
return cached.token;
|
|
58
|
+
}
|
|
59
|
+
return mintToken();
|
|
60
|
+
};
|
|
61
|
+
// POST with bearer; one transparent re-mint+retry on 401 (a dropped daily run
|
|
62
|
+
// is expensive, so it's worth recovering from a token that died mid-flight).
|
|
63
|
+
const authedPost = async (path, body) => {
|
|
64
|
+
const doPost = (token) => fetchImpl(`${cfg.apiBaseUrl}${path}`, {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
headers: {
|
|
67
|
+
'Content-Type': 'application/json',
|
|
68
|
+
Authorization: `Bearer ${token}`,
|
|
69
|
+
},
|
|
70
|
+
body: JSON.stringify(body),
|
|
71
|
+
});
|
|
72
|
+
let res = await doPost(await getToken());
|
|
73
|
+
if (res.status === 401) {
|
|
74
|
+
cached = undefined;
|
|
75
|
+
res = await doPost(await mintToken());
|
|
76
|
+
}
|
|
77
|
+
return res;
|
|
78
|
+
};
|
|
79
|
+
return {
|
|
80
|
+
async claim({ from, to, limit }, log = consoleLogger) {
|
|
81
|
+
const res = await authedPost('/v1/burn-ledger-claims', {
|
|
82
|
+
executor_id: cfg.executorId,
|
|
83
|
+
from: from.toISOString(),
|
|
84
|
+
to: to.toISOString(),
|
|
85
|
+
...(limit !== undefined ? { limit } : {}),
|
|
86
|
+
});
|
|
87
|
+
if (res.status !== 201) {
|
|
88
|
+
throw new Error(`dispersed claim failed. code: ${res.status} body: ${JSON.stringify(await res.text())}`);
|
|
89
|
+
}
|
|
90
|
+
const result = (await res.json());
|
|
91
|
+
log.info(`dispersed claim ${result.claim.uuid}: ${result.claimed_count} obligations, expires ${result.claim.claim_expires_at}`);
|
|
92
|
+
return result;
|
|
93
|
+
},
|
|
94
|
+
async settle(req, log = consoleLogger) {
|
|
95
|
+
const res = await authedPost('/v1/burn-ledger-settlements', req);
|
|
96
|
+
if (res.status === 409) {
|
|
97
|
+
throw new DispersedConflictError(await res.text());
|
|
98
|
+
}
|
|
99
|
+
if (res.status !== 201 && res.status !== 200) {
|
|
100
|
+
throw new Error(`dispersed settle failed. code: ${res.status} body: ${JSON.stringify(await res.text())}`);
|
|
101
|
+
}
|
|
102
|
+
const result = (await res.json());
|
|
103
|
+
log.info(`dispersed settle ${result.settlement.uuid} for tx ${req.tx_signature} (replay=${result.idempotent_replay})`);
|
|
104
|
+
return result;
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
//# sourceMappingURL=dispersed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispersed.js","sourceRoot":"","sources":["../../../../src/client/dispersed.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,14 +16,14 @@ export const JOB_TABLE = 'job';
|
|
|
16
16
|
export const BURN_TABLE = 'burn';
|
|
17
17
|
export const BRIDGE_TRANSFER_TABLE = 'bridge_transfer';
|
|
18
18
|
export const NETWORK_REVENUE_TABLE = 'network_revenue';
|
|
19
|
-
export const
|
|
20
|
-
export const TX_BURN_ADJUSTMENT_TABLE = 'tx_burn_adjustment';
|
|
19
|
+
export const BURN_CORRECTION_TABLE = 'burn_correction';
|
|
21
20
|
export const ENTITY_EPOCH_INFO_TABLE = 'entity_epoch_info';
|
|
22
21
|
export const POLYGON_UPGRADE_TABLE = 'polygon_upgrade';
|
|
23
22
|
export const CURRENT_USER_GRANT_SPEND_TABLE = 'current_user_grant_spend';
|
|
24
23
|
export const USER_GRANT_SPEND_TABLE = 'user_grant_spend';
|
|
25
24
|
export const GRANT_BURN_ID_TABLE = 'grant_burn_id';
|
|
26
25
|
export const GRANT_BURN_TABLE = 'grant_burn';
|
|
26
|
+
export const DISPERSED_SETTLEMENT_TABLE = 'dispersed_settlement';
|
|
27
27
|
BigInt.prototype.toJSON = function () {
|
|
28
28
|
return this.toString();
|
|
29
29
|
};
|
|
@@ -1039,7 +1039,7 @@ export const pgClient = (config) => {
|
|
|
1039
1039
|
let buyBurnId;
|
|
1040
1040
|
let grantBurnId;
|
|
1041
1041
|
if (p.buyBurn) {
|
|
1042
|
-
const { burned, usdcSpent, eurToUsdc, renderToUsdc, quoteAmt, tags,
|
|
1042
|
+
const { burned, usdcSpent, eurToUsdc, renderToUsdc, quoteAmt, tags, downCorrectionId, escrowCorrectionId, toBurn, fromEscrowUsdc, } = p.buyBurn;
|
|
1043
1043
|
let pricedAt = p.buyBurn.pricedAt;
|
|
1044
1044
|
if (!pricedAt) {
|
|
1045
1045
|
const jobs = await fetchJobs1(trx.trx, { ids: p.jobs.map((j) => String(j.id)) }, log);
|
|
@@ -1058,8 +1058,8 @@ export const pgClient = (config) => {
|
|
|
1058
1058
|
quote_amt: quoteAmt,
|
|
1059
1059
|
burned: burned,
|
|
1060
1060
|
tags: tags.join(','),
|
|
1061
|
-
|
|
1062
|
-
|
|
1061
|
+
down_correction_id: downCorrectionId,
|
|
1062
|
+
escrow_correction_id: escrowCorrectionId,
|
|
1063
1063
|
to_burn: toBurn,
|
|
1064
1064
|
from_escrow_usdc: fromEscrowUsdc ?? 0,
|
|
1065
1065
|
};
|
|
@@ -1597,6 +1597,61 @@ export const pgClient = (config) => {
|
|
|
1597
1597
|
log.debug(`burn id ${id} inserted ${num} of ${p.jobs.length} burn jobs`);
|
|
1598
1598
|
});
|
|
1599
1599
|
},
|
|
1600
|
+
async insertDispersedOutbox(p, log = consoleLogger) {
|
|
1601
|
+
const res = await db
|
|
1602
|
+
.insertInto(DISPERSED_SETTLEMENT_TABLE)
|
|
1603
|
+
.values({
|
|
1604
|
+
tx_signature: p.txSignature,
|
|
1605
|
+
claim_uuid: p.claimUuid,
|
|
1606
|
+
obligation_uuids: p.obligationUuids,
|
|
1607
|
+
settled_amount: p.settledAmount,
|
|
1608
|
+
burned_amount: p.burnedAmount,
|
|
1609
|
+
executed_at: p.executedAt,
|
|
1610
|
+
eur_render: p.eurRender ?? null,
|
|
1611
|
+
last_valid_block_height: p.lastValidBlockHeight ?? null,
|
|
1612
|
+
})
|
|
1613
|
+
.onConflict((oc) => oc.column('tx_signature').doNothing())
|
|
1614
|
+
.executeTakeFirst();
|
|
1615
|
+
log.debug(`dispersed outbox tx ${p.txSignature}: ${res.numInsertedOrUpdatedRows} row(s)`);
|
|
1616
|
+
},
|
|
1617
|
+
async markDispersedSettled(txSignature, p, log = consoleLogger) {
|
|
1618
|
+
await db
|
|
1619
|
+
.updateTable(DISPERSED_SETTLEMENT_TABLE)
|
|
1620
|
+
.set({ settled_at: new Date(), idempotent_replay: p.idempotentReplay })
|
|
1621
|
+
.where('tx_signature', '=', txSignature)
|
|
1622
|
+
.execute();
|
|
1623
|
+
log.debug(`dispersed settled tx ${txSignature}`);
|
|
1624
|
+
},
|
|
1625
|
+
async getUnsettledDispersed(log = consoleLogger) {
|
|
1626
|
+
const rows = await db
|
|
1627
|
+
.selectFrom(DISPERSED_SETTLEMENT_TABLE)
|
|
1628
|
+
.selectAll()
|
|
1629
|
+
.where('settled_at', 'is', null)
|
|
1630
|
+
.execute();
|
|
1631
|
+
log.debug(`dispersed outbox: ${rows.length} unsettled`);
|
|
1632
|
+
return rows.map((r) => ({
|
|
1633
|
+
txSignature: r.tx_signature,
|
|
1634
|
+
claimUuid: r.claim_uuid,
|
|
1635
|
+
obligationUuids: r.obligation_uuids,
|
|
1636
|
+
settledAmount: r.settled_amount,
|
|
1637
|
+
burnedAmount: r.burned_amount,
|
|
1638
|
+
executedAt: r.executed_at,
|
|
1639
|
+
eurRender: r.eur_render ?? undefined,
|
|
1640
|
+
// int8 comes back as a string from pg; block heights are well within JS safe-integer range.
|
|
1641
|
+
lastValidBlockHeight: r.last_valid_block_height != null
|
|
1642
|
+
? Number(r.last_valid_block_height)
|
|
1643
|
+
: undefined,
|
|
1644
|
+
}));
|
|
1645
|
+
},
|
|
1646
|
+
async voidDispersedOutbox(txSignature, log = consoleLogger) {
|
|
1647
|
+
const res = await db
|
|
1648
|
+
.deleteFrom(DISPERSED_SETTLEMENT_TABLE)
|
|
1649
|
+
.where('tx_signature', '=', txSignature)
|
|
1650
|
+
// Never delete a row we already settled — void only applies to pending (un-landed) rows.
|
|
1651
|
+
.where('settled_at', 'is', null)
|
|
1652
|
+
.executeTakeFirst();
|
|
1653
|
+
log.debug(`dispersed outbox void tx ${txSignature}: ${res.numDeletedRows} row(s)`);
|
|
1654
|
+
},
|
|
1600
1655
|
async fetchJobs(db, f, log = consoleLogger) {
|
|
1601
1656
|
return await fetchJobs1(db.trx, f, log);
|
|
1602
1657
|
},
|
|
@@ -1660,85 +1715,73 @@ export const pgClient = (config) => {
|
|
|
1660
1715
|
: {},
|
|
1661
1716
|
}));
|
|
1662
1717
|
},
|
|
1663
|
-
async
|
|
1664
|
-
//
|
|
1718
|
+
async createBurnCorrection(trx, p, log = consoleLogger) {
|
|
1719
|
+
// Anchor + unit invariants are also DB CHECKs; validate here for a clearer error.
|
|
1720
|
+
if ((p.jobId == null) === (p.solTxId == null))
|
|
1721
|
+
throw new Error('burn_correction needs exactly one anchor (jobId XOR solTxId)');
|
|
1722
|
+
if (p.kind === 'down_adjust' && p.amountRender == null)
|
|
1723
|
+
throw new Error('down_adjust needs amountRender');
|
|
1724
|
+
if (p.kind === 'escrow_credit' && p.amountUsdc == null)
|
|
1725
|
+
throw new Error('escrow_credit needs amountUsdc');
|
|
1726
|
+
// Idempotent per (anchor, kind) — a re-run for the same bad job/tx is a no-op.
|
|
1665
1727
|
const row = await trx.trx
|
|
1666
|
-
.insertInto(
|
|
1728
|
+
.insertInto(BURN_CORRECTION_TABLE)
|
|
1667
1729
|
.values({
|
|
1730
|
+
kind: p.kind,
|
|
1731
|
+
job_id: p.jobId,
|
|
1668
1732
|
sol_tx_id: p.solTxId,
|
|
1669
|
-
|
|
1733
|
+
amount_render: p.amountRender,
|
|
1734
|
+
amount_usdc: p.amountUsdc,
|
|
1670
1735
|
description: p.description,
|
|
1671
1736
|
})
|
|
1672
|
-
.onConflict((oc) => oc.
|
|
1737
|
+
.onConflict((oc) => oc.doNothing())
|
|
1673
1738
|
.returning('id')
|
|
1674
1739
|
.executeTakeFirst();
|
|
1675
1740
|
if (!row) {
|
|
1676
|
-
log.info(`
|
|
1741
|
+
log.info(`burn_correction ${p.kind} for ${p.jobId != null ? `job ${p.jobId}` : `sol_tx ${p.solTxId}`} already exists — skipped`);
|
|
1677
1742
|
return undefined;
|
|
1678
1743
|
}
|
|
1679
|
-
log.info(`created
|
|
1744
|
+
log.info(`created burn_correction ${row.id} (${p.kind}, ${p.jobId != null ? `job ${p.jobId}` : `sol_tx ${p.solTxId}`}): ${p.kind === 'down_adjust' ? `${p.amountRender} RENDER` : `${p.amountUsdc} USDC`}`);
|
|
1680
1745
|
return Number(row.id);
|
|
1681
1746
|
},
|
|
1682
|
-
async
|
|
1747
|
+
async getBurnCorrections(trx, ps, log = consoleLogger) {
|
|
1683
1748
|
if (!ps.toFill)
|
|
1684
1749
|
return [];
|
|
1750
|
+
// fill column depends on kind: down_adjust consumes (to_burn - burned); escrow_credit consumes
|
|
1751
|
+
// from_escrow_usdc. Each links via its own burn FK so one burn can carry one of each.
|
|
1752
|
+
const linkCol = ps.kind === 'down_adjust' ? 'down_correction_id' : 'escrow_correction_id';
|
|
1685
1753
|
const q = trx.trx
|
|
1686
|
-
.selectFrom(
|
|
1687
|
-
.selectAll(
|
|
1688
|
-
.
|
|
1689
|
-
.select([`${JOB_TABLE}.completed_at`, `${JOB_TABLE}.render_amt`])
|
|
1690
|
-
.leftJoin(BURN_TABLE, `${BURN_TABLE}.job_burn_adjustment_id`, `${JOB_BURN_ADJUSTMENT_TABLE}.id`)
|
|
1754
|
+
.selectFrom(BURN_CORRECTION_TABLE)
|
|
1755
|
+
.selectAll(BURN_CORRECTION_TABLE)
|
|
1756
|
+
.leftJoin(BURN_TABLE, `${BURN_TABLE}.${linkCol}`, `${BURN_CORRECTION_TABLE}.id`)
|
|
1691
1757
|
.select(({ fn, lit }) => [
|
|
1692
1758
|
fn.sum(fn.coalesce(`${BURN_TABLE}.to_burn`, lit(0))).as('tot_to_burn'),
|
|
1693
1759
|
fn.sum(fn.coalesce(`${BURN_TABLE}.burned`, lit(0))).as('tot_burned'),
|
|
1694
|
-
])
|
|
1695
|
-
.groupBy([
|
|
1696
|
-
`${JOB_BURN_ADJUSTMENT_TABLE}.id`,
|
|
1697
|
-
`${JOB_TABLE}.completed_at`,
|
|
1698
|
-
`${JOB_TABLE}.render_amt`,
|
|
1699
|
-
]);
|
|
1700
|
-
log.info(`getJobBurnAdjustments ${q.compile().sql}`);
|
|
1701
|
-
const res = await q.execute();
|
|
1702
|
-
return res
|
|
1703
|
-
.map((r) => ({
|
|
1704
|
-
id: Number(r.id),
|
|
1705
|
-
createdAt: r.created_at,
|
|
1706
|
-
jobId: Number(r.job_id),
|
|
1707
|
-
downAdjRndrUsed: Number(r.down_adj_rndr_used),
|
|
1708
|
-
downAdjToBurn: Number(r.down_adj_to_burn),
|
|
1709
|
-
adjusted: Number(r.tot_to_burn) - Number(r.tot_burned),
|
|
1710
|
-
job: {
|
|
1711
|
-
id: Number(r.job_id),
|
|
1712
|
-
completedAt: r.completed_at,
|
|
1713
|
-
rndrUsed: BigInt(r.render_amt),
|
|
1714
|
-
},
|
|
1715
|
-
}))
|
|
1716
|
-
.filter((a) => a.adjusted < a.downAdjToBurn);
|
|
1717
|
-
},
|
|
1718
|
-
async getTxBurnAdjustments(trx, ps, log = consoleLogger) {
|
|
1719
|
-
if (!ps.toFill)
|
|
1720
|
-
return [];
|
|
1721
|
-
const q = trx.trx
|
|
1722
|
-
.selectFrom(TX_BURN_ADJUSTMENT_TABLE)
|
|
1723
|
-
.selectAll(TX_BURN_ADJUSTMENT_TABLE)
|
|
1724
|
-
.leftJoin(BURN_TABLE, `${BURN_TABLE}.tx_burn_adjustment_id`, `${TX_BURN_ADJUSTMENT_TABLE}.id`)
|
|
1725
|
-
.select(({ fn, lit }) => [
|
|
1726
1760
|
fn
|
|
1727
1761
|
.sum(fn.coalesce(`${BURN_TABLE}.from_escrow_usdc`, lit(0)))
|
|
1728
1762
|
.as('tot_from_escrow'),
|
|
1729
1763
|
])
|
|
1730
|
-
.
|
|
1731
|
-
|
|
1764
|
+
.where(`${BURN_CORRECTION_TABLE}.kind`, '=', ps.kind)
|
|
1765
|
+
.groupBy(`${BURN_CORRECTION_TABLE}.id`)
|
|
1766
|
+
.orderBy(`${BURN_CORRECTION_TABLE}.created_at`, 'asc');
|
|
1767
|
+
log.info(`getBurnCorrections(${ps.kind}) ${q.compile().sql}`);
|
|
1732
1768
|
const res = await q.execute();
|
|
1733
1769
|
return res
|
|
1734
1770
|
.map((r) => ({
|
|
1735
1771
|
id: Number(r.id),
|
|
1736
1772
|
createdAt: r.created_at,
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1773
|
+
jobId: r.job_id != null ? Number(r.job_id) : undefined,
|
|
1774
|
+
solTxId: r.sol_tx_id != null ? Number(r.sol_tx_id) : undefined,
|
|
1775
|
+
kind: r.kind,
|
|
1776
|
+
amountRender: r.amount_render != null ? Number(r.amount_render) : undefined,
|
|
1777
|
+
amountUsdc: r.amount_usdc != null ? Number(r.amount_usdc) : undefined,
|
|
1778
|
+
consumed: ps.kind === 'down_adjust'
|
|
1779
|
+
? Number(r.tot_to_burn) - Number(r.tot_burned)
|
|
1780
|
+
: Number(r.tot_from_escrow),
|
|
1740
1781
|
}))
|
|
1741
|
-
.filter((
|
|
1782
|
+
.filter((c) => c.kind === 'down_adjust'
|
|
1783
|
+
? c.consumed < (c.amountRender ?? 0)
|
|
1784
|
+
: c.consumed < (c.amountUsdc ?? 0));
|
|
1742
1785
|
},
|
|
1743
1786
|
async insertPolygonUpgrade(db, p, log = consoleLogger) {
|
|
1744
1787
|
const solTxId = await getOrInsertTx(db.trx, {
|