github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/evm/emulator/emulator_test.go (about)

     1  package emulator_test
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"math/big"
     7  	"testing"
     8  
     9  	gethCommon "github.com/onflow/go-ethereum/common"
    10  	gethTypes "github.com/onflow/go-ethereum/core/types"
    11  	gethVM "github.com/onflow/go-ethereum/core/vm"
    12  	gethParams "github.com/onflow/go-ethereum/params"
    13  	"github.com/stretchr/testify/require"
    14  
    15  	"github.com/onflow/flow-go/fvm/evm/emulator"
    16  	"github.com/onflow/flow-go/fvm/evm/testutils"
    17  	"github.com/onflow/flow-go/fvm/evm/types"
    18  	"github.com/onflow/flow-go/model/flow"
    19  )
    20  
    21  var blockNumber = big.NewInt(10)
    22  var defaultCtx = types.NewDefaultBlockContext(blockNumber.Uint64())
    23  
    24  func RunWithNewEmulator(t testing.TB, backend *testutils.TestBackend, rootAddr flow.Address, f func(*emulator.Emulator)) {
    25  	env := emulator.NewEmulator(backend, rootAddr)
    26  	f(env)
    27  }
    28  
    29  func RunWithNewBlockView(t testing.TB, em *emulator.Emulator, f func(blk types.BlockView)) {
    30  	blk, err := em.NewBlockView(defaultCtx)
    31  	require.NoError(t, err)
    32  	f(blk)
    33  }
    34  
    35  func RunWithNewReadOnlyBlockView(t testing.TB, em *emulator.Emulator, f func(blk types.ReadOnlyBlockView)) {
    36  	blk, err := em.NewReadOnlyBlockView(defaultCtx)
    37  	require.NoError(t, err)
    38  	f(blk)
    39  }
    40  
    41  func TestNativeTokenBridging(t *testing.T) {
    42  	testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
    43  		testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
    44  			originalBalance := big.NewInt(10000)
    45  			testAccount := types.NewAddressFromString("test")
    46  			bridgeAccount := types.NewAddressFromString("bridge")
    47  			nonce := uint64(0)
    48  
    49  			t.Run("mint tokens to the first account", func(t *testing.T) {
    50  				RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
    51  					RunWithNewBlockView(t, env, func(blk types.BlockView) {
    52  						call := types.NewDepositCall(bridgeAccount, testAccount, originalBalance, nonce)
    53  						res, err := blk.DirectCall(call)
    54  						require.NoError(t, err)
    55  						require.Equal(t, defaultCtx.DirectCallBaseGasUsage, res.GasConsumed)
    56  						expectedHash, err := call.Hash()
    57  						require.NoError(t, err)
    58  						require.Equal(t, expectedHash, res.TxHash)
    59  						nonce += 1
    60  					})
    61  				})
    62  				RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
    63  					RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) {
    64  						retBalance, err := blk.BalanceOf(testAccount)
    65  						require.NoError(t, err)
    66  						require.Equal(t, originalBalance, retBalance)
    67  						// check balance of bridgeAccount to be zero
    68  
    69  						retBalance, err = blk.BalanceOf(bridgeAccount)
    70  						require.NoError(t, err)
    71  						require.Equal(t, big.NewInt(0), retBalance)
    72  					})
    73  				})
    74  			})
    75  			t.Run("tokens withdraw", func(t *testing.T) {
    76  				amount := big.NewInt(1000)
    77  				RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
    78  					RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) {
    79  						retBalance, err := blk.BalanceOf(testAccount)
    80  						require.NoError(t, err)
    81  						require.Equal(t, originalBalance, retBalance)
    82  					})
    83  				})
    84  				RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
    85  					RunWithNewBlockView(t, env, func(blk types.BlockView) {
    86  						call := types.NewWithdrawCall(bridgeAccount, testAccount, amount, nonce)
    87  						res, err := blk.DirectCall(call)
    88  						require.NoError(t, err)
    89  						require.Equal(t, defaultCtx.DirectCallBaseGasUsage, res.GasConsumed)
    90  						expectedHash, err := call.Hash()
    91  						require.NoError(t, err)
    92  						require.Equal(t, expectedHash, res.TxHash)
    93  						nonce += 1
    94  					})
    95  				})
    96  				RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
    97  					RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) {
    98  						retBalance, err := blk.BalanceOf(testAccount)
    99  						require.NoError(t, err)
   100  						require.Equal(t, amount.Sub(originalBalance, amount), retBalance)
   101  						// check balance of bridgeAccount to be zero
   102  
   103  						retBalance, err = blk.BalanceOf(bridgeAccount)
   104  						require.NoError(t, err)
   105  						require.Equal(t, big.NewInt(0), retBalance)
   106  					})
   107  				})
   108  			})
   109  		})
   110  	})
   111  }
   112  
   113  func TestContractInteraction(t *testing.T) {
   114  	t.Parallel()
   115  	testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   116  		testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   117  
   118  			testContract := testutils.GetStorageTestContract(t)
   119  
   120  			testAccount := types.NewAddressFromString("test")
   121  			bridgeAccount := types.NewAddressFromString("bridge")
   122  			nonce := uint64(0)
   123  
   124  			amount := big.NewInt(0).Mul(big.NewInt(1337), big.NewInt(gethParams.Ether))
   125  			amountToBeTransfered := big.NewInt(0).Mul(big.NewInt(100), big.NewInt(gethParams.Ether))
   126  
   127  			// fund test account
   128  			RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
   129  				RunWithNewBlockView(t, env, func(blk types.BlockView) {
   130  					_, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, testAccount, amount, nonce))
   131  					require.NoError(t, err)
   132  					nonce += 1
   133  				})
   134  			})
   135  
   136  			var contractAddr types.Address
   137  
   138  			t.Run("deploy contract", func(t *testing.T) {
   139  				RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
   140  					RunWithNewBlockView(t, env, func(blk types.BlockView) {
   141  						call := types.NewDeployCall(
   142  							testAccount,
   143  							testContract.ByteCode,
   144  							math.MaxUint64,
   145  							amountToBeTransfered,
   146  							nonce)
   147  						res, err := blk.DirectCall(call)
   148  						require.NoError(t, err)
   149  						require.NotNil(t, res.DeployedContractAddress)
   150  						contractAddr = *res.DeployedContractAddress
   151  						expectedHash, err := call.Hash()
   152  						require.NoError(t, err)
   153  						require.Equal(t, expectedHash, res.TxHash)
   154  						nonce += 1
   155  					})
   156  					RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) {
   157  						require.NotNil(t, contractAddr)
   158  						retCode, err := blk.CodeOf(contractAddr)
   159  						require.NoError(t, err)
   160  						require.NotEmpty(t, retCode)
   161  
   162  						retBalance, err := blk.BalanceOf(contractAddr)
   163  						require.NoError(t, err)
   164  						require.Equal(t, amountToBeTransfered, retBalance)
   165  
   166  						retBalance, err = blk.BalanceOf(testAccount)
   167  						require.NoError(t, err)
   168  						require.Equal(t, amount.Sub(amount, amountToBeTransfered), retBalance)
   169  					})
   170  				})
   171  			})
   172  
   173  			t.Run("call contract", func(t *testing.T) {
   174  				num := big.NewInt(10)
   175  				RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
   176  					RunWithNewBlockView(t, env, func(blk types.BlockView) {
   177  						res, err := blk.DirectCall(
   178  							types.NewContractCall(
   179  								testAccount,
   180  								contractAddr,
   181  								testContract.MakeCallData(t, "store", num),
   182  								1_000_000,
   183  								big.NewInt(0), // this should be zero because the contract doesn't have receiver
   184  								nonce,
   185  							),
   186  						)
   187  						require.NoError(t, err)
   188  						require.GreaterOrEqual(t, res.GasConsumed, uint64(40_000))
   189  						nonce += 1
   190  					})
   191  				})
   192  
   193  				RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
   194  					RunWithNewBlockView(t, env, func(blk types.BlockView) {
   195  						res, err := blk.DirectCall(
   196  							types.NewContractCall(
   197  								testAccount,
   198  								contractAddr,
   199  								testContract.MakeCallData(t, "retrieve"),
   200  								1_000_000,
   201  								big.NewInt(0), // this should be zero because the contract doesn't have receiver
   202  								nonce,
   203  							),
   204  						)
   205  						require.NoError(t, err)
   206  						nonce += 1
   207  
   208  						ret := new(big.Int).SetBytes(res.ReturnedValue)
   209  						require.Equal(t, num, ret)
   210  						require.GreaterOrEqual(t, res.GasConsumed, uint64(23_000))
   211  					})
   212  				})
   213  
   214  				RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
   215  					RunWithNewBlockView(t, env, func(blk types.BlockView) {
   216  						res, err := blk.DirectCall(
   217  							types.NewContractCall(
   218  								testAccount,
   219  								contractAddr,
   220  								testContract.MakeCallData(t, "blockNumber"),
   221  								1_000_000,
   222  								big.NewInt(0), // this should be zero because the contract doesn't have receiver
   223  								nonce,
   224  							),
   225  						)
   226  						require.NoError(t, err)
   227  						nonce += 1
   228  
   229  						ret := new(big.Int).SetBytes(res.ReturnedValue)
   230  						require.Equal(t, blockNumber, ret)
   231  					})
   232  				})
   233  
   234  				RunWithNewEmulator(t, backend, rootAddr, func(em *emulator.Emulator) {
   235  					ctx := types.NewDefaultBlockContext(blockNumber.Uint64())
   236  					blk, err := em.NewBlockView(ctx)
   237  					require.NoError(t, err)
   238  					res, err := blk.DirectCall(
   239  						types.NewContractCall(
   240  							testAccount,
   241  							contractAddr,
   242  							testContract.MakeCallData(t, "chainID"),
   243  							1_000_000,
   244  							big.NewInt(0), // this should be zero because the contract doesn't have receiver
   245  							nonce,
   246  						),
   247  					)
   248  					require.NoError(t, err)
   249  					nonce += 1
   250  
   251  					ret := new(big.Int).SetBytes(res.ReturnedValue)
   252  					require.Equal(t, types.FlowEVMPreviewNetChainID, ret)
   253  				})
   254  			})
   255  
   256  			t.Run("test sending transactions (happy case)", func(t *testing.T) {
   257  				account := testutils.GetTestEOAAccount(t, testutils.EOATestAccount1KeyHex)
   258  				fAddr := account.Address()
   259  				RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
   260  					RunWithNewBlockView(t, env, func(blk types.BlockView) {
   261  						_, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, fAddr, amount, account.Nonce()))
   262  						require.NoError(t, err)
   263  					})
   264  				})
   265  
   266  				RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
   267  					ctx := types.NewDefaultBlockContext(blockNumber.Uint64())
   268  					ctx.GasFeeCollector = types.NewAddressFromString("coinbase")
   269  					coinbaseOrgBalance := gethCommon.Big1
   270  					// small amount of money to create account
   271  					RunWithNewBlockView(t, env, func(blk types.BlockView) {
   272  						_, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, ctx.GasFeeCollector, coinbaseOrgBalance, 0))
   273  						require.NoError(t, err)
   274  					})
   275  
   276  					blk, err := env.NewBlockView(ctx)
   277  					require.NoError(t, err)
   278  					tx := account.PrepareAndSignTx(
   279  						t,
   280  						testAccount.ToCommon(), // to
   281  						nil,                    // data
   282  						big.NewInt(1000),       // amount
   283  						gethParams.TxGas,       // gas limit
   284  						gethCommon.Big1,        // gas fee
   285  
   286  					)
   287  					res, err := blk.RunTransaction(tx)
   288  					require.NoError(t, err)
   289  					require.NoError(t, res.VMError)
   290  					require.NoError(t, res.ValidationError)
   291  					require.Greater(t, res.GasConsumed, uint64(0))
   292  
   293  					// check the balance of coinbase
   294  					RunWithNewReadOnlyBlockView(t, env, func(blk2 types.ReadOnlyBlockView) {
   295  						bal, err := blk2.BalanceOf(ctx.GasFeeCollector)
   296  						require.NoError(t, err)
   297  						expected := gethParams.TxGas*gethCommon.Big1.Uint64() + gethCommon.Big1.Uint64()
   298  						require.Equal(t, expected, bal.Uint64())
   299  
   300  						nonce, err := blk2.NonceOf(fAddr)
   301  						require.NoError(t, err)
   302  						require.Equal(t, 1, int(nonce))
   303  					})
   304  				})
   305  			})
   306  
   307  			t.Run("test batch running transactions", func(t *testing.T) {
   308  				account := testutils.GetTestEOAAccount(t, testutils.EOATestAccount1KeyHex)
   309  				account.SetNonce(account.Nonce() + 1)
   310  				fAddr := account.Address()
   311  				RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
   312  					RunWithNewBlockView(t, env, func(blk types.BlockView) {
   313  						_, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, fAddr, amount, account.Nonce()))
   314  						require.NoError(t, err)
   315  					})
   316  				})
   317  
   318  				RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
   319  					ctx := types.NewDefaultBlockContext(blockNumber.Uint64())
   320  					ctx.GasFeeCollector = types.NewAddressFromString("coinbase-collector")
   321  					coinbaseOrgBalance := gethCommon.Big1
   322  					// small amount of money to create account
   323  					RunWithNewBlockView(t, env, func(blk types.BlockView) {
   324  						_, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, ctx.GasFeeCollector, coinbaseOrgBalance, 0))
   325  						require.NoError(t, err)
   326  					})
   327  
   328  					blk, err := env.NewBlockView(ctx)
   329  					require.NoError(t, err)
   330  
   331  					const batchSize = 3
   332  					txs := make([]*gethTypes.Transaction, batchSize)
   333  					for i := range txs {
   334  						txs[i] = account.PrepareAndSignTx(
   335  							t,
   336  							testAccount.ToCommon(), // to
   337  							nil,                    // data
   338  							big.NewInt(1000),       // amount
   339  							gethParams.TxGas,       // gas limit
   340  							gethCommon.Big1,        // gas fee
   341  
   342  						)
   343  					}
   344  
   345  					results, err := blk.BatchRunTransactions(txs)
   346  					require.NoError(t, err)
   347  					for _, res := range results {
   348  						require.NoError(t, res.VMError)
   349  						require.NoError(t, res.ValidationError)
   350  						require.Greater(t, res.GasConsumed, uint64(0))
   351  					}
   352  
   353  					// check the balance of coinbase
   354  					RunWithNewReadOnlyBlockView(t, env, func(blk2 types.ReadOnlyBlockView) {
   355  						bal, err := blk2.BalanceOf(ctx.GasFeeCollector)
   356  						require.NoError(t, err)
   357  						expected := gethParams.TxGas*batchSize + gethCommon.Big1.Uint64()
   358  						require.Equal(t, expected, bal.Uint64())
   359  
   360  						nonce, err := blk2.NonceOf(fAddr)
   361  						require.NoError(t, err)
   362  						require.Equal(t, batchSize+1, int(nonce))
   363  					})
   364  				})
   365  			})
   366  
   367  			t.Run("test runing transactions with dynamic fees (happy case)", func(t *testing.T) {
   368  				account := testutils.GetTestEOAAccount(t, testutils.EOATestAccount1KeyHex)
   369  				fAddr := account.Address()
   370  				RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
   371  					RunWithNewBlockView(t, env, func(blk types.BlockView) {
   372  						_, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, fAddr, amount, account.Nonce()))
   373  						require.NoError(t, err)
   374  					})
   375  				})
   376  				account.SetNonce(account.Nonce() + 4)
   377  
   378  				RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
   379  					ctx := types.NewDefaultBlockContext(blockNumber.Uint64())
   380  					ctx.GasFeeCollector = types.NewAddressFromString("coinbase")
   381  					coinbaseOrgBalance := gethCommon.Big1
   382  					// small amount of money to create account
   383  					RunWithNewBlockView(t, env, func(blk types.BlockView) {
   384  						_, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, ctx.GasFeeCollector, coinbaseOrgBalance, 1))
   385  						require.NoError(t, err)
   386  					})
   387  
   388  					blk, err := env.NewBlockView(ctx)
   389  					require.NoError(t, err)
   390  					tx := account.SignTx(
   391  						t,
   392  						gethTypes.NewTx(&gethTypes.DynamicFeeTx{
   393  							ChainID:   types.FlowEVMPreviewNetChainID,
   394  							Nonce:     account.Nonce(),
   395  							GasTipCap: big.NewInt(2),
   396  							GasFeeCap: big.NewInt(3),
   397  							Gas:       gethParams.TxGas,
   398  							To:        &gethCommon.Address{},
   399  							Value:     big.NewInt(1),
   400  						}),
   401  					)
   402  					account.SetNonce(account.Nonce() + 1)
   403  
   404  					res, err := blk.RunTransaction(tx)
   405  					require.NoError(t, err)
   406  					require.NoError(t, res.VMError)
   407  					require.NoError(t, res.ValidationError)
   408  					require.Greater(t, res.GasConsumed, uint64(0))
   409  				})
   410  			})
   411  
   412  			t.Run("test sending transactions (invalid nonce)", func(t *testing.T) {
   413  				account := testutils.GetTestEOAAccount(t, testutils.EOATestAccount1KeyHex)
   414  				fAddr := account.Address()
   415  				RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
   416  					RunWithNewBlockView(t, env, func(blk types.BlockView) {
   417  						_, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, fAddr, amount, account.Nonce()))
   418  						require.NoError(t, err)
   419  					})
   420  				})
   421  
   422  				RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
   423  					ctx := types.NewDefaultBlockContext(blockNumber.Uint64())
   424  					blk, err := env.NewBlockView(ctx)
   425  					require.NoError(t, err)
   426  					tx := account.SignTx(t,
   427  						gethTypes.NewTransaction(
   428  							100,                    // nonce
   429  							testAccount.ToCommon(), // to
   430  							big.NewInt(1000),       // amount
   431  							gethParams.TxGas,       // gas limit
   432  							gethCommon.Big1,        // gas fee
   433  							nil,                    // data
   434  						),
   435  					)
   436  					res, err := blk.RunTransaction(tx)
   437  					require.NoError(t, err)
   438  					require.Error(t, res.ValidationError)
   439  				})
   440  			})
   441  
   442  			t.Run("test sending transactions (bad signature)", func(t *testing.T) {
   443  				RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
   444  					ctx := types.NewDefaultBlockContext(blockNumber.Uint64())
   445  					blk, err := env.NewBlockView(ctx)
   446  					require.NoError(t, err)
   447  					tx := gethTypes.NewTx(&gethTypes.LegacyTx{
   448  						Nonce:    0,
   449  						GasPrice: gethCommon.Big1,
   450  						Gas:      gethParams.TxGas, // gas limit
   451  						To:       nil,              // to
   452  						Value:    big.NewInt(1000), // amount
   453  						Data:     nil,              // data
   454  						V:        big.NewInt(1),
   455  						R:        big.NewInt(2),
   456  						S:        big.NewInt(3),
   457  					})
   458  					res, err := blk.RunTransaction(tx)
   459  					require.NoError(t, err)
   460  					require.Error(t, res.ValidationError)
   461  				})
   462  			})
   463  		})
   464  	})
   465  }
   466  
   467  func TestDeployAtFunctionality(t *testing.T) {
   468  	testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   469  		testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   470  			testContract := testutils.GetStorageTestContract(t)
   471  			testAccount := types.NewAddressFromString("test")
   472  			bridgeAccount := types.NewAddressFromString("bridge")
   473  
   474  			amount := big.NewInt(0).Mul(big.NewInt(1337), big.NewInt(gethParams.Ether))
   475  			amountToBeTransfered := big.NewInt(0).Mul(big.NewInt(100), big.NewInt(gethParams.Ether))
   476  
   477  			// fund test account
   478  			RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
   479  				RunWithNewBlockView(t, env, func(blk types.BlockView) {
   480  					_, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, testAccount, amount, 0))
   481  					require.NoError(t, err)
   482  				})
   483  			})
   484  
   485  			t.Run("deploy contract at target address", func(t *testing.T) {
   486  				RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
   487  					target := types.Address{1, 2, 3}
   488  					RunWithNewBlockView(t, env, func(blk types.BlockView) {
   489  						res, err := blk.DirectCall(
   490  							types.NewDeployCallWithTargetAddress(
   491  								testAccount,
   492  								target,
   493  								testContract.ByteCode,
   494  								math.MaxUint64,
   495  								amountToBeTransfered,
   496  								0,
   497  							),
   498  						)
   499  						require.NoError(t, err)
   500  						require.NotNil(t, res.DeployedContractAddress)
   501  						require.Equal(t, target, *res.DeployedContractAddress)
   502  					})
   503  					RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) {
   504  						require.NotNil(t, target)
   505  						retCode, err := blk.CodeOf(target)
   506  						require.NoError(t, err)
   507  						require.NotEmpty(t, retCode)
   508  
   509  						retBalance, err := blk.BalanceOf(target)
   510  						require.NoError(t, err)
   511  						require.Equal(t, amountToBeTransfered, retBalance)
   512  
   513  						retBalance, err = blk.BalanceOf(testAccount)
   514  						require.NoError(t, err)
   515  						require.Equal(t, amount.Sub(amount, amountToBeTransfered), retBalance)
   516  					})
   517  					// test deployment to an address that is already exist
   518  					RunWithNewBlockView(t, env, func(blk types.BlockView) {
   519  						res, err := blk.DirectCall(
   520  							types.NewDeployCallWithTargetAddress(
   521  								testAccount,
   522  								target,
   523  								testContract.ByteCode,
   524  								math.MaxUint64,
   525  								amountToBeTransfered,
   526  								0),
   527  						)
   528  						require.NoError(t, err)
   529  						require.Equal(t, gethVM.ErrContractAddressCollision, res.VMError)
   530  					})
   531  					// test deployment with not enough gas
   532  					RunWithNewBlockView(t, env, func(blk types.BlockView) {
   533  						res, err := blk.DirectCall(
   534  							types.NewDeployCallWithTargetAddress(
   535  								testAccount,
   536  								types.Address{3, 4, 5},
   537  								testContract.ByteCode,
   538  								100,
   539  								new(big.Int),
   540  								0),
   541  						)
   542  						require.NoError(t, err)
   543  						require.Equal(t, fmt.Errorf("out of gas"), res.VMError)
   544  					})
   545  				})
   546  			})
   547  		})
   548  	})
   549  }
   550  
   551  // Self destruct test deploys a contract with a selfdestruct function
   552  // this function is called and we make sure the balance the contract had
   553  // is returned to the address provided, and the contract data stays according to the
   554  // EIP 6780 https://eips.ethereum.org/EIPS/eip-6780 in case where the selfdestruct
   555  // is not caleld in the same transaction as deployment.
   556  func TestSelfdestruct(t *testing.T) {
   557  	testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   558  		testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   559  			testutils.RunWithEOATestAccount(t, backend, rootAddr, func(testAccount *testutils.EOATestAccount) {
   560  
   561  				testContract := testutils.GetStorageTestContract(t)
   562  				testAddress := types.NewAddressFromString("testaddr")
   563  				bridgeAccount := types.NewAddressFromString("bridge")
   564  
   565  				startBalance := big.NewInt(0).Mul(big.NewInt(1000), big.NewInt(gethParams.Ether))
   566  				deployBalance := big.NewInt(0).Mul(big.NewInt(10), big.NewInt(gethParams.Ether))
   567  				var contractAddr types.Address
   568  
   569  				// setup the test with funded account and deploying a selfdestruct contract.
   570  				RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
   571  					RunWithNewBlockView(t, env, func(blk types.BlockView) {
   572  						_, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, testAddress, startBalance, 0))
   573  						require.NoError(t, err)
   574  					})
   575  
   576  					RunWithNewBlockView(t, env, func(blk types.BlockView) {
   577  						res, err := blk.DirectCall(
   578  							types.NewDeployCall(
   579  								testAddress,
   580  								testContract.ByteCode,
   581  								math.MaxUint64,
   582  								deployBalance,
   583  								0),
   584  						)
   585  						require.NoError(t, err)
   586  						require.NotNil(t, res.DeployedContractAddress)
   587  						contractAddr = *res.DeployedContractAddress
   588  					})
   589  
   590  					RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) {
   591  						bal, err := blk.BalanceOf(testAddress)
   592  						require.NoError(t, err)
   593  						require.Equal(t, big.NewInt(0).Sub(startBalance, deployBalance), bal)
   594  
   595  						bal, err = blk.BalanceOf(contractAddr)
   596  						require.NoError(t, err)
   597  						require.Equal(t, deployBalance, bal)
   598  					})
   599  
   600  					// call the destroy method which executes selfdestruct call.
   601  					RunWithNewBlockView(t, env, func(blk types.BlockView) {
   602  						res, err := blk.DirectCall(&types.DirectCall{
   603  							Type:     types.DirectCallTxType,
   604  							From:     testAddress,
   605  							To:       contractAddr,
   606  							Data:     testContract.MakeCallData(t, "destroy"),
   607  							Value:    big.NewInt(0),
   608  							GasLimit: 100_000,
   609  						})
   610  						require.NoError(t, err)
   611  						require.False(t, res.Failed())
   612  					})
   613  
   614  					// after calling selfdestruct the balance should be returned to the caller and
   615  					// equal initial funded balance of the caller.
   616  					RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) {
   617  						bal, err := blk.BalanceOf(testAddress)
   618  						require.NoError(t, err)
   619  						require.Equal(t, startBalance, bal)
   620  
   621  						bal, err = blk.BalanceOf(contractAddr)
   622  						require.NoError(t, err)
   623  						require.Equal(t, big.NewInt(0), bal)
   624  
   625  						nonce, err := blk.NonceOf(contractAddr)
   626  						require.NoError(t, err)
   627  						require.Equal(t, uint64(1), nonce)
   628  
   629  						code, err := blk.CodeOf(contractAddr)
   630  						require.NoError(t, err)
   631  						require.True(t, len(code) > 0)
   632  					})
   633  				})
   634  			})
   635  		})
   636  	})
   637  }
   638  
   639  func TestTransfers(t *testing.T) {
   640  	testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   641  		testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   642  
   643  			testAccount1 := types.NewAddressFromString("test1")
   644  			testAccount2 := types.NewAddressFromString("test2")
   645  			bridgeAccount := types.NewAddressFromString("bridge")
   646  
   647  			amount := big.NewInt(0).Mul(big.NewInt(1337), big.NewInt(gethParams.Ether))
   648  			amountToBeTransfered := big.NewInt(0).Mul(big.NewInt(100), big.NewInt(gethParams.Ether))
   649  
   650  			RunWithNewEmulator(t, backend, rootAddr, func(em *emulator.Emulator) {
   651  				RunWithNewBlockView(t, em, func(blk types.BlockView) {
   652  					_, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, testAccount1, amount, 0))
   653  					require.NoError(t, err)
   654  				})
   655  			})
   656  
   657  			RunWithNewEmulator(t, backend, rootAddr, func(em *emulator.Emulator) {
   658  				RunWithNewBlockView(t, em, func(blk types.BlockView) {
   659  					_, err := blk.DirectCall(types.NewTransferCall(testAccount1, testAccount2, amountToBeTransfered, 0))
   660  					require.NoError(t, err)
   661  				})
   662  			})
   663  
   664  			RunWithNewEmulator(t, backend, rootAddr, func(em *emulator.Emulator) {
   665  				RunWithNewReadOnlyBlockView(t, em, func(blk types.ReadOnlyBlockView) {
   666  					bal, err := blk.BalanceOf(testAccount2)
   667  					require.NoError(t, err)
   668  					require.Equal(t, amountToBeTransfered.Uint64(), bal.Uint64())
   669  
   670  					bal, err = blk.BalanceOf(testAccount1)
   671  					require.NoError(t, err)
   672  					require.Equal(t, new(big.Int).Sub(amount, amountToBeTransfered).Uint64(), bal.Uint64())
   673  				})
   674  			})
   675  		})
   676  	})
   677  }
   678  
   679  func TestStorageNoSideEffect(t *testing.T) {
   680  	testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   681  		testutils.RunWithTestFlowEVMRootAddress(t, backend, func(flowEVMRoot flow.Address) {
   682  			var err error
   683  			em := emulator.NewEmulator(backend, flowEVMRoot)
   684  			testAccount := types.NewAddressFromString("test")
   685  			bridgeAccount := types.NewAddressFromString("bridge")
   686  
   687  			amount := big.NewInt(10)
   688  			RunWithNewBlockView(t, em, func(blk types.BlockView) {
   689  				_, err = blk.DirectCall(types.NewDepositCall(bridgeAccount, testAccount, amount, 0))
   690  				require.NoError(t, err)
   691  			})
   692  
   693  			orgSize := backend.TotalStorageSize()
   694  			RunWithNewBlockView(t, em, func(blk types.BlockView) {
   695  				_, err = blk.DirectCall(types.NewDepositCall(bridgeAccount, testAccount, amount, 0))
   696  				require.NoError(t, err)
   697  			})
   698  			require.Equal(t, orgSize, backend.TotalStorageSize())
   699  		})
   700  	})
   701  }
   702  
   703  func TestCallingExtraPrecompiles(t *testing.T) {
   704  	testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   705  		testutils.RunWithTestFlowEVMRootAddress(t, backend, func(flowEVMRoot flow.Address) {
   706  			RunWithNewEmulator(t, backend, flowEVMRoot, func(em *emulator.Emulator) {
   707  
   708  				testAccount := types.NewAddressFromString("test")
   709  				bridgeAccount := types.NewAddressFromString("bridge")
   710  				amount := big.NewInt(10_000_000)
   711  				RunWithNewBlockView(t, em, func(blk types.BlockView) {
   712  					_, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, testAccount, amount, 0))
   713  					require.NoError(t, err)
   714  				})
   715  
   716  				input := []byte{1, 2}
   717  				output := []byte{3, 4}
   718  				addr := testutils.RandomAddress(t)
   719  				pc := &MockedPrecompile{
   720  					AddressFunc: func() types.Address {
   721  						return addr
   722  					},
   723  					RequiredGasFunc: func(input []byte) uint64 {
   724  						return uint64(10)
   725  					},
   726  					RunFunc: func(inp []byte) ([]byte, error) {
   727  						require.Equal(t, input, inp)
   728  						return output, nil
   729  					},
   730  				}
   731  
   732  				ctx := types.NewDefaultBlockContext(blockNumber.Uint64())
   733  				ctx.ExtraPrecompiles = []types.Precompile{pc}
   734  
   735  				blk, err := em.NewBlockView(ctx)
   736  				require.NoError(t, err)
   737  
   738  				res, err := blk.DirectCall(
   739  					types.NewContractCall(
   740  						testAccount,
   741  						types.NewAddress(addr.ToCommon()),
   742  						input,
   743  						1_000_000,
   744  						big.NewInt(0), // this should be zero because the contract doesn't have receiver
   745  						0,
   746  					),
   747  				)
   748  				require.NoError(t, err)
   749  				require.Equal(t, output, res.ReturnedValue)
   750  			})
   751  		})
   752  	})
   753  }
   754  
   755  type MockedPrecompile struct {
   756  	AddressFunc     func() types.Address
   757  	RequiredGasFunc func(input []byte) uint64
   758  	RunFunc         func(input []byte) ([]byte, error)
   759  }
   760  
   761  func (mp *MockedPrecompile) Address() types.Address {
   762  	if mp.AddressFunc == nil {
   763  		panic("Address not set for the mocked precompile")
   764  	}
   765  	return mp.AddressFunc()
   766  }
   767  
   768  func (mp *MockedPrecompile) RequiredGas(input []byte) uint64 {
   769  	if mp.RequiredGasFunc == nil {
   770  		panic("RequiredGas not set for the mocked precompile")
   771  	}
   772  	return mp.RequiredGasFunc(input)
   773  }
   774  
   775  func (mp *MockedPrecompile) Run(input []byte) ([]byte, error) {
   776  	if mp.RunFunc == nil {
   777  		panic("Run not set for the mocked precompile")
   778  	}
   779  	return mp.RunFunc(input)
   780  }