@safe-global/relay-kit
Version:
SDK for Safe Smart Accounts with support for ERC-4337 and Relay
1,134 lines (1,115 loc) • 58 kB
JavaScript
var __defProp = Object.defineProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// test-utils/fixtures.ts
var fixtures_exports = {};
__export(fixtures_exports, {
BUNDLER_URL: () => BUNDLER_URL,
CHAIN_ID: () => CHAIN_ID,
ENTRYPOINT_ADDRESS_V06: () => ENTRYPOINT_ADDRESS_V06,
ENTRYPOINT_ADDRESS_V07: () => ENTRYPOINT_ADDRESS_V07,
GAS_ESTIMATION: () => GAS_ESTIMATION,
OWNER_1: () => OWNER_1,
OWNER_2: () => OWNER_2,
PAYMASTER_ADDRESS: () => PAYMASTER_ADDRESS,
PAYMASTER_TOKEN_ADDRESS: () => PAYMASTER_TOKEN_ADDRESS,
PAYMASTER_URL: () => PAYMASTER_URL,
PREDICTED_SAFE_ADDRESS: () => PREDICTED_SAFE_ADDRESS,
RPC_URL: () => RPC_URL,
SAFE_4337_MODULE_ADDRESS_V0_2_0: () => SAFE_4337_MODULE_ADDRESS_V0_2_0,
SAFE_4337_MODULE_ADDRESS_V0_3_0: () => SAFE_4337_MODULE_ADDRESS_V0_3_0,
SAFE_ADDRESS_4337_FALLBACKHANDLER_NOT_ENABLED: () => SAFE_ADDRESS_4337_FALLBACKHANDLER_NOT_ENABLED,
SAFE_ADDRESS_4337_MODULE_NOT_ENABLED: () => SAFE_ADDRESS_4337_MODULE_NOT_ENABLED,
SAFE_ADDRESS_4337_PASSKEY: () => SAFE_ADDRESS_4337_PASSKEY,
SAFE_ADDRESS_v1_3_0: () => SAFE_ADDRESS_v1_3_0,
SAFE_ADDRESS_v1_4_1_WITH_0_2_0_MODULE: () => SAFE_ADDRESS_v1_4_1_WITH_0_2_0_MODULE,
SAFE_ADDRESS_v1_4_1_WITH_0_3_0_MODULE: () => SAFE_ADDRESS_v1_4_1_WITH_0_3_0_MODULE,
SAFE_MODULES_V0_3_0: () => SAFE_MODULES_V0_3_0,
SAFE_OPERATION_RESPONSE: () => SAFE_OPERATION_RESPONSE,
SHARED_SIGNER: () => SHARED_SIGNER,
SPONSORED_GAS_ESTIMATION: () => SPONSORED_GAS_ESTIMATION,
USER_OPERATION_BY_HASH: () => USER_OPERATION_BY_HASH,
USER_OPERATION_GAS_PRICE: () => USER_OPERATION_GAS_PRICE,
USER_OPERATION_HASH: () => USER_OPERATION_HASH,
USER_OPERATION_HEX_VALUES: () => USER_OPERATION_HEX_VALUES,
USER_OPERATION_RECEIPT: () => USER_OPERATION_RECEIPT,
USER_OPERATION_V06: () => USER_OPERATION_V06,
USER_OPERATION_V07: () => USER_OPERATION_V07,
USER_OPERATION_V07_HASH: () => USER_OPERATION_V07_HASH
});
import { SignatureTypes } from "@safe-global/types-kit";
var OWNER_1 = "0xFfAC5578BE8AC1B2B9D13b34cAf4A074B96B8A1b";
var OWNER_2 = "0x3059EfD1BCe33be41eeEfd5fb6D520d7fEd54E43";
var PREDICTED_SAFE_ADDRESS = "0xB71d0a777A692870163FFfd777094217a52DD9e4";
var SAFE_ADDRESS_v1_4_1_WITH_0_3_0_MODULE = "0x5f92e52CD555539a0D30c81FcF6703c04E11dA48";
var SAFE_ADDRESS_v1_4_1_WITH_0_2_0_MODULE = "0x717f4BB83D8DF2e5a3Cc603Ee27263ac9EFB6c12";
var SAFE_ADDRESS_v1_3_0 = "0x8C35a08Af278518B59D04ddDe3F1b370aD766D22";
var SAFE_ADDRESS_4337_MODULE_NOT_ENABLED = "0xfC82a1e4A045a44527e8b45FC70332C8F66fc32B";
var SAFE_ADDRESS_4337_FALLBACKHANDLER_NOT_ENABLED = "0xA6FDc4e18404E1715D1bC51B07266c91393C6622";
var SAFE_ADDRESS_4337_PASSKEY = "0x02DCbFD25178b6b8eFb45603D30b5123179117DD";
var SAFE_MODULES_V0_3_0 = "0.3.0";
var PAYMASTER_ADDRESS = "0x0000000000325602a77416A16136FDafd04b299f";
var PAYMASTER_TOKEN_ADDRESS = "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238";
var CHAIN_ID = "0xaa36a7";
var SAFE_4337_MODULE_ADDRESS_V0_2_0 = "0xa581c4A4DB7175302464fF3C06380BC3270b4037";
var SAFE_4337_MODULE_ADDRESS_V0_3_0 = "0x75cf11467937ce3F2f357CE24ffc3DBF8fD5c226";
var SHARED_SIGNER = "0x";
var RPC_URL = "https://sepolia.gateway.tenderly.co";
var BUNDLER_URL = "https://bundler.url";
var PAYMASTER_URL = "https://paymaster.url";
var USER_OPERATION_HASH = "0x3cb881d1969036174f38d636d22108d1d032145518b53104fc0b1e1296d2cc9c";
var ENTRYPOINT_ADDRESS_V06 = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789";
var ENTRYPOINT_ADDRESS_V07 = "0x0000000071727De22E5E9d8BAf0edAc6f37da032";
var USER_OPERATION_RECEIPT = {
userOpHash: "0x3cb881d1969036174f38d636d22108d1d032145518b53104fc0b1e1296d2cc9c",
sender: "0x1405B3659a11a16459fc27Fa1925b60388C38Ce1",
nonce: "1",
actualGasUsed: "0x27067",
actualGasCost: "0x42f29418377167",
success: true,
logs: [],
receipt: {
transactionHash: "0xef262d20f68e4900aa6380b8ac0f66f9c00a7d988179fa177ad9c9758f0e380e",
transactionIndex: "0x63",
blockHash: "0x65f8249337ffede2067a006a96da47d3d3445ca72492a6a82afa02899f05d2e5",
blockNumber: "0x5378b9",
from: "0x4337001Fff419768e088Ce247456c1B892888084",
to: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
cumulativeGasUsed: "0xc1a846",
gasUsed: "0x25e6c",
contractAddress: null,
logs: [],
logsBloom: "0x000000000000900000000000000000000000000000000000080000000002000000080000000000000402000100000000001000000000000080000200000100000000000000000000000000080000000000000000000000000000002000002000000000000a0000000000000000000800000000000000000000000010000200000000000060100000000000000040000000800000000000000008800000000000000000000000000000400000000000000200000000000000000002000000008000000002000100000001000000000000000000000020000000000000000020010040000000000020000010000008000200000000000000000000000000000000",
status: "0x1",
effectiveGasPrice: "0x1b67f3c201"
}
};
var USER_OPERATION_V06 = {
sender: "0x1405B3659a11a16459fc27Fa1925b60388C38Ce1",
nonce: "1",
initCode: "0x",
callData: "0x7bb3742800000000000000000000000038869bf66a61cf6bdb996a6ae40d5853fd43b52600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001848d80ff0a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000132001c7d4b196cb0c7b01d743fbc6116a902379c723800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000d725e11588f040d86c4c49d8236e32a5868549f000000000000000000000000000000000000000000000000000000000000186a0001c7d4b196cb0c7b01d743fbc6116a902379c723800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000d725e11588f040d86c4c49d8236e32a5868549f000000000000000000000000000000000000000000000000000000000000186a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
callGasLimit: 120784n,
verificationGasLimit: 83056n,
preVerificationGas: 48568n,
maxFeePerGas: 193584757388n,
maxPriorityFeePerGas: 1380000000n,
paymasterAndData: "0x",
signature: "0x000000000000000000000000a397ca32ee7fb5282256ee3465da0843485930b803d747516aac76e152f834051ac18fd2b3c0565590f9d65085538993c85c9bb189c940d15c15402c7c2885821b"
};
var USER_OPERATION_V07 = {
sender: "0x26874a65eA7B6B6655e4582c8D215e1De05dd39b",
nonce: "0x0",
factory: "0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67",
factoryData: "0x1688f0b900000000000000000000000029fcb43b46531bca003ddc8fcb67ffe91900c7620000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000009a15e37d88ba5900000000000000000000000000000000000000000000000000000000000001e4b63e800d000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000002dd68b007b46fbe91b9a7c3eda5a7a1063cb5b47000000000000000000000000000000000000000000000000000000000000014000000000000000000000000075cf11467937ce3f2f357ce24ffc3dbf8fd5c2260000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000bc16a6fbc93f62187a137f30c92e3f90bbbaa49200000000000000000000000000000000000000000000000000000000000000648d0dc49f0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000075cf11467937ce3f2f357ce24ffc3dbf8fd5c2260000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
callData: "0x7bb3742800000000000000000000000038869bf66a61cf6bdb996a6ae40d5853fd43b52600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001848d80ff0a0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000013200fc3e86566895fb007c6a0d3809eb2827df94f75100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000bc16a6fbc93f62187a137f30c92e3f90bbbaa49200000000000000000000000000000000000000000000000000000000000186a000fc3e86566895fb007c6a0d3809eb2827df94f75100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000bc16a6fbc93f62187a137f30c92e3f90bbbaa49200000000000000000000000000000000000000000000000000000000000186a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
callGasLimit: 120784n,
verificationGasLimit: 83056n,
preVerificationGas: 48568n,
maxFeePerGas: 193584757388n,
maxPriorityFeePerGas: 1380000000n,
paymaster: void 0,
paymasterVerificationGasLimit: void 0,
paymasterPostOpGasLimit: void 0,
paymasterData: void 0,
signature: "0x0000679fa3ac000067a1786c8c012f3bef75848690703f17ab0519669bc38bc2629bd8b3118f6280936933fa675bc52dde81cc71c3e0c4587e17ddecf21f845a7a34862b586776501845b1511c"
};
var USER_OPERATION_V07_HASH = "0xea46190691c27950a9c4246be1e4550fa1bd85bcf1ad9fe7329b51666722b285";
var USER_OPERATION_HEX_VALUES = {
sender: "0x1405B3659a11a16459fc27Fa1925b60388C38Ce1",
nonce: "0x1",
initCode: "0x",
callData: "0x7bb3742800000000000000000000000038869bf66a61cf6bdb996a6ae40d5853fd43b52600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001848d80ff0a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000132001c7d4b196cb0c7b01d743fbc6116a902379c723800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000d725e11588f040d86c4c49d8236e32a5868549f000000000000000000000000000000000000000000000000000000000000186a0001c7d4b196cb0c7b01d743fbc6116a902379c723800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000d725e11588f040d86c4c49d8236e32a5868549f000000000000000000000000000000000000000000000000000000000000186a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
callGasLimit: "0x1d7d0",
verificationGasLimit: "0x14470",
preVerificationGas: "0xbdb8",
maxFeePerGas: "0x2d128cfa8c",
maxPriorityFeePerGas: "0x52412100",
paymasterAndData: "0x",
signature: "0x000000000000000000000000a397ca32ee7fb5282256ee3465da0843485930b803d747516aac76e152f834051ac18fd2b3c0565590f9d65085538993c85c9bb189c940d15c15402c7c2885821b"
};
var USER_OPERATION_BY_HASH = {
userOperation: USER_OPERATION_HEX_VALUES,
entryPoint: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
transactionHash: "0xef262d20f68e4900aa6380b8ac0f66f9c00a7d988179fa177ad9c9758f0e380e",
blockHash: "0x65f8249337ffede2067a006a96da47d3d3445ca72492a6a82afa02899f05d2e5",
blockNumber: "0x5378b9"
};
var GAS_ESTIMATION = {
verificationGasLimit: "0x186A0",
preVerificationGas: "0x186A0",
callGasLimit: "0x186A0"
};
var SAFE_OPERATION_RESPONSE = {
created: "2024-05-31T10:12:21.169031Z",
modified: "2024-05-31T10:12:21.169031Z",
safeOperationHash: "0x5a62b1d61f8fca5f766e9456523bb42765d318058b5f235f967ffe3c2af8b1d7",
validAfter: null,
validUntil: null,
moduleAddress: "0xa581c4A4DB7175302464fF3C06380BC3270b4037",
confirmations: [
{
created: "2024-05-31T10:12:21.184585Z",
modified: "2024-05-31T10:12:21.184585Z",
owner: "0x3059EfD1BCe33be41eeEfd5fb6D520d7fEd54E43",
signature: "0xcb28e74375889e400a4d8aca46b8c59e1cf8825e373c26fa99c2fd7c078080e64fe30eaf1125257bdfe0b358b5caef68aa0420478145f52decc8e74c979d43ab1d",
signatureType: SignatureTypes.EOA
}
],
preparedSignature: "0xcb28e74375889e400a4d8aca46b8c59e1cf8825e373c26fa99c2fd7c078080e64fe30eaf1125257bdfe0b358b5caef68aa0420478145f52decc8e74c979d43ab1c",
userOperation: {
ethereumTxHash: null,
sender: "0xE322e721bCe76cE7FCf3A475f139A9314571ad3D",
userOperationHash: "0x5d23b7d96a718582601183b1849a4c76b2a13d3787f15074d62a0b6e4a3f76a1",
nonce: "3",
initCode: "0x",
callData: "0x7bb37428000000000000000000000000e322e721bce76ce7fcf3a475f139a9314571ad3d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
callGasLimit: "122497",
verificationGasLimit: "123498",
preVerificationGas: "50705",
maxFeePerGas: "105183831060",
maxPriorityFeePerGas: "1380000000",
paymaster: null,
paymasterData: null,
signature: "0x54158da2d357241ee1c5c8fca9c4e1bfa6b92a60bd0ed1bea56f4092b008435153d6264a8a8c00925383ecaeaf9d839a2dc1ff006703c65b7f05d0ce8cdd57ab1b",
entryPoint: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
}
};
var SPONSORED_GAS_ESTIMATION = {
paymasterAndData: "0x1405B3659a11a16459fc27Fa1925b60388C38Ce1",
...GAS_ESTIMATION
};
var USER_OPERATION_GAS_PRICE = {
fast: { maxFeePerGas: "0x186A0", maxPriorityFeePerGas: "0x30D40" }
};
// test-utils/helpers.ts
import { encodeFunctionData as encodeFunctionData4, parseAbi as parseAbi2 } from "viem";
// src/packs/safe-4337/Safe4337Pack.ts
import { getAddress as getAddress2, toHex as toHex5 } from "viem";
import semverSatisfies from "semver/functions/satisfies.js";
import Safe, {
EthSafeSignature as EthSafeSignature2,
encodeMultiSendData as encodeMultiSendData2,
getMultiSendContract,
SafeProvider,
generateOnChainIdentifier
} from "@safe-global/protocol-kit";
// src/RelayKitBasePack.ts
var RelayKitBasePack = class {
/**
* Creates a new RelayKitBasePack instance.
* The packs implemented using our SDK should extend this class and therefore provide a Safe SDK instance
* @param {Safe} protocolKit - The Safe SDK instance
*/
constructor(protocolKit) {
this.protocolKit = protocolKit;
}
};
// src/packs/safe-4337/Safe4337Pack.ts
import {
OperationType as OperationType3,
SigningMethod
} from "@safe-global/types-kit";
import {
getSafeModuleSetupDeployment,
getSafe4337ModuleDeployment,
getSafeWebAuthnShareSignerDeployment
} from "@safe-global/safe-modules-deployments";
import { encodeFunctionData as encodeFunctionData3, zeroAddress, concat as concat2 } from "viem";
// src/packs/safe-4337/BaseSafeOperation.ts
import { encodePacked, hashTypedData } from "viem";
import { buildSignatureBytes } from "@safe-global/protocol-kit";
var BaseSafeOperation = class {
constructor(userOperation, options) {
this.signatures = /* @__PURE__ */ new Map();
this.userOperation = userOperation;
this.options = options;
}
getSignature(signer) {
return this.signatures.get(signer.toLowerCase());
}
addSignature(signature) {
this.signatures.set(signature.signer.toLowerCase(), signature);
}
encodedSignatures() {
return buildSignatureBytes(Array.from(this.signatures.values()));
}
getUserOperation() {
return {
...this.userOperation,
signature: encodePacked(
["uint48", "uint48", "bytes"],
[
this.options.validAfter || 0,
this.options.validUntil || 0,
this.encodedSignatures()
]
)
};
}
getHash() {
return hashTypedData({
domain: {
chainId: Number(this.options.chainId),
verifyingContract: this.options.moduleAddress
},
types: this.getEIP712Type(),
primaryType: "SafeOp",
message: this.getSafeOperation()
});
}
};
var BaseSafeOperation_default = BaseSafeOperation;
// src/packs/safe-4337/constants.ts
import { parseAbi } from "viem";
var DEFAULT_SAFE_VERSION = "1.4.1";
var DEFAULT_SAFE_MODULES_VERSION = "0.2.0";
var EIP712_SAFE_OPERATION_TYPE_V06 = {
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" }
]
};
var EIP712_SAFE_OPERATION_TYPE_V07 = {
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" }
]
};
var ABI = parseAbi([
"function enableModules(address[])",
"function multiSend(bytes memory transactions) public payable",
"function executeUserOp(address to, uint256 value, bytes data, uint8 operation)",
"function approve(address _spender, uint256 _value)",
"function configure((uint256 x, uint256 y, uint176 verifiers) signer)"
]);
var ENTRYPOINT_ABI = [
{
inputs: [
{ name: "sender", type: "address" },
{ name: "key", type: "uint192" }
],
name: "getNonce",
outputs: [{ name: "nonce", type: "uint256" }],
stateMutability: "view",
type: "function"
}
];
var ENTRYPOINT_ADDRESS_V062 = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789";
var ENTRYPOINT_ADDRESS_V072 = "0x0000000071727De22E5E9d8BAf0edAc6f37da032";
var RPC_4337_CALLS = /* @__PURE__ */ ((RPC_4337_CALLS2) => {
RPC_4337_CALLS2["ESTIMATE_USER_OPERATION_GAS"] = "eth_estimateUserOperationGas";
RPC_4337_CALLS2["SEND_USER_OPERATION"] = "eth_sendUserOperation";
RPC_4337_CALLS2["GET_USER_OPERATION_BY_HASH"] = "eth_getUserOperationByHash";
RPC_4337_CALLS2["GET_USER_OPERATION_RECEIPT"] = "eth_getUserOperationReceipt";
RPC_4337_CALLS2["SUPPORTED_ENTRY_POINTS"] = "eth_supportedEntryPoints";
RPC_4337_CALLS2["CHAIN_ID"] = "eth_chainId";
RPC_4337_CALLS2["GET_PAYMASTER_STUB_DATA"] = "pm_getPaymasterStubData";
RPC_4337_CALLS2["GET_PAYMASTER_DATA"] = "pm_getPaymasterData";
return RPC_4337_CALLS2;
})(RPC_4337_CALLS || {});
// src/packs/safe-4337/SafeOperationV06.ts
var SafeOperationV06 = class extends BaseSafeOperation_default {
constructor(userOperation, options) {
super(userOperation, options);
}
addEstimations(estimations) {
this.userOperation.maxFeePerGas = BigInt(
estimations.maxFeePerGas || this.userOperation.maxFeePerGas
);
this.userOperation.maxPriorityFeePerGas = BigInt(
estimations.maxPriorityFeePerGas || this.userOperation.maxPriorityFeePerGas
);
this.userOperation.verificationGasLimit = BigInt(
estimations.verificationGasLimit || this.userOperation.verificationGasLimit
);
this.userOperation.preVerificationGas = BigInt(
estimations.preVerificationGas || this.userOperation.preVerificationGas
);
this.userOperation.callGasLimit = BigInt(
estimations.callGasLimit || this.userOperation.callGasLimit
);
this.userOperation.paymasterAndData = estimations.paymasterAndData || this.userOperation.paymasterAndData;
}
getSafeOperation() {
return {
safe: this.userOperation.sender,
nonce: this.userOperation.nonce,
initCode: this.userOperation.initCode,
callData: this.userOperation.callData,
callGasLimit: this.userOperation.callGasLimit,
verificationGasLimit: this.userOperation.verificationGasLimit,
preVerificationGas: this.userOperation.preVerificationGas,
maxFeePerGas: this.userOperation.maxFeePerGas,
maxPriorityFeePerGas: this.userOperation.maxPriorityFeePerGas,
paymasterAndData: this.userOperation.paymasterAndData,
validAfter: this.options.validAfter || 0,
validUntil: this.options.validUntil || 0,
entryPoint: this.options.entryPoint
};
}
getEIP712Type() {
return EIP712_SAFE_OPERATION_TYPE_V06;
}
};
var SafeOperationV06_default = SafeOperationV06;
// src/packs/safe-4337/SafeOperationV07.ts
import { concat, isAddress, pad, toHex } from "viem";
var SafeOperationV07 = class extends BaseSafeOperation_default {
constructor(userOperation, options) {
super(userOperation, options);
}
addEstimations(estimations) {
this.userOperation.maxFeePerGas = BigInt(
estimations.maxFeePerGas || this.userOperation.maxFeePerGas
);
this.userOperation.maxPriorityFeePerGas = BigInt(
estimations.maxPriorityFeePerGas || this.userOperation.maxPriorityFeePerGas
);
this.userOperation.verificationGasLimit = BigInt(
estimations.verificationGasLimit || this.userOperation.verificationGasLimit
);
this.userOperation.preVerificationGas = BigInt(
estimations.preVerificationGas || this.userOperation.preVerificationGas
);
this.userOperation.callGasLimit = BigInt(
estimations.callGasLimit || this.userOperation.callGasLimit
);
this.userOperation.paymasterPostOpGasLimit = estimations.paymasterPostOpGasLimit ? BigInt(estimations.paymasterPostOpGasLimit) : this.userOperation.paymasterPostOpGasLimit;
this.userOperation.paymasterVerificationGasLimit = estimations.paymasterVerificationGasLimit ? BigInt(estimations.paymasterVerificationGasLimit) : this.userOperation.paymasterVerificationGasLimit;
this.userOperation.paymaster = estimations.paymaster || this.userOperation.paymaster;
this.userOperation.paymasterData = estimations.paymasterData || this.userOperation.paymasterData;
}
getSafeOperation() {
const initCode = this.userOperation.factory ? concat([
this.userOperation.factory,
this.userOperation.factoryData || "0x"
]) : "0x";
const paymasterAndData = isAddress(this.userOperation.paymaster || "") ? concat([
this.userOperation.paymaster,
pad(toHex(this.userOperation.paymasterVerificationGasLimit || 0n), {
size: 16
}),
pad(toHex(this.userOperation.paymasterPostOpGasLimit || 0n), {
size: 16
}),
this.userOperation.paymasterData || "0x"
]) : "0x";
return {
safe: this.userOperation.sender,
nonce: this.userOperation.nonce,
initCode,
callData: this.userOperation.callData,
callGasLimit: this.userOperation.callGasLimit,
verificationGasLimit: this.userOperation.verificationGasLimit,
preVerificationGas: this.userOperation.preVerificationGas,
maxFeePerGas: this.userOperation.maxFeePerGas,
maxPriorityFeePerGas: this.userOperation.maxPriorityFeePerGas,
paymasterAndData,
validAfter: this.options.validAfter || 0,
validUntil: this.options.validUntil || 0,
entryPoint: this.options.entryPoint
};
}
getEIP712Type() {
return EIP712_SAFE_OPERATION_TYPE_V07;
}
};
var SafeOperationV07_default = SafeOperationV07;
// src/packs/safe-4337/utils/index.ts
import { createPublicClient, encodeFunctionData as encodeFunctionData2, http, rpcSchema } from "viem";
import { OperationType as OperationType2 } from "@safe-global/types-kit";
import { encodeMultiSendData } from "@safe-global/protocol-kit";
// src/packs/safe-4337/utils/entrypoint.ts
var EQ_0_2_0 = "0.2.0";
var EQ_OR_GT_0_3_0 = ">=0.3.0";
function sameString(str1, str2) {
return str1.toLowerCase() === str2.toLowerCase();
}
function entryPointToSafeModules(entryPoint) {
const moduleVersionToEntryPoint = {
[ENTRYPOINT_ADDRESS_V062]: EQ_0_2_0,
[ENTRYPOINT_ADDRESS_V072]: EQ_OR_GT_0_3_0
};
return moduleVersionToEntryPoint[entryPoint];
}
function isEntryPointV6(address) {
return sameString(address, ENTRYPOINT_ADDRESS_V062);
}
function isEntryPointV7(address) {
return sameString(address, ENTRYPOINT_ADDRESS_V072);
}
async function getSafeNonceFromEntrypoint(protocolKit, safeAddress, entryPointAddress) {
const safeProvider = protocolKit.getSafeProvider();
const newNonce = await safeProvider.readContract({
address: entryPointAddress || "0x",
abi: ENTRYPOINT_ABI,
functionName: "getNonce",
args: [safeAddress, 0n]
});
return newNonce;
}
// src/packs/safe-4337/utils/signing.ts
import { encodePacked as encodePacked2, toHex as toHex2 } from "viem";
import { EthSafeSignature, buildSignatureBytes as buildSignatureBytes2 } from "@safe-global/protocol-kit";
var DUMMY_CLIENT_DATA_FIELDS = [
`"origin":"https://safe.global"`,
`"padding":"This pads the clientDataJSON so that we can leave room for additional implementation specific fields for a more accurate 'preVerificationGas' estimate."`
].join(",");
var DUMMY_AUTHENTICATOR_DATA = new Uint8Array(37);
DUMMY_AUTHENTICATOR_DATA.fill(254);
DUMMY_AUTHENTICATOR_DATA[32] = 4;
function getDummySignature(signer, threshold) {
const signatures = [];
for (let i = 0; i < threshold; i++) {
const isContractSignature = true;
const passkeySignature = getSignatureBytes({
authenticatorData: DUMMY_AUTHENTICATOR_DATA,
clientDataFields: DUMMY_CLIENT_DATA_FIELDS,
r: BigInt(`0x${"ec".repeat(32)}`),
s: BigInt(`0x${"d5a".repeat(21)}f`)
});
signatures.push(new EthSafeSignature(signer, passkeySignature, isContractSignature));
}
return encodePacked2(["uint48", "uint48", "bytes"], [0, 0, buildSignatureBytes2(signatures)]);
}
function getSignatureBytes({
authenticatorData,
clientDataFields,
r,
s
}) {
const encodeUint256 = (x) => x.toString(16).padStart(64, "0");
const byteSize = (data) => 32 * (Math.ceil(data.length / 32) + 1);
const encodeBytes = (data) => `${encodeUint256(data.length)}${toHex2(data).slice(2)}`.padEnd(byteSize(data) * 2, "0");
const authenticatorDataOffset = 32 * 4;
const clientDataFieldsOffset = authenticatorDataOffset + byteSize(authenticatorData);
return "0x" + encodeUint256(authenticatorDataOffset) + encodeUint256(clientDataFieldsOffset) + encodeUint256(r) + encodeUint256(s) + encodeBytes(authenticatorData) + encodeBytes(new TextEncoder().encode(clientDataFields));
}
// src/packs/safe-4337/utils/userOperations.ts
import { encodeFunctionData, getAddress, hexToBytes, sliceHex, toHex as toHex3 } from "viem";
import {
OperationType
} from "@safe-global/types-kit";
function encodeExecuteUserOpCallData(transaction) {
return encodeFunctionData({
abi: ABI,
functionName: "executeUserOp",
args: [
transaction.to,
BigInt(transaction.value),
transaction.data,
transaction.operation || OperationType.Call
]
});
}
async function getCallData(protocolKit, transactions, paymasterOptions, amountToApprove) {
if (amountToApprove) {
const approveToPaymasterTransaction = {
to: paymasterOptions.paymasterTokenAddress,
data: encodeFunctionData({
abi: ABI,
functionName: "approve",
args: [paymasterOptions.paymasterAddress, amountToApprove]
}),
value: "0",
operation: OperationType.Call
// Call for approve
};
transactions.push(approveToPaymasterTransaction);
}
const isBatch = transactions.length > 1;
const multiSendAddress = protocolKit.getMultiSendAddress();
const callData = isBatch ? encodeExecuteUserOpCallData({
to: multiSendAddress,
value: "0",
data: encodeMultiSendCallData(transactions),
operation: OperationType.DelegateCall
}) : encodeExecuteUserOpCallData(transactions[0]);
return callData;
}
function unpackInitCode(initCode) {
const initCodeBytes = hexToBytes(initCode);
return initCodeBytes.length > 0 ? {
factory: getAddress(sliceHex(initCode, 0, 20)),
factoryData: sliceHex(initCode, 20)
} : {};
}
async function createUserOperation(protocolKit, transactions, {
amountToApprove,
entryPoint,
paymasterOptions,
customNonce
}) {
const safeAddress = await protocolKit.getAddress();
const nonce = customNonce || await getSafeNonceFromEntrypoint(protocolKit, safeAddress, entryPoint);
const isSafeDeployed = await protocolKit.isSafeDeployed();
const paymasterAndData = paymasterOptions && "paymasterAddress" in paymasterOptions ? paymasterOptions.paymasterAddress : "0x";
const callData = await getCallData(
protocolKit,
transactions,
paymasterOptions,
amountToApprove
);
const initCode = isSafeDeployed ? "0x" : await protocolKit.getInitCode();
if (isEntryPointV6(entryPoint)) {
return {
sender: safeAddress,
nonce: nonce.toString(),
initCode,
callData,
callGasLimit: 1n,
verificationGasLimit: 1n,
preVerificationGas: 1n,
maxFeePerGas: 1n,
maxPriorityFeePerGas: 1n,
paymasterAndData,
signature: "0x"
};
}
return {
sender: safeAddress,
nonce: nonce.toString(),
...unpackInitCode(initCode),
callData,
callGasLimit: 1n,
verificationGasLimit: 1n,
preVerificationGas: 1n,
maxFeePerGas: 1n,
maxPriorityFeePerGas: 1n,
paymaster: paymasterAndData,
paymasterData: "0x",
paymasterVerificationGasLimit: void 0,
paymasterPostOpGasLimit: void 0,
signature: "0x"
};
}
function userOperationToHexValues(userOperation, entryPointAddress) {
const userOpV07 = userOperation;
const userOperationWithHexValues = {
...userOperation,
nonce: toHex3(BigInt(userOperation.nonce)),
callGasLimit: toHex3(userOperation.callGasLimit),
verificationGasLimit: toHex3(userOperation.verificationGasLimit),
preVerificationGas: toHex3(userOperation.preVerificationGas),
maxFeePerGas: toHex3(userOperation.maxFeePerGas),
maxPriorityFeePerGas: toHex3(userOperation.maxPriorityFeePerGas),
...isEntryPointV7(entryPointAddress) ? {
paymaster: userOpV07.paymaster !== "0x" ? userOpV07.paymaster : null,
paymasterData: userOpV07.paymasterData !== "0x" ? userOpV07.paymasterData : null,
paymasterVerificationGasLimit: userOpV07.paymasterVerificationGasLimit ? toHex3(userOpV07.paymasterVerificationGasLimit) : null,
paymasterPostOpGasLimit: userOpV07.paymasterPostOpGasLimit ? toHex3(userOpV07.paymasterPostOpGasLimit) : null
} : {}
};
return userOperationWithHexValues;
}
// src/packs/safe-4337/utils/getRelayKitVersion.ts
var getRelayKitVersion = () => "4.0.4";
// src/packs/safe-4337/utils/encodeNonce.ts
import { toHex as toHex4 } from "viem";
// src/packs/safe-4337/utils/index.ts
function createBundlerClient(bundlerUrl) {
const provider = createPublicClient({
transport: http(bundlerUrl),
rpcSchema: rpcSchema()
});
return provider;
}
function encodeMultiSendCallData(transactions) {
return encodeFunctionData2({
abi: ABI,
functionName: "multiSend",
args: [
encodeMultiSendData(
transactions.map((tx) => ({ ...tx, operation: tx.operation ?? OperationType2.Call }))
)
]
});
}
// src/packs/safe-4337/SafeOperationFactory.ts
var SafeOperationFactory = class {
/**
* Creates a new SafeOperation with proper validation
* @param userOperation - The base user operation
* @param options - Configuration options
* @returns Validated SafeOperation instance
*/
static createSafeOperation(userOperation, options) {
if (isEntryPointV6(options.entryPoint)) {
return new SafeOperationV06_default(userOperation, options);
}
return new SafeOperationV07_default(userOperation, options);
}
};
var SafeOperationFactory_default = SafeOperationFactory;
// src/packs/safe-4337/estimators/pimlico/PimlicoFeeEstimator.ts
var PimlicoFeeEstimator = class {
async preEstimateUserOperationGas({
bundlerUrl,
userOperation,
entryPoint,
paymasterOptions
}) {
const bundlerClient = createBundlerClient(bundlerUrl);
const feeData = await this.#getUserOperationGasPrices(bundlerClient);
const chainId = await this.#getChainId(bundlerClient);
let paymasterStubData = {};
if (paymasterOptions) {
const paymasterClient = createBundlerClient(
paymasterOptions.paymasterUrl
);
const context = "paymasterTokenAddress" in paymasterOptions ? {
token: paymasterOptions.paymasterTokenAddress
} : void 0;
paymasterStubData = await paymasterClient.request({
method: "pm_getPaymasterStubData" /* GET_PAYMASTER_STUB_DATA */,
params: [userOperationToHexValues(userOperation, entryPoint), entryPoint, chainId, context]
});
}
return {
...feeData,
...paymasterStubData
};
}
async postEstimateUserOperationGas({
userOperation,
entryPoint,
paymasterOptions
}) {
if (!paymasterOptions) return {};
const paymasterClient = createBundlerClient(
paymasterOptions.paymasterUrl
);
if (paymasterOptions.isSponsored) {
const params = [
userOperationToHexValues(userOperation, entryPoint),
entryPoint
];
if (paymasterOptions.sponsorshipPolicyId) {
params.push({
sponsorshipPolicyId: paymasterOptions.sponsorshipPolicyId
});
}
const sponsoredData = await paymasterClient.request({
method: "pm_sponsorUserOperation" /* SPONSOR_USER_OPERATION */,
params
});
return sponsoredData;
}
const chainId = await this.#getChainId(paymasterClient);
const erc20PaymasterData = await paymasterClient.request({
method: "pm_getPaymasterData" /* GET_PAYMASTER_DATA */,
params: [
userOperationToHexValues(userOperation, entryPoint),
entryPoint,
chainId,
{ token: paymasterOptions.paymasterTokenAddress }
]
});
return erc20PaymasterData;
}
async #getUserOperationGasPrices(client) {
const feeData = await client.request({
method: "pimlico_getUserOperationGasPrice" /* GET_USER_OPERATION_GAS_PRICE */
});
const {
fast: { maxFeePerGas, maxPriorityFeePerGas }
} = feeData;
return {
maxFeePerGas,
maxPriorityFeePerGas
};
}
async #getChainId(client) {
const chainId = await client.request({ method: "eth_chainId" });
return chainId;
}
};
// src/packs/safe-4337/Safe4337Pack.ts
var MAX_ERC20_AMOUNT_TO_APPROVE = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn;
var EQ_OR_GT_1_4_1 = ">=1.4.1";
var Safe4337Pack = class _Safe4337Pack extends RelayKitBasePack {
#BUNDLER_URL;
#ENTRYPOINT_ADDRESS;
#SAFE_4337_MODULE_ADDRESS = "0x";
#SAFE_WEBAUTHN_SHARED_SIGNER_ADDRESS = "0x";
#bundlerClient;
#chainId;
#paymasterOptions;
#onchainIdentifier = "";
/**
* Creates an instance of the Safe4337Pack.
*
* @param {Safe4337Options} options - The initialization parameters.
*/
constructor({
protocolKit,
bundlerClient,
bundlerUrl,
chainId,
paymasterOptions,
entryPointAddress,
safe4337ModuleAddress,
safeWebAuthnSharedSignerAddress,
onchainAnalytics
}) {
super(protocolKit);
this.#BUNDLER_URL = bundlerUrl;
this.#bundlerClient = bundlerClient;
this.#chainId = chainId;
this.#paymasterOptions = paymasterOptions;
this.#ENTRYPOINT_ADDRESS = entryPointAddress;
this.#SAFE_4337_MODULE_ADDRESS = safe4337ModuleAddress;
this.#SAFE_WEBAUTHN_SHARED_SIGNER_ADDRESS = safeWebAuthnSharedSignerAddress || "0x";
if (onchainAnalytics?.project) {
const { project, platform } = onchainAnalytics;
this.#onchainIdentifier = generateOnChainIdentifier({
project,
platform,
tool: "relay-kit",
toolVersion: getRelayKitVersion()
});
}
}
/**
* Initializes a Safe4337Pack class.
* This method creates the protocolKit instance based on the input parameters.
* When the Safe address is provided, it will use the existing Safe.
* When the Safe address is not provided, it will use the predictedSafe feature with the provided owners and threshold.
* It will use the correct contract addresses for the fallbackHandler and the module and will add the data to enable the 4337 module.
*
* @param {Safe4337InitOptions} initOptions - The initialization parameters.
* @return {Promise<Safe4337Pack>} The Promise object that will be resolved into an instance of Safe4337Pack.
*/
static async init(initOptions) {
const {
provider,
signer,
options,
bundlerUrl,
customContracts,
paymasterOptions,
onchainAnalytics
} = initOptions;
let protocolKit;
const bundlerClient = createBundlerClient(bundlerUrl);
const chainId = await bundlerClient.request({ method: "eth_chainId" /* CHAIN_ID */ });
let safeModulesSetupAddress = customContracts?.safeModulesSetupAddress;
const network = parseInt(chainId, 16).toString();
const safeModulesVersion = initOptions.safeModulesVersion || DEFAULT_SAFE_MODULES_VERSION;
if (!safeModulesSetupAddress) {
const safeModuleSetupDeployment = getSafeModuleSetupDeployment({
released: true,
version: safeModulesVersion,
network
});
safeModulesSetupAddress = safeModuleSetupDeployment?.networkAddresses[network];
}
let safe4337ModuleAddress = customContracts?.safe4337ModuleAddress;
if (!safe4337ModuleAddress) {
const safe4337ModuleDeployment = getSafe4337ModuleDeployment({
released: true,
version: safeModulesVersion,
network
});
safe4337ModuleAddress = safe4337ModuleDeployment?.networkAddresses[network];
}
if (!safeModulesSetupAddress || !safe4337ModuleAddress) {
throw new Error(
`Safe4337Module and/or SafeModuleSetup not available for chain ${network} and modules version ${safeModulesVersion}`
);
}
let safeWebAuthnSharedSignerAddress = customContracts?.safeWebAuthnSharedSignerAddress;
if ("safeAddress" in options) {
protocolKit = await Safe.init({
provider,
signer,
safeAddress: options.safeAddress
});
const safeVersion = protocolKit.getContractVersion();
const isSafeVersion4337Compatible = semverSatisfies(safeVersion, EQ_OR_GT_1_4_1);
if (!isSafeVersion4337Compatible) {
throw new Error(
`Incompatibility detected: The current Safe Account version (${safeVersion}) is not supported. EIP-4337 requires the Safe to use at least v1.4.1.`
);
}
const safeModules = await protocolKit.getModules();
const is4337ModulePresent = safeModules.some((module) => module === safe4337ModuleAddress);
if (!is4337ModulePresent) {
throw new Error(
`Incompatibility detected: The EIP-4337 module is not enabled in the provided Safe Account. Enable this module (address: ${safe4337ModuleAddress}) to add compatibility.`
);
}
const safeFallbackhandler = await protocolKit.getFallbackHandler();
const is4337FallbackhandlerPresent = safeFallbackhandler === safe4337ModuleAddress;
if (!is4337FallbackhandlerPresent) {
throw new Error(
`Incompatibility detected: The EIP-4337 fallbackhandler is not attached to the Safe Account. Attach this fallbackhandler (address: ${safe4337ModuleAddress}) to ensure compatibility.`
);
}
} else {
if (!options.owners || !options.threshold) {
throw new Error("Owners and threshold are required to deploy a new Safe");
}
const safeVersion = options.safeVersion || DEFAULT_SAFE_VERSION;
const enable4337ModuleTransaction = {
to: safeModulesSetupAddress,
value: "0",
data: encodeFunctionData3({
abi: ABI,
functionName: "enableModules",
args: [[safe4337ModuleAddress]]
}),
operation: OperationType3.DelegateCall
// DelegateCall required for enabling the 4337 module
};
const setupTransactions = [enable4337ModuleTransaction];
const isApproveTransactionRequired = !!paymasterOptions && !paymasterOptions.isSponsored && !!paymasterOptions.paymasterTokenAddress;
if (isApproveTransactionRequired) {
const { paymasterAddress, amountToApprove = MAX_ERC20_AMOUNT_TO_APPROVE } = paymasterOptions;
const approveToPaymasterTransaction = {
to: paymasterOptions.paymasterTokenAddress,
data: encodeFunctionData3({
abi: ABI,
functionName: "approve",
args: [paymasterAddress, amountToApprove]
}),
value: "0",
operation: OperationType3.Call
// Call for approve
};
setupTransactions.push(approveToPaymasterTransaction);
}
const safeProvider = await SafeProvider.init({ provider, signer, safeVersion });
const isPasskeySigner = await safeProvider.isPasskeySigner();
if (isPasskeySigner) {
if (!safeWebAuthnSharedSignerAddress) {
const safeWebAuthnSharedSignerDeployment = getSafeWebAuthnShareSignerDeployment({
released: true,
version: "0.2.1",
network
});
safeWebAuthnSharedSignerAddress = safeWebAuthnSharedSignerDeployment?.networkAddresses[network];
}
if (!safeWebAuthnSharedSignerAddress) {
throw new Error(`safeWebAuthnSharedSignerAddress not available for chain ${network}`);
}
const passkeySigner = await safeProvider.getExternalSigner();
const checkSummedOwners = options.owners.map((owner) => getAddress2(owner));
const checkSummedSignerAddress = getAddress2(safeWebAuthnSharedSignerAddress);
if (!checkSummedOwners.includes(checkSummedSignerAddress)) {
options.owners.push(checkSummedSignerAddress);
}
const sharedSignerTransaction = {
to: safeWebAuthnSharedSignerAddress,
value: "0",
data: passkeySigner.encodeConfigure(),
operation: OperationType3.DelegateCall
// DelegateCall required into the SafeWebAuthnSharedSigner instance in order for it to set its configuration.
};
setupTransactions.push(sharedSignerTransaction);
}
let deploymentTo;
let deploymentData;
const isBatch = setupTransactions.length > 1;
if (isBatch) {
const multiSendContract = await getMultiSendContract({
safeProvider,
safeVersion,
deploymentType: options.deploymentType || void 0
});
const batchData = encodeFunctionData3({
abi: ABI,
functionName: "multiSend",
args: [encodeMultiSendData2(setupTransactions)]
});
deploymentTo = multiSendContract.getAddress();
deploymentData = batchData;
} else {
deploymentTo = enable4337ModuleTransaction.to;
deploymentData = enable4337ModuleTransaction.data;
}
protocolKit = await Safe.init({
provider,
signer,
predictedSafe: {
safeDeploymentConfig: {
safeVersion,
saltNonce: options.saltNonce || void 0,
deploymentType: options.deploymentType || void 0
},
safeAccountConfig: {
owners: options.owners,
threshold: options.threshold,
to: deploymentTo,
data: deploymentData,
fallbackHandler: safe4337ModuleAddress,
paymentToken: zeroAddress,
payment: 0,
paymentReceiver: zeroAddress
}
},
onchainAnalytics
});
}
let selectedEntryPoint;
if (customContracts?.entryPointAddress) {
const requiredSafeModulesVersion = entryPointToSafeModules(customContracts?.entryPointAddress);
if (!semverSatisfies(safeModulesVersion, requiredSafeModulesVersion))
throw new Error(
`The selected entrypoint ${customContracts?.entryPointAddress} is not compatible with version ${safeModulesVersion} of Safe modules`
);
selectedEntryPoint = customContracts?.entryPointAddress;
} else {
const supportedEntryPoints = await bundlerClient.request({
method: "eth_supportedEntryPoints" /* SUPPORTED_ENTRY_POINTS */
});
if (!supportedEntryPoints.length) {
throw new Error("No entrypoint provided or available through the bundler");
}
selectedEntryPoint = supportedEntryPoints.find((entryPoint) => {
const requiredSafeModulesVersion = entryPointToSafeModules(entryPoint);
return semverSatisfies(safeModulesVersion, requiredSafeModulesVersion);
});
if (!selectedEntryPoint) {
throw new Error(
`Incompatibility detected: None of the entrypoints provided by the bundler is compatible with the Safe modules version ${safeModulesVersion}`
);
}
}
return new _Safe4337Pack({
chainId: BigInt(chainId),
protocolKit,
bundlerClient,
paymasterOptions,
bundlerUrl,
entryPointAddress: selectedEntryPoint,
safe4337ModuleAddress,
safeWebAuthnSharedSignerAddress,
onchainAnalytics
});
}
/**
* Estimates gas for the SafeOperation.
*
* @param {EstimateFeeProps} props - The parameters for the gas estimation.
* @param {BaseSafeOperation} props.safeOperation - The SafeOperation to estimate the gas.
* @param {IFeeEstimator} props.feeEstimator - The function to estimate the gas.
* @return {Promise<BaseSafeOperation>} The Promise object that will be resolved into the gas estimation.
*/
async getEstimateFee({
safeOperation,
feeEstimator = new PimlicoFeeEstimator()
}) {
const threshold = await this.protocolKit.getThreshold();
const preEstimationData = await feeEstimator?.preEstimateUserOperationGas?.({
bundlerUrl: this.#BUNDLER_URL,
entryPoint: this.#ENTRYPOINT_ADDRESS,
userOperation: safeOperation.getUserOperation(),
paymasterOptions: this.#paymasterOptions
});
if (preEstimationData) {
safeOperation.addEstimations(preEstimationData);
}
const estimateUserOperationGas = await this.#bundlerClient.request({
method: "eth_estimateUserOperationGas" /* ESTIMATE_USER_OPERATION_GAS */,
params: [
{
...userOperationToHexValues(safeOperation.getUserOperation(), this.#ENTRYPOINT_ADDRESS),
signature: getDummySignature(this.#SAFE_WEBAUTHN_SHARED_SIGNER_ADDRESS, threshold)
},
this.#ENTRYPOINT_ADDRESS
]
});
if (estimateUserOperationGas) {
safeOperation.addEstimations(estimateUserOperationGas);
}
const postEstimationData = await feeEstimator?.postEstimateUserOperationGas?.({
bundlerUrl: this.#BUNDLER_URL,
entryPoint: this.#ENTRYPOINT_ADDRESS,
userOperation: {
...safeOperation.getUserOperation(),
signature: getDummySignature(this.#SAFE_WEBAUTHN_SHARED_SIGNER_ADDRESS, threshold)
},
paymasterOptions: this.#paymasterOptions
});
if (postEstimationData) {
safeOperation.addEstimations(postEstimationData);
}
return safeOperation;
}
/**
* Creates a relayed transaction based on the provided parameters.
*
* @param {MetaTransactionData[]} transactions - The transactions to batch in a SafeOperation.
* @param options - Optional configuration options for the transaction creation.
* @return {Promise<BaseSafeOperation>} The Promise object will resolve a SafeOperation.
*/
async createTransaction({
transactions,
options = {}
}) {
const { amountToApprove, validUntil, validAfter, feeEstimator, customNonce } = options;
const userOperation = await createUserOperation(this.protocolKit, transactions, {
entryPoint: this.#ENTRYPOINT_ADDRESS,
paymasterOptions: this.#paymasterOptions,
amountToApprove,
customNonce
});
if (this.#onchainIdentifier) {
userOperation.callData += this.#onchainIdentifier;
}
const safeOperation = SafeOperationFactory_default.createSafeOperation(userOperation, {
chainId: this.#chainId,
moduleAddress: this.#SAFE_4337_MODULE_ADDRESS,
entryPoint: this.#ENTRYPOINT_ADDRESS,
validUntil,
validAfter
});
return await this.getEstimateFee({
safeOperation,
feeEstimator
});
}
/**
* Converts a SafeOperationResponse to an SafeOperation.
*
* @param {SafeOperationResponse} safeOperationResponse - The SafeOperationResponse to convert to SafeOperation
* @returns {BaseSafeOperation} - The SafeOperation object
*/
#toSafeOperation(safeOperationResponse) {
const { validUntil, validAfter, userOperation } = safeOperationResponse;
const paymaster = userOperation?.paymaster || "0x";
const paymasterData = userOperation?.paymasterData || "0x";
const safeOperation = SafeOperationFactory_default.createSafeOperation(
{
sender: userOperation?.sender || "0x",
nonce: userOperation?.nonce || "0",
initCode: userOperation?.initCode || "",
callData: userOperation?.callData || "",
callGasLimit: BigInt(userOperation?.callGasLimit || 0n),
verificationGasLimit: BigInt(userOperation?.verificationGasLim