diff --git a/fvm/bootstrap.go b/fvm/bootstrap.go index 4dd299fc32a..52a326d0ba6 100644 --- a/fvm/bootstrap.go +++ b/fvm/bootstrap.go @@ -390,7 +390,7 @@ func (b *bootstrapExecutor) Execute() error { b.setStakingAllowlist(service, b.identities.NodeIDs()) // sets up the EVM environment - b.setupEVM(service) + b.setupEVM(service, flowToken) return nil } @@ -788,12 +788,12 @@ func (b *bootstrapExecutor) setStakingAllowlist( panicOnMetaInvokeErrf("failed to set staking allow-list: %s", txError, err) } -func (b *bootstrapExecutor) setupEVM(service flow.Address) { +func (b *bootstrapExecutor) setupEVM(serviceAddress, flowTokenAddress flow.Address) { if b.setupEVMEnabled { b.createAccount(nil) // account for storage tx := blueprints.DeployContractTransaction( - service, - stdlib.ContractCode, + serviceAddress, + stdlib.ContractCode(flowTokenAddress), stdlib.ContractName, ) // WithEVMEnabled should only be used after we create an account for storage diff --git a/fvm/evm/evm.go b/fvm/evm/evm.go index a7b7d5552f6..0bda4dfb767 100644 --- a/fvm/evm/evm.go +++ b/fvm/evm/evm.go @@ -1,8 +1,6 @@ package evm import ( - "fmt" - "github.com/onflow/cadence/runtime" "github.com/onflow/flow-go/fvm/environment" @@ -15,13 +13,7 @@ import ( ) func RootAccountAddress(chainID flow.ChainID) (flow.Address, error) { - // TODO handle other chains - switch chainID { - case flow.Emulator: - return chainID.Chain().AddressAtIndex(environment.EVMAccountIndex) - default: - return flow.Address{}, fmt.Errorf("unsupported chain %s", chainID) - } + return chainID.Chain().AddressAtIndex(environment.EVMAccountIndex) } func SetupEnvironment( diff --git a/fvm/evm/evm_test.go b/fvm/evm/evm_test.go index 2231e8e2a05..9d54d02fc9b 100644 --- a/fvm/evm/evm_test.go +++ b/fvm/evm/evm_test.go @@ -108,3 +108,51 @@ func RunWithNewTestVM(t *testing.T, chain flow.Chain, f func(fvm.Context, fvm.VM f(fvm.NewContextFromParent(ctx, fvm.WithEVMEnabled(true)), vm, snapshotTree) } + +// TODO: test with actual amount +func TestEVMAddressDeposit(t *testing.T) { + + t.Parallel() + + RunWithTestBackend(t, func(backend types.Backend) { + RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { + RunWithDeployedContract(t, backend, rootAddr, func(testContract *TestContract) { + RunWithEOATestAccount(t, backend, rootAddr, func(testAccount *EOATestAccount) { + chain := flow.Emulator.Chain() + RunWithNewTestVM(t, chain, func(ctx fvm.Context, vm fvm.VM, snapshot snapshot.SnapshotTree) { + + code := []byte(fmt.Sprintf( + ` + import EVM from %s + import FlowToken from %s + + access(all) + fun main() { + let address = EVM.EVMAddress( + bytes: [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ) + let vault <- FlowToken.createEmptyVault() as! @FlowToken.Vault + address.deposit(from: <-vault) + } + `, + chain.ServiceAddress().HexWithPrefix(), + fvm.FlowTokenAddress(chain).HexWithPrefix(), + )) + + script := fvm.Script(code) + + executionSnapshot, output, err := vm.Run( + ctx, + script, + snapshot) + require.NoError(t, err) + require.NoError(t, output.Err) + + // TODO: + _ = executionSnapshot + }) + }) + }) + }) + }) +} diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index 8fe8a1b7819..4469353025f 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -1,3 +1,5 @@ +import "FlowToken" + access(all) contract EVM { @@ -9,33 +11,45 @@ contract EVM { access(all) let bytes: [UInt8; 20] - /// Constructs a new EVM address from the given byte representation. + /// Constructs a new EVM address from the given byte representation init(bytes: [UInt8; 20]) { self.bytes = bytes } + + /// Deposits the given vault into the EVM account with the given address + access(all) + fun deposit(from: @FlowToken.Vault) { + let amount = from.balance + destroy from + InternalEVM.deposit( + to: self.bytes, + amount: amount + ) + } } access(all) struct Balance { - /// The balance in FLOW. + /// The balance in FLOW access(all) let flow: UFix64 - /// Constructs a new balance, given the balance in FLOW. + /// Constructs a new balance, given the balance in FLOW init(flow: UFix64) { self.flow = flow } // TODO: // /// Returns the balance in terms of atto-FLOW. - // /// Atto-FLOW is the smallest denomination of FLOW inside EVM. + // /// Atto-FLOW is the smallest denomination of FLOW inside EVM // access(all) // fun toAttoFlow(): UInt64 } access(all) resource BridgedAccount { + access(self) let addressBytes: [UInt8; 20] @@ -50,17 +64,19 @@ contract EVM { return EVMAddress(bytes: self.addressBytes) } + /// Deposits the given vault into the bridged account's balance + access(all) + fun deposit(from: @FlowToken.Vault) { + self.address().deposit(from: <-from) + } + // TODO: - // /// Deposits the given vault into the bridged account's balance - // access(all) - // fun deposit(from: @FlowToken.Vault) - // // /// Withdraws the balance from the bridged account's balance // access(all) // fun withdraw(balance: Balance): @FlowToken.Vault // // /// Deploys a contract to the EVM environment. - // /// Returns the address of the newly deployed contract. + // /// Returns the address of the newly deployed contract // access(all) // fun deploy( // code: [UInt8], @@ -69,7 +85,7 @@ contract EVM { // ): EVMAddress /// Calls a function with the given data. - /// The execution is limited by the given amount of gas. + /// The execution is limited by the given amount of gas access(all) fun call( to: EVMAddress, @@ -99,7 +115,7 @@ contract EVM { /// and deposits the gas fees into the provided coinbase address. /// /// Returns true if the transaction was successful, - /// and returns false otherwise. + /// and returns false otherwise access(all) fun run(tx: [UInt8], coinbase: EVMAddress) { InternalEVM.run(tx: tx, coinbase: coinbase.bytes) diff --git a/fvm/evm/stdlib/contract.go b/fvm/evm/stdlib/contract.go index 678c0775dc3..3c66b97441d 100644 --- a/fvm/evm/stdlib/contract.go +++ b/fvm/evm/stdlib/contract.go @@ -2,6 +2,8 @@ package stdlib import ( _ "embed" + "fmt" + "regexp" "github.com/onflow/cadence" "github.com/onflow/cadence/runtime" @@ -16,7 +18,16 @@ import ( ) //go:embed contract.cdc -var ContractCode []byte +var contractCode string + +var flowTokenImportPattern = regexp.MustCompile(`^import "FlowToken"\n`) + +func ContractCode(flowTokenAddress flow.Address) []byte { + return []byte(flowTokenImportPattern.ReplaceAllString( + contractCode, + fmt.Sprintf("import FlowToken from %s", flowTokenAddress.HexWithPrefix()), + )) +} const ContractName = "EVM" @@ -273,6 +284,65 @@ func newInternalEVMTypeCreateBridgedAccountFunction( ) } +const internalEVMTypeDepositFunctionName = "deposit" + +var internalEVMTypeDepositFunctionType = &sema.FunctionType{ + Parameters: []sema.Parameter{ + { + Label: "to", + TypeAnnotation: sema.NewTypeAnnotation(evmAddressBytesType), + }, + { + Label: "amount", + TypeAnnotation: sema.NewTypeAnnotation(sema.UFix64Type), + }, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.VoidType), +} + +func newInternalEVMTypeDepositFunction( + gauge common.MemoryGauge, + handler types.ContractHandler, +) *interpreter.HostFunctionValue { + return interpreter.NewHostFunctionValue( + gauge, + internalEVMTypeCallFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + inter := invocation.Interpreter + locationRange := invocation.LocationRange + + // Get to address + + toAddressValue, ok := invocation.Arguments[0].(*interpreter.ArrayValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + toAddress, err := AddressBytesArrayValueToEVMAddress(inter, locationRange, toAddressValue) + if err != nil { + panic(err) + } + + // Get amount + + amountValue, ok := invocation.Arguments[1].(interpreter.UFix64Value) + if !ok { + panic(errors.NewUnreachableError()) + } + + amount := types.Balance(amountValue) + + // Call + + const isAuthorized = false + account := handler.AccountByAddress(toAddress, isAuthorized) + account.Deposit(types.NewFlowTokenVault(amount)) + + return interpreter.Void + }, + ) +} + func NewInternalEVMContractValue( gauge common.MemoryGauge, handler types.ContractHandler, @@ -286,6 +356,7 @@ func NewInternalEVMContractValue( internalEVMTypeRunFunctionName: newInternalEVMTypeRunFunction(gauge, handler), internalEVMTypeCreateBridgedAccountFunctionName: newInternalEVMTypeCreateBridgedAccountFunction(gauge, handler), internalEVMTypeCallFunctionName: newInternalEVMTypeCallFunction(gauge, handler), + internalEVMTypeDepositFunctionName: newInternalEVMTypeDepositFunction(gauge, handler), }, nil, nil, @@ -320,6 +391,12 @@ var InternalEVMContractType = func() *sema.CompositeType { internalEVMTypeCallFunctionType, "", ), + sema.NewUnmeteredPublicFunctionMember( + ty, + internalEVMTypeDepositFunctionName, + internalEVMTypeDepositFunctionType, + "", + ), }) return ty }() diff --git a/fvm/evm/stdlib/contract_test.go b/fvm/evm/stdlib/contract_test.go index 4e28936aa77..8be075f1d07 100644 --- a/fvm/evm/stdlib/contract_test.go +++ b/fvm/evm/stdlib/contract_test.go @@ -8,6 +8,7 @@ import ( "github.com/onflow/cadence/encoding/json" "github.com/onflow/cadence/runtime" "github.com/onflow/cadence/runtime/common" + contracts2 "github.com/onflow/flow-core-contracts/lib/go/contracts" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -61,10 +62,12 @@ func (t *testContractHandler) Run(tx []byte, coinbase types.Address) { type testFlowAccount struct { address types.Address - vault *types.FLOWTokenVault + balance func() types.Balance + transfer func(address types.Address, balance types.Balance) + deposit func(vault *types.FLOWTokenVault) + withdraw func(balance types.Balance) *types.FLOWTokenVault deploy func(code types.Code, limit types.GasLimit, balance types.Balance) types.Address call func(address types.Address, data types.Data, limit types.GasLimit, balance types.Balance) types.Data - transfer func(address types.Address, balance types.Balance) } var _ types.Account = &testFlowAccount{} @@ -74,31 +77,31 @@ func (t *testFlowAccount) Address() types.Address { } func (t *testFlowAccount) Balance() types.Balance { - if t.vault == nil { + if t.balance == nil { return types.Balance(0) } - return t.vault.Balance() + return t.balance() } func (t *testFlowAccount) Transfer(address types.Address, balance types.Balance) { if t.transfer == nil { - panic("unexpected Call") + panic("unexpected Transfer") } t.transfer(address, balance) } func (t *testFlowAccount) Deposit(vault *types.FLOWTokenVault) { - if t.vault == nil { - t.vault = types.NewFlowTokenVault(0) + if t.deposit == nil { + panic("unexpected Deposit") } - t.vault.Deposit(vault) + t.deposit(vault) } func (t *testFlowAccount) Withdraw(balance types.Balance) *types.FLOWTokenVault { - if t.vault == nil { - return types.NewFlowTokenVault(0) + if t.withdraw == nil { + panic("unexpected Withdraw") } - return t.vault.Withdraw(balance) + return t.withdraw(balance) } func (t *testFlowAccount) Deploy(code types.Code, limit types.GasLimit, balance types.Balance) types.Address { @@ -115,6 +118,95 @@ func (t *testFlowAccount) Call(address types.Address, data types.Data, limit typ return t.call(address, data, limit, balance) } +func deployContracts( + t *testing.T, + rt runtime.Runtime, + contractsAddress flow.Address, + runtimeInterface *TestRuntimeInterface, + env runtime.Environment, + nextTransactionLocation func() common.TransactionLocation, +) { + + contractsAddressHex := contractsAddress.Hex() + + contracts := []struct { + name string + code []byte + deployTx []byte + }{ + { + name: "FungibleToken", + code: contracts2.FungibleToken(), + }, + { + name: "NonFungibleToken", + code: contracts2.NonFungibleToken(), + }, + { + name: "MetadataViews", + code: contracts2.MetadataViews( + contractsAddressHex, + contractsAddressHex, + ), + }, + { + name: "FungibleTokenMetadataViews", + code: contracts2.FungibleTokenMetadataViews( + contractsAddressHex, + contractsAddressHex, + ), + }, + { + name: "ViewResolver", + code: contracts2.ViewResolver(), + }, + { + name: "FlowToken", + code: contracts2.FlowToken( + contractsAddressHex, + contractsAddressHex, + contractsAddressHex, + ), + deployTx: []byte(` + transaction(name: String, code: String) { + prepare(signer: AuthAccount) { + signer.contracts.add(name: name, code: code.utf8, signer) + } + } + `), + }, + { + name: stdlib.ContractName, + code: stdlib.ContractCode(contractsAddress), + }, + } + + for _, contract := range contracts { + + deployTx := contract.deployTx + if len(deployTx) == 0 { + deployTx = blueprints.DeployContractTransactionTemplate + } + + err := rt.ExecuteTransaction( + runtime.Script{ + Source: deployTx, + Arguments: EncodeArgs([]cadence.Value{ + cadence.String(contract.name), + cadence.String(contract.code), + }), + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: env, + Location: nextTransactionLocation(), + }, + ) + require.NoError(t, err) + } + +} + func TestEVMAddressConstructionAndReturn(t *testing.T) { t.Parallel() @@ -123,9 +215,9 @@ func TestEVMAddressConstructionAndReturn(t *testing.T) { env := runtime.NewBaseInterpreterEnvironment(runtime.Config{}) - contractAddress := flow.BytesToAddress([]byte{0x1}) + contractsAddress := flow.BytesToAddress([]byte{0x1}) - stdlib.SetupEnvironment(env, handler, contractAddress) + stdlib.SetupEnvironment(env, handler, contractsAddress) rt := runtime.NewInterpreterRuntime(runtime.Config{}) @@ -144,7 +236,7 @@ func TestEVMAddressConstructionAndReturn(t *testing.T) { runtimeInterface := &TestRuntimeInterface{ Storage: NewTestLedger(nil, nil), OnGetSigningAccounts: func() ([]runtime.Address, error) { - return []runtime.Address{runtime.Address(contractAddress)}, nil + return []runtime.Address{runtime.Address(contractsAddress)}, nil }, OnResolveLocation: SingleIdentifierLocationResolver(t), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { @@ -180,23 +272,9 @@ func TestEVMAddressConstructionAndReturn(t *testing.T) { nextTransactionLocation := NewTransactionLocationGenerator() nextScriptLocation := NewScriptLocationGenerator() - // Deploy EVM contract + // Deploy contracts - err := rt.ExecuteTransaction( - runtime.Script{ - Source: blueprints.DeployContractTransactionTemplate, - Arguments: EncodeArgs([]cadence.Value{ - cadence.String(stdlib.ContractName), - cadence.String(stdlib.ContractCode), - }), - }, - runtime.Context{ - Interface: runtimeInterface, - Environment: env, - Location: nextTransactionLocation(), - }, - ) - require.NoError(t, err) + deployContracts(t, rt, contractsAddress, runtimeInterface, env, nextTransactionLocation) // Run script @@ -215,7 +293,7 @@ func TestEVMAddressConstructionAndReturn(t *testing.T) { ) require.NoError(t, err) - evmAddressCadenceType := stdlib.NewEVMAddressCadenceType(common.Address(contractAddress)) + evmAddressCadenceType := stdlib.NewEVMAddressCadenceType(common.Address(contractsAddress)) assert.Equal(t, cadence.Struct{ @@ -236,9 +314,9 @@ func TestBalanceConstructionAndReturn(t *testing.T) { env := runtime.NewBaseInterpreterEnvironment(runtime.Config{}) - contractAddress := flow.BytesToAddress([]byte{0x1}) + contractsAddress := flow.BytesToAddress([]byte{0x1}) - stdlib.SetupEnvironment(env, handler, contractAddress) + stdlib.SetupEnvironment(env, handler, contractsAddress) rt := runtime.NewInterpreterRuntime(runtime.Config{}) @@ -257,7 +335,7 @@ func TestBalanceConstructionAndReturn(t *testing.T) { runtimeInterface := &TestRuntimeInterface{ Storage: NewTestLedger(nil, nil), OnGetSigningAccounts: func() ([]runtime.Address, error) { - return []runtime.Address{runtime.Address(contractAddress)}, nil + return []runtime.Address{runtime.Address(contractsAddress)}, nil }, OnResolveLocation: SingleIdentifierLocationResolver(t), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { @@ -280,23 +358,9 @@ func TestBalanceConstructionAndReturn(t *testing.T) { nextTransactionLocation := NewTransactionLocationGenerator() nextScriptLocation := NewScriptLocationGenerator() - // Deploy EVM contract + // Deploy contracts - err := rt.ExecuteTransaction( - runtime.Script{ - Source: blueprints.DeployContractTransactionTemplate, - Arguments: EncodeArgs([]cadence.Value{ - cadence.String(stdlib.ContractName), - cadence.String(stdlib.ContractCode), - }), - }, - runtime.Context{ - Interface: runtimeInterface, - Environment: env, - Location: nextTransactionLocation(), - }, - ) - require.NoError(t, err) + deployContracts(t, rt, contractsAddress, runtimeInterface, env, nextTransactionLocation) // Run script @@ -318,7 +382,7 @@ func TestBalanceConstructionAndReturn(t *testing.T) { ) require.NoError(t, err) - evmBalanceCadenceType := stdlib.NewBalanceCadenceType(common.Address(contractAddress)) + evmBalanceCadenceType := stdlib.NewBalanceCadenceType(common.Address(contractsAddress)) assert.Equal(t, cadence.Struct{ @@ -373,9 +437,9 @@ func TestEVMRun(t *testing.T) { env := runtime.NewBaseInterpreterEnvironment(runtime.Config{}) - contractAddress := flow.BytesToAddress([]byte{0x1}) + contractsAddress := flow.BytesToAddress([]byte{0x1}) - stdlib.SetupEnvironment(env, handler, contractAddress) + stdlib.SetupEnvironment(env, handler, contractsAddress) rt := runtime.NewInterpreterRuntime(runtime.Config{}) @@ -395,7 +459,7 @@ func TestEVMRun(t *testing.T) { runtimeInterface := &TestRuntimeInterface{ Storage: NewTestLedger(nil, nil), OnGetSigningAccounts: func() ([]runtime.Address, error) { - return []runtime.Address{runtime.Address(contractAddress)}, nil + return []runtime.Address{runtime.Address(contractsAddress)}, nil }, OnResolveLocation: SingleIdentifierLocationResolver(t), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { @@ -418,27 +482,13 @@ func TestEVMRun(t *testing.T) { nextTransactionLocation := NewTransactionLocationGenerator() nextScriptLocation := NewScriptLocationGenerator() - // Deploy EVM contract + // Deploy contracts - err := rt.ExecuteTransaction( - runtime.Script{ - Source: blueprints.DeployContractTransactionTemplate, - Arguments: EncodeArgs([]cadence.Value{ - cadence.String(stdlib.ContractName), - cadence.String(stdlib.ContractCode), - }), - }, - runtime.Context{ - Interface: runtimeInterface, - Environment: env, - Location: nextTransactionLocation(), - }, - ) - require.NoError(t, err) + deployContracts(t, rt, contractsAddress, runtimeInterface, env, nextTransactionLocation) // Run script - _, err = rt.ExecuteScript( + _, err := rt.ExecuteScript( runtime.Script{ Source: script, Arguments: EncodeArgs([]cadence.Value{evmTx, coinbase}), @@ -462,9 +512,9 @@ func TestEVMCreateBridgedAccount(t *testing.T) { env := runtime.NewBaseInterpreterEnvironment(runtime.Config{}) - contractAddress := flow.BytesToAddress([]byte{0x1}) + contractsAddress := flow.BytesToAddress([]byte{0x1}) - stdlib.SetupEnvironment(env, handler, contractAddress) + stdlib.SetupEnvironment(env, handler, contractsAddress) rt := runtime.NewInterpreterRuntime(runtime.Config{}) @@ -490,7 +540,7 @@ func TestEVMCreateBridgedAccount(t *testing.T) { runtimeInterface := &TestRuntimeInterface{ Storage: NewTestLedger(nil, nil), OnGetSigningAccounts: func() ([]runtime.Address, error) { - return []runtime.Address{runtime.Address(contractAddress)}, nil + return []runtime.Address{runtime.Address(contractsAddress)}, nil }, OnResolveLocation: SingleIdentifierLocationResolver(t), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { @@ -513,23 +563,9 @@ func TestEVMCreateBridgedAccount(t *testing.T) { nextTransactionLocation := NewTransactionLocationGenerator() nextScriptLocation := NewScriptLocationGenerator() - // Deploy EVM contract + // Deploy contracts - err := rt.ExecuteTransaction( - runtime.Script{ - Source: blueprints.DeployContractTransactionTemplate, - Arguments: EncodeArgs([]cadence.Value{ - cadence.String(stdlib.ContractName), - cadence.String(stdlib.ContractCode), - }), - }, - runtime.Context{ - Interface: runtimeInterface, - Environment: env, - Location: nextTransactionLocation(), - }, - ) - require.NoError(t, err) + deployContracts(t, rt, contractsAddress, runtimeInterface, env, nextTransactionLocation) // Run script @@ -597,9 +633,9 @@ func TestBridgedAccountCall(t *testing.T) { env := runtime.NewBaseInterpreterEnvironment(runtime.Config{}) - contractAddress := flow.BytesToAddress([]byte{0x1}) + contractsAddress := flow.BytesToAddress([]byte{0x1}) - stdlib.SetupEnvironment(env, handler, contractAddress) + stdlib.SetupEnvironment(env, handler, contractsAddress) rt := runtime.NewInterpreterRuntime(runtime.Config{}) @@ -628,7 +664,7 @@ func TestBridgedAccountCall(t *testing.T) { runtimeInterface := &TestRuntimeInterface{ Storage: NewTestLedger(nil, nil), OnGetSigningAccounts: func() ([]runtime.Address, error) { - return []runtime.Address{runtime.Address(contractAddress)}, nil + return []runtime.Address{runtime.Address(contractsAddress)}, nil }, OnResolveLocation: SingleIdentifierLocationResolver(t), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { @@ -651,23 +687,9 @@ func TestBridgedAccountCall(t *testing.T) { nextTransactionLocation := NewTransactionLocationGenerator() nextScriptLocation := NewScriptLocationGenerator() - // Deploy EVM contract + // Deploy contracts - err = rt.ExecuteTransaction( - runtime.Script{ - Source: blueprints.DeployContractTransactionTemplate, - Arguments: EncodeArgs([]cadence.Value{ - cadence.String(stdlib.ContractName), - cadence.String(stdlib.ContractCode), - }), - }, - runtime.Context{ - Interface: runtimeInterface, - Environment: env, - Location: nextTransactionLocation(), - }, - ) - require.NoError(t, err) + deployContracts(t, rt, contractsAddress, runtimeInterface, env, nextTransactionLocation) // Run script @@ -691,3 +713,97 @@ func TestBridgedAccountCall(t *testing.T) { require.Equal(t, expected, actual) } + +func TestEVMAddressDeposit(t *testing.T) { + + t.Parallel() + + var deposited bool + + handler := &testContractHandler{ + + accountByAddress: func(fromAddress types.Address, isAuthorized bool) types.Account { + assert.Equal(t, types.Address{2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, fromAddress) + assert.False(t, isAuthorized) + + return &testFlowAccount{ + address: fromAddress, + deposit: func(vault *types.FLOWTokenVault) { + deposited = true + assert.Zero(t, vault.Balance()) + }, + } + }, + } + + env := runtime.NewBaseInterpreterEnvironment(runtime.Config{}) + + contractsAddress := flow.BytesToAddress([]byte{0x1}) + + stdlib.SetupEnvironment(env, handler, contractsAddress) + + rt := runtime.NewInterpreterRuntime(runtime.Config{}) + + script := []byte(` + import EVM from 0x1 + import FlowToken from 0x1 + + access(all) + fun main() { + let address = EVM.EVMAddress( + bytes: [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ) + let vault <- FlowToken.createEmptyVault() as! @FlowToken.Vault + address.deposit(from: <-vault) + } + `) + + accountCodes := map[common.Location][]byte{} + var events []cadence.Event + + runtimeInterface := &TestRuntimeInterface{ + Storage: NewTestLedger(nil, nil), + OnGetSigningAccounts: func() ([]runtime.Address, error) { + return []runtime.Address{runtime.Address(contractsAddress)}, nil + }, + OnResolveLocation: SingleIdentifierLocationResolver(t), + OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { + accountCodes[location] = code + return nil + }, + OnGetAccountContractCode: func(location common.AddressLocation) (code []byte, err error) { + code = accountCodes[location] + return code, nil + }, + OnEmitEvent: func(event cadence.Event) error { + events = append(events, event) + return nil + }, + OnDecodeArgument: func(b []byte, t cadence.Type) (cadence.Value, error) { + return json.Decode(nil, b) + }, + } + + nextTransactionLocation := NewTransactionLocationGenerator() + nextScriptLocation := NewScriptLocationGenerator() + + // Deploy contracts + + deployContracts(t, rt, contractsAddress, runtimeInterface, env, nextTransactionLocation) + + // Run script + + _, err := rt.ExecuteScript( + runtime.Script{ + Source: script, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: env, + Location: nextScriptLocation(), + }, + ) + require.NoError(t, err) + + require.True(t, deposited) +}