permissionless
Version:
A utility library for working with ERC-4337
1,090 lines • 39.3 kB
JavaScript
import { concat, decodeFunctionData, encodeAbiParameters, encodeFunctionData, encodePacked, getAddress, getContractAddress, hashMessage, hashTypedData, hexToBigInt, keccak256, pad, size, slice, toBytes, toHex, zeroAddress } from "viem";
import { entryPoint06Abi, entryPoint07Abi, entryPoint07Address, toSmartAccount } from "viem/account-abstraction";
import { getChainId, readContract } from "viem/actions";
import { getAction } from "viem/utils";
import { getAccountNonce } from "../../actions/public/getAccountNonce.js";
import { decode7579Calls } from "../../utils/decode7579Calls.js";
import { encode7579Calls } from "../../utils/encode7579Calls.js";
import { isSmartAccountDeployed } from "../../utils/isSmartAccountDeployed.js";
import { toOwner } from "../../utils/toOwner.js";
import { signUserOperation } from "./signUserOperation.js";
const multiSendAbi = [
{
inputs: [
{
internalType: "bytes",
name: "transactions",
type: "bytes"
}
],
name: "multiSend",
outputs: [],
stateMutability: "payable",
type: "function"
}
];
const initSafe7579Abi = [
{
type: "function",
name: "initSafe7579",
inputs: [
{
name: "safe7579",
type: "address",
internalType: "address"
},
{
name: "executors",
type: "tuple[]",
internalType: "struct ModuleInit[]",
components: [
{
name: "module",
type: "address",
internalType: "address"
},
{
name: "initData",
type: "bytes",
internalType: "bytes"
}
]
},
{
name: "fallbacks",
type: "tuple[]",
internalType: "struct ModuleInit[]",
components: [
{
name: "module",
type: "address",
internalType: "address"
},
{
name: "initData",
type: "bytes",
internalType: "bytes"
}
]
},
{
name: "hooks",
type: "tuple[]",
internalType: "struct ModuleInit[]",
components: [
{
name: "module",
type: "address",
internalType: "address"
},
{
name: "initData",
type: "bytes",
internalType: "bytes"
}
]
},
{
name: "attesters",
type: "address[]",
internalType: "address[]"
},
{
name: "threshold",
type: "uint8",
internalType: "uint8"
}
],
outputs: [],
stateMutability: "nonpayable"
}
];
const preValidationSetupAbi = [
{
type: "function",
name: "preValidationSetup",
inputs: [
{
name: "initHash",
type: "bytes32",
internalType: "bytes32"
},
{
name: "to",
type: "address",
internalType: "address"
},
{
name: "preInit",
type: "bytes",
internalType: "bytes"
}
],
outputs: [],
stateMutability: "nonpayable"
}
];
const enableModulesAbi = [
{
inputs: [
{
internalType: "address[]",
name: "modules",
type: "address[]"
}
],
name: "enableModules",
outputs: [],
stateMutability: "nonpayable",
type: "function"
}
];
const setupAbi = [
{
inputs: [
{
internalType: "address[]",
name: "_owners",
type: "address[]"
},
{
internalType: "uint256",
name: "_threshold",
type: "uint256"
},
{
internalType: "address",
name: "to",
type: "address"
},
{
internalType: "bytes",
name: "data",
type: "bytes"
},
{
internalType: "address",
name: "fallbackHandler",
type: "address"
},
{
internalType: "address",
name: "paymentToken",
type: "address"
},
{
internalType: "uint256",
name: "payment",
type: "uint256"
},
{
internalType: "address payable",
name: "paymentReceiver",
type: "address"
}
],
name: "setup",
outputs: [],
stateMutability: "nonpayable",
type: "function"
}
];
const createProxyWithNonceAbi = [
{
inputs: [
{
internalType: "address",
name: "_singleton",
type: "address"
},
{
internalType: "bytes",
name: "initializer",
type: "bytes"
},
{
internalType: "uint256",
name: "saltNonce",
type: "uint256"
}
],
name: "createProxyWithNonce",
outputs: [
{
internalType: "contract SafeProxy",
name: "proxy",
type: "address"
}
],
stateMutability: "nonpayable",
type: "function"
}
];
const setupSafeAbi = [
{
type: "function",
name: "setupSafe",
inputs: [
{
name: "initData",
type: "tuple",
internalType: "struct Safe7579Launchpad.InitData",
components: [
{
name: "singleton",
type: "address",
internalType: "address"
},
{
name: "owners",
type: "address[]",
internalType: "address[]"
},
{
name: "threshold",
type: "uint256",
internalType: "uint256"
},
{
name: "setupTo",
type: "address",
internalType: "address"
},
{
name: "setupData",
type: "bytes",
internalType: "bytes"
},
{
name: "safe7579",
type: "address",
internalType: "contract ISafe7579"
},
{
name: "validators",
type: "tuple[]",
internalType: "struct ModuleInit[]",
components: [
{
name: "module",
type: "address",
internalType: "address"
},
{
name: "initData",
type: "bytes",
internalType: "bytes"
}
]
},
{
name: "callData",
type: "bytes",
internalType: "bytes"
}
]
}
],
outputs: [],
stateMutability: "nonpayable"
}
];
const executeUserOpWithErrorStringAbi = [
{
inputs: [
{
internalType: "address",
name: "to",
type: "address"
},
{
internalType: "uint256",
name: "value",
type: "uint256"
},
{
internalType: "bytes",
name: "data",
type: "bytes"
},
{
internalType: "uint8",
name: "operation",
type: "uint8"
}
],
name: "executeUserOpWithErrorString",
outputs: [],
stateMutability: "nonpayable",
type: "function"
}
];
export const 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" }
]
};
export const 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" }
]
};
const SAFE_VERSION_TO_ADDRESSES_MAP = {
"1.4.1": {
"0.6": {
SAFE_MODULE_SETUP_ADDRESS: "0x8EcD4ec46D4D2a6B64fE960B3D64e8B94B2234eb",
SAFE_4337_MODULE_ADDRESS: "0xa581c4A4DB7175302464fF3C06380BC3270b4037",
SAFE_PROXY_FACTORY_ADDRESS: "0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67",
SAFE_SINGLETON_ADDRESS: "0x41675C099F32341bf84BFc5382aF534df5C7461a",
MULTI_SEND_ADDRESS: "0x38869bf66a61cF6bDB996A6aE40D5853Fd43B526",
MULTI_SEND_CALL_ONLY_ADDRESS: "0x9641d764fc13c8B624c04430C7356C1C7C8102e2"
},
"0.7": {
SAFE_MODULE_SETUP_ADDRESS: "0x2dd68b007B46fBe91B9A7c3EDa5A7a1063cB5b47",
SAFE_4337_MODULE_ADDRESS: "0x75cf11467937ce3F2f357CE24ffc3DBF8fD5c226",
SAFE_PROXY_FACTORY_ADDRESS: "0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67",
SAFE_SINGLETON_ADDRESS: "0x41675C099F32341bf84BFc5382aF534df5C7461a",
MULTI_SEND_ADDRESS: "0x38869bf66a61cF6bDB996A6aE40D5853Fd43B526",
MULTI_SEND_CALL_ONLY_ADDRESS: "0x9641d764fc13c8B624c04430C7356C1C7C8102e2"
}
}
};
const adjustVInSignature = (signingMethod, signature) => {
const ETHEREUM_V_VALUES = [0, 1, 27, 28];
const MIN_VALID_V_VALUE_FOR_SAFE_ECDSA = 27;
let signatureV = Number.parseInt(signature.slice(-2), 16);
if (!ETHEREUM_V_VALUES.includes(signatureV)) {
throw new Error("Invalid signature");
}
if (signingMethod === "eth_sign") {
if (signatureV < MIN_VALID_V_VALUE_FOR_SAFE_ECDSA) {
signatureV += MIN_VALID_V_VALUE_FOR_SAFE_ECDSA;
}
signatureV += 4;
}
if (signingMethod === "eth_signTypedData") {
if (signatureV < MIN_VALID_V_VALUE_FOR_SAFE_ECDSA) {
signatureV += MIN_VALID_V_VALUE_FOR_SAFE_ECDSA;
}
}
return (signature.slice(0, -2) + signatureV.toString(16));
};
const generateSafeMessageMessage = (message) => {
const signableMessage = message;
if (typeof signableMessage === "string" || signableMessage.raw) {
return hashMessage(signableMessage);
}
return hashTypedData(message);
};
const encodeInternalTransaction = (tx) => {
const encoded = encodePacked(
// uint8 = 1 byte for operation
// address = 20 bytes for to address
// uint256 = 32 bytes for value
// uint256 = 32 bytes for data length
// bytes = dynamic length for data
["uint8", "address", "uint256", "uint256", "bytes"], [
tx.operation,
tx.to,
tx.value,
BigInt(tx.data.slice(2).length / 2),
tx.data
]);
return encoded.slice(2);
};
const encodeMultiSend = (txs) => {
const data = `0x${txs
.map((tx) => encodeInternalTransaction(tx))
.join("")}`;
return encodeFunctionData({
abi: multiSendAbi,
functionName: "multiSend",
args: [data]
});
};
const get7579LaunchPadInitData = ({ safe4337ModuleAddress, safeSingletonAddress, erc7579LaunchpadAddress, owners, validators, executors, fallbacks, hooks, attesters, threshold, attestersThreshold }) => {
const initData = {
singleton: safeSingletonAddress,
owners: owners,
threshold: threshold,
setupTo: erc7579LaunchpadAddress,
setupData: encodeFunctionData({
abi: initSafe7579Abi,
functionName: "initSafe7579",
args: [
safe4337ModuleAddress, // SAFE_7579_ADDRESS,
executors.map((executor) => ({
module: executor.address,
initData: executor.context
})),
fallbacks.map((fallback) => ({
module: fallback.address,
initData: fallback.context
})),
hooks.map((hook) => ({
module: hook.address,
initData: hook.context
})),
attesters.sort((left, right) => left.toLowerCase().localeCompare(right.toLowerCase())),
attestersThreshold
]
}),
safe7579: safe4337ModuleAddress,
validators: validators
};
return initData;
};
const getInitializerCode = async ({ owners, threshold, safeModuleSetupAddress, safe4337ModuleAddress, multiSendAddress, safeSingletonAddress, erc7579LaunchpadAddress, setupTransactions = [], safeModules = [], validators = [], executors = [], fallbacks = [], hooks = [], attesters = [], attestersThreshold = 0, paymentToken = zeroAddress, payment = BigInt(0), paymentReceiver = zeroAddress }) => {
if (erc7579LaunchpadAddress) {
const initData = get7579LaunchPadInitData({
safe4337ModuleAddress,
safeSingletonAddress,
erc7579LaunchpadAddress,
owners,
validators,
executors,
fallbacks,
threshold,
hooks,
attesters,
attestersThreshold
});
const initHash = keccak256(encodeAbiParameters([
{
internalType: "address",
name: "singleton",
type: "address"
},
{
internalType: "address[]",
name: "owners",
type: "address[]"
},
{
internalType: "uint256",
name: "threshold",
type: "uint256"
},
{
internalType: "address",
name: "setupTo",
type: "address"
},
{
internalType: "bytes",
name: "setupData",
type: "bytes"
},
{
internalType: "contract ISafe7579",
name: "safe7579",
type: "address"
},
{
internalType: "struct ModuleInit[]",
name: "validators",
type: "tuple[]",
components: [
{
internalType: "address",
name: "module",
type: "address"
},
{
internalType: "bytes",
name: "initData",
type: "bytes"
}
]
}
], [
initData.singleton,
initData.owners,
initData.threshold,
initData.setupTo,
initData.setupData,
initData.safe7579,
initData.validators.map((validator) => ({
module: validator.address,
initData: validator.context
}))
]));
return encodeFunctionData({
abi: preValidationSetupAbi,
functionName: "preValidationSetup",
args: [initHash, zeroAddress, "0x"]
});
}
const multiSendCallData = encodeMultiSend([
{
to: safeModuleSetupAddress,
data: encodeFunctionData({
abi: enableModulesAbi,
functionName: "enableModules",
args: [[safe4337ModuleAddress, ...safeModules]]
}),
value: BigInt(0),
operation: 1
},
...setupTransactions.map((tx) => ({ ...tx, operation: 0 }))
]);
return encodeFunctionData({
abi: setupAbi,
functionName: "setup",
args: [
owners,
threshold,
multiSendAddress,
multiSendCallData,
safe4337ModuleAddress,
paymentToken,
payment,
paymentReceiver
]
});
};
export function getPaymasterAndData(unpackedUserOperation) {
return unpackedUserOperation.paymaster
? concat([
unpackedUserOperation.paymaster,
pad(toHex(unpackedUserOperation.paymasterVerificationGasLimit ||
BigInt(0)), {
size: 16
}),
pad(toHex(unpackedUserOperation.paymasterPostOpGasLimit || BigInt(0)), {
size: 16
}),
unpackedUserOperation.paymasterData || "0x"
])
: "0x";
}
const getAccountInitCode = async ({ owners, threshold, safeModuleSetupAddress, safe4337ModuleAddress, safeSingletonAddress, erc7579LaunchpadAddress, multiSendAddress, paymentToken, payment, paymentReceiver, saltNonce = BigInt(0), setupTransactions = [], safeModules = [], validators = [], executors = [], fallbacks = [], hooks = [], attesters = [], attestersThreshold = 0 }) => {
const initializer = await getInitializerCode({
owners,
threshold,
safeModuleSetupAddress,
safe4337ModuleAddress,
multiSendAddress,
setupTransactions,
safeSingletonAddress,
safeModules,
erc7579LaunchpadAddress,
validators,
executors,
fallbacks,
hooks,
attesters,
attestersThreshold,
paymentToken,
payment,
paymentReceiver
});
const initCodeCallData = encodeFunctionData({
abi: createProxyWithNonceAbi,
functionName: "createProxyWithNonce",
args: [
erc7579LaunchpadAddress ?? safeSingletonAddress,
initializer,
saltNonce
]
});
return initCodeCallData;
};
export const getDefaultAddresses = (safeVersion, entryPointVersion, { addModuleLibAddress: _addModuleLibAddress, safeModuleSetupAddress: _safeModuleSetupAddress, safe4337ModuleAddress: _safe4337ModuleAddress, safeProxyFactoryAddress: _safeProxyFactoryAddress, safeSingletonAddress: _safeSingletonAddress, multiSendAddress: _multiSendAddress, multiSendCallOnlyAddress: _multiSendCallOnlyAddress }) => {
const safeModuleSetupAddress = _safeModuleSetupAddress ??
_addModuleLibAddress ??
SAFE_VERSION_TO_ADDRESSES_MAP[safeVersion][entryPointVersion]
.SAFE_MODULE_SETUP_ADDRESS;
const safe4337ModuleAddress = _safe4337ModuleAddress ??
SAFE_VERSION_TO_ADDRESSES_MAP[safeVersion][entryPointVersion]
.SAFE_4337_MODULE_ADDRESS;
const safeProxyFactoryAddress = _safeProxyFactoryAddress ??
SAFE_VERSION_TO_ADDRESSES_MAP[safeVersion][entryPointVersion]
.SAFE_PROXY_FACTORY_ADDRESS;
const safeSingletonAddress = _safeSingletonAddress ??
SAFE_VERSION_TO_ADDRESSES_MAP[safeVersion][entryPointVersion]
.SAFE_SINGLETON_ADDRESS;
const multiSendAddress = _multiSendAddress ??
SAFE_VERSION_TO_ADDRESSES_MAP[safeVersion][entryPointVersion]
.MULTI_SEND_ADDRESS;
const multiSendCallOnlyAddress = _multiSendCallOnlyAddress ??
SAFE_VERSION_TO_ADDRESSES_MAP[safeVersion][entryPointVersion]
.MULTI_SEND_CALL_ONLY_ADDRESS;
return {
safeModuleSetupAddress,
safe4337ModuleAddress,
safeProxyFactoryAddress,
safeSingletonAddress,
multiSendAddress,
multiSendCallOnlyAddress
};
};
function isErc7579Args(args) {
return args.erc7579LaunchpadAddress !== undefined;
}
const proxyCreationCodeAbi = [
{
inputs: [],
name: "proxyCreationCode",
outputs: [
{
internalType: "bytes",
name: "",
type: "bytes"
}
],
stateMutability: "pure",
type: "function"
}
];
const getAccountAddress = async ({ client, owners, threshold, safeModuleSetupAddress, safe4337ModuleAddress, safeProxyFactoryAddress, safeSingletonAddress, multiSendAddress, erc7579LaunchpadAddress, paymentToken, payment, paymentReceiver, setupTransactions = [], safeModules = [], saltNonce = BigInt(0), validators = [], executors = [], fallbacks = [], hooks = [], attesters = [], attestersThreshold = 0 }) => {
const proxyCreationCode = await readContract(client, {
abi: proxyCreationCodeAbi,
address: safeProxyFactoryAddress,
functionName: "proxyCreationCode"
});
const initializer = await getInitializerCode({
owners,
threshold,
safeModuleSetupAddress,
safe4337ModuleAddress,
multiSendAddress,
setupTransactions,
safeSingletonAddress,
safeModules,
erc7579LaunchpadAddress,
validators,
executors,
fallbacks,
hooks,
attesters,
attestersThreshold,
paymentToken,
payment,
paymentReceiver
});
const deploymentCode = encodePacked(["bytes", "uint256"], [
proxyCreationCode,
hexToBigInt(erc7579LaunchpadAddress ?? safeSingletonAddress)
]);
const salt = keccak256(encodePacked(["bytes32", "uint256"], [keccak256(encodePacked(["bytes"], [initializer])), saltNonce]));
return getContractAddress({
from: safeProxyFactoryAddress,
salt,
bytecode: deploymentCode,
opcode: "CREATE2"
});
};
/**
* @description Creates an Simple Account from a private key.
*
* @returns A Private Key Simple Account.
*/
export async function toSafeSmartAccount(parameters) {
const { client, owners: _owners, address, threshold = BigInt(_owners.length), version, safe4337ModuleAddress: _safe4337ModuleAddress, safeProxyFactoryAddress: _safeProxyFactoryAddress, safeSingletonAddress: _safeSingletonAddress, erc7579LaunchpadAddress, saltNonce = BigInt(0), validUntil = 0, validAfter = 0, nonceKey, paymentToken, payment, paymentReceiver, onchainIdentifier } = parameters;
const owners = await Promise.all(_owners.map(async (owner) => {
if ("account" in owner) {
return owner.account;
}
if ("request" in owner) {
return toOwner({
owner: owner
});
}
return owner;
}));
const localOwners = await Promise.all(_owners
.filter((owner) => {
if ("type" in owner && owner.type === "local") {
return true;
}
if ("request" in owner) {
return true;
}
if ("account" in owner) {
// walletClient
return true;
}
return false;
})
.map((owner) => toOwner({
owner: owner
})));
const entryPoint = {
address: parameters.entryPoint?.address ?? entryPoint07Address,
abi: (parameters.entryPoint?.version ?? "0.7") === "0.6"
? entryPoint06Abi
: entryPoint07Abi,
version: parameters.entryPoint?.version ?? "0.7"
};
let _safeModuleSetupAddress = undefined;
let _multiSendAddress = undefined;
let _multiSendCallOnlyAddress = undefined;
let safeModules = undefined;
let setupTransactions = [];
let validators = [];
let executors = [];
let fallbacks = [];
let hooks = [];
let attesters = [];
let attestersThreshold = 0;
if (!isErc7579Args(parameters)) {
_safeModuleSetupAddress = parameters.safeModuleSetupAddress;
_multiSendAddress = parameters.multiSendAddress;
_multiSendCallOnlyAddress = parameters.multiSendCallOnlyAddress;
safeModules = parameters.safeModules;
setupTransactions = parameters.setupTransactions ?? [];
}
if (isErc7579Args(parameters)) {
validators = parameters.validators ?? [];
executors = parameters.executors ?? [];
fallbacks = parameters.fallbacks ?? [];
hooks = parameters.hooks ?? [];
attesters = parameters.attesters ?? [];
attestersThreshold = parameters.attestersThreshold ?? 0;
}
const { safeModuleSetupAddress, safe4337ModuleAddress, safeProxyFactoryAddress, safeSingletonAddress, multiSendAddress, multiSendCallOnlyAddress } = getDefaultAddresses(version, entryPoint.version, {
safeModuleSetupAddress: _safeModuleSetupAddress,
safe4337ModuleAddress: _safe4337ModuleAddress,
safeProxyFactoryAddress: _safeProxyFactoryAddress,
safeSingletonAddress: _safeSingletonAddress,
multiSendAddress: _multiSendAddress,
multiSendCallOnlyAddress: _multiSendCallOnlyAddress
});
let accountAddress = address;
let chainId;
const getMemoizedChainId = async () => {
if (chainId)
return chainId;
chainId = client.chain
? client.chain.id
: await getAction(client, getChainId, "getChainId")({});
return chainId;
};
const getFactoryArgs = async () => {
return {
factory: safeProxyFactoryAddress,
factoryData: await getAccountInitCode({
owners: owners.map((owner) => owner.address),
threshold,
safeModuleSetupAddress,
safe4337ModuleAddress,
safeSingletonAddress,
multiSendAddress,
erc7579LaunchpadAddress,
saltNonce,
setupTransactions,
safeModules,
validators,
executors,
fallbacks,
hooks,
attesters,
attestersThreshold,
paymentToken,
payment,
paymentReceiver
})
};
};
return toSmartAccount({
client,
entryPoint,
getFactoryArgs,
async getAddress() {
if (accountAddress)
return accountAddress;
// Get the sender address based on the init code
accountAddress = await getAccountAddress({
client,
owners: owners.map((owner) => owner.address),
threshold,
safeModuleSetupAddress,
safe4337ModuleAddress,
safeProxyFactoryAddress,
safeSingletonAddress,
multiSendAddress,
erc7579LaunchpadAddress,
saltNonce,
setupTransactions,
safeModules,
validators,
executors,
fallbacks,
hooks,
attesters,
attestersThreshold,
paymentToken,
payment,
paymentReceiver
});
return accountAddress;
},
async encodeCalls(calls) {
const hasMultipleCalls = calls.length > 1;
if (erc7579LaunchpadAddress) {
const safeDeployed = await isSmartAccountDeployed(client, await this.getAddress());
if (!safeDeployed) {
const initData = get7579LaunchPadInitData({
safe4337ModuleAddress,
safeSingletonAddress,
erc7579LaunchpadAddress,
owners: owners.map((owner) => owner.address),
threshold,
validators,
executors,
fallbacks,
hooks,
attesters,
attestersThreshold
});
return encodeFunctionData({
abi: setupSafeAbi,
functionName: "setupSafe",
args: [
{
...initData,
validators: initData.validators.map((validator) => ({
module: validator.address,
initData: validator.context
})),
callData: encode7579Calls({
mode: {
type: hasMultipleCalls
? "batchcall"
: "call",
revertOnError: false,
selector: "0x",
context: "0x"
},
callData: calls
})
}
]
});
}
return encode7579Calls({
mode: {
type: hasMultipleCalls ? "batchcall" : "call",
revertOnError: false,
selector: "0x",
context: "0x"
},
callData: calls
});
}
let to;
let value;
let data;
let operationType = 0;
if (hasMultipleCalls) {
to = multiSendCallOnlyAddress;
value = BigInt(0);
data = encodeMultiSend(calls.map((tx) => ({
to: tx.to,
value: tx.value ?? 0n,
data: tx.data ?? "0x",
operation: 0
})));
operationType = 1;
}
else {
const call = calls.length === 0 ? undefined : calls[0];
if (!call) {
throw new Error("No calls to encode");
}
to = call.to;
data = call.data ?? "0x";
value = call.value ?? 0n;
}
const calldata = encodeFunctionData({
abi: executeUserOpWithErrorStringAbi,
functionName: "executeUserOpWithErrorString",
args: [to, value, data, operationType]
});
if (onchainIdentifier) {
return concat([calldata, onchainIdentifier]);
}
return calldata;
},
async decodeCalls(callData) {
try {
const decoded = decodeFunctionData({
abi: setupSafeAbi,
data: callData
});
return decode7579Calls(decoded.args[0].callData).callData;
}
catch (_) { }
try {
return decode7579Calls(callData).callData;
}
catch (_) { }
const decoded = decodeFunctionData({
abi: executeUserOpWithErrorStringAbi,
data: callData
});
const to = decoded.args[0];
const value = decoded.args[1];
const data = decoded.args[2];
if (to === multiSendCallOnlyAddress) {
const decodedMultiSend = decodeFunctionData({
abi: multiSendAbi,
data: data
});
const dataToDecode = decodedMultiSend.args[0];
const transactions = [];
let position = 0;
const dataLength = size(dataToDecode);
while (position < dataLength) {
// skip the operation type
position += 1;
const to = getAddress(slice(dataToDecode, position, position + 20));
position += 20;
const value = BigInt(slice(dataToDecode, position, position + 32));
position += 32;
const dataLength = Number(BigInt(slice(dataToDecode, position, position + 32)) *
BigInt(2));
position += 32;
const data = slice(dataToDecode, position, position + dataLength);
position += dataLength;
transactions.push({ to, value, data });
}
return transactions;
}
return [{ to, value, data }];
},
async getNonce(args) {
return getAccountNonce(client, {
address: await this.getAddress(),
entryPointAddress: entryPoint.address,
key: nonceKey ?? args?.key
});
},
async getStubSignature() {
return encodePacked(["uint48", "uint48", "bytes"], [
0,
0,
`0x${owners
.map((_) => "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
.join("")}`
]);
},
async sign({ hash }) {
return this.signMessage({ message: hash });
},
async signMessage({ message }) {
if (localOwners.length !== owners.length) {
throw new Error("Owners length mismatch, currently not supported");
}
const messageHash = hashTypedData({
domain: {
chainId: await getMemoizedChainId(),
verifyingContract: await this.getAddress()
},
types: {
SafeMessage: [{ name: "message", type: "bytes" }]
},
primaryType: "SafeMessage",
message: {
message: generateSafeMessageMessage(message)
}
});
const signatures = await Promise.all(localOwners.map(async (localOwner) => ({
signer: localOwner.address,
data: adjustVInSignature("eth_sign", await localOwner.signMessage({
message: {
raw: toBytes(messageHash)
}
}))
})));
signatures.sort((left, right) => left.signer
.toLowerCase()
.localeCompare(right.signer.toLowerCase()));
const signatureBytes = concat(signatures.map((sig) => sig.data));
return erc7579LaunchpadAddress
? concat([zeroAddress, signatureBytes])
: signatureBytes;
},
async signTypedData(typedData) {
if (localOwners.length !== owners.length) {
throw new Error("Owners length mismatch, currently not supported");
}
const signatures = await Promise.all(localOwners.map(async (localOwner) => ({
signer: localOwner.address,
data: adjustVInSignature("eth_signTypedData", await localOwner.signTypedData({
domain: {
chainId: await getMemoizedChainId(),
verifyingContract: await this.getAddress()
},
types: {
SafeMessage: [
{ name: "message", type: "bytes" }
]
},
primaryType: "SafeMessage",
message: {
message: generateSafeMessageMessage(typedData)
}
}))
})));
signatures.sort((left, right) => left.signer
.toLowerCase()
.localeCompare(right.signer.toLowerCase()));
const signatureBytes = concat(signatures.map((sig) => sig.data));
return erc7579LaunchpadAddress
? concat([zeroAddress, signatureBytes])
: signatureBytes;
},
async signUserOperation(parameters) {
const { chainId = await getMemoizedChainId(), ...userOperation } = parameters;
if (localOwners.length !== owners.length) {
throw new Error("Owners length mismatch use SafeSmartAccount.signUserOperation from `permissionless/accounts/safe`");
}
let signatures = undefined;
for (const owner of localOwners) {
signatures = await signUserOperation({
...userOperation,
version,
entryPoint,
owners: localOwners,
account: owner,
chainId: await getMemoizedChainId(),
signatures,
validAfter,
validUntil,
safe4337ModuleAddress
});
}
if (!signatures) {
throw new Error("No signatures found");
}
return signatures;
}
});
}
//# sourceMappingURL=toSafeSmartAccount.js.map