Skip to content

Commit

Permalink
feat: consolidate MAv2Base
Browse files Browse the repository at this point in the history
  • Loading branch information
adamegyed committed Jan 30, 2025
1 parent ddd6b04 commit 7d20b9f
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 121 deletions.
69 changes: 39 additions & 30 deletions aa-sdk/core/src/account/smartContractAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,11 @@ export type ToSmartContractAccountParams<
getDummySignature: () => Hex | Promise<Hex>;
encodeExecute: (tx: AccountOp) => Promise<Hex>;
encodeBatchExecute?: (txs: AccountOp[]) => Promise<Hex>;
getNonce?: (nonceKey?: bigint) => Promise<bigint>;
// if not provided, will default to just using signMessage over the Hex
signUserOperationHash?: (uoHash: Hex) => Promise<Hex>;
encodeUpgradeToAndCall?: (params: UpgradeToAndCallParams) => Promise<Hex>;
getImplementationAddress?: () => Promise<NullAddress | Address>;
} & Omit<CustomSource, "signTransaction" | "address">;
// [!endregion ToSmartContractAccountParams]

Expand Down Expand Up @@ -260,6 +262,7 @@ export async function toSmartContractAccount<
source,
accountAddress,
getAccountInitCode,
getNonce,
signMessage,
signTypedData,
encodeBatchExecute,
Expand Down Expand Up @@ -339,11 +342,13 @@ export async function toSmartContractAccount(
getAccountInitCode,
signMessage,
signTypedData,
encodeBatchExecute,
encodeExecute,
encodeBatchExecute,
getNonce,
getDummySignature,
signUserOperationHash,
encodeUpgradeToAndCall,
getImplementationAddress,
} = params;

const client = createBundlerClient({
Expand Down Expand Up @@ -410,16 +415,18 @@ export async function toSmartContractAccount(
return initCode === "0x";
};

const getNonce = async (nonceKey = 0n): Promise<bigint> => {
if (!(await isAccountDeployed())) {
return 0n;
}

return entryPointContract.read.getNonce([
accountAddress_,
nonceKey,
]) as Promise<bigint>;
};
const getNonce_ =
getNonce ??
(async (nonceKey = 0n): Promise<bigint> => {
if (!(await isAccountDeployed())) {
return 0n;
}

return entryPointContract.read.getNonce([
accountAddress_,
nonceKey,
]) as Promise<bigint>;
});

const account = toAccount({
address: accountAddress_,
Expand Down Expand Up @@ -468,25 +475,27 @@ export async function toSmartContractAccount(
return create6492Signature(isDeployed, signature);
};

const getImplementationAddress = async (): Promise<NullAddress | Address> => {
const storage = await client.getStorageAt({
address: account.address,
// This is the default slot for the implementation address for Proxies
slot: "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
const getImplementationAddress_ =
getImplementationAddress ??
(async () => {
const storage = await client.getStorageAt({
address: account.address,
// This is the default slot for the implementation address for Proxies
slot: "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
});

if (storage == null) {
throw new FailedToGetStorageSlotError(
"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
"Proxy Implementation Address"
);
}

// The storage slot contains a full bytes32, but we want only the last 20 bytes.
// So, slice off the leading `0x` and the first 12 bytes (24 characters), leaving the last 20 bytes, then prefix with `0x`.
return `0x${storage.slice(26)}`;
});

if (storage == null) {
throw new FailedToGetStorageSlotError(
"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
"Proxy Implementation Address"
);
}

// The storage slot contains a full bytes32, but we want only the last 20 bytes.
// So, slice off the leading `0x` and the first 12 bytes (24 characters), leaving the last 20 bytes, then prefix with `0x`.
return `0x${storage.slice(26)}`;
};

if (entryPoint.version !== "0.6.0" && entryPoint.version !== "0.7.0") {
throw new InvalidEntryPointError(chain, entryPoint.version);
}
Expand All @@ -510,9 +519,9 @@ export async function toSmartContractAccount(
encodeUpgradeToAndCall: encodeUpgradeToAndCall_,
getEntryPoint: () => entryPoint,
isAccountDeployed,
getAccountNonce: getNonce,
getAccountNonce: getNonce_,
signMessageWith6492,
signTypedDataWith6492,
getImplementationAddress,
getImplementationAddress: getImplementationAddress_,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import {
getEntryPoint,
InvalidEntityIdError,
InvalidNonceKeyError,
toSmartContractAccount,
type AccountOp,
type EntryPointDef,
type SmartAccountSigner,
type SmartContractAccountWithSigner,
type ToSmartContractAccountParams,
Expand All @@ -24,6 +24,8 @@ import {
} from "viem";
import { modularAccountAbi } from "../../abis/modularAccountAbi.js";
import { serializeModuleEntity } from "../../actions/common/utils.js";
import { nativeSMASigner } from "../nativeSMASigner.js";
import { singleSignerMessageSigner } from "../../modules/single-signer-validation/signer.js";

export const executeUserOpSelector: Hex = "0x8DD7712F";

Expand Down Expand Up @@ -57,8 +59,9 @@ export type ValidationDataParams =
};

export type MAV2Account<
TSigner extends SmartAccountSigner = SmartAccountSigner
> = SmartContractAccountWithSigner<"MAV2Account", TSigner, "0.7.0"> & {
TSigner extends SmartAccountSigner = SmartAccountSigner,
Name extends string = string
> = SmartContractAccountWithSigner<Name, TSigner, "0.7.0"> & {
signerEntity: SignerEntity;
getExecutionData: (selector: Hex) => Promise<ExecutionDataView>;
getValidationData: (
Expand All @@ -68,32 +71,47 @@ export type MAV2Account<
};

export type CreateMAV2BaseFunctionsParams<
TTransport extends Transport = Transport
> = Pick<
ToSmartContractAccountParams<"MAV2Account", TTransport, Chain, "0.7.0">,
"transport" | "chain"
TTransport extends Transport = Transport,
TSigner extends SmartAccountSigner = SmartAccountSigner,
Name extends string = string
> = Omit<
ToSmartContractAccountParams<Name, TTransport, Chain, "0.7.0">,
// Implements the following methods required by `toSmartContractAccount`, and passes through any other parameters.
| "encodeExecute"
| "encodeBatchExecute"
| "getNonce"
| "signMessage"
| "signTypedData"
| "getDummySignature"
> & {
// salt?: bigint;
// factoryAddress?: Address;
// initCode?: Hex;
// initialOwner?: Address;
entryPoint?: EntryPointDef<"0.7.0", Chain>;
signer: TSigner;
signerEntity?: SignerEntity;
accountAddress: Address;
};

export async function createMAv2BaseFunctions(
export type CreateMAV2BaseReturnType<
TSigner extends SmartAccountSigner = SmartAccountSigner,
Name extends string = string
> = Promise<MAV2Account<TSigner, Name>>;

export async function createMAv2Base(
config: CreateMAV2BaseFunctionsParams
) {
): CreateMAV2BaseReturnType {
const {
transport,
chain,
signer,
entryPoint = getEntryPoint(chain, { version: "0.7.0" }),
signerEntity = {
isGlobalValidation: true,
entityId: DEFAULT_OWNER_ENTITY_ID,
},
signerEntity: {
isGlobalValidation = true,
entityId = DEFAULT_OWNER_ENTITY_ID,
} = {},
accountAddress,
...remainingToSmartContractAccountParams
} = config;

if (entityId > Number(maxUint32)) {
Expand Down Expand Up @@ -136,7 +154,7 @@ export async function createMAv2BaseFunctions(
const isAccountDeployed: () => Promise<boolean> = async () =>
!!(await client.getCode({ address: accountAddress }));
// TODO: add deferred action flag
const getAccountNonce = async (nonceKey: bigint = 0n): Promise<bigint> => {
const getNonce = async (nonceKey: bigint = 0n): Promise<bigint> => {
if (nonceKey > maxUint152) {
throw new InvalidNonceKeyError(nonceKey);
}
Expand Down Expand Up @@ -206,12 +224,26 @@ export async function createMAv2BaseFunctions(
: callData;
};

const baseAccount = await toSmartContractAccount({
...remainingToSmartContractAccountParams,
transport,
chain,
entryPoint,
accountAddress,
encodeExecute,
encodeBatchExecute,
getNonce,
...(entityId === DEFAULT_OWNER_ENTITY_ID
? nativeSMASigner(signer, chain, accountAddress)
: singleSignerMessageSigner(signer, chain, accountAddress, entityId)),
});

return {
...baseAccount,
getSigner: () => signer,
signerEntity,
getExecutionData,
getValidationData,
encodeCallData,
getAccountNonce,
encodeExecute,
encodeBatchExecute,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,13 @@ import type {
SmartAccountSigner,
ToSmartContractAccountParams,
} from "@aa-sdk/core";
import {
getEntryPoint,
toSmartContractAccount,
EntityIdOverrideError,
} from "@aa-sdk/core";
import { getEntryPoint, EntityIdOverrideError } from "@aa-sdk/core";
import { type Chain, type Hex, type Transport, type Address } from "viem";
import { DEFAULT_OWNER_ENTITY_ID } from "../utils.js";
import { singleSignerMessageSigner } from "../modules/single-signer-validation/signer.js";
import { nativeSMASigner } from "./nativeSMASigner.js";
import {
type SignerEntity,
type MAV2Account,
createMAv2BaseFunctions,
createMAv2Base,
} from "./common/modularAccountV2Base.js";

export type CreateSMA7702AccountParams<
Expand Down Expand Up @@ -76,38 +70,19 @@ export async function createSMA7702Account(
throw new EntityIdOverrideError();
}

const { encodeExecute, encodeBatchExecute, ...baseFunctions } =
await createMAv2BaseFunctions({
transport,
chain,
entryPoint,
signerEntity,
accountAddress: _accountAddress,
});
const implementation: Address = "0x69007702764179f14F51cdce752f4f775d74E139";

const getImplementationAddress = async () => implementation;

const baseAccount = await toSmartContractAccount({
return createMAv2Base({
source: "SMA7702Account",
transport,
chain,
signer,
entryPoint,
signerEntity,
accountAddress: _accountAddress,
source: `MAV2Account`,
encodeExecute,
encodeBatchExecute,
getAccountInitCode,
...(entityId === DEFAULT_OWNER_ENTITY_ID
? nativeSMASigner(signer, chain, _accountAddress)
: singleSignerMessageSigner(signer, chain, _accountAddress, entityId)),
});

const implementation: Address = "0x69007702764179f14F51cdce752f4f775d74E139";

const getImplementationAddress = async () => implementation;

return {
...baseAccount,
...baseFunctions,
getSigner: () => signer,
signerEntity,
getImplementationAddress,
};
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import type {
import {
createBundlerClient,
getEntryPoint,
toSmartContractAccount,
getAccountAddress,
} from "@aa-sdk/core";
import {
Expand All @@ -18,16 +17,11 @@ import {
type Transport,
} from "viem";
import { accountFactoryAbi } from "../abis/accountFactoryAbi.js";
import {
getDefaultMAV2FactoryAddress,
DEFAULT_OWNER_ENTITY_ID,
} from "../utils.js";
import { singleSignerMessageSigner } from "../modules/single-signer-validation/signer.js";
import { nativeSMASigner } from "./nativeSMASigner.js";
import { getDefaultMAV2FactoryAddress } from "../utils.js";
import {
type SignerEntity,
type MAV2Account,
createMAv2BaseFunctions,
createMAv2Base,
} from "./common/modularAccountV2Base.js";

export type CreateSMAV2AccountParams<
Expand Down Expand Up @@ -73,11 +67,7 @@ export async function createSMAV2Account(
initialOwner,
accountAddress,
entryPoint = getEntryPoint(chain, { version: "0.7.0" }),
signerEntity = {
isGlobalValidation: true,
entityId: DEFAULT_OWNER_ENTITY_ID,
},
signerEntity: { entityId = DEFAULT_OWNER_ENTITY_ID } = {},
signerEntity,
} = config;

const client = createBundlerClient({
Expand Down Expand Up @@ -110,33 +100,14 @@ export async function createSMAV2Account(
getAccountInitCode,
});

const { encodeExecute, encodeBatchExecute, ...baseFunctions } =
await createMAv2BaseFunctions({
transport,
chain,
entryPoint,
signerEntity,
accountAddress: _accountAddress,
});

const baseAccount = await toSmartContractAccount({
return createMAv2Base({
source: `SMAV2Account`,
transport,
chain,
signer,
entryPoint,
accountAddress: _accountAddress,
source: `MAV2Account`,
encodeExecute,
encodeBatchExecute,
getAccountInitCode,
...(entityId === DEFAULT_OWNER_ENTITY_ID
? nativeSMASigner(signer, chain, _accountAddress)
: singleSignerMessageSigner(signer, chain, _accountAddress, entityId)),
});

return {
...baseAccount,
...baseFunctions,
getSigner: () => signer,
signerEntity,
};
});
}
Loading

0 comments on commit 7d20b9f

Please sign in to comment.