Repository navigation
Conversation
The keeper only quoted on Ethereum mainnet, so on the Sepolia sandbox no projection defer ran and below-floor or over-cap swaps reverted on chain and backed off instead of waiting with a partner-visible reason. Pin the QuoterV2 per chain id and keep the unquoted first-route fallback only for chains without one.
The marker starts the RECOVERY_DELAY clock. A failed attempt or a retry backoff skip sent nothing, so a deposit whose every attempt reverted kept batchOpenedAt at zero until the deadline job marked it recovering, and its refund landed a whole extra delay late.
A skip that waits on another execution leaves the poke to that execution's own sequence, so an implicit-nonce poke never races it. A backoff reached by the top-of-cycle pending check now falls through to the slot check, which pokes, instead of returning a cycle early.
applyFeePolicy is permissionless and the contract expects the keeper to finalize an announced increase once its 24 h timelock elapsed, but no keeper code called it, so increases stayed pending forever.
A timed-out apply left the increase looking pending, so every later cycle re-sent it, stacked keeper nonces and held the worker loop for another receipt timeout. The test also pins that the apply lands before the swap reads the fee policy.
Without it the fixture fell into the apply path and logged a spurious getBlock failure on every run.
A payment below MIN_SWAP_FLOOR never arms the forwarder's batch marker, so recover() cannot move it and the oldest-first planner held every later deposit of the account until the refund deadline. The planner now passes over it with a below_minimum waiting reason, and the stranded balance monitor flags a marked refund the contract cannot recover so the operator refunds it by hand.
A recovering deposit whose funds left the clone through an unrecorded path (permissionless swap/forwardAll) would have been flagged for a manual refund, paying the client twice. The alert now also names the memo that keeps a later keeper recover exactly-once.
The check compared only the stuck deposits' EURe with the clone balance, so EURe of a younger settling deposit or left by an earlier manual refund could stand in for funds that already left on the permissionless path, prompting a double refund.
A hand refund of a payment stuck below MIN_SWAP_FLOOR left its EURe on the clone, bypassed the one-refund-per-client gate, and left no recovery record. Topping the clone up to one wei above the floor lets poke arm the marker, so the keeper's normal recover and refund pipeline refund the payer's own EURe after RECOVERY_DELAY; the top-up stays on the clone below the floor and the odd wei keeps it unattributed. The alert now names the clone's balance in decimal EURe, the exact top-up (decimal and raw) and the token, and the funds-left warning no longer claims that all funds left a partly swapped deposit.
A recover that is mined but whose execution row is still Pending has already taken its EURe, yet the ledger still counted it, so a sibling stuck deposit got "reconcile, do not refund" instead of the operator alert. The keeper confirms the row on its next cycle; waiting one monitoring pass gives the right verdict.
A deposit marked recovering on a clone at or above the floor can stay ineligible for recover indefinitely when younger forwards keep re-timing the batch marker, and no monitor reported it. Warn once it has waited RECOVERY_DELAY plus an hour without a pending or confirmed recover, and error past TRIGGER_DELAY; the sub-floor case keeps its own operator alert.
On Sepolia the pool routinely drifts from the oracle, so DEPTH BELOW FLOOR fired as an error every pass with production triage text, which contradicts the rule that an error is an incident trigger. Off mainnet it now warns with a hint to re-centre the sandbox pool; mainnet is unchanged.
The ADR still listed sub-minimum remainders as refunded through the refund path, although below MIN_SWAP_FLOOR the keeper cannot recover them without the operator's top-up. Partners saw the EUR 1 minimum only under sandbox specifics; production now states it too, with when a smaller payment comes back.
The below_minimum clear is a bulk update, so the deposit instance planAction picks still held the old reason and wait start. When pricing deferred it in the same cycle, Sequelize dropped the unchanged waitingSince from the UPDATE and the row kept a NULL wait start, which partners saw as waiting since createdAt. Keeping the instances in step makes the deferral start a fresh wait.
The account-wide retry backoff counted failed rows of every kind, so a deposit whose swaps kept failing had its recover held back for up to an hour after it became eligible, missing the promised window the poke fix was meant to keep. Swaps and forwards still back off on failures of any kind.
The pendingFeePolicyEffectiveAt read sat in the cycle's critical state read, so one failing read aborted the whole cycle, recover and poke included. The apply is best-effort and the contract always settles under its applied policy, so its read now fails with it.
The unmined-apply guard compared public latest and pending nonces, but on mainnet keeper writes go through the private relay, whose pending transactions the public counts never show. A timed-out apply was then re-sent every cycle, stacking nonces behind it. The keeper now remembers each forwarder's last apply and sends nothing while it has no receipt and is younger than a relay's lifetime, and logs each skip reason once so a blocked increase leaves a trace. The public-nonce check stays for the public pool; it counts any keeper send, of any account, and the docs now say so.
SquidRouter routes are requested with 4% slippage, but the Base USDC arrival wait and the resume recovery required 99.8% of the estimate. A completed swap that filled 0.59% under its quote therefore never passed the wait, and every cron run resumed the same rebalance, waited 30 minutes and failed, blocking all further rebalancing. Derive the Squid arrival tolerance from the slippage constant so the two cannot drift apart again. The Avenia leg keeps the default tolerance.
…ival-tolerance Stop the rebalancer from wedging on SquidRouter fills below the quote
The monitor took MIN_SWAP_FLOOR, RECOVERY_DELAY and TRIGGER_DELAY from whichever account the unordered query listed first and applied them to every clone, so during a migration the REFUND NEEDS OPERATOR top-up and the REFUND OVERDUE bounds could come from another factory. A clone off the trusted factory, which the keeper refuses, also got a top-up it could never act on; it is now skipped with an error, as the executor and the config reconciliation do.
Monerium's sandbox has not approved any white-label profile in 12 days, which blocks the partner's end-to-end registration test. A pending profile can already link addresses and receive an IBAN, so the keeper's approval gate was the only blocker. Keyed on SANDBOX_ENABLED like the sandbox auto-activation; production keeps waiting for approved.
The security spec, the architecture doc and ADR-0007 said every partner registration waits for an approved profile. The sandbox now proceeds on pending, so the spec carries the scope, the approved KYB mirror and the refund caveat, and the architecture doc's auto-activation scope matches the code (sandbox only, not everything outside production).
The sandbox walkthrough said the provider approves each test profile before Vortex deploys anything. Partners testing registrations now see a pending profile proceed, and need to know that created, incomplete and in-review profiles still wait and that payouts can be refused until the provider approves the profile.
REFUND NEEDS OPERATOR prescribed a top-up even when another marked payment on the same clone was flagged for reconciling in the same pass. The top-up arms the marker for that payment too, whose recover cannot succeed, so the keeper could keep retrying it while the payment the alert promised to refund waited. The unswapped payments now get a warning that they wait for the reconcile, which does not claim their own funds left the clone.
The keeper sends no second recover while another refund of the client is open, but REFUND NEEDS OPERATOR still asked for the top-up. Sent then, it arms the marker at the floor (at least minSwapAmount), and if the open refund outlives TRIGGER_DELAY the permissionless swap and forwardAll can reach the payer's and Vortex's EURe before the keeper may recover. The alert now warns that the payment waits and returns with the amount once that refund is redeemed.
REFUND OVERDUE counted a deposit whose recover attempts had failed as one no recover had been sent for, and only hinted at marker re-timing or another stuck refund. A recover that keeps reverting may mean its funds left by a path the ledger does not record, where a hand refund pays twice, so the alert now says no recover has succeeded, names the latest error and asks to reconcile the ledger instead.
With MONERIUM_B2B_AUTO_RECOVERY=off the deadline pass neither marks nor reports anything, and a clone below MIN_SWAP_FLOOR arms no marker, so the stranded check never fires and REFUND NEEDS OPERATOR needs a marked deposit: a payment below the floor reached nobody. The stranded monitor now warns for such a payment past its window and says to mark it, then follow REFUND NEEDS OPERATOR.
Every other alert and doc names "the internal B2B runbook §x"; the DEPTH BELOW FLOOR line alone said "the B2B operations runbook", so a search for the runbook reference missed it.
Monitoring invariant 2 first called DEPTH BELOW FLOOR an error-level line outright and only qualified it at the end; the code and the architecture doc log it as a warning off mainnet, so the invariant now says so where it introduces the line.
The keeper remembers the last fee apply and the logged skip reason per forwarder in module memory, and every fee-apply test used the same forwarder, so an earlier test's apply or skip carried into the next: a second test of the same skip saw it already logged and failed. A fresh forwarder per test keeps them independent without a test-only export.
Every sub-floor refund now leaves an unattr: top-up row on the clone, and only the settlingDeposits filter keeps the keeper from flagging, swapping and forwarding that Vortex EURe; no executor test covered it. The tests pin the filter, the poke once the top-up lifts the clone to the floor, and the recover of exactly the payer's EURe.
Keeper invariant 5 said only provider-attributed deposits make an account a conversion candidate while its own parenthetical named the unattr: cycle and pending executions, and invariant 7 and the fee section still said the executor only runs for accounts with a settling deposit. Both predate the candidate change; they now name all three.
The off-mode report sat between REFUND OVERDUE and the sub-floor reconcile rule, so "a swapped deposit... only warns" read as if it described the off-mode warning.
The two wait lines said "this alert then returns with the top-up amount", but they are not the alert that returns; naming REFUND NEEDS OPERATOR tells the operator which line carries the amount to send.
In off mode a marked sibling (e.g. one whose recover is confirmed and waits for the manual redeem) silenced the report for every other payment past its window on the clone, and nothing else reports them. The report names only minted or converting rows and the other sub-floor lines only marked ones, so they never name the same deposit.
The operator had to find the refund that holds the top-up back by hand, and in alert and off mode the hold lifts only once that deposit is closed after its manual redeem, not when the redeem went out.
Dropping the blockNumber filter passed every test, although the admin recover route refuses an unindexed deposit, so the report would ask to mark one that cannot be marked.
Fix four B2B keeper gaps found in the live sandbox tests
…box-approve-pending # Conflicts: # docs/api/pages/14-managed-profiles.md
…prove-pending Treat pending Monerium profiles as approved on the sandbox
…te-1bp Use the 1 bp EURe/EURC pool for the mainnet initial route
✅ Deploy Preview for vortexfi ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for vortex-sandbox ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for vrtx-dashboard canceled.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.