Skip to content

Commit

Permalink
Feat: Enable sendUserOp for userOpBuilder service (#680)
Browse files Browse the repository at this point in the history
* chores:run prettier

* add EncodeLib for SmartSession formatSignature

* formatSignature implementation

* add sendUserOp implementation

* remove signature field from sendUserOp body

* chores: refactor code

use viem methods for encoding/decoding
  • Loading branch information
KannuSingh authored Aug 27, 2024
1 parent 5825941 commit b9d266f
Show file tree
Hide file tree
Showing 8 changed files with 388 additions and 16 deletions.
1 change: 1 addition & 0 deletions advanced/wallets/react-wallet-v2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"react-dom": "17.0.2",
"react-hot-toast": "^2.4.1",
"react-qr-reader-es6": "2.2.1-2",
"solady": "^0.0.234",
"tronweb": "^4.4.0",
"valtio": "1.13.2",
"viem": "2.17.8",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ import {
createSmartAccountClient
} from 'permissionless'
import { PimlicoBundlerActions, pimlicoBundlerActions } from 'permissionless/actions/pimlico'
import { PIMLICO_NETWORK_NAMES, publicClientUrl, publicRPCUrl, UrlConfig } from '@/utils/SmartAccountUtil'
import {
PIMLICO_NETWORK_NAMES,
publicClientUrl,
publicRPCUrl,
UrlConfig
} from '@/utils/SmartAccountUtil'
import { Chain } from '@/consts/smartAccounts'
import { EntryPoint } from 'permissionless/types/entrypoint'
import { Erc7579Actions, erc7579Actions } from 'permissionless/actions/erc7579'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { SmartSessionMode } from '@biconomy/permission-context-builder'
import { LibZip } from 'solady'
import { Address, encodeAbiParameters, encodePacked, Hex } from 'viem'
import { enableSessionsStructAbi } from './SmartSessionUserOpBuilder'

type EnableSessions = {
isigner: Address
isignerInitData: Hex
userOpPolicies: readonly { policy: Address; initData: Hex }[]
erc1271Policies: readonly { policy: Address; initData: Hex }[]
actions: readonly {
actionId: Hex
actionPolicies: readonly { policy: Address; initData: Hex }[]
}[]
permissionEnableSig: Hex
}

export function packMode(data: Hex, mode: SmartSessionMode, signerId: Hex): Hex {
return encodePacked(['uint8', 'bytes32', 'bytes'], [mode, signerId, data])
}

export function encodeUse(signerId: Hex, sig: Hex) {
const data = encodeAbiParameters([{ type: 'bytes' }], [sig])
const compressedData = LibZip.flzCompress(data) as Hex
return packMode(compressedData, SmartSessionMode.USE, signerId)
}

export function encodeEnable(signerId: Hex, sig: Hex, enableData: EnableSessions) {
const data = encodeAbiParameters(
[enableSessionsStructAbi[0], { type: 'bytes' }],
[
{
...enableData
},
sig
]
)
const compressedData = LibZip.flzCompress(data) as Hex
return packMode(compressedData, SmartSessionMode.UNSAFE_ENABLE, signerId)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,28 @@ import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'
import {
FillUserOpParams,
FillUserOpResponse,
SendUserOpWithSigantureParams,
SendUserOpWithSigantureResponse,
SendUserOpWithSignatureParams,
SendUserOpWithSignatureResponse,
UserOpBuilder
} from './UserOpBuilder'
import {
Address,
Chain,
createPublicClient,
GetStorageAtReturnType,
Hex,
http,
pad,
parseAbi,
PublicClient,
trim
trim,
zeroAddress
} from 'viem'
import { signerToSafeSmartAccount } from 'permissionless/accounts'
import {
createSmartAccountClient,
ENTRYPOINT_ADDRESS_V07,
getAccountNonce,
getPackedUserOperation,
getUserOperationHash
} from 'permissionless'
import {
Expand All @@ -31,6 +34,7 @@ import { bundlerUrl, paymasterUrl, publicClientUrl } from '@/utils/SmartAccountU

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

const ERC_7579_LAUNCHPAD_ADDRESS: Address = '0xEBe001b3D534B9B6E2500FB78E67a1A137f561CE'

Expand Down Expand Up @@ -93,15 +97,33 @@ export class SafeUserOpBuilder implements UserOpBuilder {
chain: this.chain,
bundlerTransport,
middleware: {
sponsorUserOperation: paymasterClient.sponsorUserOperation, // optional
sponsorUserOperation:
params.capabilities.paymasterService && paymasterClient.sponsorUserOperation, // optional
gasPrice: async () => (await pimlicoBundlerClient.getUserOperationGasPrice()).fast // if using pimlico bundler
}
})
const account = smartAccountClient.account

const validatorAddress = (params.capabilities.permissions?.context.slice(0, 42) ||
zeroAddress) as Address
let nonce: bigint = await getAccountNonce(this.publicClient, {
sender: this.accountAddress,
entryPoint: ENTRYPOINT_ADDRESS_V07,
key: BigInt(
pad(validatorAddress, {
dir: 'right',
size: 24
}) || 0
)
})

const userOp = await smartAccountClient.prepareUserOperationRequest({
userOperation: {
callData: await account.encodeCallData(params.calls)
nonce: nonce,
callData: await account.encodeCallData(params.calls),
callGasLimit: BigInt('0x1E8480'),
verificationGasLimit: BigInt('0x1E8480'),
preVerificationGas: BigInt('0x1E8480')
},
account: account
})
Expand All @@ -115,10 +137,48 @@ export class SafeUserOpBuilder implements UserOpBuilder {
hash
}
}
sendUserOpWithSignature(
params: SendUserOpWithSigantureParams
): Promise<SendUserOpWithSigantureResponse> {
throw new Error('Method not implemented.')
async sendUserOpWithSignature(
params: SendUserOpWithSignatureParams
): Promise<SendUserOpWithSignatureResponse> {
const { userOp, permissionsContext } = params
if (permissionsContext) {
const formattedSignature = await formatSignature(this.publicClient, {
signature: userOp.signature,
permissionsContext,
accountAddress: userOp.sender
})
userOp.signature = formattedSignature
}
const bundlerTransport = http(bundlerUrl({ chain: this.chain }), {
timeout: 30000
})
const pimlicoBundlerClient = createPimlicoBundlerClient({
chain: this.chain,
transport: bundlerTransport,
entryPoint: ENTRYPOINT_ADDRESS_V07
})

const userOpHash = await pimlicoBundlerClient.sendUserOperation({
userOperation: {
...userOp,
callData: userOp.callData,
callGasLimit: BigInt(userOp.callGasLimit),
nonce: BigInt(userOp.nonce),
preVerificationGas: BigInt(userOp.preVerificationGas),
verificationGasLimit: BigInt(userOp.verificationGasLimit),
sender: userOp.sender,
signature: userOp.signature,
maxFeePerGas: BigInt(userOp.maxFeePerGas),
maxPriorityFeePerGas: BigInt(userOp.maxPriorityFeePerGas)
}
})
const receipt = await pimlicoBundlerClient.waitForUserOperationReceipt({
hash: userOpHash
})

return {
receipt: receipt.receipt.transactionHash
}
}

private async getVersion(): Promise<string> {
Expand Down
Loading

0 comments on commit b9d266f

Please sign in to comment.