Skip to content

Commit

Permalink
chore: abstractions
Browse files Browse the repository at this point in the history
  • Loading branch information
joepegler committed Dec 2, 2024
1 parent 0449f36 commit abc7e95
Show file tree
Hide file tree
Showing 34 changed files with 334 additions and 267 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ bun i
```

```typescript
import { createNexusClient } from "@biconomy/sdk";
import { createSmartAccountClient } from "@biconomy/sdk";
import { http } from "viem";

const nexusClient = await createNexusClient({
const nexusClient = await createSmartAccountClient({
signer: account,
chain,
transport: http(),
Expand Down
8 changes: 4 additions & 4 deletions src/sdk/account/toNexusAccount.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ import {
import type { MasterClient, NetworkConfig } from "../../test/testUtils"
import {
type NexusClient,
createNexusClient
} from "../clients/createNexusClient"
createSmartAccountClient
} from "../clients/createSmartAccountClient"
import { k1ValidatorAddress } from "../constants"
import type { NexusAccount } from "./toNexusAccount"
import {
Expand Down Expand Up @@ -84,7 +84,7 @@ describe("nexus.account", async () => {
transport: http()
})

nexusClient = await createNexusClient({
nexusClient = await createSmartAccountClient({
signer: eoaAccount,
chain,
transport: http(),
Expand All @@ -100,7 +100,7 @@ describe("nexus.account", async () => {
})

test("should override account address", async () => {
const newNexusClient = await createNexusClient({
const newNexusClient = await createSmartAccountClient({
chain,
transport: http(),
bundlerTransport: http(bundlerUrl),
Expand Down
4 changes: 2 additions & 2 deletions src/sdk/account/utils/getChain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ type StringOrStrings = string | string[]
*
* @example
*
* import { getCustomChain, createNexusClient } from "@biconomy/sdk"
* import { getCustomChain, createSmartAccountClient } from "@biconomy/sdk"
*
* const customChain = getCustomChain(
* "My Custom Chain",
Expand All @@ -80,7 +80,7 @@ type StringOrStrings = string | string[]
* transport: http()
* })
*
* const smartAccountCustomChain = await createNexusClient({
* const smartAccountCustomChain = await createSmartAccountClient({
* signer: walletClientWithCustomChain,
* bundlerUrl,
* customChain
Expand Down
7 changes: 5 additions & 2 deletions src/sdk/clients/createBicoPaymasterClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ import {
type BicoPaymasterClient,
createBicoPaymasterClient
} from "./createBicoPaymasterClient"
import { type NexusClient, createNexusClient } from "./createNexusClient"
import {
type NexusClient,
createSmartAccountClient
} from "./createSmartAccountClient"

describe.runIf(paymasterTruthy())("bico.paymaster", async () => {
let network: NetworkConfig
Expand Down Expand Up @@ -84,7 +87,7 @@ describe.runIf(paymasterTruthy())("bico.paymaster", async () => {
})
nexusAccountAddress = await nexusAccount.getCounterFactualAddress()

nexusClient = await createNexusClient({
nexusClient = await createSmartAccountClient({
signer: account,
chain,
transport: http(),
Expand Down
2 changes: 1 addition & 1 deletion src/sdk/clients/createBundlerClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { type NexusAccount, toNexusAccount } from "../account/toNexusAccount"
import { safeMultiplier } from "../account/utils"
import { MAINNET_ADDRESS_K1_VALIDATOR_ADDRESS } from "../constants"
import { MAINNET_ADDRESS_K1_VALIDATOR_FACTORY_ADDRESS } from "../constants"
import type { NexusClient } from "./createNexusClient"
import type { NexusClient } from "./createSmartAccountClient"
import { erc7579Actions } from "./decorators/erc7579"
import { smartAccountActions } from "./decorators/smartAccount"

Expand Down
63 changes: 39 additions & 24 deletions src/sdk/clients/createMeeClient.test.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,29 @@
import {
http,
type Account,
type Address,
type Chain,
type LocalAccount,
type PrivateKeyAccount,
type PublicClient,
createPublicClient
} from "viem"
import { beforeAll, describe, expect, test } from "vitest"
import { toNetwork } from "../../test/testSetup"
import { toNetworks } from "../../test/testSetup"
import type { NetworkConfig } from "../../test/testUtils"
import { type NexusAccount, addressEquals } from "../account"
import { toNexusAccount } from "../account/toNexusAccount"
import { MAINNET_ADDRESS_K1_VALIDATOR_ADDRESS } from "../constants"
import { MAINNET_ADDRESS_K1_VALIDATOR_FACTORY_ADDRESS } from "../constants"
import { createMeeClient } from "./createMeeClient"
import type { MeeClient } from "./createMeeClient"

describe("mee.client", async () => {
let networkOne: NetworkConfig
let networkTwo: NetworkConfig

let chainOne: Chain
let chainTwo: Chain

let eoaAccount: Account
let eoaAccount: LocalAccount
let recipientAddress: Address
let nexusAccountOne: NexusAccount
let nexusAccountTwo: NexusAccount
let nexusAccountAddressOne: Address
let nexusAccountAddressTwo: Address
let publicClientOne: PublicClient
let publicClientTwo: PublicClient

Expand Down Expand Up @@ -58,36 +52,57 @@ describe("mee.client", async () => {
transport: http()
})

nexusAccountOne = await toNexusAccount({
meeClient = await createMeeClient({
accountParams: {
signer: eoaAccount,
k1ValidatorAddress: MAINNET_ADDRESS_K1_VALIDATOR_ADDRESS,
factoryAddress: MAINNET_ADDRESS_K1_VALIDATOR_FACTORY_ADDRESS,
chainList: [
{
transport: http(),
chain: chainOne
},
{
transport: http(),
chain: chainTwo
}
]
}
})
})

test("should get chainIds from publicClients", async () => {
const chainIds = await Promise.all(
[publicClientOne, publicClientTwo].map((client) => client.getChainId())
)
expect(chainIds).to.equal([chainOne.id, chainTwo.id])
})

test("should have relevant meeClient properties", async () => {
expect(meeClient).toHaveProperty("accounts")
expect(typeof meeClient.prepareSuperTransaction).toBe("function")
})

test("should alternatively create a mee client from two distinct nexus accounts", async () => {
const nexusAccountOne = await toNexusAccount({
transport: http(),
chain: chainOne,
signer: eoaAccount,
k1ValidatorAddress: MAINNET_ADDRESS_K1_VALIDATOR_ADDRESS,
factoryAddress: MAINNET_ADDRESS_K1_VALIDATOR_FACTORY_ADDRESS
})
nexusAccountTwo = await toNexusAccount({
const nexusAccountTwo = await toNexusAccount({
transport: http(),
chain: chainTwo,
signer: eoaAccount,
k1ValidatorAddress: MAINNET_ADDRESS_K1_VALIDATOR_ADDRESS,
factoryAddress: MAINNET_ADDRESS_K1_VALIDATOR_FACTORY_ADDRESS
})

meeClient = createMeeClient({
const meeClient = await createMeeClient({
accounts: [nexusAccountOne, nexusAccountTwo]
})
})

test("should get chainIds from publicClients", async () => {
const chainIds = await Promise.all(
[publicClientOne, publicClientTwo].map((client) => client.getChainId())
)
expect(chainIds).to.equal([chainOne.id, chainTwo.id])
})

test("should have matching addresses on different chains", async () => {
nexusAccountAddressOne = await nexusAccountOne.getAddress()
nexusAccountAddressTwo = await nexusAccountTwo.getAddress()
expect(addressEquals(nexusAccountAddressOne, nexusAccountAddressTwo))
expect(meeClient).toHaveProperty("accounts")
})
})
122 changes: 106 additions & 16 deletions src/sdk/clients/createMeeClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,35 @@ import {
type Client,
type ClientConfig,
type CreateClientErrorType,
type OneOf,
type Prettify,
type RpcSchema,
type Transport,
createClient
} from "viem"
import {
type ToNexusSmartAccountParameters,
toNexusAccount
} from "../account/toNexusAccount"
import { Logger } from "../account/utils/Logger"
import type { AnyData, ModularSmartAccount } from "../modules"
import { HttpMethod, httpRequest } from "./decorators/mee/Helpers"
import { type MeeActions, meeActions } from "./decorators/mee/decorators"
import type { MeeRpcSchema } from "./decorators/mee/decorators/meeAction"
import { type MeeActions, meeActions } from "./decorators/mee"
import type { MeeRpcSchema } from "./decorators/mee/prepareSuperTransaction"

export const DEFAULT_MEE_NODE = "https://biconomy.io/mee"

export type ErrorType<name extends string = "Error"> = Error & { name: name }
export enum HttpMethod {
Get = "get",
Post = "post",
Delete = "delete"
}
export interface HttpRequest {
url: string
method: HttpMethod
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
body?: Record<string, any>
}

export type MeeClientConfig<
transport extends Transport | undefined = Transport | undefined,
Expand All @@ -28,12 +44,17 @@ export type MeeClientConfig<
"account" | "cacheTime" | "key" | "name" | "pollingInterval" | "rpcSchema"
>
> & {
/** Client that points to an Execution RPC URL. */
client?: client | Client
transport?: transport
/** Accounts to be used for the mee client */
accounts: account[]
}
} & OneOf<
| {
/** Accounts to be used for the mee client */
accounts: account[]
}
| {
accountParams: ChainAbstractedAccountParams
}
>

export type MeeClient<
transport extends Transport = Transport,
Expand Down Expand Up @@ -64,17 +85,26 @@ export function createMeeClient<
rpcSchema extends RpcSchema | undefined = undefined
>(
parameters: MeeClientConfig<transport, account>
): MeeClient<Transport, account>
): Promise<MeeClient<Transport, account>>

export function createMeeClient(parameters: MeeClientConfig): MeeClient {
export async function createMeeClient(
parameters: MeeClientConfig
): Promise<MeeClient> {
const {
accountParams: accountParams_,
accounts,
client: client_,
key = "mee",
name = "Mee Client",
transport = http(DEFAULT_MEE_NODE)
} = parameters

let accounts_ = accounts
if (!accounts_) {
if (!accountParams_) throw new Error("No account params")
accounts_ = await toAccounts(accountParams_)
}

const client = Object.assign(
createClient({
...parameters,
Expand All @@ -84,20 +114,80 @@ export function createMeeClient(parameters: MeeClientConfig): MeeClient {
type: "meeClient"
}),
{
accounts,
accounts: accounts_,
client: client_,
// Temporay while we wait for EIP1193 to be supported by meeNode
// Can remove this when MEE node is EIP1193 compliant
request: (body: Record<string, AnyData>) => {
console.log("request body", body)
return httpRequest({
url: DEFAULT_MEE_NODE,
method: HttpMethod.Post,
body
request: async <T>(
body: Record<string, AnyData>,
url = DEFAULT_MEE_NODE,
method = HttpMethod.Post
) => {
const stringifiedBody = JSON.stringify(body)
Logger.log("HTTP Request", { url, body: stringifiedBody })

const response = await fetch(url, {
method,
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: stringifiedBody
})

// biome-ignore lint/suspicious/noExplicitAny: <explanation>
let jsonResponse: any
try {
jsonResponse = await response.json()
Logger.log("HTTP Response", jsonResponse)
} catch (error) {
if (!response.ok) {
throw new Error([response.statusText, response.status].join(", "))
}
}

if (response.ok) {
return jsonResponse as T
}

const errorFields = [
"error",
"message",
"msg",
"data",
"detail",
"nonFieldErrors"
]
const firstError = errorFields.find((field) =>
Boolean(jsonResponse[field])
)
if (firstError) {
throw new Error([response.status, firstError].join(", "))
}
throw new Error([response.status, jsonResponse.statusText].join(", "))
}
}
)

return client.extend(meeActions) as MeeClient
}

export type ChainAbstractedAccountParams = Omit<
ToNexusSmartAccountParameters,
"transport" | "chain"
> & {
chainList: {
transport: ToNexusSmartAccountParameters["transport"]
chain: ToNexusSmartAccountParameters["chain"]
}[]
}
export const toAccounts = async (
accountParams: ChainAbstractedAccountParams
): Promise<ModularSmartAccount[]> => {
const { chainList, ...chainAgnosticParams } = accountParams
return await Promise.all(
chainList.map((params) =>
toNexusAccount({ ...chainAgnosticParams, ...params })
)
)
}
Loading

0 comments on commit abc7e95

Please sign in to comment.