@@ -13,6 +13,7 @@ import { before, describe, it } from "node:test";
1313import { HardhatError } from "@nomicfoundation/hardhat-errors" ;
1414import { assertRejectsWithHardhatError } from "@nomicfoundation/hardhat-test-utils" ;
1515import { mkdtemp } from "@nomicfoundation/hardhat-utils/fs" ;
16+ import { numberToHexString } from "@nomicfoundation/hardhat-utils/hex" ;
1617
1718import { createHardhatRuntimeEnvironment } from "../../../../../src/hre.js" ;
1819import {
@@ -205,6 +206,202 @@ describe("edr-provider", () => {
205206 }
206207 assert . fail ( "Function did not throw any error" ) ;
207208 } ) ;
209+
210+ describe ( "eth_getProof" , ( ) => {
211+ let tmpCacheDir : string ;
212+
213+ before ( async ( ) => {
214+ tmpCacheDir = await mkdtemp ( "edr-provider-eth-getProof" ) ;
215+ } ) ;
216+
217+ it ( "should return account proof on local network" , async ( ) => {
218+ const { provider } = await hre . network . connect ( ) ;
219+
220+ const accounts = await provider . request ( {
221+ method : "eth_accounts" ,
222+ } ) ;
223+
224+ assert . ok (
225+ Array . isArray ( accounts ) && accounts . length > 0 ,
226+ "Accounts should be a non empty array" ,
227+ ) ;
228+
229+ const account = accounts [ 0 ] ;
230+
231+ const proof = await provider . request ( {
232+ method : "eth_getProof" ,
233+ params : [
234+ account ,
235+ [ ] , // storage keys (empty array)
236+ "latest" ,
237+ ] ,
238+ } ) ;
239+
240+ assert . equal (
241+ proof . address ,
242+ account ,
243+ "Address should match the requested account" ,
244+ ) ;
245+
246+ // Default hardhat accounts have 10_000 ETH
247+ assert . equal (
248+ proof . balance ,
249+ numberToHexString ( 10_000n * 10n ** 18n ) ,
250+ "Balance should be 10_000 ETH" ,
251+ ) ;
252+
253+ // Check cryptographic proof structure
254+ assert . ok (
255+ Array . isArray ( proof . accountProof ) && proof . accountProof . length > 0 ,
256+ "accountProof should be a non empty array" ,
257+ ) ;
258+ assert . ok (
259+ Array . isArray ( proof . storageProof ) && proof . storageProof . length === 0 ,
260+ // we passed `[]` as storage keys in the request
261+ "StorageProof should be an empty array for 0 storage keys" ,
262+ ) ;
263+ } ) ;
264+
265+ it ( "should return storage proof for contract on local network" , async ( ) => {
266+ const { provider } = await hre . network . connect ( ) ;
267+
268+ // Define arbitrary address and storage key
269+ const contractAddress = "0x1234567890123456789012345678901234567890" ;
270+ const slotZero =
271+ "0x0000000000000000000000000000000000000000000000000000000000000000" ;
272+ const valueOne =
273+ "0x0000000000000000000000000000000000000000000000000000000000000001" ;
274+
275+ // Set storage directly (bypassing deployment & mining)
276+ await provider . request ( {
277+ method : "hardhat_setStorageAt" ,
278+ params : [ contractAddress , slotZero , valueOne ] ,
279+ } ) ;
280+
281+ const proof = await provider . request ( {
282+ method : "eth_getProof" ,
283+ params : [ contractAddress , [ slotZero ] , "latest" ] ,
284+ } ) ;
285+
286+ assert . equal ( proof . address , contractAddress , "Address should match" ) ;
287+ assert . equal (
288+ proof . storageProof . length ,
289+ 1 ,
290+ "Should return 1 storage proof" ,
291+ ) ;
292+ assert . equal (
293+ proof . storageProof [ 0 ] . key ,
294+ slotZero ,
295+ "Storage key should match" ,
296+ ) ;
297+ assert . equal (
298+ proof . storageProof [ 0 ] . value ,
299+ "0x1" ,
300+ "Storage value should be 0x1" ,
301+ ) ;
302+ assert . ok (
303+ proof . storageProof [ 0 ] . proof . length > 0 ,
304+ "Storage proof should not be empty" ,
305+ ) ;
306+ } ) ;
307+
308+ it ( "should return proof on fork without local changes" , async ( ) => {
309+ // NOTE: Accounts are disabled in the network configuration to prevent local state changes, which would
310+ // cause eth_getProof to fail with a "proof not supported in fork mode" error.
311+
312+ const forkedHre = await createHardhatRuntimeEnvironment ( {
313+ paths : { cache : tmpCacheDir } ,
314+ networks : {
315+ edrOptimism : {
316+ type : "edr-simulated" ,
317+ chainId : 10 ,
318+ chainType : "op" ,
319+ // Disable default accounts to prevent local state modification
320+ accounts : [ ] ,
321+ forking : {
322+ url : "https://mainnet.optimism.io" ,
323+ enabled : true ,
324+ } ,
325+ } ,
326+ } ,
327+ } ) ;
328+
329+ const { provider } = await forkedHre . network . connect ( "edrOptimism" ) ;
330+
331+ try {
332+ // WETH Optimism address
333+ const targetAddress = "0x4200000000000000000000000000000000000006" ;
334+
335+ const proof = await provider . request ( {
336+ method : "eth_getProof" ,
337+ params : [ targetAddress , [ ] , "latest" ] ,
338+ } ) ;
339+
340+ assert . equal (
341+ proof . address ,
342+ targetAddress ,
343+ "Should return proof for the requested address" ,
344+ ) ;
345+ assert . ok (
346+ proof . accountProof . length > 0 ,
347+ "Should have account proof from remote" ,
348+ ) ;
349+ } finally {
350+ await provider . close ( ) ;
351+ }
352+ } ) ;
353+
354+ it ( "should throw error on fork with local changes" , async ( ) => {
355+ // NOTE: Accounts are NOT disabled in the network configuration, so Hardhat modifies the state by
356+ // injecting default accounts, causing eth_getProof to fail with a
357+ // "proof not supported in fork mode" error.
358+
359+ const forkedHre = await createHardhatRuntimeEnvironment ( {
360+ paths : { cache : tmpCacheDir } ,
361+ networks : {
362+ edrOptimism : {
363+ type : "edr-simulated" ,
364+ chainId : 10 ,
365+ chainType : "op" ,
366+ forking : {
367+ url : "https://mainnet.optimism.io" ,
368+ enabled : true ,
369+ } ,
370+ } ,
371+ } ,
372+ } ) ;
373+
374+ const { provider } = await forkedHre . network . connect ( "edrOptimism" ) ;
375+
376+ try {
377+ const accounts = await provider . request ( { method : "eth_accounts" } ) ;
378+ const sender = accounts [ 0 ] ;
379+
380+ // We expect this to fail
381+ await provider . request ( {
382+ method : "eth_getProof" ,
383+ params : [ sender , [ ] , "latest" ] ,
384+ } ) ;
385+ } catch ( error ) {
386+ assert . ok (
387+ ProviderError . isProviderError ( error ) ,
388+ "Error is not a ProviderError" ,
389+ ) ;
390+
391+ assert . match (
392+ error . message ,
393+ / p r o o f i s n o t s u p p o r t e d i n f o r k m o d e w h e n l o c a l c h a n g e s h a v e b e e n m a d e / ,
394+ "Error message should explain lack of support for local blocks on fork" ,
395+ ) ;
396+
397+ return ;
398+ } finally {
399+ await provider . close ( ) ;
400+ }
401+
402+ assert . fail ( "eth_getProof should have thrown an error" ) ;
403+ } ) ;
404+ } ) ;
208405 } ) ;
209406
210407 describe ( "EdrProvider#onSubscriptionEvent" , ( ) => {
0 commit comments