DeploySEALED
!%!◆@●○^#░○◆▓^◇##█▓~▪╳#*◇■◆%■*##~█##▪!╱?^╳╳^◇▪▒◇▫╱◆&$█▓□■■~■◇@╱▒
Transaction ID
Execution Fee
0.00322 FLOWTransaction Summary
DeployContract deployment
Contract deployment
Script Arguments
0nameString
DCAHandlerEVMV2
1codeString
import FlowTransactionScheduler from 0xe467b9dd11fa00df
import FlowTransactionSchedulerUtils from 0xe467b9dd11fa00df
import FlowToken from 0x1654653399040a61
import FungibleToken from 0xf233dcee88fe0abe
import DCAServiceEVM from 0xca7ee55e4fc3251a
/// DCAHandlerEVMV2: Autonomous Scheduled Transaction Handler for EVM-Native DCA
///
/// This is an enhanced version of DCAHandlerEVM that uses the Manager pattern
/// for autonomous rescheduling. After each execution, it automatically schedules
/// the next execution without requiring backend intervention.
///
/// Key features:
/// - Works with existing DCAServiceEVM (no migration needed)
/// - Uses FlowTransactionSchedulerUtils.Manager for auto-rescheduling
/// - Withdraws fees from a configured fee vault capability
///
access(all) contract DCAHandlerEVMV2 {
// ============================================================
// Events
// ============================================================
access(all) event HandlerCreated(uuid: UInt64)
access(all) event ExecutionTriggered(planId: UInt64, success: Bool, nextScheduled: Bool)
access(all) event ExecutionSkipped(planId: UInt64, reason: String)
access(all) event NextExecutionScheduled(planId: UInt64, scheduledId: UInt64, timestamp: UFix64)
access(all) event NextExecutionSchedulingFailed(planId: UInt64, reason: String)
// ============================================================
// Storage Paths
// ============================================================
access(all) let HandlerStoragePath: StoragePath
// ============================================================
// Configuration Structs
// ============================================================
/// Configuration for autonomous rescheduling
access(all) struct ScheduleConfig {
access(all) let schedulerManagerCap: Capability<auth(FlowTransactionSchedulerUtils.Owner) &{FlowTransactionSchedulerUtils.Manager}>
access(all) let feeVaultCap: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>
access(all) let priority: FlowTransactionScheduler.Priority
access(all) let executionEffort: UInt64
init(
schedulerManagerCap: Capability<auth(FlowTransactionSchedulerUtils.Owner) &{FlowTransactionSchedulerUtils.Manager}>,
feeVaultCap: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>,
priority: FlowTransactionScheduler.Priority,
executionEffort: UInt64
) {
self.schedulerManagerCap = schedulerManagerCap
self.feeVaultCap = feeVaultCap
self.priority = priority
self.executionEffort = executionEffort
}
}
/// Transaction data passed to handler (includes scheduling config)
access(all) struct TransactionData {
access(all) let planId: UInt64
access(all) let scheduleConfig: ScheduleConfig
init(planId: UInt64, scheduleConfig: ScheduleConfig) {
self.planId = planId
self.scheduleConfig = scheduleConfig
}
}
// ============================================================
// Handler Resource
// ============================================================
access(all) resource Handler: FlowTransactionScheduler.TransactionHandler {
/// Main execution entrypoint called by FlowTransactionScheduler
access(FlowTransactionScheduler.Execute)
fun executeTransaction(id: UInt64, data: AnyStruct?) {
// Parse transaction data
let txData = data as? TransactionData
if txData == nil {
log("DCAHandlerEVMV2: Invalid transaction data")
return
}
let planId = txData!.planId
let scheduleConfig = txData!.scheduleConfig
// Get plan details
let planOpt = DCAServiceEVM.getPlan(planId: planId)
if planOpt == nil {
emit ExecutionSkipped(planId: planId, reason: "Plan not found")
return
}
let plan = planOpt!
// Check if plan is active
if plan.getStatus() != DCAServiceEVM.PlanStatus.Active {
emit ExecutionSkipped(planId: planId, reason: "Plan not active")
return
}
// Execute the DCA plan via DCAServiceEVM
let success = DCAServiceEVM.executePlan(planId: planId)
// If successful and plan still active, schedule next execution
var nextScheduled = false
if success {
// Re-fetch plan to get updated nextExecutionTime
let updatedPlanOpt = DCAServiceEVM.getPlan(planId: planId)
if updatedPlanOpt != nil {
let updatedPlan = updatedPlanOpt!
// Only reschedule if plan is still active
if updatedPlan.getStatus() == DCAServiceEVM.PlanStatus.Active {
nextScheduled = self.scheduleNextExecution(
planId: planId,
nextExecutionTime: updatedPlan.nextExecutionTime,
scheduleConfig: scheduleConfig
)
}
}
}
emit ExecutionTriggered(planId: planId, success: success, nextScheduled: nextScheduled)
}
/// Schedule the next execution via FlowTransactionScheduler Manager
access(self) fun scheduleNextExecution(
planId: UInt64,
nextExecutionTime: UFix64?,
scheduleConfig: ScheduleConfig
): Bool {
// Verify nextExecutionTime is provided
if nextExecutionTime == nil {
emit NextExecutionSchedulingFailed(planId: planId, reason: "Next execution time not set")
return false
}
// Prepare transaction data for next execution
let nextTxData = TransactionData(
planId: planId,
scheduleConfig: scheduleConfig
)
// Estimate fees
let estimate = FlowTransactionScheduler.estimate(
data: nextTxData,
timestamp: nextExecutionTime!,
priority: scheduleConfig.priority,
executionEffort: scheduleConfig.executionEffort
)
let feeAmount = estimate.flowFee ?? 0.001 // Default to 0.001 if nil
let feeWithBuffer = feeAmount * 1.1 // Add 10% buffer
// Borrow fee vault
let feeVault = scheduleConfig.feeVaultCap.borrow()
if feeVault == nil {
emit NextExecutionSchedulingFailed(planId: planId, reason: "Could not borrow fee vault")
return false
}
// Check balance
if feeVault!.balance < feeWithBuffer {
emit NextExecutionSchedulingFailed(
planId: planId,
reason: "Insufficient fees. Required: ".concat(feeWithBuffer.toString()).concat(", Available: ").concat(feeVault!.balance.toString())
)
return false
}
// Withdraw fees
let fees <- feeVault!.withdraw(amount: feeWithBuffer)
// Borrow scheduler manager
let schedulerManager = scheduleConfig.schedulerManagerCap.borrow()
if schedulerManager == nil {
// Return fees to vault
feeVault!.deposit(from: <-fees)
emit NextExecutionSchedulingFailed(planId: planId, reason: "Could not borrow scheduler manager")
return false
}
// Schedule next execution using Manager.scheduleByHandler()
let scheduledId = schedulerManager!.scheduleByHandler(
handlerTypeIdentifier: self.getType().identifier,
handlerUUID: self.uuid,
data: nextTxData,
timestamp: nextExecutionTime!,
priority: scheduleConfig.priority,
executionEffort: scheduleConfig.executionEffort,
fees: <-fees as! @FlowToken.Vault
)
if scheduledId == 0 {
emit NextExecutionSchedulingFailed(planId: planId, reason: "scheduleByHandler returned 0")
return false
}
emit NextExecutionScheduled(planId: planId, scheduledId: scheduledId, timestamp: nextExecutionTime!)
return true
}
init() {
emit HandlerCreated(uuid: self.uuid)
}
}
// ============================================================
// Factory Functions
// ============================================================
/// Create a new handler resource
access(all) fun createHandler(): @Handler {
return <- create Handler()
}
/// Create schedule configuration
access(all) fun createScheduleConfig(
schedulerManagerCap: Capability<auth(FlowTransactionSchedulerUtils.Owner) &{FlowTransactionSchedulerUtils.Manager}>,
feeVaultCap: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>,
priority: FlowTransactionScheduler.Priority,
executionEffort: UInt64
): ScheduleConfig {
return ScheduleConfig(
schedulerManagerCap: schedulerManagerCap,
feeVaultCap: feeVaultCap,
priority: priority,
executionEffort: executionEffort
)
}
/// Create transaction data
access(all) fun createTransactionData(
planId: UInt64,
scheduleConfig: ScheduleConfig
): TransactionData {
return TransactionData(
planId: planId,
scheduleConfig: scheduleConfig
)
}
// ============================================================
// Init
// ============================================================
init() {
self.HandlerStoragePath = /storage/DCAHandlerEVMV2
}
}
Cadence Script
1transaction(name: String, code: String ) {
2 prepare(signer: auth(AddContract) &Account) {
3 signer.contracts.add(name: name, code: code.utf8 )
4 }
5 }