From 3f2ac2769ad7f0f96368679419ea3e27241ed085 Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Mon, 15 Apr 2024 20:08:49 -0500 Subject: [PATCH 1/8] add batch onboarding operations --- .../batch_evm_address_requires_onboarding.cdc | 24 +++++++ .../bridge/batch_type_requires_onboarding.cdc | 19 ++++++ .../batch_onboard_by_evm_address.cdc | 65 +++++++++++++++++++ .../onboarding/batch_onboard_by_type.cdc | 60 +++++++++++++++++ .../bridge/onboarding/onboard_by_type.cdc | 10 +-- .../onboarding/onboard_by_type_identifier.cdc | 55 ++++++++++++++++ 6 files changed, 226 insertions(+), 7 deletions(-) create mode 100644 cadence/scripts/bridge/batch_evm_address_requires_onboarding.cdc create mode 100644 cadence/scripts/bridge/batch_type_requires_onboarding.cdc create mode 100644 cadence/transactions/bridge/onboarding/batch_onboard_by_evm_address.cdc create mode 100644 cadence/transactions/bridge/onboarding/batch_onboard_by_type.cdc create mode 100644 cadence/transactions/bridge/onboarding/onboard_by_type_identifier.cdc diff --git a/cadence/scripts/bridge/batch_evm_address_requires_onboarding.cdc b/cadence/scripts/bridge/batch_evm_address_requires_onboarding.cdc new file mode 100644 index 00000000..869de085 --- /dev/null +++ b/cadence/scripts/bridge/batch_evm_address_requires_onboarding.cdc @@ -0,0 +1,24 @@ +import "FlowEVMBridgeUtils" +import "FlowEVMBridge" + +/// Returns whether a EVM contract needs to be onboarded to the FlowEVMBridge +/// +/// @param evmAddresses: Array of hex-encoded address of the EVM contract as a String without 0x prefix to check for +/// onboarding status +/// +/// @return Whether the contract requires onboarding to the FlowEVMBridge if the type is bridgeable, otherwise nil +/// indexed on the hex-encoded address of the EVM contract without 0x prefix +/// +access(all) fun main(evmAddresses: [String]): {String: Bool?} { + let results: {String: Bool?} = {} + for addressHex in evmAddresses { + if results[addressHex] != nil { + continue + } + if let address = FlowEVMBridgeUtils.getEVMAddressFromHexString(address: addressHex) { + let requiresOnboarding = FlowEVMBridge.evmAddressRequiresOnboarding(address) + results.insert(key: addressHex, requiresOnboarding) + } + } + return results +} diff --git a/cadence/scripts/bridge/batch_type_requires_onboarding.cdc b/cadence/scripts/bridge/batch_type_requires_onboarding.cdc new file mode 100644 index 00000000..f9980f8b --- /dev/null +++ b/cadence/scripts/bridge/batch_type_requires_onboarding.cdc @@ -0,0 +1,19 @@ +import "FlowEVMBridge" + +/// Returns whether a type needs to be onboarded to the FlowEVMBridge +/// +/// @param Types: The array of types to check for onboarding status +/// +/// @return Whether the type requires onboarding to the FlowEVMBridge if the type is bridgeable, otherwise nil indexed +/// on the type +/// +access(all) fun main(types: [Type]): {Type: Bool?} { + let results: {Type: Bool?} = {} + for type in types { + if results[type] != nil { + continue + } + results.insert(key: type, FlowEVMBridge.typeRequiresOnboarding(type)) + } + return results +} diff --git a/cadence/transactions/bridge/onboarding/batch_onboard_by_evm_address.cdc b/cadence/transactions/bridge/onboarding/batch_onboard_by_evm_address.cdc new file mode 100644 index 00000000..784fadf1 --- /dev/null +++ b/cadence/transactions/bridge/onboarding/batch_onboard_by_evm_address.cdc @@ -0,0 +1,65 @@ +import "FungibleToken" +import "FlowToken" + +import "ScopedFTProviders" + +import "EVM" + +import "FlowEVMBridge" +import "FlowEVMBridgeUtils" +import "FlowEVMBridgeConfig" + +/// This transaction onboards the NFT type to the bridge, configuring the bridge to move NFTs between environments +/// NOTE: This must be done before bridging a Cadence-native NFT to EVM +/// +/// @param contractAddressHex: Array of EVM contract addresses (as hex string without 0x prefix) defining the +/// bridgeable asset to be onboarded +/// +transaction(addressesAsHex: [String]) { + + let scopedProvider: @ScopedFTProviders.ScopedFTProvider + + prepare(signer: auth(CopyValue, BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue) &Account) { + + /* --- Configure a ScopedFTProvider --- */ + // + // Issue and store bridge-dedicated Provider Capability in storage if necessary + if signer.storage.type(at: FlowEVMBridgeConfig.providerCapabilityStoragePath) == nil { + let providerCap = signer.capabilities.storage.issue( + /storage/flowTokenVault + ) + signer.storage.save(providerCap, to: FlowEVMBridgeConfig.providerCapabilityStoragePath) + } + // Copy the stored Provider capability and create a ScopedFTProvider + let providerCapCopy = signer.storage.copy>( + from: FlowEVMBridgeConfig.providerCapabilityStoragePath + ) ?? panic("Invalid Provider Capability found in storage.") + // Set a withdrawal limit for the provider + let providerLimit = FlowEVMBridgeConfig.onboardFee * UFix64(addressesAsHex.length) + let providerFilter = ScopedFTProviders.AllowanceFilter(providerLimit) + // Create ScopedFTProvider to expire just after this transaction + self.scopedProvider <- ScopedFTProviders.createScopedFTProvider( + provider: providerCapCopy, + filters: [ providerFilter ], + expiration: getCurrentBlock().timestamp + 1.0 + ) + } + + execute { + // Iterate over provided array + for addressHex in addressesAsHex { + // Convert hex string to EVMAddress + let address = FlowEVMBridgeUtils.getEVMAddressFromHexString(address: addressHex) + // Continue if the hex is not a valid EVM address or if the address is already onboarded + if address == nil || FlowEVMBridge.evmAddressRequiresOnboarding(address!) != true { + continue + } + + FlowEVMBridge.onboardByEVMAddress( + address!, + feeProvider: &self.scopedProvider as auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + ) + } + destroy self.scopedProvider + } +} diff --git a/cadence/transactions/bridge/onboarding/batch_onboard_by_type.cdc b/cadence/transactions/bridge/onboarding/batch_onboard_by_type.cdc new file mode 100644 index 00000000..e3f0c49c --- /dev/null +++ b/cadence/transactions/bridge/onboarding/batch_onboard_by_type.cdc @@ -0,0 +1,60 @@ +import "FungibleToken" +import "FlowToken" + +import "ScopedFTProviders" + +import "EVM" + +import "FlowEVMBridge" +import "FlowEVMBridgeConfig" + +/// This transaction onboards the asset type to the bridge, configuring the bridge to move assets between environments +/// NOTE: This must be done before bridging a Cadence-native asset to EVM +/// +/// @param identifer: The Cadence type identifier of the bridgeable asset to onboarded to the bridge +/// +transaction(types: [Type]) { + + let scopedProvider: @ScopedFTProviders.ScopedFTProvider + + prepare(signer: auth(CopyValue, BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue) &Account) { + + /* --- Configure a ScopedFTProvider --- */ + // + // Issue and store bridge-dedicated Provider Capability in storage if necessary + if signer.storage.type(at: FlowEVMBridgeConfig.providerCapabilityStoragePath) == nil { + let providerCap = signer.capabilities.storage.issue( + /storage/flowTokenVault + ) + signer.storage.save(providerCap, to: FlowEVMBridgeConfig.providerCapabilityStoragePath) + } + // Copy the stored Provider capability and create a ScopedFTProvider + let providerCapCopy = signer.storage.copy>( + from: FlowEVMBridgeConfig.providerCapabilityStoragePath + ) ?? panic("Invalid Provider Capability found in storage.") + // Set a withdrawal limit for the provider + let providerLimit = FlowEVMBridgeConfig.onboardFee * UFix64(types.length) + let providerFilter = ScopedFTProviders.AllowanceFilter(providerLimit) + // Create ScopedFTProvider to expire just after this transaction + self.scopedProvider <- ScopedFTProviders.createScopedFTProvider( + provider: providerCapCopy, + filters: [ providerFilter ], + expiration: getCurrentBlock().timestamp + 1.0 + ) + } + + execute { + for type in types { + + if FlowEVMBridge.typeRequiresOnboarding(type) != true { + continue + } + // Onboard the asset Type + FlowEVMBridge.onboardByType( + self.type, + feeProvider: &self.scopedProvider as auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + ) + } + destroy self.scopedProvider + } +} diff --git a/cadence/transactions/bridge/onboarding/onboard_by_type.cdc b/cadence/transactions/bridge/onboarding/onboard_by_type.cdc index 45ecbb01..26c852c7 100644 --- a/cadence/transactions/bridge/onboarding/onboard_by_type.cdc +++ b/cadence/transactions/bridge/onboarding/onboard_by_type.cdc @@ -11,17 +11,13 @@ import "FlowEVMBridgeConfig" /// This transaction onboards the asset type to the bridge, configuring the bridge to move assets between environments /// NOTE: This must be done before bridging a Cadence-native asset to EVM /// -/// @param identifer: The Cadence type identifier of the bridgeable asset to onboarded to the bridge +/// @param type: The Cadence type of the bridgeable asset to onboard to the bridge /// -transaction(identifier: String) { +transaction(type: Type) { - let type: Type let scopedProvider: @ScopedFTProviders.ScopedFTProvider prepare(signer: auth(CopyValue, BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue) &Account) { - /* --- Construct the type from identifier --- */ - // - self.type = CompositeType(identifier) ?? panic("Invalid type identifier") /* --- Configure a ScopedFTProvider --- */ // @@ -47,7 +43,7 @@ transaction(identifier: String) { execute { // Onboard the asset Type FlowEVMBridge.onboardByType( - self.type, + type, feeProvider: &self.scopedProvider as auth(FungibleToken.Withdraw) &{FungibleToken.Provider} ) destroy self.scopedProvider diff --git a/cadence/transactions/bridge/onboarding/onboard_by_type_identifier.cdc b/cadence/transactions/bridge/onboarding/onboard_by_type_identifier.cdc new file mode 100644 index 00000000..45ecbb01 --- /dev/null +++ b/cadence/transactions/bridge/onboarding/onboard_by_type_identifier.cdc @@ -0,0 +1,55 @@ +import "FungibleToken" +import "FlowToken" + +import "ScopedFTProviders" + +import "EVM" + +import "FlowEVMBridge" +import "FlowEVMBridgeConfig" + +/// This transaction onboards the asset type to the bridge, configuring the bridge to move assets between environments +/// NOTE: This must be done before bridging a Cadence-native asset to EVM +/// +/// @param identifer: The Cadence type identifier of the bridgeable asset to onboarded to the bridge +/// +transaction(identifier: String) { + + let type: Type + let scopedProvider: @ScopedFTProviders.ScopedFTProvider + + prepare(signer: auth(CopyValue, BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue) &Account) { + /* --- Construct the type from identifier --- */ + // + self.type = CompositeType(identifier) ?? panic("Invalid type identifier") + + /* --- Configure a ScopedFTProvider --- */ + // + // Issue and store bridge-dedicated Provider Capability in storage if necessary + if signer.storage.type(at: FlowEVMBridgeConfig.providerCapabilityStoragePath) == nil { + let providerCap = signer.capabilities.storage.issue( + /storage/flowTokenVault + ) + signer.storage.save(providerCap, to: FlowEVMBridgeConfig.providerCapabilityStoragePath) + } + // Copy the stored Provider capability and create a ScopedFTProvider + let providerCapCopy = signer.storage.copy>( + from: FlowEVMBridgeConfig.providerCapabilityStoragePath + ) ?? panic("Invalid Provider Capability found in storage.") + let providerFilter = ScopedFTProviders.AllowanceFilter(FlowEVMBridgeConfig.onboardFee) + self.scopedProvider <- ScopedFTProviders.createScopedFTProvider( + provider: providerCapCopy, + filters: [ providerFilter ], + expiration: getCurrentBlock().timestamp + 1.0 + ) + } + + execute { + // Onboard the asset Type + FlowEVMBridge.onboardByType( + self.type, + feeProvider: &self.scopedProvider as auth(FungibleToken.Withdraw) &{FungibleToken.Provider} + ) + destroy self.scopedProvider + } +} From 09357501f5d69eb7a999d5a9cdd8fe5ea3c084b3 Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Mon, 15 Apr 2024 20:12:39 -0500 Subject: [PATCH 2/8] update txn comments --- .../bridge/onboarding/batch_onboard_by_evm_address.cdc | 2 +- .../bridge/onboarding/batch_onboard_by_type.cdc | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cadence/transactions/bridge/onboarding/batch_onboard_by_evm_address.cdc b/cadence/transactions/bridge/onboarding/batch_onboard_by_evm_address.cdc index 784fadf1..a1c47059 100644 --- a/cadence/transactions/bridge/onboarding/batch_onboard_by_evm_address.cdc +++ b/cadence/transactions/bridge/onboarding/batch_onboard_by_evm_address.cdc @@ -12,7 +12,7 @@ import "FlowEVMBridgeConfig" /// This transaction onboards the NFT type to the bridge, configuring the bridge to move NFTs between environments /// NOTE: This must be done before bridging a Cadence-native NFT to EVM /// -/// @param contractAddressHex: Array of EVM contract addresses (as hex string without 0x prefix) defining the +/// @param addressesAsHex: Array of EVM contract addresses (as hex string without 0x prefix) defining the /// bridgeable asset to be onboarded /// transaction(addressesAsHex: [String]) { diff --git a/cadence/transactions/bridge/onboarding/batch_onboard_by_type.cdc b/cadence/transactions/bridge/onboarding/batch_onboard_by_type.cdc index e3f0c49c..55d9d889 100644 --- a/cadence/transactions/bridge/onboarding/batch_onboard_by_type.cdc +++ b/cadence/transactions/bridge/onboarding/batch_onboard_by_type.cdc @@ -11,7 +11,7 @@ import "FlowEVMBridgeConfig" /// This transaction onboards the asset type to the bridge, configuring the bridge to move assets between environments /// NOTE: This must be done before bridging a Cadence-native asset to EVM /// -/// @param identifer: The Cadence type identifier of the bridgeable asset to onboarded to the bridge +/// @param types: The Cadence types of the bridgeable asset to onboard to the bridge /// transaction(types: [Type]) { @@ -44,8 +44,8 @@ transaction(types: [Type]) { } execute { - for type in types { - + for type in types { + // Continue on if the type does not require onboarding if FlowEVMBridge.typeRequiresOnboarding(type) != true { continue } From 87b2ca1245eea1cc3a34de6f9ec9d2a4180f0c39 Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Mon, 15 Apr 2024 20:18:34 -0500 Subject: [PATCH 3/8] fix failing tests --- .../bridge/type_requires_onboarding.cdc | 9 +++------ .../type_requires_onboarding_by_identifier.cdc | 14 ++++++++++++++ cadence/tests/flow_evm_bridge_tests.cdc | 18 +++++++++--------- 3 files changed, 26 insertions(+), 15 deletions(-) create mode 100644 cadence/scripts/bridge/type_requires_onboarding_by_identifier.cdc diff --git a/cadence/scripts/bridge/type_requires_onboarding.cdc b/cadence/scripts/bridge/type_requires_onboarding.cdc index 3b14f14b..73861859 100644 --- a/cadence/scripts/bridge/type_requires_onboarding.cdc +++ b/cadence/scripts/bridge/type_requires_onboarding.cdc @@ -2,13 +2,10 @@ import "FlowEVMBridge" /// Returns whether a type needs to be onboarded to the FlowEVMBridge /// -/// @param identifier: The identifier of the Cadence Type in question +/// @param type: The Cadence Type in question /// /// @return Whether the type requires onboarding to the FlowEVMBridge if the type is bridgeable, otherwise nil /// -access(all) fun main(identifier: String): Bool? { - if let type = CompositeType(identifier) { - return FlowEVMBridge.typeRequiresOnboarding(type) - } - return nil +access(all) fun main(type: Type): Bool? { + return FlowEVMBridge.typeRequiresOnboarding(type) } diff --git a/cadence/scripts/bridge/type_requires_onboarding_by_identifier.cdc b/cadence/scripts/bridge/type_requires_onboarding_by_identifier.cdc new file mode 100644 index 00000000..3b14f14b --- /dev/null +++ b/cadence/scripts/bridge/type_requires_onboarding_by_identifier.cdc @@ -0,0 +1,14 @@ +import "FlowEVMBridge" + +/// Returns whether a type needs to be onboarded to the FlowEVMBridge +/// +/// @param identifier: The identifier of the Cadence Type in question +/// +/// @return Whether the type requires onboarding to the FlowEVMBridge if the type is bridgeable, otherwise nil +/// +access(all) fun main(identifier: String): Bool? { + if let type = CompositeType(identifier) { + return FlowEVMBridge.typeRequiresOnboarding(type) + } + return nil +} diff --git a/cadence/tests/flow_evm_bridge_tests.cdc b/cadence/tests/flow_evm_bridge_tests.cdc index a3ef446c..2bcce576 100644 --- a/cadence/tests/flow_evm_bridge_tests.cdc +++ b/cadence/tests/flow_evm_bridge_tests.cdc @@ -440,8 +440,8 @@ fun testUpdateBridgeFeesSucceeds() { access(all) fun testOnboardNFTByTypeSucceeds() { - var onboaringRequiredResult = executeScript( - "../scripts/bridge/type_requires_onboarding.cdc", + var onboaringRequiredResult: Test.ScriptResult = executeScript( + "../scripts/bridge/type_requires_onboarding_by_identifier.cdc", [exampleNFTIdentifier] ) Test.expect(onboaringRequiredResult, Test.beSucceeded()) @@ -449,14 +449,14 @@ fun testOnboardNFTByTypeSucceeds() { Test.assertEqual(true, requiresOnboarding) var onboardingResult = executeTransaction( - "../transactions/bridge/onboarding/onboard_by_type.cdc", + "../transactions/bridge/onboarding/onboard_by_type_identifier.cdc", [exampleNFTIdentifier], alice ) Test.expect(onboardingResult, Test.beSucceeded()) onboaringRequiredResult = executeScript( - "../scripts/bridge/type_requires_onboarding.cdc", + "../scripts/bridge/type_requires_onboarding_by_identifier.cdc", [exampleNFTIdentifier] ) Test.expect(onboaringRequiredResult, Test.beSucceeded()) @@ -464,7 +464,7 @@ fun testOnboardNFTByTypeSucceeds() { Test.assertEqual(false, requiresOnboarding) onboardingResult = executeTransaction( - "../transactions/bridge/onboarding/onboard_by_type.cdc", + "../transactions/bridge/onboarding/onboard_by_type_identifier.cdc", [exampleNFTIdentifier], alice ) @@ -510,7 +510,7 @@ fun testOnboardERC721ByEVMAddressSucceeds() { access(all) fun testOnboardTokenByTypeSucceeds() { var onboaringRequiredResult = executeScript( - "../scripts/bridge/type_requires_onboarding.cdc", + "../scripts/bridge/type_requires_onboarding_by_identifier.cdc", [exampleTokenIdentifier] ) Test.expect(onboaringRequiredResult, Test.beSucceeded()) @@ -518,14 +518,14 @@ fun testOnboardTokenByTypeSucceeds() { Test.assertEqual(true, requiresOnboarding) var onboardingResult = executeTransaction( - "../transactions/bridge/onboarding/onboard_by_type.cdc", + "../transactions/bridge/onboarding/onboard_by_type_identifier.cdc", [exampleTokenIdentifier], alice ) Test.expect(onboardingResult, Test.beSucceeded()) onboaringRequiredResult = executeScript( - "../scripts/bridge/type_requires_onboarding.cdc", + "../scripts/bridge/type_requires_onboarding_by_identifier.cdc", [exampleTokenIdentifier] ) Test.expect(onboaringRequiredResult, Test.beSucceeded()) @@ -533,7 +533,7 @@ fun testOnboardTokenByTypeSucceeds() { Test.assertEqual(false, requiresOnboarding) onboardingResult = executeTransaction( - "../transactions/bridge/onboarding/onboard_by_type.cdc", + "../transactions/bridge/onboarding/onboard_by_type_identifier.cdc", [exampleTokenIdentifier], alice ) From c7f5b952fb9aaf96830505fc99558c2dc22a4649 Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Tue, 16 Apr 2024 13:59:42 -0500 Subject: [PATCH 4/8] add EVMUtils contract --- cadence/contracts/utils/EVMUtils.cdc | 51 ++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 cadence/contracts/utils/EVMUtils.cdc diff --git a/cadence/contracts/utils/EVMUtils.cdc b/cadence/contracts/utils/EVMUtils.cdc new file mode 100644 index 00000000..cc42ed71 --- /dev/null +++ b/cadence/contracts/utils/EVMUtils.cdc @@ -0,0 +1,51 @@ +import "EVM" + +/// Contract containing EVM-related utility methods +/// +access(all) contract EVMUtils { + /// Returns an EVMAddress as a hex string without a 0x prefix + /// + /// @param address: The EVMAddress to convert to a hex string + /// + /// @return The hex string representation of the EVMAddress without 0x prefix + /// + // TODO: Remove once EVMAddress.toString() is available + access(all) + view fun getEVMAddressAsHexString(address: EVM.EVMAddress): String { + let bytes = address.bytes + // Iterating & appending to an array is not allowed in a `view` method and this method must be `view` for + // certain use cases in the bridge contracts - namely for emitting values in pre- & post-conditions + let addressBytes: [UInt8] = [ + bytes[0], bytes[1], bytes[2], bytes[3], + bytes[4], bytes[5], bytes[6], bytes[7], + bytes[8], bytes[9], bytes[10], bytes[11], + bytes[12], bytes[13], bytes[14], bytes[15], + bytes[16], bytes[17], bytes[18], bytes[19] + ] + return String.encodeHex(addressBytes) + } + + /// Returns an EVMAddress as a hex string without a 0x prefix, truncating the string's last 20 bytes if exceeded + /// + /// @param address: The hex string to convert to an EVMAddress without the 0x prefix + /// + /// @return The EVMAddress representation of the hex string + /// + access(all) + fun getEVMAddressFromHexString(address: String): EVM.EVMAddress? { + if address.length != 40 { + return nil + } + var addressBytes: [UInt8] = address.decodeHex() + if addressBytes.length != 20 { + return nil + } + return EVM.EVMAddress(bytes: [ + addressBytes[0], addressBytes[1], addressBytes[2], addressBytes[3], + addressBytes[4], addressBytes[5], addressBytes[6], addressBytes[7], + addressBytes[8], addressBytes[9], addressBytes[10], addressBytes[11], + addressBytes[12], addressBytes[13], addressBytes[14], addressBytes[15], + addressBytes[16], addressBytes[17], addressBytes[18], addressBytes[19] + ]) + } +} From ef57cd62a4d556787ecfe6b6efaae0ff75bf50d9 Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Tue, 16 Apr 2024 14:01:39 -0500 Subject: [PATCH 5/8] move EVMAddress de/serialization to EVMUtils --- cadence/contracts/bridge/FlowEVMBridge.cdc | 14 ++--- .../contracts/bridge/FlowEVMBridgeConfig.cdc | 5 ++ .../contracts/bridge/FlowEVMBridgeUtils.cdc | 51 ++----------------- .../contracts/bridge/IFlowEVMNFTBridge.cdc | 10 ++-- .../contracts/bridge/IFlowEVMTokenBridge.cdc | 10 ++-- .../batch_evm_address_requires_onboarding.cdc | 4 +- .../evm_address_requires_onboarding.cdc | 4 +- .../bridge/get_associated_evm_address.cdc | 4 +- .../scripts/bridge/get_bridge_coa_address.cdc | 4 +- .../evm/get_evm_address_string_from_bytes.cdc | 4 +- ..._deployed_address_string_from_deployer.cdc | 4 +- .../get_deployed_contract_address_string.cdc | 4 +- cadence/scripts/utils/balance_of.cdc | 5 +- .../derive_bridged_nft_contract_name.cdc | 3 +- .../derive_bridged_token_contract_name.cdc | 3 +- cadence/scripts/utils/get_factory_address.cdc | 4 +- cadence/scripts/utils/get_token_decimals.cdc | 3 +- cadence/scripts/utils/is_owner.cdc | 5 +- .../scripts/utils/is_owner_or_approved.cdc | 5 +- cadence/scripts/utils/token_uri.cdc | 3 +- cadence/tests/flow_evm_bridge_tests.cdc | 6 +++ .../batch_onboard_by_evm_address.cdc | 4 +- .../onboarding/onboard_by_evm_address.cdc | 4 +- .../evm/transfer_flow_to_evm_address.cdc | 4 +- .../example-assets/evm-assets/mint_erc20.cdc | 6 +-- .../evm-assets/safe_mint_erc721.cdc | 6 +-- flow.json | 7 +++ 27 files changed, 81 insertions(+), 105 deletions(-) diff --git a/cadence/contracts/bridge/FlowEVMBridge.cdc b/cadence/contracts/bridge/FlowEVMBridge.cdc index 39f81074..1b449369 100644 --- a/cadence/contracts/bridge/FlowEVMBridge.cdc +++ b/cadence/contracts/bridge/FlowEVMBridge.cdc @@ -8,6 +8,7 @@ import "FlowToken" import "EVM" +import "EVMUtils" import "BridgePermissions" import "ICrossVM" import "IEVMBridgeNFTMinter" @@ -140,7 +141,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { emit Onboarded( type: type, cadenceContractAddress: FlowEVMBridgeUtils.getContractAddress(fromType: type)!, - evmContractAddress: FlowEVMBridgeUtils.getEVMAddressAsHexString(address: onboardingValues.evmContractAddress) + evmContractAddress: EVMUtils.getEVMAddressAsHexString(address: onboardingValues.evmContractAddress) ) } @@ -581,14 +582,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { if !FlowEVMBridgeUtils.isValidFlowAsset(type: type) { return nil } - if type == Type<@FlowToken.Vault>() { - return false - } else if type.isSubtype(of: Type<@{NonFungibleToken.NFT}>()) { - return !FlowEVMBridgeNFTEscrow.isInitialized(forType: type) - } else if type.isSubtype(of: Type<@{FungibleToken.Vault}>()) { - return !FlowEVMBridgeTokenEscrow.isInitialized(forType: type) - } - return nil + return FlowEVMBridgeConfig.getEVMAddressAssociated(with: type) == nil } /// Returns whether an EVM-native asset needs to be onboarded to the bridge @@ -812,7 +806,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { assetName: name, symbol: symbol, isERC721: isERC721, - evmContractAddress: FlowEVMBridgeUtils.getEVMAddressAsHexString(address: evmContractAddress) + evmContractAddress: EVMUtils.getEVMAddressAsHexString(address: evmContractAddress) ) } } diff --git a/cadence/contracts/bridge/FlowEVMBridgeConfig.cdc b/cadence/contracts/bridge/FlowEVMBridgeConfig.cdc index 406dcc36..82c52eab 100644 --- a/cadence/contracts/bridge/FlowEVMBridgeConfig.cdc +++ b/cadence/contracts/bridge/FlowEVMBridgeConfig.cdc @@ -21,6 +21,8 @@ contract FlowEVMBridgeConfig { /// Mapping of Type to its associated EVMAddress as relevant to the bridge access(self) let typeToEVMAddress: {Type: EVM.EVMAddress} + access(self) + let evmAddressHexToType: {String: Type} /* --- Path Constants --- */ // @@ -60,6 +62,7 @@ contract FlowEVMBridgeConfig { access(account) fun associateType(_ type: Type, with evmAddress: EVM.EVMAddress) { self.typeToEVMAddress[type] = evmAddress + } /***************** @@ -100,11 +103,13 @@ contract FlowEVMBridgeConfig { self.onboardFee = 0.0 self.baseFee = 0.0 self.defaultDecimals = 18 + self.typeToEVMAddress = { Type<@FlowToken.Vault>(): EVM.EVMAddress( bytes: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] ) } + self.evmAddressHexToType = {} self.adminStoragePath = /storage/flowEVMBridgeConfigAdmin self.coaStoragePath = /storage/evm self.providerCapabilityStoragePath = /storage/bridgeFlowVaultProvider diff --git a/cadence/contracts/bridge/FlowEVMBridgeUtils.cdc b/cadence/contracts/bridge/FlowEVMBridgeUtils.cdc index bc0e158c..2520ecd6 100644 --- a/cadence/contracts/bridge/FlowEVMBridgeUtils.cdc +++ b/cadence/contracts/bridge/FlowEVMBridgeUtils.cdc @@ -7,6 +7,7 @@ import "FlowStorageFees" import "EVM" +import "EVMUtils" import "FlowEVMBridgeConfig" import "BridgePermissions" @@ -29,52 +30,6 @@ contract FlowEVMBridgeUtils { Public Bridge Utils **************************/ - /// Returns an EVMAddress as a hex string without a 0x prefix - /// - /// @param address: The EVMAddress to convert to a hex string - /// - /// @return The hex string representation of the EVMAddress without 0x prefix - /// - // TODO: Remove once EVMAddress.toString() is available - access(all) - view fun getEVMAddressAsHexString(address: EVM.EVMAddress): String { - let bytes = address.bytes - // Iterating & appending to an array is not allowed in a `view` method and this method must be `view` for - // certain use cases in the bridge contracts - namely for emitting values in pre- & post-conditions - let addressBytes: [UInt8] = [ - bytes[0], bytes[1], bytes[2], bytes[3], - bytes[4], bytes[5], bytes[6], bytes[7], - bytes[8], bytes[9], bytes[10], bytes[11], - bytes[12], bytes[13], bytes[14], bytes[15], - bytes[16], bytes[17], bytes[18], bytes[19] - ] - return String.encodeHex(addressBytes) - } - - /// Returns an EVMAddress as a hex string without a 0x prefix, truncating the string's last 20 bytes if exceeded - /// - /// @param address: The hex string to convert to an EVMAddress without the 0x prefix - /// - /// @return The EVMAddress representation of the hex string - /// - access(all) - fun getEVMAddressFromHexString(address: String): EVM.EVMAddress? { - if address.length != 40 { - return nil - } - var addressBytes: [UInt8] = address.decodeHex() - if addressBytes.length != 20 { - return nil - } - return EVM.EVMAddress(bytes: [ - addressBytes[0], addressBytes[1], addressBytes[2], addressBytes[3], - addressBytes[4], addressBytes[5], addressBytes[6], addressBytes[7], - addressBytes[8], addressBytes[9], addressBytes[10], addressBytes[11], - addressBytes[12], addressBytes[13], addressBytes[14], addressBytes[15], - addressBytes[16], addressBytes[17], addressBytes[18], addressBytes[19] - ]) - } - /// Calculates the fee bridge fee based on the given storage usage. If includeBase is true, the base fee is included /// in the resulting calculation. /// @@ -529,7 +484,7 @@ contract FlowEVMBridgeUtils { view fun deriveBridgedNFTContractName(from evmContract: EVM.EVMAddress): String { return self.contractNamePrefixes[Type<@{NonFungibleToken.NFT}>()]!["bridged"]! .concat(self.delimiter) - .concat("0x".concat(self.getEVMAddressAsHexString(address: evmContract))) + .concat("0x".concat(EVMUtils.getEVMAddressAsHexString(address: evmContract))) } /// Derives the Cadence contract name for a given EVM fungible token of the form @@ -543,7 +498,7 @@ contract FlowEVMBridgeUtils { view fun deriveBridgedTokenContractName(from evmContract: EVM.EVMAddress): String { return self.contractNamePrefixes[Type<@{FungibleToken.Vault}>()]!["bridged"]! .concat(self.delimiter) - .concat("0x".concat(self.getEVMAddressAsHexString(address: evmContract))) + .concat("0x".concat(EVMUtils.getEVMAddressAsHexString(address: evmContract))) } /**************** diff --git a/cadence/contracts/bridge/IFlowEVMNFTBridge.cdc b/cadence/contracts/bridge/IFlowEVMNFTBridge.cdc index d6fea52c..c7e34b7f 100644 --- a/cadence/contracts/bridge/IFlowEVMNFTBridge.cdc +++ b/cadence/contracts/bridge/IFlowEVMNFTBridge.cdc @@ -3,8 +3,8 @@ import "NonFungibleToken" import "EVM" +import "EVMUtils" import "FlowEVMBridgeConfig" -import "FlowEVMBridgeUtils" import "CrossVMNFT" access(all) contract interface IFlowEVMNFTBridge { @@ -69,8 +69,8 @@ access(all) contract interface IFlowEVMNFTBridge { type: token.getType(), id: token.id, evmID: CrossVMNFT.getEVMID(from: &token as &{NonFungibleToken.NFT}) ?? UInt256(token.id), - to: FlowEVMBridgeUtils.getEVMAddressAsHexString(address: to), - evmContractAddress: FlowEVMBridgeUtils.getEVMAddressAsHexString( + to: EVMUtils.getEVMAddressAsHexString(address: to), + evmContractAddress: EVMUtils.getEVMAddressAsHexString( address: self.getAssociatedEVMAddress(with: token.getType()) ?? panic("Could not find EVM Contract address associated with provided NFT") ), bridgeAddress: self.account.address @@ -104,8 +104,8 @@ access(all) contract interface IFlowEVMNFTBridge { type: result.getType(), id: result.id, evmID: id, - caller: FlowEVMBridgeUtils.getEVMAddressAsHexString(address: owner), - evmContractAddress: FlowEVMBridgeUtils.getEVMAddressAsHexString( + caller: EVMUtils.getEVMAddressAsHexString(address: owner), + evmContractAddress: EVMUtils.getEVMAddressAsHexString( address: self.getAssociatedEVMAddress(with: result.getType()) ?? panic("Could not find EVM Contract address associated with provided NFT") ), bridgeAddress: self.account.address diff --git a/cadence/contracts/bridge/IFlowEVMTokenBridge.cdc b/cadence/contracts/bridge/IFlowEVMTokenBridge.cdc index b6e87540..849790bb 100644 --- a/cadence/contracts/bridge/IFlowEVMTokenBridge.cdc +++ b/cadence/contracts/bridge/IFlowEVMTokenBridge.cdc @@ -3,7 +3,7 @@ import "NonFungibleToken" import "EVM" -import "FlowEVMBridgeUtils" +import "EVMUtils" access(all) contract interface IFlowEVMTokenBridge { @@ -64,8 +64,8 @@ access(all) contract interface IFlowEVMTokenBridge { emit BridgedTokensToEVM( type: vault.getType(), amount: vault.balance, - to: FlowEVMBridgeUtils.getEVMAddressAsHexString(address: to), - evmContractAddress: FlowEVMBridgeUtils.getEVMAddressAsHexString( + to: EVMUtils.getEVMAddressAsHexString(address: to), + evmContractAddress: EVMUtils.getEVMAddressAsHexString( address: self.getAssociatedEVMAddress(with: vault.getType()) ?? panic("Could not find EVM Contract address associated with provided NFT") ), bridgeAddress: self.account.address @@ -98,8 +98,8 @@ access(all) contract interface IFlowEVMTokenBridge { emit BridgedTokensFromEVM( type: result.getType(), amount: amount, - caller: FlowEVMBridgeUtils.getEVMAddressAsHexString(address: owner), - evmContractAddress: FlowEVMBridgeUtils.getEVMAddressAsHexString( + caller: EVMUtils.getEVMAddressAsHexString(address: owner), + evmContractAddress: EVMUtils.getEVMAddressAsHexString( address: self.getAssociatedEVMAddress(with: result.getType()) ?? panic("Could not find EVM Contract address associated with provided Vault") ), bridgeAddress: self.account.address diff --git a/cadence/scripts/bridge/batch_evm_address_requires_onboarding.cdc b/cadence/scripts/bridge/batch_evm_address_requires_onboarding.cdc index 869de085..3f464219 100644 --- a/cadence/scripts/bridge/batch_evm_address_requires_onboarding.cdc +++ b/cadence/scripts/bridge/batch_evm_address_requires_onboarding.cdc @@ -1,4 +1,4 @@ -import "FlowEVMBridgeUtils" +import "EVMUtils" import "FlowEVMBridge" /// Returns whether a EVM contract needs to be onboarded to the FlowEVMBridge @@ -15,7 +15,7 @@ access(all) fun main(evmAddresses: [String]): {String: Bool?} { if results[addressHex] != nil { continue } - if let address = FlowEVMBridgeUtils.getEVMAddressFromHexString(address: addressHex) { + if let address = EVMUtils.getEVMAddressFromHexString(address: addressHex) { let requiresOnboarding = FlowEVMBridge.evmAddressRequiresOnboarding(address) results.insert(key: addressHex, requiresOnboarding) } diff --git a/cadence/scripts/bridge/evm_address_requires_onboarding.cdc b/cadence/scripts/bridge/evm_address_requires_onboarding.cdc index 000e8519..0affdae9 100644 --- a/cadence/scripts/bridge/evm_address_requires_onboarding.cdc +++ b/cadence/scripts/bridge/evm_address_requires_onboarding.cdc @@ -1,4 +1,4 @@ -import "FlowEVMBridgeUtils" +import "EVMUtils" import "FlowEVMBridge" /// Returns whether a EVM contract needs to be onboarded to the FlowEVMBridge @@ -8,7 +8,7 @@ import "FlowEVMBridge" /// @return Whether the contract requires onboarding to the FlowEVMBridge if the type is bridgeable, otherwise nil /// access(all) fun main(evmAddressHex: String): Bool? { - if let address = FlowEVMBridgeUtils.getEVMAddressFromHexString(address: evmAddressHex) { + if let address = EVMUtils.getEVMAddressFromHexString(address: evmAddressHex) { return FlowEVMBridge.evmAddressRequiresOnboarding(address) } return nil diff --git a/cadence/scripts/bridge/get_associated_evm_address.cdc b/cadence/scripts/bridge/get_associated_evm_address.cdc index 0f4dec8e..89ad1f17 100644 --- a/cadence/scripts/bridge/get_associated_evm_address.cdc +++ b/cadence/scripts/bridge/get_associated_evm_address.cdc @@ -1,7 +1,7 @@ import "EVM" +import "EVMUtils" import "FlowEVMBridgeConfig" -import "FlowEVMBridgeUtils" /// Returns the EVM address associated with the given Cadence type (as its identifier String) /// @@ -13,7 +13,7 @@ access(all) fun main(identifier: String): String? { if let type = CompositeType(identifier) { if let address = FlowEVMBridgeConfig.getEVMAddressAssociated(with: type) { - return FlowEVMBridgeUtils.getEVMAddressAsHexString(address: address) + return EVMUtils.getEVMAddressAsHexString(address: address) } } return nil diff --git a/cadence/scripts/bridge/get_bridge_coa_address.cdc b/cadence/scripts/bridge/get_bridge_coa_address.cdc index 24a940a1..4a2d26cc 100644 --- a/cadence/scripts/bridge/get_bridge_coa_address.cdc +++ b/cadence/scripts/bridge/get_bridge_coa_address.cdc @@ -1,6 +1,6 @@ import "EVM" -import "FlowEVMBridgeUtils" +import "EVMUtils" import "FlowEVMBridge" /// Returns the EVM address associated with the FlowEVMBridge @@ -9,5 +9,5 @@ import "FlowEVMBridge" /// access(all) fun main(): String { let address: EVM.EVMAddress = FlowEVMBridge.getBridgeCOAEVMAddress() - return FlowEVMBridgeUtils.getEVMAddressAsHexString(address: address) + return EVMUtils.getEVMAddressAsHexString(address: address) } \ No newline at end of file diff --git a/cadence/scripts/evm/get_evm_address_string_from_bytes.cdc b/cadence/scripts/evm/get_evm_address_string_from_bytes.cdc index c0c19bec..279f5248 100644 --- a/cadence/scripts/evm/get_evm_address_string_from_bytes.cdc +++ b/cadence/scripts/evm/get_evm_address_string_from_bytes.cdc @@ -1,6 +1,6 @@ import "EVM" -import "FlowEVMBridgeUtils" +import "EVMUtils" /// Converts EVM address bytes into to a hex string /// @@ -13,5 +13,5 @@ access(all) fun main(bytes: [UInt8]): String? { bytes[15], bytes[16], bytes[17], bytes[18], bytes[19] ] ) - return FlowEVMBridgeUtils.getEVMAddressAsHexString(address: address) + return EVMUtils.getEVMAddressAsHexString(address: address) } diff --git a/cadence/scripts/test/get_deployed_address_string_from_deployer.cdc b/cadence/scripts/test/get_deployed_address_string_from_deployer.cdc index 7da0a811..1798ed72 100644 --- a/cadence/scripts/test/get_deployed_address_string_from_deployer.cdc +++ b/cadence/scripts/test/get_deployed_address_string_from_deployer.cdc @@ -2,11 +2,11 @@ import "EVM" import "EVMDeployer" -import "FlowEVMBridgeUtils" +import "EVMUtils" access(all) fun main(name: String): String { - return FlowEVMBridgeUtils.getEVMAddressAsHexString( + return EVMUtils.getEVMAddressAsHexString( address: EVMDeployer.deployedAddresses[name] ?? panic("Could not find deployed address for: ".concat(name)) ) } \ No newline at end of file diff --git a/cadence/scripts/test/get_deployed_contract_address_string.cdc b/cadence/scripts/test/get_deployed_contract_address_string.cdc index 56744dd5..883431c1 100644 --- a/cadence/scripts/test/get_deployed_contract_address_string.cdc +++ b/cadence/scripts/test/get_deployed_contract_address_string.cdc @@ -1,9 +1,9 @@ import "EVM" -import "FlowEVMBridgeUtils" +import "EVMUtils" import "EVMDeployer" access(all) fun main(): String { - return FlowEVMBridgeUtils.getEVMAddressAsHexString(address: EVMDeployer.deployedContractAddress) + return EVMUtils.getEVMAddressAsHexString(address: EVMDeployer.deployedContractAddress) } \ No newline at end of file diff --git a/cadence/scripts/utils/balance_of.cdc b/cadence/scripts/utils/balance_of.cdc index 5fbee589..29069a45 100644 --- a/cadence/scripts/utils/balance_of.cdc +++ b/cadence/scripts/utils/balance_of.cdc @@ -1,5 +1,6 @@ import "EVM" +import "EVMUtils" import "FlowEVMBridgeUtils" /// Returns the balance of the owner (hex-encoded EVM address - minus 0x prefix) of a given ERC20 fungible token defined @@ -13,9 +14,9 @@ import "FlowEVMBridgeUtils" /// access(all) fun main(owner: String, evmContractAddress: String): UInt256 { return FlowEVMBridgeUtils.balanceOf( - owner: FlowEVMBridgeUtils.getEVMAddressFromHexString(address: owner) + owner: EVMUtils.getEVMAddressFromHexString(address: owner) ?? panic("Invalid owner address"), - evmContractAddress: FlowEVMBridgeUtils.getEVMAddressFromHexString(address: evmContractAddress) + evmContractAddress: EVMUtils.getEVMAddressFromHexString(address: evmContractAddress) ?? panic("Invalid EVM contract address") ) } diff --git a/cadence/scripts/utils/derive_bridged_nft_contract_name.cdc b/cadence/scripts/utils/derive_bridged_nft_contract_name.cdc index 244c73b9..bae8dcd1 100644 --- a/cadence/scripts/utils/derive_bridged_nft_contract_name.cdc +++ b/cadence/scripts/utils/derive_bridged_nft_contract_name.cdc @@ -1,10 +1,11 @@ import "EVM" +import "EVMUtils" import "FlowEVMBridgeUtils" access(all) fun main(evmAddressHex: String): String { return FlowEVMBridgeUtils.deriveBridgedNFTContractName( - from: FlowEVMBridgeUtils.getEVMAddressFromHexString(address: evmAddressHex) ?? panic("Could not parse EVM address from hex string") + from: EVMUtils.getEVMAddressFromHexString(address: evmAddressHex) ?? panic("Could not parse EVM address from hex string") ) } diff --git a/cadence/scripts/utils/derive_bridged_token_contract_name.cdc b/cadence/scripts/utils/derive_bridged_token_contract_name.cdc index 47162364..2f194d09 100644 --- a/cadence/scripts/utils/derive_bridged_token_contract_name.cdc +++ b/cadence/scripts/utils/derive_bridged_token_contract_name.cdc @@ -1,10 +1,11 @@ import "EVM" +import "EVMUtils" import "FlowEVMBridgeUtils" access(all) fun main(evmAddressHex: String): String { return FlowEVMBridgeUtils.deriveBridgedTokenContractName( - from: FlowEVMBridgeUtils.getEVMAddressFromHexString(address: evmAddressHex) ?? panic("Could not parse EVM address from hex string") + from: EVMUtils.getEVMAddressFromHexString(address: evmAddressHex) ?? panic("Could not parse EVM address from hex string") ) } diff --git a/cadence/scripts/utils/get_factory_address.cdc b/cadence/scripts/utils/get_factory_address.cdc index 6ba84697..810eb1ce 100644 --- a/cadence/scripts/utils/get_factory_address.cdc +++ b/cadence/scripts/utils/get_factory_address.cdc @@ -1,5 +1,7 @@ import "EVM" +import "EVMUtils" + import "FlowEVMBridgeUtils" /// Returns the EVM address of the FlowEVMBridgeFactory solidity contract @@ -7,5 +9,5 @@ import "FlowEVMBridgeUtils" /// @return The EVM address of the FlowEVMBridgeFactory contract as hex string (without 0x prefix) /// access(all) fun main(): String { - return FlowEVMBridgeUtils.getEVMAddressAsHexString(address: FlowEVMBridgeUtils.bridgeFactoryEVMAddress) + return EVMUtils.getEVMAddressAsHexString(address: FlowEVMBridgeUtils.bridgeFactoryEVMAddress) } \ No newline at end of file diff --git a/cadence/scripts/utils/get_token_decimals.cdc b/cadence/scripts/utils/get_token_decimals.cdc index 3c690f21..3a04ce1f 100644 --- a/cadence/scripts/utils/get_token_decimals.cdc +++ b/cadence/scripts/utils/get_token_decimals.cdc @@ -1,9 +1,10 @@ +import "EVMUtils" import "FlowEVMBridgeUtils" access(all) fun main(erc20ContractAddressHex: String): UInt8 { return FlowEVMBridgeUtils.getTokenDecimals( - evmContractAddress: FlowEVMBridgeUtils.getEVMAddressFromHexString(address: erc20ContractAddressHex) + evmContractAddress: EVMUtils.getEVMAddressFromHexString(address: erc20ContractAddressHex) ?? panic("Invalid ERC20 address") ) } diff --git a/cadence/scripts/utils/is_owner.cdc b/cadence/scripts/utils/is_owner.cdc index 4906ce6b..b642476d 100644 --- a/cadence/scripts/utils/is_owner.cdc +++ b/cadence/scripts/utils/is_owner.cdc @@ -1,5 +1,6 @@ import "EVM" +import "EVMUtils" import "FlowEVMBridgeUtils" /// Returns whether the given owner (hex-encoded EVM address - minus 0x prefix) is the owner of the given ERC721 NFT @@ -14,9 +15,9 @@ import "FlowEVMBridgeUtils" access(all) fun main(ofNFT: UInt256, owner: String, evmContractAddress: String): Bool { return FlowEVMBridgeUtils.isOwner( ofNFT: ofNFT, - owner: FlowEVMBridgeUtils.getEVMAddressFromHexString(address: owner) + owner: EVMUtils.getEVMAddressFromHexString(address: owner) ?? panic("Invalid owner address"), - evmContractAddress: FlowEVMBridgeUtils.getEVMAddressFromHexString(address: evmContractAddress) + evmContractAddress: EVMUtils.getEVMAddressFromHexString(address: evmContractAddress) ?? panic("Invalid EVM contract address") ) } diff --git a/cadence/scripts/utils/is_owner_or_approved.cdc b/cadence/scripts/utils/is_owner_or_approved.cdc index 7def15e9..423f0bab 100644 --- a/cadence/scripts/utils/is_owner_or_approved.cdc +++ b/cadence/scripts/utils/is_owner_or_approved.cdc @@ -1,5 +1,6 @@ import "EVM" +import "EVMUtils" import "FlowEVMBridgeUtils" /// Returns whether the given owner (hex-encoded EVM address - minus 0x prefix) is the owner or approved of the given @@ -14,9 +15,9 @@ import "FlowEVMBridgeUtils" access(all) fun main(ofNFT: UInt256, owner: String, evmContractAddress: String): Bool { return FlowEVMBridgeUtils.isOwnerOrApproved( ofNFT: ofNFT, - owner: FlowEVMBridgeUtils.getEVMAddressFromHexString(address: owner) + owner: EVMUtils.getEVMAddressFromHexString(address: owner) ?? panic("Invalid owner address"), - evmContractAddress: FlowEVMBridgeUtils.getEVMAddressFromHexString(address: evmContractAddress) + evmContractAddress: EVMUtils.getEVMAddressFromHexString(address: evmContractAddress) ?? panic("Invalid EVM contract address") ) } diff --git a/cadence/scripts/utils/token_uri.cdc b/cadence/scripts/utils/token_uri.cdc index 384504ac..6cc61e12 100644 --- a/cadence/scripts/utils/token_uri.cdc +++ b/cadence/scripts/utils/token_uri.cdc @@ -1,5 +1,6 @@ import "EVM" +import "EVMUtils" import "FlowEVMBridgeUtils" /// Returns the tokenURI of the given tokenID from the given EVM contract address @@ -11,7 +12,7 @@ import "FlowEVMBridgeUtils" /// @return The tokenURI of the given tokenID from the given EVM contract address. Reverts if the call is unsuccessful /// access(all) fun main(contractAddressHex: String, tokenID: UInt256): String? { - let address = FlowEVMBridgeUtils.getEVMAddressFromHexString(address: contractAddressHex) + let address = EVMUtils.getEVMAddressFromHexString(address: contractAddressHex) ?? panic("Problem ") return FlowEVMBridgeUtils.getTokenURI( evmContractAddress: address, diff --git a/cadence/tests/flow_evm_bridge_tests.cdc b/cadence/tests/flow_evm_bridge_tests.cdc index 2bcce576..fbaf3bca 100644 --- a/cadence/tests/flow_evm_bridge_tests.cdc +++ b/cadence/tests/flow_evm_bridge_tests.cdc @@ -107,6 +107,12 @@ fun setup() { arguments: [] ) Test.expect(err, Test.beNil()) + err = Test.deployContract( + name: "EVMUtils", + path: "../contracts/utils/EVMUtils.cdc", + arguments: [] + ) + Test.expect(err, Test.beNil()) err = Test.deployContract( name: "FlowEVMBridgeConfig", path: "../contracts/bridge/FlowEVMBridgeConfig.cdc", diff --git a/cadence/transactions/bridge/onboarding/batch_onboard_by_evm_address.cdc b/cadence/transactions/bridge/onboarding/batch_onboard_by_evm_address.cdc index a1c47059..06bc4c6c 100644 --- a/cadence/transactions/bridge/onboarding/batch_onboard_by_evm_address.cdc +++ b/cadence/transactions/bridge/onboarding/batch_onboard_by_evm_address.cdc @@ -5,8 +5,8 @@ import "ScopedFTProviders" import "EVM" +import "EVMUtils" import "FlowEVMBridge" -import "FlowEVMBridgeUtils" import "FlowEVMBridgeConfig" /// This transaction onboards the NFT type to the bridge, configuring the bridge to move NFTs between environments @@ -49,7 +49,7 @@ transaction(addressesAsHex: [String]) { // Iterate over provided array for addressHex in addressesAsHex { // Convert hex string to EVMAddress - let address = FlowEVMBridgeUtils.getEVMAddressFromHexString(address: addressHex) + let address = EVMUtils.getEVMAddressFromHexString(address: addressHex) // Continue if the hex is not a valid EVM address or if the address is already onboarded if address == nil || FlowEVMBridge.evmAddressRequiresOnboarding(address!) != true { continue diff --git a/cadence/transactions/bridge/onboarding/onboard_by_evm_address.cdc b/cadence/transactions/bridge/onboarding/onboard_by_evm_address.cdc index 945eda06..2905d148 100644 --- a/cadence/transactions/bridge/onboarding/onboard_by_evm_address.cdc +++ b/cadence/transactions/bridge/onboarding/onboard_by_evm_address.cdc @@ -5,8 +5,8 @@ import "ScopedFTProviders" import "EVM" +import "EVMUtils" import "FlowEVMBridge" -import "FlowEVMBridgeUtils" import "FlowEVMBridgeConfig" /// This transaction onboards the NFT type to the bridge, configuring the bridge to move NFTs between environments @@ -23,7 +23,7 @@ transaction(contractAddressHex: String) { prepare(signer: auth(CopyValue, BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue) &Account) { /* --- Construct EVMAddress from hex string (no leading `"0x"`) --- */ // - self.contractAddress = FlowEVMBridgeUtils.getEVMAddressFromHexString(address: contractAddressHex) + self.contractAddress = EVMUtils.getEVMAddressFromHexString(address: contractAddressHex) ?? panic("Invalid EVM address string provided") /* --- Configure a ScopedFTProvider --- */ diff --git a/cadence/transactions/evm/transfer_flow_to_evm_address.cdc b/cadence/transactions/evm/transfer_flow_to_evm_address.cdc index 95ad270c..800d0471 100644 --- a/cadence/transactions/evm/transfer_flow_to_evm_address.cdc +++ b/cadence/transactions/evm/transfer_flow_to_evm_address.cdc @@ -3,7 +3,7 @@ import "FlowToken" import "EVM" -import "FlowEVMBridgeUtils" +import "EVMUtils" /// Transfers $FLOW from the signer's account Cadence Flow balance to the recipient's hex-encoded EVM address. /// Note that a COA must have a $FLOW balance in EVM before transferring value to another EVM address. @@ -26,7 +26,7 @@ transaction(recipientEVMAddressHex: String, amount: UFix64, gasLimit: UInt64) { ) ?? panic("Could not borrow reference to the owner's Vault!") self.sentVault <- vaultRef.withdraw(amount: amount) as! @FlowToken.Vault - self.recipientEVMAddress = FlowEVMBridgeUtils.getEVMAddressFromHexString(address: recipientEVMAddressHex) + self.recipientEVMAddress = EVMUtils.getEVMAddressFromHexString(address: recipientEVMAddressHex) ?? panic("Invalid recipient EVM address") } diff --git a/cadence/transactions/example-assets/evm-assets/mint_erc20.cdc b/cadence/transactions/example-assets/evm-assets/mint_erc20.cdc index 311c80ff..429053a9 100644 --- a/cadence/transactions/example-assets/evm-assets/mint_erc20.cdc +++ b/cadence/transactions/example-assets/evm-assets/mint_erc20.cdc @@ -1,6 +1,6 @@ import "EVM" -import "FlowEVMBridgeUtils" +import "EVMUtils" transaction( recipientHexAddress: String, @@ -17,9 +17,9 @@ transaction( } execute { - let recipientAddress = FlowEVMBridgeUtils.getEVMAddressFromHexString(address: recipientHexAddress) + let recipientAddress = EVMUtils.getEVMAddressFromHexString(address: recipientHexAddress) ?? panic("Invalid recipient address") - let erc20Address = FlowEVMBridgeUtils.getEVMAddressFromHexString(address: erc20HexAddress) + let erc20Address = EVMUtils.getEVMAddressFromHexString(address: erc20HexAddress) ?? panic("Invalid ERC20 address") let calldata = EVM.encodeABIWithSignature( "mint(address,uint256)", diff --git a/cadence/transactions/example-assets/evm-assets/safe_mint_erc721.cdc b/cadence/transactions/example-assets/evm-assets/safe_mint_erc721.cdc index bf7b1955..5f7e57d8 100644 --- a/cadence/transactions/example-assets/evm-assets/safe_mint_erc721.cdc +++ b/cadence/transactions/example-assets/evm-assets/safe_mint_erc721.cdc @@ -1,6 +1,6 @@ import "EVM" -import "FlowEVMBridgeUtils" +import "EVMUtils" transaction( recipientHexAddress: String, @@ -18,9 +18,9 @@ transaction( } execute { - let recipientAddress = FlowEVMBridgeUtils.getEVMAddressFromHexString(address: recipientHexAddress) + let recipientAddress = EVMUtils.getEVMAddressFromHexString(address: recipientHexAddress) ?? panic("Invalid recipient address") - let erc721Address = FlowEVMBridgeUtils.getEVMAddressFromHexString(address: erc721HexAddress) + let erc721Address = EVMUtils.getEVMAddressFromHexString(address: erc721HexAddress) ?? panic("Invalid ERC721 address") let calldata = EVM.encodeABIWithSignature( "safeMint(address,uint256,string)", diff --git a/flow.json b/flow.json index 9fe93644..5166957a 100644 --- a/flow.json +++ b/flow.json @@ -43,6 +43,13 @@ "previewnet": "b6763b4399a888c8" } }, + "EVMUtils": { + "source": "./cadence/contracts/standards/EVMUtils.cdc", + "aliases": { + "emulator": "f8d6e0586b0a20c7", + "testing": "0000000000000007" + } + }, "FlowEVMBridgeAccessor": { "source": "./cadence/contracts/bridge/FlowEVMBridgeAccessor.cdc", "aliases": { From 9f18dc30ccf2df294a4718a9938c9f469de6ea75 Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Tue, 16 Apr 2024 14:20:01 -0500 Subject: [PATCH 6/8] add reverse mapping to config & refactor evmAddressRequiresOnboarding for batch onboarding --- cadence/contracts/bridge/FlowEVMBridge.cdc | 10 ++----- .../contracts/bridge/FlowEVMBridgeConfig.cdc | 30 ++++++++++++++----- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/cadence/contracts/bridge/FlowEVMBridge.cdc b/cadence/contracts/bridge/FlowEVMBridge.cdc index 1b449369..761bcdb9 100644 --- a/cadence/contracts/bridge/FlowEVMBridge.cdc +++ b/cadence/contracts/bridge/FlowEVMBridge.cdc @@ -593,19 +593,15 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { /// access(all) fun evmAddressRequiresOnboarding(_ address: EVM.EVMAddress): Bool? { - // If the address was deployed by the bridge or a Cadence contract has been deployed to define the - // corresponding NFT, it's already been onboarded - let nftContractName = FlowEVMBridgeUtils.deriveBridgedNFTContractName(from: address) - let tokenContractName = FlowEVMBridgeUtils.deriveBridgedTokenContractName(from: address) - if FlowEVMBridgeUtils.isEVMContractBridgeOwned(evmContractAddress: address) || - self.account.contracts.get(name: nftContractName) != nil || - self.account.contracts.get(name: tokenContractName) != nil { + // See if the bridge already has a known type associated with the given address + if FlowEVMBridgeConfig.getTypeAssociated(with: address) != nil { return false } // Dealing with EVM-native asset, check if it's NFT or FT exclusively if FlowEVMBridgeUtils.isValidEVMAsset(evmContractAddress: address) { return true } + // Not onboarded and not a valid asset, so return nil return nil } diff --git a/cadence/contracts/bridge/FlowEVMBridgeConfig.cdc b/cadence/contracts/bridge/FlowEVMBridgeConfig.cdc index 82c52eab..295b7932 100644 --- a/cadence/contracts/bridge/FlowEVMBridgeConfig.cdc +++ b/cadence/contracts/bridge/FlowEVMBridgeConfig.cdc @@ -2,6 +2,8 @@ import "EVM" import "FlowToken" +import "EVMUtils" + /// This contract is used to store configuration information shared by FlowEVMBridge contracts /// access(all) @@ -21,6 +23,8 @@ contract FlowEVMBridgeConfig { /// Mapping of Type to its associated EVMAddress as relevant to the bridge access(self) let typeToEVMAddress: {Type: EVM.EVMAddress} + /// Reverse mapping of typeToEVMAddress. Note the EVMAddress is stored as a hex string since the EVMAddress type + /// as of contract development is not a hashable or equatable type and making it so is not supported by Cadence access(self) let evmAddressHexToType: {String: Type} @@ -53,6 +57,14 @@ contract FlowEVMBridgeConfig { return self.typeToEVMAddress[type] } + /// Retrieves the type associated with a given EVMAddress if it has been onboarded to the bridge + /// + access(all) + view fun getTypeAssociated(with evmAddress: EVM.EVMAddress): Type? { + let evmAddressHex = EVMUtils.getEVMAddressAsHexString(address: evmAddress) + return self.evmAddressHexToType[evmAddressHex] + } + /**************************** Bridge Account Methods ****************************/ @@ -62,7 +74,8 @@ contract FlowEVMBridgeConfig { access(account) fun associateType(_ type: Type, with evmAddress: EVM.EVMAddress) { self.typeToEVMAddress[type] = evmAddress - + let evmAddressHex = EVMUtils.getEVMAddressAsHexString(address: evmAddress) + self.evmAddressHexToType[evmAddressHex] = type } /***************** @@ -103,13 +116,16 @@ contract FlowEVMBridgeConfig { self.onboardFee = 0.0 self.baseFee = 0.0 self.defaultDecimals = 18 - - self.typeToEVMAddress = { - Type<@FlowToken.Vault>(): EVM.EVMAddress( - bytes: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + // Although $FLOW does not have ERC20 address, we associate the the Vault with the EVM address from which + // EVM transfers originate + // See FLIP #223 - https://github.com/onflow/flips/pull/225 + let flowOriginationAddress = EVM.EVMAddress( + bytes: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0] ) - } - self.evmAddressHexToType = {} + let flowVaultType = Type<@FlowToken.Vault>() + let flowOriginationAddressHex = EVMUtils.getEVMAddressAsHexString(address: flowOriginationAddress) + self.typeToEVMAddress = { flowVaultType: flowOriginationAddress } + self.evmAddressHexToType = { flowOriginationAddressHex: flowVaultType} self.adminStoragePath = /storage/flowEVMBridgeConfigAdmin self.coaStoragePath = /storage/evm self.providerCapabilityStoragePath = /storage/bridgeFlowVaultProvider From acb4c4ebd6667d57dd91392d5169529705aa01b2 Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Tue, 16 Apr 2024 14:41:03 -0500 Subject: [PATCH 7/8] add coverage for batch scripts & txns --- cadence/tests/flow_evm_bridge_tests.cdc | 155 +++++++++++++++--- .../onboarding/batch_onboard_by_type.cdc | 2 +- 2 files changed, 136 insertions(+), 21 deletions(-) diff --git a/cadence/tests/flow_evm_bridge_tests.cdc b/cadence/tests/flow_evm_bridge_tests.cdc index fbaf3bca..a7fad6f2 100644 --- a/cadence/tests/flow_evm_bridge_tests.cdc +++ b/cadence/tests/flow_evm_bridge_tests.cdc @@ -3,6 +3,8 @@ import BlockchainHelpers import "FungibleToken" import "NonFungibleToken" +import "ExampleNFT" +import "ExampleToken" import "FlowStorageFees" import "test_helpers.cdc" @@ -34,9 +36,13 @@ access(all) let erc721URI = "URI" // ERC20 values access(all) let erc20MintAmount: UInt256 = 100_000_000_000_000_000_000 +// Fee initialiazation values access(all) let expectedOnboardFee = 1.0 access(all) let expectedBaseFee = 0.001 +// Test height snapshot for test state resets +access(all) var snapshot: UInt64 = 0 + access(all) fun setup() { // Deploy supporting util contracts @@ -446,6 +452,7 @@ fun testUpdateBridgeFeesSucceeds() { access(all) fun testOnboardNFTByTypeSucceeds() { + snapshot = getCurrentBlockHeight() var onboaringRequiredResult: Test.ScriptResult = executeScript( "../scripts/bridge/type_requires_onboarding_by_identifier.cdc", [exampleNFTIdentifier] @@ -478,69 +485,123 @@ fun testOnboardNFTByTypeSucceeds() { } access(all) -fun testOnboardERC721ByEVMAddressSucceeds() { - let erc721AddressHex = getDeployedAddressFromDeployer(name: "erc721") - Test.assertEqual(40, erc721AddressHex.length) - +fun testOnboardTokenByTypeSucceeds() { var onboaringRequiredResult = executeScript( - "../scripts/bridge/evm_address_requires_onboarding.cdc", - [erc721AddressHex] + "../scripts/bridge/type_requires_onboarding_by_identifier.cdc", + [exampleTokenIdentifier] ) Test.expect(onboaringRequiredResult, Test.beSucceeded()) var requiresOnboarding = onboaringRequiredResult.returnValue as! Bool? ?? panic("Problem getting onboarding requirement") Test.assertEqual(true, requiresOnboarding) var onboardingResult = executeTransaction( - "../transactions/bridge/onboarding/onboard_by_evm_address.cdc", - [erc721AddressHex], + "../transactions/bridge/onboarding/onboard_by_type_identifier.cdc", + [exampleTokenIdentifier], alice ) Test.expect(onboardingResult, Test.beSucceeded()) onboaringRequiredResult = executeScript( - "../scripts/bridge/evm_address_requires_onboarding.cdc", - [erc721AddressHex] + "../scripts/bridge/type_requires_onboarding_by_identifier.cdc", + [exampleTokenIdentifier] ) Test.expect(onboaringRequiredResult, Test.beSucceeded()) requiresOnboarding = onboaringRequiredResult.returnValue as! Bool? ?? panic("Problem getting onboarding requirement") Test.assertEqual(false, requiresOnboarding) onboardingResult = executeTransaction( - "../transactions/bridge/onboarding/onboard_by_evm_address.cdc", - [erc721AddressHex], + "../transactions/bridge/onboarding/onboard_by_type_identifier.cdc", + [exampleTokenIdentifier], alice ) Test.expect(onboardingResult, Test.beFailed()) } access(all) -fun testOnboardTokenByTypeSucceeds() { - var onboaringRequiredResult = executeScript( +fun testBatchOnboardByTypeSucceeds() { + Test.assert(snapshot != 0, message: "Expected snapshot to be taken before onboarding any types") + Test.reset(to: snapshot) + + let nftOnboaringRequiredResult = executeScript( + "../scripts/bridge/type_requires_onboarding_by_identifier.cdc", + [exampleNFTIdentifier] + ) + Test.expect(nftOnboaringRequiredResult, Test.beSucceeded()) + let nftRequiresOnboarding = nftOnboaringRequiredResult.returnValue as! Bool? ?? panic("Problem getting onboarding requirement") + Test.assertEqual(true, nftRequiresOnboarding) + let tokenOnboaringRequiredResult = executeScript( "../scripts/bridge/type_requires_onboarding_by_identifier.cdc", [exampleTokenIdentifier] ) + Test.expect(tokenOnboaringRequiredResult, Test.beSucceeded()) + let tokenRequiresOnboarding = tokenOnboaringRequiredResult.returnValue as! Bool? ?? panic("Problem getting onboarding requirement") + Test.assertEqual(true, tokenRequiresOnboarding) + + let exampleNFTType = Type<@ExampleNFT.NFT>() + let exampleTokenType = Type<@ExampleToken.Vault>() + var onboardingResult = executeTransaction( + "../transactions/bridge/onboarding/batch_onboard_by_type.cdc", + [[exampleNFTType, exampleTokenType]], + alice + ) + Test.expect(onboardingResult, Test.beSucceeded()) + + let expectedBatchOnboardingRequired: {Type: Bool?} = { + exampleNFTType: false, + exampleTokenType: false + } + let batchOnboaringRequiredResult = executeScript( + "../scripts/bridge/batch_type_requires_onboarding.cdc", + [[exampleNFTType, exampleTokenType]] + ) + Test.expect(batchOnboaringRequiredResult, Test.beSucceeded()) + let batchRequiresOnboarding = batchOnboaringRequiredResult.returnValue as! {Type: Bool?}? ?? panic("Problem getting onboarding requirement") + Test.assertEqual(expectedBatchOnboardingRequired, batchRequiresOnboarding) + + // Should succeed as batch onboarding skips already onboarded types + onboardingResult = executeTransaction( + "../transactions/bridge/onboarding/batch_onboard_by_type.cdc", + [[exampleNFTType, exampleTokenType]], + alice + ) + Test.expect(onboardingResult, Test.beSucceeded()) +} + + + +access(all) +fun testOnboardERC721ByEVMAddressSucceeds() { + snapshot = getCurrentBlockHeight() + + let erc721AddressHex = getDeployedAddressFromDeployer(name: "erc721") + Test.assertEqual(40, erc721AddressHex.length) + + var onboaringRequiredResult = executeScript( + "../scripts/bridge/evm_address_requires_onboarding.cdc", + [erc721AddressHex] + ) Test.expect(onboaringRequiredResult, Test.beSucceeded()) var requiresOnboarding = onboaringRequiredResult.returnValue as! Bool? ?? panic("Problem getting onboarding requirement") Test.assertEqual(true, requiresOnboarding) var onboardingResult = executeTransaction( - "../transactions/bridge/onboarding/onboard_by_type_identifier.cdc", - [exampleTokenIdentifier], + "../transactions/bridge/onboarding/onboard_by_evm_address.cdc", + [erc721AddressHex], alice ) Test.expect(onboardingResult, Test.beSucceeded()) onboaringRequiredResult = executeScript( - "../scripts/bridge/type_requires_onboarding_by_identifier.cdc", - [exampleTokenIdentifier] + "../scripts/bridge/evm_address_requires_onboarding.cdc", + [erc721AddressHex] ) Test.expect(onboaringRequiredResult, Test.beSucceeded()) requiresOnboarding = onboaringRequiredResult.returnValue as! Bool? ?? panic("Problem getting onboarding requirement") Test.assertEqual(false, requiresOnboarding) onboardingResult = executeTransaction( - "../transactions/bridge/onboarding/onboard_by_type_identifier.cdc", - [exampleTokenIdentifier], + "../transactions/bridge/onboarding/onboard_by_evm_address.cdc", + [erc721AddressHex], alice ) Test.expect(onboardingResult, Test.beFailed()) @@ -582,6 +643,60 @@ fun testOnboardERC20ByEVMAddressSucceeds() { Test.expect(onboardingResult, Test.beFailed()) } +access(all) +fun testBatchOnboardByEVMAddressSucceeds() { + Test.assert(snapshot != 0, message: "Expected snapshot to be taken before onboarding any EVM contracts") + Test.reset(to: snapshot) + + let erc721AddressHex = getDeployedAddressFromDeployer(name: "erc721") + let erc20AddressHex = getDeployedAddressFromDeployer(name: "erc20") + Test.assertEqual(40, erc721AddressHex.length) + Test.assertEqual(40, erc20AddressHex.length) + + var erc721OnboaringRequiredResult = executeScript( + "../scripts/bridge/evm_address_requires_onboarding.cdc", + [erc721AddressHex] + ) + Test.expect(erc721OnboaringRequiredResult, Test.beSucceeded()) + var erc721RequiresOnboarding = erc721OnboaringRequiredResult.returnValue as! Bool? ?? panic("Problem getting onboarding requirement") + Test.assertEqual(true, erc721RequiresOnboarding) + var erc20OnboaringRequiredResult = executeScript( + "../scripts/bridge/evm_address_requires_onboarding.cdc", + [erc20AddressHex] + ) + Test.expect(erc20OnboaringRequiredResult, Test.beSucceeded()) + var erc20RequiresOnboarding = erc20OnboaringRequiredResult.returnValue as! Bool? ?? panic("Problem getting onboarding requirement") + Test.assertEqual(true, erc20RequiresOnboarding) + + var batchOnboardingResult = executeTransaction( + "../transactions/bridge/onboarding/batch_onboard_by_evm_address.cdc", + [[erc721AddressHex, erc20AddressHex]], + alice + ) + Test.expect(batchOnboardingResult, Test.beSucceeded()) + + let expectedBatchRequiresOnboarding: {String: Bool?} = { + erc721AddressHex: false, + erc20AddressHex: false + } + let batchOnboaringRequiredResult = executeScript( + "../scripts/bridge/batch_evm_address_requires_onboarding.cdc", + [[erc721AddressHex, erc20AddressHex]] + ) + Test.expect(batchOnboaringRequiredResult, Test.beSucceeded()) + let batchRequiresOnboarding = batchOnboaringRequiredResult.returnValue as! {String: Bool?}? ?? panic("Problem getting onboarding requirement") + Test.assertEqual(expectedBatchRequiresOnboarding, batchRequiresOnboarding) + + // Batch onboarding should succeed as it skips already onboarded contracts + batchOnboardingResult = executeTransaction( + "../transactions/bridge/onboarding/batch_onboard_by_evm_address.cdc", + [[erc721AddressHex, erc20AddressHex]], + alice + ) + Test.expect(batchOnboardingResult, Test.beSucceeded()) + +} + /* --- BRIDGING NFTS - Test bridging both Cadence- & EVM-native NFTs --- */ access(all) diff --git a/cadence/transactions/bridge/onboarding/batch_onboard_by_type.cdc b/cadence/transactions/bridge/onboarding/batch_onboard_by_type.cdc index 55d9d889..5d2feaa5 100644 --- a/cadence/transactions/bridge/onboarding/batch_onboard_by_type.cdc +++ b/cadence/transactions/bridge/onboarding/batch_onboard_by_type.cdc @@ -51,7 +51,7 @@ transaction(types: [Type]) { } // Onboard the asset Type FlowEVMBridge.onboardByType( - self.type, + type, feeProvider: &self.scopedProvider as auth(FungibleToken.Withdraw) &{FungibleToken.Provider} ) } From 428634e3eb6c22e186bdfc4f9802a30201392d7f Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Tue, 16 Apr 2024 15:45:30 -0500 Subject: [PATCH 8/8] add EVMUtils to coverage normalization script --- local/normalize_coverage_report.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/local/normalize_coverage_report.sh b/local/normalize_coverage_report.sh index d2d2bb8c..266a0d27 100644 --- a/local/normalize_coverage_report.sh +++ b/local/normalize_coverage_report.sh @@ -6,6 +6,7 @@ sed -i 's/A.0000000000000007.CrossVMNFT/cadence\/contracts\/bridge\/CrossVMNFT.c sed -i 's/A.0000000000000007.CrossVMToken/cadence\/contracts\/bridge\/CrossVMToken.cdc/' coverage.lcov sed -i 's/A.0000000000000007.FlowEVMBridgeConfig/cadence\/contracts\/bridge\/FlowEVMBridgeConfig.cdc/' coverage.lcov sed -i 's/A.0000000000000007.FlowEVMBridgeUtils/cadence\/contracts\/bridge\/FlowEVMBridgeUtils.cdc/' coverage.lcov +sed -i 's/A.0000000000000007.EVMUtils/cadence\/contracts\/bridge\/EVMUtils.cdc/' coverage.lcov sed -i 's/A.0000000000000007.FlowEVMBridgeNFTEscrow/cadence\/contracts\/bridge\/FlowEVMBridgeNFTEscrow.cdc/' coverage.lcov sed -i 's/A.0000000000000007.FlowEVMBridgeTokenEscrow/cadence\/contracts\/bridge\/FlowEVMBridgeTokenEscrow.cdc/' coverage.lcov sed -i 's/A.0000000000000007.FlowEVMBridgeTemplates/cadence\/contracts\/bridge\/FlowEVMBridgeTemplates.cdc/' coverage.lcov