@locker-labs/sdk
Version:
A utility library for enhancing your ERC-4337 experience
906 lines (876 loc) • 27.3 kB
text/typescript
import {
getContract,
encodeAbiParameters,
encodeFunctionData,
type Address,
type GetContractReturnType,
type Transport,
type PublicClient,
type Client,
type EncodeFunctionDataParameters,
type Chain,
type Hex,
type ReadContractReturnType,
} from "viem";
import {
ChainNotFoundError,
AccountNotFoundError,
isSmartAccountClient,
IncompatibleClientError,
type SmartContractAccount,
type GetAccountParameter,
type SendUserOperationResult,
type GetEntryPointFromAccount,
type UserOperationOverridesParameter,
type UserOperationContext,
type GetContextParameter,
} from "@aa-sdk/core";
import {
installPlugin as installPlugin_,
type Plugin,
type FunctionReference,
} from "@account-kit/smart-contracts";
type ExecutionActions<
TAccount extends SmartContractAccount | undefined =
| SmartContractAccount
| undefined,
TContext extends UserOperationContext | undefined =
| UserOperationContext
| undefined,
TEntryPointVersion extends
GetEntryPointFromAccount<TAccount> = GetEntryPointFromAccount<TAccount>,
> = {
updateOwners: (
args: Pick<
EncodeFunctionDataParameters<
typeof MultiOwnerPluginExecutionFunctionAbi,
"updateOwners"
>,
"args"
> &
UserOperationOverridesParameter<TEntryPointVersion> &
GetAccountParameter<TAccount> &
GetContextParameter<TContext>,
) => Promise<SendUserOperationResult<TEntryPointVersion>>;
};
type InstallArgs = [{ type: "address[]" }];
export type InstallMultiOwnerPluginParams = {
args: Parameters<typeof encodeAbiParameters<InstallArgs>>[1];
pluginAddress?: Address;
dependencyOverrides?: FunctionReference[];
};
type ManagementActions<
TAccount extends SmartContractAccount | undefined =
| SmartContractAccount
| undefined,
TContext extends UserOperationContext | undefined =
| Record<string, any>
| undefined,
TEntryPointVersion extends
GetEntryPointFromAccount<TAccount> = GetEntryPointFromAccount<TAccount>,
> = {
installMultiOwnerPlugin: (
args: UserOperationOverridesParameter<TEntryPointVersion> &
InstallMultiOwnerPluginParams &
GetAccountParameter<TAccount> &
GetContextParameter<TContext>,
) => Promise<SendUserOperationResult<TEntryPointVersion>>;
};
type ReadAndEncodeActions<
TAccount extends SmartContractAccount | undefined =
| SmartContractAccount
| undefined,
> = {
encodeUpdateOwners: (
args: Pick<
EncodeFunctionDataParameters<
typeof MultiOwnerPluginExecutionFunctionAbi,
"updateOwners"
>,
"args"
>,
) => Hex;
encodeEip712Domain: (
args: Pick<
EncodeFunctionDataParameters<
typeof MultiOwnerPluginExecutionFunctionAbi,
"eip712Domain"
>,
"args"
>,
) => Hex;
readEip712Domain: (
args: GetAccountParameter<TAccount>,
) => Promise<
ReadContractReturnType<
typeof MultiOwnerPluginExecutionFunctionAbi,
"eip712Domain"
>
>;
encodeIsValidSignature: (
args: Pick<
EncodeFunctionDataParameters<
typeof MultiOwnerPluginExecutionFunctionAbi,
"isValidSignature"
>,
"args"
>,
) => Hex;
readIsValidSignature: (
args: Pick<
EncodeFunctionDataParameters<
typeof MultiOwnerPluginExecutionFunctionAbi,
"isValidSignature"
>,
"args"
> &
GetAccountParameter<TAccount>,
) => Promise<
ReadContractReturnType<
typeof MultiOwnerPluginExecutionFunctionAbi,
"isValidSignature"
>
>;
};
export type MultiOwnerPluginActions<
TAccount extends SmartContractAccount | undefined =
| SmartContractAccount
| undefined,
TContext extends UserOperationContext | undefined =
| UserOperationContext
| undefined,
> = ExecutionActions<TAccount, TContext> &
ManagementActions<TAccount, TContext> &
ReadAndEncodeActions<TAccount>;
const addresses = {
1: "0xcE0000007B008F50d762D155002600004cD6c647" as Address,
10: "0xcE0000007B008F50d762D155002600004cD6c647" as Address,
137: "0xcE0000007B008F50d762D155002600004cD6c647" as Address,
252: "0xcE0000007B008F50d762D155002600004cD6c647" as Address,
2523: "0xcE0000007B008F50d762D155002600004cD6c647" as Address,
8453: "0xcE0000007B008F50d762D155002600004cD6c647" as Address,
42161: "0xcE0000007B008F50d762D155002600004cD6c647" as Address,
80001: "0xcE0000007B008F50d762D155002600004cD6c647" as Address,
80002: "0xcE0000007B008F50d762D155002600004cD6c647" as Address,
84532: "0xcE0000007B008F50d762D155002600004cD6c647" as Address,
421614: "0xcE0000007B008F50d762D155002600004cD6c647" as Address,
7777777: "0xcE0000007B008F50d762D155002600004cD6c647" as Address,
11155111: "0xcE0000007B008F50d762D155002600004cD6c647" as Address,
11155420: "0xcE0000007B008F50d762D155002600004cD6c647" as Address,
999999999: "0xcE0000007B008F50d762D155002600004cD6c647" as Address,
} as Record<number, Address>;
export const MultiOwnerPlugin: Plugin<typeof MultiOwnerPluginAbi> = {
meta: {
name: "Multi Owner Plugin",
version: "1.0.0",
addresses,
},
getContract: <C extends Client>(
client: C,
address?: Address,
): GetContractReturnType<
typeof MultiOwnerPluginAbi,
PublicClient,
Address
> => {
if (!client.chain) throw new ChainNotFoundError();
return getContract({
address: address || addresses[client.chain.id],
abi: MultiOwnerPluginAbi,
client: client,
}) as GetContractReturnType<
typeof MultiOwnerPluginAbi,
PublicClient,
Address
>;
},
};
export const multiOwnerPluginActions: <
TTransport extends Transport = Transport,
TChain extends Chain | undefined = Chain | undefined,
TAccount extends SmartContractAccount | undefined =
| SmartContractAccount
| undefined,
TContext extends UserOperationContext | undefined =
| UserOperationContext
| undefined,
>(
client: Client<TTransport, TChain, TAccount>,
) => MultiOwnerPluginActions<TAccount, TContext> = (client) => ({
updateOwners({ args, overrides, context, account = client.account }) {
if (!account) {
throw new AccountNotFoundError();
}
if (!isSmartAccountClient(client)) {
throw new IncompatibleClientError(
"SmartAccountClient",
"updateOwners",
client,
);
}
const uo = encodeFunctionData({
abi: MultiOwnerPluginExecutionFunctionAbi,
functionName: "updateOwners",
args,
});
return client.sendUserOperation({ uo, overrides, account, context });
},
installMultiOwnerPlugin({
account = client.account,
overrides,
context,
...params
}) {
if (!account) {
throw new AccountNotFoundError();
}
if (!isSmartAccountClient(client)) {
throw new IncompatibleClientError(
"SmartAccountClient",
"installMultiOwnerPlugin",
client,
);
}
const chain = client.chain;
if (!chain) {
throw new ChainNotFoundError();
}
const dependencies = params.dependencyOverrides ?? [];
const pluginAddress =
params.pluginAddress ??
(MultiOwnerPlugin.meta.addresses[chain.id] as Address | undefined);
if (!pluginAddress) {
throw new Error(
"missing MultiOwnerPlugin address for chain " + chain.name,
);
}
return installPlugin_(client, {
pluginAddress,
pluginInitData: encodeAbiParameters([{ type: "address[]" }], params.args),
dependencies,
overrides,
account,
context,
});
},
encodeUpdateOwners({ args }) {
return encodeFunctionData({
abi: MultiOwnerPluginExecutionFunctionAbi,
functionName: "updateOwners",
args,
});
},
encodeEip712Domain() {
return encodeFunctionData({
abi: MultiOwnerPluginExecutionFunctionAbi,
functionName: "eip712Domain",
});
},
async readEip712Domain({ account = client.account }) {
if (!account) {
throw new AccountNotFoundError();
}
if (!isSmartAccountClient(client)) {
throw new IncompatibleClientError(
"SmartAccountClient",
"readEip712Domain",
client,
);
}
return client.readContract({
address: account.address,
abi: MultiOwnerPluginExecutionFunctionAbi,
functionName: "eip712Domain",
});
},
encodeIsValidSignature({ args }) {
return encodeFunctionData({
abi: MultiOwnerPluginExecutionFunctionAbi,
functionName: "isValidSignature",
args,
});
},
async readIsValidSignature({ args, account = client.account }) {
if (!account) {
throw new AccountNotFoundError();
}
if (!isSmartAccountClient(client)) {
throw new IncompatibleClientError(
"SmartAccountClient",
"readIsValidSignature",
client,
);
}
return client.readContract({
address: account.address,
abi: MultiOwnerPluginExecutionFunctionAbi,
functionName: "isValidSignature",
args,
});
},
});
export const MultiOwnerPluginExecutionFunctionAbi = [
{
type: "function",
name: "updateOwners",
inputs: [
{ name: "ownersToAdd", type: "address[]", internalType: "address[]" },
{ name: "ownersToRemove", type: "address[]", internalType: "address[]" },
],
outputs: [],
stateMutability: "nonpayable",
},
{
type: "function",
name: "eip712Domain",
inputs: [],
outputs: [
{ name: "fields", type: "bytes1", internalType: "bytes1" },
{ name: "name", type: "string", internalType: "string" },
{ name: "version", type: "string", internalType: "string" },
{ name: "chainId", type: "uint256", internalType: "uint256" },
{ name: "verifyingContract", type: "address", internalType: "address" },
{ name: "salt", type: "bytes32", internalType: "bytes32" },
{ name: "extensions", type: "uint256[]", internalType: "uint256[]" },
],
stateMutability: "view",
},
{
type: "function",
name: "isValidSignature",
inputs: [
{ name: "digest", type: "bytes32", internalType: "bytes32" },
{ name: "signature", type: "bytes", internalType: "bytes" },
],
outputs: [{ name: "", type: "bytes4", internalType: "bytes4" }],
stateMutability: "view",
},
] as const;
export const MultiOwnerPluginAbi = [
{
type: "function",
name: "eip712Domain",
inputs: [],
outputs: [
{ name: "fields", type: "bytes1", internalType: "bytes1" },
{ name: "name", type: "string", internalType: "string" },
{ name: "version", type: "string", internalType: "string" },
{ name: "chainId", type: "uint256", internalType: "uint256" },
{ name: "verifyingContract", type: "address", internalType: "address" },
{ name: "salt", type: "bytes32", internalType: "bytes32" },
{ name: "extensions", type: "uint256[]", internalType: "uint256[]" },
],
stateMutability: "view",
},
{
type: "function",
name: "encodeMessageData",
inputs: [
{ name: "account", type: "address", internalType: "address" },
{ name: "message", type: "bytes", internalType: "bytes" },
],
outputs: [{ name: "", type: "bytes", internalType: "bytes" }],
stateMutability: "view",
},
{
type: "function",
name: "getMessageHash",
inputs: [
{ name: "account", type: "address", internalType: "address" },
{ name: "message", type: "bytes", internalType: "bytes" },
],
outputs: [{ name: "", type: "bytes32", internalType: "bytes32" }],
stateMutability: "view",
},
{
type: "function",
name: "isOwnerOf",
inputs: [
{ name: "account", type: "address", internalType: "address" },
{ name: "ownerToCheck", type: "address", internalType: "address" },
],
outputs: [{ name: "", type: "bool", internalType: "bool" }],
stateMutability: "view",
},
{
type: "function",
name: "isValidSignature",
inputs: [
{ name: "digest", type: "bytes32", internalType: "bytes32" },
{ name: "signature", type: "bytes", internalType: "bytes" },
],
outputs: [{ name: "", type: "bytes4", internalType: "bytes4" }],
stateMutability: "view",
},
{
type: "function",
name: "onInstall",
inputs: [{ name: "data", type: "bytes", internalType: "bytes" }],
outputs: [],
stateMutability: "nonpayable",
},
{
type: "function",
name: "onUninstall",
inputs: [{ name: "", type: "bytes", internalType: "bytes" }],
outputs: [],
stateMutability: "nonpayable",
},
{
type: "function",
name: "ownersOf",
inputs: [{ name: "account", type: "address", internalType: "address" }],
outputs: [{ name: "", type: "address[]", internalType: "address[]" }],
stateMutability: "view",
},
{
type: "function",
name: "pluginManifest",
inputs: [],
outputs: [
{
name: "",
type: "tuple",
internalType: "struct PluginManifest",
components: [
{ name: "interfaceIds", type: "bytes4[]", internalType: "bytes4[]" },
{
name: "dependencyInterfaceIds",
type: "bytes4[]",
internalType: "bytes4[]",
},
{
name: "executionFunctions",
type: "bytes4[]",
internalType: "bytes4[]",
},
{
name: "permittedExecutionSelectors",
type: "bytes4[]",
internalType: "bytes4[]",
},
{
name: "permitAnyExternalAddress",
type: "bool",
internalType: "bool",
},
{ name: "canSpendNativeToken", type: "bool", internalType: "bool" },
{
name: "permittedExternalCalls",
type: "tuple[]",
internalType: "struct ManifestExternalCallPermission[]",
components: [
{
name: "externalAddress",
type: "address",
internalType: "address",
},
{ name: "permitAnySelector", type: "bool", internalType: "bool" },
{ name: "selectors", type: "bytes4[]", internalType: "bytes4[]" },
],
},
{
name: "userOpValidationFunctions",
type: "tuple[]",
internalType: "struct ManifestAssociatedFunction[]",
components: [
{
name: "executionSelector",
type: "bytes4",
internalType: "bytes4",
},
{
name: "associatedFunction",
type: "tuple",
internalType: "struct ManifestFunction",
components: [
{
name: "functionType",
type: "uint8",
internalType: "enum ManifestAssociatedFunctionType",
},
{ name: "functionId", type: "uint8", internalType: "uint8" },
{
name: "dependencyIndex",
type: "uint256",
internalType: "uint256",
},
],
},
],
},
{
name: "runtimeValidationFunctions",
type: "tuple[]",
internalType: "struct ManifestAssociatedFunction[]",
components: [
{
name: "executionSelector",
type: "bytes4",
internalType: "bytes4",
},
{
name: "associatedFunction",
type: "tuple",
internalType: "struct ManifestFunction",
components: [
{
name: "functionType",
type: "uint8",
internalType: "enum ManifestAssociatedFunctionType",
},
{ name: "functionId", type: "uint8", internalType: "uint8" },
{
name: "dependencyIndex",
type: "uint256",
internalType: "uint256",
},
],
},
],
},
{
name: "preUserOpValidationHooks",
type: "tuple[]",
internalType: "struct ManifestAssociatedFunction[]",
components: [
{
name: "executionSelector",
type: "bytes4",
internalType: "bytes4",
},
{
name: "associatedFunction",
type: "tuple",
internalType: "struct ManifestFunction",
components: [
{
name: "functionType",
type: "uint8",
internalType: "enum ManifestAssociatedFunctionType",
},
{ name: "functionId", type: "uint8", internalType: "uint8" },
{
name: "dependencyIndex",
type: "uint256",
internalType: "uint256",
},
],
},
],
},
{
name: "preRuntimeValidationHooks",
type: "tuple[]",
internalType: "struct ManifestAssociatedFunction[]",
components: [
{
name: "executionSelector",
type: "bytes4",
internalType: "bytes4",
},
{
name: "associatedFunction",
type: "tuple",
internalType: "struct ManifestFunction",
components: [
{
name: "functionType",
type: "uint8",
internalType: "enum ManifestAssociatedFunctionType",
},
{ name: "functionId", type: "uint8", internalType: "uint8" },
{
name: "dependencyIndex",
type: "uint256",
internalType: "uint256",
},
],
},
],
},
{
name: "executionHooks",
type: "tuple[]",
internalType: "struct ManifestExecutionHook[]",
components: [
{
name: "executionSelector",
type: "bytes4",
internalType: "bytes4",
},
{
name: "preExecHook",
type: "tuple",
internalType: "struct ManifestFunction",
components: [
{
name: "functionType",
type: "uint8",
internalType: "enum ManifestAssociatedFunctionType",
},
{ name: "functionId", type: "uint8", internalType: "uint8" },
{
name: "dependencyIndex",
type: "uint256",
internalType: "uint256",
},
],
},
{
name: "postExecHook",
type: "tuple",
internalType: "struct ManifestFunction",
components: [
{
name: "functionType",
type: "uint8",
internalType: "enum ManifestAssociatedFunctionType",
},
{ name: "functionId", type: "uint8", internalType: "uint8" },
{
name: "dependencyIndex",
type: "uint256",
internalType: "uint256",
},
],
},
],
},
],
},
],
stateMutability: "pure",
},
{
type: "function",
name: "pluginMetadata",
inputs: [],
outputs: [
{
name: "",
type: "tuple",
internalType: "struct PluginMetadata",
components: [
{ name: "name", type: "string", internalType: "string" },
{ name: "version", type: "string", internalType: "string" },
{ name: "author", type: "string", internalType: "string" },
{
name: "permissionDescriptors",
type: "tuple[]",
internalType: "struct SelectorPermission[]",
components: [
{
name: "functionSelector",
type: "bytes4",
internalType: "bytes4",
},
{
name: "permissionDescription",
type: "string",
internalType: "string",
},
],
},
],
},
],
stateMutability: "pure",
},
{
type: "function",
name: "postExecutionHook",
inputs: [
{ name: "functionId", type: "uint8", internalType: "uint8" },
{ name: "preExecHookData", type: "bytes", internalType: "bytes" },
],
outputs: [],
stateMutability: "nonpayable",
},
{
type: "function",
name: "preExecutionHook",
inputs: [
{ name: "functionId", type: "uint8", internalType: "uint8" },
{ name: "sender", type: "address", internalType: "address" },
{ name: "value", type: "uint256", internalType: "uint256" },
{ name: "data", type: "bytes", internalType: "bytes" },
],
outputs: [{ name: "", type: "bytes", internalType: "bytes" }],
stateMutability: "nonpayable",
},
{
type: "function",
name: "preRuntimeValidationHook",
inputs: [
{ name: "functionId", type: "uint8", internalType: "uint8" },
{ name: "sender", type: "address", internalType: "address" },
{ name: "value", type: "uint256", internalType: "uint256" },
{ name: "data", type: "bytes", internalType: "bytes" },
],
outputs: [],
stateMutability: "nonpayable",
},
{
type: "function",
name: "preUserOpValidationHook",
inputs: [
{ name: "functionId", type: "uint8", internalType: "uint8" },
{
name: "userOp",
type: "tuple",
internalType: "struct UserOperation",
components: [
{ name: "sender", type: "address", internalType: "address" },
{ name: "nonce", type: "uint256", internalType: "uint256" },
{ name: "initCode", type: "bytes", internalType: "bytes" },
{ name: "callData", type: "bytes", internalType: "bytes" },
{ name: "callGasLimit", type: "uint256", internalType: "uint256" },
{
name: "verificationGasLimit",
type: "uint256",
internalType: "uint256",
},
{
name: "preVerificationGas",
type: "uint256",
internalType: "uint256",
},
{ name: "maxFeePerGas", type: "uint256", internalType: "uint256" },
{
name: "maxPriorityFeePerGas",
type: "uint256",
internalType: "uint256",
},
{ name: "paymasterAndData", type: "bytes", internalType: "bytes" },
{ name: "signature", type: "bytes", internalType: "bytes" },
],
},
{ name: "userOpHash", type: "bytes32", internalType: "bytes32" },
],
outputs: [{ name: "", type: "uint256", internalType: "uint256" }],
stateMutability: "nonpayable",
},
{
type: "function",
name: "runtimeValidationFunction",
inputs: [
{ name: "functionId", type: "uint8", internalType: "uint8" },
{ name: "sender", type: "address", internalType: "address" },
{ name: "", type: "uint256", internalType: "uint256" },
{ name: "", type: "bytes", internalType: "bytes" },
],
outputs: [],
stateMutability: "view",
},
{
type: "function",
name: "supportsInterface",
inputs: [{ name: "interfaceId", type: "bytes4", internalType: "bytes4" }],
outputs: [{ name: "", type: "bool", internalType: "bool" }],
stateMutability: "view",
},
{
type: "function",
name: "updateOwners",
inputs: [
{ name: "ownersToAdd", type: "address[]", internalType: "address[]" },
{ name: "ownersToRemove", type: "address[]", internalType: "address[]" },
],
outputs: [],
stateMutability: "nonpayable",
},
{
type: "function",
name: "userOpValidationFunction",
inputs: [
{ name: "functionId", type: "uint8", internalType: "uint8" },
{
name: "userOp",
type: "tuple",
internalType: "struct UserOperation",
components: [
{ name: "sender", type: "address", internalType: "address" },
{ name: "nonce", type: "uint256", internalType: "uint256" },
{ name: "initCode", type: "bytes", internalType: "bytes" },
{ name: "callData", type: "bytes", internalType: "bytes" },
{ name: "callGasLimit", type: "uint256", internalType: "uint256" },
{
name: "verificationGasLimit",
type: "uint256",
internalType: "uint256",
},
{
name: "preVerificationGas",
type: "uint256",
internalType: "uint256",
},
{ name: "maxFeePerGas", type: "uint256", internalType: "uint256" },
{
name: "maxPriorityFeePerGas",
type: "uint256",
internalType: "uint256",
},
{ name: "paymasterAndData", type: "bytes", internalType: "bytes" },
{ name: "signature", type: "bytes", internalType: "bytes" },
],
},
{ name: "userOpHash", type: "bytes32", internalType: "bytes32" },
],
outputs: [{ name: "", type: "uint256", internalType: "uint256" }],
stateMutability: "view",
},
{
type: "event",
name: "OwnerUpdated",
inputs: [
{
name: "account",
type: "address",
indexed: true,
internalType: "address",
},
{
name: "addedOwners",
type: "address[]",
indexed: false,
internalType: "address[]",
},
{
name: "removedOwners",
type: "address[]",
indexed: false,
internalType: "address[]",
},
],
anonymous: false,
},
{ type: "error", name: "AlreadyInitialized", inputs: [] },
{ type: "error", name: "EmptyOwnersNotAllowed", inputs: [] },
{ type: "error", name: "InvalidAction", inputs: [] },
{
type: "error",
name: "InvalidOwner",
inputs: [{ name: "owner", type: "address", internalType: "address" }],
},
{ type: "error", name: "NotAuthorized", inputs: [] },
{
type: "error",
name: "NotContractCaller",
inputs: [{ name: "caller", type: "address", internalType: "address" }],
},
{
type: "error",
name: "NotImplemented",
inputs: [
{ name: "selector", type: "bytes4", internalType: "bytes4" },
{ name: "functionId", type: "uint8", internalType: "uint8" },
],
},
{ type: "error", name: "NotInitialized", inputs: [] },
{
type: "error",
name: "OwnerDoesNotExist",
inputs: [{ name: "owner", type: "address", internalType: "address" }],
},
] as const;