With MagicSpend, Base Account users can use their Coinbase balances onchain. This means users can easily start using onchain apps without needing to onramp funds into their wallet.

This also means that apps might not have all the balance information typically available to them by reading onchain data. Base Account indicates that this is the case by responding to wallet_getCapabilities RPC calls with the auxiliaryFunds capability for each chain Base Account users can use their Coinbase balances on.

If your app supports Base Account, it should not assume it knows the full balances available to a user if the auxiliaryFunds capability is present on a given chain. For example, if your app disables a transaction button if it sees that the wallet has insufficient funds, your app should take auxiliaryFunds into account and enable the button if the account has auxiliaryFunds on the chain the user is transacting on.

Why it matters

MagicSpend makes onboarding smoother by letting users pay gas or send funds even when their onchain wallet balance is zero. Your interface should therefore never disable an action just because the onchain balance is insufficient.


  1. Ensure you have the user’s address stored in your component state (from your wallet connection flow).

  2. Drop the component below into your UI. It will check whether MagicSpend (auxiliaryFunds) is available for that address on Base and if not, disable the send button accordingly.

SendButton.tsx
import { useEffect, useState } from "react";
import { createBaseAccountSDK, base } from "@base-org/account";

const sdk = createBaseAccountSDK({
  appName: "Magic Spend Demo",
  appLogoUrl: "https://base.org/logo.png",
  appChainIds: [base.constants.CHAIN_IDS.base],
});

const provider = sdk.getProvider();

interface Props {
  address?: string; // wallet address from your app state
}

export function SendButton({ address }: Props) {
  const [hasAuxFunds, setHasAuxFunds] = useState<boolean | null>(null);

  useEffect(() => {
    if (!address) return; // Wallet not connected yet

    (async () => {
      try {
        const capabilities = await provider.request({
          method: "wallet_getCapabilities",
          params: [address],
        });
        const supported =
          capabilities?.[base.constants.CHAIN_IDS.base]?.auxiliaryFunds
            ?.supported ?? false;
        setHasAuxFunds(supported);
      } catch (err) {
        console.error("wallet_getCapabilities failed", err);
        setHasAuxFunds(false);
      }
    })();
  }, [address]);

  const disabled = hasAuxFunds !== true;

  return (
    <button disabled={disabled} onClick={() => console.log("Send!")}>
      {hasAuxFunds ? "Send Transaction" : "Insufficient Balance"}
    </button>
  );
}

What the code does

  1. Receives the current address from your own wallet logic.
  2. Calls wallet_getCapabilities whenever the address changes.
  3. Reads auxiliaryFunds.supported for the Base chain (8453).
  4. Enables the button when MagicSpend is available; otherwise shows “Insufficient Balance”.

Next steps

  • Handle loading/error states if you need fine-grained UX
  • Combine this check with your existing onchain balance logic for fallback flows