Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { describe, expect, test } from "vitest"
import { createPublicClient, http } from "viem"
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"
import { sepolia } from "viem/chains"
import { toKernelSmartAccount } from "./toKernelSmartAccount"

describe("Kernel Smart Account - Strict Validation", () => {
test("creates account with strictValidation disabled by default", async () => {
const owner = privateKeyToAccount(generatePrivateKey())
const publicClient = createPublicClient({
chain: sepolia,
transport: http()
})

const account = await toKernelSmartAccount({
client: publicClient,
owners: [owner],
index: 0n
})

expect(account.strictValidation).toBe(false)
})

test("creates account with strictValidation enabled", async () => {
const owner = privateKeyToAccount(generatePrivateKey())
const publicClient = createPublicClient({
chain: sepolia,
transport: http()
})

const account = await toKernelSmartAccount({
client: publicClient,
owners: [owner],
index: 0n,
strictValidation: true
})

expect(account.strictValidation).toBe(true)
})

test("strictValidation property is accessible on account", async () => {
const owner = privateKeyToAccount(generatePrivateKey())
const publicClient = createPublicClient({
chain: sepolia,
transport: http()
})

const accountWithoutStrict = await toKernelSmartAccount({
client: publicClient,
owners: [owner],
index: 0n
})

const accountWithStrict = await toKernelSmartAccount({
client: publicClient,
owners: [owner],
index: 1n,
strictValidation: true
})

expect("strictValidation" in accountWithoutStrict).toBe(true)
expect("strictValidation" in accountWithStrict).toBe(true)
expect(accountWithoutStrict.strictValidation).toBe(false)
expect(accountWithStrict.strictValidation).toBe(true)
})
})
16 changes: 12 additions & 4 deletions packages/permissionless/accounts/kernel/toKernelSmartAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,7 @@ export type ToKernelSmartAccountParameters<
>
version?: kernelVersion
eip7702?: eip7702
strictValidation?: boolean
} & (eip7702 extends true
? {
owner: OneOf<
Expand Down Expand Up @@ -480,8 +481,11 @@ export type KernelSmartAccountImplementation<
eip7702 extends true
? {
implementation: Address
strictValidation?: boolean
}
: object,
: {
strictValidation?: boolean
},
eip7702
// {
// // entryPoint === ENTRYPOINT_ADDRESS_V06 ? "0.2.2" : "0.3.0-beta"
Expand Down Expand Up @@ -536,7 +540,8 @@ export async function toKernelSmartAccount<
metaFactoryAddress: _metaFactoryAddress,
accountLogicAddress: _accountLogicAddress,
useMetaFactory = true,
eip7702 = false
eip7702 = false,
strictValidation = false
} = parameters

const owners = (() => {
Expand Down Expand Up @@ -729,9 +734,12 @@ export async function toKernelSmartAccount<
getFactoryArgs,
extend: eip7702
? {
implementation: accountLogicAddress
implementation: accountLogicAddress,
strictValidation
}
: undefined,
: {
strictValidation
},
authorization: eip7702
? {
address: accountLogicAddress,
Expand Down
83 changes: 65 additions & 18 deletions packages/permissionless/actions/smartAccount/sendTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import type {
import {
type SendUserOperationParameters,
type SmartAccount,
estimateUserOperationGas,
prepareUserOperation,
sendUserOperation,
waitForUserOperationReceipt
} from "viem/account-abstraction"
Expand Down Expand Up @@ -95,24 +97,69 @@ export async function sendTransaction<

if (!to) throw new Error("Missing to address")

userOpHash = await getAction(
client,
sendUserOperation,
"sendUserOperation"
)({
...args,
calls: [
{
to,
value: value || BigInt(0),
data: data || "0x"
}
],
account,
maxFeePerGas,
maxPriorityFeePerGas,
nonce: nonce ? BigInt(nonce) : undefined
})
const calls = [
{
to,
value: value || BigInt(0),
data: data || "0x"
}
]

// Check if account has strictValidation enabled
if ("strictValidation" in account && account.strictValidation) {
// For strict validation: prepare UserOp, sign it, then estimate gas with real signature
let userOperation = await getAction(
client,
prepareUserOperation,
"prepareUserOperation"
)({
account,
calls,
maxFeePerGas,
maxPriorityFeePerGas,
nonce: nonce ? BigInt(nonce) : undefined
})

// Sign the UserOperation before gas estimation
const signature = await account.signUserOperation(userOperation)
userOperation = { ...userOperation, signature }

// Estimate gas with the real signature
const gasEstimates = await getAction(
client,
estimateUserOperationGas,
"estimateUserOperationGas"
)({
userOperation
})

// Send the signed UserOperation with accurate gas estimates
userOpHash = await getAction(
client,
sendUserOperation,
"sendUserOperation"
)({
...args,
calls,
account,
...gasEstimates,
signature
})
} else {
// Default flow: use viem's standard sendUserOperation
userOpHash = await getAction(
client,
sendUserOperation,
"sendUserOperation"
)({
...args,
calls,
account,
maxFeePerGas,
maxPriorityFeePerGas,
nonce: nonce ? BigInt(nonce) : undefined
})
}
} else {
userOpHash = await getAction(
client,
Expand Down