From 1694d69f0a35d77c65b0388284ad9c38c32168bd Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Thu, 7 Nov 2024 19:51:07 +0700 Subject: [PATCH] update imports --- packages/entrykit/package.json | 2 + packages/entrykit/src/createWagmiConfig.ts | 2 +- packages/entrykit/src/exports/internal.ts | 3 - packages/entrykit/src/getBundlerTransport.ts | 10 +- packages/entrykit/src/getSessionAccount.ts | 4 +- .../src/onboarding/useClaimGasPass.ts | 13 +- packages/entrykit/src/passkey/getAccount.ts | 2 +- .../entrykit/src/passkey/passkeyConnector.ts | 2 +- .../smart-account/isCoinbaseSmartAccount.ts | 6 - .../smart-account/toCoinbaseSmartAccount.ts | 825 ------------------ packages/entrykit/src/transports/common.ts | 36 - .../entrykit/src/transports/gasEstimator.ts | 21 - .../methods/estimateUserOperationGas.ts | 18 - .../transports/methods/sendUserOperation.ts | 73 -- .../src/transports/quarryPassIssuer.ts | 26 - .../entrykit/src/transports/userOpExecutor.ts | 58 -- packages/entrykit/src/transports/wiresaw.ts | 115 --- pnpm-lock.yaml | 6 + 18 files changed, 21 insertions(+), 1201 deletions(-) delete mode 100644 packages/entrykit/src/smart-account/isCoinbaseSmartAccount.ts delete mode 100644 packages/entrykit/src/smart-account/toCoinbaseSmartAccount.ts delete mode 100644 packages/entrykit/src/transports/common.ts delete mode 100644 packages/entrykit/src/transports/gasEstimator.ts delete mode 100644 packages/entrykit/src/transports/methods/estimateUserOperationGas.ts delete mode 100644 packages/entrykit/src/transports/methods/sendUserOperation.ts delete mode 100644 packages/entrykit/src/transports/quarryPassIssuer.ts delete mode 100644 packages/entrykit/src/transports/userOpExecutor.ts delete mode 100644 packages/entrykit/src/transports/wiresaw.ts diff --git a/packages/entrykit/package.json b/packages/entrykit/package.json index faab8dc497..e93a9c72a2 100644 --- a/packages/entrykit/package.json +++ b/packages/entrykit/package.json @@ -44,8 +44,10 @@ "@latticexyz/common": "workspace:*", "@latticexyz/config": "workspace:*", "@latticexyz/explorer": "workspace:*", + "@latticexyz/paymaster": "workspace:*", "@latticexyz/protocol-parser": "workspace:*", "@latticexyz/store": "workspace:*", + "@latticexyz/wiresaw": "workspace:*", "@latticexyz/world": "workspace:*", "@latticexyz/world-modules": "workspace:*", "@noble/curves": "^1.6.0", diff --git a/packages/entrykit/src/createWagmiConfig.ts b/packages/entrykit/src/createWagmiConfig.ts index 2ae0e5d404..64ce2be21d 100644 --- a/packages/entrykit/src/createWagmiConfig.ts +++ b/packages/entrykit/src/createWagmiConfig.ts @@ -3,7 +3,7 @@ import { WalletList, connectorsForWallets, getDefaultWallets } from "@rainbow-me import { Config, CreateConfigParameters, createConfig } from "wagmi"; import { passkeyWallet } from "./passkey/passkeyWallet"; import { mapObject } from "@latticexyz/common/utils"; -import { wiresaw } from "./transports/wiresaw"; +import { wiresaw } from "@latticexyz/wiresaw/internal"; export type CreateWagmiConfigOptions< chains extends readonly [Chain, ...Chain[]] = readonly [Chain, ...Chain[]], diff --git a/packages/entrykit/src/exports/internal.ts b/packages/entrykit/src/exports/internal.ts index f032c9c8c4..5c4027840b 100644 --- a/packages/entrykit/src/exports/internal.ts +++ b/packages/entrykit/src/exports/internal.ts @@ -15,8 +15,5 @@ export { createWagmiConfig, type CreateWagmiConfigOptions } from "../createWagmi // And some additional internal things export * from "../passkey/passkeyWallet"; export * from "../passkey/passkeyConnector"; -export * from "../smart-account/toCoinbaseSmartAccount"; -export * from "../smart-account/isCoinbaseSmartAccount"; export * from "../getConnectors"; export * from "../getWallets"; -export * from "../transports/wiresaw"; diff --git a/packages/entrykit/src/getBundlerTransport.ts b/packages/entrykit/src/getBundlerTransport.ts index 689f2625d6..ed3b8325ae 100644 --- a/packages/entrykit/src/getBundlerTransport.ts +++ b/packages/entrykit/src/getBundlerTransport.ts @@ -1,14 +1,14 @@ import { transactionQueue } from "@latticexyz/common/actions"; import { Chain, createClient, fallback, http, webSocket } from "viem"; import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"; -import { userOpExecutor } from "./transports/userOpExecutor"; -import { gasEstimator } from "./transports/gasEstimator"; -import { wiresaw } from "./transports/wiresaw"; +import { wiresaw } from "@latticexyz/wiresaw/internal"; +import { gasEstimator, userOpExecutor } from "@latticexyz/paymaster/internal"; export function getBundlerTransport(chain: Chain) { const bundlerHttpUrl = chain.rpcUrls.bundler?.http[0]; - const bundlerTransport = chain.rpcUrls.bundler?.http[0] - ? wiresaw(gasEstimator(http(bundlerHttpUrl))) + // TODO: bundler websocket + const bundlerTransport = bundlerHttpUrl + ? gasEstimator(wiresaw(http(bundlerHttpUrl))) : chain.id === 31337 ? userOpExecutor({ executor: createClient({ diff --git a/packages/entrykit/src/getSessionAccount.ts b/packages/entrykit/src/getSessionAccount.ts index a7a2c81f99..0e1e487b22 100644 --- a/packages/entrykit/src/getSessionAccount.ts +++ b/packages/entrykit/src/getSessionAccount.ts @@ -1,7 +1,7 @@ import { Address, Chain, Client, Transport } from "viem"; -import { getSessionSigner } from "./getSessionSigner"; -import { toCoinbaseSmartAccount } from "./smart-account/toCoinbaseSmartAccount"; import { SmartAccount } from "viem/account-abstraction"; +import { toCoinbaseSmartAccount } from "@latticexyz/common/accounts"; +import { getSessionSigner } from "./getSessionSigner"; export async function getSessionAccount({ client, diff --git a/packages/entrykit/src/onboarding/useClaimGasPass.ts b/packages/entrykit/src/onboarding/useClaimGasPass.ts index c09071f125..fc46d45210 100644 --- a/packages/entrykit/src/onboarding/useClaimGasPass.ts +++ b/packages/entrykit/src/onboarding/useClaimGasPass.ts @@ -1,7 +1,7 @@ import { Address } from "viem"; import { useEntryKitConfig } from "../EntryKitConfigProvider"; import { useMutation, useQueryClient } from "@tanstack/react-query"; -import { quarryPassIssuer } from "../transports/quarryPassIssuer"; +import { claimGasPass } from "@latticexyz/paymaster/internal"; export function useClaimGasPass() { const queryClient = useQueryClient(); @@ -12,16 +12,9 @@ export function useClaimGasPass() { mutationKey, onError: (error) => console.error(error), mutationFn: async (userAddress: Address) => { - const transport = quarryPassIssuer()({ chain }); // TODO: handle case where you already have a pass? - await transport.request({ - method: "quarry_issuePass", - params: ["0x01", userAddress], - }); - await transport.request({ - method: "quarry_claimAllowance", - params: ["0x01", userAddress], - }); + // TODO: get returned tx hashes to check if success + await claimGasPass({ chain, userAddress }); await Promise.all([ queryClient.invalidateQueries({ queryKey: ["getAllowance"] }), diff --git a/packages/entrykit/src/passkey/getAccount.ts b/packages/entrykit/src/passkey/getAccount.ts index 87cc8580d7..f63857822e 100644 --- a/packages/entrykit/src/passkey/getAccount.ts +++ b/packages/entrykit/src/passkey/getAccount.ts @@ -2,7 +2,7 @@ import { Client } from "viem"; import { toWebAuthnAccount } from "viem/account-abstraction"; import { cache } from "./cache"; import { P256Credential } from "webauthn-p256"; -import { toCoinbaseSmartAccount, ToCoinbaseSmartAccountReturnType } from "../smart-account/toCoinbaseSmartAccount"; +import { toCoinbaseSmartAccount, ToCoinbaseSmartAccountReturnType } from "@latticexyz/common/accounts"; export async function getAccount(client: Client, id: P256Credential["id"]): Promise { const { publicKeys } = cache.getState(); diff --git a/packages/entrykit/src/passkey/passkeyConnector.ts b/packages/entrykit/src/passkey/passkeyConnector.ts index bc2ab1269b..82dfb46793 100644 --- a/packages/entrykit/src/passkey/passkeyConnector.ts +++ b/packages/entrykit/src/passkey/passkeyConnector.ts @@ -20,7 +20,7 @@ import { createBundlerClient } from "../createBundlerClient"; import { observer } from "@latticexyz/explorer/observer"; import { getPaymasterAddress } from "../getPaymasterAddress"; import { getBundlerTransport } from "../getBundlerTransport"; -import { wiresaw } from "../transports/wiresaw"; +import { wiresaw } from "@latticexyz/wiresaw/internal"; export type PasskeyConnectorOptions = { // TODO: figure out what we wanna do across chains diff --git a/packages/entrykit/src/smart-account/isCoinbaseSmartAccount.ts b/packages/entrykit/src/smart-account/isCoinbaseSmartAccount.ts deleted file mode 100644 index 2360d2262e..0000000000 --- a/packages/entrykit/src/smart-account/isCoinbaseSmartAccount.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Account } from "viem"; -import { ToCoinbaseSmartAccountReturnType } from "./toCoinbaseSmartAccount"; - -export function isCoinbaseSmartAccount(account: Account | undefined): account is ToCoinbaseSmartAccountReturnType { - return account != null && "__isCoinbaseSmartAccount" in account && account.__isCoinbaseSmartAccount === true; -} diff --git a/packages/entrykit/src/smart-account/toCoinbaseSmartAccount.ts b/packages/entrykit/src/smart-account/toCoinbaseSmartAccount.ts deleted file mode 100644 index b160f5164e..0000000000 --- a/packages/entrykit/src/smart-account/toCoinbaseSmartAccount.ts +++ /dev/null @@ -1,825 +0,0 @@ -// Forked from https://github.com/wevm/viem/blob/main/src/account-abstraction/accounts/implementations/toCoinbaseSmartAccount.ts -// to match our forked contracts (upgrade to v0.7 entrypoint) -import { - Client, - OneOf, - LocalAccount, - Prettify, - Assign, - pad, - encodeFunctionData, - hashMessage, - TypedDataDefinition, - hashTypedData, - Hash, - encodeAbiParameters, - stringToHex, - size, - encodePacked, - parseSignature, - Address, - TypedData, - BaseError, -} from "viem"; -import { - WebAuthnAccount, - SmartAccount, - SmartAccountImplementation, - entryPoint07Abi, - entryPoint07Address, - getUserOperationHash, - UserOperation, - toSmartAccount, -} from "viem/account-abstraction"; -import { readContract } from "viem/actions"; -import { type WebAuthnData, parseSignature as parseP256Signature, Hex } from "webauthn-p256"; - -export type ToCoinbaseSmartAccountParameters = { - address?: Address | undefined; - client: Client; - owners: readonly OneOf[]; - nonce?: bigint | undefined; - signer?: OneOf; -}; - -export type ToCoinbaseSmartAccountReturnType = Prettify>; - -export type CoinbaseSmartAccountImplementation = Assign< - SmartAccountImplementation< - typeof entryPoint07Abi, - "0.7", - { - __isCoinbaseSmartAccount: true; - abi: typeof abi; - factory: { abi: typeof factoryAbi; address: Address }; - signer: OneOf; - } - >, - { - sign: NonNullable; - // TODO: should this be inside `extend` of `SmartAccountImplementation`? - isOwner: (account: LocalAccount | WebAuthnAccount) => Promise; - } ->; - -/** - * @description Create a Coinbase Smart Account. - * - * @param parameters - {@link ToCoinbaseSmartAccountParameters} - * @returns Coinbase Smart Account. {@link ToCoinbaseSmartAccountReturnType} - * - * @example - * import { toCoinbaseSmartAccount } from 'viem/account-abstraction' - * import { privateKeyToAccount } from 'viem/accounts' - * import { client } from './client.js' - * - * const account = toCoinbaseSmartAccount({ - * client, - * owners: [privateKeyToAccount('0x...')], - * }) - */ -export async function toCoinbaseSmartAccount( - parameters: ToCoinbaseSmartAccountParameters, -): Promise { - const { client, owners, nonce = 0n } = parameters; - - let address = parameters.address; - - const entryPoint = { - abi: entryPoint07Abi, - address: entryPoint07Address, - version: "0.7", - } as const; - const factory = { - abi: factoryAbi, - // TODO: make configurable? - address: "0x356336adA1619BeC1Ae4E6D94Dd9c0490DA414a8", - } as const; - - if (!owners.length) { - throw new Error("`owners` must have at least one account."); - } - - function accountToBytes(account: LocalAccount | WebAuthnAccount) { - return account.type === "webAuthn" ? account.publicKey : pad(account.address); - } - - const owner = parameters.signer ?? owners[0]; - const ownerIndex = owners.indexOf(owner); - if (ownerIndex === -1) { - throw new Error("`signer` must be one of `owners`."); - } - - let counter = 0n; - - return toSmartAccount({ - client, - entryPoint, - - extend: { - __isCoinbaseSmartAccount: true as const, - abi, - factory, - signer: owner, - }, - - async encodeCalls(calls) { - if (calls.length === 1) - return encodeFunctionData({ - abi, - functionName: "execute", - args: [calls[0].to, calls[0].value ?? 0n, calls[0].data ?? "0x"], - }); - return encodeFunctionData({ - abi, - functionName: "executeBatch", - args: [ - calls.map((call) => ({ - data: call.data ?? "0x", - target: call.to, - value: call.value ?? 0n, - })), - ], - }); - }, - - async getAddress() { - address ??= await readContract(client, { - ...factory, - functionName: "getAddress", - args: [owners.map(accountToBytes), nonce], - }); - return address; - }, - - async getNonce() { - // allows for 256 nonces per ms - const counterBits = 8n; - counter = (counter + 1n) % (1n << counterBits); - - const timestamp = BigInt(Date.now()); - const nonce = ((timestamp << counterBits) + counter) << 64n; - - return nonce; - }, - - async getFactoryArgs() { - const factoryData = encodeFunctionData({ - abi: factory.abi, - functionName: "createAccount", - args: [owners.map(accountToBytes), nonce], - }); - return { factory: factory.address, factoryData }; - }, - - async getStubSignature() { - if (owner.type === "webAuthn") - // eslint-disable-next-line max-len - return "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001949fc7c88032b9fcb5f6efc7a7b8c63668eae9871b765e23123bb473ff57aa831a7c0d9276168ebcc29f2875a0239cffdf2a9cd1c2007c5c77c071db9264df1d000000000000000000000000000000000000000000000000000000000000002549960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2273496a396e6164474850596759334b7156384f7a4a666c726275504b474f716d59576f4d57516869467773222c226f726967696e223a2268747470733a2f2f7369676e2e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000"; - return wrapSignature({ - signature: - "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c", - ownerIndex, - }); - }, - - async sign(parameters) { - const address = await this.getAddress(); - - const hash = toReplaySafeHash({ - address, - chainId: client.chain!.id, - hash: parameters.hash, - }); - - const signature = await sign({ hash, owner }); - - return wrapSignature({ signature, ownerIndex }); - }, - - async signMessage(parameters) { - const { message } = parameters; - const address = await this.getAddress(); - - const hash = toReplaySafeHash({ - address, - chainId: client.chain!.id, - hash: hashMessage(message), - }); - - const signature = await sign({ hash, owner }); - - return wrapSignature({ signature, ownerIndex }); - }, - - async signTypedData(parameters) { - const { domain, types, primaryType, message } = parameters as TypedDataDefinition; - const address = await this.getAddress(); - - const hash = toReplaySafeHash({ - address, - chainId: client.chain!.id, - hash: hashTypedData({ - domain, - message, - primaryType, - types, - }), - }); - - const signature = await sign({ hash, owner }); - - return wrapSignature({ signature, ownerIndex }); - }, - - async signUserOperation(parameters) { - const { chainId = client.chain!.id, ...userOperation } = parameters; - - const address = await this.getAddress(); - const hash = getUserOperationHash({ - chainId, - entryPointAddress: entryPoint.address, - entryPointVersion: entryPoint.version, - userOperation: { - ...(userOperation as unknown as UserOperation), - sender: address, - }, - }); - - const signature = await sign({ hash, owner }); - - return wrapSignature({ signature, ownerIndex }); - }, - - userOperation: { - async estimateGas(userOperation) { - if (owner.type !== "webAuthn") return; - - // Accounts with WebAuthn owner require a minimum verification gas limit of 800,000. - return { - verificationGasLimit: BigInt(Math.max(Number(userOperation.verificationGasLimit ?? 0n), 800_000)), - }; - }, - }, - - async isOwner(account: LocalAccount | WebAuthnAccount) { - return await readContract(client, { - abi, - address: await this.getAddress(), - functionName: "isOwnerBytes", - args: [accountToBytes(account)], - }); - }, - }); -} - -///////////////////////////////////////////////////////////////////////////////////////////// -// Utilities -///////////////////////////////////////////////////////////////////////////////////////////// - -/** @internal */ -export async function sign({ hash, owner }: { hash: Hash; owner: OneOf }) { - // WebAuthn Account (Passkey) - if (owner.type === "webAuthn") { - const { signature, webauthn } = await owner.sign({ - hash, - }); - return toWebAuthnSignature({ signature, webauthn }); - } - - if (owner.sign) return owner.sign({ hash }); - - throw new BaseError("`owner` does not support raw sign."); -} - -/** @internal */ -export function toReplaySafeHash({ address, chainId, hash }: { address: Address; chainId: number; hash: Hash }) { - return hashTypedData({ - domain: { - chainId, - name: "Coinbase Smart Wallet", - verifyingContract: address, - version: "1", - }, - types: { - CoinbaseSmartWalletMessage: [ - { - name: "hash", - type: "bytes32", - }, - ], - }, - primaryType: "CoinbaseSmartWalletMessage", - message: { - hash, - }, - }); -} - -/** @internal */ -export function toWebAuthnSignature({ webauthn, signature }: { webauthn: WebAuthnData; signature: Hex }) { - const { r, s } = parseP256Signature(signature); - return encodeAbiParameters( - [ - { - components: [ - { - name: "authenticatorData", - type: "bytes", - }, - { name: "clientDataJSON", type: "bytes" }, - { name: "challengeIndex", type: "uint256" }, - { name: "typeIndex", type: "uint256" }, - { - name: "r", - type: "uint256", - }, - { - name: "s", - type: "uint256", - }, - ], - type: "tuple", - }, - ], - [ - { - authenticatorData: webauthn.authenticatorData, - clientDataJSON: stringToHex(webauthn.clientDataJSON), - challengeIndex: BigInt(webauthn.challengeIndex), - typeIndex: BigInt(webauthn.typeIndex), - r, - s, - }, - ], - ); -} - -/** @internal */ -export function wrapSignature(parameters: { ownerIndex: number; signature: Hex }) { - const { ownerIndex } = parameters; - const signatureData = (() => { - if (size(parameters.signature) !== 65) return parameters.signature; - const signature = parseSignature(parameters.signature); - return encodePacked(["bytes32", "bytes32", "uint8"], [signature.r, signature.s, signature.yParity === 0 ? 27 : 28]); - })(); - return encodeAbiParameters( - [ - { - components: [ - { - name: "ownerIndex", - type: "uint8", - }, - { - name: "signatureData", - type: "bytes", - }, - ], - type: "tuple", - }, - ], - [ - { - ownerIndex, - signatureData, - }, - ], - ); -} - -///////////////////////////////////////////////////////////////////////////////////////////// -// Constants -///////////////////////////////////////////////////////////////////////////////////////////// - -export const abi = [ - { inputs: [], stateMutability: "nonpayable", type: "constructor" }, - { - inputs: [{ name: "owner", type: "bytes" }], - name: "AlreadyOwner", - type: "error", - }, - { inputs: [], name: "Initialized", type: "error" }, - { - inputs: [{ name: "owner", type: "bytes" }], - name: "InvalidEthereumAddressOwner", - type: "error", - }, - { - inputs: [{ name: "key", type: "uint256" }], - name: "InvalidNonceKey", - type: "error", - }, - { - inputs: [{ name: "owner", type: "bytes" }], - name: "InvalidOwnerBytesLength", - type: "error", - }, - { inputs: [], name: "LastOwner", type: "error" }, - { - inputs: [{ name: "index", type: "uint256" }], - name: "NoOwnerAtIndex", - type: "error", - }, - { - inputs: [{ name: "ownersRemaining", type: "uint256" }], - name: "NotLastOwner", - type: "error", - }, - { - inputs: [{ name: "selector", type: "bytes4" }], - name: "SelectorNotAllowed", - type: "error", - }, - { inputs: [], name: "Unauthorized", type: "error" }, - { inputs: [], name: "UnauthorizedCallContext", type: "error" }, - { inputs: [], name: "UpgradeFailed", type: "error" }, - { - inputs: [ - { name: "index", type: "uint256" }, - { name: "expectedOwner", type: "bytes" }, - { name: "actualOwner", type: "bytes" }, - ], - name: "WrongOwnerAtIndex", - type: "error", - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - - name: "index", - type: "uint256", - }, - { indexed: false, name: "owner", type: "bytes" }, - ], - name: "AddOwner", - type: "event", - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - - name: "index", - type: "uint256", - }, - { indexed: false, name: "owner", type: "bytes" }, - ], - name: "RemoveOwner", - type: "event", - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - - name: "implementation", - type: "address", - }, - ], - name: "Upgraded", - type: "event", - }, - { stateMutability: "payable", type: "fallback" }, - { - inputs: [], - name: "REPLAYABLE_NONCE_KEY", - outputs: [{ name: "", type: "uint256" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [{ name: "owner", type: "address" }], - name: "addOwnerAddress", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [ - { name: "x", type: "bytes32" }, - { name: "y", type: "bytes32" }, - ], - name: "addOwnerPublicKey", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [{ name: "functionSelector", type: "bytes4" }], - name: "canSkipChainIdValidation", - outputs: [{ name: "", type: "bool" }], - stateMutability: "pure", - type: "function", - }, - { - inputs: [], - name: "domainSeparator", - outputs: [{ name: "", type: "bytes32" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [], - name: "eip712Domain", - outputs: [ - { name: "fields", type: "bytes1" }, - { name: "name", type: "string" }, - { name: "version", type: "string" }, - { name: "chainId", type: "uint256" }, - { name: "verifyingContract", type: "address" }, - { name: "salt", type: "bytes32" }, - { name: "extensions", type: "uint256[]" }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [], - name: "entryPoint", - outputs: [{ name: "", type: "address" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { name: "target", type: "address" }, - { name: "value", type: "uint256" }, - { name: "data", type: "bytes" }, - ], - name: "execute", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - components: [ - { name: "target", type: "address" }, - { name: "value", type: "uint256" }, - { name: "data", type: "bytes" }, - ], - - name: "calls", - type: "tuple[]", - }, - ], - name: "executeBatch", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [{ name: "calls", type: "bytes[]" }], - name: "executeWithoutChainIdValidation", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - components: [ - { name: "sender", type: "address" }, - { name: "nonce", type: "uint256" }, - { name: "initCode", type: "bytes" }, - { name: "callData", type: "bytes" }, - { name: "callGasLimit", type: "uint256" }, - { - name: "verificationGasLimit", - type: "uint256", - }, - { - name: "preVerificationGas", - type: "uint256", - }, - { name: "maxFeePerGas", type: "uint256" }, - { - name: "maxPriorityFeePerGas", - type: "uint256", - }, - { name: "paymasterAndData", type: "bytes" }, - { name: "signature", type: "bytes" }, - ], - - name: "userOp", - type: "tuple", - }, - ], - name: "getUserOpHashWithoutChainId", - outputs: [{ name: "", type: "bytes32" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [], - name: "implementation", - outputs: [{ name: "$", type: "address" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [{ name: "owners", type: "bytes[]" }], - name: "initialize", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [{ name: "account", type: "address" }], - name: "isOwnerAddress", - outputs: [{ name: "", type: "bool" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [{ name: "account", type: "bytes" }], - name: "isOwnerBytes", - outputs: [{ name: "", type: "bool" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { name: "x", type: "bytes32" }, - { name: "y", type: "bytes32" }, - ], - name: "isOwnerPublicKey", - outputs: [{ name: "", type: "bool" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { name: "hash", type: "bytes32" }, - { name: "signature", type: "bytes" }, - ], - name: "isValidSignature", - outputs: [{ name: "result", type: "bytes4" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [], - name: "nextOwnerIndex", - outputs: [{ name: "", type: "uint256" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [{ name: "index", type: "uint256" }], - name: "ownerAtIndex", - outputs: [{ name: "", type: "bytes" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [], - name: "ownerCount", - outputs: [{ name: "", type: "uint256" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [], - name: "proxiableUUID", - outputs: [{ name: "", type: "bytes32" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { name: "index", type: "uint256" }, - { name: "owner", type: "bytes" }, - ], - name: "removeLastOwner", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [ - { name: "index", type: "uint256" }, - { name: "owner", type: "bytes" }, - ], - name: "removeOwnerAtIndex", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [], - name: "removedOwnersCount", - outputs: [{ name: "", type: "uint256" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [{ name: "hash", type: "bytes32" }], - name: "replaySafeHash", - outputs: [{ name: "", type: "bytes32" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { name: "newImplementation", type: "address" }, - { name: "data", type: "bytes" }, - ], - name: "upgradeToAndCall", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - components: [ - { name: "sender", type: "address" }, - { name: "nonce", type: "uint256" }, - { name: "initCode", type: "bytes" }, - { name: "callData", type: "bytes" }, - { name: "callGasLimit", type: "uint256" }, - { - name: "verificationGasLimit", - type: "uint256", - }, - { - name: "preVerificationGas", - type: "uint256", - }, - { name: "maxFeePerGas", type: "uint256" }, - { - name: "maxPriorityFeePerGas", - type: "uint256", - }, - { name: "paymasterAndData", type: "bytes" }, - { name: "signature", type: "bytes" }, - ], - - name: "userOp", - type: "tuple", - }, - { name: "userOpHash", type: "bytes32" }, - { name: "missingAccountFunds", type: "uint256" }, - ], - name: "validateUserOp", - outputs: [{ name: "validationData", type: "uint256" }], - stateMutability: "nonpayable", - type: "function", - }, - { stateMutability: "payable", type: "receive" }, -] as const; - -const factoryAbi = [ - { - inputs: [{ name: "implementation_", type: "address" }], - stateMutability: "payable", - type: "constructor", - }, - { inputs: [], name: "OwnerRequired", type: "error" }, - { - inputs: [ - { name: "owners", type: "bytes[]" }, - { name: "nonce", type: "uint256" }, - ], - name: "createAccount", - outputs: [ - { - name: "account", - type: "address", - }, - ], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { name: "owners", type: "bytes[]" }, - { name: "nonce", type: "uint256" }, - ], - name: "getAddress", - outputs: [{ name: "", type: "address" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [], - name: "implementation", - outputs: [{ name: "", type: "address" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [], - name: "initCodeHash", - outputs: [{ name: "", type: "bytes32" }], - stateMutability: "view", - type: "function", - }, -] as const; diff --git a/packages/entrykit/src/transports/common.ts b/packages/entrykit/src/transports/common.ts deleted file mode 100644 index fa019201eb..0000000000 --- a/packages/entrykit/src/transports/common.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { type EIP1193Parameters, type EIP1193RequestFn, type RpcSchema, UnionToTuple } from "viem"; - -export type getRpcMethod = Extract< - rpcSchema[number], - { Method: method } ->; - -export type getRpcSchema = UnionToTuple< - getRpcMethod ->; - -export type getRpcReturnType = { - [k in keyof rpcSchema & number as rpcSchema[k]["Method"]]: rpcSchema[k]["ReturnType"]; -}[method]; - -// TODO: figure out how to dedupe these -// one gives nice results for narrowing inside the request function body, but has a big union return type -// the other has nice return types, but has a big union inside the function body that doesn't narrow - -export type TransportRequestFnMapped = < - args extends EIP1193Parameters = EIP1193Parameters, ->( - args: args, - options?: Parameters[1], -) => Promise>; - -export type TransportRequestFn = < - args extends EIP1193Parameters = EIP1193Parameters, - method extends Extract = Extract< - rpcSchema[number], - { Method: args["method"] } - >, ->( - args: args, - options?: Parameters[1], -) => Promise; diff --git a/packages/entrykit/src/transports/gasEstimator.ts b/packages/entrykit/src/transports/gasEstimator.ts deleted file mode 100644 index f39dd446e0..0000000000 --- a/packages/entrykit/src/transports/gasEstimator.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { BundlerRpcSchema, Transport } from "viem"; -import { estimateUserOperationGas } from "./methods/estimateUserOperationGas"; -import { TransportRequestFn, getRpcSchema } from "./common"; - -export function gasEstimator(getTransport: transport): transport { - return ((opts) => { - const { request: originalRequest, ...rest } = getTransport(opts); - - const request: TransportRequestFn> = async ( - { method, params }, - options, - ) => { - if (method === "eth_estimateUserOperationGas") { - return estimateUserOperationGas(params); - } - return originalRequest({ method, params }, options); - }; - - return { request, ...rest }; - }) as transport; -} diff --git a/packages/entrykit/src/transports/methods/estimateUserOperationGas.ts b/packages/entrykit/src/transports/methods/estimateUserOperationGas.ts deleted file mode 100644 index 4f193e79ff..0000000000 --- a/packages/entrykit/src/transports/methods/estimateUserOperationGas.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { BundlerRpcSchema } from "viem"; -import { formatUserOperationRequest } from "viem/account-abstraction"; -import { getRpcMethod } from "../common"; - -type rpcMethod = getRpcMethod; - -export async function estimateUserOperationGas( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - _params: rpcMethod["Parameters"], -): Promise { - return formatUserOperationRequest({ - callGasLimit: 1_000_000n, - preVerificationGas: 100_000n, - verificationGasLimit: 1_000_000n, - paymasterVerificationGasLimit: 100_000n, - paymasterPostOpGasLimit: 100_000n, - }); -} diff --git a/packages/entrykit/src/transports/methods/sendUserOperation.ts b/packages/entrykit/src/transports/methods/sendUserOperation.ts deleted file mode 100644 index ef236f3450..0000000000 --- a/packages/entrykit/src/transports/methods/sendUserOperation.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { Client, Transport, Chain, Account, RpcUserOperation, RpcUserOperationReceipt, parseEther } from "viem"; -import { - formatUserOperation, - toPackedUserOperation, - getUserOperationHash, - entryPoint07Address, - entryPoint07Abi, -} from "viem/account-abstraction"; -import { setBalance, writeContract } from "viem/actions"; -import { getAction } from "viem/utils"; - -// TODO: move this into a generic to support other versions? -const entryPointVersion = "0.7"; -type entryPointVersion = typeof entryPointVersion; - -export async function sendUserOperation({ - executor, - rpcUserOp, -}: { - executor: Client; - rpcUserOp: RpcUserOperation; -}): Promise< - Pick, "success" | "userOpHash"> & { - receipt: Pick["receipt"], "transactionHash">; - } -> { - if (executor.chain.id === 31337) { - await setBalance( - executor.extend(() => ({ mode: "anvil" })), - { - address: executor.account.address, - value: parseEther("100"), - }, - ); - } - - const userOp = formatUserOperation(rpcUserOp); - const gas = - userOp.preVerificationGas + - userOp.verificationGasLimit + - (userOp.paymasterVerificationGasLimit ?? 0n) + - (userOp.paymasterPostOpGasLimit ?? 0n) + - userOp.callGasLimit; - - const packedUserOp = toPackedUserOperation(userOp); - - const userOpHash = getUserOperationHash({ - userOperation: userOp, - chainId: executor.chain.id, - entryPointVersion: "0.7", - entryPointAddress: entryPoint07Address, - }); - - const transactionHash = await getAction( - executor, - writeContract, - "writeContract", - )({ - abi: entryPoint07Abi, - address: entryPoint07Address, - functionName: "handleOps", - args: [[packedUserOp], executor.account.address], - chain: executor.chain, - account: executor.account, - gas, - }); - - return { - success: true, - userOpHash, - receipt: { transactionHash }, - }; -} diff --git a/packages/entrykit/src/transports/quarryPassIssuer.ts b/packages/entrykit/src/transports/quarryPassIssuer.ts deleted file mode 100644 index 8b5b052db5..0000000000 --- a/packages/entrykit/src/transports/quarryPassIssuer.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Address, EIP1193RequestFn, Hex, Transport, http } from "viem"; - -export type QuarryPassIssuerRpcSchema = [ - { - Method: "quarry_issuePass"; - Parameters: [passId: Hex, receiver: Address]; - ReturnType: { message: string }; - }, - { - Method: "quarry_claimAllowance"; - Parameters: [passId: Hex, receiver: Address]; - ReturnType: { message: string }; - }, -]; - -export function quarryPassIssuer(): Transport<"http", {}, EIP1193RequestFn> { - return ({ chain }) => { - if (!chain) throw new Error("No chain provided to issuer transport."); - - const url = "quarryPassIssuer" in chain.rpcUrls ? chain.rpcUrls.quarryPassIssuer.http[0] : undefined; - // TODO: add fallback for anvil to do what quarryPassIssuer does internally - if (!url) throw new Error(`No \`quarryPassIssuer\` RPC URL found for chain ${chain.id}.`); - - return http(url)({ chain, retryCount: 0 }); - }; -} diff --git a/packages/entrykit/src/transports/userOpExecutor.ts b/packages/entrykit/src/transports/userOpExecutor.ts deleted file mode 100644 index 67cc7d0ce2..0000000000 --- a/packages/entrykit/src/transports/userOpExecutor.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { - BundlerRpcSchema, - EIP1193RequestFn, - Hash, - RpcUserOperationReceipt, - Transport, - createTransport, - numberToHex, -} from "viem"; -import { entryPoint07Address } from "viem/account-abstraction"; -import { TransportRequestFn } from "./common"; -import { estimateUserOperationGas } from "./methods/estimateUserOperationGas"; -import { sendUserOperation } from "./methods/sendUserOperation"; -import { ConnectedClient } from "../common"; - -export function userOpExecutor({ executor }: { executor: ConnectedClient }): Transport { - return () => { - const receipts = new Map>(); - - const request: TransportRequestFn = async ({ method, params }) => { - // TODO: move chain/ID into args and executors as accounts instead of clients? - if (method === "eth_chainId") { - return numberToHex(executor.chain.id); - } - - if (method === "eth_supportedEntryPoints") { - return [entryPoint07Address]; - } - - if (method === "eth_sendUserOperation") { - const [rpcUserOp, entrypoint] = params; - if (entrypoint === entryPoint07Address) { - const result = await sendUserOperation({ executor, rpcUserOp }); - receipts.set(result.userOpHash, result as RpcUserOperationReceipt<"0.7">); - return result.userOpHash; - } - } - - if (method === "eth_getUserOperationReceipt") { - const [hash] = params; - return receipts.get(hash) ?? null; - } - - if (method === "eth_estimateUserOperationGas") { - return await estimateUserOperationGas(params); - } - - throw new Error("Not implemented"); - }; - - return createTransport({ - key: "userOpExecutor", - type: "userOpExecutor", - name: "User Operation Executor Transport", - request: request as EIP1193RequestFn, - }); - }; -} diff --git a/packages/entrykit/src/transports/wiresaw.ts b/packages/entrykit/src/transports/wiresaw.ts deleted file mode 100644 index 4f4772b706..0000000000 --- a/packages/entrykit/src/transports/wiresaw.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { - BundlerRpcSchema, - Hash, - Hex, - PublicRpcSchema, - RpcTransactionReceipt, - RpcUserOperationReceipt, - Transport, - http, -} from "viem"; -import { getRpcMethod, getRpcSchema, TransportRequestFn, TransportRequestFnMapped } from "./common"; - -// TODO: dedupe this with quarry package - -export type WiresawRpcSchema = [ - { - Method: "wiresaw_call"; - Parameters: getRpcMethod["Parameters"]; - ReturnType: getRpcMethod["ReturnType"]; - }, - { - Method: "wiresaw_sendUserOperation"; - Parameters: getRpcMethod["Parameters"]; - ReturnType: { - userOpHash: Hex; - txHash: Hex; - }; - }, -]; - -export type OverriddenMethods = [ - ...getRpcSchema, - ...getRpcSchema, -]; - -export function wiresaw(getTransport: transport): transport { - const cache = new Map< - Hex, - { - receipts: Map; - userOpReceipts: Map; - } - >(); - - function getReceiptsCache(chainId: Hex) { - const cached = cache.get(chainId) ?? { - receipts: new Map(), - userOpReceipts: new Map(), - }; - if (!cache.has(chainId)) cache.set(chainId, cached); - return cached; - } - - return ((args) => { - const getWiresawTransport = - args.chain?.rpcUrls && "wiresaw" in args.chain.rpcUrls ? http(args.chain.rpcUrls.wiresaw.http[0]) : undefined; - if (!getWiresawTransport) return getTransport(args); - - const transport = getTransport(args) as { request: TransportRequestFn }; - const wiresawTransport = getWiresawTransport(args) as { request: TransportRequestFn }; - - let chainId: Hex | null = null; - async function getChainId() { - return (chainId ??= await transport.request({ method: "eth_chainId" })); - } - - const request: TransportRequestFnMapped = async ({ method, params }, opts) => { - const { receipts, userOpReceipts } = getReceiptsCache(await getChainId()); - - if (method === "eth_chainId") { - return await getChainId(); - } - - // TODO: only route to wiresaw if using pending block tag - if (method === "eth_call") { - return wiresawTransport.request({ method: "wiresaw_call", params }); - } - - if (method === "eth_getTransactionReceipt") { - const [hash] = params; - const receipt = receipts.get(hash) ?? (await transport.request({ method, params })); - if (!receipts.has(hash) && receipt) receipts.set(hash, receipt); - return receipt; - } - - if (method === "eth_sendUserOperation") { - const result = await wiresawTransport.request({ - method: "wiresaw_sendUserOperation", - params, - }); - - // :haroldsmile: - userOpReceipts.set(result.userOpHash, { - success: true, - userOpHash: result.userOpHash, - receipt: { transactionHash: result.txHash }, - } as RpcUserOperationReceipt); - - return result.userOpHash; - } - - if (method === "eth_getUserOperationReceipt") { - const [hash] = params; - - const receipt = userOpReceipts.get(hash) ?? (await transport.request({ method, params })); - if (!userOpReceipts.has(hash) && receipt) userOpReceipts.set(hash, receipt); - return receipt; - } - - return await transport.request({ method, params }, opts); - }; - - return { ...transport, request }; - }) as transport; -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b2528718ad..238719737a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -468,12 +468,18 @@ importers: '@latticexyz/explorer': specifier: workspace:* version: link:../explorer + '@latticexyz/paymaster': + specifier: workspace:* + version: link:../paymaster '@latticexyz/protocol-parser': specifier: workspace:* version: link:../protocol-parser '@latticexyz/store': specifier: workspace:* version: link:../store + '@latticexyz/wiresaw': + specifier: workspace:* + version: link:../wiresaw '@latticexyz/world': specifier: workspace:* version: link:../world