From 1d6ac35c15f444d3283624fdaa0b2b5af8e89920 Mon Sep 17 00:00:00 2001 From: Joe Pegler Date: Tue, 14 Jan 2025 21:51:13 +0000 Subject: [PATCH] feat: buildInstructions devEx --- .../buildBalanceInstructions.test.ts | 49 -------- .../decorators/buildBalanceInstructions.ts | 52 -------- .../decorators/buildBridgeInstructions.ts | 6 +- .../decorators/buildInstructions.test.ts | 115 ++++++++++++++++++ .../account/decorators/buildInstructions.ts | 78 ++++++++++++ .../decorators/getUnifiedERC20Balance.ts | 4 +- .../account/decorators/queryBridge.test.ts | 4 +- .../account/toMultiChainNexusAccount.test.ts | 13 +- src/sdk/account/toMultiChainNexusAccount.ts | 28 ++--- src/sdk/clients/createHttpClient.test.ts | 2 +- src/sdk/clients/createMeeClient.test.ts | 88 +++++++++----- .../clients/decorators/mee/getQuote.test.ts | 41 +++++++ src/sdk/clients/decorators/mee/getQuote.ts | 26 +++- 13 files changed, 345 insertions(+), 161 deletions(-) delete mode 100644 src/sdk/account/decorators/buildBalanceInstructions.test.ts delete mode 100644 src/sdk/account/decorators/buildBalanceInstructions.ts create mode 100644 src/sdk/account/decorators/buildInstructions.test.ts create mode 100644 src/sdk/account/decorators/buildInstructions.ts diff --git a/src/sdk/account/decorators/buildBalanceInstructions.test.ts b/src/sdk/account/decorators/buildBalanceInstructions.test.ts deleted file mode 100644 index 21263e436..000000000 --- a/src/sdk/account/decorators/buildBalanceInstructions.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { Address, Chain, LocalAccount } from "viem" -import { base } from "viem/chains" -import { beforeAll, describe, expect, it } from "vitest" -import { toNetwork } from "../../../test/testSetup" -import type { NetworkConfig } from "../../../test/testUtils" -import { type MeeClient, createMeeClient } from "../../clients/createMeeClient" -import { mcUSDC } from "../../constants/tokens" -import { - type MultichainSmartAccount, - toMultichainNexusAccount -} from "../toMultiChainNexusAccount" -import { buildBalanceInstructions } from "./buildBalanceInstructions" - -describe("mee:buildBalanceInstruction", () => { - let network: NetworkConfig - let eoaAccount: LocalAccount - let paymentChain: Chain - let paymentToken: Address - let mcNexus: MultichainSmartAccount - let meeClient: MeeClient - - beforeAll(async () => { - network = await toNetwork("MAINNET_FROM_ENV_VARS") - - paymentChain = network.chain - paymentToken = network.paymentToken! - eoaAccount = network.account! - - mcNexus = await toMultichainNexusAccount({ - chains: [base, paymentChain], - signer: eoaAccount - }) - - meeClient = createMeeClient({ account: mcNexus }) - }) - - it("should adjust the account balance", async () => { - const instructions = await buildBalanceInstructions({ - account: mcNexus, - amount: BigInt(1000), - token: mcUSDC, - chain: base - }) - - expect(instructions.length).toBeGreaterThan(0) - expect(instructions[0]).toHaveProperty("calls") - expect(instructions[0].calls.length).toBeGreaterThan(0) - }) -}) diff --git a/src/sdk/account/decorators/buildBalanceInstructions.ts b/src/sdk/account/decorators/buildBalanceInstructions.ts deleted file mode 100644 index ddf80b157..000000000 --- a/src/sdk/account/decorators/buildBalanceInstructions.ts +++ /dev/null @@ -1,52 +0,0 @@ -import type { Chain, erc20Abi } from "viem" -import type { Instruction } from "../../clients/decorators/mee/getQuote" -import type { BaseMultichainSmartAccount } from "../toMultiChainNexusAccount" -import type { MultichainContract } from "../utils/getMultichainContract" -import buildBridgeInstructions from "./buildBridgeInstructions" -import { getUnifiedERC20Balance } from "./getUnifiedERC20Balance" - -export type BuildBalanceInstructionParams = { - /** Optional smart account to execute the transaction. If not provided, uses the client's default account */ - account: BaseMultichainSmartAccount - /** The amount of tokens to require */ - amount: bigint - /** The token to require */ - token: MultichainContract - /** The chain to require the token on */ - chain: Chain -} - -/** - * Makes sure that the user has enough funds on the selected chain before filling the - * supertransaction. Bridges funds from other chains if needed. - * - * @param client - The Mee client to use - * @param params - The parameters for the balance requirement - * @returns Instructions for any required bridging operations - * @example - * const instructions = await buildBalanceInstruction(client, { - * amount: BigInt(1000), - * token: mcUSDC, - * chain: base - * }) - */ - -export const buildBalanceInstructions = async ( - params: BuildBalanceInstructionParams -): Promise => { - const { amount, token, chain, account } = params - const unifiedBalance = await getUnifiedERC20Balance({ - mcToken: token, - account - }) - const { instructions } = await buildBridgeInstructions({ - account, - amount: amount, - toChain: chain, - unifiedBalance - }) - - return instructions -} - -export default buildBalanceInstructions diff --git a/src/sdk/account/decorators/buildBridgeInstructions.ts b/src/sdk/account/decorators/buildBridgeInstructions.ts index be74e3f8c..572ad2d92 100644 --- a/src/sdk/account/decorators/buildBridgeInstructions.ts +++ b/src/sdk/account/decorators/buildBridgeInstructions.ts @@ -124,7 +124,7 @@ export type BridgingInstructions = { * @example * const instructions = await buildBridgeInstruction(client, { * amount: BigInt(1000), - * token: mcUSDC, + * mcToken: mcUSDC, * chain: base * }) */ @@ -144,9 +144,9 @@ export const buildBridgeInstructions = async ( // Create token address mapping const tokenMapping: MultichainAddressMapping = { on: (chainId: number) => - unifiedBalance.token.deployments.get(chainId) || "0x", + unifiedBalance.mcToken.deployments.get(chainId) || "0x", deployments: Array.from( - unifiedBalance.token.deployments.entries(), + unifiedBalance.mcToken.deployments.entries(), ([chainId, address]) => ({ chainId, address diff --git a/src/sdk/account/decorators/buildInstructions.test.ts b/src/sdk/account/decorators/buildInstructions.test.ts new file mode 100644 index 000000000..fbf6e4b13 --- /dev/null +++ b/src/sdk/account/decorators/buildInstructions.test.ts @@ -0,0 +1,115 @@ +import type { Address, Chain, LocalAccount } from "viem" +import { base } from "viem/chains" +import { beforeAll, describe, expect, it } from "vitest" +import { toNetwork } from "../../../test/testSetup" +import type { NetworkConfig } from "../../../test/testUtils" +import { type MeeClient, createMeeClient } from "../../clients/createMeeClient" +import { + Instruction, + SupertransactionLike +} from "../../clients/decorators/mee/getQuote" +import { mcUSDC } from "../../constants/tokens" +import { + type MultichainSmartAccount, + toMultichainNexusAccount +} from "../toMultiChainNexusAccount" +import { buildInstructions } from "./buildInstructions" + +describe("mee:buildInstructions", () => { + let network: NetworkConfig + let eoaAccount: LocalAccount + let paymentChain: Chain + let paymentToken: Address + let mcNexus: MultichainSmartAccount + let meeClient: MeeClient + + beforeAll(async () => { + network = await toNetwork("MAINNET_FROM_ENV_VARS") + + paymentChain = network.chain + paymentToken = network.paymentToken! + eoaAccount = network.account! + + mcNexus = await toMultichainNexusAccount({ + chains: [base, paymentChain], + signer: eoaAccount + }) + + meeClient = createMeeClient({ account: mcNexus }) + }) + + it("should use the default action while building instructions", async () => { + const instructions = await buildInstructions({ + account: mcNexus, + action: { + type: "DEFAULT", + parameters: [ + { + calls: [ + { + to: "0x0000000000000000000000000000000000000000", + gasLimit: 50000n, + value: 0n + } + ], + chainId: 8453 + } + ] + } + }) + + expect(instructions).toMatchInlineSnapshot(` + [ + { + "calls": [ + { + "gasLimit": 50000n, + "to": "0x0000000000000000000000000000000000000000", + "value": 0n, + }, + ], + "chainId": 8453, + }, + ] + `) + expect(instructions.length).toBeGreaterThan(0) + }) + + it("should use the bridge action while building instructions", async () => { + const initialInstructions = await buildInstructions({ + account: mcNexus, + action: { + type: "BRIDGE", + parameters: { + amount: BigInt(1000), + mcToken: mcUSDC, + chain: base + } + } + }) + + const instructions = await buildInstructions({ + currentInstructions: initialInstructions, + account: mcNexus, + action: { + type: "DEFAULT", + parameters: [ + { + calls: [ + { + to: "0x0000000000000000000000000000000000000000", + gasLimit: 50000n, + value: 0n + } + ], + chainId: 8453 + } + ] + } + }) + + expect(instructions.length).toBe(2) + expect(instructions[0].calls.length).toBe(2) // Bridge instructions generates two calls + expect(instructions[1].calls.length).toBe(1) // Default instruction in this case generates one call + }) +}) diff --git a/src/sdk/account/decorators/buildInstructions.ts b/src/sdk/account/decorators/buildInstructions.ts new file mode 100644 index 000000000..c52a36fdc --- /dev/null +++ b/src/sdk/account/decorators/buildInstructions.ts @@ -0,0 +1,78 @@ +import type { Chain, erc20Abi } from "viem" +import type { Instruction } from "../../clients/decorators/mee/getQuote" +import type { BaseMultichainSmartAccount } from "../toMultiChainNexusAccount" +import type { MultichainContract } from "../utils/getMultichainContract" +import buildBridgeInstructions from "./buildBridgeInstructions" +import { getUnifiedERC20Balance } from "./getUnifiedERC20Balance" + +export type BridgeInstructionsForBridgeAction = { + /** The amount of tokens to require */ + amount: bigint + /** The token to require */ + mcToken: MultichainContract + /** The chain to require the token on */ + chain: Chain +} + +/** + * Parameters for querying bridge operations + */ +export type BuildInstructionsParams = { + /** The multichain smart account to check balances for */ + account: BaseMultichainSmartAccount + /** The chain to build instructions for */ + action: DefaultBuildAction | BridgeBuildAction + /** The current instructions */ + currentInstructions?: Instruction[] +} + +type DefaultBuildAction = { + type: "DEFAULT" + parameters: Instruction[] | Instruction +} + +type BridgeBuildAction = { + type: "BRIDGE" + parameters: BridgeInstructionsForBridgeAction +} + +/** + * Makes sure that the user has enough funds on the selected chain before filling the + * supertransaction. Bridges funds from other chains if needed. + * + * @param client - The Mee client to use + * @param params - The parameters for the balance requirement + * @returns Instructions for any required bridging operations + * @example + * const instructions = await buildInstructions(client, { + * amount: BigInt(1000), + * mcToken: mcUSDC, + * chain: base + * }) + */ + +export const buildInstructions = async ( + params: BuildInstructionsParams +): Promise => { + const { account, action, currentInstructions = [] } = params + + switch (action.type) { + case "BRIDGE": { + const { amount, mcToken, chain } = action.parameters + const unifiedBalance = await getUnifiedERC20Balance({ mcToken, account }) + const { instructions } = await buildBridgeInstructions({ + account, + amount: amount, + toChain: chain, + unifiedBalance + }) + return [...currentInstructions, ...instructions] + } + default: + return Array.isArray(action.parameters) + ? [...currentInstructions, ...action.parameters] + : [...currentInstructions, action.parameters] + } +} + +export default buildInstructions diff --git a/src/sdk/account/decorators/getUnifiedERC20Balance.ts b/src/sdk/account/decorators/getUnifiedERC20Balance.ts index 1e41e97ac..3434373ae 100644 --- a/src/sdk/account/decorators/getUnifiedERC20Balance.ts +++ b/src/sdk/account/decorators/getUnifiedERC20Balance.ts @@ -19,7 +19,7 @@ export type RelevantBalance = UnifiedBalanceItem & { chainId: number } */ export type UnifiedERC20Balance = { /** The multichain ERC20 token contract */ - token: MultichainContract + mcToken: MultichainContract /** Individual balance breakdown per chain */ breakdown: RelevantBalance[] } & UnifiedBalanceItem @@ -105,6 +105,6 @@ export async function getUnifiedERC20Balance( } }), breakdown: balances, - token: mcToken + mcToken } } diff --git a/src/sdk/account/decorators/queryBridge.test.ts b/src/sdk/account/decorators/queryBridge.test.ts index 518c3c014..70e148913 100644 --- a/src/sdk/account/decorators/queryBridge.test.ts +++ b/src/sdk/account/decorators/queryBridge.test.ts @@ -41,9 +41,9 @@ describe("mee:queryBridge", () => { const tokenMapping: MultichainAddressMapping = { on: (chainId: number) => - unifiedBalance.token.deployments.get(chainId) || "0x", + unifiedBalance.mcToken.deployments.get(chainId) || "0x", deployments: Array.from( - unifiedBalance.token.deployments.entries(), + unifiedBalance.mcToken.deployments.entries(), ([chainId, address]) => ({ chainId, address }) ) } diff --git a/src/sdk/account/toMultiChainNexusAccount.test.ts b/src/sdk/account/toMultiChainNexusAccount.test.ts index 7c181f257..310a5d95f 100644 --- a/src/sdk/account/toMultiChainNexusAccount.test.ts +++ b/src/sdk/account/toMultiChainNexusAccount.test.ts @@ -106,19 +106,26 @@ describe("mee.toMultiChainNexusAccount", async () => { test("mcNexus to have decorators successfully applied", async () => { expect(mcNexus.getUnifiedERC20Balance).toBeInstanceOf(Function) - expect(mcNexus.buildBalanceInstructions).toBeInstanceOf(Function) + expect(mcNexus.buildInstructions).toBeInstanceOf(Function) expect(mcNexus.buildBridgeInstructions).toBeInstanceOf(Function) expect(mcNexus.queryBridge).toBeDefined() }) + test("should check unified balance", async () => { + const unifiedBalance = await mcNexus.getUnifiedERC20Balance(mcUSDC) + expect(unifiedBalance).toHaveProperty("mcToken") + expect(unifiedBalance).toHaveProperty("breakdown") + expect(unifiedBalance.mcToken).toHaveProperty("deployments") + }) + test("should query bridge", async () => { const unifiedBalance = await mcNexus.getUnifiedERC20Balance(mcUSDC) const tokenMapping = { on: (chainId: number) => - unifiedBalance.token.deployments.get(chainId) || "0x", + unifiedBalance.mcToken.deployments.get(chainId) || "0x", deployments: Array.from( - unifiedBalance.token.deployments.entries(), + unifiedBalance.mcToken.deployments.entries(), ([chainId, address]) => ({ chainId, address }) ) } diff --git a/src/sdk/account/toMultiChainNexusAccount.ts b/src/sdk/account/toMultiChainNexusAccount.ts index f617cceda..ba7f9567d 100644 --- a/src/sdk/account/toMultiChainNexusAccount.ts +++ b/src/sdk/account/toMultiChainNexusAccount.ts @@ -10,15 +10,15 @@ import { toNexusAccount } from "./toNexusAccount" import type { MultichainContract } from "./utils/getMultichainContract" import type { Signer } from "./utils/toSigner" -import { - type BuildBalanceInstructionParams, - buildBalanceInstructions as buildBalanceInstructionsDecorator -} from "./decorators/buildBalanceInstructions" import { type BridgingInstructions, type BuildBridgeInstructionParams, buildBridgeInstructions as buildBridgeInstructionsDecorator } from "./decorators/buildBridgeInstructions" +import { + type BuildInstructionsParams, + buildInstructions as buildInstructionsDecorator +} from "./decorators/buildInstructions" import { type UnifiedERC20Balance, getUnifiedERC20Balance as getUnifiedERC20BalanceDecorator @@ -71,14 +71,14 @@ export type MultichainSmartAccount = BaseMultichainSmartAccount & { * @param params - The parameters for the balance requirement * @returns Instructions for any required bridging operations * @example - * const instructions = await mcAccount.buildBalanceInstructions({ + * const instructions = await mcAccount.buildInstructions({ * amount: BigInt(1000), - * token: mcUSDC, + * mcToken: mcUSDC, * chain: base * }) */ - buildBalanceInstructions: ( - params: Omit + buildInstructions: ( + params: Omit ) => Promise /** * Function to build instructions for bridging a token across all deployments @@ -87,7 +87,7 @@ export type MultichainSmartAccount = BaseMultichainSmartAccount & { * @example * const instructions = await mcAccount.buildBridgeInstructions({ * amount: BigInt(1000), - * token: mcUSDC, + * mcToken: mcUSDC, * chain: base * }) */ @@ -101,7 +101,7 @@ export type MultichainSmartAccount = BaseMultichainSmartAccount & { * @example * const result = await mcAccount.queryBridge({ * amount: BigInt(1000), - * token: mcUSDC, + * mcToken: mcUSDC, * chain: base * }) */ @@ -150,9 +150,9 @@ export async function toMultichainNexusAccount( return getUnifiedERC20BalanceDecorator({ mcToken, account: baseAccount }) } - const buildBalanceInstructions = ( - params: Omit - ) => buildBalanceInstructionsDecorator({ ...params, account: baseAccount }) + const buildInstructions = ( + params: Omit + ) => buildInstructionsDecorator({ ...params, account: baseAccount }) const buildBridgeInstructions = ( params: Omit @@ -164,7 +164,7 @@ export async function toMultichainNexusAccount( return { ...baseAccount, getUnifiedERC20Balance, - buildBalanceInstructions, + buildInstructions, buildBridgeInstructions, queryBridge } diff --git a/src/sdk/clients/createHttpClient.test.ts b/src/sdk/clients/createHttpClient.test.ts index 395be01df..e442cead1 100644 --- a/src/sdk/clients/createHttpClient.test.ts +++ b/src/sdk/clients/createHttpClient.test.ts @@ -10,7 +10,7 @@ import { import createHttpClient from "./createHttpClient" import { type MeeClient, createMeeClient } from "./createMeeClient" -describe("mee.createHttp Client", async () => { +describe("mee.createHttpClient", async () => { let network: NetworkConfig let eoaAccount: LocalAccount let paymentChain: Chain diff --git a/src/sdk/clients/createMeeClient.test.ts b/src/sdk/clients/createMeeClient.test.ts index 6749aa10a..6c17c2462 100644 --- a/src/sdk/clients/createMeeClient.test.ts +++ b/src/sdk/clients/createMeeClient.test.ts @@ -7,6 +7,7 @@ import { type MultichainSmartAccount, toMultichainNexusAccount } from "../account/toMultiChainNexusAccount" +import { mcUSDC } from "../constants/tokens" import { type MeeClient, createMeeClient } from "./createMeeClient" import type { Instruction } from "./decorators/mee" @@ -113,29 +114,36 @@ describe("mee.createMeeClient", async () => { test("should demo the devEx of preparing instructions", async () => { // These can be any 'Instruction', or any helper method that resolves to a 'Instruction', - // including 'buildBalanceInstruction'. They all are resolved in the 'getQuote' method under the hood. - const preparedInstructions: Instruction[] = [ - { - calls: [ + // including 'buildInstructions'. They all are resolved in the 'getQuote' method under the hood. + const currentInstructions = await meeClient.account.buildInstructions({ + action: { + type: "DEFAULT", + parameters: [ { - to: "0x0000000000000000000000000000000000000000", - gasLimit: 50000n, - value: 0n - } - ], - chainId: 8453 - }, - { - calls: [ - { - to: "0x0000000000000000000000000000000000000000", - gasLimit: 50000n, - value: 0n + calls: [ + { + to: "0x0000000000000000000000000000000000000000", + gasLimit: 50000n, + value: 0n + } + ], + chainId: 8453 } - ], - chainId: 8453 + ] + } + }) + + const preparedInstructions = await meeClient.account.buildInstructions({ + currentInstructions, + action: { + type: "BRIDGE", + parameters: { + amount: BigInt(1000), + mcToken: mcUSDC, + chain: base + } } - ] + }) expect(preparedInstructions).toBeDefined() @@ -161,19 +169,39 @@ describe("mee.createMeeClient", async () => { async () => { console.time("execute:hashTimer") console.time("execute:receiptTimer") - const { hash } = await meeClient.execute({ - instructions: [ - { - calls: [ + + const instructions = [ + mcNexus.buildInstructions({ + action: { + type: "BRIDGE", + parameters: { + amount: BigInt(1000), + mcToken: mcUSDC, + chain: base + } + } + }), + mcNexus.buildInstructions({ + action: { + type: "DEFAULT", + parameters: [ { - to: "0x0000000000000000000000000000000000000000", - gasLimit: 50000n, - value: 0n + calls: [ + { + to: "0x0000000000000000000000000000000000000000", + gasLimit: 50000n, + value: 0n + } + ], + chainId: base.id } - ], - chainId: 8453 + ] } - ], + }) + ] + + const { hash } = await meeClient.execute({ + instructions, feeToken: { address: paymentToken, chainId: paymentChain.id diff --git a/src/sdk/clients/decorators/mee/getQuote.test.ts b/src/sdk/clients/decorators/mee/getQuote.test.ts index 5f14531cc..976bb275b 100644 --- a/src/sdk/clients/decorators/mee/getQuote.test.ts +++ b/src/sdk/clients/decorators/mee/getQuote.test.ts @@ -5,6 +5,7 @@ import { toNetwork } from "../../../../test/testSetup" import type { NetworkConfig } from "../../../../test/testUtils" import type { MultichainSmartAccount } from "../../../account/toMultiChainNexusAccount" import { toMultichainNexusAccount } from "../../../account/toMultiChainNexusAccount" +import { mcUSDC } from "../../../constants/tokens" import { type MeeClient, createMeeClient } from "../../createMeeClient" import { type Instruction, getQuote } from "./getQuote" @@ -68,4 +69,44 @@ describe("mee.getQuote", () => { expect(quote).toBeDefined() }) + + test("should resolve unresolved instructions", async () => { + const quote = await getQuote(meeClient, { + instructions: [ + mcNexus.buildInstructions({ + action: { + type: "BRIDGE", + parameters: { + amount: BigInt(1000), + mcToken: mcUSDC, + chain: base + } + } + }), + mcNexus.buildInstructions({ + action: { + type: "DEFAULT", + parameters: [ + { + calls: [ + { + to: "0x0000000000000000000000000000000000000000", + gasLimit: 50000n, + value: 0n + } + ], + chainId: base.id + } + ] + } + }) + ], + feeToken: { + address: paymentToken, + chainId: paymentChain.id + } + }) + + expect(quote.userOps.length).toEqual(3) + }) }) diff --git a/src/sdk/clients/decorators/mee/getQuote.ts b/src/sdk/clients/decorators/mee/getQuote.ts index 43f775415..8c1486699 100644 --- a/src/sdk/clients/decorators/mee/getQuote.ts +++ b/src/sdk/clients/decorators/mee/getQuote.ts @@ -39,20 +39,25 @@ export type Instruction = { /** * Represents a supertransaction, which is a collection of instructions to be executed in a single transaction - * @type SuperTransaction + * @type Supertransaction */ -export type SuperTransaction = { +export type Supertransaction = { /** Array of instructions to be executed in the transaction */ instructions: Instruction[] /** Token to be used for paying transaction fees */ feeToken: FeeTokenInfo } +export type SupertransactionLike = { + instructions: (Promise | Instruction[])[] | Instruction[] + feeToken: FeeTokenInfo +} + /** * Parameters required for requesting a quote from the MEE service * @type GetQuoteParams */ -export type GetQuoteParams = SuperTransaction & { +export type GetQuoteParams = SupertransactionLike & { /** Optional smart account to execute the transaction. If not provided, uses the client's default account */ account?: MultichainSmartAccount } @@ -199,16 +204,27 @@ export const getQuote = async ( ): Promise => { const { account: account_ = client.account, instructions, feeToken } = params - const validUserOps = instructions.every((userOp) => + const resolvedInstructions: Instruction[] = ( + await Promise.all( + instructions.flatMap((instructions_) => + typeof instructions_ === "function" + ? (instructions_ as () => Promise)() + : instructions_ + ) + ) + ).flat() + + const validUserOps = resolvedInstructions.every((userOp) => account_.deploymentOn(userOp.chainId) ) const validPaymentAccount = account_.deploymentOn(feeToken.chainId) if (!validPaymentAccount || !validUserOps) { + console.log(resolvedInstructions.map((x) => x.chainId)) throw Error("Account is not deployed on necessary chain(s)") } const userOpResults = await Promise.all( - instructions.map((userOp) => { + resolvedInstructions.map((userOp) => { const deployment = account_.deploymentOn(userOp.chainId) if (deployment) { return Promise.all([