@@ -73,6 +73,17 @@ access(all) contract FlowYieldVaultsSchedulerV1 {
7373 timestamp : UFix64
7474 )
7575
76+ /// Emitted when Supervisor fails to self-reschedule.
77+ ///
78+ /// This is primarily used to surface insufficient fee vault balance, which would otherwise
79+ /// cause the Supervisor to stop monitoring without any on-chain signal.
80+ access (all ) event SupervisorRescheduleFailed (
81+ timestamp : UFix64 ,
82+ requiredFee : UFix64 ? ,
83+ availableBalance : UFix64 ? ,
84+ error : String
85+ )
86+
7687 /// Entitlement to schedule transactions
7788 access (all ) entitlement Schedule
7889
@@ -213,10 +224,9 @@ access(all) contract FlowYieldVaultsSchedulerV1 {
213224
214225 // Borrow the AutoBalancer and call scheduleNextRebalance() directly
215226 let autoBalancerRef = scheduleCap ! .borrow ()!
216- let scheduleError = autoBalancerRef .scheduleNextRebalance (whileExecuting : nil )
217227
218- if scheduleError ! = nil {
219- emit YieldVaultRecoveryFailed (yieldVaultID : yieldVaultID , error : scheduleError ! )
228+ if let scheduleError = autoBalancerRef . scheduleNextRebalance ( whileExecuting : nil ) {
229+ emit YieldVaultRecoveryFailed (yieldVaultID : yieldVaultID , error : scheduleError )
220230 // Leave in pending queue for retry on next Supervisor run
221231 continue
222232 }
@@ -262,9 +272,24 @@ access(all) contract FlowYieldVaultsSchedulerV1 {
262272 destroy txn
263273
264274 let nextTimestamp = getCurrentBlock ().timestamp + recurringInterval
265- let supervisorCap = FlowYieldVaultsSchedulerRegistry .getSupervisorCap ()
266275
267- if supervisorCap == nil || ! supervisorCap ! .check () {
276+ let supervisorCap = FlowYieldVaultsSchedulerRegistry .getSupervisorCap ()
277+ if supervisorCap == nil {
278+ emit SupervisorRescheduleFailed (
279+ timestamp : nextTimestamp ,
280+ requiredFee : nil ,
281+ availableBalance : nil ,
282+ error : " Missing Supervisor capability"
283+ )
284+ return
285+ }
286+ if ! supervisorCap ! .check () {
287+ emit SupervisorRescheduleFailed (
288+ timestamp : nextTimestamp ,
289+ requiredFee : nil ,
290+ availableBalance : nil ,
291+ error : " Invalid Supervisor capability"
292+ )
268293 return
269294 }
270295
@@ -276,34 +301,50 @@ access(all) contract FlowYieldVaultsSchedulerV1 {
276301 let baseFee = est .flowFee ?? FlowYieldVaultsSchedulerV1 .MIN_FEE_FALLBACK
277302 let required = baseFee * FlowYieldVaultsSchedulerV1 .FEE_MARGIN_MULTIPLIER
278303
279- if let vaultRef = self .feesCap .borrow () {
280- if vaultRef .balance > = required {
281- let fees <- vaultRef .withdraw (amount : required ) as ! @FlowToken.Vault
304+ let vaultRef = self .feesCap .borrow ()
305+ if vaultRef == nil {
306+ emit SupervisorRescheduleFailed (
307+ timestamp : nextTimestamp ,
308+ requiredFee : required ,
309+ availableBalance : nil ,
310+ error : " Could not borrow fee vault"
311+ )
312+ return
313+ }
314+ if vaultRef ! .balance < required {
315+ emit SupervisorRescheduleFailed (
316+ timestamp : nextTimestamp ,
317+ requiredFee : required ,
318+ availableBalance : vaultRef ! .balance ,
319+ error : " Insufficient fee vault balance"
320+ )
321+ return
322+ }
282323
283- let nextData : {String : AnyStruct } = {
284- " priority" : priority .rawValue ,
285- " executionEffort" : executionEffort ,
286- " recurringInterval" : recurringInterval ,
287- " scanForStuck" : scanForStuck
288- }
324+ let fees <- vaultRef ! .withdraw (amount : required ) as ! @FlowToken.Vault
289325
290- let selfTxn <- FlowTransactionScheduler .schedule (
291- handlerCap : supervisorCap ! ,
292- data : nextData ,
293- timestamp : nextTimestamp ,
294- priority : priority ,
295- executionEffort : executionEffort ,
296- fees : <- fees
297- )
298-
299- emit SupervisorRescheduled (
300- scheduledTransactionID : selfTxn .id ,
301- timestamp : nextTimestamp
302- )
303-
304- self ._scheduledTransaction <- ! selfTxn
305- }
326+ let nextData = {
327+ " priority" : priority .rawValue ,
328+ " executionEffort" : executionEffort ,
329+ " recurringInterval" : recurringInterval ,
330+ " scanForStuck" : scanForStuck
306331 }
332+
333+ let selfTxn <- FlowTransactionScheduler .schedule (
334+ handlerCap : supervisorCap ! ,
335+ data : nextData ,
336+ timestamp : nextTimestamp ,
337+ priority : priority ,
338+ executionEffort : executionEffort ,
339+ fees : <- fees
340+ )
341+
342+ emit SupervisorRescheduled (
343+ scheduledTransactionID : selfTxn .id ,
344+ timestamp : nextTimestamp
345+ )
346+
347+ self ._scheduledTransaction <- ! selfTxn
307348 }
308349
309350 /// Cancels the scheduled transaction if it is scheduled.
@@ -401,7 +442,7 @@ access(all) contract FlowYieldVaultsSchedulerV1 {
401442 access (account ) fun enqueuePendingYieldVault (yieldVaultID : UInt64 ) {
402443 assert (
403444 FlowYieldVaultsSchedulerRegistry .isRegistered (yieldVaultID : yieldVaultID ),
404- message : " enqueuePendingYieldVault: YieldVault #" . concat (yieldVaultID .toString ()). concat ( " is not registered" )
445+ message : " enqueuePendingYieldVault: YieldVault #\ (yieldVaultID .toString ()) is not registered"
405446 )
406447 FlowYieldVaultsSchedulerRegistry .enqueuePending (yieldVaultID : yieldVaultID )
407448 }
0 commit comments