From 83bb106dfd58199ba7c2fe5ca6f9ff4cba3d5b4d Mon Sep 17 00:00:00 2001 From: ramtinms Date: Mon, 11 Mar 2024 12:40:34 -0700 Subject: [PATCH 1/6] add events --- fvm/evm/stdlib/contract.cdc | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index af3a98390bd..c1289a9acc1 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -7,6 +7,18 @@ contract EVM { access(all) event CadenceOwnedAccountCreated(addressBytes: [UInt8; 20]) + /// FLOWTokenDeposit is emitted when FLOW tokens is bridged + /// into the EVM environment. Note that this event is not emitted + /// for transfer of flow tokens between two EVM addresses. + access(all) + event FLOWTokenDeposit(addressBytes: [UInt8; 20], amount: UFix64) + + /// FLOWTokenWithdraw is emitted when FLOW tokens are bridged + /// out of the EVM environment. Note that this event is not emitted + /// for transfer of flow tokens between two EVM addresses. + access(all) + event FLOWTokenWithdraw(addressBytes: [UInt8; 20], amount: UFix64) + /// EVMAddress is an EVM-compatible address access(all) struct EVMAddress { @@ -56,10 +68,12 @@ contract EVM { /// Deposits the given vault into the EVM account with the given address access(all) fun deposit(from: @FlowToken.Vault) { + let amount = from.balance InternalEVM.deposit( from: <-from, to: self.bytes ) + emit FLOWTokenDeposit(addressBytes: self.bytes, amount: amount) } } @@ -224,6 +238,7 @@ contract EVM { from: self.addressBytes, amount: balance.attoflow ) as! @FlowToken.Vault + emit FLOWTokenWithdraw(addressBytes: self.addressBytes, amount: balance.inFLOW) return <-vault } From 4bc17953970557f9593f1a6d59f48ab89a4e51e1 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Mon, 11 Mar 2024 14:22:01 -0700 Subject: [PATCH 2/6] add tests for cadence events --- fvm/evm/stdlib/contract.cdc | 2 +- fvm/evm/stdlib/contract_test.go | 65 +++++++++++++++++++++++++++++++-- fvm/evm/testutils/cadence.go | 9 +++++ 3 files changed, 71 insertions(+), 5 deletions(-) diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index c1289a9acc1..8656159f77d 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -238,7 +238,7 @@ contract EVM { from: self.addressBytes, amount: balance.attoflow ) as! @FlowToken.Vault - emit FLOWTokenWithdraw(addressBytes: self.addressBytes, amount: balance.inFLOW) + emit FLOWTokenWithdraw(addressBytes: self.addressBytes, amount: balance.inFLOW()) return <-vault } diff --git a/fvm/evm/stdlib/contract_test.go b/fvm/evm/stdlib/contract_test.go index cf96de84edc..22765b6e511 100644 --- a/fvm/evm/stdlib/contract_test.go +++ b/fvm/evm/stdlib/contract_test.go @@ -2987,8 +2987,10 @@ func TestEVMCreateCadenceOwnedAccount(t *testing.T) { false, ) - // Run script + // reset events + events = make([]cadence.Event, 0) + // Run script actual, err := rt.ExecuteScript( runtime.Script{ Source: script, @@ -3018,6 +3020,20 @@ func TestEVMCreateCadenceOwnedAccount(t *testing.T) { )) require.Equal(t, expected, actual) + + // check deposit event + expectedEventTypes := []string{ + "EVM.CadenceOwnedAccountCreated", + "EVM.CadenceOwnedAccountCreated", + } + CheckCadenceEventTypes(t, events, expectedEventTypes) + + // check cadence owned account created events + expectedCoaAddress := types.Address{3} + require.Equal(t, expectedCoaAddress.ToCadenceValue().ToGoValue(), events[0].Fields[0].ToGoValue()) + + expectedCoaAddress = types.Address{4} + require.Equal(t, expectedCoaAddress.ToCadenceValue().ToGoValue(), events[1].Fields[0].ToGoValue()) } func TestCadenceOwnedAccountCall(t *testing.T) { @@ -3270,10 +3286,12 @@ func TestCOADeposit(t *testing.T) { var deposited bool + var expectedCoaAddress = types.Address{5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + handler := &testContractHandler{ accountByAddress: func(fromAddress types.Address, isAuthorized bool) types.Account { - assert.Equal(t, types.Address{5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, fromAddress) + assert.Equal(t, expectedCoaAddress, fromAddress) assert.False(t, isAuthorized) return &testFlowAccount{ @@ -3358,6 +3376,9 @@ func TestCOADeposit(t *testing.T) { // Run script + // reset events before script execution + events = make([]cadence.Event, 0) + _, err = rt.ExecuteScript( runtime.Script{ Source: script, @@ -3371,6 +3392,22 @@ func TestCOADeposit(t *testing.T) { require.NoError(t, err) require.True(t, deposited) + + // check deposit event + expectedEventTypes := []string{ + "FlowToken.MinterCreated", + "FlowToken.TokensMinted", + "EVM.CadenceOwnedAccountCreated", + "EVM.FLOWTokenDeposit", + } + CheckCadenceEventTypes(t, events, expectedEventTypes) + + // token deposit event + tokenDepositEvent := events[3] + // check address + require.Equal(t, expectedCoaAddress.ToCadenceValue().ToGoValue(), tokenDepositEvent.Fields[0].ToGoValue()) + // check amount + require.Equal(t, expectedBalance.ToGoValue(), tokenDepositEvent.Fields[1].ToGoValue()) } func TestCadenceOwnedAccountWithdraw(t *testing.T) { @@ -3390,10 +3427,12 @@ func TestCadenceOwnedAccountWithdraw(t *testing.T) { var nextUUID uint64 = 1 + var expectedCoaAddress = types.Address{5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + handler := &testContractHandler{ flowTokenAddress: common.Address(contractsAddress), accountByAddress: func(fromAddress types.Address, isAuthorized bool) types.Account { - assert.Equal(t, types.Address{5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, fromAddress) + assert.Equal(t, expectedCoaAddress, fromAddress) assert.Equal(t, deposited, isAuthorized) return &testFlowAccount{ @@ -3498,8 +3537,9 @@ func TestCadenceOwnedAccountWithdraw(t *testing.T) { false, ) + // reset events + events = make([]cadence.Event, 0) // Run script - result, err := rt.ExecuteScript( runtime.Script{ Source: script, @@ -3517,6 +3557,23 @@ func TestCadenceOwnedAccountWithdraw(t *testing.T) { assert.Equal(t, expectedWithdrawBalance, result) assert.Equal(t, []string{"1"}, logs) + + // check deposit event + expectedEventTypes := []string{ + "FlowToken.MinterCreated", + "FlowToken.TokensMinted", + "EVM.CadenceOwnedAccountCreated", + "EVM.FLOWTokenDeposit", + "EVM.FLOWTokenWithdraw", + } + CheckCadenceEventTypes(t, events, expectedEventTypes) + + // token deposit event + tokenWithdrawEvent := events[4] + // check address + require.Equal(t, expectedCoaAddress.ToCadenceValue().ToGoValue(), tokenWithdrawEvent.Fields[0].ToGoValue()) + // check amount + require.Equal(t, expectedWithdrawBalance.ToGoValue(), tokenWithdrawEvent.Fields[1].ToGoValue()) } func TestCadenceOwnedAccountDeploy(t *testing.T) { diff --git a/fvm/evm/testutils/cadence.go b/fvm/evm/testutils/cadence.go index 577ab740c59..0ec8fd7ca87 100644 --- a/fvm/evm/testutils/cadence.go +++ b/fvm/evm/testutils/cadence.go @@ -9,6 +9,7 @@ import ( "strconv" "strings" "sync/atomic" + "testing" "time" "github.com/onflow/atree" @@ -20,6 +21,7 @@ import ( "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/sema" cadenceStdlib "github.com/onflow/cadence/runtime/stdlib" + "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" ) @@ -715,3 +717,10 @@ func (i *TestRuntimeInterface) InteractionUsed() (uint64, error) { return i.OnInteractionUsed() } + +func CheckCadenceEventTypes(t testing.TB, events []cadence.Event, expectedTypes []string) { + require.Equal(t, len(events), len(expectedTypes)) + for i, ev := range events { + require.Equal(t, expectedTypes[i], ev.EventType.QualifiedIdentifier) + } +} From 3d25ad2dba54286fe47d9020ada748af1370615b Mon Sep 17 00:00:00 2001 From: ramtinms Date: Wed, 20 Mar 2024 12:59:37 -0700 Subject: [PATCH 3/6] fix test --- fvm/fvm_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fvm/fvm_test.go b/fvm/fvm_test.go index 92245acf949..53adf81cd4a 100644 --- a/fvm/fvm_test.go +++ b/fvm/fvm_test.go @@ -3325,7 +3325,7 @@ func TestEVM(t *testing.T) { require.NoError(t, err) require.NoError(t, output.Err) - require.Len(t, output.Events, 6) + require.Len(t, output.Events, 7) evmLocation := types.EVMLocation{} txExe, blockExe := output.Events[4], output.Events[5] From 592c51c48908a39ec0eac63314fd36f9aa22c9c0 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Thu, 21 Mar 2024 11:35:07 -0700 Subject: [PATCH 4/6] apply PR feedbacks --- fvm/evm/stdlib/contract.cdc | 24 ++++++++++++++++++------ fvm/evm/stdlib/contract_test.go | 6 +++--- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index 8656159f77d..6121da98e42 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -7,17 +7,17 @@ contract EVM { access(all) event CadenceOwnedAccountCreated(addressBytes: [UInt8; 20]) - /// FLOWTokenDeposit is emitted when FLOW tokens is bridged + /// FLOWTokensDeposited is emitted when FLOW tokens is bridged /// into the EVM environment. Note that this event is not emitted /// for transfer of flow tokens between two EVM addresses. access(all) - event FLOWTokenDeposit(addressBytes: [UInt8; 20], amount: UFix64) + event FLOWTokensDeposited(addressBytes: [UInt8; 20], amount: UFix64) - /// FLOWTokenWithdraw is emitted when FLOW tokens are bridged + /// FLOWTokensWithdrawn is emitted when FLOW tokens are bridged /// out of the EVM environment. Note that this event is not emitted /// for transfer of flow tokens between two EVM addresses. access(all) - event FLOWTokenWithdraw(addressBytes: [UInt8; 20], amount: UFix64) + event FLOWTokensWithdrawn(addressBytes: [UInt8; 20], amount: UFix64) /// EVMAddress is an EVM-compatible address access(all) @@ -69,11 +69,14 @@ contract EVM { access(all) fun deposit(from: @FlowToken.Vault) { let amount = from.balance + if amount == 0.0 { + panic("calling deposit function with an empty vault is not allowed") + } InternalEVM.deposit( from: <-from, to: self.bytes ) - emit FLOWTokenDeposit(addressBytes: self.bytes, amount: amount) + emit FLOWTokensDeposited(addressBytes: self.bytes, amount: amount) } } @@ -114,6 +117,12 @@ contract EVM { fun inAttoFLOW(): UInt { return self.attoflow } + + /// Returns true if the balance is zero + access(all) + fun isZero(): Bool { + return self.attoflow == 0 + } } /// reports the status of evm execution. @@ -234,11 +243,14 @@ contract EVM { /// rounding error, this function would fail. access(all) fun withdraw(balance: Balance): @FlowToken.Vault { + if balance.isZero() { + panic("calling withdraw function with zero balance is not allowed") + } let vault <- InternalEVM.withdraw( from: self.addressBytes, amount: balance.attoflow ) as! @FlowToken.Vault - emit FLOWTokenWithdraw(addressBytes: self.addressBytes, amount: balance.inFLOW()) + emit FLOWTokensWithdrawn(addressBytes: self.addressBytes, amount: balance.inFLOW()) return <-vault } diff --git a/fvm/evm/stdlib/contract_test.go b/fvm/evm/stdlib/contract_test.go index 22765b6e511..789f98a9033 100644 --- a/fvm/evm/stdlib/contract_test.go +++ b/fvm/evm/stdlib/contract_test.go @@ -3398,7 +3398,7 @@ func TestCOADeposit(t *testing.T) { "FlowToken.MinterCreated", "FlowToken.TokensMinted", "EVM.CadenceOwnedAccountCreated", - "EVM.FLOWTokenDeposit", + "EVM.FLOWTokensDeposited", } CheckCadenceEventTypes(t, events, expectedEventTypes) @@ -3563,8 +3563,8 @@ func TestCadenceOwnedAccountWithdraw(t *testing.T) { "FlowToken.MinterCreated", "FlowToken.TokensMinted", "EVM.CadenceOwnedAccountCreated", - "EVM.FLOWTokenDeposit", - "EVM.FLOWTokenWithdraw", + "EVM.FLOWTokensDeposited", + "EVM.FLOWTokensWithdrawn", } CheckCadenceEventTypes(t, events, expectedEventTypes) From eda7709a31dcb63f6d1bfcb81ed1c386d8cd685d Mon Sep 17 00:00:00 2001 From: ramtinms Date: Thu, 21 Mar 2024 11:48:32 -0700 Subject: [PATCH 5/6] fix handler test --- fvm/evm/handler/handler_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fvm/evm/handler/handler_test.go b/fvm/evm/handler/handler_test.go index b917e3e53a7..3a306696e88 100644 --- a/fvm/evm/handler/handler_test.go +++ b/fvm/evm/handler/handler_test.go @@ -64,7 +64,7 @@ func TestHandler_TransactionRunOrPanic(t *testing.T) { return result, nil }, } - handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, bs, aa, backend, em) + handler := handler.NewContractHandler(flow.Emulator, rootAddr, flowTokenAddress, bs, aa, backend, em) coinbase := types.NewAddress(gethCommon.Address{}) @@ -842,6 +842,6 @@ func SetupHandler(t testing.TB, backend types.Backend, rootAddr flow.Address) *h aa := handler.NewAddressAllocator() emulator := emulator.NewEmulator(backend, rootAddr) - handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, bs, aa, backend, emulator) + handler := handler.NewContractHandler(flow.Emulator, rootAddr, flowTokenAddress, bs, aa, backend, emulator) return handler } From 45d51e26d37342bd41dc3fc077631678fa1d9083 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Thu, 21 Mar 2024 12:16:42 -0700 Subject: [PATCH 6/6] fix test --- fvm/evm/evm_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fvm/evm/evm_test.go b/fvm/evm/evm_test.go index 9f2f34e04de..7d5b0bdc817 100644 --- a/fvm/evm/evm_test.go +++ b/fvm/evm/evm_test.go @@ -888,9 +888,11 @@ func setupCOA( let vaultRef = account.borrow<&FlowToken.Vault>(from: /storage/flowTokenVault) ?? panic("Could not borrow reference to the owner's Vault!") - let vault <- vaultRef.withdraw(amount: amount) as! @FlowToken.Vault - cadenceOwnedAccount1.deposit(from: <-vault) - + if amount > 0.0 { + let vault <- vaultRef.withdraw(amount: amount) as! @FlowToken.Vault + cadenceOwnedAccount1.deposit(from: <-vault) + } + account.save<@EVM.CadenceOwnedAccount>(<-cadenceOwnedAccount1, to: /storage/coa) account.link<&EVM.CadenceOwnedAccount{EVM.Addressable}>(/public/coa,