Smart Contract

DCATestHandler

A.ca7ee55e4fc3251a.DCATestHandler

Valid From

135,730,152

Deployed

5d ago
Feb 23, 2026, 12:48:07 AM UTC

Dependents

4 imports
1import FlowTransactionScheduler from 0xe467b9dd11fa00df
2import FlowTransactionSchedulerUtils from 0xe467b9dd11fa00df
3import FlowToken from 0x1654653399040a61
4import FungibleToken from 0xf233dcee88fe0abe
5
6/// DCATestHandler: Simple Cadence-only test handler for scheduled transactions
7///
8/// This is a minimal test handler to verify the scheduled transaction pattern
9/// works correctly BEFORE adding EVM complexity.
10///
11/// Pattern: Follows official scaffold (CounterLoopTransactionHandler)
12/// - LoopConfig carries capabilities in TransactionData
13/// - Handler uses Manager.scheduleByHandler() to reschedule
14/// - No EVM calls - just logs and events
15///
16access(all) contract DCATestHandler {
17
18    // ============================================================
19    // Events
20    // ============================================================
21
22    access(all) event HandlerCreated(uuid: UInt64)
23    access(all) event TestExecuted(executionId: UInt64, count: UInt64, timestamp: UFix64)
24    access(all) event NextScheduled(executionId: UInt64, scheduledId: UInt64, timestamp: UFix64)
25    access(all) event SchedulingFailed(executionId: UInt64, reason: String)
26    access(all) event TestCompleted(executionId: UInt64, totalExecutions: UInt64)
27
28    // ============================================================
29    // Storage Paths
30    // ============================================================
31
32    access(all) let HandlerStoragePath: StoragePath
33    access(all) let HandlerPublicPath: PublicPath
34
35    // ============================================================
36    // LoopConfig: Capabilities passed in TransactionData
37    // ============================================================
38
39    access(all) struct LoopConfig {
40        access(all) let schedulerManagerCap: Capability<auth(FlowTransactionSchedulerUtils.Owner) &{FlowTransactionSchedulerUtils.Manager}>
41        access(all) let feeProviderCap: Capability<auth(FungibleToken.Withdraw) &FlowToken.Vault>
42        access(all) let priority: FlowTransactionScheduler.Priority
43        access(all) let executionEffort: UInt64
44        access(all) let intervalSeconds: UFix64
45        access(all) let maxExecutions: UInt64
46
47        init(
48            schedulerManagerCap: Capability<auth(FlowTransactionSchedulerUtils.Owner) &{FlowTransactionSchedulerUtils.Manager}>,
49            feeProviderCap: Capability<auth(FungibleToken.Withdraw) &FlowToken.Vault>,
50            priority: FlowTransactionScheduler.Priority,
51            executionEffort: UInt64,
52            intervalSeconds: UFix64,
53            maxExecutions: UInt64
54        ) {
55            self.schedulerManagerCap = schedulerManagerCap
56            self.feeProviderCap = feeProviderCap
57            self.priority = priority
58            self.executionEffort = executionEffort
59            self.intervalSeconds = intervalSeconds
60            self.maxExecutions = maxExecutions
61        }
62    }
63
64    // ============================================================
65    // TransactionData
66    // ============================================================
67
68    access(all) struct TransactionData {
69        access(all) let executionId: UInt64
70        access(all) let currentCount: UInt64
71        access(all) let loopConfig: LoopConfig
72
73        init(executionId: UInt64, currentCount: UInt64, loopConfig: LoopConfig) {
74            self.executionId = executionId
75            self.currentCount = currentCount
76            self.loopConfig = loopConfig
77        }
78    }
79
80    // ============================================================
81    // Handler Resource
82    // ============================================================
83
84    access(all) resource Handler: FlowTransactionScheduler.TransactionHandler {
85
86        access(FlowTransactionScheduler.Execute)
87        fun executeTransaction(id: UInt64, data: AnyStruct?) {
88            let txData = data as? TransactionData
89            if txData == nil {
90                log("DCATestHandler: Invalid transaction data")
91                return
92            }
93
94            let executionId = txData!.executionId
95            let currentCount = txData!.currentCount
96            let loopConfig = txData!.loopConfig
97            let newCount = currentCount + 1
98
99            log("DCATestHandler: Executing #".concat(newCount.toString()).concat(" of ").concat(loopConfig.maxExecutions.toString()))
100
101            emit TestExecuted(
102                executionId: executionId,
103                count: newCount,
104                timestamp: getCurrentBlock().timestamp
105            )
106
107            // Check if we should continue
108            if newCount >= loopConfig.maxExecutions {
109                emit TestCompleted(executionId: executionId, totalExecutions: newCount)
110                log("DCATestHandler: Completed all executions")
111                return
112            }
113
114            // Schedule next execution
115            let nextTime = getCurrentBlock().timestamp + loopConfig.intervalSeconds
116            let nextTxData = TransactionData(
117                executionId: executionId,
118                currentCount: newCount,
119                loopConfig: loopConfig
120            )
121
122            // Estimate fees
123            let estimate = FlowTransactionScheduler.estimate(
124                data: nextTxData,
125                timestamp: nextTime,
126                priority: loopConfig.priority,
127                executionEffort: loopConfig.executionEffort
128            )
129
130            let feeAmount = estimate.flowFee ?? 0.001
131            let feeWithBuffer = feeAmount * 1.1
132
133            // Borrow fee vault
134            let feeVault = loopConfig.feeProviderCap.borrow()
135            if feeVault == nil {
136                emit SchedulingFailed(executionId: executionId, reason: "Could not borrow fee vault")
137                return
138            }
139
140            if feeVault!.balance < feeWithBuffer {
141                emit SchedulingFailed(executionId: executionId, reason: "Insufficient fees: ".concat(feeWithBuffer.toString()))
142                return
143            }
144
145            let fees <- feeVault!.withdraw(amount: feeWithBuffer)
146
147            // Borrow scheduler manager
148            let schedulerManager = loopConfig.schedulerManagerCap.borrow()
149            if schedulerManager == nil {
150                feeVault!.deposit(from: <-fees)
151                emit SchedulingFailed(executionId: executionId, reason: "Could not borrow scheduler manager")
152                return
153            }
154
155            // Schedule next using Manager.scheduleByHandler()
156            let scheduledId = schedulerManager!.scheduleByHandler(
157                handlerTypeIdentifier: self.getType().identifier,
158                handlerUUID: self.uuid,
159                data: nextTxData,
160                timestamp: nextTime,
161                priority: loopConfig.priority,
162                executionEffort: loopConfig.executionEffort,
163                fees: <-fees as! @FlowToken.Vault
164            )
165
166            if scheduledId == 0 {
167                emit SchedulingFailed(executionId: executionId, reason: "scheduleByHandler returned 0")
168                return
169            }
170
171            emit NextScheduled(executionId: executionId, scheduledId: scheduledId, timestamp: nextTime)
172            log("DCATestHandler: Scheduled next execution #".concat((newCount + 1).toString()).concat(" at ").concat(nextTime.toString()))
173        }
174
175        init() {
176            emit HandlerCreated(uuid: self.uuid)
177        }
178    }
179
180    // ============================================================
181    // Factory Functions
182    // ============================================================
183
184    access(all) fun createHandler(): @Handler {
185        return <- create Handler()
186    }
187
188    access(all) fun createLoopConfig(
189        schedulerManagerCap: Capability<auth(FlowTransactionSchedulerUtils.Owner) &{FlowTransactionSchedulerUtils.Manager}>,
190        feeProviderCap: Capability<auth(FungibleToken.Withdraw) &FlowToken.Vault>,
191        priority: FlowTransactionScheduler.Priority,
192        executionEffort: UInt64,
193        intervalSeconds: UFix64,
194        maxExecutions: UInt64
195    ): LoopConfig {
196        return LoopConfig(
197            schedulerManagerCap: schedulerManagerCap,
198            feeProviderCap: feeProviderCap,
199            priority: priority,
200            executionEffort: executionEffort,
201            intervalSeconds: intervalSeconds,
202            maxExecutions: maxExecutions
203        )
204    }
205
206    access(all) fun createTransactionData(
207        executionId: UInt64,
208        currentCount: UInt64,
209        loopConfig: LoopConfig
210    ): TransactionData {
211        return TransactionData(
212            executionId: executionId,
213            currentCount: currentCount,
214            loopConfig: loopConfig
215        )
216    }
217
218    // ============================================================
219    // Init
220    // ============================================================
221
222    init() {
223        self.HandlerStoragePath = /storage/DCATestHandler
224        self.HandlerPublicPath = /public/DCATestHandler
225    }
226}
227