feat(cashout): route Cashout V1 wallets via cutover guard (ENG-357)#395
Open
islandbitcoin wants to merge 1 commit into
Open
feat(cashout): route Cashout V1 wallets via cutover guard (ENG-357)#395islandbitcoin wants to merge 1 commit into
islandbitcoin wants to merge 1 commit into
Conversation
Cashout V1 always debited the legacy USD wallet and credited the bank-owner USD wallet. Post-cutover the user's funds live in an ETH-USDT cash wallet, so the offer must debit USDT and credit the bank-owner's USDT wallet. The Flash bank-owner account holds both a USD and a USDT wallet, so the route simply selects the matching pair on both sides — no cross-currency swap. - Add resolveCashoutWalletSelection: reads the cutover config + per-account migration and runs evaluateCashWalletCutoverGuard to pick the route. Source and destination wallets are resolved server-side from the guard, NOT from the client-supplied walletId (trusted only for wallet-level auth). The guard blocks the cashout while a migration is in-flight or has failed. - CashoutManager.createOffer builds a USD or USDT invoice per route; the USD/JMD payout math is unchanged (1 USDT = 1 USD). - executeCashout authorizes by account, since an old client may still present the zeroed legacy USD walletId while the offer settles in USDT, instead of an exact wallet-id match. - CashoutValidator and CashoutDetails.payment.amount are currency-aware; the USD path stays byte-identical. ErpNext.draftCashout records the USDT amount. Adds unit coverage for the routing decision tree. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
What
Cashout V1 always debited the user's legacy USD wallet and credited the Flash bank-owner's USD wallet. Post-cutover the user's funds live in an ETH-USDT cash wallet, so the offer must debit USDT and credit the bank-owner's USDT wallet. The bank-owner account holds both a USD and a USDT wallet, so the route selects the matching pair on each side — no cross-currency swap.
How
resolveCashoutWalletSelection(new,src/app/cash-wallet-cutover/cashout-routing.ts) reads the cutover config + the per-account migration record and runs the existingevaluateCashWalletCutoverGuardto pick the route:legacy_usd→ user's USD wallet + bank-owner USD wallet (unchanged).usdt→ the account's USDT wallet + the bank-owner's USDT wallet.walletId(trusted only for wallet-level auth) — this protects old clients that still send the zeroed legacy USD walletId after migration.CashoutManager.createOfferbuilds a USD or USDT invoice per route; the USD/JMD payout math is unchanged (1 USDT = 1 USD).executeCashoutnow authorizes by account (provided walletId and the offer's settlement wallet must share an account) instead of an exact wallet-id match, so an old client presenting the legacy USD walletId can still execute a USDT-settled offer.CashoutValidatorandCashoutDetails.payment.amountare currency-aware (USDAmount | USDTAmount); the USD branches stay byte-identical.ErpNext.draftCashoutrecords the USDT amount (asNumber, sinceUSDTAmounthas noasDollars);wallet_id/flash_walletalready capture the USDT source/destination wallets for audit.Acceptance criteria
Builds on
tmp/bridge-rebase-pr-ready(the cutover machinery lives there). blockedBy ENG-345 (Done).Testing
tsc --noEmit: no new type errors (my files type-clean).cashout-routing.spec.tscovering pre→legacy, complete→usdt, mid-migration→legacy, failed→blocked, and missing-USDT-wallet→error. (Note: the unit suite does not complete on my local host — a ts-jest cold-compile hang that also affects existing specs — so the spec is type-checked here and will run in CI.)🤖 Generated with Claude Code