diff --git a/src/Bundler.ts b/src/Bundler.ts index 55fcb52..29aa539 100644 --- a/src/Bundler.ts +++ b/src/Bundler.ts @@ -103,7 +103,7 @@ export class Bundler { ); } var res = jsonRpcResult as GasEstimationResult; - const gasEstimationResult: GasEstimationResult = { + const gasEstimationResult: GasEstimationResult = { callGasLimit: BigInt(res.callGasLimit), preVerificationGas: BigInt(res.preVerificationGas), verificationGasLimit: BigInt(res.verificationGasLimit), diff --git a/src/abstractionkit.ts b/src/abstractionkit.ts index 7d2501a..ff2aa90 100644 --- a/src/abstractionkit.ts +++ b/src/abstractionkit.ts @@ -1,5 +1,8 @@ export { SmartAccount } from "./account/SmartAccount"; -export { SocialRecoveryModule, RecoveryRequest } from "./account/Safe/modules/SocialRecoveryModule"; +export { + SocialRecoveryModule, + RecoveryRequest, +} from "./account/Safe/modules/SocialRecoveryModule"; export { SafeAccountV0_2_0 } from "./account/Safe/SafeAccountV0_2_0"; export { SafeAccountV0_3_0 } from "./account/Safe/SafeAccountV0_3_0"; @@ -18,7 +21,7 @@ export { getFunctionSelector, fetchAccountNonce, calculateUserOperationMaxGasCost, - sendJsonRpcRequest + sendJsonRpcRequest, } from "./utils"; export { @@ -28,10 +31,10 @@ export { SafeModuleExecutorFunctionSelector, SafeUserOperationTypedDataDomain, WebauthPublicKey, - EOADummySignature, + EOADummySignature, WebauthDummySignerSignaturePair, WebauthSignatureData, - SignerSignaturePair + SignerSignaturePair, } from "./account/Safe/types"; export { diff --git a/src/account/Safe/SafeAccount.ts b/src/account/Safe/SafeAccount.ts index 208fe41..a8e9cd8 100644 --- a/src/account/Safe/SafeAccount.ts +++ b/src/account/Safe/SafeAccount.ts @@ -5,20 +5,21 @@ import { keccak256, solidityPacked, solidityPackedKeccak256, - ethers, + ethers, } from "ethers"; import { SmartAccount } from "../SmartAccount"; -import { - BaseUserOperationDummyValues, - ZeroAddress, - Safe_L2_V1_4_1, - ENTRYPOINT_V6, ENTRYPOINT_V7 +import { + BaseUserOperationDummyValues, + ZeroAddress, + Safe_L2_V1_4_1, + ENTRYPOINT_V6, + ENTRYPOINT_V7, } from "../../constants"; import { MetaTransaction, Operation, StateOverrideSet, - BaseUserOperation, + BaseUserOperation, UserOperationV6, UserOperationV7, } from "../../types"; @@ -27,12 +28,12 @@ import { getFunctionSelector, fetchAccountNonce, fetchGasPrice, - sendEthCallRequest, - sendEthGetCodeRequest, + sendEthCallRequest, + sendEthGetCodeRequest, } from "../../utils"; import { - CreateBaseUserOperationOverrides, + CreateBaseUserOperationOverrides, Signer, SafeUserOperationTypedDataDomain, SafeUserOperationV6TypedDataValues, @@ -40,9 +41,9 @@ import { SignerSignaturePair, WebauthSignatureData, SafeModuleExecutorFunctionSelector, - EOADummySignature, - WebAuthnSignatureOverrides, - BaseInitOverrides + EOADummySignature, + WebAuthnSignatureOverrides, + BaseInitOverrides, } from "./types"; import { decodeMultiSendCallData, encodeMultiSendCallData } from "./multisend"; import { AbstractionKitError } from "src/errors"; @@ -50,24 +51,23 @@ import { Bundler } from "src/Bundler"; import { SendUseroperationResponse } from "../SendUseroperationResponse"; import { SafeAccountFactory } from "src/factory/SafeAccountFactory"; - export class SafeAccount extends SmartAccount { - static readonly DEFAULT_SAFE_SINGLETON = Safe_L2_V1_4_1; + static readonly DEFAULT_SAFE_SINGLETON = Safe_L2_V1_4_1; - static readonly DEFAULT_WEB_AUTHN_SHARED_SIGNER: string = - "0xfD90FAd33ee8b58f32c00aceEad1358e4AFC23f9" - static readonly DEFAULT_WEB_AUTHN_SIGNER_SINGLETON: string = - "0x270D7E4a57E6322f336261f3EaE2BADe72E68d72" + static readonly DEFAULT_WEB_AUTHN_SHARED_SIGNER: string = + "0xfD90FAd33ee8b58f32c00aceEad1358e4AFC23f9"; + static readonly DEFAULT_WEB_AUTHN_SIGNER_SINGLETON: string = + "0x270D7E4a57E6322f336261f3EaE2BADe72E68d72"; static readonly DEFAULT_WEB_AUTHN_SIGNER_FACTORY: string = - "0xF7488fFbe67327ac9f37D5F722d83Fc900852Fbf" + "0xF7488fFbe67327ac9f37D5F722d83Fc900852Fbf"; static readonly DEFAULT_WEB_AUTHN_FCLP256_VERIFIER: string = - "0x445a0683e494ea0c5AF3E83c5159fBE47Cf9e765"; - static readonly DEFAULT_WEB_AUTHN_PRECOMPILE: string = - "0x0000000000000000000000000000000000000000"; //zero address means no precompile - static readonly DEFAULT_WEB_AUTHN_SIGNER_PROXY_CREATION_CODE = - "0x61010060405234801561001157600080fd5b506040516101ee3803806101ee83398101604081905261003091610058565b6001600160a01b0390931660805260a09190915260c0526001600160b01b031660e0526100bc565b6000806000806080858703121561006e57600080fd5b84516001600160a01b038116811461008557600080fd5b60208601516040870151606088015192965090945092506001600160b01b03811681146100b157600080fd5b939692955090935050565b60805160a05160c05160e05160ff6100ef60003960006008015260006031015260006059015260006080015260ff6000f3fe608060408190527f00000000000000000000000000000000000000000000000000000000000000003660b681018290527f000000000000000000000000000000000000000000000000000000000000000060a082018190527f00000000000000000000000000000000000000000000000000000000000000008285018190527f00000000000000000000000000000000000000000000000000000000000000009490939192600082376000806056360183885af490503d6000803e8060c3573d6000fd5b503d6000f3fea2646970667358221220ddd9bb059ba7a6497d560ca97aadf4dbf0476f578378554a50d41c6bb654beae64736f6c63430008180033" - - static readonly DEFAULT_MULTISEND_CONTRACT_ADDRESS = + "0x445a0683e494ea0c5AF3E83c5159fBE47Cf9e765"; + static readonly DEFAULT_WEB_AUTHN_PRECOMPILE: string = + "0x0000000000000000000000000000000000000000"; //zero address means no precompile + static readonly DEFAULT_WEB_AUTHN_SIGNER_PROXY_CREATION_CODE = + "0x61010060405234801561001157600080fd5b506040516101ee3803806101ee83398101604081905261003091610058565b6001600160a01b0390931660805260a09190915260c0526001600160b01b031660e0526100bc565b6000806000806080858703121561006e57600080fd5b84516001600160a01b038116811461008557600080fd5b60208601516040870151606088015192965090945092506001600160b01b03811681146100b157600080fd5b939692955090935050565b60805160a05160c05160e05160ff6100ef60003960006008015260006031015260006059015260006080015260ff6000f3fe608060408190527f00000000000000000000000000000000000000000000000000000000000000003660b681018290527f000000000000000000000000000000000000000000000000000000000000000060a082018190527f00000000000000000000000000000000000000000000000000000000000000008285018190527f00000000000000000000000000000000000000000000000000000000000000009490939192600082376000806056360183885af490503d6000803e8060c3573d6000fd5b503d6000f3fea2646970667358221220ddd9bb059ba7a6497d560ca97aadf4dbf0476f578378554a50d41c6bb654beae64736f6c63430008180033"; + + static readonly DEFAULT_MULTISEND_CONTRACT_ADDRESS = "0x38869bf66a61cF6bDB996A6aE40D5853Fd43B526"; static readonly initializerFunctionSelector: string = "0xb63e800d"; @@ -91,12 +91,10 @@ export class SafeAccount extends SmartAccount { "uint8", //operation ]; - - protected isInitWebAuthn: boolean; + protected isInitWebAuthn: boolean; protected x: bigint | null = null; protected y: bigint | null = null; - readonly entrypointAddress: string; readonly safe4337ModuleAddress: string; protected factoryAddress: string | null; @@ -113,25 +111,27 @@ export class SafeAccount extends SmartAccount { this.factoryAddress = null; this.factoryData = null; - this.isInitWebAuthn = false + this.isInitWebAuthn = false; } public static createProxyAddress( initializerCallData: string, - overrides:{ - c2Nonce?: bigint, - safeFactoryAddress?: string, - singletonInitHash?: string, - } = {} + overrides: { + c2Nonce?: bigint; + safeFactoryAddress?: string; + singletonInitHash?: string; + } = {}, ): string { - const c2Nonce = overrides.c2Nonce??0n; + const c2Nonce = overrides.c2Nonce ?? 0n; if (c2Nonce < 0n) { throw RangeError("c2Nonce can't be negative"); } - const safeFactoryAddress = - overrides.safeFactoryAddress??SafeAccountFactory.DEFAULT_FACTORY_ADDRESS; - const singletonInitHash = - overrides.singletonInitHash??SafeAccount.DEFAULT_SAFE_SINGLETON.singletonInitHash; + const safeFactoryAddress = + overrides.safeFactoryAddress ?? + SafeAccountFactory.DEFAULT_FACTORY_ADDRESS; + const singletonInitHash = + overrides.singletonInitHash ?? + SafeAccount.DEFAULT_SAFE_SINGLETON.singletonInitHash; const salt = keccak256( solidityPacked( ["bytes32", "uint256"], @@ -152,8 +152,7 @@ export class SafeAccount extends SmartAccount { */ public static createAccountCallDataSingleTransaction( metaTransaction: MetaTransaction, - safeModuleExecutorFunctionSelector: SafeModuleExecutorFunctionSelector = - SafeAccount.DEFAULT_EXECUTOR_FUCNTION_SELECTOR, + safeModuleExecutorFunctionSelector: SafeModuleExecutorFunctionSelector = SafeAccount.DEFAULT_EXECUTOR_FUCNTION_SELECTOR, ): string { const value = metaTransaction.value ?? 0; const data = metaTransaction.data ?? "0x"; @@ -163,7 +162,7 @@ export class SafeAccount extends SmartAccount { value, data, operation, - safeModuleExecutorFunctionSelector + safeModuleExecutorFunctionSelector, ); return executorFunctionCallData; } @@ -173,18 +172,20 @@ export class SafeAccount extends SmartAccount { */ public static createAccountCallDataBatchTransactions( metaTransactions: MetaTransaction[], - overrides:{ - safeModuleExecutorFunctionSelector: SafeModuleExecutorFunctionSelector, - multisendContractAddress: string, - } + overrides: { + safeModuleExecutorFunctionSelector: SafeModuleExecutorFunctionSelector; + multisendContractAddress: string; + }, ): string { if (metaTransactions.length < 1) { throw RangeError("There should be at least one metaTransaction"); } - const safeModuleExecutorFunctionSelector = overrides.safeModuleExecutorFunctionSelector?? - SafeAccount.DEFAULT_EXECUTOR_FUCNTION_SELECTOR; - const multisendContractAddress = overrides.multisendContractAddress?? - SafeAccount.DEFAULT_MULTISEND_CONTRACT_ADDRESS; + const safeModuleExecutorFunctionSelector = + overrides.safeModuleExecutorFunctionSelector ?? + SafeAccount.DEFAULT_EXECUTOR_FUCNTION_SELECTOR; + const multisendContractAddress = + overrides.multisendContractAddress ?? + SafeAccount.DEFAULT_MULTISEND_CONTRACT_ADDRESS; const multiData = encodeMultiSendCallData(metaTransactions); @@ -200,7 +201,7 @@ export class SafeAccount extends SmartAccount { 0n, multiSendCallData, Operation.Delegate, - safeModuleExecutorFunctionSelector + safeModuleExecutorFunctionSelector, ); return executorFunctionCallData; @@ -214,8 +215,7 @@ export class SafeAccount extends SmartAccount { value: bigint, data: string, operation: Operation, - safeModuleExecutorFunctionSelector: SafeModuleExecutorFunctionSelector = - SafeAccount.DEFAULT_EXECUTOR_FUCNTION_SELECTOR, + safeModuleExecutorFunctionSelector: SafeModuleExecutorFunctionSelector = SafeAccount.DEFAULT_EXECUTOR_FUCNTION_SELECTOR, ): string { const executorFunctionInputParameters = [to, value, data, operation]; const callData = createCallData( @@ -233,19 +233,22 @@ export class SafeAccount extends SmartAccount { public static decodeAccountCallData( callData: string, ): [MetaTransaction, SafeModuleExecutorFunctionSelector] { - - let safeModuleExecutorFunctionSelector:SafeModuleExecutorFunctionSelector | null = null; - if (callData.startsWith( - SafeModuleExecutorFunctionSelector.executeUserOpWithErrorString) - ){ - safeModuleExecutorFunctionSelector = - SafeModuleExecutorFunctionSelector.executeUserOpWithErrorString - } - else if(callData.startsWith(SafeModuleExecutorFunctionSelector.executeUserOp)){ - safeModuleExecutorFunctionSelector = - SafeModuleExecutorFunctionSelector.executeUserOp - } - if(safeModuleExecutorFunctionSelector != null){ + let safeModuleExecutorFunctionSelector: SafeModuleExecutorFunctionSelector | null = + null; + if ( + callData.startsWith( + SafeModuleExecutorFunctionSelector.executeUserOpWithErrorString, + ) + ) { + safeModuleExecutorFunctionSelector = + SafeModuleExecutorFunctionSelector.executeUserOpWithErrorString; + } else if ( + callData.startsWith(SafeModuleExecutorFunctionSelector.executeUserOp) + ) { + safeModuleExecutorFunctionSelector = + SafeModuleExecutorFunctionSelector.executeUserOp; + } + if (safeModuleExecutorFunctionSelector != null) { const abiCoder = AbiCoder.defaultAbiCoder(); const params = "0x" + callData.slice(10); const decodedParams = abiCoder.decode( @@ -257,21 +260,21 @@ export class SafeAccount extends SmartAccount { ], params, ); - let accountCallDataString; - if (typeof decodedParams[2] !== "string") { - accountCallDataString = new TextDecoder().decode(decodedParams[2]); - } else { - accountCallDataString = decodedParams[2]; - } + let accountCallDataString; + if (typeof decodedParams[2] !== "string") { + accountCallDataString = new TextDecoder().decode(decodedParams[2]); + } else { + accountCallDataString = decodedParams[2]; + } return [ - { - to:decodedParams[0] as string, - value:BigInt(decodedParams[1] as string), - data: accountCallDataString, - operation: Number(decodedParams[3]), - }, - safeModuleExecutorFunctionSelector + { + to: decodedParams[0] as string, + value: BigInt(decodedParams[1] as string), + data: accountCallDataString, + operation: Number(decodedParams[3]), + }, + safeModuleExecutorFunctionSelector, ]; } else { throw new AbstractionKitError( @@ -288,7 +291,7 @@ export class SafeAccount extends SmartAccount { ); } } - + /** * adds a token approve call to the call data for a token paymaster * @returns callData @@ -298,12 +301,11 @@ export class SafeAccount extends SmartAccount { tokenAddress: string, paymasterAddress: string, approveAmount: bigint, - multisendContractAddress: string = - SafeAccount.DEFAULT_MULTISEND_CONTRACT_ADDRESS, + multisendContractAddress: string = SafeAccount.DEFAULT_MULTISEND_CONTRACT_ADDRESS, ): string { - const [metaTransaction, safeModuleExecutorFunctionSelector] = + const [metaTransaction, safeModuleExecutorFunctionSelector] = SafeAccount.decodeAccountCallData(callData); - + const approveFunctionSignature = "approve(address,uint256)"; const approveFunctionSelector = getFunctionSelector( approveFunctionSignature, @@ -331,7 +333,7 @@ export class SafeAccount extends SmartAccount { multiSendCallDataParams = decodedCalldata + encodedApproveMetatransaction.slice(2); } else { - const encodedCallDataMetaTransaction = encodeMultiSendCallData([ + const encodedCallDataMetaTransaction = encodeMultiSendCallData([ metaTransaction, ]); multiSendCallDataParams = @@ -348,12 +350,12 @@ export class SafeAccount extends SmartAccount { 0n, multiSendCallData, Operation.Delegate, - safeModuleExecutorFunctionSelector + safeModuleExecutorFunctionSelector, ); return executorFunctionCallData; } - + /** * formate a list of eip712 signatures to a useroperation signature * @param signersAddresses - signers public addresses @@ -365,18 +367,18 @@ export class SafeAccount extends SmartAccount { public static formatEip712SignaturesToUseroperationSignature( signersAddresses: string[], signatures: string[], - overrides:{ - validAfter?: bigint, - validUntil?: bigint, - } = {} + overrides: { + validAfter?: bigint; + validUntil?: bigint; + } = {}, ): string { if (signersAddresses.length != signatures.length) { throw RangeError( "signersAddresses and signatures arrays should be the same length", ); } - const validAfter = overrides.validAfter??0n; - const validUntil = overrides.validUntil??0n; + const validAfter = overrides.validAfter ?? 0n; + const validUntil = overrides.validUntil ?? 0n; const signersSignatures: Map = new Map(); @@ -395,56 +397,55 @@ export class SafeAccount extends SmartAccount { return SafeAccount.formatEip712SingleSignatureToUseroperationSignature( formatedSignature, - { - validAfter, - validUntil, - } + { + validAfter, + validUntil, + }, ); } - protected static getUserOperationEip712Hash( + protected static getUserOperationEip712Hash( useroperation: UserOperationV6 | UserOperationV7, - chainId:bigint, - overrides:{ - validAfter?: bigint, - validUntil?: bigint, - entrypointAddress?: string, - safe4337ModuleAddress?: string, - } = {} - ): string{ - if('initCode' in useroperation){ - return SafeAccount.getUserOperationEip712Hash_V6( - useroperation, - chainId, - overrides - ); - }else{ - return SafeAccount.getUserOperationEip712Hash_V7( - useroperation, - chainId, - overrides - ); - } - } - + chainId: bigint, + overrides: { + validAfter?: bigint; + validUntil?: bigint; + entrypointAddress?: string; + safe4337ModuleAddress?: string; + } = {}, + ): string { + if ("initCode" in useroperation) { + return SafeAccount.getUserOperationEip712Hash_V6( + useroperation, + chainId, + overrides, + ); + } else { + return SafeAccount.getUserOperationEip712Hash_V7( + useroperation, + chainId, + overrides, + ); + } + } public static getUserOperationEip712Hash_V6( useroperation: UserOperationV6, - chainId:bigint, - overrides:{ - validAfter?: bigint, - validUntil?: bigint, - entrypointAddress?: string, - safe4337ModuleAddress?: string, - } = {} - ): string{ - const validAfter = overrides.validAfter??0n; - const validUntil = overrides.validUntil??0n; - - const entrypointAddress = overrides.entrypointAddress??ENTRYPOINT_V6; - const safe4337ModuleAddress = - overrides.safe4337ModuleAddress?? - "0xa581c4A4DB7175302464fF3C06380BC3270b4037"; + chainId: bigint, + overrides: { + validAfter?: bigint; + validUntil?: bigint; + entrypointAddress?: string; + safe4337ModuleAddress?: string; + } = {}, + ): string { + const validAfter = overrides.validAfter ?? 0n; + const validUntil = overrides.validUntil ?? 0n; + + const entrypointAddress = overrides.entrypointAddress ?? ENTRYPOINT_V6; + const safe4337ModuleAddress = + overrides.safe4337ModuleAddress ?? + "0xa581c4A4DB7175302464fF3C06380BC3270b4037"; const SafeUserOperation: SafeUserOperationV6TypedDataValues = { safe: useroperation.sender, @@ -462,23 +463,23 @@ export class SafeAccount extends SmartAccount { entryPoint: entrypointAddress, }; - const EIP712_SAFE_OPERATION_V6_TYPE = { - SafeOp: [ - { type: "address", name: "safe" }, - { type: "uint256", name: "nonce" }, - { type: "bytes", name: "initCode" }, - { type: "bytes", name: "callData" }, - { type: "uint256", name: "callGasLimit" }, - { type: "uint256", name: "verificationGasLimit" }, - { type: "uint256", name: "preVerificationGas" }, - { type: "uint256", name: "maxFeePerGas" }, - { type: "uint256", name: "maxPriorityFeePerGas" }, - { type: "bytes", name: "paymasterAndData" }, - { type: "uint48", name: "validAfter" }, - { type: "uint48", name: "validUntil" }, - { type: "address", name: "entryPoint" }, - ], - }; + const EIP712_SAFE_OPERATION_V6_TYPE = { + SafeOp: [ + { type: "address", name: "safe" }, + { type: "uint256", name: "nonce" }, + { type: "bytes", name: "initCode" }, + { type: "bytes", name: "callData" }, + { type: "uint256", name: "callGasLimit" }, + { type: "uint256", name: "verificationGasLimit" }, + { type: "uint256", name: "preVerificationGas" }, + { type: "uint256", name: "maxFeePerGas" }, + { type: "uint256", name: "maxPriorityFeePerGas" }, + { type: "bytes", name: "paymasterAndData" }, + { type: "uint48", name: "validAfter" }, + { type: "uint48", name: "validUntil" }, + { type: "address", name: "entryPoint" }, + ], + }; const domain: SafeUserOperationTypedDataDomain = { chainId, @@ -489,95 +490,87 @@ export class SafeAccount extends SmartAccount { domain, EIP712_SAFE_OPERATION_V6_TYPE, SafeUserOperation, - ) + ); } - public static getUserOperationEip712Hash_V7( + public static getUserOperationEip712Hash_V7( useroperation: UserOperationV7, - chainId:bigint, - overrides:{ - validAfter?: bigint, - validUntil?: bigint, - entrypointAddress?: string, - safe4337ModuleAddress?: string, - } = {} - ): string{ - const validAfter = overrides.validAfter??0n; - const validUntil = overrides.validUntil??0n; - - const entrypointAddress = overrides.entrypointAddress??ENTRYPOINT_V7; - const safe4337ModuleAddress = - overrides.safe4337ModuleAddress?? - "0x75cf11467937ce3F2f357CE24ffc3DBF8fD5c226"; - - const abiCoder = AbiCoder.defaultAbiCoder(); - - let initCode = "0x"; - if(useroperation.factory != null){ - initCode = useroperation.factory; - if(useroperation.factoryData != null){ - initCode += useroperation.factoryData.slice(2); - } - } - - let paymasterAndData = "0x"; - if(useroperation.paymaster != null){ - paymasterAndData = useroperation.paymaster; - if(useroperation.paymasterVerificationGasLimit != null){ - paymasterAndData += - abiCoder.encode( - ["uint128"], - [ - useroperation.paymasterVerificationGasLimit - ] - ).slice(34); - } - if(useroperation.paymasterPostOpGasLimit != null){ - paymasterAndData += - abiCoder.encode( - ["uint128"], - [ - useroperation.paymasterPostOpGasLimit - ] - ).slice(34); - } - if(useroperation.paymasterData != null){ - paymasterAndData += useroperation.paymasterData.slice(2); - } - } + chainId: bigint, + overrides: { + validAfter?: bigint; + validUntil?: bigint; + entrypointAddress?: string; + safe4337ModuleAddress?: string; + } = {}, + ): string { + const validAfter = overrides.validAfter ?? 0n; + const validUntil = overrides.validUntil ?? 0n; + + const entrypointAddress = overrides.entrypointAddress ?? ENTRYPOINT_V7; + const safe4337ModuleAddress = + overrides.safe4337ModuleAddress ?? + "0x75cf11467937ce3F2f357CE24ffc3DBF8fD5c226"; + + const abiCoder = AbiCoder.defaultAbiCoder(); + + let initCode = "0x"; + if (useroperation.factory != null) { + initCode = useroperation.factory; + if (useroperation.factoryData != null) { + initCode += useroperation.factoryData.slice(2); + } + } + + let paymasterAndData = "0x"; + if (useroperation.paymaster != null) { + paymasterAndData = useroperation.paymaster; + if (useroperation.paymasterVerificationGasLimit != null) { + paymasterAndData += abiCoder + .encode(["uint128"], [useroperation.paymasterVerificationGasLimit]) + .slice(34); + } + if (useroperation.paymasterPostOpGasLimit != null) { + paymasterAndData += abiCoder + .encode(["uint128"], [useroperation.paymasterPostOpGasLimit]) + .slice(34); + } + if (useroperation.paymasterData != null) { + paymasterAndData += useroperation.paymasterData.slice(2); + } + } const SafeUserOperation: SafeUserOperationV7TypedDataValues = { safe: useroperation.sender, nonce: useroperation.nonce, - initCode: initCode, + initCode: initCode, callData: useroperation.callData, verificationGasLimit: useroperation.verificationGasLimit, - callGasLimit: useroperation.callGasLimit, + callGasLimit: useroperation.callGasLimit, preVerificationGas: useroperation.preVerificationGas, maxPriorityFeePerGas: useroperation.maxPriorityFeePerGas, - maxFeePerGas: useroperation.maxFeePerGas, + maxFeePerGas: useroperation.maxFeePerGas, paymasterAndData: paymasterAndData, validAfter: validAfter, validUntil: validUntil, entryPoint: entrypointAddress, }; - const EIP712_SAFE_OPERATION_V7_TYPE = { - SafeOp: [ - { type: "address", name: "safe" }, - { type: "uint256", name: "nonce" }, - { type: "bytes", name: "initCode" }, - { type: "bytes", name: "callData" }, - { type: "uint128", name: "verificationGasLimit" }, - { type: "uint128", name: "callGasLimit" }, - { type: "uint256", name: "preVerificationGas" }, - { type: "uint128", name: "maxPriorityFeePerGas" }, - { type: "uint128", name: "maxFeePerGas" }, - { type: "bytes", name: "paymasterAndData" }, - { type: "uint48", name: "validAfter" }, - { type: "uint48", name: "validUntil" }, - { type: "address", name: "entryPoint" }, - ], - }; + const EIP712_SAFE_OPERATION_V7_TYPE = { + SafeOp: [ + { type: "address", name: "safe" }, + { type: "uint256", name: "nonce" }, + { type: "bytes", name: "initCode" }, + { type: "bytes", name: "callData" }, + { type: "uint128", name: "verificationGasLimit" }, + { type: "uint128", name: "callGasLimit" }, + { type: "uint256", name: "preVerificationGas" }, + { type: "uint128", name: "maxPriorityFeePerGas" }, + { type: "uint128", name: "maxFeePerGas" }, + { type: "bytes", name: "paymasterAndData" }, + { type: "uint48", name: "validAfter" }, + { type: "uint48", name: "validUntil" }, + { type: "address", name: "entryPoint" }, + ], + }; const domain: SafeUserOperationTypedDataDomain = { chainId, @@ -588,10 +581,9 @@ export class SafeAccount extends SmartAccount { domain, EIP712_SAFE_OPERATION_V7_TYPE, SafeUserOperation, - ) + ); } - /** * formate an eip712 signature to a useroperation signature * @param signature - an eip712 signature @@ -601,13 +593,13 @@ export class SafeAccount extends SmartAccount { */ public static formatEip712SingleSignatureToUseroperationSignature( signature: string, - overrides:{ - validAfter?: bigint, - validUntil?: bigint, - } = {} + overrides: { + validAfter?: bigint; + validUntil?: bigint; + } = {}, ): string { - const validAfter = overrides.validAfter??0n; - const validUntil = overrides.validUntil??0n; + const validAfter = overrides.validAfter ?? 0n; + const validUntil = overrides.validUntil ?? 0n; if (validAfter < 0n) { throw RangeError("validAfter can't be negative"); @@ -654,58 +646,60 @@ export class SafeAccount extends SmartAccount { protected static createAccountAddressAndFactoryAddressAndData( owners: Signer[], overrides: BaseInitOverrides, - safe4337ModuleAddress: string, - safeModuleSetupddress: string, + safe4337ModuleAddress: string, + safeModuleSetupddress: string, ): [string, string, string] { if (owners.length < 1) { throw RangeError("There should be at least one owner"); } const initializerCallData = SafeAccount.createBaseInitializerCallData( owners, - overrides.threshold??1, - safe4337ModuleAddress, - safeModuleSetupddress, - overrides.multisendContractAddress ?? - SafeAccount.DEFAULT_MULTISEND_CONTRACT_ADDRESS, - overrides.webAuthnSharedSigner ?? - SafeAccount.DEFAULT_WEB_AUTHN_SHARED_SIGNER, - overrides.eip7212WebAuthPrecompileVerifierForSharedSigner ?? - SafeAccount.DEFAULT_WEB_AUTHN_PRECOMPILE, - overrides.eip7212WebAuthContractVerifierForSharedSigner ?? - SafeAccount.DEFAULT_WEB_AUTHN_FCLP256_VERIFIER, + overrides.threshold ?? 1, + safe4337ModuleAddress, + safeModuleSetupddress, + overrides.multisendContractAddress ?? + SafeAccount.DEFAULT_MULTISEND_CONTRACT_ADDRESS, + overrides.webAuthnSharedSigner ?? + SafeAccount.DEFAULT_WEB_AUTHN_SHARED_SIGNER, + overrides.eip7212WebAuthPrecompileVerifierForSharedSigner ?? + SafeAccount.DEFAULT_WEB_AUTHN_PRECOMPILE, + overrides.eip7212WebAuthContractVerifierForSharedSigner ?? + SafeAccount.DEFAULT_WEB_AUTHN_FCLP256_VERIFIER, ); - let safeAccountFactory; - if(overrides.safeAccountFactoryAddress != null){ - safeAccountFactory = new SafeAccountFactory( - overrides.safeAccountFactoryAddress); - }else{ - safeAccountFactory = new SafeAccountFactory(); - } - - let safeSingleton = overrides.safeAccountSingleton ?? - SafeAccount.DEFAULT_SAFE_SINGLETON; - const sender = this.createProxyAddress( - initializerCallData, - { - c2Nonce:overrides.c2Nonce ?? 0n, - safeFactoryAddress:safeAccountFactory.address, - singletonInitHash:safeSingleton.singletonInitHash, - } - ); + let safeAccountFactory; + if (overrides.safeAccountFactoryAddress != null) { + safeAccountFactory = new SafeAccountFactory( + overrides.safeAccountFactoryAddress, + ); + } else { + safeAccountFactory = new SafeAccountFactory(); + } + + let safeSingleton = + overrides.safeAccountSingleton ?? SafeAccount.DEFAULT_SAFE_SINGLETON; + const sender = this.createProxyAddress(initializerCallData, { + c2Nonce: overrides.c2Nonce ?? 0n, + safeFactoryAddress: safeAccountFactory.address, + singletonInitHash: safeSingleton.singletonInitHash, + }); const generatorFunctionInputParameters = [ safeSingleton.singletonAddress, initializerCallData, - overrides.c2Nonce ?? 0n, + overrides.c2Nonce ?? 0n, ]; const factoryGeneratorFunctionCallData = - safeAccountFactory.getFactoryGeneratorFunctionCallData( - generatorFunctionInputParameters, - ); + safeAccountFactory.getFactoryGeneratorFunctionCallData( + generatorFunctionInputParameters, + ); - return [sender, safeAccountFactory.address, factoryGeneratorFunctionCallData]; + return [ + sender, + safeAccountFactory.address, + factoryGeneratorFunctionCallData, + ]; } protected static createBaseInitializerCallData( @@ -713,14 +707,10 @@ export class SafeAccount extends SmartAccount { threshold: number, safe4337ModuleAddress: string, safeModuleSetupddress: string, - multisendContractAddress: string = - SafeAccount.DEFAULT_MULTISEND_CONTRACT_ADDRESS, - webAuthnSharedSigner = - SafeAccount.DEFAULT_WEB_AUTHN_SHARED_SIGNER, - eip7212WebAuthPrecompileVerifierForSharedSigner:string = - SafeAccount.DEFAULT_WEB_AUTHN_PRECOMPILE, - eip7212WebAuthContractVerifierForSharedSigner:string = - SafeAccount.DEFAULT_WEB_AUTHN_FCLP256_VERIFIER, + multisendContractAddress: string = SafeAccount.DEFAULT_MULTISEND_CONTRACT_ADDRESS, + webAuthnSharedSigner = SafeAccount.DEFAULT_WEB_AUTHN_SHARED_SIGNER, + eip7212WebAuthPrecompileVerifierForSharedSigner: string = SafeAccount.DEFAULT_WEB_AUTHN_PRECOMPILE, + eip7212WebAuthContractVerifierForSharedSigner: string = SafeAccount.DEFAULT_WEB_AUTHN_FCLP256_VERIFIER, ): string { if (owners.length < 1) { throw RangeError("There should be at least one owner"); @@ -733,101 +723,100 @@ export class SafeAccount extends SmartAccount { if (threshold > owners.length) { throw RangeError("threshold can't be larger than number of owners"); } - + const enable4337ModuleCallData = createCallData( "0x8d0dc49f", //enableModules ["address[]"], [[safe4337ModuleAddress]], ); - let isInitWebAuthn = false; - let initializerFunctionInputParameters; - - const owners_str: string[] = []; - for(const owner of owners){ - if(typeof(owner) != "string"){ - isInitWebAuthn = true; - }else{ - owners_str.push(owner); - } - } - - if(isInitWebAuthn){ - const safeModuleSetupCallData: MetaTransaction = { - to: safeModuleSetupddress, - value: 0n, - data: enable4337ModuleCallData, - operation: Operation.Delegate, - }; - const txs = []; - txs.push(safeModuleSetupCallData); - const modOwners = []; - - let numOfWebAuthOwners = 0 - for(const owner of owners){ - if(typeof(owner) != "string"){ - if (numOfWebAuthOwners > 0) { - throw RangeError( - "Only one WebAuthn owner can be set during initialization"); - } - const addWebauthSigner = createCallData( - "0x0dd9692f", //configure - ["uint256", "uint256", "uint176"], - [ - owner.x, - owner.y, - ( - "0x" + - eip7212WebAuthPrecompileVerifierForSharedSigner.slice(-4) + - eip7212WebAuthContractVerifierForSharedSigner.slice(2) - ), - ], - ); - - const setSignerCallData: MetaTransaction = { - to: webAuthnSharedSigner, - value: 0n, - data: addWebauthSigner, - operation: Operation.Delegate, - }; - txs.push(setSignerCallData); - modOwners.push(webAuthnSharedSigner); - numOfWebAuthOwners++ - }else{ - modOwners.push(owner); - } - } - - const encodedInit = encodeMultiSendCallData(txs); - - const mutisendSelector = "0x8d80ff0a"; - const multiSendCallData = createCallData( - mutisendSelector, - ["bytes"], - [encodedInit], - ); - - initializerFunctionInputParameters = [ - modOwners, - threshold, - multisendContractAddress, //to Contract address for optional delegate call during initialization - multiSendCallData, //Data payload for optional delegate call during initialization - safe4337ModuleAddress, //fallbackHandler Handler for fallback calls to this contract - ZeroAddress, //paymentToken (Safe specific, can be ignored) - 0, //payment (Safe specific, can be ignored) - ZeroAddress, //paymentReceiver (Safe specific, can be ignored) - ]; - }else{ - initializerFunctionInputParameters = [ - owners_str, //_owners - threshold, //_threshold - safeModuleSetupddress, //to Contract address for optional delegate call during initialization - enable4337ModuleCallData, //Data payload for optional delegate call during initialization - safe4337ModuleAddress, //fallbackHandler Handler for fallback calls to this contract - ZeroAddress, //paymentToken (Safe specific, can be ignored) - 0, //payment (Safe specific, can be ignored) - ZeroAddress, //paymentReceiver (Safe specific, can be ignored) - ]; - } + let isInitWebAuthn = false; + let initializerFunctionInputParameters; + + const owners_str: string[] = []; + for (const owner of owners) { + if (typeof owner != "string") { + isInitWebAuthn = true; + } else { + owners_str.push(owner); + } + } + + if (isInitWebAuthn) { + const safeModuleSetupCallData: MetaTransaction = { + to: safeModuleSetupddress, + value: 0n, + data: enable4337ModuleCallData, + operation: Operation.Delegate, + }; + const txs = []; + txs.push(safeModuleSetupCallData); + const modOwners = []; + + let numOfWebAuthOwners = 0; + for (const owner of owners) { + if (typeof owner != "string") { + if (numOfWebAuthOwners > 0) { + throw RangeError( + "Only one WebAuthn owner can be set during initialization", + ); + } + const addWebauthSigner = createCallData( + "0x0dd9692f", //configure + ["uint256", "uint256", "uint176"], + [ + owner.x, + owner.y, + "0x" + + eip7212WebAuthPrecompileVerifierForSharedSigner.slice(-4) + + eip7212WebAuthContractVerifierForSharedSigner.slice(2), + ], + ); + + const setSignerCallData: MetaTransaction = { + to: webAuthnSharedSigner, + value: 0n, + data: addWebauthSigner, + operation: Operation.Delegate, + }; + txs.push(setSignerCallData); + modOwners.push(webAuthnSharedSigner); + numOfWebAuthOwners++; + } else { + modOwners.push(owner); + } + } + + const encodedInit = encodeMultiSendCallData(txs); + + const mutisendSelector = "0x8d80ff0a"; + const multiSendCallData = createCallData( + mutisendSelector, + ["bytes"], + [encodedInit], + ); + + initializerFunctionInputParameters = [ + modOwners, + threshold, + multisendContractAddress, //to Contract address for optional delegate call during initialization + multiSendCallData, //Data payload for optional delegate call during initialization + safe4337ModuleAddress, //fallbackHandler Handler for fallback calls to this contract + ZeroAddress, //paymentToken (Safe specific, can be ignored) + 0, //payment (Safe specific, can be ignored) + ZeroAddress, //paymentReceiver (Safe specific, can be ignored) + ]; + } else { + initializerFunctionInputParameters = [ + owners_str, //_owners + threshold, //_threshold + safeModuleSetupddress, //to Contract address for optional delegate call during initialization + enable4337ModuleCallData, //Data payload for optional delegate call during initialization + safe4337ModuleAddress, //fallbackHandler Handler for fallback calls to this contract + ZeroAddress, //paymentToken (Safe specific, can be ignored) + 0, //payment (Safe specific, can be ignored) + ZeroAddress, //paymentReceiver (Safe specific, can be ignored) + ]; + } return createCallData( SafeAccount.initializerFunctionSelector, @@ -844,15 +833,15 @@ export class SafeAccount extends SmartAccount { */ protected static createFactoryAddressAndData( owners: Signer[], - overrides: BaseInitOverrides = {}, - safe4337ModuleAddress: string, - safeModuleSetupddress: string, - ): [string,string] { + overrides: BaseInitOverrides = {}, + safe4337ModuleAddress: string, + safeModuleSetupddress: string, + ): [string, string] { if (owners.length < 1) { throw RangeError("There should be at least one owner"); } - const threshold = overrides.threshold??1; - const c2Nonce = overrides.c2Nonce??0; + const threshold = overrides.threshold ?? 1; + const c2Nonce = overrides.c2Nonce ?? 0; if (threshold < 1) { throw RangeError("threshold should be at least one"); } @@ -865,32 +854,32 @@ export class SafeAccount extends SmartAccount { throw RangeError("c2Nonce can't be negative"); } - const initializerCallData = SafeAccount.createBaseInitializerCallData( + const initializerCallData = SafeAccount.createBaseInitializerCallData( owners, - overrides.threshold??1, - safe4337ModuleAddress, - safeModuleSetupddress, - overrides.multisendContractAddress ?? - SafeAccount.DEFAULT_MULTISEND_CONTRACT_ADDRESS, - overrides.webAuthnSharedSigner ?? - SafeAccount.DEFAULT_WEB_AUTHN_SHARED_SIGNER, - overrides.eip7212WebAuthPrecompileVerifierForSharedSigner ?? - SafeAccount.DEFAULT_WEB_AUTHN_PRECOMPILE, - overrides.eip7212WebAuthContractVerifierForSharedSigner ?? - SafeAccount.DEFAULT_WEB_AUTHN_FCLP256_VERIFIER, + overrides.threshold ?? 1, + safe4337ModuleAddress, + safeModuleSetupddress, + overrides.multisendContractAddress ?? + SafeAccount.DEFAULT_MULTISEND_CONTRACT_ADDRESS, + overrides.webAuthnSharedSigner ?? + SafeAccount.DEFAULT_WEB_AUTHN_SHARED_SIGNER, + overrides.eip7212WebAuthPrecompileVerifierForSharedSigner ?? + SafeAccount.DEFAULT_WEB_AUTHN_PRECOMPILE, + overrides.eip7212WebAuthContractVerifierForSharedSigner ?? + SafeAccount.DEFAULT_WEB_AUTHN_FCLP256_VERIFIER, ); - - let safeAccountFactory; - if(overrides.safeAccountFactoryAddress != null){ - safeAccountFactory = new SafeAccountFactory( - overrides.safeAccountFactoryAddress); - }else{ - safeAccountFactory = new SafeAccountFactory(); - } - let safeSingleton = overrides.safeAccountSingleton ?? - SafeAccount.DEFAULT_SAFE_SINGLETON; + let safeAccountFactory; + if (overrides.safeAccountFactoryAddress != null) { + safeAccountFactory = new SafeAccountFactory( + overrides.safeAccountFactoryAddress, + ); + } else { + safeAccountFactory = new SafeAccountFactory(); + } + let safeSingleton = + overrides.safeAccountSingleton ?? SafeAccount.DEFAULT_SAFE_SINGLETON; const generatorFunctionInputParameters = [ safeSingleton.singletonAddress, @@ -916,8 +905,7 @@ export class SafeAccount extends SmartAccount { tokenAddress: string, paymasterAddress: string, approveAmount: bigint, - multisendContractAddress: string = - SafeAccount.DEFAULT_MULTISEND_CONTRACT_ADDRESS, + multisendContractAddress: string = SafeAccount.DEFAULT_MULTISEND_CONTRACT_ADDRESS, ): string { return SafeAccount.prependTokenPaymasterApproveToCallDataStatic( callData, @@ -939,62 +927,62 @@ export class SafeAccount extends SmartAccount { public async estimateUserOperationGas( userOperation: UserOperationV6 | UserOperationV7, bundlerRpc: string, - overrides:{ - stateOverrideSet?: StateOverrideSet, - dummySignatures?: SignerSignaturePair[], - } = {} + overrides: { + stateOverrideSet?: StateOverrideSet; + dummySignatures?: SignerSignaturePair[]; + } = {}, ): Promise<[bigint, bigint, bigint]> { - if(overrides.dummySignatures != null){ - if(overrides.dummySignatures.length < 1){ - throw RangeError("Number of dummySignatures can't be less than 1"); - } - - userOperation.signature = - SafeAccount.formatSignaturesToUseroperationSignature( - overrides.dummySignatures, - { - validAfter:0xffffffffffffn, - validUntil:0xffffffffffffn, - } - ); - }else if(userOperation.signature.length < 3){ - userOperation.signature = - SafeAccount.formatSignaturesToUseroperationSignature( - [EOADummySignature], - { - validAfter:0xffffffffffffn, - validUntil:0xffffffffffffn, - } - ); - } + if (overrides.dummySignatures != null) { + if (overrides.dummySignatures.length < 1) { + throw RangeError("Number of dummySignatures can't be less than 1"); + } + + userOperation.signature = + SafeAccount.formatSignaturesToUseroperationSignature( + overrides.dummySignatures, + { + validAfter: 0xffffffffffffn, + validUntil: 0xffffffffffffn, + }, + ); + } else if (userOperation.signature.length < 3) { + userOperation.signature = + SafeAccount.formatSignaturesToUseroperationSignature( + [EOADummySignature], + { + validAfter: 0xffffffffffffn, + validUntil: 0xffffffffffffn, + }, + ); + } const bundler = new Bundler(bundlerRpc); - const inputMaxFeePerGas = userOperation.maxFeePerGas; - const inputMaxPriorityFeePerGas = userOperation.maxPriorityFeePerGas; - userOperation.maxFeePerGas = 0n; - userOperation.maxPriorityFeePerGas = 0n; + const inputMaxFeePerGas = userOperation.maxFeePerGas; + const inputMaxPriorityFeePerGas = userOperation.maxPriorityFeePerGas; + userOperation.maxFeePerGas = 0n; + userOperation.maxPriorityFeePerGas = 0n; const estimation = await bundler.estimateUserOperationGas( userOperation, this.entrypointAddress, overrides.stateOverrideSet, ); - userOperation.maxFeePerGas = inputMaxFeePerGas; - userOperation.maxPriorityFeePerGas = inputMaxPriorityFeePerGas; + userOperation.maxFeePerGas = inputMaxFeePerGas; + userOperation.maxPriorityFeePerGas = inputMaxPriorityFeePerGas; const preVerificationGas = BigInt(estimation.preVerificationGas); - let verificationGasLimit:bigint; - if(overrides.dummySignatures != null){ - verificationGasLimit = - BigInt(estimation.verificationGasLimit) + - (BigInt(overrides.dummySignatures.length) * 55_000n); - }else{ - verificationGasLimit =BigInt(estimation.verificationGasLimit); - } + let verificationGasLimit: bigint; + if (overrides.dummySignatures != null) { + verificationGasLimit = + BigInt(estimation.verificationGasLimit) + + BigInt(overrides.dummySignatures.length) * 55_000n; + } else { + verificationGasLimit = BigInt(estimation.verificationGasLimit); + } const callGasLimit = BigInt(estimation.callGasLimit); - return [preVerificationGas, verificationGasLimit, callGasLimit]; + return [preVerificationGas, verificationGasLimit, callGasLimit]; } /** @@ -1009,20 +997,23 @@ export class SafeAccount extends SmartAccount { */ protected async createBaseUserOperationAndFactoryAddressAndFactoryData( transactions: MetaTransaction[], - isV06:boolean, + isV06: boolean, providerRpc?: string, bundlerRpc?: string, overrides: CreateBaseUserOperationOverrides = {}, - ): Promise<[BaseUserOperation, string | null, string | null]> { + ): Promise<[BaseUserOperation, string | null, string | null]> { if (transactions.length < 1) { throw RangeError("There should be at least one transaction"); } - const webAuthnSharedSigner = - overrides.webAuthnSharedSigner??SafeAccount.DEFAULT_WEB_AUTHN_SHARED_SIGNER; - const safeModuleExecutorFunctionSelector = - overrides.safeModuleExecutorFunctionSelector??SafeAccount.DEFAULT_EXECUTOR_FUCNTION_SELECTOR; - const multisendContractAddress = - overrides.multisendContractAddress??SafeAccount.DEFAULT_MULTISEND_CONTRACT_ADDRESS; + const webAuthnSharedSigner = + overrides.webAuthnSharedSigner ?? + SafeAccount.DEFAULT_WEB_AUTHN_SHARED_SIGNER; + const safeModuleExecutorFunctionSelector = + overrides.safeModuleExecutorFunctionSelector ?? + SafeAccount.DEFAULT_EXECUTOR_FUCNTION_SELECTOR; + const multisendContractAddress = + overrides.multisendContractAddress ?? + SafeAccount.DEFAULT_MULTISEND_CONTRACT_ADDRESS; let nonce = 0n as bigint; @@ -1043,96 +1034,92 @@ export class SafeAccount extends SmartAccount { nonce = overrides.nonce; } - let factoryAddress:string | null = this.factoryAddress; - let factoryData:string | null = this.factoryData; + let factoryAddress: string | null = this.factoryAddress; + let factoryData: string | null = this.factoryData; if (nonce > 0n) { - factoryAddress = null; + factoryAddress = null; factoryData = null; - }else if(this.isInitWebAuthn){ - const eip7212WebAuthPrecompileVerifier = - overrides.eip7212WebAuthPrecompileVerifier?? - SafeAccount.DEFAULT_WEB_AUTHN_PRECOMPILE; - const eip7212WebAuthContractVerifier = - overrides.eip7212WebAuthContractVerifier?? - SafeAccount.DEFAULT_WEB_AUTHN_FCLP256_VERIFIER; - const webAuthnSignerFactory = - overrides.webAuthnSignerFactory?? - SafeAccount.DEFAULT_WEB_AUTHN_SIGNER_FACTORY; - const webAuthnSignerSingleton = - overrides.webAuthnSignerSingleton?? - SafeAccount.DEFAULT_WEB_AUTHN_SIGNER_SINGLETON; - - if(this.x == null || this.y == null){ - throw RangeError( - "Invalide account initialization with Webauthn signer." + - "Webauthn signer publickey can be null!!" - ); - } - - const createDeterministicWebAuthnVerifierOwner :MetaTransaction = - SafeAccount.createDeployWebAuthnVerifierMetaTransaction( - this.x, - this.y, - { - eip7212WebAuthPrecompileVerifier, - eip7212WebAuthContractVerifier, - webAuthnSignerFactory, - } - ); - + } else if (this.isInitWebAuthn) { + const eip7212WebAuthPrecompileVerifier = + overrides.eip7212WebAuthPrecompileVerifier ?? + SafeAccount.DEFAULT_WEB_AUTHN_PRECOMPILE; + const eip7212WebAuthContractVerifier = + overrides.eip7212WebAuthContractVerifier ?? + SafeAccount.DEFAULT_WEB_AUTHN_FCLP256_VERIFIER; + const webAuthnSignerFactory = + overrides.webAuthnSignerFactory ?? + SafeAccount.DEFAULT_WEB_AUTHN_SIGNER_FACTORY; + const webAuthnSignerSingleton = + overrides.webAuthnSignerSingleton ?? + SafeAccount.DEFAULT_WEB_AUTHN_SIGNER_SINGLETON; + + if (this.x == null || this.y == null) { + throw RangeError( + "Invalide account initialization with Webauthn signer." + + "Webauthn signer publickey can be null!!", + ); + } + + const createDeterministicWebAuthnVerifierOwner: MetaTransaction = + SafeAccount.createDeployWebAuthnVerifierMetaTransaction( + this.x, + this.y, + { + eip7212WebAuthPrecompileVerifier, + eip7212WebAuthContractVerifier, + webAuthnSignerFactory, + }, + ); + const deterministicWebAuthnVerifierAddress = - SafeAccount.createWebAuthnSignerVerifierAddress( - this.x, - this.y, - { - eip7212WebAuthPrecompileVerifier, - eip7212WebAuthContractVerifier, - webAuthnSignerFactory, - webAuthnSignerSingleton, - } - ) + SafeAccount.createWebAuthnSignerVerifierAddress(this.x, this.y, { + eip7212WebAuthPrecompileVerifier, + eip7212WebAuthContractVerifier, + webAuthnSignerFactory, + webAuthnSignerSingleton, + }); const swapSingletonWithDeterministicWebAuthnVerifierOwnerCallData = - createCallData( - "0xe318b52b", //swapOwner - [ - "address", //prevOwner - "address", //oldOwner - "address" //newOwner - ], - [ - "0x0000000000000000000000000000000000000001", //SENTINEL_OWNERS - webAuthnSharedSigner, - deterministicWebAuthnVerifierAddress - ] - ); - - const swapSingletonWithDeterministicWebAuthnVerifierOwner :MetaTransaction = - { - to: this.accountAddress, - value: 0n, - data: swapSingletonWithDeterministicWebAuthnVerifierOwnerCallData, - } + createCallData( + "0xe318b52b", //swapOwner + [ + "address", //prevOwner + "address", //oldOwner + "address", //newOwner + ], + [ + "0x0000000000000000000000000000000000000001", //SENTINEL_OWNERS + webAuthnSharedSigner, + deterministicWebAuthnVerifierAddress, + ], + ); - const clearWebauthSharedSignerCallData = createCallData( - "0x0dd9692f", //configure - ["uint256", "uint256", "uint176"], - [0, 0, 0], - ); + const swapSingletonWithDeterministicWebAuthnVerifierOwner: MetaTransaction = + { + to: this.accountAddress, + value: 0n, + data: swapSingletonWithDeterministicWebAuthnVerifierOwnerCallData, + }; + + const clearWebauthSharedSignerCallData = createCallData( + "0x0dd9692f", //configure + ["uint256", "uint256", "uint176"], + [0, 0, 0], + ); - const clearWebauthSharedSigner: MetaTransaction = { - to: webAuthnSharedSigner, - value: 0n, - data: clearWebauthSharedSignerCallData, - operation: Operation.Delegate, - }; + const clearWebauthSharedSigner: MetaTransaction = { + to: webAuthnSharedSigner, + value: 0n, + data: clearWebauthSharedSignerCallData, + operation: Operation.Delegate, + }; transactions = [ createDeterministicWebAuthnVerifierOwner, - swapSingletonWithDeterministicWebAuthnVerifierOwner, - clearWebauthSharedSigner - ].concat(transactions) + swapSingletonWithDeterministicWebAuthnVerifierOwner, + clearWebauthSharedSigner, + ].concat(transactions); } if (nonce < 0n) { @@ -1144,46 +1131,44 @@ export class SafeAccount extends SmartAccount { if (transactions.length == 1) { callData = SafeAccount.createAccountCallDataSingleTransaction( transactions[0], - safeModuleExecutorFunctionSelector + safeModuleExecutorFunctionSelector, ); } else { - callData = - SafeAccount.createAccountCallDataBatchTransactions( - transactions, - { - safeModuleExecutorFunctionSelector: safeModuleExecutorFunctionSelector, - multisendContractAddress: multisendContractAddress - } - ); + callData = SafeAccount.createAccountCallDataBatchTransactions( + transactions, + { + safeModuleExecutorFunctionSelector: + safeModuleExecutorFunctionSelector, + multisendContractAddress: multisendContractAddress, + }, + ); } } else { callData = overrides.callData; } let maxFeePerGas = BaseUserOperationDummyValues.maxFeePerGas; - let maxPriorityFeePerGas = BaseUserOperationDummyValues.maxPriorityFeePerGas; + let maxPriorityFeePerGas = + BaseUserOperationDummyValues.maxPriorityFeePerGas; if ( overrides.maxFeePerGas == null || overrides.maxPriorityFeePerGas == null ) { if (providerRpc != null) { [maxFeePerGas, maxPriorityFeePerGas] = await fetchGasPrice(providerRpc); - if(maxFeePerGas == 0n){ - maxFeePerGas = 1n; - } - if(maxPriorityFeePerGas == 0n){ - maxPriorityFeePerGas = 1n; - } - - } else { - throw new AbstractionKitError( - "BAD_DATA", - ( - "providerRpc cant't be null if maxFeePerGas and " + - "maxPriorityFeePerGas are not overriden" - ), - ); - } + if (maxFeePerGas == 0n) { + maxFeePerGas = 1n; + } + if (maxPriorityFeePerGas == 0n) { + maxPriorityFeePerGas = 1n; + } + } else { + throw new AbstractionKitError( + "BAD_DATA", + "providerRpc cant't be null if maxFeePerGas and " + + "maxPriorityFeePerGas are not overriden", + ); + } } if ( typeof overrides.maxFeePerGas === "bigint" && @@ -1199,7 +1184,7 @@ export class SafeAccount extends SmartAccount { throw RangeError("maxPriorityFeePerGas overrid can't be negative"); } - maxFeePerGas = + maxFeePerGas = overrides.maxFeePerGas ?? maxFeePerGas * BigInt( @@ -1227,90 +1212,87 @@ export class SafeAccount extends SmartAccount { }; let preVerificationGas = BaseUserOperationDummyValues.preVerificationGas; - let verificationGasLimit = BaseUserOperationDummyValues.verificationGasLimit; + let verificationGasLimit = + BaseUserOperationDummyValues.verificationGasLimit; let callGasLimit = BaseUserOperationDummyValues.callGasLimit; - + if ( overrides.preVerificationGas == null || overrides.verificationGasLimit == null || overrides.callGasLimit == null ) { if (bundlerRpc != null) { - userOperation.callGasLimit = 0n; + userOperation.callGasLimit = 0n; userOperation.verificationGasLimit = 0n; userOperation.preVerificationGas = 0n; const inputMaxFeePerGas = userOperation.maxFeePerGas; const inputMaxPriorityFeePerGas = userOperation.maxPriorityFeePerGas; userOperation.maxFeePerGas = 0n; userOperation.maxPriorityFeePerGas = 0n; - - let userOperationToEstimate: UserOperationV6 | UserOperationV7; - if(isV06){ - let initCode = "0x"; - if(factoryAddress != null){ - initCode = factoryAddress; - - if(factoryData != null){ - initCode += factoryData.slice(2); - } - } - userOperationToEstimate = { - ...userOperation, - initCode:initCode, - paymasterAndData: "0x" - } - }else{ - userOperationToEstimate = { - ...userOperation, - factory: factoryAddress, - factoryData: factoryData, - paymaster: null, - paymasterVerificationGasLimit: null, - paymasterPostOpGasLimit: null, - paymasterData: null, - } - } - let dummySignatures; - if(overrides.dummySignatures != null){ - if(overrides.dummySignatures.length < 1){ - throw RangeError( - "Number of dummySignatures can't be less than 1"); - } - dummySignatures = overrides.dummySignatures; - }else{ - dummySignatures = [EOADummySignature]; - } - userOperation.signature = - SafeAccount.formatSignaturesToUseroperationSignature( - dummySignatures, - { - validAfter:0xffffffffffffn, - validUntil:0xffffffffffffn, - webAuthnSharedSigner - } - ); + + let userOperationToEstimate: UserOperationV6 | UserOperationV7; + if (isV06) { + let initCode = "0x"; + if (factoryAddress != null) { + initCode = factoryAddress; + + if (factoryData != null) { + initCode += factoryData.slice(2); + } + } + userOperationToEstimate = { + ...userOperation, + initCode: initCode, + paymasterAndData: "0x", + }; + } else { + userOperationToEstimate = { + ...userOperation, + factory: factoryAddress, + factoryData: factoryData, + paymaster: null, + paymasterVerificationGasLimit: null, + paymasterPostOpGasLimit: null, + paymasterData: null, + }; + } + let dummySignatures; + if (overrides.dummySignatures != null) { + if (overrides.dummySignatures.length < 1) { + throw RangeError("Number of dummySignatures can't be less than 1"); + } + dummySignatures = overrides.dummySignatures; + } else { + dummySignatures = [EOADummySignature]; + } + userOperation.signature = + SafeAccount.formatSignaturesToUseroperationSignature( + dummySignatures, + { + validAfter: 0xffffffffffffn, + validUntil: 0xffffffffffffn, + webAuthnSharedSigner, + }, + ); [preVerificationGas, verificationGasLimit, callGasLimit] = await this.estimateUserOperationGas( userOperationToEstimate, bundlerRpc, - { - stateOverrideSet: overrides.state_override_set, - dummySignatures: overrides.dummySignatures, - } + { + stateOverrideSet: overrides.state_override_set, + dummySignatures: overrides.dummySignatures, + }, ); - verificationGasLimit += - (BigInt(dummySignatures.length) * 55_000n); + verificationGasLimit += BigInt(dummySignatures.length) * 55_000n; - userOperation.maxFeePerGas = inputMaxFeePerGas; + userOperation.maxFeePerGas = inputMaxFeePerGas; userOperation.maxPriorityFeePerGas = inputMaxPriorityFeePerGas; } else { throw new AbstractionKitError( "BAD_DATA", - ( - "bundlerRpc cant't be null if preVerificationGas," + - "verificationGasLimit and callGasLimit are not overriden" - ), + "bundlerRpc cant't be null if preVerificationGas," + + "verificationGasLimit and callGasLimit are not overriden", ); } } @@ -1363,7 +1345,7 @@ export class SafeAccount extends SmartAccount { ((overrides.callGasLimitPercentageMultiplier ?? 0) + 100) / 100, ), ); - + return [userOperation, factoryAddress, factoryData]; } @@ -1380,13 +1362,13 @@ export class SafeAccount extends SmartAccount { useroperation: UserOperationV6 | UserOperationV7, privateKeys: string[], chainId: bigint, - overrides:{ - validAfter?: bigint, - validUntil?: bigint, - } = {} + overrides: { + validAfter?: bigint; + validUntil?: bigint; + } = {}, ): string { - const validAfter = overrides.validAfter??0n; - const validUntil = overrides.validUntil??0n; + const validAfter = overrides.validAfter ?? 0n; + const validUntil = overrides.validUntil ?? 0n; if (privateKeys.length < 1) { throw RangeError("There should be at least one privateKey"); @@ -1401,100 +1383,93 @@ export class SafeAccount extends SmartAccount { throw RangeError("validUntil can't be negative"); } - const userOperationEip712Hash = SafeAccount.getUserOperationEip712Hash( - useroperation, - chainId, - { - validAfter, - validUntil, - entrypointAddress:this.entrypointAddress, - safe4337ModuleAddress:this.safe4337ModuleAddress - } - ) - - const signersAddresses = []; - const signatures = []; - for (const privateKey of privateKeys) { - const wallet = new Wallet(privateKey); - const SignerSignaturePair = wallet.signingKey.sign( - userOperationEip712Hash, - ).serialized; - signersAddresses.push(wallet.address); - signatures.push(SignerSignaturePair); - } - - return SafeAccount.formatEip712SignaturesToUseroperationSignature( - signersAddresses, - signatures, - { - validAfter, - validUntil, - } - ); + const userOperationEip712Hash = SafeAccount.getUserOperationEip712Hash( + useroperation, + chainId, + { + validAfter, + validUntil, + entrypointAddress: this.entrypointAddress, + safe4337ModuleAddress: this.safe4337ModuleAddress, + }, + ); + + const signersAddresses = []; + const signatures = []; + for (const privateKey of privateKeys) { + const wallet = new Wallet(privateKey); + const SignerSignaturePair = wallet.signingKey.sign( + userOperationEip712Hash, + ).serialized; + signersAddresses.push(wallet.address); + signatures.push(SignerSignaturePair); + } + + return SafeAccount.formatEip712SignaturesToUseroperationSignature( + signersAddresses, + signatures, + { + validAfter, + validUntil, + }, + ); } public static createWebAuthnSignerVerifierAddress( x: bigint, y: bigint, - overrides:{ - eip7212WebAuthPrecompileVerifier?:string, - eip7212WebAuthContractVerifier?:string, - webAuthnSignerFactory?:string, - webAuthnSignerSingleton?:string, - } = {} + overrides: { + eip7212WebAuthPrecompileVerifier?: string; + eip7212WebAuthContractVerifier?: string; + webAuthnSignerFactory?: string; + webAuthnSignerSingleton?: string; + } = {}, ): string { - const eip7212WebAuthPrecompileVerifier = - overrides.eip7212WebAuthPrecompileVerifier?? - SafeAccount.DEFAULT_WEB_AUTHN_PRECOMPILE; - const eip7212WebAuthContractVerifier = - overrides.eip7212WebAuthContractVerifier?? - SafeAccount.DEFAULT_WEB_AUTHN_FCLP256_VERIFIER; + const eip7212WebAuthPrecompileVerifier = + overrides.eip7212WebAuthPrecompileVerifier ?? + SafeAccount.DEFAULT_WEB_AUTHN_PRECOMPILE; + const eip7212WebAuthContractVerifier = + overrides.eip7212WebAuthContractVerifier ?? + SafeAccount.DEFAULT_WEB_AUTHN_FCLP256_VERIFIER; const webAuthnSignerFactory = - overrides.webAuthnSignerFactory?? - SafeAccount.DEFAULT_WEB_AUTHN_SIGNER_FACTORY; + overrides.webAuthnSignerFactory ?? + SafeAccount.DEFAULT_WEB_AUTHN_SIGNER_FACTORY; const webAuthnSignerSingleton = - overrides.webAuthnSignerSingleton?? - SafeAccount.DEFAULT_WEB_AUTHN_SIGNER_SINGLETON; + overrides.webAuthnSignerSingleton ?? + SafeAccount.DEFAULT_WEB_AUTHN_SIGNER_SINGLETON; - if( - eip7212WebAuthPrecompileVerifier.length != 42 || - eip7212WebAuthPrecompileVerifier.slice(0,38) != ZeroAddress.slice(0,38) - ){ + if ( + eip7212WebAuthPrecompileVerifier.length != 42 || + eip7212WebAuthPrecompileVerifier.slice(0, 38) != ZeroAddress.slice(0, 38) + ) { throw RangeError( - "Invalide precompile address. " + - "It should have the format 0x000000000000000000000000000000000000____"); - } - const codeHash = keccak256( + "Invalide precompile address. " + + "It should have the format 0x000000000000000000000000000000000000____", + ); + } + const codeHash = keccak256( solidityPacked( - [ - "bytes", - "uint256", - "uint256", - "uint256", - "uint256", - ], - [ - SafeAccount.DEFAULT_WEB_AUTHN_SIGNER_PROXY_CREATION_CODE, - webAuthnSignerSingleton, - x, - y, - ( - "0x" + - eip7212WebAuthPrecompileVerifier.slice(-4) + - eip7212WebAuthContractVerifier.slice(2) - ), - ] - ), + ["bytes", "uint256", "uint256", "uint256", "uint256"], + [ + SafeAccount.DEFAULT_WEB_AUTHN_SIGNER_PROXY_CREATION_CODE, + webAuthnSignerSingleton, + x, + y, + "0x" + + eip7212WebAuthPrecompileVerifier.slice(-4) + + eip7212WebAuthContractVerifier.slice(2), + ], + ), ); const proxyAdd = solidityPackedKeccak256( ["bytes1", "address", "bytes32", "bytes32"], - [ - "0xff", - webAuthnSignerFactory, - "0x0000000000000000000000000000000000000000000000000000000000000000", - codeHash - ], + [ + "0xff", + webAuthnSignerFactory, + "0x0000000000000000000000000000000000000000000000000000000000000000", + codeHash, + ], ).slice(-40); return "0x" + proxyAdd; @@ -1511,450 +1486,456 @@ export class SafeAccount extends SmartAccount { */ public static formatSignaturesToUseroperationSignature( signatures: SignerSignaturePair[], - overrides: WebAuthnSignatureOverrides = {}, - ): string { - const validAfter = overrides.validAfter??0n; - const validUntil = overrides.validUntil??0n; + overrides: WebAuthnSignatureOverrides = {}, + ): string { + const validAfter = overrides.validAfter ?? 0n; + const validUntil = overrides.validUntil ?? 0n; - const formatedSignature = this.buildSignaturesFromSingerSignaturePairs( - signatures, - overrides - ); + const formatedSignature = this.buildSignaturesFromSingerSignaturePairs( + signatures, + overrides, + ); return solidityPacked( ["uint48", "uint48", "bytes"], [validAfter, validUntil, formatedSignature], ); } - + public static getLowerCaseAddress( - signer:Signer, - webAuthnSignatureOverrides: WebAuthnSignatureOverrides = {}, - ):string{ - if(typeof(signer) == "string"){ - return signer.toLowerCase() - }else{ - const eip7212WebAuthPrecompileVerifier = - webAuthnSignatureOverrides.eip7212WebAuthPrecompileVerifier?? - SafeAccount.DEFAULT_WEB_AUTHN_PRECOMPILE; - const eip7212WebAuthContractVerifier = - webAuthnSignatureOverrides.eip7212WebAuthContractVerifier?? - SafeAccount.DEFAULT_WEB_AUTHN_FCLP256_VERIFIER; - const webAuthnSignerFactory = - webAuthnSignatureOverrides.webAuthnSignerFactory?? - SafeAccount.DEFAULT_WEB_AUTHN_SIGNER_FACTORY; - const webAuthnSignerSingleton = - webAuthnSignatureOverrides.webAuthnSignerSingleton?? - SafeAccount.DEFAULT_WEB_AUTHN_SIGNER_SINGLETON; + signer: Signer, + webAuthnSignatureOverrides: WebAuthnSignatureOverrides = {}, + ): string { + if (typeof signer == "string") { + return signer.toLowerCase(); + } else { + const eip7212WebAuthPrecompileVerifier = + webAuthnSignatureOverrides.eip7212WebAuthPrecompileVerifier ?? + SafeAccount.DEFAULT_WEB_AUTHN_PRECOMPILE; + const eip7212WebAuthContractVerifier = + webAuthnSignatureOverrides.eip7212WebAuthContractVerifier ?? + SafeAccount.DEFAULT_WEB_AUTHN_FCLP256_VERIFIER; + const webAuthnSignerFactory = + webAuthnSignatureOverrides.webAuthnSignerFactory ?? + SafeAccount.DEFAULT_WEB_AUTHN_SIGNER_FACTORY; + const webAuthnSignerSingleton = + webAuthnSignatureOverrides.webAuthnSignerSingleton ?? + SafeAccount.DEFAULT_WEB_AUTHN_SIGNER_SINGLETON; return SafeAccount.createWebAuthnSignerVerifierAddress( signer.x, signer.y, - { - eip7212WebAuthPrecompileVerifier, - eip7212WebAuthContractVerifier, - webAuthnSignerFactory, - webAuthnSignerSingleton, - } - ).toLowerCase() + { + eip7212WebAuthPrecompileVerifier, + eip7212WebAuthContractVerifier, + webAuthnSignerFactory, + webAuthnSignerSingleton, + }, + ).toLowerCase(); } } public static sortSignatures( - signatures: SignerSignaturePair[], - webAuthnSignatureOverrides: WebAuthnSignatureOverrides = {}, - ){ - signatures.sort( - ( - left, right - ) => SafeAccount.getLowerCaseAddress( - left.signer, webAuthnSignatureOverrides - ).localeCompare( - SafeAccount.getLowerCaseAddress( - right.signer,webAuthnSignatureOverrides) - ) - ) + signatures: SignerSignaturePair[], + webAuthnSignatureOverrides: WebAuthnSignatureOverrides = {}, + ) { + signatures.sort((left, right) => + SafeAccount.getLowerCaseAddress( + left.signer, + webAuthnSignatureOverrides, + ).localeCompare( + SafeAccount.getLowerCaseAddress( + right.signer, + webAuthnSignatureOverrides, + ), + ), + ); } - + public static buildSignaturesFromSingerSignaturePairs( - signatures: SignerSignaturePair[], - webAuthnSignatureOverrides: WebAuthnSignatureOverrides = {}, - ): string{ - SafeAccount.sortSignatures(signatures, webAuthnSignatureOverrides) - const start = 65 * signatures.length + signatures: SignerSignaturePair[], + webAuthnSignatureOverrides: WebAuthnSignatureOverrides = {}, + ): string { + SafeAccount.sortSignatures(signatures, webAuthnSignatureOverrides); + const start = 65 * signatures.length; const { segments } = signatures.reduce( - ({ segments, offset }, { signer, signature, isContractSignature }) => { - isContractSignature = isContractSignature || (typeof(signer) != "string") - if(isContractSignature){ - if(typeof(signer) == "string"){//ECDSAPublicAddress - return { - segments: [ - ...segments, - ethers.solidityPacked( - ['uint256', 'uint256', 'uint8'], - [signer, start + offset, 0] - ) - ], - offset: offset + 32 + ethers.dataLength(signature), - } - }else{//WebauthPublicKey - if(webAuthnSignatureOverrides.isInit == null){ - throw RangeError( - "Must define isInit parameter when using WebAuthn"); - } - if(webAuthnSignatureOverrides.isInit){ - const webauthnsharedsigner = - webAuthnSignatureOverrides.webAuthnSharedSigner?? - SafeAccount.DEFAULT_WEB_AUTHN_SHARED_SIGNER - signer = webauthnsharedsigner - }else{ - const eip7212WebAuthPrecompileVerifier = - webAuthnSignatureOverrides.eip7212WebAuthPrecompileVerifier?? - SafeAccount.DEFAULT_WEB_AUTHN_PRECOMPILE; - const eip7212WebAuthContractVerifier = - webAuthnSignatureOverrides.eip7212WebAuthContractVerifier?? - SafeAccount.DEFAULT_WEB_AUTHN_FCLP256_VERIFIER; - const webAuthnSignerFactory = - webAuthnSignatureOverrides.webAuthnSignerFactory?? - SafeAccount.DEFAULT_WEB_AUTHN_SIGNER_FACTORY; - const webAuthnSignerSingleton = - webAuthnSignatureOverrides.webAuthnSignerSingleton?? - SafeAccount.DEFAULT_WEB_AUTHN_SIGNER_SINGLETON; - - signer = SafeAccount.createWebAuthnSignerVerifierAddress( - signer.x, - signer.y, - { - eip7212WebAuthPrecompileVerifier, - eip7212WebAuthContractVerifier, - webAuthnSignerFactory, - webAuthnSignerSingleton, - } - ) + ({ segments, offset }, { signer, signature, isContractSignature }) => { + isContractSignature = isContractSignature || typeof signer != "string"; + if (isContractSignature) { + if (typeof signer == "string") { + //ECDSAPublicAddress + return { + segments: [ + ...segments, + ethers.solidityPacked( + ["uint256", "uint256", "uint8"], + [signer, start + offset, 0], + ), + ], + offset: offset + 32 + ethers.dataLength(signature), + }; + } else { + //WebauthPublicKey + if (webAuthnSignatureOverrides.isInit == null) { + throw RangeError( + "Must define isInit parameter when using WebAuthn", + ); + } + if (webAuthnSignatureOverrides.isInit) { + const webauthnsharedsigner = + webAuthnSignatureOverrides.webAuthnSharedSigner ?? + SafeAccount.DEFAULT_WEB_AUTHN_SHARED_SIGNER; + signer = webauthnsharedsigner; + } else { + const eip7212WebAuthPrecompileVerifier = + webAuthnSignatureOverrides.eip7212WebAuthPrecompileVerifier ?? + SafeAccount.DEFAULT_WEB_AUTHN_PRECOMPILE; + const eip7212WebAuthContractVerifier = + webAuthnSignatureOverrides.eip7212WebAuthContractVerifier ?? + SafeAccount.DEFAULT_WEB_AUTHN_FCLP256_VERIFIER; + const webAuthnSignerFactory = + webAuthnSignatureOverrides.webAuthnSignerFactory ?? + SafeAccount.DEFAULT_WEB_AUTHN_SIGNER_FACTORY; + const webAuthnSignerSingleton = + webAuthnSignatureOverrides.webAuthnSignerSingleton ?? + SafeAccount.DEFAULT_WEB_AUTHN_SIGNER_SINGLETON; + + signer = SafeAccount.createWebAuthnSignerVerifierAddress( + signer.x, + signer.y, + { + eip7212WebAuthPrecompileVerifier, + eip7212WebAuthContractVerifier, + webAuthnSignerFactory, + webAuthnSignerSingleton, + }, + ); + } + return { + segments: [ + ...segments, + ethers.solidityPacked( + ["uint256", "uint256", "uint8"], + [signer, start + offset, 0], + ), + ], + offset: offset + 32 + ethers.dataLength(signature), + }; } + } else { return { segments: [ - ...segments, - ethers.solidityPacked( - ['uint256', 'uint256', 'uint8'], - [signer, start + offset, 0] - ) - ], - offset: offset + 32 + ethers.dataLength(signature), - } - } - }else{ - return { - segments: [ - ...segments, - ethers.solidityPacked(['bytes'], [signature]) - ], - offset: 0, + ...segments, + ethers.solidityPacked(["bytes"], [signature]), + ], + offset: 0, + }; } - } - }, - { segments: [] as string[], offset: 0 }, - ) + }, + { segments: [] as string[], offset: 0 }, + ); return ethers.concat([ - ...segments, - ...signatures.map(({ signature }) => ethers.solidityPacked( - ['uint256', 'bytes'], [ethers.dataLength(signature), signature])), - ]) - } + ...segments, + ...signatures.map(({ signature }) => + ethers.solidityPacked( + ["uint256", "bytes"], + [ethers.dataLength(signature), signature], + ), + ), + ]); + } - public static createWebAuthnSignature(signatureData:WebauthSignatureData):string{ + public static createWebAuthnSignature( + signatureData: WebauthSignatureData, + ): string { return ethers.AbiCoder.defaultAbiCoder().encode( - ['bytes', 'bytes', 'uint256[2]'], + ["bytes", "bytes", "uint256[2]"], [ new Uint8Array(signatureData.authenticatorData), signatureData.clientDataFields, signatureData.rs, ], - ) + ); } - public async createSwapOwnerMetaTransactions( - nodeRpcUrl: string, - newOwner: Signer, - oldOwner: Signer, - overrides:{ - prevOwner?: string, - eip7212WebAuthPrecompileVerifier?:string, - eip7212WebAuthContractVerifier?:string, - webAuthnSignerFactory?:string, - webAuthnSignerSingleton?:string, - } = {} - ):Promise{ - let deployNewOwnerSignerMetaTransaction: MetaTransaction | null = null; - let newOwnerT:string; - let oldOwnerT:string; - - if(typeof(newOwner) != 'string'){ - newOwnerT = SafeAccount.createWebAuthnSignerVerifierAddress( - newOwner.x, - newOwner.y, - { - eip7212WebAuthPrecompileVerifier: - overrides.eip7212WebAuthPrecompileVerifier, - eip7212WebAuthContractVerifier: - overrides.eip7212WebAuthContractVerifier, - webAuthnSignerFactory:overrides.webAuthnSignerFactory, - webAuthnSignerSingleton:overrides.webAuthnSignerSingleton, - } - ) - const newOwnerCode = await sendEthGetCodeRequest( - nodeRpcUrl, newOwnerT, 'latest'); - const newOwnerNotDeployed = newOwnerCode.length < 3; - if(newOwnerNotDeployed){ - deployNewOwnerSignerMetaTransaction = - SafeAccount.createDeployWebAuthnVerifierMetaTransaction( - newOwner.x, - newOwner.y, - { - eip7212WebAuthPrecompileVerifier: - overrides.eip7212WebAuthPrecompileVerifier, - eip7212WebAuthContractVerifier: - overrides.eip7212WebAuthContractVerifier, - webAuthnSignerFactory: - overrides.webAuthnSignerFactory, - } - ); - } - }else{ - newOwnerT = newOwner; - } - if(typeof(oldOwner) != 'string'){ - oldOwnerT = SafeAccount.createWebAuthnSignerVerifierAddress( - oldOwner.x, - oldOwner.y, - { - eip7212WebAuthPrecompileVerifier: - overrides.eip7212WebAuthPrecompileVerifier, - eip7212WebAuthContractVerifier: - overrides.eip7212WebAuthContractVerifier, - webAuthnSignerFactory:overrides.webAuthnSignerFactory, - webAuthnSignerSingleton:overrides.webAuthnSignerSingleton, - } - ) - }else{ - oldOwnerT = oldOwner; - } - - let prevOwnerT = overrides.prevOwner; - if(prevOwnerT == null){ - const owners = await this.getOwners(nodeRpcUrl); - const oldOwnerIndex = owners.indexOf(oldOwnerT); - if(oldOwnerIndex == -1){ - throw RangeError("oldOwner is not a current owner."); - }else if(oldOwnerIndex == 0){ - prevOwnerT = "0x0000000000000000000000000000000000000001"; - }else if(oldOwnerIndex > 0){ - prevOwnerT = owners[oldOwnerIndex-1]; - }else{ - throw RangeError("Invalid owner index"); - } - } - const swapMetaTransaction = this.createStandardSwapOwnerMetaTransaction( - newOwnerT, - oldOwnerT, - prevOwnerT - ); - if(deployNewOwnerSignerMetaTransaction == null){ - return [swapMetaTransaction]; - }else{ - return [deployNewOwnerSignerMetaTransaction, swapMetaTransaction]; - } - } - - public async createRemoveOwnerMetaTransaction( - nodeRpcUrl: string, - ownerToDelete: Signer, - threshold: number, - overrides:{ - prevOwner?: string, - eip7212WebAuthPrecompileVerifier?:string, - eip7212WebAuthContractVerifier?:string, - webAuthnSignerFactory?:string, - webAuthnSignerSingleton?:string, - } = {} - ):Promise{ - let ownerToDeleteT:string; - - if(typeof(ownerToDelete) != 'string'){ - ownerToDeleteT = SafeAccount.createWebAuthnSignerVerifierAddress( - ownerToDelete.x, - ownerToDelete.y, - { - eip7212WebAuthPrecompileVerifier: - overrides.eip7212WebAuthPrecompileVerifier, - eip7212WebAuthContractVerifier: - overrides.eip7212WebAuthContractVerifier, - webAuthnSignerFactory:overrides.webAuthnSignerFactory, - webAuthnSignerSingleton:overrides.webAuthnSignerSingleton, - } - ) - }else{ - ownerToDeleteT = ownerToDelete; - } - - let prevOwnerT = overrides.prevOwner; - if(prevOwnerT == null){ - const owners = await this.getOwners(nodeRpcUrl); - const ownerToDeleteIndex = owners.indexOf(ownerToDeleteT); - if(ownerToDeleteIndex == -1){ - throw RangeError("ownerToDelete is not a current owner."); - }else if(ownerToDeleteIndex == 0){ - prevOwnerT = "0x0000000000000000000000000000000000000001"; - }else if(ownerToDeleteIndex > 0){ - prevOwnerT = owners[ownerToDeleteIndex-1]; - }else{ - throw RangeError("Invalid owner index"); - } - } - return this.createStandardRemoveOwnerMetaTransaction( - ownerToDeleteT, - threshold, - prevOwnerT - ); - } - - public createAddOwnerWithThresholdMetaTransaction( - newOwner: string, - threshold: number - ):MetaTransaction{ - const functionSelector = "0x0d582f13"; //addOwnerWithThreshold - const callData = createCallData( - functionSelector, - [ - "address", //owner - "uint256" //_threshold - ], - [ - newOwner, - threshold - ] - - ); - return { - to: this.accountAddress, - data: callData, - value: 0n - } - } - - public createStandardSwapOwnerMetaTransaction( - newOwner: string, - oldOwner: string, - prevOwner: string - ):MetaTransaction{ - const functionSelector = "0xe318b52b"; //swapOwner - const callData = createCallData( - functionSelector, - [ - "address", //prevOwner - "address", //oldOwner - "address" //newOwner - ], - [ - prevOwner, //SENTINEL_OWNERS - oldOwner, - newOwner, - ] - - ); - return { - to: this.accountAddress, - data: callData, - value: 0n - } - } - - public createStandardRemoveOwnerMetaTransaction( - ownerToDelete: string, - threshold: number, - prevOwner: string - ):MetaTransaction{ - const functionSelector = "0xf8dc5dd9"; //removeOwner - const callData = createCallData( - functionSelector, - [ - "address", //prevOwner - "address", //owner - "uint256" //_threshold - ], - [ - prevOwner, //SENTINEL_OWNERS - ownerToDelete, - threshold - ] - - ); - return { - to: this.accountAddress, - data: callData, - value: 0n - } - } - - public static createDeployWebAuthnVerifierMetaTransaction( - x: bigint, - y: bigint, - overrides:{ - eip7212WebAuthPrecompileVerifier?:string, - eip7212WebAuthContractVerifier?:string, - webAuthnSignerFactory?:string, - } = {} - ):MetaTransaction{ - const eip7212WebAuthPrecompileVerifier = - overrides.eip7212WebAuthPrecompileVerifier?? - SafeAccount.DEFAULT_WEB_AUTHN_PRECOMPILE; - const eip7212WebAuthContractVerifier = - overrides.eip7212WebAuthContractVerifier ?? - SafeAccount.DEFAULT_WEB_AUTHN_FCLP256_VERIFIER; - const webAuthnSignerFactory = - overrides.webAuthnSignerFactory ?? - SafeAccount.DEFAULT_WEB_AUTHN_SIGNER_FACTORY; - - const createDeterministicWebAuthnVerifierOwnerCallData = createCallData( - "0x0d2f0489", //createSigner - ["uint256", "uint256", "uint176"], - [ - x, - y, - ( - "0x" + - eip7212WebAuthPrecompileVerifier.slice(-4) + - eip7212WebAuthContractVerifier.slice(2) - ) - ], - ); - - return { - to: webAuthnSignerFactory, - value: 0n, - data: createDeterministicWebAuthnVerifierOwnerCallData, - } - } - - - public async getOwners(nodeRpcUrl: string):Promise{ - const functionSignature = "getOwners()"; - const functionSelector = getFunctionSelector( - functionSignature, - ); - const callData = createCallData(functionSelector, [], []); - - const ethCallParams ={ - to: this.accountAddress, - data: callData, - }; - const recoveryRequestResult = await sendEthCallRequest( - nodeRpcUrl, ethCallParams, "latest"); - - const abiCoder = AbiCoder.defaultAbiCoder(); - const decodedCalldata = abiCoder.decode( - ["address[]"], recoveryRequestResult); - - return decodedCalldata[0]; - } + public async createSwapOwnerMetaTransactions( + nodeRpcUrl: string, + newOwner: Signer, + oldOwner: Signer, + overrides: { + prevOwner?: string; + eip7212WebAuthPrecompileVerifier?: string; + eip7212WebAuthContractVerifier?: string; + webAuthnSignerFactory?: string; + webAuthnSignerSingleton?: string; + } = {}, + ): Promise { + let deployNewOwnerSignerMetaTransaction: MetaTransaction | null = null; + let newOwnerT: string; + let oldOwnerT: string; + + if (typeof newOwner != "string") { + newOwnerT = SafeAccount.createWebAuthnSignerVerifierAddress( + newOwner.x, + newOwner.y, + { + eip7212WebAuthPrecompileVerifier: + overrides.eip7212WebAuthPrecompileVerifier, + eip7212WebAuthContractVerifier: + overrides.eip7212WebAuthContractVerifier, + webAuthnSignerFactory: overrides.webAuthnSignerFactory, + webAuthnSignerSingleton: overrides.webAuthnSignerSingleton, + }, + ); + const newOwnerCode = await sendEthGetCodeRequest( + nodeRpcUrl, + newOwnerT, + "latest", + ); + const newOwnerNotDeployed = newOwnerCode.length < 3; + if (newOwnerNotDeployed) { + deployNewOwnerSignerMetaTransaction = + SafeAccount.createDeployWebAuthnVerifierMetaTransaction( + newOwner.x, + newOwner.y, + { + eip7212WebAuthPrecompileVerifier: + overrides.eip7212WebAuthPrecompileVerifier, + eip7212WebAuthContractVerifier: + overrides.eip7212WebAuthContractVerifier, + webAuthnSignerFactory: overrides.webAuthnSignerFactory, + }, + ); + } + } else { + newOwnerT = newOwner; + } + if (typeof oldOwner != "string") { + oldOwnerT = SafeAccount.createWebAuthnSignerVerifierAddress( + oldOwner.x, + oldOwner.y, + { + eip7212WebAuthPrecompileVerifier: + overrides.eip7212WebAuthPrecompileVerifier, + eip7212WebAuthContractVerifier: + overrides.eip7212WebAuthContractVerifier, + webAuthnSignerFactory: overrides.webAuthnSignerFactory, + webAuthnSignerSingleton: overrides.webAuthnSignerSingleton, + }, + ); + } else { + oldOwnerT = oldOwner; + } + + let prevOwnerT = overrides.prevOwner; + if (prevOwnerT == null) { + const owners = await this.getOwners(nodeRpcUrl); + const oldOwnerIndex = owners.indexOf(oldOwnerT); + if (oldOwnerIndex == -1) { + throw RangeError("oldOwner is not a current owner."); + } else if (oldOwnerIndex == 0) { + prevOwnerT = "0x0000000000000000000000000000000000000001"; + } else if (oldOwnerIndex > 0) { + prevOwnerT = owners[oldOwnerIndex - 1]; + } else { + throw RangeError("Invalid owner index"); + } + } + const swapMetaTransaction = this.createStandardSwapOwnerMetaTransaction( + newOwnerT, + oldOwnerT, + prevOwnerT, + ); + if (deployNewOwnerSignerMetaTransaction == null) { + return [swapMetaTransaction]; + } else { + return [deployNewOwnerSignerMetaTransaction, swapMetaTransaction]; + } + } + + public async createRemoveOwnerMetaTransaction( + nodeRpcUrl: string, + ownerToDelete: Signer, + threshold: number, + overrides: { + prevOwner?: string; + eip7212WebAuthPrecompileVerifier?: string; + eip7212WebAuthContractVerifier?: string; + webAuthnSignerFactory?: string; + webAuthnSignerSingleton?: string; + } = {}, + ): Promise { + let ownerToDeleteT: string; + + if (typeof ownerToDelete != "string") { + ownerToDeleteT = SafeAccount.createWebAuthnSignerVerifierAddress( + ownerToDelete.x, + ownerToDelete.y, + { + eip7212WebAuthPrecompileVerifier: + overrides.eip7212WebAuthPrecompileVerifier, + eip7212WebAuthContractVerifier: + overrides.eip7212WebAuthContractVerifier, + webAuthnSignerFactory: overrides.webAuthnSignerFactory, + webAuthnSignerSingleton: overrides.webAuthnSignerSingleton, + }, + ); + } else { + ownerToDeleteT = ownerToDelete; + } + + let prevOwnerT = overrides.prevOwner; + if (prevOwnerT == null) { + const owners = await this.getOwners(nodeRpcUrl); + const ownerToDeleteIndex = owners.indexOf(ownerToDeleteT); + if (ownerToDeleteIndex == -1) { + throw RangeError("ownerToDelete is not a current owner."); + } else if (ownerToDeleteIndex == 0) { + prevOwnerT = "0x0000000000000000000000000000000000000001"; + } else if (ownerToDeleteIndex > 0) { + prevOwnerT = owners[ownerToDeleteIndex - 1]; + } else { + throw RangeError("Invalid owner index"); + } + } + return this.createStandardRemoveOwnerMetaTransaction( + ownerToDeleteT, + threshold, + prevOwnerT, + ); + } + + public createAddOwnerWithThresholdMetaTransaction( + newOwner: string, + threshold: number, + ): MetaTransaction { + const functionSelector = "0x0d582f13"; //addOwnerWithThreshold + const callData = createCallData( + functionSelector, + [ + "address", //owner + "uint256", //_threshold + ], + [newOwner, threshold], + ); + return { + to: this.accountAddress, + data: callData, + value: 0n, + }; + } + + public createStandardSwapOwnerMetaTransaction( + newOwner: string, + oldOwner: string, + prevOwner: string, + ): MetaTransaction { + const functionSelector = "0xe318b52b"; //swapOwner + const callData = createCallData( + functionSelector, + [ + "address", //prevOwner + "address", //oldOwner + "address", //newOwner + ], + [ + prevOwner, //SENTINEL_OWNERS + oldOwner, + newOwner, + ], + ); + return { + to: this.accountAddress, + data: callData, + value: 0n, + }; + } + + public createStandardRemoveOwnerMetaTransaction( + ownerToDelete: string, + threshold: number, + prevOwner: string, + ): MetaTransaction { + const functionSelector = "0xf8dc5dd9"; //removeOwner + const callData = createCallData( + functionSelector, + [ + "address", //prevOwner + "address", //owner + "uint256", //_threshold + ], + [ + prevOwner, //SENTINEL_OWNERS + ownerToDelete, + threshold, + ], + ); + return { + to: this.accountAddress, + data: callData, + value: 0n, + }; + } + + public static createDeployWebAuthnVerifierMetaTransaction( + x: bigint, + y: bigint, + overrides: { + eip7212WebAuthPrecompileVerifier?: string; + eip7212WebAuthContractVerifier?: string; + webAuthnSignerFactory?: string; + } = {}, + ): MetaTransaction { + const eip7212WebAuthPrecompileVerifier = + overrides.eip7212WebAuthPrecompileVerifier ?? + SafeAccount.DEFAULT_WEB_AUTHN_PRECOMPILE; + const eip7212WebAuthContractVerifier = + overrides.eip7212WebAuthContractVerifier ?? + SafeAccount.DEFAULT_WEB_AUTHN_FCLP256_VERIFIER; + const webAuthnSignerFactory = + overrides.webAuthnSignerFactory ?? + SafeAccount.DEFAULT_WEB_AUTHN_SIGNER_FACTORY; + + const createDeterministicWebAuthnVerifierOwnerCallData = createCallData( + "0x0d2f0489", //createSigner + ["uint256", "uint256", "uint176"], + [ + x, + y, + "0x" + + eip7212WebAuthPrecompileVerifier.slice(-4) + + eip7212WebAuthContractVerifier.slice(2), + ], + ); + + return { + to: webAuthnSignerFactory, + value: 0n, + data: createDeterministicWebAuthnVerifierOwnerCallData, + }; + } + + public async getOwners(nodeRpcUrl: string): Promise { + const functionSignature = "getOwners()"; + const functionSelector = getFunctionSelector(functionSignature); + const callData = createCallData(functionSelector, [], []); + + const ethCallParams = { + to: this.accountAddress, + data: callData, + }; + const recoveryRequestResult = await sendEthCallRequest( + nodeRpcUrl, + ethCallParams, + "latest", + ); + + const abiCoder = AbiCoder.defaultAbiCoder(); + const decodedCalldata = abiCoder.decode( + ["address[]"], + recoveryRequestResult, + ); + + return decodedCalldata[0]; + } } diff --git a/src/account/Safe/SafeAccountV0_2_0.ts b/src/account/Safe/SafeAccountV0_2_0.ts index a183b04..ffb4cf9 100644 --- a/src/account/Safe/SafeAccountV0_2_0.ts +++ b/src/account/Safe/SafeAccountV0_2_0.ts @@ -2,7 +2,7 @@ import { SafeAccount } from "./SafeAccount"; import { InitCodeOverrides, Signer, - CreateUserOperationV6Overrides, + CreateUserOperationV6Overrides, } from "./types"; import { UserOperationV6, MetaTransaction } from "../../types"; @@ -10,28 +10,30 @@ import { SafeAccountFactory } from "src/factory/SafeAccountFactory"; import { ENTRYPOINT_V6 } from "src/constants"; export class SafeAccountV0_2_0 extends SafeAccount { - static readonly DEFAULT_ENTRYPOINT_ADDRESS = ENTRYPOINT_V6; + static readonly DEFAULT_ENTRYPOINT_ADDRESS = ENTRYPOINT_V6; static readonly DEFAULT_SAFE_4337_MODULE_ADDRESS = "0xa581c4A4DB7175302464fF3C06380BC3270b4037"; - static readonly DEFAULT_SAFE_MODULE_SETUP_ADDRESS = + static readonly DEFAULT_SAFE_MODULE_SETUP_ADDRESS = "0x8EcD4ec46D4D2a6B64fE960B3D64e8B94B2234eb"; - - constructor( + + constructor( accountAddress: string, - overrides:{ - safe4337ModuleAddress?: string, - entrypointAddress?: string, - } = {} + overrides: { + safe4337ModuleAddress?: string; + entrypointAddress?: string; + } = {}, ) { - const safe4337ModuleAddress = overrides.safe4337ModuleAddress?? - SafeAccountV0_2_0.DEFAULT_SAFE_4337_MODULE_ADDRESS; - const entrypointAddress = overrides.entrypointAddress?? - SafeAccountV0_2_0.DEFAULT_ENTRYPOINT_ADDRESS; + const safe4337ModuleAddress = + overrides.safe4337ModuleAddress ?? + SafeAccountV0_2_0.DEFAULT_SAFE_4337_MODULE_ADDRESS; + const entrypointAddress = + overrides.entrypointAddress ?? + SafeAccountV0_2_0.DEFAULT_ENTRYPOINT_ADDRESS; super(accountAddress, safe4337ModuleAddress, entrypointAddress); } - - /** + + /** * calculate account addressfrom initial owners * @param owners - list of account owners addresses * @param overrides - override values to change the initialization default values @@ -39,20 +41,22 @@ export class SafeAccountV0_2_0 extends SafeAccount { */ public static createAccountAddress( owners: Signer[], - overrides: InitCodeOverrides = {},//mod - ): string { - const [accountAddress, , ] = - SafeAccount.createAccountAddressAndFactoryAddressAndData( - owners, - overrides, - overrides.safe4337ModuleAddress ?? SafeAccountV0_2_0.DEFAULT_SAFE_4337_MODULE_ADDRESS, - overrides.safeModuleSetupddress ?? SafeAccountV0_2_0.DEFAULT_SAFE_MODULE_SETUP_ADDRESS, - ); + overrides: InitCodeOverrides = {}, //mod + ): string { + const [accountAddress, ,] = + SafeAccount.createAccountAddressAndFactoryAddressAndData( + owners, + overrides, + overrides.safe4337ModuleAddress ?? + SafeAccountV0_2_0.DEFAULT_SAFE_4337_MODULE_ADDRESS, + overrides.safeModuleSetupddress ?? + SafeAccountV0_2_0.DEFAULT_SAFE_MODULE_SETUP_ADDRESS, + ); - return accountAddress; + return accountAddress; } - /** + /** * To create and initialize a SafeAccount object from its * initial owners * @remarks @@ -69,76 +73,71 @@ export class SafeAccountV0_2_0 extends SafeAccount { let isInitWebAuthn = false; let x = 0n; let y = 0n; - for(const owner of owners){ - if(typeof(owner) != "string"){ - if (isInitWebAuthn) { - throw RangeError( - "Only one Webauth signer is allowed during initialization" - ); - } + for (const owner of owners) { + if (typeof owner != "string") { + if (isInitWebAuthn) { + throw RangeError( + "Only one Webauth signer is allowed during initialization", + ); + } isInitWebAuthn = true; x = owner.x; y = owner.y; } } - const [accountAddress, factoryAddress, factoryData] = - SafeAccountV0_2_0.createAccountAddressAndFactoryAddressAndData( - owners, - overrides, - overrides.safe4337ModuleAddress ?? SafeAccountV0_2_0.DEFAULT_SAFE_4337_MODULE_ADDRESS, - overrides.safeModuleSetupddress ?? SafeAccountV0_2_0.DEFAULT_SAFE_MODULE_SETUP_ADDRESS, - ); + const [accountAddress, factoryAddress, factoryData] = + SafeAccountV0_2_0.createAccountAddressAndFactoryAddressAndData( + owners, + overrides, + overrides.safe4337ModuleAddress ?? + SafeAccountV0_2_0.DEFAULT_SAFE_4337_MODULE_ADDRESS, + overrides.safeModuleSetupddress ?? + SafeAccountV0_2_0.DEFAULT_SAFE_MODULE_SETUP_ADDRESS, + ); - const safe = new SafeAccountV0_2_0( - accountAddress, - { - safe4337ModuleAddress:overrides.safe4337ModuleAddress, - entrypointAddress:overrides.entrypointAddress - } - ); + const safe = new SafeAccountV0_2_0(accountAddress, { + safe4337ModuleAddress: overrides.safe4337ModuleAddress, + entrypointAddress: overrides.entrypointAddress, + }); safe.factoryAddress = factoryAddress; safe.factoryData = factoryData; - if(isInitWebAuthn){ - safe.isInitWebAuthn = isInitWebAuthn; - safe.x = x; - safe.y = y; - } - + if (isInitWebAuthn) { + safe.isInitWebAuthn = isInitWebAuthn; + safe.x = x; + safe.y = y; + } + return safe; } - public static getUserOperationEip712Hash( + public static getUserOperationEip712Hash( useroperation: UserOperationV6, - chainId:bigint, - overrides:{ - validAfter?: bigint, - validUntil?: bigint, - entrypointAddress?: string, - safe4337ModuleAddress?: string, - } = {} - ): string{ - const validAfter = overrides.validAfter??0n; - const validUntil = overrides.validUntil??0n; - const entrypointAddress = overrides.entrypointAddress?? - SafeAccountV0_2_0.DEFAULT_ENTRYPOINT_ADDRESS; - const safe4337ModuleAddress = - overrides.safe4337ModuleAddress?? - SafeAccountV0_2_0.DEFAULT_SAFE_4337_MODULE_ADDRESS; + chainId: bigint, + overrides: { + validAfter?: bigint; + validUntil?: bigint; + entrypointAddress?: string; + safe4337ModuleAddress?: string; + } = {}, + ): string { + const validAfter = overrides.validAfter ?? 0n; + const validUntil = overrides.validUntil ?? 0n; + const entrypointAddress = + overrides.entrypointAddress ?? + SafeAccountV0_2_0.DEFAULT_ENTRYPOINT_ADDRESS; + const safe4337ModuleAddress = + overrides.safe4337ModuleAddress ?? + SafeAccountV0_2_0.DEFAULT_SAFE_4337_MODULE_ADDRESS; - return SafeAccount.getUserOperationEip712Hash( - useroperation, - chainId, - { - validAfter, - validUntil, - entrypointAddress, - safe4337ModuleAddress - } - ) - } + return SafeAccount.getUserOperationEip712Hash(useroperation, chainId, { + validAfter, + validUntil, + entrypointAddress, + safe4337ModuleAddress, + }); + } - - /** + /** * calculate account address and initcode from owners * @param owners - list of account owners addresses * @param overrides - override values to change the initialization default values @@ -148,81 +147,83 @@ export class SafeAccountV0_2_0 extends SafeAccount { owners: Signer[], overrides: InitCodeOverrides, ): [string, string] { - let safeAccountFactory; - if(overrides.safeAccountFactoryAddress != null){ - safeAccountFactory = new SafeAccountFactory(overrides.safeAccountFactoryAddress); - }else{ - safeAccountFactory = new SafeAccountFactory(); - } + let safeAccountFactory; + if (overrides.safeAccountFactoryAddress != null) { + safeAccountFactory = new SafeAccountFactory( + overrides.safeAccountFactoryAddress, + ); + } else { + safeAccountFactory = new SafeAccountFactory(); + } - let [ - sender, - safeAccountFactoryAddress, - factoryData - ] = SafeAccount.createAccountAddressAndFactoryAddressAndData( - owners, - overrides, - overrides.safe4337ModuleAddress ?? SafeAccountV0_2_0.DEFAULT_SAFE_4337_MODULE_ADDRESS, - overrides.safeModuleSetupddress ?? SafeAccountV0_2_0.DEFAULT_SAFE_MODULE_SETUP_ADDRESS, - ); + let [sender, safeAccountFactoryAddress, factoryData] = + SafeAccount.createAccountAddressAndFactoryAddressAndData( + owners, + overrides, + overrides.safe4337ModuleAddress ?? + SafeAccountV0_2_0.DEFAULT_SAFE_4337_MODULE_ADDRESS, + overrides.safeModuleSetupddress ?? + SafeAccountV0_2_0.DEFAULT_SAFE_MODULE_SETUP_ADDRESS, + ); - let initCode = - safeAccountFactoryAddress + factoryData.slice(2); - return [sender, initCode]; - } + let initCode = safeAccountFactoryAddress + factoryData.slice(2); + return [sender, initCode]; + } - public static createInitializerCallData( + public static createInitializerCallData( owners: Signer[], threshold: number, - overrides:{ - safe4337ModuleAddress?: string, - safeModuleSetupddress?: string, - multisendContractAddress?: string, - webAuthnSharedSigner?:string, - eip7212WebAuthPrecompileVerifierForSharedSigner?:string, - eip7212WebAuthContractVerifierForSharedSigner?:string, - } = {} + overrides: { + safe4337ModuleAddress?: string; + safeModuleSetupddress?: string; + multisendContractAddress?: string; + webAuthnSharedSigner?: string; + eip7212WebAuthPrecompileVerifierForSharedSigner?: string; + eip7212WebAuthContractVerifierForSharedSigner?: string; + } = {}, ): string { - const safe4337ModuleAddress = overrides.safe4337ModuleAddress?? - SafeAccountV0_2_0.DEFAULT_SAFE_4337_MODULE_ADDRESS; - const safeModuleSetupddress = overrides.safeModuleSetupddress?? - SafeAccountV0_2_0.DEFAULT_SAFE_MODULE_SETUP_ADDRESS; + const safe4337ModuleAddress = + overrides.safe4337ModuleAddress ?? + SafeAccountV0_2_0.DEFAULT_SAFE_4337_MODULE_ADDRESS; + const safeModuleSetupddress = + overrides.safeModuleSetupddress ?? + SafeAccountV0_2_0.DEFAULT_SAFE_MODULE_SETUP_ADDRESS; - return SafeAccount.createBaseInitializerCallData( - owners, - threshold, - safe4337ModuleAddress, - safeModuleSetupddress, - overrides.multisendContractAddress, - overrides.webAuthnSharedSigner, - overrides.eip7212WebAuthPrecompileVerifierForSharedSigner, - overrides.eip7212WebAuthContractVerifierForSharedSigner, - ); - } + return SafeAccount.createBaseInitializerCallData( + owners, + threshold, + safe4337ModuleAddress, + safeModuleSetupddress, + overrides.multisendContractAddress, + overrides.webAuthnSharedSigner, + overrides.eip7212WebAuthPrecompileVerifierForSharedSigner, + overrides.eip7212WebAuthContractVerifierForSharedSigner, + ); + } - /** + /** * create account initcode * @param owners - list of account owners addresses * @param overrides - overrides values to change default values - * @returns initcode + * @returns initcode */ public static createInitCode( owners: Signer[], overrides: InitCodeOverrides, - ): string { - let [ - safeAccountFactoryAddress, - factoryData - ] = SafeAccount.createFactoryAddressAndData( - owners, - overrides, - overrides.safe4337ModuleAddress ?? SafeAccountV0_2_0.DEFAULT_SAFE_4337_MODULE_ADDRESS, - overrides.safeModuleSetupddress ?? SafeAccountV0_2_0.DEFAULT_SAFE_MODULE_SETUP_ADDRESS, - ); - return safeAccountFactoryAddress + factoryData.slice(2); - } + ): string { + let [safeAccountFactoryAddress, factoryData] = + SafeAccount.createFactoryAddressAndData( + owners, + overrides, + overrides.safe4337ModuleAddress ?? + SafeAccountV0_2_0.DEFAULT_SAFE_4337_MODULE_ADDRESS, + overrides.safeModuleSetupddress ?? + SafeAccountV0_2_0.DEFAULT_SAFE_MODULE_SETUP_ADDRESS, + ); + return safeAccountFactoryAddress + factoryData.slice(2); + } - /** + /** * createUserOperation will determine the nonce, fetch the gas prices, * estimate gas limits and return a useroperation to be signed. * you can override all these values using the overrides parameter. @@ -238,38 +239,35 @@ export class SafeAccountV0_2_0 extends SafeAccount { bundlerRpc?: string, overrides: CreateUserOperationV6Overrides = {}, ): Promise { - let [ - userOperation, - factoryAddress, - factoryData - ] = await this.createBaseUserOperationAndFactoryAddressAndFactoryData( - transactions, - true, - providerRpc, - bundlerRpc, - overrides - ); + let [userOperation, factoryAddress, factoryData] = + await this.createBaseUserOperationAndFactoryAddressAndFactoryData( + transactions, + true, + providerRpc, + bundlerRpc, + overrides, + ); - let initCode = "0x"; + let initCode = "0x"; - if(overrides.initCode == null){ - if (factoryAddress != null){ - let factoryDataStr = "0x"; - if (factoryData != null){ - factoryDataStr = factoryData; - } - initCode = factoryAddress + factoryDataStr.slice(2); - } - }else{ - initCode = overrides.initCode; - } + if (overrides.initCode == null) { + if (factoryAddress != null) { + let factoryDataStr = "0x"; + if (factoryData != null) { + factoryDataStr = factoryData; + } + initCode = factoryAddress + factoryDataStr.slice(2); + } + } else { + initCode = overrides.initCode; + } - const userOperationV6: UserOperationV6 = { + const userOperationV6: UserOperationV6 = { ...userOperation, initCode, - paymasterAndData: "0x" + paymasterAndData: "0x", }; - return userOperationV6; - } + return userOperationV6; + } } diff --git a/src/account/Safe/SafeAccountV0_3_0.ts b/src/account/Safe/SafeAccountV0_3_0.ts index 7fac47a..e05a288 100644 --- a/src/account/Safe/SafeAccountV0_3_0.ts +++ b/src/account/Safe/SafeAccountV0_3_0.ts @@ -2,35 +2,37 @@ import { SafeAccount } from "./SafeAccount"; import { InitCodeOverrides, Signer, - CreateUserOperationV7Overrides, + CreateUserOperationV7Overrides, } from "./types"; import { UserOperationV7, MetaTransaction } from "../../types"; import { ENTRYPOINT_V7 } from "src/constants"; export class SafeAccountV0_3_0 extends SafeAccount { - static readonly DEFAULT_ENTRYPOINT_ADDRESS = ENTRYPOINT_V7; + static readonly DEFAULT_ENTRYPOINT_ADDRESS = ENTRYPOINT_V7; static readonly DEFAULT_SAFE_4337_MODULE_ADDRESS = "0x75cf11467937ce3F2f357CE24ffc3DBF8fD5c226"; - static readonly DEFAULT_SAFE_MODULE_SETUP_ADDRESS = + static readonly DEFAULT_SAFE_MODULE_SETUP_ADDRESS = "0x2dd68b007B46fBe91B9A7c3EDa5A7a1063cB5b47"; - constructor( + constructor( accountAddress: string, - overrides:{ - safe4337ModuleAddress?: string, - entrypointAddress?: string, - } = {} + overrides: { + safe4337ModuleAddress?: string; + entrypointAddress?: string; + } = {}, ) { - const safe4337ModuleAddress = overrides.safe4337ModuleAddress?? - SafeAccountV0_3_0.DEFAULT_SAFE_4337_MODULE_ADDRESS; - const entrypointAddress = overrides.entrypointAddress?? - SafeAccountV0_3_0.DEFAULT_ENTRYPOINT_ADDRESS; + const safe4337ModuleAddress = + overrides.safe4337ModuleAddress ?? + SafeAccountV0_3_0.DEFAULT_SAFE_4337_MODULE_ADDRESS; + const entrypointAddress = + overrides.entrypointAddress ?? + SafeAccountV0_3_0.DEFAULT_ENTRYPOINT_ADDRESS; super(accountAddress, safe4337ModuleAddress, entrypointAddress); } - - /** + + /** * calculate account addressfrom initial owners * @param owners - list of account owners addresses * @param overrides - override values to change the initialization default values @@ -40,18 +42,20 @@ export class SafeAccountV0_3_0 extends SafeAccount { owners: Signer[], overrides: InitCodeOverrides = {}, ): string { - const [accountAddress, , ] = - SafeAccount.createAccountAddressAndFactoryAddressAndData( - owners, - overrides, - overrides.safe4337ModuleAddress ?? SafeAccountV0_3_0.DEFAULT_SAFE_4337_MODULE_ADDRESS, - overrides.safeModuleSetupddress ?? SafeAccountV0_3_0.DEFAULT_SAFE_MODULE_SETUP_ADDRESS, - ); - - return accountAddress; - } - - /** + const [accountAddress, ,] = + SafeAccount.createAccountAddressAndFactoryAddressAndData( + owners, + overrides, + overrides.safe4337ModuleAddress ?? + SafeAccountV0_3_0.DEFAULT_SAFE_4337_MODULE_ADDRESS, + overrides.safeModuleSetupddress ?? + SafeAccountV0_3_0.DEFAULT_SAFE_MODULE_SETUP_ADDRESS, + ); + + return accountAddress; + } + + /** * To create and initialize a SafeAccount object from its * initial owners * @remarks @@ -68,105 +72,102 @@ export class SafeAccountV0_3_0 extends SafeAccount { let isInitWebAuthn = false; let x = 0n; let y = 0n; - for(const owner of owners){ - if(typeof(owner) != "string"){ - if (isInitWebAuthn) { - throw RangeError( - "Only one Webauth signer is allowed during initialization" - ); - } + for (const owner of owners) { + if (typeof owner != "string") { + if (isInitWebAuthn) { + throw RangeError( + "Only one Webauth signer is allowed during initialization", + ); + } isInitWebAuthn = true; x = owner.x; y = owner.y; } } - const [accountAddress, factoryAddress, factoryData] = - SafeAccountV0_3_0.createAccountAddressAndFactoryAddressAndData( - owners, - overrides, - overrides.safe4337ModuleAddress ?? SafeAccountV0_3_0.DEFAULT_SAFE_4337_MODULE_ADDRESS, - overrides.safeModuleSetupddress ?? SafeAccountV0_3_0.DEFAULT_SAFE_MODULE_SETUP_ADDRESS, - ); - - const safe = new SafeAccountV0_3_0( - accountAddress, - { - safe4337ModuleAddress:overrides.safe4337ModuleAddress, - entrypointAddress:overrides.entrypointAddress, - } - ); + const [accountAddress, factoryAddress, factoryData] = + SafeAccountV0_3_0.createAccountAddressAndFactoryAddressAndData( + owners, + overrides, + overrides.safe4337ModuleAddress ?? + SafeAccountV0_3_0.DEFAULT_SAFE_4337_MODULE_ADDRESS, + overrides.safeModuleSetupddress ?? + SafeAccountV0_3_0.DEFAULT_SAFE_MODULE_SETUP_ADDRESS, + ); + + const safe = new SafeAccountV0_3_0(accountAddress, { + safe4337ModuleAddress: overrides.safe4337ModuleAddress, + entrypointAddress: overrides.entrypointAddress, + }); safe.factoryAddress = factoryAddress; safe.factoryData = factoryData; - if(isInitWebAuthn){ - safe.isInitWebAuthn = isInitWebAuthn; - safe.x = x; - safe.y = y; - } - + if (isInitWebAuthn) { + safe.isInitWebAuthn = isInitWebAuthn; + safe.x = x; + safe.y = y; + } + return safe; } - public static getUserOperationEip712Hash( + public static getUserOperationEip712Hash( useroperation: UserOperationV7, - chainId:bigint, - overrides:{ - validAfter?: bigint, - validUntil?: bigint, - entrypointAddress?: string, - safe4337ModuleAddress?: string, - } = {} - ): string{ - const validAfter = overrides.validAfter??0n; - const validUntil = overrides.validUntil??0n; - const entrypointAddress = overrides.entrypointAddress?? - SafeAccountV0_3_0.DEFAULT_ENTRYPOINT_ADDRESS; - const safe4337ModuleAddress = - overrides.safe4337ModuleAddress?? - SafeAccountV0_3_0.DEFAULT_SAFE_4337_MODULE_ADDRESS; - - return SafeAccount.getUserOperationEip712Hash( - useroperation, - chainId, - { - validAfter, - validUntil, - entrypointAddress, - safe4337ModuleAddress - } - ) - } - - - public static createInitializerCallData( + chainId: bigint, + overrides: { + validAfter?: bigint; + validUntil?: bigint; + entrypointAddress?: string; + safe4337ModuleAddress?: string; + } = {}, + ): string { + const validAfter = overrides.validAfter ?? 0n; + const validUntil = overrides.validUntil ?? 0n; + const entrypointAddress = + overrides.entrypointAddress ?? + SafeAccountV0_3_0.DEFAULT_ENTRYPOINT_ADDRESS; + const safe4337ModuleAddress = + overrides.safe4337ModuleAddress ?? + SafeAccountV0_3_0.DEFAULT_SAFE_4337_MODULE_ADDRESS; + + return SafeAccount.getUserOperationEip712Hash(useroperation, chainId, { + validAfter, + validUntil, + entrypointAddress, + safe4337ModuleAddress, + }); + } + + public static createInitializerCallData( owners: Signer[], threshold: number, - overrides:{ - safe4337ModuleAddress?: string, - safeModuleSetupddress?: string, - multisendContractAddress?: string, - webAuthnSharedSigner?:string, - eip7212WebAuthPrecompileVerifierForSharedSigner?:string, - eip7212WebAuthContractVerifierForSharedSigner?:string, - } = {} + overrides: { + safe4337ModuleAddress?: string; + safeModuleSetupddress?: string; + multisendContractAddress?: string; + webAuthnSharedSigner?: string; + eip7212WebAuthPrecompileVerifierForSharedSigner?: string; + eip7212WebAuthContractVerifierForSharedSigner?: string; + } = {}, ): string { - const safe4337ModuleAddress = overrides.safe4337ModuleAddress?? - SafeAccountV0_3_0.DEFAULT_SAFE_4337_MODULE_ADDRESS; - const safeModuleSetupddress = overrides.safeModuleSetupddress?? - SafeAccountV0_3_0.DEFAULT_SAFE_MODULE_SETUP_ADDRESS; - - return SafeAccount.createBaseInitializerCallData( - owners, - threshold, - safe4337ModuleAddress, - safeModuleSetupddress, - overrides.multisendContractAddress, - overrides.webAuthnSharedSigner, - overrides.eip7212WebAuthPrecompileVerifierForSharedSigner, - overrides.eip7212WebAuthContractVerifierForSharedSigner, - ); - } - - /** + const safe4337ModuleAddress = + overrides.safe4337ModuleAddress ?? + SafeAccountV0_3_0.DEFAULT_SAFE_4337_MODULE_ADDRESS; + const safeModuleSetupddress = + overrides.safeModuleSetupddress ?? + SafeAccountV0_3_0.DEFAULT_SAFE_MODULE_SETUP_ADDRESS; + + return SafeAccount.createBaseInitializerCallData( + owners, + threshold, + safe4337ModuleAddress, + safeModuleSetupddress, + overrides.multisendContractAddress, + overrides.webAuthnSharedSigner, + overrides.eip7212WebAuthPrecompileVerifierForSharedSigner, + overrides.eip7212WebAuthContractVerifierForSharedSigner, + ); + } + + /** * create account factory address and factory data * @param owners - list of account owners addresses * @param overrides - override values to change the initialization default values @@ -175,16 +176,18 @@ export class SafeAccountV0_3_0 extends SafeAccount { public static createFactoryAddressAndData( owners: Signer[], overrides: InitCodeOverrides, - ): [string, string] { - return SafeAccount.createFactoryAddressAndData( - owners, - overrides, - overrides.safe4337ModuleAddress ?? SafeAccountV0_3_0.DEFAULT_SAFE_4337_MODULE_ADDRESS, - overrides.safeModuleSetupddress ?? SafeAccountV0_3_0.DEFAULT_SAFE_MODULE_SETUP_ADDRESS, - ); - } - - /** + ): [string, string] { + return SafeAccount.createFactoryAddressAndData( + owners, + overrides, + overrides.safe4337ModuleAddress ?? + SafeAccountV0_3_0.DEFAULT_SAFE_4337_MODULE_ADDRESS, + overrides.safeModuleSetupddress ?? + SafeAccountV0_3_0.DEFAULT_SAFE_MODULE_SETUP_ADDRESS, + ); + } + + /** * createUserOperation will determine the nonce, fetch the gas prices, * estimate gas limits and return a useroperation to be signed. * you can override all these values using the overrides parameter. @@ -200,28 +203,25 @@ export class SafeAccountV0_3_0 extends SafeAccount { bundlerRpc?: string, overrides: CreateUserOperationV7Overrides = {}, ): Promise { - let [ - userOperation, - factoryAddress, - factoryData - ] = await this.createBaseUserOperationAndFactoryAddressAndFactoryData( - transactions, - false, - providerRpc, - bundlerRpc, - overrides - ); - - const userOperationV7: UserOperationV7 = { + let [userOperation, factoryAddress, factoryData] = + await this.createBaseUserOperationAndFactoryAddressAndFactoryData( + transactions, + false, + providerRpc, + bundlerRpc, + overrides, + ); + + const userOperationV7: UserOperationV7 = { ...userOperation, - factory: factoryAddress, - factoryData, - paymaster: null, - paymasterVerificationGasLimit: null, - paymasterPostOpGasLimit: null, - paymasterData: null, + factory: factoryAddress, + factoryData, + paymaster: null, + paymasterVerificationGasLimit: null, + paymasterPostOpGasLimit: null, + paymasterData: null, }; - return userOperationV7; - } + return userOperationV7; + } } diff --git a/src/account/Safe/types.ts b/src/account/Safe/types.ts index b9e0005..21a6026 100644 --- a/src/account/Safe/types.ts +++ b/src/account/Safe/types.ts @@ -33,22 +33,23 @@ export interface CreateBaseUserOperationOverrides { /** pass some state overrides for gas estimation"*/ state_override_set?: StateOverrideSet; - dummySignatures?: SignerSignaturePair[], + dummySignatures?: SignerSignaturePair[]; - webAuthnSharedSigner?:string, - webAuthnSignerFactory?:string, - webAuthnSignerSingleton?:string, + webAuthnSharedSigner?: string; + webAuthnSignerFactory?: string; + webAuthnSignerSingleton?: string; - eip7212WebAuthPrecompileVerifier?:string, - eip7212WebAuthContractVerifier?:string, - safeModuleExecutorFunctionSelector?: SafeModuleExecutorFunctionSelector, - multisendContractAddress?: string, + eip7212WebAuthPrecompileVerifier?: string; + eip7212WebAuthContractVerifier?: string; + safeModuleExecutorFunctionSelector?: SafeModuleExecutorFunctionSelector; + multisendContractAddress?: string; } /** * Overrides for the "createUserOperation" function */ -export interface CreateUserOperationV6Overrides extends CreateBaseUserOperationOverrides{ +export interface CreateUserOperationV6Overrides + extends CreateBaseUserOperationOverrides { /** set the initCode instead of using the calculated value */ initCode?: string; } @@ -56,16 +57,17 @@ export interface CreateUserOperationV6Overrides extends CreateBaseUserOperationO /** * Overrides for the "createUserOperation" function */ -export interface CreateUserOperationV7Overrides extends CreateBaseUserOperationOverrides{ +export interface CreateUserOperationV7Overrides + extends CreateBaseUserOperationOverrides { /** set the factory address instead of using the calculated value */ factory?: string; - /** set the factory data instead of using the calculated value */ + /** set the factory data instead of using the calculated value */ factoryData?: string; } export interface SafeAccountSingleton { - singletonAddress:string; - singletonInitHash:string; + singletonAddress: string; + singletonInitHash: string; } /** @@ -80,10 +82,10 @@ export interface InitCodeOverrides { * @defaultValue 0 */ c2Nonce?: bigint; - safe4337ModuleAddress?: string; - safeModuleSetupddress?: string; + safe4337ModuleAddress?: string; + safeModuleSetupddress?: string; - entrypointAddress?: string; + entrypointAddress?: string; /** Safe contract singleton address * @defaultValue "0x29fcB43b46531BcA003ddC8FCB67FFE91900C762" */ @@ -95,10 +97,10 @@ export interface InitCodeOverrides { /** Safe 4337 module address * @defaultValue "0xa581c4A4DB7175302464fF3C06380BC3270b4037" */ - multisendContractAddress?: string; - webAuthnSharedSigner?: string; - eip7212WebAuthPrecompileVerifierForSharedSigner?:string; - eip7212WebAuthContractVerifierForSharedSigner?:string; + multisendContractAddress?: string; + webAuthnSharedSigner?: string; + eip7212WebAuthPrecompileVerifierForSharedSigner?: string; + eip7212WebAuthContractVerifierForSharedSigner?: string; } export interface BaseInitOverrides { @@ -111,7 +113,7 @@ export interface BaseInitOverrides { */ c2Nonce?: bigint; - safeAccountSingleton?: SafeAccountSingleton; + safeAccountSingleton?: SafeAccountSingleton; /** Safe Factory address * @defaultValue "0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67" */ @@ -119,21 +121,21 @@ export interface BaseInitOverrides { /** Safe 4337 module address * @defaultValue "0xa581c4A4DB7175302464fF3C06380BC3270b4037" */ - multisendContractAddress?: string; - webAuthnSharedSigner?: string; - eip7212WebAuthPrecompileVerifierForSharedSigner?:string; - eip7212WebAuthContractVerifierForSharedSigner?:string; + multisendContractAddress?: string; + webAuthnSharedSigner?: string; + eip7212WebAuthPrecompileVerifierForSharedSigner?: string; + eip7212WebAuthContractVerifierForSharedSigner?: string; } export interface WebAuthnSignatureOverrides { - isInit?:boolean, - webAuthnSharedSigner?:string, - eip7212WebAuthPrecompileVerifier?:string, - eip7212WebAuthContractVerifier?:string, - webAuthnSignerFactory?:string, - webAuthnSignerSingleton?:string, - validAfter?: bigint, - validUntil?: bigint, + isInit?: boolean; + webAuthnSharedSigner?: string; + eip7212WebAuthPrecompileVerifier?: string; + eip7212WebAuthContractVerifier?: string; + webAuthnSignerFactory?: string; + webAuthnSignerSingleton?: string; + validAfter?: bigint; + validUntil?: bigint; } /** @@ -167,51 +169,52 @@ export interface SafeUserOperationV6TypedDataValues { export interface SafeUserOperationV7TypedDataValues { safe: string; nonce: bigint; - initCode: string; + initCode: string; callData: string; - verificationGasLimit: bigint; - callGasLimit: bigint; + verificationGasLimit: bigint; + callGasLimit: bigint; preVerificationGas: bigint; - maxPriorityFeePerGas: bigint; - maxFeePerGas: bigint; + maxPriorityFeePerGas: bigint; + maxFeePerGas: bigint; paymasterAndData: string; validAfter: bigint; validUntil: bigint; entryPoint: string; } -export type ECDSAPublicAddress = string +export type ECDSAPublicAddress = string; export interface WebauthPublicKey { - x:bigint, - y:bigint, + x: bigint; + y: bigint; } -export type Signer = ECDSAPublicAddress | WebauthPublicKey +export type Signer = ECDSAPublicAddress | WebauthPublicKey; -export type ECDSASignature = string +export type ECDSASignature = string; export interface WebauthSignatureData { - authenticatorData: ArrayBuffer - clientDataFields: string - rs: [bigint, bigint] + authenticatorData: ArrayBuffer; + clientDataFields: string; + rs: [bigint, bigint]; } export interface SignerSignaturePair { - signer: Signer - signature: string - isContractSignature?: boolean + signer: Signer; + signature: string; + isContractSignature?: boolean; } -export const EOADummySignature : SignerSignaturePair = { +export const EOADummySignature: SignerSignaturePair = { signer: "0xfD90FAd33ee8b58f32c00aceEad1358e4AFC23f9", - signature: "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - isContractSignature: false -} + signature: + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + isContractSignature: false, +}; export const WebauthDummySignerSignaturePair: SignerSignaturePair = { signer: "0xfD90FAd33ee8b58f32c00aceEad1358e4AFC23f9", - signature: "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e06c92f0ac5c4ef9e74721c23d80a9fc12f259ca84afb160f0890483539b9e6080d824c0e6c795157ad5d1ee5eff1ceeb3031009a595f9360919b83dd411c5a78d0000000000000000000000000000000000000000000000000000000000000025a24f744b28d73f066bf3203d145765a7bc735e6328168c8b03e476da3ad0d8fe0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e226f726967696e223a2268747470733a2f2f736166652e676c6f62616c220000", - isContractSignature: true -} - + signature: + "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e06c92f0ac5c4ef9e74721c23d80a9fc12f259ca84afb160f0890483539b9e6080d824c0e6c795157ad5d1ee5eff1ceeb3031009a595f9360919b83dd411c5a78d0000000000000000000000000000000000000000000000000000000000000025a24f744b28d73f066bf3203d145765a7bc735e6328168c8b03e476da3ad0d8fe0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e226f726967696e223a2268747470733a2f2f736166652e676c6f62616c220000", + isContractSignature: true, +}; diff --git a/src/constants.ts b/src/constants.ts index cd0e41b..56d16cd 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -6,9 +6,10 @@ export const ENTRYPOINT_V7 = "0x0000000071727De22E5E9d8BAf0edAc6f37da032"; export const ENTRYPOINT_V6 = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"; export const Safe_L2_V1_4_1: SafeAccountSingleton = { - singletonAddress:"0x29fcB43b46531BcA003ddC8FCB67FFE91900C762", - singletonInitHash:"0xe298282cefe913ab5d282047161268a8222e4bd4ed106300c547894bbefd31ee" -} + singletonAddress: "0x29fcB43b46531BcA003ddC8FCB67FFE91900C762", + singletonInitHash: + "0xe298282cefe913ab5d282047161268a8222e4bd4ed106300c547894bbefd31ee", +}; export const BaseUserOperationDummyValues = { //dummy values for somewhat accurate gas estimation diff --git a/src/errors.ts b/src/errors.ts index 2ad211a..be22e3a 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -82,14 +82,18 @@ export class AbstractionKitError extends Error { this.context = context; } - //get a string representation of AbstractionKitError - //Usefull with React Native, as Error "cause" is not shown in the error trace - stringify(): string { - return JSON.stringify( - this, - ["name", "code", "message", "cause", "errno", "context"] - ) - } + //get a string representation of AbstractionKitError + //Usefull with React Native, as Error "cause" is not shown in the error trace + stringify(): string { + return JSON.stringify(this, [ + "name", + "code", + "message", + "cause", + "errno", + "context", + ]); + } } export function ensureError(value: unknown): Error { diff --git a/src/factory/SafeAccountFactory.ts b/src/factory/SafeAccountFactory.ts index 912d1a2..d54a6b3 100644 --- a/src/factory/SafeAccountFactory.ts +++ b/src/factory/SafeAccountFactory.ts @@ -1,6 +1,7 @@ import { SmartAccountFactory } from "./SmartAccountFactory"; export class SafeAccountFactory extends SmartAccountFactory { - static readonly DEFAULT_FACTORY_ADDRESS= "0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67"; + static readonly DEFAULT_FACTORY_ADDRESS = + "0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67"; constructor(address: string = SafeAccountFactory.DEFAULT_FACTORY_ADDRESS) { const generatorFunctionSelector = "0x1688f0b9"; //createProxyWithNonce const generatorFunctionInputAbi = [ diff --git a/src/paymaster/CandidePaymaster.ts b/src/paymaster/CandidePaymaster.ts index 199e462..696965a 100644 --- a/src/paymaster/CandidePaymaster.ts +++ b/src/paymaster/CandidePaymaster.ts @@ -1,8 +1,5 @@ import { Paymaster } from "./Paymaster"; -import { - calculateUserOperationMaxGasCost, - sendJsonRpcRequest -} from "../utils"; +import { calculateUserOperationMaxGasCost, sendJsonRpcRequest } from "../utils"; import { UserOperationV6, UserOperationV7, @@ -13,24 +10,24 @@ import { PaymasterMetadataV7, PaymasterMetadataV6, ERC20Token, - SponsorMetadata, + SponsorMetadata, } from "../types"; import { CandidePaymasterContext, PrependTokenPaymasterApproveAccount, - CreatePaymasterUserOperationOverrides + CreatePaymasterUserOperationOverrides, } from "./types"; import { Bundler } from "src/Bundler"; import { AbstractionKitError, ensureError } from "src/errors"; import { ENTRYPOINT_V7, ENTRYPOINT_V6 } from "src/constants"; export class CandidePaymaster extends Paymaster { - readonly rpcUrl: string; - private version: "v3" | "v2"| "v1" | undefined; - private entrypointDataV7: SupportedERC20TokensAndMetadataV7| undefined; - private entrypointDataV6: SupportedERC20TokensAndMetadataV6| undefined; - private isInitilized = false; - + readonly rpcUrl: string; + private version: "v3" | "v2" | "v1" | undefined; + private entrypointDataV7: SupportedERC20TokensAndMetadataV7 | undefined; + private entrypointDataV6: SupportedERC20TokensAndMetadataV6 | undefined; + private isInitilized = false; + constructor(rpcUrl: string) { super(); this.rpcUrl = rpcUrl; @@ -43,51 +40,56 @@ export class CandidePaymaster extends Paymaster { */ private async initialize(): Promise { try { - const paymasterVersionJsonRpcResult = (await sendJsonRpcRequest( - this.rpcUrl, - "pm_clientVersion", - [], - )) as string; - - if(paymasterVersionJsonRpcResult.startsWith("Candide/v3")){ - this.version = "v3"; - }else if(paymasterVersionJsonRpcResult.startsWith("Candide/v2")){ - this.version = "v2"; - }else if(paymasterVersionJsonRpcResult.startsWith("Candide/v1")){ - this.version = "v1"; - }else{ - throw RangeError( - "Invalide paymaster version received from paymaster rpc"); - } + const paymasterVersionJsonRpcResult = (await sendJsonRpcRequest( + this.rpcUrl, + "pm_clientVersion", + [], + )) as string; + + if (paymasterVersionJsonRpcResult.startsWith("Candide/v3")) { + this.version = "v3"; + } else if (paymasterVersionJsonRpcResult.startsWith("Candide/v2")) { + this.version = "v2"; + } else if (paymasterVersionJsonRpcResult.startsWith("Candide/v1")) { + this.version = "v1"; + } else { + throw RangeError( + "Invalide paymaster version received from paymaster rpc", + ); + } const entrypointsAddresses = await this.getSupportedEntrypointsLive(); - if(entrypointsAddresses.some( - x => x.toLowerCase() === ENTRYPOINT_V7.toLowerCase()) - ){ - const supportedTokensAndMetadataResultV7 = - await this.getSupportedERC20TokensAndPaymasterMetadata( - ENTRYPOINT_V7); - - this.entrypointDataV7 = - supportedTokensAndMetadataResultV7 as SupportedERC20TokensAndMetadataV7??null; - } - - if(entrypointsAddresses.some( - x => x.toLowerCase() === ENTRYPOINT_V6.toLowerCase()) - ){ - const supportedTokensAndMetadataResultV6 = - await this.getSupportedERC20TokensAndPaymasterMetadata( - ENTRYPOINT_V6); - - this.entrypointDataV6 = - supportedTokensAndMetadataResultV6 as SupportedERC20TokensAndMetadataV6??null; - } - - if(this.entrypointDataV7 == null && this.entrypointDataV6 == null){ - throw RangeError("Invalide data received during initilization."); - } - this.isInitilized = true; - return null; + if ( + entrypointsAddresses.some( + (x) => x.toLowerCase() === ENTRYPOINT_V7.toLowerCase(), + ) + ) { + const supportedTokensAndMetadataResultV7 = + await this.getSupportedERC20TokensAndPaymasterMetadata(ENTRYPOINT_V7); + + this.entrypointDataV7 = + (supportedTokensAndMetadataResultV7 as SupportedERC20TokensAndMetadataV7) ?? + null; + } + + if ( + entrypointsAddresses.some( + (x) => x.toLowerCase() === ENTRYPOINT_V6.toLowerCase(), + ) + ) { + const supportedTokensAndMetadataResultV6 = + await this.getSupportedERC20TokensAndPaymasterMetadata(ENTRYPOINT_V6); + + this.entrypointDataV6 = + (supportedTokensAndMetadataResultV6 as SupportedERC20TokensAndMetadataV6) ?? + null; + } + + if (this.entrypointDataV7 == null && this.entrypointDataV6 == null) { + throw RangeError("Invalide data received during initilization."); + } + this.isInitilized = true; + return null; } catch (err) { const error = ensureError(err); @@ -100,55 +102,55 @@ export class CandidePaymaster extends Paymaster { ); } } - + private async getSupportedERC20TokensAndPaymasterMetadata( - entrypoint: string - ): Promise< - SupportedERC20TokensAndMetadataV7 |SupportedERC20TokensAndMetadataV6 | null - > { - if(!this.isInitilized){ + entrypoint: string, + ): Promise< + SupportedERC20TokensAndMetadataV7 | SupportedERC20TokensAndMetadataV6 | null + > { + if (!this.isInitilized) { try { - let jsonRpcResult; - if(this.version == "v3"){ - jsonRpcResult = (await sendJsonRpcRequest( - this.rpcUrl, - "pm_supportedERC20Tokens", - [entrypoint], - )); - }else{ - jsonRpcResult = (await sendJsonRpcRequest( - this.rpcUrl, - "pm_supportedERC20Tokens", - [], - )); - } - if(entrypoint == ENTRYPOINT_V7){ - jsonRpcResult = jsonRpcResult as SupportedERC20TokensAndMetadataV7; - return { - tokens: jsonRpcResult.tokens.map((gasToken) => ({ - name: gasToken.name, - symbol: gasToken.symbol, - address: gasToken.address, - decimal: Number(gasToken.decimal), - exchangeRate: BigInt(gasToken.exchangeRate), - })), - paymasterMetadata: jsonRpcResult.paymasterMetadata, - }; - }else if(entrypoint == ENTRYPOINT_V6){ - jsonRpcResult = jsonRpcResult as SupportedERC20TokensAndMetadataV6; - return { - tokens: jsonRpcResult.tokens.map((gasToken) => ({ - name: gasToken.name, - symbol: gasToken.symbol, - address: gasToken.address, - decimal: Number(gasToken.decimal), - exchangeRate: BigInt(gasToken.exchangeRate), - })), - paymasterMetadata: jsonRpcResult.paymasterMetadata, - }; - }else{ - throw RangeError("unsupported entrypoint."); - } + let jsonRpcResult; + if (this.version == "v3") { + jsonRpcResult = await sendJsonRpcRequest( + this.rpcUrl, + "pm_supportedERC20Tokens", + [entrypoint], + ); + } else { + jsonRpcResult = await sendJsonRpcRequest( + this.rpcUrl, + "pm_supportedERC20Tokens", + [], + ); + } + if (entrypoint == ENTRYPOINT_V7) { + jsonRpcResult = jsonRpcResult as SupportedERC20TokensAndMetadataV7; + return { + tokens: jsonRpcResult.tokens.map((gasToken) => ({ + name: gasToken.name, + symbol: gasToken.symbol, + address: gasToken.address, + decimal: Number(gasToken.decimal), + exchangeRate: BigInt(gasToken.exchangeRate), + })), + paymasterMetadata: jsonRpcResult.paymasterMetadata, + }; + } else if (entrypoint == ENTRYPOINT_V6) { + jsonRpcResult = jsonRpcResult as SupportedERC20TokensAndMetadataV6; + return { + tokens: jsonRpcResult.tokens.map((gasToken) => ({ + name: gasToken.name, + symbol: gasToken.symbol, + address: gasToken.address, + decimal: Number(gasToken.decimal), + exchangeRate: BigInt(gasToken.exchangeRate), + })), + paymasterMetadata: jsonRpcResult.paymasterMetadata, + }; + } else { + throw RangeError("unsupported entrypoint."); + } } catch (err) { const error = ensureError(err); @@ -161,75 +163,75 @@ export class CandidePaymaster extends Paymaster { ); } } else { - if(entrypoint == ENTRYPOINT_V7){ - return this.entrypointDataV7??null; - }else if(entrypoint == ENTRYPOINT_V6){ - return this.entrypointDataV6??null; - } + if (entrypoint == ENTRYPOINT_V7) { + return this.entrypointDataV7 ?? null; + } else if (entrypoint == ENTRYPOINT_V6) { + return this.entrypointDataV6 ?? null; + } } - return null; + return null; } private async getSupportedEntrypointsLive(): Promise { - try { - if(this.version == "v3"){ - const supportedEntrypoints = await sendJsonRpcRequest( - this.rpcUrl, - "pm_supportedEntryPoints", - [], - ); - - return supportedEntrypoints as string[]; - }else{ - const supportedEntrypoint = await sendJsonRpcRequest( - this.rpcUrl, - "pm_supportedEntryPoint", - [], - ); - - return [supportedEntrypoint] as string[]; - } - } catch (err) { - const error = ensureError(err); - - throw new AbstractionKitError( - "PAYMASTER_ERROR", - "getSupportedEntrypoint failed", - { - cause: error, - }, - ); - } - } - + try { + if (this.version == "v3") { + const supportedEntrypoints = await sendJsonRpcRequest( + this.rpcUrl, + "pm_supportedEntryPoints", + [], + ); + + return supportedEntrypoints as string[]; + } else { + const supportedEntrypoint = await sendJsonRpcRequest( + this.rpcUrl, + "pm_supportedEntryPoint", + [], + ); + + return [supportedEntrypoint] as string[]; + } + } catch (err) { + const error = ensureError(err); + + throw new AbstractionKitError( + "PAYMASTER_ERROR", + "getSupportedEntrypoint failed", + { + cause: error, + }, + ); + } + } + async getSupportedEntrypoints(): Promise { - if(!this.isInitilized){ + if (!this.isInitilized) { await this.initialize(); } - let suppotedEntrypoints = []; - if(this.entrypointDataV7 != null){ - suppotedEntrypoints.push(ENTRYPOINT_V7); - } - if(this.entrypointDataV6 != null){ - suppotedEntrypoints.push(ENTRYPOINT_V6); - } - return suppotedEntrypoints; - } - - async getPaymasterMetaData( - entrypoint: string - ): Promise { - if(!this.isInitilized){ + let suppotedEntrypoints = []; + if (this.entrypointDataV7 != null) { + suppotedEntrypoints.push(ENTRYPOINT_V7); + } + if (this.entrypointDataV6 != null) { + suppotedEntrypoints.push(ENTRYPOINT_V6); + } + return suppotedEntrypoints; + } + + async getPaymasterMetaData( + entrypoint: string, + ): Promise { + if (!this.isInitilized) { await this.initialize(); } - if(entrypoint == ENTRYPOINT_V7 && this.entrypointDataV7 != null){ - return this.entrypointDataV7.paymasterMetadata; - }else if(entrypoint == ENTRYPOINT_V6 && this.entrypointDataV6 != null){ - return this.entrypointDataV6.paymasterMetadata; - }else{ - throw RangeError("unsupported entrypoint."); - } + if (entrypoint == ENTRYPOINT_V7 && this.entrypointDataV7 != null) { + return this.entrypointDataV7.paymasterMetadata; + } else if (entrypoint == ENTRYPOINT_V6 && this.entrypointDataV6 != null) { + return this.entrypointDataV6.paymasterMetadata; + } else { + throw RangeError("unsupported entrypoint."); + } } /** @@ -237,12 +239,13 @@ export class CandidePaymaster extends Paymaster { * @param erc20TokenAddress - token address to check if supported */ async isSupportedERC20Token( - erc20TokenAddress: string, - entrypoint: string = ENTRYPOINT_V7 - ): Promise { + erc20TokenAddress: string, + entrypoint: string = ENTRYPOINT_V7, + ): Promise { const gasToken = this.getSupportedERC20TokenData( - erc20TokenAddress, entrypoint - ) + erc20TokenAddress, + entrypoint, + ); if (!gasToken) { return false; } else { @@ -257,19 +260,19 @@ export class CandidePaymaster extends Paymaster { */ async getSupportedERC20TokenData( erc20TokenAddress: string, - entrypoint: string = ENTRYPOINT_V7 + entrypoint: string = ENTRYPOINT_V7, ): Promise { - if(!this.isInitilized){ + if (!this.isInitilized) { await this.initialize(); } - let supportedTokens: ERC20Token[]; - if(entrypoint == ENTRYPOINT_V7 && this.entrypointDataV7 != null){ - supportedTokens = this.entrypointDataV7.tokens; - }else if(entrypoint == ENTRYPOINT_V6 && this.entrypointDataV6 != null){ - supportedTokens = this.entrypointDataV6.tokens; - }else{ - throw RangeError("unsupported entrypoint."); - } + let supportedTokens: ERC20Token[]; + if (entrypoint == ENTRYPOINT_V7 && this.entrypointDataV7 != null) { + supportedTokens = this.entrypointDataV7.tokens; + } else if (entrypoint == ENTRYPOINT_V6 && this.entrypointDataV6 != null) { + supportedTokens = this.entrypointDataV6.tokens; + } else { + throw RangeError("unsupported entrypoint."); + } const gasToken = supportedTokens.find( (token) => @@ -292,303 +295,292 @@ export class CandidePaymaster extends Paymaster { /** * createPaymasterUserOperation will estimate gas and set * paymasterAndData - * gas limits will only change if the estimated gas limits returned by - * the bundler is more than the input gas limits, then gas overrides - * and multipliers will be applied + * gas limits will only change if the estimated gas limits returned by + * the bundler is more than the input gas limits, then gas overrides + * and multipliers will be applied * @param useroperation - useroperation to add paymaster support for * @param bundlerRpc - bundler rpc for gas estimation * @param context - paymaster context data * @param createPaymasterUserOperationOverrides - createPaymasterUserOperationOverrides values to change default values * @returns promise with UserOperation */ - async createPaymasterUserOperation( + async createPaymasterUserOperation( userOperationInput: UserOperationV7, bundlerRpc: string, context?: CandidePaymasterContext, - createPaymasterUserOperationOverrides?:CreatePaymasterUserOperationOverrides + createPaymasterUserOperationOverrides?: CreatePaymasterUserOperationOverrides, ): Promise<[UserOperationV7, SponsorMetadata | undefined]>; - async createPaymasterUserOperation( + async createPaymasterUserOperation( userOperationInput: UserOperationV6, bundlerRpc: string, context?: CandidePaymasterContext, - createPaymasterUserOperationOverrides?:CreatePaymasterUserOperationOverrides + createPaymasterUserOperationOverrides?: CreatePaymasterUserOperationOverrides, ): Promise<[UserOperationV6, SponsorMetadata | undefined]>; async createPaymasterUserOperation( userOperationInput: UserOperationV7 | UserOperationV6, bundlerRpc: string, context?: CandidePaymasterContext, - createPaymasterUserOperationOverrides?:CreatePaymasterUserOperationOverrides + createPaymasterUserOperationOverrides?: CreatePaymasterUserOperationOverrides, ): Promise<[UserOperationV7 | UserOperationV6, SponsorMetadata | undefined]> { - if(context == null){ - context = {} - } - if(createPaymasterUserOperationOverrides == null){ - createPaymasterUserOperationOverrides = {} - } - let userOperation = {...userOperationInput}; - if(!this.isInitilized){ + if (context == null) { + context = {}; + } + if (createPaymasterUserOperationOverrides == null) { + createPaymasterUserOperationOverrides = {}; + } + let userOperation = { ...userOperationInput }; + if (!this.isInitilized) { await this.initialize(); } - let sponsorMetadata = undefined; - try { - let entrypointAddress:string; - if('initCode' in userOperation){ - if(this.entrypointDataV6 == null){ - throw RangeError("useroperation v0.06 is not supported"); - } - entrypointAddress = ENTRYPOINT_V6; - const paymasterMetadata = this.entrypointDataV6.paymasterMetadata; - userOperation.paymasterAndData = paymasterMetadata.dummyPaymasterAndData; - - let preVerificationGas = userOperation.preVerificationGas; - let verificationGasLimit = userOperation.verificationGasLimit; - let callGasLimit = userOperation.callGasLimit; - - //call the bundler to estimate gas if one of the gas overrides - //is not provided - if ( - createPaymasterUserOperationOverrides.preVerificationGas == null || - createPaymasterUserOperationOverrides.verificationGasLimit == null || - createPaymasterUserOperationOverrides.callGasLimit == null - ) { - - if (bundlerRpc != null) { - const bundler = new Bundler(bundlerRpc); - - userOperation.callGasLimit = 0n - userOperation.verificationGasLimit = 0n - userOperation.preVerificationGas = 0n - const inputMaxFeePerGas = userOperation.maxFeePerGas - const inputMaxPriorityFeePerGas = userOperation.maxPriorityFeePerGas - userOperation.maxFeePerGas = 0n - userOperation.maxPriorityFeePerGas = 0n - const estimation = - await bundler.estimateUserOperationGas( - userOperation, - entrypointAddress as string, - createPaymasterUserOperationOverrides.state_override_set, - ); - - // only change gas limits if the esitmated limits is higher than - // the supplied - if(preVerificationGas < estimation.preVerificationGas){ - preVerificationGas = estimation.preVerificationGas; - } - if(verificationGasLimit < estimation.verificationGasLimit){ - verificationGasLimit = estimation.verificationGasLimit; - } - if(callGasLimit < estimation.callGasLimit){ - callGasLimit = estimation.callGasLimit; - } - - userOperation.maxFeePerGas = inputMaxFeePerGas - userOperation.maxPriorityFeePerGas = inputMaxPriorityFeePerGas - } else { - throw new AbstractionKitError( - "BAD_DATA", - "bundlerRpc cant't be null if preVerificationGas,verificationGasLimit and callGasLimit are not overriden", - ); - } - } - - //check gas overrides type and range - if ( - typeof createPaymasterUserOperationOverrides.preVerificationGas - === "bigint" && - createPaymasterUserOperationOverrides.preVerificationGas < 0n - ) { - throw RangeError("preVerificationGas overrid can't be negative"); - } - - if ( - typeof createPaymasterUserOperationOverrides.verificationGasLimit - === "bigint" && - createPaymasterUserOperationOverrides.verificationGasLimit < 0n - ) { - throw RangeError("verificationGasLimit overrid can't be negative"); - } - - if ( - typeof createPaymasterUserOperationOverrides.callGasLimit - === "bigint" && - createPaymasterUserOperationOverrides.callGasLimit < 0n - ) { - throw RangeError("callGasLimit overrid can't be negative"); - } - - //apply gas overrides - userOperation.preVerificationGas = - createPaymasterUserOperationOverrides.preVerificationGas ?? - ( - preVerificationGas * - BigInt( - ((createPaymasterUserOperationOverrides.preVerificationGasPercentageMultiplier ?? 0) + 100) - ) - )/100n; - - userOperation.verificationGasLimit = - createPaymasterUserOperationOverrides.verificationGasLimit ?? - ( - verificationGasLimit * - BigInt( - ((createPaymasterUserOperationOverrides.verificationGasLimitPercentageMultiplier ?? 0) + 100) - ) - )/100n; - - userOperation.callGasLimit = - createPaymasterUserOperationOverrides.callGasLimit ?? - ( - callGasLimit * - BigInt( - ((createPaymasterUserOperationOverrides.callGasLimitPercentageMultiplier ?? 0) + 100) - ) - )/100n; - - //add small buffer to preVerification gas - userOperation.preVerificationGas = userOperation.preVerificationGas + 100n; - //add gas to compensate for paymasterAndData verification overhead - userOperation.verificationGasLimit = - userOperation.verificationGasLimit + 10000n; - //call the paymaster rpc to sponsor the useroperation - const jsonRpcResult = await sendJsonRpcRequest( - this.rpcUrl, - "pm_sponsorUserOperation", - [userOperation, entrypointAddress, context], - ); - const result = jsonRpcResult as PmUserOperationV6Result; - const resultMod = { - paymasterAndData: result.paymasterAndData, - //the paymaster may decide to override the gas prices and gas limits - callGasLimit: - result.callGasLimit == null - ? undefined - : BigInt(result.callGasLimit), - preVerificationGas: - result.preVerificationGas == null - ? undefined - : BigInt(result.preVerificationGas), - verificationGasLimit: - result.verificationGasLimit == null - ? undefined - : BigInt(result.verificationGasLimit), - maxFeePerGas: - result.maxFeePerGas == null - ? undefined - : BigInt(result.maxFeePerGas), - maxPriorityFeePerGas: - result.maxPriorityFeePerGas == null - ? undefined - : BigInt(result.maxPriorityFeePerGas), - sponsorMetadata: - result.sponsorMetadata == null - ? undefined - : result.sponsorMetadata, - - }; - - //override gas limits and gas prices if the paymaster modified them - //needed in case the paymaster modifies the useroperation before - //generating the paymasterAndData - userOperation.paymasterAndData = resultMod.paymasterAndData; - userOperation.callGasLimit = - resultMod.callGasLimit ?? userOperation.callGasLimit; - userOperation.preVerificationGas = - resultMod.preVerificationGas ?? userOperation.preVerificationGas; - userOperation.verificationGasLimit = - resultMod.verificationGasLimit ?? userOperation.verificationGasLimit; - userOperation.maxFeePerGas = - resultMod.maxFeePerGas ?? userOperation.maxFeePerGas; - userOperation.maxPriorityFeePerGas = - resultMod.maxPriorityFeePerGas ?? userOperation.maxPriorityFeePerGas; - sponsorMetadata = resultMod.sponsorMetadata; - - return [ - userOperation satisfies UserOperationV6, - sponsorMetadata - ]; - }else{ - if(this.entrypointDataV7 == null){ - throw RangeError("useroperation v0.07 is not supported"); - } - entrypointAddress = ENTRYPOINT_V7; - const paymasterMetadata = this.entrypointDataV7.paymasterMetadata; - let paymasterAndData = paymasterMetadata.dummyPaymasterAndData; - userOperation.paymaster = paymasterAndData.paymaster; - userOperation.paymasterVerificationGasLimit = - paymasterAndData.paymasterVerificationGasLimit; - userOperation.paymasterPostOpGasLimit = paymasterAndData.paymasterPostOpGasLimit; - userOperation.paymasterData = paymasterAndData.paymasterData; - - - //call the paymaster rpc to sponsor the useroperation - const jsonRpcResult = await sendJsonRpcRequest( - this.rpcUrl, - "pm_sponsorUserOperation", - [userOperation, entrypointAddress, context], - ); - - const result = jsonRpcResult as PmUserOperationV7Result; - const resultMod = { - paymaster: result.paymaster, - paymasterVerificationGasLimit: - BigInt(result.paymasterVerificationGasLimit), - paymasterPostOpGasLimit: - BigInt(result.paymasterPostOpGasLimit), - paymasterData: result.paymasterData, - //the paymaster may decide to override the gas prices and gas limits - callGasLimit: - result.callGasLimit == null - ? undefined - : BigInt(result.callGasLimit), - preVerificationGas: - result.preVerificationGas == null - ? undefined - : BigInt(result.preVerificationGas), - verificationGasLimit: - result.verificationGasLimit == null - ? undefined - : BigInt(result.verificationGasLimit), - maxFeePerGas: - result.maxFeePerGas == null - ? undefined - : BigInt(result.maxFeePerGas), - maxPriorityFeePerGas: - result.maxPriorityFeePerGas == null - ? undefined - : BigInt(result.maxPriorityFeePerGas), - sponsorMetadata: - result.sponsorMetadata == null - ? undefined - : result.sponsorMetadata, - - }; - - //override gas limits and gas prices if the paymaster modified them - //needed in case the paymaster modifies the useroperation before - //generating the paymasterAndData - userOperation.paymaster = resultMod.paymaster; - userOperation.paymasterVerificationGasLimit = - resultMod.paymasterVerificationGasLimit; - userOperation.paymasterPostOpGasLimit = - resultMod.paymasterPostOpGasLimit; - userOperation.paymasterData = resultMod.paymasterData; - userOperation.callGasLimit = - resultMod.callGasLimit ?? userOperation.callGasLimit; - userOperation.preVerificationGas = - resultMod.preVerificationGas ?? userOperation.preVerificationGas; - userOperation.verificationGasLimit = - resultMod.verificationGasLimit ?? userOperation.verificationGasLimit; - userOperation.maxFeePerGas = - resultMod.maxFeePerGas ?? userOperation.maxFeePerGas; - userOperation.maxPriorityFeePerGas = - resultMod.maxPriorityFeePerGas ?? userOperation.maxPriorityFeePerGas; - sponsorMetadata = resultMod.sponsorMetadata; - - return [ - userOperation satisfies UserOperationV7, - sponsorMetadata - ]; - } + let sponsorMetadata = undefined; + try { + let entrypointAddress: string; + if ("initCode" in userOperation) { + if (this.entrypointDataV6 == null) { + throw RangeError("useroperation v0.06 is not supported"); + } + entrypointAddress = ENTRYPOINT_V6; + const paymasterMetadata = this.entrypointDataV6.paymasterMetadata; + userOperation.paymasterAndData = + paymasterMetadata.dummyPaymasterAndData; + + let preVerificationGas = userOperation.preVerificationGas; + let verificationGasLimit = userOperation.verificationGasLimit; + let callGasLimit = userOperation.callGasLimit; + + //call the bundler to estimate gas if one of the gas overrides + //is not provided + if ( + createPaymasterUserOperationOverrides.preVerificationGas == null || + createPaymasterUserOperationOverrides.verificationGasLimit == null || + createPaymasterUserOperationOverrides.callGasLimit == null + ) { + if (bundlerRpc != null) { + const bundler = new Bundler(bundlerRpc); + + userOperation.callGasLimit = 0n; + userOperation.verificationGasLimit = 0n; + userOperation.preVerificationGas = 0n; + const inputMaxFeePerGas = userOperation.maxFeePerGas; + const inputMaxPriorityFeePerGas = + userOperation.maxPriorityFeePerGas; + userOperation.maxFeePerGas = 0n; + userOperation.maxPriorityFeePerGas = 0n; + const estimation = await bundler.estimateUserOperationGas( + userOperation, + entrypointAddress as string, + createPaymasterUserOperationOverrides.state_override_set, + ); + + // only change gas limits if the esitmated limits is higher than + // the supplied + if (preVerificationGas < estimation.preVerificationGas) { + preVerificationGas = estimation.preVerificationGas; + } + if (verificationGasLimit < estimation.verificationGasLimit) { + verificationGasLimit = estimation.verificationGasLimit; + } + if (callGasLimit < estimation.callGasLimit) { + callGasLimit = estimation.callGasLimit; + } + + userOperation.maxFeePerGas = inputMaxFeePerGas; + userOperation.maxPriorityFeePerGas = inputMaxPriorityFeePerGas; + } else { + throw new AbstractionKitError( + "BAD_DATA", + "bundlerRpc cant't be null if preVerificationGas,verificationGasLimit and callGasLimit are not overriden", + ); + } + } + + //check gas overrides type and range + if ( + typeof createPaymasterUserOperationOverrides.preVerificationGas === + "bigint" && + createPaymasterUserOperationOverrides.preVerificationGas < 0n + ) { + throw RangeError("preVerificationGas overrid can't be negative"); + } + + if ( + typeof createPaymasterUserOperationOverrides.verificationGasLimit === + "bigint" && + createPaymasterUserOperationOverrides.verificationGasLimit < 0n + ) { + throw RangeError("verificationGasLimit overrid can't be negative"); + } + + if ( + typeof createPaymasterUserOperationOverrides.callGasLimit === + "bigint" && + createPaymasterUserOperationOverrides.callGasLimit < 0n + ) { + throw RangeError("callGasLimit overrid can't be negative"); + } + + //apply gas overrides + userOperation.preVerificationGas = + createPaymasterUserOperationOverrides.preVerificationGas ?? + (preVerificationGas * + BigInt( + (createPaymasterUserOperationOverrides.preVerificationGasPercentageMultiplier ?? + 0) + 100, + )) / + 100n; + + userOperation.verificationGasLimit = + createPaymasterUserOperationOverrides.verificationGasLimit ?? + (verificationGasLimit * + BigInt( + (createPaymasterUserOperationOverrides.verificationGasLimitPercentageMultiplier ?? + 0) + 100, + )) / + 100n; + + userOperation.callGasLimit = + createPaymasterUserOperationOverrides.callGasLimit ?? + (callGasLimit * + BigInt( + (createPaymasterUserOperationOverrides.callGasLimitPercentageMultiplier ?? + 0) + 100, + )) / + 100n; + + //add small buffer to preVerification gas + userOperation.preVerificationGas = + userOperation.preVerificationGas + 100n; + //add gas to compensate for paymasterAndData verification overhead + userOperation.verificationGasLimit = + userOperation.verificationGasLimit + 10000n; + //call the paymaster rpc to sponsor the useroperation + const jsonRpcResult = await sendJsonRpcRequest( + this.rpcUrl, + "pm_sponsorUserOperation", + [userOperation, entrypointAddress, context], + ); + const result = jsonRpcResult as PmUserOperationV6Result; + const resultMod = { + paymasterAndData: result.paymasterAndData, + //the paymaster may decide to override the gas prices and gas limits + callGasLimit: + result.callGasLimit == null + ? undefined + : BigInt(result.callGasLimit), + preVerificationGas: + result.preVerificationGas == null + ? undefined + : BigInt(result.preVerificationGas), + verificationGasLimit: + result.verificationGasLimit == null + ? undefined + : BigInt(result.verificationGasLimit), + maxFeePerGas: + result.maxFeePerGas == null + ? undefined + : BigInt(result.maxFeePerGas), + maxPriorityFeePerGas: + result.maxPriorityFeePerGas == null + ? undefined + : BigInt(result.maxPriorityFeePerGas), + sponsorMetadata: + result.sponsorMetadata == null ? undefined : result.sponsorMetadata, + }; + + //override gas limits and gas prices if the paymaster modified them + //needed in case the paymaster modifies the useroperation before + //generating the paymasterAndData + userOperation.paymasterAndData = resultMod.paymasterAndData; + userOperation.callGasLimit = + resultMod.callGasLimit ?? userOperation.callGasLimit; + userOperation.preVerificationGas = + resultMod.preVerificationGas ?? userOperation.preVerificationGas; + userOperation.verificationGasLimit = + resultMod.verificationGasLimit ?? userOperation.verificationGasLimit; + userOperation.maxFeePerGas = + resultMod.maxFeePerGas ?? userOperation.maxFeePerGas; + userOperation.maxPriorityFeePerGas = + resultMod.maxPriorityFeePerGas ?? userOperation.maxPriorityFeePerGas; + sponsorMetadata = resultMod.sponsorMetadata; + + return [userOperation satisfies UserOperationV6, sponsorMetadata]; + } else { + if (this.entrypointDataV7 == null) { + throw RangeError("useroperation v0.07 is not supported"); + } + entrypointAddress = ENTRYPOINT_V7; + const paymasterMetadata = this.entrypointDataV7.paymasterMetadata; + let paymasterAndData = paymasterMetadata.dummyPaymasterAndData; + userOperation.paymaster = paymasterAndData.paymaster; + userOperation.paymasterVerificationGasLimit = + paymasterAndData.paymasterVerificationGasLimit; + userOperation.paymasterPostOpGasLimit = + paymasterAndData.paymasterPostOpGasLimit; + userOperation.paymasterData = paymasterAndData.paymasterData; + + //call the paymaster rpc to sponsor the useroperation + const jsonRpcResult = await sendJsonRpcRequest( + this.rpcUrl, + "pm_sponsorUserOperation", + [userOperation, entrypointAddress, context], + ); + + const result = jsonRpcResult as PmUserOperationV7Result; + const resultMod = { + paymaster: result.paymaster, + paymasterVerificationGasLimit: BigInt( + result.paymasterVerificationGasLimit, + ), + paymasterPostOpGasLimit: BigInt(result.paymasterPostOpGasLimit), + paymasterData: result.paymasterData, + //the paymaster may decide to override the gas prices and gas limits + callGasLimit: + result.callGasLimit == null + ? undefined + : BigInt(result.callGasLimit), + preVerificationGas: + result.preVerificationGas == null + ? undefined + : BigInt(result.preVerificationGas), + verificationGasLimit: + result.verificationGasLimit == null + ? undefined + : BigInt(result.verificationGasLimit), + maxFeePerGas: + result.maxFeePerGas == null + ? undefined + : BigInt(result.maxFeePerGas), + maxPriorityFeePerGas: + result.maxPriorityFeePerGas == null + ? undefined + : BigInt(result.maxPriorityFeePerGas), + sponsorMetadata: + result.sponsorMetadata == null ? undefined : result.sponsorMetadata, + }; + + //override gas limits and gas prices if the paymaster modified them + //needed in case the paymaster modifies the useroperation before + //generating the paymasterAndData + userOperation.paymaster = resultMod.paymaster; + userOperation.paymasterVerificationGasLimit = + resultMod.paymasterVerificationGasLimit; + userOperation.paymasterPostOpGasLimit = + resultMod.paymasterPostOpGasLimit; + userOperation.paymasterData = resultMod.paymasterData; + userOperation.callGasLimit = + resultMod.callGasLimit ?? userOperation.callGasLimit; + userOperation.preVerificationGas = + resultMod.preVerificationGas ?? userOperation.preVerificationGas; + userOperation.verificationGasLimit = + resultMod.verificationGasLimit ?? userOperation.verificationGasLimit; + userOperation.maxFeePerGas = + resultMod.maxFeePerGas ?? userOperation.maxFeePerGas; + userOperation.maxPriorityFeePerGas = + resultMod.maxPriorityFeePerGas ?? userOperation.maxPriorityFeePerGas; + sponsorMetadata = resultMod.sponsorMetadata; + + return [userOperation satisfies UserOperationV7, sponsorMetadata]; + } } catch (err) { const error = ensureError(err); @@ -610,36 +602,36 @@ export class CandidePaymaster extends Paymaster { * @param createPaymasterUserOperationOverrides - createPaymasterUserOperationOverrides values to change default values * @returns promise with UserOperation */ - async createSponsorPaymasterUserOperation( + async createSponsorPaymasterUserOperation( userOperation: UserOperationV7, bundlerRpc: string, - createPaymasterUserOperationOverrides?:CreatePaymasterUserOperationOverrides + createPaymasterUserOperationOverrides?: CreatePaymasterUserOperationOverrides, ): Promise<[UserOperationV7, SponsorMetadata | undefined]>; - async createSponsorPaymasterUserOperation( + async createSponsorPaymasterUserOperation( userOperation: UserOperationV6, bundlerRpc: string, - createPaymasterUserOperationOverrides?:CreatePaymasterUserOperationOverrides + createPaymasterUserOperationOverrides?: CreatePaymasterUserOperationOverrides, ): Promise<[UserOperationV6, SponsorMetadata | undefined]>; async createSponsorPaymasterUserOperation( userOperation: UserOperationV7 | UserOperationV6, bundlerRpc: string, - createPaymasterUserOperationOverrides:CreatePaymasterUserOperationOverrides = {} + createPaymasterUserOperationOverrides: CreatePaymasterUserOperationOverrides = {}, ): Promise<[UserOperationV7 | UserOperationV6, SponsorMetadata | undefined]> { - if("initCode"in userOperation){ - return await this.createPaymasterUserOperation( - userOperation as UserOperationV6, - bundlerRpc, - {}, - createPaymasterUserOperationOverrides, - ); - }else{ - return await this.createPaymasterUserOperation( - userOperation as UserOperationV7, - bundlerRpc, - {}, - createPaymasterUserOperationOverrides, - ); - } + if ("initCode" in userOperation) { + return await this.createPaymasterUserOperation( + userOperation as UserOperationV6, + bundlerRpc, + {}, + createPaymasterUserOperationOverrides, + ); + } else { + return await this.createPaymasterUserOperation( + userOperation as UserOperationV7, + bundlerRpc, + {}, + createPaymasterUserOperationOverrides, + ); + } } /** @@ -652,55 +644,55 @@ export class CandidePaymaster extends Paymaster { * @param createPaymasterUserOperationOverrides - createPaymasterUserOperationOverrides values to change default values * @returns promise with UserOperation */ - async createTokenPaymasterUserOperation( + async createTokenPaymasterUserOperation( smartAccount: PrependTokenPaymasterApproveAccount, userOperation: UserOperationV7, tokenAddress: string, bundlerRpc: string, - createPaymasterUserOperationOverrides?:CreatePaymasterUserOperationOverrides + createPaymasterUserOperationOverrides?: CreatePaymasterUserOperationOverrides, ): Promise; - async createTokenPaymasterUserOperation( + async createTokenPaymasterUserOperation( smartAccount: PrependTokenPaymasterApproveAccount, userOperation: UserOperationV6, tokenAddress: string, bundlerRpc: string, - createPaymasterUserOperationOverrides?:CreatePaymasterUserOperationOverrides + createPaymasterUserOperationOverrides?: CreatePaymasterUserOperationOverrides, ): Promise; async createTokenPaymasterUserOperation( smartAccount: PrependTokenPaymasterApproveAccount, userOperation: UserOperationV7 | UserOperationV6, tokenAddress: string, bundlerRpc: string, - createPaymasterUserOperationOverrides:CreatePaymasterUserOperationOverrides = {} + createPaymasterUserOperationOverrides: CreatePaymasterUserOperationOverrides = {}, ): Promise { try { - if(!this.isInitilized){ - await this.initialize(); - } - - let entrypoint; - if('initCode' in userOperation){ - entrypoint = ENTRYPOINT_V6; - }else{ - entrypoint = ENTRYPOINT_V7; - } - const maxErc20Cost = - await this.calculateUserOperationErc20TokenMaxGasCost( - userOperation, - tokenAddress, - ); - - const approveAmount = maxErc20Cost * 2n; //for the extra cost of the paymasterAndData - - const metadata = await this.getPaymasterMetaData(entrypoint); - - if(metadata == null){ - throw RangeError("unsupported entrypoint."); - } + if (!this.isInitilized) { + await this.initialize(); + } + + let entrypoint; + if ("initCode" in userOperation) { + entrypoint = ENTRYPOINT_V6; + } else { + entrypoint = ENTRYPOINT_V7; + } + const maxErc20Cost = + await this.calculateUserOperationErc20TokenMaxGasCost( + userOperation, + tokenAddress, + ); + + const approveAmount = maxErc20Cost * 2n; //for the extra cost of the paymasterAndData + + const metadata = await this.getPaymasterMetaData(entrypoint); + + if (metadata == null) { + throw RangeError("unsupported entrypoint."); + } const paymasterAddress = metadata.address; - const callDataWithApprove = + const callDataWithApprove = smartAccount.prependTokenPaymasterApproveToCallData( userOperation.callData, tokenAddress, @@ -709,27 +701,27 @@ export class CandidePaymaster extends Paymaster { ); userOperation.callData = callDataWithApprove; - if("initCode"in userOperation){ - let [resultUserOp, ] = await this.createPaymasterUserOperation( - userOperation as UserOperationV6, - bundlerRpc, - { - token: tokenAddress, - }, - createPaymasterUserOperationOverrides, - ); - return resultUserOp; - }else{ - let [resultUserOp, ] = await this.createPaymasterUserOperation( - userOperation as UserOperationV7, - bundlerRpc, - { - token: tokenAddress, - }, - createPaymasterUserOperationOverrides, - ); - return resultUserOp; - } + if ("initCode" in userOperation) { + let [resultUserOp] = await this.createPaymasterUserOperation( + userOperation as UserOperationV6, + bundlerRpc, + { + token: tokenAddress, + }, + createPaymasterUserOperationOverrides, + ); + return resultUserOp; + } else { + let [resultUserOp] = await this.createPaymasterUserOperation( + userOperation as UserOperationV7, + bundlerRpc, + { + token: tokenAddress, + }, + createPaymasterUserOperationOverrides, + ); + return resultUserOp; + } } catch (err) { const error = ensureError(err); @@ -748,19 +740,20 @@ export class CandidePaymaster extends Paymaster { erc20TokenAddress: string, ): Promise { try { - if(!this.isInitilized){ - await this.initialize(); - } + if (!this.isInitilized) { + await this.initialize(); + } - let entrypoint; - if('initCode' in userOperation){ - entrypoint = ENTRYPOINT_V6; - }else{ - entrypoint = ENTRYPOINT_V7; - } + let entrypoint; + if ("initCode" in userOperation) { + entrypoint = ENTRYPOINT_V6; + } else { + entrypoint = ENTRYPOINT_V7; + } const supportedERC20TokensData = await this.getSupportedERC20TokenData( - erc20TokenAddress, entrypoint + erc20TokenAddress, + entrypoint, ); if (supportedERC20TokensData == null) { throw new AbstractionKitError( @@ -771,13 +764,13 @@ export class CandidePaymaster extends Paymaster { supportedERC20TokensAndPaymasterMetadataV7: JSON.stringify( this.entrypointDataV7, (_key, value) => - typeof value === "bigint" ? "0x" + value.toString(16) : value + typeof value === "bigint" ? "0x" + value.toString(16) : value, ), - supportedERC20TokensAndPaymasterMetadataV6: JSON.stringify( + supportedERC20TokensAndPaymasterMetadataV6: JSON.stringify( this.entrypointDataV6, (_key, value) => - typeof value === "bigint" ? "0x" + value.toString(16) : value - ) + typeof value === "bigint" ? "0x" + value.toString(16) : value, + ), }, }, ); diff --git a/src/paymaster/Paymaster.ts b/src/paymaster/Paymaster.ts index c9675dc..9fde5f6 100644 --- a/src/paymaster/Paymaster.ts +++ b/src/paymaster/Paymaster.ts @@ -1,11 +1,18 @@ -import type { UserOperationV7, UserOperationV6, SponsorMetadata } from "../types"; -import { CandidePaymasterContext, CreatePaymasterUserOperationOverrides} from "./types"; +import type { + UserOperationV7, + UserOperationV6, + SponsorMetadata, +} from "../types"; +import { + CandidePaymasterContext, + CreatePaymasterUserOperationOverrides, +} from "./types"; export abstract class Paymaster { abstract createPaymasterUserOperation( userOperation: UserOperationV7 | UserOperationV6, bundlerRpc: string, context: CandidePaymasterContext, - createPaymasterUserOperationOverrides:CreatePaymasterUserOperationOverrides - ): Promise<[UserOperationV7 | UserOperationV6, SponsorMetadata | undefined]>; + createPaymasterUserOperationOverrides: CreatePaymasterUserOperationOverrides, + ): Promise<[UserOperationV7 | UserOperationV6, SponsorMetadata | undefined]>; } diff --git a/src/paymaster/types.ts b/src/paymaster/types.ts index 0241d55..4d41edf 100644 --- a/src/paymaster/types.ts +++ b/src/paymaster/types.ts @@ -23,15 +23,14 @@ export interface CreatePaymasterUserOperationOverrides { verificationGasLimit?: bigint; /** set the preVerificationGas instead of estimating gas using the bundler*/ preVerificationGas?: bigint; - - /** set the callGasLimitPercentageMultiplier instead of estimating gas using the bundler*/ + + /** set the callGasLimitPercentageMultiplier instead of estimating gas using the bundler*/ callGasLimitPercentageMultiplier?: number; /** set the verificationGasLimitPercentageMultiplier instead of estimating gas using the bundler*/ verificationGasLimitPercentageMultiplier?: number; /** set the preVerificationGasPercentageMultiplier instead of estimating gas using the bundler*/ preVerificationGasPercentageMultiplier?: number; - - /** pass some state overrides for gas estimation"*/ + + /** pass some state overrides for gas estimation"*/ state_override_set?: StateOverrideSet; } - diff --git a/src/types.ts b/src/types.ts index be51b09..4e204e2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -11,7 +11,7 @@ export interface BaseUserOperation { maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; signature: string; -}; +} /** * Wrapper for a useroperation for an entrypoint v0.06 @@ -19,8 +19,7 @@ export interface BaseUserOperation { export interface UserOperationV6 extends BaseUserOperation { initCode: string; paymasterAndData: string; -}; - +} /** * Wrapper for a useroperation for an entrypoint v0.07 @@ -28,11 +27,11 @@ export interface UserOperationV6 extends BaseUserOperation { export interface UserOperationV7 extends BaseUserOperation { factory: string | null; factoryData: string | null; - paymaster: string | null; - paymasterVerificationGasLimit: bigint | null - paymasterPostOpGasLimit: bigint | null - paymasterData: string | null -}; + paymaster: string | null; + paymasterVerificationGasLimit: bigint | null; + paymasterPostOpGasLimit: bigint | null; + paymasterData: string | null; +} export type AbiInputValue = | string @@ -112,23 +111,23 @@ export type UserOperationReceiptResult = { } | null; export type SponsorMetadata = { - name: string, - description: string, - url: string, - icons: string[], -} + name: string; + description: string; + url: string; + icons: string[]; +}; export type PmUserOperationV7Result = { - paymaster: string, - paymasterVerificationGasLimit: bigint, - paymasterPostOpGasLimit: bigint, - paymasterData: string, - callGasLimit?: bigint, - verificationGasLimit?: bigint, - preVerificationGas?: bigint, - maxFeePerGas?: bigint, - maxPriorityFeePerGas?: bigint, - sponsorMetadata?: SponsorMetadata + paymaster: string; + paymasterVerificationGasLimit: bigint; + paymasterPostOpGasLimit: bigint; + paymasterData: string; + callGasLimit?: bigint; + verificationGasLimit?: bigint; + preVerificationGas?: bigint; + maxFeePerGas?: bigint; + maxPriorityFeePerGas?: bigint; + sponsorMetadata?: SponsorMetadata; }; export type PmUserOperationV6Result = { @@ -138,7 +137,7 @@ export type PmUserOperationV6Result = { verificationGasLimit?: bigint; maxFeePerGas?: bigint; maxPriorityFeePerGas?: bigint; - sponsorMetadata?: SponsorMetadata + sponsorMetadata?: SponsorMetadata; }; /** @@ -190,11 +189,11 @@ interface BasePaymasterMetadata { export interface PaymasterMetadataV7 extends BasePaymasterMetadata { /** dummyPaymasterAndData to use for gas estimation */ dummyPaymasterAndData: { - paymaster: string, - paymasterVerificationGasLimit: bigint, - paymasterPostOpGasLimit: bigint, - paymasterData: string - }; + paymaster: string; + paymasterVerificationGasLimit: bigint; + paymasterPostOpGasLimit: bigint; + paymasterData: string; + }; } export interface PaymasterMetadataV6 extends BasePaymasterMetadata { diff --git a/src/utils.ts b/src/utils.ts index c3866a5..d19680b 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -10,7 +10,7 @@ import { JsonRpcError, GasOption, JsonRpcResult, - UserOperationV7, + UserOperationV7, } from "./types"; import { AbstractionKitError, @@ -30,18 +30,17 @@ export function createUserOperationHash( entrypointAddress: string, chainId: bigint, ): string { - let packedUserOperationHash: string; - if('initCode' in useroperation){ - packedUserOperationHash = keccak256( - createPackedUserOperationV6(useroperation), - ); - }else{ - packedUserOperationHash = keccak256( - createPackedUserOperationV7(useroperation), - ); - - } - + let packedUserOperationHash: string; + if ("initCode" in useroperation) { + packedUserOperationHash = keccak256( + createPackedUserOperationV6(useroperation), + ); + } else { + packedUserOperationHash = keccak256( + createPackedUserOperationV7(useroperation), + ); + } + const abiCoder = AbiCoder.defaultAbiCoder(); const encodedUserOperationHash = abiCoder.encode( ["bytes32", "address", "uint256"], @@ -93,7 +92,6 @@ export function createPackedUserOperationV6( return packedUserOperation; } - /** * createPackedUserOperation for the standard entrypointv0.7 hash * @param useroperation -useroperation to pack @@ -102,91 +100,69 @@ export function createPackedUserOperationV6( export function createPackedUserOperationV7( useroperation: UserOperationV7, ): string { - const abiCoder = AbiCoder.defaultAbiCoder(); - - let initCode = "0x"; - if(useroperation.factory != null){ - initCode = useroperation.factory; - if(useroperation.factoryData != null){ - initCode += useroperation.factoryData.slice(2); - } - } - - let accountGasLimits = "0x" + - abiCoder.encode( - ["uint128"], - [ - useroperation.verificationGasLimit, - ] - ).slice(34) + - abiCoder.encode( - ["uint128"], - [ - useroperation.callGasLimit - ] - ).slice(34); - - let gasFees = "0x" + - abiCoder.encode( - ["uint128"], - [ - useroperation.maxPriorityFeePerGas, - ] - ).slice(34) + - abiCoder.encode( - ["uint128"], - [ - useroperation.maxFeePerGas - ] - ).slice(34); - - let paymasterAndData = "0x"; - if(useroperation.paymaster != null){ - paymasterAndData = useroperation.paymaster; - if(useroperation.paymasterVerificationGasLimit != null){ - paymasterAndData += - abiCoder.encode( - ["uint128"], - [ - useroperation.paymasterVerificationGasLimit - ] - ).slice(34); - } - if(useroperation.paymasterPostOpGasLimit != null){ - paymasterAndData += - abiCoder.encode( - ["uint128"], - [ - useroperation.paymasterPostOpGasLimit - ] - ).slice(34); - } - if(useroperation.paymasterData != null){ - paymasterAndData += useroperation.paymasterData.slice(2); - } - } + const abiCoder = AbiCoder.defaultAbiCoder(); + + let initCode = "0x"; + if (useroperation.factory != null) { + initCode = useroperation.factory; + if (useroperation.factoryData != null) { + initCode += useroperation.factoryData.slice(2); + } + } + + let accountGasLimits = + "0x" + + abiCoder + .encode(["uint128"], [useroperation.verificationGasLimit]) + .slice(34) + + abiCoder.encode(["uint128"], [useroperation.callGasLimit]).slice(34); + + let gasFees = + "0x" + + abiCoder + .encode(["uint128"], [useroperation.maxPriorityFeePerGas]) + .slice(34) + + abiCoder.encode(["uint128"], [useroperation.maxFeePerGas]).slice(34); + + let paymasterAndData = "0x"; + if (useroperation.paymaster != null) { + paymasterAndData = useroperation.paymaster; + if (useroperation.paymasterVerificationGasLimit != null) { + paymasterAndData += abiCoder + .encode(["uint128"], [useroperation.paymasterVerificationGasLimit]) + .slice(34); + } + if (useroperation.paymasterPostOpGasLimit != null) { + paymasterAndData += abiCoder + .encode(["uint128"], [useroperation.paymasterPostOpGasLimit]) + .slice(34); + } + if (useroperation.paymasterData != null) { + paymasterAndData += useroperation.paymasterData.slice(2); + } + } const useroperationValuesArrayWithHashedByteValues = [ useroperation.sender, useroperation.nonce, keccak256(initCode), keccak256(useroperation.callData), - accountGasLimits, + accountGasLimits, useroperation.preVerificationGas, - gasFees, + gasFees, keccak256(paymasterAndData), ]; const packedUserOperation = abiCoder.encode( [ - "address", - "uint256", - "bytes32", - "bytes32", - "bytes32", - "uint256", - "bytes32", - "bytes32", + "address", + "uint256", + "bytes32", + "bytes32", + "bytes32", + "uint256", + "bytes32", + "bytes32", ], useroperationValuesArrayWithHashedByteValues, ); @@ -236,7 +212,7 @@ export async function sendJsonRpcRequest( ); const requestOptions: RequestInit = { method: "POST", - headers: { 'Content-Type': 'application/json' }, + headers: { "Content-Type": "application/json" }, body: raw, redirect: "follow", }; @@ -373,121 +349,116 @@ export async function fetchGasPrice( export function calculateUserOperationMaxGasCost( useroperation: UserOperationV6 | UserOperationV7, ): bigint { - if ('initCode' in useroperation) { - const isPaymasterAndData = - useroperation.paymasterAndData == "0x" || - useroperation.paymasterAndData == null; - const mul = isPaymasterAndData ? 3n : 0n; - const requiredGas = - useroperation.callGasLimit + - useroperation.verificationGasLimit * mul + - useroperation.preVerificationGas; - return requiredGas * useroperation.maxFeePerGas; - }else{ - const requiredGas = useroperation.verificationGasLimit + - useroperation.callGasLimit + - (useroperation.paymasterVerificationGasLimit ?? 0n) + - (useroperation.paymasterPostOpGasLimit ?? 0n) + - useroperation.preVerificationGas; - - return requiredGas * useroperation.maxFeePerGas; - } + if ("initCode" in useroperation) { + const isPaymasterAndData = + useroperation.paymasterAndData == "0x" || + useroperation.paymasterAndData == null; + const mul = isPaymasterAndData ? 3n : 0n; + const requiredGas = + useroperation.callGasLimit + + useroperation.verificationGasLimit * mul + + useroperation.preVerificationGas; + return requiredGas * useroperation.maxFeePerGas; + } else { + const requiredGas = + useroperation.verificationGasLimit + + useroperation.callGasLimit + + (useroperation.paymasterVerificationGasLimit ?? 0n) + + (useroperation.paymasterPostOpGasLimit ?? 0n) + + useroperation.preVerificationGas; + + return requiredGas * useroperation.maxFeePerGas; + } } type EthCallTransaction = { - from?:string; - to:string; - gas?:bigint; - gasPrice?:bigint; - value?:bigint; - data?:string; -} + from?: string; + to: string; + gas?: bigint; + gasPrice?: bigint; + value?: bigint; + data?: string; +}; export async function sendEthCallRequest( - nodeRpcUrl: string, - ethCallTransaction: EthCallTransaction, - blockNumber: string|bigint, + nodeRpcUrl: string, + ethCallTransaction: EthCallTransaction, + blockNumber: string | bigint, ): Promise { - const params = [ - ethCallTransaction, - blockNumber - ]; - - try { - const data = await sendJsonRpcRequest(nodeRpcUrl, "eth_call", params); - - if (typeof data === "string") { - try { - return data; - } catch (err) { - const error = ensureError(err); - - throw new AbstractionKitError( - "BAD_DATA", - "eth_call returned ill formed data", - { - cause: error, - }, - ); - } - } else { - throw new AbstractionKitError( - "BAD_DATA", - "eth_call returned ill formed data", - { - context: JSON.stringify(data), - }, - ); - } - } catch (err) { - const error = ensureError(err); - - throw new AbstractionKitError("BAD_DATA", "eth_call failed", { - cause: error, - }); - } + const params = [ethCallTransaction, blockNumber]; + + try { + const data = await sendJsonRpcRequest(nodeRpcUrl, "eth_call", params); + + if (typeof data === "string") { + try { + return data; + } catch (err) { + const error = ensureError(err); + + throw new AbstractionKitError( + "BAD_DATA", + "eth_call returned ill formed data", + { + cause: error, + }, + ); + } + } else { + throw new AbstractionKitError( + "BAD_DATA", + "eth_call returned ill formed data", + { + context: JSON.stringify(data), + }, + ); + } + } catch (err) { + const error = ensureError(err); + + throw new AbstractionKitError("BAD_DATA", "eth_call failed", { + cause: error, + }); + } } export async function sendEthGetCodeRequest( - nodeRpcUrl: string, - contractAddress: string, - blockNumber: string|bigint, + nodeRpcUrl: string, + contractAddress: string, + blockNumber: string | bigint, ): Promise { - const params = [ - contractAddress, - blockNumber - ]; - - try { - const data = await sendJsonRpcRequest(nodeRpcUrl, "eth_getCode", params); - - if (typeof data === "string") { - try { - return data; - } catch (err) { - const error = ensureError(err); - - throw new AbstractionKitError( - "BAD_DATA", - "eth_getCode returned ill formed data", - { - cause: error, - }, - ); - } - } else { - throw new AbstractionKitError( - "BAD_DATA", - "eth_getCode returned ill formed data", - { - context: JSON.stringify(data), - }, - ); - } - } catch (err) { - const error = ensureError(err); - - throw new AbstractionKitError("BAD_DATA", "eth_getCode failed", { - cause: error, - }); - } + const params = [contractAddress, blockNumber]; + + try { + const data = await sendJsonRpcRequest(nodeRpcUrl, "eth_getCode", params); + + if (typeof data === "string") { + try { + return data; + } catch (err) { + const error = ensureError(err); + + throw new AbstractionKitError( + "BAD_DATA", + "eth_getCode returned ill formed data", + { + cause: error, + }, + ); + } + } else { + throw new AbstractionKitError( + "BAD_DATA", + "eth_getCode returned ill formed data", + { + context: JSON.stringify(data), + }, + ); + } + } catch (err) { + const error = ensureError(err); + + throw new AbstractionKitError("BAD_DATA", "eth_getCode failed", { + cause: error, + }); + } }