Daimo Pay SDK

The best way to embed Daimo Pay into your app is via the @daimo/pay SDK.

The SDK is open source. You can find it on NPM and GitHub. For installation instructions, see the Quickstart. For a live demo of common use-cases, see the Demo Page. For more details, see below.

SDK flow demo

DaimoPayButton Reference

Here are the three ways to use Daimo Pay when configuring it with your appId

ModeSetResult
CheckouttoAddress, toChain, toToken, toUnitsShows a fixed amount. Funds settle to toAddress.
DeposittoAddress, toChain, toTokenUser chooses amount. Funds settle to toAddress.
Contract CalltoAddress, toChain, toToken, toUnits, toCallDataConverts to toToken, auto-approves toUnits, then calls the contract at toAddress with toCallData.
  • Name
    appId*
    Type
    string
    Description

    Your public app ID. Use pay-demo for prototyping only.

  • Name
    intent
    Type
    string
    Description

    The intent verb, sets the modal title and the primary button label. Examples: "Pay", "Deposit", or "Add Money".
    Purely visual. Does not change payment behavior.

  • Name
    toAddress*
    Type
    string
    Description

    The destination address to transfer to, or contract to call.

    Contract whitelist required
    If the toAddress is a contract it must be whitelisted first. Email founders@daimo.com with:
    Whitelist contract: 0xYourContractAddress
    Chain: <chainId>

  • Name
    toChain*
    Type
    number
    Description

    The destination chain ID toAddress is on.

  • Name
    toUnits
    Type
    string
    Description

    Controls the exact amount to receive. Must be a precise decimal string (e.g. "1.00" for $1.00 USDC). Providing more decimals than the underlying token supports will result in an error.

    When specified: User pays a fixed amount. You receive exactly this amount. Ideal for checkouts.
    When omitted: User can input any amount to send. Ideal for deposits or top-ups into your app.

  • Name
    toToken*
    Type
    string
    Description

    Token to receive on the destination chain. Provide an ERC20 address or use the zero address 0x000…0000 to receive the native token. Examples: ETH, CELO, POL.
    If toCallData is set and toToken is an ERC-20, the amount is approved to toAddress immediately before the call; otherwise the amount is transferred to toAddress.

  • Name
    refundAddress**
    Type
    string
    Description

    Required fallback refund address. Must be valid on all supported networks.

    How refunds are routed
    • Wallet flow (user pays from a connected wallet):
      If the payment bounces, the refund is automatically returned to that wallet.
    • Manual address flow (user copies an address/QR):
      If the payment bounces or the user underpays/overpays, the refund is sent to refundAddress.
      We do this because we cannot reliably refund to exchange/custodial deposit addresses.

    Choose an address you control on every supported network so refunds never get stuck.

  • Name
    toCallData
    Type
    string
    Description

    Optional calldata to execute an arbitrary contract call if toAddress is a contract. If specified, we'll automatically make a token approval of toUnits to the contract before executing the call.

    Contract whitelist required To request whitelisting email us at founders@daimo.com with:
    Whitelist contract: 0xYourContractAddress
    Chain: <chainId>

  • Name
    paymentOptions
    Type
    string[]
    Description

    Optional filter to display only a subset of payment options. By default, all payment options are enabled.
    The Wallet and Manual Address options are always enabled and cannot be disabled.
    Lets you filter these options: AllExchanges, Coinbase, Binance, Lemon, AllPaymentApps (desktop only), Venmo, CashApp, MercadoPago, Revolut, Wise, Zelle.
    With these options, even users without a wallet can complete the flow.

  • Name
    preferredChains
    Type
    number[]
    Description

    Optional preferred chain IDs. Assets on these chains will appear first.

  • Name
    preferredTokens
    Type
    object[]
    Description

    Optional preferred tokens. Assets in these specific tokens will appear first, taking precedence over preferredChains. Each token has the format: { chain: number, address: string }.

  • Name
    metadata
    Type
    object
    Description

    Optional. Attach key-value data to the payment object that persists across webhooks and API responses. For example, attach your system’s unique ID to simplify tracking, or store customer information with their payment.

    You can specify up to 50 key-value pairs. Keys and values are stored as strings.

  • Name
    redirectReturnUrl
    Type
    string
    Description

    Optional. Redirect URL to return to the app. After the user pays with Coinbase, Binance, or one of the Payment Apps, they will be redirected back to this URL.

  • Name
    defaultOpen
    Type
    boolean
    Description

    Optional. If set, automatically open the modal on first render.

  • Name
    closeOnSuccess
    Type
    boolean
    Description

    Optional. If set, automatically closes the modal after a successful payment.

  • Name
    resetOnSuccess
    Type
    boolean
    Description

    Optional. If set, automatically resets the payment after a successful transaction, using the original props. This allows the user to initiate a new payment without refreshing the page.

import { encodeFunctionData, getAddress } from "viem";
import { optimismUSDC } from '@daimo/pay-common'

<DaimoPayButton
  {/* Required props */}
  appId="pay-demo"
  toAddress="RECIPIENT_ADDRESS_HERE" or "CONTRACT_ADDRESS_HERE"
  toChain={optimismUSDC.chainId} /* Optimism Chain ID*/
  toToken={getAddress(optimismUSDC.token)} /* Optimism USDC */

  {/* Optional props */}
  intent="Checkout"
  toUnits="1.00" /* 1 USDC */
  toCallData={encodeFunctionData({ /* Example contract call */
    abi: YOUR_CONTRACT_ABI,
    functionName: "FUNCTION_NAME",
    args: ["RECIPIENT_ADDRESS", quantity],
  })}
  paymentOptions={["Coinbase", "Binance"]}
  preferredChains={[42161, 8453]} // Prefer Arbitrum, Base
  preferredTokens={[ // Prefer Optimism USDC
    { chain: 10, address: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85" }
  ]},
  metadata={{ mySystemId: "123", name: "John Doe" }}
  defaultOpen() /* Automatically open the modal */
  closeOnSuccess() /* Automatically close the modal after transaction*/
  resetOnSuccess() /* Reset modal after a successful transaction */

  {/* Event handlers */}
  onPaymentStarted={(e) => console.log(e)}
  onPaymentCompleted={(e) => console.log(e)}
  onPaymentBounced={(e) => console.log(e)}
  onOpen={() => console.log("Modal opened")}
  onClose={() => console.log("Modal closed")}
/>


Alternatively, if you want to use Daimo Pay via the Payments API, you can initialize a payment by passing its payId to DaimoPayButton.

  • Name
    payId*
    Type
    string
    Description

    Use id generated from your api call.

  • Name
    defaultOpen
    Type
    boolean
    Description

    Optional. If set, automatically open the modal on first render.

  • Name
    closeOnSuccess
    Type
    boolean
    Description

    Optional. If set, automatically closes the modal after a successful payment.

  • Name
    resetOnSuccess
    Type
    boolean
    Description

    Optional. If set, automatically resets the payment after a successful transaction, using the original props. This allows the user to initiate a new payment without refreshing the page.


<DaimoPayButton
  payId="EqxRaFwRHjtXHBJTJuvCxr8nmhNjrNZtVGcHLEDGgn1F"
  defaultOpen() /* Automatically open the modal */
  closeOnSuccess() /* Automatically close the modal after transaction*/
  resetOnSuccess() /* Reset modal after a successful transaction */
/>

Modifying Payment Parameters

Once the DaimoPayButton is rendered, its payment parameters are frozen to ensure a consistent and predictable user experience. This means that changes to props like toUnits or toAddress after initial render will not automatically update the current payment.

If your app needs to dynamically update the payment, such as when the user changes the amount, selects a different token, or switches networks, you can use the resetPayment function provided by the useDaimoPayUI hook.

Calling resetPayment updates the current payment parameters. It accepts the same parameters as DaimoPayButton. You can pass only the props you wish to change, any omitted fields will retain their previous values.

import { useDaimoPayUI } from "@daimo/pay";

const { resetPayment } = useDaimoPayUI();

// Example: user selects a new amount and token
function handleAmountChange(newAmount: string) {
  resetPayment({
    toChain: 8453, // Base
    toUnits: newAmount, // updated amount
    toToken: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base USDC
  });
}

Themes and Customization

You can customize the look of both the button and the modal.

  • Modal: Configure in DaimoPayProvider using mode, theme, or customTheme.
  • Button: Provide your own UI with DaimoPayButton.Custom.
  • Name
    mode
    Type
    string
    Description

    Light, dark, or auto. Ignored if theme or customTheme is set.

  • Name
    theme
    Type
    string
    Description

    Named theme for the modal and wallet UI. One of: auto | web95 | retro | soft | midnight | minimal | rounded | nouns. Default: auto. See the ConnectKit theming docs for previews.

    Note: theme (or customTheme) overrides mode. Use theme="auto" to follow the user’s system preference.

  • Name
    customTheme
    Type
    object
    Description

    Custom theme overrides for the modal and wallet UI. Use this when theme is not enough.
    Build and export themes with the ConnectKit theme builder

import { Roboto } from "next/font/google";

const roboto = Roboto({
  variable: "--font-roboto",
  subsets: ["latin"],
  weight: ["300", "400", "500", "700", "900"],
});
// Only override the font
const robotoTheme = {
'--ck-font-family': 'var(--font-roboto)',
}

<DaimoPayProvider
  mode="dark" /* will be override by customTheme */
  {/* alternatively, theme="auto" */}
  customTheme={robotoTheme}
/>

DaimoPayButton.Custom

In addition, you can replace the DaimoPayButton entirely with your own button using DaimoPayButton.Custom.

  • Name
    children*
    Type
    function
    Description

    Custom renderer function that receives show/hide methods and returns React node.

<DaimoPayButton.Custom
  appId="pay-demo"
  toAddress="0x1234567890123456789012345678901234567890"
  toChain={10} /* Optimism */
  toUnits="1.23" /* $1.23 Optimism USDC */
  toToken="0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85" /* Optimism USDC */
>
  {({ show, hide }) => <button onClick={show}>Custom Pay Button</button>}
</DaimoPayButton.Custom>

Event Handlers

  • Name
    onPaymentStarted
    Type
    function
    Description

    Called when the user sends payment and the transaction is seen on-chain.

  • Name
    onPaymentCompleted
    Type
    function
    Description

    Called when the destination transfer or call completes successfully.

  • Name
    onPaymentBounced
    Type
    function
    Description

    Called when the destination call reverts and funds are refunded.

  • Name
    onOpen
    Type
    function
    Description

    Called when the payment modal opens. Useful for analytics or UI transitions.

  • Name
    onClose
    Type
    function
    Description

    Called when the payment modal closes, whether from user dismissal or after a completed payment.

The events passed to the onPayment... handlers match the events passed to webhooks. You can use them for immediate feedback, while using webhooks for things like backend order status tracking. This ensures robust tracking even if the user loses network during payment.

const [latest, setLatest] = useState<DaimoPayEvent>();

return (<>
  <DaimoPayButton
    {/* For existing payments created via API, use payId. */}
    payId="72hebvwpDJDA9qNubzb2jYjAbdDgjH4uD53rBnh8BR3A"

    {/* Either way, the onPayment... handlers always work. */}
    onPaymentStarted={setLatest}
    onPaymentCompleted={setLatest}
    onPaymentBounced={setLatest}
    onOpen={() => console.log("Modal opened")}
    onClose={() => console.log("Modal closed")}
  />
  {latest.type === 'payment_started' && <Spinner />}
  {latest.type === 'payment_completed' && <SuccessLink txHash={latest.txHash} />}
  {latest.type === 'payment_bounced' && <RevertLink txHash={latest.txHash} />}
</>);

Configuration Reference

The @daimo/pay SDK uses a configuration object to set up providers for your application. This configuration is essential for initializing the SDK and ensuring compatibility with the supported chains and transports. Below is a detailed reference to the configuration options available in defaultConfig.

defaultConfig Parameters

  • Name
    appName*
    Type
    string
    Description

    Required name of your application. This is displayed to users during wallet connections and other interactions.

  • Name
    appIcon
    Type
    string
    Description

    Optional URL pointing to the app's icon. Used in wallet connection UIs.

  • Name
    appDescription
    Type
    string
    Description

    Optional description of your app. Shown to users in wallet connection prompts.

  • Name
    appUrl
    Type
    string
    Description

    Optional URL of your app. Used in wallet connection interfaces.

  • Name
    ssr
    Type
    boolean
    Description

    Indicates whether the app uses server-side rendering (SSR). Set to true for SSR-enabled projects. Default is false.

  • Name
    connectors
    Type
    function[]
    Description

    Specifies wallet connectors. Defaults to Daimo's recommended connectors and configurations. Will override all default connectors and additionalConnectors.

  • Name
    additionalConnectors
    Type
    function[]
    Description

    Optional additional wallet connectors to use in the configuration. Extend the list of default connectors with custom wallet connectors.

  • Name
    transports
    Type
    object
    Description

    Defines the network transport (e.g., HTTP) for each supported chain. If not provided, default wagmi HTTP transports are used.

import { getDefaultConfig } from "@daimo/pay";
import { createConfig, http } from "wagmi";
import { mainnet, optimism } from "wagmi/chains";

// Define your configuration
const config = createConfig(
  getDefaultConfig({
    appName: "My Ethereum App",
    appIcon: "https://example.com/icon.png",
    appDescription: "An app for seamless crypto payments",
    appUrl: "https://example.com",
    additionalConnectors: [], // Add custom wallet connectors here
    transports: {
      [mainnet.id]: http("https://eth.llamarpc.com"),
      [optimism.id]: http("https://optimism-rpc.publicnode.com"),
    },
  }),
);

Was this page helpful?