From ae1fbc466f862da918240f2ba90d817bec548c5e Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Wed, 18 Mar 2026 12:05:30 +0200 Subject: [PATCH 1/4] Replace COA.call with COA.callWithSigAndArgs for reduced computation cost --- cadence/contracts/bridge/FlowEVMBridge.cdc | 21 +- .../bridge/FlowEVMBridgeAccessor.cdc | 26 +- .../bridge/FlowEVMBridgeHandlers.cdc | 14 +- .../contracts/bridge/FlowEVMBridgeUtils.cdc | 327 +++++++++--------- .../FlowEVMBridgeHandlerInterfaces.cdc | 2 +- .../bridge/interfaces/IFlowEVMNFTBridge.cdc | 2 +- .../bridge/interfaces/IFlowEVMTokenBridge.cdc | 2 +- .../cross-vm-nfts/ExampleEVMNativeNFT.cdc | 87 ++--- .../ExampleEVMNativeNFTGivenEVMAddress.cdc | 87 ++--- .../scripts/utils/get_deployer_address.cdc | 14 +- .../scripts/utils/get_registry_address.cdc | 14 +- cadence/tests/scripts/get_registry.cdc | 18 +- .../bridge/admin/evm/add_deployer.cdc | 30 +- .../admin/evm/set_delegated_deployer.cdc | 27 +- .../admin/evm/set_deployment_registry.cdc | 27 +- .../bridge/admin/evm/set_registrar.cdc | 27 +- .../bridge/admin/evm/upsert_deployer.cdc | 30 +- .../example-assets/evm-assets/mint_erc20.cdc | 12 +- .../evm-assets/safe_mint_erc721.cdc | 12 +- .../evm-assets/safe_transfer_from_erc721.cdc | 20 +- .../evm-assets/transfer_erc20.cdc | 9 +- .../example-assets/evm-assets/unwrap_flow.cdc | 10 +- .../example-assets/evm-assets/wrap_flow.cdc | 9 +- 23 files changed, 409 insertions(+), 418 deletions(-) diff --git a/cadence/contracts/bridge/FlowEVMBridge.cdc b/cadence/contracts/bridge/FlowEVMBridge.cdc index a4999ac6..b829cfa7 100644 --- a/cadence/contracts/bridge/FlowEVMBridge.cdc +++ b/cadence/contracts/bridge/FlowEVMBridge.cdc @@ -394,7 +394,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { type: Type, id: UInt256, feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}, - protectedTransferCall: fun (EVM.EVMAddress): EVM.Result + protectedTransferCall: fun (EVM.EVMAddress): EVM.ResultDecoded ): @{NonFungibleToken.NFT} { pre { !FlowEVMBridgeConfig.isPaused(): "Bridge operations are currently paused" @@ -406,7 +406,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { let customAssocByType = FlowEVMBridgeCustomAssociations.getEVMAddressAssociated(with: type) let customAssocByEVMAddr = bridgedAssoc != nil ? FlowEVMBridgeCustomAssociations.getTypeAssociated(with: bridgedAssoc!) : nil // Initialize the internal handler method that will be used to move the NFT from EVM - var handler: (fun (EVM.EVMAddress, Type, UInt256, auth(FungibleToken.Withdraw) &{FungibleToken.Provider}, fun (EVM.EVMAddress): EVM.Result): @{NonFungibleToken.NFT})? = nil + var handler: (fun (EVM.EVMAddress, Type, UInt256, auth(FungibleToken.Withdraw) &{FungibleToken.Provider}, fun (EVM.EVMAddress): EVM.ResultDecoded): @{NonFungibleToken.NFT})? = nil if bridgedAssoc != nil && customAssocByType == nil && customAssocByEVMAddr == nil { // Common case - bridge-defined counterpart in non-native VM handler = self.handleDefaultNFTFromEVM @@ -532,7 +532,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { type: Type, amount: UInt256, feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}, - protectedTransferCall: fun (): EVM.Result + protectedTransferCall: fun (): EVM.ResultDecoded ): @{FungibleToken.Vault} { pre { !FlowEVMBridgeConfig.isPaused(): "Bridge operations are currently paused" @@ -598,12 +598,13 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { message: "Unexpected error bridging FT from EVM" ) // Burn the EVM tokens that have now been transferred to the bridge in EVM - let burnResult: EVM.Result = FlowEVMBridgeUtils.call( + let burnResult = FlowEVMBridgeUtils.callWithSigAndArgs( signature: "burn(uint256)", targetEVMAddress: associatedAddress, args: [amount], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: nil ) assert(burnResult.status == EVM.Status.successful, message: "Burn of EVM tokens failed") @@ -951,7 +952,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { type: Type, id: UInt256, feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}, - protectedTransferCall: fun (EVM.EVMAddress): EVM.Result + protectedTransferCall: fun (EVM.EVMAddress): EVM.ResultDecoded ): @{NonFungibleToken.NFT} { /* Provision fee */ // @@ -1013,7 +1014,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { type: Type, id: UInt256, feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}, - protectedTransferCall: fun (EVM.EVMAddress): EVM.Result + protectedTransferCall: fun (EVM.EVMAddress): EVM.ResultDecoded ): @{NonFungibleToken.NFT} { let evmPointer = FlowEVMBridgeCustomAssociations.getEVMPointerAsRegistered(forType: type) ?? panic("Could not find custom association for cross-VM NFT \(type.identifier) with id \(id). " @@ -1045,7 +1046,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { type: Type, id: UInt256, feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}, - protectedTransferCall: fun (EVM.EVMAddress): EVM.Result + protectedTransferCall: fun (EVM.EVMAddress): EVM.ResultDecoded ): @{NonFungibleToken.NFT} { pre { FlowEVMBridgeUtils.isCadenceNative(type: type): @@ -1110,7 +1111,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { type: Type, id: UInt256, feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}, - protectedTransferCall: fun (EVM.EVMAddress): EVM.Result + protectedTransferCall: fun (EVM.EVMAddress): EVM.ResultDecoded ): @{NonFungibleToken.NFT} { pre { id <= UInt256(UInt64.max): @@ -1158,7 +1159,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { type: Type, id: UInt256, feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}, - protectedTransferCall: fun (EVM.EVMAddress): EVM.Result + protectedTransferCall: fun (EVM.EVMAddress): EVM.ResultDecoded ): @{NonFungibleToken.NFT} { pre { !FlowEVMBridgeUtils.isCadenceNative(type: type): // expect this type to be bridge-defined diff --git a/cadence/contracts/bridge/FlowEVMBridgeAccessor.cdc b/cadence/contracts/bridge/FlowEVMBridgeAccessor.cdc index 1c1b23c9..c6394d8e 100644 --- a/cadence/contracts/bridge/FlowEVMBridgeAccessor.cdc +++ b/cadence/contracts/bridge/FlowEVMBridgeAccessor.cdc @@ -53,7 +53,7 @@ contract FlowEVMBridgeAccessor { ): @{NonFungibleToken.NFT} { // Define a callback function, enabling the bridge to act on the ephemeral COA reference in scope var executed = false - fun callback(target: EVM.EVMAddress): EVM.Result { + fun callback(target: EVM.EVMAddress): EVM.ResultDecoded { pre { !executed: "Callback can only be executed once" FlowEVMBridge.getAssociatedEVMAddress(with: type) ?? FlowEVMBridgeConfig.getLegacyEVMAddressAssociated(with: type) != nil: @@ -69,14 +69,13 @@ contract FlowEVMBridgeAccessor { message: "Target EVM contract \(target.toString()) is not association with NFT Type \(type.identifier) - COA `safeTransferFrom` callback rejected") executed = true - return caller.call( + return caller.callWithSigAndArgs( to: target, - data: EVM.encodeABIWithSignature( - "safeTransferFrom(address,address,uint256)", - [caller.address(), FlowEVMBridge.getBridgeCOAEVMAddress(), id] - ), + signature: "safeTransferFrom(address,address,uint256)", + args: [caller.address(), FlowEVMBridge.getBridgeCOAEVMAddress(), id], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: nil ) } // Execute the bridge request @@ -129,7 +128,7 @@ contract FlowEVMBridgeAccessor { let roundedAmount = FlowEVMBridgeUtils.castERC20AmountToCadencePrecision(amount, erc20Address: associatedEVMAddress) // Define a callback function, enabling the bridge to act on the ephemeral COA reference in scope var executed = false - fun callback(): EVM.Result { + fun callback(): EVM.ResultDecoded { pre { !executed: "Callback can only be executed once" } @@ -137,14 +136,13 @@ contract FlowEVMBridgeAccessor { executed: "Callback must be executed" } executed = true - return caller.call( + return caller.callWithSigAndArgs( to: associatedEVMAddress, - data: EVM.encodeABIWithSignature( - "transfer(address,uint256)", - [FlowEVMBridge.getBridgeCOAEVMAddress(), roundedAmount] - ), + signature: "transfer(address,uint256)", + args: [FlowEVMBridge.getBridgeCOAEVMAddress(), roundedAmount], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: nil ) } // Execute the bridge request diff --git a/cadence/contracts/bridge/FlowEVMBridgeHandlers.cdc b/cadence/contracts/bridge/FlowEVMBridgeHandlers.cdc index 986f8ed7..19137964 100644 --- a/cadence/contracts/bridge/FlowEVMBridgeHandlers.cdc +++ b/cadence/contracts/bridge/FlowEVMBridgeHandlers.cdc @@ -127,7 +127,7 @@ access(all) contract FlowEVMBridgeHandlers { owner: EVM.EVMAddress, type: Type, amount: UInt256, - protectedTransferCall: fun (): EVM.Result + protectedTransferCall: fun (): EVM.ResultDecoded ): @{FungibleToken.Vault} { let evmAddress = self.getTargetEVMAddress()! @@ -262,12 +262,13 @@ access(all) contract FlowEVMBridgeHandlers { let preBalance = FlowEVMBridgeUtils.balanceOf(owner: coa.address(), evmContractAddress: wflowAddress) // Wrap the deposited FLOW as WFLOW, giving the bridge COA the necessary WFLOW to transfer - let wrapResult = FlowEVMBridgeUtils.call( + let wrapResult = FlowEVMBridgeUtils.callWithSigAndArgs( signature: "deposit()", targetEVMAddress: wflowAddress, args: [], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: balance + value: balance, + resultTypes: nil ) assert(wrapResult.status == EVM.Status.successful, message: "Failed to wrap FLOW as WFLOW") @@ -307,7 +308,7 @@ access(all) contract FlowEVMBridgeHandlers { owner: EVM.EVMAddress, type: Type, amount: UInt256, - protectedTransferCall: fun (): EVM.Result + protectedTransferCall: fun (): EVM.ResultDecoded ): @{FungibleToken.Vault} { let wflowAddress = self.getTargetEVMAddress()! @@ -335,12 +336,13 @@ access(all) contract FlowEVMBridgeHandlers { let preBalance = coa.balance().attoflow // Unwrap the transferred WFLOW to FLOW, giving the bridge COA the necessary FLOW to withdraw from EVM - let unwrapResult = FlowEVMBridgeUtils.call( + let unwrapResult = FlowEVMBridgeUtils.callWithSigAndArgs( signature: "withdraw(uint256)", targetEVMAddress: wflowAddress, args: [amount], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: nil ) assert(unwrapResult.status == EVM.Status.successful, message: "Failed to unwrap WFLOW as FLOW") diff --git a/cadence/contracts/bridge/FlowEVMBridgeUtils.cdc b/cadence/contracts/bridge/FlowEVMBridgeUtils.cdc index df2446b7..55f6e6ac 100644 --- a/cadence/contracts/bridge/FlowEVMBridgeUtils.cdc +++ b/cadence/contracts/bridge/FlowEVMBridgeUtils.cdc @@ -189,20 +189,20 @@ contract FlowEVMBridgeUtils { /// access(all) fun evmAddressAllowsBridging(_ address: EVM.EVMAddress): Bool { - let callResult = self.dryCall( + let callResult = self.dryCallWithSigAndArgs( signature: "allowsBridging()", targetEVMAddress: address, args: [], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: [Type()] ) // Contract doesn't support the method - proceed permissionlessly if callResult.status != EVM.Status.successful { return true } // Contract is IBridgePermissions - return the result - let decodedResult = EVM.decodeABI(types: [Type()], data: callResult.data) as! [AnyStruct] - return (decodedResult.length == 1 && decodedResult[0] as! Bool) == true ? true : false + return (callResult.results.length == 1 && callResult.results[0] as! Bool) == true ? true : false } /// Identifies if an asset is Cadence- or EVM-native, defined by whether a bridge contract defines it or not @@ -256,19 +256,19 @@ contract FlowEVMBridgeUtils { access(all) fun isEVMContractBridgeOwned(evmContractAddress: EVM.EVMAddress): Bool { // Ask the bridge factory if the given contract address was deployed by the bridge - let callResult = self.dryCall( - signature: "isBridgeDeployed(address)", - targetEVMAddress: self.bridgeFactoryEVMAddress, - args: [evmContractAddress], - gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 - ) + let callResult = self.dryCallWithSigAndArgs( + signature: "isBridgeDeployed(address)", + targetEVMAddress: self.bridgeFactoryEVMAddress, + args: [evmContractAddress], + gasLimit: FlowEVMBridgeConfig.gasLimit, + value: 0.0, + resultTypes: [Type()] + ) assert(callResult.status == EVM.Status.successful, message: "Call to bridge factory failed") - let decodedResult = EVM.decodeABI(types: [Type()], data: callResult.data) - assert(decodedResult.length == 1, message: "Invalid response length") + assert(callResult.results.length == 1, message: "Invalid response length") - return decodedResult[0] as! Bool + return callResult.results[0] as! Bool } /// Identifies if an asset is ERC721. Reverts on EVM call failure. @@ -279,19 +279,19 @@ contract FlowEVMBridgeUtils { /// access(all) fun isERC721(evmContractAddress: EVM.EVMAddress): Bool { - let callResult = self.dryCall( + let callResult = self.dryCallWithSigAndArgs( signature: "isERC721(address)", targetEVMAddress: self.bridgeFactoryEVMAddress, args: [evmContractAddress], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: [Type()] ) assert(callResult.status == EVM.Status.successful, message: "Call to bridge factory failed") - let decodedResult = EVM.decodeABI(types: [Type()], data: callResult.data) - assert(decodedResult.length == 1, message: "Invalid response length") + assert(callResult.results.length == 1, message: "Invalid response length") - return decodedResult[0] as! Bool + return callResult.results[0] as! Bool } /// Identifies if an asset is ERC20 as far as is possible without true EVM type introspection. Reverts on EVM call @@ -303,19 +303,19 @@ contract FlowEVMBridgeUtils { /// access(all) fun isERC20(evmContractAddress: EVM.EVMAddress): Bool { - let callResult = self.dryCall( + let callResult = self.dryCallWithSigAndArgs( signature: "isERC20(address)", targetEVMAddress: self.bridgeFactoryEVMAddress, args: [evmContractAddress], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: [Type()] ) assert(callResult.status == EVM.Status.successful, message: "Call to bridge factory failed") - let decodedResult = EVM.decodeABI(types: [Type()], data: callResult.data) - assert(decodedResult.length == 1, message: "Invalid response length") + assert(callResult.results.length == 1, message: "Invalid response length") - return decodedResult[0] as! Bool + return callResult.results[0] as! Bool } /// Returns whether the contract address is either an ERC721 or ERC20 exclusively. Reverts on EVM call failure. @@ -326,16 +326,16 @@ contract FlowEVMBridgeUtils { /// access(all) fun isValidEVMAsset(evmContractAddress: EVM.EVMAddress): Bool { - let callResult = self.dryCall( + let callResult = self.dryCallWithSigAndArgs( signature: "isValidAsset(address)", targetEVMAddress: self.bridgeFactoryEVMAddress, args: [evmContractAddress], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: [Type()] ) - let decodedResult = EVM.decodeABI(types: [Type()], data: callResult.data) - assert(decodedResult.length == 1, message: "Invalid response length") - return decodedResult[0] as! Bool + assert(callResult.results.length == 1, message: "Invalid response length") + return callResult.results[0] as! Bool } /// Returns whether the given type is either an NFT or FT exclusively @@ -526,19 +526,19 @@ contract FlowEVMBridgeUtils { /// access(all) fun getName(evmContractAddress: EVM.EVMAddress): String { - let callResult = self.dryCall( + let callResult = self.dryCallWithSigAndArgs( signature: "name()", targetEVMAddress: evmContractAddress, args: [], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: [Type()] ) assert(callResult.status == EVM.Status.successful, message: "Call for EVM asset name failed") - let decodedResult = EVM.decodeABI(types: [Type()], data: callResult.data) as! [AnyStruct] - assert(decodedResult.length == 1, message: "Invalid response length") + assert(callResult.results.length == 1, message: "Invalid response length") - return decodedResult[0] as! String + return callResult.results[0] as! String } /// Retrieves the NFT/FT symbol from the given EVM contract address - applies for both ERC20 & ERC721 @@ -550,17 +550,17 @@ contract FlowEVMBridgeUtils { /// access(all) fun getSymbol(evmContractAddress: EVM.EVMAddress): String { - let callResult = self.dryCall( + let callResult = self.dryCallWithSigAndArgs( signature: "symbol()", targetEVMAddress: evmContractAddress, args: [], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: [Type()] ) assert(callResult.status == EVM.Status.successful, message: "Call for EVM asset symbol failed") - let decodedResult = EVM.decodeABI(types: [Type()], data: callResult.data) as! [AnyStruct] - assert(decodedResult.length == 1, message: "Invalid response length") - return decodedResult[0] as! String + assert(callResult.results.length == 1, message: "Invalid response length") + return callResult.results[0] as! String } /// Retrieves the tokenURI for the given NFT ID from the given EVM contract address. Reverts on EVM call failure. @@ -573,19 +573,19 @@ contract FlowEVMBridgeUtils { /// access(all) fun getTokenURI(evmContractAddress: EVM.EVMAddress, id: UInt256): String { - let callResult = self.dryCall( + let callResult = self.dryCallWithSigAndArgs( signature: "tokenURI(uint256)", targetEVMAddress: evmContractAddress, args: [id], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: [Type()] ) assert(callResult.status == EVM.Status.successful, message: "Call to EVM for tokenURI failed") - let decodedResult = EVM.decodeABI(types: [Type()], data: callResult.data) as! [AnyStruct] - assert(decodedResult.length == 1, message: "Invalid response length") + assert(callResult.results.length == 1, message: "Invalid response length") - return decodedResult[0] as! String + return callResult.results[0] as! String } /// Retrieves the contract URI from the given EVM contract address. Returns nil on EVM call failure. @@ -596,18 +596,18 @@ contract FlowEVMBridgeUtils { /// access(all) fun getContractURI(evmContractAddress: EVM.EVMAddress): String? { - let callResult = self.dryCall( + let callResult = self.dryCallWithSigAndArgs( signature: "contractURI()", targetEVMAddress: evmContractAddress, args: [], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: [Type()] ) if callResult.status != EVM.Status.successful { return nil } - let decodedResult = EVM.decodeABI(types: [Type()], data: callResult.data) as! [AnyStruct] - return decodedResult.length == 1 ? decodedResult[0] as! String : nil + return callResult.results.length == 1 ? callResult.results[0] as! String : nil } /// Retrieves the number of decimals for a given ERC20 contract address. Reverts on EVM call failure. @@ -618,19 +618,19 @@ contract FlowEVMBridgeUtils { /// access(all) fun getTokenDecimals(evmContractAddress: EVM.EVMAddress): UInt8 { - let callResult = self.dryCall( - signature: "decimals()", - targetEVMAddress: evmContractAddress, - args: [], - gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 - ) + let callResult = self.dryCallWithSigAndArgs( + signature: "decimals()", + targetEVMAddress: evmContractAddress, + args: [], + gasLimit: FlowEVMBridgeConfig.gasLimit, + value: 0.0, + resultTypes: [Type()] + ) assert(callResult.status == EVM.Status.successful, message: "Call for EVM asset decimals failed") - let decodedResult = EVM.decodeABI(types: [Type()], data: callResult.data) as! [AnyStruct] - assert(decodedResult.length == 1, message: "Invalid response length") + assert(callResult.results.length == 1, message: "Invalid response length") - return decodedResult[0] as! UInt8 + return callResult.results[0] as! UInt8 } /// Determines if the provided owner address is either the owner or approved for the NFT in the ERC721 contract @@ -670,18 +670,18 @@ contract FlowEVMBridgeUtils { /// access(all) fun ownerOf(id: UInt256, evmContractAddress: EVM.EVMAddress): EVM.EVMAddress? { - let callResult = self.dryCall( - signature: "ownerOf(uint256)", - targetEVMAddress: evmContractAddress, - args: [id], - gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 - ) + let callResult = self.dryCallWithSigAndArgs( + signature: "ownerOf(uint256)", + targetEVMAddress: evmContractAddress, + args: [id], + gasLimit: FlowEVMBridgeConfig.gasLimit, + value: 0.0, + resultTypes: [Type()] + ) if callResult.status == EVM.Status.failed { return nil } - let decodedCallResult = EVM.decodeABI(types: [Type()], data: callResult.data) - return decodedCallResult.length == 1 ? decodedCallResult[0] as! EVM.EVMAddress : nil + return callResult.results.length == 1 ? callResult.results[0] as! EVM.EVMAddress : nil } /// Returns whether the given owner is approved for the given NFT. Reverts on EVM call failure. @@ -694,17 +694,17 @@ contract FlowEVMBridgeUtils { /// access(all) fun isApproved(ofNFT: UInt256, owner: EVM.EVMAddress, evmContractAddress: EVM.EVMAddress): Bool { - let callResult = self.dryCall( + let callResult = self.dryCallWithSigAndArgs( signature: "getApproved(uint256)", targetEVMAddress: evmContractAddress, args: [ofNFT], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: [Type()] ) assert(callResult.status == EVM.Status.successful, message: "Call to ERC721.getApproved(uint256) failed") - let decodedCallResult = EVM.decodeABI(types: [Type()], data: callResult.data) - if decodedCallResult.length == 1 { - let actualApproved = decodedCallResult[0] as! EVM.EVMAddress + if callResult.results.length == 1 { + let actualApproved = callResult.results[0] as! EVM.EVMAddress return actualApproved.equals(owner) } return false @@ -721,18 +721,16 @@ contract FlowEVMBridgeUtils { /// access(all) fun erc721Exists(erc721Address: EVM.EVMAddress, id: UInt256): Bool { - let existsResponse = EVM.decodeABI( - types: [Type()], - data: self.dryCall( - signature: "exists(uint256)", - targetEVMAddress: erc721Address, - args: [id], - gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 - ).data, - ) - assert(existsResponse.length == 1, message: "Invalid response length") - return existsResponse[0] as! Bool + let callResult = self.dryCallWithSigAndArgs( + signature: "exists(uint256)", + targetEVMAddress: erc721Address, + args: [id], + gasLimit: FlowEVMBridgeConfig.gasLimit, + value: 0.0, + resultTypes: [Type()] + ) + assert(callResult.results.length == 1, message: "Invalid response length") + return callResult.results[0] as! Bool } /// Returns the ERC20 balance of the owner at the given ERC20 contract address. Reverts on EVM call failure. @@ -745,17 +743,17 @@ contract FlowEVMBridgeUtils { /// access(all) fun balanceOf(owner: EVM.EVMAddress, evmContractAddress: EVM.EVMAddress): UInt256 { - let callResult = self.dryCall( + let callResult = self.dryCallWithSigAndArgs( signature: "balanceOf(address)", targetEVMAddress: evmContractAddress, args: [owner], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: [Type()] ) assert(callResult.status == EVM.Status.successful, message: "Call to ERC20.balanceOf(address) failed") - let decodedResult = EVM.decodeABI(types: [Type()], data: callResult.data) as! [AnyStruct] - assert(decodedResult.length == 1, message: "Invalid response length") - return decodedResult[0] as! UInt256 + assert(callResult.results.length == 1, message: "Invalid response length") + return callResult.results[0] as! UInt256 } /// Determines if the owner has sufficient funds to bridge the given amount at the ERC20 contract address @@ -780,17 +778,17 @@ contract FlowEVMBridgeUtils { /// access(all) fun totalSupply(evmContractAddress: EVM.EVMAddress): UInt256 { - let callResult = self.dryCall( + let callResult = self.dryCallWithSigAndArgs( signature: "totalSupply()", targetEVMAddress: evmContractAddress, args: [], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: [Type()] ) assert(callResult.status == EVM.Status.successful, message: "Call to ERC20.totalSupply() failed") - let decodedResult = EVM.decodeABI(types: [Type()], data: callResult.data) as! [AnyStruct] - assert(decodedResult.length == 1, message: "Invalid response length") - return decodedResult[0] as! UInt256 + assert(callResult.results.length == 1, message: "Invalid response length") + return callResult.results[0] as! UInt256 } /// Converts the given amount of ERC20 tokens to the equivalent amount in FLOW tokens based on the ERC20s decimals @@ -852,21 +850,21 @@ contract FlowEVMBridgeUtils { /// access(all) fun getDeclaredCadenceAddressFromCrossVM(evmContract: EVM.EVMAddress): Address? { - let cadenceAddrRes = self.dryCall( + let cadenceAddrRes = self.dryCallWithSigAndArgs( signature: "getCadenceAddress()", targetEVMAddress: evmContract, args: [], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: [Type()] ) if cadenceAddrRes.status != EVM.Status.successful { return nil } - let decodedCadenceAddr = EVM.decodeABI(types: [Type()], data: cadenceAddrRes.data) - assert(decodedCadenceAddr.length == 1) - var cadenceAddrStr = decodedCadenceAddr[0] as! String + assert(cadenceAddrRes.results.length == 1, message: "Invalid response length") + var cadenceAddrStr = cadenceAddrRes.results[0] as! String if cadenceAddrStr[1] != "x" { - cadenceAddrStr = "0x".concat(cadenceAddrStr) + cadenceAddrStr = "0x\(cadenceAddrStr)" } return Address.fromString(cadenceAddrStr) ?? nil } @@ -883,19 +881,19 @@ contract FlowEVMBridgeUtils { /// access(all) fun getDeclaredCadenceTypeFromCrossVM(evmContract: EVM.EVMAddress): Type? { - let cadenceIdentifierRes = self.dryCall( + let cadenceIdentifierRes = self.dryCallWithSigAndArgs( signature: "getCadenceIdentifier()", targetEVMAddress: evmContract, args: [], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: [Type()] ) if cadenceIdentifierRes.status != EVM.Status.successful { return nil } - let decodedCadenceIdentifier = EVM.decodeABI(types: [Type()], data: cadenceIdentifierRes.data) - assert(decodedCadenceIdentifier.length == 1) - let cadenceIdentifier = decodedCadenceIdentifier[0] as! String + assert(cadenceIdentifierRes.results.length == 1, message: "Invalid response length") + let cadenceIdentifier = cadenceIdentifierRes.results[0] as! String return CompositeType(cadenceIdentifier) ?? nil } @@ -910,21 +908,21 @@ contract FlowEVMBridgeUtils { access(all) fun supportsICrossVMBridgeERC721Fulfillment(evmContract: EVM.EVMAddress): Bool { let interfaceID = EVM.EVMBytes4(value: "2e608d70".decodeHex().toConstantSized<[UInt8; 4]>()!) - let supportsRes = self.dryCall( + let supportsRes = self.dryCallWithSigAndArgs( signature: "supportsInterface(bytes4)", targetEVMAddress: evmContract, args: [interfaceID], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: [Type()] ) if supportsRes.status != EVM.Status.successful { return false } - let decodedSupports = EVM.decodeABI(types: [Type()], data: supportsRes.data) - if decodedSupports.length != 1 { + if supportsRes.results.length != 1 { return false } - return decodedSupports[0] as! Bool + return supportsRes.results[0] as! Bool } /// Returns whether the provided EVM contract conforms to ICrossVMBridgeCallable.sol contract interface. @@ -938,21 +936,21 @@ contract FlowEVMBridgeUtils { access(all) fun supportsICrossVMBridgeCallable(evmContract: EVM.EVMAddress): Bool { let interfaceID = EVM.EVMBytes4(value: "b7f9a9ec".decodeHex().toConstantSized<[UInt8; 4]>()!) - let supportsRes = self.dryCall( + let supportsRes = self.dryCallWithSigAndArgs( signature: "supportsInterface(bytes4)", targetEVMAddress: evmContract, args: [interfaceID], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: [Type()] ) if supportsRes.status != EVM.Status.successful { return false } - let decodedSupports = EVM.decodeABI(types: [Type()], data: supportsRes.data) - if decodedSupports.length != 1 { + if supportsRes.results.length != 1 { return false } - return decodedSupports[0] as! Bool + return supportsRes.results[0] as! Bool } /// Returns whether the provided EVM contract conforms to both ICrossVMBridgeERC721Fulfillment and @@ -977,18 +975,18 @@ contract FlowEVMBridgeUtils { /// access(all) fun getVMBridgeAddressFromICrossVMBridgeCallable(evmContract: EVM.EVMAddress): EVM.EVMAddress? { - let cadenceIdentifierRes = self.dryCall( + let cadenceIdentifierRes = self.dryCallWithSigAndArgs( signature: "vmBridgeAddress()", targetEVMAddress: evmContract, args: [], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: [Type()] ) if cadenceIdentifierRes.status != EVM.Status.successful { return nil } - let decodedCadenceIdentifier = EVM.decodeABI(types: [Type()], data: cadenceIdentifierRes.data) - return decodedCadenceIdentifier.length == 1 ? decodedCadenceIdentifier[0] as! EVM.EVMAddress : nil + return cadenceIdentifierRes.results.length == 1 ? cadenceIdentifierRes.results[0] as! EVM.EVMAddress : nil } /************************ @@ -1306,21 +1304,23 @@ contract FlowEVMBridgeUtils { /// Shared helper simplifying calls using the bridge account's COA /// access(account) - fun call( + fun callWithSigAndArgs( signature: String, targetEVMAddress: EVM.EVMAddress, args: [AnyStruct], gasLimit: UInt64, - value: UFix64 - ): EVM.Result { - let calldata = EVM.encodeABIWithSignature(signature, args) + value: UFix64, + resultTypes: [Type]? + ): EVM.ResultDecoded { let valueBalance = EVM.Balance(attoflow: 0) valueBalance.setFLOW(flow: value) - return self.borrowCOA().call( + return self.borrowCOA().callWithSigAndArgs( to: targetEVMAddress, - data: calldata, + signature: signature, + args: args, gasLimit: gasLimit, - value: valueBalance + value: valueBalance.attoflow, + resultTypes: resultTypes ) } @@ -1328,21 +1328,23 @@ contract FlowEVMBridgeUtils { /// call within EVM, serving solely as a mechanism for retrieving data from Flow-EVM environment. /// access(account) - fun dryCall( + fun dryCallWithSigAndArgs( signature: String, targetEVMAddress: EVM.EVMAddress, args: [AnyStruct], gasLimit: UInt64, - value: UFix64 - ): EVM.Result { - let calldata = EVM.encodeABIWithSignature(signature, args) + value: UFix64, + resultTypes: [Type]? + ): EVM.ResultDecoded { let valueBalance = EVM.Balance(attoflow: 0) valueBalance.setFLOW(flow: value) - return self.borrowCOA().dryCall( + return self.borrowCOA().dryCallWithSigAndArgs( to: targetEVMAddress, - data: calldata, + signature: signature, + args: args, gasLimit: gasLimit, - value: valueBalance + value: valueBalance.attoflow, + resultTypes: resultTypes ) } @@ -1358,12 +1360,13 @@ contract FlowEVMBridgeUtils { assert(bridgePreStatus, message: "Bridge COA does not own ERC721 requesting to be transferred") assert(!toPreStatus, message: "Recipient already owns ERC721 attempting to be transferred") - let transferResult: EVM.Result = self.call( + let transferResult = self.callWithSigAndArgs( signature: "safeTransferFrom(address,address,uint256)", targetEVMAddress: erc721Address, args: [bridgeCOAAddress, to, id], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: nil ) assert( transferResult.status == EVM.Status.successful, @@ -1383,12 +1386,13 @@ contract FlowEVMBridgeUtils { fun mustSafeMintERC721(erc721Address: EVM.EVMAddress, to: EVM.EVMAddress, id: UInt256, uri: String) { let bridgeCOAAddress = self.getBridgeCOAEVMAddress() - let mintResult: EVM.Result = self.call( + let mintResult = self.callWithSigAndArgs( signature: "safeMint(address,uint256,string)", targetEVMAddress: erc721Address, args: [to, id, uri], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: nil ) assert(mintResult.status == EVM.Status.successful, message: "Mint to bridge recipient failed") @@ -1401,12 +1405,13 @@ contract FlowEVMBridgeUtils { /// access(account) fun mustFulfillNFTToEVM(erc721Address: EVM.EVMAddress, to: EVM.EVMAddress, id: UInt256, maybeBytes: EVM.EVMBytes?) { - let fulfillResult = self.call( + let fulfillResult = self.callWithSigAndArgs( signature: "fulfillToEVM(address,uint256,bytes)", targetEVMAddress: erc721Address, args: [to, id, maybeBytes ?? EVM.EVMBytes(value: [])], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: nil ) assert( fulfillResult.status == EVM.Status.successful, @@ -1425,12 +1430,13 @@ contract FlowEVMBridgeUtils { fun mustUpdateTokenURI(erc721Address: EVM.EVMAddress, id: UInt256, uri: String) { let bridgeCOAAddress = self.getBridgeCOAEVMAddress() - let updateResult: EVM.Result = self.call( + let updateResult = self.callWithSigAndArgs( signature: "updateTokenURI(uint256,string)", targetEVMAddress: erc721Address, args: [id, uri], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: nil ) assert(updateResult.status == EVM.Status.successful, message: "URI update failed") } @@ -1444,7 +1450,7 @@ contract FlowEVMBridgeUtils { owner: EVM.EVMAddress, id: UInt256, erc721Address: EVM.EVMAddress, - protectedTransferCall: fun (EVM.EVMAddress): EVM.Result + protectedTransferCall: fun (EVM.EVMAddress): EVM.ResultDecoded ) { // Ensure the named owner is authorized to act on the NFT let isAuthorized = self.isOwnerOrApproved(ofNFT: id, owner: owner, evmContractAddress: erc721Address) @@ -1476,12 +1482,13 @@ contract FlowEVMBridgeUtils { ) let bridgeCOA = self.getBridgeCOAEVMAddress() - let unwrapResult: EVM.Result = self.call( + let unwrapResult = self.callWithSigAndArgs( signature: "withdrawTo(address,uint256[])", targetEVMAddress: erc721WrapperAddress, args: [bridgeCOA, [id]], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: nil ) assert( unwrapResult.status == EVM.Status.successful, @@ -1500,12 +1507,13 @@ contract FlowEVMBridgeUtils { fun mustMintERC20(to: EVM.EVMAddress, amount: UInt256, erc20Address: EVM.EVMAddress) { let toPreBalance = self.balanceOf(owner: to, evmContractAddress: erc20Address) // Mint tokens to the recipient - let mintResult: EVM.Result = self.call( + let mintResult = self.callWithSigAndArgs( signature: "mint(address,uint256)", targetEVMAddress: erc20Address, args: [to, amount], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: nil ) assert(mintResult.status == EVM.Status.successful, message: "Mint to bridge ERC20 contract failed") // Ensure bridge to recipient was succcessful @@ -1530,12 +1538,13 @@ contract FlowEVMBridgeUtils { ) // Transfer tokens to the recipient - let transferResult: EVM.Result = self.call( + let transferResult = self.callWithSigAndArgs( signature: "transfer(address,uint256)", targetEVMAddress: erc20Address, args: [to, amount], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: nil ) assert(transferResult.status == EVM.Status.successful, message: "transfer call to ERC20 contract failed") @@ -1564,7 +1573,7 @@ contract FlowEVMBridgeUtils { owner: EVM.EVMAddress, amount: UInt256, erc20Address: EVM.EVMAddress, - protectedTransferCall: fun (): EVM.Result + protectedTransferCall: fun (): EVM.ResultDecoded ) { // Ensure the caller is has sufficient balance to bridge the requested amount let hasSufficientBalance = self.hasSufficientBalance( @@ -1602,12 +1611,13 @@ contract FlowEVMBridgeUtils { /// access(account) fun mustBurnERC721(erc721Address: EVM.EVMAddress, id: UInt256) { - let burnResult = FlowEVMBridgeUtils.call( + let burnResult = FlowEVMBridgeUtils.callWithSigAndArgs( signature: "burn(uint256)", targetEVMAddress: erc721Address, args: [id], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: nil ) assert(burnResult.status == EVM.Status.successful, message: "0x\(erc721Address.toString()).burn(\(id)) failed with error code \(burnResult.errorCode) and message: \(burnResult.errorMessage)") @@ -1625,17 +1635,17 @@ contract FlowEVMBridgeUtils { isERC721: Bool ): EVM.EVMAddress { let deployerTag = isERC721 ? "ERC721" : "ERC20" - let deployResult: EVM.Result = self.call( + let deployResult = self.callWithSigAndArgs( signature: "deploy(string,string,string,string,string,string)", targetEVMAddress: self.bridgeFactoryEVMAddress, args: [deployerTag, name, symbol, cadenceAddress.toString(), flowIdentifier, contractURI], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: [Type()] ) assert(deployResult.status == EVM.Status.successful, message: "EVM Token contract deployment failed") - let decodedResult: [AnyStruct] = EVM.decodeABI(types: [Type()], data: deployResult.data) - assert(decodedResult.length == 1, message: "Invalid response length") - return decodedResult[0] as! EVM.EVMAddress + assert(deployResult.results.length == 1, message: "Invalid response length") + return deployResult.results[0] as! EVM.EVMAddress } /// Calls `setSymbol(string)` on the EVM contract as exposed on FlowEVMBridgedERC721 contracts, enabling Cadence @@ -1644,12 +1654,13 @@ contract FlowEVMBridgeUtils { /// access(account) fun tryUpdateSymbol(_ evmContractAddress: EVM.EVMAddress, symbol: String): Bool { - return self.call( + return self.callWithSigAndArgs( signature: "setSymbol(string)", targetEVMAddress: evmContractAddress, args: [symbol], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: 0.0 + value: 0.0, + resultTypes: nil ).status == EVM.Status.successful } diff --git a/cadence/contracts/bridge/interfaces/FlowEVMBridgeHandlerInterfaces.cdc b/cadence/contracts/bridge/interfaces/FlowEVMBridgeHandlerInterfaces.cdc index e22869d4..c9b09770 100644 --- a/cadence/contracts/bridge/interfaces/FlowEVMBridgeHandlerInterfaces.cdc +++ b/cadence/contracts/bridge/interfaces/FlowEVMBridgeHandlerInterfaces.cdc @@ -188,7 +188,7 @@ access(all) contract FlowEVMBridgeHandlerInterfaces { owner: EVM.EVMAddress, type: Type, amount: UInt256, - protectedTransferCall: fun (): EVM.Result + protectedTransferCall: fun (): EVM.ResultDecoded ): @{FungibleToken.Vault} { pre { self.isEnabled(): diff --git a/cadence/contracts/bridge/interfaces/IFlowEVMNFTBridge.cdc b/cadence/contracts/bridge/interfaces/IFlowEVMNFTBridge.cdc index 0dee5839..7d04c8ad 100644 --- a/cadence/contracts/bridge/interfaces/IFlowEVMNFTBridge.cdc +++ b/cadence/contracts/bridge/interfaces/IFlowEVMNFTBridge.cdc @@ -101,7 +101,7 @@ access(all) contract interface IFlowEVMNFTBridge { type: Type, id: UInt256, feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}, - protectedTransferCall: fun (EVM.EVMAddress): EVM.Result + protectedTransferCall: fun (EVM.EVMAddress): EVM.ResultDecoded ): @{NonFungibleToken.NFT} { post { emit BridgedNFTFromEVM( diff --git a/cadence/contracts/bridge/interfaces/IFlowEVMTokenBridge.cdc b/cadence/contracts/bridge/interfaces/IFlowEVMTokenBridge.cdc index 7033ebef..1e45628a 100644 --- a/cadence/contracts/bridge/interfaces/IFlowEVMTokenBridge.cdc +++ b/cadence/contracts/bridge/interfaces/IFlowEVMTokenBridge.cdc @@ -95,7 +95,7 @@ access(all) contract interface IFlowEVMTokenBridge { type: Type, amount: UInt256, feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}, - protectedTransferCall: fun (): EVM.Result + protectedTransferCall: fun (): EVM.ResultDecoded ): @{FungibleToken.Vault} { post { emit BridgedTokensFromEVM( diff --git a/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFT.cdc b/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFT.cdc index f7346411..5bf457e6 100644 --- a/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFT.cdc +++ b/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFT.cdc @@ -53,63 +53,65 @@ access(all) contract ExampleEVMNativeNFT: NonFungibleToken, ICrossVM, ICrossVMAs /// Returns the token URI for the provided ERC721 id, treating the corresponding ERC721 as the source of truth access(all) fun tokenURI(id: UInt256): String { - let tokenURIRes = self.call( - signature: "tokenURI(uint256)", - targetEVMAddress: self.getEVMContractAddress(), - args: [id], - gasLimit: 100_000, - value: 0.0 - ) + let tokenURIRes = self.callWithSigAndArgs( + signature: "tokenURI(uint256)", + targetEVMAddress: self.getEVMContractAddress(), + args: [id], + gasLimit: 100_000, + value: 0.0, + resultTypes: [Type()] + ) assert( tokenURIRes.status == EVM.Status.successful, message: "Error calling ERC721.tokenURI(uint256) with message: ".concat(tokenURIRes.errorMessage) ) - let decodedURIData = EVM.decodeABI(types: [Type()], data: tokenURIRes.data) assert( - decodedURIData.length == 1, - message: "Unexpected tokenURI(uint256) return length of ".concat(decodedURIData.length.toString()) + tokenURIRes.results.length == 1, + message: "Unexpected tokenURI(uint256) return length of ".concat(tokenURIRes.results.length.toString()) ) - return decodedURIData[0] as! String + return tokenURIRes.results[0] as! String } /// Returns the token URI for the provided ERC721 id, treating the corresponding ERC721 as the source of truth access(all) fun contractURI(): String { - let contractURIRes = self.call( - signature: "contractURI()", - targetEVMAddress: self.getEVMContractAddress(), - args: [], - gasLimit: 100_000, - value: 0.0 - ) + let contractURIRes = self.callWithSigAndArgs( + signature: "contractURI()", + targetEVMAddress: self.getEVMContractAddress(), + args: [], + gasLimit: 100_000, + value: 0.0, + resultTypes: [Type()] + ) assert( contractURIRes.status == EVM.Status.successful, message: "Error calling ERC721.contractURI(uint256) with message: ".concat(contractURIRes.errorMessage) ) - let decodedURIData = EVM.decodeABI(types: [Type()], data: contractURIRes.data) assert( - decodedURIData.length == 1, - message: "Unexpected contractURI(uint256) return length of ".concat(decodedURIData.length.toString()) + contractURIRes.results.length == 1, + message: "Unexpected contractURI(uint256) return length of ".concat(contractURIRes.results.length.toString()) ) - return decodedURIData[0] as! String + return contractURIRes.results[0] as! String } /* --- Internal Helpers --- */ - access(self) fun call( + access(self) fun callWithSigAndArgs( signature: String, targetEVMAddress: EVM.EVMAddress, args: [AnyStruct], gasLimit: UInt64, - value: UFix64 - ): EVM.Result { - let calldata = EVM.encodeABIWithSignature(signature, args) + value: UFix64, + resultTypes: [Type]? + ): EVM.ResultDecoded { let valueBalance = EVM.Balance(attoflow: 0) valueBalance.setFLOW(flow: value) - return self.borrowCOA().call( + return self.borrowCOA().callWithSigAndArgs( to: targetEVMAddress, - data: calldata, + signature: signature, + args: args, gasLimit: gasLimit, - value: valueBalance + value: valueBalance.attoflow, + resultTypes: resultTypes ) } @@ -431,17 +433,21 @@ access(all) contract ExampleEVMNativeNFT: NonFungibleToken, ICrossVM, ICrossVMAs self.evmContractAddress = deployResult.deployedContract! // Assign name & symbol based on ERC721 contract - let nameRes = coa.call( + let nameRes = coa.callWithSigAndArgs( to: self.evmContractAddress, - data: EVM.encodeABIWithSignature("name()", []), + signature: "name()", + args: [], gasLimit: 100_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: [Type()] ) - let symbolRes = coa.call( + let symbolRes = coa.callWithSigAndArgs( to: self.evmContractAddress, - data: EVM.encodeABIWithSignature("symbol()", []), + signature: "symbol()", + args: [], gasLimit: 100_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: [Type()] ) assert( nameRes.status == EVM.Status.successful, @@ -452,13 +458,10 @@ access(all) contract ExampleEVMNativeNFT: NonFungibleToken, ICrossVM, ICrossVMAs message: "Error on ERC721.symbol() call with message: ".concat(symbolRes.errorMessage) ) - let decodedNameData = EVM.decodeABI(types: [Type()], data: nameRes.data) - let decodedSymbolData = EVM.decodeABI(types: [Type()], data: symbolRes.data) - assert(decodedNameData.length == 1, message: "Unexpected name() return length of ".concat(decodedNameData.length.toString())) - assert(decodedSymbolData.length == 1, message: "Unexpected symbol() return length of ".concat(decodedSymbolData.length.toString())) + assert(nameRes.results.length == 1, message: "Unexpected name() return length of ".concat(nameRes.results.length.toString())) + assert(symbolRes.results.length == 1, message: "Unexpected symbol() return length of ".concat(symbolRes.results.length.toString())) - self.name = decodedNameData[0] as! String - self.symbol = decodedSymbolData[0] as! String + self.name = nameRes.results[0] as! String + self.symbol = symbolRes.results[0] as! String } } - \ No newline at end of file diff --git a/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFTGivenEVMAddress.cdc b/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFTGivenEVMAddress.cdc index ed1c5f9e..9374dcd7 100644 --- a/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFTGivenEVMAddress.cdc +++ b/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFTGivenEVMAddress.cdc @@ -53,63 +53,65 @@ access(all) contract ExampleEVMNativeNFTGivenEVMAddress: NonFungibleToken, ICros /// Returns the token URI for the provided ERC721 id, treating the corresponding ERC721 as the source of truth access(all) fun tokenURI(id: UInt256): String { - let tokenURIRes = self.call( - signature: "tokenURI(uint256)", - targetEVMAddress: self.getEVMContractAddress(), - args: [id], - gasLimit: 100_000, - value: 0.0 - ) + let tokenURIRes = self.callWithSigAndArgs( + signature: "tokenURI(uint256)", + targetEVMAddress: self.getEVMContractAddress(), + args: [id], + gasLimit: 100_000, + value: 0.0, + resultTypes: [Type()] + ) assert( tokenURIRes.status == EVM.Status.successful, message: "Error calling ERC721.tokenURI(uint256) with message: ".concat(tokenURIRes.errorMessage) ) - let decodedURIData = EVM.decodeABI(types: [Type()], data: tokenURIRes.data) assert( - decodedURIData.length == 1, - message: "Unexpected tokenURI(uint256) return length of ".concat(decodedURIData.length.toString()) + tokenURIRes.results.length == 1, + message: "Unexpected tokenURI(uint256) return length of ".concat(tokenURIRes.results.length.toString()) ) - return decodedURIData[0] as! String + return tokenURIRes.results[0] as! String } /// Returns the token URI for the provided ERC721 id, treating the corresponding ERC721 as the source of truth access(all) fun contractURI(): String { - let contractURIRes = self.call( - signature: "contractURI()", - targetEVMAddress: self.getEVMContractAddress(), - args: [], - gasLimit: 100_000, - value: 0.0 - ) + let contractURIRes = self.callWithSigAndArgs( + signature: "contractURI()", + targetEVMAddress: self.getEVMContractAddress(), + args: [], + gasLimit: 100_000, + value: 0.0, + resultTypes: [Type()] + ) assert( contractURIRes.status == EVM.Status.successful, message: "Error calling ERC721.contractURI(uint256) with message: ".concat(contractURIRes.errorMessage) ) - let decodedURIData = EVM.decodeABI(types: [Type()], data: contractURIRes.data) assert( - decodedURIData.length == 1, - message: "Unexpected contractURI(uint256) return length of ".concat(decodedURIData.length.toString()) + contractURIRes.results.length == 1, + message: "Unexpected contractURI(uint256) return length of ".concat(contractURIRes.results.length.toString()) ) - return decodedURIData[0] as! String + return contractURIRes.results[0] as! String } /* --- Internal Helpers --- */ - access(self) fun call( + access(self) fun callWithSigAndArgs( signature: String, targetEVMAddress: EVM.EVMAddress, args: [AnyStruct], gasLimit: UInt64, - value: UFix64 - ): EVM.Result { - let calldata = EVM.encodeABIWithSignature(signature, args) + value: UFix64, + resultTypes: [Type]? + ): EVM.ResultDecoded { let valueBalance = EVM.Balance(attoflow: 0) valueBalance.setFLOW(flow: value) - return self.borrowCOA().call( + return self.borrowCOA().callWithSigAndArgs( to: targetEVMAddress, - data: calldata, + signature: signature, + args: args, gasLimit: gasLimit, - value: valueBalance + value: valueBalance.attoflow, + resultTypes: resultTypes ) } @@ -413,17 +415,21 @@ access(all) contract ExampleEVMNativeNFTGivenEVMAddress: NonFungibleToken, ICros self.evmContractAddress = EVM.addressFromString(erc721Address) // Assign name & symbol based on ERC721 contract - let nameRes = coa.call( + let nameRes = coa.callWithSigAndArgs( to: self.evmContractAddress, - data: EVM.encodeABIWithSignature("name()", []), + signature: "name()", + args: [], gasLimit: 100_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: [Type()] ) - let symbolRes = coa.call( + let symbolRes = coa.callWithSigAndArgs( to: self.evmContractAddress, - data: EVM.encodeABIWithSignature("symbol()", []), + signature: "symbol()", + args: [], gasLimit: 100_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: [Type()] ) assert( nameRes.status == EVM.Status.successful, @@ -434,13 +440,10 @@ access(all) contract ExampleEVMNativeNFTGivenEVMAddress: NonFungibleToken, ICros message: "Error on ERC721.symbol() call with message: ".concat(symbolRes.errorMessage) ) - let decodedNameData = EVM.decodeABI(types: [Type()], data: nameRes.data) - let decodedSymbolData = EVM.decodeABI(types: [Type()], data: symbolRes.data) - assert(decodedNameData.length == 1, message: "Unexpected name() return length of ".concat(decodedNameData.length.toString())) - assert(decodedSymbolData.length == 1, message: "Unexpected symbol() return length of ".concat(decodedSymbolData.length.toString())) + assert(nameRes.results.length == 1, message: "Unexpected name() return length of ".concat(nameRes.results.length.toString())) + assert(symbolRes.results.length == 1, message: "Unexpected symbol() return length of ".concat(symbolRes.results.length.toString())) - self.name = decodedNameData[0] as! String - self.symbol = decodedSymbolData[0] as! String + self.name = nameRes.results[0] as! String + self.symbol = symbolRes.results[0] as! String } } - \ No newline at end of file diff --git a/cadence/scripts/utils/get_deployer_address.cdc b/cadence/scripts/utils/get_deployer_address.cdc index 8fa52675..fd1436a1 100644 --- a/cadence/scripts/utils/get_deployer_address.cdc +++ b/cadence/scripts/utils/get_deployer_address.cdc @@ -9,11 +9,13 @@ fun main(coaHost: Address, deployerTag: String): String { .storage .borrow(from: /storage/evm) ?? panic("Could not borrow CadenceOwnedAccount from host=".concat(coaHost.toString())) - let res = coa.call( + let res = coa.callWithSigAndArgs( to: FlowEVMBridgeUtils.getBridgeFactoryEVMAddress(), - data: EVM.encodeABIWithSignature("getDeployer(string)", [deployerTag]), + signature: "getDeployer(string)", + args: [deployerTag], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: EVM.Balance(attoflow: UInt(0)) + value: 0, + resultTypes: [Type()] ) assert( @@ -21,9 +23,7 @@ fun main(coaHost: Address, deployerTag: String): String { message: "getRegistry call to FlowEVMBridgeFactory failed" ) - let decodedRes = EVM.decodeABI(types: [Type()], data: res.data) + assert(res.results.length == 1, message: "Invalid response length") - assert(decodedRes.length == 1, message: "Invalid response length") - - return (decodedRes[0] as! EVM.EVMAddress).toString() + return (res.results[0] as! EVM.EVMAddress).toString() } diff --git a/cadence/scripts/utils/get_registry_address.cdc b/cadence/scripts/utils/get_registry_address.cdc index 687d0906..c7de437e 100644 --- a/cadence/scripts/utils/get_registry_address.cdc +++ b/cadence/scripts/utils/get_registry_address.cdc @@ -9,11 +9,13 @@ fun main(coaHost: Address): String { .storage .borrow(from: /storage/evm) ?? panic("Could not borrow CadenceOwnedAccount from host=".concat(coaHost.toString())) - let res = coa.call( + let res = coa.callWithSigAndArgs( to: FlowEVMBridgeUtils.getBridgeFactoryEVMAddress(), - data: EVM.encodeABIWithSignature("getRegistry()", []), + signature: "getRegistry()", + args: [], gasLimit: FlowEVMBridgeConfig.gasLimit, - value: EVM.Balance(attoflow: UInt(0)) + value: 0, + resultTypes: [Type()] ) assert( @@ -21,9 +23,7 @@ fun main(coaHost: Address): String { message: "getRegistry call to FlowEVMBridgeFactory failed" ) - let decodedRes = EVM.decodeABI(types: [Type()], data: res.data) + assert(res.results.length == 1, message: "Invalid response length") - assert(decodedRes.length == 1, message: "Invalid response length") - - return (decodedRes[0] as! EVM.EVMAddress).toString() + return (res.results[0] as! EVM.EVMAddress).toString() } diff --git a/cadence/tests/scripts/get_registry.cdc b/cadence/tests/scripts/get_registry.cdc index b6eb4172..e12d8827 100644 --- a/cadence/tests/scripts/get_registry.cdc +++ b/cadence/tests/scripts/get_registry.cdc @@ -10,21 +10,19 @@ fun main(): String { from: /storage/evm ) ?? panic("Problem borrowing COA") // Confirm the registry address was set - let postRegistryResult = coa.call( + let postRegistryResult = coa.callWithSigAndArgs( to: FlowEVMBridgeUtils.getBridgeFactoryEVMAddress(), - data: EVM.encodeABIWithSignature("owner()", []), + signature: "owner()", + args: [], gasLimit: 15_000_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: [Type()] ) assert( postRegistryResult.status == EVM.Status.successful, message: "Failed to get registry address from FlowBridgeFactory contract" ) - let decodedResult = EVM.decodeABI( - types: [Type()], - data: postRegistryResult.data - ) - assert(decodedResult.length == 1, message: "Invalid response from getRegistry() call to FlowBridgeFactory contract") - return (decodedResult[0] as! EVM.EVMAddress).toString() -} \ No newline at end of file + assert(postRegistryResult.results.length == 1, message: "Invalid response from getRegistry() call to FlowBridgeFactory contract") + return (postRegistryResult.results[0] as! EVM.EVMAddress).toString() +} diff --git a/cadence/transactions/bridge/admin/evm/add_deployer.cdc b/cadence/transactions/bridge/admin/evm/add_deployer.cdc index a4be41f0..91c118ff 100644 --- a/cadence/transactions/bridge/admin/evm/add_deployer.cdc +++ b/cadence/transactions/bridge/admin/evm/add_deployer.cdc @@ -23,35 +23,29 @@ transaction(deployerTag: String, deployerEVMAddressHex: String) { execute { // Execute the call - let callResult = self.coa.call( + let callResult = self.coa.callWithSigAndArgs( to: FlowEVMBridgeUtils.getBridgeFactoryEVMAddress(), - data: EVM.encodeABIWithSignature( - "addDeployer(string,address)", - [deployerTag, self.targetDeployerEVMAddress] - ), + signature: "addDeployer(string,address)", + args: [deployerTag, self.targetDeployerEVMAddress], gasLimit: 15_000_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: nil ) assert(callResult.status == EVM.Status.successful, message: "Failed to add deployer") // Confirm the deployer was added under the tag - let postDeployerResult = self.coa.call( + let postDeployerResult = self.coa.callWithSigAndArgs( to: FlowEVMBridgeUtils.getBridgeFactoryEVMAddress(), - data: EVM.encodeABIWithSignature( - "getDeployer(string)", - [deployerTag] - ), + signature: "getDeployer(string)", + args: [deployerTag], gasLimit: 15_000_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: [Type()] ) assert(postDeployerResult.status == EVM.Status.successful, message: "Failed to get deployer") - let decodedResult = EVM.decodeABI( - types: [Type()], - data: postDeployerResult.data - ) - assert(decodedResult.length == 1, message: "Invalid response from getDeployer call") - self.postDeployer = decodedResult[0] as! EVM.EVMAddress + assert(postDeployerResult.results.length == 1, message: "Invalid response from getDeployer call") + self.postDeployer = postDeployerResult.results[0] as! EVM.EVMAddress } post { diff --git a/cadence/transactions/bridge/admin/evm/set_delegated_deployer.cdc b/cadence/transactions/bridge/admin/evm/set_delegated_deployer.cdc index a7a02b38..af52c69f 100644 --- a/cadence/transactions/bridge/admin/evm/set_delegated_deployer.cdc +++ b/cadence/transactions/bridge/admin/evm/set_delegated_deployer.cdc @@ -22,32 +22,29 @@ transaction(deployerEVMAddressHex: String) { execute { // Execute the call - let callResult = self.coa.call( + let callResult = self.coa.callWithSigAndArgs( to: self.targetDeployerEVMAddress, - data: EVM.encodeABIWithSignature( - "setDelegatedDeployer(address)", - [FlowEVMBridgeUtils.getBridgeFactoryEVMAddress()] - ), + signature: "setDelegatedDeployer(address)", + args: [FlowEVMBridgeUtils.getBridgeFactoryEVMAddress()], gasLimit: 15_000_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: nil ) assert(callResult.status == EVM.Status.successful, message: "Failed to set delegated deployer") // Confirm the delegated deployer was set - let postDelegatedDeployerResult = self.coa.call( + let postDelegatedDeployerResult = self.coa.callWithSigAndArgs( to: self.targetDeployerEVMAddress, - data: EVM.encodeABIWithSignature("delegatedDeployer()", []), + signature: "delegatedDeployer()", + args: [], gasLimit: 15_000_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: [Type()] ) assert(postDelegatedDeployerResult.status == EVM.Status.successful, message: "Failed to get delegated deployer") - let decodedResult = EVM.decodeABI( - types: [Type()], - data: postDelegatedDeployerResult.data - ) as! [AnyStruct] - assert(decodedResult.length == 1, message: "Invalid response from delegatedDeployer() call") - self.postDelegatedDeployer = decodedResult[0] as! EVM.EVMAddress + assert(postDelegatedDeployerResult.results.length == 1, message: "Invalid response from delegatedDeployer() call") + self.postDelegatedDeployer = postDelegatedDeployerResult.results[0] as! EVM.EVMAddress } post { diff --git a/cadence/transactions/bridge/admin/evm/set_deployment_registry.cdc b/cadence/transactions/bridge/admin/evm/set_deployment_registry.cdc index 68bd94aa..4197f382 100644 --- a/cadence/transactions/bridge/admin/evm/set_deployment_registry.cdc +++ b/cadence/transactions/bridge/admin/evm/set_deployment_registry.cdc @@ -26,35 +26,32 @@ transaction(registryEVMAddressHex: String) { execute { // Execute call - let callResult = self.coa.call( + let callResult = self.coa.callWithSigAndArgs( to: FlowEVMBridgeUtils.getBridgeFactoryEVMAddress(), - data: EVM.encodeABIWithSignature( - "setDeploymentRegistry(address)", - [self.targetRegistryEVMAddress] - ), + signature: "setDeploymentRegistry(address)", + args: [self.targetRegistryEVMAddress], gasLimit: 15_000_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: nil ) assert(callResult.status == EVM.Status.successful, message: "Failed to set registry in FlowBridgeFactory contract") // Confirm the registry address was set - let postRegistryResult = self.coa.call( + let postRegistryResult = self.coa.callWithSigAndArgs( to: FlowEVMBridgeUtils.getBridgeFactoryEVMAddress(), - data: EVM.encodeABIWithSignature("getRegistry()", []), + signature: "getRegistry()", + args: [], gasLimit: 15_000_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: [Type()] ) assert( postRegistryResult.status == EVM.Status.successful, message: "Failed to get registry address from FlowBridgeFactory contract" ) - let decodedResult = EVM.decodeABI( - types: [Type()], - data: postRegistryResult.data - ) - assert(decodedResult.length == 1, message: "Invalid response from getRegistry() call to FlowBridgeFactory contract") - self.postRegistry = decodedResult[0] as! EVM.EVMAddress + assert(postRegistryResult.results.length == 1, message: "Invalid response from getRegistry() call to FlowBridgeFactory contract") + self.postRegistry = postRegistryResult.results[0] as! EVM.EVMAddress } post { diff --git a/cadence/transactions/bridge/admin/evm/set_registrar.cdc b/cadence/transactions/bridge/admin/evm/set_registrar.cdc index 4b8c2e33..aef96926 100644 --- a/cadence/transactions/bridge/admin/evm/set_registrar.cdc +++ b/cadence/transactions/bridge/admin/evm/set_registrar.cdc @@ -21,32 +21,29 @@ transaction(registryEVMAddressHex: String) { } execute { - let callResult = self.coa.call( + let callResult = self.coa.callWithSigAndArgs( to: self.targetRegistryEVMAddress, - data: EVM.encodeABIWithSignature( - "setRegistrar(address)", - [FlowEVMBridgeUtils.getBridgeFactoryEVMAddress()] - ), + signature: "setRegistrar(address)", + args: [FlowEVMBridgeUtils.getBridgeFactoryEVMAddress()], gasLimit: 15_000_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: nil ) assert(callResult.status == EVM.Status.successful, message: "Failed to set registrar") // Confirm the registrar was set - let postRegistrarResult = self.coa.call( + let postRegistrarResult = self.coa.callWithSigAndArgs( to: self.targetRegistryEVMAddress, - data: EVM.encodeABIWithSignature("registrar()", []), + signature: "registrar()", + args: [], gasLimit: 15_000_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: [Type()] ) assert(postRegistrarResult.status == EVM.Status.successful, message: "Failed to get registrar") - let decodedResult = EVM.decodeABI( - types: [Type()], - data: postRegistrarResult.data - ) - assert(decodedResult.length == 1, message: "Invalid response from registrar() call to registry contract") - self.postRegistrar = decodedResult[0] as! EVM.EVMAddress + assert(postRegistrarResult.results.length == 1, message: "Invalid response from registrar() call to registry contract") + self.postRegistrar = postRegistrarResult.results[0] as! EVM.EVMAddress } post { diff --git a/cadence/transactions/bridge/admin/evm/upsert_deployer.cdc b/cadence/transactions/bridge/admin/evm/upsert_deployer.cdc index a8ab11cf..07f48a57 100644 --- a/cadence/transactions/bridge/admin/evm/upsert_deployer.cdc +++ b/cadence/transactions/bridge/admin/evm/upsert_deployer.cdc @@ -23,35 +23,29 @@ transaction(deployerTag: String, deployerEVMAddressHex: String) { execute { // Execute the call - let callResult = self.coa.call( + let callResult = self.coa.callWithSigAndArgs( to: FlowEVMBridgeUtils.getBridgeFactoryEVMAddress(), - data: EVM.encodeABIWithSignature( - "upsertDeployer(string,address)", - [deployerTag, self.targetDeployerEVMAddress] - ), + signature: "upsertDeployer(string,address)", + args: [deployerTag, self.targetDeployerEVMAddress], gasLimit: 15_000_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: nil ) assert(callResult.status == EVM.Status.successful, message: "Failed to add deployer") // Confirm the deployer was added under the tag - let postDeployerResult = self.coa.call( + let postDeployerResult = self.coa.callWithSigAndArgs( to: FlowEVMBridgeUtils.getBridgeFactoryEVMAddress(), - data: EVM.encodeABIWithSignature( - "getDeployer(string)", - [deployerTag] - ), + signature: "getDeployer(string)", + args: [deployerTag], gasLimit: 15_000_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: [Type()] ) assert(postDeployerResult.status == EVM.Status.successful, message: "Failed to get deployer") - let decodedResult = EVM.decodeABI( - types: [Type()], - data: postDeployerResult.data - ) - assert(decodedResult.length == 1, message: "Invalid response from getDeployer call") - self.postDeployer = decodedResult[0] as! EVM.EVMAddress + assert(postDeployerResult.results.length == 1, message: "Invalid response from getDeployer call") + self.postDeployer = postDeployerResult.results[0] as! EVM.EVMAddress } post { diff --git a/cadence/transactions/example-assets/evm-assets/mint_erc20.cdc b/cadence/transactions/example-assets/evm-assets/mint_erc20.cdc index 891b9e32..f78ebb4a 100644 --- a/cadence/transactions/example-assets/evm-assets/mint_erc20.cdc +++ b/cadence/transactions/example-assets/evm-assets/mint_erc20.cdc @@ -17,15 +17,13 @@ transaction( execute { let recipientAddress = EVM.addressFromString(recipientHexAddress) let erc20Address = EVM.addressFromString(erc20HexAddress) - let calldata = EVM.encodeABIWithSignature( - "mint(address,uint256)", - [recipientAddress, amount] - ) - let callResult = self.coa.call( + let callResult = self.coa.callWithSigAndArgs( to: erc20Address, - data: calldata, + signature: "mint(address,uint256)", + args: [recipientAddress, amount], gasLimit: gasLimit, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: nil ) assert(callResult.status == EVM.Status.successful, message: "ERC20 mint failed with code: ".concat(callResult.errorCode.toString())) } 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 a8beef41..3cb18dfd 100644 --- a/cadence/transactions/example-assets/evm-assets/safe_mint_erc721.cdc +++ b/cadence/transactions/example-assets/evm-assets/safe_mint_erc721.cdc @@ -18,15 +18,13 @@ transaction( execute { let recipientAddress = EVM.addressFromString(recipientHexAddress) let erc721Address = EVM.addressFromString(erc721HexAddress) - let calldata = EVM.encodeABIWithSignature( - "safeMint(address,uint256,string)", - [recipientAddress, tokenId, uri] - ) - let callResult = self.coa.call( + let callResult = self.coa.callWithSigAndArgs( to: erc721Address, - data: calldata, + signature: "safeMint(address,uint256,string)", + args: [recipientAddress, tokenId, uri], gasLimit: gasLimit, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: nil ) assert(callResult.status == EVM.Status.successful, message: "ERC721 mint failed with code: ".concat(callResult.errorCode.toString())) } diff --git a/cadence/transactions/example-assets/evm-assets/safe_transfer_from_erc721.cdc b/cadence/transactions/example-assets/evm-assets/safe_transfer_from_erc721.cdc index 478d09f3..28d2cab8 100644 --- a/cadence/transactions/example-assets/evm-assets/safe_transfer_from_erc721.cdc +++ b/cadence/transactions/example-assets/evm-assets/safe_transfer_from_erc721.cdc @@ -28,22 +28,20 @@ transaction(evmContractAddressHex: String, recipientAddressHex: String, id: UInt } execute { - let calldata = EVM.encodeABIWithSignature( - "safeTransferFrom(address,address,uint256)", - [self.coa.address(), self.recipientAddress, id] - ) - let callResult = self.coa.call( + let callResult = self.coa.callWithSigAndArgs( to: self.evmContractAddress, - data: calldata, + signature: "safeTransferFrom(address,address,uint256)", + args: [self.coa.address(), self.recipientAddress, id], gasLimit: 15_000_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: nil ) assert(callResult.status == EVM.Status.successful, message: "Call to ERC721 contract failed") self.recipientOwnerCheck = FlowEVMBridgeUtils.isOwnerOrApproved( - ofNFT: id, - owner: self.recipientAddress, - evmContractAddress: self.evmContractAddress - ) + ofNFT: id, + owner: self.recipientAddress, + evmContractAddress: self.evmContractAddress + ) } post { diff --git a/cadence/transactions/example-assets/evm-assets/transfer_erc20.cdc b/cadence/transactions/example-assets/evm-assets/transfer_erc20.cdc index 133334b4..5aeb6d30 100644 --- a/cadence/transactions/example-assets/evm-assets/transfer_erc20.cdc +++ b/cadence/transactions/example-assets/evm-assets/transfer_erc20.cdc @@ -24,12 +24,13 @@ transaction(evmContractAddressHex: String, recipientAddressHex: String, amount: } execute { - let calldata = EVM.encodeABIWithSignature("transfer(address,uint256)", [self.recipientAddress, amount]) - let callResult = self.coa.call( + let callResult = self.coa.callWithSigAndArgs( to: self.evmContractAddress, - data: calldata, + signature: "transfer(address,uint256)", + args: [self.recipientAddress, amount], gasLimit: 15_000_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: nil ) assert(callResult.status == EVM.Status.successful, message: "Call to ERC20 contract failed") self.postBalance = FlowEVMBridgeUtils.balanceOf(owner: self.coa.address(), evmContractAddress: self.evmContractAddress) diff --git a/cadence/transactions/example-assets/evm-assets/unwrap_flow.cdc b/cadence/transactions/example-assets/evm-assets/unwrap_flow.cdc index 448259ac..03eaf42a 100644 --- a/cadence/transactions/example-assets/evm-assets/unwrap_flow.cdc +++ b/cadence/transactions/example-assets/evm-assets/unwrap_flow.cdc @@ -32,16 +32,16 @@ transaction(wflowContractHex: String, amount: UInt256) { } execute { - // Encode the withdraw function call - let calldata = EVM.encodeABIWithSignature("withdraw(uint256)", [UInt(amount)]) // Define the value to send to the WFLOW contract - 0 to unwrap let value = EVM.Balance(attoflow: 0) // Call the WFLOW contract which should complete the unwrap - let result = self.coa.call( + let result = self.coa.callWithSigAndArgs( to: self.wflowAddress, - data: calldata, + signature: "withdraw(uint256)", + args: [UInt(amount)], gasLimit: 15_000_000, - value: value + value: value.attoflow, + resultTypes: nil ) assert(result.status == EVM.Status.successful, message: "Failed to unwrap FLOW as WFLOW") self.postBalance = UInt(FlowEVMBridgeUtils.balanceOf(owner: self.coa.address(), evmContractAddress: self.wflowAddress)) diff --git a/cadence/transactions/example-assets/evm-assets/wrap_flow.cdc b/cadence/transactions/example-assets/evm-assets/wrap_flow.cdc index d29974b1..19abdd1e 100644 --- a/cadence/transactions/example-assets/evm-assets/wrap_flow.cdc +++ b/cadence/transactions/example-assets/evm-assets/wrap_flow.cdc @@ -30,12 +30,13 @@ transaction(wflowContractHex: String, amount: UFix64) { // Define the value to send to the WFLOW contract let balance = EVM.Balance(attoflow: 0) balance.setFLOW(flow: amount) - let calldata = EVM.encodeABIWithSignature("deposit()", []) - let result = self.coa.call( + let result = self.coa.callWithSigAndArgs( to: EVM.addressFromString(wflowContractHex), - data: calldata, + signature: "deposit()", + args: [], gasLimit: 15_000_000, - value: balance + value: balance.attoflow, + resultTypes: nil ) assert(result.status == EVM.Status.successful, message: "Failed to wrap FLOW as WFLOW") } From e9246a2388ed8913cb586e758010f9c2cdb3fb1b Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Fri, 3 Apr 2026 15:42:23 +0300 Subject: [PATCH 2/4] Avoid creating EVM.Balance structs for value of 0 --- .../contracts/bridge/FlowEVMBridgeUtils.cdc | 22 ++++++++++++++----- .../cross-vm-nfts/ExampleEVMNativeNFT.cdc | 11 +++++++--- .../ExampleEVMNativeNFTGivenEVMAddress.cdc | 11 +++++++--- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/cadence/contracts/bridge/FlowEVMBridgeUtils.cdc b/cadence/contracts/bridge/FlowEVMBridgeUtils.cdc index 55f6e6ac..3b414037 100644 --- a/cadence/contracts/bridge/FlowEVMBridgeUtils.cdc +++ b/cadence/contracts/bridge/FlowEVMBridgeUtils.cdc @@ -1312,14 +1312,19 @@ contract FlowEVMBridgeUtils { value: UFix64, resultTypes: [Type]? ): EVM.ResultDecoded { - let valueBalance = EVM.Balance(attoflow: 0) - valueBalance.setFLOW(flow: value) + var valueBalance: UInt = 0 + if value > 0.0 { + let balance = EVM.Balance(attoflow: 0) + balance.setFLOW(flow: value) + valueBalance = balance.inAttoFLOW() + } + return self.borrowCOA().callWithSigAndArgs( to: targetEVMAddress, signature: signature, args: args, gasLimit: gasLimit, - value: valueBalance.attoflow, + value: valueBalance, resultTypes: resultTypes ) } @@ -1336,14 +1341,19 @@ contract FlowEVMBridgeUtils { value: UFix64, resultTypes: [Type]? ): EVM.ResultDecoded { - let valueBalance = EVM.Balance(attoflow: 0) - valueBalance.setFLOW(flow: value) + var valueBalance: UInt = 0 + if value > 0.0 { + let balance = EVM.Balance(attoflow: 0) + balance.setFLOW(flow: value) + valueBalance = balance.inAttoFLOW() + } + return self.borrowCOA().dryCallWithSigAndArgs( to: targetEVMAddress, signature: signature, args: args, gasLimit: gasLimit, - value: valueBalance.attoflow, + value: valueBalance, resultTypes: resultTypes ) } diff --git a/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFT.cdc b/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFT.cdc index 5bf457e6..4f8872c5 100644 --- a/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFT.cdc +++ b/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFT.cdc @@ -103,14 +103,19 @@ access(all) contract ExampleEVMNativeNFT: NonFungibleToken, ICrossVM, ICrossVMAs value: UFix64, resultTypes: [Type]? ): EVM.ResultDecoded { - let valueBalance = EVM.Balance(attoflow: 0) - valueBalance.setFLOW(flow: value) + var valueBalance: UInt = 0 + if value > 0.0 { + let balance = EVM.Balance(attoflow: 0) + balance.setFLOW(flow: value) + valueBalance = balance.inAttoFLOW() + } + return self.borrowCOA().callWithSigAndArgs( to: targetEVMAddress, signature: signature, args: args, gasLimit: gasLimit, - value: valueBalance.attoflow, + value: valueBalance, resultTypes: resultTypes ) } diff --git a/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFTGivenEVMAddress.cdc b/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFTGivenEVMAddress.cdc index 9374dcd7..30a602eb 100644 --- a/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFTGivenEVMAddress.cdc +++ b/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFTGivenEVMAddress.cdc @@ -103,14 +103,19 @@ access(all) contract ExampleEVMNativeNFTGivenEVMAddress: NonFungibleToken, ICros value: UFix64, resultTypes: [Type]? ): EVM.ResultDecoded { - let valueBalance = EVM.Balance(attoflow: 0) - valueBalance.setFLOW(flow: value) + var valueBalance: UInt = 0 + if value > 0.0 { + let balance = EVM.Balance(attoflow: 0) + balance.setFLOW(flow: value) + valueBalance = balance.inAttoFLOW() + } + return self.borrowCOA().callWithSigAndArgs( to: targetEVMAddress, signature: signature, args: args, gasLimit: gasLimit, - value: valueBalance.attoflow, + value: valueBalance, resultTypes: resultTypes ) } From f931ba9cf110fea54b4e57a95eeb06cff2d3eba6 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Fri, 3 Apr 2026 16:32:07 +0300 Subject: [PATCH 3/4] Use the Cadence String interpolation feature instead of concat() --- cadence/contracts/bridge/FlowEVMBridge.cdc | 67 ++++++------------- .../bridge/FlowEVMBridgeHandlers.cdc | 43 ++++-------- .../contracts/bridge/FlowEVMBridgeUtils.cdc | 39 +++++------ .../FlowEVMBridgeHandlerInterfaces.cdc | 14 ++-- .../bridge/interfaces/IFlowEVMNFTBridge.cdc | 3 +- .../bridge/interfaces/IFlowEVMTokenBridge.cdc | 3 +- .../cross-vm-nfts/ExampleEVMNativeNFT.cdc | 30 ++++----- .../ExampleEVMNativeNFTGivenEVMAddress.cdc | 28 ++++---- .../scripts/utils/get_deployer_address.cdc | 2 +- .../scripts/utils/get_registry_address.cdc | 2 +- .../example-assets/evm-assets/mint_erc20.cdc | 2 +- .../evm-assets/safe_mint_erc721.cdc | 2 +- .../example-assets/evm-assets/unwrap_flow.cdc | 2 +- 13 files changed, 89 insertions(+), 148 deletions(-) diff --git a/cadence/contracts/bridge/FlowEVMBridge.cdc b/cadence/contracts/bridge/FlowEVMBridge.cdc index b829cfa7..aeaa9b6c 100644 --- a/cadence/contracts/bridge/FlowEVMBridge.cdc +++ b/cadence/contracts/bridge/FlowEVMBridge.cdc @@ -75,10 +75,10 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { pre { !FlowEVMBridgeConfig.isPaused(): "Bridge operations are currently paused" !FlowEVMBridgeConfig.isCadenceTypeBlocked(type): - "This Cadence Type ".concat(type.identifier).concat(" is currently blocked from being onboarded") + "This Cadence Type \(type.identifier) is currently blocked from being onboarded" self.typeRequiresOnboarding(type) == true: "Onboarding is not needed for this type" FlowEVMBridgeUtils.typeAllowsBridging(type): - "This Cadence Type ".concat(type.identifier).concat(" is currently opted-out of bridge onboarding") + "This Cadence Type \(type.identifier) is currently opted-out of bridge onboarding" FlowEVMBridgeUtils.isCadenceNative(type: type): "Only Cadence-native assets can be onboarded by Type" } /* Custom cross-VM Implementation check */ @@ -115,7 +115,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { let vault <-createVaultFunction(type) assert( vault.getType() == type, - message: "Requested to onboard type=".concat(type.identifier).concat( "but contract returned type=").concat(vault.getType().identifier) + message: "Requested to onboard type=\(type.identifier) but contract returned type=\(vault.getType().identifier)" ) FlowEVMBridgeTokenEscrow.initializeEscrow( with: <-vault, @@ -125,7 +125,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { evmTokenAddress: onboardingValues.evmContractAddress ) } else { - panic("Attempted to onboard unsupported type: ".concat(type.identifier)) + panic("Attempted to onboard unsupported type: \(type.identifier)") } /* Confirmation */ @@ -157,7 +157,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { pre { !FlowEVMBridgeConfig.isPaused(): "Bridge operations are currently paused" !FlowEVMBridgeConfig.isEVMAddressBlocked(address): - "This EVM contract ".concat(address.toString()).concat(" is currently blocked from being onboarded") + "This EVM contract \(address.toString()) is currently blocked from being onboarded" } /* Custom cross-VM Implementation check */ // @@ -216,18 +216,13 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { !FlowEVMBridgeConfig.isCadenceTypeBlocked(type): "Type \(type.identifier) has been blocked from onboarding" FlowEVMBridgeUtils.isCadenceNative(type: type): - "Attempting to register a bridge-deployed NFT - cannot update a bridge-defined asset. If updating your EVM " - .concat("contract's Cadence association, deploy your Cadence NFT contract and register using the newly defined Cadence type") + "Attempting to register a bridge-deployed NFT - cannot update a bridge-defined asset. If updating your EVM contract's Cadence association, deploy your Cadence NFT contract and register using the newly defined Cadence type" FlowEVMBridgeCustomAssociations.getEVMAddressAssociated(with: type) == nil: - "A custom association has already been declared for type \(type.identifier) with EVM address " - .concat(FlowEVMBridgeCustomAssociations.getEVMAddressAssociated(with: type)!.toString()) - .concat(". Custom associations can only be declared once for any given Cadence Type or EVM contract") + "A custom association has already been declared for type \(type.identifier) with EVM address \(FlowEVMBridgeCustomAssociations.getEVMAddressAssociated(with: type)!.toString()). Custom associations can only be declared once for any given Cadence Type or EVM contract" fulfillmentMinter?.check() ?? true: - "NFTFulfillmentMinter Capability is invalid - Issue a new " - .concat("Capability and try again") + "NFTFulfillmentMinter Capability is invalid - Issue a new Capability and try again" fulfillmentMinter != nil ? fulfillmentMinter!.borrow()!.getType().address! == type.address! : true: - "NFTFulfillmentMinter must be defined by a contract deployed to the registered type address \(type.address!) " - .concat(" but found defining address of \(fulfillmentMinter!.borrow()!.getType().address!)") + "NFTFulfillmentMinter must be defined by a contract deployed to the registered type address \(type.address!) but found defining address of \(fulfillmentMinter!.borrow()!.getType().address!)" } /* Provision fees */ // @@ -248,15 +243,12 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { ) assert( FlowEVMBridgeCustomAssociations.getTypeAssociated(with: evmPointer.evmContractAddress) == nil, - message: "A custom association has already been declared for EVM address \(evmPointer.evmContractAddress.toString()) with Cadence Type " - .concat(FlowEVMBridgeCustomAssociations.getTypeAssociated(with: evmPointer.evmContractAddress)?.identifier ?? "") - .concat(". Custom associations can only be declared once for any given Cadence Type or EVM contract") + message: "A custom association has already been declared for EVM address \(evmPointer.evmContractAddress.toString()) with Cadence Type \(FlowEVMBridgeCustomAssociations.getTypeAssociated(with: evmPointer.evmContractAddress)?.identifier ?? ""). Custom associations can only be declared once for any given Cadence Type or EVM contract" ) assert( FlowEVMBridgeUtils.isERC721(evmContractAddress: evmPointer.evmContractAddress) && !FlowEVMBridgeUtils.isERC20(evmContractAddress: evmPointer.evmContractAddress), - message: "Cross-VM NFTs must be implemented as ERC721 exclusively, but detected an invalid EVM interface " - .concat("at EVM contract \(evmPointer.evmContractAddress.toString())") + message: "Cross-VM NFTs must be implemented as ERC721 exclusively, but detected an invalid EVM interface at EVM contract \(evmPointer.evmContractAddress.toString())" ) // Get pointer on EVM side @@ -282,15 +274,11 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { // Check supportsInterface() for CrossVMBridgeERC721Fulfillment if NFT is Cadence-native if evmPointer.nativeVM == CrossVMMetadataViews.VM.Cadence { assert(FlowEVMBridgeUtils.supportsCadenceNativeNFTEVMInterfaces(evmContract: evmPointer.evmContractAddress), - message: "Corresponding EVM contract does not implement necessary EVM interfaces ICrossVMBridgeERC721Fulfillment " - .concat("and/or ICrossVMBridgeCallable. All Cadence-native cross-VM NFTs must implement these interfaces and ") - .concat("grant the bridge COA the ability to fulfill bridge requests moving NFTs into EVM.")) + message: "Corresponding EVM contract does not implement necessary EVM interfaces ICrossVMBridgeERC721Fulfillment and/or ICrossVMBridgeCallable. All Cadence-native cross-VM NFTs must implement these interfaces and grant the bridge COA the ability to fulfill bridge requests moving NFTs into EVM.") let designatedVMBridgeAddress = FlowEVMBridgeUtils.getVMBridgeAddressFromICrossVMBridgeCallable(evmContract: evmPointer.evmContractAddress) - ?? panic("Could not recover declared VM bridge address from EVM contract \(evmPointer.evmContractAddress.toString()). " - .concat("Ensure the contract conforms to ICrossVMBridgeCallable and declare the vmBridgeAddress as \(FlowEVMBridgeUtils.getBridgeCOAEVMAddress().toString())")) + ?? panic("Could not recover declared VM bridge address from EVM contract \(evmPointer.evmContractAddress.toString()). Ensure the contract conforms to ICrossVMBridgeCallable and declare the vmBridgeAddress as \(FlowEVMBridgeUtils.getBridgeCOAEVMAddress().toString())") assert(designatedVMBridgeAddress.equals(FlowEVMBridgeUtils.getBridgeCOAEVMAddress()), - message: "ICrossVMBridgeCallable declared \(designatedVMBridgeAddress.toString())" - .concat(" as vmBridgeAddress which must be declared as \(FlowEVMBridgeUtils.getBridgeCOAEVMAddress().toString())")) + message: "ICrossVMBridgeCallable declared \(designatedVMBridgeAddress.toString()) as vmBridgeAddress which must be declared as \(FlowEVMBridgeUtils.getBridgeCOAEVMAddress().toString())") } /* Native VM consistency check */ @@ -299,21 +287,14 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { let legacyEVMAssoc = FlowEVMBridgeConfig.getLegacyEVMAddressAssociated(with: type) let legacyCadenceAssoc = FlowEVMBridgeConfig.getLegacyTypeAssociated(with: evmPointer.evmContractAddress) assert(legacyEVMAssoc == nil || legacyCadenceAssoc == nil, - message: "Both the EVM contract \(evmPointer.evmContractAddress.toString()) and the Cadence Type \(type.identifier) " - .concat("have already been onboarded to the VM bridge - one side of this association will have to be redeployed ") - .concat("and the declared association updated to a non-onboarded target in order to register as a custom cross-VM asset.")) + message: "Both the EVM contract \(evmPointer.evmContractAddress.toString()) and the Cadence Type \(type.identifier) have already been onboarded to the VM bridge - one side of this association will have to be redeployed and the declared association updated to a non-onboarded target in order to register as a custom cross-VM asset.") // Ensure the native VM is consistent if the NFT has been previously onboarded via the permissionless path if legacyEVMAssoc != nil { assert(evmPointer.nativeVM == CrossVMMetadataViews.VM.Cadence, - message: "Attempting to register NFT \(type.identifier) as EVM-native after it has already been " - .concat("onboarded as Cadence-native. This NFT must be configured as Cadence-native with an ERC721 ") - .concat("implementing CrossVMBridgeERC721Fulfillment base contract allowing the bridge to fulfill ") - .concat("NFTs moving into EVM")) + message: "Attempting to register NFT \(type.identifier) as EVM-native after it has already been onboarded as Cadence-native. This NFT must be configured as Cadence-native with an ERC721 implementing CrossVMBridgeERC721Fulfillment base contract allowing the bridge to fulfill NFTs moving into EVM") } else if legacyCadenceAssoc != nil { assert(evmPointer.nativeVM == CrossVMMetadataViews.VM.EVM, - message: "Attempting to register NFT \(type.identifier) as Cadence-native after it has already been " - .concat("onboarded as EVM-native. This NFT must be configured as EVM-native and provide an NFTFulfillmentMinter ") - .concat("Capability so the bridge may fulfill NFTs moving into Cadence.")) + message: "Attempting to register NFT \(type.identifier) as Cadence-native after it has already been onboarded as EVM-native. This NFT must be configured as EVM-native and provide an NFTFulfillmentMinter Capability so the bridge may fulfill NFTs moving into Cadence.") } FlowEVMBridgeCustomAssociations.saveCustomAssociation( @@ -843,8 +824,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { to: EVM.EVMAddress, feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}) { let evmPointer = FlowEVMBridgeCustomAssociations.getEVMPointerAsRegistered(forType: token.getType()) - ?? panic("Could not find custom association for cross-VM NFT \(token.getType().identifier) with id \(token.id). " - .concat("Ensure this NFT has been registered as a cross-VM.")) + ?? panic("Could not find custom association for cross-VM NFT \(token.getType().identifier) with id \(token.id). Ensure this NFT has been registered as a cross-VM.") return evmPointer.nativeVM == CrossVMMetadataViews.VM.Cadence ? self.handleCadenceNativeCrossVMNFTToEVM(token: <-token, to: to, feeProvider: feeProvider) : self.handleEVMNativeCrossVMNFTToEVM(token: <-token, to: to, feeProvider: feeProvider) @@ -918,9 +898,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { } let bridgedAssociation = FlowEVMBridgeConfig.getLegacyEVMAddressAssociated(with: token.getType())! let updatedCadenceAssociation = FlowEVMBridgeCustomAssociations.getTypeAssociated(with: bridgedAssociation) - ?? panic("Could not find a custom cross-VM association for NFT \(token.getType().identifier) #\(token.id). " - .concat("The handleUpdatedBridgedNFTToEVM route is intended for bridged Cadence NFTs associated with ") - .concat(" ERC721 contracts that have registered as a custom cross-VM NFT collection.")) + ?? panic("Could not find a custom cross-VM association for NFT \(token.getType().identifier) #\(token.id). The handleUpdatedBridgedNFTToEVM route is intended for bridged Cadence NFTs associated with ERC721 contracts that have registered as a custom cross-VM NFT collection.") // Ensure the updated/custom type is not paused - the top-level pause check only covers the // caller-supplied bridge-defined type, so we must re-check here after resolving the migration target @@ -1017,8 +995,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { protectedTransferCall: fun (EVM.EVMAddress): EVM.ResultDecoded ): @{NonFungibleToken.NFT} { let evmPointer = FlowEVMBridgeCustomAssociations.getEVMPointerAsRegistered(forType: type) - ?? panic("Could not find custom association for cross-VM NFT \(type.identifier) with id \(id). " - .concat("Ensure this NFT has been registered as a cross-VM.")) + ?? panic("Could not find custom association for cross-VM NFT \(type.identifier) with id \(id). Ensure this NFT has been registered as a cross-VM.") if evmPointer.nativeVM == CrossVMMetadataViews.VM.Cadence { return <- self.handleCadenceNativeCrossVMNFTFromEVM( owner: owner, @@ -1080,9 +1057,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { ) } else { // Bridged token not wrapped nor owned by caller - could not determine owner - panic("Bridged ERC721 \(bridgedAssociation!.toString()) ID \(id) still exists after \(type.identifier) " - .concat("was updated to associate with ERC721 \(customERC721.toString()), but the bridged token is ") - .concat("neither wrapped nor owned by caller \(owner.toString()). Could not determine owner.")) + panic("Bridged ERC721 \(bridgedAssociation!.toString()) ID \(id) still exists after \(type.identifier) was updated to associate with ERC721 \(customERC721.toString()), but the bridged token is neither wrapped nor owned by caller \(owner.toString()). Could not determine owner.") } // Burn the bridged ERC721, taking the bridged representation out of circulation in favor of custom ERC721 FlowEVMBridgeUtils.mustBurnERC721(erc721Address: bridgedAssociation!, id: id) diff --git a/cadence/contracts/bridge/FlowEVMBridgeHandlers.cdc b/cadence/contracts/bridge/FlowEVMBridgeHandlers.cdc index 19137964..e7cd9c11 100644 --- a/cadence/contracts/bridge/FlowEVMBridgeHandlers.cdc +++ b/cadence/contracts/bridge/FlowEVMBridgeHandlers.cdc @@ -105,7 +105,7 @@ access(all) contract FlowEVMBridgeHandlers { let amount = tokens.balance let uintAmount = FlowEVMBridgeUtils.convertCadenceAmountToERC20Amount(amount, erc20Address: evmAddress) - assert(uintAmount > UInt256(0), message: "Amount to bridge must be greater than 0") + assert(uintAmount > 0, message: "Amount to bridge must be greater than 0") Burner.burn(<-tokens) @@ -147,7 +147,7 @@ access(all) contract FlowEVMBridgeHandlers { // After state confirmation, mint the tokens and return let minter = self.borrowMinter() - ?? panic("Cannot bridge - Minter not set in ".concat(self.getType().identifier)) + ?? panic("Cannot bridge - Minter not set in \(self.getType().identifier)") let minted <- minter.mint(amount: ufixAmount) return <-minted } @@ -170,7 +170,7 @@ access(all) contract FlowEVMBridgeHandlers { access(FlowEVMBridgeHandlerInterfaces.Admin) fun setMinter(_ minter: @{FlowEVMBridgeHandlerInterfaces.TokenMinter}) { pre { - self.minter == nil: "Minter has already been set in ".concat(self.getType().identifier) + self.minter == nil: "Minter has already been set in \(self.getType().identifier)" } self.minter <-! minter } @@ -179,7 +179,7 @@ access(all) contract FlowEVMBridgeHandlers { access(FlowEVMBridgeHandlerInterfaces.Admin) fun enableBridging() { pre { - self.minter != nil: "Cannot enable ".concat(self.getType().identifier).concat(" without a minter") + self.minter != nil: "Cannot enable \(self.getType().identifier) without a minter" } self.enabled = true } @@ -277,16 +277,12 @@ access(all) contract FlowEVMBridgeHandlers { // Cover underflow assert( postBalance > preBalance, - message: "Escrowed WFLOW balance did not increment after wrapping FLOW - pre: " - .concat(preBalance.toString()).concat(" | post: ").concat(postBalance.toString()) + message: "Escrowed WFLOW balance did not increment after wrapping FLOW - pre: \(preBalance.toString()) | post: \(postBalance.toString())" ) // Confirm bridge COA's WFLOW balance has incremented by the expected amount assert( postBalance - preBalance == uintAmount, - message: "Escrowed WFLOW balance after wrapping does not match requested amount - expected: " - .concat((preBalance + uintAmount).toString()) - .concat(" | actual: ") - .concat((postBalance - preBalance).toString()) + message: "Escrowed WFLOW balance after wrapping does not match requested amount - expected: \(preBalance + uintAmount).toString()) | actual: \(postBalance - preBalance).toString())" ) // Transfer WFLOW to recipient @@ -319,8 +315,7 @@ access(all) contract FlowEVMBridgeHandlers { ) assert( ufixAmount > 0.0, - message: "Requested UInt256 amount ".concat(amount.toString()).concat(" converted to 0.0 ") - .concat(" - try bridging a larger amount to avoid UFix64 precision loss during conversion") + message: "Requested UInt256 amount \(amount.toString()) converted to 0.0 - try bridging a larger amount to avoid UFix64 precision loss during conversion" ) // Transfers WFLOW to bridge COA as escrow @@ -351,34 +346,24 @@ access(all) contract FlowEVMBridgeHandlers { // Cover underflow assert( postBalance > preBalance, - message: "Escrowed FLOW Balance did not increment after unwrapping WFLOW - pre: ".concat(preBalance.toString()) - .concat(" | post: ").concat(postBalance.toString()) + message: "Escrowed FLOW Balance did not increment after unwrapping WFLOW - pre: \(preBalance.toString()) | post: \(postBalance.toString())" ) // Confirm bridge COA's FLOW balance has incremented by the expected amount assert( UInt256(postBalance - preBalance) == amount, - message: "Escrowed WFLOW balance after unwrapping does not match requested amount - expected: " - .concat((UInt256(preBalance) + amount).toString()) - .concat(" | actual: ") - .concat((postBalance - preBalance).toString()) + message: "Escrowed WFLOW balance after unwrapping does not match requested amount - expected: \(UInt256(preBalance) + amount).toString()) | actual: \(postBalance - preBalance).toString())" ) // Withdraw escrowed FLOW from bridge COA let withdrawBalance = EVM.Balance(attoflow: UInt(amount)) assert( UInt256(withdrawBalance.attoflow) == amount, - message: "Requested balance failed to convert to attoflow - expected: " - .concat(amount.toString()) - .concat(" | actual: ") - .concat(withdrawBalance.attoflow.toString()) + message: "Requested balance failed to convert to attoflow - expected: \(amount.toString()) | actual: \(withdrawBalance.attoflow.toString())" ) let flowVault <- coa.withdraw(balance: withdrawBalance) assert( flowVault.balance == ufixAmount, - message: "Resulting FLOW Vault balance does not match requested amount - expected: " - .concat(ufixAmount.toString()) - .concat(" | actual: ") - .concat(flowVault.balance.toString()) + message: "Resulting FLOW Vault balance does not match requested amount - expected: \(ufixAmount.toString()) | actual: \(flowVault.balance.toString())" ) return <-flowVault } @@ -390,15 +375,13 @@ access(all) contract FlowEVMBridgeHandlers { /// Sets the target type for the handler access(FlowEVMBridgeHandlerInterfaces.Admin) fun setTargetType(_ type: Type) { - panic("WFLOWTokenHandler has targetType set to " - .concat(self.targetType.identifier).concat(" at initialization")) + panic("WFLOWTokenHandler has targetType set to \(self.targetType.identifier) at initialization") } /// Sets the target EVM address for the handler access(FlowEVMBridgeHandlerInterfaces.Admin) fun setTargetEVMAddress(_ address: EVM.EVMAddress) { - panic("WFLOWTokenHandler has EVMAddress set to " - .concat(self.targetEVMAddress.toString()).concat(" at initialization")) + panic("WFLOWTokenHandler has EVMAddress set to \(self.targetEVMAddress.toString()) at initialization") } /// Sets the target type for the handler diff --git a/cadence/contracts/bridge/FlowEVMBridgeUtils.cdc b/cadence/contracts/bridge/FlowEVMBridgeUtils.cdc index 3b414037..d217fcfc 100644 --- a/cadence/contracts/bridge/FlowEVMBridgeUtils.cdc +++ b/cadence/contracts/bridge/FlowEVMBridgeUtils.cdc @@ -172,9 +172,9 @@ contract FlowEVMBridgeUtils { access(all) view fun typeAllowsBridging(_ type: Type): Bool { let contractAddress = self.getContractAddress(fromType: type) - ?? panic("Could not construct contract address from type identifier: ".concat(type.identifier)) + ?? panic("Could not construct contract address from type identifier: \(type.identifier)") let contractName = self.getContractName(fromType: type) - ?? panic("Could not construct contract name from type identifier: ".concat(type.identifier)) + ?? panic("Could not construct contract name from type identifier: \(type.identifier)") if let bridgePermissions = getAccount(contractAddress).contracts.borrow<&{IBridgePermissions}>(name: contractName) { return bridgePermissions.allowsBridging() } @@ -214,7 +214,7 @@ contract FlowEVMBridgeUtils { access(all) view fun isCadenceNative(type: Type): Bool { let definingAddress = self.getContractAddress(fromType: type) - ?? panic("Could not construct address from type identifier: ".concat(type.identifier)) + ?? panic("Could not construct address from type identifier: \(type.identifier)") return definingAddress != self.account.address } @@ -230,7 +230,7 @@ contract FlowEVMBridgeUtils { access(all) view fun isBridgeDefined(type: Type): Bool { let definingAddress = self.getContractAddress(fromType: type) - ?? panic("Could not construct address from type identifier: ".concat(type.identifier)) + ?? panic("Could not construct address from type identifier: \(type.identifier)") return definingAddress == self.account.address } @@ -381,10 +381,10 @@ contract FlowEVMBridgeUtils { // Retrieve the Cadence type's defining contract name, address, & its identifier var name = self.getContractName(fromType: forAssetType) - ?? panic("Could not contract name from type: ".concat(forAssetType.identifier)) + ?? panic("Could not contract name from type: \(forAssetType.identifier)") let identifier = forAssetType.identifier let cadenceAddress = self.getContractAddress(fromType: forAssetType) - ?? panic("Could not derive contract address for token type: ".concat(identifier)) + ?? panic("Could not derive contract address for token type: \(identifier)") // Initialize asset symbol which will be assigned later // based on presence of asset-defined metadata var symbol: String? = nil @@ -411,7 +411,7 @@ contract FlowEVMBridgeUtils { ) as! MetadataViews.NFTCollectionDisplay? { name = collectionDisplay.name let serializedDisplay = SerializeMetadata.serializeFromDisplays(nftDisplay: nil, collectionDisplay: collectionDisplay)! - contractURI = "data:application/json;utf8,{".concat(serializedDisplay).concat("}") + contractURI = "data:application/json;utf8,{\(serializedDisplay)}" } if symbol == nil { symbol = SerializeMetadata.deriveSymbol(fromString: name) @@ -427,7 +427,7 @@ contract FlowEVMBridgeUtils { } if contractURI.length == 0 && ftDisplay != nil { let serializedDisplay = SerializeMetadata.serializeFTDisplay(ftDisplay!) - contractURI = "data:application/json;utf8,{".concat(serializedDisplay).concat("}") + contractURI = "data:application/json;utf8,{\(serializedDisplay)}" } // Derive a symbol from the name if no metadata view provided one if symbol == nil { @@ -475,7 +475,7 @@ contract FlowEVMBridgeUtils { let isERC20 = self.isERC20(evmContractAddress: evmContractAddress) assert( isERC20, - message: "Contract ".concat(evmContractAddress.toString()).concat("defines an asset that is not currently supported by the bridge") + message: "Contract \(evmContractAddress.toString())defines an asset that is not currently supported by the bridge" ) cadenceContractName = self.deriveBridgedTokenContractName(from: evmContractAddress) decimals = self.getTokenDecimals(evmContractAddress: evmContractAddress) @@ -1013,14 +1013,11 @@ contract FlowEVMBridgeUtils { } assert(prefix.length > 1, message: "Invalid prefix") if let splitIdentifier = self.splitObjectIdentifier(identifier: fromType.identifier) { - let sourceContractAddress = Address.fromString("0x".concat(splitIdentifier[1]))! + let sourceContractAddress = Address.fromString("0x\(splitIdentifier[1])")! let sourceContractName = splitIdentifier[2] let resourceName = splitIdentifier[3] return StoragePath( - identifier: prefix.concat(self.delimiter) - .concat(sourceContractAddress.toString()).concat(self.delimiter) - .concat(sourceContractName).concat(self.delimiter) - .concat(resourceName) + identifier: "\(prefix)\(self.delimiter)\(sourceContractAddress.toString())\(self.delimiter)\(sourceContractName)\(self.delimiter)\(resourceName)" ) ?? nil } return nil @@ -1035,9 +1032,7 @@ contract FlowEVMBridgeUtils { /// access(all) view fun deriveBridgedNFTContractName(from evmContract: EVM.EVMAddress): String { - return self.contractNamePrefixes[Type<@{NonFungibleToken.NFT}>()]!["bridged"]! - .concat(self.delimiter) - .concat(evmContract.toString()) + return "\(self.contractNamePrefixes[Type<@{NonFungibleToken.NFT}>()]!["bridged"]!)\(self.delimiter)\(evmContract.toString())" } /// Derives the Cadence contract name for a given EVM fungible token of the form @@ -1049,9 +1044,7 @@ contract FlowEVMBridgeUtils { /// access(all) view fun deriveBridgedTokenContractName(from evmContract: EVM.EVMAddress): String { - return self.contractNamePrefixes[Type<@{FungibleToken.Vault}>()]!["bridged"]! - .concat(self.delimiter) - .concat(evmContract.toString()) + return "\(self.contractNamePrefixes[Type<@{FungibleToken.Vault}>()]!["bridged"]!)\(self.delimiter)\(evmContract.toString())" } /**************** @@ -1131,12 +1124,12 @@ contract FlowEVMBridgeUtils { // Ensure the parts do not exceed the max UFix64 value before conversion assert( scaledValue <= UInt256(UFix64.max), - message: "Scaled integer value ".concat(value.toString()).concat(" exceeds max UFix64 value") + message: "Scaled integer value \(value.toString()) exceeds max UFix64 value" ) /// Check for the max value that can be converted to a UFix64 without overflowing assert( scaledValue == UInt256(UFix64.max) ? scaledFractional < 0.09551616 : true, - message: "Scaled integer value ".concat(value.toString()).concat(" exceeds max UFix64 value") + message: "Scaled integer value \(value.toString()) exceeds max UFix64 value" ) return UFix64(scaledValue) + scaledFractional @@ -1252,7 +1245,7 @@ contract FlowEVMBridgeUtils { view fun buildCompositeType(address: Address, contractName: String, resourceName: String): Type? { let addressStr = address.toString() let subtract0x = addressStr.slice(from: 2, upTo: addressStr.length) - let identifier = "A".concat(".").concat(subtract0x).concat(".").concat(contractName).concat(".").concat(resourceName) + let identifier = "A.\(subtract0x).\(contractName).\(resourceName)" return CompositeType(identifier) } diff --git a/cadence/contracts/bridge/interfaces/FlowEVMBridgeHandlerInterfaces.cdc b/cadence/contracts/bridge/interfaces/FlowEVMBridgeHandlerInterfaces.cdc index c9b09770..b29ef6b7 100644 --- a/cadence/contracts/bridge/interfaces/FlowEVMBridgeHandlerInterfaces.cdc +++ b/cadence/contracts/bridge/interfaces/FlowEVMBridgeHandlerInterfaces.cdc @@ -125,7 +125,7 @@ access(all) contract FlowEVMBridgeHandlerInterfaces { access(Admin) fun disableBridging() { pre { self.isEnabled(): - "Cannot disable: ".concat(self.getType().identifier).concat(" is already disabled") + "Cannot disable: \(self.getType().identifier) is already disabled" } post { !self.isEnabled(): @@ -173,12 +173,9 @@ access(all) contract FlowEVMBridgeHandlerInterfaces { ) { pre { self.isEnabled(): - "TokenHandler ".concat(self.getType().identifier).concat(" with uuid ") - .concat(self.uuid.toString()).concat(" is not yet enabled") + "TokenHandler \(self.getType().identifier) with uuid \(self.uuid.toString()) is not yet enabled" tokens.getType() == self.getTargetType(): - "TokenHandler ".concat(self.getType().identifier).concat(" with uuid ").concat(self.uuid.toString()) - .concat(" expects ").concat(self.getTargetType()?.identifier ?? "nil") - .concat(" but received ").concat(tokens.getType().identifier) + "TokenHandler \(self.getType().identifier) with uuid \(self.uuid.toString()) expects \(self.getTargetType()?.identifier ?? "nil") but received \(tokens.getType().identifier)" tokens.balance > 0.0: "Attempting to bridge 0.0 tokens - zero amounts are unsupported" } @@ -192,9 +189,8 @@ access(all) contract FlowEVMBridgeHandlerInterfaces { ): @{FungibleToken.Vault} { pre { self.isEnabled(): - "TokenHandler ".concat(self.getType().identifier).concat(" with uuid ") - .concat(self.uuid.toString()).concat(" is not yet enabled") - amount > UInt256(0): "Attempting to bridge 0 tokens from EVM - zero amounts are unsupported" + "TokenHandler \(self.getType().identifier) with uuid \(self.uuid.toString()) is not yet enabled" + amount > 0: "Attempting to bridge 0 tokens from EVM - zero amounts are unsupported" } post { result.getType() == self.getTargetType(): diff --git a/cadence/contracts/bridge/interfaces/IFlowEVMNFTBridge.cdc b/cadence/contracts/bridge/interfaces/IFlowEVMNFTBridge.cdc index 7d04c8ad..054afb6a 100644 --- a/cadence/contracts/bridge/interfaces/IFlowEVMNFTBridge.cdc +++ b/cadence/contracts/bridge/interfaces/IFlowEVMNFTBridge.cdc @@ -74,8 +74,7 @@ access(all) contract interface IFlowEVMNFTBridge { to: to.toString(), evmContractAddress: self.getAssociatedEVMAddress(with: token.getType())?.toString() ?? panic( - "Could not find EVM Contract address associated with provided NFT identifier=" - .concat(token.getType().identifier) + "Could not find EVM Contract address associated with provided NFT identifier=\(token.getType().identifier)" ), bridgeAddress: self.account.address ) diff --git a/cadence/contracts/bridge/interfaces/IFlowEVMTokenBridge.cdc b/cadence/contracts/bridge/interfaces/IFlowEVMTokenBridge.cdc index 1e45628a..180d6628 100644 --- a/cadence/contracts/bridge/interfaces/IFlowEVMTokenBridge.cdc +++ b/cadence/contracts/bridge/interfaces/IFlowEVMTokenBridge.cdc @@ -68,8 +68,7 @@ access(all) contract interface IFlowEVMTokenBridge { to: to.toString(), evmContractAddress: self.getAssociatedEVMAddress(with: vault.getType())?.toString() ?? panic( - "Could not find EVM Contract address associated with provided Token identifier=" - .concat(vault.getType().identifier) + "Could not find EVM Contract address associated with provided Token identifier=\(vault.getType().identifier)" ), bridgeAddress: self.account.address ) diff --git a/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFT.cdc b/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFT.cdc index 4f8872c5..db8b59ab 100644 --- a/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFT.cdc +++ b/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFT.cdc @@ -63,11 +63,11 @@ access(all) contract ExampleEVMNativeNFT: NonFungibleToken, ICrossVM, ICrossVMAs ) assert( tokenURIRes.status == EVM.Status.successful, - message: "Error calling ERC721.tokenURI(uint256) with message: ".concat(tokenURIRes.errorMessage) + message: "Error calling ERC721.tokenURI(uint256) with message: \(tokenURIRes.errorMessage)" ) assert( tokenURIRes.results.length == 1, - message: "Unexpected tokenURI(uint256) return length of ".concat(tokenURIRes.results.length.toString()) + message: "Unexpected tokenURI(uint256) return length of \(tokenURIRes.results.length.toString())" ) return tokenURIRes.results[0] as! String } @@ -84,11 +84,11 @@ access(all) contract ExampleEVMNativeNFT: NonFungibleToken, ICrossVM, ICrossVMAs ) assert( contractURIRes.status == EVM.Status.successful, - message: "Error calling ERC721.contractURI(uint256) with message: ".concat(contractURIRes.errorMessage) + message: "Error calling ERC721.contractURI(uint256) with message: \(contractURIRes.errorMessage)" ) assert( contractURIRes.results.length == 1, - message: "Unexpected contractURI(uint256) return length of ".concat(contractURIRes.results.length.toString()) + message: "Unexpected contractURI(uint256) return length of \(contractURIRes.results.length.toString())" ) return contractURIRes.results[0] as! String } @@ -123,8 +123,7 @@ access(all) contract ExampleEVMNativeNFT: NonFungibleToken, ICrossVM, ICrossVMAs access(self) view fun borrowCOA(): auth(EVM.Owner) &EVM.CadenceOwnedAccount { return self.account.storage.borrow( from: /storage/evm - ) ?? panic("Could not borrow CadenceOwnedAccount (COA) from /storage/evm. " - .concat("Ensure this account has a COA configured to successfully call into EVM.")) + ) ?? panic("Could not borrow CadenceOwnedAccount (COA) from /storage/evm. Ensure this account has a COA configured to successfully call into EVM.") } /// We choose the name NFT here, but this type can have any name now @@ -138,8 +137,7 @@ access(all) contract ExampleEVMNativeNFT: NonFungibleToken, ICrossVM, ICrossVMAs ) { pre { erc721ID <= UInt256(UInt64.max): - "Provided EVM ID ".concat(erc721ID.toString()) - .concat(" exceeds the assignable Cadence ID of UInt64.max ").concat(UInt64.max.toString()) + "Provided EVM ID \(erc721ID.toString()) exceeds the assignable Cadence ID of UInt64.max \(UInt64.max.toString())" } self.id = UInt64(erc721ID) } @@ -168,10 +166,10 @@ access(all) contract ExampleEVMNativeNFT: NonFungibleToken, ICrossVM, ICrossVMAs case Type(): let collectionDisplay = (ExampleEVMNativeNFT.resolveContractView(resourceType: self.getType(), viewType: Type()) as! MetadataViews.NFTCollectionDisplay?)! return MetadataViews.Display( - name: ExampleEVMNativeNFT.getName().concat(" #").concat(self.id.toString()), + name: "\(ExampleEVMNativeNFT.getName()) #\(self.id.toString())", description: collectionDisplay.description, thumbnail: MetadataViews.HTTPFile( - url: "https://example-nft.flow.com/nft/".concat(self.id.toString()) + url: "https://example-nft.flow.com/nft/\(self.id.toString())" ) ) case Type(): @@ -179,7 +177,7 @@ access(all) contract ExampleEVMNativeNFT: NonFungibleToken, ICrossVM, ICrossVMAs self.id ) case Type(): - return MetadataViews.ExternalURL("https://example-nft.flow.com/".concat(self.id.toString())) + return MetadataViews.ExternalURL("https://example-nft.flow.com/\(self.id.toString())") case Type(): return ExampleEVMNativeNFT.resolveContractView(resourceType: Type<@ExampleEVMNativeNFT.NFT>(), viewType: Type()) case Type(): @@ -432,7 +430,7 @@ access(all) contract ExampleEVMNativeNFT: NonFungibleToken, ICrossVM, ICrossVMAs ) assert( deployResult.status == EVM.Status.successful, - message: "ERC721 deployment failed with message: ".concat(deployResult.errorMessage) + message: "ERC721 deployment failed with message: \(deployResult.errorMessage)" ) self.evmContractAddress = deployResult.deployedContract! @@ -456,15 +454,15 @@ access(all) contract ExampleEVMNativeNFT: NonFungibleToken, ICrossVM, ICrossVMAs ) assert( nameRes.status == EVM.Status.successful, - message: "Error on ERC721.name() call with message: ".concat(nameRes.errorMessage) + message: "Error on ERC721.name() call with message: \(nameRes.errorMessage)" ) assert( symbolRes.status == EVM.Status.successful, - message: "Error on ERC721.symbol() call with message: ".concat(symbolRes.errorMessage) + message: "Error on ERC721.symbol() call with message: \(symbolRes.errorMessage)" ) - assert(nameRes.results.length == 1, message: "Unexpected name() return length of ".concat(nameRes.results.length.toString())) - assert(symbolRes.results.length == 1, message: "Unexpected symbol() return length of ".concat(symbolRes.results.length.toString())) + assert(nameRes.results.length == 1, message: "Unexpected name() return length of \(nameRes.results.length.toString())") + assert(symbolRes.results.length == 1, message: "Unexpected symbol() return length of \(symbolRes.results.length.toString())") self.name = nameRes.results[0] as! String self.symbol = symbolRes.results[0] as! String diff --git a/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFTGivenEVMAddress.cdc b/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFTGivenEVMAddress.cdc index 30a602eb..4d3e6db1 100644 --- a/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFTGivenEVMAddress.cdc +++ b/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFTGivenEVMAddress.cdc @@ -63,11 +63,11 @@ access(all) contract ExampleEVMNativeNFTGivenEVMAddress: NonFungibleToken, ICros ) assert( tokenURIRes.status == EVM.Status.successful, - message: "Error calling ERC721.tokenURI(uint256) with message: ".concat(tokenURIRes.errorMessage) + message: "Error calling ERC721.tokenURI(uint256) with message: \(tokenURIRes.errorMessage)" ) assert( tokenURIRes.results.length == 1, - message: "Unexpected tokenURI(uint256) return length of ".concat(tokenURIRes.results.length.toString()) + message: "Unexpected tokenURI(uint256) return length of \(tokenURIRes.results.length.toString())" ) return tokenURIRes.results[0] as! String } @@ -84,11 +84,11 @@ access(all) contract ExampleEVMNativeNFTGivenEVMAddress: NonFungibleToken, ICros ) assert( contractURIRes.status == EVM.Status.successful, - message: "Error calling ERC721.contractURI(uint256) with message: ".concat(contractURIRes.errorMessage) + message: "Error calling ERC721.contractURI(uint256) with message: \(contractURIRes.errorMessage)" ) assert( contractURIRes.results.length == 1, - message: "Unexpected contractURI(uint256) return length of ".concat(contractURIRes.results.length.toString()) + message: "Unexpected contractURI(uint256) return length of \(contractURIRes.results.length.toString())" ) return contractURIRes.results[0] as! String } @@ -123,8 +123,7 @@ access(all) contract ExampleEVMNativeNFTGivenEVMAddress: NonFungibleToken, ICros access(self) view fun borrowCOA(): auth(EVM.Owner) &EVM.CadenceOwnedAccount { return self.account.storage.borrow( from: /storage/evm - ) ?? panic("Could not borrow CadenceOwnedAccount (COA) from /storage/evm. " - .concat("Ensure this account has a COA configured to successfully call into EVM.")) + ) ?? panic("Could not borrow CadenceOwnedAccount (COA) from /storage/evm. Ensure this account has a COA configured to successfully call into EVM.") } /// We choose the name NFT here, but this type can have any name now @@ -138,8 +137,7 @@ access(all) contract ExampleEVMNativeNFTGivenEVMAddress: NonFungibleToken, ICros ) { pre { erc721ID <= UInt256(UInt64.max): - "Provided EVM ID ".concat(erc721ID.toString()) - .concat(" exceeds the assignable Cadence ID of UInt64.max ").concat(UInt64.max.toString()) + "Provided EVM ID \(erc721ID.toString()) exceeds the assignable Cadence ID of UInt64.max \(UInt64.max.toString())" } self.id = UInt64(erc721ID) } @@ -168,10 +166,10 @@ access(all) contract ExampleEVMNativeNFTGivenEVMAddress: NonFungibleToken, ICros case Type(): let collectionDisplay = (ExampleEVMNativeNFTGivenEVMAddress.resolveContractView(resourceType: self.getType(), viewType: Type()) as! MetadataViews.NFTCollectionDisplay?)! return MetadataViews.Display( - name: ExampleEVMNativeNFTGivenEVMAddress.getName().concat(" #").concat(self.id.toString()), + name: "\(ExampleEVMNativeNFTGivenEVMAddress.getName()) #\(self.id.toString())", description: collectionDisplay.description, thumbnail: MetadataViews.HTTPFile( - url: "https://example-nft.flow.com/nft/".concat(self.id.toString()) + url: "https://example-nft.flow.com/nft/\(self.id.toString())" ) ) case Type(): @@ -179,7 +177,7 @@ access(all) contract ExampleEVMNativeNFTGivenEVMAddress: NonFungibleToken, ICros self.id ) case Type(): - return MetadataViews.ExternalURL("https://example-nft.flow.com/".concat(self.id.toString())) + return MetadataViews.ExternalURL("https://example-nft.flow.com/\(self.id.toString())") case Type(): return ExampleEVMNativeNFTGivenEVMAddress.resolveContractView(resourceType: Type<@ExampleEVMNativeNFTGivenEVMAddress.NFT>(), viewType: Type()) case Type(): @@ -438,15 +436,15 @@ access(all) contract ExampleEVMNativeNFTGivenEVMAddress: NonFungibleToken, ICros ) assert( nameRes.status == EVM.Status.successful, - message: "Error on ERC721.name() call with message: ".concat(nameRes.errorMessage) + message: "Error on ERC721.name() call with message: \(nameRes.errorMessage)" ) assert( symbolRes.status == EVM.Status.successful, - message: "Error on ERC721.symbol() call with message: ".concat(symbolRes.errorMessage) + message: "Error on ERC721.symbol() call with message: \(symbolRes.errorMessage)" ) - assert(nameRes.results.length == 1, message: "Unexpected name() return length of ".concat(nameRes.results.length.toString())) - assert(symbolRes.results.length == 1, message: "Unexpected symbol() return length of ".concat(symbolRes.results.length.toString())) + assert(nameRes.results.length == 1, message: "Unexpected name() return length of \(nameRes.results.length.toString())") + assert(symbolRes.results.length == 1, message: "Unexpected symbol() return length of \(symbolRes.results.length.toString())") self.name = nameRes.results[0] as! String self.symbol = symbolRes.results[0] as! String diff --git a/cadence/scripts/utils/get_deployer_address.cdc b/cadence/scripts/utils/get_deployer_address.cdc index fd1436a1..9b7e4789 100644 --- a/cadence/scripts/utils/get_deployer_address.cdc +++ b/cadence/scripts/utils/get_deployer_address.cdc @@ -8,7 +8,7 @@ fun main(coaHost: Address, deployerTag: String): String { let coa = getAuthAccount(coaHost) .storage .borrow(from: /storage/evm) - ?? panic("Could not borrow CadenceOwnedAccount from host=".concat(coaHost.toString())) + ?? panic("Could not borrow CadenceOwnedAccount from host=\(coaHost.toString())") let res = coa.callWithSigAndArgs( to: FlowEVMBridgeUtils.getBridgeFactoryEVMAddress(), signature: "getDeployer(string)", diff --git a/cadence/scripts/utils/get_registry_address.cdc b/cadence/scripts/utils/get_registry_address.cdc index c7de437e..92ee2e1d 100644 --- a/cadence/scripts/utils/get_registry_address.cdc +++ b/cadence/scripts/utils/get_registry_address.cdc @@ -8,7 +8,7 @@ fun main(coaHost: Address): String { let coa = getAuthAccount(coaHost) .storage .borrow(from: /storage/evm) - ?? panic("Could not borrow CadenceOwnedAccount from host=".concat(coaHost.toString())) + ?? panic("Could not borrow CadenceOwnedAccount from host=\(coaHost.toString())") let res = coa.callWithSigAndArgs( to: FlowEVMBridgeUtils.getBridgeFactoryEVMAddress(), signature: "getRegistry()", diff --git a/cadence/transactions/example-assets/evm-assets/mint_erc20.cdc b/cadence/transactions/example-assets/evm-assets/mint_erc20.cdc index f78ebb4a..e0260371 100644 --- a/cadence/transactions/example-assets/evm-assets/mint_erc20.cdc +++ b/cadence/transactions/example-assets/evm-assets/mint_erc20.cdc @@ -25,6 +25,6 @@ transaction( value: 0, resultTypes: nil ) - assert(callResult.status == EVM.Status.successful, message: "ERC20 mint failed with code: ".concat(callResult.errorCode.toString())) + assert(callResult.status == EVM.Status.successful, message: "ERC20 mint failed with code: \(callResult.errorCode.toString())") } } 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 3cb18dfd..99176d89 100644 --- a/cadence/transactions/example-assets/evm-assets/safe_mint_erc721.cdc +++ b/cadence/transactions/example-assets/evm-assets/safe_mint_erc721.cdc @@ -26,6 +26,6 @@ transaction( value: 0, resultTypes: nil ) - assert(callResult.status == EVM.Status.successful, message: "ERC721 mint failed with code: ".concat(callResult.errorCode.toString())) + assert(callResult.status == EVM.Status.successful, message: "ERC721 mint failed with code: \(callResult.errorCode.toString())") } } diff --git a/cadence/transactions/example-assets/evm-assets/unwrap_flow.cdc b/cadence/transactions/example-assets/evm-assets/unwrap_flow.cdc index 03eaf42a..7c7e3813 100644 --- a/cadence/transactions/example-assets/evm-assets/unwrap_flow.cdc +++ b/cadence/transactions/example-assets/evm-assets/unwrap_flow.cdc @@ -26,7 +26,7 @@ transaction(wflowContractHex: String, amount: UInt256) { self.preBalance = UInt(FlowEVMBridgeUtils.balanceOf(owner: self.coa.address(), evmContractAddress: self.wflowAddress)) assert( self.preBalance >= UInt(amount), - message: "Amount exceeds current WFLOW balance of ".concat(self.preBalance.toString()) + message: "Amount exceeds current WFLOW balance of \(self.preBalance.toString())" ) self.postBalance = 0 } From f8c0d1b88e0949fdb1041eeddacbb4462bea9286 Mon Sep 17 00:00:00 2001 From: Josh Hannan Date: Thu, 9 Apr 2026 12:27:38 -0500 Subject: [PATCH 4/4] Update EVM dependency source from mainnet to testnet Switches the EVM contract dependency from mainnet://e467b9dd11fa00df.EVM to testnet://8c5303eaa26202d6.EVM and updates the hash and block height to match the testnet version. All tests pass. --- flow.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flow.json b/flow.json index d4bc7afd..9b87ff84 100644 --- a/flow.json +++ b/flow.json @@ -312,9 +312,9 @@ } }, "EVM": { - "source": "mainnet://e467b9dd11fa00df.EVM", - "hash": "960b0c7df7ee536956af196fba8c8d5dd4f7a89a4ecc61467e31287c4617b0dd", - "block_height": 142536527, + "source": "testnet://8c5303eaa26202d6.EVM", + "hash": "02fdf06350d1aa9eaff70bb355968e46160a226ded80daf229330e7a1d8d8c2c", + "block_height": 315529260, "aliases": { "emulator": "f8d6e0586b0a20c7", "mainnet": "e467b9dd11fa00df",