Skip to content

useCoinbaseWithdrawal

Start a Coinbase withdrawal, confirm 2FA, and track status.

When to use

On the withdraw step, inside CoinbaseProvider. Always combine with useCoinbaseWithdrawalCooldown so Start cannot double-submit.

Example

tsx
import {
  useCoinbaseSelection,
  useCoinbaseWithdrawal,
  useCoinbaseWithdrawalCooldown,
} from '@swapped/connect-sdk/react'

function WithdrawButton() {
  const { canSubmit } = useCoinbaseSelection()
  const {
    startWithdrawal,
    confirmWithdrawal,
    cancelWithdrawal,
    status,
    requires2fa,
    isStartingWithdrawal,
    isConfirming,
    error,
  } = useCoinbaseWithdrawal()
  const { isInCooldown, remainingMs } = useCoinbaseWithdrawalCooldown()

  async function onWithdraw() {
    // Uses token / network / amount / funding from CoinbaseProvider.
    // If the amount is above the fee-reserve max, the amount input is synced
    // to the adjusted value before onAmountAdjusted runs.
    const result = await startWithdrawal(undefined, {
      onAmountAdjusted: async ({ formatted }) =>
        window.confirm(
          `Withdraw ${formatted.adjustedAmount} instead (fee reserve $${formatted.feeReserveFiat})?`,
        ),
    })

    if (result.status === 'idle') {
      // User declined the adjusted amount — stay on the form.
      return
    }

    if (result.status === 'requires2fa') {
      const code = window.prompt('Enter Coinbase 2FA code') // replace with your UI
      if (code) {
        const confirmResult = await confirmWithdrawal(code)
        if (confirmResult.status === 'requires2fa') {
          // Wrong code — status stays requires2fa; hook sets `is2faInvalid`.
          // Show feedback and call clear2faInvalid() when the user edits the code.
        }
      } else {
        cancelWithdrawal()
      }
    }
  }

  return (
    <div>
      {error && <p>{error.message}</p>}
      <p>Status: {status}</p>
      <button
        type="button"
        disabled={!canSubmit || isStartingWithdrawal || isConfirming || isInCooldown}
        onClick={() => void onWithdraw()}
      >
        {isInCooldown
          ? `Wait ${Math.ceil(remainingMs / 1000)}s`
          : 'Withdraw'}
      </button>
      {requires2fa && <p>Enter your 2FA code to continue</p>}
    </div>
  )
}

Override individual fields when needed:

tsx
await startWithdrawal({ amount: '10' })

Returns

FieldPurpose
statusidle | starting | requires2fa | confirming | completed | error
requires2faConvenience flag
startWithdrawal(overrides?, options?)Builds the request from context; optional partial overrides + onAmountAdjusted
confirmWithdrawal / cancelWithdrawal2FA actions
isStartingWithdrawal / isConfirmingIn-progress flags
result / errorOutcome / last hard error
is2faInvalid / clear2faInvalidWrong 2FA code flag (status stays requires2fa)

When the SDK auto-adjusts an above-spendable amount, the React hook syncs the amount input via setAmountExact before your onAmountAdjusted callback runs, so the form already shows the value the user is being asked to approve. Returning false from the callback cancels — status becomes idle and no cooldown is started. See core withdrawal for the full adjustment payload.

The 2FA code can come from SMS, email, or an authenticator app and is typically 6–7 characters. Do not hardcode a 6-digit-only input in your UI.

Passkeys are not supported → COINBASE_PASSKEY_NOT_SUPPORTED.

Success UI

When status === 'completed', result holds the full CoinbaseWithdrawal. Use it (or useCoinbaseCompletedTransactionSummary) for the success screen, then call restartSession() before another payment.

Typical step branching:

tsx
const { result, requires2fa, isConfirming, status } = useCoinbaseWithdrawal()

if (result) {
  // success — show amount, network fee, destination, id, …
} else if (requires2fa || isConfirming || status === 'confirming') {
  // 2FA form
} else {
  // withdraw form
}

Full walkthrough: Example.

Errors

CodeWhenWhat to do
REACT_COINBASE_PROVIDER_REQUIREDOutside providerWrap with CoinbaseProvider
COINBASE_SELECTION_INCOMPLETEMissing token / amount / account nameComplete selection first
SESSION_NOT_ACTIVESession cannot pay againrestartSession()
COINBASE_NOT_CONNECTEDNot connectedConnect
COINBASE_SESSION_EXPIREDToken invalidReconnect
COINBASE_COOLDOWN_ACTIVEStart during cooldownUse cooldown
COINBASE_INVALID_WITHDRAWAL_AMOUNTAmount below min, invalid, or above spendable with no viable capFix amount first
COINBASE_PASSKEY_NOT_SUPPORTEDPasskey-only confirmSMS / authenticator
COINBASE_NO_ACTIVE_WITHDRAWALConfirm with nothing pendingStart again

See Errors.