|
1 | 1 | import "FlowTransactionScheduler" |
| 2 | +import "FungibleToken" |
2 | 3 | import "FlowToken" |
| 4 | +import "EVM" |
| 5 | +import "MetadataViews" |
3 | 6 |
|
4 | 7 | /// FlowTransactionSchedulerUtils provides utility functionality for working with scheduled transactions |
5 | 8 | /// on the Flow blockchain. Currently, it only includes a Manager resource for managing scheduled transactions. |
@@ -561,6 +564,174 @@ access(all) contract FlowTransactionSchedulerUtils { |
561 | 564 | return getAccount(at).capabilities.borrow<&{Manager}>(self.managerPublicPath) |
562 | 565 | } |
563 | 566 |
|
| 567 | + /********************************************* |
| 568 | + |
| 569 | + COA Handler Utils |
| 570 | +
|
| 571 | + **********************************************/ |
| 572 | + |
| 573 | + access(all) view fun coaHandlerStoragePath(): StoragePath { |
| 574 | + return /storage/coaScheduledTransactionHandler |
| 575 | + } |
| 576 | + |
| 577 | + access(all) view fun coaHandlerPublicPath(): PublicPath { |
| 578 | + return /public/coaScheduledTransactionHandler |
| 579 | + } |
| 580 | + |
| 581 | + /// COATransactionHandler is a resource that wraps a capability to a COA (Contract Owned Account) |
| 582 | + /// and implements the TransactionHandler interface to allow scheduling transactions for COAs. |
| 583 | + /// This handler enables users to schedule transactions that will be executed on behalf of their COA. |
| 584 | + access(all) resource COATransactionHandler: FlowTransactionScheduler.TransactionHandler { |
| 585 | + /// The capability to the COA resource |
| 586 | + access(self) let coaCapability: Capability<auth(EVM.Owner) &EVM.CadenceOwnedAccount> |
| 587 | + |
| 588 | + /// The capability to the FlowToken vault |
| 589 | + access(self) let flowTokenVaultCapability: Capability<auth(FungibleToken.Withdraw) &FlowToken.Vault> |
| 590 | + |
| 591 | + init(coaCapability: Capability<auth(EVM.Owner) &EVM.CadenceOwnedAccount>, |
| 592 | + flowTokenVaultCapability: Capability<auth(FungibleToken.Withdraw) &FlowToken.Vault>, |
| 593 | + ) |
| 594 | + { |
| 595 | + self.coaCapability = coaCapability |
| 596 | + self.flowTokenVaultCapability = flowTokenVaultCapability |
| 597 | + } |
| 598 | + |
| 599 | + /// Execute the scheduled transaction using the COA |
| 600 | + /// @param id: The ID of the scheduled transaction |
| 601 | + /// @param data: Optional data passed to the transaction execution. In this case, the data needs to be a COAHandlerParams struct with valid values. |
| 602 | + access(FlowTransactionScheduler.Execute) fun executeTransaction(id: UInt64, data: AnyStruct?) { |
| 603 | + let coa = self.coaCapability.borrow() |
| 604 | + ?? panic("COA capability is invalid or expired for scheduled transaction with ID \(id)") |
| 605 | + |
| 606 | + if let params = data as? COAHandlerParams { |
| 607 | + switch params.txType { |
| 608 | + case COAHandlerTxType.DepositFLOW: |
| 609 | + if params.amount == nil { |
| 610 | + panic("Amount is required for deposit for scheduled transaction with ID \(id)") |
| 611 | + } |
| 612 | + let vault = self.flowTokenVaultCapability.borrow() |
| 613 | + ?? panic("FlowToken vault capability is invalid or expired for scheduled transaction with ID \(id)") |
| 614 | + coa.deposit(from: <-vault.withdraw(amount: params.amount!) as! @FlowToken.Vault) |
| 615 | + case COAHandlerTxType.WithdrawFLOW: |
| 616 | + if params.amount == nil { |
| 617 | + panic("Amount is required for withdrawal from COA for scheduled transaction with ID \(id)") |
| 618 | + } |
| 619 | + let vault = self.flowTokenVaultCapability.borrow() |
| 620 | + ?? panic("FlowToken vault capability is invalid or expired for scheduled transaction with ID \(id)") |
| 621 | + let amount = EVM.Balance(attoflow: 0) |
| 622 | + amount.setFLOW(flow: params.amount!) |
| 623 | + vault.deposit(from: <-coa.withdraw(balance: amount) as! @{FungibleToken.Vault}) |
| 624 | + case COAHandlerTxType.Call: |
| 625 | + if params.callToEVMAddress == nil || params.data == nil || params.gasLimit == nil || params.value == nil { |
| 626 | + panic("Call to EVM address, data, gas limit, and value are required for EVM call for scheduled transaction with ID \(id)") |
| 627 | + } |
| 628 | + let result = coa.call(to: params.callToEVMAddress!, data: params.data!, gasLimit: params.gasLimit!, value: params.value!) |
| 629 | + } |
| 630 | + } else { |
| 631 | + panic("Invalid scheduled transactiondata type for COA handler execution for tx with ID \(id)! Expected FlowTransactionSchedulerUtils.COAHandlerParams but got \(data.getType().identifier)") |
| 632 | + } |
| 633 | + } |
| 634 | + |
| 635 | + /// Get the views supported by this handler |
| 636 | + /// @return: Array of view types |
| 637 | + access(all) view fun getViews(): [Type] { |
| 638 | + return [ |
| 639 | + Type<COAHandlerView>(), |
| 640 | + Type<StoragePath>(), |
| 641 | + Type<PublicPath>(), |
| 642 | + Type<MetadataViews.Display>() |
| 643 | + ] |
| 644 | + } |
| 645 | + |
| 646 | + /// Resolve a view for this handler |
| 647 | + /// @param viewType: The type of view to resolve |
| 648 | + /// @return: The resolved view data, or nil if not supported |
| 649 | + access(all) fun resolveView(_ viewType: Type): AnyStruct? { |
| 650 | + if viewType == Type<COAHandlerView>() { |
| 651 | + return COAHandlerView( |
| 652 | + coaOwner: self.coaCapability.borrow()?.owner?.address, |
| 653 | + coaEVMAddress: self.coaCapability.borrow()?.address(), |
| 654 | + ) |
| 655 | + } |
| 656 | + if viewType == Type<StoragePath>() { |
| 657 | + return FlowTransactionSchedulerUtils.coaHandlerStoragePath() |
| 658 | + } else if viewType == Type<PublicPath>() { |
| 659 | + return FlowTransactionSchedulerUtils.coaHandlerPublicPath() |
| 660 | + } else if viewType == Type<MetadataViews.Display>() { |
| 661 | + return MetadataViews.Display( |
| 662 | + name: "COA Scheduled Transaction Handler", |
| 663 | + description: "Scheduled Transaction Handler that can execute transactions on behalf of a COA", |
| 664 | + thumbnail: MetadataViews.HTTPFile( |
| 665 | + url: "" |
| 666 | + ) |
| 667 | + ) |
| 668 | + } |
| 669 | + return nil |
| 670 | + } |
| 671 | + } |
| 672 | + |
| 673 | + /// Enum for COA handler execution type |
| 674 | + access(all) enum COAHandlerTxType: UInt8 { |
| 675 | + access(all) case DepositFLOW |
| 676 | + access(all) case WithdrawFLOW |
| 677 | + access(all) case Call |
| 678 | + |
| 679 | + // TODO: Should we have other transaction types?? |
| 680 | + } |
| 681 | + |
| 682 | + access(all) struct COAHandlerParams { |
| 683 | + |
| 684 | + access(all) let txType: COAHandlerTxType |
| 685 | + |
| 686 | + access(all) let amount: UFix64? |
| 687 | + access(all) let callToEVMAddress: EVM.EVMAddress? |
| 688 | + access(all) let data: [UInt8]? |
| 689 | + access(all) let gasLimit: UInt64? |
| 690 | + access(all) let value: EVM.Balance? |
| 691 | + |
| 692 | + init(txType: UInt8, amount: UFix64?, callToEVMAddress: [UInt8; 20]?, data: [UInt8]?, gasLimit: UInt64?, value: UFix64?) { |
| 693 | + self.txType = COAHandlerTxType(rawValue: txType) |
| 694 | + ?? panic("Invalid COA transaction type enum") |
| 695 | + self.amount = amount |
| 696 | + self.callToEVMAddress = callToEVMAddress != nil ? EVM.EVMAddress(bytes: callToEVMAddress!) : nil |
| 697 | + self.data = data |
| 698 | + self.gasLimit = gasLimit |
| 699 | + if let unwrappedValue = value { |
| 700 | + self.value = EVM.Balance(attoflow: 0) |
| 701 | + self.value!.setFLOW(flow: unwrappedValue) |
| 702 | + } else { |
| 703 | + self.value = nil |
| 704 | + } |
| 705 | + } |
| 706 | + } |
| 707 | + |
| 708 | + /// View struct for COA handler metadata |
| 709 | + access(all) struct COAHandlerView { |
| 710 | + access(all) let coaOwner: Address? |
| 711 | + access(all) let coaEVMAddress: EVM.EVMAddress? |
| 712 | + |
| 713 | + // TODO: Should we include other metadata about the COA, like balance, code, etc??? |
| 714 | +
|
| 715 | + init(coaOwner: Address?, coaEVMAddress: EVM.EVMAddress?) { |
| 716 | + self.coaOwner = coaOwner |
| 717 | + self.coaEVMAddress = coaEVMAddress |
| 718 | + } |
| 719 | + } |
| 720 | + |
| 721 | + /// Create a COA transaction handler |
| 722 | + /// @param coaCapability: Capability to the COA resource |
| 723 | + /// @param metadata: Optional metadata about the handler |
| 724 | + /// @return: A new COATransactionHandler resource |
| 725 | + access(all) fun createCOATransactionHandler( |
| 726 | + coaCapability: Capability<auth(EVM.Owner) &EVM.CadenceOwnedAccount>, |
| 727 | + flowTokenVaultCapability: Capability<auth(FungibleToken.Withdraw) &FlowToken.Vault>, |
| 728 | + ): @COATransactionHandler { |
| 729 | + return <-create COATransactionHandler( |
| 730 | + coaCapability: coaCapability, |
| 731 | + flowTokenVaultCapability: flowTokenVaultCapability, |
| 732 | + ) |
| 733 | + } |
| 734 | + |
564 | 735 | /******************************************** |
565 | 736 | |
566 | 737 | Scheduled Transactions Metadata Views |
|
0 commit comments