Skip to main content

Overview

External wallets are user-controlled blockchain wallets registered through the delegation process. Users maintain full custody of their private keys while granting spending authority to the platform for card payments. The platform can view balance and allowance information but never has access to private keys.

Viewing External Wallets

Retrieve all external wallets registered by the authenticated user:
curl -X GET "https://api.example.com/v1/wallet/external" \
  -H "x-client-key: YOUR_PUBLIC_KEY" \
  -H "Authorization: Bearer USER_ACCESS_TOKEN"

Response Fields

FieldTypeDescription
addressstringBlockchain wallet address controlled by user
currencystringCurrency delegated from this wallet
balancestringCurrent wallet balance (on-chain)
allowancestringRemaining spending authority granted to platform
networkstringBlockchain network (linea, ethereum, solana)

Understanding Balance vs Allowance

External wallet data includes two critical values:
Balance is the total amount of cryptocurrency in the user’s wallet on the blockchain.
  • Retrieved directly from blockchain
  • Updated in real-time
  • Includes all funds, not just delegated amount
  • User controls this completely
Example: User has 5000 USDC total in their wallet. This is the balance value.
Key Relationship: Allowance can never exceed balance. Even if user delegated 5000 USDC but only has 2000 USDC remaining in wallet, maximum chargeable amount is 2000 USDC.

Interpreting Wallet Status

Different balance/allowance combinations indicate different states:
{
  "balance": "5000.00",
  "allowance": "4800"
}
Status: Wallet is funded and has ample allowance.Action: None needed. User can make card purchases normally.
{
  "balance": "5000.00",
  "allowance": "200"
}
Status: Wallet is funded but allowance is nearly exhausted.Action: Prompt user to redelegate with higher allowance to continue card payments.
{
  "balance": "150.00",
  "allowance": "3500"
}
Status: Allowance is high but wallet balance is low.Action: Platform can only charge up to 150 USDC (balance), not 3500 USDC (allowance). Prompt user to add funds to wallet.
{
  "balance": "5000.00",
  "allowance": "0"
}
Status: Allowance fully consumed, card payments blocked.Action: User must redelegate to continue using card. Balance is sufficient once redelegation completes.
{
  "balance": "0.00",
  "allowance": "0"
}
Status: Wallet is empty and no spending authority remains.Action: User must fund wallet and redelegate before card payments can resume.

Registering External Wallets

External wallets are registered through the delegation process. Users cannot be added directly via the API.
1

User Initiates Delegation

User clicks “Connect Wallet” or “Add External Wallet” in your application.
2

Request Delegation Token

Your backend calls GET /v1/delegation/token to obtain a single-use token.
3

Frontend Wallet Interaction

User connects Web3 wallet and approves blockchain transaction granting spending authority.
4

Submit Delegation Proof

Your backend calls blockchain-specific endpoint to finalize registration:
  • POST /v1/delegation/evm/post-approval for Linea/Ethereum
  • POST /v1/delegation/solana/post-approval for Solana
5

Verify Registration

Call GET /v1/wallet/external to confirm wallet appears in user’s registered wallets.
Complete Delegation Guide →

Network-Specific Details

EVM Networks (Linea, Ethereum)

  • Length: 42 characters
  • Format: Hexadecimal starting with “0x”
  • Example: 0x3a11a86cf218c448be519728cd3ac5c741fb3424
  • Checksum: Case-sensitive checksummed addresses
{
  "address": "0x3a11a86cf218c448be519728cd3ac5c741fb3424",
  "network": "linea"
}

Solana

  • Length: 32-44 characters
  • Format: Base58-encoded public key
  • Example: DYw8jCTfwHNRJhhmFcbXvVDTqWMEVFBX6ZKUmG5CNSKK
{
  "address": "DYw8jCTfwHNRJhhmFcbXvVDTqWMEVFBX6ZKUmG5CNSKK",
  "network": "solana"
}

Implementation Examples

Display External Wallets

async function fetchExternalWallets(userToken) {
  const response = await fetch(
    'https://api.example.com/v1/wallet/external',
    {
      headers: {
        'x-client-key': 'YOUR_PUBLIC_KEY',
        'Authorization': `Bearer ${userToken}`
      }
    }
  );

  const wallets = await response.json();

  return wallets.map(wallet => ({
    ...wallet,
    balanceNumber: parseFloat(wallet.balance),
    allowanceNumber: parseFloat(wallet.allowance),
    isLowAllowance: parseFloat(wallet.allowance) < parseFloat(wallet.balance) * 0.2,
    isLowBalance: parseFloat(wallet.balance) < 100,
    effectiveLimit: Math.min(
      parseFloat(wallet.balance),
      parseFloat(wallet.allowance)
    )
  }));
}

// Usage
const wallets = await fetchExternalWallets(userToken);

wallets.forEach(wallet => {
  console.log(`${wallet.network} ${wallet.currency.toUpperCase()}`);
  console.log(`  Address: ${wallet.address}`);
  console.log(`  Balance: ${wallet.balance}`);
  console.log(`  Allowance: ${wallet.allowance}`);
  console.log(`  Effective Limit: ${wallet.effectiveLimit}`);

  if (wallet.isLowAllowance) {
    console.log('  ⚠️ Low allowance - suggest redelegation');
  }

  if (wallet.isLowBalance) {
    console.log('  ⚠️ Low balance - suggest funding wallet');
  }
});

Wallet Status Component

import { useState, useEffect } from 'react';

function ExternalWalletList({ userToken }) {
  const [wallets, setWallets] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchWallets();
  }, []);

  const fetchWallets = async () => {
    setLoading(true);
    const response = await fetch(
      'https://api.example.com/v1/wallet/external',
      {
        headers: {
          'x-client-key': 'YOUR_PUBLIC_KEY',
          'Authorization': `Bearer ${userToken}`
        }
      }
    );

    const data = await response.json();
    setWallets(data);
    setLoading(false);
  };

  const getWalletStatus = (wallet) => {
    const balance = parseFloat(wallet.balance);
    const allowance = parseFloat(wallet.allowance);

    if (allowance === 0) return { status: 'exhausted', color: 'red' };
    if (allowance < balance * 0.2) return { status: 'low-allowance', color: 'orange' };
    if (balance < 100) return { status: 'low-balance', color: 'yellow' };
    return { status: 'healthy', color: 'green' };
  };

  const formatAddress = (address) => {
    return `${address.slice(0, 6)}...${address.slice(-4)}`;
  };

  if (loading) return <div>Loading wallets...</div>;

  if (wallets.length === 0) {
    return (
      <div className="empty-state">
        <p>No external wallets connected</p>
        <button onClick={() => window.location.href = '/connect-wallet'}>
          Connect Wallet
        </button>
      </div>
    );
  }

  return (
    <div className="wallet-list">
      <h3>External Wallets</h3>

      {wallets.map((wallet, index) => {
        const { status, color } = getWalletStatus(wallet);
        const effectiveLimit = Math.min(
          parseFloat(wallet.balance),
          parseFloat(wallet.allowance)
        );

        return (
          <div key={index} className={`wallet-card status-${color}`}>
            <div className="wallet-header">
              <div>
                <h4>{wallet.currency.toUpperCase()} on {wallet.network}</h4>
                <code>{formatAddress(wallet.address)}</code>
              </div>
              <div className={`status-badge ${color}`}>
                {status.replace('-', ' ')}
              </div>
            </div>

            <div className="wallet-stats">
              <div className="stat">
                <label>Balance</label>
                <span>{wallet.balance} {wallet.currency.toUpperCase()}</span>
              </div>

              <div className="stat">
                <label>Allowance</label>
                <span>{wallet.allowance} {wallet.currency.toUpperCase()}</span>
              </div>

              <div className="stat">
                <label>Available for Card</label>
                <span className="highlight">
                  {effectiveLimit.toFixed(2)} {wallet.currency.toUpperCase()}
                </span>
              </div>
            </div>

            {status === 'low-allowance' && (
              <div className="warning-banner">
                Allowance is low. Redelegate to continue card payments.
                <button onClick={() => handleRedelegate(wallet)}>
                  Redelegate
                </button>
              </div>
            )}

            {status === 'low-balance' && (
              <div className="warning-banner">
                Wallet balance is low. Add funds to enable purchases.
                <button onClick={() => handleFundWallet(wallet)}>
                  Fund Wallet
                </button>
              </div>
            )}

            {status === 'exhausted' && (
              <div className="error-banner">
                Allowance exhausted. Redelegate to resume card payments.
                <button onClick={() => handleRedelegate(wallet)}>
                  Redelegate Now
                </button>
              </div>
            )}
          </div>
        );
      })}
    </div>
  );
}

Monitoring and Alerts

Implement proactive monitoring to alert users before issues occur:

Allowance Monitoring

Alert when allowance drops below 50% of original delegation.Message: “Your allowance is half-used. Consider redelegating soon.”
Stronger alert when allowance drops below 20%.Message: “Your allowance is running low. Redelegate now to avoid payment issues.”
Critical alert when allowance reaches 0.Message: “Card payments blocked. Redelegate immediately to continue.”

Balance Monitoring

Alert when balance drops below minimum threshold (e.g., $100).Message: “Your wallet balance is low. Add funds to enable purchases.”
Alert when balance reaches 0.Message: “Your wallet is empty. Add funds to resume card payments.”

Use Cases

Register wallets on different networks to optimize for fees or speed:
  • Linea for daily spending (low fees)
  • Ethereum for large purchases (more liquidity)
  • Solana for fast transactions (quick confirmations)
Use priority management to control which network is charged first.
Register wallets with different currencies:
  • USDC wallet for stable value
  • USDT wallet as backup
  • Multiple stablecoins for redundancy
Platform automatically tries next currency if primary is insufficient.
Users can keep majority of funds in DeFi protocols while maintaining delegated wallet for card payments:
  • Delegate small amount for daily spending
  • Keep bulk in yield-earning protocols
  • Transfer and redelegate as needed
Register separate wallets for personal vs. business spending:
  • Personal wallet with priority 1
  • Business wallet with priority 2
  • Clear separation for accounting

Best Practices

Poll for Updates

External wallet data is retrieved from blockchain in real-time. Poll this endpoint periodically (every 30-60 seconds) when user is viewing wallet information to show current status.

Show Effective Limit

Display min(balance, allowance) as the actual spendable amount. This is the real limit for card transactions.

Proactive Redelegation Prompts

Alert users when allowance drops below 20% and provide one-click redelegation flow. Don’t wait until allowance is exhausted.

Network Fee Information

Display approximate network fees for each blockchain to help users choose cost-effective options when registering multiple wallets.

Blockchain Explorer Links

Provide links to blockchain explorers (Etherscan, Solscan) so users can verify wallet state independently.

Security Reminders

Remind users that they control their private keys and should secure their wallet software properly. Platform never has access to keys.

Next Steps