@gvnrdao/dh-sdk 0.0.338 → 0.0.340
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/browser/dist/browser.js +1 -1
- package/dist/graphs/diamond-hands.d.ts +14 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1138 -182
- package/dist/index.mjs +1073 -117
- package/dist/interfaces/chunks/loan-operations.i.d.ts +34 -0
- package/dist/modules/diamond-hands-sdk.d.ts +22 -5
- package/dist/modules/loan/loan-query.module.d.ts +12 -1
- package/dist/sign-guard.js +141 -2
- package/dist/sign-guard.mjs +135 -2
- package/dist/utils/address-conversion.utils.d.ts +15 -6
- package/dist/utils/agent-delegation.utils.d.ts +39 -0
- package/dist/utils/assert-provider-chain.d.ts +16 -0
- package/dist/utils/borrower-ucd-debt-summary.d.ts +44 -0
- package/dist/utils/concurrency-limiter.utils.d.ts +59 -0
- package/dist/utils/lit-action-chain-name.d.ts +14 -0
- package/dist/utils/loan-helpers.utils.d.ts +115 -0
- package/dist/utils/sign-guard/errors.d.ts +5 -1
- package/dist/utils/sign-guard/index.d.ts +1 -0
- package/dist/utils/sign-guard/psm-exchange.d.ts +122 -0
- package/package.json +3 -4
package/dist/index.js
CHANGED
|
@@ -2351,7 +2351,7 @@ ${errorReport}`);
|
|
|
2351
2351
|
}
|
|
2352
2352
|
init_debug_logger();
|
|
2353
2353
|
init_session_signature_cache();
|
|
2354
|
-
var
|
|
2354
|
+
var import_ethers23 = require("ethers");
|
|
2355
2355
|
var EXPIRED_LOAN_MIN_LIQUIDATION_THRESHOLD_BPS = 11e3;
|
|
2356
2356
|
var GRACE_PERIOD_DAYS = 30;
|
|
2357
2357
|
var SATOSHIS_PER_BITCOIN = 100000000n;
|
|
@@ -5279,12 +5279,12 @@ ${auth.clientId}`;
|
|
|
5279
5279
|
}
|
|
5280
5280
|
async function executeVaultSnapshot(params) {
|
|
5281
5281
|
global.ethers = {
|
|
5282
|
-
...
|
|
5282
|
+
...import_ethers23.ethers,
|
|
5283
5283
|
providers: {
|
|
5284
|
-
StaticJsonRpcProvider:
|
|
5284
|
+
StaticJsonRpcProvider: import_ethers23.ethers.JsonRpcProvider
|
|
5285
5285
|
},
|
|
5286
|
-
Contract:
|
|
5287
|
-
utils:
|
|
5286
|
+
Contract: import_ethers23.ethers.Contract,
|
|
5287
|
+
utils: import_ethers23.ethers
|
|
5288
5288
|
// v6 moved utils to top level
|
|
5289
5289
|
};
|
|
5290
5290
|
global.Lit = {
|
|
@@ -5540,8 +5540,8 @@ function getSepoliaConfig() {
|
|
|
5540
5540
|
positionManagerCore: SEPOLIA_CONTRACTS.PositionManagerCoreModule || "",
|
|
5541
5541
|
positionManagerViews: SEPOLIA_CONTRACTS.PositionManagerViews || "",
|
|
5542
5542
|
simplePsmV2: SEPOLIA_CONTRACTS.SimplePSMV2 || "",
|
|
5543
|
-
mockUsdcToken: SEPOLIA_CONTRACTS
|
|
5544
|
-
mockUsdtToken: SEPOLIA_CONTRACTS
|
|
5543
|
+
mockUsdcToken: SEPOLIA_CONTRACTS.mockUsdcToken || "",
|
|
5544
|
+
mockUsdtToken: SEPOLIA_CONTRACTS.mockUsdtToken || "",
|
|
5545
5545
|
loanOperationsManager: SEPOLIA_CONTRACTS.LoanOperationsManagerModule || "",
|
|
5546
5546
|
termManager: SEPOLIA_CONTRACTS.TermManagerModule || "",
|
|
5547
5547
|
circuitBreaker: SEPOLIA_CONTRACTS.CircuitBreakerModule || "",
|
|
@@ -5617,14 +5617,17 @@ function getMainnetConfig() {
|
|
|
5617
5617
|
agentModuleFactory: MAINNET_CONTRACTS.AgentModuleFactory || ""
|
|
5618
5618
|
},
|
|
5619
5619
|
subgraphs: {
|
|
5620
|
-
//
|
|
5621
|
-
//
|
|
5622
|
-
//
|
|
5623
|
-
//
|
|
5624
|
-
//
|
|
5625
|
-
//
|
|
5626
|
-
//
|
|
5627
|
-
|
|
5620
|
+
// The MAINNET subgraph on The Graph's decentralized network — the same id
|
|
5621
|
+
// lit-ops-server's ETHEREUM_SUBGRAPH_URL points at (infra/terraform). The path segment is
|
|
5622
|
+
// the subgraph's PUBLIC ID, not a credential. This used to be a SEPOLIA id under a
|
|
5623
|
+
// chainId-1 config: unusable as shipped, but a caller who added their own key would have
|
|
5624
|
+
// read Sepolia data as mainnet.
|
|
5625
|
+
//
|
|
5626
|
+
// The gateway needs an `Authorization: Bearer <query key>` the SDK does not (and must
|
|
5627
|
+
// not) hold, so this URL only works for a standalone caller supplying their own key.
|
|
5628
|
+
// Service mode never uses it — queries go through the lit-ops-server proxy, which adds
|
|
5629
|
+
// the key server-side. Same rule as rpcUrls above: never ship a keyed URL in a client config.
|
|
5630
|
+
diamondHandsUrl: "https://gateway.thegraph.com/api/subgraphs/id/8Gt9zaSCgkxxSiLMGaKVj7qU3ds1heGVGxgWcXXpwbPd"
|
|
5628
5631
|
},
|
|
5629
5632
|
litNetwork: "chipotle",
|
|
5630
5633
|
debug: false
|
|
@@ -5863,7 +5866,7 @@ __export(src_exports, {
|
|
|
5863
5866
|
module.exports = __toCommonJS(src_exports);
|
|
5864
5867
|
|
|
5865
5868
|
// src/modules/diamond-hands-sdk.ts
|
|
5866
|
-
var
|
|
5869
|
+
var import_ethers21 = require("ethers");
|
|
5867
5870
|
|
|
5868
5871
|
// src/types/result.ts
|
|
5869
5872
|
function success(value) {
|
|
@@ -6862,6 +6865,41 @@ async function resolveAuthorizationInput(provider, signer, ctx) {
|
|
|
6862
6865
|
return { timestamp: result.timestamp, signature: result.signature };
|
|
6863
6866
|
}
|
|
6864
6867
|
|
|
6868
|
+
// src/utils/loan-helpers.utils.ts
|
|
6869
|
+
function baseMintFeeWei(mintAmountWei, originationFeeBps) {
|
|
6870
|
+
if (mintAmountWei < 0n)
|
|
6871
|
+
throw new Error("baseMintFeeWei: mintAmountWei cannot be negative");
|
|
6872
|
+
if (!Number.isInteger(originationFeeBps) || originationFeeBps < 0 || originationFeeBps > 1e4) {
|
|
6873
|
+
throw new Error(
|
|
6874
|
+
`baseMintFeeWei: originationFeeBps must be an integer in [0, 10000] (got ${String(originationFeeBps)})`
|
|
6875
|
+
);
|
|
6876
|
+
}
|
|
6877
|
+
return mintAmountWei * BigInt(originationFeeBps) / 10000n;
|
|
6878
|
+
}
|
|
6879
|
+
function debtAfterMintExceedsLoanCap(params) {
|
|
6880
|
+
const { currentDebtWei, mintAmountWei, mintFeeWei, maxLoanWei } = params;
|
|
6881
|
+
for (const [name, v] of Object.entries(params)) {
|
|
6882
|
+
if (v < 0n)
|
|
6883
|
+
throw new Error(`debtAfterMintExceedsLoanCap: ${name} cannot be negative`);
|
|
6884
|
+
}
|
|
6885
|
+
return currentDebtWei + mintAmountWei + mintFeeWei > maxLoanWei;
|
|
6886
|
+
}
|
|
6887
|
+
function maxPrincipalWithinLoanCap(params) {
|
|
6888
|
+
const { maxLoanWei, currentDebtWei, originationFeeBps } = params;
|
|
6889
|
+
if (maxLoanWei < 0n || currentDebtWei < 0n) {
|
|
6890
|
+
throw new Error("maxPrincipalWithinLoanCap: amounts cannot be negative");
|
|
6891
|
+
}
|
|
6892
|
+
if (!Number.isInteger(originationFeeBps) || originationFeeBps < 0 || originationFeeBps > 1e4) {
|
|
6893
|
+
throw new Error(
|
|
6894
|
+
`maxPrincipalWithinLoanCap: originationFeeBps must be an integer in [0, 10000] (got ${String(originationFeeBps)})`
|
|
6895
|
+
);
|
|
6896
|
+
}
|
|
6897
|
+
const room = maxLoanWei - currentDebtWei;
|
|
6898
|
+
if (room <= 0n)
|
|
6899
|
+
return 0n;
|
|
6900
|
+
return room * 10000n / (10000n + BigInt(originationFeeBps));
|
|
6901
|
+
}
|
|
6902
|
+
|
|
6865
6903
|
// src/utils/eip712-login.ts
|
|
6866
6904
|
var import_ethers4 = require("ethers");
|
|
6867
6905
|
function buildLoginDomain(chainId) {
|
|
@@ -8305,23 +8343,37 @@ var BitcoinUtils = class {
|
|
|
8305
8343
|
};
|
|
8306
8344
|
|
|
8307
8345
|
// src/utils/address-conversion.utils.ts
|
|
8308
|
-
function
|
|
8346
|
+
function networksOfValidAddress(address) {
|
|
8347
|
+
const lower2 = address.toLowerCase();
|
|
8348
|
+
if (lower2.startsWith("bcrt1"))
|
|
8349
|
+
return ["regtest"];
|
|
8350
|
+
if (lower2.startsWith("bc1"))
|
|
8351
|
+
return ["mainnet"];
|
|
8352
|
+
if (lower2.startsWith("tb1"))
|
|
8353
|
+
return ["testnet"];
|
|
8354
|
+
if (address.startsWith("1") || address.startsWith("3"))
|
|
8355
|
+
return ["mainnet"];
|
|
8356
|
+
return ["testnet", "regtest"];
|
|
8357
|
+
}
|
|
8358
|
+
function safeValidateBitcoinAddress(address, network) {
|
|
8309
8359
|
if (!address || typeof address !== "string") {
|
|
8310
8360
|
throw new Error(`Invalid Bitcoin address: must be a non-empty string`);
|
|
8311
8361
|
}
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
|
|
8362
|
+
if (network !== "mainnet" && network !== "testnet" && network !== "regtest") {
|
|
8363
|
+
throw new Error(
|
|
8364
|
+
`Bitcoin network is required to validate an address (got ${JSON.stringify(network)})`
|
|
8365
|
+
);
|
|
8316
8366
|
}
|
|
8317
|
-
if (
|
|
8318
|
-
|
|
8319
|
-
|
|
8320
|
-
|
|
8321
|
-
}
|
|
8322
|
-
|
|
8323
|
-
|
|
8324
|
-
|
|
8367
|
+
if (!BitcoinUtils.validateAddress(address)) {
|
|
8368
|
+
throw new Error(
|
|
8369
|
+
`Invalid Bitcoin address: ${address} (malformed or failed its checksum \u2014 check for a typo)`
|
|
8370
|
+
);
|
|
8371
|
+
}
|
|
8372
|
+
const belongsTo = networksOfValidAddress(address);
|
|
8373
|
+
if (!belongsTo.includes(network)) {
|
|
8374
|
+
throw new Error(
|
|
8375
|
+
`Bitcoin address ${address} is not a ${network} address \u2014 it is a ${belongsTo.join("/")} address. Sending across networks would lose the funds.`
|
|
8376
|
+
);
|
|
8325
8377
|
}
|
|
8326
8378
|
return address;
|
|
8327
8379
|
}
|
|
@@ -10465,6 +10517,101 @@ var LoanStatus = /* @__PURE__ */ ((LoanStatus2) => {
|
|
|
10465
10517
|
|
|
10466
10518
|
// src/modules/loan/loan-query.module.ts
|
|
10467
10519
|
var import_ethers12 = require("ethers");
|
|
10520
|
+
|
|
10521
|
+
// src/constants/chunks/sdk-limits.ts
|
|
10522
|
+
var THE_GRAPH_MAX_BATCH_SIZE = 1e3;
|
|
10523
|
+
|
|
10524
|
+
// src/utils/borrower-ucd-debt-summary.ts
|
|
10525
|
+
var BORROWER_DEBT_ROWS_PAGE_SIZE = THE_GRAPH_MAX_BATCH_SIZE;
|
|
10526
|
+
var BORROWER_DEBT_ROWS_MAX_PAGES = 10;
|
|
10527
|
+
async function collectBorrowerDebtRows(fetchPage) {
|
|
10528
|
+
const pageSize = BORROWER_DEBT_ROWS_PAGE_SIZE;
|
|
10529
|
+
const rows = [];
|
|
10530
|
+
for (let page = 0; page < BORROWER_DEBT_ROWS_MAX_PAGES; page++) {
|
|
10531
|
+
const batch = await fetchPage(page * pageSize, pageSize);
|
|
10532
|
+
if (batch.length > pageSize) {
|
|
10533
|
+
throw new Error(
|
|
10534
|
+
`Borrower debt page ${page} returned ${batch.length} rows for first: ${pageSize}`
|
|
10535
|
+
);
|
|
10536
|
+
}
|
|
10537
|
+
rows.push(...batch);
|
|
10538
|
+
if (batch.length < pageSize) {
|
|
10539
|
+
return rows;
|
|
10540
|
+
}
|
|
10541
|
+
}
|
|
10542
|
+
throw new Error(
|
|
10543
|
+
`Borrower debt rows exceeded ${BORROWER_DEBT_ROWS_MAX_PAGES} pages of ${pageSize} (${rows.length} rows so far); refusing to return a partial total`
|
|
10544
|
+
);
|
|
10545
|
+
}
|
|
10546
|
+
function parseSubgraphLoanStatus(label) {
|
|
10547
|
+
if (typeof label !== "string") {
|
|
10548
|
+
throw new Error(`Subgraph loan status is not a string: ${String(label)}`);
|
|
10549
|
+
}
|
|
10550
|
+
const value = LoanStatus[label];
|
|
10551
|
+
if (typeof value !== "number") {
|
|
10552
|
+
throw new Error(`Unknown subgraph loan status label: "${label}"`);
|
|
10553
|
+
}
|
|
10554
|
+
return value;
|
|
10555
|
+
}
|
|
10556
|
+
function parseSubgraphWei(row) {
|
|
10557
|
+
const raw = row.ucdDebt || "0";
|
|
10558
|
+
if (typeof raw !== "string") {
|
|
10559
|
+
throw new Error(
|
|
10560
|
+
`ucdDebt on position ${row.id} is not a string: ${String(raw)}`
|
|
10561
|
+
);
|
|
10562
|
+
}
|
|
10563
|
+
let wei;
|
|
10564
|
+
try {
|
|
10565
|
+
wei = BigInt(raw);
|
|
10566
|
+
} catch (error) {
|
|
10567
|
+
throw new Error(
|
|
10568
|
+
`ucdDebt on position ${row.id} is not an integer wei string: "${raw}"`,
|
|
10569
|
+
{ cause: error }
|
|
10570
|
+
);
|
|
10571
|
+
}
|
|
10572
|
+
if (wei < 0n) {
|
|
10573
|
+
throw new Error(`Negative ucdDebt on position ${row.id}: ${raw}`);
|
|
10574
|
+
}
|
|
10575
|
+
return wei;
|
|
10576
|
+
}
|
|
10577
|
+
var WEI_PER_UCD = 10n ** 18n;
|
|
10578
|
+
function weiToHumanUcdString(wei) {
|
|
10579
|
+
if (wei < 0n) {
|
|
10580
|
+
throw new Error(`Cannot render negative wei: ${wei}`);
|
|
10581
|
+
}
|
|
10582
|
+
const whole = wei / WEI_PER_UCD;
|
|
10583
|
+
const frac = (wei % WEI_PER_UCD).toString().padStart(18, "0").replace(/0+$/, "");
|
|
10584
|
+
return frac.length > 0 ? `${whole}.${frac}` : `${whole}`;
|
|
10585
|
+
}
|
|
10586
|
+
var STATUS_ORDINALS = Object.values(LoanStatus).filter((v) => typeof v === "number").sort((a, b) => a - b);
|
|
10587
|
+
function buildBorrowerUcdDebtSummary(borrower, rows, fetchedAt = Date.now()) {
|
|
10588
|
+
let totalWei = 0n;
|
|
10589
|
+
const counts = /* @__PURE__ */ new Map();
|
|
10590
|
+
for (const row of rows) {
|
|
10591
|
+
totalWei += parseSubgraphWei(row);
|
|
10592
|
+
const status = parseSubgraphLoanStatus(row.status);
|
|
10593
|
+
counts.set(status, (counts.get(status) ?? 0) + 1);
|
|
10594
|
+
}
|
|
10595
|
+
const byStatus = [];
|
|
10596
|
+
for (const status of STATUS_ORDINALS) {
|
|
10597
|
+
const count = counts.get(status);
|
|
10598
|
+
if (count === void 0) {
|
|
10599
|
+
continue;
|
|
10600
|
+
}
|
|
10601
|
+
byStatus.push({ status, statusLabel: LoanStatus[status], count });
|
|
10602
|
+
}
|
|
10603
|
+
return {
|
|
10604
|
+
borrower: borrower.toLowerCase(),
|
|
10605
|
+
loanCount: rows.length,
|
|
10606
|
+
totalUcdDebt: totalWei.toString(),
|
|
10607
|
+
totalUcdDebtHuman: weiToHumanUcdString(totalWei),
|
|
10608
|
+
byStatus,
|
|
10609
|
+
source: "subgraph",
|
|
10610
|
+
fetchedAt
|
|
10611
|
+
};
|
|
10612
|
+
}
|
|
10613
|
+
|
|
10614
|
+
// src/modules/loan/loan-query.module.ts
|
|
10468
10615
|
var POSITION_CORE_ABI = [
|
|
10469
10616
|
"function getPositionDetails(bytes32) view returns (tuple(bytes32 positionId, bytes32 pkpId, uint256 ucdDebt, string vaultAddress, address borrower, uint40 createdAt, uint40 lastUpdated, uint16 selectedTerm, uint40 expiryAt, uint8 status, uint40 previousExpiryAt, uint16 totalTerm))"
|
|
10470
10617
|
];
|
|
@@ -10857,6 +11004,43 @@ var LoanQuery = class {
|
|
|
10857
11004
|
async getLoansByBorrower(borrower, pagination, orderBy, orderDirection) {
|
|
10858
11005
|
return this.getLoans({ borrower, orderBy, orderDirection }, pagination);
|
|
10859
11006
|
}
|
|
11007
|
+
/**
|
|
11008
|
+
* Combined UCD debt + by-status counts for a borrower from ONE pass over the
|
|
11009
|
+
* subgraph's raw rows (`graphClient.getBorrowerDebtRows`): BigInt wei sum, strict
|
|
11010
|
+
* status labels, `source: "subgraph"`.
|
|
11011
|
+
*
|
|
11012
|
+
* INDEXED figure, not a transaction input — the subgraph lags writes. Anything that
|
|
11013
|
+
* sizes a repayment reads the chain (`getPositionDetailsView`). Fails loud: a page
|
|
11014
|
+
* failure or the page cap is a SUBGRAPH-category failure carrying the cause; there
|
|
11015
|
+
* is no partial total.
|
|
11016
|
+
*/
|
|
11017
|
+
async getBorrowerUcdDebtSummary(borrower) {
|
|
11018
|
+
const trimmed = typeof borrower === "string" ? borrower.trim() : "";
|
|
11019
|
+
if (!(0, import_ethers12.isAddress)(trimmed)) {
|
|
11020
|
+
return failure(
|
|
11021
|
+
new SDKError({
|
|
11022
|
+
message: `Borrower must be an EVM address for a UCD debt summary, got "${trimmed}"`,
|
|
11023
|
+
category: "VALIDATION" /* VALIDATION */,
|
|
11024
|
+
severity: "LOW" /* LOW */,
|
|
11025
|
+
context: { borrower: trimmed }
|
|
11026
|
+
})
|
|
11027
|
+
);
|
|
11028
|
+
}
|
|
11029
|
+
return tryCatchAsync(
|
|
11030
|
+
async () => {
|
|
11031
|
+
const rows = await this.config.graphClient.getBorrowerDebtRows(trimmed);
|
|
11032
|
+
return buildBorrowerUcdDebtSummary(trimmed, rows);
|
|
11033
|
+
},
|
|
11034
|
+
(error) => error instanceof SDKError ? error : new SDKError({
|
|
11035
|
+
message: `Borrower UCD debt summary failed for ${trimmed}: ${error instanceof Error ? error.message : String(error)}`,
|
|
11036
|
+
category: "SUBGRAPH" /* SUBGRAPH */,
|
|
11037
|
+
severity: "MEDIUM" /* MEDIUM */,
|
|
11038
|
+
originalError: error instanceof Error ? error : void 0,
|
|
11039
|
+
cause: error,
|
|
11040
|
+
context: { borrower: trimmed }
|
|
11041
|
+
})
|
|
11042
|
+
);
|
|
11043
|
+
}
|
|
10860
11044
|
/**
|
|
10861
11045
|
* Get active loans (status = ACTIVE)
|
|
10862
11046
|
*
|
|
@@ -12391,10 +12575,36 @@ var GraphClient = class {
|
|
|
12391
12575
|
}
|
|
12392
12576
|
};
|
|
12393
12577
|
|
|
12394
|
-
// src/constants/chunks/sdk-limits.ts
|
|
12395
|
-
var THE_GRAPH_MAX_BATCH_SIZE = 1e3;
|
|
12396
|
-
|
|
12397
12578
|
// src/graphs/diamond-hands.ts
|
|
12579
|
+
var GET_USER_POSITIONS_DOCUMENT = `
|
|
12580
|
+
query GetUserPositions($borrower: Bytes!, $first: Int, $skip: Int, $orderBy: Position_orderBy, $orderDirection: OrderDirection) {
|
|
12581
|
+
user(id: $borrower) {
|
|
12582
|
+
id
|
|
12583
|
+
positions(first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection) {
|
|
12584
|
+
id
|
|
12585
|
+
pkpId
|
|
12586
|
+
borrower {
|
|
12587
|
+
createdAt
|
|
12588
|
+
id
|
|
12589
|
+
}
|
|
12590
|
+
# btcAmount removed - balance is now queried on-chain via lit-actions with signature validation
|
|
12591
|
+
ucdMinted
|
|
12592
|
+
ucdPaid
|
|
12593
|
+
ucdDebt
|
|
12594
|
+
collateralRatio
|
|
12595
|
+
requestedCollateralRatio
|
|
12596
|
+
selectedTerm
|
|
12597
|
+
status
|
|
12598
|
+
createdAt
|
|
12599
|
+
createdAtBlock
|
|
12600
|
+
lastUpdated
|
|
12601
|
+
originalTerm
|
|
12602
|
+
remainingDebt
|
|
12603
|
+
expiryAt
|
|
12604
|
+
}
|
|
12605
|
+
}
|
|
12606
|
+
}
|
|
12607
|
+
`;
|
|
12398
12608
|
function normalizePkpId2(pkpId) {
|
|
12399
12609
|
const raw = pkpId.startsWith("0x") ? pkpId.slice(2) : pkpId;
|
|
12400
12610
|
if (raw.length === 64 && raw.startsWith("000000000000000000000000") && raw !== "0".repeat(64)) {
|
|
@@ -12712,35 +12922,7 @@ var DiamondHandsGraph = class {
|
|
|
12712
12922
|
* Get user positions from subgraph
|
|
12713
12923
|
*/
|
|
12714
12924
|
async getUserPositionsOnly(userAddress, first, skip, orderBy, orderDirection) {
|
|
12715
|
-
const query =
|
|
12716
|
-
query GetUserPositions($borrower: Bytes!, $first: Int, $skip: Int, $orderBy: Position_orderBy, $orderDirection: OrderDirection) {
|
|
12717
|
-
user(id: $borrower) {
|
|
12718
|
-
id
|
|
12719
|
-
positions(first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection) {
|
|
12720
|
-
id
|
|
12721
|
-
pkpId
|
|
12722
|
-
borrower {
|
|
12723
|
-
createdAt
|
|
12724
|
-
id
|
|
12725
|
-
}
|
|
12726
|
-
# btcAmount removed - balance is now queried on-chain via lit-actions with signature validation
|
|
12727
|
-
ucdMinted
|
|
12728
|
-
ucdPaid
|
|
12729
|
-
ucdDebt
|
|
12730
|
-
collateralRatio
|
|
12731
|
-
requestedCollateralRatio
|
|
12732
|
-
selectedTerm
|
|
12733
|
-
status
|
|
12734
|
-
createdAt
|
|
12735
|
-
createdAtBlock
|
|
12736
|
-
lastUpdated
|
|
12737
|
-
originalTerm
|
|
12738
|
-
remainingDebt
|
|
12739
|
-
expiryAt
|
|
12740
|
-
}
|
|
12741
|
-
}
|
|
12742
|
-
}
|
|
12743
|
-
`;
|
|
12925
|
+
const query = GET_USER_POSITIONS_DOCUMENT;
|
|
12744
12926
|
const variables = {
|
|
12745
12927
|
borrower: userAddress.toLowerCase(),
|
|
12746
12928
|
first: first || void 0,
|
|
@@ -12758,6 +12940,45 @@ var DiamondHandsGraph = class {
|
|
|
12758
12940
|
}
|
|
12759
12941
|
return positions;
|
|
12760
12942
|
}
|
|
12943
|
+
/**
|
|
12944
|
+
* Raw `{ id, status, ucdDebt }` for EVERY position of a borrower: one round trip per
|
|
12945
|
+
* 1000 positions, no count walk, no vault RPCs, and no `LoanData` transform (which
|
|
12946
|
+
* coerces wei to a float and leaves `status` a string cast to the numeric enum).
|
|
12947
|
+
*
|
|
12948
|
+
* Deliberately issues the `GetUserPositions` document rather than a leaner one: the
|
|
12949
|
+
* lit-ops graph proxy only passes allowlisted documents (graph-document-policy.ts),
|
|
12950
|
+
* and that one is already listed, so this needs no server change. The extra fields
|
|
12951
|
+
* cost bytes, not round trips.
|
|
12952
|
+
*
|
|
12953
|
+
* Throws past `BORROWER_DEBT_ROWS_MAX_PAGES` — never a partial set.
|
|
12954
|
+
*/
|
|
12955
|
+
async getBorrowerDebtRows(borrower) {
|
|
12956
|
+
const borrowerId = borrower.toLowerCase();
|
|
12957
|
+
return collectBorrowerDebtRows(async (skip, first) => {
|
|
12958
|
+
const result = await this.client.execute(GET_USER_POSITIONS_DOCUMENT, {
|
|
12959
|
+
borrower: borrowerId,
|
|
12960
|
+
first,
|
|
12961
|
+
skip,
|
|
12962
|
+
orderBy: "id",
|
|
12963
|
+
orderDirection: "asc"
|
|
12964
|
+
});
|
|
12965
|
+
if (result?.user == null) {
|
|
12966
|
+
if (skip > 0) {
|
|
12967
|
+
throw new Error(
|
|
12968
|
+
`getBorrowerDebtRows: user ${borrowerId} vanished from the subgraph at skip ${skip}`
|
|
12969
|
+
);
|
|
12970
|
+
}
|
|
12971
|
+
return [];
|
|
12972
|
+
}
|
|
12973
|
+
const positions = result.user.positions;
|
|
12974
|
+
if (!Array.isArray(positions)) {
|
|
12975
|
+
throw new Error(
|
|
12976
|
+
`getBorrowerDebtRows: subgraph returned no positions array for ${borrowerId} at skip ${skip}`
|
|
12977
|
+
);
|
|
12978
|
+
}
|
|
12979
|
+
return positions.filter((p) => p != null);
|
|
12980
|
+
});
|
|
12981
|
+
}
|
|
12761
12982
|
/**
|
|
12762
12983
|
* Get user positions, plus an accurate total count.
|
|
12763
12984
|
*
|
|
@@ -18970,6 +19191,625 @@ async function mintAgentPkp(params) {
|
|
|
18970
19191
|
clearTimeout(timeout);
|
|
18971
19192
|
}
|
|
18972
19193
|
}
|
|
19194
|
+
var AGENT_STATUS = { None: 0, Active: 1, Revoked: 2 };
|
|
19195
|
+
function planAgentBinding(params) {
|
|
19196
|
+
const { isActive, record, nowSeconds } = params;
|
|
19197
|
+
if (isActive)
|
|
19198
|
+
return { kind: "reuse", agent: record.agent };
|
|
19199
|
+
const status = Number(record.status);
|
|
19200
|
+
if (status === AGENT_STATUS.None || status === AGENT_STATUS.Revoked) {
|
|
19201
|
+
return { kind: "register" };
|
|
19202
|
+
}
|
|
19203
|
+
if (status !== AGENT_STATUS.Active) {
|
|
19204
|
+
throw new Error(`Unknown agent status ${status} in AgentDelegationRegistry`);
|
|
19205
|
+
}
|
|
19206
|
+
if (Number(record.validUntil) > nowSeconds) {
|
|
19207
|
+
throw new Error(
|
|
19208
|
+
"AgentDelegationRegistry reports an unexpired agent as inactive \u2014 the registry is paused. Agent delegation cannot be changed until it is unpaused."
|
|
19209
|
+
);
|
|
19210
|
+
}
|
|
19211
|
+
return { kind: "rotate" };
|
|
19212
|
+
}
|
|
19213
|
+
|
|
19214
|
+
// src/utils/assert-provider-chain.ts
|
|
19215
|
+
async function describeProviderChainMismatch(provider, chainId) {
|
|
19216
|
+
const actual = Number((await provider.getNetwork()).chainId);
|
|
19217
|
+
if (actual === chainId)
|
|
19218
|
+
return null;
|
|
19219
|
+
return `Network mismatch: the SDK was configured for chainId ${chainId}, but the provider is connected to chainId ${actual}. Switch the wallet/RPC to chainId ${chainId}, or configure the SDK for chainId ${actual}. Nothing was signed.`;
|
|
19220
|
+
}
|
|
19221
|
+
|
|
19222
|
+
// src/utils/sign-guard/psm-exchange.ts
|
|
19223
|
+
var import_ethers20 = require("ethers");
|
|
19224
|
+
|
|
19225
|
+
// src/utils/sign-guard/errors.ts
|
|
19226
|
+
var TxValidationError = class extends Error {
|
|
19227
|
+
/**
|
|
19228
|
+
* @param subject What is being refused. Defaults to the server-returned transaction the
|
|
19229
|
+
* validator exists for; client-built flows (the PSM exchange) name themselves instead.
|
|
19230
|
+
*/
|
|
19231
|
+
constructor(msg, subject = "server-returned transaction") {
|
|
19232
|
+
super(`Refusing to sign ${subject}: ${msg}`);
|
|
19233
|
+
this.name = "TxValidationError";
|
|
19234
|
+
}
|
|
19235
|
+
};
|
|
19236
|
+
|
|
19237
|
+
// src/utils/sign-guard/signable-functions.ts
|
|
19238
|
+
var import_ethers17 = require("ethers");
|
|
19239
|
+
var SIGNABLE_FUNCTIONS = [
|
|
19240
|
+
"function mintUCD(bytes32 positionId, uint256 mintAmount, uint256 mintFee, uint256 newDebt, uint256 newCollateral, uint256 btcPrice, bytes32 authorizedSpendsHash, bytes32 ucdDebtHash, bytes32 contractHash, uint256 quantumTimestamp, bytes calldata mintValidatorSignature) external returns (bool)",
|
|
19241
|
+
// `makePayment` is the ONLY repayment entry point. A `repayPosition` entry
|
|
19242
|
+
// used to sit here; it is not a function on any deployed contract — the
|
|
19243
|
+
// compiled PositionManager ABI has no `repay*` function at all, and the name
|
|
19244
|
+
// survives only in `archive/` prototypes. Nothing in lit-ops-server, the
|
|
19245
|
+
// lit-actions, or the SDK ever emitted it, and AgentModule never whitelisted
|
|
19246
|
+
// it. A signable-function whitelist should not carry calldata shapes the
|
|
19247
|
+
// protocol cannot execute.
|
|
19248
|
+
"function makePayment(bytes32 positionId, uint256 paymentAmount, uint256 quantumTimestamp, uint256 btcPrice, bytes calldata paymentValidatorSignature) external returns (bool)",
|
|
19249
|
+
"function extendPosition(bytes32 positionId, uint256 selectedTerm, uint256 quantumTimestamp, uint256 btcPrice, uint256 availableBTCBalance, uint256 proRataRenewalFee, bytes calldata extensionValidatorSignature) external returns (bool)",
|
|
19250
|
+
"function extendPosition(bytes32 positionId, uint256 selectedTerm, uint256 quantumTimestamp, uint256 btcPrice, uint256 availableBTCBalance, bytes calldata extensionValidatorSignature) external returns (bool)",
|
|
19251
|
+
// Struct-version: matches the actual on-chain function signature and the
|
|
19252
|
+
// SDK's calldata builder. An earlier 3-arg declaration was stale and would
|
|
19253
|
+
// silently fail to decode real `withdrawBTC` calldata. networkFee is NOT a
|
|
19254
|
+
// contract param — it is an off-chain Phase-2 BTC tx fee, validated elsewhere.
|
|
19255
|
+
"function withdrawBTC((bytes32 positionId, bytes32 actionHash, bytes32 authorizedSpendsHash, bytes32 ucdDebtHash, bytes32 contractBundleHash, string withdrawalAddress, uint256 totalDeduction, uint256 newCollateral, uint256 quantumTimestamp, uint256 btcPrice, string utxoTxid, uint32 utxoVout) params, bytes withdrawalValidatorSignature, bytes btcSpendAuthSignature) external returns (bool)",
|
|
19256
|
+
// `cancelStaleSpendByOwner` removed in audit #62210 follow-up — the on-chain
|
|
19257
|
+
// function reverts permanently. The trustless replacement
|
|
19258
|
+
// (`cancelStaleSpendWithProof` on BTCSpendAuthorizer) lands via a separate
|
|
19259
|
+
// entry when its LIT Action ships.
|
|
19260
|
+
"function approve(address spender, uint256 amount) external returns (bool)",
|
|
19261
|
+
"function setPositionDelegate(bytes32 positionId, address newDelegate) external",
|
|
19262
|
+
// Position creation. Signed opaquely by the SDK (like withdrawBTC), so
|
|
19263
|
+
// without an entry here it decoded as "<unparseable>" and the CLI's
|
|
19264
|
+
// WithdrawGuard waved it through — the flow that locks the user's BTC
|
|
19265
|
+
// collateral had no client-side pin at all.
|
|
19266
|
+
"function createPosition(bytes32 pkpId, bytes calldata validatorSignature, string mainnetVaultAddress, string regtestVaultAddress, uint256 selectedTermMonths, uint256 validatorVersion, bytes calldata pkpPublicKey) external returns (bytes32 positionId)",
|
|
19267
|
+
// SimplePSMV2 — the PSM exchange pair. Direct EOA calls, never routed through
|
|
19268
|
+
// AgentModule; the parity test documents them as client-only.
|
|
19269
|
+
"function swap(address stablecoin, uint256 amountIn, uint256 minUcdOut) external returns (uint256)",
|
|
19270
|
+
"function redeem(address stablecoin, uint256 ucdAmount, uint256 minStablecoinOut) external returns (uint256)",
|
|
19271
|
+
// BitcoinWithdrawalAddressRegistry — `msg.sender` must be the borrower, so
|
|
19272
|
+
// this too is client-only and never module-whitelisted.
|
|
19273
|
+
"function addAddress(string btcAddress) external"
|
|
19274
|
+
];
|
|
19275
|
+
var IFACE = new import_ethers17.ethers.Interface(SIGNABLE_FUNCTIONS);
|
|
19276
|
+
function encodeSignable(fn, args) {
|
|
19277
|
+
return IFACE.encodeFunctionData(fn, args);
|
|
19278
|
+
}
|
|
19279
|
+
function decodeSignable(data) {
|
|
19280
|
+
try {
|
|
19281
|
+
const parsed = IFACE.parseTransaction({ data });
|
|
19282
|
+
if (!parsed) {
|
|
19283
|
+
throw new TxValidationError(
|
|
19284
|
+
`unsignedTx.data does not decode against the signable-function whitelist`
|
|
19285
|
+
);
|
|
19286
|
+
}
|
|
19287
|
+
return { name: parsed.name, args: parsed.args };
|
|
19288
|
+
} catch (e) {
|
|
19289
|
+
if (e instanceof TxValidationError)
|
|
19290
|
+
throw e;
|
|
19291
|
+
throw new TxValidationError(
|
|
19292
|
+
`unsignedTx.data does not decode against the signable-function whitelist: ${e.message}`
|
|
19293
|
+
);
|
|
19294
|
+
}
|
|
19295
|
+
}
|
|
19296
|
+
|
|
19297
|
+
// src/utils/sign-guard/tx-validator.ts
|
|
19298
|
+
var import_ethers19 = require("ethers");
|
|
19299
|
+
|
|
19300
|
+
// src/utils/sign-guard/fee-ceiling.ts
|
|
19301
|
+
var import_ethers18 = require("ethers");
|
|
19302
|
+
var MAX_GAS_LIMIT = 5000000n;
|
|
19303
|
+
var MAX_FEE_PER_GAS_WEI = 2000000000000n;
|
|
19304
|
+
var DEFAULT_MAX_TX_FEE_WEI = 250000000000000000n;
|
|
19305
|
+
var DEFAULT_FEE_CEILING = {
|
|
19306
|
+
maxGasLimit: MAX_GAS_LIMIT,
|
|
19307
|
+
maxFeePerGasWei: MAX_FEE_PER_GAS_WEI,
|
|
19308
|
+
maxTxFeeWei: DEFAULT_MAX_TX_FEE_WEI
|
|
19309
|
+
};
|
|
19310
|
+
function fmtEth(wei) {
|
|
19311
|
+
return `${import_ethers18.ethers.formatEther(wei)} ETH`;
|
|
19312
|
+
}
|
|
19313
|
+
function fmtGwei(wei) {
|
|
19314
|
+
return `${import_ethers18.ethers.formatUnits(wei, "gwei")} gwei`;
|
|
19315
|
+
}
|
|
19316
|
+
function worstCaseFeeWei(gasLimit, maxFeePerGas) {
|
|
19317
|
+
if (gasLimit == null || maxFeePerGas == null)
|
|
19318
|
+
return null;
|
|
19319
|
+
return gasLimit * maxFeePerGas;
|
|
19320
|
+
}
|
|
19321
|
+
function assertFeeCeiling(gasLimit, maxFeePerGas, raise, ceiling = DEFAULT_FEE_CEILING) {
|
|
19322
|
+
if (gasLimit != null && gasLimit > ceiling.maxGasLimit) {
|
|
19323
|
+
raise(`gasLimit ${gasLimit} exceeds the signing ceiling of ${ceiling.maxGasLimit}`);
|
|
19324
|
+
}
|
|
19325
|
+
if (maxFeePerGas != null && maxFeePerGas > ceiling.maxFeePerGasWei) {
|
|
19326
|
+
raise(
|
|
19327
|
+
`maxFeePerGas ${fmtGwei(maxFeePerGas)} exceeds the signing ceiling of ${fmtGwei(ceiling.maxFeePerGasWei)}`
|
|
19328
|
+
);
|
|
19329
|
+
}
|
|
19330
|
+
const worst = worstCaseFeeWei(gasLimit, maxFeePerGas);
|
|
19331
|
+
if (worst != null && worst > ceiling.maxTxFeeWei) {
|
|
19332
|
+
raise(
|
|
19333
|
+
`worst-case transaction fee ${fmtEth(worst)} (gasLimit ${gasLimit} \xD7 ${fmtGwei(maxFeePerGas)}) exceeds the signing ceiling of ${fmtEth(ceiling.maxTxFeeWei)}.${ceiling.raiseHint ? ` ${ceiling.raiseHint}` : ""}`
|
|
19334
|
+
);
|
|
19335
|
+
}
|
|
19336
|
+
}
|
|
19337
|
+
|
|
19338
|
+
// src/utils/sign-guard/tx-validator.ts
|
|
19339
|
+
function eqAddr(a, b) {
|
|
19340
|
+
if (!a || !b)
|
|
19341
|
+
return false;
|
|
19342
|
+
try {
|
|
19343
|
+
return import_ethers19.ethers.getAddress(a) === import_ethers19.ethers.getAddress(b);
|
|
19344
|
+
} catch {
|
|
19345
|
+
return false;
|
|
19346
|
+
}
|
|
19347
|
+
}
|
|
19348
|
+
function readToAddress(tx) {
|
|
19349
|
+
const to = tx["to"];
|
|
19350
|
+
if (typeof to !== "string") {
|
|
19351
|
+
throw new TxValidationError(`unsignedTx.to is missing or not a string`);
|
|
19352
|
+
}
|
|
19353
|
+
return to;
|
|
19354
|
+
}
|
|
19355
|
+
function readData(tx) {
|
|
19356
|
+
const data = tx["data"];
|
|
19357
|
+
if (typeof data !== "string" || !data.startsWith("0x")) {
|
|
19358
|
+
throw new TxValidationError(`unsignedTx.data missing or malformed`);
|
|
19359
|
+
}
|
|
19360
|
+
return data;
|
|
19361
|
+
}
|
|
19362
|
+
function readChainId(tx) {
|
|
19363
|
+
const c = tx["chainId"];
|
|
19364
|
+
if (c == null)
|
|
19365
|
+
return null;
|
|
19366
|
+
if (typeof c === "number")
|
|
19367
|
+
return c;
|
|
19368
|
+
if (typeof c === "string")
|
|
19369
|
+
return parseInt(c.startsWith("0x") ? c.slice(2) : c, c.startsWith("0x") ? 16 : 10);
|
|
19370
|
+
if (typeof c === "bigint")
|
|
19371
|
+
return Number(c);
|
|
19372
|
+
return null;
|
|
19373
|
+
}
|
|
19374
|
+
function readOptionalBigInt(tx, key) {
|
|
19375
|
+
const v = tx[key];
|
|
19376
|
+
if (v == null)
|
|
19377
|
+
return null;
|
|
19378
|
+
try {
|
|
19379
|
+
if (typeof v === "bigint")
|
|
19380
|
+
return v;
|
|
19381
|
+
if (typeof v === "number")
|
|
19382
|
+
return BigInt(v);
|
|
19383
|
+
if (typeof v === "string")
|
|
19384
|
+
return BigInt(v);
|
|
19385
|
+
return BigInt(v.toString());
|
|
19386
|
+
} catch {
|
|
19387
|
+
throw new TxValidationError(`unsignedTx.${key} is present but not a valid integer`);
|
|
19388
|
+
}
|
|
19389
|
+
}
|
|
19390
|
+
function readValue(tx) {
|
|
19391
|
+
const v = tx["value"];
|
|
19392
|
+
if (v == null)
|
|
19393
|
+
return 0n;
|
|
19394
|
+
if (typeof v === "string")
|
|
19395
|
+
return BigInt(v);
|
|
19396
|
+
if (typeof v === "number")
|
|
19397
|
+
return BigInt(v);
|
|
19398
|
+
if (typeof v === "bigint")
|
|
19399
|
+
return v;
|
|
19400
|
+
const maybe = v;
|
|
19401
|
+
if (typeof maybe.toBigInt === "function")
|
|
19402
|
+
return maybe.toBigInt();
|
|
19403
|
+
if (typeof maybe.toString === "function")
|
|
19404
|
+
return BigInt(maybe.toString());
|
|
19405
|
+
throw new TxValidationError(`unsignedTx.value has unrecognized type`);
|
|
19406
|
+
}
|
|
19407
|
+
function normalizePositionId(id) {
|
|
19408
|
+
const hex = id.startsWith("0x") ? id : `0x${id}`;
|
|
19409
|
+
return import_ethers19.ethers.zeroPadValue(hex, 32).toLowerCase();
|
|
19410
|
+
}
|
|
19411
|
+
function requirePositionId(actual, expected) {
|
|
19412
|
+
const txPositionId = String(actual).toLowerCase();
|
|
19413
|
+
if (txPositionId !== normalizePositionId(expected)) {
|
|
19414
|
+
throw new TxValidationError(
|
|
19415
|
+
`positionId mismatch \u2014 tx ${txPositionId}, expected ${normalizePositionId(expected)}`
|
|
19416
|
+
);
|
|
19417
|
+
}
|
|
19418
|
+
}
|
|
19419
|
+
function validateUnsignedTx(unsignedTx, expected, vctx) {
|
|
19420
|
+
const txChainId = readChainId(unsignedTx);
|
|
19421
|
+
if (txChainId == null) {
|
|
19422
|
+
throw new TxValidationError(
|
|
19423
|
+
`unsignedTx.chainId is missing \u2014 refusing to sign a transaction with no chain binding (client is on ${vctx.chainId})`
|
|
19424
|
+
);
|
|
19425
|
+
}
|
|
19426
|
+
if (txChainId !== vctx.chainId) {
|
|
19427
|
+
throw new TxValidationError(
|
|
19428
|
+
`chainId mismatch \u2014 tx says ${txChainId}, client is on ${vctx.chainId}`
|
|
19429
|
+
);
|
|
19430
|
+
}
|
|
19431
|
+
const value = readValue(unsignedTx);
|
|
19432
|
+
if (value !== 0n) {
|
|
19433
|
+
throw new TxValidationError(
|
|
19434
|
+
`unsignedTx.value is non-zero (${value}); none of the protocol functions accept ETH`
|
|
19435
|
+
);
|
|
19436
|
+
}
|
|
19437
|
+
assertFeeCeiling(
|
|
19438
|
+
readOptionalBigInt(unsignedTx, "gasLimit"),
|
|
19439
|
+
readOptionalBigInt(unsignedTx, "maxFeePerGas") ?? readOptionalBigInt(unsignedTx, "gasPrice"),
|
|
19440
|
+
(msg) => {
|
|
19441
|
+
throw new TxValidationError(msg);
|
|
19442
|
+
},
|
|
19443
|
+
vctx.feeCeiling
|
|
19444
|
+
);
|
|
19445
|
+
const to = readToAddress(unsignedTx);
|
|
19446
|
+
const data = readData(unsignedTx);
|
|
19447
|
+
const { name, args } = decodeSignable(data);
|
|
19448
|
+
const pm = vctx.contracts.PositionManager;
|
|
19449
|
+
switch (expected.kind) {
|
|
19450
|
+
case "mint": {
|
|
19451
|
+
if (name !== "mintUCD") {
|
|
19452
|
+
throw new TxValidationError(`expected mintUCD, server returned ${name}`);
|
|
19453
|
+
}
|
|
19454
|
+
if (!eqAddr(to, pm)) {
|
|
19455
|
+
throw new TxValidationError(`mintUCD must target PositionManager (${pm}), got ${to}`);
|
|
19456
|
+
}
|
|
19457
|
+
requirePositionId(args[0], expected.positionId);
|
|
19458
|
+
const txMintAmount = BigInt(String(args[1]));
|
|
19459
|
+
if (txMintAmount.toString() !== expected.amountWei) {
|
|
19460
|
+
throw new TxValidationError(`mintAmount mismatch \u2014 tx ${txMintAmount}, expected ${expected.amountWei}`);
|
|
19461
|
+
}
|
|
19462
|
+
const txMintFee = BigInt(String(args[2]));
|
|
19463
|
+
if (txMintFee.toString() !== expected.mintFeeWei) {
|
|
19464
|
+
throw new TxValidationError(`mintFee mismatch \u2014 tx ${txMintFee}, expected ${expected.mintFeeWei}`);
|
|
19465
|
+
}
|
|
19466
|
+
break;
|
|
19467
|
+
}
|
|
19468
|
+
case "repay": {
|
|
19469
|
+
if (name !== "makePayment") {
|
|
19470
|
+
throw new TxValidationError(`expected makePayment, server returned ${name}`);
|
|
19471
|
+
}
|
|
19472
|
+
if (!eqAddr(to, pm)) {
|
|
19473
|
+
throw new TxValidationError(`${name} must target PositionManager (${pm}), got ${to}`);
|
|
19474
|
+
}
|
|
19475
|
+
requirePositionId(args[0], expected.positionId);
|
|
19476
|
+
const txAmount = BigInt(String(args[1]));
|
|
19477
|
+
if (txAmount.toString() !== expected.amountWei) {
|
|
19478
|
+
throw new TxValidationError(`paymentAmount mismatch \u2014 tx ${txAmount}, expected ${expected.amountWei}`);
|
|
19479
|
+
}
|
|
19480
|
+
break;
|
|
19481
|
+
}
|
|
19482
|
+
case "extend": {
|
|
19483
|
+
if (name !== "extendPosition") {
|
|
19484
|
+
throw new TxValidationError(`expected extendPosition, server returned ${name}`);
|
|
19485
|
+
}
|
|
19486
|
+
if (!eqAddr(to, pm)) {
|
|
19487
|
+
throw new TxValidationError(`extendPosition must target PositionManager (${pm}), got ${to}`);
|
|
19488
|
+
}
|
|
19489
|
+
requirePositionId(args[0], expected.positionId);
|
|
19490
|
+
const txTerm = Number(args[1]);
|
|
19491
|
+
if (txTerm !== expected.selectedTerm) {
|
|
19492
|
+
throw new TxValidationError(`selectedTerm mismatch \u2014 tx ${txTerm}, expected ${expected.selectedTerm}`);
|
|
19493
|
+
}
|
|
19494
|
+
if (expected.upperBoundProRataFeeWei != null && args.length >= 7) {
|
|
19495
|
+
const txFee = BigInt(String(args[5]));
|
|
19496
|
+
const upper = BigInt(expected.upperBoundProRataFeeWei);
|
|
19497
|
+
if (txFee > upper) {
|
|
19498
|
+
throw new TxValidationError(
|
|
19499
|
+
`proRataRenewalFee=${txFee} exceeds upper bound=${upper}. A compromised validator could otherwise inflate the fee to drain UCD.`
|
|
19500
|
+
);
|
|
19501
|
+
}
|
|
19502
|
+
}
|
|
19503
|
+
break;
|
|
19504
|
+
}
|
|
19505
|
+
case "withdraw-btc": {
|
|
19506
|
+
if (name !== "withdrawBTC") {
|
|
19507
|
+
throw new TxValidationError(`expected withdrawBTC, server returned ${name}`);
|
|
19508
|
+
}
|
|
19509
|
+
if (!eqAddr(to, pm)) {
|
|
19510
|
+
throw new TxValidationError(`withdrawBTC must target PositionManager (${pm}), got ${to}`);
|
|
19511
|
+
}
|
|
19512
|
+
const paramsTuple = args[0];
|
|
19513
|
+
requirePositionId(paramsTuple[0], expected.positionId);
|
|
19514
|
+
const txWithdrawalAddress = String(paramsTuple[5]);
|
|
19515
|
+
if (txWithdrawalAddress !== expected.btcAddress) {
|
|
19516
|
+
throw new TxValidationError(`btcAddress mismatch \u2014 tx '${txWithdrawalAddress}', expected '${expected.btcAddress}'`);
|
|
19517
|
+
}
|
|
19518
|
+
if (expected.upperBoundTotalDeduction != null) {
|
|
19519
|
+
const txDeduction = BigInt(String(paramsTuple[6]));
|
|
19520
|
+
const upper = BigInt(expected.upperBoundTotalDeduction);
|
|
19521
|
+
if (txDeduction > upper) {
|
|
19522
|
+
throw new TxValidationError(
|
|
19523
|
+
`totalDeduction=${txDeduction} exceeds approved upper bound=${upper}. A compromised validator could otherwise drain collateral beyond the approved amount.`
|
|
19524
|
+
);
|
|
19525
|
+
}
|
|
19526
|
+
}
|
|
19527
|
+
break;
|
|
19528
|
+
}
|
|
19529
|
+
case "ucd-approve": {
|
|
19530
|
+
if (name !== "approve") {
|
|
19531
|
+
throw new TxValidationError(`expected ERC-20 approve, server returned ${name}`);
|
|
19532
|
+
}
|
|
19533
|
+
const ucd = vctx.contracts.UCDToken;
|
|
19534
|
+
if (!eqAddr(to, ucd)) {
|
|
19535
|
+
throw new TxValidationError(`approve must target UCDToken (${ucd}), got ${to}`);
|
|
19536
|
+
}
|
|
19537
|
+
const spender = String(args[0]);
|
|
19538
|
+
const ok = expected.spenderCandidates.some((s) => eqAddr(spender, s));
|
|
19539
|
+
if (!ok) {
|
|
19540
|
+
throw new TxValidationError(
|
|
19541
|
+
`approve spender ${spender} not in expected set ${expected.spenderCandidates.join(", ")}`
|
|
19542
|
+
);
|
|
19543
|
+
}
|
|
19544
|
+
const amt = BigInt(String(args[1]));
|
|
19545
|
+
const min = BigInt(expected.minAmountWei);
|
|
19546
|
+
if (amt < min) {
|
|
19547
|
+
throw new TxValidationError(`approve amount ${amt} is less than required ${min}`);
|
|
19548
|
+
}
|
|
19549
|
+
const SANITY_MAX = min * 1000n;
|
|
19550
|
+
if (amt > SANITY_MAX) {
|
|
19551
|
+
throw new TxValidationError(`approve amount ${amt} exceeds sanity cap ${SANITY_MAX} (1000x requested)`);
|
|
19552
|
+
}
|
|
19553
|
+
break;
|
|
19554
|
+
}
|
|
19555
|
+
case "create-position": {
|
|
19556
|
+
if (name !== "createPosition") {
|
|
19557
|
+
throw new TxValidationError(`expected createPosition, server returned ${name}`);
|
|
19558
|
+
}
|
|
19559
|
+
if (!eqAddr(to, pm)) {
|
|
19560
|
+
throw new TxValidationError(`createPosition must target PositionManager (${pm}), got ${to}`);
|
|
19561
|
+
}
|
|
19562
|
+
const txTerm = BigInt(String(args[4]));
|
|
19563
|
+
if (txTerm !== BigInt(expected.selectedTerm)) {
|
|
19564
|
+
throw new TxValidationError(`selectedTerm mismatch \u2014 tx ${txTerm}, expected ${expected.selectedTerm}`);
|
|
19565
|
+
}
|
|
19566
|
+
break;
|
|
19567
|
+
}
|
|
19568
|
+
case "set-position-delegate": {
|
|
19569
|
+
if (name !== "setPositionDelegate") {
|
|
19570
|
+
throw new TxValidationError(`expected setPositionDelegate, server returned ${name}`);
|
|
19571
|
+
}
|
|
19572
|
+
if (!eqAddr(to, expected.registryAddress)) {
|
|
19573
|
+
throw new TxValidationError(
|
|
19574
|
+
`setPositionDelegate must target PositionDelegateRegistry (${expected.registryAddress}), got ${to}`
|
|
19575
|
+
);
|
|
19576
|
+
}
|
|
19577
|
+
requirePositionId(args[0], expected.positionId);
|
|
19578
|
+
const txDelegate = String(args[1]);
|
|
19579
|
+
if (!eqAddr(txDelegate, expected.delegate)) {
|
|
19580
|
+
throw new TxValidationError(`delegate mismatch \u2014 tx ${txDelegate}, expected ${expected.delegate}`);
|
|
19581
|
+
}
|
|
19582
|
+
break;
|
|
19583
|
+
}
|
|
19584
|
+
case "bwar-add-address": {
|
|
19585
|
+
if (name !== "addAddress") {
|
|
19586
|
+
throw new TxValidationError(`expected addAddress, decoded ${name}`);
|
|
19587
|
+
}
|
|
19588
|
+
if (!eqAddr(to, expected.registryAddress)) {
|
|
19589
|
+
throw new TxValidationError(
|
|
19590
|
+
`addAddress must target the BitcoinWithdrawalAddressRegistry (${expected.registryAddress}), got ${to}`
|
|
19591
|
+
);
|
|
19592
|
+
}
|
|
19593
|
+
if (String(args[0]) !== expected.btcAddress) {
|
|
19594
|
+
throw new TxValidationError(
|
|
19595
|
+
`btcAddress mismatch \u2014 tx says '${String(args[0])}', user asked for '${expected.btcAddress}'`
|
|
19596
|
+
);
|
|
19597
|
+
}
|
|
19598
|
+
break;
|
|
19599
|
+
}
|
|
19600
|
+
case "stablecoin-approve": {
|
|
19601
|
+
if (name !== "approve") {
|
|
19602
|
+
throw new TxValidationError(`expected ERC-20 approve, server returned ${name}`);
|
|
19603
|
+
}
|
|
19604
|
+
if (!eqAddr(to, expected.tokenAddress)) {
|
|
19605
|
+
throw new TxValidationError(`approve must target the stablecoin token (${expected.tokenAddress}), got ${to}`);
|
|
19606
|
+
}
|
|
19607
|
+
if (!eqAddr(String(args[0]), expected.spender)) {
|
|
19608
|
+
throw new TxValidationError(`approve spender must be the PSM (${expected.spender}), got ${String(args[0])}`);
|
|
19609
|
+
}
|
|
19610
|
+
if (BigInt(String(args[1])).toString() !== expected.amountUnits) {
|
|
19611
|
+
throw new TxValidationError(
|
|
19612
|
+
`approve amount must be EXACTLY ${expected.amountUnits} (exact-amount rule), got ${BigInt(String(args[1]))}`
|
|
19613
|
+
);
|
|
19614
|
+
}
|
|
19615
|
+
break;
|
|
19616
|
+
}
|
|
19617
|
+
case "ucd-approve-controller": {
|
|
19618
|
+
if (name !== "approve") {
|
|
19619
|
+
throw new TxValidationError(`expected ERC-20 approve, server returned ${name}`);
|
|
19620
|
+
}
|
|
19621
|
+
if (!eqAddr(to, expected.ucdTokenAddress)) {
|
|
19622
|
+
throw new TxValidationError(`approve must target UCDToken (${expected.ucdTokenAddress}), got ${to}`);
|
|
19623
|
+
}
|
|
19624
|
+
if (!eqAddr(String(args[0]), expected.spender)) {
|
|
19625
|
+
throw new TxValidationError(`approve spender must be the UCDController (${expected.spender}), got ${String(args[0])}`);
|
|
19626
|
+
}
|
|
19627
|
+
if (BigInt(String(args[1])).toString() !== expected.amountWei) {
|
|
19628
|
+
throw new TxValidationError(
|
|
19629
|
+
`approve amount must be EXACTLY ${expected.amountWei} (exact-amount rule), got ${BigInt(String(args[1]))}`
|
|
19630
|
+
);
|
|
19631
|
+
}
|
|
19632
|
+
break;
|
|
19633
|
+
}
|
|
19634
|
+
case "psm-swap": {
|
|
19635
|
+
if (name !== "swap")
|
|
19636
|
+
throw new TxValidationError(`expected swap, server returned ${name}`);
|
|
19637
|
+
if (!eqAddr(to, expected.psmAddress)) {
|
|
19638
|
+
throw new TxValidationError(`swap must target the PSM (${expected.psmAddress}), got ${to}`);
|
|
19639
|
+
}
|
|
19640
|
+
if (!eqAddr(String(args[0]), expected.stablecoin))
|
|
19641
|
+
throw new TxValidationError(`swap stablecoin mismatch`);
|
|
19642
|
+
if (BigInt(String(args[1])).toString() !== expected.amountIn)
|
|
19643
|
+
throw new TxValidationError(`swap amountIn mismatch`);
|
|
19644
|
+
if (BigInt(String(args[2])).toString() !== expected.minOut)
|
|
19645
|
+
throw new TxValidationError(`swap minUcdOut mismatch`);
|
|
19646
|
+
if (BigInt(expected.minOut) === 0n) {
|
|
19647
|
+
throw new TxValidationError(`swap minUcdOut is 0 \u2014 a slippage floor is mandatory (SlippageProtectionRequired)`);
|
|
19648
|
+
}
|
|
19649
|
+
break;
|
|
19650
|
+
}
|
|
19651
|
+
case "psm-redeem": {
|
|
19652
|
+
if (name !== "redeem")
|
|
19653
|
+
throw new TxValidationError(`expected redeem, server returned ${name}`);
|
|
19654
|
+
if (!eqAddr(to, expected.psmAddress)) {
|
|
19655
|
+
throw new TxValidationError(`redeem must target the PSM (${expected.psmAddress}), got ${to}`);
|
|
19656
|
+
}
|
|
19657
|
+
if (!eqAddr(String(args[0]), expected.stablecoin))
|
|
19658
|
+
throw new TxValidationError(`redeem stablecoin mismatch`);
|
|
19659
|
+
if (BigInt(String(args[1])).toString() !== expected.amountUcdWei)
|
|
19660
|
+
throw new TxValidationError(`redeem ucdAmount mismatch`);
|
|
19661
|
+
if (BigInt(String(args[2])).toString() !== expected.minOut)
|
|
19662
|
+
throw new TxValidationError(`redeem minStablecoinOut mismatch`);
|
|
19663
|
+
if (BigInt(expected.minOut) === 0n) {
|
|
19664
|
+
throw new TxValidationError(`redeem minStablecoinOut is 0 \u2014 a slippage floor is mandatory (SlippageProtectionRequired)`);
|
|
19665
|
+
}
|
|
19666
|
+
break;
|
|
19667
|
+
}
|
|
19668
|
+
default: {
|
|
19669
|
+
const _exhaustive = expected;
|
|
19670
|
+
throw new TxValidationError(`unhandled expected action kind: ${JSON.stringify(_exhaustive)}`);
|
|
19671
|
+
}
|
|
19672
|
+
}
|
|
19673
|
+
}
|
|
19674
|
+
|
|
19675
|
+
// src/utils/sign-guard/psm-exchange.ts
|
|
19676
|
+
function buildPsmApproveTx(p) {
|
|
19677
|
+
return {
|
|
19678
|
+
to: p.token,
|
|
19679
|
+
data: encodeSignable("approve", [p.spender, p.amount]),
|
|
19680
|
+
value: "0x0",
|
|
19681
|
+
chainId: p.chainId
|
|
19682
|
+
};
|
|
19683
|
+
}
|
|
19684
|
+
function buildPsmSwapTx(p) {
|
|
19685
|
+
return {
|
|
19686
|
+
to: p.psm,
|
|
19687
|
+
data: encodeSignable("swap", [p.stablecoin, p.amountIn, p.minOut]),
|
|
19688
|
+
value: "0x0",
|
|
19689
|
+
chainId: p.chainId
|
|
19690
|
+
};
|
|
19691
|
+
}
|
|
19692
|
+
function buildPsmRedeemTx(p) {
|
|
19693
|
+
return {
|
|
19694
|
+
to: p.psm,
|
|
19695
|
+
data: encodeSignable("redeem", [p.stablecoin, p.ucdAmount, p.minOut]),
|
|
19696
|
+
value: "0x0",
|
|
19697
|
+
chainId: p.chainId
|
|
19698
|
+
};
|
|
19699
|
+
}
|
|
19700
|
+
var PSM_READS_ABI = ["function supportedStablecoins(address) view returns (bool)"];
|
|
19701
|
+
var ERC20_READS_ABI = ["function allowance(address owner, address spender) view returns (uint256)"];
|
|
19702
|
+
function psmExchangeReadsFromProvider(provider, psmAddress) {
|
|
19703
|
+
const psm = new import_ethers20.ethers.Contract(psmAddress, PSM_READS_ABI, provider);
|
|
19704
|
+
return {
|
|
19705
|
+
isStablecoinSupported: async (stablecoin) => await psm.getFunction("supportedStablecoins")(stablecoin),
|
|
19706
|
+
allowance: async (token, owner, spender) => await new import_ethers20.ethers.Contract(token, ERC20_READS_ABI, provider).getFunction("allowance")(owner, spender)
|
|
19707
|
+
};
|
|
19708
|
+
}
|
|
19709
|
+
var refuse = (direction, msg) => new TxValidationError(msg, `PSM ${direction}`);
|
|
19710
|
+
function requireAddress(direction, label, value) {
|
|
19711
|
+
if (!value || !import_ethers20.ethers.isAddress(value)) {
|
|
19712
|
+
throw refuse(direction, `${label} address is missing or invalid (${String(value)})`);
|
|
19713
|
+
}
|
|
19714
|
+
return import_ethers20.ethers.getAddress(value);
|
|
19715
|
+
}
|
|
19716
|
+
async function planPsmExchange(params) {
|
|
19717
|
+
const { direction, amountIn, minOut, vctx, reads } = params;
|
|
19718
|
+
const chainId = vctx.chainId;
|
|
19719
|
+
if (amountIn <= 0n) {
|
|
19720
|
+
throw refuse(direction, `amount must be greater than zero (got ${amountIn})`);
|
|
19721
|
+
}
|
|
19722
|
+
if (minOut <= 0n) {
|
|
19723
|
+
throw refuse(direction, `minimum-out floor must be greater than zero (got ${minOut}) \u2014 a zero floor is refused on-chain`);
|
|
19724
|
+
}
|
|
19725
|
+
const owner = requireAddress(direction, "owner", params.owner);
|
|
19726
|
+
const psm = requireAddress(direction, "SimplePSMV2", params.addresses.psm);
|
|
19727
|
+
const stablecoin = requireAddress(direction, "stablecoin", params.addresses.stablecoin);
|
|
19728
|
+
const isSwap = direction === "swap";
|
|
19729
|
+
const token = isSwap ? stablecoin : requireAddress(direction, "UCDToken", params.addresses.ucdToken);
|
|
19730
|
+
const spender = isSwap ? psm : requireAddress(direction, "UCDController", params.addresses.ucdController);
|
|
19731
|
+
if (!await reads.isStablecoinSupported(stablecoin)) {
|
|
19732
|
+
throw refuse(direction, `stablecoin ${stablecoin} is not supported by the PSM at ${psm}`);
|
|
19733
|
+
}
|
|
19734
|
+
const approveExpected = (amount) => isSwap ? { kind: "stablecoin-approve", tokenAddress: token, spender, amountUnits: amount.toString() } : { kind: "ucd-approve-controller", ucdTokenAddress: token, spender, amountWei: amount.toString() };
|
|
19735
|
+
const allowanceBefore = await reads.allowance(token, owner, spender);
|
|
19736
|
+
const approvals = [];
|
|
19737
|
+
if (allowanceBefore < amountIn) {
|
|
19738
|
+
if (allowanceBefore > 0n) {
|
|
19739
|
+
approvals.push({
|
|
19740
|
+
step: "reset-approve",
|
|
19741
|
+
tx: buildPsmApproveTx({ token, spender, amount: 0n, chainId }),
|
|
19742
|
+
expected: approveExpected(0n)
|
|
19743
|
+
});
|
|
19744
|
+
}
|
|
19745
|
+
approvals.push({
|
|
19746
|
+
step: "approve",
|
|
19747
|
+
tx: buildPsmApproveTx({ token, spender, amount: amountIn, chainId }),
|
|
19748
|
+
expected: approveExpected(amountIn)
|
|
19749
|
+
});
|
|
19750
|
+
}
|
|
19751
|
+
const exec = isSwap ? {
|
|
19752
|
+
step: "swap",
|
|
19753
|
+
tx: buildPsmSwapTx({ psm, stablecoin, amountIn, minOut, chainId }),
|
|
19754
|
+
expected: { kind: "psm-swap", psmAddress: psm, stablecoin, amountIn: amountIn.toString(), minOut: minOut.toString() }
|
|
19755
|
+
} : {
|
|
19756
|
+
step: "redeem",
|
|
19757
|
+
tx: buildPsmRedeemTx({ psm, stablecoin, ucdAmount: amountIn, minOut, chainId }),
|
|
19758
|
+
expected: {
|
|
19759
|
+
kind: "psm-redeem",
|
|
19760
|
+
psmAddress: psm,
|
|
19761
|
+
stablecoin,
|
|
19762
|
+
amountUcdWei: amountIn.toString(),
|
|
19763
|
+
minOut: minOut.toString()
|
|
19764
|
+
}
|
|
19765
|
+
};
|
|
19766
|
+
for (const leg of [...approvals, exec]) {
|
|
19767
|
+
validateUnsignedTx({ ...leg.tx }, leg.expected, vctx);
|
|
19768
|
+
}
|
|
19769
|
+
return { direction, owner, token, spender, amountIn, minOut, allowanceBefore, approvals, exec };
|
|
19770
|
+
}
|
|
19771
|
+
async function executePsmPlan(plan, signer, vctx) {
|
|
19772
|
+
const approvalHashes = [];
|
|
19773
|
+
const residual = () => approvalHashes.length === 0 ? "" : ` An approval already landed (tx ${approvalHashes[approvalHashes.length - 1]}): an allowance of exactly ${plan.amountIn} to ${plan.spender} stands. It authorizes only that amount; the next exchange uses or replaces it.`;
|
|
19774
|
+
const sendLeg = async (leg) => {
|
|
19775
|
+
const request = { to: leg.tx.to, data: leg.tx.data, value: 0n, chainId: leg.tx.chainId };
|
|
19776
|
+
validateUnsignedTx({ ...request }, leg.expected, vctx);
|
|
19777
|
+
const sent = await signer.sendTransaction(request);
|
|
19778
|
+
const receipt = await sent.wait();
|
|
19779
|
+
if (!receipt)
|
|
19780
|
+
throw new Error(`PSM ${leg.step} tx ${sent.hash} returned no receipt`);
|
|
19781
|
+
if (receipt.status !== 1)
|
|
19782
|
+
throw new Error(`PSM ${leg.step} tx ${sent.hash} reverted`);
|
|
19783
|
+
return { hash: sent.hash, blockNumber: receipt.blockNumber };
|
|
19784
|
+
};
|
|
19785
|
+
for (const leg of plan.approvals) {
|
|
19786
|
+
const landed = await sendLeg(leg).catch((e) => {
|
|
19787
|
+
throw new Error(`PSM ${leg.step} failed: ${e instanceof Error ? e.message : String(e)}.${residual()}`);
|
|
19788
|
+
});
|
|
19789
|
+
approvalHashes.push(landed.hash);
|
|
19790
|
+
}
|
|
19791
|
+
const exec = await sendLeg(plan.exec).catch((e) => {
|
|
19792
|
+
throw new Error(`PSM ${plan.exec.step} failed: ${e instanceof Error ? e.message : String(e)}.${residual()}`);
|
|
19793
|
+
});
|
|
19794
|
+
return { ...exec, approvalHashes };
|
|
19795
|
+
}
|
|
19796
|
+
|
|
19797
|
+
// src/utils/lit-action-chain-name.ts
|
|
19798
|
+
var LIT_ACTION_CHAIN_NAMES = Object.freeze({
|
|
19799
|
+
1: "ethereum",
|
|
19800
|
+
11155111: "sepolia",
|
|
19801
|
+
1337: "hardhat",
|
|
19802
|
+
31337: "hardhat"
|
|
19803
|
+
});
|
|
19804
|
+
function litActionChainNameForChainId(chainId) {
|
|
19805
|
+
const name = LIT_ACTION_CHAIN_NAMES[Number(chainId)];
|
|
19806
|
+
if (!name) {
|
|
19807
|
+
throw new Error(
|
|
19808
|
+
`Unsupported chainId ${String(chainId)} for a Lit Action (supported: ${Object.keys(LIT_ACTION_CHAIN_NAMES).join(", ")})`
|
|
19809
|
+
);
|
|
19810
|
+
}
|
|
19811
|
+
return name;
|
|
19812
|
+
}
|
|
18973
19813
|
|
|
18974
19814
|
// src/modules/mock/mock-token-manager.module.ts
|
|
18975
19815
|
var MockTokenManager = class {
|
|
@@ -19723,7 +20563,7 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
19723
20563
|
*/
|
|
19724
20564
|
constructor(config) {
|
|
19725
20565
|
if (!config.provider && config.ethRpcUrl) {
|
|
19726
|
-
config.provider = new
|
|
20566
|
+
config.provider = new import_ethers21.JsonRpcProvider(config.ethRpcUrl);
|
|
19727
20567
|
}
|
|
19728
20568
|
this.config = config;
|
|
19729
20569
|
if (config.debug) {
|
|
@@ -19954,6 +20794,18 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
19954
20794
|
*/
|
|
19955
20795
|
static async create(config) {
|
|
19956
20796
|
const userProvidedServiceEndpoint = config.serviceEndpoint;
|
|
20797
|
+
if (typeof config.chainId === "number" && config.provider) {
|
|
20798
|
+
const mismatch = await describeProviderChainMismatch(config.provider, config.chainId);
|
|
20799
|
+
if (mismatch) {
|
|
20800
|
+
return failure(
|
|
20801
|
+
new SDKError({
|
|
20802
|
+
message: mismatch,
|
|
20803
|
+
category: "CONFIGURATION" /* CONFIGURATION */,
|
|
20804
|
+
severity: "HIGH" /* HIGH */
|
|
20805
|
+
})
|
|
20806
|
+
);
|
|
20807
|
+
}
|
|
20808
|
+
}
|
|
19957
20809
|
const enrichedConfig = await _DiamondHandsSDK.enrichConfigWithNetworkDefaults(config);
|
|
19958
20810
|
if (userProvidedServiceEndpoint) {
|
|
19959
20811
|
enrichedConfig.serviceEndpoint = userProvidedServiceEndpoint;
|
|
@@ -20003,7 +20855,7 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
20003
20855
|
const network = await config.provider.getNetwork();
|
|
20004
20856
|
chainId = Number(network.chainId);
|
|
20005
20857
|
} else if (config.ethRpcUrl) {
|
|
20006
|
-
const tempProvider = new
|
|
20858
|
+
const tempProvider = new import_ethers21.JsonRpcProvider(
|
|
20007
20859
|
config.ethRpcUrl
|
|
20008
20860
|
);
|
|
20009
20861
|
const network = await tempProvider.getNetwork();
|
|
@@ -20539,7 +21391,7 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
20539
21391
|
}
|
|
20540
21392
|
const loanOps = this.loanOps();
|
|
20541
21393
|
const protocolConfig = await loanOps.getProtocolConfig();
|
|
20542
|
-
const requestAmountWei = (0,
|
|
21394
|
+
const requestAmountWei = (0, import_ethers21.parseEther)(
|
|
20543
21395
|
request.amount.toString()
|
|
20544
21396
|
);
|
|
20545
21397
|
const minLoanValueWei = BigInt(protocolConfig.minimumLoanValueWei);
|
|
@@ -20594,6 +21446,53 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
20594
21446
|
if (this.config.debug) {
|
|
20595
21447
|
log.info(` Position PKP ID: ${position2.pkpId}`);
|
|
20596
21448
|
}
|
|
21449
|
+
const termsResult = await this.getTermsWithFees();
|
|
21450
|
+
if (!termsResult.success) {
|
|
21451
|
+
return {
|
|
21452
|
+
success: false,
|
|
21453
|
+
error: `Cannot verify the loan cap before minting: ${termsResult.error.message}`
|
|
21454
|
+
};
|
|
21455
|
+
}
|
|
21456
|
+
const positionTermMonths = Number(position2.selectedTerm);
|
|
21457
|
+
const positionTerm = termsResult.value.terms.find(
|
|
21458
|
+
(term) => term.termMonths === positionTermMonths
|
|
21459
|
+
);
|
|
21460
|
+
if (!positionTerm) {
|
|
21461
|
+
return {
|
|
21462
|
+
success: false,
|
|
21463
|
+
error: `Cannot verify the loan cap before minting: TermManager has no fee entry for the position's ${positionTermMonths}-month term`
|
|
21464
|
+
};
|
|
21465
|
+
}
|
|
21466
|
+
const currentDebtWei = BigInt(position2.ucdDebt);
|
|
21467
|
+
const baseFeeWei = baseMintFeeWei(
|
|
21468
|
+
requestAmountWei,
|
|
21469
|
+
positionTerm.originationFeeBps
|
|
21470
|
+
);
|
|
21471
|
+
if (debtAfterMintExceedsLoanCap({
|
|
21472
|
+
currentDebtWei,
|
|
21473
|
+
mintAmountWei: requestAmountWei,
|
|
21474
|
+
mintFeeWei: baseFeeWei,
|
|
21475
|
+
maxLoanWei: maxLoanValueWei
|
|
21476
|
+
})) {
|
|
21477
|
+
const debtAfterWei = currentDebtWei + requestAmountWei + baseFeeWei;
|
|
21478
|
+
const maxPrincipalUcd = Number(
|
|
21479
|
+
maxPrincipalWithinLoanCap({
|
|
21480
|
+
maxLoanWei: maxLoanValueWei,
|
|
21481
|
+
currentDebtWei,
|
|
21482
|
+
originationFeeBps: positionTerm.originationFeeBps
|
|
21483
|
+
}) / BigInt(10 ** 18)
|
|
21484
|
+
);
|
|
21485
|
+
return {
|
|
21486
|
+
success: false,
|
|
21487
|
+
error: `Amount ${request.amount} UCD plus the ${positionTerm.originationFeeBps / 100}% origination fee (${(0, import_ethers21.formatEther)(baseFeeWei)} UCD) would take this loan's debt to ${(0, import_ethers21.formatEther)(debtAfterWei)} UCD, above the protocol maximum of ${(0, import_ethers21.formatEther)(maxLoanValueWei)} UCD. The largest amount you can mint now is ${maxPrincipalUcd} UCD.`
|
|
21488
|
+
};
|
|
21489
|
+
}
|
|
21490
|
+
if (this.config.debug) {
|
|
21491
|
+
log.info(
|
|
21492
|
+
`\u2705 Fee-inclusive loan cap check passed: ${(0, import_ethers21.formatEther)(currentDebtWei + requestAmountWei + baseFeeWei)} UCD <= ${(0, import_ethers21.formatEther)(maxLoanValueWei)} UCD`,
|
|
21493
|
+
{}
|
|
21494
|
+
);
|
|
21495
|
+
}
|
|
20597
21496
|
let pkpPublicKey;
|
|
20598
21497
|
let pkpEthAddress;
|
|
20599
21498
|
const pkpCache = this.cacheManager.getCache("pkp-data", {
|
|
@@ -20621,7 +21520,7 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
20621
21520
|
pkpNftAddress,
|
|
20622
21521
|
this.getChipotlePublicKeyFallback()
|
|
20623
21522
|
);
|
|
20624
|
-
pkpEthAddress = (0,
|
|
21523
|
+
pkpEthAddress = (0, import_ethers21.computeAddress)(pkpPublicKey);
|
|
20625
21524
|
pkpCache.set(request.positionId, {
|
|
20626
21525
|
publicKey: pkpPublicKey,
|
|
20627
21526
|
ethAddress: pkpEthAddress,
|
|
@@ -20683,7 +21582,7 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
20683
21582
|
` UCDController: ${contracts?.UCDController || "MISSING"}`
|
|
20684
21583
|
);
|
|
20685
21584
|
}
|
|
20686
|
-
const chain = this.config.chain || (
|
|
21585
|
+
const chain = this.config.chain || litActionChainNameForChainId(network.chainId);
|
|
20687
21586
|
const devBitcoinProviderUrl = request.customBitcoinRpcUrl || this.config.bitcoinProviders?.[0]?.url;
|
|
20688
21587
|
if (this.config.debug) {
|
|
20689
21588
|
log.info(` Network: ${chain} (chainId: ${network.chainId})`);
|
|
@@ -20964,8 +21863,8 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
20964
21863
|
throw new Error(`Position not found: ${request.positionId}`);
|
|
20965
21864
|
}
|
|
20966
21865
|
const currentDebt = position.ucdDebt.toString();
|
|
20967
|
-
const ucdDebtHash = (0,
|
|
20968
|
-
const contractHash = (0,
|
|
21866
|
+
const ucdDebtHash = (0, import_ethers21.keccak256)(import_ethers21.AbiCoder.defaultAbiCoder().encode(["uint256"], [currentDebt]));
|
|
21867
|
+
const contractHash = (0, import_ethers21.keccak256)(import_ethers21.AbiCoder.defaultAbiCoder().encode(
|
|
20969
21868
|
["address", "address", "address", "address"],
|
|
20970
21869
|
[
|
|
20971
21870
|
positionManagerAddress,
|
|
@@ -21034,7 +21933,7 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
21034
21933
|
const coreAbi = [
|
|
21035
21934
|
"function getPositionDetails(bytes32) view returns (tuple(bytes32 positionId, bytes32 pkpId, uint256 ucdDebt, string vaultAddress, address borrower, uint40 createdAt, uint40 lastUpdated, uint16 selectedTerm, uint40 expiryAt, uint8 status, uint40 previousExpiryAt, uint16 totalTerm))"
|
|
21036
21935
|
];
|
|
21037
|
-
const positionCore = new
|
|
21936
|
+
const positionCore = new import_ethers21.Contract(
|
|
21038
21937
|
coreAddress,
|
|
21039
21938
|
coreAbi,
|
|
21040
21939
|
this.getSignerOrThrow().provider
|
|
@@ -21042,8 +21941,8 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
21042
21941
|
const currentPosition = await positionCore["getPositionDetails"](
|
|
21043
21942
|
positionIdBytes32
|
|
21044
21943
|
);
|
|
21045
|
-
const currentDebtHash = (0,
|
|
21046
|
-
|
|
21944
|
+
const currentDebtHash = (0, import_ethers21.keccak256)(
|
|
21945
|
+
import_ethers21.AbiCoder.defaultAbiCoder().encode(
|
|
21047
21946
|
["uint256"],
|
|
21048
21947
|
[currentPosition.ucdDebt]
|
|
21049
21948
|
)
|
|
@@ -21090,7 +21989,7 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
21090
21989
|
"function getCurrentQuantum() view returns (uint256)",
|
|
21091
21990
|
"function isQuantumValid(bytes32,uint256) view returns (bool)"
|
|
21092
21991
|
];
|
|
21093
|
-
const registry = new
|
|
21992
|
+
const registry = new import_ethers21.Contract(
|
|
21094
21993
|
registryAddress,
|
|
21095
21994
|
registryAbi,
|
|
21096
21995
|
this.getSignerOrThrow().provider
|
|
@@ -21206,7 +22105,7 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
21206
22105
|
});
|
|
21207
22106
|
}
|
|
21208
22107
|
const signatureHexMint = finalSignature.startsWith("0x") ? finalSignature : "0x" + finalSignature;
|
|
21209
|
-
const mintIface = new
|
|
22108
|
+
const mintIface = new import_ethers21.Interface(positionManagerAbi);
|
|
21210
22109
|
const mintCalldata = mintIface.encodeFunctionData("mintUCD", [
|
|
21211
22110
|
positionIdBytes32,
|
|
21212
22111
|
validationResponse.mintAmount,
|
|
@@ -21242,7 +22141,7 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
21242
22141
|
if (viewsAddress) {
|
|
21243
22142
|
let diagnosis = null;
|
|
21244
22143
|
try {
|
|
21245
|
-
const views = new
|
|
22144
|
+
const views = new import_ethers21.Contract(
|
|
21246
22145
|
viewsAddress,
|
|
21247
22146
|
[
|
|
21248
22147
|
"function diagnoseMintUCD(bytes32,uint256,uint256,uint256,uint256,uint256,bytes32,bytes32,bytes32,uint256,bytes) view returns (string)"
|
|
@@ -21283,6 +22182,25 @@ Error data: none`
|
|
|
21283
22182
|
}
|
|
21284
22183
|
const selector = simError.selector;
|
|
21285
22184
|
const errorName = simError.errorName ?? (selector ? `Unknown error ${selector}` : "unknown");
|
|
22185
|
+
const MINT_GUARD_CODES = {
|
|
22186
|
+
"1": "MINT_GUARD_LOAN_MAX_EXCEEDED \u2014 the position's total debt after this mint (existing debt + principal + fee) exceeds maximumLoanValueUcd",
|
|
22187
|
+
"2": "MINT_GUARD_CIRCUIT_BREAKER_EXCEEDED \u2014 exceeds CircuitBreaker maxSingleLoanValue",
|
|
22188
|
+
"3": "MINT_GUARD_PSM_DAILY_EXCEEDED \u2014 exceeds the PSM daily mint limit"
|
|
22189
|
+
};
|
|
22190
|
+
let loanCapDiagnostics = "";
|
|
22191
|
+
if (selector === "0xe6dd4d41") {
|
|
22192
|
+
const guardCode = BigInt(
|
|
22193
|
+
"0x" + String(simError.data).slice(10).padStart(64, "0")
|
|
22194
|
+
).toString();
|
|
22195
|
+
const guardMeaning = MINT_GUARD_CODES[guardCode] ?? `unknown MintGuardFailed code ${guardCode}`;
|
|
22196
|
+
loanCapDiagnostics = `
|
|
22197
|
+
|
|
22198
|
+
MintGuardFailed(${guardCode}): ${guardMeaning}
|
|
22199
|
+
Lit mintAmount: ${validationResponse.mintAmount}
|
|
22200
|
+
Lit mintFee: ${validationResponse.mintFee}
|
|
22201
|
+
Lit newDebt: ${validationResponse.newDebt}
|
|
22202
|
+
Protocol maximum: ${(0, import_ethers21.formatEther)(maxLoanValueWei)} UCD (fee-inclusive)`;
|
|
22203
|
+
}
|
|
21286
22204
|
const mintDebtDiagnostics = selector === "0xb9d419a7" || // DebtUpdateVerificationFailed()
|
|
21287
22205
|
selector === "0xc7e20553" || // DebtUpdateVerificationFailedDetailed(...)
|
|
21288
22206
|
selector === "0x0cfd2a97" || // MintVerificationMismatch(uint256,uint256)
|
|
@@ -21328,7 +22246,7 @@ Quantum Timing Analysis:
|
|
|
21328
22246
|
Error selector: ${selector}
|
|
21329
22247
|
Error data: ${simError.data}
|
|
21330
22248
|
Call path: mintUCD -> mintUCDWithAuthorization -> increaseDebtFromMint -> finalizeMintDebtIncrease
|
|
21331
|
-
Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
22249
|
+
Message: ${causeMessage}${quantumContext}${loanCapDiagnostics}${mintDebtDiagnostics}`
|
|
21332
22250
|
);
|
|
21333
22251
|
}
|
|
21334
22252
|
const tx = await sendEip1559Transaction({
|
|
@@ -21353,7 +22271,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
21353
22271
|
if (receipt.logs && receipt.logs.length > 0) {
|
|
21354
22272
|
for (const receiptLog of receipt.logs) {
|
|
21355
22273
|
if (receiptLog.topics && receiptLog.topics[0] === "0x08c379a0") {
|
|
21356
|
-
const iface = new
|
|
22274
|
+
const iface = new import_ethers21.Interface([
|
|
21357
22275
|
"error Error(string)"
|
|
21358
22276
|
]);
|
|
21359
22277
|
const decoded = iface.decodeErrorResult("Error", receiptLog.data);
|
|
@@ -21370,7 +22288,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
21370
22288
|
const minimalAbi = [
|
|
21371
22289
|
"event UCDMintedWithAuthorization(bytes32 indexed positionId, address indexed borrower, uint256 mintAmount, uint256 mintFee, uint256 newDebt, uint256 newCollateral, uint256 btcPrice, uint256 quantumTimestamp, bytes32 authorizedSpendsHash)"
|
|
21372
22290
|
];
|
|
21373
|
-
const iface = new
|
|
22291
|
+
const iface = new import_ethers21.Interface(minimalAbi);
|
|
21374
22292
|
for (const receiptLog of receipt.logs) {
|
|
21375
22293
|
const parsed = (() => {
|
|
21376
22294
|
const logIface = iface;
|
|
@@ -21557,7 +22475,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
21557
22475
|
pkpNftAddress,
|
|
21558
22476
|
this.getChipotlePublicKeyFallback()
|
|
21559
22477
|
);
|
|
21560
|
-
const pkpEthAddress = (0,
|
|
22478
|
+
const pkpEthAddress = (0, import_ethers21.computeAddress)(pkpPublicKey);
|
|
21561
22479
|
pkpData = {
|
|
21562
22480
|
publicKey: pkpPublicKey,
|
|
21563
22481
|
ethAddress: pkpEthAddress,
|
|
@@ -21959,7 +22877,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
21959
22877
|
}
|
|
21960
22878
|
const loanOpsForUcd = loanOpsForUcdResult.value;
|
|
21961
22879
|
const ucdTokenAddress = await loanOpsForUcd.ucdToken();
|
|
21962
|
-
const ucdToken = new
|
|
22880
|
+
const ucdToken = new import_ethers21.Contract(
|
|
21963
22881
|
ucdTokenAddress,
|
|
21964
22882
|
[
|
|
21965
22883
|
"function balanceOf(address) view returns (uint256)",
|
|
@@ -21975,9 +22893,9 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
21975
22893
|
if (ucdBalance < debtWei) {
|
|
21976
22894
|
return {
|
|
21977
22895
|
success: false,
|
|
21978
|
-
error: `Insufficient UCD balance for liquidation: ${(0,
|
|
22896
|
+
error: `Insufficient UCD balance for liquidation: ${(0, import_ethers21.formatEther)(
|
|
21979
22897
|
ucdBalance
|
|
21980
|
-
)} UCD (need ${(0,
|
|
22898
|
+
)} UCD (need ${(0, import_ethers21.formatEther)(debtWei)} UCD)`,
|
|
21981
22899
|
positionId: request.positionId,
|
|
21982
22900
|
wasLiquidated: false
|
|
21983
22901
|
};
|
|
@@ -21986,7 +22904,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
21986
22904
|
if (request.skipUcdApproval) {
|
|
21987
22905
|
return {
|
|
21988
22906
|
success: false,
|
|
21989
|
-
error: `Insufficient UCD allowance for UCDController: need at least ${(0,
|
|
22907
|
+
error: `Insufficient UCD allowance for UCDController: need at least ${(0, import_ethers21.formatEther)(
|
|
21990
22908
|
debtWei
|
|
21991
22909
|
)} UCD, or omit skipUcdApproval to let the SDK submit approve`,
|
|
21992
22910
|
positionId: request.positionId,
|
|
@@ -22510,7 +23428,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
22510
23428
|
if (typeof signatureBytes === "string" && signatureBytes.startsWith("{")) {
|
|
22511
23429
|
try {
|
|
22512
23430
|
const parsed = JSON.parse(signatureBytes);
|
|
22513
|
-
signatureBytes = parsed.signature ?? (parsed.r && parsed.s && parsed.v ?
|
|
23431
|
+
signatureBytes = parsed.signature ?? (parsed.r && parsed.s && parsed.v ? import_ethers21.Signature.from({
|
|
22514
23432
|
r: parsed.r,
|
|
22515
23433
|
s: parsed.s,
|
|
22516
23434
|
v: parsed.v
|
|
@@ -22521,7 +23439,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
22521
23439
|
if (!signatureBytes.startsWith("0x")) {
|
|
22522
23440
|
signatureBytes = "0x" + signatureBytes;
|
|
22523
23441
|
}
|
|
22524
|
-
const extendIface = new
|
|
23442
|
+
const extendIface = new import_ethers21.Interface([
|
|
22525
23443
|
"function extendPosition(bytes32 positionId, uint256 selectedTerm, uint256 quantumTimestamp, uint256 btcPrice, uint256 availableBTCBalance, uint256 proRataRenewalFee, bytes calldata extensionValidatorSignature) external returns (bool)"
|
|
22526
23444
|
]);
|
|
22527
23445
|
const extendCalldata = extendIface.encodeFunctionData("extendPosition", [
|
|
@@ -23070,7 +23988,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
23070
23988
|
` Converting payment amount: ${request.paymentAmount} (type: ${typeof request.paymentAmount})`
|
|
23071
23989
|
);
|
|
23072
23990
|
}
|
|
23073
|
-
const paymentAmountWei = (0,
|
|
23991
|
+
const paymentAmountWei = (0, import_ethers21.parseEther)(
|
|
23074
23992
|
request.paymentAmount.toString()
|
|
23075
23993
|
);
|
|
23076
23994
|
if (this.config.debug) {
|
|
@@ -23137,7 +24055,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
23137
24055
|
};
|
|
23138
24056
|
}
|
|
23139
24057
|
const ucdTokenAddress = await loanOps.ucdToken();
|
|
23140
|
-
const ucdToken = new
|
|
24058
|
+
const ucdToken = new import_ethers21.Contract(
|
|
23141
24059
|
ucdTokenAddress,
|
|
23142
24060
|
[
|
|
23143
24061
|
"function balanceOf(address) view returns (uint256)",
|
|
@@ -23161,14 +24079,14 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
23161
24079
|
]);
|
|
23162
24080
|
if (this.config.debug) {
|
|
23163
24081
|
log.info(` UCD funds source: ${fundsSource}${isSelfRepay ? " (self)" : " (borrower; agent submitting)"}`);
|
|
23164
|
-
log.info(` UCD Balance: ${(0,
|
|
23165
|
-
log.info(` UCD Allowance: ${(0,
|
|
24082
|
+
log.info(` UCD Balance: ${(0, import_ethers21.formatEther)(balance)} UCD`);
|
|
24083
|
+
log.info(` UCD Allowance: ${(0, import_ethers21.formatEther)(allowance)} UCD`);
|
|
23166
24084
|
log.info(` Required: ${request.paymentAmount} UCD`);
|
|
23167
24085
|
}
|
|
23168
24086
|
if (balance < paymentAmountWei) {
|
|
23169
24087
|
return {
|
|
23170
24088
|
success: false,
|
|
23171
|
-
error: `Insufficient UCD balance: ${(0,
|
|
24089
|
+
error: `Insufficient UCD balance: ${(0, import_ethers21.formatEther)(
|
|
23172
24090
|
balance
|
|
23173
24091
|
)} UCD (need ${request.paymentAmount} UCD)${isSelfRepay ? "" : ` \u2014 borrower ${fundsSource} lacks funds`}`
|
|
23174
24092
|
};
|
|
@@ -23177,7 +24095,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
23177
24095
|
if (!isSelfRepay) {
|
|
23178
24096
|
return {
|
|
23179
24097
|
success: false,
|
|
23180
|
-
error: `Borrower ${fundsSource} has insufficient UCD allowance to PositionManager (${(0,
|
|
24098
|
+
error: `Borrower ${fundsSource} has insufficient UCD allowance to PositionManager (${(0, import_ethers21.formatEther)(allowance)} UCD, need ${request.paymentAmount}). The borrower must approve PositionManager before a delegated agent can repay.`
|
|
23181
24099
|
};
|
|
23182
24100
|
}
|
|
23183
24101
|
if (this.config.debug) {
|
|
@@ -23446,7 +24364,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
23446
24364
|
const paymentSigner = this.getSignerOrThrow();
|
|
23447
24365
|
const paymentFrom = await paymentSigner.getAddress();
|
|
23448
24366
|
const paymentProvider = contractManager.getProvider();
|
|
23449
|
-
const paymentIface = new
|
|
24367
|
+
const paymentIface = new import_ethers21.Interface([
|
|
23450
24368
|
"function makePayment(bytes32 positionId, uint256 paymentAmount, uint256 quantumTimestamp, uint256 btcPrice, bytes calldata paymentValidatorSignature) external returns (bool)"
|
|
23451
24369
|
]);
|
|
23452
24370
|
const paymentCalldata = paymentIface.encodeFunctionData("makePayment", [
|
|
@@ -23795,7 +24713,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
23795
24713
|
const pkpId = position.pkpId;
|
|
23796
24714
|
if (litValidatorAddr && pkpId) {
|
|
23797
24715
|
try {
|
|
23798
|
-
const litValidator = new
|
|
24716
|
+
const litValidator = new import_ethers21.Contract(
|
|
23799
24717
|
litValidatorAddr,
|
|
23800
24718
|
["function pkpOwners(bytes32) view returns (address)"],
|
|
23801
24719
|
this.getProviderOrThrow()
|
|
@@ -23803,7 +24721,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
23803
24721
|
const owner = await litValidator.pkpOwners(
|
|
23804
24722
|
pkpId
|
|
23805
24723
|
);
|
|
23806
|
-
if (owner && owner !==
|
|
24724
|
+
if (owner && owner !== import_ethers21.ZeroAddress) {
|
|
23807
24725
|
controller = owner;
|
|
23808
24726
|
}
|
|
23809
24727
|
} catch {
|
|
@@ -23855,7 +24773,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
23855
24773
|
pkpNftAddress,
|
|
23856
24774
|
this.getChipotlePublicKeyFallback()
|
|
23857
24775
|
);
|
|
23858
|
-
pkpEthAddress = (0,
|
|
24776
|
+
pkpEthAddress = (0, import_ethers21.computeAddress)(pkpPublicKey);
|
|
23859
24777
|
pkpCache.set(positionId, {
|
|
23860
24778
|
publicKey: pkpPublicKey,
|
|
23861
24779
|
ethAddress: pkpEthAddress,
|
|
@@ -23925,7 +24843,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
23925
24843
|
` BTCSpendAuthorizer: ${contracts.BTCSpendAuthorizer || "MISSING"}`
|
|
23926
24844
|
);
|
|
23927
24845
|
}
|
|
23928
|
-
const chain = this.config.chain || (
|
|
24846
|
+
const chain = this.config.chain || litActionChainNameForChainId(network.chainId);
|
|
23929
24847
|
const devBitcoinProviderUrl = this.config.bitcoinProviders?.[0]?.url;
|
|
23930
24848
|
if (this.config.debug) {
|
|
23931
24849
|
log.info(` Network: ${chain} (chainId: ${network.chainId})`);
|
|
@@ -24228,7 +25146,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
24228
25146
|
utxoVout: withdrawalParams.utxoVout
|
|
24229
25147
|
});
|
|
24230
25148
|
}
|
|
24231
|
-
const withdrawIface = new
|
|
25149
|
+
const withdrawIface = new import_ethers21.Interface([
|
|
24232
25150
|
"function withdrawBTC((bytes32 positionId, bytes32 actionHash, bytes32 authorizedSpendsHash, bytes32 ucdDebtHash, bytes32 contractBundleHash, string withdrawalAddress, uint256 totalDeduction, uint256 newCollateral, uint256 quantumTimestamp, uint256 btcPrice, string utxoTxid, uint32 utxoVout) params, bytes withdrawalValidatorSignature, bytes btcSpendAuthSignature) external returns (bool)"
|
|
24233
25151
|
]);
|
|
24234
25152
|
const withdrawCalldata = withdrawIface.encodeFunctionData("withdrawBTC", [
|
|
@@ -24459,7 +25377,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
24459
25377
|
}
|
|
24460
25378
|
const pkpId = positionDetails.pkpId;
|
|
24461
25379
|
const vaultAddress = positionDetails.vaultAddress;
|
|
24462
|
-
if (!pkpId || pkpId ===
|
|
25380
|
+
if (!pkpId || pkpId === import_ethers21.ZeroHash) {
|
|
24463
25381
|
return { success: false, error: "Position has no PKP" };
|
|
24464
25382
|
}
|
|
24465
25383
|
if (!vaultAddress) {
|
|
@@ -24479,14 +25397,8 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
24479
25397
|
if (!publicKey) {
|
|
24480
25398
|
return { success: false, error: "Failed to resolve PKP public key" };
|
|
24481
25399
|
}
|
|
24482
|
-
const chipotlePkpAddress = (0,
|
|
24483
|
-
const
|
|
24484
|
-
1: "ethereum",
|
|
24485
|
-
11155111: "sepolia",
|
|
24486
|
-
1337: "hardhat",
|
|
24487
|
-
31337: "hardhat"
|
|
24488
|
-
};
|
|
24489
|
-
const chainName = STANDALONE_CHAIN_NAMES[chainId];
|
|
25400
|
+
const chipotlePkpAddress = (0, import_ethers21.computeAddress)(publicKey);
|
|
25401
|
+
const chainName = LIT_ACTION_CHAIN_NAMES[chainId];
|
|
24490
25402
|
if (!chainName) {
|
|
24491
25403
|
return {
|
|
24492
25404
|
success: false,
|
|
@@ -24858,7 +25770,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
24858
25770
|
const btcSpendAuthorizerAbi = [
|
|
24859
25771
|
"function getAuthorizedSpends(bytes32) view returns (tuple(string txid, uint32 vout, uint256 satoshis, string targetAddress, uint256 targetAmount, uint256 authorizedAt)[])"
|
|
24860
25772
|
];
|
|
24861
|
-
const btcSpendAuthorizer = new
|
|
25773
|
+
const btcSpendAuthorizer = new import_ethers21.Contract(
|
|
24862
25774
|
btcSpendAuthorizerAddress,
|
|
24863
25775
|
btcSpendAuthorizerAbi,
|
|
24864
25776
|
this.getProviderOrThrow()
|
|
@@ -24944,7 +25856,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
24944
25856
|
const abi = [
|
|
24945
25857
|
"function getAuthorizedSpends(bytes32) view returns (tuple(string txid, uint32 vout, uint256 satoshis, string targetAddress, uint256 targetAmount, uint256 authorizedAt)[])"
|
|
24946
25858
|
];
|
|
24947
|
-
const contract = new
|
|
25859
|
+
const contract = new import_ethers21.Contract(
|
|
24948
25860
|
btcSpendAuthorizerAddress,
|
|
24949
25861
|
abi,
|
|
24950
25862
|
this.getProviderOrThrow()
|
|
@@ -24958,7 +25870,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
24958
25870
|
targetAddress: spend.targetAddress,
|
|
24959
25871
|
targetAmount: Number(spend.targetAmount),
|
|
24960
25872
|
authorizedAt: Number(spend.authorizedAt),
|
|
24961
|
-
utxoKey: (0,
|
|
25873
|
+
utxoKey: (0, import_ethers21.solidityPackedKeccak256)(
|
|
24962
25874
|
["string", "uint32"],
|
|
24963
25875
|
[spend.txid, Number(spend.vout)]
|
|
24964
25876
|
)
|
|
@@ -25164,7 +26076,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
25164
26076
|
error: attRes.error ?? attRes.reason ?? `LIT recovery attestation rejected${attRes.failedStep ? ` at step ${attRes.failedStep}` : ""}`
|
|
25165
26077
|
};
|
|
25166
26078
|
}
|
|
25167
|
-
const btcSpendAuthorizer = new
|
|
26079
|
+
const btcSpendAuthorizer = new import_ethers21.Contract(
|
|
25168
26080
|
btcSpendAuthorizerAddress,
|
|
25169
26081
|
[
|
|
25170
26082
|
"function cancelStaleSpendWithProof(bytes32 positionId, bytes32 utxoKey, uint256 authorizedAt, string calldata invalidatorTxid, uint256 attestationTimestamp, bytes calldata litSignature) external"
|
|
@@ -25261,7 +26173,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
25261
26173
|
const positionDetails = await this.getPositionDetailsView(
|
|
25262
26174
|
params.positionId
|
|
25263
26175
|
);
|
|
25264
|
-
if (!positionDetails?.pkpId || positionDetails.pkpId ===
|
|
26176
|
+
if (!positionDetails?.pkpId || positionDetails.pkpId === import_ethers21.ZeroHash) {
|
|
25265
26177
|
return { success: false, error: "Position has no PKP" };
|
|
25266
26178
|
}
|
|
25267
26179
|
const pkpCache = this.cacheManager.getCache("pkp-data", {
|
|
@@ -25278,7 +26190,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
25278
26190
|
if (!publicKey) {
|
|
25279
26191
|
return { success: false, error: "Failed to resolve PKP public key" };
|
|
25280
26192
|
}
|
|
25281
|
-
const chipotlePkpAddress = (0,
|
|
26193
|
+
const chipotlePkpAddress = (0, import_ethers21.computeAddress)(publicKey);
|
|
25282
26194
|
const dc = this.config.contractAddresses || {};
|
|
25283
26195
|
const chainName = chainId === 1 ? "ethereum" : chainId === 11155111 ? "sepolia" : chainId === 1337 || chainId === 31337 ? "hardhat" : void 0;
|
|
25284
26196
|
if (!chainName) {
|
|
@@ -25708,6 +26620,18 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
25708
26620
|
async getLoansByBorrower(borrower, pagination, orderBy, orderDirection) {
|
|
25709
26621
|
return this.loanQuery.getLoansByBorrower(borrower, pagination, orderBy, orderDirection);
|
|
25710
26622
|
}
|
|
26623
|
+
/**
|
|
26624
|
+
* Combined UCD debt (canonical wei string + display string) and by-status loan
|
|
26625
|
+
* counts for a borrower, from one pass over the subgraph. `source: "subgraph"` —
|
|
26626
|
+
* an INDEXED, informational figure that lags writes; never size a transaction
|
|
26627
|
+
* from it (repayments read the chain via `getPositionDetailsView`).
|
|
26628
|
+
*
|
|
26629
|
+
* Fails loud with a SUBGRAPH-category `SDKError` (cause attached) on any page
|
|
26630
|
+
* failure or past 10,000 positions; never returns a partial total.
|
|
26631
|
+
*/
|
|
26632
|
+
async getBorrowerUcdDebtSummary(borrower) {
|
|
26633
|
+
return this.loanQuery.getBorrowerUcdDebtSummary(borrower);
|
|
26634
|
+
}
|
|
25711
26635
|
/**
|
|
25712
26636
|
* Get all active loans
|
|
25713
26637
|
*
|
|
@@ -25833,11 +26757,12 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
25833
26757
|
}
|
|
25834
26758
|
/**
|
|
25835
26759
|
* Execute a PSM stablecoin → UCD swap.
|
|
25836
|
-
*
|
|
26760
|
+
* Approves EXACTLY `amountWei` to the PSM when the current allowance is short (resetting a
|
|
26761
|
+
* partial allowance to zero first); every leg is validated by the sign-guard before it is sent.
|
|
25837
26762
|
*
|
|
25838
26763
|
* @param params.stablecoinAddress - ERC-20 address of the stablecoin to swap in
|
|
25839
26764
|
* @param params.amountWei - Stablecoin amount in native decimals (bigint)
|
|
25840
|
-
* @param params.minUcdOutWei - Minimum UCD to receive;
|
|
26765
|
+
* @param params.minUcdOutWei - Minimum UCD to receive; must be > 0 (reverts on-chain if below)
|
|
25841
26766
|
* @param params.signer - Connected signer for the approval and swap transactions
|
|
25842
26767
|
*/
|
|
25843
26768
|
async psmSwap(params) {
|
|
@@ -25847,32 +26772,23 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
25847
26772
|
"SimplePSMV2 address not configured \u2014 provide contractAddresses.simplePsmV2"
|
|
25848
26773
|
);
|
|
25849
26774
|
}
|
|
25850
|
-
|
|
25851
|
-
|
|
25852
|
-
|
|
25853
|
-
|
|
25854
|
-
|
|
25855
|
-
|
|
25856
|
-
|
|
25857
|
-
if (allowance < params.amountWei) {
|
|
25858
|
-
const approveTx = await stablecoin.approve(psmAddress, import_ethers17.MaxUint256);
|
|
25859
|
-
await approveTx.wait();
|
|
25860
|
-
}
|
|
25861
|
-
const psm = SimplePSMV2__factory.connect(psmAddress, params.signer);
|
|
25862
|
-
const tx = await psm.swap(params.stablecoinAddress, params.amountWei, params.minUcdOutWei);
|
|
25863
|
-
const receipt = await tx.wait();
|
|
25864
|
-
if (!receipt)
|
|
25865
|
-
throw new Error("PSM swap transaction receipt unavailable");
|
|
25866
|
-
return { hash: tx.hash, blockNumber: receipt.blockNumber };
|
|
26775
|
+
return this.runPsmExchange({
|
|
26776
|
+
direction: "swap",
|
|
26777
|
+
signer: params.signer,
|
|
26778
|
+
addresses: { psm: psmAddress, stablecoin: params.stablecoinAddress },
|
|
26779
|
+
amountIn: params.amountWei,
|
|
26780
|
+
minOut: params.minUcdOutWei
|
|
26781
|
+
});
|
|
25867
26782
|
}
|
|
25868
26783
|
/**
|
|
25869
26784
|
* Execute a PSM UCD → stablecoin redeem.
|
|
25870
|
-
*
|
|
25871
|
-
* UCDToken.burn(from, amount) calls
|
|
26785
|
+
* Approves EXACTLY `ucdAmountWei` of UCD to the UCDController (not the PSM) to satisfy the
|
|
26786
|
+
* M-4 burn allowance guard: UCDToken.burn(from, amount) calls
|
|
26787
|
+
* _spendAllowance(from, msg.sender=ucdController, amount).
|
|
25872
26788
|
*
|
|
25873
26789
|
* @param params.stablecoinAddress - ERC-20 address of the stablecoin to receive
|
|
25874
26790
|
* @param params.ucdAmountWei - UCD amount to redeem (18 decimals, bigint)
|
|
25875
|
-
* @param params.minStablecoinOutWei - Minimum stablecoin to receive
|
|
26791
|
+
* @param params.minStablecoinOutWei - Minimum stablecoin to receive; must be > 0
|
|
25876
26792
|
* @param params.signer - Connected signer
|
|
25877
26793
|
*/
|
|
25878
26794
|
async psmRedeem(params) {
|
|
@@ -25885,23 +26801,51 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
25885
26801
|
throw new Error("UCDToken address not configured \u2014 provide contractAddresses.ucdToken");
|
|
25886
26802
|
if (!ucdControllerAddress)
|
|
25887
26803
|
throw new Error("UCDController address not configured \u2014 provide contractAddresses.ucdController");
|
|
25888
|
-
|
|
25889
|
-
|
|
25890
|
-
|
|
25891
|
-
|
|
25892
|
-
|
|
25893
|
-
|
|
25894
|
-
|
|
25895
|
-
|
|
25896
|
-
|
|
25897
|
-
|
|
25898
|
-
|
|
25899
|
-
|
|
25900
|
-
|
|
25901
|
-
|
|
25902
|
-
|
|
25903
|
-
|
|
25904
|
-
|
|
26804
|
+
return this.runPsmExchange({
|
|
26805
|
+
direction: "redeem",
|
|
26806
|
+
signer: params.signer,
|
|
26807
|
+
addresses: {
|
|
26808
|
+
psm: psmAddress,
|
|
26809
|
+
stablecoin: params.stablecoinAddress,
|
|
26810
|
+
ucdToken: ucdAddress,
|
|
26811
|
+
ucdController: ucdControllerAddress
|
|
26812
|
+
},
|
|
26813
|
+
amountIn: params.ucdAmountWei,
|
|
26814
|
+
minOut: params.minStablecoinOutWei
|
|
26815
|
+
});
|
|
26816
|
+
}
|
|
26817
|
+
/**
|
|
26818
|
+
* Shared PSM path: plan + validate every leg against the chain the SIGNER is on, then send.
|
|
26819
|
+
* The signer's chain must match the SDK's configured chain — the addresses came from it.
|
|
26820
|
+
*/
|
|
26821
|
+
async runPsmExchange(params) {
|
|
26822
|
+
const provider = params.signer.provider;
|
|
26823
|
+
if (!provider) {
|
|
26824
|
+
throw new Error(`PSM ${params.direction} needs a signer connected to a provider`);
|
|
26825
|
+
}
|
|
26826
|
+
const configuredChainId = this.config.chainId ?? this.config.networkOverride?.chainId;
|
|
26827
|
+
if (typeof configuredChainId !== "number") {
|
|
26828
|
+
throw new Error(`PSM ${params.direction} needs the SDK's chainId \u2014 configure chainId`);
|
|
26829
|
+
}
|
|
26830
|
+
const mismatch = await describeProviderChainMismatch(provider, configuredChainId);
|
|
26831
|
+
if (mismatch)
|
|
26832
|
+
throw new Error(mismatch);
|
|
26833
|
+
const vctx = {
|
|
26834
|
+
chainId: configuredChainId,
|
|
26835
|
+
network: String(configuredChainId),
|
|
26836
|
+
contracts: {}
|
|
26837
|
+
};
|
|
26838
|
+
const plan = await planPsmExchange({
|
|
26839
|
+
direction: params.direction,
|
|
26840
|
+
owner: await params.signer.getAddress(),
|
|
26841
|
+
addresses: params.addresses,
|
|
26842
|
+
amountIn: params.amountIn,
|
|
26843
|
+
minOut: params.minOut,
|
|
26844
|
+
vctx,
|
|
26845
|
+
reads: psmExchangeReadsFromProvider(provider, params.addresses.psm)
|
|
26846
|
+
});
|
|
26847
|
+
const { hash, blockNumber } = await executePsmPlan(plan, params.signer, vctx);
|
|
26848
|
+
return { hash, blockNumber };
|
|
25905
26849
|
}
|
|
25906
26850
|
/**
|
|
25907
26851
|
* Wait for the subgraph to index up to (and including) the given block number.
|
|
@@ -25945,7 +26889,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
25945
26889
|
*
|
|
25946
26890
|
* Deliberately omits `minWithdrawRatioBps` — see {@link readLoanGrant}.
|
|
25947
26891
|
*/
|
|
25948
|
-
static LOAN_GRANT_PREFIX_IFACE = new
|
|
26892
|
+
static LOAN_GRANT_PREFIX_IFACE = new import_ethers21.Interface([
|
|
25949
26893
|
"function getLoanGrant(bytes32 positionId) view returns (address borrower, uint32 scopeBits, uint32 minCollateralRatioBps)"
|
|
25950
26894
|
]);
|
|
25951
26895
|
/**
|
|
@@ -25966,9 +26910,9 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
25966
26910
|
to: registryAddress,
|
|
25967
26911
|
data: iface.encodeFunctionData("getLoanGrant", [pid])
|
|
25968
26912
|
});
|
|
25969
|
-
if ((0,
|
|
26913
|
+
if ((0, import_ethers21.getBytes)(data).length < 96) {
|
|
25970
26914
|
throw new SDKError({
|
|
25971
|
-
message: `AgentDelegationRegistry at ${registryAddress} returned ${(0,
|
|
26915
|
+
message: `AgentDelegationRegistry at ${registryAddress} returned ${(0, import_ethers21.getBytes)(data).length} bytes for getLoanGrant(bytes32) \u2014 expected at least 96. Wrong address, or a registry version this SDK does not support.`,
|
|
25972
26916
|
category: "CONTRACT" /* CONTRACT */,
|
|
25973
26917
|
severity: "HIGH" /* HIGH */
|
|
25974
26918
|
});
|
|
@@ -25977,10 +26921,10 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
25977
26921
|
"getLoanGrant",
|
|
25978
26922
|
data
|
|
25979
26923
|
);
|
|
25980
|
-
const raw = (0,
|
|
26924
|
+
const raw = (0, import_ethers21.getBytes)(data);
|
|
25981
26925
|
const withdrawScopeSupported = raw.length >= 128;
|
|
25982
26926
|
const minWithdrawRatioBps = withdrawScopeSupported ? Number(
|
|
25983
|
-
|
|
26927
|
+
import_ethers21.AbiCoder.defaultAbiCoder().decode(["uint32"], raw.slice(96, 128))[0]
|
|
25984
26928
|
) : 0;
|
|
25985
26929
|
return {
|
|
25986
26930
|
borrower,
|
|
@@ -26079,18 +27023,30 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
26079
27023
|
const user = await signer.getAddress();
|
|
26080
27024
|
const registry = AgentDelegationRegistry__factory.connect(addr, signer);
|
|
26081
27025
|
let agentAddress;
|
|
26082
|
-
const
|
|
26083
|
-
|
|
26084
|
-
|
|
27026
|
+
const [isActive, record, latestBlock] = await Promise.all([
|
|
27027
|
+
registry.isAgentActive(user),
|
|
27028
|
+
registry.agentOf(user),
|
|
27029
|
+
this.getProviderOrThrow().getBlock("latest")
|
|
27030
|
+
]);
|
|
27031
|
+
if (!latestBlock) {
|
|
27032
|
+
throw new SDKError({
|
|
27033
|
+
message: "Could not read the latest block to check the agent registration's expiry",
|
|
27034
|
+
category: "NETWORK" /* NETWORK */,
|
|
27035
|
+
severity: "HIGH" /* HIGH */
|
|
27036
|
+
});
|
|
27037
|
+
}
|
|
27038
|
+
const plan = planAgentBinding({ isActive, record, nowSeconds: latestBlock.timestamp });
|
|
27039
|
+
if (plan.kind === "reuse") {
|
|
27040
|
+
agentAddress = plan.agent;
|
|
26085
27041
|
} else {
|
|
26086
27042
|
agentAddress = await mintAgentPkp({
|
|
26087
27043
|
serviceEndpoint: this.config.serviceEndpoint,
|
|
26088
27044
|
authHeader: this.serverSession ? () => this.serverSession.getAuthHeader() : void 0
|
|
26089
27045
|
});
|
|
26090
27046
|
const validitySeconds = options?.agentValiditySeconds ?? 90 * 24 * 60 * 60;
|
|
26091
|
-
const validUntil =
|
|
26092
|
-
const
|
|
26093
|
-
await
|
|
27047
|
+
const validUntil = latestBlock.timestamp + validitySeconds;
|
|
27048
|
+
const bindTx = plan.kind === "rotate" ? await registry.rotateAgent(user, agentAddress, validUntil) : await registry.registerAgent(user, agentAddress, validUntil);
|
|
27049
|
+
await bindTx.wait();
|
|
26094
27050
|
}
|
|
26095
27051
|
const currentDelegate = await getPositionDelegate(pid, signer, pdrAddr).catch(
|
|
26096
27052
|
() => null
|
|
@@ -26129,7 +27085,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
26129
27085
|
getPositionDelegate(pid, signer, pdrAddr),
|
|
26130
27086
|
registry.agentOf(user).then((a) => a.agent)
|
|
26131
27087
|
]);
|
|
26132
|
-
if (currentDelegate !==
|
|
27088
|
+
if (currentDelegate !== import_ethers21.ZeroAddress && currentDelegate.toLowerCase() === userAgent.toLowerCase()) {
|
|
26133
27089
|
const clearTx = await setPositionDelegate(
|
|
26134
27090
|
pid,
|
|
26135
27091
|
"0x0000000000000000000000000000000000000000",
|
|
@@ -26521,7 +27477,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
26521
27477
|
const maxUcdWhole = BigInt(result[3].toString());
|
|
26522
27478
|
return success({
|
|
26523
27479
|
liquidationThreshold: result[0].toString(),
|
|
26524
|
-
minimumLoanValueUcd: (0,
|
|
27480
|
+
minimumLoanValueUcd: (0, import_ethers21.formatUnits)(result[2], 18),
|
|
26525
27481
|
minimumLoanValueWei: result[2].toString(),
|
|
26526
27482
|
maxSingleLoanValueUcd: maxUcdWhole.toString(),
|
|
26527
27483
|
maxSingleLoanValueWei: (maxUcdWhole * 10n ** 18n).toString()
|
|
@@ -26558,7 +27514,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
26558
27514
|
const balanceWei = await provider.getBalance(address);
|
|
26559
27515
|
return success({
|
|
26560
27516
|
balanceWei: balanceWei.toString(),
|
|
26561
|
-
balanceEth: (0,
|
|
27517
|
+
balanceEth: (0, import_ethers21.formatEther)(balanceWei)
|
|
26562
27518
|
});
|
|
26563
27519
|
} catch (error) {
|
|
26564
27520
|
return failure(new SDKError({
|
|
@@ -26967,7 +27923,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
26967
27923
|
* Convert decimal position ID to bytes32 format
|
|
26968
27924
|
*/
|
|
26969
27925
|
toBytes32(value) {
|
|
26970
|
-
return (0,
|
|
27926
|
+
return (0, import_ethers21.zeroPadValue)((0, import_ethers21.toBeHex)(BigInt(value)), 32);
|
|
26971
27927
|
}
|
|
26972
27928
|
/**
|
|
26973
27929
|
* Check if an error indicates a technical failure vs business logic rejection
|
|
@@ -27008,7 +27964,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
27008
27964
|
const coreModuleAbi = [
|
|
27009
27965
|
"function getPositionDetails(bytes32) view returns (tuple(bytes32 positionId, bytes32 pkpId, uint256 ucdDebt, string vaultAddress, address borrower, uint40 createdAt, uint40 lastUpdated, uint16 selectedTerm, uint40 expiryAt, uint8 status, uint40 previousExpiryAt, uint16 totalTerm))"
|
|
27010
27966
|
];
|
|
27011
|
-
const coreModule = new
|
|
27967
|
+
const coreModule = new import_ethers21.Contract(
|
|
27012
27968
|
coreModuleAddress,
|
|
27013
27969
|
coreModuleAbi,
|
|
27014
27970
|
provider
|
|
@@ -27075,7 +28031,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
27075
28031
|
const corePositionAbi = [
|
|
27076
28032
|
"function getPositionDetails(bytes32) view returns (tuple(bytes32 positionId, bytes32 pkpId, uint256 ucdDebt, string vaultAddress, address borrower, uint40 createdAt, uint40 lastUpdated, uint16 selectedTerm, uint40 expiryAt, uint8 status, uint40 previousExpiryAt, uint16 totalTerm))"
|
|
27077
28033
|
];
|
|
27078
|
-
const corePositionContract = new
|
|
28034
|
+
const corePositionContract = new import_ethers21.Contract(
|
|
27079
28035
|
coreAddress,
|
|
27080
28036
|
corePositionAbi,
|
|
27081
28037
|
provider
|
|
@@ -27086,7 +28042,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
27086
28042
|
positionIdBytes32
|
|
27087
28043
|
);
|
|
27088
28044
|
const positionIdFromCore = corePosition?.positionId || corePosition?.[0] || null;
|
|
27089
|
-
if (positionIdFromCore && positionIdFromCore !==
|
|
28045
|
+
if (positionIdFromCore && positionIdFromCore !== import_ethers21.ZeroHash && positionIdFromCore !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
|
|
27090
28046
|
if (this.config.debug) {
|
|
27091
28047
|
log.info("Position exists in core contract", {
|
|
27092
28048
|
positionId,
|
|
@@ -27154,7 +28110,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
27154
28110
|
throw new Error("loanOperationsManager address not configured");
|
|
27155
28111
|
}
|
|
27156
28112
|
const runner = this.config.contractSigner || this.getProviderOrThrow();
|
|
27157
|
-
const contract = new
|
|
28113
|
+
const contract = new import_ethers21.Contract(
|
|
27158
28114
|
addr.loanOperationsManager,
|
|
27159
28115
|
[
|
|
27160
28116
|
"function getProtocolConfig() external view returns (uint256 liquidationThreshold, uint256 minimumLoanValueUcd, uint256 minimumLoanValueWei, uint256 maxSingleLoanValueUcd)"
|
|
@@ -27167,7 +28123,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
27167
28123
|
const maxUcdWhole = BigInt(result[3].toString());
|
|
27168
28124
|
return {
|
|
27169
28125
|
liquidationThreshold: result[0].toString(),
|
|
27170
|
-
minimumLoanValueUcd: (0,
|
|
28126
|
+
minimumLoanValueUcd: (0, import_ethers21.formatUnits)(result[2], 18),
|
|
27171
28127
|
minimumLoanValueWei: result[2].toString(),
|
|
27172
28128
|
maxSingleLoanValueUcd: maxUcdWhole.toString(),
|
|
27173
28129
|
maxSingleLoanValueWei: (maxUcdWhole * 10n ** 18n).toString()
|
|
@@ -27185,8 +28141,8 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
27185
28141
|
const MIN_REVEAL_DELAY = 60;
|
|
27186
28142
|
const MAX_RANDOM_DELAY = 240;
|
|
27187
28143
|
const entropyInput = positionId + quantumTimestamp.toString();
|
|
27188
|
-
const hash = (0,
|
|
27189
|
-
(0,
|
|
28144
|
+
const hash = (0, import_ethers21.keccak256)(
|
|
28145
|
+
(0, import_ethers21.toUtf8Bytes)(entropyInput)
|
|
27190
28146
|
);
|
|
27191
28147
|
const randomValue = BigInt(hash);
|
|
27192
28148
|
const randomDelay = Number(randomValue % BigInt(MAX_RANDOM_DELAY));
|
|
@@ -27211,12 +28167,12 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
27211
28167
|
const rawPositionId = /^0x/i.test(params.positionId.trim()) ? params.positionId.trim().slice(2) : params.positionId.trim();
|
|
27212
28168
|
const canonicalPositionId = "0x" + rawPositionId.padStart(64, "0").toLowerCase();
|
|
27213
28169
|
const intentTimestamp = Math.floor(Date.now() / 1e3);
|
|
27214
|
-
const intentActionHash = (0,
|
|
27215
|
-
const intentHash = (0,
|
|
28170
|
+
const intentActionHash = (0, import_ethers21.keccak256)((0, import_ethers21.toUtf8Bytes)("liquidate-position"));
|
|
28171
|
+
const intentHash = (0, import_ethers21.solidityPackedKeccak256)(
|
|
27216
28172
|
["bytes32", "uint256", "uint256", "address", "bytes32"],
|
|
27217
28173
|
[canonicalPositionId, intentTimestamp, chainId, intentSigner, intentActionHash]
|
|
27218
28174
|
);
|
|
27219
|
-
const intentSignature = await signer.signMessage((0,
|
|
28175
|
+
const intentSignature = await signer.signMessage((0, import_ethers21.getBytes)(intentHash));
|
|
27220
28176
|
const response = await fetch(endpoint, {
|
|
27221
28177
|
method: "POST",
|
|
27222
28178
|
headers: { "Content-Type": "application/json" },
|
|
@@ -27268,7 +28224,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
27268
28224
|
const vrfSeedRaw = await lmResult.value["vrfSeeds"](positionId);
|
|
27269
28225
|
const vrfSeed = BigInt(vrfSeedRaw.toString());
|
|
27270
28226
|
if (vrfSeed !== 0n) {
|
|
27271
|
-
const finalEntropy = (0,
|
|
28227
|
+
const finalEntropy = (0, import_ethers21.solidityPackedKeccak256)(
|
|
27272
28228
|
["uint256", "uint256", "bytes32"],
|
|
27273
28229
|
[vrfSeed, BigInt(quantumTimestamp.toString()), positionId]
|
|
27274
28230
|
);
|
|
@@ -27546,7 +28502,7 @@ var EventHelpers = {
|
|
|
27546
28502
|
};
|
|
27547
28503
|
|
|
27548
28504
|
// src/utils/safe-agent-delegation.utils.ts
|
|
27549
|
-
var
|
|
28505
|
+
var import_ethers22 = require("ethers");
|
|
27550
28506
|
init_deployment_addresses();
|
|
27551
28507
|
var SAFE_ABI = [
|
|
27552
28508
|
"function getModulesPaginated(address start, uint256 pageSize) view returns (address[] modules, address next)"
|
|
@@ -27564,7 +28520,7 @@ var MODULE_LIST_SENTINEL = "0x0000000000000000000000000000000000000001";
|
|
|
27564
28520
|
var MODULE_PAGE_SIZE = 50;
|
|
27565
28521
|
var lower = (v) => v.trim().toLowerCase();
|
|
27566
28522
|
async function classifyModule(provider, moduleAddress, safeAddress, positionManager, factoryAddress) {
|
|
27567
|
-
const module2 = new
|
|
28523
|
+
const module2 = new import_ethers22.Contract(moduleAddress, MODULE_ABI, provider);
|
|
27568
28524
|
let boundSafe;
|
|
27569
28525
|
try {
|
|
27570
28526
|
boundSafe = await module2.safe();
|
|
@@ -27584,7 +28540,7 @@ async function classifyModule(provider, moduleAddress, safeAddress, positionMana
|
|
|
27584
28540
|
if (!factoryAddress)
|
|
27585
28541
|
return null;
|
|
27586
28542
|
try {
|
|
27587
|
-
const factory = new
|
|
28543
|
+
const factory = new import_ethers22.Contract(factoryAddress, FACTORY_ABI, provider);
|
|
27588
28544
|
return await factory.isFromFactory(moduleAddress) === true;
|
|
27589
28545
|
} catch {
|
|
27590
28546
|
return null;
|
|
@@ -27613,7 +28569,7 @@ async function getSafeAgentDelegation(params) {
|
|
|
27613
28569
|
let modules;
|
|
27614
28570
|
let next;
|
|
27615
28571
|
try {
|
|
27616
|
-
const safe = new
|
|
28572
|
+
const safe = new import_ethers22.Contract(safeAddress, SAFE_ABI, provider);
|
|
27617
28573
|
const page = await safe.getModulesPaginated(
|
|
27618
28574
|
MODULE_LIST_SENTINEL,
|
|
27619
28575
|
MODULE_PAGE_SIZE
|
|
@@ -27641,7 +28597,7 @@ async function getSafeAgentDelegation(params) {
|
|
|
27641
28597
|
continue;
|
|
27642
28598
|
let agentAddress = null;
|
|
27643
28599
|
try {
|
|
27644
|
-
agentAddress = await new
|
|
28600
|
+
agentAddress = await new import_ethers22.Contract(
|
|
27645
28601
|
moduleAddress,
|
|
27646
28602
|
MODULE_ABI,
|
|
27647
28603
|
provider
|
|
@@ -27655,7 +28611,7 @@ async function getSafeAgentDelegation(params) {
|
|
|
27655
28611
|
let validUntil;
|
|
27656
28612
|
let status;
|
|
27657
28613
|
try {
|
|
27658
|
-
const record = await new
|
|
28614
|
+
const record = await new import_ethers22.Contract(
|
|
27659
28615
|
registryAddress,
|
|
27660
28616
|
REGISTRY_ABI,
|
|
27661
28617
|
provider
|