UNPKG

@tevm/utils

Version:

A custom implementation of ethereumjs blockchain

268 lines (264 loc) 9.77 kB
import { DefensiveNullCheckError, InvalidBytesSizeError, UnreachableCodeError } from '@tevm/errors'; export { formatAbi, parseAbi } from 'abitype'; import { privateKeyToAccount } from 'viem/accounts'; export { mnemonicToAccount } from 'viem/accounts'; import { hexToBytes, keccak256, toBytes, toHex, getAddress, bytesToHex } from 'viem/utils'; export { boolToBytes, boolToHex, bytesToBigInt, bytesToBigint, bytesToBool, bytesToHex, bytesToNumber, decodeAbiParameters, decodeErrorResult, decodeEventLog, decodeFunctionData, decodeFunctionResult, encodeAbiParameters, encodeDeployData, encodeErrorResult, encodeEventTopics, encodeFunctionData, encodeFunctionResult, encodePacked, formatEther, formatGwei, formatLog, fromBytes, fromHex, fromRlp, getAddress, hexToBigInt, hexToBool, hexToBytes, hexToNumber, hexToString, isAddress, isBytes, isHex, keccak256, numberToHex, parseEther, parseGwei, serializeTransaction, stringToHex, toBytes, toHex, toRlp } from 'viem/utils'; import { ecrecover } from '@ethereumjs/util'; export { Account as EthjsAccount, Address as EthjsAddress, GWEI_TO_WEI, KECCAK256_RLP, KECCAK256_RLP_ARRAY, KeyEncoding, TypeOutput, ValueEncoding, Withdrawal, bytesToUnprefixedHex, bytesToUtf8, concatBytes, createAccount, createAddressFromString, createWithdrawal, ecrecover, equalsBytes, fetchFromProvider, getProvider, randomBytes, setLengthLeft, toType } from '@ethereumjs/util'; // src/Bloom.ts // src/Bloom.ts var zeros = (bytes) => { return new Uint8Array(bytes); }; var BYTE_SIZE = 256; var Bloom = class { bitvector; /** * Represents a Bloom filter. * @throws {InvalidBytesSizeError} If the byte size of the bitvector is not 256. */ constructor(bitvector) { if (!bitvector) { this.bitvector = zeros(BYTE_SIZE); } else { if (bitvector.length !== BYTE_SIZE) throw new InvalidBytesSizeError(BYTE_SIZE, bitvector.length); this.bitvector = bitvector; } } /** * Adds an element to a bit vector of a 64 byte bloom filter. * @param e - The element to add * @throws {never} */ add(e) { const eBytes = hexToBytes(keccak256(e)); const mask = 2047; for (let i = 0; i < 3; i++) { const first2bytes = new DataView(eBytes.buffer).getUint16(i * 2); const loc = mask & first2bytes; const byteLoc = loc >> 3; const bitLoc = 1 << loc % 8; let item = this.bitvector[BYTE_SIZE - byteLoc - 1]; if (item === void 0) { throw new DefensiveNullCheckError("item is not defined. There is a bug in the implementation"); } item |= bitLoc; this.bitvector[BYTE_SIZE - byteLoc - 1] = item; } } /** * Checks if an element is in the bloom. * @param e - The element to check * @throws {never} */ check(e) { const eBytes = hexToBytes(keccak256(e)); const mask = 2047; let match = true; for (let i = 0; i < 3 && match; i++) { const first2bytes = new DataView(eBytes.buffer).getUint16(i * 2); const loc = mask & first2bytes; const byteLoc = loc >> 3; const bitLoc = 1 << loc % 8; const item = this.bitvector[BYTE_SIZE - byteLoc - 1]; if (item === void 0) { throw new DefensiveNullCheckError("item is not defined. There is a bug in the implementation"); } match = (item & bitLoc) !== 0; } return Boolean(match); } /** * Checks if multiple topics are in a bloom. * @returns `true` if every topic is in the bloom * @throws {never} */ multiCheck(topics) { return topics.every((t) => this.check(t)); } /** * Bitwise or blooms together. * @throws {never} */ or(bloom) { for (let i = 0; i <= BYTE_SIZE; i++) { const a = this.bitvector[i]; const b = bloom.bitvector[i]; if (a === void 0) { throw new DefensiveNullCheckError("a is not defined. Please open an issue in the tevm github repo"); } if (b === void 0) { throw new DefensiveNullCheckError("b is not defined. Please open an issue in the tevm github repo"); } this.bitvector[i] = a | b; } } }; var encodeKey = (bytes) => { if (bytes instanceof Uint8Array) { return bytesToHex(bytes); } return bytes; }; var createMemoryDb = (initialDb) => { const db = initialDb ?? /* @__PURE__ */ new Map(); return { get: (key) => { return Promise.resolve(db.get(encodeKey(key))); }, put: (key, value) => { db.set(encodeKey(key), value); return Promise.resolve(); }, del: (key) => { db.delete(encodeKey(key)); return Promise.resolve(); }, shallowCopy: () => { return createMemoryDb(new Map(db)); }, // For compatability open: () => Promise.resolve(), batch: (stack) => { for (const item of stack) { if (item.type === "del") { db.delete(encodeKey(item.key)); } else if (item.type === "put") { db.set(encodeKey(item.key), item.value); } else { return Promise.reject(new UnreachableCodeError(item)); } } return Promise.resolve(); } }; }; var PREFUNDED_PRIVATE_KEYS = [ "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", // 0 "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d", // 1 "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a", // 2 "0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6", // 3 "0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a", // 4 "0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba", // 5 "0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e", // 6 "0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356", // 7 "0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97", // 8 "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6" // 9 ]; var PREFUNDED_PUBLIC_KEYS = [ "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", // 0 "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", // 1 "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", // 2 "0x90F79bf6EB2c4f870365E785982E1f101E93b906", // 3 "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", // 4 "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", // 5 "0x976EA74026E726554dB657fA54763abd0C3a0aa9", // 6 "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955", // 7 "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f", // 8 "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720" // 9 ]; var PREFUNDED_ACCOUNTS = [ privateKeyToAccount(PREFUNDED_PRIVATE_KEYS[0]), privateKeyToAccount(PREFUNDED_PRIVATE_KEYS[1]), privateKeyToAccount(PREFUNDED_PRIVATE_KEYS[2]), privateKeyToAccount(PREFUNDED_PRIVATE_KEYS[3]), privateKeyToAccount(PREFUNDED_PRIVATE_KEYS[4]), privateKeyToAccount(PREFUNDED_PRIVATE_KEYS[5]), privateKeyToAccount(PREFUNDED_PRIVATE_KEYS[6]), privateKeyToAccount(PREFUNDED_PRIVATE_KEYS[7]), privateKeyToAccount(PREFUNDED_PRIVATE_KEYS[8]), privateKeyToAccount(PREFUNDED_PRIVATE_KEYS[9]) ]; var PREFUNDED_SEED = Object.freeze({ mnemonic: "test test test test test test test test test test test junk", derivationPath: "m/44'/60'/0'/0/" }); // src/signature.js function recoverPublicKey({ hash, signature }) { const v = signature.yParity !== void 0 ? signature.yParity : signature.v !== void 0 ? signature.v - 27 : (() => { throw new Error("Either v or yParity must be provided in signature"); })(); const rBytes = new Uint8Array(32); const sBytes = new Uint8Array(32); const rBigInt = typeof signature.r === "string" ? BigInt(signature.r) : signature.r; const sBigInt = typeof signature.s === "string" ? BigInt(signature.s) : signature.s; for (let i = 0; i < 32; i++) { rBytes[31 - i] = Number(rBigInt >> BigInt(8 * i) & 0xffn); sBytes[31 - i] = Number(sBigInt >> BigInt(8 * i) & 0xffn); } const publicKey = ecrecover(toBytes(hash), BigInt(v), rBytes, sBytes); if (!publicKey) { throw new Error("Failed to recover public key"); } return `0x04${toHex(publicKey).slice(2)}`; } function recoverAddress({ hash, signature }) { const publicKey = recoverPublicKey({ hash, signature }); const publicKeyBytes = toBytes(publicKey).slice(1); const addressHash = keccak256(publicKeyBytes); return getAddress(`0x${addressHash.slice(-40)}`); } function hashMessage(message) { const prefix = `Ethereum Signed Message: ${message.length}`; return keccak256(toBytes(prefix + message)); } function recoverMessageAddress({ message, signature }) { const hash = hashMessage(message); return recoverAddress({ hash, signature }); } function verifyMessage({ address, message, signature }) { try { const recoveredAddress = recoverMessageAddress({ message, signature }); return recoveredAddress.toLowerCase() === address.toLowerCase(); } catch { return false; } } async function signMessage({ privateKey, message }) { const { signMessage: viemSignMessage } = await import('viem/accounts'); const signature = await viemSignMessage({ privateKey, message }); const v = Number.parseInt(signature.slice(130, 132), 16); const yParity = ( /** @type {0 | 1} */ v - 27 ); return { r: BigInt(signature.slice(0, 66)), // First 32 bytes as hex s: BigInt(`0x${signature.slice(66, 130)}`), // Next 32 bytes as hex v, // Already 27/28 yParity }; } function invariant(condition, error = new DefensiveNullCheckError()) { if (!condition) { throw error; } } export { Bloom, PREFUNDED_ACCOUNTS, PREFUNDED_PRIVATE_KEYS, PREFUNDED_PUBLIC_KEYS, PREFUNDED_SEED, createMemoryDb, hashMessage, invariant, recoverAddress, recoverMessageAddress, recoverPublicKey, signMessage, verifyMessage }; //# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map