diff --git a/cadence/contracts/bridge/FlowEVMBridgeHandlers.cdc b/cadence/contracts/bridge/FlowEVMBridgeHandlers.cdc index dea4350b..b77d0246 100644 --- a/cadence/contracts/bridge/FlowEVMBridgeHandlers.cdc +++ b/cadence/contracts/bridge/FlowEVMBridgeHandlers.cdc @@ -192,14 +192,6 @@ access(all) contract FlowEVMBridgeHandlers { } } - /** - TODO: - - Configure handler - - - - Update association between FLOW & WFLOW - - Remove any pre-conditions on FLOW in bridging functionality - */ - /// Facilitates moving Flow between Cadence and EVM as WFLOW. Since WFLOW is an artifact of the EVM ecosystem, /// wrapping the native token as an ERC20, it does not have a place in Cadence's fungible token ecosystem. /// Given the native interface on EVM.CadenceOwnedAccount and EVM.EVMAddress to move FLOW between Cadence and EVM, @@ -338,7 +330,7 @@ access(all) contract FlowEVMBridgeHandlers { // Unwrap the transferred WFLOW to FLOW, giving the bridge COA the necessary FLOW to withdraw from EVM let unwrapResult = FlowEVMBridgeUtils.call( - signature: "withdraw(uint)", + signature: "withdraw(uint256)", targetEVMAddress: wflowAddress, args: [amount], gasLimit: FlowEVMBridgeConfig.gasLimit, @@ -351,7 +343,8 @@ access(all) contract FlowEVMBridgeHandlers { // Cover underflow assert( postBalance > preBalance, - message: "Escrowed FLOW Balance did not increment after unwrapping WFLOW" + message: "Escrowed FLOW Balance did not increment after unwrapping WFLOW - pre: ".concat(preBalance.toString()) + .concat(" post: ").concat(postBalance.toString()) ) // Confirm bridge COA's FLOW balance has incremented by the expected amount assert( @@ -382,7 +375,9 @@ access(all) contract FlowEVMBridgeHandlers { return <-flowVault } - /* --- Admin --- */ + /* --- HandlerAdmin --- */ + // Conforms to HandlerAdmin for enableBridging, but most of the methods are unnecessary given the strict + // association between FLOW and WFLOW /// Sets the target type for the handler access(FlowEVMBridgeHandlerInterfaces.Admin) diff --git a/cadence/tests/flow_evm_wflow_handler_tests.cdc b/cadence/tests/flow_evm_wflow_handler_tests.cdc index a89013d7..e6f3575a 100644 --- a/cadence/tests/flow_evm_wflow_handler_tests.cdc +++ b/cadence/tests/flow_evm_wflow_handler_tests.cdc @@ -5,6 +5,7 @@ import "FungibleToken" import "NonFungibleToken" import "FlowStorageFees" import "EVM" +import "FlowEVMBridgeConfig" import "test_helpers.cdc" @@ -347,7 +348,7 @@ fun testOnboardWFLOWByEVMAddressFails() { Test.expect(onboardingResult, Test.beFailed()) } -/* --- BRIDGING FUNGIBLE TOKENS - Test bridging both Cadence- & EVM-native fungible tokens --- */ +/* --- BRIDGING FLOW to EVM as WFLOW and WFLOW from EVM as FLOW --- */ // Now enable TokenHandler to bridge in both directions access(all) @@ -361,6 +362,22 @@ fun testEnableWFLOWTokenHandlerSucceeds() { // TODO: Validate event emission and values } +// Validate that funds can be bridged from Cadence to EVM, resulting in balance increase in WFLOW as target +access(all) +fun testBridgeZeroFLOWTokenToEVMFails() { + // Attempt bridge 0 FLOW to EVM - should fail + bridgeTokensToEVM( + signer: alice, + vaultIdentifier: buildTypeIdentifier( + address: flowTokenAccountAddress, + contractName: "FlowToken", + resourceName: "Vault" + ), + amount: 0.0, + beFailed: true + ) +} + // Validate that funds can be bridged from Cadence to EVM, resulting in balance increase in WFLOW as target access(all) fun testBridgeFLOWTokenToEVMFirstSucceeds() { @@ -372,6 +389,7 @@ fun testBridgeFLOWTokenToEVMFirstSucceeds() { var cadenceBalance = getBalance(ownerAddr: alice.address, storagePathIdentifier: "flowTokenVault") ?? panic("Problem getting FlowToken balance") Test.assert(cadenceBalance == flowFundingAmount - coaFundingAmount, message: "Invalid Cadence balance") + // Leave some FLOW as it's needed for storage, transaction, and bridge fees let remainder = 1.0 let bridgeAmount = cadenceBalance - remainder @@ -406,9 +424,23 @@ fun testBridgeFLOWTokenToEVMFirstSucceeds() { // With all funds now in EVM, we can test bridging back to Cadence access(all) -fun testBridgeWFLOWTokenFromEVMSecondSucceeds() { +fun testBridgeZeroWFLOWTokenFromEVMSecondFails() { + bridgeTokensFromEVM( + signer: alice, + vaultIdentifier: buildTypeIdentifier( + address: flowTokenAccountAddress, + contractName: "FlowToken", + resourceName: "Vault" + ), + amount: UInt256(0), + beFailed: true + ) +} - let wflowTotalSupplyBefore = getEVMTotalSupply(erc20AddressHex: wflowAddressHex) +// With all funds now in EVM, we can test bridging back to Cadence +access(all) +fun testBridgeWFLOWTokenFromEVMSecondSucceeds() { + // let wflowTotalSupplyBefore = getEVMTotalSupply(erc20AddressHex: wflowAddressHex) let cadenceBalanceBefore = getBalance(ownerAddr: alice.address, storagePathIdentifier: "flowTokenVault") ?? panic("Problem getting FlowToken balance") @@ -430,19 +462,20 @@ fun testBridgeWFLOWTokenFromEVMSecondSucceeds() { // Confirm that Alice's balance has been bridged to Cadence let cadenceBalanceAfter = getBalance(ownerAddr: alice.address, storagePathIdentifier: "flowTokenVault") ?? panic("Problem getting FlowToken balance") - Test.assertEqual(ufixEVMbalance, cadenceBalanceAfter) + let expectedBalanceAfter = cadenceBalanceBefore + ufixEVMbalance - FlowEVMBridgeConfig.baseFee + Test.assertEqual(expectedBalanceAfter, cadenceBalanceAfter) - // Confirm that the ERC20 balance was burned in the process of bridging + // Confirm that the WFLOW balance was transferred out in the process of bridging let evmBalanceAfter = balanceOf(evmAddressHex: aliceCOAAddressHex, erc20AddressHex: wflowAddressHex) Test.assertEqual(UInt256(0), evmBalanceAfter) - // // Validate that the ERC20 balance in circulation remained the same - // let erc20TotalSupplyAfter = getEVMTotalSupply(erc20AddressHex: wflowAddressHex) - // Test.assertEqual(wflowTotalSupplyBefore, erc20TotalSupplyAfter) + // Validate that the WFLOW supply in circulation reduced to 0 + let wflowTotalSupplyAfter = getEVMTotalSupply(erc20AddressHex: wflowAddressHex) + Test.assertEqual(UInt256(0), wflowTotalSupplyAfter) - // // Validate that all ERC20 funds are now in escrow since all bridged to Cadence - // let escrowBalance = balanceOf(evmAddressHex: getBridgeCOAAddressHex(), erc20AddressHex: wflowAddressHex) - // Test.assertEqual(erc20TotalSupplyAfter, escrowBalance) + // Validate that all WFLOW funds are now in escrow since all bridged to Cadence + let escrowBalance = balanceOf(evmAddressHex: getBridgeCOAAddressHex(), erc20AddressHex: wflowAddressHex) + Test.assertEqual(wflowTotalSupplyAfter, escrowBalance) } // // Now test bridging with liquidity flow moving entirely from EVM to Cadence and back diff --git a/cadence/transactions/example-assets/evm-assets/unwrap_flow.cdc b/cadence/transactions/example-assets/evm-assets/unwrap_flow.cdc index a31d85c3..448259ac 100644 --- a/cadence/transactions/example-assets/evm-assets/unwrap_flow.cdc +++ b/cadence/transactions/example-assets/evm-assets/unwrap_flow.cdc @@ -5,7 +5,7 @@ import "EVM" import "FlowEVMBridgeUtils" -/// This transactions wraps FLOW tokens as WFLOW tokens, using the signing COA's EVM FLOW balance primarily. If the +/// This transactions wraps FLOW tokens as WFLOW tokens, using the signing COA's EVM FLOW balance primarily. If the /// EVM balance is insufficient, the transaction will transfer FLOW from the Cadence balance to the EVM balance. /// /// @param wflowContractHex: The EVM address of the WFLOW contract as a hex string @@ -22,13 +22,18 @@ transaction(wflowContractHex: String, amount: UInt256) { self.wflowAddress = EVM.addressFromString(wflowContractHex) self.coa = signer.storage.borrow(from: /storage/evm) ?? panic("Could not borrow COA from provided gateway address") + 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()) + ) self.postBalance = 0 } execute { // Encode the withdraw function call - let calldata = EVM.encodeABIWithSignature("withdraw(uint)", [UInt(amount)]) + 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 @@ -38,7 +43,7 @@ transaction(wflowContractHex: String, amount: UInt256) { gasLimit: 15_000_000, value: value ) - assert(result.status == EVM.Status.successful, message: "Failed to wrap FLOW as WFLOW") + 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)) }