diff --git a/.changeset/full-peas-open.md b/.changeset/full-peas-open.md new file mode 100644 index 00000000..16821856 --- /dev/null +++ b/.changeset/full-peas-open.md @@ -0,0 +1,5 @@ +--- +"permissionless": patch +--- + +Added authorisation support for installModule and installModules diff --git a/packages/permissionless-test/src/utils.ts b/packages/permissionless-test/src/utils.ts index 014ebe69..e6c28dce 100644 --- a/packages/permissionless-test/src/utils.ts +++ b/packages/permissionless-test/src/utils.ts @@ -766,7 +766,7 @@ export const getCoreSmartAccounts = (): Array<{ supportsEntryPointV06: false, supportsEntryPointV07: true, supportsEntryPointV08: false, - isEip7702Compliant: true, + isEip7702Compliant: false, isEip1271Compliant: false }, { diff --git a/packages/permissionless/actions/erc7579/installModule.test.ts b/packages/permissionless/actions/erc7579/installModule.test.ts index 980724ff..dea2a385 100644 --- a/packages/permissionless/actions/erc7579/installModule.test.ts +++ b/packages/permissionless/actions/erc7579/installModule.test.ts @@ -1,13 +1,17 @@ import { encodeAbiParameters, encodePacked, isHash, zeroAddress } from "viem" +import { privateKeyToAccount } from "viem/accounts" import { describe, expect } from "vitest" import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { getCoreSmartAccounts } from "../../../permissionless-test/src/utils" +import { + getCoreSmartAccounts, + getPublicClient +} from "../../../permissionless-test/src/utils" import { erc7579Actions } from "../erc7579" import { installModule } from "./installModule" describe.each(getCoreSmartAccounts())( "installModule $name", - ({ getErc7579SmartAccountClient, name }) => { + ({ getErc7579SmartAccountClient, name, isEip7702Compliant }) => { testWithRpc.skipIf(!getErc7579SmartAccountClient)( "installModule", async ({ rpc }) => { @@ -15,14 +19,22 @@ describe.each(getCoreSmartAccounts())( throw new Error("getErc7579SmartAccountClient not defined") } + const privateKey = + "0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356" + + const privateKeyAccount = privateKeyToAccount(privateKey) + const smartClientWithoutExtend = await getErc7579SmartAccountClient({ entryPoint: { version: "0.7" }, + privateKey, ...rpc }) + const publicClient = getPublicClient(rpc.anvilRpc) + const smartClient = smartClientWithoutExtend.extend( erc7579Actions() ) @@ -47,7 +59,17 @@ describe.each(getCoreSmartAccounts())( ) ] ) - : moduleData + : moduleData, + authorization: isEip7702Compliant + ? await privateKeyAccount.signAuthorization({ + address: (smartClient.account as any) + .implementation, + chainId: smartClient.chain.id, + nonce: await publicClient.getTransactionCount({ + address: smartClient.account.address + }) + }) + : undefined }) expect(isHash(opHash)).toBe(true) @@ -88,14 +110,22 @@ describe.each(getCoreSmartAccounts())( throw new Error("getErc7579SmartAccountClient not defined") } + const privateKey = + "0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356" + + const privateKeyAccount = privateKeyToAccount(privateKey) + const smartClientWithoutExtend = await getErc7579SmartAccountClient({ entryPoint: { version: "0.7" }, + privateKey, ...rpc }) + const publicClient = getPublicClient(rpc.anvilRpc) + const smartClient = smartClientWithoutExtend.extend( erc7579Actions() ) @@ -112,7 +142,17 @@ describe.each(getCoreSmartAccounts())( value: 0n, data: "0x" } - ] + ], + authorization: isEip7702Compliant + ? await privateKeyAccount.signAuthorization({ + address: (smartClient.account as any) + .implementation, + chainId: smartClient.chain.id, + nonce: await publicClient.getTransactionCount({ + address: smartClient.account.address + }) + }) + : undefined }) await smartClient.waitForUserOperationReceipt({ diff --git a/packages/permissionless/actions/erc7579/installModule.ts b/packages/permissionless/actions/erc7579/installModule.ts index 5eaaf28e..721a4801 100644 --- a/packages/permissionless/actions/erc7579/installModule.ts +++ b/packages/permissionless/actions/erc7579/installModule.ts @@ -1,4 +1,4 @@ -import type { Address, Client, Hex, OneOf } from "viem" +import type { Address, Client, Hex, OneOf, SignedAuthorization } from "viem" import { type GetSmartAccountParameter, type PaymasterActions, @@ -13,6 +13,7 @@ import type { ModuleType } from "./supportsModule.js" export type InstallModuleParameters< TSmartAccount extends SmartAccount | undefined > = GetSmartAccountParameter & { + authorization?: SignedAuthorization | undefined type: ModuleType address: Address maxFeePerGas?: bigint @@ -63,6 +64,7 @@ export function installModule( type, calls, paymaster, + authorization, paymasterContext } = parameters @@ -91,6 +93,7 @@ export function installModule( maxFeePerGas, maxPriorityFeePerGas, nonce, + authorization, account }) } diff --git a/packages/permissionless/actions/erc7579/installModules.test.ts b/packages/permissionless/actions/erc7579/installModules.test.ts index 7d057f67..3607c634 100644 --- a/packages/permissionless/actions/erc7579/installModules.test.ts +++ b/packages/permissionless/actions/erc7579/installModules.test.ts @@ -1,13 +1,17 @@ import { encodeAbiParameters, encodePacked, isHash, zeroAddress } from "viem" +import { privateKeyToAccount } from "viem/accounts" import { describe, expect } from "vitest" import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { getCoreSmartAccounts } from "../../../permissionless-test/src/utils" +import { + getCoreSmartAccounts, + getPublicClient +} from "../../../permissionless-test/src/utils" import { erc7579Actions } from "../erc7579" import { installModules } from "./installModules" describe.each(getCoreSmartAccounts())( "installModules $name", - ({ getErc7579SmartAccountClient, name }) => { + ({ getErc7579SmartAccountClient, name, isEip7702Compliant }) => { testWithRpc.skipIf(!getErc7579SmartAccountClient)( "installModules", async ({ rpc }) => { @@ -15,14 +19,22 @@ describe.each(getCoreSmartAccounts())( throw new Error("getErc7579SmartAccountClient not defined") } + const privateKey = + "0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356" + + const privateKeyAccount = privateKeyToAccount(privateKey) + const smartClientWithoutExtend = await getErc7579SmartAccountClient({ entryPoint: { version: "0.7" }, + privateKey, ...rpc }) + const publicClient = getPublicClient(rpc.anvilRpc) + const smartClient = smartClientWithoutExtend.extend( erc7579Actions() ) @@ -41,6 +53,16 @@ describe.each(getCoreSmartAccounts())( data: "0x" } ], + authorization: isEip7702Compliant + ? await privateKeyAccount.signAuthorization({ + address: (smartClient.account as any) + .implementation, + chainId: smartClient.chain.id, + nonce: await publicClient.getTransactionCount({ + address: smartClient.account.address + }) + }) + : undefined, modules: [ { type: "executor", @@ -102,19 +124,36 @@ describe.each(getCoreSmartAccounts())( throw new Error("getErc7579SmartAccountClient not defined") } + const privateKey = + "0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356" + + const privateKeyAccount = privateKeyToAccount(privateKey) + const smartClientWithoutExtend = await getErc7579SmartAccountClient({ entryPoint: { version: "0.7" }, + privateKey, ...rpc }) + const publicClient = getPublicClient(rpc.anvilRpc) const smartClient = smartClientWithoutExtend.extend( erc7579Actions() ) const userOpHash = await smartClient.sendUserOperation({ + authorization: isEip7702Compliant + ? await privateKeyAccount.signAuthorization({ + address: (smartClient.account as any) + .implementation, + chainId: smartClient.chain.id, + nonce: await publicClient.getTransactionCount({ + address: smartClient.account.address + }) + }) + : undefined, calls: [ { to: smartClient.account.address, diff --git a/packages/permissionless/actions/erc7579/installModules.ts b/packages/permissionless/actions/erc7579/installModules.ts index 0b1dbb1a..957f5754 100644 --- a/packages/permissionless/actions/erc7579/installModules.ts +++ b/packages/permissionless/actions/erc7579/installModules.ts @@ -1,4 +1,11 @@ -import type { Address, Chain, Client, Hex, Transport } from "viem" +import type { + Address, + Chain, + Client, + Hex, + SignedAuthorization, + Transport +} from "viem" import { type PaymasterActions, type SmartAccount, @@ -14,6 +21,7 @@ import { export type InstallModulesParameters< TSmartAccount extends SmartAccount | undefined > = EncodeInstallModuleParameters & { + authorization?: SignedAuthorization | undefined maxFeePerGas?: bigint maxPriorityFeePerGas?: bigint nonce?: bigint @@ -54,6 +62,7 @@ export async function installModules< modules, paymaster, paymasterContext, + authorization, calls } = parameters @@ -80,6 +89,7 @@ export async function installModules< paymasterContext, maxFeePerGas, maxPriorityFeePerGas, + authorization, nonce, account: account }) diff --git a/packages/permissionless/actions/erc7579/uninstallModule.test.ts b/packages/permissionless/actions/erc7579/uninstallModule.test.ts index 65376b31..ffcba8f3 100644 --- a/packages/permissionless/actions/erc7579/uninstallModule.test.ts +++ b/packages/permissionless/actions/erc7579/uninstallModule.test.ts @@ -1,13 +1,17 @@ import { encodeAbiParameters, encodePacked, isHash, zeroAddress } from "viem" +import { privateKeyToAccount } from "viem/accounts" import { describe, expect } from "vitest" import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { getCoreSmartAccounts } from "../../../permissionless-test/src/utils" +import { + getCoreSmartAccounts, + getPublicClient +} from "../../../permissionless-test/src/utils" import { erc7579Actions } from "../erc7579" import { uninstallModule } from "./uninstallModule" describe.each(getCoreSmartAccounts())( "uninstallModule $name", - ({ getErc7579SmartAccountClient, name }) => { + ({ getErc7579SmartAccountClient, name, isEip7702Compliant }) => { testWithRpc.skipIf(!getErc7579SmartAccountClient)( "uninstallModule", async ({ rpc }) => { @@ -15,14 +19,22 @@ describe.each(getCoreSmartAccounts())( throw new Error("getErc7579SmartAccountClient not defined") } + const privateKey = + "0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356" + + const privateKeyAccount = privateKeyToAccount(privateKey) + const smartClientWithoutExtend = await getErc7579SmartAccountClient({ entryPoint: { version: "0.7" }, + privateKey, ...rpc }) + const publicClient = getPublicClient(rpc.anvilRpc) + const smartClient = smartClientWithoutExtend.extend( erc7579Actions() ) @@ -46,7 +58,17 @@ describe.each(getCoreSmartAccounts())( ) ] ) - : moduleData + : moduleData, + authorization: isEip7702Compliant + ? await privateKeyAccount.signAuthorization({ + address: (smartClient.account as any) + .implementation, + chainId: smartClient.chain.id, + nonce: await publicClient.getTransactionCount({ + address: smartClient.account.address + }) + }) + : undefined }) await smartClient.waitForUserOperationReceipt({ diff --git a/packages/permissionless/actions/smartAccount/sendTransaction.test.ts b/packages/permissionless/actions/smartAccount/sendTransaction.test.ts index 251fbeab..fe507b33 100644 --- a/packages/permissionless/actions/smartAccount/sendTransaction.test.ts +++ b/packages/permissionless/actions/smartAccount/sendTransaction.test.ts @@ -73,16 +73,16 @@ describe.each(getCoreSmartAccounts())( async ({ rpc }) => { const { anvilRpc } = rpc - const privateKeyAccount = privateKeyToAccount( + const privateKey = "0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356" - ) + + const privateKeyAccount = privateKeyToAccount(privateKey) const smartClient = await getSmartAccountClient({ entryPoint: { version: "0.7" }, - privateKey: - "0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356", // anvil private key + privateKey, // anvil private key ...rpc })