Skip to main content

createKrakenExchangeTransfer

Creates a transfer from a Kraken exchange account to an external wallet address. This allows users to withdraw cryptocurrency from their Kraken holdings to a wallet they control. Before calling this function, use getKrakenAccounts to get the accountId and verify available balance, and getKrakenWhitelistedAddresses to check if the destination address is valid.

Usage

import {
  createKrakenExchangeTransfer,
  getKrakenAccounts,
} from '@dynamic-labs-sdk/client';

// Get the user's Kraken accounts
const accounts = await getKrakenAccounts();

// Create a transfer from the first account
const transfer = await createKrakenExchangeTransfer({
  accountId: accounts[0].id,
  to: '0x742d35Cc6634C0532925a3b844Bc9e7595f7ABCD',
  amount: 0.5,
  currency: 'ETH',
});

console.log('Transfer ID:', transfer.id);
console.log('Status:', transfer.status);

Parameters

ParameterTypeDescription
accountIdstringThe Kraken account ID to transfer from. Get this from getKrakenAccounts().
tostringThe destination wallet address. Must be whitelisted if the user has address whitelisting enabled.
amountnumberThe amount to transfer.
currencystringThe cryptocurrency to transfer (e.g., 'ETH', 'BTC', 'USDC').
descriptionstring (optional)A description for the transfer.
networkstring (optional)Network name (e.g., 'ethereum', 'polygon').
networkObjectobject (optional)Network details with chainName and networkId. Use this for multi-network tokens.
networkObject.chainNamestringThe chain name (e.g., 'EVM').
networkObject.networkIdstringThe network ID (e.g., '1' for Ethereum mainnet, '137' for Polygon).
mfaCodestring (optional)MFA code if required by the user’s Kraken account settings.
idstring (optional)Idempotency key to prevent duplicate transfers on retry.
clientDynamicClient (optional)The Dynamic client instance. Only required when using multiple clients.

Returns

Promise<ExchangeTransferResponse> - A promise that resolves to the transfer details:
type ExchangeTransferResponse = {
  id: string;                  // Unique transfer ID
  exchangeAccountId?: string;  // The Kraken account ID
  status?: string;             // Transfer status: 'pending', 'completed', 'failed', etc.
  amount: number;              // Amount transferred
  currency: string;            // Currency code
  createdAt?: Date;            // When the transfer was created
};

Transfer Status Values

StatusDescription
pendingTransfer has been submitted and is being processed
completedTransfer has been successfully completed
failedTransfer failed (check error details)

Examples

Basic transfer

import {
  createKrakenExchangeTransfer,
  getKrakenAccounts,
} from '@dynamic-labs-sdk/client';

const transferEth = async (destinationAddress, amount) => {
  // Get accounts and find one with ETH balance
  const accounts = await getKrakenAccounts();

  const accountWithEth = accounts.find(acc =>
    acc.balances.some(
      b => b.currency === 'ETH' && parseFloat(b.balance) >= amount
    )
  );

  if (!accountWithEth) {
    throw new Error('No account with sufficient ETH balance');
  }

  const transfer = await createKrakenExchangeTransfer({
    accountId: accountWithEth.id,
    to: destinationAddress,
    amount,
    currency: 'ETH',
    description: 'Withdraw ETH to wallet',
  });

  return transfer;
};

// Usage
const transfer = await transferEth('0x742d35Cc...', 0.1);
console.log('Transfer created:', transfer.id);

Transfer USDC on a specific network

When transferring tokens that exist on multiple networks, specify the target network:
import { createKrakenExchangeTransfer } from '@dynamic-labs-sdk/client';

// Transfer USDC to Polygon
const transfer = await createKrakenExchangeTransfer({
  accountId: 'acc_123',
  to: '0x742d35Cc6634C0532925a3b844Bc9e7595f7ABCD',
  amount: 100,
  currency: 'USDC',
  networkObject: {
    chainName: 'EVM',
    networkId: '137', // Polygon
  },
  description: 'USDC to Polygon wallet',
});

// Transfer USDC to Ethereum mainnet
const mainnetTransfer = await createKrakenExchangeTransfer({
  accountId: 'acc_123',
  to: '0x742d35Cc6634C0532925a3b844Bc9e7595f7ABCD',
  amount: 100,
  currency: 'USDC',
  networkObject: {
    chainName: 'EVM',
    networkId: '1', // Ethereum mainnet
  },
});

Transfer with MFA

If the user has MFA enabled on their Kraken account:
import { createKrakenExchangeTransfer } from '@dynamic-labs-sdk/client';

const transferWithMfa = async (params, mfaCode) => {
  const transfer = await createKrakenExchangeTransfer({
    ...params,
    mfaCode, // User-provided MFA code from their authenticator app
  });

  return transfer;
};

// Usage with user-provided MFA code
const transfer = await transferWithMfa(
  {
    accountId: 'acc_123',
    to: '0x742d35...',
    amount: 1.0,
    currency: 'BTC',
  },
  '123456' // MFA code from user
);

Idempotent transfers

Use an idempotency key to safely retry failed requests without creating duplicate transfers:
import { createKrakenExchangeTransfer } from '@dynamic-labs-sdk/client';
import { v4 as uuidv4 } from 'uuid';

const createSafeTransfer = async (params) => {
  // Generate a unique ID for this transfer request
  const transferId = uuidv4();

  const transfer = await createKrakenExchangeTransfer({
    ...params,
    id: transferId, // Idempotency key
  });

  return transfer;
};

// If the network fails and you retry, the same transfer is returned
// instead of creating a duplicate

Complete transfer flow with validation

import {
  getKrakenAccounts,
  getKrakenWhitelistedAddresses,
  createKrakenExchangeTransfer,
} from '@dynamic-labs-sdk/client';

const executeTransfer = async ({ currency, amount, destinationAddress }) => {
  // Step 1: Get accounts and verify balance
  const accounts = await getKrakenAccounts();

  let selectedAccount = null;
  let availableBalance = 0;

  for (const account of accounts) {
    const balance = account.balances.find(b => b.currency === currency);
    if (balance) {
      const available = parseFloat(balance.availableBalance || balance.balance);
      if (available >= amount) {
        selectedAccount = account;
        availableBalance = available;
        break;
      }
    }
  }

  if (!selectedAccount) {
    throw new Error(
      `Insufficient ${currency} balance. ` +
      `Requested: ${amount}, Available: ${availableBalance}`
    );
  }

  // Step 2: Verify destination address is whitelisted (if required)
  const { destinations, enforcesAddressWhitelist } =
    await getKrakenWhitelistedAddresses();

  if (enforcesAddressWhitelist) {
    const isWhitelisted = destinations.some(
      dest =>
        dest.address.toLowerCase() === destinationAddress.toLowerCase() &&
        dest.tokens?.includes(currency)
    );

    if (!isWhitelisted) {
      throw new Error(
        `Address ${destinationAddress} is not whitelisted for ${currency}. ` +
        'Add it in your Kraken account settings.'
      );
    }
  }

  // Step 3: Create the transfer
  const transfer = await createKrakenExchangeTransfer({
    accountId: selectedAccount.id,
    to: destinationAddress,
    amount,
    currency,
  });

  return {
    success: true,
    transferId: transfer.id,
    status: transfer.status,
    amount: transfer.amount,
    currency: transfer.currency,
  };
};

// Usage
try {
  const result = await executeTransfer({
    currency: 'ETH',
    amount: 0.5,
    destinationAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f7ABCD',
  });
  console.log('Transfer successful:', result);
} catch (error) {
  console.error('Transfer failed:', error.message);
}

React component with transfer form

import { useState } from 'react';
import { createKrakenExchangeTransfer } from '@dynamic-labs-sdk/client';

const TransferButton = ({
  accountId,
  currency,
  destination,
  amount,
  onSuccess,
  onError,
}) => {
  const [submitting, setSubmitting] = useState(false);

  const handleTransfer = async () => {
    setSubmitting(true);

    try {
      const transfer = await createKrakenExchangeTransfer({
        accountId,
        to: destination,
        amount: parseFloat(amount),
        currency,
      });

      onSuccess(transfer);
    } catch (error) {
      onError(error.message);
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <button onClick={handleTransfer} disabled={submitting}>
      {submitting ? 'Processing...' : `Transfer ${amount} ${currency}`}
    </button>
  );
};

Handle transfer errors

import { createKrakenExchangeTransfer } from '@dynamic-labs-sdk/client';

const handleTransfer = async (params) => {
  try {
    const transfer = await createKrakenExchangeTransfer(params);
    return { success: true, transfer };
  } catch (error) {
    const message = error.message || 'Unknown error';

    // Handle specific error cases
    if (message.includes('insufficient') || message.includes('balance')) {
      return {
        success: false,
        error: 'Insufficient balance for this transfer',
        code: 'INSUFFICIENT_BALANCE',
      };
    }

    if (message.includes('whitelist')) {
      return {
        success: false,
        error: 'Destination address is not whitelisted',
        code: 'NOT_WHITELISTED',
      };
    }

    if (message.includes('mfa') || message.includes('authentication')) {
      return {
        success: false,
        error: 'MFA verification required',
        code: 'MFA_REQUIRED',
      };
    }

    return {
      success: false,
      error: message,
      code: 'UNKNOWN_ERROR',
    };
  }
};

Prerequisites

  • User must have connected their Kraken account through Dynamic
  • The destination address must be whitelisted in Kraken if the user has address whitelisting enabled
  • The Kraken account must have sufficient balance for the transfer

Notes

  • Transfers are processed by Kraken and may take time to complete depending on network conditions
  • The status field indicates the current state of the transfer
  • Use idempotency keys (id parameter) in production to prevent duplicate transfers from network retries
  • For tokens on multiple networks (like USDC), always specify the networkObject to ensure the transfer goes to the correct chain