@gvnrdao/dh-sdk 0.0.314 → 0.0.315
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/dist/index.js
CHANGED
|
@@ -9178,6 +9178,23 @@ function createPKPManager(config) {
|
|
|
9178
9178
|
// src/modules/loan/loan-creator.module.ts
|
|
9179
9179
|
var import_ethers11 = require("ethers");
|
|
9180
9180
|
var import_dh_lit_actions = __toESM(require_pkg_src());
|
|
9181
|
+
|
|
9182
|
+
// src/utils/contract-wallet.utils.ts
|
|
9183
|
+
var contractWalletCache = /* @__PURE__ */ new Map();
|
|
9184
|
+
async function isContractWallet(provider, address) {
|
|
9185
|
+
const { chainId } = await provider.getNetwork();
|
|
9186
|
+
const cacheKey = `${chainId}:${address.toLowerCase()}`;
|
|
9187
|
+
const cached = contractWalletCache.get(cacheKey);
|
|
9188
|
+
if (cached !== void 0)
|
|
9189
|
+
return cached;
|
|
9190
|
+
const code = await provider.getCode(address);
|
|
9191
|
+
const isContract = code !== "0x";
|
|
9192
|
+
contractWalletCache.set(cacheKey, isContract);
|
|
9193
|
+
return isContract;
|
|
9194
|
+
}
|
|
9195
|
+
|
|
9196
|
+
// src/modules/loan/loan-creator.module.ts
|
|
9197
|
+
var CONTRACT_WALLET_POLL_INTERVAL_MS = 4e3;
|
|
9181
9198
|
var LoanCreator = class {
|
|
9182
9199
|
config;
|
|
9183
9200
|
transactionTimeoutMs;
|
|
@@ -9401,6 +9418,11 @@ var LoanCreator = class {
|
|
|
9401
9418
|
validatorSignature: validatorSignature.substring(0, 20) + "..."
|
|
9402
9419
|
});
|
|
9403
9420
|
}
|
|
9421
|
+
this.config.onPkpCreated?.({
|
|
9422
|
+
tokenId: pkpData.tokenId,
|
|
9423
|
+
ethAddress: pkpData.ethAddress,
|
|
9424
|
+
publicKey: pkpData.publicKey
|
|
9425
|
+
});
|
|
9404
9426
|
const addressesResult = await this.deriveBitcoinAddresses(
|
|
9405
9427
|
pkpData.publicKey
|
|
9406
9428
|
);
|
|
@@ -9573,6 +9595,12 @@ var LoanCreator = class {
|
|
|
9573
9595
|
error: encErr instanceof Error ? encErr.message : String(encErr)
|
|
9574
9596
|
});
|
|
9575
9597
|
}
|
|
9598
|
+
const provider = this.config.contractManager.getProvider();
|
|
9599
|
+
const senderIsContractWallet = await isContractWallet(
|
|
9600
|
+
provider,
|
|
9601
|
+
await signerResult.value.getAddress()
|
|
9602
|
+
);
|
|
9603
|
+
const searchFromBlock = senderIsContractWallet ? await provider.getBlockNumber() : 0;
|
|
9576
9604
|
const tx = await positionManager.createPosition(
|
|
9577
9605
|
pkpIdBytes32,
|
|
9578
9606
|
validatorSignature,
|
|
@@ -9594,19 +9622,20 @@ var LoanCreator = class {
|
|
|
9594
9622
|
}
|
|
9595
9623
|
if (this.config.debug) {
|
|
9596
9624
|
log.info("\u23F3 Waiting for transaction confirmation", {
|
|
9597
|
-
hash
|
|
9625
|
+
// For a contract wallet this is the wallet's own id, not an L1 hash — which is
|
|
9626
|
+
// exactly why the confirmation below does not wait on it.
|
|
9627
|
+
hash: tx.hash,
|
|
9628
|
+
senderIsContractWallet,
|
|
9629
|
+
searchFromBlock: senderIsContractWallet ? searchFromBlock : void 0
|
|
9598
9630
|
});
|
|
9599
9631
|
}
|
|
9600
9632
|
const confirmations = await this.createPositionConfirmations();
|
|
9601
|
-
const receipt = await
|
|
9602
|
-
|
|
9603
|
-
|
|
9604
|
-
|
|
9605
|
-
|
|
9606
|
-
|
|
9607
|
-
)
|
|
9608
|
-
)
|
|
9609
|
-
]);
|
|
9633
|
+
const receipt = senderIsContractWallet ? await this.awaitPositionCreatedByPkpId(
|
|
9634
|
+
positionManager,
|
|
9635
|
+
pkpIdBytes32,
|
|
9636
|
+
searchFromBlock,
|
|
9637
|
+
confirmations
|
|
9638
|
+
) : await this.awaitReceiptByHash(tx, confirmations);
|
|
9610
9639
|
if (!receipt)
|
|
9611
9640
|
throw new Error("Transaction was not mined (receipt is null)");
|
|
9612
9641
|
if (this.config.debug && receipt.logs) {
|
|
@@ -9740,6 +9769,81 @@ var LoanCreator = class {
|
|
|
9740
9769
|
}
|
|
9741
9770
|
return 3;
|
|
9742
9771
|
}
|
|
9772
|
+
/**
|
|
9773
|
+
* EOA path: the returned hash is the L1 hash, so `tx.wait` is exact.
|
|
9774
|
+
*
|
|
9775
|
+
* The timer is cleared on settle — an uncleared one keeps the process alive for the
|
|
9776
|
+
* full timeout after a fast confirmation, which in Node holds the event loop open.
|
|
9777
|
+
*/
|
|
9778
|
+
async awaitReceiptByHash(tx, confirmations) {
|
|
9779
|
+
let timer;
|
|
9780
|
+
try {
|
|
9781
|
+
return await Promise.race([
|
|
9782
|
+
tx.wait(confirmations),
|
|
9783
|
+
new Promise((_, reject) => {
|
|
9784
|
+
timer = setTimeout(
|
|
9785
|
+
() => reject(new Error("Transaction confirmation timeout")),
|
|
9786
|
+
this.transactionTimeoutMs
|
|
9787
|
+
);
|
|
9788
|
+
})
|
|
9789
|
+
]);
|
|
9790
|
+
} finally {
|
|
9791
|
+
if (timer)
|
|
9792
|
+
clearTimeout(timer);
|
|
9793
|
+
}
|
|
9794
|
+
}
|
|
9795
|
+
/**
|
|
9796
|
+
* Contract-wallet path: find this position by its `PositionCreated` log.
|
|
9797
|
+
*
|
|
9798
|
+
* `pkpId` is minted fresh per attempt and is the event's second indexed argument, so a
|
|
9799
|
+
* topic filter on it identifies exactly this position without reference to any transaction
|
|
9800
|
+
* hash. The filter deliberately leaves topic0 unconstrained: two `PositionCreated` ABIs are
|
|
9801
|
+
* in circulation (see `extractPositionId`) and matching on the freshly-minted pkpId alone,
|
|
9802
|
+
* scoped to this contract and to blocks at or after the send, is already unambiguous.
|
|
9803
|
+
*
|
|
9804
|
+
* The same confirmation depth as the EOA path is applied to the log's block, for the reason
|
|
9805
|
+
* given at the call site: `positionId` does not survive a reorg unchanged.
|
|
9806
|
+
*/
|
|
9807
|
+
async awaitPositionCreatedByPkpId(positionManager, pkpIdBytes32, fromBlock, confirmations) {
|
|
9808
|
+
const provider = this.config.contractManager.getProvider();
|
|
9809
|
+
const contractAddress = await positionManager.getAddress();
|
|
9810
|
+
const deadline = Date.now() + this.transactionTimeoutMs;
|
|
9811
|
+
while (Date.now() < deadline) {
|
|
9812
|
+
const logs = await provider.getLogs({
|
|
9813
|
+
address: contractAddress,
|
|
9814
|
+
topics: [null, null, pkpIdBytes32],
|
|
9815
|
+
fromBlock,
|
|
9816
|
+
toBlock: "latest"
|
|
9817
|
+
});
|
|
9818
|
+
if (logs.length > 1) {
|
|
9819
|
+
throw new Error(
|
|
9820
|
+
`Expected at most one PositionCreated log for pkpId ${pkpIdBytes32}, found ${logs.length}`
|
|
9821
|
+
);
|
|
9822
|
+
}
|
|
9823
|
+
const positionLog = logs[0];
|
|
9824
|
+
if (positionLog) {
|
|
9825
|
+
const head = await provider.getBlockNumber();
|
|
9826
|
+
const depth = head - positionLog.blockNumber + 1;
|
|
9827
|
+
if (depth >= confirmations) {
|
|
9828
|
+
const receipt = await provider.getTransactionReceipt(
|
|
9829
|
+
positionLog.transactionHash
|
|
9830
|
+
);
|
|
9831
|
+
if (!receipt) {
|
|
9832
|
+
throw new Error(
|
|
9833
|
+
`PositionCreated log for pkpId ${pkpIdBytes32} has no receipt at ${positionLog.transactionHash} \u2014 the block was reorged out mid-confirmation.`
|
|
9834
|
+
);
|
|
9835
|
+
}
|
|
9836
|
+
return receipt;
|
|
9837
|
+
}
|
|
9838
|
+
}
|
|
9839
|
+
await new Promise(
|
|
9840
|
+
(resolve) => setTimeout(resolve, CONTRACT_WALLET_POLL_INTERVAL_MS)
|
|
9841
|
+
);
|
|
9842
|
+
}
|
|
9843
|
+
throw new Error(
|
|
9844
|
+
`Timed out after ${this.transactionTimeoutMs}ms waiting for the PositionCreated log for pkpId ${pkpIdBytes32}. The wallet may not have executed the transaction yet; the position must be rediscovered by pkpId before it is used.`
|
|
9845
|
+
);
|
|
9846
|
+
}
|
|
9743
9847
|
/**
|
|
9744
9848
|
* Re-read the receipt after confirmations and re-derive `positionId`, so a reorg
|
|
9745
9849
|
* cannot leave the caller holding an id that never survived to the canonical chain.
|
|
@@ -18772,7 +18876,8 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
18772
18876
|
litNetwork: config.litNetwork,
|
|
18773
18877
|
loanCreationValidatorVersion: config.validators?.loanCreation ?? 1,
|
|
18774
18878
|
ethRpcUrl: config.ethRpcUrl,
|
|
18775
|
-
temperSignaturesTest: config.temperSignaturesTest
|
|
18879
|
+
temperSignaturesTest: config.temperSignaturesTest,
|
|
18880
|
+
onPkpCreated: config.onPkpCreated
|
|
18776
18881
|
});
|
|
18777
18882
|
if (!loanCreatorResult.success) {
|
|
18778
18883
|
throw new Error(
|
package/dist/index.mjs
CHANGED
|
@@ -9099,6 +9099,23 @@ function createPKPManager(config) {
|
|
|
9099
9099
|
// src/modules/loan/loan-creator.module.ts
|
|
9100
9100
|
import { Contract as Contract3, Interface as Interface2, zeroPadValue as zeroPadValue4, toBeHex, SigningKey } from "ethers";
|
|
9101
9101
|
var import_dh_lit_actions = __toESM(require_pkg_src());
|
|
9102
|
+
|
|
9103
|
+
// src/utils/contract-wallet.utils.ts
|
|
9104
|
+
var contractWalletCache = /* @__PURE__ */ new Map();
|
|
9105
|
+
async function isContractWallet(provider, address) {
|
|
9106
|
+
const { chainId } = await provider.getNetwork();
|
|
9107
|
+
const cacheKey = `${chainId}:${address.toLowerCase()}`;
|
|
9108
|
+
const cached = contractWalletCache.get(cacheKey);
|
|
9109
|
+
if (cached !== void 0)
|
|
9110
|
+
return cached;
|
|
9111
|
+
const code = await provider.getCode(address);
|
|
9112
|
+
const isContract = code !== "0x";
|
|
9113
|
+
contractWalletCache.set(cacheKey, isContract);
|
|
9114
|
+
return isContract;
|
|
9115
|
+
}
|
|
9116
|
+
|
|
9117
|
+
// src/modules/loan/loan-creator.module.ts
|
|
9118
|
+
var CONTRACT_WALLET_POLL_INTERVAL_MS = 4e3;
|
|
9102
9119
|
var LoanCreator = class {
|
|
9103
9120
|
config;
|
|
9104
9121
|
transactionTimeoutMs;
|
|
@@ -9322,6 +9339,11 @@ var LoanCreator = class {
|
|
|
9322
9339
|
validatorSignature: validatorSignature.substring(0, 20) + "..."
|
|
9323
9340
|
});
|
|
9324
9341
|
}
|
|
9342
|
+
this.config.onPkpCreated?.({
|
|
9343
|
+
tokenId: pkpData.tokenId,
|
|
9344
|
+
ethAddress: pkpData.ethAddress,
|
|
9345
|
+
publicKey: pkpData.publicKey
|
|
9346
|
+
});
|
|
9325
9347
|
const addressesResult = await this.deriveBitcoinAddresses(
|
|
9326
9348
|
pkpData.publicKey
|
|
9327
9349
|
);
|
|
@@ -9494,6 +9516,12 @@ var LoanCreator = class {
|
|
|
9494
9516
|
error: encErr instanceof Error ? encErr.message : String(encErr)
|
|
9495
9517
|
});
|
|
9496
9518
|
}
|
|
9519
|
+
const provider = this.config.contractManager.getProvider();
|
|
9520
|
+
const senderIsContractWallet = await isContractWallet(
|
|
9521
|
+
provider,
|
|
9522
|
+
await signerResult.value.getAddress()
|
|
9523
|
+
);
|
|
9524
|
+
const searchFromBlock = senderIsContractWallet ? await provider.getBlockNumber() : 0;
|
|
9497
9525
|
const tx = await positionManager.createPosition(
|
|
9498
9526
|
pkpIdBytes32,
|
|
9499
9527
|
validatorSignature,
|
|
@@ -9515,19 +9543,20 @@ var LoanCreator = class {
|
|
|
9515
9543
|
}
|
|
9516
9544
|
if (this.config.debug) {
|
|
9517
9545
|
log.info("\u23F3 Waiting for transaction confirmation", {
|
|
9518
|
-
hash
|
|
9546
|
+
// For a contract wallet this is the wallet's own id, not an L1 hash — which is
|
|
9547
|
+
// exactly why the confirmation below does not wait on it.
|
|
9548
|
+
hash: tx.hash,
|
|
9549
|
+
senderIsContractWallet,
|
|
9550
|
+
searchFromBlock: senderIsContractWallet ? searchFromBlock : void 0
|
|
9519
9551
|
});
|
|
9520
9552
|
}
|
|
9521
9553
|
const confirmations = await this.createPositionConfirmations();
|
|
9522
|
-
const receipt = await
|
|
9523
|
-
|
|
9524
|
-
|
|
9525
|
-
|
|
9526
|
-
|
|
9527
|
-
|
|
9528
|
-
)
|
|
9529
|
-
)
|
|
9530
|
-
]);
|
|
9554
|
+
const receipt = senderIsContractWallet ? await this.awaitPositionCreatedByPkpId(
|
|
9555
|
+
positionManager,
|
|
9556
|
+
pkpIdBytes32,
|
|
9557
|
+
searchFromBlock,
|
|
9558
|
+
confirmations
|
|
9559
|
+
) : await this.awaitReceiptByHash(tx, confirmations);
|
|
9531
9560
|
if (!receipt)
|
|
9532
9561
|
throw new Error("Transaction was not mined (receipt is null)");
|
|
9533
9562
|
if (this.config.debug && receipt.logs) {
|
|
@@ -9661,6 +9690,81 @@ var LoanCreator = class {
|
|
|
9661
9690
|
}
|
|
9662
9691
|
return 3;
|
|
9663
9692
|
}
|
|
9693
|
+
/**
|
|
9694
|
+
* EOA path: the returned hash is the L1 hash, so `tx.wait` is exact.
|
|
9695
|
+
*
|
|
9696
|
+
* The timer is cleared on settle — an uncleared one keeps the process alive for the
|
|
9697
|
+
* full timeout after a fast confirmation, which in Node holds the event loop open.
|
|
9698
|
+
*/
|
|
9699
|
+
async awaitReceiptByHash(tx, confirmations) {
|
|
9700
|
+
let timer;
|
|
9701
|
+
try {
|
|
9702
|
+
return await Promise.race([
|
|
9703
|
+
tx.wait(confirmations),
|
|
9704
|
+
new Promise((_, reject) => {
|
|
9705
|
+
timer = setTimeout(
|
|
9706
|
+
() => reject(new Error("Transaction confirmation timeout")),
|
|
9707
|
+
this.transactionTimeoutMs
|
|
9708
|
+
);
|
|
9709
|
+
})
|
|
9710
|
+
]);
|
|
9711
|
+
} finally {
|
|
9712
|
+
if (timer)
|
|
9713
|
+
clearTimeout(timer);
|
|
9714
|
+
}
|
|
9715
|
+
}
|
|
9716
|
+
/**
|
|
9717
|
+
* Contract-wallet path: find this position by its `PositionCreated` log.
|
|
9718
|
+
*
|
|
9719
|
+
* `pkpId` is minted fresh per attempt and is the event's second indexed argument, so a
|
|
9720
|
+
* topic filter on it identifies exactly this position without reference to any transaction
|
|
9721
|
+
* hash. The filter deliberately leaves topic0 unconstrained: two `PositionCreated` ABIs are
|
|
9722
|
+
* in circulation (see `extractPositionId`) and matching on the freshly-minted pkpId alone,
|
|
9723
|
+
* scoped to this contract and to blocks at or after the send, is already unambiguous.
|
|
9724
|
+
*
|
|
9725
|
+
* The same confirmation depth as the EOA path is applied to the log's block, for the reason
|
|
9726
|
+
* given at the call site: `positionId` does not survive a reorg unchanged.
|
|
9727
|
+
*/
|
|
9728
|
+
async awaitPositionCreatedByPkpId(positionManager, pkpIdBytes32, fromBlock, confirmations) {
|
|
9729
|
+
const provider = this.config.contractManager.getProvider();
|
|
9730
|
+
const contractAddress = await positionManager.getAddress();
|
|
9731
|
+
const deadline = Date.now() + this.transactionTimeoutMs;
|
|
9732
|
+
while (Date.now() < deadline) {
|
|
9733
|
+
const logs = await provider.getLogs({
|
|
9734
|
+
address: contractAddress,
|
|
9735
|
+
topics: [null, null, pkpIdBytes32],
|
|
9736
|
+
fromBlock,
|
|
9737
|
+
toBlock: "latest"
|
|
9738
|
+
});
|
|
9739
|
+
if (logs.length > 1) {
|
|
9740
|
+
throw new Error(
|
|
9741
|
+
`Expected at most one PositionCreated log for pkpId ${pkpIdBytes32}, found ${logs.length}`
|
|
9742
|
+
);
|
|
9743
|
+
}
|
|
9744
|
+
const positionLog = logs[0];
|
|
9745
|
+
if (positionLog) {
|
|
9746
|
+
const head = await provider.getBlockNumber();
|
|
9747
|
+
const depth = head - positionLog.blockNumber + 1;
|
|
9748
|
+
if (depth >= confirmations) {
|
|
9749
|
+
const receipt = await provider.getTransactionReceipt(
|
|
9750
|
+
positionLog.transactionHash
|
|
9751
|
+
);
|
|
9752
|
+
if (!receipt) {
|
|
9753
|
+
throw new Error(
|
|
9754
|
+
`PositionCreated log for pkpId ${pkpIdBytes32} has no receipt at ${positionLog.transactionHash} \u2014 the block was reorged out mid-confirmation.`
|
|
9755
|
+
);
|
|
9756
|
+
}
|
|
9757
|
+
return receipt;
|
|
9758
|
+
}
|
|
9759
|
+
}
|
|
9760
|
+
await new Promise(
|
|
9761
|
+
(resolve) => setTimeout(resolve, CONTRACT_WALLET_POLL_INTERVAL_MS)
|
|
9762
|
+
);
|
|
9763
|
+
}
|
|
9764
|
+
throw new Error(
|
|
9765
|
+
`Timed out after ${this.transactionTimeoutMs}ms waiting for the PositionCreated log for pkpId ${pkpIdBytes32}. The wallet may not have executed the transaction yet; the position must be rediscovered by pkpId before it is used.`
|
|
9766
|
+
);
|
|
9767
|
+
}
|
|
9664
9768
|
/**
|
|
9665
9769
|
* Re-read the receipt after confirmations and re-derive `positionId`, so a reorg
|
|
9666
9770
|
* cannot leave the caller holding an id that never survived to the canonical chain.
|
|
@@ -18701,7 +18805,8 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
18701
18805
|
litNetwork: config.litNetwork,
|
|
18702
18806
|
loanCreationValidatorVersion: config.validators?.loanCreation ?? 1,
|
|
18703
18807
|
ethRpcUrl: config.ethRpcUrl,
|
|
18704
|
-
temperSignaturesTest: config.temperSignaturesTest
|
|
18808
|
+
temperSignaturesTest: config.temperSignaturesTest,
|
|
18809
|
+
onPkpCreated: config.onPkpCreated
|
|
18705
18810
|
});
|
|
18706
18811
|
if (!loanCreatorResult.success) {
|
|
18707
18812
|
throw new Error(
|
|
@@ -102,6 +102,24 @@ interface BaseSDKConfig {
|
|
|
102
102
|
* contract wallet can leave that request open for minutes.
|
|
103
103
|
*/
|
|
104
104
|
onSessionSignatureResolved?: (ok: boolean) => void;
|
|
105
|
+
/**
|
|
106
|
+
* Fires during loan creation the moment the PKP has been minted and validated,
|
|
107
|
+
* before the `createPosition` transaction is submitted.
|
|
108
|
+
*
|
|
109
|
+
* The PKP is minted fresh per attempt, so `tokenId` is a unique, send-independent
|
|
110
|
+
* handle on the position that transaction is about to create — it is the only
|
|
111
|
+
* identifier a caller can hold *before* a hash exists. UIs use it to recognise
|
|
112
|
+
* their own position in an indexed feed without depending on the transaction
|
|
113
|
+
* hash, which a contract wallet does not return in a usable form.
|
|
114
|
+
*
|
|
115
|
+
* Note `tokenId` is a decimal string; the on-chain and subgraph `pkpId` is the
|
|
116
|
+
* same value as 32-byte hex. Normalise before comparing.
|
|
117
|
+
*/
|
|
118
|
+
onPkpCreated?: (pkp: {
|
|
119
|
+
tokenId: string;
|
|
120
|
+
ethAddress: string;
|
|
121
|
+
publicKey: string;
|
|
122
|
+
}) => void;
|
|
105
123
|
ethRpcUrl?: string;
|
|
106
124
|
chainId?: number;
|
|
107
125
|
networkOverride?: {
|
|
@@ -61,6 +61,15 @@ export interface LoanCreatorConfig {
|
|
|
61
61
|
ethRpcUrl?: string;
|
|
62
62
|
/** Security testing: Temper signatures to test contract validation */
|
|
63
63
|
temperSignaturesTest?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Fires once the PKP is minted and validated, before `createPosition` is submitted.
|
|
66
|
+
* See `SDKConfig.onPkpCreated` for why this exists and for the tokenId encoding caveat.
|
|
67
|
+
*/
|
|
68
|
+
onPkpCreated?: (pkp: {
|
|
69
|
+
tokenId: string;
|
|
70
|
+
ethAddress: string;
|
|
71
|
+
publicKey: string;
|
|
72
|
+
}) => void;
|
|
64
73
|
}
|
|
65
74
|
/**
|
|
66
75
|
* Loan creation audit trail
|
|
@@ -133,6 +142,26 @@ export declare class LoanCreator {
|
|
|
133
142
|
* Override with `createPositionConfirmations` when a chain needs more.
|
|
134
143
|
*/
|
|
135
144
|
private createPositionConfirmations;
|
|
145
|
+
/**
|
|
146
|
+
* EOA path: the returned hash is the L1 hash, so `tx.wait` is exact.
|
|
147
|
+
*
|
|
148
|
+
* The timer is cleared on settle — an uncleared one keeps the process alive for the
|
|
149
|
+
* full timeout after a fast confirmation, which in Node holds the event loop open.
|
|
150
|
+
*/
|
|
151
|
+
private awaitReceiptByHash;
|
|
152
|
+
/**
|
|
153
|
+
* Contract-wallet path: find this position by its `PositionCreated` log.
|
|
154
|
+
*
|
|
155
|
+
* `pkpId` is minted fresh per attempt and is the event's second indexed argument, so a
|
|
156
|
+
* topic filter on it identifies exactly this position without reference to any transaction
|
|
157
|
+
* hash. The filter deliberately leaves topic0 unconstrained: two `PositionCreated` ABIs are
|
|
158
|
+
* in circulation (see `extractPositionId`) and matching on the freshly-minted pkpId alone,
|
|
159
|
+
* scoped to this contract and to blocks at or after the send, is already unambiguous.
|
|
160
|
+
*
|
|
161
|
+
* The same confirmation depth as the EOA path is applied to the log's block, for the reason
|
|
162
|
+
* given at the call site: `positionId` does not survive a reorg unchanged.
|
|
163
|
+
*/
|
|
164
|
+
private awaitPositionCreatedByPkpId;
|
|
136
165
|
/**
|
|
137
166
|
* Re-read the receipt after confirmations and re-derive `positionId`, so a reorg
|
|
138
167
|
* cannot leave the caller holding an id that never survived to the canonical chain.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract-wallet detection.
|
|
3
|
+
*
|
|
4
|
+
* A Safe (or any smart-account) returns its OWN transaction identifier from
|
|
5
|
+
* `eth_sendTransaction` — the safeTxHash — not an L1 transaction hash. Anything that
|
|
6
|
+
* waits on that identifier waits forever, because it never lands on chain. Callers
|
|
7
|
+
* that need to confirm a transaction must therefore know which kind of sender they
|
|
8
|
+
* are dealing with and confirm by some other means.
|
|
9
|
+
*
|
|
10
|
+
* Contract-ness is settled by asking the chain for code at the address, which is the
|
|
11
|
+
* same test the servers use for the EIP-1271 path — not the wallet name, which is
|
|
12
|
+
* whatever the connector chose to call itself.
|
|
13
|
+
*/
|
|
14
|
+
import type { Provider } from "ethers";
|
|
15
|
+
/**
|
|
16
|
+
* True when `address` has code on the chain behind `provider`.
|
|
17
|
+
*
|
|
18
|
+
* Deliberately propagates provider errors rather than assuming EOA: a failed `getCode`
|
|
19
|
+
* means the provider is unreachable, and guessing "EOA" there would send the caller
|
|
20
|
+
* back into a wait that cannot complete.
|
|
21
|
+
*/
|
|
22
|
+
export declare function isContractWallet(provider: Provider, address: string): Promise<boolean>;
|
|
23
|
+
/** Test seam — clears memoised detections. */
|
|
24
|
+
export declare function resetContractWalletCache(): void;
|
package/package.json
CHANGED