From e8c9570e67b8f86af7914c71aebdd9434c7c5453 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 27 Feb 2024 19:44:41 -0800 Subject: [PATCH] allow deposit for all EVM addresses --- fvm/evm/evm_test.go | 99 +++++++++++++++++++++++++++ fvm/evm/stdlib/contract.cdc | 14 ++-- fvm/evm/stdlib/contract_test.go | 114 ++++++++++++++++++++++++++++++++ 3 files changed, 223 insertions(+), 4 deletions(-) diff --git a/fvm/evm/evm_test.go b/fvm/evm/evm_test.go index bbb60af2aa1..42c7c01e690 100644 --- a/fvm/evm/evm_test.go +++ b/fvm/evm/evm_test.go @@ -89,6 +89,67 @@ func TestEVMRun(t *testing.T) { } func TestEVMAddressDeposit(t *testing.T) { + + t.Parallel() + chain := flow.Emulator.Chain() + sc := systemcontracts.SystemContractsForChain(chain.ChainID()) + RunWithNewEnvironment(t, + chain, func( + ctx fvm.Context, + vm fvm.VM, + snapshot snapshot.SnapshotTree, + testContract *TestContract, + testAccount *EOATestAccount, + ) { + + code := []byte(fmt.Sprintf( + ` + import EVM from %s + import FlowToken from %s + + transaction(addr: [UInt8; 20]) { + prepare(account: AuthAccount) { + let admin = account.borrow<&FlowToken.Administrator>(from: /storage/flowTokenAdmin)! + let minter <- admin.createNewMinter(allowedAmount: 1.0) + let vault <- minter.mintTokens(amount: 1.0) + destroy minter + + let address = EVM.EVMAddress(addr) + address.deposit(from: <-vault) + } + } + `, + sc.EVMContract.Address.HexWithPrefix(), + sc.FlowToken.Address.HexWithPrefix(), + )) + + addr := RandomAddress(t) + + tx := fvm.Transaction( + flow.NewTransactionBody(). + SetScript(code). + AddAuthorizer(sc.FlowServiceAccount.Address). + AddArgument(json.MustEncode(cadence.NewArray( + ConvertToCadence(addr.Bytes()), + ).WithType(stdlib.EVMAddressBytesCadenceType))), + 0) + + execSnap, output, err := vm.Run( + ctx, + tx, + snapshot) + require.NoError(t, err) + require.NoError(t, output.Err) + + snapshot = snapshot.Append(execSnap) + + expectedBalance := types.OneFlowBalance + bal := getEVMAccountBalance(t, ctx, vm, snapshot, addr) + require.Equal(t, expectedBalance, bal) + }) +} + +func TestCOAAddressDeposit(t *testing.T) { t.Parallel() chain := flow.Emulator.Chain() sc := systemcontracts.SystemContractsForChain(chain.ChainID()) @@ -430,6 +491,44 @@ func TestCadenceArch(t *testing.T) { }) } +func getEVMAccountBalance( + t *testing.T, + ctx fvm.Context, + vm fvm.VM, + snap snapshot.SnapshotTree, + address types.Address, +) types.Balance { + code := []byte(fmt.Sprintf( + ` + import EVM from %s + access(all) + fun main(addr: [UInt8; 20]): UInt { + return EVM.EVMAddress(bytes: addr).balance().inAttoFLOW() + } + `, + systemcontracts.SystemContractsForChain( + ctx.Chain.ChainID(), + ).EVMContract.Address.HexWithPrefix(), + )) + + script := fvm.Script(code).WithArguments( + json.MustEncode( + cadence.NewArray( + ConvertToCadence(address.Bytes()), + ).WithType(stdlib.EVMAddressBytesCadenceType), + ), + ) + _, output, err := vm.Run( + ctx, + script, + snap) + require.NoError(t, err) + require.NoError(t, output.Err) + val, ok := output.Value.(cadence.UInt) + require.True(t, ok) + return val.Big() +} + func RunWithNewEnvironment( t *testing.T, chain flow.Chain, diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index 32221a2d2bc..af3a98390bd 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -52,6 +52,15 @@ contract EVM { address: self.bytes ) } + + /// Deposits the given vault into the EVM account with the given address + access(all) + fun deposit(from: @FlowToken.Vault) { + InternalEVM.deposit( + from: <-from, + to: self.bytes + ) + } } access(all) @@ -201,10 +210,7 @@ contract EVM { /// Deposits the given vault into the cadence owned account's balance access(all) fun deposit(from: @FlowToken.Vault) { - InternalEVM.deposit( - from: <-from, - to: self.addressBytes - ) + self.address().deposit(from: <-from) } /// Withdraws the balance from the cadence owned account's balance diff --git a/fvm/evm/stdlib/contract_test.go b/fvm/evm/stdlib/contract_test.go index 995901cad14..f4d3760aff5 100644 --- a/fvm/evm/stdlib/contract_test.go +++ b/fvm/evm/stdlib/contract_test.go @@ -3143,6 +3143,120 @@ func TestEVMAddressDeposit(t *testing.T) { t.Parallel() + expectedBalanceInUFix64, err := cadence.NewUFix64FromParts(1, 23000000) + require.NoError(t, err) + expectedBalance := types.NewBalanceFromUFix64(expectedBalanceInUFix64) + + 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.Equal( + t, + types.Balance(expectedBalance), + vault.Balance(), + ) + }, + } + }, + } + + contractsAddress := flow.BytesToAddress([]byte{0x1}) + + transactionEnvironment := newEVMTransactionEnvironment(handler, contractsAddress) + scriptEnvironment := newEVMScriptEnvironment(handler, contractsAddress) + + rt := runtime.NewInterpreterRuntime(runtime.Config{}) + + script := []byte(` + import EVM from 0x1 + import FlowToken from 0x1 + + access(all) + fun main() { + let admin = getAuthAccount(0x1) + .borrow<&FlowToken.Administrator>(from: /storage/flowTokenAdmin)! + let minter <- admin.createNewMinter(allowedAmount: 1.23) + let vault <- minter.mintTokens(amount: 1.23) + destroy minter + + 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] + ) + 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: LocationResolver, + 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, + transactionEnvironment, + nextTransactionLocation, + false, + ) + + // Run script + + _, err = rt.ExecuteScript( + runtime.Script{ + Source: script, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: scriptEnvironment, + Location: nextScriptLocation(), + }, + ) + require.NoError(t, err) + + require.True(t, deposited) +} + +func TestCOADeposit(t *testing.T) { + + t.Parallel() + expectedBalance, err := cadence.NewUFix64FromParts(1, 23000000) require.NoError(t, err)