permissionless
Version:
A utility library for working with ERC-4337
256 lines (219 loc) • 9.24 kB
text/typescript
import { type Hex, zeroAddress } from "viem"
import { privateKeyToAccount } from "viem/accounts"
import { describe, expect } from "vitest"
import { testWithRpc } from "../../../permissionless-test/src/testWithRpc"
import {
getCoreSmartAccounts,
getPublicClient
} from "../../../permissionless-test/src/utils"
import { getCallsStatus } from "./getCallsStatus"
import { sendCalls } from "./sendCalls"
const privateKey =
"0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356"
describe.each(getCoreSmartAccounts())(
"getCallsStatus $name",
({
getSmartAccountClient,
supportsEntryPointV06,
supportsEntryPointV07,
supportsEntryPointV08,
isEip7702Compliant
}) => {
testWithRpc.skipIf(!supportsEntryPointV06)(
"getCallsStatus_v06",
async ({ rpc }) => {
const { anvilRpc } = rpc
const smartClient = await getSmartAccountClient({
entryPoint: {
version: "0.6"
},
...rpc
})
const { id: userOpHash } = await sendCalls(smartClient, {
calls: [
{
to: zeroAddress,
data: "0x",
value: 0n
}
]
})
expect(userOpHash).toBeTruthy()
await smartClient.waitForUserOperationReceipt({
hash: userOpHash as Hex
})
const status = await getCallsStatus(smartClient, {
id: userOpHash
})
expect(status).toBeTruthy()
expect(status.id).toBe(userOpHash)
expect(status.version).toBe("1.0")
expect(status.chainId).toBe(smartClient.chain.id)
expect(status.atomic).toBe(true)
expect(status.status).toBe("success")
expect(status.statusCode).toBe(200)
expect(status.receipts).toBeDefined()
expect(status.receipts?.length).toBeGreaterThan(0)
expect(status.receipts?.[0].status).toBe("success")
}
)
testWithRpc.skipIf(!supportsEntryPointV06)(
"getCallsStatus_v06 pending status",
async ({ rpc }) => {
const smartClient = await getSmartAccountClient({
entryPoint: {
version: "0.6"
},
...rpc
})
const { id: userOpHash } = await sendCalls(smartClient, {
calls: [
{
to: zeroAddress,
data: "0x",
value: 0n
}
]
})
expect(userOpHash).toBeTruthy()
// Check status immediately (should be pending)
const status = await getCallsStatus(smartClient, {
id: userOpHash
})
expect(status).toBeTruthy()
expect(status.id).toBe(userOpHash)
expect(status.version).toBe("1.0")
expect(status.chainId).toBe(smartClient.chain.id)
expect(status.atomic).toBe(true)
expect(["pending", "success"]).toContain(status.status)
expect([100, 200]).toContain(status.statusCode)
}
)
testWithRpc.skipIf(!supportsEntryPointV07)(
"getCallsStatus_v07",
async ({ rpc }) => {
const { anvilRpc } = rpc
const privateKeyAccount = privateKeyToAccount(privateKey)
const smartClient = await getSmartAccountClient({
entryPoint: {
version: "0.7"
},
privateKey,
...rpc
})
const publicClient = getPublicClient(anvilRpc)
const { id: userOpHash } = await sendCalls(smartClient, {
calls: [
{
to: zeroAddress,
data: "0x",
value: 0n
}
],
authorization: isEip7702Compliant
? await privateKeyAccount.signAuthorization({
address: (smartClient.account as any)
.implementation,
chainId: smartClient.chain.id,
nonce: await publicClient.getTransactionCount({
address: smartClient.account.address
})
})
: undefined
})
expect(userOpHash).toBeTruthy()
await smartClient.waitForUserOperationReceipt({
hash: userOpHash as Hex
})
const status = await getCallsStatus(smartClient, {
id: userOpHash
})
expect(status).toBeTruthy()
expect(status.id).toBe(userOpHash)
expect(status.version).toBe("1.0")
expect(status.chainId).toBe(smartClient.chain.id)
expect(status.atomic).toBe(true)
expect(status.status).toBe("success")
expect(status.statusCode).toBe(200)
expect(status.receipts).toBeDefined()
expect(status.receipts?.length).toBeGreaterThan(0)
expect(status.receipts?.[0].status).toBe("success")
}
)
testWithRpc.skipIf(!supportsEntryPointV08)(
"getCallsStatus_v08",
async ({ rpc }) => {
const { anvilRpc } = rpc
const privateKeyAccount = privateKeyToAccount(privateKey)
const smartClient = await getSmartAccountClient({
entryPoint: {
version: "0.8"
},
privateKey,
...rpc
})
const publicClient = getPublicClient(anvilRpc)
const authorization = isEip7702Compliant
? await privateKeyAccount.signAuthorization({
address: (smartClient.account as any).implementation,
chainId: smartClient.chain.id,
nonce: await publicClient.getTransactionCount({
address: smartClient.account.address
})
})
: undefined
const { id: userOpHash } = await sendCalls(smartClient, {
calls: [
{
to: zeroAddress,
data: "0x",
value: 0n
}
],
authorization
})
expect(userOpHash).toBeTruthy()
await smartClient.waitForUserOperationReceipt({
hash: userOpHash as Hex
})
const status = await getCallsStatus(smartClient, {
id: userOpHash
})
expect(status).toBeTruthy()
expect(status.id).toBe(userOpHash)
expect(status.version).toBe("1.0")
expect(status.chainId).toBe(smartClient.chain.id)
expect(status.atomic).toBe(true)
expect(status.status).toBe("success")
expect(status.statusCode).toBe(200)
expect(status.receipts).toBeDefined()
expect(status.receipts?.length).toBeGreaterThan(0)
expect(status.receipts?.[0].status).toBe("success")
}
)
testWithRpc.skipIf(!supportsEntryPointV06)(
"getCallsStatus_v06 with invalid hash",
async ({ rpc }) => {
const smartClient = await getSmartAccountClient({
entryPoint: {
version: "0.6"
},
...rpc
})
const invalidHash =
"0x1234567890123456789012345678901234567890123456789012345678901234"
const status = await getCallsStatus(smartClient, {
id: invalidHash
})
expect(status).toBeTruthy()
expect(status.id).toBe(invalidHash)
expect(status.version).toBe("1.0")
expect(status.chainId).toBe(smartClient.chain.id)
expect(status.atomic).toBe(true)
expect(status.status).toBe("pending")
expect(status.statusCode).toBe(100)
expect(status.receipts).toBeUndefined()
}
)
}
)