diff --git a/apps/iframe/src/constants/requestLabels.ts b/apps/iframe/src/constants/requestLabels.ts index 03a7a5f2f8..de5c25d4f5 100644 --- a/apps/iframe/src/constants/requestLabels.ts +++ b/apps/iframe/src/constants/requestLabels.ts @@ -6,6 +6,7 @@ export const requestLabels = { personal_sign: "Signature Request", wallet_addEthereumChain: "Add Network", wallet_requestPermissions: "Permission Request", + wallet_sendCalls: "Send Calls", wallet_switchEthereumChain: "Switch Network", wallet_watchAsset: "Watch Asset", [HappyMethodNames.USE_ABI]: "Record ABI", diff --git a/apps/iframe/src/requests/userOps.ts b/apps/iframe/src/requests/userOps.ts index 31bc150e07..e5e7761e60 100644 --- a/apps/iframe/src/requests/userOps.ts +++ b/apps/iframe/src/requests/userOps.ts @@ -38,6 +38,7 @@ export type SendUserOpArgs = { tx: RpcTransactionRequest validator: Address signer: UserOpSigner + paymaster?: Address // developers may specify a particular deployed paymaster when they use `wallet_sendCalls` } export type UserOpWrappedCall = { @@ -57,11 +58,10 @@ export enum VALIDATOR_TYPE { PERMISSION = "0x02", } -export async function sendUserOp({ user, tx, validator, signer }: SendUserOpArgs, retry = 2) { +export async function sendUserOp({ user, tx, validator, signer, paymaster }: SendUserOpArgs, retry = 2) { const smartAccountClient = (await getSmartAccountClient())! const account = smartAccountClient.account.address - // [DEBUGLOG] // let debugNonce = 0n try { // We need the separate nonce lookup because: // - we do local nonce management to be able to have multiple userOps in flight @@ -70,7 +70,7 @@ export async function sendUserOp({ user, tx, validator, signer }: SendUserOpArgs getNextNonce(account, validator), smartAccountClient.prepareUserOperation({ account: smartAccountClient.account, - paymaster: contractAddresses.HappyPaymaster, + paymaster: paymaster ?? contractAddresses.HappyPaymaster, // Specify this array to avoid fetching the nonce from here too. // We don't really need the dummy signature, but it does not incur an extra network // call and it makes the type system happy. @@ -85,8 +85,6 @@ export async function sendUserOp({ user, tx, validator, signer }: SendUserOpArgs } satisfies PrepareUserOperationParameters), // TS too dumb without this ]) - // [DEBUGLOG] // debugNonce = nonce - // sendUserOperationNow does not want account included const { account: _, ...preparedUserOp } = { ..._preparedUserOp, nonce } preparedUserOp.signature = await signer(preparedUserOp, smartAccountClient) @@ -105,7 +103,6 @@ export async function sendUserOp({ user, tx, validator, signer }: SendUserOpArgs addPendingUserOp(user.address, pendingUserOpDetails) - // [DEBUGLOG] // console.log("sending", userOpHash, retry) const userOpReceipt = await submitUserOp(smartAccountClient, validator, preparedUserOp) receiptCache.put(userOpHash, [ @@ -119,8 +116,6 @@ export async function sendUserOp({ user, tx, validator, signer }: SendUserOpArgs } as GetUserOperationReturnType, ]) - // [DEBUGLOG] // console.log("receipt", userOpHash, retry) - markUserOpAsConfirmed(user.address, pendingUserOpDetails, userOpReceipt) return userOpReceipt.userOpHash @@ -128,9 +123,7 @@ export async function sendUserOp({ user, tx, validator, signer }: SendUserOpArgs // https://docs.stackup.sh/docs/entrypoint-errors // https://docs.pimlico.io/infra/bundler/entrypoint-errors - // [DEBUGLOG] // console.log("error", nonceB, error.details || error, retry) - - // Most likely the transaction didn't land, so need to resynchronize the nonce. + // (most likely) the transaction didn't land, so need to resynchronize the nonce. deleteNonce(account, validator) if (retry > 0) return sendUserOp({ user, tx, validator, signer }, retry - 1) diff --git a/apps/iframe/src/state/injectedClient.ts b/apps/iframe/src/state/injectedClient.ts index d1048fb79d..12d57f4ef2 100644 --- a/apps/iframe/src/state/injectedClient.ts +++ b/apps/iframe/src/state/injectedClient.ts @@ -1,6 +1,7 @@ import { accessorsFromAtom } from "@happy.tech/common" import { type Atom, atom } from "jotai" import { createWalletClient, custom } from "viem" +import { eip5792Actions } from "viem/experimental" import { InjectedProviderProxy } from "#src/connections/InjectedProviderProxy.ts" import { userAtom } from "./user" import type { AccountWalletClient } from "./walletClient" @@ -10,7 +11,7 @@ export const injectedClientAtom: Atom = atom, [...WalletRpcSchema, ...PublicRpcSchema] -> +> & + Eip5792Actions export const walletClientAtom: Atom = atom((get) => { const user = get(userAtom) @@ -19,7 +21,7 @@ export const walletClientAtom: Atom = atom | null + walletClient: (WalletClient & Eip5792Actions) | null } { const { provider, user } = useHappyChain() @@ -27,7 +28,7 @@ export default function useClients(): { ? createWalletClient({ account: user.address, transport: custom(provider), - }) + }).extend(eip5792Actions()) : null, [user, provider], ) diff --git a/support/wallet-common/lib/index.ts b/support/wallet-common/lib/index.ts index 7954801407..64dd597383 100644 --- a/support/wallet-common/lib/index.ts +++ b/support/wallet-common/lib/index.ts @@ -42,6 +42,8 @@ export type { } from "./interfaces/eip1193" export type { EIP6963ProviderInfo, EIP6963ProviderDetail, EIP6963AnnounceProviderEvent } from "./interfaces/eip6963" +export { HappyWalletCapability } from "./interfaces/eip5792" + export type { MsgsFromApp, MsgsFromIframe, diff --git a/support/wallet-common/lib/interfaces/eip5792.ts b/support/wallet-common/lib/interfaces/eip5792.ts new file mode 100644 index 0000000000..452ca89cd4 --- /dev/null +++ b/support/wallet-common/lib/interfaces/eip5792.ts @@ -0,0 +1,4 @@ +export enum HappyWalletCapability { + // AtomicBatch = "atomicBatch", // coming soon! + BoopPaymaster = "boopPaymaster", +}