@gvnrdao/dh-sdk 0.0.324 → 0.0.328
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/deployments.js +2 -26
- package/dist/deployments.mjs +2 -26
- package/dist/index.d.ts +1 -0
- package/dist/index.js +238 -65
- package/dist/index.mjs +238 -65
- package/dist/interfaces/chunks/loan-operations.i.d.ts +37 -0
- package/dist/modules/diamond-hands-sdk.d.ts +75 -2
- package/dist/safe-delegation.js +2 -26
- package/dist/safe-delegation.mjs +2 -26
- package/dist/utils/authorization-provider.utils.d.ts +65 -0
- package/dist/utils/mint-authorization.utils.d.ts +10 -16
- package/package.json +3 -3
- package/browser/dist/397.browser.js +0 -2
- package/browser/dist/397.browser.js.LICENSE.txt +0 -1
- package/browser/dist/833.browser.js +0 -2
- package/browser/dist/833.browser.js.LICENSE.txt +0 -1
- package/browser/dist/index.d.ts +0 -8
- package/browser/dist/index.d.ts.map +0 -1
- package/browser/dist/index.js +0 -25
package/dist/index.mjs
CHANGED
|
@@ -360,7 +360,7 @@ var init_deployment_addresses = __esm({
|
|
|
360
360
|
CARRY_AGENT_REGISTRY: "0x5595CB538D44E6Cf179D20364fD631D0477e69f7",
|
|
361
361
|
CARRY_AGENT_REGISTRY_IMPL: "0x1b11a3D5e46F3672E643c997f0d569DFf0b64a01",
|
|
362
362
|
AGENT_DELEGATION_REGISTRY: "0x6AE7fc6bE0925126b9D25b1B7Ce5AD5d127ec025",
|
|
363
|
-
AGENT_DELEGATION_REGISTRY_IMPL: "
|
|
363
|
+
AGENT_DELEGATION_REGISTRY_IMPL: "0xE931E4b9ceF2A48Ac7D57192da4B523756cFC310",
|
|
364
364
|
DH_AGENT_DELEGATE: "0xa5A14489ae883960df6E1463A41a47aBE9Ecc5AF"
|
|
365
365
|
}
|
|
366
366
|
};
|
|
@@ -630,7 +630,7 @@ var init_deployment_addresses = __esm({
|
|
|
630
630
|
LOCALHOST_DEPLOYMENT = {
|
|
631
631
|
network: "localhost",
|
|
632
632
|
chainId: 1337,
|
|
633
|
-
timestamp: "2026-07-
|
|
633
|
+
timestamp: "2026-07-20T19:45:58.514Z",
|
|
634
634
|
deployer: "",
|
|
635
635
|
contracts: {
|
|
636
636
|
MessageHashBuilder: "0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9",
|
|
@@ -650,30 +650,6 @@ var init_deployment_addresses = __esm({
|
|
|
650
650
|
PositionManager: "0x851356ae760d987E095750cCeb3bC6014560891C",
|
|
651
651
|
OperationAuthorizationRegistry: "0xc96304e3c037f81dA488ed9dEa1D8F2a48278a75",
|
|
652
652
|
PKPValidation: "0xD0141E899a65C95a556fE2B27e5982A6DE7fDD7A"
|
|
653
|
-
},
|
|
654
|
-
latestEnv: {
|
|
655
|
-
UCD_TOKEN: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
|
|
656
|
-
UCD_CONTROLLER: "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9",
|
|
657
|
-
POSITION_MANAGER: "0x851356ae760d987E095750cCeb3bC6014560891C",
|
|
658
|
-
PKP_VALIDATION_REGISTRY: "0xD0141E899a65C95a556fE2B27e5982A6DE7fDD7A",
|
|
659
|
-
PKP_VALIDATION_CID_V1: "0x12203af2054b0ab6e18d898ddfc7985593439fb82d22336133387c1169f98d3702ab",
|
|
660
|
-
PKP_VALIDATION_CID_V3: "0x12203af2054b0ab6e18d898ddfc7985593439fb82d22336133387c1169f98d3702ab",
|
|
661
|
-
PKP_ETH_ADDRESS: "0x406f6a9855de2f5577b1525f0600461f02859bd0",
|
|
662
|
-
OPERATION_AUTHORIZATION_REGISTRY: "0xc96304e3c037f81dA488ed9dEa1D8F2a48278a75",
|
|
663
|
-
UCD_MINT_VALIDATOR_ADDRESS: "0x406f6a9855de2f5577b1525f0600461f02859bd0",
|
|
664
|
-
UCD_MINT_VALIDATOR_VERSION: 1,
|
|
665
|
-
BTC_WITHDRAWAL_VALIDATOR_ADDRESS: "0x406f6a9855de2f5577b1525f0600461f02859bd0",
|
|
666
|
-
BTC_WITHDRAWAL_VALIDATOR_VERSION: 3,
|
|
667
|
-
UPDATE_BALANCE_VALIDATOR_ADDRESS: "0x406f6a9855de2f5577b1525f0600461f02859bd0",
|
|
668
|
-
UPDATE_BALANCE_VALIDATOR_VERSION: 1,
|
|
669
|
-
PROCESS_PAYMENT_VALIDATOR_ADDRESS: "0x406f6a9855de2f5577b1525f0600461f02859bd0",
|
|
670
|
-
PROCESS_PAYMENT_VALIDATOR_VERSION: 1,
|
|
671
|
-
EXTEND_POSITION_VALIDATOR_ADDRESS: "0x406f6a9855de2f5577b1525f0600461f02859bd0",
|
|
672
|
-
EXTEND_POSITION_VALIDATOR_VERSION: 1,
|
|
673
|
-
POSITION_MANAGER_CORE_MODULE: "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e",
|
|
674
|
-
LOAN_OPERATIONS_MANAGER_MODULE: "0x68B1D87F95878fE05B998F19b66F4baba5De1aed",
|
|
675
|
-
TERM_MANAGER_MODULE: "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82",
|
|
676
|
-
COMMUNITY_MANAGER_MODULE: ""
|
|
677
653
|
}
|
|
678
654
|
};
|
|
679
655
|
LOCALHOST_CONTRACTS = {
|
|
@@ -1393,7 +1369,7 @@ var require_pkg_src = __commonJS({
|
|
|
1393
1369
|
description: "Production Btc Transaction Signer",
|
|
1394
1370
|
version: "1.1.1",
|
|
1395
1371
|
deployed: true,
|
|
1396
|
-
deployedAt:
|
|
1372
|
+
deployedAt: 1786465606156,
|
|
1397
1373
|
size: 67999,
|
|
1398
1374
|
hash: "83f3644c68819215e05ba8db3225571354a009458fc1437f606df6cb9142d3f9"
|
|
1399
1375
|
},
|
|
@@ -1406,7 +1382,7 @@ var require_pkg_src = __commonJS({
|
|
|
1406
1382
|
description: "Production Ucd Mint Validator",
|
|
1407
1383
|
version: "1.2.1",
|
|
1408
1384
|
deployed: true,
|
|
1409
|
-
deployedAt:
|
|
1385
|
+
deployedAt: 1786465627201,
|
|
1410
1386
|
size: 82739,
|
|
1411
1387
|
hash: "2bdd9aebdaad58a43a4731d5b7284f2531b7a9caaf8caf5719d891c4ed33cb2e",
|
|
1412
1388
|
validatorWalletAddress: "0x82DB9F515957aA380B596C6eA2Bd30CEF181f236",
|
|
@@ -1424,7 +1400,7 @@ var require_pkg_src = __commonJS({
|
|
|
1424
1400
|
description: "Production Process Payment Validator",
|
|
1425
1401
|
version: "1.2.1",
|
|
1426
1402
|
deployed: true,
|
|
1427
|
-
deployedAt:
|
|
1403
|
+
deployedAt: 1786465654796,
|
|
1428
1404
|
size: 78736,
|
|
1429
1405
|
hash: "17191ecc9c009e0fa5ab52c876330beb2a9a75d30cccb92e7c0c04f169df088b",
|
|
1430
1406
|
validatorWalletAddress: "0x82DB9F515957aA380B596C6eA2Bd30CEF181f236",
|
|
@@ -1442,7 +1418,7 @@ var require_pkg_src = __commonJS({
|
|
|
1442
1418
|
description: "Production Extend Position Validator",
|
|
1443
1419
|
version: "1.2.1",
|
|
1444
1420
|
deployed: true,
|
|
1445
|
-
deployedAt:
|
|
1421
|
+
deployedAt: 1786465681874,
|
|
1446
1422
|
size: 76515,
|
|
1447
1423
|
hash: "1d4e085335af80f194a663a577876902c83015177e95906a0b461fa93bd5da08",
|
|
1448
1424
|
validatorWalletAddress: "0x82DB9F515957aA380B596C6eA2Bd30CEF181f236",
|
|
@@ -1452,21 +1428,17 @@ var require_pkg_src = __commonJS({
|
|
|
1452
1428
|
}
|
|
1453
1429
|
},
|
|
1454
1430
|
btcWithdrawal: {
|
|
1455
|
-
cid: "
|
|
1431
|
+
cid: "QmRYVoTTDxk2McvBqxSm9M7YcaBGdTqjnZGPX4Y191DaZh",
|
|
1456
1432
|
authorizedCidHex: cidToHex(
|
|
1457
|
-
"
|
|
1433
|
+
"QmRYVoTTDxk2McvBqxSm9M7YcaBGdTqjnZGPX4Y191DaZh"
|
|
1458
1434
|
),
|
|
1459
1435
|
name: "Btc Withdrawal",
|
|
1460
1436
|
description: "Production Btc Withdrawal",
|
|
1461
|
-
|
|
1462
|
-
// stamped its cohort 1.2.1, but this action was already at 1.2.3 from the vout
|
|
1463
|
-
// canonicalization fix. Reusing the cohort stamp made a newer CID read as older
|
|
1464
|
-
// than the one it replaced, which a rotation ceremony would treat as a rollback.
|
|
1465
|
-
version: "1.2.4",
|
|
1437
|
+
version: "1.2.7",
|
|
1466
1438
|
deployed: true,
|
|
1467
|
-
deployedAt:
|
|
1468
|
-
size:
|
|
1469
|
-
hash: "
|
|
1439
|
+
deployedAt: 1786446046614,
|
|
1440
|
+
size: 97456,
|
|
1441
|
+
hash: "3cae8250861832f91ee3c573abeb50ff27b9acd34148176071cbfbabb359fc91",
|
|
1470
1442
|
validatorWalletAddress: "0x82DB9F515957aA380B596C6eA2Bd30CEF181f236",
|
|
1471
1443
|
pkp: {
|
|
1472
1444
|
publicKey: "0x043616787c5432415c24378c4ef48de2bcd6bb7b575b837e3cff09171802662da7105d79586c7659677a0ecbaddac4cce06cb2a11f69a16fa0c4d7002ac7d51a4d",
|
|
@@ -1578,19 +1550,17 @@ var require_pkg_src = __commonJS({
|
|
|
1578
1550
|
hash: "03ee5be239c91c735d2c95db89a0d1b77e602cfc18af8eb64b3281fddcd90a72"
|
|
1579
1551
|
},
|
|
1580
1552
|
agentModuleExecutor: {
|
|
1581
|
-
|
|
1582
|
-
// holds this action and no PKPs until an agent is minted into it. NOT yet
|
|
1583
|
-
// bound on mainnet — the Base `DH-Agent-Mainnet` group is created at rollout.
|
|
1584
|
-
cid: "QmVgjGXUuxoGf8XyddW8a7btaazT9yEotpKoBgb65oPvcF",
|
|
1553
|
+
cid: "QmdhC18wF7PFGMQ8aThdPsEVSpuDewA27dFvbA3bG6kVDQ",
|
|
1585
1554
|
authorizedCidHex: cidToHex(
|
|
1586
|
-
"
|
|
1555
|
+
"QmdhC18wF7PFGMQ8aThdPsEVSpuDewA27dFvbA3bG6kVDQ"
|
|
1587
1556
|
),
|
|
1588
1557
|
name: "Agent Module Executor",
|
|
1589
|
-
description: "
|
|
1590
|
-
version: "1.
|
|
1558
|
+
description: "Production Agent Module Executor",
|
|
1559
|
+
version: "1.1.1",
|
|
1591
1560
|
deployed: true,
|
|
1592
|
-
|
|
1593
|
-
|
|
1561
|
+
deployedAt: 1786446108348,
|
|
1562
|
+
size: 11655,
|
|
1563
|
+
hash: "d4e2149f843d6c2699fc61114d54b268ba4ed2c46ebdaee1145e0f101ec2a1ec"
|
|
1594
1564
|
},
|
|
1595
1565
|
staleSpendRecoveryValidator: {
|
|
1596
1566
|
cid: "QmXQWT8V4wjuvH8BbC7xrkYxsVu5AKfvfWcS7jnYfrexYm",
|
|
@@ -1619,7 +1589,7 @@ var require_pkg_src = __commonJS({
|
|
|
1619
1589
|
description: "Production Btc Utxo Invalidator",
|
|
1620
1590
|
version: "1.0.0",
|
|
1621
1591
|
deployed: true,
|
|
1622
|
-
deployedAt:
|
|
1592
|
+
deployedAt: 1786465704240,
|
|
1623
1593
|
size: 62553,
|
|
1624
1594
|
hash: "1dcea2b49e545c423195be22e072dc5e3039ed1c95a8490b23505956029f5b33"
|
|
1625
1595
|
}
|
|
@@ -5528,8 +5498,14 @@ function getMainnetConfig() {
|
|
|
5528
5498
|
agentModuleFactory: MAINNET_CONTRACTS.AgentModuleFactory || ""
|
|
5529
5499
|
},
|
|
5530
5500
|
subgraphs: {
|
|
5501
|
+
// KNOWN-WRONG placeholder (see GOAL-PLAN step 2 / dogfood F-CLI-4): this is a
|
|
5502
|
+
// SEPOLIA subgraph id in a mainnet (chainId 1) config, and this gateway host
|
|
5503
|
+
// needs an Authorization key the SDK does not (and must not) hold — so the
|
|
5504
|
+
// URL is doubly unusable. The `CCTPsd…` path segment is the subgraph's
|
|
5505
|
+
// PUBLIC ID, not a credential. The fix is a chain-correct SERVER-side proxy
|
|
5506
|
+
// (see mcp/docs/SUBGRAPH-PROXY-PLAN.md) — same rule as rpcUrls above:
|
|
5507
|
+
// never ship an API-keyed URL in a client config.
|
|
5531
5508
|
diamondHandsUrl: "https://gateway-arbitrum.network.thegraph.com/api/subgraphs/id/CCTPsdYqco2jChDLLBQTbdJWwoukVoMt1cXeR9ti6r9A"
|
|
5532
|
-
// Published Sepolia subgraph on Arbitrum One
|
|
5533
5509
|
},
|
|
5534
5510
|
litNetwork: "chipotle",
|
|
5535
5511
|
debug: false
|
|
@@ -6326,7 +6302,7 @@ async function generateMintAuthorization(positionId, amount, chainId, signerOrPr
|
|
|
6326
6302
|
signature
|
|
6327
6303
|
};
|
|
6328
6304
|
}
|
|
6329
|
-
async function generatePaymentAuthorization(positionId, amount, chainId,
|
|
6305
|
+
async function generatePaymentAuthorization(positionId, amount, chainId, signerOrPrecomputed) {
|
|
6330
6306
|
if (typeof positionId !== "string") {
|
|
6331
6307
|
throw new Error(`positionId must be a string, got ${typeof positionId}`);
|
|
6332
6308
|
}
|
|
@@ -6336,6 +6312,18 @@ async function generatePaymentAuthorization(positionId, amount, chainId, signer)
|
|
|
6336
6312
|
if (typeof chainId !== "number") {
|
|
6337
6313
|
throw new Error(`chainId must be a number, got ${typeof chainId}`);
|
|
6338
6314
|
}
|
|
6315
|
+
if (typeof signerOrPrecomputed === "object" && "signature" in signerOrPrecomputed && !("signMessage" in signerOrPrecomputed)) {
|
|
6316
|
+
const { timestamp: timestamp2, signature: signature2 } = signerOrPrecomputed;
|
|
6317
|
+
return {
|
|
6318
|
+
positionId,
|
|
6319
|
+
timestamp: timestamp2,
|
|
6320
|
+
chainId,
|
|
6321
|
+
amount: amount.toString(),
|
|
6322
|
+
action: "make-payment",
|
|
6323
|
+
signature: signature2
|
|
6324
|
+
};
|
|
6325
|
+
}
|
|
6326
|
+
const signer = signerOrPrecomputed;
|
|
6339
6327
|
const timestamp = await calculateNextQuantumTimestamp();
|
|
6340
6328
|
if (typeof timestamp !== "number") {
|
|
6341
6329
|
throw new Error(`timestamp must be a number, got ${typeof timestamp}`);
|
|
@@ -6577,6 +6565,26 @@ async function generateExtendAuthorization(positionId, newTerm, chainId, signerO
|
|
|
6577
6565
|
};
|
|
6578
6566
|
}
|
|
6579
6567
|
|
|
6568
|
+
// src/utils/authorization-provider.utils.ts
|
|
6569
|
+
async function resolveAuthorizationInput(provider, signer, ctx) {
|
|
6570
|
+
if (!provider)
|
|
6571
|
+
return signer;
|
|
6572
|
+
const result = await provider(ctx);
|
|
6573
|
+
if (!result || typeof result.signature !== "string" || result.signature.length === 0) {
|
|
6574
|
+
throw new Error(
|
|
6575
|
+
"authorizationProvider returned no signature \u2014 refusing to fall back to the connected signer, which would prompt for the owner quorum this provider exists to avoid"
|
|
6576
|
+
);
|
|
6577
|
+
}
|
|
6578
|
+
if (!Number.isInteger(result.timestamp)) {
|
|
6579
|
+
throw new Error(
|
|
6580
|
+
`authorizationProvider returned a non-integer timestamp (${String(
|
|
6581
|
+
result.timestamp
|
|
6582
|
+
)}) \u2014 the protocol call must carry the same value the envelope was signed over`
|
|
6583
|
+
);
|
|
6584
|
+
}
|
|
6585
|
+
return { timestamp: result.timestamp, signature: result.signature };
|
|
6586
|
+
}
|
|
6587
|
+
|
|
6580
6588
|
// src/utils/eip712-login.ts
|
|
6581
6589
|
import { hexlify, randomBytes } from "ethers";
|
|
6582
6590
|
function buildLoginDomain(chainId) {
|
|
@@ -20002,11 +20010,20 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
20002
20010
|
{}
|
|
20003
20011
|
);
|
|
20004
20012
|
}
|
|
20013
|
+
const mintAuthInput = await resolveAuthorizationInput(
|
|
20014
|
+
request.authorizationProvider,
|
|
20015
|
+
signer2,
|
|
20016
|
+
{
|
|
20017
|
+
positionId: request.positionId,
|
|
20018
|
+
timestamp: calculateNextQuantumTimestamp(),
|
|
20019
|
+
amount: requestAmountWei.toString()
|
|
20020
|
+
}
|
|
20021
|
+
);
|
|
20005
20022
|
const auth = await generateMintAuthorization(
|
|
20006
20023
|
request.positionId,
|
|
20007
20024
|
BigInt(requestAmountWei.toString()),
|
|
20008
20025
|
chainId,
|
|
20009
|
-
|
|
20026
|
+
mintAuthInput
|
|
20010
20027
|
);
|
|
20011
20028
|
if (this.config.debug) {
|
|
20012
20029
|
log.info(` Authorization generated (attempt ${attempt})`, {});
|
|
@@ -21597,7 +21614,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
21597
21614
|
* @param _selectedTerm - Extension term in months
|
|
21598
21615
|
* @returns Transaction receipt
|
|
21599
21616
|
*/
|
|
21600
|
-
async extendPosition(_positionId, _selectedTerm, _rpcUrl, _bitcoinRpcUrl) {
|
|
21617
|
+
async extendPosition(_positionId, _selectedTerm, _rpcUrl, _bitcoinRpcUrl, _authorizationProvider) {
|
|
21601
21618
|
this.ensureInitialized();
|
|
21602
21619
|
if (typeof _positionId !== "string") {
|
|
21603
21620
|
throw new Error(`positionId must be a string, got ${typeof _positionId}`);
|
|
@@ -21691,11 +21708,23 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
21691
21708
|
}
|
|
21692
21709
|
}
|
|
21693
21710
|
const signer = this.getSignerOrThrow();
|
|
21711
|
+
const extendAuthInput = await resolveAuthorizationInput(
|
|
21712
|
+
_authorizationProvider,
|
|
21713
|
+
signer,
|
|
21714
|
+
{
|
|
21715
|
+
positionId: _positionId,
|
|
21716
|
+
timestamp: calculateNextQuantumTimestamp(),
|
|
21717
|
+
// The extend envelope binds the TERM, not an amount. Carried as "0" so
|
|
21718
|
+
// the context shape stays uniform across the four operations.
|
|
21719
|
+
amount: "0",
|
|
21720
|
+
selectedTerm: _selectedTerm
|
|
21721
|
+
}
|
|
21722
|
+
);
|
|
21694
21723
|
const auth = await generateExtendAuthorization(
|
|
21695
21724
|
_positionId,
|
|
21696
21725
|
_selectedTerm,
|
|
21697
21726
|
chainId,
|
|
21698
|
-
|
|
21727
|
+
extendAuthInput
|
|
21699
21728
|
);
|
|
21700
21729
|
if (this.config.debug) {
|
|
21701
21730
|
log.info(" Extend authorization generated", {
|
|
@@ -21951,7 +21980,8 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
21951
21980
|
positionId,
|
|
21952
21981
|
selectedTerm,
|
|
21953
21982
|
request.rpcUrl,
|
|
21954
|
-
request.customBitcoinRpcUrl
|
|
21983
|
+
request.customBitcoinRpcUrl,
|
|
21984
|
+
request.authorizationProvider
|
|
21955
21985
|
);
|
|
21956
21986
|
let newExpiryAt;
|
|
21957
21987
|
try {
|
|
@@ -22479,11 +22509,20 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
22479
22509
|
}
|
|
22480
22510
|
let auth;
|
|
22481
22511
|
try {
|
|
22512
|
+
const paymentAuthInput = await resolveAuthorizationInput(
|
|
22513
|
+
request.authorizationProvider,
|
|
22514
|
+
signer,
|
|
22515
|
+
{
|
|
22516
|
+
positionId: request.positionId,
|
|
22517
|
+
timestamp: calculateNextQuantumTimestamp(),
|
|
22518
|
+
amount: paymentAmountWei.toString()
|
|
22519
|
+
}
|
|
22520
|
+
);
|
|
22482
22521
|
auth = await generatePaymentAuthorization(
|
|
22483
22522
|
request.positionId,
|
|
22484
22523
|
paymentAmountWei,
|
|
22485
22524
|
chainId,
|
|
22486
|
-
|
|
22525
|
+
paymentAuthInput
|
|
22487
22526
|
);
|
|
22488
22527
|
} catch (authError) {
|
|
22489
22528
|
if (authError.message && authError.message.includes("BigNumber")) {
|
|
@@ -22995,7 +23034,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
22995
23034
|
* @param customBitcoinRpcUrl - Optional custom Bitcoin RPC URL (for local testing)
|
|
22996
23035
|
* @returns Withdrawal result with transaction details
|
|
22997
23036
|
*/
|
|
22998
|
-
async withdrawBTC(positionId, withdrawalAddress, withdrawalAmount) {
|
|
23037
|
+
async withdrawBTC(positionId, withdrawalAddress, withdrawalAmount, options) {
|
|
22999
23038
|
this.ensureInitialized();
|
|
23000
23039
|
try {
|
|
23001
23040
|
if (this.config.debug) {
|
|
@@ -23210,11 +23249,21 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
23210
23249
|
{}
|
|
23211
23250
|
);
|
|
23212
23251
|
}
|
|
23252
|
+
const withdrawAuthInput = await resolveAuthorizationInput(
|
|
23253
|
+
options?.authorizationProvider,
|
|
23254
|
+
signer,
|
|
23255
|
+
{
|
|
23256
|
+
positionId,
|
|
23257
|
+
timestamp: calculateNextQuantumTimestamp(),
|
|
23258
|
+
amount: BigInt(withdrawalAmount).toString(),
|
|
23259
|
+
destinationAddress: withdrawalAddress
|
|
23260
|
+
}
|
|
23261
|
+
);
|
|
23213
23262
|
const auth = await generateWithdrawAuthorization(
|
|
23214
23263
|
positionId,
|
|
23215
23264
|
BigInt(withdrawalAmount),
|
|
23216
23265
|
chainId,
|
|
23217
|
-
|
|
23266
|
+
withdrawAuthInput,
|
|
23218
23267
|
withdrawalAddress
|
|
23219
23268
|
// Pass destination address for message hash
|
|
23220
23269
|
);
|
|
@@ -24288,6 +24337,11 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
24288
24337
|
* (`MAX_ATTESTATION_AGE = 1 hour`) and replay-protects via
|
|
24289
24338
|
* `authorizedAt` binding.
|
|
24290
24339
|
*
|
|
24340
|
+
* WHO PAYS: in **service mode** lit-ops-server performs BOTH steps and its
|
|
24341
|
+
* relayer funds the transaction — the borrower is never prompted, because the
|
|
24342
|
+
* Bitcoin withdrawal this cleans up after has already settled. In **direct
|
|
24343
|
+
* mode** (CLI / tests / hardhat) the SDK's own signer submits step 2.
|
|
24344
|
+
*
|
|
24291
24345
|
* `invalidatorTxid` is either:
|
|
24292
24346
|
* - the borrower's own authorized broadcast (classified "consumed-as-authorized"
|
|
24293
24347
|
* by the LIT validator), once it has ≥6 confirmations on the BTC
|
|
@@ -24300,7 +24354,6 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
24300
24354
|
async recoverStaleSpend(params) {
|
|
24301
24355
|
this.ensureInitialized();
|
|
24302
24356
|
try {
|
|
24303
|
-
const signer = this.getSignerOrThrow();
|
|
24304
24357
|
const contractAddresses = this.getContractAddressesOrThrow();
|
|
24305
24358
|
const btcSpendAuthorizerAddress = contractAddresses.btcSpendAuthorizer;
|
|
24306
24359
|
if (!btcSpendAuthorizerAddress) {
|
|
@@ -24346,6 +24399,28 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
24346
24399
|
success: false,
|
|
24347
24400
|
error: json.error ?? "stale-spend recovery request failed"
|
|
24348
24401
|
};
|
|
24402
|
+
if (!attRes.success || !attRes.attestation) {
|
|
24403
|
+
return {
|
|
24404
|
+
success: false,
|
|
24405
|
+
failedStep: "attestation",
|
|
24406
|
+
error: attRes.error ?? attRes.reason ?? `LIT recovery attestation rejected${attRes.failedStep ? ` at step ${attRes.failedStep}` : ""}`
|
|
24407
|
+
};
|
|
24408
|
+
}
|
|
24409
|
+
if (!attRes.transactionHash && !attRes.alreadyCleared) {
|
|
24410
|
+
return {
|
|
24411
|
+
success: false,
|
|
24412
|
+
failedStep: "submit",
|
|
24413
|
+
error: "lit-ops-server returned a stale-spend attestation but did not submit it. The server is on a build without the recovery relayer, or its STALE_SPEND_RELAYER_PRIVATE_KEY is unset."
|
|
24414
|
+
};
|
|
24415
|
+
}
|
|
24416
|
+
return {
|
|
24417
|
+
success: true,
|
|
24418
|
+
...attRes.transactionHash && {
|
|
24419
|
+
transactionHash: attRes.transactionHash
|
|
24420
|
+
},
|
|
24421
|
+
...attRes.classification && { classification: attRes.classification },
|
|
24422
|
+
attestation: attRes.attestation
|
|
24423
|
+
};
|
|
24349
24424
|
} else {
|
|
24350
24425
|
const rpcUrl = params.rpcUrl ?? this.config.ethRpcUrl;
|
|
24351
24426
|
if (!rpcUrl) {
|
|
@@ -24355,6 +24430,13 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
24355
24430
|
error: "ethRpcUrl required for recoverStaleSpend \u2014 set DiamondHandsSDKConfig.ethRpcUrl or pass rpcUrl"
|
|
24356
24431
|
};
|
|
24357
24432
|
}
|
|
24433
|
+
if (!this.config.contractSigner) {
|
|
24434
|
+
return {
|
|
24435
|
+
success: false,
|
|
24436
|
+
failedStep: "config",
|
|
24437
|
+
error: "Contract signer not configured \u2014 recoverStaleSpend submits its own recovery transaction in direct mode. Set DiamondHandsSDKConfig.contractSigner, or use service mode, where the server relays and pays for it."
|
|
24438
|
+
};
|
|
24439
|
+
}
|
|
24358
24440
|
const sdkBitcoinProviderUrl = this.config.bitcoinProviders?.[0]?.url;
|
|
24359
24441
|
attRes = await this.litOps.requestStaleSpendRecoveryAttestation({
|
|
24360
24442
|
chainId,
|
|
@@ -24395,7 +24477,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
24395
24477
|
[
|
|
24396
24478
|
"function cancelStaleSpendWithProof(bytes32 positionId, bytes32 utxoKey, uint256 authorizedAt, string calldata invalidatorTxid, uint256 attestationTimestamp, bytes calldata litSignature) external"
|
|
24397
24479
|
],
|
|
24398
|
-
|
|
24480
|
+
this.getSignerOrThrow()
|
|
24399
24481
|
);
|
|
24400
24482
|
const tx = await btcSpendAuthorizer["cancelStaleSpendWithProof"](
|
|
24401
24483
|
attRes.attestation.positionId,
|
|
@@ -25203,10 +25285,22 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
25203
25285
|
"getLoanGrant",
|
|
25204
25286
|
data
|
|
25205
25287
|
);
|
|
25288
|
+
const raw = getBytes5(data);
|
|
25289
|
+
const withdrawScopeSupported = raw.length >= 128;
|
|
25290
|
+
const minWithdrawRatioBps = withdrawScopeSupported ? Number(
|
|
25291
|
+
AbiCoder2.defaultAbiCoder().decode(["uint32"], raw.slice(96, 128))[0]
|
|
25292
|
+
) : 0;
|
|
25206
25293
|
return {
|
|
25207
25294
|
borrower,
|
|
25208
25295
|
scopeBits: Number(scopeBits),
|
|
25209
|
-
minCollateralRatioBps: Number(minCollateralRatioBps)
|
|
25296
|
+
minCollateralRatioBps: Number(minCollateralRatioBps),
|
|
25297
|
+
minWithdrawRatioBps,
|
|
25298
|
+
/**
|
|
25299
|
+
* True when the deployed registry is v2.1.0+ and therefore honours `SCOPE_WITHDRAW`. The
|
|
25300
|
+
* frontend gates its Withdrawal control on this so the control is absent — not disabled,
|
|
25301
|
+
* not "coming soon" — on a chain whose registry cannot support it.
|
|
25302
|
+
*/
|
|
25303
|
+
withdrawScopeSupported
|
|
25210
25304
|
};
|
|
25211
25305
|
}
|
|
25212
25306
|
/**
|
|
@@ -25248,10 +25342,14 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
25248
25342
|
const mintEnabled = hasGrant && (scopes & 4) !== 0;
|
|
25249
25343
|
const floor = grant.minCollateralRatioBps;
|
|
25250
25344
|
const agent = user ? await registry.agentOf(user) : null;
|
|
25345
|
+
const withdrawEnabled = hasGrant && grant.withdrawScopeSupported && (scopes & 8) !== 0;
|
|
25251
25346
|
return {
|
|
25252
25347
|
renewEnabled: hasGrant && (scopes & 1) !== 0,
|
|
25253
25348
|
repayEnabled: hasGrant && (scopes & 2) !== 0,
|
|
25254
25349
|
mintEnabled,
|
|
25350
|
+
withdrawEnabled,
|
|
25351
|
+
withdrawScopeSupported: grant.withdrawScopeSupported,
|
|
25352
|
+
minWithdrawRatioBps: withdrawEnabled ? grant.minWithdrawRatioBps : 0,
|
|
25255
25353
|
minCollateralRatioBps: mintEnabled ? floor : 0,
|
|
25256
25354
|
effectiveMintFloorBps: mintEnabled ? Math.max(floor, protocolMin) : 0,
|
|
25257
25355
|
protocolMinFloorBps: protocolMin,
|
|
@@ -25437,8 +25535,83 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
25437
25535
|
const receipt = await tx.wait();
|
|
25438
25536
|
return { hash: tx.hash, blockNumber: receipt?.blockNumber ?? 0 };
|
|
25439
25537
|
}
|
|
25440
|
-
/**
|
|
25441
|
-
|
|
25538
|
+
/**
|
|
25539
|
+
* Grant the agent the WITHDRAW scope for a position (registry v2.1.0+). Borrower-signed.
|
|
25540
|
+
*
|
|
25541
|
+
* CONTRACT WALLETS ONLY, and this is enforced here rather than on-chain. The withdrawal
|
|
25542
|
+
* validator's only non-borrower arm is `checkSafeAgentWithdrawAuthorization`, which requires a
|
|
25543
|
+
* factory-provenanced `AgentModule` whose `safe()` is the borrower — something an EOA cannot
|
|
25544
|
+
* have. The registry deliberately does NOT check wallet type ("Callable by any borrower (this
|
|
25545
|
+
* function does not itself require a Safe)"), so an EOA can record a grant that no code path
|
|
25546
|
+
* will ever satisfy. Refusing here keeps the supported path from writing a dead record.
|
|
25547
|
+
*
|
|
25548
|
+
* That refusal is ergonomics, NOT enforcement — `enableAutoWithdraw` is `external` and reachable
|
|
25549
|
+
* from any wallet. The real reason delegated withdrawal is closed to EOAs is that they can sign
|
|
25550
|
+
* inside the ~44s window themselves, so delegating buys them nothing and only adds risk.
|
|
25551
|
+
*
|
|
25552
|
+
* @param minWithdrawRatioBps Post-withdrawal collateral-ratio floor for AGENT withdrawals.
|
|
25553
|
+
* Clamped by the same governance minimum as the mint floor. **Inert at zero debt** — it
|
|
25554
|
+
* is a ratio, and the post-withdrawal ratio is infinite for any amount once debt is 0.
|
|
25555
|
+
* The operative bound there is the quorum-gated destination allowlist. Do not surface
|
|
25556
|
+
* this to users as a cap on how much an agent may withdraw.
|
|
25557
|
+
* There is no in-place raise: changing it means disable-then-re-enable, deliberately —
|
|
25558
|
+
* on the one scope that moves custodied BTC, incident response can only disable outright.
|
|
25559
|
+
*/
|
|
25560
|
+
async enableAutoWithdraw(positionId, minWithdrawRatioBps, options) {
|
|
25561
|
+
const pid = this.toBytes32(positionId);
|
|
25562
|
+
await this.assertBorrowerIsContractWallet(pid);
|
|
25563
|
+
await this.assertMintFloorAtOrAboveProtocolMin(minWithdrawRatioBps, "withdraw");
|
|
25564
|
+
const { registry, agentAddress } = await this.prepareAgentDelegation(pid, options);
|
|
25565
|
+
const tx = await registry.enableAutoWithdraw(pid, minWithdrawRatioBps);
|
|
25566
|
+
const receipt = await tx.wait();
|
|
25567
|
+
return { hash: tx.hash, blockNumber: receipt?.blockNumber ?? 0, agentAddress };
|
|
25568
|
+
}
|
|
25569
|
+
/**
|
|
25570
|
+
* Revoke the WITHDRAW scope for a position (also zeroes its withdraw floor). Borrower-signed.
|
|
25571
|
+
*
|
|
25572
|
+
* No wallet-type check: revoking is the safe direction, and an EOA that recorded a grant before
|
|
25573
|
+
* this guard existed must be able to clear it.
|
|
25574
|
+
*/
|
|
25575
|
+
async disableAutoWithdraw(positionId) {
|
|
25576
|
+
const addr = this.agentDelegationRegistryOrThrow();
|
|
25577
|
+
const signer = this.getSignerOrThrow();
|
|
25578
|
+
const registry = AgentDelegationRegistry__factory.connect(addr, signer);
|
|
25579
|
+
const pid = this.toBytes32(positionId);
|
|
25580
|
+
const tx = await registry.disableAutoWithdraw(pid);
|
|
25581
|
+
const receipt = await tx.wait();
|
|
25582
|
+
await this.clearGate1IfNoScopesRemain(pid);
|
|
25583
|
+
return { hash: tx.hash, blockNumber: receipt?.blockNumber ?? 0 };
|
|
25584
|
+
}
|
|
25585
|
+
/**
|
|
25586
|
+
* Layer 2 of the withdrawal-delegation gating: refuse to write a grant that could never be
|
|
25587
|
+
* satisfied. See `enableAutoWithdraw` for why this lives here and not in the registry.
|
|
25588
|
+
*/
|
|
25589
|
+
async assertBorrowerIsContractWallet(pid) {
|
|
25590
|
+
const details = await this.getPositionDetailsView(pid);
|
|
25591
|
+
const borrower = details?.borrower;
|
|
25592
|
+
if (!borrower) {
|
|
25593
|
+
throw new SDKError({
|
|
25594
|
+
message: `Could not resolve the borrower for position ${pid} \u2014 refusing to write a delegation grant against an unverified position`,
|
|
25595
|
+
category: "VALIDATION" /* VALIDATION */,
|
|
25596
|
+
severity: "HIGH" /* HIGH */
|
|
25597
|
+
});
|
|
25598
|
+
}
|
|
25599
|
+
const code = await this.getProviderOrThrow().getCode(borrower);
|
|
25600
|
+
if (!code || code === "0x") {
|
|
25601
|
+
throw new SDKError({
|
|
25602
|
+
message: `Withdrawal delegation is only available to contract wallets. Borrower ${borrower} is an EOA, and the withdrawal validator has no delegate arm for one \u2014 an EOA can sign inside the withdrawal quantum window itself, so the grant would be recorded on-chain and never honoured.`,
|
|
25603
|
+
category: "VALIDATION" /* VALIDATION */,
|
|
25604
|
+
severity: "MEDIUM" /* MEDIUM */
|
|
25605
|
+
});
|
|
25606
|
+
}
|
|
25607
|
+
}
|
|
25608
|
+
/**
|
|
25609
|
+
* Pre-flight the registry's `FloorBelowProtocolMin` guard with a message a user can act on.
|
|
25610
|
+
*
|
|
25611
|
+
* `agentMintFloorMinBps` is the governance floor-of-floors for BOTH the mint and withdraw
|
|
25612
|
+
* grants — one lever, one invariant — so `scope` only shapes the message.
|
|
25613
|
+
*/
|
|
25614
|
+
async assertMintFloorAtOrAboveProtocolMin(floorBps, scope = "mint") {
|
|
25442
25615
|
if (!Number.isInteger(floorBps) || floorBps <= 0) {
|
|
25443
25616
|
throw new SDKError({
|
|
25444
25617
|
message: `Invalid collateral floor: ${floorBps} bps \u2014 must be a positive integer`,
|
|
@@ -25449,7 +25622,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
25449
25622
|
const protocolMin = await this.getAgentMintFloorMinBps();
|
|
25450
25623
|
if (protocolMin === 0) {
|
|
25451
25624
|
throw new SDKError({
|
|
25452
|
-
message:
|
|
25625
|
+
message: `Agent ${scope === "mint" ? "minting" : "withdrawal"} is not configured on this network (agentMintFloorMinBps is 0) \u2014 the registry would reject every ${scope} grant`,
|
|
25453
25626
|
category: "CONFIGURATION" /* CONFIGURATION */,
|
|
25454
25627
|
severity: "HIGH" /* HIGH */
|
|
25455
25628
|
});
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Loan Operations Interfaces
|
|
3
3
|
*/
|
|
4
4
|
import { LoanStatus } from "../../types/loanStatus";
|
|
5
|
+
import type { AuthorizationProvider } from "../../utils/authorization-provider.utils";
|
|
5
6
|
export type BitcoinAddresses = {
|
|
6
7
|
mainnet: string;
|
|
7
8
|
testnet: string;
|
|
@@ -176,6 +177,18 @@ export interface UCDMintRequest {
|
|
|
176
177
|
stubbedAvailableBtcSats?: string;
|
|
177
178
|
stubbedAuthorizedSpendsHash?: string;
|
|
178
179
|
};
|
|
180
|
+
/**
|
|
181
|
+
* Supplies the borrower authorization envelope instead of the connected
|
|
182
|
+
* signer — the seam a delegated Safe uses so its agent signs instantly
|
|
183
|
+
* rather than the SDK asking for an owner quorum inside a quantum window.
|
|
184
|
+
*
|
|
185
|
+
* Omit it and nothing changes: the SDK signs with the connected signer
|
|
186
|
+
* exactly as before, which is what every EOA borrower does.
|
|
187
|
+
*
|
|
188
|
+
* See `utils/authorization-provider.utils.ts` for why this is a callback and
|
|
189
|
+
* not a `{ timestamp, signature }` value.
|
|
190
|
+
*/
|
|
191
|
+
authorizationProvider?: AuthorizationProvider;
|
|
179
192
|
}
|
|
180
193
|
/**
|
|
181
194
|
* UCD Mint Result Interface
|
|
@@ -213,6 +226,18 @@ export interface RenewPositionResult {
|
|
|
213
226
|
export interface RenewPositionRequest {
|
|
214
227
|
positionId: string;
|
|
215
228
|
selectedTerm: number;
|
|
229
|
+
/**
|
|
230
|
+
* Supplies the borrower authorization envelope instead of the connected
|
|
231
|
+
* signer — the seam a delegated Safe uses so its agent signs instantly
|
|
232
|
+
* rather than the SDK asking for an owner quorum inside a quantum window.
|
|
233
|
+
*
|
|
234
|
+
* Omit it and nothing changes: the SDK signs with the connected signer
|
|
235
|
+
* exactly as before, which is what every EOA borrower does.
|
|
236
|
+
*
|
|
237
|
+
* See `utils/authorization-provider.utils.ts` for why this is a callback and
|
|
238
|
+
* not a `{ timestamp, signature }` value.
|
|
239
|
+
*/
|
|
240
|
+
authorizationProvider?: AuthorizationProvider;
|
|
216
241
|
}
|
|
217
242
|
/**
|
|
218
243
|
* BTC Withdrawal Result Interface
|
|
@@ -282,6 +307,18 @@ export interface PartialPaymentRequest {
|
|
|
282
307
|
positionId: string;
|
|
283
308
|
paymentAmount: number;
|
|
284
309
|
rpcUrl?: string;
|
|
310
|
+
/**
|
|
311
|
+
* Supplies the borrower authorization envelope instead of the connected
|
|
312
|
+
* signer — the seam a delegated Safe uses so its agent signs instantly
|
|
313
|
+
* rather than the SDK asking for an owner quorum inside a quantum window.
|
|
314
|
+
*
|
|
315
|
+
* Omit it and nothing changes: the SDK signs with the connected signer
|
|
316
|
+
* exactly as before, which is what every EOA borrower does.
|
|
317
|
+
*
|
|
318
|
+
* See `utils/authorization-provider.utils.ts` for why this is a callback and
|
|
319
|
+
* not a `{ timestamp, signature }` value.
|
|
320
|
+
*/
|
|
321
|
+
authorizationProvider?: AuthorizationProvider;
|
|
285
322
|
}
|
|
286
323
|
/**
|
|
287
324
|
* Confirm Balance Result Interface
|