Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.startale.com/llms.txt

Use this file to discover all available pages before exploring further.

Build a checkout button that charges a user in USDSC. Inside the Startale App, the host paymaster sponsors gas, so the user pays only the stablecoin amount. Standalone dapps configure their own paymaster through paymasterOptions for the same gasless UX.

What you’ll build

A single button that:
  1. Connects the user (if not yet connected).
  2. Sends a USDSC transfer from the smart account to a recipient address.
  3. Reads the on-chain receipt to confirm the payment.

Prerequisites

  • A working connect button.
  • A recipient address controlled by your app. For testing, your own EOA on Soneium Minato is fine.
  • A smart account funded with USDSC. On Minato, request testnet USDSC from the Startale team in Discord; there is no public USDSC faucet today.

Steps

1. Define constants

src/constants.ts
import type { Address } from 'viem'

export const USDSC_ADDRESS: Address = '0x3f99231dD03a9F0E7e3421c92B7b90fbe012985a' // Soneium Mainnet
export const USDSC_DECIMALS = 6
export const RECIPIENT: Address = '0x...' // your collection wallet

2. Build the pay button

src/PayButton.tsx
import { useWriteContract, useWaitForTransactionReceipt } from 'wagmi'
import { erc20Abi, parseUnits } from 'viem'
import { USDSC_ADDRESS, RECIPIENT, USDSC_DECIMALS } from './constants'

export function PayButton({ amount }: { amount: string }) {
  const { writeContractAsync, data: hash, isPending } = useWriteContract()
  const { isLoading, isSuccess } = useWaitForTransactionReceipt({ hash })

  const onClick = async () => {
    await writeContractAsync({
      address: USDSC_ADDRESS,
      abi: erc20Abi,
      functionName: 'transfer',
      args: [RECIPIENT, parseUnits(amount, USDSC_DECIMALS)],
    })
  }

  return (
    <button onClick={onClick} disabled={isPending || isLoading}>
      {isPending ? 'Confirming…' : isLoading ? 'Settling…' : isSuccess ? 'Paid' : `Pay ${amount} USDSC`}
    </button>
  )
}

3. Render it

src/App.tsx
<PayButton amount="5" />

4. Verify the receipt

useWaitForTransactionReceipt from wagmi resolves once the transaction lands. The isSuccess flag flips when the receipt is available with status 'success'. For a deeper verification, read the recipient’s USDSC balance after the receipt:
import { erc20Abi, formatUnits } from 'viem'
import { useReadContract } from 'wagmi'

const { data: balance } = useReadContract({
  address: USDSC_ADDRESS,
  abi: erc20Abi,
  functionName: 'balanceOf',
  args: [RECIPIENT],
})

Reading decimals dynamically

Hardcoding 6 works today, but if Startale ever upgrades USDSC to use different decimals, the math breaks. The defensive pattern reads them at runtime:
const decimals = await publicClient.readContract({
  address: USDSC_ADDRESS,
  abi: erc20Abi,
  functionName: 'decimals',
})
Cache the result; it doesn’t change often.

What you’ve learned

  • How to send an ERC-20 transfer from the user’s smart account.
  • That the user pays no gas, sponsorship is automatic.
  • How to confirm settlement on-chain with viem and wagmi.

Next

Build your first Mini App →