Skip to content

Commit

Permalink
feat: smart account client (#18)
Browse files Browse the repository at this point in the history
* feat: added simple smart account client

---------

Co-authored-by: GabiDev <[email protected]>
  • Loading branch information
VGabriel45 and GabiDev45 authored Mar 13, 2024
1 parent c351b61 commit 01d4820
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 2 deletions.
Binary file modified bun.lockb
Binary file not shown.
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"build:cjs": "tsc --project ./tsconfig/tsconfig.cjs.json && tsc-alias -p ./tsconfig/tsconfig.cjs.json && echo > ./dist/_cjs/package.json '{\"type\":\"commonjs\"}'",
"build:esm": "tsc --project ./tsconfig/tsconfig.esm.json && tsc-alias -p ./tsconfig/tsconfig.esm.json && echo > ./dist/_esm/package.json '{\"type\": \"module\",\"sideEffects\":false}'",
"build:types": "tsc --project ./tsconfig/tsconfig.types.json && tsc-alias -p ./tsconfig/tsconfig.types.json",
"clean": "rimraf ./src/_esm ./src/_cjs ./src/_types ./src/tsconfig",
"clean": "rimraf ./dist/_esm ./dist/_cjs ./dist/_types ./src/tsconfig",
"test": "vitest dev -c ./tests/vitest.config.ts",
"test:ci": "CI=true vitest -c ./tests/vitest.config.ts --coverage",
"size": "size-limit",
Expand Down Expand Up @@ -91,5 +91,8 @@
"simple-git-hooks": {
"pre-commit": "bun run format && bun run lint:fix",
"commit-msg": "npx --no -- commitlint --edit ${1}"
},
"dependencies": {
"permissionless": "^0.1.10"
}
}
2 changes: 1 addition & 1 deletion src/account/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export type BiconomySmartAccountConfig = {
* @template TAbi - The type of the ABI.
*/
export type SmartAccount<
entryPoint extends ENTRYPOINT_ADDRESS_V07_TYPE,
entryPoint = ENTRYPOINT_ADDRESS_V07_TYPE,
Name extends string = string,
transport extends Transport = Transport,
chain extends Chain | undefined = Chain | undefined,
Expand Down
129 changes: 129 additions & 0 deletions src/bundler/utils/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import type { Address, Hash, Hex } from "viem"
import type { PartialBy } from "viem/chains"
import type { ENTRYPOINT_ADDRESS_V07_TYPE } from "../../account/utils/types"

export type UserOperationWithBigIntAsHex = {
sender: Address
nonce: Hex
factory: Address
factoryData: Hex
callData: Hex
callGasLimit: Hex
verificationGasLimit: Hex
preVerificationGas: Hex
maxFeePerGas: Hex
maxPriorityFeePerGas: Hex
paymaster: Address
paymasterVerificationGasLimit: Hex
paymasterPostOpGasLimit: Hex
paymasterData: Hex
signature: Hex
initCode?: never
paymasterAndData?: never
}

export type BundlerRpcSchema<entryPoint extends ENTRYPOINT_ADDRESS_V07_TYPE> = [
{
Method: "eth_sendUserOperation"
Parameters: [
userOperation: UserOperationWithBigIntAsHex,
entryPoint: entryPoint
]
ReturnType: Hash
},
{
Method: "eth_estimateUserOperationGas"
Parameters: [
userOperation: PartialBy<
UserOperationWithBigIntAsHex,
| "callGasLimit"
| "preVerificationGas"
| "verificationGasLimit"
| "paymasterVerificationGasLimit"
| "paymasterPostOpGasLimit"
>,
entryPoint: entryPoint,
stateOverrides?: StateOverrides
]
ReturnType: {
preVerificationGas: Hex
verificationGasLimit: Hex
callGasLimit?: Hex | null
paymasterVerificationGasLimit?: Hex | null
paymasterPostOpGasLimit?: Hex | null
}
},
{
Method: "eth_supportedEntryPoints"
Parameters: []
ReturnType: Address[]
},
{
Method: "eth_chainId"
Parameters: []
ReturnType: Hex
},
{
Method: "eth_getUserOperationByHash"
Parameters: [hash: Hash]
ReturnType: {
userOperation: UserOperationWithBigIntAsHex
entryPoint: entryPoint
transactionHash: Hash
blockHash: Hash
blockNumber: Hex
}
},
{
Method: "eth_getUserOperationReceipt"
Parameters: [hash: Hash]
ReturnType: UserOperationReceiptWithBigIntAsHex
}
]

type UserOperationReceiptWithBigIntAsHex = {
userOpHash: Hash
sender: Address
nonce: Hex
actualGasUsed: Hex
actualGasCost: Hex
success: boolean
receipt: {
transactionHash: Hex
transactionIndex: Hex
blockHash: Hash
blockNumber: Hex
from: Address
to: Address | null
cumulativeGasUsed: Hex
status: "0x0" | "0x1"
gasUsed: Hex
contractAddress: Address | null
logsBloom: Hex
effectiveGasPrice: Hex
}
logs: {
data: Hex
blockNumber: Hex
blockHash: Hash
transactionHash: Hash
logIndex: Hex
transactionIndex: Hex
address: Address
topics: Hex[]
}[]
}

export type StateOverrides = {
[x: string]: {
balance?: bigint | undefined
nonce?: bigint | number | undefined
code?: Hex | undefined
state?: {
[x: Hex]: Hex
}
stateDiff?: {
[x: Hex]: Hex
}
}
}
93 changes: 93 additions & 0 deletions src/client/createSmartAccountClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import {
type SmartAccountActions,
type SmartAccountClientConfig,
smartAccountActions
} from "permissionless"

import { type Chain, type Client, type Transport, createClient } from "viem"
import type { Prettify } from "viem/chains"

import type {
ENTRYPOINT_ADDRESS_V07_TYPE,
SmartAccount
} from "../account/utils/types.js"
import type { BundlerRpcSchema } from "../bundler/utils/types.js"

export type SmartAccountClient<
entryPoint extends ENTRYPOINT_ADDRESS_V07_TYPE,
transport extends Transport = Transport,
chain extends Chain | undefined = Chain | undefined,
account extends SmartAccount<entryPoint> | undefined =
| SmartAccount<entryPoint>
| undefined
> = Prettify<
Client<
transport,
chain,
account,
BundlerRpcSchema<entryPoint>,
SmartAccountActions<entryPoint, chain, account>
>
>

/**
* Creates a EIP-4337 compliant Bundler Client with a given [Transport](https://viem.sh/docs/clients/intro.html) configured for a [Chain](https://viem.sh/docs/clients/chains.html).
*
* - Docs: https://docs.pimlico.io/permissionless/reference/clients/smartAccountClient
*
* A Bundler Client is an interface to "erc 4337" [JSON-RPC API](https://eips.ethereum.org/EIPS/eip-4337#rpc-methods-eth-namespace) methods such as sending user operation, estimating gas for a user operation, get user operation receipt, etc through Bundler Actions.
*
* @param parameters - {@link WalletClientConfig}
* @returns A Bundler Client. {@link SmartAccountClient}
*
* @example
* import { createPublicClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
*
* const smartAccountClient = createSmartAccountClient({
* chain: mainnet,
* transport: http(BUNDLER_URL),
* })
*/

export function createSmartAccountClient<
TSmartAccount extends SmartAccount<ENTRYPOINT_ADDRESS_V07_TYPE>,
TTransport extends Transport = Transport,
TChain extends Chain = Chain
>(
parameters: SmartAccountClientConfig<
ENTRYPOINT_ADDRESS_V07_TYPE,
TTransport,
TChain,
TSmartAccount
>
): SmartAccountClient<
ENTRYPOINT_ADDRESS_V07_TYPE,
TTransport,
TChain,
TSmartAccount
> {
const {
key = "Account",
name = "Smart Account Client",
bundlerTransport
} = parameters
const client = createClient({
...parameters,
key,
name,
transport: bundlerTransport,
type: "smartAccountClient"
})

return client.extend(
smartAccountActions({
middleware: parameters.middleware
})
) as SmartAccountClient<
ENTRYPOINT_ADDRESS_V07_TYPE,
TTransport,
TChain,
TSmartAccount
>
}
4 changes: 4 additions & 0 deletions src/client/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export {
type SmartAccountClient,
createSmartAccountClient
} from "./createSmartAccountClient.js"
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ export {
walletClientToSmartAccountSigner
} from "./account/index.js"

export { createSmartAccountClient } from "./client/index.js"

// TODO import from the rest of the files in the core package
13 changes: 13 additions & 0 deletions tests/account.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { baseSepolia } from "viem/chains"
import { beforeAll, describe, expect, test } from "vitest"

import {
Expand All @@ -19,6 +20,7 @@ import { DEFAULT_ECDSA_OWNERSHIP_MODULE } from "../src/account/utils/constants.j
import { SignTransactionNotSupportedBySmartAccount } from "../src/account/utils/errors.js"
import { getChain } from "../src/account/utils/helpers.js"
import { extractChainIdFromBundlerUrl } from "../src/bundler/utils/helpers.js"
import { createSmartAccountClient } from "../src/client/createSmartAccountClient.js"

describe("Biconomy Smart Account core tests", () => {
let smartAccount: Awaited<ReturnType<typeof createSmartAccount>>
Expand Down Expand Up @@ -49,6 +51,17 @@ describe("Biconomy Smart Account core tests", () => {
})
})

test("Should create a smart account client", async () => {
const smartAccountClient = createSmartAccountClient({
account: smartAccount,
entryPoint: "0x0000000071727De22E5E9d8BAf0edAc6f37da032",
chain: baseSepolia,
bundlerTransport: http(bundlerUrl)
})

console.log(smartAccountClient, "CLIENT")
})

test("Should get account address + nonce", async () => {
const address = smartAccount.address
console.log("Smart Account Address: ", address)
Expand Down

0 comments on commit 01d4820

Please sign in to comment.