Overview
In the Bluvo state machine, errors are states, not exceptions. When something goes wrong, the flow transitions to an error state (e.g.,withdraw:error2FA), and the hook exposes it as a boolean (e.g., flow.requires2FA). Your UI reacts to these booleans the same way it reacts to any other state.
Every error state has a defined recovery action, submit a code, retry, adjust parameters, or cancel. The hook also provides detection helpers like hasAmountError and hasAddressError that inspect error messages to help you show targeted UI.
Error Quick Reference
Connection Errors
| Hook Boolean | State | Severity | Recovery Action |
|---|---|---|---|
isOAuthError | oauth:error or oauth:fatal | Varies | Retry startWithdrawalFlow() (if not fatal) or cancel() |
isOAuthFatal | oauth:fatal | Fatal | cancel(), exchange rejected the connection |
isOAuthWindowBeenClosedByTheUser | oauth:window_closed_by_user | User action | Retry startWithdrawalFlow() or cancel() |
isWalletConnectionInvalid | oauth:fatal or qrcode:fatal | Fatal | cancel() and start over |
isQRCodeError | qrcode:error or qrcode:fatal | Varies | refreshQRCode() (if not fatal) or cancel() |
isQRCodeTimeout | qrcode:timeout | Recoverable | refreshQRCode() |
Wallet Errors
| Hook Boolean | State | Severity | Recovery Action |
|---|---|---|---|
isWalletError | wallet:error | Error | cancel() and retry flow |
hasWalletNotFoundError | wallet:error + “not found” | Fatal | Wallet deleted, create new wallet ID |
hasInvalidCredentialsError | wallet:error + “invalid credential” | Fatal | Tokens revoked, user must re-authenticate |
Quote Errors
| Hook Boolean | State | Severity | Recovery Action |
|---|---|---|---|
isQuoteError | quote:error | Error | Retry requestQuote() with corrected params |
isQuoteExpired | quote:expired | Expired | Call requestQuote() again |
hasAmountError | quote:error or withdraw:fatal | Validation | Show min/max from quote.additionalInfo, let user adjust |
hasAddressError | quote:error or withdraw:fatal | Validation | Prompt user to check destination address |
hasNetworkError | quote:error or withdraw:fatal | Validation | Show supported networks, let user re-select |
Withdrawal Errors
| Hook Boolean | State | Severity | Recovery Action |
|---|---|---|---|
requires2FA | withdraw:error2FA | Requires input | submit2FA(code) |
requires2FAMultiStep | withdraw:error2FAMultiStep | Requires input | submit2FAMultiStep(stepType, code) / pollFaceVerification() |
requiresSMS | withdraw:errorSMS | Requires input | submit2FA(code) (SMS code) |
requiresKYC | withdraw:errorKYC | Blocking | Show “Complete KYC on exchange” message |
hasInsufficientBalance | withdraw:errorBalance | Blocking | Show balance, let user adjust amount |
canRetry | withdraw:retrying | Auto-retry | Show spinner, retry is automatic |
isWithdrawBlocked | withdraw:blocked | Fatal | Show reason from flow.error?.message |
hasFatalError | withdraw:fatal | Fatal | Check requiresValid2FAMethod; show flow.error?.message |
requiresValid2FAMethod | withdraw:fatal + valid methods | Fatal | Show supported 2FA methods from flow.valid2FAMethods |
Exchange-Specific 2FA
| Auth Method | 2FA Type | Hook Booleans That Fire | Notes |
|---|---|---|---|
| OAuth | None | Neither requires2FA nor requires2FAMultiStep | No 2FA for withdrawals. Hide all 2FA UI. |
| OAuth | Single-step | requires2FA only | One TOTP code → submit2FA(code). requires2FAMultiStep never fires. |
| QR Code | Multi-step MFA | requires2FAMultiStep only | Step-by-step verification. requires2FA never fires. See deep dive below. |
Handling All Three in Your UI
Multi-Step 2FA Deep Dive
Whenrequires2FAMultiStep is true, the exchange requires multiple verification steps before confirming the withdrawal. This is currently used by Binance.
Step Types
| Step Type | Description | Verification Method |
|---|---|---|
GOOGLE | Google Authenticator TOTP | submit2FAMultiStep('GOOGLE', code) |
EMAIL | Email verification code | submit2FAMultiStep('EMAIL', code) |
SMS | SMS verification code | submit2FAMultiStep('SMS', code) |
FACE | Biometric face verification via QR | pollFaceVerification(), user scans QR on mobile |
ROAMING_FIDO | Passkey / security key | pollRoamingFidoVerification(), user verifies on device |
Relation Types
ThemultiStep2FARelation field determines how steps combine:
AND, All required steps must be verified. Show all step inputs simultaneously.OR, Any one required step is sufficient. Show options and let user pick.
Verification Source of Truth
The hook provides convenience booleans that checkmfa.verified first, falling back to step.status:
Complete Flow
- Withdrawal execution triggers
requires2FAMultiStep - Check
flow.multiStep2FAStepsfor required step types - For each step, render the appropriate input:
GOOGLE/EMAIL/SMS, code input →submit2FAMultiStep(type, code)FACE, show QR code fromflow.faceQrCodeUrl→ user scans →pollFaceVerification()ROAMING_FIDO, prompt user →pollRoamingFidoVerification()
- After each submission, the server returns updated step statuses
- When
flow.allMultiStep2FAStepsVerifiedbecomestrue,isReadyToConfirmfires - Call
flow.confirmWithdrawal()to finalize
FACE Verification
The FACE step requires the user to scan a QR code with their exchange mobile app and complete biometric verification:Code Example
Recovery Actions by Error State
withdraw:error2FA, Single-Step 2FA Required
withdraw:error2FA, Single-Step 2FA Required
The exchange (typically Coinbase) requires a TOTP code.Recovery: Call
flow.submit2FA(code) with the user’s authenticator code.Track: flow.invalid2FAAttempts increments on each invalid submission. Consider showing a warning after 2-3 attempts.withdraw:error2FAMultiStep, Multi-Step MFA Required
withdraw:error2FAMultiStep, Multi-Step MFA Required
The exchange requires multiple verification steps.Recovery: Use
flow.submit2FAMultiStep(stepType, code) for code-based steps and flow.pollFaceVerification() for biometric steps. Once flow.allMultiStep2FAStepsVerified is true, call flow.confirmWithdrawal().See the Multi-Step 2FA Deep Dive above for the complete implementation.withdraw:errorSMS, SMS Verification Required
withdraw:errorSMS, SMS Verification Required
The exchange sent an SMS code to the user’s registered phone.Recovery: Call
flow.submit2FA(code) with the SMS code the user received.withdraw:errorKYC, KYC Completion Required
withdraw:errorKYC, KYC Completion Required
The exchange requires the user to complete identity verification before withdrawals are allowed.Recovery: This cannot be resolved via the SDK. Direct the user to complete KYC on the exchange’s website or app, then retry the withdrawal later.
withdraw:errorBalance, Insufficient Balance
withdraw:errorBalance, Insufficient Balance
The withdrawal amount exceeds the available balance.Recovery: Show the user their current balance (from
flow.walletBalances), let them adjust the amount, and request a new quote.withdraw:blocked, Withdrawal Blocked
withdraw:blocked, Withdrawal Blocked
The exchange has blocked this withdrawal. This is non-recoverable via the SDK.Recovery: Display
flow.error?.message which contains the reason from the exchange. The user may need to resolve account-level restrictions.withdraw:fatal, Fatal Withdrawal Error
withdraw:fatal, Fatal Withdrawal Error
An unrecoverable error occurred. Check
requiresValid2FAMethod for a special case where the user’s 2FA method isn’t supported.Recovery:- If
flow.requiresValid2FAMethodistrue: showflow.valid2FAMethodsand instruct the user to enable a supported method on the exchange. - Otherwise: display
flow.error?.messageand offerflow.cancel().
withdraw:retrying, Auto-Retry in Progress
withdraw:retrying, Auto-Retry in Progress
The SDK is automatically retrying the withdrawal after a transient failure.Recovery: No action needed. Show a spinner. Track progress with
flow.retryAttempts / flow.maxRetryAttempts.quote:expired, Quote Expired
quote:expired, Quote Expired
The quote’s TTL has elapsed and it can no longer be used for withdrawal.Recovery: Call
flow.requestQuote() with the same parameters to get a fresh quote.oauth:error, Recoverable OAuth Error
oauth:error, Recoverable OAuth Error
A transient failure occurred during the OAuth flow (network timeout, temporary exchange issue).Recovery: Call
startWithdrawalFlow() again with the same exchange and wallet ID.oauth:fatal / qrcode:fatal, Fatal Connection Error
oauth:fatal / qrcode:fatal, Fatal Connection Error
The exchange permanently rejected the connection. This cannot be retried.Recovery: Call
flow.cancel() and let the user start over, potentially with a different exchange.Next Steps
State Machine Reference
Full list of 35 states, transitions, hook booleans, and context data
OAuth2 Integration
Step-by-step implementation guide with React and Next.js
Code Samples
Full working examples for Next.js, React, and more
Encryption & Security
How Bluvo encrypts and isolates exchange credentials