From f45b0d475e24a1e48c1aacc5ff97c86132f0444d Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Fri, 12 Apr 2024 15:46:17 -0500 Subject: [PATCH 01/16] add bridging interface to COA --- fvm/evm/stdlib/contract.cdc | 110 ++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index 71014e83cac..3ddd23fb0b4 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -1,4 +1,6 @@ import Crypto +import "NonFungibleToken" +import "FungibleToken" import "FlowToken" access(all) @@ -301,6 +303,59 @@ contract EVM { value: value.attoflow ) as! Result } + + /// Bridges the given NFT to the EVM environment, requiring a Provider from which to withdraw a fee to fulfill + /// the bridge request + access(all) + fun depositNFT( + nft: @{NonFungibleToken.NFT}, + feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + ) { + EVM.borrowBridgeAccessor().depositNFT(nft: <-nft, to: self.address(), feeProvider: feeProvider) + } + + /// Bridges the given NFT from the EVM environment, requiring a Provider from which to withdraw a fee to fulfill + /// the bridge request. Note: the caller should own the requested NFT in EVM + access(Owner | Bridge) + fun withdrawNFT( + type: Type, + id: UInt256, + feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + ): @{NonFungibleToken.NFT} { + return <- EVM.borrowBridgeAccessor().withdrawNFT( + caller: &self as auth(Call) &CadenceOwnedAccount, + type: type, + id: id, + feeProvider: feeProvider + ) + } + + /// Bridges the given Vault to the EVM environment, requiring a Provider from which to withdraw a fee to fulfill + /// the bridge request + access(all) + fun depositTokens( + vault: @{FungibleToken.Vault}, + feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + ) { + EVM.borrowBridgeAccessor().depositTokens(vault: <-vault, to: self.address(), feeProvider: feeProvider) + } + + /// Bridges the given fungible tokens from the EVM environment, requiring a Provider from which to withdraw a + /// fee to fulfill the bridge request. Note: the caller should own the requested tokens & sufficient balance of + /// requested tokens in EVM + access(Owner | Bridge) + fun withdrawTokens( + type: Type, + amount: UInt256, + feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + ): @{FungibleToken.Vault} { + return <- EVM.borrowBridgeAccessor().withdrawTokens( + caller: &self as auth(Call) &CadenceOwnedAccount, + type: type, + amount: amount, + feeProvider: feeProvider + ) + } } /// Creates a new cadence owned account @@ -500,4 +555,59 @@ contract EVM { fun getLatestBlock(): EVMBlock { return InternalEVM.getLatestBlock() as! EVMBlock } + + /// Interface for a resource which acts as an entrypoint to the VM bridge + access(all) + resource interface BridgeAccessor { + + /// Endpoint enabling the bridging of an NFT to EVM + access(Bridge) + fun depositNFT( + nft: @{NonFungibleToken.NFT}, + to: EVMAddress, + feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + ) + + /// Endpoint enabling the bridging of an NFT from EVM + access(Bridge) + fun withdrawNFT( + caller: auth(Call) &CadenceOwnedAccount, + type: Type, + id: UInt256, + feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + ): @{NonFungibleToken.NFT} + + /// Endpoint enabling the bridging of a fungible token vault to EVM + access(Bridge) + fun depositTokens( + vault: @{FungibleToken.Vault}, + to: EVMAddress, + feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + ) + + /// Endpoint enabling the bridging of fungible tokens from EVM + access(Bridge) + fun withdrawTokens( + caller: auth(Call) &CadenceOwnedAccount, + type: Type, + amount: UInt256, + feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + ): @{FungibleToken.Vault} + } + + /// Interface which captures a Capability to the bridge Accessor, saving it within the BridgeRouter resource + access(all) + resource interface BridgeRouter { + + /// Returns a reference to the BridgeAccessor designated for internal bridge requests + access(Bridge) view fun borrowBridgeAccessor(): auth(Bridge) &{BridgeAccessor} + } + + /// Returns a reference to the BridgeAccessor designated for internal bridge requests + access(self) + view fun borrowBridgeAccessor(): auth(Bridge) &{BridgeAccessor} { + return self.account.storage.borrow(from: /storage/evmBridgeRouter) + ?.borrowBridgeAccessor() + ?? panic("Could not borrow reference to the EVM bridge") + } } From eea11a0241d4758531a8bd4818cfe63cdfcf9380 Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Mon, 15 Apr 2024 16:59:33 -0500 Subject: [PATCH 02/16] fix updated EVM dependency aliasing --- fvm/bootstrap.go | 4 ++-- fvm/evm/stdlib/contract.go | 19 +++++++++++++++---- fvm/evm/stdlib/contract_test.go | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/fvm/bootstrap.go b/fvm/bootstrap.go index 85beee49d50..3f556178a43 100644 --- a/fvm/bootstrap.go +++ b/fvm/bootstrap.go @@ -895,7 +895,7 @@ func (b *bootstrapExecutor) setStakingAllowlist( panicOnMetaInvokeErrf("failed to set staking allow-list: %s", txError, err) } -func (b *bootstrapExecutor) setupEVM(serviceAddress, fungibleTokenAddress, flowTokenAddress flow.Address) { +func (b *bootstrapExecutor) setupEVM(serviceAddress, nonFungibleTokenAddress, fungibleTokenAddress, flowTokenAddress flow.Address) { if b.setupEVMEnabled { // account for storage // we dont need to deploy anything to this account, but it needs to exist @@ -906,7 +906,7 @@ func (b *bootstrapExecutor) setupEVM(serviceAddress, fungibleTokenAddress, flowT // deploy the EVM contract to the service account tx := blueprints.DeployContractTransaction( serviceAddress, - stdlib.ContractCode(flowTokenAddress), + stdlib.ContractCode(nonFungibleTokenAddress, fungibleTokenAddress, flowTokenAddress), stdlib.ContractName, ) // WithEVMEnabled should only be used after we create an account for storage diff --git a/fvm/evm/stdlib/contract.go b/fvm/evm/stdlib/contract.go index 532a327956e..8a06af45871 100644 --- a/fvm/evm/stdlib/contract.go +++ b/fvm/evm/stdlib/contract.go @@ -27,13 +27,24 @@ import ( //go:embed contract.cdc var contractCode string -var flowTokenImportPattern = regexp.MustCompile(`(?m)^import "FlowToken"\n`) +var nftImportPattern = regexp.MustCompile(`(?m)^import "NonFungibleToken"`) +var fungibleTokenImportPattern = regexp.MustCompile(`(?m)^import "FungibleToken"`) +var flowTokenImportPattern = regexp.MustCompile(`(?m)^import "FlowToken"`) -func ContractCode(flowTokenAddress flow.Address) []byte { - return []byte(flowTokenImportPattern.ReplaceAllString( +func ContractCode(nonFungibleTokenAddress, fungibleTokenAddress, flowTokenAddress flow.Address) []byte { + contractCode = nftImportPattern.ReplaceAllString( + contractCode, + fmt.Sprintf("import NonFungibleToken from %s", nonFungibleTokenAddress.HexWithPrefix()), + ) + contractCode = fungibleTokenImportPattern.ReplaceAllString( + contractCode, + fmt.Sprintf("import FungibleToken from %s", fungibleTokenAddress.HexWithPrefix()), + ) + contractCode = flowTokenImportPattern.ReplaceAllString( contractCode, fmt.Sprintf("import FlowToken from %s", flowTokenAddress.HexWithPrefix()), - )) + ) + return []byte(contractCode) } const ContractName = "EVM" diff --git a/fvm/evm/stdlib/contract_test.go b/fvm/evm/stdlib/contract_test.go index 5e3c542288e..2006eeb1d75 100644 --- a/fvm/evm/stdlib/contract_test.go +++ b/fvm/evm/stdlib/contract_test.go @@ -238,7 +238,7 @@ func deployContracts( }, { name: stdlib.ContractName, - code: stdlib.ContractCode(contractsAddress), + code: stdlib.ContractCode(contractsAddress, contractsAddress, contractsAddress), }, } From 4f5de06ba6f5d81320528d19e4df5eee9c5f8e72 Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Mon, 15 Apr 2024 18:18:56 -0500 Subject: [PATCH 03/16] update EVM bridging interfaces for Cadence 0.42 --- fvm/evm/stdlib/contract.cdc | 38 ++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index 3ddd23fb0b4..eabc6dfcb7f 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -309,7 +309,7 @@ contract EVM { access(all) fun depositNFT( nft: @{NonFungibleToken.NFT}, - feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + feeProvider: &{FungibleToken.Provider} ) { EVM.borrowBridgeAccessor().depositNFT(nft: <-nft, to: self.address(), feeProvider: feeProvider) } @@ -320,10 +320,10 @@ contract EVM { fun withdrawNFT( type: Type, id: UInt256, - feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + feeProvider: &{FungibleToken.Provider} ): @{NonFungibleToken.NFT} { return <- EVM.borrowBridgeAccessor().withdrawNFT( - caller: &self as auth(Call) &CadenceOwnedAccount, + caller: &self, type: type, id: id, feeProvider: feeProvider @@ -335,7 +335,7 @@ contract EVM { access(all) fun depositTokens( vault: @{FungibleToken.Vault}, - feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + feeProvider: &{FungibleToken.Provider} ) { EVM.borrowBridgeAccessor().depositTokens(vault: <-vault, to: self.address(), feeProvider: feeProvider) } @@ -347,10 +347,10 @@ contract EVM { fun withdrawTokens( type: Type, amount: UInt256, - feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + feeProvider: &{FungibleToken.Provider} ): @{FungibleToken.Vault} { return <- EVM.borrowBridgeAccessor().withdrawTokens( - caller: &self as auth(Call) &CadenceOwnedAccount, + caller: &self, type: type, amount: amount, feeProvider: feeProvider @@ -561,37 +561,37 @@ contract EVM { resource interface BridgeAccessor { /// Endpoint enabling the bridging of an NFT to EVM - access(Bridge) + access(all) fun depositNFT( nft: @{NonFungibleToken.NFT}, to: EVMAddress, - feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + feeProvider: &{FungibleToken.Provider} ) /// Endpoint enabling the bridging of an NFT from EVM - access(Bridge) + access(all) fun withdrawNFT( - caller: auth(Call) &CadenceOwnedAccount, + caller: &CadenceOwnedAccount, type: Type, id: UInt256, - feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + feeProvider: &{FungibleToken.Provider} ): @{NonFungibleToken.NFT} /// Endpoint enabling the bridging of a fungible token vault to EVM - access(Bridge) + access(all) fun depositTokens( vault: @{FungibleToken.Vault}, to: EVMAddress, - feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + feeProvider: &{FungibleToken.Provider} ) /// Endpoint enabling the bridging of fungible tokens from EVM - access(Bridge) + access(all) fun withdrawTokens( - caller: auth(Call) &CadenceOwnedAccount, + caller: &CadenceOwnedAccount, type: Type, amount: UInt256, - feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + feeProvider: &{FungibleToken.Provider} ): @{FungibleToken.Vault} } @@ -600,13 +600,13 @@ contract EVM { resource interface BridgeRouter { /// Returns a reference to the BridgeAccessor designated for internal bridge requests - access(Bridge) view fun borrowBridgeAccessor(): auth(Bridge) &{BridgeAccessor} + access(all) view fun borrowBridgeAccessor(): &{BridgeAccessor} } /// Returns a reference to the BridgeAccessor designated for internal bridge requests access(self) - view fun borrowBridgeAccessor(): auth(Bridge) &{BridgeAccessor} { - return self.account.storage.borrow(from: /storage/evmBridgeRouter) + view fun borrowBridgeAccessor(): &{BridgeAccessor} { + return self.account.borrow<&{BridgeRouter}>(from: /storage/evmBridgeRouter) ?.borrowBridgeAccessor() ?? panic("Could not borrow reference to the EVM bridge") } From 0f00313c2ee07c8acddfab3b5533dd4d8c0f9c2b Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Mon, 15 Apr 2024 19:01:26 -0500 Subject: [PATCH 04/16] remove entitlement from EVM & fix bootstrap --- fvm/bootstrap.go | 2 +- fvm/evm/stdlib/contract.cdc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fvm/bootstrap.go b/fvm/bootstrap.go index 3f556178a43..5c7e09d31ed 100644 --- a/fvm/bootstrap.go +++ b/fvm/bootstrap.go @@ -434,7 +434,7 @@ func (b *bootstrapExecutor) Execute() error { b.deployStakingCollection(service, &env) // sets up the EVM environment - b.setupEVM(service, fungibleToken, flowToken) + b.setupEVM(service, nonFungibleToken, fungibleToken, flowToken) err = expectAccounts(systemcontracts.EVMStorageAccountIndex) if err != nil { diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index eabc6dfcb7f..068b56ebaab 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -316,7 +316,7 @@ contract EVM { /// Bridges the given NFT from the EVM environment, requiring a Provider from which to withdraw a fee to fulfill /// the bridge request. Note: the caller should own the requested NFT in EVM - access(Owner | Bridge) + access(all) fun withdrawNFT( type: Type, id: UInt256, @@ -343,7 +343,7 @@ contract EVM { /// Bridges the given fungible tokens from the EVM environment, requiring a Provider from which to withdraw a /// fee to fulfill the bridge request. Note: the caller should own the requested tokens & sufficient balance of /// requested tokens in EVM - access(Owner | Bridge) + access(all) fun withdrawTokens( type: Type, amount: UInt256, From b37e4c31a77cc9c171ac818eac2e098a67a4ce4b Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Tue, 16 Apr 2024 11:18:43 -0500 Subject: [PATCH 05/16] fix casting statement in EVM contract --- fvm/evm/stdlib/contract.cdc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index 068b56ebaab..ea83792e17f 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -323,7 +323,7 @@ contract EVM { feeProvider: &{FungibleToken.Provider} ): @{NonFungibleToken.NFT} { return <- EVM.borrowBridgeAccessor().withdrawNFT( - caller: &self, + caller: &self as &CadenceOwnedAccount, type: type, id: id, feeProvider: feeProvider @@ -350,7 +350,7 @@ contract EVM { feeProvider: &{FungibleToken.Provider} ): @{FungibleToken.Vault} { return <- EVM.borrowBridgeAccessor().withdrawTokens( - caller: &self, + caller: &self as &CadenceOwnedAccount, type: type, amount: amount, feeProvider: feeProvider From d07704a962f71c47f3787503646e0900b1d8e0c5 Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Tue, 16 Apr 2024 11:28:44 -0500 Subject: [PATCH 06/16] fix accepted NFT & Vault conformance types in EVM contract --- fvm/evm/stdlib/contract.cdc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index ea83792e17f..4271c3b5896 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -308,7 +308,7 @@ contract EVM { /// the bridge request access(all) fun depositNFT( - nft: @{NonFungibleToken.NFT}, + nft: @NonFungibleToken.NFT, feeProvider: &{FungibleToken.Provider} ) { EVM.borrowBridgeAccessor().depositNFT(nft: <-nft, to: self.address(), feeProvider: feeProvider) @@ -321,7 +321,7 @@ contract EVM { type: Type, id: UInt256, feeProvider: &{FungibleToken.Provider} - ): @{NonFungibleToken.NFT} { + ): @NonFungibleToken.NFT { return <- EVM.borrowBridgeAccessor().withdrawNFT( caller: &self as &CadenceOwnedAccount, type: type, @@ -334,7 +334,7 @@ contract EVM { /// the bridge request access(all) fun depositTokens( - vault: @{FungibleToken.Vault}, + vault: @FungibleToken.Vault, feeProvider: &{FungibleToken.Provider} ) { EVM.borrowBridgeAccessor().depositTokens(vault: <-vault, to: self.address(), feeProvider: feeProvider) @@ -348,7 +348,7 @@ contract EVM { type: Type, amount: UInt256, feeProvider: &{FungibleToken.Provider} - ): @{FungibleToken.Vault} { + ): @FungibleToken.Vault { return <- EVM.borrowBridgeAccessor().withdrawTokens( caller: &self as &CadenceOwnedAccount, type: type, @@ -563,7 +563,7 @@ contract EVM { /// Endpoint enabling the bridging of an NFT to EVM access(all) fun depositNFT( - nft: @{NonFungibleToken.NFT}, + nft: @NonFungibleToken.NFT, to: EVMAddress, feeProvider: &{FungibleToken.Provider} ) @@ -575,12 +575,12 @@ contract EVM { type: Type, id: UInt256, feeProvider: &{FungibleToken.Provider} - ): @{NonFungibleToken.NFT} + ): @NonFungibleToken.NFT /// Endpoint enabling the bridging of a fungible token vault to EVM access(all) fun depositTokens( - vault: @{FungibleToken.Vault}, + vault: @FungibleToken.Vault, to: EVMAddress, feeProvider: &{FungibleToken.Provider} ) @@ -592,7 +592,7 @@ contract EVM { type: Type, amount: UInt256, feeProvider: &{FungibleToken.Provider} - ): @{FungibleToken.Vault} + ): @FungibleToken.Vault } /// Interface which captures a Capability to the bridge Accessor, saving it within the BridgeRouter resource From 46b39cfd685709e871deccd0878065f6a40084aa Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Tue, 16 Apr 2024 11:53:54 -0500 Subject: [PATCH 07/16] fix change_contract_code_migration --- cmd/util/ledger/migrations/change_contract_code_migration.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/util/ledger/migrations/change_contract_code_migration.go b/cmd/util/ledger/migrations/change_contract_code_migration.go index ff4271aa1ff..c327a677e1f 100644 --- a/cmd/util/ledger/migrations/change_contract_code_migration.go +++ b/cmd/util/ledger/migrations/change_contract_code_migration.go @@ -223,6 +223,8 @@ func SystemContractChanges(chainID flow.ChainID, options SystemContractsMigratio NewSystemContractChange( systemContracts.EVMContract, evm.ContractCode( + flow.HexToAddress(env.NonFungibleTokenAddress), + flow.HexToAddress(env.FungibleTokenAddress), flow.HexToAddress(env.FlowTokenAddress), ), ), From 8e04ecbb3a0247f799d47e044229d061bca8ca3b Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Tue, 16 Apr 2024 13:32:34 -0500 Subject: [PATCH 08/16] commit fvm_test.go patch thanks to @m-peter --- fvm/fvm_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fvm/fvm_test.go b/fvm/fvm_test.go index 0dc6433f670..ed111eca870 100644 --- a/fvm/fvm_test.go +++ b/fvm/fvm_test.go @@ -2835,6 +2835,7 @@ func TestEVM(t *testing.T) { t.Run("successful transaction", newVMTest(). withContextOptions( fvm.WithEVMEnabled(true), + fvm.WithChain(flow.Emulator.Chain()), fvm.WithCadenceLogging(true), ). run(func( @@ -2993,6 +2994,7 @@ func TestEVM(t *testing.T) { // so we have to use emulator here so that the EVM storage contract is deployed // to the 5th address fvm.WithChain(flow.Emulator.Chain()), + fvm.WithEVMEnabled(true), ). run(func( t *testing.T, From 9d0eb933a812d0c6d3cda8055a69a0923321a178 Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Tue, 16 Apr 2024 15:31:40 -0500 Subject: [PATCH 09/16] fix stdlib.ContractCode var mutation scope pointed out by @m-peter --- fvm/evm/stdlib/contract.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fvm/evm/stdlib/contract.go b/fvm/evm/stdlib/contract.go index 8a06af45871..2eeee2c811f 100644 --- a/fvm/evm/stdlib/contract.go +++ b/fvm/evm/stdlib/contract.go @@ -32,19 +32,19 @@ var fungibleTokenImportPattern = regexp.MustCompile(`(?m)^import "FungibleToken" var flowTokenImportPattern = regexp.MustCompile(`(?m)^import "FlowToken"`) func ContractCode(nonFungibleTokenAddress, fungibleTokenAddress, flowTokenAddress flow.Address) []byte { - contractCode = nftImportPattern.ReplaceAllString( + evmContract := nftImportPattern.ReplaceAllString( contractCode, fmt.Sprintf("import NonFungibleToken from %s", nonFungibleTokenAddress.HexWithPrefix()), ) - contractCode = fungibleTokenImportPattern.ReplaceAllString( - contractCode, + evmContract = fungibleTokenImportPattern.ReplaceAllString( + evmContract, fmt.Sprintf("import FungibleToken from %s", fungibleTokenAddress.HexWithPrefix()), ) - contractCode = flowTokenImportPattern.ReplaceAllString( - contractCode, + evmContract = flowTokenImportPattern.ReplaceAllString( + evmContract, fmt.Sprintf("import FlowToken from %s", flowTokenAddress.HexWithPrefix()), ) - return []byte(contractCode) + return []byte(evmContract) } const ContractName = "EVM" From f55c14bc1a284b05657227c8935c4c128269187d Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Wed, 17 Apr 2024 12:39:43 -0500 Subject: [PATCH 10/16] add BridgeAccessorUpdated event & BridgeAccessor setter to EVM contract --- fvm/evm/stdlib/contract.cdc | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index 4271c3b5896..a3791ba5785 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -28,6 +28,19 @@ contract EVM { access(all) event FLOWTokensWithdrawn(addressBytes: [UInt8; 20], amount: UFix64) + /// BridgeAccessorUpdated is emitted when the BridgeAccessor Capability + /// is updated in the stored BridgeRouter along with identifying + /// information about both. + access(all) + event BridgeAccessorUpdated( + routerType: Type, + routerUUID: UInt64, + routerAddress: Address, + accessorType: Type, + accessorUUID: UInt64, + accessorAddress: Address + ) + /// EVMAddress is an EVM-compatible address access(all) struct EVMAddress { @@ -601,6 +614,13 @@ contract EVM { /// Returns a reference to the BridgeAccessor designated for internal bridge requests access(all) view fun borrowBridgeAccessor(): &{BridgeAccessor} + + /// Sets the BridgeAccessor Capability in the BridgeRouter + access(all) fun setBridgeAccessor(_ accessor: Capability<&{BridgeAccessor}>) { + pre { + accessor.check(): "Invalid BridgeAccessor Capability provided" + } + } } /// Returns a reference to the BridgeAccessor designated for internal bridge requests From 37ee20c71948275bbf0724226bda1c5168289d85 Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Wed, 17 Apr 2024 16:39:01 -0500 Subject: [PATCH 11/16] fix failing fvm tests --- fvm/fvm_test.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/fvm/fvm_test.go b/fvm/fvm_test.go index ed111eca870..7c1aace4db1 100644 --- a/fvm/fvm_test.go +++ b/fvm/fvm_test.go @@ -2835,7 +2835,10 @@ func TestEVM(t *testing.T) { t.Run("successful transaction", newVMTest(). withContextOptions( fvm.WithEVMEnabled(true), - fvm.WithChain(flow.Emulator.Chain()), + // default is testnet, but testnet has a special EVM storage contract location + // so we have to use emulator here so that the EVM storage contract is deployed + // to the 5th address + fvm.WithChain(flow.Emulator.Chain()), fvm.WithCadenceLogging(true), ). run(func( @@ -2989,13 +2992,8 @@ func TestEVM(t *testing.T) { ) t.Run("deploy contract code", newVMTest(). - withContextOptions( - // default is testnet, but testnet has a special EVM storage contract location - // so we have to use emulator here so that the EVM storage contract is deployed - // to the 5th address - fvm.WithChain(flow.Emulator.Chain()), - fvm.WithEVMEnabled(true), - ). + withBootstrapProcedureOptions(fvm.WithSetupEVMEnabled(true)). + withContextOptions(ctxOpts...). run(func( t *testing.T, vm fvm.VM, From f3b72b295f696057d6749ec30e65d22b56635a19 Mon Sep 17 00:00:00 2001 From: Janez Podhostnik Date: Thu, 18 Apr 2024 17:43:25 +0200 Subject: [PATCH 12/16] cherry-pick fix --- fvm/fvm_test.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/fvm/fvm_test.go b/fvm/fvm_test.go index 7c1aace4db1..8556329be1f 100644 --- a/fvm/fvm_test.go +++ b/fvm/fvm_test.go @@ -2836,9 +2836,9 @@ func TestEVM(t *testing.T) { withContextOptions( fvm.WithEVMEnabled(true), // default is testnet, but testnet has a special EVM storage contract location - // so we have to use emulator here so that the EVM storage contract is deployed - // to the 5th address - fvm.WithChain(flow.Emulator.Chain()), + // so we have to use emulator here so that the EVM storage contract is deployed + // to the 5th address + fvm.WithChain(flow.Emulator.Chain()), fvm.WithCadenceLogging(true), ). run(func( @@ -2993,7 +2993,13 @@ func TestEVM(t *testing.T) { t.Run("deploy contract code", newVMTest(). withBootstrapProcedureOptions(fvm.WithSetupEVMEnabled(true)). - withContextOptions(ctxOpts...). + withContextOptions( + fvm.WithEVMEnabled(true), + // default is testnet, but testnet has a special EVM storage contract location + // so we have to use emulator here so that the EVM storage contract is deployed + // to the 5th address + fvm.WithChain(flow.Emulator.Chain()), + ). run(func( t *testing.T, vm fvm.VM, From f617988ff7979ae8da9054882fc6feb834d7bf61 Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Thu, 18 Apr 2024 12:59:57 -0500 Subject: [PATCH 13/16] update EVM contract with 1.0 bridging interface --- fvm/evm/stdlib/contract.cdc | 69 +++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index a3791ba5785..1130a4c59f0 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -12,6 +12,7 @@ contract EVM { access(all) entitlement Call access(all) entitlement Deploy access(all) entitlement Owner + access(all) entitlement Bridge access(all) event CadenceOwnedAccountCreated(addressBytes: [UInt8; 20]) @@ -321,22 +322,22 @@ contract EVM { /// the bridge request access(all) fun depositNFT( - nft: @NonFungibleToken.NFT, - feeProvider: &{FungibleToken.Provider} + nft: @{NonFungibleToken.NFT}, + feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} ) { EVM.borrowBridgeAccessor().depositNFT(nft: <-nft, to: self.address(), feeProvider: feeProvider) } /// Bridges the given NFT from the EVM environment, requiring a Provider from which to withdraw a fee to fulfill /// the bridge request. Note: the caller should own the requested NFT in EVM - access(all) + access(Owner | Bridge) fun withdrawNFT( type: Type, id: UInt256, - feeProvider: &{FungibleToken.Provider} - ): @NonFungibleToken.NFT { + feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + ): @{NonFungibleToken.NFT} { return <- EVM.borrowBridgeAccessor().withdrawNFT( - caller: &self as &CadenceOwnedAccount, + caller: &self as auth(Call) &CadenceOwnedAccount, type: type, id: id, feeProvider: feeProvider @@ -347,8 +348,8 @@ contract EVM { /// the bridge request access(all) fun depositTokens( - vault: @FungibleToken.Vault, - feeProvider: &{FungibleToken.Provider} + vault: @{FungibleToken.Vault}, + feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} ) { EVM.borrowBridgeAccessor().depositTokens(vault: <-vault, to: self.address(), feeProvider: feeProvider) } @@ -356,14 +357,14 @@ contract EVM { /// Bridges the given fungible tokens from the EVM environment, requiring a Provider from which to withdraw a /// fee to fulfill the bridge request. Note: the caller should own the requested tokens & sufficient balance of /// requested tokens in EVM - access(all) + access(Owner | Bridge) fun withdrawTokens( type: Type, amount: UInt256, - feeProvider: &{FungibleToken.Provider} - ): @FungibleToken.Vault { + feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + ): @{FungibleToken.Vault} { return <- EVM.borrowBridgeAccessor().withdrawTokens( - caller: &self as &CadenceOwnedAccount, + caller: &self as auth(Call) &CadenceOwnedAccount, type: type, amount: amount, feeProvider: feeProvider @@ -574,38 +575,38 @@ contract EVM { resource interface BridgeAccessor { /// Endpoint enabling the bridging of an NFT to EVM - access(all) + access(Bridge) fun depositNFT( - nft: @NonFungibleToken.NFT, + nft: @{NonFungibleToken.NFT}, to: EVMAddress, - feeProvider: &{FungibleToken.Provider} + feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} ) /// Endpoint enabling the bridging of an NFT from EVM - access(all) + access(Bridge) fun withdrawNFT( - caller: &CadenceOwnedAccount, + caller: auth(Call) &CadenceOwnedAccount, type: Type, id: UInt256, - feeProvider: &{FungibleToken.Provider} - ): @NonFungibleToken.NFT + feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + ): @{NonFungibleToken.NFT} /// Endpoint enabling the bridging of a fungible token vault to EVM - access(all) + access(Bridge) fun depositTokens( - vault: @FungibleToken.Vault, + vault: @{FungibleToken.Vault}, to: EVMAddress, - feeProvider: &{FungibleToken.Provider} + feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} ) /// Endpoint enabling the bridging of fungible tokens from EVM - access(all) + access(Bridge) fun withdrawTokens( - caller: &CadenceOwnedAccount, + caller: auth(Call) &CadenceOwnedAccount, type: Type, amount: UInt256, - feeProvider: &{FungibleToken.Provider} - ): @FungibleToken.Vault + feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + ): @{FungibleToken.Vault} } /// Interface which captures a Capability to the bridge Accessor, saving it within the BridgeRouter resource @@ -613,20 +614,28 @@ contract EVM { resource interface BridgeRouter { /// Returns a reference to the BridgeAccessor designated for internal bridge requests - access(all) view fun borrowBridgeAccessor(): &{BridgeAccessor} + access(Bridge) view fun borrowBridgeAccessor(): auth(Bridge) &{BridgeAccessor} /// Sets the BridgeAccessor Capability in the BridgeRouter - access(all) fun setBridgeAccessor(_ accessor: Capability<&{BridgeAccessor}>) { + access(Bridge) fun setBridgeAccessor(_ accessor: Capability) { pre { accessor.check(): "Invalid BridgeAccessor Capability provided" + emit BridgeAccessorUpdated( + routerType: self.getType(), + routerUUID: self.uuid, + routerAddress: self.owner?.address ?? panic("Router must have an owner to be identified"), + accessorType: accessor.borrow()!.getType(), + accessorUUID: accessor.borrow()!.uuid, + accessorAddress: accessor.address + ) } } } /// Returns a reference to the BridgeAccessor designated for internal bridge requests access(self) - view fun borrowBridgeAccessor(): &{BridgeAccessor} { - return self.account.borrow<&{BridgeRouter}>(from: /storage/evmBridgeRouter) + view fun borrowBridgeAccessor(): auth(Bridge) &{BridgeAccessor} { + return self.account.storage.borrow(from: /storage/evmBridgeRouter) ?.borrowBridgeAccessor() ?? panic("Could not borrow reference to the EVM bridge") } From 1777bef5986754eb7e409361781a4fd73d094ac1 Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:52:44 -0500 Subject: [PATCH 14/16] update state commitments --- engine/execution/state/bootstrap/bootstrap_test.go | 2 +- utils/unittest/execution_state.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/execution/state/bootstrap/bootstrap_test.go b/engine/execution/state/bootstrap/bootstrap_test.go index 9f618a43e1e..1780e9848fb 100644 --- a/engine/execution/state/bootstrap/bootstrap_test.go +++ b/engine/execution/state/bootstrap/bootstrap_test.go @@ -53,7 +53,7 @@ func TestBootstrapLedger(t *testing.T) { } func TestBootstrapLedger_ZeroTokenSupply(t *testing.T) { - expectedStateCommitmentBytes, _ := hex.DecodeString("42aae9eb1f9238cac7b0cdb01515fa6df78435ef59c470484a63b74b8eb68911") + expectedStateCommitmentBytes, _ := hex.DecodeString("6815248ba64e2a37bb8674108c83c97a04c12acb3e44ce585d3aab15af0b9d05") expectedStateCommitment, err := flow.ToStateCommitment(expectedStateCommitmentBytes) require.NoError(t, err) diff --git a/utils/unittest/execution_state.go b/utils/unittest/execution_state.go index 96620db0070..5a9c503f87b 100644 --- a/utils/unittest/execution_state.go +++ b/utils/unittest/execution_state.go @@ -23,7 +23,7 @@ const ServiceAccountPrivateKeySignAlgo = crypto.ECDSAP256 const ServiceAccountPrivateKeyHashAlgo = hash.SHA2_256 // Pre-calculated state commitment with root account with the above private key -const GenesisStateCommitmentHex = "5d2ea7f6fe9754d7dcf3f649d1cb8ab3e4aa02f9ed51d88826dd49399c4d12f1" +const GenesisStateCommitmentHex = "8bf21dc9fbef2b74c2b5a7147bdd51358eb97c618c19903ed9c9bf58bc4e7d23" var GenesisStateCommitment flow.StateCommitment From 27d91161ce0a35923dc00538a2f8edc049e0af0b Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Thu, 18 Apr 2024 16:45:08 -0500 Subject: [PATCH 15/16] fix execution tests with updated state commitments --- utils/unittest/execution_state.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/unittest/execution_state.go b/utils/unittest/execution_state.go index 5a9c503f87b..f3931369a2a 100644 --- a/utils/unittest/execution_state.go +++ b/utils/unittest/execution_state.go @@ -87,10 +87,10 @@ func genesisCommitHexByChainID(chainID flow.ChainID) string { return GenesisStateCommitmentHex } if chainID == flow.Testnet { - return "76ab2529c0ca1d67790f4d3cd4834a3d8441492d90c4b06de3ab920b02ac25f9" + return "87f26f515391aac9dca250b614bf85cf0f64d3d1d0626b36d070132af8e188d8" } if chainID == flow.Sandboxnet { return "e1c08b17f9e5896f03fe28dd37ca396c19b26628161506924fbf785834646ea1" } - return "fae892769c66db9c7678fca9ccf33f794abae2bdcff7a6e0b1a04cd72562e61b" + return "9e0b468135e41c50809f6d010e92ad4642fd84799a0915e64b6d8772ec77a49b" } From ae64dbc960f99a2affd85b9ca37bd1959c821d3f Mon Sep 17 00:00:00 2001 From: Janez Podhostnik Date: Fri, 26 Apr 2024 16:44:47 +0200 Subject: [PATCH 16/16] fix test --- engine/execution/state/bootstrap/bootstrap_test.go | 2 +- utils/unittest/execution_state.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/execution/state/bootstrap/bootstrap_test.go b/engine/execution/state/bootstrap/bootstrap_test.go index 3d09a042c78..fabcbc62abb 100644 --- a/engine/execution/state/bootstrap/bootstrap_test.go +++ b/engine/execution/state/bootstrap/bootstrap_test.go @@ -53,7 +53,7 @@ func TestBootstrapLedger(t *testing.T) { } func TestBootstrapLedger_ZeroTokenSupply(t *testing.T) { - expectedStateCommitmentBytes, _ := hex.DecodeString("87002823a7d14ddd8cd71296433ff1fe6ee139a0318dcb7b5fa7a320047ae43b") + expectedStateCommitmentBytes, _ := hex.DecodeString("e6f94b1e9887b475720aa2277300f9838d2c8c7bff619ce27d54be4788d9851e") expectedStateCommitment, err := flow.ToStateCommitment(expectedStateCommitmentBytes) require.NoError(t, err) diff --git a/utils/unittest/execution_state.go b/utils/unittest/execution_state.go index d8724a1d0de..a052b049e2d 100644 --- a/utils/unittest/execution_state.go +++ b/utils/unittest/execution_state.go @@ -23,7 +23,7 @@ const ServiceAccountPrivateKeySignAlgo = crypto.ECDSAP256 const ServiceAccountPrivateKeyHashAlgo = hash.SHA2_256 // Pre-calculated state commitment with root account with the above private key -const GenesisStateCommitmentHex = "3083504c73c70c527903726c7e1811e3f767b8d83da2d0c1aa433cd0c97a4f3b" +const GenesisStateCommitmentHex = "92ca07adaa707b88b5904184bdfa61ec73510095460bb18cae087fc3fd3d5d16" var GenesisStateCommitment flow.StateCommitment @@ -87,10 +87,10 @@ func genesisCommitHexByChainID(chainID flow.ChainID) string { return GenesisStateCommitmentHex } if chainID == flow.Testnet { - return "4bd716d5d5aa783b3020c8e9d9094d11dfd9103a899d50f75a3cb0ce7bc2b83b" + return "b85eb5e6c4c4b011f0d69ce114cd18375d2a50ab3e8cf57665662f2d196b4ded" } if chainID == flow.Sandboxnet { return "e1c08b17f9e5896f03fe28dd37ca396c19b26628161506924fbf785834646ea1" } - return "25854b600d6f84010acb5877842375605340cfc1ba7be9ba7f5830acb5c1b19e" + return "065bc6c49a12fcacb7c7f2289bf4432a02c1057d77f14e0b01c2899f40b4199e" }