diff --git a/config/cspell-ts.json b/config/cspell-ts.json index f990bb5a27a..eb5213d8632 100644 --- a/config/cspell-ts.json +++ b/config/cspell-ts.json @@ -24,6 +24,7 @@ } ], "words": [ + "viem", "immediates", "unerasable", "bytelist", diff --git a/package-lock.json b/package-lock.json index cfe7d534269..b9387c30058 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3514,6 +3514,28 @@ "node": ">=16.5.0" } }, + "node_modules/abitype": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.8.tgz", + "integrity": "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3 >=3.22.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/abort-controller": { "version": "3.0.0", "dev": true, @@ -8073,6 +8095,22 @@ "ws": "*" } }, + "node_modules/isows": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.6.tgz", + "integrity": "sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "dev": true, @@ -9965,6 +10003,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ox": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.6.9.tgz", + "integrity": "sha512-wi5ShvzE4eOcTwQVsIPdFr+8ycyX+5le/96iAJutaZAvCes1J0+RvpEPg5QDPDiaR0XQQAvZVl7AwqQcINuUug==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.10.1", + "@noble/curves": "^1.6.0", + "@noble/hashes": "^1.5.0", + "@scure/bip32": "^1.5.0", + "@scure/bip39": "^1.4.0", + "abitype": "^1.0.6", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/p-defer": { "version": "4.0.1", "dev": true, @@ -12711,6 +12779,37 @@ "node": ">= 0.8" } }, + "node_modules/viem": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.27.2.tgz", + "integrity": "sha512-VwsB+RswcflbwBNPMvzTHuafDA51iT8v4SuIFcudTP2skmxcdodbgoOLP4dYELVnCzcedxoSJDOeext4V3zdnA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@noble/curves": "1.8.1", + "@noble/hashes": "1.7.1", + "@scure/bip32": "1.6.2", + "@scure/bip39": "1.5.4", + "abitype": "1.0.8", + "isows": "1.0.6", + "ox": "0.6.9", + "ws": "8.18.1" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/vite": { "version": "6.3.2", "dev": true, @@ -14047,7 +14146,6 @@ "@types/minimist": "^1.2.5", "@types/node-dir": "^0.0.37", "benchmark": "^2.1.4", - "ethers": "^6.13.5", "mcl-wasm": "^1.8.0", "micro-eth-signer": "^0.15.0", "minimist": "^1.2.8", @@ -14055,6 +14153,7 @@ "nyc": "^17.1.0", "solc": "^0.8.28", "tape": "^5.9.0", + "viem": "^2.27.2", "yargs": "^17.7.2" }, "engines": { diff --git a/packages/vm/examples/helpers/tx-builder.ts b/packages/vm/examples/helpers/tx-builder.ts index 89ade5e7138..1c8c897eecf 100644 --- a/packages/vm/examples/helpers/tx-builder.ts +++ b/packages/vm/examples/helpers/tx-builder.ts @@ -1,37 +1,5 @@ -import { AbiCoder, Interface } from 'ethers' // cspell:disable-line - import type { LegacyTxData } from '@ethereumjs/tx' -export const encodeFunction = ( - method: string, - params?: { - types: any[] - values: unknown[] - }, -): string => { - const parameters = params?.types ?? [] - const methodWithParameters = `function ${method}(${parameters.join(',')})` - const signatureHash = new Interface([methodWithParameters]).getFunction(method)?.selector - const encodedArgs = new AbiCoder().encode(parameters, params?.values ?? []) - - return signatureHash + encodedArgs.slice(2) -} - -export const encodeDeployment = ( - bytecode: string, - params?: { - types: any[] - values: unknown[] - }, -) => { - const deploymentData = '0x' + bytecode - if (params) { - const argumentsEncoded = new AbiCoder().encode(params.types, params.values) - return deploymentData + argumentsEncoded.slice(2) - } - return deploymentData -} - export const buildTransaction = (data: Partial): LegacyTxData => { const defaultData: Partial = { nonce: BigInt(0), diff --git a/packages/vm/examples/run-solidity-contract.ts b/packages/vm/examples/run-solidity-contract.ts index e03448eeba1..5c901d88cd8 100644 --- a/packages/vm/examples/run-solidity-contract.ts +++ b/packages/vm/examples/run-solidity-contract.ts @@ -6,11 +6,11 @@ import { Common, Hardfork, Mainnet } from '@ethereumjs/common' import { createLegacyTx } from '@ethereumjs/tx' import { bytesToHex, createAddressFromPrivateKey, hexToBytes } from '@ethereumjs/util' import { createVM, runTx } from '@ethereumjs/vm' -import { AbiCoder, Interface } from 'ethers' import solc from 'solc' +import { decodeAbiParameters, encodeAbiParameters, encodeFunctionData } from 'viem' import { getAccountNonce, insertAccount } from './helpers/account-utils.ts' -import { buildTransaction, encodeDeployment, encodeFunction } from './helpers/tx-builder.ts' +import { buildTransaction } from './helpers/tx-builder.ts' import type { Address, PrefixedHexString } from '@ethereumjs/util' import type { VM } from '@ethereumjs/vm' @@ -100,10 +100,8 @@ async function deployContract( // Contracts are deployed by sending their deployment bytecode to the address 0 // The contract params should be abi-encoded and appended to the deployment bytecode. - const data = encodeDeployment(deploymentBytecode, { - types: ['string'], - values: [greeting], - }) + const data = + '0x' + deploymentBytecode + encodeAbiParameters([{ type: 'string' }], [greeting]).slice(2) const txData = { data, nonce: await getAccountNonce(vm, senderPrivateKey), @@ -126,9 +124,18 @@ async function setGreeting( contractAddress: Address, greeting: string, ) { - const data = encodeFunction('setGreeting', { - types: ['string'], - values: [greeting], + const data = encodeFunctionData({ + abi: [ + { + type: 'function', + name: 'setGreeting', + inputs: [{ type: 'string' }], + outputs: [], + stateMutability: 'nonpayable', + }, + ], + functionName: 'setGreeting', + args: [greeting], }) const txData = { @@ -147,8 +154,18 @@ async function setGreeting( } async function getGreeting(vm: VM, contractAddress: Address, caller: Address) { - const sigHash = new Interface(['function greet()']).getFunction('greet')! - .selector as PrefixedHexString + const sigHash = encodeFunctionData({ + abi: [ + { + type: 'function', + name: 'greet', + inputs: [], + outputs: [{ type: 'string' }], + stateMutability: 'view', + }, + ], + functionName: 'greet', + }) const greetResult = await vm.evm.runCall({ to: contractAddress, @@ -162,7 +179,7 @@ async function getGreeting(vm: VM, contractAddress: Address, caller: Address) { throw greetResult.execResult.exceptionError } - const results = new AbiCoder().decode(['string'], greetResult.execResult.returnValue) + const results = decodeAbiParameters([{ type: 'string' }], greetResult.execResult.returnValue) return results[0] } diff --git a/packages/vm/package.json b/packages/vm/package.json index aed1d44be26..9176490ac06 100644 --- a/packages/vm/package.json +++ b/packages/vm/package.json @@ -76,13 +76,13 @@ "devDependencies": { "@ethereumjs/blockchain": "^10.0.0", "@ethereumjs/ethash": "^10.0.0", + "@ethereumjs/testdata": "1.0.0", "@paulmillr/trusted-setups": "^0.2.0", "@types/benchmark": "^2.1.5", "@types/core-js": "^2.5.8", "@types/minimist": "^1.2.5", "@types/node-dir": "^0.0.37", "benchmark": "^2.1.4", - "ethers": "^6.13.5", "mcl-wasm": "^1.8.0", "micro-eth-signer": "^0.15.0", "minimist": "^1.2.8", @@ -90,8 +90,8 @@ "nyc": "^17.1.0", "solc": "^0.8.28", "tape": "^5.9.0", - "yargs": "^17.7.2", - "@ethereumjs/testdata": "1.0.0" + "viem": "^2.27.2", + "yargs": "^17.7.2" }, "engines": { "node": ">=18" diff --git a/packages/vm/test/api/customChain.spec.ts b/packages/vm/test/api/customChain.spec.ts index e2972eb912d..3146b87e82b 100644 --- a/packages/vm/test/api/customChain.spec.ts +++ b/packages/vm/test/api/customChain.spec.ts @@ -9,7 +9,7 @@ import { createAddressFromString, hexToBytes, } from '@ethereumjs/util' -import { Interface } from 'ethers' +import { encodeFunctionData } from 'viem' import { assert, describe, it } from 'vitest' import { createVM, runTx } from '../../src/index.ts' @@ -96,7 +96,12 @@ describe('VM initialized with custom state', () => { common.setHardfork(Hardfork.London) const vm = await createVM({ blockchain, common }) await vm.stateManager.generateCanonicalGenesis!(genesisState) - const calldata = new Interface(['function retrieve()']).getFunction('retrieve')!.selector + const calldata = encodeFunctionData({ + abi: [ + { type: 'function', name: 'retrieve', inputs: [], outputs: [], stateMutability: 'view' }, + ], + functionName: 'retrieve', + }) const callResult = await vm.evm.runCall({ to: createAddressFromString(contractAddress),