Skip to content

Commit

Permalink
api: stub preVerificationGas
Browse files Browse the repository at this point in the history
  • Loading branch information
dcposch committed Feb 10, 2024
1 parent 83bd64a commit c3223bd
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 117 deletions.
124 changes: 69 additions & 55 deletions apps/scratchpad/src/createAccount.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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,
Expand All @@ -32,7 +38,6 @@ import {
createWalletClient,
encodeAbiParameters,
getAbiItem,
getAddress,
hexToBytes,
http,
} from "viem";
Expand All @@ -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);
Expand All @@ -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) => {
Expand Down Expand Up @@ -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");

Expand Down Expand Up @@ -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");
}
Expand All @@ -152,6 +165,7 @@ export async function createAccount() {
functionName: "getAddress",
args,
});
console.log(`new account address ${address}`);

// Deploy account
const hash = await walletClient.writeContract({
Expand All @@ -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({
Expand All @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion packages/daimo-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
49 changes: 5 additions & 44 deletions packages/daimo-api/src/contract/paymaster.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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;
24 changes: 7 additions & 17 deletions packages/daimo-api/src/network/bundlerClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,14 @@ 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";
import { NameRegistry } from "../contract/nameRegistry";
import { OpIndexer } from "../contract/opIndexer";
import { chainConfig } from "../env";

interface GasEstimate {
preVerificationGas: Hex;
}

interface GasPriceParams {
maxFeePerGas: Hex;
maxPriorityFeePerGas: Hex;
Expand Down Expand Up @@ -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() {
Expand Down

0 comments on commit c3223bd

Please sign in to comment.