diff --git a/apps/scratchpad/src/createAccount.ts b/apps/scratchpad/src/createAccount.ts index c62cf9b43..c587b6011 100644 --- a/apps/scratchpad/src/createAccount.ts +++ b/apps/scratchpad/src/createAccount.ts @@ -1,5 +1,6 @@ import { getBundlerClientFromEnv } from "@daimo/api/src/network/bundlerClient"; import { ViemClient } from "@daimo/api/src/network/viemClient"; +<<<<<<< Updated upstream import { UserOpHex, assertNotNull } from "@daimo/common"; import { daimoAccountABI, @@ -9,18 +10,23 @@ import { daimoPaymasterV2Address, entryPointABI, erc20ABI, +======= +import { UserOpHex, assert } from "@daimo/common"; +import { + daimoAccountABI, + daimoAccountFactoryConfig, + daimoEphemeralNotesAddress, + daimoEphemeralNotesV2Address, +>>>>>>> Stashed changes } from "@daimo/contract"; import { - DaimoNonce, - DaimoNonceMetadata, - DaimoNonceType, DaimoOpSender, OpSenderCallback, SigningCallback, } from "@daimo/userop"; +import { p256 } from "@noble/curves/p256"; import { base64urlnopad } from "@scure/base"; import crypto from "node:crypto"; -import { Constants } from "userop"; import { Chain, Hex, @@ -32,7 +38,6 @@ import { createWalletClient, encodeAbiParameters, getAbiItem, - getAddress, hexToBytes, http, } from "viem"; @@ -48,7 +53,7 @@ export async function createAccount() { // Viem const chain = chainConfig.chainL2; const publicClient = createPublicClient({ chain, transport: http() }); - console.log(`Connected to ${chain.name}, ${publicClient.transport.url}`); + console.log(`Connected to ${chain.name} ${chain.id}`); const funderPrivKey = `0x${process.env.DAIMO_API_PRIVATE_KEY}` as const; const funderAccount = privateKeyToAccount(funderPrivKey); @@ -60,11 +65,20 @@ export async function createAccount() { }); // Generate keypair - const p256 = { name: "ECDSA", namedCurve: "P-256", hash: "SHA-256" }; - const key = await crypto.subtle.generateKey(p256, true, ["sign", "verify"]); - const pubKeyDer = await crypto.subtle.exportKey("spki", key.publicKey); - const pubKeyHex = Buffer.from(pubKeyDer).toString("hex"); - console.log(`Generated pubkey: ${pubKeyHex}`); + // const p256 = { name: "ECDSA", namedCurve: "P-256", hash: "SHA-256" }; + const kH = "ce0a8faf51e77b5889d0cfb9b24075703fa60d1e643380bfa68b661551e7e3a8"; + const privKeyBytes = Buffer.from(kH, "hex"); + // const k = await crypto.subtle.importKey("raw", kB, p256, false, ["sign"]); + const pubKeyRawHex = [ + "65a2fa44daad46eab0278703edb6c4dcf5e30b8a9aec09fdc71a56f52aa392e4", + "4a7a9e4604aa36898209997288e902ac544a555e4b5e0a9efef2b59233f3f437", + ].join(""); + + // const key = await crypto.subtle.generateKey(p256, true, ["sign", "verify"]); + // const pubKeyDer = await crypto.subtle.exportKey("spki", key.publicKey); + // const pubKeyHex = Buffer.from(pubKeyDer).toString("hex"); + // console.log(`Generated pubkey: ${pubKeyHex}`); + // const pubKeyRawHex = pubKeyHex.substring(56); // Daimo account const signer: SigningCallback = async (challenge: Hex) => { @@ -93,13 +107,12 @@ export async function createAccount() { authenticatorData[32] = 5; // flags: user present (1) + user verified (4) const message = concat([authenticatorData, new Uint8Array(clientDataHash)]); - const sigRaw = await crypto.subtle.sign( - p256, - key.privateKey, - Buffer.from(message) - ); + //const sigRaw = await crypto.subtle.sign(p256, k, Buffer.from(message)); + const sig = p256.sign(privKeyBytes, message); + const sigRaw = sig.toCompactRawBytes(); + assert(sigRaw.length === 64, "Invalid signature length"); - // DER encode + // Extract R,S const r = Buffer.from(sigRaw).subarray(0, 32).toString("hex"); const s = Buffer.from(sigRaw).subarray(32, 64).toString("hex"); @@ -137,7 +150,7 @@ export async function createAccount() { // return bundlerClient.sendUserOp(op, vc); }; - const pubKey = Buffer.from(pubKeyHex.substring(56), "hex"); + const pubKey = Buffer.from(pubKeyRawHex, "hex"); if (pubKey.length !== 64) { throw new Error("Invalid public key, wrong length"); } @@ -152,6 +165,7 @@ export async function createAccount() { functionName: "getAddress", args, }); + console.log(`new account address ${address}`); // Deploy account const hash = await walletClient.writeContract({ @@ -160,9 +174,9 @@ export async function createAccount() { args, value: 0n, }); - console.log(`[API] deploy transaction ${hash}`); + console.log(`deploy transaction ${hash}`); const tx = await publicClient.waitForTransactionReceipt({ hash }); - console.log(`[API] deploy transaction ${tx.status}`); + console.log(`deploy transaction status ${tx.status}`); const chainId = chainConfig.chainL2.id; const account = await DaimoOpSender.init({ @@ -179,41 +193,41 @@ export async function createAccount() { const addr = account.getAddress(); console.log(`Burner Daimo account: ${addr}`); - // Fund it from the faucet - const usdcTxHash = await walletClient.writeContract({ - abi: erc20ABI, - address: chainConfig.tokenAddress, - functionName: "transfer", - args: [addr, 1000000n], - }); - console.log(`Faucet sent USDC to Daimo account: ${usdcTxHash}`); - await waitForTx(publicClient, usdcTxHash); - - const depositTxHash = await walletClient.writeContract({ - address: getAddress(Constants.ERC4337.EntryPoint), - abi: entryPointABI, - functionName: "depositTo", - args: [addr], - value: 10n ** 16n, // 0.01 ETH - }); - console.log(`Faucet deposited prefund: ${depositTxHash}`); - await waitForTx(publicClient, depositTxHash); - - // Finally, we should be able to do a userop from our new Daimo account. - // Send $0.50 USDC to nibnalin.eth - const recipient = `0xF05b5f04B7a77Ca549C0dE06beaF257f40C66FDB`; - const nonce = new DaimoNonce(new DaimoNonceMetadata(DaimoNonceType.Send)); - const userOpHash = await account.erc20transfer(recipient, "0.1", { - nonce, - chainGasConstants: { - maxPriorityFeePerGas: "1000000", - maxFeePerGas: "100000050", - estimatedFee: 0.1, - paymasterAddress: daimoPaymasterV2Address, - preVerificationGas: "0", - }, - }); - console.log("✅ userop accepted by bundler: ", userOpHash); + // // Fund it from the faucet + // const usdcTxHash = await walletClient.writeContract({ + // abi: erc20ABI, + // address: chainConfig.tokenAddress, + // functionName: "transfer", + // args: [addr, 1000000n], + // }); + // console.log(`Faucet sent USDC to Daimo account: ${usdcTxHash}`); + // await waitForTx(publicClient, usdcTxHash); + + // const depositTxHash = await walletClient.writeContract({ + // address: getAddress(Constants.ERC4337.EntryPoint), + // abi: entryPointABI, + // functionName: "depositTo", + // args: [addr], + // value: 10n ** 16n, // 0.01 ETH + // }); + // console.log(`Faucet deposited prefund: ${depositTxHash}`); + // await waitForTx(publicClient, depositTxHash); + + // // Finally, we should be able to do a userop from our new Daimo account. + // // Send $0.50 USDC to nibnalin.eth + // const recipient = `0xF05b5f04B7a77Ca549C0dE06beaF257f40C66FDB`; + // const nonce = new DaimoNonce(new DaimoNonceMetadata(DaimoNonceType.Send)); + // const userOpHash = await account.erc20transfer(recipient, "0.1", { + // nonce, + // chainGasConstants: { + // maxPriorityFeePerGas: "1000000", + // maxFeePerGas: "100000050", + // estimatedFee: 0.1, + // paymasterAddress: daimoPaymasterV2Address, + // preVerificationGas: "0", + // }, + // }); + // console.log("✅ userop accepted by bundler: ", userOpHash); } async function waitForTx( diff --git a/packages/daimo-api/package.json b/packages/daimo-api/package.json index d482172f6..26d4846ce 100644 --- a/packages/daimo-api/package.json +++ b/packages/daimo-api/package.json @@ -9,7 +9,7 @@ "start": "OTEL_SERVICE_NAME=daimo-api node --max-old-space-size=7168 -- ../../node_modules/.bin/ts-node -r ./src/server/tracing.ts src/server/server.ts", "test": "tsc --noEmit && DAIMO_DOMAIN=example.com tape -r ts-node/register/transpile-only test/**/*.test.ts", "lint": "npm run lint:deps && npm run lint:style", - "lint:deps": "npx depcheck --ignores @tsconfig/node20,@types/tape", + "lint:deps": "npx depcheck --ignores @tsconfig/node20,@types/tape,ts-node", "lint:style": "eslint . --max-warnings=0" }, "repository": "https://github.com/daimo-eth/daimo.git", diff --git a/packages/daimo-api/src/contract/paymaster.ts b/packages/daimo-api/src/contract/paymaster.ts index 358c055b4..b40669cc0 100644 --- a/packages/daimo-api/src/contract/paymaster.ts +++ b/packages/daimo-api/src/contract/paymaster.ts @@ -1,5 +1,5 @@ -import { ChainGasConstants, EAccount, UserOpHex } from "@daimo/common"; -import { daimoChainFromId, daimoPaymasterV2Address } from "@daimo/contract"; +import { ChainGasConstants, EAccount } from "@daimo/common"; +import { daimoPaymasterV2Address } from "@daimo/contract"; import { hexToBigInt } from "viem"; import { DB } from "../db/db"; @@ -92,16 +92,12 @@ export class Paymaster { "get-user-operation-gas-price-params", () => this.bundlerClient.getUserOperationGasPriceParams() ); - const estimatedPreVerificationGas = await retryBackoff( - "estimate-preverification-gas", - () => this.bundlerClient.estimatePreVerificationGas(getDummyOp()) - ); const maxFeePerGas = hexToBigInt(gasPriceParams.maxFeePerGas); const maxPriorityFeePerGas = hexToBigInt( gasPriceParams.maxPriorityFeePerGas ); - const preVerificationGas = estimatedPreVerificationGas; + const preVerificationGas = calcPreVerificationGas(); // this.estimatedFee = this.estimatePimlicoFee(); @@ -149,41 +145,6 @@ export class Paymaster { } } -function getDummyOp(): UserOpHex { - return dummyAddDeviceOps[daimoChainFromId(chainConfig.chainL2.id)]; +function calcPreVerificationGas() { + return 10_000_000n; // TODO } - -// Arbitrary add device ops used for gas estimation via Pimlico. -// See AccountSendUserop.t.sol and p256.playground for test op generation. -const dummyAddDeviceOps = { - base: { - sender: "0xFBfa6A0D1F44b60d7CCA4b95d5a2CfB15246DB0D", - nonce: "0xd642bab777d7280fcaa3d46d12f2294c0000000000000000", - initCode: "0x", - callData: - "0x34fcd5be000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa9604500000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000", - callGasLimit: "0x493e0", - verificationGasLimit: "0xaae60", - preVerificationGas: "0xf4240", - maxFeePerGas: "0xa76aa9", - maxPriorityFeePerGas: "0xa76aa9", - paymasterAndData: "0x939263eafe57038a072cb4edd6b25dd81a8a6c56", - signature: - "0x010000655d836301000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001ae451cfd35ad2d0b611f9c6a6b782e9dc102c8b6864b4d159ff9bf6f6ba1dbd0698430a4f67db38a250ad441a0dbf639b850d1697c4b269c0e4337436d51c313000000000000000000000000000000000000000000000000000000000000002500000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a22415141415a56324459397a6977776f6e4473445859703875356f4638546b4653466f383971744e5a3463315166304d42692d7073227d000000000000", - }, - baseSepolia: { - sender: "0xCaa88b53CDDF460Abf96D9C57F3714fB25A0738c", - nonce: "0x4000000000000005c6fba6fbe829ece85acfcfe949005b00000000000000000", - initCode: "0x", - callData: - "0x34fcd5be000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000caa88b53cddf460abf96d9c57f3714fb25a0738c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064b3033ef2000000000000000000000000000000000000000000000000000000000000000165a2fa44daad46eab0278703edb6c4dcf5e30b8a9aec09fdc71a56f52aa392e44a7a9e4604aa36898209997288e902ac544a555e4b5e0a9efef2b59233f3f43700000000000000000000000000000000000000000000000000000000", - callGasLimit: "0x493e0", - verificationGasLimit: "0xaae60", - preVerificationGas: "0x5208", - maxFeePerGas: "0x5f5e132", - maxPriorityFeePerGas: "0x5f5e100", - paymasterAndData: "0x99d720cd5a04c16dc5377638e3f6d609c895714f", - signature: - "0x0100000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000001700000000000000000000000000000000000000000000000000000000000000012dec57c39ecd3a573bb35e4d1bc16d3db6d5ee8ab024605aa910631d38bee5fe6036d125bc72d63a29ff6ab63e25a5273acb9824b818e919d83ed0f883d6e94100000000000000000000000000000000000000000000000000000000000000250000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000777b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a224151414141414141414d43655f78414d677a69437a5a5338613130754455577662736c343633394b4c6c46306157762d36485349222c226f726967696e223a2268747470733a2f2f6461696d6f2e78797a227d000000000000000000", - }, -} as const; diff --git a/packages/daimo-api/src/network/bundlerClient.ts b/packages/daimo-api/src/network/bundlerClient.ts index beca60de8..229cd1752 100644 --- a/packages/daimo-api/src/network/bundlerClient.ts +++ b/packages/daimo-api/src/network/bundlerClient.ts @@ -9,7 +9,7 @@ import { UserOpHex, assert, lookup } from "@daimo/common"; import { entryPointABI } from "@daimo/contract"; import { trace } from "@opentelemetry/api"; import { BundlerJsonRpcProvider, Constants } from "userop"; -import { Address, Hex, PublicClient, hexToBigInt, isHex } from "viem"; +import { Address, Hex, PublicClient, hexToBigInt } from "viem"; import { CompressionInfo, compressBundle } from "./bundleCompression"; import { ViemClient } from "./viemClient"; @@ -17,10 +17,6 @@ import { NameRegistry } from "../contract/nameRegistry"; import { OpIndexer } from "../contract/opIndexer"; import { chainConfig } from "../env"; -interface GasEstimate { - preVerificationGas: Hex; -} - interface GasPriceParams { maxFeePerGas: Hex; maxPriorityFeePerGas: Hex; @@ -169,18 +165,12 @@ export class BundlerClient { } async estimatePreVerificationGas(op: UserOpHex) { - const args = [op, Constants.ERC4337.EntryPoint]; - const gasEstimate = (await this.provider.send( - "eth_estimateUserOperationGas", - args - )) as GasEstimate; - console.log( - `[BUNDLER] estimated userOp gas: ${JSON.stringify(op)}: ${JSON.stringify( - gasEstimate - )}` - ); - assert(isHex(gasEstimate.preVerificationGas)); - return hexToBigInt(gasEstimate.preVerificationGas); + // const args = [op, Constants.ERC4337.EntryPoint]; + + // TODO: compute preVerificationGas from the op + // Use Pimlico forumla: https://github.com/pimlicolabs/alto/blob/main/src/entrypoint-0.6/utils/validation.ts#L305-L368 + // x Optimism formula: https://optimistic.etherscan.io/address/0xc0d3c0d3c0d3c0d3c0d3c0d3c0d3c0d3c0d3000f#code + return 1_000_000; } async getUserOperationGasPriceParams() {