Skip to content

Commit b096716

Browse files
committed
add tests for executing COA transactions
1 parent 26e26f7 commit b096716

File tree

8 files changed

+469
-20
lines changed

8 files changed

+469
-20
lines changed

contracts/FlowTransactionSchedulerUtils.cdc

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,12 @@ access(all) contract FlowTransactionSchedulerUtils {
602602
self.flowTokenVaultCapability = flowTokenVaultCapability
603603
}
604604

605+
access(self) fun emitError(id: UInt64, errorMessage: String) {
606+
let coa = self.coaCapability.borrow()!
607+
emit COAHandlerExecutionError(id: id, owner: self.owner?.address, coaAddress: coa.address().toString(),
608+
errorMessage: errorMessage)
609+
}
610+
605611
/// Execute the scheduled transaction using the COA
606612
/// @param id: The ID of the scheduled transaction
607613
/// @param data: Optional data passed to the transaction execution. In this case, the data must be a COAHandlerParams struct with valid values.
@@ -614,49 +620,62 @@ access(all) contract FlowTransactionSchedulerUtils {
614620
}
615621

616622
if let transactions = data as? [COAHandlerParams] {
617-
for txParams in transactions {
623+
for index, txParams in transactions {
618624
switch txParams.txType {
619625
case COAHandlerTxType.DepositFLOW:
620626
if txParams.amount == nil {
621-
emit COAHandlerExecutionError(id: id, owner: self.owner?.address, coaAddress: coa!.address().toString(),
622-
errorMessage: "Amount is required for deposit for scheduled transaction with ID \(id)")
627+
self.emitError(id: id, errorMessage: "Amount is required for deposit for scheduled transaction with ID \(id) and index \(index)")
623628
return
624629
}
625630
let vault = self.flowTokenVaultCapability.borrow()
626631
if vault == nil {
627-
emit COAHandlerExecutionError(id: id, owner: self.owner?.address, coaAddress: coa!.address().toString(), errorMessage: "FlowToken vault capability is invalid or expired for scheduled transaction with ID \(id)")
632+
self.emitError(id: id, errorMessage: "FlowToken vault capability is invalid or expired for scheduled transaction with ID \(id) and index \(index)")
628633
return
629634
}
635+
if txParams.amount! > vault!.balance && !txParams.revertOnFailure {
636+
self.emitError(id: id, errorMessage: "Insufficient FLOW in FlowToken vault for deposit into COA for scheduled transaction with ID \(id) and index \(index)")
637+
continue
638+
}
630639
coa!.deposit(from: <-vault!.withdraw(amount: txParams.amount!) as! @FlowToken.Vault)
631640
case COAHandlerTxType.WithdrawFLOW:
632641
if txParams.amount == nil {
633-
emit COAHandlerExecutionError(id: id, owner: self.owner?.address, coaAddress: coa!.address().toString(),
634-
errorMessage: "Amount is required for withdrawal from COA for scheduled transaction with ID \(id)")
642+
self.emitError(id: id, errorMessage: "Amount is required for withdrawal from COA for scheduled transaction with ID \(id) and index \(index)")
635643
}
636644

637645
let vault = self.flowTokenVaultCapability.borrow()
638646
if vault == nil {
639-
emit COAHandlerExecutionError(id: id, owner: self.owner?.address, coaAddress: coa!.address().toString(),
640-
errorMessage: "FlowToken vault capability is invalid or expired for scheduled transaction with ID \(id)")
647+
self.emitError(id: id, errorMessage: "FlowToken vault capability is invalid or expired for scheduled transaction with ID \(id) and index \(index)")
641648
return
642649
}
643650

644651
let amount = EVM.Balance(attoflow: 0)
645652
amount.setFLOW(flow: txParams.amount!)
646653

654+
if amount.attoflow > coa!.balance().attoflow && !txParams.revertOnFailure {
655+
self.emitError(id: id, errorMessage: "Insufficient FLOW in COA vault for withdrawal from COA for scheduled transaction with ID \(id) and index \(index)")
656+
continue
657+
}
658+
647659
vault!.deposit(from: <-coa!.withdraw(balance: amount))
648660
case COAHandlerTxType.Call:
649661
if txParams.callToEVMAddress == nil || txParams.data == nil || txParams.gasLimit == nil || txParams.value == nil {
650-
emit COAHandlerExecutionError(id: id, owner: self.owner?.address, coaAddress: coa!.address().toString(),
651-
errorMessage: "Call to EVM address, data, gas limit, and value are required for EVM call for scheduled transaction with ID \(id)")
662+
self.emitError(id: id, errorMessage: "Call to EVM address, data, gas limit, and value are required for EVM call for scheduled transaction with ID \(id) and index \(index)")
652663
return
653664
}
654665
let result = coa!.call(to: txParams.callToEVMAddress!, data: txParams.data!, gasLimit: txParams.gasLimit!, value: txParams.value!)
666+
667+
if result.status != EVM.Status.successful {
668+
if !txParams.revertOnFailure {
669+
self.emitError(id: id, errorMessage: "EVM call failed for scheduled transaction with ID \(id) and index \(index) with error: \(result.errorCode):\(result.errorMessage)")
670+
continue
671+
} else {
672+
panic("EVM call failed for scheduled transaction with ID \(id) and index \(index) with error: \(result.errorCode):\(result.errorMessage)")
673+
}
674+
}
655675
}
656676
}
657677
} else {
658-
emit COAHandlerExecutionError(id: id, owner: self.owner?.address, coaAddress: coa!.address().toString(),
659-
errorMessage: "Invalid scheduled transaction data type for COA handler execution for tx with ID \(id)! Expected FlowTransactionSchedulerUtils.COAHandlerParams but got \(data.getType().identifier)")
678+
self.emitError(id: id, errorMessage: "Invalid scheduled transaction data type for COA handler execution for tx with ID \(id)! Expected [FlowTransactionSchedulerUtils.COAHandlerParams] but got \(data.getType().identifier)")
660679
return
661680
}
662681
}
@@ -738,13 +757,22 @@ access(all) contract FlowTransactionSchedulerUtils {
738757
assert(amount != nil, message: "Amount is required for withdrawal but was not provided")
739758
}
740759
if self.txType == COAHandlerTxType.Call {
741-
assert(callToEVMAddress != nil && callToEVMAddress!.length == 20, message: "Call to EVM address is required for EVM call but was not provided or is invalid")
760+
assert(callToEVMAddress != nil && callToEVMAddress!.length == 40, message: "Call to EVM address is required for EVM call but was not provided or is invalid length (must be 40 hex chars)")
742761
assert(data != nil, message: "Data is required for EVM call but was not provided")
743762
assert(gasLimit != nil, message: "Gas limit is required for EVM call but was not provided")
744763
assert(value != nil, message: "Value is required for EVM call but was not provided")
745764
}
746765
self.amount = amount
747-
self.callToEVMAddress = callToEVMAddress != nil ? EVM.EVMAddress(bytes: callToEVMAddress!.decodeHex() as! [UInt8; 20]) : nil
766+
if callToEVMAddress != nil {
767+
let decodedAddress = callToEVMAddress!.decodeHex()
768+
assert(
769+
decodedAddress.length == 20,
770+
message:"Invalid EVM address length for scheduled transaction! Expected 20 bytes but got \(decodedAddress.length) bytes for EVM address \(callToEVMAddress!)"
771+
)
772+
self.callToEVMAddress = EVM.EVMAddress(bytes: decodedAddress.toConstantSized<[UInt8; 20]>()!)
773+
} else {
774+
self.callToEVMAddress = nil
775+
}
748776
self.data = data
749777
self.gasLimit = gasLimit
750778
if let unwrappedValue = value {

lib/go/contracts/internal/assets/assets.go

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)