Skip to content

Commit

Permalink
chore: refactor userOp builder to use generic implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
lukaisailovic committed Aug 15, 2024
1 parent 84024f6 commit 601a2fa
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 70 deletions.
10 changes: 10 additions & 0 deletions advanced/wallets/react-wallet-v2/src/consts/smartAccounts.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { KernelSmartAccountLib } from '@/lib/smart-accounts/KernelSmartAccountLib'
import { SafeSmartAccountLib } from '@/lib/smart-accounts/SafeSmartAccountLib'
import { getAddress } from 'viem'
import { goerli, polygonMumbai, sepolia } from 'viem/chains'

// Types
Expand All @@ -15,3 +16,12 @@ export const availableSmartAccounts = {
safe: SafeSmartAccountLib,
kernel: KernelSmartAccountLib
}

export const SAFE_FALLBACK_HANDLER_STORAGE_SLOT =
'0x6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d5'

export const SAFE_4337_MODULE_ADDRESSES = [
getAddress('0xa581c4A4DB7175302464fF3C06380BC3270b4037'),
getAddress('0x75cf11467937ce3F2f357CE24ffc3DBF8fD5c226'),
getAddress('0x3Fdb5BC686e861480ef99A6E3FaAe03c0b9F32e2')
]
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
createPublicClient,
GetStorageAtReturnType,
Hex,
hexToString,
http,
parseAbi,
PublicClient,
Expand All @@ -31,10 +30,9 @@ import {
import { bundlerUrl, paymasterUrl, publicClientUrl } from '@/utils/SmartAccountUtil'

import { getChainById } from '@/utils/ChainUtil'
import { SAFE_FALLBACK_HANDLER_STORAGE_SLOT } from '@/consts/smartAccounts'

const ERC_7579_LAUNCHPAD_ADDRESS: Address = '0xEBe001b3D534B9B6E2500FB78E67a1A137f561CE'
const FALLBACK_HANDLER_STORAGE_SLOT =
'0x6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d5'

export class SafeUserOpBuilder implements UserOpBuilder {
protected chain: Chain
Expand Down Expand Up @@ -147,15 +145,12 @@ export class SafeUserOpBuilder implements UserOpBuilder {
}
return false
}
private async getStorageAt(slot: Hex): Promise<GetStorageAtReturnType> {
const result = await this.publicClient.getStorageAt({

private async getFallbackHandlerAddress(): Promise<Address> {
const value = await this.publicClient.getStorageAt({
address: this.accountAddress,
slot: slot
slot: SAFE_FALLBACK_HANDLER_STORAGE_SLOT
})
return result
}
private async getFallbackHandlerAddress(): Promise<Address> {
const storage = await this.getStorageAt(FALLBACK_HANDLER_STORAGE_SLOT)
return trim(storage as Hex)
return trim(value as Hex)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { publicClientUrl } from '@/utils/SmartAccountUtil'
import { UserOperation } from 'permissionless'
import { Address, Chain, createPublicClient, Hex, http, parseAbi, PublicClient } from 'viem'
import { Address, Hex } from 'viem'

type Call = { to: Address; value: bigint; data: Hex }

Expand Down Expand Up @@ -41,43 +40,3 @@ export interface UserOpBuilder {
params: SendUserOpWithSigantureParams
): Promise<SendUserOpWithSigantureResponse>
}

export enum ImplementationType {
Safe = 'safe'
}

export type AccountImplementation = {
type: ImplementationType
}
type GetAccountImplementationParams = {
account: Address
chain?: Chain
publicClient?: PublicClient
}
export async function getAccountImplementation(
params: GetAccountImplementationParams
): Promise<AccountImplementation> {
let publicClient = params.publicClient
if (!publicClient) {
if (!params.chain) {
throw new Error('publicClient or chain must be provided')
}
publicClient = createPublicClient({
transport: http(publicClientUrl({ chain: params.chain }))
})
}
const accountImplementation = await publicClient.readContract({
address: params.account,
abi: parseAbi([
'function accountId() external view returns (string memory accountImplementationId)'
]),
functionName: 'accountId',
args: []
})
if (accountImplementation.includes('safe')) {
return {
type: ImplementationType.Safe
}
}
throw new Error('Unsupported implementation type')
}
20 changes: 3 additions & 17 deletions advanced/wallets/react-wallet-v2/src/pages/api/build.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { SafeUserOpBuilder } from '@/lib/smart-accounts/builders/SafeUserOpBuilder'
import {
ErrorResponse,
FillUserOpResponse,
getAccountImplementation,
ImplementationType,
UserOpBuilder
} from '@/lib/smart-accounts/builders/UserOpBuilder'
import { ErrorResponse, FillUserOpResponse } from '@/lib/smart-accounts/builders/UserOpBuilder'
import { getChainById } from '@/utils/ChainUtil'
import { getUserOpBuilder } from '@/utils/UserOpBuilderUtil'
import { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(
Expand All @@ -17,19 +11,11 @@ export default async function handler(
const account = req.body.account
const chain = getChainById(chainId)
try {
const accountImplementation = await getAccountImplementation({
const builder = await getUserOpBuilder({
account,
chain
})

let builder: UserOpBuilder

switch (accountImplementation.type) {
case ImplementationType.Safe:
builder = new SafeUserOpBuilder(account, chainId)
break
}

const response = await builder.fillUserOp(req.body)

res.status(200).json(response)
Expand Down
48 changes: 48 additions & 0 deletions advanced/wallets/react-wallet-v2/src/utils/UserOpBuilderUtil.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { SafeUserOpBuilder } from '@/lib/smart-accounts/builders/SafeUserOpBuilder'
import { UserOpBuilder } from '@/lib/smart-accounts/builders/UserOpBuilder'
import {
Address,
Chain,
createPublicClient,
getAddress,
http,
PublicClient,
size,
slice
} from 'viem'
import { publicClientUrl } from './SmartAccountUtil'
import {
SAFE_4337_MODULE_ADDRESSES,
SAFE_FALLBACK_HANDLER_STORAGE_SLOT
} from '@/consts/smartAccounts'

type GetUserOpBuilderParams = {
account: Address
chain: Chain
publicClient?: PublicClient
}

export async function getUserOpBuilder(params: GetUserOpBuilderParams): Promise<UserOpBuilder> {
let publicClient = params.publicClient
if (!publicClient) {
publicClient = createPublicClient({
transport: http(publicClientUrl({ chain: params.chain }))
})
}
if (await isSafeAccount(publicClient, params.account)) {
return new SafeUserOpBuilder(params.account, params.chain.id)
}
throw new Error('Unsupported implementation type')
}

async function isSafeAccount(publicClient: PublicClient, address: Address): Promise<Boolean> {
const storageValue = await publicClient.getStorageAt({
address,
slot: SAFE_FALLBACK_HANDLER_STORAGE_SLOT
})
if (!storageValue) {
return false
}
const safe4337ModuleAddress = getAddress(slice(storageValue, size(storageValue) - 20))
return SAFE_4337_MODULE_ADDRESSES.includes(safe4337ModuleAddress)
}

0 comments on commit 601a2fa

Please sign in to comment.