@gvnrdao/dh-sdk 0.0.333 → 0.0.335
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/README.md +13 -1
- package/browser/dist/browser.js +1 -1
- package/dist/constants/chunks/protocol-license.d.ts +48 -0
- package/dist/constants/index.d.ts +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +117 -18
- package/dist/index.mjs +119 -19
- package/dist/modules/diamond-hands-sdk.d.ts +18 -0
- package/dist/utils/chunks/eip1559-broadcast.utils.d.ts +57 -4
- package/package.json +1 -1
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protocol license data for Diamond Hands.
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth: the repository `LICENSE` file (byte-identical in
|
|
5
|
+
* `sdk/LICENSE` and `contracts/LICENSE`). `ContractVersionRegistry` publishes
|
|
6
|
+
* the SPDX identifier and a CIDv1 (raw/sha2-256) URI of that same file:
|
|
7
|
+
*
|
|
8
|
+
* LICENSE = "UNLICENSED"
|
|
9
|
+
* LICENSE_URI = "ipfs://bafkreigunb4phugonefe66by524nhnrgrpol4233ytg7e7eg7qomhy3s3a"
|
|
10
|
+
*
|
|
11
|
+
* `getLicense()` returns the bundled text so callers do not depend on an IPFS
|
|
12
|
+
* gateway. Fetching the CID at runtime would be a second copy of these bytes
|
|
13
|
+
* plus a network failure mode. A unit test locks the file, the CID, and the
|
|
14
|
+
* on-chain constants together.
|
|
15
|
+
*/
|
|
16
|
+
export declare const PROTOCOL_LICENSE_IDENTIFIER: "UNLICENSED";
|
|
17
|
+
export declare const PROTOCOL_LICENSE_URI: "ipfs://bafkreigunb4phugonefe66by524nhnrgrpol4233ytg7e7eg7qomhy3s3a";
|
|
18
|
+
export type ProtocolLicense = {
|
|
19
|
+
/** SPDX identifier published on-chain as `ContractVersionRegistry.LICENSE`. */
|
|
20
|
+
identifier: typeof PROTOCOL_LICENSE_IDENTIFIER;
|
|
21
|
+
/**
|
|
22
|
+
* Content-addressed IPFS URI published on-chain as
|
|
23
|
+
* `ContractVersionRegistry.LICENSE_URI`. The CID is CIDv1 / raw / sha2-256
|
|
24
|
+
* of `text`.
|
|
25
|
+
*/
|
|
26
|
+
uri: typeof PROTOCOL_LICENSE_URI;
|
|
27
|
+
/** Full proprietary license text (byte-identical to the CID payload). */
|
|
28
|
+
text: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Return the Diamond Hands protocol license.
|
|
32
|
+
*
|
|
33
|
+
* This is the package `LICENSE` file — the same bytes pinned at
|
|
34
|
+
* {@link PROTOCOL_LICENSE_URI} and advertised by `ContractVersionRegistry`.
|
|
35
|
+
*
|
|
36
|
+
* @returns Identifier (`UNLICENSED`), IPFS URI, and full license text.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* import { getLicense } from "@gvnrdao/dh-sdk";
|
|
41
|
+
*
|
|
42
|
+
* const license = getLicense();
|
|
43
|
+
* console.log(license.identifier); // "UNLICENSED"
|
|
44
|
+
* console.log(license.uri); // "ipfs://bafkrei…"
|
|
45
|
+
* console.log(license.text); // full GVNR DAO LLC reservation
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare function getLicense(): ProtocolLicense;
|
package/dist/index.d.ts
CHANGED
|
@@ -49,6 +49,8 @@ export { LoanStatus } from './types/loanStatus';
|
|
|
49
49
|
export type { LoanEvents, LoanEventsFilter, PositionCreatedEvent, PaymentEvent, StatusUpdateEvent, LiquidationEvent, UCDMintEvent, WithdrawalEvent, RenewalEvent, OperationFailureEvent, MintRequestEvent, BurnRequestEvent, CollateralContractEvent, } from './types/event-types';
|
|
50
50
|
export { PaymentType, PositionStatus, isValidPositionStatus, isValidPaymentType, numericToPositionStatus, EventHelpers, } from './types/event-types';
|
|
51
51
|
export type { ProtocolEvent, ProtocolEventsFilter, ProtocolEventsResult, } from './types/protocol-event-types';
|
|
52
|
+
export { getLicense, PROTOCOL_LICENSE_IDENTIFIER, PROTOCOL_LICENSE_URI, } from './constants/chunks/protocol-license';
|
|
53
|
+
export type { ProtocolLicense } from './constants/chunks/protocol-license';
|
|
52
54
|
export { fetchProtocolPauseStatus, assertProtocolNotPaused, } from './protocol/protocol-pause';
|
|
53
55
|
export type { ProtocolPauseStatus, ProtocolPauseKey, } from './protocol/protocol-pause';
|
|
54
56
|
export { SDKError, ErrorCategory, ErrorSeverity } from './utils/error-handler';
|
package/dist/index.js
CHANGED
|
@@ -4264,11 +4264,11 @@ ${auth.clientId}`;
|
|
|
4264
4264
|
);
|
|
4265
4265
|
const prices = await Promise.race([fetchAll, timeoutPromise]);
|
|
4266
4266
|
const sorted = [...prices].sort((a, b) => a - b);
|
|
4267
|
-
let
|
|
4267
|
+
let median2;
|
|
4268
4268
|
if (this.devSingleProvider) {
|
|
4269
|
-
|
|
4269
|
+
median2 = sorted[0];
|
|
4270
4270
|
} else {
|
|
4271
|
-
|
|
4271
|
+
median2 = sorted[1];
|
|
4272
4272
|
const spread = (sorted[2] - sorted[0]) / sorted[0];
|
|
4273
4273
|
if (spread > PRICE_ORACLE_TOLERANCE) {
|
|
4274
4274
|
const names = this.sources.map((s) => s.name).join(", ");
|
|
@@ -4278,10 +4278,10 @@ ${auth.clientId}`;
|
|
|
4278
4278
|
);
|
|
4279
4279
|
}
|
|
4280
4280
|
}
|
|
4281
|
-
const priceCents = Math.round(
|
|
4281
|
+
const priceCents = Math.round(median2 * 100);
|
|
4282
4282
|
const priceWith8Decimals = BigInt(priceCents) * 1000000n;
|
|
4283
4283
|
const totalElapsed = Date.now() - startTime;
|
|
4284
|
-
console.log(`[Price Oracle] Median price: $${
|
|
4284
|
+
console.log(`[Price Oracle] Median price: $${median2.toLocaleString()} \u2014 total time: ${totalElapsed}ms`);
|
|
4285
4285
|
console.log(`[Price Oracle] Price with 8 decimals: ${priceWith8Decimals}`);
|
|
4286
4286
|
return priceWith8Decimals;
|
|
4287
4287
|
}
|
|
@@ -5771,6 +5771,8 @@ __export(src_exports, {
|
|
|
5771
5771
|
LoanQuery: () => LoanQuery,
|
|
5772
5772
|
LoanStatus: () => LoanStatus,
|
|
5773
5773
|
PKPManager: () => PKPManager,
|
|
5774
|
+
PROTOCOL_LICENSE_IDENTIFIER: () => PROTOCOL_LICENSE_IDENTIFIER,
|
|
5775
|
+
PROTOCOL_LICENSE_URI: () => PROTOCOL_LICENSE_URI,
|
|
5774
5776
|
PaymentType: () => PaymentType,
|
|
5775
5777
|
PositionStatus: () => PositionStatus,
|
|
5776
5778
|
RECOVERY_INVALIDATE_ACTION: () => RECOVERY_INVALIDATE_ACTION,
|
|
@@ -5809,6 +5811,7 @@ __export(src_exports, {
|
|
|
5809
5811
|
getContractsByNetwork: () => getContractsByNetwork,
|
|
5810
5812
|
getDelegateStatus: () => getDelegateStatus,
|
|
5811
5813
|
getDeploymentByNetwork: () => getDeploymentByNetwork,
|
|
5814
|
+
getLicense: () => getLicense,
|
|
5812
5815
|
getPositionDelegate: () => getPositionDelegate,
|
|
5813
5816
|
getSafeAgentDelegation: () => getSafeAgentDelegation,
|
|
5814
5817
|
isClosePositionAuth: () => isClosePositionAuth,
|
|
@@ -6233,26 +6236,83 @@ var MAKE_PAYMENT_GAS_CEILING = BigInt(1e6);
|
|
|
6233
6236
|
var EXTEND_POSITION_GAS_CEILING = BigInt(1e6);
|
|
6234
6237
|
var LIQUIDATION_COMMIT_GAS_CEILING = BigInt(1e6);
|
|
6235
6238
|
var LIQUIDATION_REVEAL_GAS_CEILING = BigInt(2e6);
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6239
|
+
var PRIORITY_FEE_FLOOR_WEI = (0, import_ethers.parseUnits)("0.1", "gwei");
|
|
6240
|
+
var PRIORITY_FEE_CAP_WEI = (0, import_ethers.parseUnits)("3", "gwei");
|
|
6241
|
+
var FEE_HISTORY_BLOCK_COUNT = 20;
|
|
6242
|
+
var FEE_HISTORY_REWARD_PERCENTILE = 75;
|
|
6243
|
+
function isJsonRpcSendable(provider) {
|
|
6244
|
+
return typeof provider.send === "function";
|
|
6245
|
+
}
|
|
6246
|
+
function parseFeeHistory(raw) {
|
|
6247
|
+
if (typeof raw !== "object" || raw === null) {
|
|
6248
|
+
throw new Error(`eth_feeHistory returned a non-object result: ${JSON.stringify(raw)}`);
|
|
6249
|
+
}
|
|
6250
|
+
const { baseFeePerGas, reward } = raw;
|
|
6251
|
+
if (!Array.isArray(baseFeePerGas) || baseFeePerGas.length === 0) {
|
|
6252
|
+
throw new Error(
|
|
6253
|
+
`eth_feeHistory returned no baseFeePerGas \u2014 the RPC does not support EIP-1559 fee history: ${JSON.stringify(raw)}`
|
|
6254
|
+
);
|
|
6255
|
+
}
|
|
6256
|
+
if (!Array.isArray(reward) || reward.length === 0) {
|
|
6257
|
+
throw new Error(
|
|
6258
|
+
`eth_feeHistory returned no reward percentiles \u2014 the RPC ignored the rewardPercentiles argument: ${JSON.stringify(raw)}`
|
|
6259
|
+
);
|
|
6247
6260
|
}
|
|
6261
|
+
const nextBaseFee = (0, import_ethers.getBigInt)(baseFeePerGas[baseFeePerGas.length - 1], "baseFeePerGas");
|
|
6262
|
+
const tips = reward.map((perBlock, i) => {
|
|
6263
|
+
if (!Array.isArray(perBlock) || perBlock.length === 0) {
|
|
6264
|
+
throw new Error(`eth_feeHistory reward row ${i} is empty: ${JSON.stringify(raw)}`);
|
|
6265
|
+
}
|
|
6266
|
+
return (0, import_ethers.getBigInt)(perBlock[0], `reward[${i}]`);
|
|
6267
|
+
});
|
|
6268
|
+
return { nextBaseFee, tips };
|
|
6269
|
+
}
|
|
6270
|
+
function median(values) {
|
|
6271
|
+
const sorted = [...values].sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
6272
|
+
const mid = sorted.length >> 1;
|
|
6273
|
+
const hi = sorted[mid];
|
|
6274
|
+
if (hi === void 0) {
|
|
6275
|
+
throw new Error("median: no values");
|
|
6276
|
+
}
|
|
6277
|
+
if (sorted.length % 2 === 1)
|
|
6278
|
+
return hi;
|
|
6279
|
+
const lo = sorted[mid - 1];
|
|
6280
|
+
if (lo === void 0) {
|
|
6281
|
+
throw new Error("median: no values");
|
|
6282
|
+
}
|
|
6283
|
+
return (lo + hi) / 2n;
|
|
6284
|
+
}
|
|
6285
|
+
function clamp(value, floor, cap) {
|
|
6286
|
+
if (value < floor)
|
|
6287
|
+
return floor;
|
|
6288
|
+
if (value > cap)
|
|
6289
|
+
return cap;
|
|
6290
|
+
return value;
|
|
6291
|
+
}
|
|
6292
|
+
async function resolveEip1559FeeFields(feeProvider) {
|
|
6293
|
+
if (!isJsonRpcSendable(feeProvider)) {
|
|
6294
|
+
throw new Error(
|
|
6295
|
+
"resolveEip1559FeeFields: feeProvider must be a JSON-RPC provider exposing send() (the SDK read provider), not a wallet BrowserProvider \u2014 eth_feeHistory is required"
|
|
6296
|
+
);
|
|
6297
|
+
}
|
|
6298
|
+
const history = parseFeeHistory(
|
|
6299
|
+
await feeProvider.send("eth_feeHistory", [
|
|
6300
|
+
(0, import_ethers.toQuantity)(FEE_HISTORY_BLOCK_COUNT),
|
|
6301
|
+
"latest",
|
|
6302
|
+
[FEE_HISTORY_REWARD_PERCENTILE]
|
|
6303
|
+
])
|
|
6304
|
+
);
|
|
6305
|
+
const marketTip = median(history.tips);
|
|
6306
|
+
const maxPriorityFeePerGas = clamp(marketTip, PRIORITY_FEE_FLOOR_WEI, PRIORITY_FEE_CAP_WEI);
|
|
6307
|
+
const maxFeePerGas = history.nextBaseFee * 2n + maxPriorityFeePerGas;
|
|
6248
6308
|
return { maxFeePerGas, maxPriorityFeePerGas };
|
|
6249
6309
|
}
|
|
6250
6310
|
async function sendEip1559Transaction(params) {
|
|
6251
|
-
const { signer, to, data, gasLimit } = params;
|
|
6311
|
+
const { signer, feeProvider, to, data, gasLimit } = params;
|
|
6252
6312
|
if (!signer.provider) {
|
|
6253
6313
|
throw new Error("Signer must have a provider attached");
|
|
6254
6314
|
}
|
|
6255
|
-
const fees = await resolveEip1559FeeFields(
|
|
6315
|
+
const fees = await resolveEip1559FeeFields(feeProvider);
|
|
6256
6316
|
return signer.sendTransaction({
|
|
6257
6317
|
to,
|
|
6258
6318
|
data,
|
|
@@ -6278,6 +6338,19 @@ var SDK_DEFAULTS = {
|
|
|
6278
6338
|
DEBUG: false
|
|
6279
6339
|
};
|
|
6280
6340
|
|
|
6341
|
+
// src/constants/chunks/protocol-license.ts
|
|
6342
|
+
var PROTOCOL_LICENSE_IDENTIFIER = "UNLICENSED";
|
|
6343
|
+
var PROTOCOL_LICENSE_URI = "ipfs://bafkreigunb4phugonefe66by524nhnrgrpol4233ytg7e7eg7qomhy3s3a";
|
|
6344
|
+
var PROTOCOL_LICENSE_TEXT = '(c) GVNR DAO LLC, 2026\n\nExcept as otherwise provided in the header of a specific source code\nfile in this repository: (a) all intellectual property (including all\nsource code, designs and protocols) contained in this repository has been\npublished for informational purposes only; (b) no license, right of\nreproduction or distribution or other right with respect thereto is\ngranted or implied; and (c) all moral, intellectual property and other\nrights are hereby reserved by the copyright holder.\n\nTHE SOFTWARE AND INTELLECTUAL PROPERTY INCLUDED IN THIS REPOSITORY\nIS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS," AND ANY EXPRESS\nOR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE OR\nINTELLECTUAL PROPERTY (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT\nOF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\nBUSINESS INTERRUPTION), HOWEVER CAUSED OR CLAIMED (WHETHER IN CONTRACT,\nSTRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)), EVEN IF\nSUCH DAMAGES WERE REASONABLY FORESEEABLE OR THE COPYRIGHT HOLDERS WERE\nADVISED OF THE POSSIBILITY OF SUCH DAMAGE.';
|
|
6345
|
+
var PROTOCOL_LICENSE = Object.freeze({
|
|
6346
|
+
identifier: PROTOCOL_LICENSE_IDENTIFIER,
|
|
6347
|
+
uri: PROTOCOL_LICENSE_URI,
|
|
6348
|
+
text: PROTOCOL_LICENSE_TEXT
|
|
6349
|
+
});
|
|
6350
|
+
function getLicense() {
|
|
6351
|
+
return PROTOCOL_LICENSE;
|
|
6352
|
+
}
|
|
6353
|
+
|
|
6281
6354
|
// src/utils/service-endpoint-policy.ts
|
|
6282
6355
|
function assertSafeServiceEndpoint(endpoint) {
|
|
6283
6356
|
let url;
|
|
@@ -21209,6 +21282,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
21209
21282
|
}
|
|
21210
21283
|
const tx = await sendEip1559Transaction({
|
|
21211
21284
|
signer,
|
|
21285
|
+
feeProvider: this.getProviderOrThrow(),
|
|
21212
21286
|
to: positionManagerAddress,
|
|
21213
21287
|
data: mintCalldata,
|
|
21214
21288
|
gasLimit: MINT_UCD_GAS_CEILING
|
|
@@ -22430,6 +22504,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
22430
22504
|
}
|
|
22431
22505
|
const tx = await sendEip1559Transaction({
|
|
22432
22506
|
signer: extendSigner,
|
|
22507
|
+
feeProvider: this.getProviderOrThrow(),
|
|
22433
22508
|
to: extendTo,
|
|
22434
22509
|
data: extendCalldata,
|
|
22435
22510
|
gasLimit: EXTEND_POSITION_GAS_CEILING
|
|
@@ -23358,6 +23433,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
23358
23433
|
}
|
|
23359
23434
|
const tx = await sendEip1559Transaction({
|
|
23360
23435
|
signer: paymentSigner,
|
|
23436
|
+
feeProvider: this.getProviderOrThrow(),
|
|
23361
23437
|
to: paymentContractAddress,
|
|
23362
23438
|
data: paymentCalldata,
|
|
23363
23439
|
gasLimit: MAKE_PAYMENT_GAS_CEILING
|
|
@@ -24123,6 +24199,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
24123
24199
|
});
|
|
24124
24200
|
tx = await sendEip1559Transaction({
|
|
24125
24201
|
signer: withdrawSigner,
|
|
24202
|
+
feeProvider: this.getProviderOrThrow(),
|
|
24126
24203
|
to: positionManagerAddress,
|
|
24127
24204
|
data: withdrawCalldata,
|
|
24128
24205
|
gasLimit: WITHDRAW_BTC_GAS_CEILING
|
|
@@ -26594,6 +26671,25 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
26594
26671
|
getConfig() {
|
|
26595
26672
|
return this.config;
|
|
26596
26673
|
}
|
|
26674
|
+
/**
|
|
26675
|
+
* Return the Diamond Hands protocol license.
|
|
26676
|
+
*
|
|
26677
|
+
* Same data as the standalone `getLicense()` export: the package `LICENSE`
|
|
26678
|
+
* file, which is the CIDv1 payload of `ContractVersionRegistry.LICENSE_URI`.
|
|
26679
|
+
* Does not fetch IPFS.
|
|
26680
|
+
*
|
|
26681
|
+
* @returns Identifier (`UNLICENSED`), IPFS URI, and full license text.
|
|
26682
|
+
*
|
|
26683
|
+
* @example
|
|
26684
|
+
* ```ts
|
|
26685
|
+
* const license = sdk.getLicense();
|
|
26686
|
+
* console.log(license.identifier); // "UNLICENSED"
|
|
26687
|
+
* console.log(license.uri); // "ipfs://bafkrei…"
|
|
26688
|
+
* ```
|
|
26689
|
+
*/
|
|
26690
|
+
getLicense() {
|
|
26691
|
+
return getLicense();
|
|
26692
|
+
}
|
|
26597
26693
|
/**
|
|
26598
26694
|
* Clear all caches
|
|
26599
26695
|
*/
|
|
@@ -27560,6 +27656,8 @@ async function getSafeAgentDelegation(params) {
|
|
|
27560
27656
|
LoanQuery,
|
|
27561
27657
|
LoanStatus,
|
|
27562
27658
|
PKPManager,
|
|
27659
|
+
PROTOCOL_LICENSE_IDENTIFIER,
|
|
27660
|
+
PROTOCOL_LICENSE_URI,
|
|
27563
27661
|
PaymentType,
|
|
27564
27662
|
PositionStatus,
|
|
27565
27663
|
RECOVERY_INVALIDATE_ACTION,
|
|
@@ -27597,6 +27695,7 @@ async function getSafeAgentDelegation(params) {
|
|
|
27597
27695
|
getContractsByNetwork,
|
|
27598
27696
|
getDelegateStatus,
|
|
27599
27697
|
getDeploymentByNetwork,
|
|
27698
|
+
getLicense,
|
|
27600
27699
|
getPositionDelegate,
|
|
27601
27700
|
getSafeAgentDelegation,
|
|
27602
27701
|
isClosePositionAuth,
|
package/dist/index.mjs
CHANGED
|
@@ -4270,11 +4270,11 @@ ${auth.clientId}`;
|
|
|
4270
4270
|
);
|
|
4271
4271
|
const prices = await Promise.race([fetchAll, timeoutPromise]);
|
|
4272
4272
|
const sorted = [...prices].sort((a, b) => a - b);
|
|
4273
|
-
let
|
|
4273
|
+
let median2;
|
|
4274
4274
|
if (this.devSingleProvider) {
|
|
4275
|
-
|
|
4275
|
+
median2 = sorted[0];
|
|
4276
4276
|
} else {
|
|
4277
|
-
|
|
4277
|
+
median2 = sorted[1];
|
|
4278
4278
|
const spread = (sorted[2] - sorted[0]) / sorted[0];
|
|
4279
4279
|
if (spread > PRICE_ORACLE_TOLERANCE) {
|
|
4280
4280
|
const names = this.sources.map((s) => s.name).join(", ");
|
|
@@ -4284,10 +4284,10 @@ ${auth.clientId}`;
|
|
|
4284
4284
|
);
|
|
4285
4285
|
}
|
|
4286
4286
|
}
|
|
4287
|
-
const priceCents = Math.round(
|
|
4287
|
+
const priceCents = Math.round(median2 * 100);
|
|
4288
4288
|
const priceWith8Decimals = BigInt(priceCents) * 1000000n;
|
|
4289
4289
|
const totalElapsed = Date.now() - startTime;
|
|
4290
|
-
console.log(`[Price Oracle] Median price: $${
|
|
4290
|
+
console.log(`[Price Oracle] Median price: $${median2.toLocaleString()} \u2014 total time: ${totalElapsed}ms`);
|
|
4291
4291
|
console.log(`[Price Oracle] Price with 8 decimals: ${priceWith8Decimals}`);
|
|
4292
4292
|
return priceWith8Decimals;
|
|
4293
4293
|
}
|
|
@@ -6137,33 +6137,94 @@ var SDKError = class _SDKError extends Error {
|
|
|
6137
6137
|
};
|
|
6138
6138
|
|
|
6139
6139
|
// src/utils/chunks/eip1559-broadcast.utils.ts
|
|
6140
|
-
import {
|
|
6140
|
+
import {
|
|
6141
|
+
getBigInt,
|
|
6142
|
+
parseUnits,
|
|
6143
|
+
toQuantity
|
|
6144
|
+
} from "ethers";
|
|
6141
6145
|
var MINT_UCD_GAS_CEILING = BigInt(13e5);
|
|
6142
6146
|
var WITHDRAW_BTC_GAS_CEILING = BigInt(3e6);
|
|
6143
6147
|
var MAKE_PAYMENT_GAS_CEILING = BigInt(1e6);
|
|
6144
6148
|
var EXTEND_POSITION_GAS_CEILING = BigInt(1e6);
|
|
6145
6149
|
var LIQUIDATION_COMMIT_GAS_CEILING = BigInt(1e6);
|
|
6146
6150
|
var LIQUIDATION_REVEAL_GAS_CEILING = BigInt(2e6);
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6151
|
+
var PRIORITY_FEE_FLOOR_WEI = parseUnits("0.1", "gwei");
|
|
6152
|
+
var PRIORITY_FEE_CAP_WEI = parseUnits("3", "gwei");
|
|
6153
|
+
var FEE_HISTORY_BLOCK_COUNT = 20;
|
|
6154
|
+
var FEE_HISTORY_REWARD_PERCENTILE = 75;
|
|
6155
|
+
function isJsonRpcSendable(provider) {
|
|
6156
|
+
return typeof provider.send === "function";
|
|
6157
|
+
}
|
|
6158
|
+
function parseFeeHistory(raw) {
|
|
6159
|
+
if (typeof raw !== "object" || raw === null) {
|
|
6160
|
+
throw new Error(`eth_feeHistory returned a non-object result: ${JSON.stringify(raw)}`);
|
|
6161
|
+
}
|
|
6162
|
+
const { baseFeePerGas, reward } = raw;
|
|
6163
|
+
if (!Array.isArray(baseFeePerGas) || baseFeePerGas.length === 0) {
|
|
6164
|
+
throw new Error(
|
|
6165
|
+
`eth_feeHistory returned no baseFeePerGas \u2014 the RPC does not support EIP-1559 fee history: ${JSON.stringify(raw)}`
|
|
6166
|
+
);
|
|
6158
6167
|
}
|
|
6168
|
+
if (!Array.isArray(reward) || reward.length === 0) {
|
|
6169
|
+
throw new Error(
|
|
6170
|
+
`eth_feeHistory returned no reward percentiles \u2014 the RPC ignored the rewardPercentiles argument: ${JSON.stringify(raw)}`
|
|
6171
|
+
);
|
|
6172
|
+
}
|
|
6173
|
+
const nextBaseFee = getBigInt(baseFeePerGas[baseFeePerGas.length - 1], "baseFeePerGas");
|
|
6174
|
+
const tips = reward.map((perBlock, i) => {
|
|
6175
|
+
if (!Array.isArray(perBlock) || perBlock.length === 0) {
|
|
6176
|
+
throw new Error(`eth_feeHistory reward row ${i} is empty: ${JSON.stringify(raw)}`);
|
|
6177
|
+
}
|
|
6178
|
+
return getBigInt(perBlock[0], `reward[${i}]`);
|
|
6179
|
+
});
|
|
6180
|
+
return { nextBaseFee, tips };
|
|
6181
|
+
}
|
|
6182
|
+
function median(values) {
|
|
6183
|
+
const sorted = [...values].sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
6184
|
+
const mid = sorted.length >> 1;
|
|
6185
|
+
const hi = sorted[mid];
|
|
6186
|
+
if (hi === void 0) {
|
|
6187
|
+
throw new Error("median: no values");
|
|
6188
|
+
}
|
|
6189
|
+
if (sorted.length % 2 === 1)
|
|
6190
|
+
return hi;
|
|
6191
|
+
const lo = sorted[mid - 1];
|
|
6192
|
+
if (lo === void 0) {
|
|
6193
|
+
throw new Error("median: no values");
|
|
6194
|
+
}
|
|
6195
|
+
return (lo + hi) / 2n;
|
|
6196
|
+
}
|
|
6197
|
+
function clamp(value, floor, cap) {
|
|
6198
|
+
if (value < floor)
|
|
6199
|
+
return floor;
|
|
6200
|
+
if (value > cap)
|
|
6201
|
+
return cap;
|
|
6202
|
+
return value;
|
|
6203
|
+
}
|
|
6204
|
+
async function resolveEip1559FeeFields(feeProvider) {
|
|
6205
|
+
if (!isJsonRpcSendable(feeProvider)) {
|
|
6206
|
+
throw new Error(
|
|
6207
|
+
"resolveEip1559FeeFields: feeProvider must be a JSON-RPC provider exposing send() (the SDK read provider), not a wallet BrowserProvider \u2014 eth_feeHistory is required"
|
|
6208
|
+
);
|
|
6209
|
+
}
|
|
6210
|
+
const history = parseFeeHistory(
|
|
6211
|
+
await feeProvider.send("eth_feeHistory", [
|
|
6212
|
+
toQuantity(FEE_HISTORY_BLOCK_COUNT),
|
|
6213
|
+
"latest",
|
|
6214
|
+
[FEE_HISTORY_REWARD_PERCENTILE]
|
|
6215
|
+
])
|
|
6216
|
+
);
|
|
6217
|
+
const marketTip = median(history.tips);
|
|
6218
|
+
const maxPriorityFeePerGas = clamp(marketTip, PRIORITY_FEE_FLOOR_WEI, PRIORITY_FEE_CAP_WEI);
|
|
6219
|
+
const maxFeePerGas = history.nextBaseFee * 2n + maxPriorityFeePerGas;
|
|
6159
6220
|
return { maxFeePerGas, maxPriorityFeePerGas };
|
|
6160
6221
|
}
|
|
6161
6222
|
async function sendEip1559Transaction(params) {
|
|
6162
|
-
const { signer, to, data, gasLimit } = params;
|
|
6223
|
+
const { signer, feeProvider, to, data, gasLimit } = params;
|
|
6163
6224
|
if (!signer.provider) {
|
|
6164
6225
|
throw new Error("Signer must have a provider attached");
|
|
6165
6226
|
}
|
|
6166
|
-
const fees = await resolveEip1559FeeFields(
|
|
6227
|
+
const fees = await resolveEip1559FeeFields(feeProvider);
|
|
6167
6228
|
return signer.sendTransaction({
|
|
6168
6229
|
to,
|
|
6169
6230
|
data,
|
|
@@ -6189,6 +6250,19 @@ var SDK_DEFAULTS = {
|
|
|
6189
6250
|
DEBUG: false
|
|
6190
6251
|
};
|
|
6191
6252
|
|
|
6253
|
+
// src/constants/chunks/protocol-license.ts
|
|
6254
|
+
var PROTOCOL_LICENSE_IDENTIFIER = "UNLICENSED";
|
|
6255
|
+
var PROTOCOL_LICENSE_URI = "ipfs://bafkreigunb4phugonefe66by524nhnrgrpol4233ytg7e7eg7qomhy3s3a";
|
|
6256
|
+
var PROTOCOL_LICENSE_TEXT = '(c) GVNR DAO LLC, 2026\n\nExcept as otherwise provided in the header of a specific source code\nfile in this repository: (a) all intellectual property (including all\nsource code, designs and protocols) contained in this repository has been\npublished for informational purposes only; (b) no license, right of\nreproduction or distribution or other right with respect thereto is\ngranted or implied; and (c) all moral, intellectual property and other\nrights are hereby reserved by the copyright holder.\n\nTHE SOFTWARE AND INTELLECTUAL PROPERTY INCLUDED IN THIS REPOSITORY\nIS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS," AND ANY EXPRESS\nOR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE OR\nINTELLECTUAL PROPERTY (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT\nOF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\nBUSINESS INTERRUPTION), HOWEVER CAUSED OR CLAIMED (WHETHER IN CONTRACT,\nSTRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)), EVEN IF\nSUCH DAMAGES WERE REASONABLY FORESEEABLE OR THE COPYRIGHT HOLDERS WERE\nADVISED OF THE POSSIBILITY OF SUCH DAMAGE.';
|
|
6257
|
+
var PROTOCOL_LICENSE = Object.freeze({
|
|
6258
|
+
identifier: PROTOCOL_LICENSE_IDENTIFIER,
|
|
6259
|
+
uri: PROTOCOL_LICENSE_URI,
|
|
6260
|
+
text: PROTOCOL_LICENSE_TEXT
|
|
6261
|
+
});
|
|
6262
|
+
function getLicense() {
|
|
6263
|
+
return PROTOCOL_LICENSE;
|
|
6264
|
+
}
|
|
6265
|
+
|
|
6192
6266
|
// src/utils/service-endpoint-policy.ts
|
|
6193
6267
|
function assertSafeServiceEndpoint(endpoint) {
|
|
6194
6268
|
let url;
|
|
@@ -21134,6 +21208,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
21134
21208
|
}
|
|
21135
21209
|
const tx = await sendEip1559Transaction({
|
|
21136
21210
|
signer,
|
|
21211
|
+
feeProvider: this.getProviderOrThrow(),
|
|
21137
21212
|
to: positionManagerAddress,
|
|
21138
21213
|
data: mintCalldata,
|
|
21139
21214
|
gasLimit: MINT_UCD_GAS_CEILING
|
|
@@ -22355,6 +22430,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
22355
22430
|
}
|
|
22356
22431
|
const tx = await sendEip1559Transaction({
|
|
22357
22432
|
signer: extendSigner,
|
|
22433
|
+
feeProvider: this.getProviderOrThrow(),
|
|
22358
22434
|
to: extendTo,
|
|
22359
22435
|
data: extendCalldata,
|
|
22360
22436
|
gasLimit: EXTEND_POSITION_GAS_CEILING
|
|
@@ -23283,6 +23359,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
23283
23359
|
}
|
|
23284
23360
|
const tx = await sendEip1559Transaction({
|
|
23285
23361
|
signer: paymentSigner,
|
|
23362
|
+
feeProvider: this.getProviderOrThrow(),
|
|
23286
23363
|
to: paymentContractAddress,
|
|
23287
23364
|
data: paymentCalldata,
|
|
23288
23365
|
gasLimit: MAKE_PAYMENT_GAS_CEILING
|
|
@@ -24048,6 +24125,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
24048
24125
|
});
|
|
24049
24126
|
tx = await sendEip1559Transaction({
|
|
24050
24127
|
signer: withdrawSigner,
|
|
24128
|
+
feeProvider: this.getProviderOrThrow(),
|
|
24051
24129
|
to: positionManagerAddress,
|
|
24052
24130
|
data: withdrawCalldata,
|
|
24053
24131
|
gasLimit: WITHDRAW_BTC_GAS_CEILING
|
|
@@ -26519,6 +26597,25 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
26519
26597
|
getConfig() {
|
|
26520
26598
|
return this.config;
|
|
26521
26599
|
}
|
|
26600
|
+
/**
|
|
26601
|
+
* Return the Diamond Hands protocol license.
|
|
26602
|
+
*
|
|
26603
|
+
* Same data as the standalone `getLicense()` export: the package `LICENSE`
|
|
26604
|
+
* file, which is the CIDv1 payload of `ContractVersionRegistry.LICENSE_URI`.
|
|
26605
|
+
* Does not fetch IPFS.
|
|
26606
|
+
*
|
|
26607
|
+
* @returns Identifier (`UNLICENSED`), IPFS URI, and full license text.
|
|
26608
|
+
*
|
|
26609
|
+
* @example
|
|
26610
|
+
* ```ts
|
|
26611
|
+
* const license = sdk.getLicense();
|
|
26612
|
+
* console.log(license.identifier); // "UNLICENSED"
|
|
26613
|
+
* console.log(license.uri); // "ipfs://bafkrei…"
|
|
26614
|
+
* ```
|
|
26615
|
+
*/
|
|
26616
|
+
getLicense() {
|
|
26617
|
+
return getLicense();
|
|
26618
|
+
}
|
|
26522
26619
|
/**
|
|
26523
26620
|
* Clear all caches
|
|
26524
26621
|
*/
|
|
@@ -27484,6 +27581,8 @@ export {
|
|
|
27484
27581
|
LoanQuery,
|
|
27485
27582
|
LoanStatus,
|
|
27486
27583
|
PKPManager,
|
|
27584
|
+
PROTOCOL_LICENSE_IDENTIFIER,
|
|
27585
|
+
PROTOCOL_LICENSE_URI,
|
|
27487
27586
|
PaymentType,
|
|
27488
27587
|
PositionStatus,
|
|
27489
27588
|
RECOVERY_INVALIDATE_ACTION,
|
|
@@ -27522,6 +27621,7 @@ export {
|
|
|
27522
27621
|
getContractsByNetwork,
|
|
27523
27622
|
getDelegateStatus,
|
|
27524
27623
|
getDeploymentByNetwork,
|
|
27624
|
+
getLicense,
|
|
27525
27625
|
getPositionDelegate,
|
|
27526
27626
|
getSafeAgentDelegation,
|
|
27527
27627
|
isClosePositionAuth,
|
|
@@ -17,6 +17,7 @@ import { Satoshis } from "../types/branded/domain-values";
|
|
|
17
17
|
import { SDKError } from "../utils/error-handler";
|
|
18
18
|
import type { CreateLoanRequest, CreateLoanResult, LoanDataDetail, UCDMintRequest, UCDMintResult, PartialPaymentRequest, PartialPaymentResult, BTCWithdrawalResult, RenewPositionRequest, RenewPositionResult, LiquidationRequest, LiquidationResult, ConfirmBalanceRequest, ConfirmBalanceResult, TermsWithFeesResult } from "../interfaces/chunks/loan-operations.i";
|
|
19
19
|
import type { DiamondHandsSDKConfig } from "../interfaces/chunks/config.i";
|
|
20
|
+
import { type ProtocolLicense } from "../constants/chunks/protocol-license";
|
|
20
21
|
import type { PKPData } from "../interfaces/chunks/pkp-integration.i";
|
|
21
22
|
import { type AuthorizationProvider } from "../utils/authorization-provider.utils";
|
|
22
23
|
import type { DhServerLoginPayload } from "../utils/eip712-login";
|
|
@@ -1375,6 +1376,23 @@ export declare class DiamondHandsSDK {
|
|
|
1375
1376
|
* Get SDK configuration
|
|
1376
1377
|
*/
|
|
1377
1378
|
getConfig(): DiamondHandsSDKConfig;
|
|
1379
|
+
/**
|
|
1380
|
+
* Return the Diamond Hands protocol license.
|
|
1381
|
+
*
|
|
1382
|
+
* Same data as the standalone `getLicense()` export: the package `LICENSE`
|
|
1383
|
+
* file, which is the CIDv1 payload of `ContractVersionRegistry.LICENSE_URI`.
|
|
1384
|
+
* Does not fetch IPFS.
|
|
1385
|
+
*
|
|
1386
|
+
* @returns Identifier (`UNLICENSED`), IPFS URI, and full license text.
|
|
1387
|
+
*
|
|
1388
|
+
* @example
|
|
1389
|
+
* ```ts
|
|
1390
|
+
* const license = sdk.getLicense();
|
|
1391
|
+
* console.log(license.identifier); // "UNLICENSED"
|
|
1392
|
+
* console.log(license.uri); // "ipfs://bafkrei…"
|
|
1393
|
+
* ```
|
|
1394
|
+
*/
|
|
1395
|
+
getLicense(): ProtocolLicense;
|
|
1378
1396
|
/**
|
|
1379
1397
|
* Clear all caches
|
|
1380
1398
|
*/
|
|
@@ -24,9 +24,14 @@ export declare const MINT_UCD_GAS_CEILING: bigint;
|
|
|
24
24
|
* gasLimit — no estimateGas runs on this path). The on-chain flow now spans PositionManager →
|
|
25
25
|
* CollateralManager → LoanOperationsManager → BTCSpendAuthorizer plus the audit
|
|
26
26
|
* #3 Chainlink feed-staleness gate and the BitcoinWithdrawalAddressRegistry
|
|
27
|
-
* allowlist STATICCALL; observed end-to-end cost
|
|
28
|
-
* hardcoded 1,000,000 limit caused a nested out-of-gas.
|
|
29
|
-
*
|
|
27
|
+
* allowlist STATICCALL; observed end-to-end cost was ~1.0–1.3M gas when this
|
|
28
|
+
* was sized, so the prior hardcoded 1,000,000 limit caused a nested out-of-gas.
|
|
29
|
+
* A mainnet withdrawBTC measured 750,919 gas on 2026-09-04 (2 vault inputs,
|
|
30
|
+
* 1 prior reservation; tx 0xa244a184…) — recorded as a data point, NOT a new
|
|
31
|
+
* ceiling basis: cost grows with the number of open reservations
|
|
32
|
+
* (`_computeAuthorizedSpendsHash` is O(N)), so the ceiling stays sized to the
|
|
33
|
+
* observed MAX. 3M gives ~2x headroom over it while staying well under the
|
|
34
|
+
* block gas limit.
|
|
30
35
|
*/
|
|
31
36
|
export declare const WITHDRAW_BTC_GAS_CEILING: bigint;
|
|
32
37
|
/**
|
|
@@ -86,12 +91,60 @@ export declare const LIQUIDATION_COMMIT_GAS_CEILING: bigint;
|
|
|
86
91
|
* provisional until re-measured against a real liquidation.
|
|
87
92
|
*/
|
|
88
93
|
export declare const LIQUIDATION_REVEAL_GAS_CEILING: bigint;
|
|
89
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Priority-fee (tip) policy for every timing-sensitive broadcast in this file.
|
|
96
|
+
*
|
|
97
|
+
* History that sizes these numbers (docs/withdraw-btc-gas-cost-recommendations-2026-09-05.md):
|
|
98
|
+
* - 2026-07-02: a 0 gwei tip (the read RPC reported a real 0n
|
|
99
|
+
* eth_maxPriorityFeePerGas) sat unmined 119–143 s → QuantumOutsideWindow.
|
|
100
|
+
* - 2026-08-07: a 0.0007 gwei tip was skipped by one NEAR-EMPTY block, mined
|
|
101
|
+
* 24 s later → DeadZoneViolation (see quantum-timing.ts).
|
|
102
|
+
* - 2026-09-04: a mainnet withdrawBTC went out with ethers' hardcoded 1 gwei
|
|
103
|
+
* fallback tip (the wallet provider did not answer eth_maxPriorityFeePerGas)
|
|
104
|
+
* while the market p50 tip was ~0.03 gwei — 93% of a $1.99 tx was tip.
|
|
105
|
+
*
|
|
106
|
+
* So the tip is read from the market (eth_feeHistory on the app's READ RPC,
|
|
107
|
+
* never the wallet provider) at a percentile that buys next-block inclusion,
|
|
108
|
+
* then clamped: the floor keeps builders from skipping us (a quantum miss
|
|
109
|
+
* wastes the Lit signatures, the Chipotle spend and the reverted tx's base
|
|
110
|
+
* fee — far more than the tip), the cap stops a spiking feeHistory from
|
|
111
|
+
* overpaying. At ~751k gas the floor costs ≈ $0.18 (ETH $2,458).
|
|
112
|
+
*/
|
|
113
|
+
export declare const PRIORITY_FEE_FLOOR_WEI: bigint;
|
|
114
|
+
export declare const PRIORITY_FEE_CAP_WEI: bigint;
|
|
115
|
+
/** Blocks of eth_feeHistory sampled; the median across them absorbs a single spike block. */
|
|
116
|
+
export declare const FEE_HISTORY_BLOCK_COUNT = 20;
|
|
117
|
+
/**
|
|
118
|
+
* Reward percentile per block. p75 (not p50) because these sends are
|
|
119
|
+
* quantum-bounded: paying above the median tip buys inclusion in the next
|
|
120
|
+
* slot or two, which is what the 60 s window actually needs.
|
|
121
|
+
*/
|
|
122
|
+
export declare const FEE_HISTORY_REWARD_PERCENTILE = 75;
|
|
123
|
+
/** The subset of ethers' JsonRpcApiProvider these helpers need — raw JSON-RPC access. */
|
|
124
|
+
export interface JsonRpcSendable {
|
|
125
|
+
send(method: string, params: unknown[]): Promise<unknown>;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Resolve EIP-1559 fee fields from the app's READ RPC.
|
|
129
|
+
*
|
|
130
|
+
* `feeProvider` must be a JSON-RPC provider (ethers `JsonRpcProvider` or any
|
|
131
|
+
* object exposing `send`), NOT the wallet's `BrowserProvider`: wallet
|
|
132
|
+
* providers routinely fail `eth_maxPriorityFeePerGas`, and ethers'
|
|
133
|
+
* `getFeeData()` then silently substitutes a 1 gwei tip — the 2026-09-04
|
|
134
|
+
* overpayment. This helper never calls `getFeeData()`.
|
|
135
|
+
*
|
|
136
|
+
* One `eth_feeHistory` round-trip replaces the three RPC calls `getFeeData()`
|
|
137
|
+
* made (block, gasPrice, priorityFee), so it is also faster on the
|
|
138
|
+
* quantum-gated path that runs right before the send.
|
|
139
|
+
*/
|
|
140
|
+
export declare function resolveEip1559FeeFields(feeProvider: Provider): Promise<{
|
|
90
141
|
maxFeePerGas: bigint;
|
|
91
142
|
maxPriorityFeePerGas: bigint;
|
|
92
143
|
}>;
|
|
93
144
|
export declare function sendEip1559Transaction(params: {
|
|
94
145
|
signer: Signer;
|
|
146
|
+
/** The SDK READ provider (JSON-RPC). Fee fields come from here, never from `signer.provider`. */
|
|
147
|
+
feeProvider: Provider;
|
|
95
148
|
to: string;
|
|
96
149
|
data: string;
|
|
97
150
|
gasLimit: bigint;
|
package/package.json
CHANGED