abstractionkit 0.3.0 → 0.3.2
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/CHANGELOG.md +177 -71
- package/README.md +158 -73
- package/dist/index.cjs +848 -39
- package/dist/index.d.cts +231 -60
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +231 -60
- package/dist/index.d.mts.map +1 -1
- package/dist/index.iife.js +848 -39
- package/dist/index.mjs +844 -40
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -793,6 +793,52 @@ async function handlefetchGasPrice(providerRpc, polygonGasStation, gasLevel = Ga
|
|
|
793
793
|
return [maxFeePerGas, maxPriorityFeePerGas];
|
|
794
794
|
}
|
|
795
795
|
//#endregion
|
|
796
|
+
//#region src/signer/negotiate.ts
|
|
797
|
+
/**
|
|
798
|
+
* Pick the best mutually-supported signing scheme for one signer against an
|
|
799
|
+
* account's accepted schemes. Later in the `accepted` array = lower preference;
|
|
800
|
+
* the account ranks by preference.
|
|
801
|
+
*
|
|
802
|
+
* Throws a detailed {@link AbstractionKitError} if no scheme overlaps,
|
|
803
|
+
* citing the signer's address, what the account accepts, and what the
|
|
804
|
+
* signer can do.
|
|
805
|
+
*/
|
|
806
|
+
function pickScheme(signer, accepted, context) {
|
|
807
|
+
const signerCan = [];
|
|
808
|
+
if (typeof signer.signTypedData === "function") signerCan.push("typedData");
|
|
809
|
+
if (typeof signer.signHash === "function") signerCan.push("hash");
|
|
810
|
+
for (const scheme of accepted) if (signerCan.includes(scheme)) return scheme;
|
|
811
|
+
throw new AbstractionKitError("BAD_DATA", buildMismatchMessage({
|
|
812
|
+
accountName: context.accountName,
|
|
813
|
+
signerIndex: context.signerIndex,
|
|
814
|
+
signerAddress: signer.address,
|
|
815
|
+
accepted,
|
|
816
|
+
signerCan
|
|
817
|
+
}));
|
|
818
|
+
}
|
|
819
|
+
function buildMismatchMessage(params) {
|
|
820
|
+
const { accountName, signerIndex, signerAddress, accepted, signerCan } = params;
|
|
821
|
+
const canStr = signerCan.length > 0 ? signerCan.join(", ") : "none";
|
|
822
|
+
return `No compatible signing scheme for signer[${signerIndex}] ${signerAddress}. ${accountName} accepts: [${accepted.join(", ")}]; signer provides: [${canStr}]. ` + (signerCan.length === 0 ? "Signer must implement at least one of `signHash` or `signTypedData`. " : "") + "Hint: `fromViem` / `fromEthersWallet` give both; `fromViemWalletClient` gives only `typedData` (use Safe for JSON-RPC wallets).";
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Invoke a signer for one scheme. Keeps the dispatch in one place so the
|
|
826
|
+
* account-side code stays linear. `typedData` is optional: accounts that
|
|
827
|
+
* only accept the `"hash"` scheme (Simple7702, Calibur) pass just `hash`.
|
|
828
|
+
*
|
|
829
|
+
* `context` is always forwarded to the signer so power-user implementations
|
|
830
|
+
* can inspect the userOp.
|
|
831
|
+
*/
|
|
832
|
+
async function invokeSigner(signer, scheme, payload) {
|
|
833
|
+
if (scheme === "typedData") {
|
|
834
|
+
if (typeof signer.signTypedData !== "function") throw new AbstractionKitError("BAD_DATA", `signer ${signer.address} is missing signTypedData`);
|
|
835
|
+
if (!payload.typedData) throw new AbstractionKitError("BAD_DATA", `scheme "typedData" selected but no typedData payload provided`);
|
|
836
|
+
return signer.signTypedData(payload.typedData, payload.context);
|
|
837
|
+
}
|
|
838
|
+
if (typeof signer.signHash !== "function") throw new AbstractionKitError("BAD_DATA", `signer ${signer.address} is missing signHash`);
|
|
839
|
+
return signer.signHash(payload.hash, payload.context);
|
|
840
|
+
}
|
|
841
|
+
//#endregion
|
|
796
842
|
//#region src/utils7702.ts
|
|
797
843
|
const SET_CODE_TX_TYPE = "0x04";
|
|
798
844
|
/**
|
|
@@ -1103,11 +1149,14 @@ var Bundler = class {
|
|
|
1103
1149
|
state_override_set
|
|
1104
1150
|
]);
|
|
1105
1151
|
const res = jsonRpcResult;
|
|
1106
|
-
|
|
1152
|
+
const gasEstimationResult = {
|
|
1107
1153
|
callGasLimit: BigInt(res.callGasLimit),
|
|
1108
1154
|
preVerificationGas: BigInt(res.preVerificationGas),
|
|
1109
1155
|
verificationGasLimit: BigInt(res.verificationGasLimit)
|
|
1110
1156
|
};
|
|
1157
|
+
if (res.paymasterVerificationGasLimit != null) gasEstimationResult.paymasterVerificationGasLimit = BigInt(res.paymasterVerificationGasLimit);
|
|
1158
|
+
if (res.paymasterPostOpGasLimit != null) gasEstimationResult.paymasterPostOpGasLimit = BigInt(res.paymasterPostOpGasLimit);
|
|
1159
|
+
return gasEstimationResult;
|
|
1111
1160
|
} catch (err) {
|
|
1112
1161
|
throw new AbstractionKitError("BUNDLER_ERROR", "bundler eth_estimateUserOperationGas rpc call failed", { cause: ensureError(err) });
|
|
1113
1162
|
}
|
|
@@ -1567,6 +1616,30 @@ var BaseSimple7702Account = class BaseSimple7702Account extends SmartAccount {
|
|
|
1567
1616
|
return new Wallet(privateKey).signingKey.sign(userOperationHash).serialized;
|
|
1568
1617
|
}
|
|
1569
1618
|
/**
|
|
1619
|
+
* Schemes Simple7702 accepts from a Signer. Only raw-hash ECDSA, since
|
|
1620
|
+
* the delegatee verifies a plain signature over the userOp hash.
|
|
1621
|
+
*/
|
|
1622
|
+
static ACCEPTED_SIGNING_SCHEMES = ["hash"];
|
|
1623
|
+
/**
|
|
1624
|
+
* Sign a UserOperation with an {@link AkSigner}. Signer must implement
|
|
1625
|
+
* `signHash`, since Simple7702 only verifies raw ECDSA over the userOp
|
|
1626
|
+
* hash. JSON-RPC wallets and anything that only provides `signTypedData`
|
|
1627
|
+
* fail offline with a specific error.
|
|
1628
|
+
*/
|
|
1629
|
+
async baseSignUserOperationWithSigner(useroperation, signer, chainId) {
|
|
1630
|
+
return invokeSigner(signer, pickScheme(signer, BaseSimple7702Account.ACCEPTED_SIGNING_SCHEMES, {
|
|
1631
|
+
accountName: "Simple7702 (raw ECDSA over userOpHash)",
|
|
1632
|
+
signerIndex: 0
|
|
1633
|
+
}), {
|
|
1634
|
+
hash: createUserOperationHash(useroperation, this.entrypointAddress, chainId),
|
|
1635
|
+
context: {
|
|
1636
|
+
userOperation: useroperation,
|
|
1637
|
+
chainId,
|
|
1638
|
+
entryPoint: this.entrypointAddress
|
|
1639
|
+
}
|
|
1640
|
+
});
|
|
1641
|
+
}
|
|
1642
|
+
/**
|
|
1570
1643
|
* Submit a signed UserOperation to a bundler for on-chain inclusion.
|
|
1571
1644
|
* @param userOperation - The signed UserOperation to submit
|
|
1572
1645
|
* @param bundlerRpc - Bundler RPC endpoint
|
|
@@ -1677,6 +1750,18 @@ var Simple7702Account = class Simple7702Account extends BaseSimple7702Account {
|
|
|
1677
1750
|
return this.baseSignUserOperation(useroperation, privateKey, chainId);
|
|
1678
1751
|
}
|
|
1679
1752
|
/**
|
|
1753
|
+
* Sign a {@link UserOperationV8} using an {@link ExternalSigner}.
|
|
1754
|
+
* Simple7702 only accepts raw-hash ECDSA; signers without `signHash`
|
|
1755
|
+
* fail offline with an actionable error.
|
|
1756
|
+
*
|
|
1757
|
+
* For signing with a raw private-key string, use the sync
|
|
1758
|
+
* {@link signUserOperation} method, or wrap explicitly with
|
|
1759
|
+
* `fromPrivateKey(pk)`.
|
|
1760
|
+
*/
|
|
1761
|
+
async signUserOperationWithSigner(useroperation, signer, chainId) {
|
|
1762
|
+
return this.baseSignUserOperationWithSigner(useroperation, signer, chainId);
|
|
1763
|
+
}
|
|
1764
|
+
/**
|
|
1680
1765
|
* Send a signed {@link UserOperationV8} to a bundler for on-chain inclusion.
|
|
1681
1766
|
* @param userOperation - The signed UserOperation to submit
|
|
1682
1767
|
* @param bundlerRpc - Bundler RPC endpoint
|
|
@@ -1742,6 +1827,15 @@ var Simple7702AccountV09 = class Simple7702AccountV09 extends BaseSimple7702Acco
|
|
|
1742
1827
|
return this.baseSignUserOperation(useroperation, privateKey, chainId);
|
|
1743
1828
|
}
|
|
1744
1829
|
/**
|
|
1830
|
+
* Sign a {@link UserOperationV9} using an {@link ExternalSigner}.
|
|
1831
|
+
* Simple7702 only accepts raw-hash ECDSA; signers without `signHash`
|
|
1832
|
+
* fail offline with an actionable error. For a raw pk string, use the
|
|
1833
|
+
* sync {@link signUserOperation} method or wrap with `fromPrivateKey`.
|
|
1834
|
+
*/
|
|
1835
|
+
async signUserOperationWithSigner(useroperation, signer, chainId) {
|
|
1836
|
+
return this.baseSignUserOperationWithSigner(useroperation, signer, chainId);
|
|
1837
|
+
}
|
|
1838
|
+
/**
|
|
1745
1839
|
* Send a signed {@link UserOperationV9} to a bundler for on-chain inclusion.
|
|
1746
1840
|
* @param userOperation - The signed UserOperation to submit
|
|
1747
1841
|
* @param bundlerRpc - Bundler RPC endpoint
|
|
@@ -3146,6 +3240,68 @@ var SafeAccount = class SafeAccount extends SmartAccount {
|
|
|
3146
3240
|
});
|
|
3147
3241
|
}
|
|
3148
3242
|
/**
|
|
3243
|
+
* Schemes Safe accepts from a {@link Signer}, in preference order.
|
|
3244
|
+
* `typedData` is preferred because wallets can display structured fields
|
|
3245
|
+
* rather than a hex blob; `hash` is accepted as a fallback for signers
|
|
3246
|
+
* that only support raw ECDSA.
|
|
3247
|
+
*/
|
|
3248
|
+
static ACCEPTED_SIGNING_SCHEMES = ["typedData", "hash"];
|
|
3249
|
+
/**
|
|
3250
|
+
* Sign a UserOperation using one or more {@link Signer}s. This is the
|
|
3251
|
+
* capability-oriented signing path: each signer declares what it can do
|
|
3252
|
+
* (`signHash`, `signTypedData`, both) and the account picks the best
|
|
3253
|
+
* match per signer. Incompatible signers fail offline with an actionable
|
|
3254
|
+
* error rather than a silent bundler rejection.
|
|
3255
|
+
*
|
|
3256
|
+
* Signers are invoked in parallel. For interactive wallets that share a
|
|
3257
|
+
* popup session, sequence the prompts inside your Signer implementation.
|
|
3258
|
+
*
|
|
3259
|
+
* @param useroperation - UserOperation to sign
|
|
3260
|
+
* @param signers - Signer instances (`fromViem(account)`, `fromEthersWallet(wallet)`, etc.)
|
|
3261
|
+
* @param chainId - target chain id
|
|
3262
|
+
* @param entrypointAddress - target EntryPoint
|
|
3263
|
+
* @param safe4337ModuleAddress - Safe 4337 module
|
|
3264
|
+
* @param overrides - optional validAfter / validUntil / multi-chain flag
|
|
3265
|
+
* @returns formatted signature
|
|
3266
|
+
*/
|
|
3267
|
+
static async baseSignUserOperationWithSigners(useroperation, signers, chainId, entrypointAddress, safe4337ModuleAddress, context, overrides = {}) {
|
|
3268
|
+
const validAfter = overrides.validAfter ?? 0n;
|
|
3269
|
+
const validUntil = overrides.validUntil ?? 0n;
|
|
3270
|
+
if (signers.length < 1) throw new RangeError("There should be at least one signer");
|
|
3271
|
+
const typedDataRaw = SafeAccount.getUserOperationEip712Data(useroperation, chainId, {
|
|
3272
|
+
validAfter,
|
|
3273
|
+
validUntil,
|
|
3274
|
+
entrypointAddress,
|
|
3275
|
+
safe4337ModuleAddress
|
|
3276
|
+
});
|
|
3277
|
+
const userOpHash = TypedDataEncoder.hash(typedDataRaw.domain, typedDataRaw.types, typedDataRaw.messageValue);
|
|
3278
|
+
const { EIP712Domain: _drop, ...primaryTypes } = typedDataRaw.types;
|
|
3279
|
+
const typedData = {
|
|
3280
|
+
domain: typedDataRaw.domain,
|
|
3281
|
+
types: primaryTypes,
|
|
3282
|
+
primaryType: EIP712_SAFE_OPERATION_PRIMARY_TYPE,
|
|
3283
|
+
message: typedDataRaw.messageValue
|
|
3284
|
+
};
|
|
3285
|
+
const normalizedAddresses = signers.map((signer) => getAddress(signer.address));
|
|
3286
|
+
const schemes = signers.map((signer, signerIndex) => pickScheme(signer, SafeAccount.ACCEPTED_SIGNING_SCHEMES, {
|
|
3287
|
+
accountName: "Safe (EIP-712 or raw hash over SafeOp digest)",
|
|
3288
|
+
signerIndex
|
|
3289
|
+
}));
|
|
3290
|
+
const signerSignaturePairs = (await Promise.all(signers.map((signer, i) => invokeSigner(signer, schemes[i], {
|
|
3291
|
+
hash: userOpHash,
|
|
3292
|
+
typedData,
|
|
3293
|
+
context
|
|
3294
|
+
})))).map((signature, i) => ({
|
|
3295
|
+
signer: normalizedAddresses[i],
|
|
3296
|
+
signature
|
|
3297
|
+
}));
|
|
3298
|
+
return SafeAccount.formatSignaturesToUseroperationSignature(signerSignaturePairs, {
|
|
3299
|
+
validAfter,
|
|
3300
|
+
validUntil,
|
|
3301
|
+
isMultiChainSignature: overrides.isMultiChainSignature
|
|
3302
|
+
});
|
|
3303
|
+
}
|
|
3304
|
+
/**
|
|
3149
3305
|
* compute the deterministic address for a webauthn proxy verifier based on a
|
|
3150
3306
|
* webauthn public key(x, y)
|
|
3151
3307
|
* @param x - webauthn public key x parameter
|
|
@@ -3792,7 +3948,7 @@ var SafeAccount = class SafeAccount extends SmartAccount {
|
|
|
3792
3948
|
* @param toolVersion - tool version, defaults to current abstractionkit version
|
|
3793
3949
|
* @returns the onchain idenetifier as a hex string (not 0x prefixed)
|
|
3794
3950
|
*/
|
|
3795
|
-
function generateOnChainIdentifier(project, platform = "Web", tool = "abstractionkit", toolVersion = "0.3.
|
|
3951
|
+
function generateOnChainIdentifier(project, platform = "Web", tool = "abstractionkit", toolVersion = "0.3.2") {
|
|
3796
3952
|
const identifierPrefix = "5afe";
|
|
3797
3953
|
const identifierVersion = "00";
|
|
3798
3954
|
const projectHash = keccak256("0x" + Buffer.from(project, "utf8").toString("hex")).slice(-20);
|
|
@@ -3869,6 +4025,17 @@ const DEFAULT_WEB_AUTHN_DAIMO_VERIFIER_V_0_2_1 = "0xc2b78104907F722DABAc4C69f826
|
|
|
3869
4025
|
* with the Daimo P256 verifier instead of the FCL P256 verifier
|
|
3870
4026
|
* used by the base SafeAccount class.
|
|
3871
4027
|
* @see {@link https://github.com/safe-fndn/safe-modules/blob/04e65efbce634e776cc8c1fbe90061f09e09a71b/modules/passkey/CHANGELOG.md?plain=1#L23}
|
|
4028
|
+
*
|
|
4029
|
+
* @remarks Signer typing on this class is asymmetric:
|
|
4030
|
+
* - {@link signUserOperationWithSigners} (singular Operation) signs one op
|
|
4031
|
+
* and uses {@link SignContext} like every other account.
|
|
4032
|
+
* - {@link signUserOperationsWithSigners} (plural Operations) signs a bundle
|
|
4033
|
+
* under one signature and uses {@link MultiOpSignContext}.
|
|
4034
|
+
*
|
|
4035
|
+
* To author one signer that works on both methods, type it as
|
|
4036
|
+
* `ExternalSigner<unknown>` (the shape returned by the built-in adapters).
|
|
4037
|
+
* The two narrow context types exist so signers that DO read the context
|
|
4038
|
+
* get accurate, non-optional fields per path.
|
|
3872
4039
|
*/
|
|
3873
4040
|
var SafeMultiChainSigAccountV1 = class SafeMultiChainSigAccountV1 extends SafeAccount {
|
|
3874
4041
|
static DEFAULT_ENTRYPOINT_ADDRESS = ENTRYPOINT_V9;
|
|
@@ -4087,6 +4254,23 @@ var SafeMultiChainSigAccountV1 = class SafeMultiChainSigAccountV1 extends SafeAc
|
|
|
4087
4254
|
});
|
|
4088
4255
|
}
|
|
4089
4256
|
/**
|
|
4257
|
+
* Sign a single UserOperation for multi-chain using one or more
|
|
4258
|
+
* {@link AkSigner} instances. See
|
|
4259
|
+
* {@link SafeAccountV0_3_0.signUserOperationWithSigners} for the full
|
|
4260
|
+
* design rationale. Sets the multi-chain flag automatically.
|
|
4261
|
+
*/
|
|
4262
|
+
signUserOperationWithSigners(userOperation, signers, chainId, overrides = {}) {
|
|
4263
|
+
const context = {
|
|
4264
|
+
userOperation,
|
|
4265
|
+
chainId,
|
|
4266
|
+
entryPoint: this.entrypointAddress
|
|
4267
|
+
};
|
|
4268
|
+
return SafeAccount.baseSignUserOperationWithSigners(userOperation, signers, chainId, this.entrypointAddress, this.safe4337ModuleAddress, context, {
|
|
4269
|
+
...overrides,
|
|
4270
|
+
isMultiChainSignature: true
|
|
4271
|
+
});
|
|
4272
|
+
}
|
|
4273
|
+
/**
|
|
4090
4274
|
* sign a list of useroperations - multi chain signature
|
|
4091
4275
|
* @param useroperation - useroperation to sign
|
|
4092
4276
|
* @param privateKeys - for the signers
|
|
@@ -4137,6 +4321,86 @@ var SafeMultiChainSigAccountV1 = class SafeMultiChainSigAccountV1 extends SafeAc
|
|
|
4137
4321
|
})];
|
|
4138
4322
|
}
|
|
4139
4323
|
/**
|
|
4324
|
+
* Sign a list of UserOperations with a single multi-chain signature,
|
|
4325
|
+
* using {@link AkSigner} instances typed for {@link MultiOpSignContext}
|
|
4326
|
+
* (viem, ethers, hardware wallet, HSM, MPC, Uint8Array-only). Each
|
|
4327
|
+
* signer signs the Merkle root of the UserOperation EIP-712 hashes via
|
|
4328
|
+
* raw-hash signing. `signTypedData` isn't exposed here because the
|
|
4329
|
+
* Merkle root is opaque and has no meaningful typed-data display.
|
|
4330
|
+
*
|
|
4331
|
+
* Signers always receive {@link MultiOpSignContext} regardless of bundle
|
|
4332
|
+
* length, so multi-op-typed signers can rely on `ctx.userOperations`
|
|
4333
|
+
* being defined. Pre-built adapters `fromPrivateKey`, `fromViem`, and
|
|
4334
|
+
* `fromEthersWallet` return a universal `Signer<unknown>` and work
|
|
4335
|
+
* here without retyping; user-defined single-op signers
|
|
4336
|
+
* (`Signer<SignContext>`) do not — they would receive a context shape
|
|
4337
|
+
* they didn't declare. `fromViemWalletClient` is **not** usable on the
|
|
4338
|
+
* multi-op Merkle path: it only exposes `signTypedData`, and the
|
|
4339
|
+
* Merkle root has no meaningful typed-data display. {@link pickScheme}
|
|
4340
|
+
* rejects it offline with an actionable error.
|
|
4341
|
+
*
|
|
4342
|
+
* @param userOperationsToSign - UserOperations + chain IDs + validity windows
|
|
4343
|
+
* @param signers - one Signer per owner (any order; sorted by address on-chain)
|
|
4344
|
+
* @returns one signature per input UserOperation, in the same order
|
|
4345
|
+
*/
|
|
4346
|
+
async signUserOperationsWithSigners(userOperationsToSign, signers) {
|
|
4347
|
+
if (userOperationsToSign.length < 1) throw new RangeError("There should be at least one userOperationsToSign");
|
|
4348
|
+
if (signers.length < 1) throw new RangeError("There should be at least one signer");
|
|
4349
|
+
const context = {
|
|
4350
|
+
userOperations: userOperationsToSign.map((u) => ({
|
|
4351
|
+
userOperation: u.userOperation,
|
|
4352
|
+
chainId: u.chainId
|
|
4353
|
+
})),
|
|
4354
|
+
entryPoint: this.entrypointAddress
|
|
4355
|
+
};
|
|
4356
|
+
if (userOperationsToSign.length > 1) {
|
|
4357
|
+
const userOperationsHashes = [];
|
|
4358
|
+
userOperationsToSign.forEach((uopToSign) => {
|
|
4359
|
+
const userOperationHash = SafeAccount.getUserOperationEip712Hash_V9(uopToSign.userOperation, uopToSign.chainId, {
|
|
4360
|
+
validAfter: uopToSign.validAfter,
|
|
4361
|
+
validUntil: uopToSign.validUntil,
|
|
4362
|
+
safe4337ModuleAddress: this.safe4337ModuleAddress,
|
|
4363
|
+
entrypointAddress: this.entrypointAddress
|
|
4364
|
+
});
|
|
4365
|
+
userOperationsHashes.push(userOperationHash);
|
|
4366
|
+
});
|
|
4367
|
+
const [root, proofs] = generateMerkleProofs(userOperationsHashes);
|
|
4368
|
+
const merkleTreeRootHash = TypedDataEncoder.hash({ verifyingContract: this.safe4337ModuleAddress }, EIP712_MULTI_CHAIN_OPERATIONS_TYPE, { merkleTreeRoot: root });
|
|
4369
|
+
const normalizedAddresses = signers.map((signer) => getAddress(signer.address));
|
|
4370
|
+
signers.forEach((signer, i) => {
|
|
4371
|
+
pickScheme(signer, ["hash"], {
|
|
4372
|
+
accountName: "SafeMultiChainSigAccountV1 (multi-op Merkle root)",
|
|
4373
|
+
signerIndex: i
|
|
4374
|
+
});
|
|
4375
|
+
});
|
|
4376
|
+
const signatures = await Promise.all(signers.map((signer) => invokeSigner(signer, "hash", {
|
|
4377
|
+
hash: merkleTreeRootHash,
|
|
4378
|
+
context
|
|
4379
|
+
})));
|
|
4380
|
+
const signerSignaturePairs = signers.map((_signer, i) => ({
|
|
4381
|
+
signer: normalizedAddresses[i],
|
|
4382
|
+
signature: signatures[i]
|
|
4383
|
+
}));
|
|
4384
|
+
const userOpSignatures = [];
|
|
4385
|
+
userOperationsToSign.forEach((uopToSign, index) => {
|
|
4386
|
+
userOpSignatures.push(SafeAccount.formatSignaturesToUseroperationSignature(signerSignaturePairs, {
|
|
4387
|
+
validAfter: uopToSign.validAfter,
|
|
4388
|
+
validUntil: uopToSign.validUntil,
|
|
4389
|
+
isMultiChainSignature: true,
|
|
4390
|
+
multiChainMerkleProof: proofs[index]
|
|
4391
|
+
}));
|
|
4392
|
+
});
|
|
4393
|
+
return userOpSignatures;
|
|
4394
|
+
} else {
|
|
4395
|
+
const u = userOperationsToSign[0];
|
|
4396
|
+
return [await SafeAccount.baseSignUserOperationWithSigners(u.userOperation, signers, u.chainId, this.entrypointAddress, this.safe4337ModuleAddress, context, {
|
|
4397
|
+
validAfter: u.validAfter,
|
|
4398
|
+
validUntil: u.validUntil,
|
|
4399
|
+
isMultiChainSignature: true
|
|
4400
|
+
})];
|
|
4401
|
+
}
|
|
4402
|
+
}
|
|
4403
|
+
/**
|
|
4140
4404
|
* Compute the EIP-712 hash of a multi-chain Merkle tree root for a set of UserOperations.
|
|
4141
4405
|
* This hash is what signers sign to approve multiple cross-chain operations at once.
|
|
4142
4406
|
* @param userOperationsToSignsToSign - list of UserOperations with their target chain IDs
|
|
@@ -4180,36 +4444,39 @@ var SafeMultiChainSigAccountV1 = class SafeMultiChainSigAccountV1 extends SafeAc
|
|
|
4180
4444
|
* @param overrides - overrides for the default values
|
|
4181
4445
|
* @returns signature
|
|
4182
4446
|
*/
|
|
4183
|
-
static formatSignaturesToUseroperationsSignatures(userOperationsToSign, signerSignaturePairs
|
|
4447
|
+
static formatSignaturesToUseroperationsSignatures(userOperationsToSign, signerSignaturePairs) {
|
|
4184
4448
|
if (userOperationsToSign.length < 1) throw new RangeError("There should be at least one userOperationsToSign");
|
|
4185
|
-
const
|
|
4449
|
+
const defaultOverrides = {
|
|
4186
4450
|
eip7212WebAuthnPrecompileVerifier: SafeMultiChainSigAccountV1.DEFAULT_WEB_AUTHN_PRECOMPILE,
|
|
4187
4451
|
eip7212WebAuthnContractVerifier: SafeMultiChainSigAccountV1.DEFAULT_WEB_AUTHN_DAIMO_VERIFIER,
|
|
4188
4452
|
webAuthnSignerFactory: SafeMultiChainSigAccountV1.DEFAULT_WEB_AUTHN_SIGNER_FACTORY,
|
|
4189
4453
|
webAuthnSignerSingleton: SafeMultiChainSigAccountV1.DEFAULT_WEB_AUTHN_SIGNER_SINGLETON,
|
|
4190
4454
|
webAuthnSignerProxyCreationCode: SafeMultiChainSigAccountV1.DEFAULT_WEB_AUTHN_SIGNER_PROXY_CREATION_CODE,
|
|
4191
4455
|
safe4337ModuleAddress: SafeMultiChainSigAccountV1.DEFAULT_SAFE_4337_MODULE_ADDRESS,
|
|
4192
|
-
webAuthnSharedSigner: SafeMultiChainSigAccountV1.DEFAULT_WEB_AUTHN_SHARED_SIGNER
|
|
4193
|
-
...overrides
|
|
4456
|
+
webAuthnSharedSigner: SafeMultiChainSigAccountV1.DEFAULT_WEB_AUTHN_SHARED_SIGNER
|
|
4194
4457
|
};
|
|
4195
4458
|
if (userOperationsToSign.length === 1) return [SafeAccount.formatSignaturesToUseroperationSignature(signerSignaturePairs, {
|
|
4196
|
-
...
|
|
4459
|
+
...defaultOverrides,
|
|
4460
|
+
...userOperationsToSign[0].overrides,
|
|
4461
|
+
validAfter: userOperationsToSign[0].validAfter,
|
|
4462
|
+
validUntil: userOperationsToSign[0].validUntil,
|
|
4197
4463
|
isMultiChainSignature: true
|
|
4198
4464
|
})];
|
|
4199
4465
|
const userOperationsHashes = [];
|
|
4200
|
-
userOperationsToSign.forEach((
|
|
4201
|
-
const userOperationHash = SafeAccount.getUserOperationEip712Hash_V9(
|
|
4202
|
-
validAfter:
|
|
4203
|
-
validUntil:
|
|
4204
|
-
safe4337ModuleAddress:
|
|
4466
|
+
userOperationsToSign.forEach((userOperationToSign, _index) => {
|
|
4467
|
+
const userOperationHash = SafeAccount.getUserOperationEip712Hash_V9(userOperationToSign.userOperation, userOperationToSign.chainId, {
|
|
4468
|
+
validAfter: userOperationToSign.validAfter,
|
|
4469
|
+
validUntil: userOperationToSign.validUntil,
|
|
4470
|
+
safe4337ModuleAddress: userOperationToSign.overrides?.safe4337ModuleAddress ?? defaultOverrides.safe4337ModuleAddress
|
|
4205
4471
|
});
|
|
4206
4472
|
userOperationsHashes.push(userOperationHash);
|
|
4207
4473
|
});
|
|
4208
4474
|
const [_root, proofs] = generateMerkleProofs(userOperationsHashes);
|
|
4209
4475
|
const userOpSignatures = [];
|
|
4210
|
-
userOperationsToSign.forEach((
|
|
4476
|
+
userOperationsToSign.forEach((userOperationToSign, index) => {
|
|
4211
4477
|
userOpSignatures.push(SafeAccount.formatSignaturesToUseroperationSignature(signerSignaturePairs, {
|
|
4212
|
-
...
|
|
4478
|
+
...defaultOverrides,
|
|
4479
|
+
...userOperationToSign.overrides,
|
|
4213
4480
|
isMultiChainSignature: true,
|
|
4214
4481
|
multiChainMerkleProof: proofs[index]
|
|
4215
4482
|
}));
|
|
@@ -4610,34 +4877,42 @@ var Calibur7702Account = class Calibur7702Account extends SmartAccount {
|
|
|
4610
4877
|
return Calibur7702Account.wrapSignature(keyHash, ecdsaSig, hookData);
|
|
4611
4878
|
}
|
|
4612
4879
|
/**
|
|
4613
|
-
*
|
|
4614
|
-
*
|
|
4615
|
-
*
|
|
4616
|
-
|
|
4880
|
+
* Schemes Calibur accepts from a Signer. Only raw-hash ECDSA, since
|
|
4881
|
+
* the account verifies a plain signature over the userOp hash, then
|
|
4882
|
+
* wraps with `(keyHash, signature, hookData)`.
|
|
4883
|
+
*/
|
|
4884
|
+
static ACCEPTED_SIGNING_SCHEMES = ["hash"];
|
|
4885
|
+
/**
|
|
4886
|
+
* Sign a UserOperation using an {@link ExternalSigner}. Calibur only
|
|
4887
|
+
* accepts raw-hash ECDSA; signers without `signHash` fail offline with
|
|
4888
|
+
* an actionable error.
|
|
4617
4889
|
*
|
|
4618
|
-
*
|
|
4619
|
-
*
|
|
4620
|
-
*
|
|
4621
|
-
*
|
|
4622
|
-
* @param signer - Async signing function: `(hash: string) => Promise<string>`
|
|
4623
|
-
* @param chainId - Target chain ID
|
|
4624
|
-
* @param overrides - Optional overrides (keyHash for secondary keys, hookData)
|
|
4625
|
-
* @returns Promise resolving to the hex-encoded wrapped signature
|
|
4890
|
+
* For signing with a raw private-key string, use the sync
|
|
4891
|
+
* {@link signUserOperation} method, or wrap explicitly with
|
|
4892
|
+
* `fromPrivateKey(pk)`. For secondary (non-root) keys, pass the key
|
|
4893
|
+
* hash via `overrides.keyHash`.
|
|
4626
4894
|
*
|
|
4627
4895
|
* @example
|
|
4628
|
-
*
|
|
4896
|
+
* import { fromViem, fromEthersWallet } from "abstractionkit";
|
|
4629
4897
|
* userOp.signature = await account.signUserOperationWithSigner(
|
|
4630
|
-
* userOp,
|
|
4631
|
-
* (hash) => walletClient.signMessage({ message: { raw: hash } }),
|
|
4632
|
-
* chainId,
|
|
4898
|
+
* userOp, fromViem(privateKeyToAccount(pk)), chainId,
|
|
4633
4899
|
* );
|
|
4634
4900
|
*/
|
|
4635
4901
|
async signUserOperationWithSigner(userOperation, signer, chainId, overrides = {}) {
|
|
4636
|
-
const
|
|
4902
|
+
const signature = await invokeSigner(signer, pickScheme(signer, Calibur7702Account.ACCEPTED_SIGNING_SCHEMES, {
|
|
4903
|
+
accountName: "Calibur (raw ECDSA over userOpHash)",
|
|
4904
|
+
signerIndex: 0
|
|
4905
|
+
}), {
|
|
4906
|
+
hash: createUserOperationHash(userOperation, this.entrypointAddress, chainId),
|
|
4907
|
+
context: {
|
|
4908
|
+
userOperation,
|
|
4909
|
+
chainId,
|
|
4910
|
+
entryPoint: this.entrypointAddress
|
|
4911
|
+
}
|
|
4912
|
+
});
|
|
4637
4913
|
const keyHash = overrides.keyHash ?? ROOT_KEY_HASH;
|
|
4638
4914
|
const hookData = overrides.hookData ?? "0x";
|
|
4639
|
-
|
|
4640
|
-
return Calibur7702Account.wrapSignature(keyHash, ecdsaSig, hookData);
|
|
4915
|
+
return Calibur7702Account.wrapSignature(keyHash, signature, hookData);
|
|
4641
4916
|
}
|
|
4642
4917
|
/**
|
|
4643
4918
|
* Format a WebAuthn (passkey) assertion into Calibur's signature format.
|
|
@@ -5108,6 +5383,88 @@ var Calibur7702Account = class Calibur7702Account extends SmartAccount {
|
|
|
5108
5383
|
}
|
|
5109
5384
|
};
|
|
5110
5385
|
//#endregion
|
|
5386
|
+
//#region src/signer/adapters.ts
|
|
5387
|
+
/**
|
|
5388
|
+
* Build a Signer from a raw private key. Uses the library's existing
|
|
5389
|
+
* ethers dependency internally, so no additional packages are required on
|
|
5390
|
+
* the caller side. Supports both raw-hash and typed-data signing.
|
|
5391
|
+
*
|
|
5392
|
+
* Prefer this when all you have is a private key (test suites, server-side
|
|
5393
|
+
* scripts, scripts with env-injected keys, etc.). If you already hold a
|
|
5394
|
+
* viem Account or ethers Wallet from elsewhere in your app, pass it to
|
|
5395
|
+
* {@link fromViem} or {@link fromEthersWallet} instead.
|
|
5396
|
+
*
|
|
5397
|
+
* @example
|
|
5398
|
+
* import { fromPrivateKey } from "abstractionkit";
|
|
5399
|
+
* const signer = fromPrivateKey(process.env.PRIVATE_KEY!);
|
|
5400
|
+
* userOp.signature = await safe.signUserOperationWithSigners(userOp, [signer], chainId);
|
|
5401
|
+
*/
|
|
5402
|
+
function fromPrivateKey(privateKey) {
|
|
5403
|
+
const wallet = new Wallet(privateKey);
|
|
5404
|
+
return {
|
|
5405
|
+
address: getAddress(wallet.address),
|
|
5406
|
+
signHash: async (hash) => wallet.signingKey.sign(hash).serialized,
|
|
5407
|
+
signTypedData: async (td) => await wallet.signTypedData(td.domain, td.types, td.message)
|
|
5408
|
+
};
|
|
5409
|
+
}
|
|
5410
|
+
/**
|
|
5411
|
+
* Adapt a viem Local Account (e.g. `privateKeyToAccount(pk)`) to a Signer.
|
|
5412
|
+
* Supports both raw-hash and typed-data signing.
|
|
5413
|
+
*
|
|
5414
|
+
* @remarks Requires viem >= 2.0.
|
|
5415
|
+
*/
|
|
5416
|
+
function fromViem(account) {
|
|
5417
|
+
return {
|
|
5418
|
+
address: account.address,
|
|
5419
|
+
signHash: (hash) => account.sign({ hash }),
|
|
5420
|
+
signTypedData: (td) => account.signTypedData({
|
|
5421
|
+
domain: td.domain,
|
|
5422
|
+
types: td.types,
|
|
5423
|
+
primaryType: td.primaryType,
|
|
5424
|
+
message: td.message
|
|
5425
|
+
})
|
|
5426
|
+
};
|
|
5427
|
+
}
|
|
5428
|
+
/**
|
|
5429
|
+
* Adapt a viem WalletClient to a Signer. WalletClient is the client-style
|
|
5430
|
+
* API dApps use to drive browser / JSON-RPC wallets, so only typed-data
|
|
5431
|
+
* signing is exposed (JSON-RPC wallets can't sign raw hashes).
|
|
5432
|
+
*
|
|
5433
|
+
* Requires the client to have been constructed with an `account` (local or
|
|
5434
|
+
* JSON-RPC). For local accounts, pass that directly to `fromViem` instead
|
|
5435
|
+
* if you want raw-hash fallback.
|
|
5436
|
+
*
|
|
5437
|
+
* @remarks Requires viem >= 2.0.
|
|
5438
|
+
*/
|
|
5439
|
+
function fromViemWalletClient(client) {
|
|
5440
|
+
if (!client.account) throw new Error("fromViemWalletClient: client has no `account` configured. Construct with `createWalletClient({ account, transport, chain })`.");
|
|
5441
|
+
const account = client.account;
|
|
5442
|
+
const signTypedData = client.signTypedData;
|
|
5443
|
+
return {
|
|
5444
|
+
address: account.address,
|
|
5445
|
+
signTypedData: (td) => signTypedData({
|
|
5446
|
+
account,
|
|
5447
|
+
domain: td.domain,
|
|
5448
|
+
types: td.types,
|
|
5449
|
+
primaryType: td.primaryType,
|
|
5450
|
+
message: td.message
|
|
5451
|
+
})
|
|
5452
|
+
};
|
|
5453
|
+
}
|
|
5454
|
+
/**
|
|
5455
|
+
* Adapt an ethers `Wallet` / `HDNodeWallet` to a Signer. Supports both
|
|
5456
|
+
* raw-hash and typed-data signing.
|
|
5457
|
+
*
|
|
5458
|
+
* @remarks Requires ethers >= 6.0.
|
|
5459
|
+
*/
|
|
5460
|
+
function fromEthersWallet(wallet) {
|
|
5461
|
+
return {
|
|
5462
|
+
address: wallet.address,
|
|
5463
|
+
signHash: async (hash) => wallet.signingKey.sign(hash).serialized,
|
|
5464
|
+
signTypedData: async (td) => await wallet.signTypedData(td.domain, td.types, td.message)
|
|
5465
|
+
};
|
|
5466
|
+
}
|
|
5467
|
+
//#endregion
|
|
5111
5468
|
//#region src/account/Safe/modules/SafeModule.ts
|
|
5112
5469
|
/**
|
|
5113
5470
|
* Abstract base class for Safe modules. Provides shared utilities for
|
|
@@ -6111,6 +6468,45 @@ var SafeAccountV0_3_0 = class SafeAccountV0_3_0 extends SafeAccount {
|
|
|
6111
6468
|
signUserOperation(useroperation, privateKeys, chainId, overrides = {}) {
|
|
6112
6469
|
return SafeAccount.baseSignSingleUserOperation(useroperation, privateKeys, chainId, this.entrypointAddress, this.safe4337ModuleAddress, overrides);
|
|
6113
6470
|
}
|
|
6471
|
+
/**
|
|
6472
|
+
* Sign a UserOperation using one or more {@link ExternalSigner} instances.
|
|
6473
|
+
* Capability-oriented entry point: each Signer declares what it can do
|
|
6474
|
+
* (`signHash`, `signTypedData`, both) and the account picks the best
|
|
6475
|
+
* match per signer. Incompatible signers fail offline with an actionable
|
|
6476
|
+
* error — no silent bundler rejections.
|
|
6477
|
+
*
|
|
6478
|
+
* This method is for external signers only (viem, ethers, hardware
|
|
6479
|
+
* wallets, MPC, HSMs, Uint8Array-only signers). If you just have a raw
|
|
6480
|
+
* private-key string, use the sync {@link signUserOperation} method
|
|
6481
|
+
* instead, or wrap explicitly with `fromPrivateKey(pk)`.
|
|
6482
|
+
*
|
|
6483
|
+
* Prebuilt adapters: `fromViem`, `fromEthersWallet`,
|
|
6484
|
+
* `fromViemWalletClient`, `fromPrivateKey`. Custom signers just need to
|
|
6485
|
+
* match the `ExternalSigner` shape.
|
|
6486
|
+
*
|
|
6487
|
+
* @example
|
|
6488
|
+
* import { fromViem } from "abstractionkit";
|
|
6489
|
+
* import { privateKeyToAccount } from "viem/accounts";
|
|
6490
|
+
*
|
|
6491
|
+
* const signer = fromViem(privateKeyToAccount(pk));
|
|
6492
|
+
* userOp.signature = await account.signUserOperationWithSigners(
|
|
6493
|
+
* userOp, [signer], chainId,
|
|
6494
|
+
* );
|
|
6495
|
+
*
|
|
6496
|
+
* @param useroperation - UserOperation to sign
|
|
6497
|
+
* @param signers - one ExternalSigner per owner (any order)
|
|
6498
|
+
* @param chainId - target chain ID
|
|
6499
|
+
* @param overrides - optional validAfter / validUntil / multi-chain flag
|
|
6500
|
+
* @returns Promise resolving to the formatted signature string
|
|
6501
|
+
*/
|
|
6502
|
+
signUserOperationWithSigners(useroperation, signers, chainId, overrides = {}) {
|
|
6503
|
+
const context = {
|
|
6504
|
+
userOperation: useroperation,
|
|
6505
|
+
chainId,
|
|
6506
|
+
entryPoint: this.entrypointAddress
|
|
6507
|
+
};
|
|
6508
|
+
return SafeAccount.baseSignUserOperationWithSigners(useroperation, signers, chainId, this.entrypointAddress, this.safe4337ModuleAddress, context, overrides);
|
|
6509
|
+
}
|
|
6114
6510
|
};
|
|
6115
6511
|
//#endregion
|
|
6116
6512
|
//#region src/account/Safe/SafeAccountV0_2_0.ts
|
|
@@ -6353,6 +6749,19 @@ var SafeAccountV0_2_0 = class SafeAccountV0_2_0 extends SafeAccount {
|
|
|
6353
6749
|
signUserOperation(useroperation, privateKeys, chainId, overrides = {}) {
|
|
6354
6750
|
return SafeAccount.baseSignSingleUserOperation(useroperation, privateKeys, chainId, this.entrypointAddress, this.safe4337ModuleAddress, overrides);
|
|
6355
6751
|
}
|
|
6752
|
+
/**
|
|
6753
|
+
* Sign a UserOperation using one or more {@link AkSigner} instances.
|
|
6754
|
+
* See {@link SafeAccountV0_3_0.signUserOperationWithSigners} for full
|
|
6755
|
+
* design rationale and examples.
|
|
6756
|
+
*/
|
|
6757
|
+
signUserOperationWithSigners(useroperation, signers, chainId, overrides = {}) {
|
|
6758
|
+
const context = {
|
|
6759
|
+
userOperation: useroperation,
|
|
6760
|
+
chainId,
|
|
6761
|
+
entryPoint: this.entrypointAddress
|
|
6762
|
+
};
|
|
6763
|
+
return SafeAccount.baseSignUserOperationWithSigners(useroperation, signers, chainId, this.entrypointAddress, this.safe4337ModuleAddress, context, overrides);
|
|
6764
|
+
}
|
|
6356
6765
|
};
|
|
6357
6766
|
//#endregion
|
|
6358
6767
|
//#region src/account/Safe/SafeAccountV1_5_0_M_0_3_0.ts
|
|
@@ -6511,9 +6920,9 @@ var Paymaster = class {};
|
|
|
6511
6920
|
/** Buffer added to verificationGasLimit for paymasterAndData verification overhead */
|
|
6512
6921
|
const PAYMASTER_V06_VERIFICATION_OVERHEAD = 40000n;
|
|
6513
6922
|
/** Max value for uint256 */
|
|
6514
|
-
const UINT256_MAX = 115792089237316195423570985008687907853269984665640564039457584007913129639935n;
|
|
6923
|
+
const UINT256_MAX$1 = 115792089237316195423570985008687907853269984665640564039457584007913129639935n;
|
|
6515
6924
|
/** Multiplier for token approve amount to cover paymasterAndData cost variance */
|
|
6516
|
-
const TOKEN_APPROVE_AMOUNT_MULTIPLIER = 2n;
|
|
6925
|
+
const TOKEN_APPROVE_AMOUNT_MULTIPLIER$1 = 2n;
|
|
6517
6926
|
/**
|
|
6518
6927
|
* ERC-20 tokens that require resetting their allowance to 0 before setting a
|
|
6519
6928
|
* new approval amount (e.g. USDT on mainnet).
|
|
@@ -6523,7 +6932,7 @@ const TOKEN_APPROVE_AMOUNT_MULTIPLIER = 2n;
|
|
|
6523
6932
|
* please open an issue at https://github.com/candidelabs/abstractionkit/issues
|
|
6524
6933
|
* or use the `resetApproval` override as a workaround.
|
|
6525
6934
|
*/
|
|
6526
|
-
const TOKENS_REQUIRING_ALLOWANCE_RESET = ["0xdac17f958d2ee523a2206206994597c13d831ec7"];
|
|
6935
|
+
const TOKENS_REQUIRING_ALLOWANCE_RESET$1 = ["0xdac17f958d2ee523a2206206994597c13d831ec7"];
|
|
6527
6936
|
/**
|
|
6528
6937
|
* Client for the Candide Paymaster service.
|
|
6529
6938
|
* Supports both gas sponsorship (sponsor paymaster) and ERC-20 token payment for gas (token paymaster).
|
|
@@ -6863,12 +7272,12 @@ var CandidePaymaster = class CandidePaymaster extends Paymaster {
|
|
|
6863
7272
|
if (epData == null) throw new RangeError(`UserOperation for entrypoint ${entrypoint} is not supported`);
|
|
6864
7273
|
this.setDummyPaymasterFields(userOp, epData);
|
|
6865
7274
|
const oldCallData = userOp.callData;
|
|
6866
|
-
const requiresAllowanceReset = overrides?.resetApproval ?? TOKENS_REQUIRING_ALLOWANCE_RESET.includes(context.token.toLowerCase());
|
|
6867
|
-
let callDataWithApprove = smartAccount.prependTokenPaymasterApproveToCallData(userOp.callData, context.token, epData.paymasterMetadata.address, UINT256_MAX);
|
|
7275
|
+
const requiresAllowanceReset = overrides?.resetApproval ?? TOKENS_REQUIRING_ALLOWANCE_RESET$1.includes(context.token.toLowerCase());
|
|
7276
|
+
let callDataWithApprove = smartAccount.prependTokenPaymasterApproveToCallData(userOp.callData, context.token, epData.paymasterMetadata.address, UINT256_MAX$1);
|
|
6868
7277
|
if (requiresAllowanceReset) callDataWithApprove = smartAccount.prependTokenPaymasterApproveToCallData(callDataWithApprove, context.token, epData.paymasterMetadata.address, 0n);
|
|
6869
7278
|
userOp.callData = callDataWithApprove;
|
|
6870
7279
|
await this.estimateAndApplyGasLimits(userOp, bundlerRpc, entrypoint, overrides ?? {});
|
|
6871
|
-
const approveAmount = await this.calculateUserOperationErc20TokenMaxGasCost(smartAccount, userOp, context.token) * TOKEN_APPROVE_AMOUNT_MULTIPLIER;
|
|
7280
|
+
const approveAmount = await this.calculateUserOperationErc20TokenMaxGasCost(smartAccount, userOp, context.token) * TOKEN_APPROVE_AMOUNT_MULTIPLIER$1;
|
|
6872
7281
|
callDataWithApprove = smartAccount.prependTokenPaymasterApproveToCallData(oldCallData, context.token, epData.paymasterMetadata.address, approveAmount);
|
|
6873
7282
|
if (requiresAllowanceReset) callDataWithApprove = smartAccount.prependTokenPaymasterApproveToCallData(callDataWithApprove, context.token, epData.paymasterMetadata.address, 0n);
|
|
6874
7283
|
userOp.callData = callDataWithApprove;
|
|
@@ -6948,6 +7357,396 @@ var CandidePaymaster = class CandidePaymaster extends Paymaster {
|
|
|
6948
7357
|
}
|
|
6949
7358
|
};
|
|
6950
7359
|
//#endregion
|
|
7360
|
+
//#region src/paymaster/Erc7677Paymaster.ts
|
|
7361
|
+
/** Max value for uint256 */
|
|
7362
|
+
const UINT256_MAX = 115792089237316195423570985008687907853269984665640564039457584007913129639935n;
|
|
7363
|
+
/** Multiplier for token approve amount to cover paymasterAndData cost variance */
|
|
7364
|
+
const TOKEN_APPROVE_AMOUNT_MULTIPLIER = 2n;
|
|
7365
|
+
/**
|
|
7366
|
+
* ERC-20 tokens that require resetting their allowance to 0 before setting a
|
|
7367
|
+
* new approval amount (e.g. USDT on mainnet).
|
|
7368
|
+
*/
|
|
7369
|
+
const TOKENS_REQUIRING_ALLOWANCE_RESET = ["0xdac17f958d2ee523a2206206994597c13d831ec7"];
|
|
7370
|
+
/**
|
|
7371
|
+
* Time-to-live for cached Candide `pm_supportedERC20Tokens` responses, applied
|
|
7372
|
+
* only when the fetch is initiated for an exchange-rate lookup. Stub-data
|
|
7373
|
+
* lookups (paymaster address + dummyPaymasterAndData) reuse the cache
|
|
7374
|
+
* indefinitely since those fields are effectively static per EP.
|
|
7375
|
+
*/
|
|
7376
|
+
const CANDIDE_TOKEN_QUOTE_TTL_MS = 45e3;
|
|
7377
|
+
var Erc7677Paymaster = class Erc7677Paymaster extends Paymaster {
|
|
7378
|
+
/** The paymaster JSON-RPC endpoint URL */
|
|
7379
|
+
rpcUrl;
|
|
7380
|
+
/** Cached chain ID (hex string). Passed via constructor or resolved from the bundler at first use. */
|
|
7381
|
+
chainId;
|
|
7382
|
+
/** Detected or explicitly set paymaster provider. `null` means no provider-specific features. */
|
|
7383
|
+
provider;
|
|
7384
|
+
/**
|
|
7385
|
+
* Cached Candide `pm_supportedERC20Tokens` response, keyed by lowercase
|
|
7386
|
+
* entrypoint. Used for both token quotes and stub data to avoid a second
|
|
7387
|
+
* round-trip (`pm_getPaymasterStubData`) for Candide-hosted paymasters.
|
|
7388
|
+
*
|
|
7389
|
+
* The cache is indefinite for stub-data lookups but has a TTL for
|
|
7390
|
+
* exchange-rate lookups — see {@link CANDIDE_TOKEN_QUOTE_TTL_MS}.
|
|
7391
|
+
*/
|
|
7392
|
+
candideCache = /* @__PURE__ */ new Map();
|
|
7393
|
+
/**
|
|
7394
|
+
* Detect the paymaster provider from the RPC URL hostname.
|
|
7395
|
+
* Returns `null` for unknown hosts or malformed URLs.
|
|
7396
|
+
*
|
|
7397
|
+
* Hostname-based (not substring) so that proxies or paths containing a
|
|
7398
|
+
* provider name (e.g. `https://my-proxy.com/pimlico-compat/...`) are not
|
|
7399
|
+
* misidentified.
|
|
7400
|
+
*/
|
|
7401
|
+
static detectProvider(rpcUrl) {
|
|
7402
|
+
let host;
|
|
7403
|
+
try {
|
|
7404
|
+
host = new URL(rpcUrl).hostname.toLowerCase();
|
|
7405
|
+
} catch {
|
|
7406
|
+
return null;
|
|
7407
|
+
}
|
|
7408
|
+
if (host === "pimlico.io" || host.endsWith(".pimlico.io")) return "pimlico";
|
|
7409
|
+
if (host === "candide.dev" || host.endsWith(".candide.dev")) return "candide";
|
|
7410
|
+
return null;
|
|
7411
|
+
}
|
|
7412
|
+
/**
|
|
7413
|
+
* @param rpcUrl - Paymaster JSON-RPC endpoint. Can be the same URL as the
|
|
7414
|
+
* bundler when the provider bundles both (Candide, Pimlico, Alchemy);
|
|
7415
|
+
* can also be a separate paymaster-only endpoint.
|
|
7416
|
+
* @param options
|
|
7417
|
+
* @param options.chainId - Optional chain id as a bigint (e.g. `1n` for
|
|
7418
|
+
* mainnet). When provided, avoids a lookup at first use. Otherwise,
|
|
7419
|
+
* resolved from the bundler via `eth_chainId` on the first call.
|
|
7420
|
+
* @param options.provider - Paymaster provider. `"auto"` (default) detects
|
|
7421
|
+
* from the RPC URL. Set explicitly to override, or `null` to disable.
|
|
7422
|
+
*/
|
|
7423
|
+
constructor(rpcUrl, options = {}) {
|
|
7424
|
+
super();
|
|
7425
|
+
this.rpcUrl = rpcUrl;
|
|
7426
|
+
this.chainId = options.chainId != null ? "0x" + options.chainId.toString(16) : null;
|
|
7427
|
+
if (options.provider === void 0 || options.provider === "auto") this.provider = Erc7677Paymaster.detectProvider(rpcUrl);
|
|
7428
|
+
else this.provider = options.provider;
|
|
7429
|
+
}
|
|
7430
|
+
/**
|
|
7431
|
+
* Resolve the chain id, querying the bundler if not provided at construction.
|
|
7432
|
+
*/
|
|
7433
|
+
async getChainId(bundlerRpc) {
|
|
7434
|
+
if (this.chainId != null) return this.chainId;
|
|
7435
|
+
const id = await new Bundler(bundlerRpc).chainId();
|
|
7436
|
+
this.chainId = id;
|
|
7437
|
+
return id;
|
|
7438
|
+
}
|
|
7439
|
+
/**
|
|
7440
|
+
* Determine the EntryPoint address from the UserOperation shape.
|
|
7441
|
+
* V6 ops have `initCode`, V8+ ops have `eip7702Auth`, V7 is the default.
|
|
7442
|
+
*/
|
|
7443
|
+
resolveEntrypoint(smartAccount, userOperation) {
|
|
7444
|
+
if (smartAccount.entrypointAddress != null && smartAccount.entrypointAddress.trim() !== "") return smartAccount.entrypointAddress;
|
|
7445
|
+
if ("initCode" in userOperation) return ENTRYPOINT_V6;
|
|
7446
|
+
if ("eip7702Auth" in userOperation) return ENTRYPOINT_V8;
|
|
7447
|
+
return ENTRYPOINT_V7;
|
|
7448
|
+
}
|
|
7449
|
+
/**
|
|
7450
|
+
* Low-level ERC-7677 `pm_getPaymasterStubData` call.
|
|
7451
|
+
* Returns dummy paymaster fields intended for gas estimation.
|
|
7452
|
+
*
|
|
7453
|
+
* Most consumers should prefer {@link createPaymasterUserOperation}, which
|
|
7454
|
+
* runs the full stub → estimate → final pipeline. Use this directly if you
|
|
7455
|
+
* need to drive the flow manually.
|
|
7456
|
+
*/
|
|
7457
|
+
async getPaymasterStubData(userOperation, entrypoint, chainIdHex, context = {}) {
|
|
7458
|
+
try {
|
|
7459
|
+
return await sendJsonRpcRequest(this.rpcUrl, "pm_getPaymasterStubData", [
|
|
7460
|
+
userOperation,
|
|
7461
|
+
entrypoint,
|
|
7462
|
+
chainIdHex,
|
|
7463
|
+
context
|
|
7464
|
+
]);
|
|
7465
|
+
} catch (err) {
|
|
7466
|
+
throw new AbstractionKitError("PAYMASTER_ERROR", "pm_getPaymasterStubData failed", { cause: ensureError(err) });
|
|
7467
|
+
}
|
|
7468
|
+
}
|
|
7469
|
+
/**
|
|
7470
|
+
* Low-level ERC-7677 `pm_getPaymasterData` call.
|
|
7471
|
+
* Returns the final signed paymaster fields.
|
|
7472
|
+
*/
|
|
7473
|
+
async getPaymasterData(userOperation, entrypoint, chainIdHex, context = {}) {
|
|
7474
|
+
try {
|
|
7475
|
+
return await sendJsonRpcRequest(this.rpcUrl, "pm_getPaymasterData", [
|
|
7476
|
+
userOperation,
|
|
7477
|
+
entrypoint,
|
|
7478
|
+
chainIdHex,
|
|
7479
|
+
context
|
|
7480
|
+
]);
|
|
7481
|
+
} catch (err) {
|
|
7482
|
+
throw new AbstractionKitError("PAYMASTER_ERROR", "pm_getPaymasterData failed", { cause: ensureError(err) });
|
|
7483
|
+
}
|
|
7484
|
+
}
|
|
7485
|
+
/**
|
|
7486
|
+
* Send an arbitrary JSON-RPC request through the paymaster endpoint.
|
|
7487
|
+
* Useful for provider-specific methods that fall outside the ERC-7677 spec.
|
|
7488
|
+
*
|
|
7489
|
+
* @param method - The JSON-RPC method name
|
|
7490
|
+
* @param params - The JSON-RPC params array
|
|
7491
|
+
* @returns The `result` field from the JSON-RPC response
|
|
7492
|
+
*/
|
|
7493
|
+
async sendRPCRequest(method, params = []) {
|
|
7494
|
+
try {
|
|
7495
|
+
return await sendJsonRpcRequest(this.rpcUrl, method, params);
|
|
7496
|
+
} catch (err) {
|
|
7497
|
+
throw new AbstractionKitError("PAYMASTER_ERROR", `sendRPCRequest(${method}) failed`, { cause: ensureError(err) });
|
|
7498
|
+
}
|
|
7499
|
+
}
|
|
7500
|
+
/**
|
|
7501
|
+
* Runs the full ERC-7677 pipeline and returns a UserOperation with paymaster
|
|
7502
|
+
* fields populated. The caller is responsible for signing and sending.
|
|
7503
|
+
*
|
|
7504
|
+
* @param smartAccount - Provides the target EntryPoint; not mutated.
|
|
7505
|
+
* @param userOperation - Starting UserOperation. Not mutated — a shallow copy is returned.
|
|
7506
|
+
* @param bundlerRpc - Bundler URL used for gas estimation and, if
|
|
7507
|
+
* `options.chainId` was not provided to the constructor, chain-id lookup.
|
|
7508
|
+
* @param context - Provider-specific paymaster context
|
|
7509
|
+
* (e.g. `{ sponsorshipPolicyId }` or `{ token }`).
|
|
7510
|
+
* @param overrides - Gas estimation overrides and state-override set.
|
|
7511
|
+
*
|
|
7512
|
+
* @returns The UserOperation with paymaster + gas fields populated.
|
|
7513
|
+
*/
|
|
7514
|
+
async createPaymasterUserOperation(smartAccount, userOperation, bundlerRpc, context = {}, overrides = {}) {
|
|
7515
|
+
try {
|
|
7516
|
+
const userOp = { ...userOperation };
|
|
7517
|
+
const entrypoint = overrides.entrypoint ?? this.resolveEntrypoint(smartAccount, userOp);
|
|
7518
|
+
const chainIdHex = await this.getChainId(bundlerRpc);
|
|
7519
|
+
if (context.token != null && typeof context.token === "string") return this.tokenPaymasterFlow(smartAccount, userOp, context.token, bundlerRpc, entrypoint, chainIdHex, context, overrides);
|
|
7520
|
+
return this.sponsoredFlow(userOp, bundlerRpc, entrypoint, chainIdHex, context, overrides);
|
|
7521
|
+
} catch (err) {
|
|
7522
|
+
const error = ensureError(err);
|
|
7523
|
+
if (error instanceof AbstractionKitError) throw error;
|
|
7524
|
+
throw new AbstractionKitError("PAYMASTER_ERROR", "createPaymasterUserOperation failed", { cause: error });
|
|
7525
|
+
}
|
|
7526
|
+
}
|
|
7527
|
+
/**
|
|
7528
|
+
* Merge paymaster fields into a UserOperation. Handles both v0.6
|
|
7529
|
+
* (`paymasterAndData`) and v0.7+ split fields.
|
|
7530
|
+
*/
|
|
7531
|
+
applyPaymasterFields(userOp, fields) {
|
|
7532
|
+
if ("initCode" in userOp) {
|
|
7533
|
+
if (fields.paymasterAndData != null) userOp.paymasterAndData = fields.paymasterAndData;
|
|
7534
|
+
return;
|
|
7535
|
+
}
|
|
7536
|
+
if (fields.paymaster != null) userOp.paymaster = fields.paymaster;
|
|
7537
|
+
if (fields.paymasterData != null) userOp.paymasterData = fields.paymasterData;
|
|
7538
|
+
if (fields.paymasterVerificationGasLimit != null) userOp.paymasterVerificationGasLimit = BigInt(fields.paymasterVerificationGasLimit);
|
|
7539
|
+
if (fields.paymasterPostOpGasLimit != null) userOp.paymasterPostOpGasLimit = BigInt(fields.paymasterPostOpGasLimit);
|
|
7540
|
+
}
|
|
7541
|
+
/**
|
|
7542
|
+
* Estimate gas limits via the bundler and apply them (with multipliers).
|
|
7543
|
+
* Reads paymaster gas fields back from the bundler when present — some
|
|
7544
|
+
* providers' `pm_getPaymasterStubData` returns `paymasterPostOpGasLimit: 0x1`
|
|
7545
|
+
* as a placeholder, relying on the bundler's estimate for the real value.
|
|
7546
|
+
*
|
|
7547
|
+
* Mirrors CandidePaymaster.estimateAndApplyGasLimits default multipliers
|
|
7548
|
+
* (5%/10%/10% on preVerification/verification/call) for consistent UX.
|
|
7549
|
+
*/
|
|
7550
|
+
async estimateAndApplyGasLimits(userOp, bundlerRpc, entrypoint, overrides) {
|
|
7551
|
+
let preVerificationGas = userOp.preVerificationGas;
|
|
7552
|
+
let verificationGasLimit = userOp.verificationGasLimit;
|
|
7553
|
+
let callGasLimit = userOp.callGasLimit;
|
|
7554
|
+
if (overrides.preVerificationGas == null || overrides.verificationGasLimit == null || overrides.callGasLimit == null) {
|
|
7555
|
+
if (bundlerRpc == null) throw new AbstractionKitError("BAD_DATA", "bundlerRpc can't be null if preVerificationGas, verificationGasLimit and callGasLimit are not overridden");
|
|
7556
|
+
const bundler = new Bundler(bundlerRpc);
|
|
7557
|
+
userOp.callGasLimit = 0n;
|
|
7558
|
+
userOp.verificationGasLimit = 0n;
|
|
7559
|
+
userOp.preVerificationGas = 0n;
|
|
7560
|
+
const skipFeeZeroing = this.provider === "pimlico";
|
|
7561
|
+
const inputMaxFeePerGas = userOp.maxFeePerGas;
|
|
7562
|
+
const inputMaxPriorityFeePerGas = userOp.maxPriorityFeePerGas;
|
|
7563
|
+
if (!skipFeeZeroing) {
|
|
7564
|
+
userOp.maxFeePerGas = 0n;
|
|
7565
|
+
userOp.maxPriorityFeePerGas = 0n;
|
|
7566
|
+
}
|
|
7567
|
+
const estimation = await bundler.estimateUserOperationGas(userOp, entrypoint, overrides.state_override_set);
|
|
7568
|
+
if (!skipFeeZeroing) {
|
|
7569
|
+
userOp.maxFeePerGas = inputMaxFeePerGas;
|
|
7570
|
+
userOp.maxPriorityFeePerGas = inputMaxPriorityFeePerGas;
|
|
7571
|
+
}
|
|
7572
|
+
if (estimation.preVerificationGas > preVerificationGas) preVerificationGas = estimation.preVerificationGas;
|
|
7573
|
+
if (estimation.verificationGasLimit > verificationGasLimit) verificationGasLimit = estimation.verificationGasLimit;
|
|
7574
|
+
if (estimation.callGasLimit > callGasLimit) callGasLimit = estimation.callGasLimit;
|
|
7575
|
+
if ("paymaster" in userOp && estimation.paymasterVerificationGasLimit != null) userOp.paymasterVerificationGasLimit = estimation.paymasterVerificationGasLimit;
|
|
7576
|
+
if ("paymaster" in userOp && estimation.paymasterPostOpGasLimit != null) userOp.paymasterPostOpGasLimit = estimation.paymasterPostOpGasLimit;
|
|
7577
|
+
}
|
|
7578
|
+
if (typeof overrides.preVerificationGas === "bigint" && overrides.preVerificationGas < 0n) throw new RangeError("preVerificationGas override can't be negative");
|
|
7579
|
+
if (typeof overrides.verificationGasLimit === "bigint" && overrides.verificationGasLimit < 0n) throw new RangeError("verificationGasLimit override can't be negative");
|
|
7580
|
+
if (typeof overrides.callGasLimit === "bigint" && overrides.callGasLimit < 0n) throw new RangeError("callGasLimit override can't be negative");
|
|
7581
|
+
const applyMultiplier = (value, multiplier) => value + value * BigInt(Math.round((multiplier ?? 0) * 100)) / 10000n;
|
|
7582
|
+
userOp.preVerificationGas = overrides.preVerificationGas ?? applyMultiplier(preVerificationGas, overrides.preVerificationGasPercentageMultiplier ?? 5);
|
|
7583
|
+
userOp.verificationGasLimit = overrides.verificationGasLimit ?? applyMultiplier(verificationGasLimit, overrides.verificationGasLimitPercentageMultiplier ?? 10);
|
|
7584
|
+
userOp.callGasLimit = overrides.callGasLimit ?? applyMultiplier(callGasLimit, overrides.callGasLimitPercentageMultiplier ?? 10);
|
|
7585
|
+
if (entrypoint.toLowerCase() === "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789".toLowerCase()) userOp.verificationGasLimit += 40000n;
|
|
7586
|
+
}
|
|
7587
|
+
/**
|
|
7588
|
+
* Fetch token exchange rate and paymaster address via Pimlico's
|
|
7589
|
+
* `pimlico_getTokenQuotes` RPC.
|
|
7590
|
+
*
|
|
7591
|
+
* @returns `exchangeRate` as a bigint scaled by 10^18 (the value of 1 ETH
|
|
7592
|
+
* expressed in the token's smallest unit). Used to compute the token
|
|
7593
|
+
* approval amount via `(exchangeRate * gasCostWei) / 10^18`.
|
|
7594
|
+
*/
|
|
7595
|
+
async fetchPimlicoTokenQuote(tokenAddress, entrypoint, chainIdHex) {
|
|
7596
|
+
const quotes = (await sendJsonRpcRequest(this.rpcUrl, "pimlico_getTokenQuotes", [
|
|
7597
|
+
{ tokens: [tokenAddress] },
|
|
7598
|
+
entrypoint,
|
|
7599
|
+
chainIdHex
|
|
7600
|
+
]))?.quotes;
|
|
7601
|
+
if (!Array.isArray(quotes) || quotes.length === 0) throw new AbstractionKitError("PAYMASTER_ERROR", `pimlico_getTokenQuotes returned no quotes for token ${tokenAddress}`);
|
|
7602
|
+
const quote = quotes.find((q) => q.token.toLowerCase() === tokenAddress.toLowerCase());
|
|
7603
|
+
if (quote == null) throw new AbstractionKitError("PAYMASTER_ERROR", `pimlico_getTokenQuotes did not include token ${tokenAddress}`);
|
|
7604
|
+
return {
|
|
7605
|
+
exchangeRate: BigInt(quote.exchangeRate),
|
|
7606
|
+
paymasterAddress: quote.paymaster
|
|
7607
|
+
};
|
|
7608
|
+
}
|
|
7609
|
+
/**
|
|
7610
|
+
* Fetch (and cache) Candide's `pm_supportedERC20Tokens` response for the
|
|
7611
|
+
* given entrypoint. The response carries both exchange rates and the
|
|
7612
|
+
* `dummyPaymasterAndData` used for gas estimation, so one round-trip
|
|
7613
|
+
* suffices for the entire paymaster flow.
|
|
7614
|
+
*
|
|
7615
|
+
* @param options.enforceTTL - When true, re-fetches if the cached entry is
|
|
7616
|
+
* older than {@link CANDIDE_TOKEN_QUOTE_TTL_MS}. Set by exchange-rate
|
|
7617
|
+
* lookups (where staleness matters). Stub-data lookups leave this false
|
|
7618
|
+
* and reuse the cache indefinitely — the paymaster address and
|
|
7619
|
+
* `dummyPaymasterAndData` are effectively static per paymaster version.
|
|
7620
|
+
*/
|
|
7621
|
+
async fetchCandideSupportedTokens(entrypoint, options = {}) {
|
|
7622
|
+
const key = entrypoint.toLowerCase();
|
|
7623
|
+
const cached = this.candideCache.get(key);
|
|
7624
|
+
const isStale = cached != null && options.enforceTTL === true && Date.now() - cached.fetchedAt > CANDIDE_TOKEN_QUOTE_TTL_MS;
|
|
7625
|
+
if (cached != null && !isStale) return cached.data;
|
|
7626
|
+
const result = await sendJsonRpcRequest(this.rpcUrl, "pm_supportedERC20Tokens", [entrypoint]);
|
|
7627
|
+
this.candideCache.set(key, {
|
|
7628
|
+
data: result,
|
|
7629
|
+
fetchedAt: Date.now()
|
|
7630
|
+
});
|
|
7631
|
+
return result;
|
|
7632
|
+
}
|
|
7633
|
+
/**
|
|
7634
|
+
* Fetch token exchange rate and paymaster address via Candide's
|
|
7635
|
+
* `pm_supportedERC20Tokens` RPC.
|
|
7636
|
+
*
|
|
7637
|
+
* @returns `exchangeRate` as a bigint scaled by 10^18 (the value of 1 ETH
|
|
7638
|
+
* expressed in the token's smallest unit). Used to compute the token
|
|
7639
|
+
* approval amount via `(exchangeRate * gasCostWei) / 10^18`.
|
|
7640
|
+
*/
|
|
7641
|
+
async fetchCandideTokenQuote(tokenAddress, entrypoint) {
|
|
7642
|
+
const result = await this.fetchCandideSupportedTokens(entrypoint, { enforceTTL: true });
|
|
7643
|
+
const token = result.tokens?.find((t) => t.address.toLowerCase() === tokenAddress.toLowerCase());
|
|
7644
|
+
if (token == null) throw new AbstractionKitError("PAYMASTER_ERROR", `${tokenAddress} token is not supported by the Candide paymaster`);
|
|
7645
|
+
return {
|
|
7646
|
+
exchangeRate: BigInt(token.exchangeRate),
|
|
7647
|
+
paymasterAddress: result.paymasterMetadata.address
|
|
7648
|
+
};
|
|
7649
|
+
}
|
|
7650
|
+
/**
|
|
7651
|
+
* Convert Candide's `dummyPaymasterAndData` metadata into a stub result
|
|
7652
|
+
* compatible with {@link applyPaymasterFields}. Handles both v0.6
|
|
7653
|
+
* (concatenated hex string) and v0.7+ (structured) shapes.
|
|
7654
|
+
*/
|
|
7655
|
+
candideStubFromMetadata(metadata) {
|
|
7656
|
+
const dummy = metadata.dummyPaymasterAndData;
|
|
7657
|
+
if (typeof dummy === "string") return { paymasterAndData: dummy };
|
|
7658
|
+
return {
|
|
7659
|
+
paymaster: dummy.paymaster,
|
|
7660
|
+
paymasterData: dummy.paymasterData,
|
|
7661
|
+
paymasterVerificationGasLimit: dummy.paymasterVerificationGasLimit,
|
|
7662
|
+
paymasterPostOpGasLimit: dummy.paymasterPostOpGasLimit
|
|
7663
|
+
};
|
|
7664
|
+
}
|
|
7665
|
+
/**
|
|
7666
|
+
* Get stub paymaster data. For Candide-hosted paymasters this derives the
|
|
7667
|
+
* stub from the cached `pm_supportedERC20Tokens` response (no extra
|
|
7668
|
+
* round-trip). For other providers, falls back to `pm_getPaymasterStubData`.
|
|
7669
|
+
*/
|
|
7670
|
+
async getStubData(userOperation, entrypoint, chainIdHex, context) {
|
|
7671
|
+
if (this.provider === "candide") {
|
|
7672
|
+
const response = await this.fetchCandideSupportedTokens(entrypoint);
|
|
7673
|
+
return this.candideStubFromMetadata(response.paymasterMetadata);
|
|
7674
|
+
}
|
|
7675
|
+
return this.getPaymasterStubData(userOperation, entrypoint, chainIdHex, context);
|
|
7676
|
+
}
|
|
7677
|
+
/**
|
|
7678
|
+
* Route to the correct provider-specific token quote fetcher.
|
|
7679
|
+
* Returns `null` when no provider is configured.
|
|
7680
|
+
*/
|
|
7681
|
+
async fetchProviderTokenQuote(tokenAddress, entrypoint, chainIdHex) {
|
|
7682
|
+
switch (this.provider) {
|
|
7683
|
+
case "pimlico": return this.fetchPimlicoTokenQuote(tokenAddress, entrypoint, chainIdHex);
|
|
7684
|
+
case "candide": return this.fetchCandideTokenQuote(tokenAddress, entrypoint);
|
|
7685
|
+
default: return null;
|
|
7686
|
+
}
|
|
7687
|
+
}
|
|
7688
|
+
/**
|
|
7689
|
+
* Internal token paymaster pipeline. Called from `createPaymasterUserOperation`
|
|
7690
|
+
* when `context.token` is set and the smart account supports approval prepending.
|
|
7691
|
+
*
|
|
7692
|
+
* Three cases:
|
|
7693
|
+
* - **Provider detected**: exchange rate + paymaster address from provider RPC.
|
|
7694
|
+
* - **No provider, `context.exchangeRate` set**: uses provided rate, paymaster
|
|
7695
|
+
* address from stub.
|
|
7696
|
+
* - **No provider, no rate**: falls through to the regular sponsored flow
|
|
7697
|
+
* (developer already handled approval).
|
|
7698
|
+
*/
|
|
7699
|
+
async tokenPaymasterFlow(smartAccount, userOp, tokenAddress, bundlerRpc, entrypoint, chainIdHex, context, overrides) {
|
|
7700
|
+
let exchangeRate;
|
|
7701
|
+
let paymasterAddress = null;
|
|
7702
|
+
const providerQuote = await this.fetchProviderTokenQuote(tokenAddress, entrypoint, chainIdHex);
|
|
7703
|
+
if (providerQuote != null) {
|
|
7704
|
+
exchangeRate = providerQuote.exchangeRate;
|
|
7705
|
+
paymasterAddress = providerQuote.paymasterAddress;
|
|
7706
|
+
} else if (context.exchangeRate != null) {
|
|
7707
|
+
try {
|
|
7708
|
+
exchangeRate = BigInt(context.exchangeRate);
|
|
7709
|
+
} catch (err) {
|
|
7710
|
+
throw new AbstractionKitError("PAYMASTER_ERROR", `context.exchangeRate could not be parsed as a bigint: ${String(context.exchangeRate)}`, { cause: ensureError(err) });
|
|
7711
|
+
}
|
|
7712
|
+
if (exchangeRate <= 0n) throw new AbstractionKitError("PAYMASTER_ERROR", `context.exchangeRate must be > 0, got ${exchangeRate}`);
|
|
7713
|
+
} else return this.sponsoredFlow(userOp, bundlerRpc, entrypoint, chainIdHex, context, overrides);
|
|
7714
|
+
const stub = await this.getStubData(userOp, entrypoint, chainIdHex, context);
|
|
7715
|
+
this.applyPaymasterFields(userOp, stub);
|
|
7716
|
+
if (paymasterAddress == null) if (context.paymasterAddress != null) paymasterAddress = context.paymasterAddress;
|
|
7717
|
+
else if ("initCode" in userOp && stub.paymasterAndData != null) paymasterAddress = "0x" + stub.paymasterAndData.slice(2, 42);
|
|
7718
|
+
else if (stub.paymaster != null) paymasterAddress = stub.paymaster;
|
|
7719
|
+
else throw new AbstractionKitError("PAYMASTER_ERROR", "pm_getPaymasterStubData did not return a paymaster address. Pass paymasterAddress in the context or set a provider.");
|
|
7720
|
+
const originalCallData = userOp.callData;
|
|
7721
|
+
const requiresAllowanceReset = overrides.resetApproval ?? TOKENS_REQUIRING_ALLOWANCE_RESET.includes(tokenAddress.toLowerCase());
|
|
7722
|
+
let callDataWithApprove = smartAccount.prependTokenPaymasterApproveToCallData(userOp.callData, tokenAddress, paymasterAddress, UINT256_MAX);
|
|
7723
|
+
if (requiresAllowanceReset) callDataWithApprove = smartAccount.prependTokenPaymasterApproveToCallData(callDataWithApprove, tokenAddress, paymasterAddress, 0n);
|
|
7724
|
+
userOp.callData = callDataWithApprove;
|
|
7725
|
+
await this.estimateAndApplyGasLimits(userOp, bundlerRpc, entrypoint, overrides);
|
|
7726
|
+
const maxGasCostWei = calculateUserOperationMaxGasCost(userOp);
|
|
7727
|
+
const approveAmount = exchangeRate * maxGasCostWei / 10n ** 18n * TOKEN_APPROVE_AMOUNT_MULTIPLIER;
|
|
7728
|
+
callDataWithApprove = smartAccount.prependTokenPaymasterApproveToCallData(originalCallData, tokenAddress, paymasterAddress, approveAmount);
|
|
7729
|
+
if (requiresAllowanceReset) callDataWithApprove = smartAccount.prependTokenPaymasterApproveToCallData(callDataWithApprove, tokenAddress, paymasterAddress, 0n);
|
|
7730
|
+
userOp.callData = callDataWithApprove;
|
|
7731
|
+
const final = await this.getPaymasterData(userOp, entrypoint, chainIdHex, context);
|
|
7732
|
+
this.applyPaymasterFields(userOp, final);
|
|
7733
|
+
return userOp;
|
|
7734
|
+
}
|
|
7735
|
+
/**
|
|
7736
|
+
* The regular (non-token) sponsored flow: stub → estimate → final.
|
|
7737
|
+
* Extracted to allow `tokenPaymasterFlow` to fall through to it for Case C.
|
|
7738
|
+
*/
|
|
7739
|
+
async sponsoredFlow(userOp, bundlerRpc, entrypoint, chainIdHex, context, overrides) {
|
|
7740
|
+
const stub = await this.getStubData(userOp, entrypoint, chainIdHex, context);
|
|
7741
|
+
this.applyPaymasterFields(userOp, stub);
|
|
7742
|
+
await this.estimateAndApplyGasLimits(userOp, bundlerRpc, entrypoint, overrides);
|
|
7743
|
+
if (stub.isFinal === true) return userOp;
|
|
7744
|
+
const final = await this.getPaymasterData(userOp, entrypoint, chainIdHex, context);
|
|
7745
|
+
this.applyPaymasterFields(userOp, final);
|
|
7746
|
+
return userOp;
|
|
7747
|
+
}
|
|
7748
|
+
};
|
|
7749
|
+
//#endregion
|
|
6951
7750
|
//#region src/paymaster/AllowAllPaymaster.ts
|
|
6952
7751
|
/**
|
|
6953
7752
|
* A paymaster that sponsors all UserOperations unconditionally.
|
|
@@ -7093,6 +7892,7 @@ var abstractionkit_exports = /* @__PURE__ */ __exportAll({
|
|
|
7093
7892
|
ENTRYPOINT_V9: () => ENTRYPOINT_V9,
|
|
7094
7893
|
EOADummySignerSignaturePair: () => EOADummySignerSignaturePair,
|
|
7095
7894
|
EXECUTE_RECOVERY_PRIMARY_TYPE: () => EXECUTE_RECOVERY_PRIMARY_TYPE,
|
|
7895
|
+
Erc7677Paymaster: () => Erc7677Paymaster,
|
|
7096
7896
|
ExperimentalAllowAllParallelPaymaster: () => ExperimentalAllowAllParallelPaymaster,
|
|
7097
7897
|
GasOption: () => GasOption,
|
|
7098
7898
|
Operation: () => Operation,
|
|
@@ -7127,6 +7927,10 @@ var abstractionkit_exports = /* @__PURE__ */ __exportAll({
|
|
|
7127
7927
|
createWorldIdSignal: () => createWorldIdSignal,
|
|
7128
7928
|
fetchAccountNonce: () => fetchAccountNonce,
|
|
7129
7929
|
fetchGasPrice: () => fetchGasPrice,
|
|
7930
|
+
fromEthersWallet: () => fromEthersWallet,
|
|
7931
|
+
fromPrivateKey: () => fromPrivateKey,
|
|
7932
|
+
fromViem: () => fromViem,
|
|
7933
|
+
fromViemWalletClient: () => fromViemWalletClient,
|
|
7130
7934
|
getBalanceOf: () => getBalanceOf,
|
|
7131
7935
|
getDelegatedAddress: () => getDelegatedAddress,
|
|
7132
7936
|
getDepositInfo: () => getDepositInfo,
|
|
@@ -7143,6 +7947,6 @@ var abstractionkit_exports = /* @__PURE__ */ __exportAll({
|
|
|
7143
7947
|
simulateUserOperationWithTenderlyAndCreateShareLink: () => simulateUserOperationWithTenderlyAndCreateShareLink
|
|
7144
7948
|
});
|
|
7145
7949
|
//#endregion
|
|
7146
|
-
export { ALLOWANCE_MODULE_V0_1_0_ADDRESS, AbstractionKitError, AllowanceModule, BaseUserOperationDummyValues, Bundler, CALIBUR_CANDIDE_V0_1_0_SINGLETON_ADDRESS, CALIBUR_UNISWAP_V1_0_0_SINGLETON_ADDRESS, Calibur7702Account, CaliburKeyType, CandidePaymaster, DEFAULT_SECP256R1_PRECOMPILE_ADDRESS, EIP712_MULTI_CHAIN_OPERATIONS_PRIMARY_TYPE, EIP712_MULTI_CHAIN_OPERATIONS_TYPE, EIP712_RECOVERY_MODULE_TYPE, EIP712_SAFE_OPERATION_PRIMARY_TYPE, EIP712_SAFE_OPERATION_V6_TYPE, EIP712_SAFE_OPERATION_V7_TYPE, ENTRYPOINT_V6, ENTRYPOINT_V7, ENTRYPOINT_V8, ENTRYPOINT_V9, EOADummySignerSignaturePair, EXECUTE_RECOVERY_PRIMARY_TYPE, ExperimentalAllowAllParallelPaymaster, GasOption, Operation, PolygonChain, SAFE_MESSAGE_MODULE_TYPE, SAFE_MESSAGE_PRIMARY_TYPE, SafeAccountFactory, SafeAccountV0_2_0, SafeAccountV0_3_0, SafeAccountV1_5_0_M_0_3_0, SafeModuleExecutorFunctionSelector, SafeMultiChainSigAccountV1, SendUseroperationResponse, Simple7702Account, Simple7702AccountV09, SmartAccount, SmartAccountFactory, SocialRecoveryModule, SocialRecoveryModuleGracePeriodSelector, WebauthnDummySignerSignaturePair, WorldIdPermissionlessPaymaster, ZeroAddress, abstractionkit_exports as abstractionkit, calculateUserOperationMaxGasCost, callTenderlySimulateBundle, createAndSignEip7702DelegationAuthorization, createAndSignEip7702RawTransaction, createAndSignLegacyRawTransaction, createCallData, createEip7702DelegationAuthorizationHash, createEip7702TransactionHash, createUserOperationHash, createWorldIdSignal, fetchAccountNonce, fetchGasPrice, getBalanceOf, getDelegatedAddress, getDepositInfo, getFunctionSelector, getSafeMessageEip712Data, sendJsonRpcRequest, shareTenderlySimulationAndCreateLink, signHash, simulateSenderCallDataWithTenderly, simulateSenderCallDataWithTenderlyAndCreateShareLink, simulateUserOperationCallDataWithTenderly, simulateUserOperationCallDataWithTenderlyAndCreateShareLink, simulateUserOperationWithTenderly, simulateUserOperationWithTenderlyAndCreateShareLink };
|
|
7950
|
+
export { ALLOWANCE_MODULE_V0_1_0_ADDRESS, AbstractionKitError, AllowanceModule, BaseUserOperationDummyValues, Bundler, CALIBUR_CANDIDE_V0_1_0_SINGLETON_ADDRESS, CALIBUR_UNISWAP_V1_0_0_SINGLETON_ADDRESS, Calibur7702Account, CaliburKeyType, CandidePaymaster, DEFAULT_SECP256R1_PRECOMPILE_ADDRESS, EIP712_MULTI_CHAIN_OPERATIONS_PRIMARY_TYPE, EIP712_MULTI_CHAIN_OPERATIONS_TYPE, EIP712_RECOVERY_MODULE_TYPE, EIP712_SAFE_OPERATION_PRIMARY_TYPE, EIP712_SAFE_OPERATION_V6_TYPE, EIP712_SAFE_OPERATION_V7_TYPE, ENTRYPOINT_V6, ENTRYPOINT_V7, ENTRYPOINT_V8, ENTRYPOINT_V9, EOADummySignerSignaturePair, EXECUTE_RECOVERY_PRIMARY_TYPE, Erc7677Paymaster, ExperimentalAllowAllParallelPaymaster, GasOption, Operation, PolygonChain, SAFE_MESSAGE_MODULE_TYPE, SAFE_MESSAGE_PRIMARY_TYPE, SafeAccountFactory, SafeAccountV0_2_0, SafeAccountV0_3_0, SafeAccountV1_5_0_M_0_3_0, SafeModuleExecutorFunctionSelector, SafeMultiChainSigAccountV1, SendUseroperationResponse, Simple7702Account, Simple7702AccountV09, SmartAccount, SmartAccountFactory, SocialRecoveryModule, SocialRecoveryModuleGracePeriodSelector, WebauthnDummySignerSignaturePair, WorldIdPermissionlessPaymaster, ZeroAddress, abstractionkit_exports as abstractionkit, calculateUserOperationMaxGasCost, callTenderlySimulateBundle, createAndSignEip7702DelegationAuthorization, createAndSignEip7702RawTransaction, createAndSignLegacyRawTransaction, createCallData, createEip7702DelegationAuthorizationHash, createEip7702TransactionHash, createUserOperationHash, createWorldIdSignal, fetchAccountNonce, fetchGasPrice, fromEthersWallet, fromPrivateKey, fromViem, fromViemWalletClient, getBalanceOf, getDelegatedAddress, getDepositInfo, getFunctionSelector, getSafeMessageEip712Data, sendJsonRpcRequest, shareTenderlySimulationAndCreateLink, signHash, simulateSenderCallDataWithTenderly, simulateSenderCallDataWithTenderlyAndCreateShareLink, simulateUserOperationCallDataWithTenderly, simulateUserOperationCallDataWithTenderlyAndCreateShareLink, simulateUserOperationWithTenderly, simulateUserOperationWithTenderlyAndCreateShareLink };
|
|
7147
7951
|
|
|
7148
7952
|
//# sourceMappingURL=index.mjs.map
|