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.

USDSC (Startale USD) is the canonical stablecoin on Soneium. Charging a user is a standard ERC-20 transfer. Gas is sponsored by the Startale paymaster, so the user pays only the USDSC amount.

Contract addresses

NetworkUSDSC contract
Soneium Mainnet0x3f99231dD03a9F0E7e3421c92B7b90fbe012985a
Soneium Minato0xF2c10dC1beB13ee96036DE3F922eA871Ff2e0A7e
USDSC follows the standard ERC-20 interface. Read decimals at runtime, do not hardcode them.

Send a USDSC payment

import { useWriteContract } from 'wagmi'
import { erc20Abi, parseUnits } from 'viem'

const USDSC = '0x3f99231dD03a9F0E7e3421c92B7b90fbe012985a' as const

export function PayButton({ to, amount }: { to: `0x${string}`; amount: string }) {
  const { writeContractAsync, isPending } = useWriteContract()

  const onClick = async () => {
    const hash = await writeContractAsync({
      address: USDSC,
      abi: erc20Abi,
      functionName: 'transfer',
      args: [to, parseUnits(amount, 6)],
    })
    console.log({ hash })
  }

  return (
    <button onClick={onClick} disabled={isPending}>
      Pay {amount} USDSC
    </button>
  )
}
The parseUnits(amount, 6) call assumes USDSC uses 6 decimals. Read the actual value at runtime with decimals() if you support multiple stablecoins or want to be defensive against contract upgrades.

Approve and spend in one click

Use wallet_sendCalls (EIP-5792) when your flow needs an approve followed by a contract call. The user signs once and both calls land atomically.
import { encodeFunctionData, erc20Abi, parseUnits } from 'viem'

const calls = [
  {
    to: USDSC,
    data: encodeFunctionData({
      abi: erc20Abi,
      functionName: 'approve',
      args: [SPENDER, parseUnits('10', 6)],
    }),
  },
  {
    to: SPENDER,
    data: encodeFunctionData({
      abi: spenderAbi,
      functionName: 'subscribe',
      args: [parseUnits('10', 6)],
    }),
  },
]

const { id } = await provider.request({
  method: 'wallet_sendCalls',
  params: [
    { version: '1', from: address, chainId: '0x74c', atomicRequired: true, calls },
  ],
})
See Batched calls for the full pattern.

Pay in ETH

For native ETH payments, use useSendTransaction:
import { parseEther } from 'viem'

await sendTransactionAsync({
  to: '0xRecipient...',
  value: parseEther('0.05'),
})
Gas is still sponsored. Only the value moves from the user’s smart account.

Reading balances

import { erc20Abi, formatUnits } from 'viem'
import { publicClient } from './viem'

const balance = await publicClient.readContract({
  address: USDSC,
  abi: erc20Abi,
  functionName: 'balanceOf',
  args: [address],
})

const decimals = await publicClient.readContract({
  address: USDSC,
  abi: erc20Abi,
  functionName: 'decimals',
})

const display = formatUnits(balance, decimals)