@gvnrdao/dh-sdk 0.0.300 → 0.0.303
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/constants/chunks/deployment-addresses.d.ts +2 -0
- package/dist/constants/chunks/network-configs.d.ts +2 -0
- package/dist/deployments.js +137 -30
- package/dist/deployments.mjs +137 -30
- package/dist/graphs/diamond-hands.d.ts +18 -0
- package/dist/index.js +1839 -929
- package/dist/index.mjs +1747 -837
- package/dist/interfaces/chunks/config.i.d.ts +22 -0
- package/dist/modules/diamond-hands-sdk.d.ts +41 -0
- package/dist/modules/loan/loan-query.module.d.ts +23 -0
- package/dist/types/authorization-params.d.ts +0 -2
- package/dist/utils/btc-withdrawal-message.d.ts +31 -0
- package/dist/utils/chunks/eip1559-broadcast.utils.d.ts +15 -24
- package/dist/utils/quantum-revert.utils.d.ts +9 -0
- package/dist/utils/quantum-submission.utils.d.ts +93 -0
- package/dist/utils/server-session-store.d.ts +56 -0
- package/dist/utils/server-session.d.ts +62 -9
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -87,7 +87,8 @@ var init_deployment_addresses = __esm({
|
|
|
87
87
|
mockUsdtToken: "0xF8b7fB38b17a310960E47805Be9adba0b0e0394D",
|
|
88
88
|
PositionDelegateRegistry: "0x482369De283622E7b05731875ec137d4E3D208F9",
|
|
89
89
|
FeeRecipientRegistry: "0x95795F8403DDb629E8527B2934C4e71f5fC0C374",
|
|
90
|
-
BitcoinWithdrawalAddressRegistry: "0x6376D392097F0A81e51BD83960A97EA20eaf1dbe"
|
|
90
|
+
BitcoinWithdrawalAddressRegistry: "0x6376D392097F0A81e51BD83960A97EA20eaf1dbe",
|
|
91
|
+
CarryAgentRegistry: "0x5595CB538D44E6Cf179D20364fD631D0477e69f7"
|
|
91
92
|
},
|
|
92
93
|
upgraded: {
|
|
93
94
|
PositionManager: {
|
|
@@ -349,7 +350,9 @@ var init_deployment_addresses = __esm({
|
|
|
349
350
|
FEE_RECIPIENT_REGISTRY_IMPL: "0xd08513a7cB2dd354B26cDDf549616F12054c34c9",
|
|
350
351
|
OPERATION_AUTHORIZATION_REGISTRY_IMPL: "0xe0D981B60351f0f8fFDA2b50C8180Fb37CeF3883",
|
|
351
352
|
BITCOIN_WITHDRAWAL_ADDRESS_REGISTRY: "0x6376D392097F0A81e51BD83960A97EA20eaf1dbe",
|
|
352
|
-
BITCOIN_WITHDRAWAL_ADDRESS_REGISTRY_IMPL: "0xFF270BC4470dBA80430Bd1a3a66EB691599354fA"
|
|
353
|
+
BITCOIN_WITHDRAWAL_ADDRESS_REGISTRY_IMPL: "0xFF270BC4470dBA80430Bd1a3a66EB691599354fA",
|
|
354
|
+
CARRY_AGENT_REGISTRY: "0x5595CB538D44E6Cf179D20364fD631D0477e69f7",
|
|
355
|
+
CARRY_AGENT_REGISTRY_IMPL: "0x1b11a3D5e46F3672E643c997f0d569DFf0b64a01"
|
|
353
356
|
}
|
|
354
357
|
};
|
|
355
358
|
SEPOLIA_CONTRACTS = {
|
|
@@ -377,7 +380,8 @@ var init_deployment_addresses = __esm({
|
|
|
377
380
|
mockUsdtToken: "0xF8b7fB38b17a310960E47805Be9adba0b0e0394D",
|
|
378
381
|
PositionDelegateRegistry: "0x482369De283622E7b05731875ec137d4E3D208F9",
|
|
379
382
|
FeeRecipientRegistry: "0x95795F8403DDb629E8527B2934C4e71f5fC0C374",
|
|
380
|
-
BitcoinWithdrawalAddressRegistry: "0x6376D392097F0A81e51BD83960A97EA20eaf1dbe"
|
|
383
|
+
BitcoinWithdrawalAddressRegistry: "0x6376D392097F0A81e51BD83960A97EA20eaf1dbe",
|
|
384
|
+
CarryAgentRegistry: "0x5595CB538D44E6Cf179D20364fD631D0477e69f7"
|
|
381
385
|
};
|
|
382
386
|
MAINNET_DEPLOYMENT = {
|
|
383
387
|
network: "mainnet",
|
|
@@ -412,10 +416,85 @@ var init_deployment_addresses = __esm({
|
|
|
412
416
|
upgraded: {
|
|
413
417
|
SimplePSMV2: {
|
|
414
418
|
previousImplementation: "0x1a555FB8D6EDdF755a4575E541651EdB8B49a15A",
|
|
415
|
-
newImplementation: "
|
|
416
|
-
upgradedAt: "2026-
|
|
417
|
-
upgradedBy: "0xEba7BfAeB35ee18408b424Ee0BB45AdDf776589f",
|
|
418
|
-
reason: "
|
|
419
|
+
newImplementation: "0x8717761444b1822c2E6916aB5bD9ac96c91b6DF5",
|
|
420
|
+
upgradedAt: "2026-07-06T10:41:35.000Z",
|
|
421
|
+
upgradedBy: "AdminModule/Safe audit-batch upgradeToAndCall; executed by 0xEba7BfAeB35ee18408b424Ee0BB45AdDf776589f in tx 0x5052d1559d56b8e178699183e56c4fd84a163e987365c5af9c8d669a723c35d6 (block 25472960)",
|
|
422
|
+
reason: "Reconciled from chain \u2014 mainnet audit rollout batch (cross-ref onchain-auth/.safe-batches/mainnet-audit-execute.json)",
|
|
423
|
+
executeTx: "0x5052d1559d56b8e178699183e56c4fd84a163e987365c5af9c8d669a723c35d6",
|
|
424
|
+
block: 25472960
|
|
425
|
+
},
|
|
426
|
+
CollateralManagerModule: {
|
|
427
|
+
previousImplementation: "0x1919860e528Cd2AbbD31a17858c986b8aCc8C185",
|
|
428
|
+
newImplementation: "0x55110B9dd6B2c9f812564e7a1ef42b3c76E1A3Bb",
|
|
429
|
+
upgradedAt: "2026-07-08T20:33:59Z",
|
|
430
|
+
upgradedBy: "AdminModule generic-op 0x740efe0cfe85044479cbd8ee3058aa244057d90703dd2b37044dd6787cbf3485 (3-of-5 ADMIN_ROLE Safes, 24h timelock; executed by 0xfb3B8cbeFB7D666A6Af34C53e189EdC31862a7be in tx 0x430c8e450f12d3d9359cde356ac5e2ef39661d7a89884b5674a596c1090f6cff)",
|
|
431
|
+
reason: "Validator gas fix (#420): BitcoinAddressValidator packed-table lookups; VERSION 1.3.0 -> 1.3.1"
|
|
432
|
+
},
|
|
433
|
+
BitcoinWithdrawalAddressRegistry: {
|
|
434
|
+
previousImplementation: "0x11731bFEC3068F2cc19D016381dfEA3B93013b1C",
|
|
435
|
+
newImplementation: "0x190c08300Ae650fb88ed8156445BBeb13bE9E8a3",
|
|
436
|
+
upgradedAt: "2026-07-08T20:33:59Z",
|
|
437
|
+
upgradedBy: "AdminModule generic-op 0x2603a486c0f34554b864cc1d909b325c7e105331da10ec0fd7e0b9373fa76207 (3-of-5 ADMIN_ROLE Safes, 24h timelock; executed by 0xfb3B8cbeFB7D666A6Af34C53e189EdC31862a7be in tx 0x430c8e450f12d3d9359cde356ac5e2ef39661d7a89884b5674a596c1090f6cff)",
|
|
438
|
+
reason: "Validator gas fix (#420): BitcoinAddressValidator packed-table lookups; VERSION 1.0.0 -> 1.0.1"
|
|
439
|
+
},
|
|
440
|
+
UCDController: {
|
|
441
|
+
newImplementation: "0x28B8cAB89D89B3F83581FD2290de2Ce84535bd0A",
|
|
442
|
+
upgradedBy: "Reconciled from chain \u2014 Upgraded event log unavailable; provenance omitted (never fabricated)",
|
|
443
|
+
reason: "Reconciled from chain \u2014 mainnet audit rollout batch (cross-ref onchain-auth/.safe-batches/mainnet-audit-execute.json)"
|
|
444
|
+
},
|
|
445
|
+
PositionManagerCoreModule: {
|
|
446
|
+
previousImplementation: "0x6C8b0FB93F8d0239254aefaC24d4264cA2F8AAeB",
|
|
447
|
+
newImplementation: "0x5067Ea1d40b2Aaf4ad693bD9b6e7463C1A29E849",
|
|
448
|
+
upgradedAt: "2026-07-06T08:02:35.000Z",
|
|
449
|
+
upgradedBy: "AdminModule/Safe audit-batch upgradeToAndCall; executed by 0x2002827d34A781883Ec4c83c36ECC140c3911F2B in tx 0xeb2bfe77d46394b6d0afd46beb0a80cd9beb91f6771ccb59ff9fba27d4af4f3d (block 25472167)",
|
|
450
|
+
reason: "Reconciled from chain \u2014 mainnet audit rollout batch (cross-ref onchain-auth/.safe-batches/mainnet-audit-execute.json)",
|
|
451
|
+
executeTx: "0xeb2bfe77d46394b6d0afd46beb0a80cd9beb91f6771ccb59ff9fba27d4af4f3d",
|
|
452
|
+
block: 25472167
|
|
453
|
+
},
|
|
454
|
+
LiquidationManagerModule: {
|
|
455
|
+
previousImplementation: "0xc2f4517eeD7c4c516939a64c7938a6ab432D99c4",
|
|
456
|
+
newImplementation: "0xe7444C1d9652dAb31BaAdBE4C7c7e7Df3515dA46",
|
|
457
|
+
upgradedAt: "2026-07-06T10:40:47.000Z",
|
|
458
|
+
upgradedBy: "AdminModule/Safe audit-batch upgradeToAndCall; executed by 0xEba7BfAeB35ee18408b424Ee0BB45AdDf776589f in tx 0xc173b97af68cc9172ea0ddc91a2c90bf02eac1505e749d92aa044aa69891f48c (block 25472956)",
|
|
459
|
+
reason: "Reconciled from chain \u2014 mainnet audit rollout batch (cross-ref onchain-auth/.safe-batches/mainnet-audit-execute.json)",
|
|
460
|
+
executeTx: "0xc173b97af68cc9172ea0ddc91a2c90bf02eac1505e749d92aa044aa69891f48c",
|
|
461
|
+
block: 25472956
|
|
462
|
+
},
|
|
463
|
+
BTCSpendAuthorizer: {
|
|
464
|
+
previousImplementation: "0xb003ed89F695E8819a85513fE17260b805B3c478",
|
|
465
|
+
newImplementation: "0x40c9e8C46C65E0A57BB63da5368868A9E8Df81A5",
|
|
466
|
+
upgradedAt: "2026-07-06T10:40:59.000Z",
|
|
467
|
+
upgradedBy: "AdminModule/Safe audit-batch upgradeToAndCall; executed by 0xEba7BfAeB35ee18408b424Ee0BB45AdDf776589f in tx 0xe1b1884a7e76c7fd569522545a04e49d5128d83d27b3892cda59f143b53b5281 (block 25472957)",
|
|
468
|
+
reason: "Reconciled from chain \u2014 mainnet audit rollout batch (cross-ref onchain-auth/.safe-batches/mainnet-audit-execute.json)",
|
|
469
|
+
executeTx: "0xe1b1884a7e76c7fd569522545a04e49d5128d83d27b3892cda59f143b53b5281",
|
|
470
|
+
block: 25472957
|
|
471
|
+
},
|
|
472
|
+
PositionManager: {
|
|
473
|
+
previousImplementation: "0x9f52BaBB40FbFC5863e45B1A4014cB433950c684",
|
|
474
|
+
newImplementation: "0x600Dc0D1C2c0F94882B6Bc09172f8650047Ad5B1",
|
|
475
|
+
upgradedAt: "2026-07-06T08:42:47.000Z",
|
|
476
|
+
upgradedBy: "AdminModule/Safe audit-batch upgradeToAndCall; executed by 0x2002827d34A781883Ec4c83c36ECC140c3911F2B in tx 0xa1fbe31cb71736514a868e681573dff22c9fab91d69cec157a24a30e29eadbe6 (block 25472368)",
|
|
477
|
+
reason: "Reconciled from chain \u2014 mainnet audit rollout batch (cross-ref onchain-auth/.safe-batches/mainnet-audit-execute.json)",
|
|
478
|
+
executeTx: "0xa1fbe31cb71736514a868e681573dff22c9fab91d69cec157a24a30e29eadbe6",
|
|
479
|
+
block: 25472368
|
|
480
|
+
},
|
|
481
|
+
LoanOperationsManagerModule: {
|
|
482
|
+
previousImplementation: "0x56671D671e492Dae625413d463D47605Cf54edf6",
|
|
483
|
+
newImplementation: "0x7d90aAB4466f59BA9aC9C7a9BF14dC53734336e7",
|
|
484
|
+
upgradedAt: "2026-07-06T10:41:23.000Z",
|
|
485
|
+
upgradedBy: "AdminModule/Safe audit-batch upgradeToAndCall; executed by 0xEba7BfAeB35ee18408b424Ee0BB45AdDf776589f in tx 0x327ae9f7a3917788d22dfe95611e1afcd18583f210b03a119d9f04eca50b9e7a (block 25472959)",
|
|
486
|
+
reason: "Reconciled from chain \u2014 mainnet audit rollout batch (cross-ref onchain-auth/.safe-batches/mainnet-audit-execute.json)",
|
|
487
|
+
executeTx: "0x327ae9f7a3917788d22dfe95611e1afcd18583f210b03a119d9f04eca50b9e7a",
|
|
488
|
+
block: 25472959
|
|
489
|
+
},
|
|
490
|
+
OperationAuthorizationRegistry: {
|
|
491
|
+
previousImplementation: "0x327662917Ac8EEda47851d74AF6A151348636fCe",
|
|
492
|
+
newImplementation: "0x493118746840EDB2D396E71632D2E774CA14070e",
|
|
493
|
+
upgradedAt: "2026-07-06T10:41:47.000Z",
|
|
494
|
+
upgradedBy: "AdminModule/Safe audit-batch upgradeToAndCall; executed by 0xEba7BfAeB35ee18408b424Ee0BB45AdDf776589f in tx 0x9820e259700865a281b8376e248044048ba2fed1d17c811ac66ac54d36711a5b (block 25472961)",
|
|
495
|
+
reason: "Reconciled from chain \u2014 mainnet audit rollout batch (cross-ref onchain-auth/.safe-batches/mainnet-audit-execute.json)",
|
|
496
|
+
executeTx: "0x9820e259700865a281b8376e248044048ba2fed1d17c811ac66ac54d36711a5b",
|
|
497
|
+
block: 25472961
|
|
419
498
|
}
|
|
420
499
|
},
|
|
421
500
|
latestEnv: {
|
|
@@ -428,9 +507,37 @@ var init_deployment_addresses = __esm({
|
|
|
428
507
|
BITCOIN_PROVIDER_REGISTRY: "0x4ab3Fb96e02893Ed6FAa61bad1918390c8DbbFFC",
|
|
429
508
|
BITCOIN_PROVIDER_REGISTRY_IMPL: "0x7Bc9473E45fb3EA594e8b13992F57D0444902ddB",
|
|
430
509
|
BITCOIN_WITHDRAWAL_ADDRESS_REGISTRY: "0xf667f77FDba7458345f227eB6Bfe4148243E7270",
|
|
431
|
-
BITCOIN_WITHDRAWAL_ADDRESS_REGISTRY_IMPL: "
|
|
510
|
+
BITCOIN_WITHDRAWAL_ADDRESS_REGISTRY_IMPL: "0x190c08300Ae650fb88ed8156445BBeb13bE9E8a3",
|
|
432
511
|
SIMPLE_PSM_V2: "0xfE0C14B4E614f40Bf58845b4B04CeD17fd116532",
|
|
433
|
-
SIMPLE_PSM_V2_IMPL: "
|
|
512
|
+
SIMPLE_PSM_V2_IMPL: "0x8717761444b1822c2E6916aB5bD9ac96c91b6DF5",
|
|
513
|
+
UCD_CONTROLLER: "0x4b4f8063048bCD59D469219E3f07C6aaDF4f6157",
|
|
514
|
+
UCD_CONTROLLER_IMPL: "0x28B8cAB89D89B3F83581FD2290de2Ce84535bd0A",
|
|
515
|
+
UCD_TOKEN: "0xaAaB6B05E5EecB015898483d4e6703d7F218850b",
|
|
516
|
+
UCD_TOKEN_IMPL: "0x5f09C660036021D7e916DF47234C24a2536C3A72",
|
|
517
|
+
POSITION_MANAGER_CORE_MODULE: "0xd895B685914c1BE4e1c7aab9f89a90c203DE8d22",
|
|
518
|
+
POSITION_MANAGER_CORE_MODULE_IMPL: "0x5067Ea1d40b2Aaf4ad693bD9b6e7463C1A29E849",
|
|
519
|
+
TERM_MANAGER_MODULE: "0xDB99CdF883ffa38acD099bC39F8d1f4508c9239E",
|
|
520
|
+
TERM_MANAGER_MODULE_IMPL: "0x971f63f92990e59270E971612dd399fF2C63B0c3",
|
|
521
|
+
COLLATERAL_MANAGER_MODULE: "0x2Eb74FEc478218f5C260aAc78Bb8eC297F9d7135",
|
|
522
|
+
COLLATERAL_MANAGER_MODULE_IMPL: "0x55110B9dd6B2c9f812564e7a1ef42b3c76E1A3Bb",
|
|
523
|
+
LIQUIDATION_MANAGER_MODULE: "0x74B28695352E7bf736D6c9F76ccb7B4439477De9",
|
|
524
|
+
LIQUIDATION_MANAGER_MODULE_IMPL: "0xe7444C1d9652dAb31BaAdBE4C7c7e7Df3515dA46",
|
|
525
|
+
CIRCUIT_BREAKER_MODULE: "0xF7b545d3a4Ee056D06909AB367E2F7bFFE3cB1BC",
|
|
526
|
+
CIRCUIT_BREAKER_MODULE_IMPL: "0x40e8328D61342C6ADC51dfdC723296c908f95038",
|
|
527
|
+
ADMIN_MODULE: "0x0023B71B586795eE5fa4E83E384345C1EC8Bb946",
|
|
528
|
+
ADMIN_MODULE_IMPL: "0xb4C5C7E051A1e672F014B25CB5D55B225Aa0D512",
|
|
529
|
+
BTC_SPEND_AUTHORIZER: "0xfBbF108486fa07F1B06a811C5b66ed68DD9c508a",
|
|
530
|
+
BTC_SPEND_AUTHORIZER_IMPL: "0x40c9e8C46C65E0A57BB63da5368868A9E8Df81A5",
|
|
531
|
+
POSITION_MANAGER_VIEWS: "0x9225647a30bbACA4Acc0491B786869036914242e",
|
|
532
|
+
POSITION_MANAGER_VIEWS_IMPL: "0x9225647a30bbACA4Acc0491B786869036914242e",
|
|
533
|
+
POSITION_MANAGER: "0x12A1836e56139ccaD8722B89519d84233efc224B",
|
|
534
|
+
POSITION_MANAGER_IMPL: "0x600Dc0D1C2c0F94882B6Bc09172f8650047Ad5B1",
|
|
535
|
+
LOAN_OPERATIONS_MANAGER_MODULE: "0x2561974A40F1AEed1251d0A090A83cF8536FB9E9",
|
|
536
|
+
LOAN_OPERATIONS_MANAGER_MODULE_IMPL: "0x7d90aAB4466f59BA9aC9C7a9BF14dC53734336e7",
|
|
537
|
+
UPGRADE_VALIDATOR: "0xC46Ae71F22e3741A6CF46E399aE914C4868fEbed",
|
|
538
|
+
UPGRADE_VALIDATOR_IMPL: "0x4f835e77D1211299a1cbb8Aa2b42d9B390cC95CA",
|
|
539
|
+
OPERATION_AUTHORIZATION_REGISTRY: "0xD4505645b724754f5170a2027804a4265e860d44",
|
|
540
|
+
OPERATION_AUTHORIZATION_REGISTRY_IMPL: "0x493118746840EDB2D396E71632D2E774CA14070e"
|
|
434
541
|
}
|
|
435
542
|
};
|
|
436
543
|
MAINNET_CONTRACTS = {
|
|
@@ -461,44 +568,44 @@ var init_deployment_addresses = __esm({
|
|
|
461
568
|
LOCALHOST_DEPLOYMENT = {
|
|
462
569
|
network: "localhost",
|
|
463
570
|
chainId: 31337,
|
|
464
|
-
timestamp: "2026-
|
|
571
|
+
timestamp: "2026-06-11T12:30:59.589Z",
|
|
465
572
|
deployer: "",
|
|
466
573
|
contracts: {
|
|
467
|
-
MessageHashBuilder: "
|
|
468
|
-
UpgradeValidator: "
|
|
574
|
+
MessageHashBuilder: "0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8",
|
|
575
|
+
UpgradeValidator: "0x610178dA211FEF7D417bC0e6FeD39F05609AD788",
|
|
469
576
|
UCDToken: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
|
|
470
577
|
UCDController: "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9",
|
|
471
|
-
PositionManagerCoreModule: "
|
|
472
|
-
TermManagerModule: "
|
|
473
|
-
LoanOperationsManagerModule: "
|
|
578
|
+
PositionManagerCoreModule: "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0",
|
|
579
|
+
TermManagerModule: "0x9A676e781A523b5d0C0e43731313A708CB607508",
|
|
580
|
+
LoanOperationsManagerModule: "0x3Aa5ebB10DC797CAC828524e59A333d0A371443c",
|
|
474
581
|
BTCSpendAuthorizer: "0x59b670e9fA9D0A427751Af201D676719a970857b",
|
|
475
582
|
CollateralManagerModule: "0xa85233C63b9Ee964Add6F2cffe00Fd84eb32338f",
|
|
476
583
|
LiquidationManagerModule: "0x7a2088a1bFc9d81c55368AE168C2C02570cB814F",
|
|
477
|
-
CircuitBreakerModule: "
|
|
584
|
+
CircuitBreakerModule: "0x959922bE3CAee4b8Cd9a407cc3ac1C251C2007B1",
|
|
478
585
|
AdminModule: "0xc5a5C42992dECbae36851359345FE25997F5C42d",
|
|
479
|
-
PositionManagerViews: "
|
|
480
|
-
PositionManager: "
|
|
481
|
-
OperationAuthorizationRegistry: "
|
|
482
|
-
PKPValidation: "
|
|
586
|
+
PositionManagerViews: "0x9A9f2CCfdE556A7E9Ff0848998Aa4a0CFD8863AE",
|
|
587
|
+
PositionManager: "0xf5059a5D33d5853360D16C683c16e67980206f36",
|
|
588
|
+
OperationAuthorizationRegistry: "0xF8e31cb472bc70500f08Cd84917E5A1912Ec8397",
|
|
589
|
+
PKPValidation: "0xc0F115A19107322cFBf1cDBC7ea011C19EbDB4F8"
|
|
483
590
|
}
|
|
484
591
|
};
|
|
485
592
|
LOCALHOST_CONTRACTS = {
|
|
486
|
-
MessageHashBuilder: "
|
|
487
|
-
UpgradeValidator: "
|
|
593
|
+
MessageHashBuilder: "0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8",
|
|
594
|
+
UpgradeValidator: "0x610178dA211FEF7D417bC0e6FeD39F05609AD788",
|
|
488
595
|
UCDToken: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
|
|
489
596
|
UCDController: "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9",
|
|
490
|
-
PositionManagerCoreModule: "
|
|
491
|
-
TermManagerModule: "
|
|
492
|
-
LoanOperationsManagerModule: "
|
|
597
|
+
PositionManagerCoreModule: "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0",
|
|
598
|
+
TermManagerModule: "0x9A676e781A523b5d0C0e43731313A708CB607508",
|
|
599
|
+
LoanOperationsManagerModule: "0x3Aa5ebB10DC797CAC828524e59A333d0A371443c",
|
|
493
600
|
BTCSpendAuthorizer: "0x59b670e9fA9D0A427751Af201D676719a970857b",
|
|
494
601
|
CollateralManagerModule: "0xa85233C63b9Ee964Add6F2cffe00Fd84eb32338f",
|
|
495
602
|
LiquidationManagerModule: "0x7a2088a1bFc9d81c55368AE168C2C02570cB814F",
|
|
496
|
-
CircuitBreakerModule: "
|
|
603
|
+
CircuitBreakerModule: "0x959922bE3CAee4b8Cd9a407cc3ac1C251C2007B1",
|
|
497
604
|
AdminModule: "0xc5a5C42992dECbae36851359345FE25997F5C42d",
|
|
498
|
-
PositionManagerViews: "
|
|
499
|
-
PositionManager: "
|
|
500
|
-
OperationAuthorizationRegistry: "
|
|
501
|
-
PKPValidation: "
|
|
605
|
+
PositionManagerViews: "0x9A9f2CCfdE556A7E9Ff0848998Aa4a0CFD8863AE",
|
|
606
|
+
PositionManager: "0xf5059a5D33d5853360D16C683c16e67980206f36",
|
|
607
|
+
OperationAuthorizationRegistry: "0xF8e31cb472bc70500f08Cd84917E5A1912Ec8397",
|
|
608
|
+
PKPValidation: "0xc0F115A19107322cFBf1cDBC7ea011C19EbDB4F8"
|
|
502
609
|
};
|
|
503
610
|
ALL_DEPLOYMENTS = {
|
|
504
611
|
sepolia: SEPOLIA_DEPLOYMENT,
|
|
@@ -1190,30 +1297,30 @@ var require_pkg_src = __commonJS({
|
|
|
1190
1297
|
}
|
|
1191
1298
|
},
|
|
1192
1299
|
btcTransactionSigner: {
|
|
1193
|
-
cid: "
|
|
1300
|
+
cid: "QmSJkQAw5SJ39EGEwzWZJ39ApNN4834VgKS5rhm25YCgfk",
|
|
1194
1301
|
authorizedCidHex: cidToHex(
|
|
1195
|
-
"
|
|
1302
|
+
"QmSJkQAw5SJ39EGEwzWZJ39ApNN4834VgKS5rhm25YCgfk"
|
|
1196
1303
|
),
|
|
1197
1304
|
name: "Btc Transaction Signer",
|
|
1198
1305
|
description: "Production Btc Transaction Signer",
|
|
1199
|
-
version: "1.
|
|
1306
|
+
version: "1.1.1",
|
|
1200
1307
|
deployed: true,
|
|
1201
|
-
deployedAt:
|
|
1202
|
-
size:
|
|
1203
|
-
hash: "
|
|
1308
|
+
deployedAt: 1784316496778,
|
|
1309
|
+
size: 63679,
|
|
1310
|
+
hash: "b12aa725797af5ef07f8e6945f05741f583d6e479a4670b1121e97c58fe63191"
|
|
1204
1311
|
},
|
|
1205
1312
|
ucdMintValidator: {
|
|
1206
|
-
cid: "
|
|
1313
|
+
cid: "QmYVRMfiSC641JPbE819szSDw3YrG6uSrZc27CZkyYTGtW",
|
|
1207
1314
|
authorizedCidHex: cidToHex(
|
|
1208
|
-
"
|
|
1315
|
+
"QmYVRMfiSC641JPbE819szSDw3YrG6uSrZc27CZkyYTGtW"
|
|
1209
1316
|
),
|
|
1210
1317
|
name: "Ucd Mint Validator",
|
|
1211
1318
|
description: "Production Ucd Mint Validator",
|
|
1212
|
-
version: "1.
|
|
1319
|
+
version: "1.2.1",
|
|
1213
1320
|
deployed: true,
|
|
1214
|
-
deployedAt:
|
|
1215
|
-
size:
|
|
1216
|
-
hash: "
|
|
1321
|
+
deployedAt: 1784316505431,
|
|
1322
|
+
size: 80381,
|
|
1323
|
+
hash: "132a0a8e6e9a1b85d02e00c0e57eaa371d825717255a28c9e644e4b521760026",
|
|
1217
1324
|
validatorWalletAddress: "0x82DB9F515957aA380B596C6eA2Bd30CEF181f236",
|
|
1218
1325
|
pkp: {
|
|
1219
1326
|
publicKey: "0x041ab17cd91fc5c0b761eea6092d032807561b621b82c488826776e04a9158d61ba64d809a4729f0501289d980732cb06d8dfd06999dd9c8efd2f495dad78b31bb",
|
|
@@ -1221,17 +1328,17 @@ var require_pkg_src = __commonJS({
|
|
|
1221
1328
|
}
|
|
1222
1329
|
},
|
|
1223
1330
|
processPaymentValidator: {
|
|
1224
|
-
cid: "
|
|
1331
|
+
cid: "QmadmdFDH6sS7YLusUuuLkkt7n51iUj1bJ5KKTLhmro2iQ",
|
|
1225
1332
|
authorizedCidHex: cidToHex(
|
|
1226
|
-
"
|
|
1333
|
+
"QmadmdFDH6sS7YLusUuuLkkt7n51iUj1bJ5KKTLhmro2iQ"
|
|
1227
1334
|
),
|
|
1228
1335
|
name: "Process Payment Validator",
|
|
1229
1336
|
description: "Production Process Payment Validator",
|
|
1230
|
-
version: "1.
|
|
1337
|
+
version: "1.2.1",
|
|
1231
1338
|
deployed: true,
|
|
1232
|
-
deployedAt:
|
|
1233
|
-
size:
|
|
1234
|
-
hash: "
|
|
1339
|
+
deployedAt: 1784316520125,
|
|
1340
|
+
size: 76534,
|
|
1341
|
+
hash: "dbdf43d3f1636e0ee883d349ca28f24c5c38ed7e062b76ca75498aaf5fe594ea",
|
|
1235
1342
|
validatorWalletAddress: "0x82DB9F515957aA380B596C6eA2Bd30CEF181f236",
|
|
1236
1343
|
pkp: {
|
|
1237
1344
|
publicKey: "0x04cfccb9b6c7addc79469c0e9fafebef4c70801302c62cdb26b610723a9b8527d23f98a40158c7340d83fd0e62d02b5ec74c12f7a86d98e8c6973b4811110f19ad",
|
|
@@ -1239,17 +1346,17 @@ var require_pkg_src = __commonJS({
|
|
|
1239
1346
|
}
|
|
1240
1347
|
},
|
|
1241
1348
|
extendPositionValidator: {
|
|
1242
|
-
cid: "
|
|
1349
|
+
cid: "QmespREceysQ7WPqGKg1VpHB3hf8zsreMY8HSjiQ2xoAN4",
|
|
1243
1350
|
authorizedCidHex: cidToHex(
|
|
1244
|
-
"
|
|
1351
|
+
"QmespREceysQ7WPqGKg1VpHB3hf8zsreMY8HSjiQ2xoAN4"
|
|
1245
1352
|
),
|
|
1246
1353
|
name: "Extend Position Validator",
|
|
1247
1354
|
description: "Production Extend Position Validator",
|
|
1248
|
-
version: "1.
|
|
1355
|
+
version: "1.2.1",
|
|
1249
1356
|
deployed: true,
|
|
1250
|
-
deployedAt:
|
|
1251
|
-
size:
|
|
1252
|
-
hash: "
|
|
1357
|
+
deployedAt: 1784316534278,
|
|
1358
|
+
size: 74352,
|
|
1359
|
+
hash: "c17bfc9abef16f2372316a14c14281e2acc98afdd1662d095360f1d1c5383f1d",
|
|
1253
1360
|
validatorWalletAddress: "0x82DB9F515957aA380B596C6eA2Bd30CEF181f236",
|
|
1254
1361
|
pkp: {
|
|
1255
1362
|
publicKey: "0x04e52338f6a0c3362800f1a94c4c8f08be5030dde0fa217388c53d1c1a4790f4443c9f2886b2d2d8f4b75a9a156e9b028011b1a1cf7648da0e556aa980ec692836",
|
|
@@ -1257,17 +1364,17 @@ var require_pkg_src = __commonJS({
|
|
|
1257
1364
|
}
|
|
1258
1365
|
},
|
|
1259
1366
|
btcWithdrawal: {
|
|
1260
|
-
cid: "
|
|
1367
|
+
cid: "QmYG1o9qMBZsEMDE9DNsJMzQmvxfj6jTu9V37WwuJZAtC2",
|
|
1261
1368
|
authorizedCidHex: cidToHex(
|
|
1262
|
-
"
|
|
1369
|
+
"QmYG1o9qMBZsEMDE9DNsJMzQmvxfj6jTu9V37WwuJZAtC2"
|
|
1263
1370
|
),
|
|
1264
1371
|
name: "Btc Withdrawal",
|
|
1265
1372
|
description: "Production Btc Withdrawal",
|
|
1266
|
-
version: "1.
|
|
1373
|
+
version: "1.2.1",
|
|
1267
1374
|
deployed: true,
|
|
1268
|
-
deployedAt:
|
|
1269
|
-
size:
|
|
1270
|
-
hash: "
|
|
1375
|
+
deployedAt: 1784316549409,
|
|
1376
|
+
size: 90692,
|
|
1377
|
+
hash: "ca7276763142fe1fc812b70f6319973000faf338a91c531f898f8f6406035c64",
|
|
1271
1378
|
validatorWalletAddress: "0x82DB9F515957aA380B596C6eA2Bd30CEF181f236",
|
|
1272
1379
|
pkp: {
|
|
1273
1380
|
publicKey: "0x043616787c5432415c24378c4ef48de2bcd6bb7b575b837e3cff09171802662da7105d79586c7659677a0ecbaddac4cce06cb2a11f69a16fa0c4d7002ac7d51a4d",
|
|
@@ -1275,17 +1382,17 @@ var require_pkg_src = __commonJS({
|
|
|
1275
1382
|
}
|
|
1276
1383
|
},
|
|
1277
1384
|
liquidationValidator: {
|
|
1278
|
-
cid: "
|
|
1385
|
+
cid: "QmZJnGq2HEYW1XYZPSodyRCrbDhTcLDT8x4z76EiPg8Xcx",
|
|
1279
1386
|
authorizedCidHex: cidToHex(
|
|
1280
|
-
"
|
|
1387
|
+
"QmZJnGq2HEYW1XYZPSodyRCrbDhTcLDT8x4z76EiPg8Xcx"
|
|
1281
1388
|
),
|
|
1282
1389
|
name: "Liquidation Validator",
|
|
1283
1390
|
description: "Production Liquidation Validator",
|
|
1284
|
-
version: "1.
|
|
1391
|
+
version: "1.2.1",
|
|
1285
1392
|
deployed: true,
|
|
1286
|
-
deployedAt:
|
|
1287
|
-
size:
|
|
1288
|
-
hash: "
|
|
1393
|
+
deployedAt: 1784316560010,
|
|
1394
|
+
size: 66523,
|
|
1395
|
+
hash: "f74d0163c47d7db04d1547f72c3a9bc1e5b499669089cc6464479a361462ce81",
|
|
1289
1396
|
validatorWalletAddress: "0x82DB9F515957aA380B596C6eA2Bd30CEF181f236",
|
|
1290
1397
|
pkp: {
|
|
1291
1398
|
publicKey: "0x041a241de58976c13e3d7ca5a18e494f7330151e3818706135d50176e78ffb673e58b11f0b67a87c73fade6c6b27d6e7a7377ea9f4c10bf31880f1d725f18cc6f8",
|
|
@@ -1311,17 +1418,17 @@ var require_pkg_src = __commonJS({
|
|
|
1311
1418
|
}
|
|
1312
1419
|
},
|
|
1313
1420
|
loanVaultBtcBalance: {
|
|
1314
|
-
cid: "
|
|
1421
|
+
cid: "QmVKSkgY8e5X6v6yRD4H2wpU2oX2kbeKYh4fjPepg71mqu",
|
|
1315
1422
|
authorizedCidHex: cidToHex(
|
|
1316
|
-
"
|
|
1423
|
+
"QmVKSkgY8e5X6v6yRD4H2wpU2oX2kbeKYh4fjPepg71mqu"
|
|
1317
1424
|
),
|
|
1318
1425
|
name: "Loan Vault Btc Balance",
|
|
1319
1426
|
description: "Production Loan Vault Btc Balance",
|
|
1320
|
-
version: "1.
|
|
1427
|
+
version: "1.2.1",
|
|
1321
1428
|
deployed: true,
|
|
1322
|
-
deployedAt:
|
|
1323
|
-
size:
|
|
1324
|
-
hash: "
|
|
1429
|
+
deployedAt: 1784316574669,
|
|
1430
|
+
size: 56477,
|
|
1431
|
+
hash: "917535f2911055b2ade2204fd868dd8b7294c7ade23e82aea488aeb0872e35d7",
|
|
1325
1432
|
validatorWalletAddress: "0x82DB9F515957aA380B596C6eA2Bd30CEF181f236",
|
|
1326
1433
|
pkp: {
|
|
1327
1434
|
publicKey: "0x043616787c5432415c24378c4ef48de2bcd6bb7b575b837e3cff09171802662da7105d79586c7659677a0ecbaddac4cce06cb2a11f69a16fa0c4d7002ac7d51a4d",
|
|
@@ -1335,17 +1442,17 @@ var require_pkg_src = __commonJS({
|
|
|
1335
1442
|
// Requires BitcoinProviderRegistry.addAllowedCid(chainId, keccak256(utf8(cid)))
|
|
1336
1443
|
// on each target chain (the action decrypts registry slots). No OAR entry.
|
|
1337
1444
|
btcAddressBalance: {
|
|
1338
|
-
cid: "
|
|
1445
|
+
cid: "QmcYFLDbhQArk5gQK44Xj8CnYEAbnaNsei92sUHpRuFmZA",
|
|
1339
1446
|
authorizedCidHex: cidToHex(
|
|
1340
|
-
"
|
|
1447
|
+
"QmcYFLDbhQArk5gQK44Xj8CnYEAbnaNsei92sUHpRuFmZA"
|
|
1341
1448
|
),
|
|
1342
1449
|
name: "Btc Address Balance",
|
|
1343
1450
|
description: "Address-only signed total BTC balance via registry consensus (no positionId, no price)",
|
|
1344
|
-
version: "0.1
|
|
1451
|
+
version: "0.2.1",
|
|
1345
1452
|
deployed: true,
|
|
1346
|
-
deployedAt:
|
|
1347
|
-
size:
|
|
1348
|
-
hash: "
|
|
1453
|
+
deployedAt: 1784316590403,
|
|
1454
|
+
size: 26986,
|
|
1455
|
+
hash: "49fb14265f3208916f12806df04492900bd08fb1b14d9651ee6278cd9ae4a428",
|
|
1349
1456
|
validatorWalletAddress: "0x82DB9F515957aA380B596C6eA2Bd30CEF181f236",
|
|
1350
1457
|
pkp: {
|
|
1351
1458
|
publicKey: "0x0498cbb31facfa26e3ec83ccdabfe4fe341ca0282e0c3cb81da7113723b9726e1e49efa92165cc3faa66ced4454cea56dbab12267852d6525a55d7c4f128b912cd",
|
|
@@ -1379,17 +1486,17 @@ var require_pkg_src = __commonJS({
|
|
|
1379
1486
|
hash: "03ee5be239c91c735d2c95db89a0d1b77e602cfc18af8eb64b3281fddcd90a72"
|
|
1380
1487
|
},
|
|
1381
1488
|
staleSpendRecoveryValidator: {
|
|
1382
|
-
cid: "
|
|
1489
|
+
cid: "QmcLrhiM1JwVi5w3QFxBYi1V4DaFT3Kk9MhPywyMnTsEwK",
|
|
1383
1490
|
authorizedCidHex: cidToHex(
|
|
1384
|
-
"
|
|
1491
|
+
"QmcLrhiM1JwVi5w3QFxBYi1V4DaFT3Kk9MhPywyMnTsEwK"
|
|
1385
1492
|
),
|
|
1386
1493
|
name: "Stale Spend Recovery Validator",
|
|
1387
1494
|
description: "Production Stale Spend Recovery Validator",
|
|
1388
|
-
version: "1.
|
|
1495
|
+
version: "1.1.2",
|
|
1389
1496
|
deployed: true,
|
|
1390
|
-
deployedAt:
|
|
1391
|
-
size:
|
|
1392
|
-
hash: "
|
|
1497
|
+
deployedAt: 1784316604502,
|
|
1498
|
+
size: 36785,
|
|
1499
|
+
hash: "28bfe44970541fefe3bae8bd54f25e8775d7cc61b5832a40ba1c18587b6b6358",
|
|
1393
1500
|
validatorWalletAddress: "0x82DB9F515957aA380B596C6eA2Bd30CEF181f236",
|
|
1394
1501
|
pkp: {
|
|
1395
1502
|
publicKey: "0x046da0ad6da7dd4a0063cbeac6f2bdb6a9889c2178b23470833356a8ee5630f7f0eee0db348d9d9aaaadd23eccf0063c8f68ee5e446abe30b882bf1de4c932bc21",
|
|
@@ -2140,7 +2247,7 @@ ${errorReport}`);
|
|
|
2140
2247
|
}
|
|
2141
2248
|
init_debug_logger();
|
|
2142
2249
|
init_session_signature_cache();
|
|
2143
|
-
var
|
|
2250
|
+
var import_ethers16 = require("ethers");
|
|
2144
2251
|
var EXPIRED_LOAN_MIN_LIQUIDATION_THRESHOLD_BPS = 11e3;
|
|
2145
2252
|
var GRACE_PERIOD_DAYS = 30;
|
|
2146
2253
|
var SATOSHIS_PER_BITCOIN = 100000000n;
|
|
@@ -2177,6 +2284,20 @@ ${errorReport}`);
|
|
|
2177
2284
|
/* REGTEST */
|
|
2178
2285
|
// Local development
|
|
2179
2286
|
};
|
|
2287
|
+
var CHAIN_TO_CHAIN_ID = {
|
|
2288
|
+
[
|
|
2289
|
+
"ethereum"
|
|
2290
|
+
/* ETHEREUM */
|
|
2291
|
+
]: 1,
|
|
2292
|
+
[
|
|
2293
|
+
"sepolia"
|
|
2294
|
+
/* SEPOLIA */
|
|
2295
|
+
]: 11155111,
|
|
2296
|
+
[
|
|
2297
|
+
"hardhat"
|
|
2298
|
+
/* HARDHAT */
|
|
2299
|
+
]: 1337
|
|
2300
|
+
};
|
|
2180
2301
|
function validateChain(chain) {
|
|
2181
2302
|
const normalized = chain.toLowerCase().trim();
|
|
2182
2303
|
if (normalized === "sepolia") {
|
|
@@ -2473,6 +2594,27 @@ ${errorReport}`);
|
|
|
2473
2594
|
function createProtocolParametersModule(config) {
|
|
2474
2595
|
return new ProtocolParametersModule(config);
|
|
2475
2596
|
}
|
|
2597
|
+
function parseVaultAddress(rawVaultAddress, chain) {
|
|
2598
|
+
try {
|
|
2599
|
+
const parsed = JSON.parse(rawVaultAddress);
|
|
2600
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
2601
|
+
const chainLower = chain.toLowerCase();
|
|
2602
|
+
if (chainLower === "sepolia") {
|
|
2603
|
+
return parsed.regtest || parsed.testnet || parsed.mainnet;
|
|
2604
|
+
}
|
|
2605
|
+
const bitcoinNetwork = getBitcoinNetworkForChain(chain);
|
|
2606
|
+
if (bitcoinNetwork === "regtest") {
|
|
2607
|
+
return parsed.regtest || parsed.testnet || parsed.mainnet;
|
|
2608
|
+
}
|
|
2609
|
+
if (bitcoinNetwork === "testnet") {
|
|
2610
|
+
return parsed.testnet || parsed.regtest || parsed.mainnet;
|
|
2611
|
+
}
|
|
2612
|
+
return parsed.mainnet;
|
|
2613
|
+
}
|
|
2614
|
+
} catch (e) {
|
|
2615
|
+
}
|
|
2616
|
+
return rawVaultAddress;
|
|
2617
|
+
}
|
|
2476
2618
|
var VaultSnapshotModule = class {
|
|
2477
2619
|
constructor(config) {
|
|
2478
2620
|
this.config = config;
|
|
@@ -2580,112 +2722,11 @@ ${errorReport}`);
|
|
|
2580
2722
|
timestamp: Date.now()
|
|
2581
2723
|
};
|
|
2582
2724
|
}
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
* For authorization/security operations, ALWAYS use getVaultSnapshot().
|
|
2589
|
-
*
|
|
2590
|
-
* @param positionId - Position to snapshot
|
|
2591
|
-
* @returns Complete VaultSnapshot with all metrics (using fast balance method)
|
|
2592
|
-
*/
|
|
2593
|
-
async getVaultSnapshotFast(positionId) {
|
|
2594
|
-
const positionState = await this.queryPositionState(positionId);
|
|
2595
|
-
console.log(
|
|
2596
|
-
`[Vault Snapshot] Raw vault address from contract: "${positionState.vaultAddress}"`
|
|
2597
|
-
);
|
|
2598
|
-
const useStubbedBtcData = this.config.debugOverrides?.useStubbedBtcData === true;
|
|
2599
|
-
const [balanceResult, btcPriceUsd] = useStubbedBtcData ? this.buildStubbedBtcSnapshotInputs(positionState) : await Promise.all([
|
|
2600
|
-
this.config.vaultBalance.calculateTrustedBalance(
|
|
2601
|
-
positionId,
|
|
2602
|
-
positionState.vaultAddress,
|
|
2603
|
-
true
|
|
2604
|
-
// Always skip UTXO validation for fast path
|
|
2605
|
-
),
|
|
2606
|
-
this.config.priceOracle.getBTCPrice()
|
|
2607
|
-
]);
|
|
2608
|
-
const protocolParams = createProtocolParametersModule({
|
|
2609
|
-
termManagerAddress: this.config.termManagerAddress,
|
|
2610
|
-
loanOpsManagerAddress: this.config.loanOpsManagerAddress,
|
|
2611
|
-
liquidationManagerAddress: this.config.liquidationManagerAddress,
|
|
2612
|
-
chain: this.config.chain,
|
|
2613
|
-
chainId: this.config.chainId || 1,
|
|
2614
|
-
// Default to mainnet if not provided
|
|
2615
|
-
rpcUrl: this.config.rpcUrl
|
|
2616
|
-
});
|
|
2617
|
-
const termForFees = this.config.termOverride ?? positionState.selectedTerm;
|
|
2618
|
-
const [liquidationThresholdBps, maxEscalatedThresholdBps, termFees] = await Promise.all([
|
|
2619
|
-
protocolParams.getLiquidationThreshold(),
|
|
2620
|
-
protocolParams.getMaxEscalatedThreshold(),
|
|
2621
|
-
protocolParams.getTermFees(termForFees)
|
|
2622
|
-
]);
|
|
2623
|
-
const termStatus = calculateTermStatus(
|
|
2624
|
-
positionState.termStartTimestamp,
|
|
2625
|
-
positionState.selectedTerm
|
|
2626
|
-
);
|
|
2627
|
-
const currentLiquidationThreshold = calculateLiquidationThreshold(
|
|
2628
|
-
termStatus.isExpired,
|
|
2629
|
-
termStatus.daysIntoGracePeriod,
|
|
2630
|
-
liquidationThresholdBps,
|
|
2631
|
-
maxEscalatedThresholdBps
|
|
2632
|
-
);
|
|
2633
|
-
const collateralMetrics = calculateCollateralMetrics(
|
|
2634
|
-
balanceResult.availableBalance,
|
|
2635
|
-
btcPriceUsd,
|
|
2636
|
-
positionState.ucdDebt
|
|
2637
|
-
);
|
|
2638
|
-
const isLiquidatable = collateralMetrics.collateralRatioBps < currentLiquidationThreshold;
|
|
2639
|
-
const marginToLiquidationBps = collateralMetrics.collateralRatioBps - currentLiquidationThreshold;
|
|
2640
|
-
return {
|
|
2641
|
-
// Raw state
|
|
2642
|
-
positionId: positionState.positionId,
|
|
2643
|
-
pkpId: positionState.pkpId,
|
|
2644
|
-
borrower: positionState.borrower,
|
|
2645
|
-
vaultAddress: positionState.vaultAddress,
|
|
2646
|
-
ucdDebt: positionState.ucdDebt,
|
|
2647
|
-
termStartTimestamp: positionState.termStartTimestamp,
|
|
2648
|
-
selectedTerm: positionState.selectedTerm,
|
|
2649
|
-
status: positionState.status,
|
|
2650
|
-
// Loan status enum value (0-7)
|
|
2651
|
-
expiryAt: positionState.expiryAt,
|
|
2652
|
-
// Loan expiry timestamp
|
|
2653
|
-
previousExpiryAt: positionState.previousExpiryAt,
|
|
2654
|
-
// expiryAt before last renewal; 0 if never renewed
|
|
2655
|
-
totalTerm: positionState.totalTerm,
|
|
2656
|
-
// total accumulated loan months; 0 before first renewal
|
|
2657
|
-
lastRenewedAt: positionState.lastRenewedAt,
|
|
2658
|
-
// timestamp of most recent renewal; createdAt at origination; 0 for pre-H-5 positions
|
|
2659
|
-
// Bitcoin state
|
|
2660
|
-
totalBTCSats: balanceResult.totalBalance,
|
|
2661
|
-
totalUTXOs: balanceResult.totalUTXOs,
|
|
2662
|
-
authorizedSpendsSats: balanceResult.authorizedBalance,
|
|
2663
|
-
authorizedSpendsHash: balanceResult.authorizedSpendsHash,
|
|
2664
|
-
availableBTCSats: balanceResult.availableBalance,
|
|
2665
|
-
availableUTXOs: balanceResult.availableUTXOs,
|
|
2666
|
-
// Price data
|
|
2667
|
-
btcPriceUsd,
|
|
2668
|
-
// Calculated metrics
|
|
2669
|
-
collateralValueUsd: collateralMetrics.collateralValueUsd,
|
|
2670
|
-
collateralRatioBps: collateralMetrics.collateralRatioBps,
|
|
2671
|
-
// Term status
|
|
2672
|
-
termDurationDays: termStatus.termDurationDays,
|
|
2673
|
-
termLengthDays: termStatus.termLengthDays,
|
|
2674
|
-
isExpired: termStatus.isExpired,
|
|
2675
|
-
daysUntilExpiry: termStatus.daysUntilExpiry,
|
|
2676
|
-
daysIntoGracePeriod: termStatus.daysIntoGracePeriod,
|
|
2677
|
-
// Liquidation risk
|
|
2678
|
-
currentLiquidationThreshold,
|
|
2679
|
-
isLiquidatable,
|
|
2680
|
-
marginToLiquidationBps,
|
|
2681
|
-
// Protocol parameters (from smart contracts)
|
|
2682
|
-
liquidationThresholdBps,
|
|
2683
|
-
originationFeeBps: termFees.originationFeeBps,
|
|
2684
|
-
extensionFeeBps: termFees.extensionFeeBps,
|
|
2685
|
-
// Metadata
|
|
2686
|
-
timestamp: Date.now()
|
|
2687
|
-
};
|
|
2688
|
-
}
|
|
2725
|
+
// NOTE (remediation P1/#5): `getVaultSnapshotFast` — a snapshot built on the
|
|
2726
|
+
// skip-UTXO-validation balance path — was deleted. It had zero callers, and
|
|
2727
|
+
// a snapshot whose balance skipped UTXO validation + consensus must never
|
|
2728
|
+
// exist within reach of a signing flow. Every snapshot now runs the full
|
|
2729
|
+
// validation path above.
|
|
2689
2730
|
buildStubbedBtcSnapshotInputs(positionState) {
|
|
2690
2731
|
const stubbedAvailableBtcSats = BigInt(
|
|
2691
2732
|
this.config.debugOverrides?.stubbedAvailableBtcSats ?? "10000000"
|
|
@@ -2814,14 +2855,20 @@ ${errorReport}`);
|
|
|
2814
2855
|
expiryAt,
|
|
2815
2856
|
selectedTermMonths
|
|
2816
2857
|
);
|
|
2858
|
+
const parsedVaultAddress = parseVaultAddress(
|
|
2859
|
+
position.vaultAddress,
|
|
2860
|
+
validateChain(this.config.chain)
|
|
2861
|
+
);
|
|
2862
|
+
if (typeof parsedVaultAddress !== "string" || parsedVaultAddress.length === 0) {
|
|
2863
|
+
throw new Error(
|
|
2864
|
+
`Position ${positionIdBytes32} vaultAddress has no entry for chain "${this.config.chain}"`
|
|
2865
|
+
);
|
|
2866
|
+
}
|
|
2817
2867
|
return {
|
|
2818
2868
|
positionId: position.positionId,
|
|
2819
2869
|
pkpId: position.pkpId,
|
|
2820
2870
|
borrower: position.borrower,
|
|
2821
|
-
vaultAddress:
|
|
2822
|
-
position.vaultAddress,
|
|
2823
|
-
validateChain(this.config.chain)
|
|
2824
|
-
),
|
|
2871
|
+
vaultAddress: parsedVaultAddress,
|
|
2825
2872
|
ucdDebt: BigInt(position.ucdDebt.toString()),
|
|
2826
2873
|
termStartTimestamp,
|
|
2827
2874
|
selectedTerm: selectedTermMonths,
|
|
@@ -2832,36 +2879,8 @@ ${errorReport}`);
|
|
|
2832
2879
|
lastRenewedAt
|
|
2833
2880
|
};
|
|
2834
2881
|
}
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
*
|
|
2838
|
-
* Supports two formats:
|
|
2839
|
-
* 1. Single string: "mqM3ZR2N6DzZkafSdFqxEAkWovAU3uYTAN"
|
|
2840
|
-
* 2. JSON object: {"mainnet":"bc1q...","testnet":"mqM3ZR2N6DzZkafSdFqxEAkWovAU3uYTAN"}
|
|
2841
|
-
*
|
|
2842
|
-
* @param rawVaultAddress Raw vault address from contract
|
|
2843
|
-
* @param chain EVM chain (sepolia, ethereum)
|
|
2844
|
-
* @returns Parsed Bitcoin address for the appropriate network
|
|
2845
|
-
*/
|
|
2846
|
-
parseVaultAddress(rawVaultAddress, chain) {
|
|
2847
|
-
try {
|
|
2848
|
-
const parsed = JSON.parse(rawVaultAddress);
|
|
2849
|
-
if (typeof parsed === "object" && parsed !== null) {
|
|
2850
|
-
const chainLower = chain.toLowerCase();
|
|
2851
|
-
if (chainLower === "sepolia") {
|
|
2852
|
-
return parsed.regtest || parsed.testnet || parsed.mainnet;
|
|
2853
|
-
}
|
|
2854
|
-
const bitcoinNetwork = getBitcoinNetworkForChain(chain);
|
|
2855
|
-
if (bitcoinNetwork === "testnet") {
|
|
2856
|
-
return parsed.testnet || parsed.regtest || parsed.mainnet;
|
|
2857
|
-
} else {
|
|
2858
|
-
return parsed.mainnet;
|
|
2859
|
-
}
|
|
2860
|
-
}
|
|
2861
|
-
} catch (e) {
|
|
2862
|
-
}
|
|
2863
|
-
return rawVaultAddress;
|
|
2864
|
-
}
|
|
2882
|
+
// parseVaultAddress moved to `modules/vault-address.ts` (shared with the
|
|
2883
|
+
// phase-2 signer for the P6/#7b vault-identity binding).
|
|
2865
2884
|
/**
|
|
2866
2885
|
* Quick check if position is liquidatable
|
|
2867
2886
|
*
|
|
@@ -3199,6 +3218,13 @@ ${auth.clientId}`;
|
|
|
3199
3218
|
return {};
|
|
3200
3219
|
}
|
|
3201
3220
|
}
|
|
3221
|
+
function assertSaneSatoshis(value, context) {
|
|
3222
|
+
if (!Number.isSafeInteger(value) || value < 0 || value > MAX_BTC_AMOUNT) {
|
|
3223
|
+
throw new Error(
|
|
3224
|
+
`${context}: satoshi value ${value} is not a sane amount (must be an integer in [0, ${MAX_BTC_AMOUNT}])`
|
|
3225
|
+
);
|
|
3226
|
+
}
|
|
3227
|
+
}
|
|
3202
3228
|
var BitcoinDataProvider = class {
|
|
3203
3229
|
constructor(config) {
|
|
3204
3230
|
this.config = config;
|
|
@@ -3345,13 +3371,16 @@ ${auth.clientId}`;
|
|
|
3345
3371
|
address
|
|
3346
3372
|
);
|
|
3347
3373
|
const utxos = unspentOutputs.map(
|
|
3348
|
-
(utxo) =>
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3374
|
+
(utxo) => {
|
|
3375
|
+
const satNumber = Math.round(utxo.amount * 1e8);
|
|
3376
|
+
assertSaneSatoshis(satNumber, `fetchUTXOsFromRPC ${utxo.txid}:${utxo.vout}`);
|
|
3377
|
+
return {
|
|
3378
|
+
txid: utxo.txid,
|
|
3379
|
+
vout: utxo.vout,
|
|
3380
|
+
satoshis: BigInt(satNumber),
|
|
3381
|
+
confirmations: utxo.confirmations
|
|
3382
|
+
};
|
|
3383
|
+
}
|
|
3355
3384
|
);
|
|
3356
3385
|
return utxos;
|
|
3357
3386
|
}
|
|
@@ -3393,6 +3422,7 @@ ${auth.clientId}`;
|
|
|
3393
3422
|
"Invalid UTXO row: missing txid, vout/n, or value/satoshis"
|
|
3394
3423
|
);
|
|
3395
3424
|
}
|
|
3425
|
+
assertSaneSatoshis(satRaw, `parseBlockstreamUTXOs ${txid}:${vout}`);
|
|
3396
3426
|
utxos.push({
|
|
3397
3427
|
txid,
|
|
3398
3428
|
vout,
|
|
@@ -3571,7 +3601,10 @@ ${auth.clientId}`;
|
|
|
3571
3601
|
`Invalid response: missing required fields. funded_txo_sum: ${fundedTxoSum}, spent_txo_sum: ${spentTxoSum}`
|
|
3572
3602
|
);
|
|
3573
3603
|
}
|
|
3604
|
+
assertSaneSatoshis(fundedTxoSum, `getBalance ${cleanAddress} funded_txo_sum`);
|
|
3605
|
+
assertSaneSatoshis(spentTxoSum, `getBalance ${cleanAddress} spent_txo_sum`);
|
|
3574
3606
|
const confirmedBalance = fundedTxoSum - spentTxoSum;
|
|
3607
|
+
assertSaneSatoshis(confirmedBalance, `getBalance ${cleanAddress} confirmed balance`);
|
|
3575
3608
|
const balance = BigInt(confirmedBalance);
|
|
3576
3609
|
this.logger.log("3", `Balance: ${balance.toString()} sats`);
|
|
3577
3610
|
this.logger.log(
|
|
@@ -4050,20 +4083,590 @@ ${auth.clientId}`;
|
|
|
4050
4083
|
return priceWith8Decimals;
|
|
4051
4084
|
}
|
|
4052
4085
|
};
|
|
4086
|
+
var MAX_CLASSIFIED_PARENTS = 5;
|
|
4087
|
+
var CLASSIFICATION_CONCURRENCY = 2;
|
|
4088
|
+
var CONFIRMATION_SKEW_TOLERANCE = 3;
|
|
4089
|
+
async function mapWithConcurrency(items, limit, fn) {
|
|
4090
|
+
const results = new Array(items.length);
|
|
4091
|
+
let next = 0;
|
|
4092
|
+
const workers = Array.from(
|
|
4093
|
+
{ length: Math.min(limit, items.length) },
|
|
4094
|
+
async () => {
|
|
4095
|
+
while (true) {
|
|
4096
|
+
const i = next++;
|
|
4097
|
+
if (i >= items.length) {
|
|
4098
|
+
return;
|
|
4099
|
+
}
|
|
4100
|
+
results[i] = await fn(items[i]);
|
|
4101
|
+
}
|
|
4102
|
+
}
|
|
4103
|
+
);
|
|
4104
|
+
await Promise.all(workers);
|
|
4105
|
+
return results;
|
|
4106
|
+
}
|
|
4107
|
+
function findOutputClaimMismatch(claimedUtxos, outputs, cleanVaultAddress) {
|
|
4108
|
+
for (const claim of claimedUtxos) {
|
|
4109
|
+
const output = outputs.find((o) => o.index === claim.vout);
|
|
4110
|
+
if (!output) {
|
|
4111
|
+
return `claimed output ${claim.txid}:${claim.vout} not present in parent tx`;
|
|
4112
|
+
}
|
|
4113
|
+
if (output.scriptpubkey_address !== cleanVaultAddress) {
|
|
4114
|
+
return `claimed output ${claim.txid}:${claim.vout} pays ${output.scriptpubkey_address ?? "<no address>"}, not the vault`;
|
|
4115
|
+
}
|
|
4116
|
+
if (output.value === void 0 || output.value !== claim.satoshis) {
|
|
4117
|
+
return `claimed output ${claim.txid}:${claim.vout} value ${output.value?.toString() ?? "<missing>"} != reported ${claim.satoshis.toString()}`;
|
|
4118
|
+
}
|
|
4119
|
+
}
|
|
4120
|
+
return null;
|
|
4121
|
+
}
|
|
4122
|
+
function stripNetworkPrefix(address) {
|
|
4123
|
+
const prefixPattern = /^(REGTEST_|TESTNET_|MAINNET_)/;
|
|
4124
|
+
return address.replace(prefixPattern, "");
|
|
4125
|
+
}
|
|
4126
|
+
var VaultBalanceConsensus = class {
|
|
4127
|
+
/**
|
|
4128
|
+
* @param minConsensusProviders Policy floor (`policy.minConsensusProviders`,
|
|
4129
|
+
* remediation P1/#1). Both consensus passes THROW when fewer providers are
|
|
4130
|
+
* configured than the floor demands — below it the cross-checks cannot run
|
|
4131
|
+
* and neither trusting the primary (inflation) nor excluding UTXOs
|
|
4132
|
+
* (deflation → wrongful liquidation) is a safe direction. A floor of 1
|
|
4133
|
+
* (hardhat / single-provider sepolia) preserves the trust-primary /
|
|
4134
|
+
* exclude-unconfirmed baseline unchanged.
|
|
4135
|
+
*/
|
|
4136
|
+
constructor(primaryProvider, providerConfigs, minConsensusProviders) {
|
|
4137
|
+
this.primaryProvider = primaryProvider;
|
|
4138
|
+
this.providerConfigs = providerConfigs;
|
|
4139
|
+
this.minConsensusProviders = minConsensusProviders;
|
|
4140
|
+
this.readers = this.buildReaders();
|
|
4141
|
+
}
|
|
4142
|
+
/**
|
|
4143
|
+
* Fail-closed floor check shared by both consensus passes. Only floors above
|
|
4144
|
+
* 1 can bite: a floor of 1 with zero configs means "no esplora access
|
|
4145
|
+
* configured" (dev/test), where the documented pre-gate behaviour —
|
|
4146
|
+
* trust-primary confirmed set, exclude unconfirmed — remains correct.
|
|
4147
|
+
*/
|
|
4148
|
+
assertProviderFloor(pass) {
|
|
4149
|
+
const configured = this.providerConfigs?.length ?? 0;
|
|
4150
|
+
if (this.minConsensusProviders > 1 && configured < this.minConsensusProviders) {
|
|
4151
|
+
throw new Error(
|
|
4152
|
+
`${pass}: this chain requires at least ${this.minConsensusProviders} Bitcoin providers for consensus; ${configured} configured. Refusing to proceed without cross-check.`
|
|
4153
|
+
);
|
|
4154
|
+
}
|
|
4155
|
+
}
|
|
4156
|
+
/**
|
|
4157
|
+
* Build one data reader per configured provider (primary first). Index 0
|
|
4158
|
+
* reuses the injected primary instance; the rest are constructed from their
|
|
4159
|
+
* configs so cross-check reads hit the right endpoint. Null when no providers
|
|
4160
|
+
* are configured — both passes then fall back to primary-only behaviour.
|
|
4161
|
+
*/
|
|
4162
|
+
buildReaders() {
|
|
4163
|
+
const configs = this.providerConfigs;
|
|
4164
|
+
if (!configs || configs.length === 0) {
|
|
4165
|
+
return null;
|
|
4166
|
+
}
|
|
4167
|
+
return configs.map(
|
|
4168
|
+
(cfg, i) => i === 0 ? this.primaryProvider : new BitcoinDataProvider({
|
|
4169
|
+
providerUrl: cfg.providerUrl,
|
|
4170
|
+
auth: cfg.auth,
|
|
4171
|
+
...cfg.timeout ? { timeout: cfg.timeout } : {}
|
|
4172
|
+
})
|
|
4173
|
+
);
|
|
4174
|
+
}
|
|
4175
|
+
/**
|
|
4176
|
+
* Confirmed-UTXO-set consensus across every registered provider (strict).
|
|
4177
|
+
*
|
|
4178
|
+
* The signed balance is dominated by the primary's confirmed UTXOs, but the
|
|
4179
|
+
* UTXO set — txid, vout, and value — is read only from the primary; the
|
|
4180
|
+
* `assertBitcoinBalanceConsensus` check the validators run compares
|
|
4181
|
+
* `chain_stats` totals, not individual outpoints. So a compromised primary
|
|
4182
|
+
* can add a fabricated confirmed UTXO, or inflate a real one's value, and the
|
|
4183
|
+
* balance consensus still passes (it serves honest `chain_stats`). This
|
|
4184
|
+
* closes that: each UTXO the primary reports at or above `minConfirmations`
|
|
4185
|
+
* must be present with the EXACT same value on every registered provider.
|
|
4186
|
+
*
|
|
4187
|
+
* Semantics:
|
|
4188
|
+
* - Absent on any provider, or value mismatch → THROW (fail-closed). Confirmed
|
|
4189
|
+
* transactions are fully propagated, so absence is unambiguous forgery, not
|
|
4190
|
+
* a race.
|
|
4191
|
+
* - Present + value-matched everywhere: the UTXO counts as confirmed only if
|
|
4192
|
+
* it meets `minConfirmations` on ALL providers (gated on the minimum
|
|
4193
|
+
* reported depth). A UTXO just past the boundary on the primary but a block
|
|
4194
|
+
* behind on a lagging provider is DEMOTED — excluded until it matures —
|
|
4195
|
+
* rather than signed over. A depth spread beyond CONFIRMATION_SKEW_TOLERANCE
|
|
4196
|
+
* is not benign skew but a provider lying about depth (to inflate a shallow
|
|
4197
|
+
* UTXO past the gate, or deflate a deep one into a wrongful liquidation) →
|
|
4198
|
+
* THROW.
|
|
4199
|
+
* - SYMMETRIC (finding #6): a confirmed outpoint any secondary reports that
|
|
4200
|
+
* the primary omits entirely, reports with a different value, or reports
|
|
4201
|
+
* at a depth more than the skew tolerance BELOW the secondary's → THROW.
|
|
4202
|
+
* Without this, a compromised primary deflates the balance by omission and
|
|
4203
|
+
* forces a wrongful liquidation.
|
|
4204
|
+
*
|
|
4205
|
+
* Below `minConsensusProviders` this THROWS (P1/#1). With a floor of 1 and
|
|
4206
|
+
* 0/1 providers configured, no cross-check is possible and the primary's
|
|
4207
|
+
* confirmed UTXOs are returned unchanged (behaviour identical to before
|
|
4208
|
+
* this gate).
|
|
4209
|
+
*
|
|
4210
|
+
* Cross-checking `chain_stats` totals against the confirmed-UTXO sum was
|
|
4211
|
+
* considered and rejected: `getBalance` reads mempool-blind `chain_stats`
|
|
4212
|
+
* while `getUTXOSet` removes mempool-spent outputs, so the two legitimately
|
|
4213
|
+
* diverge during any pending spend — precisely the self-change window — and
|
|
4214
|
+
* that comparison would false-fail there. Outpoint-set consensus asserts
|
|
4215
|
+
* agreement only on confirmed outpoints, which the mempool does not touch.
|
|
4216
|
+
*
|
|
4217
|
+
* @returns Set of "txid:vout" keys admitted as confirmed-with-consensus.
|
|
4218
|
+
*/
|
|
4219
|
+
async confirmedUtxoConsensus(primaryUtxos, vaultAddress, minConfirmations) {
|
|
4220
|
+
const providerConfigs = this.providerConfigs;
|
|
4221
|
+
const readers = this.readers;
|
|
4222
|
+
this.assertProviderFloor("confirmedUtxoConsensus");
|
|
4223
|
+
const confirmedKeys = /* @__PURE__ */ new Set();
|
|
4224
|
+
const primaryConfirmed = primaryUtxos.filter(
|
|
4225
|
+
(utxo) => utxo.confirmations >= minConfirmations
|
|
4226
|
+
);
|
|
4227
|
+
for (const utxo of primaryConfirmed) {
|
|
4228
|
+
confirmedKeys.add(`${utxo.txid}:${utxo.vout}`);
|
|
4229
|
+
}
|
|
4230
|
+
if (!providerConfigs || providerConfigs.length <= 1 || !readers) {
|
|
4231
|
+
return confirmedKeys;
|
|
4232
|
+
}
|
|
4233
|
+
const providerName = (i) => providerConfigs[i].name || providerConfigs[i].providerUrl;
|
|
4234
|
+
const settled = await Promise.allSettled(
|
|
4235
|
+
readers.slice(1).map((reader) => reader.getUTXOSet(vaultAddress, minConfirmations))
|
|
4236
|
+
);
|
|
4237
|
+
const failures = settled.map((r, i) => ({ r, name: providerName(i + 1) })).filter(
|
|
4238
|
+
(x) => x.r.status === "rejected"
|
|
4239
|
+
).map(
|
|
4240
|
+
({ r, name }) => `${name} \u2192 ${r.reason?.message ?? String(r.reason)}`
|
|
4241
|
+
);
|
|
4242
|
+
if (failures.length > 0) {
|
|
4243
|
+
throw new Error(
|
|
4244
|
+
`Confirmed UTXO set read failed for ${vaultAddress} on ${failures.length}/${providerConfigs.length - 1} secondary provider(s): ${failures.join("; ")}`
|
|
4245
|
+
);
|
|
4246
|
+
}
|
|
4247
|
+
const secondaryMaps = settled.map((r) => {
|
|
4248
|
+
const map2 = /* @__PURE__ */ new Map();
|
|
4249
|
+
for (const utxo of r.value.utxos) {
|
|
4250
|
+
map2.set(`${utxo.txid}:${utxo.vout}`, {
|
|
4251
|
+
value: utxo.satoshis,
|
|
4252
|
+
confirmations: utxo.confirmations
|
|
4253
|
+
});
|
|
4254
|
+
}
|
|
4255
|
+
return map2;
|
|
4256
|
+
});
|
|
4257
|
+
const demoted = [];
|
|
4258
|
+
for (const utxo of primaryConfirmed) {
|
|
4259
|
+
const key = `${utxo.txid}:${utxo.vout}`;
|
|
4260
|
+
let minConfs = utxo.confirmations;
|
|
4261
|
+
let maxConfs = utxo.confirmations;
|
|
4262
|
+
for (let s = 0; s < secondaryMaps.length; s++) {
|
|
4263
|
+
const entry = secondaryMaps[s].get(key);
|
|
4264
|
+
const name = providerName(s + 1);
|
|
4265
|
+
if (!entry) {
|
|
4266
|
+
throw new Error(
|
|
4267
|
+
`Confirmed UTXO ${key} present on ${providerName(0)} but absent on ${name}; refusing to sign (possible fabricated UTXO).`
|
|
4268
|
+
);
|
|
4269
|
+
}
|
|
4270
|
+
if (entry.value !== utxo.satoshis) {
|
|
4271
|
+
throw new Error(
|
|
4272
|
+
`Confirmed UTXO ${key} value mismatch: ${providerName(0)}=${utxo.satoshis.toString()} vs ${name}=${entry.value.toString()}; refusing to sign.`
|
|
4273
|
+
);
|
|
4274
|
+
}
|
|
4275
|
+
if (entry.confirmations < minConfs)
|
|
4276
|
+
minConfs = entry.confirmations;
|
|
4277
|
+
if (entry.confirmations > maxConfs)
|
|
4278
|
+
maxConfs = entry.confirmations;
|
|
4279
|
+
}
|
|
4280
|
+
if (maxConfs - minConfs > CONFIRMATION_SKEW_TOLERANCE) {
|
|
4281
|
+
throw new Error(
|
|
4282
|
+
`Confirmed UTXO ${key} confirmation spread ${maxConfs - minConfs} exceeds tolerance ${CONFIRMATION_SKEW_TOLERANCE} (min ${minConfs}, max ${maxConfs}); providers disagree on depth, refusing to sign.`
|
|
4283
|
+
);
|
|
4284
|
+
}
|
|
4285
|
+
if (minConfs < minConfirmations) {
|
|
4286
|
+
confirmedKeys.delete(key);
|
|
4287
|
+
demoted.push(key);
|
|
4288
|
+
}
|
|
4289
|
+
}
|
|
4290
|
+
if (demoted.length > 0) {
|
|
4291
|
+
litDebugLog(
|
|
4292
|
+
`[VaultBalance] confirmed-consensus: ${demoted.length} UTXO(s) demoted pending provider tip skew (excluded until matured): ${demoted.join(", ")}`
|
|
4293
|
+
);
|
|
4294
|
+
}
|
|
4295
|
+
const primaryByKey = new Map(
|
|
4296
|
+
primaryUtxos.map((utxo) => [`${utxo.txid}:${utxo.vout}`, utxo])
|
|
4297
|
+
);
|
|
4298
|
+
for (let s = 0; s < secondaryMaps.length; s++) {
|
|
4299
|
+
const name = providerName(s + 1);
|
|
4300
|
+
for (const [key, entry] of secondaryMaps[s]) {
|
|
4301
|
+
if (entry.confirmations < minConfirmations)
|
|
4302
|
+
continue;
|
|
4303
|
+
const primaryUtxo = primaryByKey.get(key);
|
|
4304
|
+
if (!primaryUtxo) {
|
|
4305
|
+
throw new Error(
|
|
4306
|
+
`Confirmed UTXO ${key} present on ${name} (${entry.confirmations} confs) but absent on ${providerName(0)}; refusing to sign (possible omitted UTXO / balance deflation \u2014 may be transient during an in-flight vault spend).`
|
|
4307
|
+
);
|
|
4308
|
+
}
|
|
4309
|
+
if (primaryUtxo.satoshis !== entry.value) {
|
|
4310
|
+
throw new Error(
|
|
4311
|
+
`Confirmed UTXO ${key} value mismatch: ${providerName(0)}=${primaryUtxo.satoshis.toString()} vs ${name}=${entry.value.toString()}; refusing to sign.`
|
|
4312
|
+
);
|
|
4313
|
+
}
|
|
4314
|
+
if (entry.confirmations - primaryUtxo.confirmations > CONFIRMATION_SKEW_TOLERANCE) {
|
|
4315
|
+
throw new Error(
|
|
4316
|
+
`Confirmed UTXO ${key} depth disagreement: ${providerName(0)}=${primaryUtxo.confirmations} vs ${name}=${entry.confirmations} exceeds tolerance ${CONFIRMATION_SKEW_TOLERANCE}; the primary may be under-reporting depth to suppress collateral, refusing to sign.`
|
|
4317
|
+
);
|
|
4318
|
+
}
|
|
4319
|
+
}
|
|
4320
|
+
}
|
|
4321
|
+
return confirmedKeys;
|
|
4322
|
+
}
|
|
4323
|
+
/**
|
|
4324
|
+
* Classify unconfirmed UTXOs as protocol self-change.
|
|
4325
|
+
*
|
|
4326
|
+
* A UTXO below minConfirmations counts as confirmed-equivalent ONLY when the
|
|
4327
|
+
* transaction that created it spends exclusively vault-owned, confirmed
|
|
4328
|
+
* outputs. Such a transaction can only have been signed by the vault's PKP,
|
|
4329
|
+
* so no third party can RBF-replace or double-spend it — the reversal risk
|
|
4330
|
+
* the confirmation gate exists to close does not apply. Any non-vault input,
|
|
4331
|
+
* unconfirmed funding tx, or coinbase input disqualifies the UTXO
|
|
4332
|
+
* (fail-safe: exclusion, never inclusion).
|
|
4333
|
+
*
|
|
4334
|
+
* Multi-provider consensus (audit C-1/C-2 parity): the parent-tx proof is
|
|
4335
|
+
* verified independently against EVERY configured provider and admitted
|
|
4336
|
+
* only on unanimity. The confirmed-balance consensus the validators run
|
|
4337
|
+
* covers `chain_stats` only, so without this the primary provider alone
|
|
4338
|
+
* could fabricate an unconfirmed UTXO plus a self-consistent parent proof
|
|
4339
|
+
* and inflate the signed balance. Any provider failure or verdict
|
|
4340
|
+
* disagreement THROWS (fail-closed) — exclusion on a suppressed proof
|
|
4341
|
+
* would deflate the balance and open a wrongful-liquidation window, so
|
|
4342
|
+
* neither direction may proceed without agreement.
|
|
4343
|
+
*
|
|
4344
|
+
* The same per-provider `/tx` fetch also returns the parent's outputs, and
|
|
4345
|
+
* every provider must corroborate each claimed UTXO (index, vault address,
|
|
4346
|
+
* exact value) before admission — the UTXO list itself comes only from the
|
|
4347
|
+
* primary, so an unchecked claim would let it attach a fabricated or
|
|
4348
|
+
* inflated output to a genuine self-spend.
|
|
4349
|
+
*
|
|
4350
|
+
* Fetch cost: zero when every UTXO is confirmed (the common case); otherwise
|
|
4351
|
+
* per provider: one tx lookup per classified parent (vins + vouts in one
|
|
4352
|
+
* response), plus per distinct funding tx a confirmation check and — only
|
|
4353
|
+
* when the provider omits vin prevout data — an output lookup. Classified
|
|
4354
|
+
* parents are capped at MAX_CLASSIFIED_PARENTS (largest claimed value
|
|
4355
|
+
* first, deterministic) and evaluated CLASSIFICATION_CONCURRENCY at a time,
|
|
4356
|
+
* so third-party dust spam cannot amplify into provider rate-limiting and
|
|
4357
|
+
* an action-wide outage.
|
|
4358
|
+
*
|
|
4359
|
+
* See docs/vault-balance-confirmed-gate-plan-2026-07-14.md §3.1.
|
|
4360
|
+
*
|
|
4361
|
+
* @param utxos - Full UTXO set for the vault address
|
|
4362
|
+
* @param vaultAddress - Bitcoin address of the vault (may carry network prefix)
|
|
4363
|
+
* @param minConfirmations - Confirmation threshold for the chain
|
|
4364
|
+
* @returns Set of "txid:vout" keys for UTXOs verified as self-change
|
|
4365
|
+
*/
|
|
4366
|
+
async classifySelfChange(utxos, vaultAddress, minConfirmations) {
|
|
4367
|
+
const providerConfigs = this.providerConfigs;
|
|
4368
|
+
const readers = this.readers;
|
|
4369
|
+
const selfChange = /* @__PURE__ */ new Set();
|
|
4370
|
+
const unconfirmed = utxos.filter(
|
|
4371
|
+
(utxo) => utxo.confirmations < minConfirmations
|
|
4372
|
+
);
|
|
4373
|
+
if (unconfirmed.length === 0) {
|
|
4374
|
+
return selfChange;
|
|
4375
|
+
}
|
|
4376
|
+
this.assertProviderFloor("classifySelfChange");
|
|
4377
|
+
if (!providerConfigs || providerConfigs.length === 0 || !readers) {
|
|
4378
|
+
litDebugLog(
|
|
4379
|
+
`[VaultBalance] ${unconfirmed.length} unconfirmed UTXO(s) excluded: no bitcoinProviderConfigs for self-change classification`
|
|
4380
|
+
);
|
|
4381
|
+
return selfChange;
|
|
4382
|
+
}
|
|
4383
|
+
const cleanVaultAddress = stripNetworkPrefix(vaultAddress);
|
|
4384
|
+
const claimsByParent = /* @__PURE__ */ new Map();
|
|
4385
|
+
for (const utxo of unconfirmed) {
|
|
4386
|
+
const claims = claimsByParent.get(utxo.txid) ?? [];
|
|
4387
|
+
claims.push(utxo);
|
|
4388
|
+
claimsByParent.set(utxo.txid, claims);
|
|
4389
|
+
}
|
|
4390
|
+
const claimedSum = (txid) => claimsByParent.get(txid).reduce((sum, utxo) => sum + utxo.satoshis, 0n);
|
|
4391
|
+
const rankedParents = [...claimsByParent.keys()].sort((a, b) => {
|
|
4392
|
+
const diff = claimedSum(b) - claimedSum(a);
|
|
4393
|
+
if (diff !== 0n) {
|
|
4394
|
+
return diff > 0n ? 1 : -1;
|
|
4395
|
+
}
|
|
4396
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
4397
|
+
});
|
|
4398
|
+
const parentTxids = rankedParents.slice(0, MAX_CLASSIFIED_PARENTS);
|
|
4399
|
+
const skippedParents = rankedParents.length - parentTxids.length;
|
|
4400
|
+
if (skippedParents > 0) {
|
|
4401
|
+
litDebugLog(
|
|
4402
|
+
`[VaultBalance] self-change: ${skippedParents} parent tx(s) beyond the ${MAX_CLASSIFIED_PARENTS}-parent classification budget excluded (mempool-spam bound; largest claimed value classified first)`
|
|
4403
|
+
);
|
|
4404
|
+
}
|
|
4405
|
+
const verdicts = await mapWithConcurrency(
|
|
4406
|
+
parentTxids,
|
|
4407
|
+
CLASSIFICATION_CONCURRENCY,
|
|
4408
|
+
async (parentTxid) => ({
|
|
4409
|
+
parentTxid,
|
|
4410
|
+
verified: await this.verifyParentIsSelfSpendConsensus(
|
|
4411
|
+
parentTxid,
|
|
4412
|
+
claimsByParent.get(parentTxid),
|
|
4413
|
+
cleanVaultAddress,
|
|
4414
|
+
minConfirmations,
|
|
4415
|
+
providerConfigs,
|
|
4416
|
+
readers
|
|
4417
|
+
)
|
|
4418
|
+
})
|
|
4419
|
+
);
|
|
4420
|
+
const verifiedParents = new Set(
|
|
4421
|
+
verdicts.filter((v) => v.verified).map((v) => v.parentTxid)
|
|
4422
|
+
);
|
|
4423
|
+
for (const utxo of unconfirmed) {
|
|
4424
|
+
if (verifiedParents.has(utxo.txid)) {
|
|
4425
|
+
selfChange.add(`${utxo.txid}:${utxo.vout}`);
|
|
4426
|
+
}
|
|
4427
|
+
}
|
|
4428
|
+
if (verifiedParents.size > 0) {
|
|
4429
|
+
litDebugLog(
|
|
4430
|
+
`[VaultBalance] self-change: ${selfChange.size}/${unconfirmed.length} unconfirmed UTXO(s) verified as protocol change by ${providerConfigs.length} provider(s)`
|
|
4431
|
+
);
|
|
4432
|
+
}
|
|
4433
|
+
return selfChange;
|
|
4434
|
+
}
|
|
4435
|
+
/**
|
|
4436
|
+
* Run the self-spend proof against every configured provider and require
|
|
4437
|
+
* unanimity. Fail-closed: a provider request failure or a verdict
|
|
4438
|
+
* disagreement throws and aborts the balance calculation — see
|
|
4439
|
+
* classifySelfChange for why exclusion is not a safe fallback here.
|
|
4440
|
+
*
|
|
4441
|
+
* When the parent IS a unanimous self-spend, every provider's view of the
|
|
4442
|
+
* parent's OUTPUTS must additionally corroborate the primary's claimed
|
|
4443
|
+
* UTXOs (index exists, pays the vault, exact value). Without this, a
|
|
4444
|
+
* compromised primary could attach a fabricated or value-inflated output
|
|
4445
|
+
* to a genuine self-spend the honest providers happily verify.
|
|
4446
|
+
*
|
|
4447
|
+
* @returns True when every provider verified the parent and corroborated
|
|
4448
|
+
* the claimed outputs, false when every provider rejected the
|
|
4449
|
+
* parent (genuine third-party deposit)
|
|
4450
|
+
*/
|
|
4451
|
+
async verifyParentIsSelfSpendConsensus(parentTxid, claimedUtxos, cleanVaultAddress, minConfirmations, providerConfigs, readers) {
|
|
4452
|
+
const providerName = (i) => providerConfigs[i].name || providerConfigs[i].providerUrl;
|
|
4453
|
+
const settled = await Promise.allSettled(
|
|
4454
|
+
providerConfigs.map(
|
|
4455
|
+
(cfg, i) => this.evaluateParentForProvider(
|
|
4456
|
+
parentTxid,
|
|
4457
|
+
claimedUtxos,
|
|
4458
|
+
cleanVaultAddress,
|
|
4459
|
+
minConfirmations,
|
|
4460
|
+
readers[i],
|
|
4461
|
+
cfg
|
|
4462
|
+
)
|
|
4463
|
+
)
|
|
4464
|
+
);
|
|
4465
|
+
const failures = settled.map((r, i) => ({ r, name: providerName(i) })).filter(
|
|
4466
|
+
(x) => x.r.status === "rejected"
|
|
4467
|
+
).map(
|
|
4468
|
+
({ r, name }) => `${name} \u2192 ${r.reason?.message ?? String(r.reason)}`
|
|
4469
|
+
);
|
|
4470
|
+
if (failures.length > 0) {
|
|
4471
|
+
throw new Error(
|
|
4472
|
+
`Self-change classification failed for tx ${parentTxid} on ${failures.length}/${providerConfigs.length} provider(s): ${failures.join("; ")}`
|
|
4473
|
+
);
|
|
4474
|
+
}
|
|
4475
|
+
const evaluations = settled.map(
|
|
4476
|
+
(r) => r.value
|
|
4477
|
+
);
|
|
4478
|
+
const votes = evaluations.map((e) => e.selfSpend);
|
|
4479
|
+
const disagreements = votes.map((vote, i) => ({ vote, name: providerName(i) })).filter((x) => x.vote !== votes[0]).map(
|
|
4480
|
+
(x) => `${providerName(0)}=${votes[0]} vs ${x.name}=${x.vote}`
|
|
4481
|
+
);
|
|
4482
|
+
if (disagreements.length > 0) {
|
|
4483
|
+
throw new Error(
|
|
4484
|
+
`Self-change consensus mismatch for tx ${parentTxid}: ${disagreements.join("; ")}. A provider may be compromised or out of sync; refusing to sign.`
|
|
4485
|
+
);
|
|
4486
|
+
}
|
|
4487
|
+
if (!votes[0]) {
|
|
4488
|
+
return false;
|
|
4489
|
+
}
|
|
4490
|
+
const outputMismatches = evaluations.map((e, i) => e.outputMismatch ? `${providerName(i)} \u2192 ${e.outputMismatch}` : null).filter((m) => m !== null);
|
|
4491
|
+
if (outputMismatches.length > 0) {
|
|
4492
|
+
throw new Error(
|
|
4493
|
+
`Self-change output claim mismatch for tx ${parentTxid}: ${outputMismatches.join("; ")}. The reported UTXO set contradicts the parent transaction; refusing to sign.`
|
|
4494
|
+
);
|
|
4495
|
+
}
|
|
4496
|
+
return true;
|
|
4497
|
+
}
|
|
4498
|
+
/**
|
|
4499
|
+
* Single provider's evaluation of a parent tx: the self-spend proof plus —
|
|
4500
|
+
* when the proof holds — corroboration of the claimed outputs against this
|
|
4501
|
+
* provider's view of the parent's vout set. The output check is skipped for
|
|
4502
|
+
* non-self-spend parents (their UTXOs are excluded regardless, and a
|
|
4503
|
+
* third-party deposit's values are not ours to police).
|
|
4504
|
+
*/
|
|
4505
|
+
async evaluateParentForProvider(parentTxid, claimedUtxos, cleanVaultAddress, minConfirmations, reader, providerConfig) {
|
|
4506
|
+
const parentTx = await this.fetchParentTransaction(
|
|
4507
|
+
parentTxid,
|
|
4508
|
+
providerConfig
|
|
4509
|
+
);
|
|
4510
|
+
if (!parentTx || parentTx.inputs.length === 0) {
|
|
4511
|
+
return { selfSpend: false, outputMismatch: null };
|
|
4512
|
+
}
|
|
4513
|
+
const selfSpend = await this.verifyInputsAreSelfSpend(
|
|
4514
|
+
parentTx.inputs,
|
|
4515
|
+
cleanVaultAddress,
|
|
4516
|
+
minConfirmations,
|
|
4517
|
+
reader
|
|
4518
|
+
);
|
|
4519
|
+
const outputMismatch = selfSpend ? findOutputClaimMismatch(claimedUtxos, parentTx.outputs, cleanVaultAddress) : null;
|
|
4520
|
+
return { selfSpend, outputMismatch };
|
|
4521
|
+
}
|
|
4522
|
+
/**
|
|
4523
|
+
* Verify that a transaction's inputs spend exclusively vault-owned,
|
|
4524
|
+
* confirmed outputs, using a single provider's view of the chain.
|
|
4525
|
+
*
|
|
4526
|
+
* @param inputs - Parsed inputs of the parent transaction
|
|
4527
|
+
* @param cleanVaultAddress - Vault address with network prefix stripped
|
|
4528
|
+
* @param minConfirmations - Required depth for every funding transaction
|
|
4529
|
+
* @param reader - Data provider for funding-tx lookups (same provider the inputs came from)
|
|
4530
|
+
* @returns True only when every input is a confirmed, vault-owned outpoint
|
|
4531
|
+
*/
|
|
4532
|
+
async verifyInputsAreSelfSpend(inputs, cleanVaultAddress, minConfirmations, reader) {
|
|
4533
|
+
const fundingTxids = [...new Set(inputs.map((input) => input.txid))];
|
|
4534
|
+
const outputsByFundingTx = /* @__PURE__ */ new Map();
|
|
4535
|
+
const needsOutputLookup = fundingTxids.filter(
|
|
4536
|
+
(fundingTxid) => inputs.some(
|
|
4537
|
+
(input) => input.txid === fundingTxid && input.prevoutAddress === void 0
|
|
4538
|
+
)
|
|
4539
|
+
);
|
|
4540
|
+
await Promise.all(
|
|
4541
|
+
needsOutputLookup.map(async (fundingTxid) => {
|
|
4542
|
+
outputsByFundingTx.set(
|
|
4543
|
+
fundingTxid,
|
|
4544
|
+
await reader.getTransactionOutputs(fundingTxid)
|
|
4545
|
+
);
|
|
4546
|
+
})
|
|
4547
|
+
);
|
|
4548
|
+
for (const input of inputs) {
|
|
4549
|
+
let ownerAddress = input.prevoutAddress;
|
|
4550
|
+
if (ownerAddress === void 0) {
|
|
4551
|
+
const outputs = outputsByFundingTx.get(input.txid);
|
|
4552
|
+
ownerAddress = outputs?.find(
|
|
4553
|
+
(output) => output.index === input.vout
|
|
4554
|
+
)?.scriptpubkey_address;
|
|
4555
|
+
}
|
|
4556
|
+
if (ownerAddress !== cleanVaultAddress) {
|
|
4557
|
+
return false;
|
|
4558
|
+
}
|
|
4559
|
+
}
|
|
4560
|
+
const fundingStatuses = await Promise.all(
|
|
4561
|
+
fundingTxids.map((fundingTxid) => reader.getTransaction(fundingTxid))
|
|
4562
|
+
);
|
|
4563
|
+
for (const status of fundingStatuses) {
|
|
4564
|
+
if (!status || status.confirmations < minConfirmations) {
|
|
4565
|
+
return false;
|
|
4566
|
+
}
|
|
4567
|
+
}
|
|
4568
|
+
return true;
|
|
4569
|
+
}
|
|
4570
|
+
/**
|
|
4571
|
+
* Fetch a parent transaction's inputs (vins) and outputs (vouts) via Esplora.
|
|
4572
|
+
*
|
|
4573
|
+
* A module-private fetch (not a `BitcoinDataProvider` method) so editing this
|
|
4574
|
+
* consensus logic does not rebundle the shared provider module's other
|
|
4575
|
+
* importers — see this file's header for the ripple-set constraint.
|
|
4576
|
+
*
|
|
4577
|
+
* @param txid - Transaction ID
|
|
4578
|
+
* @param providerConfig - Esplora endpoint to query (one of the configured
|
|
4579
|
+
* consensus providers)
|
|
4580
|
+
* @returns Parsed inputs + outputs, or null ONLY when the tx is provably not
|
|
4581
|
+
* a self-spend candidate (coinbase-funded). Provider inability —
|
|
4582
|
+
* 404, malformed vin rows, missing vin array — THROWS (review
|
|
4583
|
+
* 2026-07-15 P1): the parent of a UTXO the primary lists must
|
|
4584
|
+
* exist, so "can't fetch it" is a provider failure, and treating it
|
|
4585
|
+
* as a false self-change vote would silently deflate collateral
|
|
4586
|
+
* (wrongful-liquidation direction) if every provider failed the
|
|
4587
|
+
* same way.
|
|
4588
|
+
*/
|
|
4589
|
+
async fetchParentTransaction(txid, providerConfig) {
|
|
4590
|
+
if (!providerConfig.providerUrl) {
|
|
4591
|
+
return null;
|
|
4592
|
+
}
|
|
4593
|
+
const timeout = providerConfig.timeout || 15e3;
|
|
4594
|
+
const controller = new AbortController();
|
|
4595
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
4596
|
+
try {
|
|
4597
|
+
const response = await fetch(`${providerConfig.providerUrl}/tx/${txid}`, {
|
|
4598
|
+
signal: controller.signal,
|
|
4599
|
+
headers: await authHeaders(providerConfig.auth, timeout)
|
|
4600
|
+
});
|
|
4601
|
+
if (!response.ok) {
|
|
4602
|
+
if (response.status === 404) {
|
|
4603
|
+
throw new Error(
|
|
4604
|
+
`parent tx ${txid} not found on this provider \u2014 cannot verify the self-change proof`
|
|
4605
|
+
);
|
|
4606
|
+
}
|
|
4607
|
+
throw new Error(
|
|
4608
|
+
`Bitcoin provider error: ${response.status} ${response.statusText}`
|
|
4609
|
+
);
|
|
4610
|
+
}
|
|
4611
|
+
const data = await response.json();
|
|
4612
|
+
if (!data.vin || !Array.isArray(data.vin) || data.vin.length === 0) {
|
|
4613
|
+
throw new Error(
|
|
4614
|
+
`provider returned tx ${txid} without a vin array \u2014 malformed response`
|
|
4615
|
+
);
|
|
4616
|
+
}
|
|
4617
|
+
const inputs = [];
|
|
4618
|
+
for (const vin of data.vin) {
|
|
4619
|
+
if (vin.is_coinbase) {
|
|
4620
|
+
return null;
|
|
4621
|
+
}
|
|
4622
|
+
if (vin.txid === void 0 || vin.vout === void 0) {
|
|
4623
|
+
throw new Error(
|
|
4624
|
+
`provider returned tx ${txid} with a malformed vin row (missing txid/vout)`
|
|
4625
|
+
);
|
|
4626
|
+
}
|
|
4627
|
+
inputs.push({
|
|
4628
|
+
txid: vin.txid,
|
|
4629
|
+
vout: vin.vout,
|
|
4630
|
+
prevoutAddress: vin.prevout?.scriptpubkey_address
|
|
4631
|
+
});
|
|
4632
|
+
}
|
|
4633
|
+
const outputs = Array.isArray(data.vout) ? data.vout.map((vout, index) => ({
|
|
4634
|
+
index,
|
|
4635
|
+
scriptpubkey_address: vout?.scriptpubkey_address,
|
|
4636
|
+
value: typeof vout?.value === "number" ? BigInt(vout.value) : void 0
|
|
4637
|
+
})) : [];
|
|
4638
|
+
return { inputs, outputs };
|
|
4639
|
+
} catch (error) {
|
|
4640
|
+
if (isAbortError(error)) {
|
|
4641
|
+
throw new Error(`Request timeout after ${timeout}ms`);
|
|
4642
|
+
}
|
|
4643
|
+
throw error;
|
|
4644
|
+
} finally {
|
|
4645
|
+
clearTimeout(timeoutId);
|
|
4646
|
+
}
|
|
4647
|
+
}
|
|
4648
|
+
};
|
|
4053
4649
|
var VaultBalanceModule = class {
|
|
4054
4650
|
constructor(config) {
|
|
4651
|
+
if (!Number.isInteger(config.minConfirmations) || config.minConfirmations < 1) {
|
|
4652
|
+
throw new Error(
|
|
4653
|
+
`VaultBalanceModule: minConfirmations must be a positive integer (got ${config.minConfirmations}). Pass the policy-derived balance-confirmation threshold \u2014 there is no default.`
|
|
4654
|
+
);
|
|
4655
|
+
}
|
|
4055
4656
|
this.config = config;
|
|
4056
4657
|
this.bitcoinProvider = config.bitcoinProvider;
|
|
4057
4658
|
}
|
|
4058
4659
|
/**
|
|
4059
|
-
* Calculate balance (
|
|
4660
|
+
* Calculate balance (arithmetic only - no UTXO validation)
|
|
4060
4661
|
*
|
|
4061
|
-
*
|
|
4062
|
-
* Uses simple arithmetic: Total Balance - Authorized Balance = Available Balance
|
|
4662
|
+
* Balance-only calculation: Total Balance - Authorized Balance = Available.
|
|
4063
4663
|
*
|
|
4064
|
-
*
|
|
4065
|
-
* -
|
|
4066
|
-
*
|
|
4664
|
+
* ⚠️ SECURITY (remediation P1/#5): this result skips UTXO validation, the
|
|
4665
|
+
* confirmed-consensus gate, and self-change proofing. It must NEVER feed a
|
|
4666
|
+
* signed BalanceConfirmation or any authorization decision. No production
|
|
4667
|
+
* action calls it (the `skipUTXOValidation` fast path that used to route
|
|
4668
|
+
* here was deleted); it exists for read-only diagnostics and tests. Adding
|
|
4669
|
+
* a production caller requires Master sign-off.
|
|
4067
4670
|
*
|
|
4068
4671
|
* @param positionId - The position ID to check
|
|
4069
4672
|
* @param vaultAddress - Bitcoin address of the vault
|
|
@@ -4110,17 +4713,18 @@ ${auth.clientId}`;
|
|
|
4110
4713
|
* 1. Authorized transaction not found on network (not broadcasted)
|
|
4111
4714
|
* 2. Authorized transaction has insufficient confirmations (network updating)
|
|
4112
4715
|
*
|
|
4716
|
+
* NOTE: the `skipUTXOValidation` fast path was removed (remediation P1/#5).
|
|
4717
|
+
* Every trusted-balance read runs full UTXO validation + consensus; a
|
|
4718
|
+
* balance that skipped validation must never feed a signed
|
|
4719
|
+
* BalanceConfirmation.
|
|
4720
|
+
*
|
|
4113
4721
|
* @param positionId - The position ID to check
|
|
4114
4722
|
* @param vaultAddress - Bitcoin address of the vault
|
|
4115
|
-
* @param skipUTXOValidation - If true, use fast balance-only path (skips UTXO validation)
|
|
4116
4723
|
* @returns Complete breakdown of vault balance
|
|
4117
4724
|
* @throws Error if authorized spends are in invalid state
|
|
4118
4725
|
*/
|
|
4119
|
-
async calculateTrustedBalance(positionId, vaultAddress
|
|
4120
|
-
|
|
4121
|
-
return await this.calculateBalance(positionId, vaultAddress);
|
|
4122
|
-
}
|
|
4123
|
-
const minConfirmations = this.config.minConfirmations || 6;
|
|
4726
|
+
async calculateTrustedBalance(positionId, vaultAddress) {
|
|
4727
|
+
const minConfirmations = this.config.minConfirmations;
|
|
4124
4728
|
const utxoSet = await this.bitcoinProvider.getUTXOSet(
|
|
4125
4729
|
vaultAddress,
|
|
4126
4730
|
minConfirmations
|
|
@@ -4140,8 +4744,25 @@ ${auth.clientId}`;
|
|
|
4140
4744
|
(sum, spend) => sum + spend.satoshis,
|
|
4141
4745
|
0n
|
|
4142
4746
|
);
|
|
4747
|
+
const consensus = new VaultBalanceConsensus(
|
|
4748
|
+
this.bitcoinProvider,
|
|
4749
|
+
this.config.bitcoinProviderConfigs,
|
|
4750
|
+
// Absent ⇒ 1: dev/test constructions without a policy keep the
|
|
4751
|
+
// single-provider baseline; validators pass policy.minConsensusProviders.
|
|
4752
|
+
this.config.minConsensusProviders ?? 1
|
|
4753
|
+
);
|
|
4754
|
+
const confirmedConsensusKeys = await consensus.confirmedUtxoConsensus(
|
|
4755
|
+
utxoSet.utxos,
|
|
4756
|
+
vaultAddress,
|
|
4757
|
+
minConfirmations
|
|
4758
|
+
);
|
|
4759
|
+
const selfChangeKeys = await consensus.classifySelfChange(
|
|
4760
|
+
utxoSet.utxos,
|
|
4761
|
+
vaultAddress,
|
|
4762
|
+
minConfirmations
|
|
4763
|
+
);
|
|
4143
4764
|
const availableUTXOs = utxoSet.utxos.filter(
|
|
4144
|
-
(utxo) => !this.isUTXOAuthorized(utxo, authorizedSpends)
|
|
4765
|
+
(utxo) => (confirmedConsensusKeys.has(`${utxo.txid}:${utxo.vout}`) || selfChangeKeys.has(`${utxo.txid}:${utxo.vout}`)) && !this.isUTXOAuthorized(utxo, authorizedSpends)
|
|
4145
4766
|
);
|
|
4146
4767
|
const availableBalance = availableUTXOs.reduce(
|
|
4147
4768
|
(sum, utxo) => sum + utxo.satoshis,
|
|
@@ -4373,14 +4994,84 @@ ${auth.clientId}`;
|
|
|
4373
4994
|
function createVaultBalanceModule(config) {
|
|
4374
4995
|
return new VaultBalanceModule(config);
|
|
4375
4996
|
}
|
|
4997
|
+
var POLICY_BY_CHAIN = {
|
|
4998
|
+
[
|
|
4999
|
+
"hardhat"
|
|
5000
|
+
/* HARDHAT */
|
|
5001
|
+
]: {
|
|
5002
|
+
chain: "hardhat",
|
|
5003
|
+
chainId: CHAIN_TO_CHAIN_ID[
|
|
5004
|
+
"hardhat"
|
|
5005
|
+
/* HARDHAT */
|
|
5006
|
+
],
|
|
5007
|
+
bitcoinNetwork: CHAIN_TO_BITCOIN_NETWORK[
|
|
5008
|
+
"hardhat"
|
|
5009
|
+
/* HARDHAT */
|
|
5010
|
+
],
|
|
5011
|
+
allowArbitraryBitcoinProvider: true,
|
|
5012
|
+
allowArbitraryEvmRpc: true,
|
|
5013
|
+
allowDevSinglePriceProvider: true,
|
|
5014
|
+
minBitcoinBalanceConfirmations: 1,
|
|
5015
|
+
minBitcoinSpendConfirmations: 1,
|
|
5016
|
+
minConsensusProviders: 1
|
|
5017
|
+
},
|
|
5018
|
+
[
|
|
5019
|
+
"sepolia"
|
|
5020
|
+
/* SEPOLIA */
|
|
5021
|
+
]: {
|
|
5022
|
+
chain: "sepolia",
|
|
5023
|
+
chainId: CHAIN_TO_CHAIN_ID[
|
|
5024
|
+
"sepolia"
|
|
5025
|
+
/* SEPOLIA */
|
|
5026
|
+
],
|
|
5027
|
+
bitcoinNetwork: CHAIN_TO_BITCOIN_NETWORK[
|
|
5028
|
+
"sepolia"
|
|
5029
|
+
/* SEPOLIA */
|
|
5030
|
+
],
|
|
5031
|
+
allowArbitraryBitcoinProvider: false,
|
|
5032
|
+
allowArbitraryEvmRpc: false,
|
|
5033
|
+
allowDevSinglePriceProvider: false,
|
|
5034
|
+
minBitcoinBalanceConfirmations: 1,
|
|
5035
|
+
minBitcoinSpendConfirmations: 1,
|
|
5036
|
+
minConsensusProviders: 1
|
|
5037
|
+
},
|
|
5038
|
+
[
|
|
5039
|
+
"ethereum"
|
|
5040
|
+
/* ETHEREUM */
|
|
5041
|
+
]: {
|
|
5042
|
+
chain: "ethereum",
|
|
5043
|
+
chainId: CHAIN_TO_CHAIN_ID[
|
|
5044
|
+
"ethereum"
|
|
5045
|
+
/* ETHEREUM */
|
|
5046
|
+
],
|
|
5047
|
+
bitcoinNetwork: CHAIN_TO_BITCOIN_NETWORK[
|
|
5048
|
+
"ethereum"
|
|
5049
|
+
/* ETHEREUM */
|
|
5050
|
+
],
|
|
5051
|
+
allowArbitraryBitcoinProvider: false,
|
|
5052
|
+
allowArbitraryEvmRpc: false,
|
|
5053
|
+
allowDevSinglePriceProvider: false,
|
|
5054
|
+
minBitcoinBalanceConfirmations: 6,
|
|
5055
|
+
minBitcoinSpendConfirmations: 6,
|
|
5056
|
+
minConsensusProviders: 2
|
|
5057
|
+
}
|
|
5058
|
+
};
|
|
5059
|
+
function getChainPolicy(chain) {
|
|
5060
|
+
const validated = validateChain(chain);
|
|
5061
|
+
const policy = POLICY_BY_CHAIN[validated];
|
|
5062
|
+
if (!policy) {
|
|
5063
|
+
throw new Error(`No chain policy registered for "${chain}"`);
|
|
5064
|
+
}
|
|
5065
|
+
return policy;
|
|
5066
|
+
}
|
|
4376
5067
|
async function executeVaultSnapshot(params) {
|
|
4377
5068
|
global.ethers = {
|
|
4378
|
-
...
|
|
5069
|
+
...import_ethers16.ethers,
|
|
4379
5070
|
providers: {
|
|
4380
|
-
StaticJsonRpcProvider:
|
|
5071
|
+
StaticJsonRpcProvider: import_ethers16.ethers.JsonRpcProvider
|
|
4381
5072
|
},
|
|
4382
|
-
Contract:
|
|
4383
|
-
utils:
|
|
5073
|
+
Contract: import_ethers16.ethers.Contract,
|
|
5074
|
+
utils: import_ethers16.ethers
|
|
4384
5075
|
// v6 moved utils to top level
|
|
4385
5076
|
};
|
|
4386
5077
|
global.Lit = {
|
|
@@ -4417,7 +5108,7 @@ ${auth.clientId}`;
|
|
|
4417
5108
|
// Default to mainnet if not provided
|
|
4418
5109
|
rpcUrl: params.rpcUrl,
|
|
4419
5110
|
bitcoinProvider,
|
|
4420
|
-
minConfirmations: params.minConfirmations
|
|
5111
|
+
minConfirmations: params.minConfirmations ?? getChainPolicy(params.chain).minBitcoinBalanceConfirmations
|
|
4421
5112
|
});
|
|
4422
5113
|
const vaultSnapshot = createVaultSnapshotModule({
|
|
4423
5114
|
contractAddress: params.contractAddress,
|
|
@@ -4651,7 +5342,8 @@ function getSepoliaConfig() {
|
|
|
4651
5342
|
contractVersionRegistry: SEPOLIA_CONTRACTS.ContractVersionRegistry || "",
|
|
4652
5343
|
feeRecipientRegistry: SEPOLIA_CONTRACTS.FeeRecipientRegistry || "",
|
|
4653
5344
|
positionDelegateRegistry: SEPOLIA_CONTRACTS.PositionDelegateRegistry || "",
|
|
4654
|
-
bitcoinWithdrawalAddressRegistry: SEPOLIA_CONTRACTS.BitcoinWithdrawalAddressRegistry || ""
|
|
5345
|
+
bitcoinWithdrawalAddressRegistry: SEPOLIA_CONTRACTS.BitcoinWithdrawalAddressRegistry || "",
|
|
5346
|
+
carryAgentRegistry: SEPOLIA_CONTRACTS.CarryAgentRegistry || ""
|
|
4655
5347
|
},
|
|
4656
5348
|
subgraphs: {
|
|
4657
5349
|
// Lazy getter for the same module-init snapshot reason as serviceEndpoint.
|
|
@@ -4691,7 +5383,8 @@ function getMainnetConfig() {
|
|
|
4691
5383
|
contractVersionRegistry: MAINNET_CONTRACTS.ContractVersionRegistry || "",
|
|
4692
5384
|
feeRecipientRegistry: MAINNET_CONTRACTS.FeeRecipientRegistry || "",
|
|
4693
5385
|
positionDelegateRegistry: MAINNET_CONTRACTS.PositionDelegateRegistry || "",
|
|
4694
|
-
bitcoinWithdrawalAddressRegistry: MAINNET_CONTRACTS.BitcoinWithdrawalAddressRegistry || ""
|
|
5386
|
+
bitcoinWithdrawalAddressRegistry: MAINNET_CONTRACTS.BitcoinWithdrawalAddressRegistry || "",
|
|
5387
|
+
carryAgentRegistry: MAINNET_CONTRACTS.CarryAgentRegistry || ""
|
|
4695
5388
|
},
|
|
4696
5389
|
subgraphs: {
|
|
4697
5390
|
diamondHandsUrl: "https://gateway-arbitrum.network.thegraph.com/api/subgraphs/id/CCTPsdYqco2jChDLLBQTbdJWwoukVoMt1cXeR9ti6r9A"
|
|
@@ -4913,7 +5606,7 @@ __export(src_exports, {
|
|
|
4913
5606
|
module.exports = __toCommonJS(src_exports);
|
|
4914
5607
|
|
|
4915
5608
|
// src/modules/diamond-hands-sdk.ts
|
|
4916
|
-
var
|
|
5609
|
+
var import_ethers14 = require("ethers");
|
|
4917
5610
|
|
|
4918
5611
|
// src/types/result.ts
|
|
4919
5612
|
function success(value) {
|
|
@@ -5297,22 +5990,12 @@ var SDKError = class _SDKError extends Error {
|
|
|
5297
5990
|
|
|
5298
5991
|
// src/utils/chunks/eip1559-broadcast.utils.ts
|
|
5299
5992
|
var import_ethers = require("ethers");
|
|
5300
|
-
var MINT_UCD_GAS_CEILING = BigInt(
|
|
5993
|
+
var MINT_UCD_GAS_CEILING = BigInt(13e5);
|
|
5301
5994
|
var WITHDRAW_BTC_GAS_CEILING = BigInt(3e6);
|
|
5302
5995
|
var MAKE_PAYMENT_GAS_CEILING = BigInt(1e6);
|
|
5303
5996
|
var EXTEND_POSITION_GAS_CEILING = BigInt(1e6);
|
|
5304
5997
|
var LIQUIDATION_COMMIT_GAS_CEILING = BigInt(1e6);
|
|
5305
5998
|
var LIQUIDATION_REVEAL_GAS_CEILING = BigInt(2e6);
|
|
5306
|
-
var MINT_UCD_ESTIMATE_MARGIN_BPS = 2500;
|
|
5307
|
-
async function estimateContractCallGasWithMargin(provider, from, to, data, marginBps, ceiling) {
|
|
5308
|
-
try {
|
|
5309
|
-
const estimated = await provider.estimateGas({ from, to, data });
|
|
5310
|
-
const withMargin = estimated * BigInt(1e4 + marginBps) / 10000n;
|
|
5311
|
-
return withMargin > ceiling ? ceiling : withMargin;
|
|
5312
|
-
} catch {
|
|
5313
|
-
return null;
|
|
5314
|
-
}
|
|
5315
|
-
}
|
|
5316
5999
|
async function resolveEip1559FeeFields(provider) {
|
|
5317
6000
|
const feeData = await provider.getFeeData();
|
|
5318
6001
|
let maxFeePerGas = feeData.maxFeePerGas ?? void 0;
|
|
@@ -5820,21 +6503,117 @@ async function buildSignedLoginPayload(signer, chainId, audience) {
|
|
|
5820
6503
|
return { chainId, message, signature };
|
|
5821
6504
|
}
|
|
5822
6505
|
|
|
6506
|
+
// src/utils/server-session-store.ts
|
|
6507
|
+
var REAUTH_WINDOW_SECONDS = 24 * 60 * 60;
|
|
6508
|
+
var SAFETY_MARGIN_SECONDS = 5 * 60;
|
|
6509
|
+
var STORE_PREFIX = "dh-server-session:";
|
|
6510
|
+
function sessionStoreKey(address, chainId, endpoint) {
|
|
6511
|
+
return STORE_PREFIX + `${address.trim().toLowerCase()}:${chainId}:${endpoint.replace(/\/+$/, "")}`;
|
|
6512
|
+
}
|
|
6513
|
+
function isPayloadShape(value) {
|
|
6514
|
+
return !!value && typeof value === "object" && typeof value.chainId === "number" && typeof value.signature === "string" && !!value.message && typeof value.message.address === "string" && typeof value.message.issuedAt === "number" && typeof value.message.nonce === "string";
|
|
6515
|
+
}
|
|
6516
|
+
function isPersistedShape(value) {
|
|
6517
|
+
return !!value && typeof value === "object" && value.version === 1 && typeof value.address === "string" && typeof value.chainId === "number" && typeof value.endpoint === "string" && isPayloadShape(value.payload) && (value.token === void 0 || typeof value.token === "string") && (value.tokenExpiresAt === void 0 || typeof value.tokenExpiresAt === "number");
|
|
6518
|
+
}
|
|
6519
|
+
var LocalStorageSessionStore = class {
|
|
6520
|
+
load(key) {
|
|
6521
|
+
let raw;
|
|
6522
|
+
try {
|
|
6523
|
+
raw = window.localStorage.getItem(key);
|
|
6524
|
+
} catch {
|
|
6525
|
+
return null;
|
|
6526
|
+
}
|
|
6527
|
+
if (!raw)
|
|
6528
|
+
return null;
|
|
6529
|
+
let parsed;
|
|
6530
|
+
try {
|
|
6531
|
+
parsed = JSON.parse(raw);
|
|
6532
|
+
} catch {
|
|
6533
|
+
this.clear(key);
|
|
6534
|
+
return null;
|
|
6535
|
+
}
|
|
6536
|
+
if (!isPersistedShape(parsed)) {
|
|
6537
|
+
this.clear(key);
|
|
6538
|
+
return null;
|
|
6539
|
+
}
|
|
6540
|
+
return parsed;
|
|
6541
|
+
}
|
|
6542
|
+
save(key, session) {
|
|
6543
|
+
try {
|
|
6544
|
+
window.localStorage.setItem(key, JSON.stringify(session));
|
|
6545
|
+
} catch {
|
|
6546
|
+
}
|
|
6547
|
+
}
|
|
6548
|
+
clear(key) {
|
|
6549
|
+
try {
|
|
6550
|
+
window.localStorage.removeItem(key);
|
|
6551
|
+
} catch {
|
|
6552
|
+
}
|
|
6553
|
+
}
|
|
6554
|
+
};
|
|
6555
|
+
var MemorySessionStore = class {
|
|
6556
|
+
entries = /* @__PURE__ */ new Map();
|
|
6557
|
+
load(key) {
|
|
6558
|
+
return this.entries.get(key) ?? null;
|
|
6559
|
+
}
|
|
6560
|
+
save(key, session) {
|
|
6561
|
+
this.entries.set(key, session);
|
|
6562
|
+
}
|
|
6563
|
+
clear(key) {
|
|
6564
|
+
this.entries.delete(key);
|
|
6565
|
+
}
|
|
6566
|
+
};
|
|
6567
|
+
function localStorageUsable() {
|
|
6568
|
+
try {
|
|
6569
|
+
if (typeof window === "undefined" || !window.localStorage)
|
|
6570
|
+
return false;
|
|
6571
|
+
const probe = "__dh_server_session_probe__";
|
|
6572
|
+
window.localStorage.setItem(probe, "1");
|
|
6573
|
+
window.localStorage.removeItem(probe);
|
|
6574
|
+
return true;
|
|
6575
|
+
} catch {
|
|
6576
|
+
return false;
|
|
6577
|
+
}
|
|
6578
|
+
}
|
|
6579
|
+
function createDefaultSessionStore() {
|
|
6580
|
+
return localStorageUsable() ? new LocalStorageSessionStore() : new MemorySessionStore();
|
|
6581
|
+
}
|
|
6582
|
+
|
|
5823
6583
|
// src/utils/server-session.ts
|
|
5824
6584
|
var REFRESH_LEEWAY_SECONDS = 30;
|
|
6585
|
+
var ServerLoginError = class extends Error {
|
|
6586
|
+
constructor(message, status) {
|
|
6587
|
+
super(message);
|
|
6588
|
+
this.status = status;
|
|
6589
|
+
this.name = "ServerLoginError";
|
|
6590
|
+
}
|
|
6591
|
+
};
|
|
6592
|
+
function isLoginRejection(error) {
|
|
6593
|
+
return error instanceof ServerLoginError && error.status >= 400 && error.status < 500 && error.status !== 429;
|
|
6594
|
+
}
|
|
5825
6595
|
var ServerSession = class {
|
|
5826
6596
|
signer;
|
|
5827
6597
|
serviceEndpoint;
|
|
5828
6598
|
chainId;
|
|
6599
|
+
store;
|
|
6600
|
+
onSignaturePrompt;
|
|
5829
6601
|
now;
|
|
5830
6602
|
fetchImpl;
|
|
5831
6603
|
cached = null;
|
|
5832
6604
|
inFlight = null;
|
|
6605
|
+
/**
|
|
6606
|
+
* Store key for the current signer, memoized on first use so the sync
|
|
6607
|
+
* `clear()` can drop the persisted token without an async address lookup.
|
|
6608
|
+
*/
|
|
6609
|
+
lastStoreKey = null;
|
|
5833
6610
|
constructor(opts) {
|
|
5834
6611
|
assertSafeServiceEndpoint(opts.serviceEndpoint);
|
|
5835
6612
|
this.signer = opts.signer;
|
|
5836
6613
|
this.serviceEndpoint = opts.serviceEndpoint.replace(/\/+$/, "");
|
|
5837
6614
|
this.chainId = opts.chainId;
|
|
6615
|
+
this.store = opts.persistSession === false ? null : opts.sessionStore ?? createDefaultSessionStore();
|
|
6616
|
+
this.onSignaturePrompt = opts.onSignaturePrompt;
|
|
5838
6617
|
this.now = opts.now ?? (() => Math.floor(Date.now() / 1e3));
|
|
5839
6618
|
this.fetchImpl = opts.fetchImpl ?? ((...a) => fetch(...a));
|
|
5840
6619
|
}
|
|
@@ -5847,19 +6626,56 @@ var ServerSession = class {
|
|
|
5847
6626
|
async getAuthHeader() {
|
|
5848
6627
|
return { Authorization: `Bearer ${await this.getValidToken()}` };
|
|
5849
6628
|
}
|
|
5850
|
-
/**
|
|
6629
|
+
/**
|
|
6630
|
+
* Drop the cached token (e.g. on 401 from server). The persisted envelope is
|
|
6631
|
+
* kept — the next call re-mints silently — but its stored token copy is
|
|
6632
|
+
* dropped too so a dead JWT can't be re-adopted from the store.
|
|
6633
|
+
*/
|
|
5851
6634
|
clear() {
|
|
5852
6635
|
this.cached = null;
|
|
5853
6636
|
this.inFlight = null;
|
|
6637
|
+
if (!this.store || !this.lastStoreKey)
|
|
6638
|
+
return;
|
|
6639
|
+
const persisted = this.store.load(this.lastStoreKey);
|
|
6640
|
+
if (persisted?.token) {
|
|
6641
|
+
this.store.save(this.lastStoreKey, {
|
|
6642
|
+
...persisted,
|
|
6643
|
+
token: void 0,
|
|
6644
|
+
tokenExpiresAt: void 0
|
|
6645
|
+
});
|
|
6646
|
+
}
|
|
5854
6647
|
}
|
|
5855
6648
|
/**
|
|
5856
|
-
*
|
|
5857
|
-
*
|
|
5858
|
-
*
|
|
6649
|
+
* Drop the session AND the persisted re-mint envelope (wallet disconnect /
|
|
6650
|
+
* explicit sign-out paths — anything that must remove the 24h credential
|
|
6651
|
+
* from storage).
|
|
6652
|
+
*/
|
|
6653
|
+
async clearPersisted() {
|
|
6654
|
+
this.cached = null;
|
|
6655
|
+
this.inFlight = null;
|
|
6656
|
+
if (!this.store)
|
|
6657
|
+
return;
|
|
6658
|
+
if (this.lastStoreKey) {
|
|
6659
|
+
this.store.clear(this.lastStoreKey);
|
|
6660
|
+
return;
|
|
6661
|
+
}
|
|
6662
|
+
try {
|
|
6663
|
+
const address = (await this.signer.getAddress()).toLowerCase();
|
|
6664
|
+
this.store.clear(
|
|
6665
|
+
sessionStoreKey(address, this.chainId, this.serviceEndpoint)
|
|
6666
|
+
);
|
|
6667
|
+
} catch {
|
|
6668
|
+
}
|
|
6669
|
+
}
|
|
6670
|
+
/**
|
|
6671
|
+
* Revoke the current session JWT on lit-ops-server (`POST /api/auth/logout`)
|
|
6672
|
+
* — which also tombstones the envelope's re-mint window server-side — and
|
|
6673
|
+
* remove the persisted credential locally. Clears the local cache regardless
|
|
6674
|
+
* of server response so the client stops presenting the token.
|
|
5859
6675
|
*/
|
|
5860
6676
|
async logout() {
|
|
5861
6677
|
const token = this.cached?.token ?? await this.getValidToken().catch(() => null);
|
|
5862
|
-
this.
|
|
6678
|
+
await this.clearPersisted();
|
|
5863
6679
|
if (!token)
|
|
5864
6680
|
return;
|
|
5865
6681
|
const res = await this.fetchImpl(`${this.serviceEndpoint}/api/auth/logout`, {
|
|
@@ -5882,41 +6698,116 @@ var ServerSession = class {
|
|
|
5882
6698
|
return this.inFlight;
|
|
5883
6699
|
this.inFlight = (async () => {
|
|
5884
6700
|
try {
|
|
6701
|
+
const persisted = await this.loadPersisted();
|
|
6702
|
+
if (persisted) {
|
|
6703
|
+
if (persisted.token && typeof persisted.tokenExpiresAt === "number" && persisted.tokenExpiresAt - REFRESH_LEEWAY_SECONDS > this.now()) {
|
|
6704
|
+
this.cached = {
|
|
6705
|
+
token: persisted.token,
|
|
6706
|
+
expiresAt: persisted.tokenExpiresAt
|
|
6707
|
+
};
|
|
6708
|
+
return this.cached;
|
|
6709
|
+
}
|
|
6710
|
+
try {
|
|
6711
|
+
const session2 = await this.login(persisted.payload);
|
|
6712
|
+
this.persist(persisted.payload, session2);
|
|
6713
|
+
this.cached = session2;
|
|
6714
|
+
return session2;
|
|
6715
|
+
} catch (error) {
|
|
6716
|
+
if (!isLoginRejection(error))
|
|
6717
|
+
throw error;
|
|
6718
|
+
if (this.store && this.lastStoreKey) {
|
|
6719
|
+
this.store.clear(this.lastStoreKey);
|
|
6720
|
+
}
|
|
6721
|
+
}
|
|
6722
|
+
}
|
|
6723
|
+
this.onSignaturePrompt?.();
|
|
5885
6724
|
const payload = await buildSignedLoginPayload(
|
|
5886
6725
|
this.signer,
|
|
5887
6726
|
this.chainId,
|
|
5888
|
-
this.serviceEndpoint
|
|
5889
|
-
);
|
|
5890
|
-
const
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
headers: { "Content-Type": "application/json" },
|
|
5895
|
-
body: JSON.stringify(payload)
|
|
5896
|
-
}
|
|
5897
|
-
);
|
|
5898
|
-
if (!res.ok) {
|
|
5899
|
-
const detail = await res.text().catch(() => "");
|
|
5900
|
-
throw new Error(
|
|
5901
|
-
`lit-ops-server login failed: HTTP ${res.status} ${detail.slice(0, 200)}`
|
|
5902
|
-
);
|
|
5903
|
-
}
|
|
5904
|
-
const body = await res.json();
|
|
5905
|
-
if (!body?.token || !body?.expiresAt) {
|
|
5906
|
-
throw new Error("lit-ops-server login returned malformed response");
|
|
5907
|
-
}
|
|
5908
|
-
this.cached = { token: body.token, expiresAt: body.expiresAt };
|
|
5909
|
-
return this.cached;
|
|
6727
|
+
this.serviceEndpoint
|
|
6728
|
+
);
|
|
6729
|
+
const session = await this.login(payload);
|
|
6730
|
+
this.persist(payload, session);
|
|
6731
|
+
this.cached = session;
|
|
6732
|
+
return session;
|
|
5910
6733
|
} finally {
|
|
5911
6734
|
this.inFlight = null;
|
|
5912
6735
|
}
|
|
5913
6736
|
})();
|
|
5914
6737
|
return this.inFlight;
|
|
5915
6738
|
}
|
|
6739
|
+
async login(payload) {
|
|
6740
|
+
const res = await this.fetchImpl(`${this.serviceEndpoint}/api/auth/login`, {
|
|
6741
|
+
method: "POST",
|
|
6742
|
+
headers: { "Content-Type": "application/json" },
|
|
6743
|
+
body: JSON.stringify(payload)
|
|
6744
|
+
});
|
|
6745
|
+
if (!res.ok) {
|
|
6746
|
+
const detail = await res.text().catch(() => "");
|
|
6747
|
+
throw new ServerLoginError(
|
|
6748
|
+
`lit-ops-server login failed: HTTP ${res.status} ${detail.slice(0, 200)}`,
|
|
6749
|
+
res.status
|
|
6750
|
+
);
|
|
6751
|
+
}
|
|
6752
|
+
const body = await res.json();
|
|
6753
|
+
if (!body?.token || !body?.expiresAt) {
|
|
6754
|
+
throw new Error("lit-ops-server login returned malformed response");
|
|
6755
|
+
}
|
|
6756
|
+
return { token: body.token, expiresAt: body.expiresAt };
|
|
6757
|
+
}
|
|
6758
|
+
/**
|
|
6759
|
+
* Load the persisted session for the current signer, validating that it
|
|
6760
|
+
* belongs to this address/chain/endpoint and that the envelope is still
|
|
6761
|
+
* inside the re-auth window (with a safety margin). Invalid or expired
|
|
6762
|
+
* entries are evicted.
|
|
6763
|
+
*/
|
|
6764
|
+
async loadPersisted() {
|
|
6765
|
+
if (!this.store)
|
|
6766
|
+
return null;
|
|
6767
|
+
try {
|
|
6768
|
+
const address = (await this.signer.getAddress()).toLowerCase();
|
|
6769
|
+
const key = sessionStoreKey(address, this.chainId, this.serviceEndpoint);
|
|
6770
|
+
this.lastStoreKey = key;
|
|
6771
|
+
const persisted = this.store.load(key);
|
|
6772
|
+
if (!persisted)
|
|
6773
|
+
return null;
|
|
6774
|
+
const withinWindow = this.now() < persisted.payload.message.issuedAt + REAUTH_WINDOW_SECONDS - SAFETY_MARGIN_SECONDS;
|
|
6775
|
+
if (persisted.address !== address || persisted.chainId !== this.chainId || persisted.endpoint !== this.serviceEndpoint || persisted.payload.message.address.toLowerCase() !== address || !withinWindow) {
|
|
6776
|
+
this.store.clear(key);
|
|
6777
|
+
return null;
|
|
6778
|
+
}
|
|
6779
|
+
return persisted;
|
|
6780
|
+
} catch {
|
|
6781
|
+
return null;
|
|
6782
|
+
}
|
|
6783
|
+
}
|
|
6784
|
+
/** Best-effort save of the envelope + live JWT for silent recovery. */
|
|
6785
|
+
persist(payload, session) {
|
|
6786
|
+
if (!this.store)
|
|
6787
|
+
return;
|
|
6788
|
+
const address = payload.message.address.toLowerCase();
|
|
6789
|
+
try {
|
|
6790
|
+
const key = sessionStoreKey(address, this.chainId, this.serviceEndpoint);
|
|
6791
|
+
this.lastStoreKey = key;
|
|
6792
|
+
this.store.save(key, {
|
|
6793
|
+
version: 1,
|
|
6794
|
+
address,
|
|
6795
|
+
chainId: this.chainId,
|
|
6796
|
+
endpoint: this.serviceEndpoint,
|
|
6797
|
+
payload,
|
|
6798
|
+
token: session.token,
|
|
6799
|
+
tokenExpiresAt: session.expiresAt
|
|
6800
|
+
});
|
|
6801
|
+
} catch {
|
|
6802
|
+
}
|
|
6803
|
+
}
|
|
5916
6804
|
};
|
|
5917
6805
|
|
|
5918
6806
|
// src/utils/quantum-revert.utils.ts
|
|
6807
|
+
var import_ethers5 = require("ethers");
|
|
5919
6808
|
var DEAD_ZONE_VIOLATION_SELECTOR = "0xbe4b82c1";
|
|
6809
|
+
var ERROR_STRING_SELECTOR = "0x08c379a0";
|
|
6810
|
+
var PANIC_SELECTOR = "0x4e487b71";
|
|
5920
6811
|
var QUANTUM_REVERT_NAMES = {
|
|
5921
6812
|
"0xbe4b82c1": "DeadZoneViolation()",
|
|
5922
6813
|
"0x137f3b70": "InDeadZone()",
|
|
@@ -5940,14 +6831,128 @@ function decodeQuantumRevert(e) {
|
|
|
5940
6831
|
e?.error?.data?.data ?? e?.error?.data ?? e?.error?.error?.data;
|
|
5941
6832
|
const data = typeof raw === "string" && raw.startsWith("0x") ? raw : null;
|
|
5942
6833
|
const selector = data && data.length >= 10 ? data.slice(0, 10) : null;
|
|
5943
|
-
|
|
6834
|
+
let name = selector ? QUANTUM_REVERT_NAMES[selector] ?? null : null;
|
|
6835
|
+
if (!name && selector && data) {
|
|
6836
|
+
try {
|
|
6837
|
+
if (selector === ERROR_STRING_SELECTOR) {
|
|
6838
|
+
const [message] = import_ethers5.AbiCoder.defaultAbiCoder().decode(
|
|
6839
|
+
["string"],
|
|
6840
|
+
"0x" + data.slice(10)
|
|
6841
|
+
);
|
|
6842
|
+
name = `Error("${message}")`;
|
|
6843
|
+
} else if (selector === PANIC_SELECTOR) {
|
|
6844
|
+
const [code] = import_ethers5.AbiCoder.defaultAbiCoder().decode(
|
|
6845
|
+
["uint256"],
|
|
6846
|
+
"0x" + data.slice(10)
|
|
6847
|
+
);
|
|
6848
|
+
name = `Panic(0x${code.toString(16)})`;
|
|
6849
|
+
}
|
|
6850
|
+
} catch {
|
|
6851
|
+
}
|
|
6852
|
+
}
|
|
5944
6853
|
return { data, selector, name };
|
|
5945
6854
|
}
|
|
5946
6855
|
|
|
6856
|
+
// src/utils/quantum-submission.utils.ts
|
|
6857
|
+
var MAX_DEADZONE_RESIMULATIONS = 3;
|
|
6858
|
+
var QuantumRevertError = class extends Error {
|
|
6859
|
+
constructor(errorName, selector, data, cause) {
|
|
6860
|
+
super(
|
|
6861
|
+
`Pre-send simulation reverted: ${errorName ?? selector ?? "unknown"}`
|
|
6862
|
+
);
|
|
6863
|
+
this.errorName = errorName;
|
|
6864
|
+
this.selector = selector;
|
|
6865
|
+
this.data = data;
|
|
6866
|
+
this.cause = cause;
|
|
6867
|
+
this.name = "QuantumRevertError";
|
|
6868
|
+
}
|
|
6869
|
+
};
|
|
6870
|
+
async function assertSafeQuantumSubmission(opts) {
|
|
6871
|
+
const { provider, to, from, data, quantumTimestamp } = opts;
|
|
6872
|
+
const maxResimulations = opts.maxResimulations ?? MAX_DEADZONE_RESIMULATIONS;
|
|
6873
|
+
const now = opts.now ?? (() => Math.floor(Date.now() / 1e3));
|
|
6874
|
+
const onDebug = opts.onDebug ?? (() => {
|
|
6875
|
+
});
|
|
6876
|
+
const gateOpts = {
|
|
6877
|
+
now,
|
|
6878
|
+
...opts.sleep ? { sleep: opts.sleep } : {}
|
|
6879
|
+
};
|
|
6880
|
+
await awaitSafeSubmissionWindow(quantumTimestamp, gateOpts);
|
|
6881
|
+
for (let sim = 1; ; sim++) {
|
|
6882
|
+
try {
|
|
6883
|
+
await provider.call({ to, from, data });
|
|
6884
|
+
break;
|
|
6885
|
+
} catch (simError) {
|
|
6886
|
+
const decoded = decodeQuantumRevert(simError);
|
|
6887
|
+
if (decoded.selector === DEAD_ZONE_VIOLATION_SELECTOR && sim <= maxResimulations) {
|
|
6888
|
+
const sigQuantum = Math.floor(quantumTimestamp / QUANTUM_WINDOW_SECONDS) * QUANTUM_WINDOW_SECONDS;
|
|
6889
|
+
const realCurrentQuantum = Math.floor(now() / QUANTUM_WINDOW_SECONDS) * QUANTUM_WINDOW_SECONDS;
|
|
6890
|
+
if (sigQuantum === realCurrentQuantum) {
|
|
6891
|
+
onDebug(
|
|
6892
|
+
"\u21AA\uFE0F Simulated DeadZoneViolation is a stale-latest-block artifact (signature is the current quantum) \u2014 proceeding to broadcast"
|
|
6893
|
+
);
|
|
6894
|
+
break;
|
|
6895
|
+
}
|
|
6896
|
+
const gate = await awaitSafeSubmissionWindow(quantumTimestamp, gateOpts);
|
|
6897
|
+
if (gate.waited) {
|
|
6898
|
+
onDebug(
|
|
6899
|
+
`\u{1F501} Re-simulating after crossing the quantum boundary (attempt ${sim}/${maxResimulations})`
|
|
6900
|
+
);
|
|
6901
|
+
continue;
|
|
6902
|
+
}
|
|
6903
|
+
}
|
|
6904
|
+
if (!decoded.data) {
|
|
6905
|
+
const label = simError instanceof Error ? simError.message : String(simError);
|
|
6906
|
+
onDebug(
|
|
6907
|
+
`\u26A0\uFE0F Pre-send simulation failed without revert data (${label}) \u2014 proceeding to broadcast`
|
|
6908
|
+
);
|
|
6909
|
+
break;
|
|
6910
|
+
}
|
|
6911
|
+
throw new QuantumRevertError(
|
|
6912
|
+
decoded.name,
|
|
6913
|
+
decoded.selector,
|
|
6914
|
+
decoded.data,
|
|
6915
|
+
simError
|
|
6916
|
+
);
|
|
6917
|
+
}
|
|
6918
|
+
}
|
|
6919
|
+
await awaitSafeSubmissionWindow(quantumTimestamp, gateOpts);
|
|
6920
|
+
}
|
|
6921
|
+
|
|
5947
6922
|
// src/utils/btc-withdrawal-message.ts
|
|
5948
|
-
var
|
|
6923
|
+
var import_ethers6 = require("ethers");
|
|
5949
6924
|
var QUANTUM_SECONDS = 60;
|
|
5950
6925
|
var QUANTUM_SAFE_THRESHOLD = 40;
|
|
6926
|
+
function hashBtcInputSet(utxos) {
|
|
6927
|
+
if (!Array.isArray(utxos) || utxos.length === 0) {
|
|
6928
|
+
throw new Error("hashBtcInputSet: at least one input is required");
|
|
6929
|
+
}
|
|
6930
|
+
const normalized = utxos.map((u) => {
|
|
6931
|
+
const txid = String(u.txid).toLowerCase().replace(/^0x/, "");
|
|
6932
|
+
const vout = Number(u.vout);
|
|
6933
|
+
const value = Number(u.value);
|
|
6934
|
+
if (!/^[0-9a-f]{64}$/.test(txid)) {
|
|
6935
|
+
throw new Error(`hashBtcInputSet: invalid txid "${u.txid}"`);
|
|
6936
|
+
}
|
|
6937
|
+
if (!Number.isSafeInteger(vout) || vout < 0) {
|
|
6938
|
+
throw new Error(`hashBtcInputSet: invalid vout ${u.vout} for ${txid}`);
|
|
6939
|
+
}
|
|
6940
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
6941
|
+
throw new Error(`hashBtcInputSet: invalid value ${u.value} for ${txid}:${vout}`);
|
|
6942
|
+
}
|
|
6943
|
+
return { txid, vout, value };
|
|
6944
|
+
});
|
|
6945
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6946
|
+
for (const u of normalized) {
|
|
6947
|
+
const key = `${u.txid}:${u.vout}`;
|
|
6948
|
+
if (seen.has(key)) {
|
|
6949
|
+
throw new Error(`hashBtcInputSet: duplicate outpoint ${key}`);
|
|
6950
|
+
}
|
|
6951
|
+
seen.add(key);
|
|
6952
|
+
}
|
|
6953
|
+
const canonical = normalized.sort((a, b) => a.txid < b.txid ? -1 : a.txid > b.txid ? 1 : a.vout - b.vout).map((u) => `${u.txid}:${u.vout}:${u.value}`).join("|");
|
|
6954
|
+
return (0, import_ethers6.keccak256)((0, import_ethers6.toUtf8Bytes)(canonical));
|
|
6955
|
+
}
|
|
5951
6956
|
async function buildBtcExecuteEnvelope(params) {
|
|
5952
6957
|
const {
|
|
5953
6958
|
positionId,
|
|
@@ -5968,6 +6973,17 @@ async function buildBtcExecuteEnvelope(params) {
|
|
|
5968
6973
|
"buildBtcExecuteEnvelope: targetAmount must be a positive number of satoshis"
|
|
5969
6974
|
);
|
|
5970
6975
|
}
|
|
6976
|
+
const utxos = params.utxos && params.utxos.length > 0 ? params.utxos : [{ txid, vout, value: satoshis }];
|
|
6977
|
+
const normTxid = (t) => String(t).toLowerCase().replace(/^0x/, "");
|
|
6978
|
+
const authorizedInSet = utxos.some(
|
|
6979
|
+
(u) => normTxid(u.txid) === normTxid(txid) && Number(u.vout) === Number(vout)
|
|
6980
|
+
);
|
|
6981
|
+
if (!authorizedInSet) {
|
|
6982
|
+
throw new Error(
|
|
6983
|
+
`buildBtcExecuteEnvelope: the authorized UTXO ${txid}:${vout} must be part of utxos[]`
|
|
6984
|
+
);
|
|
6985
|
+
}
|
|
6986
|
+
const utxosHash = hashBtcInputSet(utxos);
|
|
5971
6987
|
const nowSec = Date.now() / 1e3;
|
|
5972
6988
|
const currentQuantumStart = Math.floor(nowSec / QUANTUM_SECONDS) * QUANTUM_SECONDS;
|
|
5973
6989
|
const secsIntoQuantum = nowSec - currentQuantumStart;
|
|
@@ -5980,15 +6996,15 @@ async function buildBtcExecuteEnvelope(params) {
|
|
|
5980
6996
|
} else {
|
|
5981
6997
|
timestamp = currentQuantumStart;
|
|
5982
6998
|
}
|
|
5983
|
-
const actionHash = (0,
|
|
5984
|
-
(0,
|
|
6999
|
+
const actionHash = (0, import_ethers6.keccak256)(
|
|
7000
|
+
(0, import_ethers6.toUtf8Bytes)("execute-btc-withdrawal")
|
|
5985
7001
|
);
|
|
5986
7002
|
const utxoIdentifier = `${txid}:${vout}`;
|
|
5987
|
-
const positionIdBytes32 = (0,
|
|
7003
|
+
const positionIdBytes32 = (0, import_ethers6.zeroPadValue)(
|
|
5988
7004
|
positionId.startsWith("0x") ? positionId : `0x${positionId}`,
|
|
5989
7005
|
32
|
|
5990
7006
|
);
|
|
5991
|
-
const messageHash = (0,
|
|
7007
|
+
const messageHash = (0, import_ethers6.solidityPackedKeccak256)(
|
|
5992
7008
|
[
|
|
5993
7009
|
"bytes32",
|
|
5994
7010
|
// positionId
|
|
@@ -6004,6 +7020,8 @@ async function buildBtcExecuteEnvelope(params) {
|
|
|
6004
7020
|
// targetAddress (BTC destination)
|
|
6005
7021
|
"uint256",
|
|
6006
7022
|
// targetAmount (sats authorized to recipient)
|
|
7023
|
+
"bytes32",
|
|
7024
|
+
// utxosHash (canonical hash of the FULL input set — P6/#8)
|
|
6007
7025
|
"bytes32"
|
|
6008
7026
|
// actionHash
|
|
6009
7027
|
],
|
|
@@ -6015,10 +7033,11 @@ async function buildBtcExecuteEnvelope(params) {
|
|
|
6015
7033
|
networkFee,
|
|
6016
7034
|
targetAddress,
|
|
6017
7035
|
targetAmount,
|
|
7036
|
+
utxosHash,
|
|
6018
7037
|
actionHash
|
|
6019
7038
|
]
|
|
6020
7039
|
);
|
|
6021
|
-
const userSignature = await signer.signMessage((0,
|
|
7040
|
+
const userSignature = await signer.signMessage((0, import_ethers6.getBytes)(messageHash));
|
|
6022
7041
|
const borrowerAddress = await signer.getAddress();
|
|
6023
7042
|
return {
|
|
6024
7043
|
positionId,
|
|
@@ -6031,7 +7050,8 @@ async function buildBtcExecuteEnvelope(params) {
|
|
|
6031
7050
|
chainId,
|
|
6032
7051
|
timestamp,
|
|
6033
7052
|
userSignature,
|
|
6034
|
-
borrowerAddress
|
|
7053
|
+
borrowerAddress,
|
|
7054
|
+
utxos
|
|
6035
7055
|
};
|
|
6036
7056
|
}
|
|
6037
7057
|
|
|
@@ -6081,7 +7101,7 @@ function maybeTemperSignature(signature, shouldTemper, operationName) {
|
|
|
6081
7101
|
}
|
|
6082
7102
|
|
|
6083
7103
|
// src/utils/address-conversion.utils.ts
|
|
6084
|
-
var
|
|
7104
|
+
var import_ethers7 = require("ethers");
|
|
6085
7105
|
|
|
6086
7106
|
// src/utils/chunks/bitcoin-utils.ts
|
|
6087
7107
|
var import_sha256 = require("@noble/hashes/sha256");
|
|
@@ -6365,7 +7385,7 @@ function normalizeBitcoinAddress(address) {
|
|
|
6365
7385
|
}
|
|
6366
7386
|
|
|
6367
7387
|
// src/protocol/protocol-pause.ts
|
|
6368
|
-
var
|
|
7388
|
+
var import_ethers8 = require("ethers");
|
|
6369
7389
|
var GET_PROTOCOL_PAUSE_STATUS_ABI = [
|
|
6370
7390
|
"function getProtocolPauseStatus() view returns (tuple(bool positionManagerPaused, bool positionManagerLiquidationsPaused, bool positionManagerCorePaused, bool loanOperationsPaused, bool collateralManagerPaused, bool liquidationManagerPaused, bool circuitBreakerPaused, bool ucdControllerPaused, bool ucdTokenPaused, bool simplePsmPaused))"
|
|
6371
7391
|
];
|
|
@@ -6387,7 +7407,7 @@ function rowToStatus(row) {
|
|
|
6387
7407
|
return row;
|
|
6388
7408
|
}
|
|
6389
7409
|
async function fetchProtocolPauseStatus(provider, positionManagerAddress) {
|
|
6390
|
-
const iface = new
|
|
7410
|
+
const iface = new import_ethers8.Interface(GET_PROTOCOL_PAUSE_STATUS_ABI);
|
|
6391
7411
|
const data = iface.encodeFunctionData("getProtocolPauseStatus", []);
|
|
6392
7412
|
const raw = await provider.call({ to: positionManagerAddress, data });
|
|
6393
7413
|
const decoded = iface.decodeFunctionResult("getProtocolPauseStatus", raw);
|
|
@@ -6520,7 +7540,7 @@ function extractOptionalLitSignature(raw) {
|
|
|
6520
7540
|
}
|
|
6521
7541
|
|
|
6522
7542
|
// src/modules/contract/contract-manager.module.ts
|
|
6523
|
-
var
|
|
7543
|
+
var import_ethers9 = require("ethers");
|
|
6524
7544
|
init_deployment_addresses();
|
|
6525
7545
|
var POSITION_MANAGER_ABI = [
|
|
6526
7546
|
"function createPosition(bytes32 pkpId, bytes calldata validatorSignature, string mainnetVaultAddress, string regtestVaultAddress, uint256 selectedTermMonths, uint256 validatorVersion, bytes calldata pkpPublicKey) external returns (bytes32 positionId)",
|
|
@@ -6597,43 +7617,43 @@ var ContractManager = class {
|
|
|
6597
7617
|
}
|
|
6598
7618
|
const contractRunner = runner;
|
|
6599
7619
|
const contracts = {
|
|
6600
|
-
positionManager: new
|
|
7620
|
+
positionManager: new import_ethers9.Contract(
|
|
6601
7621
|
contractAddresses.positionManager,
|
|
6602
7622
|
POSITION_MANAGER_ABI,
|
|
6603
7623
|
contractRunner
|
|
6604
7624
|
),
|
|
6605
|
-
loanOperationsManager: new
|
|
6606
|
-
contractAddresses.loanOperationsManager ||
|
|
7625
|
+
loanOperationsManager: new import_ethers9.Contract(
|
|
7626
|
+
contractAddresses.loanOperationsManager || import_ethers9.ZeroAddress,
|
|
6607
7627
|
LOAN_OPS_ABI,
|
|
6608
7628
|
contractRunner
|
|
6609
7629
|
),
|
|
6610
|
-
ucdController: new
|
|
7630
|
+
ucdController: new import_ethers9.Contract(
|
|
6611
7631
|
contractAddresses.ucdToken,
|
|
6612
7632
|
UCD_CONTROLLER_ABI,
|
|
6613
7633
|
contractRunner
|
|
6614
7634
|
),
|
|
6615
|
-
priceFeed: new
|
|
6616
|
-
contractAddresses.priceFeedConsumer ||
|
|
7635
|
+
priceFeed: new import_ethers9.Contract(
|
|
7636
|
+
contractAddresses.priceFeedConsumer || import_ethers9.ZeroAddress,
|
|
6617
7637
|
PRICE_FEED_ABI,
|
|
6618
7638
|
contractRunner
|
|
6619
7639
|
),
|
|
6620
|
-
termManager: new
|
|
6621
|
-
contractAddresses.termManager ||
|
|
7640
|
+
termManager: new import_ethers9.Contract(
|
|
7641
|
+
contractAddresses.termManager || import_ethers9.ZeroAddress,
|
|
6622
7642
|
TERM_MANAGER_ABI,
|
|
6623
7643
|
contractRunner
|
|
6624
7644
|
),
|
|
6625
|
-
circuitBreaker: new
|
|
6626
|
-
contractAddresses.circuitBreaker ||
|
|
7645
|
+
circuitBreaker: new import_ethers9.Contract(
|
|
7646
|
+
contractAddresses.circuitBreaker || import_ethers9.ZeroAddress,
|
|
6627
7647
|
CIRCUIT_BREAKER_ABI,
|
|
6628
7648
|
contractRunner
|
|
6629
7649
|
),
|
|
6630
|
-
liquidationManager: new
|
|
6631
|
-
contractAddresses.liquidationManager ||
|
|
7650
|
+
liquidationManager: new import_ethers9.Contract(
|
|
7651
|
+
contractAddresses.liquidationManager || import_ethers9.ZeroAddress,
|
|
6632
7652
|
LIQUIDATION_MANAGER_ABI,
|
|
6633
7653
|
contractRunner
|
|
6634
7654
|
),
|
|
6635
|
-
bitcoinWithdrawalAddressRegistry: new
|
|
6636
|
-
contractAddresses.bitcoinWithdrawalAddressRegistry ||
|
|
7655
|
+
bitcoinWithdrawalAddressRegistry: new import_ethers9.Contract(
|
|
7656
|
+
contractAddresses.bitcoinWithdrawalAddressRegistry || import_ethers9.ZeroAddress,
|
|
6637
7657
|
BITCOIN_WITHDRAWAL_ADDRESS_REGISTRY_ABI,
|
|
6638
7658
|
contractRunner
|
|
6639
7659
|
)
|
|
@@ -6683,13 +7703,13 @@ var ContractManager = class {
|
|
|
6683
7703
|
const address = addresses[key];
|
|
6684
7704
|
if (!address) {
|
|
6685
7705
|
missing.push(String(key));
|
|
6686
|
-
} else if (!(0,
|
|
7706
|
+
} else if (!(0, import_ethers9.isAddress)(address)) {
|
|
6687
7707
|
invalid.push(String(key));
|
|
6688
7708
|
}
|
|
6689
7709
|
}
|
|
6690
7710
|
for (const key of optionalAddresses) {
|
|
6691
7711
|
const address = addresses[key];
|
|
6692
|
-
if (address && !(0,
|
|
7712
|
+
if (address && !(0, import_ethers9.isAddress)(address)) {
|
|
6693
7713
|
invalid.push(String(key));
|
|
6694
7714
|
}
|
|
6695
7715
|
}
|
|
@@ -7665,7 +8685,7 @@ function createPKPManager(config) {
|
|
|
7665
8685
|
}
|
|
7666
8686
|
|
|
7667
8687
|
// src/modules/loan/loan-creator.module.ts
|
|
7668
|
-
var
|
|
8688
|
+
var import_ethers10 = require("ethers");
|
|
7669
8689
|
var import_dh_lit_actions = __toESM(require_pkg_src());
|
|
7670
8690
|
var LoanCreator = class {
|
|
7671
8691
|
config;
|
|
@@ -7995,11 +9015,11 @@ var LoanCreator = class {
|
|
|
7995
9015
|
try {
|
|
7996
9016
|
const mainnetVaultAddress = bitcoinAddresses.mainnet;
|
|
7997
9017
|
const regtestVaultAddress = bitcoinAddresses.regtest;
|
|
7998
|
-
const pkpIdBytes32 = (0,
|
|
7999
|
-
(0,
|
|
9018
|
+
const pkpIdBytes32 = (0, import_ethers10.zeroPadValue)(
|
|
9019
|
+
(0, import_ethers10.toBeHex)(BigInt(pkpData.tokenId)),
|
|
8000
9020
|
32
|
|
8001
9021
|
);
|
|
8002
|
-
const pkpPublicKeyUncompressed =
|
|
9022
|
+
const pkpPublicKeyUncompressed = import_ethers10.SigningKey.computePublicKey(
|
|
8003
9023
|
pkpData.publicKey,
|
|
8004
9024
|
false
|
|
8005
9025
|
);
|
|
@@ -8012,7 +9032,7 @@ var LoanCreator = class {
|
|
|
8012
9032
|
);
|
|
8013
9033
|
log.debug(`BigInt conversion: ${BigInt(pkpData.tokenId).toString()}`);
|
|
8014
9034
|
log.debug(
|
|
8015
|
-
`Hex from BigInt: ${(0,
|
|
9035
|
+
`Hex from BigInt: ${(0, import_ethers10.toBeHex)(BigInt(pkpData.tokenId))}`
|
|
8016
9036
|
);
|
|
8017
9037
|
log.debug(`Final bytes32 (zero-padded): ${pkpIdBytes32}`);
|
|
8018
9038
|
log.debug(
|
|
@@ -8176,8 +9196,8 @@ var LoanCreator = class {
|
|
|
8176
9196
|
}
|
|
8177
9197
|
}
|
|
8178
9198
|
if (this.config.debug) {
|
|
8179
|
-
const pkpIdBytes32Check = (0,
|
|
8180
|
-
(0,
|
|
9199
|
+
const pkpIdBytes32Check = (0, import_ethers10.zeroPadValue)(
|
|
9200
|
+
(0, import_ethers10.toBeHex)(BigInt(pkpData.tokenId)),
|
|
8181
9201
|
32
|
|
8182
9202
|
);
|
|
8183
9203
|
log.error("\u274C Loan creation transaction failed", {
|
|
@@ -8223,8 +9243,8 @@ var LoanCreator = class {
|
|
|
8223
9243
|
const newEventAbi = [
|
|
8224
9244
|
"event PositionCreated(bytes32 indexed positionId, bytes32 indexed pkpId, address indexed borrower, uint256 requestedCollateralRatio, uint256 selectedTerm, uint256 expiryAt)"
|
|
8225
9245
|
];
|
|
8226
|
-
const oldIface = new
|
|
8227
|
-
const newIface = new
|
|
9246
|
+
const oldIface = new import_ethers10.Interface(oldEventAbi);
|
|
9247
|
+
const newIface = new import_ethers10.Interface(newEventAbi);
|
|
8228
9248
|
let positionId;
|
|
8229
9249
|
if (this.config.debug) {
|
|
8230
9250
|
log.info("\u{1F50D} Parsing transaction receipt logs", {
|
|
@@ -8318,7 +9338,7 @@ var LoanStatus = /* @__PURE__ */ ((LoanStatus2) => {
|
|
|
8318
9338
|
})(LoanStatus || {});
|
|
8319
9339
|
|
|
8320
9340
|
// src/modules/loan/loan-query.module.ts
|
|
8321
|
-
var
|
|
9341
|
+
var import_ethers11 = require("ethers");
|
|
8322
9342
|
var POSITION_CORE_ABI = [
|
|
8323
9343
|
"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))"
|
|
8324
9344
|
];
|
|
@@ -8616,7 +9636,7 @@ var LoanQuery = class {
|
|
|
8616
9636
|
const isChipotlePkpId2 = typeof rawId === "string" && rawId.length === 40 && !rawId.toLowerCase().startsWith("04");
|
|
8617
9637
|
if (isChipotlePkpId2) {
|
|
8618
9638
|
if (this.config.positionManagerCoreAddress && this.config.provider) {
|
|
8619
|
-
const pmContract = new
|
|
9639
|
+
const pmContract = new import_ethers11.Contract(
|
|
8620
9640
|
this.config.positionManagerCoreAddress,
|
|
8621
9641
|
POSITION_CORE_ABI,
|
|
8622
9642
|
this.config.provider
|
|
@@ -8847,6 +9867,56 @@ var LoanQuery = class {
|
|
|
8847
9867
|
})
|
|
8848
9868
|
);
|
|
8849
9869
|
}
|
|
9870
|
+
/**
|
|
9871
|
+
* Get loan-event history for every position owned by a wallet.
|
|
9872
|
+
*
|
|
9873
|
+
* Composes getUserPositions + getLoanEvents (the subgraph has no wallet-scoped
|
|
9874
|
+
* event collection). Each returned LoanEvents carries its own positionId, so
|
|
9875
|
+
* callers can flatten and merge into a single per-wallet timeline.
|
|
9876
|
+
*
|
|
9877
|
+
* Bitcoin deposits never appear in the subgraph — merge those in from an
|
|
9878
|
+
* Esplora source on the consumer side.
|
|
9879
|
+
*
|
|
9880
|
+
* @param userAddress - Loan-owner wallet address.
|
|
9881
|
+
* @param filter - Optional per-position event filter (same semantics as getLoanEvents).
|
|
9882
|
+
* @returns One LoanEvents object per position (empty array if the wallet has none).
|
|
9883
|
+
*
|
|
9884
|
+
* @example
|
|
9885
|
+
* ```typescript
|
|
9886
|
+
* const result = await loanQuery.getWalletLoanEvents('0x830b...');
|
|
9887
|
+
* if (result.success) {
|
|
9888
|
+
* const allEvents = result.value; // LoanEvents[] — one per position
|
|
9889
|
+
* }
|
|
9890
|
+
* ```
|
|
9891
|
+
*/
|
|
9892
|
+
async getWalletLoanEvents(userAddress, filter) {
|
|
9893
|
+
if (this.config.debug) {
|
|
9894
|
+
log.info("\u{1F50D} Querying wallet loan events", { userAddress, filter });
|
|
9895
|
+
}
|
|
9896
|
+
return tryCatchAsync(
|
|
9897
|
+
async () => {
|
|
9898
|
+
const perPosition = await this.config.graphClient.getWalletLoanEvents(
|
|
9899
|
+
userAddress,
|
|
9900
|
+
filter
|
|
9901
|
+
);
|
|
9902
|
+
if (this.config.debug) {
|
|
9903
|
+
log.info("\u2705 Retrieved wallet loan events", {
|
|
9904
|
+
userAddress,
|
|
9905
|
+
positions: perPosition.length,
|
|
9906
|
+
totalEvents: perPosition.reduce((n, p) => n + p.totalEvents, 0)
|
|
9907
|
+
});
|
|
9908
|
+
}
|
|
9909
|
+
return perPosition;
|
|
9910
|
+
},
|
|
9911
|
+
(error) => new SDKError({
|
|
9912
|
+
message: "Failed to query wallet loan events",
|
|
9913
|
+
category: "SUBGRAPH" /* SUBGRAPH */,
|
|
9914
|
+
severity: "MEDIUM" /* MEDIUM */,
|
|
9915
|
+
originalError: error instanceof Error ? error : new Error(String(error)),
|
|
9916
|
+
context: { userAddress, filter }
|
|
9917
|
+
})
|
|
9918
|
+
);
|
|
9919
|
+
}
|
|
8850
9920
|
/**
|
|
8851
9921
|
* Clear loan cache
|
|
8852
9922
|
*
|
|
@@ -8889,7 +9959,7 @@ function createLoanQuery(config) {
|
|
|
8889
9959
|
}
|
|
8890
9960
|
|
|
8891
9961
|
// src/modules/withdrawal-address/withdrawal-address.module.ts
|
|
8892
|
-
var
|
|
9962
|
+
var import_ethers12 = require("ethers");
|
|
8893
9963
|
var WithdrawalAddressModule = class {
|
|
8894
9964
|
config;
|
|
8895
9965
|
graphClient;
|
|
@@ -8906,7 +9976,7 @@ var WithdrawalAddressModule = class {
|
|
|
8906
9976
|
if (!res.success)
|
|
8907
9977
|
return res;
|
|
8908
9978
|
const target = res.value.target;
|
|
8909
|
-
if (!target || target ===
|
|
9979
|
+
if (!target || target === import_ethers12.ZeroAddress) {
|
|
8910
9980
|
return failure(
|
|
8911
9981
|
new SDKError({
|
|
8912
9982
|
code: "SDK_WITHDRAWAL_REGISTRY_NOT_CONFIGURED",
|
|
@@ -8924,7 +9994,7 @@ var WithdrawalAddressModule = class {
|
|
|
8924
9994
|
}
|
|
8925
9995
|
/** keccak256 of the canonicalized address — matches the on-chain key. */
|
|
8926
9996
|
static addressHash(btcAddress) {
|
|
8927
|
-
return (0,
|
|
9997
|
+
return (0, import_ethers12.keccak256)((0, import_ethers12.toUtf8Bytes)(normalizeBitcoinAddress(btcAddress)));
|
|
8928
9998
|
}
|
|
8929
9999
|
/**
|
|
8930
10000
|
* Add a Bitcoin address to the caller's allowlist. It becomes usable only
|
|
@@ -11588,6 +12658,39 @@ var DiamondHandsGraph = class {
|
|
|
11588
12658
|
totalEvents
|
|
11589
12659
|
};
|
|
11590
12660
|
}
|
|
12661
|
+
/**
|
|
12662
|
+
* Get the full loan-event history for every position owned by a wallet.
|
|
12663
|
+
*
|
|
12664
|
+
* The subgraph has no wallet-scoped event collection (`User` only derives
|
|
12665
|
+
* `positions`), so this composes the two existing wallet/position queries:
|
|
12666
|
+
* `getUserPositions(wallet)` to enumerate the wallet's positions, then
|
|
12667
|
+
* `getLoanEvents(positionId, filter)` per position. Each returned `LoanEvents`
|
|
12668
|
+
* already carries its own `positionId`, so callers can flatten and merge into
|
|
12669
|
+
* a single timeline.
|
|
12670
|
+
*
|
|
12671
|
+
* Note: Bitcoin deposits are UTXOs on the Bitcoin network and never appear in
|
|
12672
|
+
* the EVM subgraph — surface those from an Esplora source and merge separately.
|
|
12673
|
+
*
|
|
12674
|
+
* @param userAddress - The loan-owner wallet address.
|
|
12675
|
+
* @param filter - Optional per-position event filter (same semantics as getLoanEvents).
|
|
12676
|
+
* @returns One LoanEvents object per position; empty array if the wallet has none.
|
|
12677
|
+
*/
|
|
12678
|
+
async getWalletLoanEvents(userAddress, filter) {
|
|
12679
|
+
const { positions } = await this.getUserPositions(userAddress);
|
|
12680
|
+
const positionIds = positions.map((p) => p?.id != null ? String(p.id) : "").filter((id) => id.length > 0);
|
|
12681
|
+
if (positionIds.length === 0)
|
|
12682
|
+
return [];
|
|
12683
|
+
const CONCURRENCY = 5;
|
|
12684
|
+
const results = [];
|
|
12685
|
+
for (let i = 0; i < positionIds.length; i += CONCURRENCY) {
|
|
12686
|
+
const batch = positionIds.slice(i, i + CONCURRENCY);
|
|
12687
|
+
const settled = await Promise.all(
|
|
12688
|
+
batch.map((positionId) => this.getLoanEvents(positionId, filter))
|
|
12689
|
+
);
|
|
12690
|
+
results.push(...settled);
|
|
12691
|
+
}
|
|
12692
|
+
return results;
|
|
12693
|
+
}
|
|
11591
12694
|
// ============================================================================
|
|
11592
12695
|
// Protocol-wide Events
|
|
11593
12696
|
// ============================================================================
|
|
@@ -11937,7 +13040,7 @@ var DiamondHandsGraph = class {
|
|
|
11937
13040
|
};
|
|
11938
13041
|
|
|
11939
13042
|
// src/contracts/typechain-contracts/factories/src/psm/SimplePSMV2__factory.ts
|
|
11940
|
-
var
|
|
13043
|
+
var import_ethers13 = require("ethers");
|
|
11941
13044
|
var _abi = [
|
|
11942
13045
|
{
|
|
11943
13046
|
inputs: [],
|
|
@@ -14505,7 +15608,7 @@ var _abi = [
|
|
|
14505
15608
|
];
|
|
14506
15609
|
var _bytecode = "0x60a080604052346100ea57306080527ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460ff8160401c166100d9576002600160401b03196001600160401b03821601610073575b60405161485a90816100f0823960805181818161278e01526128870152f35b6001600160401b0319166001600160401b039081177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005581527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a13880610054565b63f92ee8a960e01b60005260046000fd5b600080fdfe6080604052600436101561001257600080fd5b6000803560e01c8062f7fce91461384657806301ffc9a7146137d5578063021d28931461377c5780630871b20614613743578063114249d214610f6157806311d954f81461371a57806313da3166146136e15780631d44a904146136385780631f77bf65146135ff578063248a9ca3146135e05780632b83cccd146131205780632f2ff15d146130ee57806334bac370146130b557806334e181251461307c57806336568abe14613037578063373c86b614612ffe5780633f4ba83a14612f375780634373b57214612f0e5780634451e89b14612b3a5780634ebd25a514612b015780634f1ef2861461280c5780635273b6f6146127e357806352d1902d1461277b578063568fafca146126ed57806359d5d02a146124205780635bc26aea146123eb5780635c975abb146123bb5780635ecc27081461229d5780638456cb59146121a257806385c15d9a1461216957806389d3ad00146120a25780638bff608e146120695780639010d07c1461201657806390ad8b4014611ffa57806391d1485414611fa15780639b759bf714611f5e5780639baf58d214611f415780639f1d0f59146118a8578063a217fddf1461188c578063a3246ad3146117cf578063ad3cb1cc14611788578063b492fc691461168e578063b9f637af14611463578063bf7d911914611424578063c4c5f62414611371578063c55f15ea1461134e578063ca15c87314611317578063ca2ce65d146112de578063cc1f8ffa146112ac578063cf46075e146111a2578063d21a74ff14611165578063d2f8253614611142578063d547741f14611107578063d5d3f78b14610f9a578063d66bd52414610f61578063e1f1c4a714610f44578063e1f3962c14610f18578063ec2f96fe14610ef4578063eda5a26a14610af7578063f1a1eb8414610ab4578063f385116d14610a7b578063f77c479114610a54578063f8c8765e14610661578063faaebd2114610628578063facdfb5e14610488578063fc99bd9a14610377578063fe32e7281461033e5763ffa1ad741461030857600080fd5b3461033b578060031936011261033b57610337610323613a68565b604051918291602083526020830190613a17565b0390f35b80fd5b503461033b57602036600319011261033b576020906040906001600160a01b0361036661392e565b168152601283522054604051908152f35b503461033b57604036600319011261033b5761039161392e565b6010546024359291906001600160a01b03163303610479576001600160a01b03168082526002602052604082205490929060ff16156104665780156104575782825260076020526040822054926103e88285613cb0565b848110610448576001600160ff1b0381116104395760008051602061482e833981519152604085968493846104369852600760205280838a205582519182526020820152a23090339061421c565b80f35b630700fcb360e11b8452600484fd5b63446c8cc960e01b8452600484fd5b63162908e360e11b8252600482fd5b506024916374ed46ad60e01b8252600452fd5b6349db3bd360e01b8252600482fd5b503461033b57602036600319011261033b576104a261392e565b6104aa613f12565b6010546001600160a01b03163303610479576001600160a01b03168082526002602052604082205460ff161561061657808252600760205260408220546040516370a0823160e01b8152306004820152602081602481865afa90811561060b5784916105d4575b50818111156105c5576105248282613b6c565b8215908115610570575b505060008051602061482e833981519152916040918486526007602052808387205582519182526020820152a260016000805160206147ce8339815191525580f35b61271081029080820461271014901517156105b157606484029184830460641417156105b157116105a257388061052e565b63a73af74d60e01b8452600484fd5b634e487b7160e01b86526011600452602486fd5b6105cf8183613b6c565b610524565b90506020813d602011610603575b816105ef602093836139c3565b810103126105fe575138610511565b600080fd5b3d91506105e2565b6040513d86823e3d90fd5b6374ed46ad60e01b8252600452602490fd5b503461033b57602036600319011261033b576020906040906001600160a01b0361065061392e565b168152600483522054604051908152f35b503461033b57608036600319011261033b5761067b61392e565b610683613944565b6044356001600160a01b03811690818103610a50576064356001600160a01b0381169390849003610a4c5760008051602061480e83398151915254604081901c60ff161595906001600160401b031680159081610a44575b6001149081610a3a575b159081610a31575b50610a225760008051602061480e83398151915280546001600160401b0319166001179055856109fd575b6001600160a01b03169081156109ee576001600160a01b03169081156109df5783156109d05761074661448a565b61074e61448a565b61075661448a565b61075e61448a565b60016000805160206147ce8339815191525561077861448a565b61078061448a565b86546001600160a01b0319908116919091178755600180549182168317905560405163313ce56760e01b8152909190602081600481855afa88918161099f575b506107d457633a24e95b60e11b8852600488fd5b6001600160a81b03199092161760a091821b60ff60a01b16176001819055901c60ff16601119810161098d575061080b8186614348565b80610965575b156109205761082e816000805160206147ee833981519152614348565b8061092f575b15610920576108519060008051602061470e833981519152614348565b90816108e9575b50156108da57806108af575b5061086c5780f35b60ff60401b1960008051602061480e833981519152541660008051602061480e833981519152556000805160206146ee833981519152602060405160018152a180f35b601080546001600160a01b031916821790558260008051602061474e8339815191528180a338610864565b63cf5a8e3360e01b8352600483fd5b6109199060008051602061470e83398151915286526000805160206146ae833981519152602052604086206144d0565b5038610858565b63cf5a8e3360e01b8552600485fd5b6000805160206147ee83398151915286526000805160206146ae83398151915260205261095f83604088206144d0565b50610834565b8580526000805160206146ae83398151915260205261098783604088206144d0565b50610811565b63167738cf60e21b8652600452602485fd5b6109c291925060203d6020116109c9575b6109ba81836139c3565b810190613ba9565b90386107c0565b503d6109b0565b630b5eba9f60e41b8752600487fd5b63c1ab6dc160e01b8752600487fd5b6336abb4df60e11b8752600487fd5b60008051602061480e833981519152805460ff60401b1916600160401b179055610718565b63f92ee8a960e01b8752600487fd5b905015386106ed565b303b1591506106e5565b8791506106db565b8580fd5b8480fd5b503461033b578060031936011261033b57546040516001600160a01b039091168152602090f35b503461033b57602036600319011261033b576020906040906001600160a01b03610aa361392e565b168152600a83522054604051908152f35b503461033b57604036600319011261033b576020906040906001600160a01b03610adc61392e565b16815260158352818120602435825283522054604051908152f35b503461033b5761016036600319011261033b57610b1261392e565b90606435604435602435610b24613a58565b9160a4359160c43560e43561010435916101243595610144359760018060a01b03601054163303610ee5576001600160a01b038c16808c52600260205260408c2054909a9060ff16610ed1578a15610ec2578615610eb3576127108811610e99576103e88111610e815760ff82169c60128e118e8115610e76575b50610e6257803b15610e4e578915610e3a578a15610e265782610bc191614260565b84151580610e1d575b610e065785151580610dfd575b610de6578b9c602460208d9e9d604051928380926370a0823160e01b82523060048301525afa9c8d15610dda579c610da6575b506001600160ff1b038c11610d97578a8d8f9760408f97838f918f8f8f8f90859f988f99879f9660008051602061476e8339815191529f978b9f988f8d6000805160206146ce8339815191529f988e8e610d4f9b60008051602061468e8339815191529f8f8f8487526002602052878720600160ff198254161790558487526003602052878720558386526004602052868620558285526006602052878686205582855260056020528585209060ff19825416179055818452600c6020528885852055818452600d6020528985852055818452600e6020528a85852055818452600f6020528b8585205581845260076020528484205582526012602052828220558d8152601360205220558a7f991d1ef1b5c0820d213328cba6682059e00a50e49c469a454eb3557e78eaf6668e80518b81528c6020820152a28c5198899889613cbd565b0390a28151908b82526020820152a28151908782526020820152a280610d73578280f35b604060008051602061482e833981519152918151908582526020820152a281808280f35b63556e7a8160e01b8e5260048efd5b909b506020813d602011610dd2575b81610dc2602093836139c3565b810103126105fe57519a38610c0a565b3d9150610db5565b604051903d90823e3d90fd5b60448c858863dd85772b60e01b8352600452602452fd5b50838610610bd7565b60448c8487637449915760e01b8352600452602452fd5b50828510610bca565b636835314760e11b8d52600160045260248dfd5b636835314760e11b8d5260048d905260248dfd5b63377e3f9960e21b8d5260048c905260248dfd5b63ca95039160e01b8d5260048e905260248dfd5b60069150108e610b9f565b631051834f60e11b8c526004526103e860245260448bfd5b63c1e9a14160e01b8c52600488905261271060245260448cfd5b63a2ec3a5560e01b8c5260048cfd5b63df407ac560e01b8c5260048cfd5b6317f4215b60e21b8c5260048b905260248cfd5b6349db3bd360e01b8b5260048bfd5b503461033b578060031936011261033b57602060ff60015460a01c16604051908152f35b503461033b57602036600319011261033b576020610f3c610f3761392e565b613df5565b604051908152f35b503461033b578060031936011261033b5760206040516127108152f35b503461033b57602036600319011261033b576020906040906001600160a01b03610f8961392e565b168152600783522054604051908152f35b503461033b57602036600319011261033b57610fb461392e565b6010546001600160a01b03163303610479576001600160a01b03168082526002602052604082205460ff16156106165780825260076020526040822054806110f1575080825260026020526040822060ff1981541690558082526003602052816040812055808252600460205281604081205580825260056020526040822060ff198154169055808252600c602052816040812055808252600d602052816040812055808252600e602052816040812055808252600f602052816040812055808252600660205281604081205580825260076020526040822054818352600760205282604081205560405190827f3302a1b37f04242bb55603f5449e7ecd690a35eef08ba1353c48c20e2aae9cda8580a2806110ce578380f35b8160409160008051602061482e8339815191529352846020820152a23880808380f35b604492916376bc059760e11b8352600452602452fd5b503461033b57604036600319011261033b5761113e600435611127613944565b9061113961113482613b79565b614088565b61411a565b5080f35b503461033b578060031936011261033b57602060ff600854166040519015158152f35b503461033b57602036600319011261033b5760209060ff906040906001600160a01b0361119061392e565b16815260058452205416604051908152f35b503461033b57602036600319011261033b576111bc61392e565b6111c4613f12565b6010546001600160a01b03163303610479576001600160a01b03168082526002602052604082205460ff161561061657808252600760205260408220546040516370a0823160e01b8152306004820152602081602481865afa90811561060b57849161126c575b5060008051602061482e833981519152916040918486526007602052808387205582519182526020820152a260016000805160206147ce8339815191525580f35b90506020813d6020116112a4575b81611287602093836139c3565b810103126105fe575160008051602061482e83398151915261122b565b3d915061127a565b503461033b57604036600319011261033b5760206112d46112cb61392e565b60243590613cf6565b6040519015158152f35b503461033b57602036600319011261033b576020906040906001600160a01b0361130661392e565b168152601383522054604051908152f35b503461033b57602036600319011261033b57604060209160043581526000805160206146ae83398151915283522054604051908152f35b503461033b578060031936011261033b576020604051670de0b6b3a76400008152f35b503461033b57604036600319011261033b5761138b61392e565b6010546024359291906001600160a01b03163303610479576001600160a01b031680825260076020526040822054929081841061140d578060008051602061482e8339815191526040866113e3866104369899613b6c565b84895260076020528289205583885260076020528188205482519182526020820152a2339061404c565b5091604492633cb8e07360e01b8352600452602452fd5b503461033b57602036600319011261033b5760209060ff906040906001600160a01b0361144f61392e565b168152600284522054166040519015158152f35b503461033b5761012036600319011261033b5761147e61392e565b6064359190604435602435611491613a58565b601054610104359660e4359260c4359260a43592906001600160a01b0316330361167f576001600160a01b038816808a52600260205260408a205490989060ff161561166b57861561165c576127108811611642576103e882116116285760ff83169060128211801561161e575b61160a578361150d91614260565b85151580611601575b6115ea578a1515806115e1575b6115ca579260008051602061468e83398151915297969592828c956115c4979460408e8e80825260036020528c8383205580825260046020528d838320558152600660205220558b8d52600560205260408d209060ff198254161790558a8c52600c6020528260408d20558a8c52600d6020528360408d20558a8c52600e6020528460408d20558a8c52600f6020528560408d205560405198899889613cbd565b0390a280f35b60448a868d63dd85772b60e01b8352600452602452fd5b50848b10611523565b60448a8588637449915760e01b8352600452602452fd5b50838610611516565b63ca95039160e01b8b52600482905260248bfd5b50600682106114ff565b631051834f60e11b8a5260048290526103e860245260448afd5b63c1e9a14160e01b8a52600488905261271060245260448afd5b63a2ec3a5560e01b8a5260048afd5b6374ed46ad60e01b8a52600489905260248afd5b6349db3bd360e01b8952600489fd5b503461033b57604036600319011261033b576116a861392e565b6001600160a01b0316808252600260205260408220546024359060ff16156117745780156117655761271061172a604085670de0b6b3a76400006117196117329688859a52600560205260ff85852054169080916012811061173f575b505088845260036020528484205490613a8b565b049581526004602052205484613a8b565b048092613b6c565b9082519182526020820152f35b61175e925060ff61175261175892613ad4565b16613ae5565b90613a8b565b3880611705565b63162908e360e11b8352600483fd5b6374ed46ad60e01b83526004829052602483fd5b503461033b578060031936011261033b5761033760408051906117ab81836139c3565b60058252640352e302e360dc1b602083015251918291602083526020830190613a17565b503461033b57602036600319011261033b5760043581526000805160206146ae83398151915260205260408120604051908160208254918281520190819285526020852090855b818110611876575050508261182c9103836139c3565b604051928392602084019060208552518091526040840192915b818110611854575050500390f35b82516001600160a01b0316845285945060209384019390920191600101611846565b8254845260209093019260019283019201611816565b503461033b578060031936011261033b57602090604051908152f35b503461033b576118b736613969565b9190926118c2613f12565b6118ca613f4e565b338152600960205260408120544303611f1f57338152600a602052600360408220541015611f1057338152600a602052604081206119088154613b9a565b90555b6001600160a01b0382168082526002602052604082205490929060ff16611e6f575b828252600260205260ff60408320541615610466578415610457578315611e6057828252600c60205260408220548510611e3b57828252600e60205260408220548015611e2c57808611611e155750846119869161415e565b6040516370a0823160e01b8152306004820152602081602481865afa908115611e0a578291611dd8575b506119bd8530338661421c565b6040516370a0823160e01b8152306004820152602081602481875afa908115611cef57908692918491611d9f575b50906119f691613b6c565b10611d905781815260076020526040812054611a128582613cb0565b818110611d81576001600160ff1b038111611d72576040849260008051602061482e833981519152928486526007602052808387205582519182526020820152a2818152600560205260ff604082205416849060128110611d11575b50611a8f670de0b6b3a7640000918484526003602052604084205490613a8b565b04908281526004602052612710611aaa604083205484613a8b565b0491611ab68382613b6c565b94808610611cfa57506001546040516318160ddd60e01b815290602090829060049082906001600160a01b03165afa908115611cef578391611cbd575b5082546001600160a01b0316803b15611ca45783809160846040518094819363ae494fa160e01b83523360048401528c60248401528b60448401528d60648401525af1801561060b57908491611ca8575b505083611c34575b6001546040516318160ddd60e01b81529190602090839060049082906001600160a01b03165afa801561060b578490611c00575b611b8a9250613b6c565b90808203611bea575050507fb3e2773606abfd36b5bd91394b3a54d1398336c65005baf7bf7a05efeffaf75b611bcd602095604051918291339588429285613bdd565b0390a360016000805160206147ce83398151915255604051908152f35b632d8562a360e11b835260045260245260449150fd5b506020823d602011611c2c575b81611c1a602093836139c3565b810103126105fe57611b8a9151611b80565b3d9150611c0d565b82546001600160a01b0316803b15611ca45783809160846040518094819363ae494fa160e01b83523060048401528a60248401528b60448401528160648401525af1801561060b57908491611c8b575b5050611b4c565b81611c95916139c3565b611ca0578238611c84565b8280fd5b8380fd5b81611cb2916139c3565b611ca0578238611b44565b90506020813d602011611ce7575b81611cd8602093836139c3565b810103126105fe575138611af3565b3d9150611ccb565b6040513d85823e3d90fd5b6371c4efed60e01b83526004869052602452604482fd5b611d21915061175260ff91613ad4565b8015611d5e5780600019048511611d4f57611a8f611d48670de0b6b3a76400009287613a8b565b9150611a6e565b63035fc53d60e41b8252600482fd5b634e487b7160e01b82526012600452602482fd5b630700fcb360e11b8352600483fd5b63446c8cc960e01b8352600483fd5b639bfa3c1760e01b8152600490fd5b919250506020813d602011611dd0575b81611dbc602093836139c3565b810103126105fe57518591906119f66119eb565b3d9150611daf565b90506020813d602011611e02575b81611df3602093836139c3565b810103126105fe5751386119b0565b3d9150611de6565b6040513d84823e3d90fd5b631d7f2ac360e01b83526004869052602452604482fd5b633b5db2f360e11b8352600483fd5b50908152600c6020526040812054630d54e9e160e01b82526004849052602452604490fd5b632cd84af360e01b8252600482fd5b60405163313ce56760e01b815291939291602081600481865afa859181611eef575b50611ea55763105482c760e21b8552600485fd5b93919293838352600560205260ff806040852054169116908103611ec9575061192d565b90506044928252600560205260ff604083205416631e43476360e11b8352600452602452fd5b611f0991925060203d6020116109c9576109ba81836139c3565b9038611e91565b63a74c1c5f60e01b8152600490fd5b3381526009602052436040822055338152600a6020526001604082205561190b565b503461033b578060031936011261033b5760206040516103e88152f35b503461033b57604036600319011261033b576020906040906001600160a01b03611f8661392e565b16815260148352818120602435825283522054604051908152f35b503461033b57604036600319011261033b576040611fbd613944565b91600435815260008051602061478e833981519152602052209060018060a01b0316600052602052602060ff604060002054166040519015158152f35b503461033b578060031936011261033b57602060405160648152f35b503461033b57604036600319011261033b5761205060209160043581526000805160206146ae8339815191528352604060243591206144b8565b905460405160039290921b1c6001600160a01b03168152f35b503461033b57602036600319011261033b576020906040906001600160a01b0361209161392e565b168152600983522054604051908152f35b503461033b57604036600319011261033b576120bc61392e565b60105460243591906001600160a01b0316330361215a576001600160a01b031690811561214b57604060008051602061476e8339815191529183855260136020528185205490848652601360205280838720558486526015602052828620438752602052828620548587526019602052838720558486526017602052438387205582519182526020820152a280f35b63df407ac560e01b8352600483fd5b6349db3bd360e01b8352600483fd5b503461033b57602036600319011261033b576020906040906001600160a01b0361219161392e565b168152600383522054604051908152f35b503461033b578060031936011261033b5760008051602061470e833981519152815260008051602061478e83398151915260209081526040808320336000908152925290205460ff1615612278576121f8613f4e565b600160ff196000805160206147ae8339815191525416176000805160206147ae833981519152557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a16040514281527f80170b5fcdd2bf1e0660ef4b8851f86685f64d41b1a19de1471947ece8725aac60203392a280f35b63e2517d3f60e01b81523360045260008051602061470e833981519152602452604490fd5b503461033b57602036600319011261033b576122b761392e565b60008051602061480e8339815191525460ff8160401c169081156123a3575b506123945760008051602061480e83398151915280546001600160481b0319166003600160401b0117905581805260008051602061478e83398151915260209081526040808420336000908152925290205460ff161561237c5761233990613c5e565b60ff60401b1960008051602061480e833981519152541660008051602061480e833981519152556000805160206146ee833981519152602060405160038152a180f35b63e2517d3f60e01b8252336004526024829052604482fd5b63f92ee8a960e01b8252600482fd5b60036001600160401b039190911610159050386122d6565b503461033b578060031936011261033b57602060ff6000805160206147ae83398151915254166040519015158152f35b503461033b57602036600319011261033b5761240561392e565b6010546001600160a01b031633036104795761043690613c5e565b503461033b57606036600319011261033b5761243a61392e565b612442613944565b60105490926044359290916001600160a01b0316330361047957612464613f4e565b6011546001600160a01b03169182156126de5760405163babcc53960e01b81526001600160a01b0386166004820181905293602090829060249082905afa908115611e0a57829161269f575b5015612690578215612681576001546001600160a01b03928316921682036125d1576040516370a0823160e01b8152306004820152602081602481865afa908115611e0a57829161259f575b50935b841561259057600019810361258a5750835b841561257b5780851161256357505061252d836020958361404c565b7f0bf519e4b3821615dbdd874fa1a5fbb4bb651b4e6beabedd4ac318e770233b2c604080518581524287820152a3604051908152f35b630555ed4f60e31b8252600485905260245260449150fd5b632e11316f60e11b8252600482fd5b93612511565b63f09872eb60e01b8252600482fd5b90506020813d6020116125c9575b816125ba602093836139c3565b810103126105fe5751386124fc565b3d91506125ad565b8181526002602052604081205460ff161561266f576040516370a0823160e01b8152306004820152602081602481865afa908115611e0a57829161263d575b5082825260076020526040822054808211156126355761262f91613b6c565b936124ff565b50508061262f565b90506020813d602011612667575b81612658602093836139c3565b810103126105fe575138612610565b3d915061264b565b60249163eef7849760e01b8252600452fd5b634e46966960e11b8152600490fd5b63133307bb60e21b8152600490fd5b90506020813d6020116126d6575b816126ba602093836139c3565b810103126126d2575180151581036126d257386124b0565b5080fd5b3d91506126ad565b630e048e7160e41b8152600490fd5b503461033b57602036600319011261033b5761270761392e565b6010546001600160a01b03163303610479576001600160a01b0316801561276c57601180546001600160a01b0319811683179091556001600160a01b03167f1e9c406f2707909cfd9f61e744f73bcab77e5997d063276f67883c5257d51cf08380a380f35b634e46966960e11b8252600482fd5b503461033b578060031936011261033b577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031630036127d457602060405160008051602061472e8339815191528152f35b63703e46dd60e11b8152600490fd5b503461033b578060031936011261033b576010546040516001600160a01b039091168152602090f35b50604036600319011261033b5761282161392e565b602435906001600160401b038211611ca05736602383011215611ca0578160040135908361284e836139fc565b9361285c60405195866139c3565b83855260208501933660248284010111611ca057806024602093018637850101526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016308114908115612ade575b50612acf576010546001600160a01b03163303612ac0576001600160a01b038116928315612ab157813b15612aa25760008051602061472e83398151915254846128fa613a68565b7f6ea8b34d59b2818d3f37a08dddaa06f5886b9c20c907b36f03ca6528e106e86f61293060405192604084526040840190613a17565b4260208401526001600160a01b038516929081900390a36040516352d1902d60e01b8152602081600481895afa879181612a6a575b5061297e57634c9c8ce360e01b87526004869052602487fd5b908160008051602061472e83398151915288979303612a585750833b15612a44576001600160a01b031916811760008051602061472e833981519152558491907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8380a2805115612a295761113e9382915190845af43d15612a21573d91612a05836139fc565b92612a1360405194856139c3565b83523d85602085013e61460c565b60609161460c565b5050505034612a355780f35b63b398979f60e01b8152600490fd5b634c9c8ce360e01b86526004829052602486fd5b632a87526960e21b8752600452602486fd5b9091506020813d602011612a9a575b81612a86602093836139c3565b81010312612a9657519038612965565b8780fd5b3d9150612a79565b636f7c43f160e01b8552600485fd5b63e6c4247b60e01b8552600485fd5b6349db3bd360e01b8452600484fd5b63703e46dd60e11b8452600484fd5b60008051602061472e833981519152546001600160a01b031614159050386128b2565b503461033b57602036600319011261033b576020906040906001600160a01b03612b2961392e565b168152600e83522054604051908152f35b503461033b57606036600319011261033b57612b5461395a565b6024356001600160401b038111611ca057612b73903690600401613993565b916044356001600160401b038111610a5057612b93903690600401613993565b60008051602061480e8339815191529291925460ff8160401c16908115612ef6575b50612ee75760008051602061480e83398151915280546001600160481b0319166002600160401b011790556000805160206147ee833981519152865260008051602061478e83398151915260209081526040808820336000908152925290205460ff1615612ec257612c2561448a565b612c2d61448a565b60016000805160206147ce83398151915255600b549160ff8316612eb357818603612ea457612c5b86613bf9565b92612c6960405194856139c3565b868452601f19612c7888613bf9565b01366020860137875b878110612daf575060019060ff191617600b5515159460ff196008541660ff871617600855865b818110612d0c578760008051602061466e833981519152602089604051908152a160ff60401b1960008051602061480e833981519152541660008051602061480e833981519152556000805160206146ee833981519152602060405160028152a180f35b808860008051602061482e8339815191526040612d34612d2f600196888d613c10565b613c36565b612d3f85898c613c10565b357f8f955fa878b4aae8b1ac37b1ab715307470f2822ceff87e23d33ef1e044814a783612d6c888d613c4a565b5193898060a01b0316968793848252600660205280838320558482526007602052858383205582519182526020820152a28151908d82526020820152a201612ca8565b612dbd612d2f828a8a613c10565b612dc8828689613c10565b6001600160a01b03909116808b52600260205260408b20549091359060ff1615612e90576103e88111612e785750906020602492604051938480926370a0823160e01b82523060048301525afa8015612e6d578a90612e37575b60019250612e308288613c4a565b5201612c81565b509060203d8111612e66575b612e4d81836139c3565b6020826000928101031261033b57509060019151612e22565b503d612e43565b6040513d8c823e3d90fd5b631051834f60e11b8b526004526103e860245260448afd5b6351104c5960e01b8b52600482905260248bfd5b63512509d360e11b8752600487fd5b6324d0d1fd60e01b8752600487fd5b63e2517d3f60e01b8652336004526000805160206147ee833981519152602452604486fd5b63f92ee8a960e01b8652600486fd5b60026001600160401b03919091161015905038612bb5565b503461033b578060031936011261033b576011546040516001600160a01b039091168152602090f35b503461033b578060031936011261033b576010546001600160a01b03163303612fef576000805160206147ae8339815191525460ff811615612fe05760ff19166000805160206147ae833981519152557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a16040514281527f107553d8191d85b405879cf752997865edd48d94e20bda4dd27223c94b31a7cc60203392a280f35b638dfc202b60e01b8252600482fd5b6349db3bd360e01b8152600490fd5b503461033b57602036600319011261033b576020906040906001600160a01b0361302661392e565b168152601683522054604051908152f35b503461033b57604036600319011261033b57613051613944565b336001600160a01b0382160361306d5761113e9060043561411a565b63334bd91960e11b8252600482fd5b503461033b57602036600319011261033b576020906040906001600160a01b036130a461392e565b168152600c83522054604051908152f35b503461033b57602036600319011261033b576020906040906001600160a01b036130dd61392e565b168152601783522054604051908152f35b503461033b57604036600319011261033b5761113e60043561310e613944565b9061311b61113482613b79565b6140d2565b503461033b5761312f36613969565b91909261313a613f12565b613142613f4e565b3381526009602052604081205443036135be57338152600a602052600360408220541015611f1057338152600a602052604081206131808154613b9a565b90555b6001600160a01b0382168082526002602052604082205490929060ff16613543575b828252600260205260ff604083205416156104665760ff6008541615613534578415610457578315611e6057828252600d602052604082205480861061351d5750828252600f6020526040822054801561350e578086116134f857508461320b91613f78565b670de0b6b3a76400008402848104670de0b6b3a7640000036134e4578282526003602052604082205461323d91613ab4565b90828152600560205260ff604082205416601281106134c4575b50828152600660205261327c612710613274604084205485613a8b565b048093613b6c565b938085106134ac575082815260076020528360408220541061348857828152600760205260408120548481106134795760008051602061482e8339815191526040826132c9888895613b6c565b8486526007602052808387205582519182526020820152a26001546040516318160ddd60e01b815290602090829060049082906001600160a01b03165afa908115611e0a578291613447575b5081546001600160a01b0316803b15611ca057826040518092633a52b90760e11b82528183816133498d3360048401613bc2565b03925af18015611cef57613432575b506001546040516318160ddd60e01b81529190602090839060049082906001600160a01b03165afa908115611cef5783916133fc575b6133989250613b6c565b8581036133e45750507f8caf04742286d017f9ac3924388e188c73e6e5094311c5e59a61a7ef86dda8bf611bcd6020956133d386338761404c565b604051918291339588429285613bdd565b635ab6e2db60e01b8252600486905260245260449150fd5b90506020823d60201161342a575b81613417602093836139c3565b810103126105fe5761339891519061338e565b3d915061340a565b9161344081600493946139c3565b9190613358565b90506020813d602011613471575b81613462602093836139c3565b810103126126d2575138613315565b3d9150613455565b6308aed4ef60e31b8252600482fd5b82815260076020526040812054635960d22160e01b82526004526024849052604490fd5b6371c4efed60e01b8252600485905260245260449150fd5b916134d760ff6117526134dd9495613ad4565b90613ab4565b9038613257565b634e487b7160e01b82526011600452602482fd5b625502a360e91b83526004869052602452604482fd5b636c6ff70d60e11b8352600483fd5b6361767b8560e01b83526004869052602452604482fd5b630df65d9f60e01b8252600482fd5b60405163313ce56760e01b815291939291602081600481865afa85918161359d575b506135795763105482c760e21b8552600485fd5b93919293838352600560205260ff806040852054169116908103611ec957506131a5565b6135b791925060203d6020116109c9576109ba81836139c3565b9038613565565b3381526009602052436040822055338152600a60205260016040822055613183565b503461033b57602036600319011261033b576020610f3c600435613b79565b503461033b57602036600319011261033b576020906040906001600160a01b0361362761392e565b168152600f83522054604051908152f35b503461033b57604036600319011261033b5761365261392e565b60105460243591906001600160a01b0316330361215a576001600160a01b031690811561214b5760406000805160206146ce8339815191529183855260126020528185205490848652601260205280838720558486526014602052828620438752602052828620548587526018602052838720558486526016602052438387205582519182526020820152a280f35b503461033b57602036600319011261033b576020906040906001600160a01b0361370961392e565b168152600683522054604051908152f35b503461033b578060031936011261033b576001546040516001600160a01b039091168152602090f35b503461033b57602036600319011261033b576020906040906001600160a01b0361376b61392e565b168152600d83522054604051908152f35b503461033b57602036600319011261033b5761379661395a565b6010546001600160a01b0316330361047957602060008051602061466e83398151915291151560ff196008541660ff821617600855604051908152a180f35b503461033b57602036600319011261033b5760043563ffffffff60e01b81168091036126d257602090635a05180f60e01b811490811561381b575b506040519015158152f35b637965db0b60e01b811491508115613835575b5082613810565b6301ffc9a760e01b1490508261382e565b503461033b57604036600319011261033b5761386061392e565b6001600160a01b0316808252600260205260408220546024359060ff161561177457670de0b6b3a7640000810290808204670de0b6b3a7640000149015171561391a57916138bc60409383835260036020528483205490613ab4565b91808252600560205260ff8483205416601281106138f1575b5061172a84836127109361173295526006602052205484613a8b565b846117329361390f61271094966134d760ff61175261172a97613ad4565b9593509350506138d5565b634e487b7160e01b83526011600452602483fd5b600435906001600160a01b03821682036105fe57565b602435906001600160a01b03821682036105fe57565b6004359081151582036105fe57565b60609060031901126105fe576004356001600160a01b03811681036105fe57906024359060443590565b9181601f840112156105fe578235916001600160401b0383116105fe576020808501948460051b0101116105fe57565b601f909101601f19168101906001600160401b038211908210176139e657604052565b634e487b7160e01b600052604160045260246000fd5b6001600160401b0381116139e657601f01601f191660200190565b919082519283825260005b848110613a43575050826000602080949584010152601f8019910116010190565b80602080928401015182828601015201613a22565b6084359060ff821682036105fe57565b60405190613a776040836139c3565b6005825264322e332e3360d81b6020830152565b81810292918115918404141715613a9e57565b634e487b7160e01b600052601160045260246000fd5b8115613abe570490565b634e487b7160e01b600052601260045260246000fd5b60ff166012039060ff8211613a9e57565b8015613b665760009060208110600116604e821060011617613b5e57600a916001915b60018111613b35575082600019048211613b2157500290565b634e487b7160e01b81526011600452602490fd5b92806000190481116134e45760018416613b55575b80029260011c613b08565b80920291613b4a565b9050600a0a90565b50600190565b91908203918211613a9e57565b60005260008051602061478e83398151915260205260016040600020015490565b6000198114613a9e5760010190565b908160209103126105fe575160ff811681036105fe5790565b6001600160a01b039091168152602081019190915260400190565b9094939260609260808301968352602083015260408201520152565b6001600160401b0381116139e65760051b60200190565b9190811015613c205760051b0190565b634e487b7160e01b600052603260045260246000fd5b356001600160a01b03811681036105fe5790565b8051821015613c205760209160051b010190565b6001600160a01b03168015613c9f57601080546001600160a01b0319811683179091556001600160a01b031660008051602061474e833981519152600080a3565b63e6c4247b60e01b60005260046000fd5b91908201809211613a9e57565b94919360e0969360ff929a99989561010088019b885260208801526040870152166060850152608084015260a083015260c08201520152565b6001600160a01b03811660008181526002602052604090205490919060ff16158015613de8575b8015613de0575b613dce5781600052600f6020526040600020548015908115613dd6575b50613dce57613d66926040918251948592839262f7fce960e01b845260048401613bc2565b0381305afa918215613dc257600092613d8e575b506000526007602052604060002054101590565b9091506040813d604011613dba575b81613daa604093836139c3565b810103126105fe57519038613d7a565b3d9150613d9d565b6040513d6000823e3d90fd5b505050600090565b9050831138613d41565b508215613d24565b5060ff6008541615613d1d565b6001546001600160a01b0391821691168103613e6a576020602491604051928380926370a0823160e01b82523060048301525afa908115613dc257600091613e3b575090565b90506020813d602011613e62575b81613e56602093836139c3565b810103126105fe575190565b3d9150613e49565b80600052600260205260ff60406000205416613e865750600090565b6040516370a0823160e01b815230600482015290602082602481845afa918215613dc257600092613ede575b506000526007602052604060002054808211600014613ed757613ed491613b6c565b90565b5050600090565b90916020823d602011613f0a575b81613ef9602093836139c3565b8101031261033b5750519038613eb2565b3d9150613eec565b60026000805160206147ce8339815191525414613f3d5760026000805160206147ce83398151915255565b633ee5aeb560e01b60005260046000fd5b60ff6000805160206147ae8339815191525416613f6757565b63d93c066560e01b60005260046000fd5b6001600160a01b0316600081815260136020526040902054801561404757816000526015602052604060002043600052602052613fbb6040600020549384613cb0565b92831061403657808260005260176020524360406000205414614018575b8311613ffc57506000526015602052604060002043600052602052604060002055565b925063029359d560e41b60005260045260245260445260646000fd5b5081600052601960205261403160406000205482613cb0565b613fd9565b63446c8cc960e01b60005260046000fd5b505050565b614081614086939261407360405194859263a9059cbb60e01b602085015260248401613bc2565b03601f1981018452836139c3565b6142ed565b565b600081815260008051602061478e8339815191526020908152604080832033845290915290205460ff16156140ba5750565b63e2517d3f60e01b6000523360045260245260446000fd5b6140dc8282614348565b91826140e757505090565b60009182526000805160206146ae8339815191526020526040909120614116916001600160a01b0316906144d0565b5090565b61412482826143ea565b918261412f57505090565b60009182526000805160206146ae8339815191526020526040909120614116916001600160a01b031690614539565b6001600160a01b03166000818152601260205260409020548015614047578160005260146020526040600020436000526020526141a16040600020549384613cb0565b928310614036578082600052601660205243604060002054146141fe575b83116141e257506000526014602052604060002043600052602052604060002055565b92506319a4e2a760e01b60005260045260245260445260646000fd5b5081600052601860205261421760406000205482613cb0565b6141bf565b6040516323b872dd60e01b60208201526001600160a01b039283166024820152929091166044830152606480830193909352918152614086916140816084836139c3565b60405163313ce56760e01b81529190602090839060049082906001600160a01b03165afa600092816142cc575b506142a357631eecbb6560e01b60005260046000fd5b60ff91821691168082036142b5575050565b631e43476360e11b60005260045260245260446000fd5b6142e691935060203d6020116109c9576109ba81836139c3565b913861428d565b906000602091828151910182855af115613dc2576000513d61433f57506001600160a01b0381163b155b61431e5750565b635274afe760e01b60009081526001600160a01b0391909116600452602490fd5b60011415614317565b600081815260008051602061478e833981519152602090815260408083206001600160a01b038616845290915290205460ff16613ed757600081815260008051602061478e833981519152602090815260408083206001600160a01b0395909516808452949091528120805460ff19166001179055339291907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9080a4600190565b600081815260008051602061478e833981519152602090815260408083206001600160a01b038616845290915290205460ff1615613ed757600081815260008051602061478e833981519152602090815260408083206001600160a01b0395909516808452949091528120805460ff19169055339291907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9080a4600190565b60ff60008051602061480e8339815191525460401c16156144a757565b631afcd79f60e31b60005260046000fd5b8054821015613c205760005260206000200190600090565b6001810190826000528160205260406000205415600014613dce578054600160401b8110156139e65761452461450d8260018794018555846144b8565b819391549060031b91821b91600019901b19161790565b90555491600052602052604060002055600190565b9060018201918160005282602052604060002054801515600014614603576000198101818111613a9e578254600019810191908211613a9e578181036145cc575b505050805480156145b657600019019061459482826144b8565b8154906000199060031b1b191690555560005260205260006040812055600190565b634e487b7160e01b600052603160045260246000fd5b6145ec6145dc61450d93866144b8565b90549060031b1c928392866144b8565b90556000528360205260406000205538808061457a565b50505050600090565b90614632575080511561462157602081519101fd5b63d6bda27560e01b60005260046000fd5b81511580614664575b614643575090565b639996b31560e01b60009081526001600160a01b0391909116600452602490fd5b50803b1561463b56fe2c0a5f5ab0ba371221bb3612c2d2f90d36566279309f526000c5b354efd1b0658c67aaac7f77de07240e5a9b36dfdccb2a89c438635abbb27a73142d2aa0f629c1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e8237170593200048b07fab687f450848f1df8a96db08fef7840f53415f18fdc2ec8e9f810f0930c7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d275027301df982f5215b4963a854cf9c73219df80cb6acd8f098189c9480c7bbc360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc17dc5b1290f7495d7f8cc68476e6941aaf510598fa925d0d2720edc8805a846c4460507c21097c15f6883572ebd157a9ee4b1d989a312314e93afd9d2ecd494002dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f033009b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006d129bd71f2401cbe77f49f546d7d5f58992faecfca6e51ba06b50b261e83331a164736f6c6343000822000a";
|
|
14507
15610
|
var isSuperArgs = (xs) => xs.length > 1;
|
|
14508
|
-
var SimplePSMV2__factory = class extends
|
|
15611
|
+
var SimplePSMV2__factory = class extends import_ethers13.ContractFactory {
|
|
14509
15612
|
constructor(...args) {
|
|
14510
15613
|
if (isSuperArgs(args)) {
|
|
14511
15614
|
super(...args);
|
|
@@ -14525,10 +15628,10 @@ var SimplePSMV2__factory = class extends import_ethers12.ContractFactory {
|
|
|
14525
15628
|
static bytecode = _bytecode;
|
|
14526
15629
|
static abi = _abi;
|
|
14527
15630
|
static createInterface() {
|
|
14528
|
-
return new
|
|
15631
|
+
return new import_ethers13.Interface(_abi);
|
|
14529
15632
|
}
|
|
14530
15633
|
static connect(address, runner) {
|
|
14531
|
-
return new
|
|
15634
|
+
return new import_ethers13.Contract(address, _abi, runner);
|
|
14532
15635
|
}
|
|
14533
15636
|
};
|
|
14534
15637
|
|
|
@@ -15000,6 +16103,15 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
15000
16103
|
return {};
|
|
15001
16104
|
return this.serverSession.getAuthHeader();
|
|
15002
16105
|
}
|
|
16106
|
+
/**
|
|
16107
|
+
* Remove the persisted lit-ops-server session (the 24h re-mint envelope and
|
|
16108
|
+
* its JWT) for the current auth signer. Call on wallet disconnect or
|
|
16109
|
+
* explicit sign-out so the credential does not outlive the wallet
|
|
16110
|
+
* connection. No-op in standalone mode.
|
|
16111
|
+
*/
|
|
16112
|
+
async clearServerSession() {
|
|
16113
|
+
await this.serverSession?.clearPersisted();
|
|
16114
|
+
}
|
|
15003
16115
|
/**
|
|
15004
16116
|
* Audit H-9: invalidate the LoanQuery cache so subsequent reads return
|
|
15005
16117
|
* the post-write state. We clear the entire loan-query cache (not just
|
|
@@ -15095,7 +16207,7 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
15095
16207
|
*/
|
|
15096
16208
|
constructor(config) {
|
|
15097
16209
|
if (!config.provider && config.ethRpcUrl) {
|
|
15098
|
-
config.provider = new
|
|
16210
|
+
config.provider = new import_ethers14.JsonRpcProvider(config.ethRpcUrl);
|
|
15099
16211
|
}
|
|
15100
16212
|
this.config = config;
|
|
15101
16213
|
if (config.debug) {
|
|
@@ -15111,7 +16223,10 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
15111
16223
|
this.serverSession = new ServerSession({
|
|
15112
16224
|
signer: config.authSigner ?? config.contractSigner,
|
|
15113
16225
|
serviceEndpoint: config.serviceEndpoint,
|
|
15114
|
-
chainId: config.chainId
|
|
16226
|
+
chainId: config.chainId,
|
|
16227
|
+
persistSession: config.sessionPersistence?.enabled,
|
|
16228
|
+
sessionStore: config.sessionPersistence?.store,
|
|
16229
|
+
onSignaturePrompt: config.onSessionSignaturePrompt
|
|
15115
16230
|
});
|
|
15116
16231
|
}
|
|
15117
16232
|
const contractManagerResult = createContractManager({
|
|
@@ -15361,7 +16476,7 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
15361
16476
|
const network = await config.provider.getNetwork();
|
|
15362
16477
|
chainId = Number(network.chainId);
|
|
15363
16478
|
} else if (config.ethRpcUrl) {
|
|
15364
|
-
const tempProvider = new
|
|
16479
|
+
const tempProvider = new import_ethers14.JsonRpcProvider(
|
|
15365
16480
|
config.ethRpcUrl
|
|
15366
16481
|
);
|
|
15367
16482
|
const network = await tempProvider.getNetwork();
|
|
@@ -15884,7 +16999,7 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
15884
16999
|
}
|
|
15885
17000
|
const loanOps = this.loanOps();
|
|
15886
17001
|
const protocolConfig = await loanOps.getProtocolConfig();
|
|
15887
|
-
const requestAmountWei = (0,
|
|
17002
|
+
const requestAmountWei = (0, import_ethers14.parseEther)(
|
|
15888
17003
|
request.amount.toString()
|
|
15889
17004
|
);
|
|
15890
17005
|
const minLoanValueWei = BigInt(protocolConfig.minimumLoanValueWei);
|
|
@@ -15966,7 +17081,7 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
15966
17081
|
pkpNftAddress,
|
|
15967
17082
|
this.getChipotlePublicKeyFallback()
|
|
15968
17083
|
);
|
|
15969
|
-
pkpEthAddress = (0,
|
|
17084
|
+
pkpEthAddress = (0, import_ethers14.computeAddress)(pkpPublicKey);
|
|
15970
17085
|
pkpCache.set(request.positionId, {
|
|
15971
17086
|
publicKey: pkpPublicKey,
|
|
15972
17087
|
ethAddress: pkpEthAddress,
|
|
@@ -16295,8 +17410,8 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
16295
17410
|
throw new Error(`Position not found: ${request.positionId}`);
|
|
16296
17411
|
}
|
|
16297
17412
|
const currentDebt = position.ucdDebt.toString();
|
|
16298
|
-
const ucdDebtHash = (0,
|
|
16299
|
-
const contractHash = (0,
|
|
17413
|
+
const ucdDebtHash = (0, import_ethers14.keccak256)(import_ethers14.AbiCoder.defaultAbiCoder().encode(["uint256"], [currentDebt]));
|
|
17414
|
+
const contractHash = (0, import_ethers14.keccak256)(import_ethers14.AbiCoder.defaultAbiCoder().encode(
|
|
16300
17415
|
["address", "address", "address", "address"],
|
|
16301
17416
|
[
|
|
16302
17417
|
positionManagerAddress,
|
|
@@ -16308,262 +17423,54 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
16308
17423
|
if (this.config.debug) {
|
|
16309
17424
|
log.info("\u{1F50D} DEBUG: Detailed Contract Call Parameters", {});
|
|
16310
17425
|
log.info(" === LIT Action Response ===", {});
|
|
16311
|
-
log.info(
|
|
16312
|
-
` authorizedSpendsHash: ${validationResponse.authorizedSpendsHash}`,
|
|
16313
|
-
{}
|
|
16314
|
-
);
|
|
16315
|
-
log.info(` mintAmount: ${validationResponse.mintAmount}`, {});
|
|
16316
|
-
log.info(` mintFee: ${validationResponse.mintFee}`, {});
|
|
16317
|
-
log.info(` newDebt: ${validationResponse.newDebt}`, {});
|
|
16318
|
-
log.info(` newCollateral: ${validationResponse.newCollateral}`, {});
|
|
16319
|
-
log.info(` btcPrice: ${validationResponse.btcPrice}`, {});
|
|
16320
|
-
log.info(
|
|
16321
|
-
` timestamp (quantumTimestamp): ${validationResponse.timestamp}`,
|
|
16322
|
-
{}
|
|
16323
|
-
);
|
|
16324
|
-
log.info(" === SDK Calculated Hashes ===", {});
|
|
16325
|
-
log.info(` Current UCD Debt (from position): ${currentDebt}`, {});
|
|
16326
|
-
log.info(` ucdDebtHash: ${ucdDebtHash}`, {});
|
|
16327
|
-
log.info(" === Contract Addresses for contractHash ===", {});
|
|
16328
|
-
log.info(` PositionManager: ${positionManagerAddress}`, {});
|
|
16329
|
-
log.info(
|
|
16330
|
-
` LoanOpsManager: ${this.getContractAddressesOrThrow().loanOperationsManager}`,
|
|
16331
|
-
{}
|
|
16332
|
-
);
|
|
16333
|
-
log.info(
|
|
16334
|
-
` TermManager: ${this.getContractAddressesOrThrow().termManager}`,
|
|
16335
|
-
{}
|
|
16336
|
-
);
|
|
16337
|
-
log.info(
|
|
16338
|
-
` UCDController: ${this.getContractAddressesOrThrow().ucdController}`,
|
|
16339
|
-
{}
|
|
16340
|
-
);
|
|
16341
|
-
log.info(` contractHash: ${contractHash}`, {});
|
|
16342
|
-
log.info(" === Signature ===", {});
|
|
16343
|
-
log.info(
|
|
16344
|
-
` Signature (first 20 chars): ${finalSignature.substring(
|
|
16345
|
-
0,
|
|
16346
|
-
20
|
|
16347
|
-
)}...`,
|
|
16348
|
-
{}
|
|
16349
|
-
);
|
|
16350
|
-
log.info(` Signature length: ${finalSignature.length}`, {});
|
|
16351
|
-
log.info(
|
|
16352
|
-
` Final signature (first 50 chars): ${finalSignature ? finalSignature.substring(0, 50) : "EMPTY"}`,
|
|
16353
|
-
{}
|
|
16354
|
-
);
|
|
16355
|
-
log.info(
|
|
16356
|
-
` Final signature is empty: ${!finalSignature || finalSignature.length === 0}`,
|
|
16357
|
-
{}
|
|
16358
|
-
);
|
|
16359
|
-
}
|
|
16360
|
-
if (this.config.debug) {
|
|
16361
|
-
log.info("\u{1F50D} PREFLIGHT: Attempting diagnostic gas estimation...", {});
|
|
16362
|
-
}
|
|
16363
|
-
try {
|
|
16364
|
-
const iface = new import_ethers13.Interface(positionManagerAbi);
|
|
16365
|
-
const signatureHex = finalSignature.startsWith("0x") ? finalSignature : "0x" + finalSignature;
|
|
16366
|
-
if (this.config.debug) {
|
|
16367
|
-
log.info("\u{1F50D} DEBUG: Signature details:", {
|
|
16368
|
-
finalSignature: finalSignature ? `${finalSignature.substring(0, 20)}... (length: ${finalSignature.length})` : "UNDEFINED/EMPTY",
|
|
16369
|
-
signatureHex: signatureHex ? `${signatureHex.substring(0, 22)}... (length: ${signatureHex.length})` : "UNDEFINED/EMPTY"
|
|
16370
|
-
});
|
|
16371
|
-
}
|
|
16372
|
-
const calldata = iface.encodeFunctionData("mintUCD", [
|
|
16373
|
-
positionIdBytes32,
|
|
16374
|
-
validationResponse.mintAmount,
|
|
16375
|
-
validationResponse.mintFee,
|
|
16376
|
-
validationResponse.newDebt,
|
|
16377
|
-
validationResponse.newCollateral,
|
|
16378
|
-
validationResponse.btcPrice,
|
|
16379
|
-
validationResponse.authorizedSpendsHash,
|
|
16380
|
-
ucdDebtHash,
|
|
16381
|
-
contractHash,
|
|
16382
|
-
validationResponse.timestamp,
|
|
16383
|
-
signatureHex
|
|
16384
|
-
]);
|
|
16385
|
-
if (this.config.debug) {
|
|
16386
|
-
log.info("\u{1F50D} DEBUG: Encoded calldata:", {
|
|
16387
|
-
length: calldata.length,
|
|
16388
|
-
signaturePortion: calldata.substring(650, 670)
|
|
16389
|
-
});
|
|
16390
|
-
}
|
|
16391
|
-
const provider = this.getSignerOrThrow().provider;
|
|
16392
|
-
if (!provider) {
|
|
16393
|
-
throw new Error("Provider not available");
|
|
16394
|
-
}
|
|
16395
|
-
const signerAddress = await this.getSignerOrThrow().getAddress();
|
|
16396
|
-
if (this.config.debug) {
|
|
16397
|
-
log.info(" Executing diagnostic eth_call...", {});
|
|
16398
|
-
}
|
|
16399
|
-
await provider.call({
|
|
16400
|
-
to: positionManagerAddress,
|
|
16401
|
-
from: signerAddress,
|
|
16402
|
-
data: calldata
|
|
16403
|
-
});
|
|
16404
|
-
if (this.config.debug) {
|
|
16405
|
-
log.info(" \u2705 Diagnostic eth_call succeeded!", {});
|
|
16406
|
-
}
|
|
16407
|
-
} catch (preflightError) {
|
|
16408
|
-
if (this.config.debug) {
|
|
16409
|
-
log.error(
|
|
16410
|
-
"\u274C PREFLIGHT CHECK FAILED - Diagnostic eth_call reverted",
|
|
16411
|
-
{}
|
|
16412
|
-
);
|
|
16413
|
-
log.error(` Error message: ${preflightError?.message}`, {});
|
|
16414
|
-
log.error(` Error code: ${preflightError?.code}`, {});
|
|
16415
|
-
log.error(` Error data: ${preflightError?.data || "none"}`, {});
|
|
16416
|
-
log.error(` Error reason: ${preflightError?.reason || "none"}`, {});
|
|
16417
|
-
log.error(
|
|
16418
|
-
` Error.error: ${JSON.stringify(preflightError?.error || {})}`,
|
|
16419
|
-
{}
|
|
16420
|
-
);
|
|
16421
|
-
}
|
|
16422
|
-
let errorData = preflightError?.data || preflightError?.info?.error?.data || preflightError?.error?.data?.data || preflightError?.error?.data || preflightError?.error?.error?.data;
|
|
16423
|
-
const errorMessage = preflightError?.reason || preflightError?.error?.reason || preflightError?.error?.error?.reason || preflightError?.message || "Unknown error";
|
|
16424
|
-
let requireMessage;
|
|
16425
|
-
if (errorData && typeof errorData === "string" && errorData.startsWith("0x") && errorData.length > 138) {
|
|
16426
|
-
try {
|
|
16427
|
-
const errorSelector = errorData.slice(0, 10);
|
|
16428
|
-
if (errorSelector === "0x08c379a0") {
|
|
16429
|
-
const iface = new import_ethers13.Interface([
|
|
16430
|
-
"function Error(string)"
|
|
16431
|
-
]);
|
|
16432
|
-
const decoded = iface.decodeFunctionData("Error", errorData);
|
|
16433
|
-
requireMessage = decoded[0];
|
|
16434
|
-
}
|
|
16435
|
-
} catch (e) {
|
|
16436
|
-
}
|
|
16437
|
-
}
|
|
16438
|
-
const isDeadZonePreflightArtifact = decodeQuantumRevert(preflightError).selector === DEAD_ZONE_VIOLATION_SELECTOR;
|
|
16439
|
-
if (isDeadZonePreflightArtifact) {
|
|
16440
|
-
const nowSec = Math.floor(Date.now() / 1e3);
|
|
16441
|
-
const sigQuantum2 = Math.floor(Number(validationResponse.timestamp) / 60) * 60;
|
|
16442
|
-
const realCurrentQuantum = Math.floor(nowSec / 60) * 60;
|
|
16443
|
-
if (sigQuantum2 === realCurrentQuantum) {
|
|
16444
|
-
if (this.config.debug) {
|
|
16445
|
-
log.info(
|
|
16446
|
-
"\u21AA\uFE0F Preflight DeadZoneViolation is a stale-latest-block artifact (signature is the current quantum) \u2014 proceeding to broadcast",
|
|
16447
|
-
{}
|
|
16448
|
-
);
|
|
16449
|
-
}
|
|
16450
|
-
} else {
|
|
16451
|
-
if (this.config.debug) {
|
|
16452
|
-
log.info(
|
|
16453
|
-
"\u{1F501} Preflight DeadZoneViolation near the boundary \u2014 deferring across it, then proceeding",
|
|
16454
|
-
{}
|
|
16455
|
-
);
|
|
16456
|
-
}
|
|
16457
|
-
await awaitSafeSubmissionWindow(
|
|
16458
|
-
Number(validationResponse.timestamp)
|
|
16459
|
-
);
|
|
16460
|
-
}
|
|
16461
|
-
}
|
|
16462
|
-
if (requireMessage && !isDeadZonePreflightArtifact) {
|
|
16463
|
-
throw new Error(
|
|
16464
|
-
`Preflight check failed - mintUCD would revert:
|
|
16465
|
-
${requireMessage}
|
|
16466
|
-
Context: Quantum timestamp=${validationResponse.timestamp}, Position=${request.positionId}`
|
|
16467
|
-
);
|
|
16468
|
-
}
|
|
16469
|
-
if (errorData && typeof errorData === "string" && errorData.startsWith("0x") && errorData.length > 10) {
|
|
16470
|
-
const selector = errorData.slice(0, 10);
|
|
16471
|
-
const knownErrors = {
|
|
16472
|
-
"0x8baa579f": "InvalidSignature()",
|
|
16473
|
-
"0xd92e233d": "ValidationFailed()",
|
|
16474
|
-
"0x3ee5aeb5": "OperationNotAuthorized()",
|
|
16475
|
-
"0x70f4b32a": "UnauthorizedSigner()",
|
|
16476
|
-
"0x48f5c3ed": "Unauthorized()",
|
|
16477
|
-
"0x36bfdeea": "StateHashMismatch()",
|
|
16478
|
-
"0x131d9a21": "QuantumExpired()",
|
|
16479
|
-
"0x52ce5d58": "QuantumAlreadyUsed()",
|
|
16480
|
-
"0x137f3b70": "InDeadZone()",
|
|
16481
|
-
"0xbe4b82c1": "DeadZoneViolation()",
|
|
16482
|
-
"0x3e76a3c9": "QuantumOutsideWindow()",
|
|
16483
|
-
"0x62278171": "InvalidValidatorSignature()",
|
|
16484
|
-
"0xb9d419a7": "DebtUpdateVerificationFailed()",
|
|
16485
|
-
"0xc7e20553": "DebtUpdateVerificationFailedDetailed(bytes32,uint256,uint256,uint256,uint256,uint256)",
|
|
16486
|
-
"0x0cfd2a97": "MintVerificationMismatch(uint256,uint256)",
|
|
16487
|
-
"0x07637bd8": "MintFailed()"
|
|
16488
|
-
};
|
|
16489
|
-
const errorName = knownErrors[selector] || `Unknown error ${selector}`;
|
|
16490
|
-
const mintDebtDiagnostics = selector === "0xb9d419a7" || selector === "0xc7e20553" || selector === "0x0cfd2a97" || selector === "0x07637bd8" ? `
|
|
16491
|
-
|
|
16492
|
-
Mint/debt diagnostics (LoanOperationsManager mint path \u2192 increaseDebtFromMint):
|
|
16493
|
-
positionId: ${request.positionId}
|
|
16494
|
-
on-chain ucdDebt before tx: ${currentDebt}
|
|
16495
|
-
Lit newDebt: ${validationResponse.newDebt}
|
|
16496
|
-
Lit mintAmount: ${validationResponse.mintAmount}
|
|
16497
|
-
Lit mintFee: ${validationResponse.mintFee}
|
|
16498
|
-
Call path: mintUCD -> mintUCDWithAuthorization -> increaseDebtFromMint -> finalizeMintDebtIncrease
|
|
16499
|
-
Contract expects UCD supply increase to match mintAmount+mintFee and debt update to newDebt.
|
|
16500
|
-
` : "";
|
|
16501
|
-
let quantumContext = "";
|
|
16502
|
-
if (selector === "0x131d9a21" || selector === "0x52ce5d58" || selector === "0x137f3b70" || selector === "0xbe4b82c1" || selector === "0x3e76a3c9") {
|
|
16503
|
-
const currentTime2 = Math.floor(Date.now() / 1e3);
|
|
16504
|
-
const currentQuantum = Math.floor(currentTime2 / 60) * 60;
|
|
16505
|
-
const sigQuantum2 = Math.floor(validationResponse.timestamp / 60) * 60;
|
|
16506
|
-
const timeDiff = currentTime2 - validationResponse.timestamp;
|
|
16507
|
-
quantumContext = `
|
|
16508
|
-
|
|
16509
|
-
Quantum Timing Analysis:
|
|
16510
|
-
Signature created: ${validationResponse.timestamp} (${new Date(
|
|
16511
|
-
validationResponse.timestamp * 1e3
|
|
16512
|
-
).toISOString()})
|
|
16513
|
-
Current time: ${currentTime2} (${new Date(
|
|
16514
|
-
currentTime2 * 1e3
|
|
16515
|
-
).toISOString()})
|
|
16516
|
-
Time elapsed: ${timeDiff} seconds
|
|
16517
|
-
Signature quantum: ${sigQuantum2}
|
|
16518
|
-
Current quantum: ${currentQuantum}
|
|
16519
|
-
Quantum window: 60 seconds
|
|
16520
|
-
Status: ${sigQuantum2 < currentQuantum ? "\u274C EXPIRED - Signature created in older quantum window" : "\u2705 VALID"}
|
|
16521
|
-
Note: QuantumOutsideWindow can also occur if the tx is mined outside the accepted past/current/next quantum set.`;
|
|
16522
|
-
}
|
|
16523
|
-
if (!isDeadZonePreflightArtifact) {
|
|
16524
|
-
throw new Error(
|
|
16525
|
-
`Preflight check failed - mintUCD would revert:
|
|
16526
|
-
Error: ${errorName}
|
|
16527
|
-
Selector: ${selector}
|
|
16528
|
-
Message: ${errorMessage}
|
|
16529
|
-
Data: ${errorData}${quantumContext}${mintDebtDiagnostics}`
|
|
16530
|
-
);
|
|
16531
|
-
}
|
|
16532
|
-
} else {
|
|
16533
|
-
const currentTime2 = Math.floor(Date.now() / 1e3);
|
|
16534
|
-
const timeSinceSig = currentTime2 - validationResponse.timestamp;
|
|
16535
|
-
const currentQuantum = Math.floor(currentTime2 / 60) * 60;
|
|
16536
|
-
const sigQuantum2 = Math.floor(validationResponse.timestamp / 60) * 60;
|
|
16537
|
-
throw new Error(
|
|
16538
|
-
`Preflight check failed - mintUCD would revert:
|
|
16539
|
-
${errorMessage}
|
|
16540
|
-
|
|
16541
|
-
Error Details:
|
|
16542
|
-
Error data: ${errorData || "0x (empty - no custom error)"}
|
|
16543
|
-
Error code: ${preflightError?.code || "none"}
|
|
16544
|
-
|
|
16545
|
-
Quantum Timing Analysis:
|
|
16546
|
-
Signature created: ${validationResponse.timestamp} (${new Date(
|
|
16547
|
-
validationResponse.timestamp * 1e3
|
|
16548
|
-
).toISOString()})
|
|
16549
|
-
Current time: ${currentTime2} (${new Date(
|
|
16550
|
-
currentTime2 * 1e3
|
|
16551
|
-
).toISOString()})
|
|
16552
|
-
Time elapsed: ${timeSinceSig} seconds
|
|
16553
|
-
Signature quantum: ${sigQuantum2}
|
|
16554
|
-
Current quantum: ${currentQuantum}
|
|
16555
|
-
Status: ${sigQuantum2 < currentQuantum ? "\u274C EXPIRED - Signature too old" : "\u2705 VALID"}
|
|
16556
|
-
|
|
16557
|
-
Possible Causes (when error data is empty):
|
|
16558
|
-
\u2022 Quantum signature expired (current=${currentQuantum}, signature=${sigQuantum2})
|
|
16559
|
-
\u2022 Contract paused or circuit breaker active
|
|
16560
|
-
\u2022 Position state changed since LIT Action signed
|
|
16561
|
-
\u2022 Authorization registry validation failed
|
|
16562
|
-
\u2022 RPC provider not returning detailed revert data
|
|
16563
|
-
|
|
16564
|
-
Position: ${request.positionId}`
|
|
16565
|
-
);
|
|
16566
|
-
}
|
|
17426
|
+
log.info(
|
|
17427
|
+
` authorizedSpendsHash: ${validationResponse.authorizedSpendsHash}`,
|
|
17428
|
+
{}
|
|
17429
|
+
);
|
|
17430
|
+
log.info(` mintAmount: ${validationResponse.mintAmount}`, {});
|
|
17431
|
+
log.info(` mintFee: ${validationResponse.mintFee}`, {});
|
|
17432
|
+
log.info(` newDebt: ${validationResponse.newDebt}`, {});
|
|
17433
|
+
log.info(` newCollateral: ${validationResponse.newCollateral}`, {});
|
|
17434
|
+
log.info(` btcPrice: ${validationResponse.btcPrice}`, {});
|
|
17435
|
+
log.info(
|
|
17436
|
+
` timestamp (quantumTimestamp): ${validationResponse.timestamp}`,
|
|
17437
|
+
{}
|
|
17438
|
+
);
|
|
17439
|
+
log.info(" === SDK Calculated Hashes ===", {});
|
|
17440
|
+
log.info(` Current UCD Debt (from position): ${currentDebt}`, {});
|
|
17441
|
+
log.info(` ucdDebtHash: ${ucdDebtHash}`, {});
|
|
17442
|
+
log.info(" === Contract Addresses for contractHash ===", {});
|
|
17443
|
+
log.info(` PositionManager: ${positionManagerAddress}`, {});
|
|
17444
|
+
log.info(
|
|
17445
|
+
` LoanOpsManager: ${this.getContractAddressesOrThrow().loanOperationsManager}`,
|
|
17446
|
+
{}
|
|
17447
|
+
);
|
|
17448
|
+
log.info(
|
|
17449
|
+
` TermManager: ${this.getContractAddressesOrThrow().termManager}`,
|
|
17450
|
+
{}
|
|
17451
|
+
);
|
|
17452
|
+
log.info(
|
|
17453
|
+
` UCDController: ${this.getContractAddressesOrThrow().ucdController}`,
|
|
17454
|
+
{}
|
|
17455
|
+
);
|
|
17456
|
+
log.info(` contractHash: ${contractHash}`, {});
|
|
17457
|
+
log.info(" === Signature ===", {});
|
|
17458
|
+
log.info(
|
|
17459
|
+
` Signature (first 20 chars): ${finalSignature.substring(
|
|
17460
|
+
0,
|
|
17461
|
+
20
|
|
17462
|
+
)}...`,
|
|
17463
|
+
{}
|
|
17464
|
+
);
|
|
17465
|
+
log.info(` Signature length: ${finalSignature.length}`, {});
|
|
17466
|
+
log.info(
|
|
17467
|
+
` Final signature (first 50 chars): ${finalSignature ? finalSignature.substring(0, 50) : "EMPTY"}`,
|
|
17468
|
+
{}
|
|
17469
|
+
);
|
|
17470
|
+
log.info(
|
|
17471
|
+
` Final signature is empty: ${!finalSignature || finalSignature.length === 0}`,
|
|
17472
|
+
{}
|
|
17473
|
+
);
|
|
16567
17474
|
}
|
|
16568
17475
|
try {
|
|
16569
17476
|
const coreAddress = this.getContractAddressesOrThrow().positionManagerCore;
|
|
@@ -16573,7 +17480,7 @@ Position: ${request.positionId}`
|
|
|
16573
17480
|
const coreAbi = [
|
|
16574
17481
|
"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))"
|
|
16575
17482
|
];
|
|
16576
|
-
const positionCore = new
|
|
17483
|
+
const positionCore = new import_ethers14.Contract(
|
|
16577
17484
|
coreAddress,
|
|
16578
17485
|
coreAbi,
|
|
16579
17486
|
this.getSignerOrThrow().provider
|
|
@@ -16581,8 +17488,8 @@ Position: ${request.positionId}`
|
|
|
16581
17488
|
const currentPosition = await positionCore["getPositionDetails"](
|
|
16582
17489
|
positionIdBytes32
|
|
16583
17490
|
);
|
|
16584
|
-
const currentDebtHash = (0,
|
|
16585
|
-
|
|
17491
|
+
const currentDebtHash = (0, import_ethers14.keccak256)(
|
|
17492
|
+
import_ethers14.AbiCoder.defaultAbiCoder().encode(
|
|
16586
17493
|
["uint256"],
|
|
16587
17494
|
[currentPosition.ucdDebt]
|
|
16588
17495
|
)
|
|
@@ -16629,7 +17536,7 @@ Position: ${request.positionId}`
|
|
|
16629
17536
|
"function getCurrentQuantum() view returns (uint256)",
|
|
16630
17537
|
"function isQuantumValid(bytes32,uint256) view returns (bool)"
|
|
16631
17538
|
];
|
|
16632
|
-
const registry = new
|
|
17539
|
+
const registry = new import_ethers14.Contract(
|
|
16633
17540
|
registryAddress,
|
|
16634
17541
|
registryAbi,
|
|
16635
17542
|
this.getSignerOrThrow().provider
|
|
@@ -16745,7 +17652,7 @@ Position: ${request.positionId}`
|
|
|
16745
17652
|
});
|
|
16746
17653
|
}
|
|
16747
17654
|
const signatureHexMint = finalSignature.startsWith("0x") ? finalSignature : "0x" + finalSignature;
|
|
16748
|
-
const mintIface = new
|
|
17655
|
+
const mintIface = new import_ethers14.Interface(positionManagerAbi);
|
|
16749
17656
|
const mintCalldata = mintIface.encodeFunctionData("mintUCD", [
|
|
16750
17657
|
positionIdBytes32,
|
|
16751
17658
|
validationResponse.mintAmount,
|
|
@@ -16760,146 +17667,121 @@ Position: ${request.positionId}`
|
|
|
16760
17667
|
signatureHexMint
|
|
16761
17668
|
]);
|
|
16762
17669
|
const fromAddress = await signer.getAddress();
|
|
16763
|
-
|
|
16764
|
-
|
|
16765
|
-
|
|
16766
|
-
positionManagerAddress,
|
|
16767
|
-
mintCalldata,
|
|
16768
|
-
MINT_UCD_ESTIMATE_MARGIN_BPS,
|
|
16769
|
-
MINT_UCD_GAS_CEILING
|
|
16770
|
-
);
|
|
16771
|
-
let tx;
|
|
16772
|
-
if (estimatedGas !== null) {
|
|
16773
|
-
if (this.config.debug) {
|
|
16774
|
-
log.info(
|
|
16775
|
-
`\u{1F4DD} Broadcasting mintUCD (gasLimit=${estimatedGas.toString()})`,
|
|
16776
|
-
{}
|
|
16777
|
-
);
|
|
16778
|
-
}
|
|
16779
|
-
await awaitSafeSubmissionWindow(Number(validationResponse.timestamp));
|
|
16780
|
-
tx = await sendEip1559Transaction({
|
|
16781
|
-
signer,
|
|
17670
|
+
try {
|
|
17671
|
+
await assertSafeQuantumSubmission({
|
|
17672
|
+
provider: signerProvider,
|
|
16782
17673
|
to: positionManagerAddress,
|
|
17674
|
+
from: fromAddress,
|
|
16783
17675
|
data: mintCalldata,
|
|
16784
|
-
|
|
17676
|
+
quantumTimestamp: Number(validationResponse.timestamp),
|
|
17677
|
+
onDebug: this.config.debug ? (msg) => log.info(msg, {}) : void 0
|
|
16785
17678
|
});
|
|
16786
|
-
}
|
|
16787
|
-
if (
|
|
16788
|
-
|
|
16789
|
-
|
|
16790
|
-
|
|
16791
|
-
|
|
16792
|
-
|
|
16793
|
-
|
|
16794
|
-
|
|
16795
|
-
|
|
16796
|
-
|
|
16797
|
-
|
|
16798
|
-
|
|
16799
|
-
|
|
16800
|
-
|
|
16801
|
-
|
|
16802
|
-
|
|
16803
|
-
|
|
16804
|
-
|
|
16805
|
-
|
|
16806
|
-
|
|
16807
|
-
|
|
16808
|
-
|
|
16809
|
-
|
|
16810
|
-
|
|
16811
|
-
|
|
16812
|
-
|
|
16813
|
-
|
|
16814
|
-
|
|
16815
|
-
|
|
16816
|
-
|
|
16817
|
-
|
|
16818
|
-
|
|
16819
|
-
|
|
16820
|
-
|
|
16821
|
-
|
|
16822
|
-
"0x70f4b32a": "UnauthorizedSigner()",
|
|
16823
|
-
"0x48f5c3ed": "Unauthorized()",
|
|
16824
|
-
"0x36bfdeea": "StateHashMismatch()",
|
|
16825
|
-
"0x131d9a21": "QuantumExpired()",
|
|
16826
|
-
"0x52ce5d58": "QuantumAlreadyUsed()",
|
|
16827
|
-
"0x137f3b70": "InDeadZone()",
|
|
16828
|
-
"0xbe4b82c1": "DeadZoneViolation()",
|
|
16829
|
-
"0x3e76a3c9": "QuantumOutsideWindow()",
|
|
16830
|
-
"0x62278171": "InvalidValidatorSignature()",
|
|
16831
|
-
"0xb9d419a7": "DebtUpdateVerificationFailed()",
|
|
16832
|
-
"0xc7e20553": "DebtUpdateVerificationFailedDetailed(bytes32,uint256,uint256,uint256,uint256,uint256)",
|
|
16833
|
-
"0x0cfd2a97": "MintVerificationMismatch(uint256,uint256)",
|
|
16834
|
-
"0x07637bd8": "MintFailed()"
|
|
16835
|
-
};
|
|
16836
|
-
const errorName = knownErrors[selector] || `Unknown error ${selector}`;
|
|
16837
|
-
throw new Error(
|
|
16838
|
-
`mintUCD reverted: ${errorName}
|
|
16839
|
-
Error selector: ${selector}
|
|
16840
|
-
Error data: ${errorData}
|
|
16841
|
-
Call path: mintUCD -> mintUCDWithAuthorization -> increaseDebtFromMint -> finalizeMintDebtIncrease
|
|
16842
|
-
Message: ${errorMessage}`
|
|
16843
|
-
);
|
|
16844
|
-
}
|
|
16845
|
-
if (errorData === "0x" || !errorData) {
|
|
16846
|
-
const viewsAddress = this.config.contractAddresses?.positionManagerViews;
|
|
16847
|
-
if (viewsAddress) {
|
|
16848
|
-
try {
|
|
16849
|
-
const views = new import_ethers13.Contract(
|
|
16850
|
-
viewsAddress,
|
|
16851
|
-
[
|
|
16852
|
-
"function diagnoseMintUCD(bytes32,uint256,uint256,uint256,uint256,uint256,bytes32,bytes32,bytes32,uint256,bytes) view returns (string)"
|
|
16853
|
-
],
|
|
16854
|
-
signerProvider
|
|
16855
|
-
);
|
|
16856
|
-
const diagnosis = await views["diagnoseMintUCD"](
|
|
16857
|
-
request.positionId,
|
|
16858
|
-
validationResponse.mintAmount,
|
|
16859
|
-
validationResponse.mintFee,
|
|
16860
|
-
validationResponse.newDebt,
|
|
16861
|
-
validationResponse.newCollateral,
|
|
16862
|
-
validationResponse.btcPrice,
|
|
16863
|
-
validationResponse.authorizedSpendsHash,
|
|
16864
|
-
ucdDebtHash,
|
|
16865
|
-
contractHash,
|
|
16866
|
-
validationResponse.timestamp,
|
|
16867
|
-
signatureHexMint
|
|
16868
|
-
);
|
|
16869
|
-
throw new Error(
|
|
16870
|
-
`mintUCD reverted with empty data; diagnosis: ${diagnosis}`
|
|
17679
|
+
} catch (simError) {
|
|
17680
|
+
if (!(simError instanceof QuantumRevertError)) {
|
|
17681
|
+
throw simError;
|
|
17682
|
+
}
|
|
17683
|
+
const simCause = simError.cause;
|
|
17684
|
+
const causeMessage = simCause?.reason ?? simCause?.shortMessage ?? (simCause instanceof Error ? simCause.message : String(simCause));
|
|
17685
|
+
if (!simError.data) {
|
|
17686
|
+
const viewsAddress = this.config.contractAddresses?.positionManagerViews;
|
|
17687
|
+
if (viewsAddress) {
|
|
17688
|
+
let diagnosis = null;
|
|
17689
|
+
try {
|
|
17690
|
+
const views = new import_ethers14.Contract(
|
|
17691
|
+
viewsAddress,
|
|
17692
|
+
[
|
|
17693
|
+
"function diagnoseMintUCD(bytes32,uint256,uint256,uint256,uint256,uint256,bytes32,bytes32,bytes32,uint256,bytes) view returns (string)"
|
|
17694
|
+
],
|
|
17695
|
+
signerProvider
|
|
17696
|
+
);
|
|
17697
|
+
diagnosis = await views["diagnoseMintUCD"](
|
|
17698
|
+
request.positionId,
|
|
17699
|
+
validationResponse.mintAmount,
|
|
17700
|
+
validationResponse.mintFee,
|
|
17701
|
+
validationResponse.newDebt,
|
|
17702
|
+
validationResponse.newCollateral,
|
|
17703
|
+
validationResponse.btcPrice,
|
|
17704
|
+
validationResponse.authorizedSpendsHash,
|
|
17705
|
+
ucdDebtHash,
|
|
17706
|
+
contractHash,
|
|
17707
|
+
validationResponse.timestamp,
|
|
17708
|
+
signatureHexMint
|
|
17709
|
+
);
|
|
17710
|
+
} catch (diagErr) {
|
|
17711
|
+
if (this.config.debug) {
|
|
17712
|
+
log.info(
|
|
17713
|
+
` diagnoseMintUCD unavailable or failed: ${diagErr?.message || diagErr}`,
|
|
17714
|
+
{}
|
|
16871
17715
|
);
|
|
16872
|
-
} catch (diagErr) {
|
|
16873
|
-
if (this.config.debug) {
|
|
16874
|
-
log.info(
|
|
16875
|
-
` diagnoseMintUCD unavailable or failed: ${diagErr?.message || diagErr}`,
|
|
16876
|
-
{}
|
|
16877
|
-
);
|
|
16878
|
-
}
|
|
16879
17716
|
}
|
|
16880
17717
|
}
|
|
17718
|
+
if (diagnosis !== null) {
|
|
17719
|
+
throw new Error(
|
|
17720
|
+
`mintUCD reverted with empty data; diagnosis: ${diagnosis}`
|
|
17721
|
+
);
|
|
17722
|
+
}
|
|
16881
17723
|
}
|
|
16882
|
-
|
|
16883
|
-
|
|
16884
|
-
|
|
16885
|
-
Error data: ${errorData || "none"}`
|
|
16886
|
-
);
|
|
16887
|
-
}
|
|
16888
|
-
}
|
|
16889
|
-
if (this.config.debug) {
|
|
16890
|
-
log.info(
|
|
16891
|
-
"\u2705 Static call clear \u2014 broadcasting mintUCD with fixed gas ceiling",
|
|
16892
|
-
{}
|
|
17724
|
+
throw new Error(
|
|
17725
|
+
`mintUCD would revert (pre-send simulation): ${causeMessage}
|
|
17726
|
+
Error data: none`
|
|
16893
17727
|
);
|
|
16894
17728
|
}
|
|
16895
|
-
|
|
16896
|
-
|
|
16897
|
-
|
|
16898
|
-
|
|
16899
|
-
|
|
16900
|
-
|
|
16901
|
-
|
|
17729
|
+
const selector = simError.selector;
|
|
17730
|
+
const errorName = simError.errorName ?? (selector ? `Unknown error ${selector}` : "unknown");
|
|
17731
|
+
const mintDebtDiagnostics = selector === "0xb9d419a7" || // DebtUpdateVerificationFailed()
|
|
17732
|
+
selector === "0xc7e20553" || // DebtUpdateVerificationFailedDetailed(...)
|
|
17733
|
+
selector === "0x0cfd2a97" || // MintVerificationMismatch(uint256,uint256)
|
|
17734
|
+
selector === "0x07637bd8" ? `
|
|
17735
|
+
|
|
17736
|
+
Mint/debt diagnostics (LoanOperationsManager mint path \u2192 increaseDebtFromMint):
|
|
17737
|
+
positionId: ${request.positionId}
|
|
17738
|
+
on-chain ucdDebt before tx: ${currentDebt}
|
|
17739
|
+
Lit newDebt: ${validationResponse.newDebt}
|
|
17740
|
+
Lit mintAmount: ${validationResponse.mintAmount}
|
|
17741
|
+
Lit mintFee: ${validationResponse.mintFee}
|
|
17742
|
+
Call path: mintUCD -> mintUCDWithAuthorization -> increaseDebtFromMint -> finalizeMintDebtIncrease
|
|
17743
|
+
Contract expects UCD supply increase to match mintAmount+mintFee and debt update to newDebt.
|
|
17744
|
+
` : "";
|
|
17745
|
+
let quantumContext = "";
|
|
17746
|
+
if (selector === "0x131d9a21" || // QuantumExpired()
|
|
17747
|
+
selector === "0x52ce5d58" || // QuantumAlreadyUsed()
|
|
17748
|
+
selector === "0x137f3b70" || // InDeadZone()
|
|
17749
|
+
selector === "0xbe4b82c1" || // DeadZoneViolation()
|
|
17750
|
+
selector === "0x3e76a3c9") {
|
|
17751
|
+
const nowSecs = Math.floor(Date.now() / 1e3);
|
|
17752
|
+
const nowQuantum = Math.floor(nowSecs / 60) * 60;
|
|
17753
|
+
const signedQuantum = Math.floor(Number(validationResponse.timestamp) / 60) * 60;
|
|
17754
|
+
const timeDiff = nowSecs - Number(validationResponse.timestamp);
|
|
17755
|
+
quantumContext = `
|
|
17756
|
+
|
|
17757
|
+
Quantum Timing Analysis:
|
|
17758
|
+
Signature created: ${validationResponse.timestamp} (${new Date(
|
|
17759
|
+
Number(validationResponse.timestamp) * 1e3
|
|
17760
|
+
).toISOString()})
|
|
17761
|
+
Current time: ${nowSecs} (${new Date(
|
|
17762
|
+
nowSecs * 1e3
|
|
17763
|
+
).toISOString()})
|
|
17764
|
+
Time elapsed: ${timeDiff} seconds
|
|
17765
|
+
Signature quantum: ${signedQuantum}
|
|
17766
|
+
Current quantum: ${nowQuantum}
|
|
17767
|
+
Quantum window: 60 seconds
|
|
17768
|
+
Status: ${signedQuantum < nowQuantum ? "\u274C EXPIRED - Signature created in older quantum window" : "\u2705 VALID"}
|
|
17769
|
+
Note: QuantumOutsideWindow can also occur if the tx is mined outside the accepted past/current/next quantum set.`;
|
|
17770
|
+
}
|
|
17771
|
+
throw new Error(
|
|
17772
|
+
`mintUCD would revert (pre-send simulation): ${errorName}
|
|
17773
|
+
Error selector: ${selector}
|
|
17774
|
+
Error data: ${simError.data}
|
|
17775
|
+
Call path: mintUCD -> mintUCDWithAuthorization -> increaseDebtFromMint -> finalizeMintDebtIncrease
|
|
17776
|
+
Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
17777
|
+
);
|
|
16902
17778
|
}
|
|
17779
|
+
const tx = await sendEip1559Transaction({
|
|
17780
|
+
signer,
|
|
17781
|
+
to: positionManagerAddress,
|
|
17782
|
+
data: mintCalldata,
|
|
17783
|
+
gasLimit: MINT_UCD_GAS_CEILING
|
|
17784
|
+
});
|
|
16903
17785
|
const receipt = await signerProvider.waitForTransaction(
|
|
16904
17786
|
tx.hash,
|
|
16905
17787
|
1,
|
|
@@ -16915,7 +17797,7 @@ Error data: ${errorData || "none"}`
|
|
|
16915
17797
|
if (receipt.logs && receipt.logs.length > 0) {
|
|
16916
17798
|
for (const receiptLog of receipt.logs) {
|
|
16917
17799
|
if (receiptLog.topics && receiptLog.topics[0] === "0x08c379a0") {
|
|
16918
|
-
const iface = new
|
|
17800
|
+
const iface = new import_ethers14.Interface([
|
|
16919
17801
|
"error Error(string)"
|
|
16920
17802
|
]);
|
|
16921
17803
|
const decoded = iface.decodeErrorResult("Error", receiptLog.data);
|
|
@@ -16932,7 +17814,7 @@ Error data: ${errorData || "none"}`
|
|
|
16932
17814
|
const minimalAbi = [
|
|
16933
17815
|
"event UCDMintedWithAuthorization(bytes32 indexed positionId, address indexed borrower, uint256 mintAmount, uint256 mintFee, uint256 newDebt, uint256 newCollateral, uint256 btcPrice, uint256 quantumTimestamp, bytes32 authorizedSpendsHash)"
|
|
16934
17816
|
];
|
|
16935
|
-
const iface = new
|
|
17817
|
+
const iface = new import_ethers14.Interface(minimalAbi);
|
|
16936
17818
|
for (const receiptLog of receipt.logs) {
|
|
16937
17819
|
const parsed = (() => {
|
|
16938
17820
|
const logIface = iface;
|
|
@@ -17119,7 +18001,7 @@ Error data: ${errorData || "none"}`
|
|
|
17119
18001
|
pkpNftAddress,
|
|
17120
18002
|
this.getChipotlePublicKeyFallback()
|
|
17121
18003
|
);
|
|
17122
|
-
const pkpEthAddress = (0,
|
|
18004
|
+
const pkpEthAddress = (0, import_ethers14.computeAddress)(pkpPublicKey);
|
|
17123
18005
|
pkpData = {
|
|
17124
18006
|
publicKey: pkpPublicKey,
|
|
17125
18007
|
ethAddress: pkpEthAddress,
|
|
@@ -17521,7 +18403,7 @@ Error data: ${errorData || "none"}`
|
|
|
17521
18403
|
}
|
|
17522
18404
|
const loanOpsForUcd = loanOpsForUcdResult.value;
|
|
17523
18405
|
const ucdTokenAddress = await loanOpsForUcd.ucdToken();
|
|
17524
|
-
const ucdToken = new
|
|
18406
|
+
const ucdToken = new import_ethers14.Contract(
|
|
17525
18407
|
ucdTokenAddress,
|
|
17526
18408
|
[
|
|
17527
18409
|
"function balanceOf(address) view returns (uint256)",
|
|
@@ -17537,9 +18419,9 @@ Error data: ${errorData || "none"}`
|
|
|
17537
18419
|
if (ucdBalance < debtWei) {
|
|
17538
18420
|
return {
|
|
17539
18421
|
success: false,
|
|
17540
|
-
error: `Insufficient UCD balance for liquidation: ${(0,
|
|
18422
|
+
error: `Insufficient UCD balance for liquidation: ${(0, import_ethers14.formatEther)(
|
|
17541
18423
|
ucdBalance
|
|
17542
|
-
)} UCD (need ${(0,
|
|
18424
|
+
)} UCD (need ${(0, import_ethers14.formatEther)(debtWei)} UCD)`,
|
|
17543
18425
|
positionId: request.positionId,
|
|
17544
18426
|
wasLiquidated: false
|
|
17545
18427
|
};
|
|
@@ -17548,7 +18430,7 @@ Error data: ${errorData || "none"}`
|
|
|
17548
18430
|
if (request.skipUcdApproval) {
|
|
17549
18431
|
return {
|
|
17550
18432
|
success: false,
|
|
17551
|
-
error: `Insufficient UCD allowance for UCDController: need at least ${(0,
|
|
18433
|
+
error: `Insufficient UCD allowance for UCDController: need at least ${(0, import_ethers14.formatEther)(
|
|
17552
18434
|
debtWei
|
|
17553
18435
|
)} UCD, or omit skipUcdApproval to let the SDK submit approve`,
|
|
17554
18436
|
positionId: request.positionId,
|
|
@@ -17985,9 +18867,12 @@ Error data: ${errorData || "none"}`
|
|
|
17985
18867
|
userSignature: auth.signature,
|
|
17986
18868
|
rpcUrl: _rpcUrl || this.config.ethRpcUrl,
|
|
17987
18869
|
// Forward a Bitcoin provider so the validator reaches a reachable esplora on Hardhat
|
|
17988
|
-
// (lit-ops maps it to the
|
|
18870
|
+
// Hardhat-ONLY: gives the validator a reachable esplora (lit-ops maps it to the
|
|
18871
|
+
// validator's devBitcoinProviderUrl). Never emitted on prod chains, where the provider
|
|
18872
|
+
// is resolved from the on-chain BitcoinProviderRegistry and this field is server-ignored
|
|
18873
|
+
// — emitting it there only misleads readers into thinking it is caller-controlled. Prefer
|
|
17989
18874
|
// a per-request override (mirrors the mint path's customBitcoinRpcUrl) over the config default.
|
|
17990
|
-
...(_bitcoinRpcUrl || this.config.bitcoinProviders?.[0]?.url) && {
|
|
18875
|
+
...this.config.chain === "hardhat" && (_bitcoinRpcUrl || this.config.bitcoinProviders?.[0]?.url) && {
|
|
17991
18876
|
bitcoinProviderUrl: _bitcoinRpcUrl || this.config.bitcoinProviders[0].url
|
|
17992
18877
|
},
|
|
17993
18878
|
...this.config.priceProviders && {
|
|
@@ -18052,7 +18937,7 @@ Error data: ${errorData || "none"}`
|
|
|
18052
18937
|
if (typeof signatureBytes === "string" && signatureBytes.startsWith("{")) {
|
|
18053
18938
|
try {
|
|
18054
18939
|
const parsed = JSON.parse(signatureBytes);
|
|
18055
|
-
signatureBytes = parsed.signature ?? (parsed.r && parsed.s && parsed.v ?
|
|
18940
|
+
signatureBytes = parsed.signature ?? (parsed.r && parsed.s && parsed.v ? import_ethers14.Signature.from({
|
|
18056
18941
|
r: parsed.r,
|
|
18057
18942
|
s: parsed.s,
|
|
18058
18943
|
v: parsed.v
|
|
@@ -18063,26 +18948,43 @@ Error data: ${errorData || "none"}`
|
|
|
18063
18948
|
if (!signatureBytes.startsWith("0x")) {
|
|
18064
18949
|
signatureBytes = "0x" + signatureBytes;
|
|
18065
18950
|
}
|
|
18066
|
-
const
|
|
18067
|
-
|
|
18068
|
-
|
|
18069
|
-
|
|
18070
|
-
`Failed to get PositionManager: ${positionManagerResult.error.message}`
|
|
18071
|
-
);
|
|
18072
|
-
}
|
|
18073
|
-
const positionManager = positionManagerResult.value;
|
|
18074
|
-
await awaitSafeSubmissionWindow(Number(quantumTimestamp));
|
|
18075
|
-
const extendPositionFn = positionManager["extendPosition"];
|
|
18076
|
-
const tx = await extendPositionFn(
|
|
18951
|
+
const extendIface = new import_ethers14.Interface([
|
|
18952
|
+
"function extendPosition(bytes32 positionId, uint256 selectedTerm, uint256 quantumTimestamp, uint256 btcPrice, uint256 availableBTCBalance, uint256 proRataRenewalFee, bytes calldata extensionValidatorSignature) external returns (bool)"
|
|
18953
|
+
]);
|
|
18954
|
+
const extendCalldata = extendIface.encodeFunctionData("extendPosition", [
|
|
18077
18955
|
positionIdBytes32,
|
|
18078
18956
|
BigInt(_selectedTerm),
|
|
18079
18957
|
quantumTimestamp,
|
|
18080
18958
|
btcPrice,
|
|
18081
18959
|
availableBTCBalance,
|
|
18082
18960
|
proRataRenewalFee,
|
|
18083
|
-
signatureBytes
|
|
18084
|
-
|
|
18085
|
-
);
|
|
18961
|
+
signatureBytes
|
|
18962
|
+
]);
|
|
18963
|
+
const extendTo = this.getContractAddressesOrThrow().positionManager;
|
|
18964
|
+
const extendSigner = this.getSignerOrThrow();
|
|
18965
|
+
try {
|
|
18966
|
+
await assertSafeQuantumSubmission({
|
|
18967
|
+
provider: this.getProviderOrThrow(),
|
|
18968
|
+
to: extendTo,
|
|
18969
|
+
from: await extendSigner.getAddress(),
|
|
18970
|
+
data: extendCalldata,
|
|
18971
|
+
quantumTimestamp: Number(quantumTimestamp),
|
|
18972
|
+
onDebug: this.config.debug ? (msg) => log.info(msg, {}) : void 0
|
|
18973
|
+
});
|
|
18974
|
+
} catch (simError) {
|
|
18975
|
+
if (!(simError instanceof QuantumRevertError)) {
|
|
18976
|
+
throw simError;
|
|
18977
|
+
}
|
|
18978
|
+
throw new Error(
|
|
18979
|
+
`extendPosition would revert (pre-send simulation): ${simError.errorName ?? simError.selector ?? "unknown"}`
|
|
18980
|
+
);
|
|
18981
|
+
}
|
|
18982
|
+
const tx = await sendEip1559Transaction({
|
|
18983
|
+
signer: extendSigner,
|
|
18984
|
+
to: extendTo,
|
|
18985
|
+
data: extendCalldata,
|
|
18986
|
+
gasLimit: EXTEND_POSITION_GAS_CEILING
|
|
18987
|
+
});
|
|
18086
18988
|
const txWithProvider = tx;
|
|
18087
18989
|
if (!txWithProvider.provider) {
|
|
18088
18990
|
const signer2 = this.getSignerOrThrow();
|
|
@@ -18595,7 +19497,7 @@ Error data: ${errorData || "none"}`
|
|
|
18595
19497
|
` Converting payment amount: ${request.paymentAmount} (type: ${typeof request.paymentAmount})`
|
|
18596
19498
|
);
|
|
18597
19499
|
}
|
|
18598
|
-
const paymentAmountWei = (0,
|
|
19500
|
+
const paymentAmountWei = (0, import_ethers14.parseEther)(
|
|
18599
19501
|
request.paymentAmount.toString()
|
|
18600
19502
|
);
|
|
18601
19503
|
if (this.config.debug) {
|
|
@@ -18657,7 +19559,7 @@ Error data: ${errorData || "none"}`
|
|
|
18657
19559
|
};
|
|
18658
19560
|
}
|
|
18659
19561
|
const ucdTokenAddress = await loanOps.ucdToken();
|
|
18660
|
-
const ucdToken = new
|
|
19562
|
+
const ucdToken = new import_ethers14.Contract(
|
|
18661
19563
|
ucdTokenAddress,
|
|
18662
19564
|
[
|
|
18663
19565
|
"function balanceOf(address) view returns (uint256)",
|
|
@@ -18672,16 +19574,16 @@ Error data: ${errorData || "none"}`
|
|
|
18672
19574
|
ucdToken["allowance"](signerAddress, positionManagerAddress)
|
|
18673
19575
|
]);
|
|
18674
19576
|
if (this.config.debug) {
|
|
18675
|
-
log.info(` UCD Balance: ${(0,
|
|
19577
|
+
log.info(` UCD Balance: ${(0, import_ethers14.formatEther)(balance)} UCD`);
|
|
18676
19578
|
log.info(
|
|
18677
|
-
` UCD Allowance: ${(0,
|
|
19579
|
+
` UCD Allowance: ${(0, import_ethers14.formatEther)(allowance)} UCD`
|
|
18678
19580
|
);
|
|
18679
19581
|
log.info(` Required: ${request.paymentAmount} UCD`);
|
|
18680
19582
|
}
|
|
18681
19583
|
if (balance < paymentAmountWei) {
|
|
18682
19584
|
return {
|
|
18683
19585
|
success: false,
|
|
18684
|
-
error: `Insufficient UCD balance: ${(0,
|
|
19586
|
+
error: `Insufficient UCD balance: ${(0, import_ethers14.formatEther)(
|
|
18685
19587
|
balance
|
|
18686
19588
|
)} UCD (need ${request.paymentAmount} UCD)`
|
|
18687
19589
|
};
|
|
@@ -18751,9 +19653,10 @@ Error data: ${errorData || "none"}`
|
|
|
18751
19653
|
rpcUrl: this.config.ethRpcUrl,
|
|
18752
19654
|
// Pass the SDK's RPC URL to LIT Action
|
|
18753
19655
|
...request.rpcUrl && { rpcUrl: request.rpcUrl },
|
|
18754
|
-
//
|
|
18755
|
-
//
|
|
18756
|
-
|
|
19656
|
+
// Hardhat-ONLY: gives the validator a reachable esplora (lit-ops maps it to the
|
|
19657
|
+
// validator's devBitcoinProviderUrl). Never emitted on prod chains, where the provider
|
|
19658
|
+
// comes from the on-chain BitcoinProviderRegistry and this field is server-ignored.
|
|
19659
|
+
...this.config.chain === "hardhat" && this.config.bitcoinProviders?.[0]?.url && {
|
|
18757
19660
|
bitcoinProviderUrl: this.config.bitcoinProviders[0].url
|
|
18758
19661
|
},
|
|
18759
19662
|
...this.config.priceProviders && {
|
|
@@ -18942,7 +19845,6 @@ Error data: ${errorData || "none"}`
|
|
|
18942
19845
|
quantumTimestamp: quantumTimestamp.toString()
|
|
18943
19846
|
});
|
|
18944
19847
|
}
|
|
18945
|
-
await awaitSafeSubmissionWindow(Number(quantumTimestamp));
|
|
18946
19848
|
const paymentAmountStr = paymentAmountWei.toString();
|
|
18947
19849
|
const quantumTimestampStr = quantumTimestamp.toString();
|
|
18948
19850
|
const btcPriceStr = btcPrice.toString();
|
|
@@ -18950,7 +19852,7 @@ Error data: ${errorData || "none"}`
|
|
|
18950
19852
|
const paymentSigner = this.getSignerOrThrow();
|
|
18951
19853
|
const paymentFrom = await paymentSigner.getAddress();
|
|
18952
19854
|
const paymentProvider = contractManager.getProvider();
|
|
18953
|
-
const paymentIface = new
|
|
19855
|
+
const paymentIface = new import_ethers14.Interface([
|
|
18954
19856
|
"function makePayment(bytes32 positionId, uint256 paymentAmount, uint256 quantumTimestamp, uint256 btcPrice, bytes calldata paymentValidatorSignature) external returns (bool)"
|
|
18955
19857
|
]);
|
|
18956
19858
|
const paymentCalldata = paymentIface.encodeFunctionData("makePayment", [
|
|
@@ -18960,58 +19862,30 @@ Error data: ${errorData || "none"}`
|
|
|
18960
19862
|
btcPriceStr,
|
|
18961
19863
|
signature
|
|
18962
19864
|
]);
|
|
18963
|
-
|
|
18964
|
-
|
|
18965
|
-
|
|
18966
|
-
|
|
18967
|
-
|
|
18968
|
-
|
|
18969
|
-
|
|
18970
|
-
|
|
18971
|
-
|
|
18972
|
-
|
|
18973
|
-
|
|
18974
|
-
|
|
18975
|
-
|
|
18976
|
-
|
|
18977
|
-
|
|
18978
|
-
|
|
18979
|
-
|
|
18980
|
-
if (decoded === "DeadZoneViolation()" && sim <= MAX_DEADZONE_RESIMULATIONS) {
|
|
18981
|
-
const nowSec = Math.floor(Date.now() / 1e3);
|
|
18982
|
-
const sigQuantum = Math.floor(Number(quantumTimestamp) / 60) * 60;
|
|
18983
|
-
const realCurrentQuantum = Math.floor(nowSec / 60) * 60;
|
|
18984
|
-
if (sigQuantum === realCurrentQuantum) {
|
|
18985
|
-
if (this.config.debug) {
|
|
18986
|
-
log.info(
|
|
18987
|
-
"\u21AA\uFE0F Simulated DeadZoneViolation is a stale-latest-block artifact (signature is the current quantum) \u2014 proceeding to broadcast",
|
|
18988
|
-
{}
|
|
18989
|
-
);
|
|
18990
|
-
}
|
|
18991
|
-
break;
|
|
18992
|
-
}
|
|
18993
|
-
const gate = await awaitSafeSubmissionWindow(Number(quantumTimestamp));
|
|
18994
|
-
if (gate.waited) {
|
|
18995
|
-
if (this.config.debug) {
|
|
18996
|
-
log.info(
|
|
18997
|
-
`\u{1F501} Re-simulating makePayment after crossing the quantum boundary (attempt ${sim}/${MAX_DEADZONE_RESIMULATIONS})`,
|
|
18998
|
-
{}
|
|
18999
|
-
);
|
|
19000
|
-
}
|
|
19001
|
-
continue;
|
|
19002
|
-
}
|
|
19003
|
-
}
|
|
19004
|
-
const label = decoded ?? simError?.reason ?? simError?.shortMessage ?? (simError instanceof Error ? simError.message : String(simError));
|
|
19005
|
-
if (this.config.debug) {
|
|
19006
|
-
log.error("\u274C makePayment pre-send simulation reverted", { error: label });
|
|
19007
|
-
}
|
|
19008
|
-
return {
|
|
19009
|
-
success: false,
|
|
19010
|
-
error: `Payment would revert (pre-send simulation): ${label}`,
|
|
19011
|
-
positionId: request.positionId,
|
|
19012
|
-
paymentAmountUCD: request.paymentAmount
|
|
19013
|
-
};
|
|
19865
|
+
try {
|
|
19866
|
+
await assertSafeQuantumSubmission({
|
|
19867
|
+
provider: paymentProvider,
|
|
19868
|
+
to: paymentContractAddress,
|
|
19869
|
+
from: paymentFrom,
|
|
19870
|
+
data: paymentCalldata,
|
|
19871
|
+
quantumTimestamp: Number(quantumTimestamp),
|
|
19872
|
+
onDebug: this.config.debug ? (msg) => log.info(msg, {}) : void 0
|
|
19873
|
+
});
|
|
19874
|
+
} catch (simError) {
|
|
19875
|
+
if (!(simError instanceof QuantumRevertError)) {
|
|
19876
|
+
throw simError;
|
|
19877
|
+
}
|
|
19878
|
+
const simCause = simError.cause;
|
|
19879
|
+
const label = simError.errorName ?? (simError.selector ? `Unknown error ${simError.selector}` : null) ?? simCause?.reason ?? simCause?.shortMessage ?? (simCause instanceof Error ? simCause.message : String(simCause));
|
|
19880
|
+
if (this.config.debug) {
|
|
19881
|
+
log.error("\u274C makePayment pre-send simulation reverted", { error: label });
|
|
19014
19882
|
}
|
|
19883
|
+
return {
|
|
19884
|
+
success: false,
|
|
19885
|
+
error: `Payment would revert (pre-send simulation): ${label}`,
|
|
19886
|
+
positionId: request.positionId,
|
|
19887
|
+
paymentAmountUCD: request.paymentAmount
|
|
19888
|
+
};
|
|
19015
19889
|
}
|
|
19016
19890
|
const tx = await sendEip1559Transaction({
|
|
19017
19891
|
signer: paymentSigner,
|
|
@@ -19294,7 +20168,7 @@ Error data: ${errorData || "none"}`
|
|
|
19294
20168
|
const pkpId = position.pkpId;
|
|
19295
20169
|
if (litValidatorAddr && pkpId) {
|
|
19296
20170
|
try {
|
|
19297
|
-
const litValidator = new
|
|
20171
|
+
const litValidator = new import_ethers14.Contract(
|
|
19298
20172
|
litValidatorAddr,
|
|
19299
20173
|
["function pkpOwners(bytes32) view returns (address)"],
|
|
19300
20174
|
this.getProviderOrThrow()
|
|
@@ -19302,7 +20176,7 @@ Error data: ${errorData || "none"}`
|
|
|
19302
20176
|
const owner = await litValidator.pkpOwners(
|
|
19303
20177
|
pkpId
|
|
19304
20178
|
);
|
|
19305
|
-
if (owner && owner !==
|
|
20179
|
+
if (owner && owner !== import_ethers14.ZeroAddress) {
|
|
19306
20180
|
controller = owner;
|
|
19307
20181
|
}
|
|
19308
20182
|
} catch {
|
|
@@ -19354,7 +20228,7 @@ Error data: ${errorData || "none"}`
|
|
|
19354
20228
|
pkpNftAddress,
|
|
19355
20229
|
this.getChipotlePublicKeyFallback()
|
|
19356
20230
|
);
|
|
19357
|
-
pkpEthAddress = (0,
|
|
20231
|
+
pkpEthAddress = (0, import_ethers14.computeAddress)(pkpPublicKey);
|
|
19358
20232
|
pkpCache.set(positionId, {
|
|
19359
20233
|
publicKey: pkpPublicKey,
|
|
19360
20234
|
ethAddress: pkpEthAddress,
|
|
@@ -19685,13 +20559,6 @@ Error data: ${errorData || "none"}`
|
|
|
19685
20559
|
error: "PositionManager address not configured"
|
|
19686
20560
|
};
|
|
19687
20561
|
}
|
|
19688
|
-
const positionManagerContract = new import_ethers13.Contract(
|
|
19689
|
-
positionManagerAddress,
|
|
19690
|
-
[
|
|
19691
|
-
"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)"
|
|
19692
|
-
],
|
|
19693
|
-
this.getSignerOrThrow()
|
|
19694
|
-
);
|
|
19695
20562
|
const withdrawalParams = {
|
|
19696
20563
|
positionId: positionIdBytes32,
|
|
19697
20564
|
actionHash: validationResponse.actionHash,
|
|
@@ -19724,86 +20591,56 @@ Error data: ${errorData || "none"}`
|
|
|
19724
20591
|
utxoVout: withdrawalParams.utxoVout
|
|
19725
20592
|
});
|
|
19726
20593
|
}
|
|
19727
|
-
|
|
20594
|
+
const withdrawIface = new import_ethers14.Interface([
|
|
20595
|
+
"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)"
|
|
20596
|
+
]);
|
|
20597
|
+
const withdrawCalldata = withdrawIface.encodeFunctionData("withdrawBTC", [
|
|
20598
|
+
withdrawalParams,
|
|
20599
|
+
signatureBytes,
|
|
20600
|
+
btcSpendAuthBytes
|
|
20601
|
+
]);
|
|
20602
|
+
const withdrawSigner = this.getSignerOrThrow();
|
|
19728
20603
|
let tx;
|
|
19729
20604
|
try {
|
|
19730
|
-
|
|
19731
|
-
|
|
19732
|
-
|
|
19733
|
-
|
|
19734
|
-
|
|
19735
|
-
|
|
20605
|
+
await assertSafeQuantumSubmission({
|
|
20606
|
+
provider: this.getProviderOrThrow(),
|
|
20607
|
+
to: positionManagerAddress,
|
|
20608
|
+
from: await withdrawSigner.getAddress(),
|
|
20609
|
+
data: withdrawCalldata,
|
|
20610
|
+
quantumTimestamp: Number(withdrawalParams.quantumTimestamp),
|
|
20611
|
+
onDebug: this.config.debug ? (msg) => log.info(msg, {}) : void 0
|
|
20612
|
+
});
|
|
20613
|
+
tx = await sendEip1559Transaction({
|
|
20614
|
+
signer: withdrawSigner,
|
|
20615
|
+
to: positionManagerAddress,
|
|
20616
|
+
data: withdrawCalldata,
|
|
20617
|
+
gasLimit: WITHDRAW_BTC_GAS_CEILING
|
|
20618
|
+
});
|
|
19736
20619
|
} catch (txError) {
|
|
19737
|
-
const txErrorMsg = txError instanceof Error ? txError.message : String(txError);
|
|
20620
|
+
const txErrorMsg = txError instanceof QuantumRevertError ? `Withdrawal would revert (pre-send simulation): ${txError.errorName ?? txError.selector ?? "unknown"}` : txError instanceof Error ? txError.message : String(txError);
|
|
19738
20621
|
if (this.config.debug) {
|
|
19739
|
-
log.
|
|
19740
|
-
|
|
19741
|
-
|
|
19742
|
-
|
|
20622
|
+
log.error("\u274C withdrawBTC transaction submission failed", {
|
|
20623
|
+
error: txErrorMsg,
|
|
20624
|
+
withdrawalParams,
|
|
20625
|
+
signatureBytesLength: signatureBytes.length
|
|
20626
|
+
});
|
|
19743
20627
|
}
|
|
19744
20628
|
try {
|
|
19745
|
-
const
|
|
19746
|
-
const
|
|
19747
|
-
|
|
19748
|
-
|
|
19749
|
-
|
|
19750
|
-
|
|
19751
|
-
|
|
19752
|
-
btcSpendAuthBytes
|
|
19753
|
-
]);
|
|
19754
|
-
const signer = this.getSignerOrThrow();
|
|
19755
|
-
const fromAddress = await signer.getAddress();
|
|
19756
|
-
const estimatedGas = await estimateContractCallGasWithMargin(
|
|
19757
|
-
this.getProviderOrThrow(),
|
|
19758
|
-
fromAddress,
|
|
19759
|
-
contractAddress,
|
|
19760
|
-
calldata,
|
|
19761
|
-
MINT_UCD_ESTIMATE_MARGIN_BPS,
|
|
19762
|
-
WITHDRAW_BTC_GAS_CEILING
|
|
19763
|
-
);
|
|
19764
|
-
const gasLimit = estimatedGas ?? WITHDRAW_BTC_GAS_CEILING;
|
|
19765
|
-
await awaitSafeSubmissionWindow(
|
|
19766
|
-
Number(withdrawalParams.quantumTimestamp)
|
|
20629
|
+
const position = await this.getPosition(positionId);
|
|
20630
|
+
const btcAmount = (Number(withdrawalAmount) / 1e8).toFixed(8);
|
|
20631
|
+
const message = formatBTCWithdrawalFailureMessage(
|
|
20632
|
+
btcAmount,
|
|
20633
|
+
position?.vaultAddress || "unknown",
|
|
20634
|
+
withdrawalAddress,
|
|
20635
|
+
positionId
|
|
19767
20636
|
);
|
|
19768
|
-
|
|
19769
|
-
|
|
19770
|
-
data: calldata,
|
|
19771
|
-
value: "0x0",
|
|
19772
|
-
gasLimit
|
|
19773
|
-
});
|
|
19774
|
-
if (this.config.debug) {
|
|
19775
|
-
log.info("\u2705 withdrawBTC raw transaction fallback succeeded", {
|
|
19776
|
-
txHash: tx.hash,
|
|
19777
|
-
gasLimit: gasLimit.toString(),
|
|
19778
|
-
estimated: estimatedGas !== null
|
|
19779
|
-
});
|
|
19780
|
-
}
|
|
19781
|
-
} catch (rawError) {
|
|
19782
|
-
const rawErrorMsg = rawError instanceof Error ? rawError.message : String(rawError);
|
|
19783
|
-
if (this.config.debug) {
|
|
19784
|
-
log.error("\u274C withdrawBTC transaction failed (raw fallback also failed)", {
|
|
19785
|
-
error: rawErrorMsg,
|
|
19786
|
-
withdrawalParams,
|
|
19787
|
-
signatureBytesLength: signatureBytes.length
|
|
19788
|
-
});
|
|
19789
|
-
}
|
|
19790
|
-
try {
|
|
19791
|
-
const position = await this.getPosition(positionId);
|
|
19792
|
-
const btcAmount = (Number(withdrawalAmount) / 1e8).toFixed(8);
|
|
19793
|
-
const message = formatBTCWithdrawalFailureMessage(
|
|
19794
|
-
btcAmount,
|
|
19795
|
-
position?.vaultAddress || "unknown",
|
|
19796
|
-
withdrawalAddress,
|
|
19797
|
-
positionId
|
|
19798
|
-
);
|
|
19799
|
-
await this._relayNotification(message);
|
|
19800
|
-
} catch {
|
|
19801
|
-
}
|
|
19802
|
-
return {
|
|
19803
|
-
success: false,
|
|
19804
|
-
error: `Transaction submission failed: ${rawErrorMsg}`
|
|
19805
|
-
};
|
|
20637
|
+
await this._relayNotification(message);
|
|
20638
|
+
} catch {
|
|
19806
20639
|
}
|
|
20640
|
+
return {
|
|
20641
|
+
success: false,
|
|
20642
|
+
error: `Transaction submission failed: ${txErrorMsg}`
|
|
20643
|
+
};
|
|
19807
20644
|
}
|
|
19808
20645
|
if (this.config.debug) {
|
|
19809
20646
|
log.info("\u2705 Transaction submitted, waiting for confirmation:", {
|
|
@@ -19826,6 +20663,12 @@ Error data: ${errorData || "none"}`
|
|
|
19826
20663
|
error: `Transaction confirmation failed: ${waitErrorMsg}`
|
|
19827
20664
|
};
|
|
19828
20665
|
}
|
|
20666
|
+
if (!receipt) {
|
|
20667
|
+
return {
|
|
20668
|
+
success: false,
|
|
20669
|
+
error: `Transaction confirmation returned no receipt. Hash: ${tx.hash}`
|
|
20670
|
+
};
|
|
20671
|
+
}
|
|
19829
20672
|
if (this.config.debug) {
|
|
19830
20673
|
log.info("\u2705 BTC withdrawal confirmed", {
|
|
19831
20674
|
blockNumber: receipt.blockNumber,
|
|
@@ -19941,7 +20784,8 @@ Error data: ${errorData || "none"}`
|
|
|
19941
20784
|
targetAmount: request.targetAmount,
|
|
19942
20785
|
networkFee: request.networkFee,
|
|
19943
20786
|
chainId,
|
|
19944
|
-
signer
|
|
20787
|
+
signer,
|
|
20788
|
+
...request.utxos ? { utxos: request.utxos } : {}
|
|
19945
20789
|
});
|
|
19946
20790
|
const headers = {
|
|
19947
20791
|
"Content-Type": "application/json",
|
|
@@ -19972,7 +20816,10 @@ Error data: ${errorData || "none"}`
|
|
|
19972
20816
|
userSignature: envelope.userSignature,
|
|
19973
20817
|
borrowerAddress: envelope.borrowerAddress,
|
|
19974
20818
|
chainId: envelope.chainId,
|
|
19975
|
-
timestamp: envelope.timestamp
|
|
20819
|
+
timestamp: envelope.timestamp,
|
|
20820
|
+
// P6/#8: the exact input set hashed into the user signature.
|
|
20821
|
+
// The server must forward this set verbatim to the signer.
|
|
20822
|
+
utxos: envelope.utxos
|
|
19976
20823
|
})
|
|
19977
20824
|
}
|
|
19978
20825
|
);
|
|
@@ -20093,7 +20940,7 @@ Error data: ${errorData || "none"}`
|
|
|
20093
20940
|
const btcSpendAuthorizerAbi = [
|
|
20094
20941
|
"function getAuthorizedSpends(bytes32) view returns (tuple(string txid, uint32 vout, uint256 satoshis, string targetAddress, uint256 targetAmount, uint256 authorizedAt)[])"
|
|
20095
20942
|
];
|
|
20096
|
-
const btcSpendAuthorizer = new
|
|
20943
|
+
const btcSpendAuthorizer = new import_ethers14.Contract(
|
|
20097
20944
|
btcSpendAuthorizerAddress,
|
|
20098
20945
|
btcSpendAuthorizerAbi,
|
|
20099
20946
|
this.getProviderOrThrow()
|
|
@@ -20179,7 +21026,7 @@ Error data: ${errorData || "none"}`
|
|
|
20179
21026
|
const abi = [
|
|
20180
21027
|
"function getAuthorizedSpends(bytes32) view returns (tuple(string txid, uint32 vout, uint256 satoshis, string targetAddress, uint256 targetAmount, uint256 authorizedAt)[])"
|
|
20181
21028
|
];
|
|
20182
|
-
const contract = new
|
|
21029
|
+
const contract = new import_ethers14.Contract(
|
|
20183
21030
|
btcSpendAuthorizerAddress,
|
|
20184
21031
|
abi,
|
|
20185
21032
|
this.getProviderOrThrow()
|
|
@@ -20193,7 +21040,7 @@ Error data: ${errorData || "none"}`
|
|
|
20193
21040
|
targetAddress: spend.targetAddress,
|
|
20194
21041
|
targetAmount: Number(spend.targetAmount),
|
|
20195
21042
|
authorizedAt: Number(spend.authorizedAt),
|
|
20196
|
-
utxoKey: (0,
|
|
21043
|
+
utxoKey: (0, import_ethers14.solidityPackedKeccak256)(
|
|
20197
21044
|
["string", "uint32"],
|
|
20198
21045
|
[spend.txid, Number(spend.vout)]
|
|
20199
21046
|
)
|
|
@@ -20312,10 +21159,13 @@ Error data: ${errorData || "none"}`
|
|
|
20312
21159
|
}
|
|
20313
21160
|
},
|
|
20314
21161
|
rpcUrl,
|
|
20315
|
-
|
|
21162
|
+
// Hardhat-ONLY: never emitted on prod chains (server-ignored there; provider comes
|
|
21163
|
+
// from the on-chain BitcoinProviderRegistry). The stale-spend action reads
|
|
21164
|
+
// `devBitcoinProviderUrl` directly on Hardhat — this legacy alias is redundant but
|
|
21165
|
+
// kept behind the Hardhat gate so no prod request carries the caller-looking field.
|
|
21166
|
+
...this.config.chain === "hardhat" && sdkBitcoinProviderUrl && { bitcoinProviderUrl: sdkBitcoinProviderUrl },
|
|
20316
21167
|
// Hardhat (chainId 1337/31337) has no on-chain BitcoinProviderRegistry —
|
|
20317
|
-
// Lit Actions read `devBitcoinProviderUrl` from globalThis.
|
|
20318
|
-
// non-hardhat chains ignore it. (Single source of truth: bitcoinProviderUrl.)
|
|
21168
|
+
// Lit Actions read `devBitcoinProviderUrl` from globalThis. Non-hardhat chains ignore it.
|
|
20319
21169
|
...sdkBitcoinProviderUrl && { devBitcoinProviderUrl: sdkBitcoinProviderUrl },
|
|
20320
21170
|
...params.minConfirmations !== void 0 && {
|
|
20321
21171
|
minConfirmations: params.minConfirmations
|
|
@@ -20329,7 +21179,7 @@ Error data: ${errorData || "none"}`
|
|
|
20329
21179
|
error: attRes.error ?? attRes.reason ?? `LIT recovery attestation rejected${attRes.failedStep ? ` at step ${attRes.failedStep}` : ""}`
|
|
20330
21180
|
};
|
|
20331
21181
|
}
|
|
20332
|
-
const btcSpendAuthorizer = new
|
|
21182
|
+
const btcSpendAuthorizer = new import_ethers14.Contract(
|
|
20333
21183
|
btcSpendAuthorizerAddress,
|
|
20334
21184
|
[
|
|
20335
21185
|
"function cancelStaleSpendWithProof(bytes32 positionId, bytes32 utxoKey, uint256 authorizedAt, string calldata invalidatorTxid, uint256 attestationTimestamp, bytes calldata litSignature) external"
|
|
@@ -20638,6 +21488,15 @@ Error data: ${errorData || "none"}`
|
|
|
20638
21488
|
async getLoanEvents(positionId, filter) {
|
|
20639
21489
|
return this.loanQuery.getLoanEvents(positionId, filter);
|
|
20640
21490
|
}
|
|
21491
|
+
/**
|
|
21492
|
+
* Get loan-event history for every position owned by a wallet.
|
|
21493
|
+
* Composes getUserPositions + getLoanEvents; returns one LoanEvents per
|
|
21494
|
+
* position (each carrying its own positionId). Bitcoin deposits are not on the
|
|
21495
|
+
* subgraph — merge those in from an Esplora source on the consumer side.
|
|
21496
|
+
*/
|
|
21497
|
+
async getWalletLoanEvents(userAddress, filter) {
|
|
21498
|
+
return this.loanQuery.getWalletLoanEvents(userAddress, filter);
|
|
21499
|
+
}
|
|
20641
21500
|
/**
|
|
20642
21501
|
* Get protocol-wide events across all indexed entity types.
|
|
20643
21502
|
* Returns a merged, sorted discriminated-union timeline.
|
|
@@ -20724,10 +21583,10 @@ Error data: ${errorData || "none"}`
|
|
|
20724
21583
|
"function allowance(address owner, address spender) view returns (uint256)",
|
|
20725
21584
|
"function approve(address spender, uint256 amount) returns (bool)"
|
|
20726
21585
|
];
|
|
20727
|
-
const stablecoin = new
|
|
21586
|
+
const stablecoin = new import_ethers14.Contract(params.stablecoinAddress, erc20Abi, params.signer);
|
|
20728
21587
|
const allowance = await stablecoin.allowance(signerAddress, psmAddress);
|
|
20729
21588
|
if (allowance < params.amountWei) {
|
|
20730
|
-
const approveTx = await stablecoin.approve(psmAddress,
|
|
21589
|
+
const approveTx = await stablecoin.approve(psmAddress, import_ethers14.MaxUint256);
|
|
20731
21590
|
await approveTx.wait();
|
|
20732
21591
|
}
|
|
20733
21592
|
const psm = SimplePSMV2__factory.connect(psmAddress, params.signer);
|
|
@@ -20762,10 +21621,10 @@ Error data: ${errorData || "none"}`
|
|
|
20762
21621
|
"function allowance(address owner, address spender) view returns (uint256)",
|
|
20763
21622
|
"function approve(address spender, uint256 amount) returns (bool)"
|
|
20764
21623
|
];
|
|
20765
|
-
const ucdToken = new
|
|
21624
|
+
const ucdToken = new import_ethers14.Contract(ucdAddress, erc20Abi, params.signer);
|
|
20766
21625
|
const allowance = await ucdToken.allowance(signerAddress, ucdControllerAddress);
|
|
20767
21626
|
if (allowance < params.ucdAmountWei) {
|
|
20768
|
-
const approveTx = await ucdToken.approve(ucdControllerAddress,
|
|
21627
|
+
const approveTx = await ucdToken.approve(ucdControllerAddress, import_ethers14.MaxUint256);
|
|
20769
21628
|
await approveTx.wait();
|
|
20770
21629
|
}
|
|
20771
21630
|
const psm = SimplePSMV2__factory.connect(psmAddress, params.signer);
|
|
@@ -20977,7 +21836,7 @@ Error data: ${errorData || "none"}`
|
|
|
20977
21836
|
const maxUcdWhole = BigInt(result[3].toString());
|
|
20978
21837
|
return success({
|
|
20979
21838
|
liquidationThreshold: result[0].toString(),
|
|
20980
|
-
minimumLoanValueUcd: (0,
|
|
21839
|
+
minimumLoanValueUcd: (0, import_ethers14.formatUnits)(result[2], 18),
|
|
20981
21840
|
minimumLoanValueWei: result[2].toString(),
|
|
20982
21841
|
maxSingleLoanValueUcd: maxUcdWhole.toString(),
|
|
20983
21842
|
maxSingleLoanValueWei: (maxUcdWhole * 10n ** 18n).toString()
|
|
@@ -21014,7 +21873,7 @@ Error data: ${errorData || "none"}`
|
|
|
21014
21873
|
const balanceWei = await provider.getBalance(address);
|
|
21015
21874
|
return success({
|
|
21016
21875
|
balanceWei: balanceWei.toString(),
|
|
21017
|
-
balanceEth: (0,
|
|
21876
|
+
balanceEth: (0, import_ethers14.formatEther)(balanceWei)
|
|
21018
21877
|
});
|
|
21019
21878
|
} catch (error) {
|
|
21020
21879
|
return failure(new SDKError({
|
|
@@ -21094,6 +21953,38 @@ Error data: ${errorData || "none"}`
|
|
|
21094
21953
|
}));
|
|
21095
21954
|
}
|
|
21096
21955
|
}
|
|
21956
|
+
/**
|
|
21957
|
+
* List a vault address's PENDING (not-yet-confirmed) incoming BTC deposits.
|
|
21958
|
+
*
|
|
21959
|
+
* A deposit is "pending" until it reaches the protocol's minimum confirmation
|
|
21960
|
+
* depth (6) — the confirmed balance endpoints (`getVaultBalance` /
|
|
21961
|
+
* `getAddressBalance`) omit anything shallower, so this surfaces in-flight
|
|
21962
|
+
* deposits (0–5 confirmations). Server-delegated: lit-ops-server queries its
|
|
21963
|
+
* configured premium Esplora provider (`/api/lit/pending-deposits`). Requires
|
|
21964
|
+
* service mode.
|
|
21965
|
+
*/
|
|
21966
|
+
async getPendingDeposits(vaultAddress) {
|
|
21967
|
+
this.ensureInitialized();
|
|
21968
|
+
if (!isServiceModeConfig(this.config)) {
|
|
21969
|
+
throw new Error("getPendingDeposits requires service mode \u2014 standalone mode is not supported");
|
|
21970
|
+
}
|
|
21971
|
+
const trimmedAddress = vaultAddress?.trim();
|
|
21972
|
+
if (!trimmedAddress) {
|
|
21973
|
+
throw new Error("vaultAddress is required");
|
|
21974
|
+
}
|
|
21975
|
+
const serviceEndpoint = this.config.serviceEndpoint;
|
|
21976
|
+
const url = `${serviceEndpoint}/api/lit/pending-deposits?vaultAddress=${encodeURIComponent(trimmedAddress)}`;
|
|
21977
|
+
const response = await fetch(url, {
|
|
21978
|
+
headers: await this.getAuthHeader()
|
|
21979
|
+
});
|
|
21980
|
+
if (!response.ok) {
|
|
21981
|
+
throw new Error(`Pending deposits request failed: ${response.status} ${response.statusText}`);
|
|
21982
|
+
}
|
|
21983
|
+
const json = await response.json();
|
|
21984
|
+
if (!json.success)
|
|
21985
|
+
throw new Error(json.error ?? "Pending deposits request failed");
|
|
21986
|
+
return json.data ?? [];
|
|
21987
|
+
}
|
|
21097
21988
|
// ============================================================================
|
|
21098
21989
|
// HIGH-LEVEL API - Mock Token Operations (Test Networks Only)
|
|
21099
21990
|
// ============================================================================
|
|
@@ -21353,7 +22244,7 @@ Error data: ${errorData || "none"}`
|
|
|
21353
22244
|
* Convert decimal position ID to bytes32 format
|
|
21354
22245
|
*/
|
|
21355
22246
|
toBytes32(value) {
|
|
21356
|
-
return (0,
|
|
22247
|
+
return (0, import_ethers14.zeroPadValue)((0, import_ethers14.toBeHex)(BigInt(value)), 32);
|
|
21357
22248
|
}
|
|
21358
22249
|
/**
|
|
21359
22250
|
* Check if an error indicates a technical failure vs business logic rejection
|
|
@@ -21394,7 +22285,7 @@ Error data: ${errorData || "none"}`
|
|
|
21394
22285
|
const coreModuleAbi = [
|
|
21395
22286
|
"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))"
|
|
21396
22287
|
];
|
|
21397
|
-
const coreModule = new
|
|
22288
|
+
const coreModule = new import_ethers14.Contract(
|
|
21398
22289
|
coreModuleAddress,
|
|
21399
22290
|
coreModuleAbi,
|
|
21400
22291
|
provider
|
|
@@ -21461,7 +22352,7 @@ Error data: ${errorData || "none"}`
|
|
|
21461
22352
|
const corePositionAbi = [
|
|
21462
22353
|
"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))"
|
|
21463
22354
|
];
|
|
21464
|
-
const corePositionContract = new
|
|
22355
|
+
const corePositionContract = new import_ethers14.Contract(
|
|
21465
22356
|
coreAddress,
|
|
21466
22357
|
corePositionAbi,
|
|
21467
22358
|
provider
|
|
@@ -21472,7 +22363,7 @@ Error data: ${errorData || "none"}`
|
|
|
21472
22363
|
positionIdBytes32
|
|
21473
22364
|
);
|
|
21474
22365
|
const positionIdFromCore = corePosition?.positionId || corePosition?.[0] || null;
|
|
21475
|
-
if (positionIdFromCore && positionIdFromCore !==
|
|
22366
|
+
if (positionIdFromCore && positionIdFromCore !== import_ethers14.ZeroHash && positionIdFromCore !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
|
|
21476
22367
|
if (this.config.debug) {
|
|
21477
22368
|
log.info("Position exists in core contract", {
|
|
21478
22369
|
positionId,
|
|
@@ -21540,7 +22431,7 @@ Error data: ${errorData || "none"}`
|
|
|
21540
22431
|
throw new Error("loanOperationsManager address not configured");
|
|
21541
22432
|
}
|
|
21542
22433
|
const runner = this.config.contractSigner || this.getProviderOrThrow();
|
|
21543
|
-
const contract = new
|
|
22434
|
+
const contract = new import_ethers14.Contract(
|
|
21544
22435
|
addr.loanOperationsManager,
|
|
21545
22436
|
[
|
|
21546
22437
|
"function getProtocolConfig() external view returns (uint256 liquidationThreshold, uint256 minimumLoanValueUcd, uint256 minimumLoanValueWei, uint256 maxSingleLoanValueUcd)"
|
|
@@ -21553,7 +22444,7 @@ Error data: ${errorData || "none"}`
|
|
|
21553
22444
|
const maxUcdWhole = BigInt(result[3].toString());
|
|
21554
22445
|
return {
|
|
21555
22446
|
liquidationThreshold: result[0].toString(),
|
|
21556
|
-
minimumLoanValueUcd: (0,
|
|
22447
|
+
minimumLoanValueUcd: (0, import_ethers14.formatUnits)(result[2], 18),
|
|
21557
22448
|
minimumLoanValueWei: result[2].toString(),
|
|
21558
22449
|
maxSingleLoanValueUcd: maxUcdWhole.toString(),
|
|
21559
22450
|
maxSingleLoanValueWei: (maxUcdWhole * 10n ** 18n).toString()
|
|
@@ -21571,8 +22462,8 @@ Error data: ${errorData || "none"}`
|
|
|
21571
22462
|
const MIN_REVEAL_DELAY = 60;
|
|
21572
22463
|
const MAX_RANDOM_DELAY = 240;
|
|
21573
22464
|
const entropyInput = positionId + quantumTimestamp.toString();
|
|
21574
|
-
const hash = (0,
|
|
21575
|
-
(0,
|
|
22465
|
+
const hash = (0, import_ethers14.keccak256)(
|
|
22466
|
+
(0, import_ethers14.toUtf8Bytes)(entropyInput)
|
|
21576
22467
|
);
|
|
21577
22468
|
const randomValue = BigInt(hash);
|
|
21578
22469
|
const randomDelay = Number(randomValue % BigInt(MAX_RANDOM_DELAY));
|
|
@@ -21589,6 +22480,20 @@ Error data: ${errorData || "none"}`
|
|
|
21589
22480
|
if (this.config.debug) {
|
|
21590
22481
|
log.info(`\u{1F310} Calling liquidation server endpoint: ${endpoint}`);
|
|
21591
22482
|
}
|
|
22483
|
+
const signer = this.getSignerOrThrow();
|
|
22484
|
+
const intentSigner = await signer.getAddress();
|
|
22485
|
+
const chainId = Number(
|
|
22486
|
+
(await this.getProviderOrThrow().getNetwork()).chainId
|
|
22487
|
+
);
|
|
22488
|
+
const rawPositionId = /^0x/i.test(params.positionId.trim()) ? params.positionId.trim().slice(2) : params.positionId.trim();
|
|
22489
|
+
const canonicalPositionId = "0x" + rawPositionId.padStart(64, "0").toLowerCase();
|
|
22490
|
+
const intentTimestamp = Math.floor(Date.now() / 1e3);
|
|
22491
|
+
const intentActionHash = (0, import_ethers14.keccak256)((0, import_ethers14.toUtf8Bytes)("liquidate-position"));
|
|
22492
|
+
const intentHash = (0, import_ethers14.solidityPackedKeccak256)(
|
|
22493
|
+
["bytes32", "uint256", "uint256", "address", "bytes32"],
|
|
22494
|
+
[canonicalPositionId, intentTimestamp, chainId, intentSigner, intentActionHash]
|
|
22495
|
+
);
|
|
22496
|
+
const intentSignature = await signer.signMessage((0, import_ethers14.getBytes)(intentHash));
|
|
21592
22497
|
const response = await fetch(endpoint, {
|
|
21593
22498
|
method: "POST",
|
|
21594
22499
|
headers: { "Content-Type": "application/json" },
|
|
@@ -21604,6 +22509,11 @@ Error data: ${errorData || "none"}`
|
|
|
21604
22509
|
UCDController: params.contractAddresses.ucdController,
|
|
21605
22510
|
LiquidationManagerModule: params.contractAddresses.liquidationManager
|
|
21606
22511
|
},
|
|
22512
|
+
liquidatorIntent: {
|
|
22513
|
+
timestamp: intentTimestamp,
|
|
22514
|
+
chainId,
|
|
22515
|
+
signature: intentSignature
|
|
22516
|
+
},
|
|
21607
22517
|
rpcUrl: params.rpcUrl || this.config.ethRpcUrl,
|
|
21608
22518
|
priceProviders: this.config.priceProviders,
|
|
21609
22519
|
pkpTokenId: params.pkpData.tokenId,
|
|
@@ -21635,7 +22545,7 @@ Error data: ${errorData || "none"}`
|
|
|
21635
22545
|
const vrfSeedRaw = await lmResult.value["vrfSeeds"](positionId);
|
|
21636
22546
|
const vrfSeed = BigInt(vrfSeedRaw.toString());
|
|
21637
22547
|
if (vrfSeed !== 0n) {
|
|
21638
|
-
const finalEntropy = (0,
|
|
22548
|
+
const finalEntropy = (0, import_ethers14.solidityPackedKeccak256)(
|
|
21639
22549
|
["uint256", "uint256", "bytes32"],
|
|
21640
22550
|
[vrfSeed, BigInt(quantumTimestamp.toString()), positionId]
|
|
21641
22551
|
);
|
|
@@ -21913,14 +22823,14 @@ var EventHelpers = {
|
|
|
21913
22823
|
};
|
|
21914
22824
|
|
|
21915
22825
|
// src/utils/position-delegate.utils.ts
|
|
21916
|
-
var
|
|
22826
|
+
var import_ethers15 = require("ethers");
|
|
21917
22827
|
var POSITION_DELEGATE_REGISTRY_ABI = [
|
|
21918
22828
|
"function positionDelegate(bytes32 positionId) view returns (address)",
|
|
21919
22829
|
"function setPositionDelegate(bytes32 positionId, address newDelegate) external",
|
|
21920
22830
|
"event PositionDelegateUpdated(bytes32 indexed positionId, address indexed oldDelegate, address indexed newDelegate)"
|
|
21921
22831
|
];
|
|
21922
22832
|
async function setPositionDelegate(positionId, delegate, signer, registryAddress) {
|
|
21923
|
-
const registry = new
|
|
22833
|
+
const registry = new import_ethers15.Contract(
|
|
21924
22834
|
registryAddress,
|
|
21925
22835
|
POSITION_DELEGATE_REGISTRY_ABI,
|
|
21926
22836
|
signer
|
|
@@ -21928,7 +22838,7 @@ async function setPositionDelegate(positionId, delegate, signer, registryAddress
|
|
|
21928
22838
|
return registry["setPositionDelegate"](positionId, delegate);
|
|
21929
22839
|
}
|
|
21930
22840
|
async function getPositionDelegate(positionId, provider, registryAddress) {
|
|
21931
|
-
const registry = new
|
|
22841
|
+
const registry = new import_ethers15.Contract(
|
|
21932
22842
|
registryAddress,
|
|
21933
22843
|
POSITION_DELEGATE_REGISTRY_ABI,
|
|
21934
22844
|
provider
|