-
Notifications
You must be signed in to change notification settings - Fork 132
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add switching for executeUserOp #1223
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,42 +1,78 @@ | ||
import type { | ||
EntryPointDef, | ||
SmartAccountSigner, | ||
AccountOp, | ||
SmartContractAccountWithSigner, | ||
ToSmartContractAccountParams, | ||
} from "@aa-sdk/core"; | ||
import { | ||
createBundlerClient, | ||
getEntryPoint, | ||
toSmartContractAccount, | ||
InvalidEntityIdError, | ||
InvalidNonceKeyError, | ||
} from "@aa-sdk/core"; | ||
import { | ||
concatHex, | ||
encodeFunctionData, | ||
getContract, | ||
maxUint32, | ||
maxUint152, | ||
zeroAddress, | ||
type Address, | ||
type Chain, | ||
type Hex, | ||
type Transport, | ||
} from "viem"; | ||
import { accountFactoryAbi } from "../abis/accountFactoryAbi.js"; | ||
import { getDefaultMAV2FactoryAddress } from "../utils.js"; | ||
import { standardExecutor } from "../../msca/account/standardExecutor.js"; | ||
import { | ||
getDefaultMAV2FactoryAddress, | ||
DEFAULT_OWNER_ENTITY_ID, | ||
} from "../utils.js"; | ||
import { singleSignerMessageSigner } from "../modules/single-signer-validation/signer.js"; | ||
import { InvalidEntityIdError, InvalidNonceKeyError } from "@aa-sdk/core"; | ||
import { modularAccountAbi } from "../abis/modularAccountAbi.js"; | ||
import { serializeModuleEntity } from "../actions/common/utils.js"; | ||
|
||
export const DEFAULT_OWNER_ENTITY_ID = 0; | ||
const executeUserOpSelector: Hex = "0x8DD7712F"; | ||
|
||
export type SignerEntity = { | ||
isGlobalValidation: boolean; | ||
entityId: number; | ||
}; | ||
|
||
export type ExecutionDataView = { | ||
module: Address; | ||
skipRuntimeValidation: boolean; | ||
allowGlobalValidation: boolean; | ||
executionHooks: readonly Hex[]; | ||
}; | ||
|
||
export type ValidationDataView = { | ||
validationHooks: readonly Hex[]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Especially, I think marking arrays as |
||
executionHooks: readonly Hex[]; | ||
selectors: readonly Hex[]; | ||
validationFlags: number; | ||
}; | ||
|
||
export type ValidationDataParams = | ||
| { | ||
validationModuleAddress: Address; | ||
entityId?: never; | ||
} | ||
| { | ||
validationModuleAddress?: never; | ||
entityId: number; | ||
}; | ||
|
||
export type SMAV2Account< | ||
TSigner extends SmartAccountSigner = SmartAccountSigner | ||
> = SmartContractAccountWithSigner<"SMAV2Account", TSigner, "0.7.0"> & { | ||
signerEntity: SignerEntity; | ||
getExecutionData: (selector: Hex) => Promise<ExecutionDataView>; | ||
getValidationData: ( | ||
args: ValidationDataParams | ||
) => Promise<ValidationDataView>; | ||
encodeCallData: (callData: Hex) => Promise<Hex>; | ||
}; | ||
|
||
export type CreateSMAV2AccountParams< | ||
|
@@ -110,14 +146,43 @@ export async function createSMAV2Account( | |
]); | ||
}; | ||
|
||
const encodeExecute: (tx: AccountOp) => Promise<Hex> = async ({ | ||
target, | ||
data, | ||
value, | ||
}) => | ||
await encodeCallData( | ||
encodeFunctionData({ | ||
abi: modularAccountAbi, | ||
functionName: "execute", | ||
args: [target, value ?? 0n, data], | ||
}) | ||
); | ||
|
||
const encodeBatchExecute: (txs: AccountOp[]) => Promise<Hex> = async (txs) => | ||
await encodeCallData( | ||
encodeFunctionData({ | ||
abi: modularAccountAbi, | ||
functionName: "executeBatch", | ||
args: [ | ||
txs.map((tx) => ({ | ||
target: tx.target, | ||
data: tx.data, | ||
value: tx.value ?? 0n, | ||
})), | ||
], | ||
}) | ||
); | ||
|
||
const baseAccount = await toSmartContractAccount({ | ||
transport, | ||
chain, | ||
entryPoint, | ||
accountAddress, | ||
source: `SMAV2Account`, | ||
encodeExecute, | ||
encodeBatchExecute, | ||
getAccountInitCode, | ||
...standardExecutor, | ||
...singleSignerMessageSigner(signer), | ||
}); | ||
|
||
|
@@ -144,10 +209,61 @@ export async function createSMAV2Account( | |
]) as Promise<bigint>; | ||
}; | ||
|
||
const accountContract = getContract({ | ||
address: baseAccount.address, | ||
abi: modularAccountAbi, | ||
client, | ||
}); | ||
|
||
const getExecutionData = async (selector: Hex) => { | ||
if (!(await baseAccount.isAccountDeployed())) { | ||
return { | ||
module: zeroAddress, | ||
skipRuntimeValidation: false, | ||
allowGlobalValidation: false, | ||
executionHooks: [], | ||
}; | ||
} | ||
|
||
return await accountContract.read.getExecutionData([selector]); | ||
}; | ||
|
||
const getValidationData = async (args: ValidationDataParams) => { | ||
if (!(await baseAccount.isAccountDeployed())) { | ||
return { | ||
validationHooks: [], | ||
executionHooks: [], | ||
selectors: [], | ||
validationFlags: 0, | ||
}; | ||
} | ||
|
||
const { validationModuleAddress, entityId } = args; | ||
return await accountContract.read.getValidationData([ | ||
serializeModuleEntity({ | ||
moduleAddress: validationModuleAddress ?? zeroAddress, | ||
entityId: entityId ?? Number(maxUint32), | ||
}), | ||
]); | ||
}; | ||
|
||
const encodeCallData = async (callData: Hex): Promise<Hex> => { | ||
const validationData = await getValidationData({ | ||
entityId: Number(entityId), | ||
}); | ||
|
||
return validationData.executionHooks.length | ||
? concatHex([executeUserOpSelector, callData]) | ||
: callData; | ||
}; | ||
|
||
return { | ||
...baseAccount, | ||
getAccountNonce, | ||
getSigner: () => signer, | ||
signerEntity, | ||
getExecutionData, | ||
getValidationData, | ||
encodeCallData, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @moldy530 info about drilling into actions |
||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,12 @@ | ||
import { custom, parseEther, publicActions } from "viem"; | ||
|
||
import { LocalAccountSigner, type SmartAccountSigner } from "@aa-sdk/core"; | ||
|
||
import { createSMAV2AccountClient } from "./client.js"; | ||
|
||
import { createSMAV2AccountClient, type SMAV2AccountClient } from "./client.js"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
import { local070Instance } from "~test/instances.js"; | ||
import { setBalance } from "viem/actions"; | ||
import { accounts } from "~test/constants.js"; | ||
import { installValidationActions } from "../actions/install-validation/installValidation.js"; | ||
import { getDefaultSingleSignerValidationModuleAddress } from "../modules/utils.js"; | ||
import { SingleSignerValidationModule } from "../modules/single-signer-validation/module.js"; | ||
import { installValidationActions } from "../actions/install-validation/installValidation.js"; | ||
|
||
describe("MA v2 Tests", async () => { | ||
const instance = local070Instance; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there are reason some fields are in this type and the next are
readonly
and others are not?As personal style, I don't use
readonly
on data structs. IMO it doesn't meaningfully prevent mutations if you rely on that for correctness, and just makes things inconvenient otherwise. For example, the following typechecks:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
without the readonly I get a type error, it seems like the return data from the view call is readonly for all Hex types
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there a way to remove the readonlys?