From 14fcda4060294009f83a10b4ff35f28e3a64c1c8 Mon Sep 17 00:00:00 2001 From: liobrasil Date: Wed, 11 Mar 2026 19:37:57 -0400 Subject: [PATCH 1/4] fix(worker-ops): recover failed workers before scheduler gating --- .../contracts/FlowYieldVaultsEVMWorkerOps.cdc | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/cadence/contracts/FlowYieldVaultsEVMWorkerOps.cdc b/cadence/contracts/FlowYieldVaultsEVMWorkerOps.cdc index 68334c9..5c3c977 100644 --- a/cadence/contracts/FlowYieldVaultsEVMWorkerOps.cdc +++ b/cadence/contracts/FlowYieldVaultsEVMWorkerOps.cdc @@ -477,6 +477,10 @@ access(all) contract FlowYieldVaultsEVMWorkerOps { let manager = FlowYieldVaultsEVMWorkerOps._getManagerFromStorage()! let worker = self.workerCap.borrow()! + // Always clear failed/stale worker entries before capacity and backlog checks. + // Otherwise a reverted in-flight worker can remain stuck until new pending work arrives. + self._checkForFailedWorkerRequests(manager: manager, worker: worker) + var message = "" var nextRunCapacity: UInt8 = 0 var pendingCount: Int? = nil @@ -556,12 +560,10 @@ access(all) contract FlowYieldVaultsEVMWorkerOps { /// @notice Main scheduler logic /// @dev Flow: - /// 1. Check for failed worker requests - /// - If a failure is identified, mark the request as failed and remove it from scheduledRequests - /// 2. If fetchCount > 0, fetch pending requests from EVM - /// 3. Preprocess requests to drop invalid requests - /// 4. Start processing requests (PENDING -> PROCESSING) - /// 5. Schedule WorkerHandlers and assign request ids to them + /// 1. If fetchCount > 0, fetch pending requests from EVM + /// 2. Preprocess requests to drop invalid requests + /// 3. Start processing requests (PENDING -> PROCESSING) + /// 4. Schedule WorkerHandlers and assign request ids to them /// @param manager The scheduler manager /// @param worker The worker resource /// @param fetchCount Number of pending requests to fetch in this run @@ -571,9 +573,6 @@ access(all) contract FlowYieldVaultsEVMWorkerOps { worker: &FlowYieldVaultsEVM.Worker, fetchCount: Int, ): String? { - // Check for failed worker requests - self._checkForFailedWorkerRequests(manager: manager, worker: worker) - // Fetch pending requests from EVM if fetchCount > 0 { if let pendingRequests = worker.getPendingRequestsFromEVM( From 4ca30be8017b3c800a33bedb60d2f31757cdae8b Mon Sep 17 00:00:00 2001 From: liobrasil Date: Thu, 12 Mar 2026 16:59:25 -0400 Subject: [PATCH 2/4] docs(worker-ops): clarify failed worker recovery comment --- cadence/contracts/FlowYieldVaultsEVMWorkerOps.cdc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cadence/contracts/FlowYieldVaultsEVMWorkerOps.cdc b/cadence/contracts/FlowYieldVaultsEVMWorkerOps.cdc index 5c3c977..d14b2b8 100644 --- a/cadence/contracts/FlowYieldVaultsEVMWorkerOps.cdc +++ b/cadence/contracts/FlowYieldVaultsEVMWorkerOps.cdc @@ -477,8 +477,7 @@ access(all) contract FlowYieldVaultsEVMWorkerOps { let manager = FlowYieldVaultsEVMWorkerOps._getManagerFromStorage()! let worker = self.workerCap.borrow()! - // Always clear failed/stale worker entries before capacity and backlog checks. - // Otherwise a reverted in-flight worker can remain stuck until new pending work arrives. + // Check and recover panicked worker entries self._checkForFailedWorkerRequests(manager: manager, worker: worker) var message = "" From cf92817950aaac07e22a1ddaf8a7fddaa71fad9e Mon Sep 17 00:00:00 2001 From: liobrasil Date: Thu, 12 Mar 2026 17:58:58 -0400 Subject: [PATCH 3/4] Adjust worker recovery effort budgeting --- FLOW_YIELD_VAULTS_EVM_BRIDGE_DESIGN.md | 12 ++++----- .../contracts/FlowYieldVaultsEVMWorkerOps.cdc | 25 +++++++++++------ .../scheduler/init_and_schedule.cdc | 27 +++++++++++++++++-- 3 files changed, 48 insertions(+), 16 deletions(-) diff --git a/FLOW_YIELD_VAULTS_EVM_BRIDGE_DESIGN.md b/FLOW_YIELD_VAULTS_EVM_BRIDGE_DESIGN.md index 74bf117..da30cee 100644 --- a/FLOW_YIELD_VAULTS_EVM_BRIDGE_DESIGN.md +++ b/FLOW_YIELD_VAULTS_EVM_BRIDGE_DESIGN.md @@ -504,12 +504,12 @@ Execution effort values are configurable via the `executionEffortConstants` dict | Key | Default | Description | |-----|---------|-------------| -| `schedulerBaseEffort` | 700 | Base effort for SchedulerHandler execution | -| `schedulerPerRequestEffort` | 1000 | Additional effort per request preprocessed | -| `workerCreateYieldVaultRequestEffort` | 5000 | Effort for CREATE_YIELDVAULT requests | -| `workerDepositRequestEffort` | 2000 | Effort for DEPOSIT_TO_YIELDVAULT requests | -| `workerWithdrawRequestEffort` | 2000 | Effort for WITHDRAW_FROM_YIELDVAULT requests | -| `workerCloseYieldVaultRequestEffort` | 5000 | Effort for CLOSE_YIELDVAULT requests | +| `schedulerBaseEffort` | 950 | Base effort for SchedulerHandler execution | +| `schedulerPerRequestEffort` | 250 | Additional effort per request preprocessed | +| `workerCreateYieldVaultRequestEffort` | 6500 | Effort for CREATE_YIELDVAULT requests | +| `workerDepositRequestEffort` | 1500 | Effort for DEPOSIT_TO_YIELDVAULT requests | +| `workerWithdrawRequestEffort` | 3000 | Effort for WITHDRAW_FROM_YIELDVAULT requests | +| `workerCloseYieldVaultRequestEffort` | 4500 | Effort for CLOSE_YIELDVAULT requests | Priority is dynamically determined based on execution effort: - **Low**: effort ≤ 2500 diff --git a/cadence/contracts/FlowYieldVaultsEVMWorkerOps.cdc b/cadence/contracts/FlowYieldVaultsEVMWorkerOps.cdc index d14b2b8..73df005 100644 --- a/cadence/contracts/FlowYieldVaultsEVMWorkerOps.cdc +++ b/cadence/contracts/FlowYieldVaultsEVMWorkerOps.cdc @@ -477,7 +477,8 @@ access(all) contract FlowYieldVaultsEVMWorkerOps { let manager = FlowYieldVaultsEVMWorkerOps._getManagerFromStorage()! let worker = self.workerCap.borrow()! - // Check and recover panicked worker entries + // Always clear failed/stale worker entries before capacity and backlog checks. + // This keeps failed requests recoverable even when no new EVM requests are pending. self._checkForFailedWorkerRequests(manager: manager, worker: worker) var message = "" @@ -747,7 +748,15 @@ access(all) contract FlowYieldVaultsEVMWorkerOps { let perRequestEffort = FlowYieldVaultsEVMWorkerOps.executionEffortConstants[ FlowYieldVaultsEVMWorkerOps.SCHEDULER_PER_REQUEST_EFFORT ]! - let executionEffort = baseEffort + UInt64(forNumberOfRequests) * perRequestEffort + // Budget for the larger of: + // - requests expected to be processed next run + // - tracked worker entries that may need recovery before any new work is scheduled + let trackedRecoveryWorkload = UInt64(FlowYieldVaultsEVMWorkerOps.scheduledRequests.length) + let requestedProcessingWorkload = UInt64(forNumberOfRequests) + let schedulerWorkload = trackedRecoveryWorkload > requestedProcessingWorkload + ? trackedRecoveryWorkload + : requestedProcessingWorkload + let executionEffort = baseEffort + schedulerWorkload * perRequestEffort let transactionId = self._scheduleTransaction( manager: manager, @@ -939,12 +948,12 @@ access(all) contract FlowYieldVaultsEVMWorkerOps { self.WORKER_CLOSE_YIELDVAULT_REQUEST_EFFORT = "workerCloseYieldVaultRequestEffort" self.executionEffortConstants = { - self.SCHEDULER_BASE_EFFORT: 700, - self.SCHEDULER_PER_REQUEST_EFFORT: 1000, - self.WORKER_CREATE_YIELDVAULT_REQUEST_EFFORT: 5000, - self.WORKER_WITHDRAW_REQUEST_EFFORT: 2000, - self.WORKER_DEPOSIT_REQUEST_EFFORT: 2000, - self.WORKER_CLOSE_YIELDVAULT_REQUEST_EFFORT: 5000 + self.SCHEDULER_BASE_EFFORT: 950, + self.SCHEDULER_PER_REQUEST_EFFORT: 250, + self.WORKER_CREATE_YIELDVAULT_REQUEST_EFFORT: 6500, + self.WORKER_WITHDRAW_REQUEST_EFFORT: 3000, + self.WORKER_DEPOSIT_REQUEST_EFFORT: 1500, + self.WORKER_CLOSE_YIELDVAULT_REQUEST_EFFORT: 4500 } self.scheduledRequests = {} diff --git a/cadence/transactions/scheduler/init_and_schedule.cdc b/cadence/transactions/scheduler/init_and_schedule.cdc index 1a044c9..5233299 100644 --- a/cadence/transactions/scheduler/init_and_schedule.cdc +++ b/cadence/transactions/scheduler/init_and_schedule.cdc @@ -97,12 +97,33 @@ transaction { } execute { + let workerCreateEffort = FlowYieldVaultsEVMWorkerOps.executionEffortConstants[ + FlowYieldVaultsEVMWorkerOps.WORKER_CREATE_YIELDVAULT_REQUEST_EFFORT + ]! + let workerWithdrawEffort = FlowYieldVaultsEVMWorkerOps.executionEffortConstants[ + FlowYieldVaultsEVMWorkerOps.WORKER_WITHDRAW_REQUEST_EFFORT + ]! + let workerDepositEffort = FlowYieldVaultsEVMWorkerOps.executionEffortConstants[ + FlowYieldVaultsEVMWorkerOps.WORKER_DEPOSIT_REQUEST_EFFORT + ]! + let workerCloseEffort = FlowYieldVaultsEVMWorkerOps.executionEffortConstants[ + FlowYieldVaultsEVMWorkerOps.WORKER_CLOSE_YIELDVAULT_REQUEST_EFFORT + ]! // Make sure WorkerHandler is registered in the manager if self.manager.getHandlerTypeIdentifiers()[self.workerHandlerTypeIdentifier] == nil { // Schedule dummy (data=nil) WorkerHandler transaction to register the WorkerHandler in the manager let workerHandlerPriority = FlowTransactionScheduler.Priority.Medium - let workerHandlerExecutionEffort = 5000 as UInt64 + var workerHandlerExecutionEffort = workerCreateEffort + if workerWithdrawEffort > workerHandlerExecutionEffort { + workerHandlerExecutionEffort = workerWithdrawEffort + } + if workerDepositEffort > workerHandlerExecutionEffort { + workerHandlerExecutionEffort = workerDepositEffort + } + if workerCloseEffort > workerHandlerExecutionEffort { + workerHandlerExecutionEffort = workerCloseEffort + } let transactionId = _scheduleTransaction( manager: self.manager, handlerCap: self.workerHandlerCap, @@ -130,7 +151,9 @@ transaction { // Schedule scheduler if !schedulerRunning { let schedulerPriority = FlowTransactionScheduler.Priority.Medium - let schedulerExecutionEffort = 700 as UInt64 + let schedulerExecutionEffort = FlowYieldVaultsEVMWorkerOps.executionEffortConstants[ + FlowYieldVaultsEVMWorkerOps.SCHEDULER_BASE_EFFORT + ]! // First scheduler run will be scheduled without any requests to preprocess // If there are pending requests, they will be preprocessed in the next scheduler execution let schedulerTransactionId = _scheduleTransaction( From 2666212bb4e02976c21b9902126e9755757212fa Mon Sep 17 00:00:00 2001 From: liobrasil Date: Thu, 12 Mar 2026 19:07:22 -0400 Subject: [PATCH 4/4] Increase close worker effort to 5000 --- FLOW_YIELD_VAULTS_EVM_BRIDGE_DESIGN.md | 2 +- cadence/contracts/FlowYieldVaultsEVMWorkerOps.cdc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/FLOW_YIELD_VAULTS_EVM_BRIDGE_DESIGN.md b/FLOW_YIELD_VAULTS_EVM_BRIDGE_DESIGN.md index da30cee..a31e57d 100644 --- a/FLOW_YIELD_VAULTS_EVM_BRIDGE_DESIGN.md +++ b/FLOW_YIELD_VAULTS_EVM_BRIDGE_DESIGN.md @@ -509,7 +509,7 @@ Execution effort values are configurable via the `executionEffortConstants` dict | `workerCreateYieldVaultRequestEffort` | 6500 | Effort for CREATE_YIELDVAULT requests | | `workerDepositRequestEffort` | 1500 | Effort for DEPOSIT_TO_YIELDVAULT requests | | `workerWithdrawRequestEffort` | 3000 | Effort for WITHDRAW_FROM_YIELDVAULT requests | -| `workerCloseYieldVaultRequestEffort` | 4500 | Effort for CLOSE_YIELDVAULT requests | +| `workerCloseYieldVaultRequestEffort` | 5000 | Effort for CLOSE_YIELDVAULT requests | Priority is dynamically determined based on execution effort: - **Low**: effort ≤ 2500 diff --git a/cadence/contracts/FlowYieldVaultsEVMWorkerOps.cdc b/cadence/contracts/FlowYieldVaultsEVMWorkerOps.cdc index 73df005..5ae4858 100644 --- a/cadence/contracts/FlowYieldVaultsEVMWorkerOps.cdc +++ b/cadence/contracts/FlowYieldVaultsEVMWorkerOps.cdc @@ -953,7 +953,7 @@ access(all) contract FlowYieldVaultsEVMWorkerOps { self.WORKER_CREATE_YIELDVAULT_REQUEST_EFFORT: 6500, self.WORKER_WITHDRAW_REQUEST_EFFORT: 3000, self.WORKER_DEPOSIT_REQUEST_EFFORT: 1500, - self.WORKER_CLOSE_YIELDVAULT_REQUEST_EFFORT: 4500 + self.WORKER_CLOSE_YIELDVAULT_REQUEST_EFFORT: 5000 } self.scheduledRequests = {}