Skip to content

Commit

Permalink
fix: decorators should now correctly respect account hoisting
Browse files Browse the repository at this point in the history
  • Loading branch information
moldy530 committed Jan 31, 2024
1 parent 8573d3f commit 86d884e
Show file tree
Hide file tree
Showing 43 changed files with 514 additions and 238 deletions.
29 changes: 13 additions & 16 deletions packages/accounts/plugingen/phases/plugin-actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const PluginActionsGenPhase: Phase = async (input) => {
addImport("viem", { name: "EncodeFunctionDataParameters", isType: true });
addImport("viem", { name: "Transport", isType: true });
addImport("viem", { name: "Chain", isType: true });
addImport("viem", { name: "Client", isType: true });
addImport("@alchemy/aa-core", {
name: "SmartContractAccount",
isType: true,
Expand All @@ -23,10 +24,6 @@ export const PluginActionsGenPhase: Phase = async (input) => {
name: "UserOperationOverrides",
isType: true,
});
addImport("@alchemy/aa-core", {
name: "SmartAccountClient",
isType: true,
});
addImport("@alchemy/aa-core", {
name: "GetAccountParameter",
isType: true,
Expand All @@ -36,6 +33,8 @@ export const PluginActionsGenPhase: Phase = async (input) => {
isType: true,
});
addImport("@alchemy/aa-core", { name: "AccountNotFoundError" });
addImport("@alchemy/aa-core", { name: "isSmartAccountClient" });
addImport("@alchemy/aa-core", { name: "IncompatibleClientError" });

const providerFunctionDefs: string[] = [];
const providerFunctions = executionAbi
Expand All @@ -58,11 +57,15 @@ export const PluginActionsGenPhase: Phase = async (input) => {
n.name
}">, "args"> & { overrides?: UserOperationOverrides; } & GetAccountParameter<TAccount>) => Promise<SendUserOperationResult>
`);
const methodName = camelCase(n.name);
return dedent`
${camelCase(n.name)}(${argsParamString}) {
${methodName}(${argsParamString}) {
if (!account) {
throw new AccountNotFoundError();
}
}
if (!isSmartAccountClient(client)) {
throw new IncompatibleClientError("SmartAccountClient", "${methodName}");
}
const uo = encodeFunctionData({
abi: ${executionAbiConst},
Expand Down Expand Up @@ -110,16 +113,10 @@ export const PluginActionsGenPhase: Phase = async (input) => {
| SmartContractAccount
| undefined
>(
client: SmartAccountClient<TTransport, TChain, TAccount>
) => ${contract.name}Actions<TAccount> = <
TTransport extends Transport = Transport,
TChain extends Chain | undefined = Chain | undefined,
TAccount extends SmartContractAccount | undefined =
| SmartContractAccount
| undefined
>(
client: SmartAccountClient<TTransport, TChain, TAccount>
) => ({ ${providerFunctions.join(",\n")} });
client: Client<TTransport, TChain, TAccount>
) => ${
contract.name
}Actions<TAccount> = (client) => ({ ${providerFunctions.join(",\n")} });
`);

return input;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,18 @@ export const ManagementActionsGenPhase: Phase = async (input) => {
`
);

const installMethodName = `install${contract.name}`;

input.content.push(dedent`
install${contract.name}({account = client.account, overrides, ...params}) {
${installMethodName}({account = client.account, overrides, ...params}) {
if (!account) {
throw new AccountNotFoundError();
}
if (!isSmartAccountClient(client)) {
throw new IncompatibleClientError("SmartAccountClient", "${installMethodName}");
}
const chain = client.chain;
if (!chain) {
throw new Error("Chain is required");
Expand Down
30 changes: 11 additions & 19 deletions packages/accounts/plugingen/phases/plugin-actions/read-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,12 @@ export const AccountReadActionsGenPhase: Phase = async (input) => {
const argsEncodeString = n.inputs.length > 0 ? "args," : "";
const isViewFunction = n.stateMutability === "view";

const encodeMethodName = `encode${pascalCase(n.name)}`;
accountFunctionActionDefs.push(
dedent`encode${pascalCase(
n.name
)}: (args: Pick<EncodeFunctionDataParameters<typeof ${executionAbiConst}, "${
n.name
}">, "args">) => Hex`
dedent`${encodeMethodName}: (args: Pick<EncodeFunctionDataParameters<typeof ${executionAbiConst}, "${n.name}">, "args">) => Hex`
);
methodContent.push(dedent`
encode${pascalCase(n.name)}(${argsParamString}) {
${encodeMethodName}(${argsParamString}) {
return encodeFunctionData({
abi: ${executionAbiConst},
functionName: "${n.name}",
Expand All @@ -45,28 +42,23 @@ export const AccountReadActionsGenPhase: Phase = async (input) => {
if (isViewFunction) {
addImport("viem", { name: "ReadContractReturnType", isType: true });
input.hasReadMethods = true;
const readMethodName = `read${pascalCase(n.name)}`;
accountFunctionActionDefs.push(
n.inputs.length > 0
? dedent`read${pascalCase(
n.name
)}: (args: Pick<EncodeFunctionDataParameters<typeof ${executionAbiConst}, "${
n.name
}">, "args"> & GetAccountParameter<TAccount>) => Promise<ReadContractReturnType<typeof ${executionAbiConst}, "${
n.name
}">>`
: dedent`read${pascalCase(
n.name
)}: (args: GetAccountParameter<TAccount>) => Promise<ReadContractReturnType<typeof ${executionAbiConst}, "${
n.name
}">>`
? dedent`${readMethodName}: (args: Pick<EncodeFunctionDataParameters<typeof ${executionAbiConst}, "${n.name}">, "args"> & GetAccountParameter<TAccount>) => Promise<ReadContractReturnType<typeof ${executionAbiConst}, "${n.name}">>`
: dedent`${readMethodName}: (args: GetAccountParameter<TAccount>) => Promise<ReadContractReturnType<typeof ${executionAbiConst}, "${n.name}">>`
);

methodContent.push(dedent`
async read${pascalCase(n.name)} (${readArgsParamString}) {
async ${readMethodName} (${readArgsParamString}) {
if (!account) {
throw new AccountNotFoundError();
}
if (!isSmartAccountClient(client)) {
throw new IncompatibleClientError("SmartAccountClient", "${readMethodName}");
}
return client.readContract({
address: account.address,
abi: ${executionAbiConst},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import type { Phase } from "../../types";
export const GetContractGenPhase: Phase = async (input) => {
const { content, contract, addImport } = input;

addImport("@alchemy/aa-core", {
name: "SmartAccountClient",
isType: true,
});
addImport("viem", { name: "getContract", isType: false });
addImport("viem", { name: "GetContractReturnType", isType: true });
addImport("viem", { name: "Address", isType: true });
Expand Down
14 changes: 11 additions & 3 deletions packages/accounts/src/light-account/actions/transferOwnership.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {
AccountNotFoundError,
IncompatibleClientError,
isSmartAccountClient,
type GetAccountParameter,
type Hex,
type SmartAccountClient,
type SmartAccountSigner,
} from "@alchemy/aa-core";
import type { Chain, Transport } from "viem";
import type { Chain, Client, Transport } from "viem";
import type { LightAccount } from "../account";

export type TransferLightAccountOwnershipParams<
Expand All @@ -26,7 +27,7 @@ export const transferOwnership: <
| LightAccount<TOwner>
| undefined
>(
client: SmartAccountClient<TTransport, TChain, TAccount>,
client: Client<TTransport, TChain, TAccount>,
args: TransferLightAccountOwnershipParams<TOwner, TAccount>
) => Promise<Hex> = async (
client,
Expand All @@ -36,6 +37,13 @@ export const transferOwnership: <
throw new AccountNotFoundError();
}

if (!isSmartAccountClient(client)) {
throw new IncompatibleClientError(
"SmartAccountClient",
"transferOwnership"
);
}

const data = account.encodeTransferOwnership(await newOwner.getAddress());
const result = await client.sendUserOperation({
uo: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import type {
GetAccountParameter,
Hex,
SmartAccountClient,
SmartAccountSigner,
} from "@alchemy/aa-core";
import type { Chain, Transport } from "viem";
import type { Chain, Client, Transport } from "viem";
import type { LightAccount } from "./account";
import { transferOwnership } from "./actions/transferOwnership.js";

Expand All @@ -30,7 +29,7 @@ export const lightAccountClientActions: <
| LightAccount<TOwner>
| undefined
>(
client: SmartAccountClient<TTransport, TChain, TAccount>
client: Client<TTransport, TChain, TAccount>
) => LightAccountClientActions<TOwner, TAccount> = (client) => ({
transferOwnership: async (args) => transferOwnership(client, args),
});
36 changes: 33 additions & 3 deletions packages/accounts/src/msca/account-loupe/decorator.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {
AccountNotFoundError,
IncompatibleClientError,
isSmartAccountClient,
type GetAccountParameter,
type SmartAccountClient,
type SmartContractAccount,
} from "@alchemy/aa-core";
import type { Address, Chain, Hash, Transport } from "viem";
import type { Address, Chain, Client, Hash, Transport } from "viem";
import { IAccountLoupeAbi } from "../abis/IAccountLoupe.js";
import type {
ExecutionFunctionConfig,
Expand Down Expand Up @@ -57,7 +58,7 @@ export const accountLoupeActions: <
| SmartContractAccount
| undefined
>(
client: SmartAccountClient<TTransport, TChain, TAccount>
client: Client<TTransport, TChain, TAccount>
) => AccountLoupeActions<TAccount> = (client) => ({
getExecutionFunctionConfig: async ({
selector,
Expand All @@ -67,6 +68,13 @@ export const accountLoupeActions: <
throw new AccountNotFoundError();
}

if (!isSmartAccountClient(client)) {
throw new IncompatibleClientError(
"SmartAccountClient",
"getExecutionFunctionConfig"
);
}

return client.readContract({
address: account.address,
abi: IAccountLoupeAbi,
Expand All @@ -80,6 +88,13 @@ export const accountLoupeActions: <
throw new AccountNotFoundError();
}

if (!isSmartAccountClient(client)) {
throw new IncompatibleClientError(
"SmartAccountClient",
"getExecutionHooks"
);
}

return client.readContract({
address: account.address,
abi: IAccountLoupeAbi,
Expand All @@ -92,6 +107,14 @@ export const accountLoupeActions: <
if (!account) {
throw new AccountNotFoundError();
}

if (!isSmartAccountClient(client)) {
throw new IncompatibleClientError(
"SmartAccountClient",
"getPreValidationHooks"
);
}

return client.readContract({
address: account.address,
abi: IAccountLoupeAbi,
Expand All @@ -105,6 +128,13 @@ export const accountLoupeActions: <
throw new AccountNotFoundError();
}

if (!isSmartAccountClient(client)) {
throw new IncompatibleClientError(
"SmartAccountClient",
"getInstalledPlugins"
);
}

return client.readContract({
address: account.address,
abi: IAccountLoupeAbi,
Expand Down
5 changes: 2 additions & 3 deletions packages/accounts/src/msca/plugin-manager/decorator.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import type {
SendUserOperationResult,
SmartAccountClient,
SmartContractAccount,
} from "@alchemy/aa-core";
import type { Chain, Transport } from "viem";
import type { Chain, Client, Transport } from "viem";
import { installPlugin, type InstallPluginParams } from "./installPlugin.js";
import {
uninstallPlugin,
Expand All @@ -30,7 +29,7 @@ export const pluginManagerActions: <
| SmartContractAccount
| undefined
>(
client: SmartAccountClient<TTransport, TChain, TAccount>
client: Client<TTransport, TChain, TAccount>
) => PluginManagerActions<TAccount> = (client) => ({
installPlugin: async (params) => installPlugin(client, params),
uninstallPlugin: async (params) => uninstallPlugin(client, params),
Expand Down
9 changes: 8 additions & 1 deletion packages/accounts/src/msca/plugin-manager/installPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {
AccountNotFoundError,
IncompatibleClientError,
isSmartAccountClient,
type GetAccountParameter,
type SmartAccountClient,
type SmartContractAccount,
Expand All @@ -11,6 +13,7 @@ import {
keccak256,
type Address,
type Chain,
type Client,
type Hash,
type Transport,
} from "viem";
Expand All @@ -36,7 +39,7 @@ export async function installPlugin<
| SmartContractAccount
| undefined
>(
client: SmartAccountClient<TTransport, TChain, TAccount>,
client: Client<TTransport, TChain, TAccount>,
{
overrides,
account = client.account,
Expand All @@ -47,6 +50,10 @@ export async function installPlugin<
throw new AccountNotFoundError();
}

if (!isSmartAccountClient(client)) {
throw new IncompatibleClientError("SmartAccountClient", "installPlugin");
}

const callData = await encodeInstallPluginUserOperation(client, params);
return client.sendUserOperation({ uo: callData, overrides, account });
}
Expand Down
10 changes: 8 additions & 2 deletions packages/accounts/src/msca/plugin-manager/uninstallPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import {
AccountNotFoundError,
IncompatibleClientError,
isSmartAccountClient,
type GetAccountParameter,
type SmartAccountClient,
type SmartContractAccount,
type UserOperationOverrides,
} from "@alchemy/aa-core";
import {
encodeFunctionData,
type Address,
type Chain,
type Client,
type Hash,
type Transport,
} from "viem";
Expand All @@ -31,7 +33,7 @@ export async function uninstallPlugin<
| SmartContractAccount
| undefined
>(
client: SmartAccountClient<TTransport, TChain, TAccount>,
client: Client<TTransport, TChain, TAccount>,
{
overrides,
account = client.account,
Expand All @@ -42,6 +44,10 @@ export async function uninstallPlugin<
throw new AccountNotFoundError();
}

if (!isSmartAccountClient(client)) {
throw new IncompatibleClientError("SmartAccountClient", "uninstallPlugin");
}

const callData = await encodeUninstallPluginUserOperation(params);
return client.sendUserOperation({ uo: callData, overrides, account });
}
Expand Down
Loading

0 comments on commit 86d884e

Please sign in to comment.