|
| 1 | +import "FlowStorageFees" |
| 2 | +import "FungibleToken" |
| 3 | +import "FlowToken" |
| 4 | + |
| 5 | +import "EVM" |
| 6 | + |
| 7 | +// Transfers $FLOW from the signer's account to the recipient's address, determining the target VM based on the format |
| 8 | +// of the recipient's hex address. Note that the sender's funds are sourced by default from the target VM, pulling any |
| 9 | +// difference from the alternate VM if available. e.g. Transfers to Flow addresses will first attempt to withdraw from |
| 10 | +// the signer's Flow vault, pulling any remaining funds from the signer's EVM account if available. Transfers to EVM |
| 11 | +// addresses will first attempt to withdraw from the signer's EVM account, pulling any remaining funds from the signer's |
| 12 | +// Flow vault if available. If the signer's balance across both VMs is insufficient, the transaction will revert. |
| 13 | +/// |
| 14 | +/// @param addressString: The recipient's address in hex format - this should be either an EVM address or a Flow address |
| 15 | +/// @param amount: The amount of $FLOW to transfer as a UFix64 value |
| 16 | +/// |
| 17 | +transaction(addressString: String, amount: UFix64) { |
| 18 | + |
| 19 | + let sentVault: @FlowToken.Vault |
| 20 | + let evmRecipient: EVM.EVMAddress? |
| 21 | + |
| 22 | + prepare(signer: auth(BorrowValue, SaveValue) &Account) { |
| 23 | + // Reference signer's COA if one exists |
| 24 | + let coa = signer.storage.borrow<auth(EVM.Withdraw) &EVM.CadenceOwnedAccount>(from: /storage/evm) |
| 25 | + |
| 26 | + // Reference signer's FlowToken Vault |
| 27 | + let sourceVault = signer.storage.borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(from: /storage/flowTokenVault) |
| 28 | + ?? panic("Could not borrow signer's FlowToken.Vault") |
| 29 | + // Ensure we don't withdraw more than required for storage |
| 30 | + let cadenceBalance = FlowStorageFees.defaultTokenAvailableBalance(signer.address) |
| 31 | + |
| 32 | + // Define optional recipients for both VMs |
| 33 | + self.evmRecipient = EVM.addressFromString(addressString) |
| 34 | + // Validate exactly one target address is assigned |
| 35 | + if self.evmRecipient == nil { |
| 36 | + panic("Malformed recipient address - not assignable as either Cadence or EVM address") |
| 37 | + } |
| 38 | + |
| 39 | + // Create empty FLOW vault to capture funds |
| 40 | + self.sentVault <- FlowToken.createEmptyVault(vaultType: Type<@FlowToken.Vault>()) |
| 41 | + |
| 42 | + // Check signer's balance can cover the amount |
| 43 | + if coa != nil { |
| 44 | + // Determine the amount to withdraw from the signer's EVM account |
| 45 | + let balance = coa!.balance() |
| 46 | + let withdrawAmount = amount < balance.inFLOW() ? amount : balance.inFLOW() |
| 47 | + balance.setFLOW(flow: withdrawAmount) |
| 48 | + |
| 49 | + // Withdraw funds from EVM to the sentVault |
| 50 | + self.sentVault.deposit(from: <-coa!.withdraw(balance: balance)) |
| 51 | + } |
| 52 | + if amount > self.sentVault.balance { |
| 53 | + // Insufficient amount withdrawn from EVM, check signer's Flow balance |
| 54 | + let difference = amount - self.sentVault.balance |
| 55 | + if difference > cadenceBalance { |
| 56 | + panic("Insufficient balance across Flow and EVM accounts") |
| 57 | + } |
| 58 | + // Withdraw from the signer's Cadence Vault and deposit to sentVault |
| 59 | + self.sentVault.deposit(from: <-sourceVault.withdraw(amount: difference)) |
| 60 | + } |
| 61 | + } |
| 62 | + |
| 63 | + pre { |
| 64 | + self.sentVault.balance == amount: "Attempting to send an incorrect amount of $FLOW" |
| 65 | + } |
| 66 | + |
| 67 | + execute { |
| 68 | + // Otherwise, complete EVM transfer |
| 69 | + self.evmRecipient!.deposit(from: <-self.sentVault) |
| 70 | + } |
| 71 | +} |
0 commit comments