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.

This page is the bridge between Installation and the feature tutorials. By the end you will have a smart account, an account client wired to the SCS bundler and paymaster, and a place to keep them in your app’s state.
Source: StartaleGroup/scs-aa-sdk. Every symbol on this page is exported from @startale-scs/aa-sdk or viem.

What you will build

StartaleAccountClient is the object you actually call sendUserOperation and ERC-7579 module actions on. The smart account and paymaster clients are wired into it once at setup time.

Account creation

import { http, createPublicClient } from "viem"
import { soneiumMinato } from "viem/chains"
import { toStartaleSmartAccount } from "@startale-scs/aa-sdk"

const publicClient = createPublicClient({ chain: soneiumMinato, transport: http() })

const account = await toStartaleSmartAccount({
  signer,
  chain: soneiumMinato,
  transport: http(),
  index: 0n,
})
ParameterRequiredSourceNotes
signeryesyour auth providerLocalAccount, viem WalletClient, EIP-1193 EthereumProvider, or EthersWallet. See Installation.
chainyesviem/chainsUse soneiumMinato for testnet and soneium for mainnet.
transportyesviemRPC transport used internally for chain reads (nonces, code).
indexoptionalyour codeA bigint salt that determines the counterfactual address. Different index values with the same signer produce different accounts.
accountAddressoptionalyour codePin a known smart account address. Useful for EIP-7702 delegation and for skipping address derivation.
validators, executors, prevalidationHooks, hook, fallbacksoptional@startale-scs/aa-sdkERC-7579 modules to install at deployment time. See Smart sessions and Social recovery for module examples.
Why index matters. Smart accounts are deployed via CREATE2, so the (signer, index) pair determines the account address before deployment. Use a stable index per user (often 0n) to keep one account per signer; vary it when you intentionally want sub-accounts under the same signer.

Account client (sponsored gas)

Wrap the account, the paymaster, and the bundler transport in a single client:
import { http } from "viem"
import {
  createSCSPaymasterClient,
  createSmartAccountClient,
} from "@startale-scs/aa-sdk"

const paymasterClient = createSCSPaymasterClient({
  transport: http(process.env.PAYMASTER_URL!),
})

const smartAccountClient = createSmartAccountClient({
  account,
  transport: http(process.env.BUNDLER_URL!),
  client: publicClient,
  paymaster: paymasterClient,
  paymasterContext: {
    paymasterId: process.env.PAYMASTER_ID!,
  },
})
SymbolSourceRole
createSCSPaymasterClient@startale-scs/aa-sdkConnects to the SCS Paymaster RPC. Returns a SCSPaymasterClient.
createSmartAccountClient@startale-scs/aa-sdkBuilds a StartaleAccountClient with bundler, paymaster, and ERC-7579 module actions.
paymasterContext.paymasterIdSCS PortalThe id of the paymaster you provisioned. Both managed and self-funded paymasters return one.

Account client (ERC-20 gas)

To accept ERC-20 tokens as gas instead of sponsoring, swap the paymasterId field for a token address:
const tokenAccountClient = createSmartAccountClient({
  account,
  transport: http(process.env.BUNDLER_URL!),
  client: publicClient,
  paymaster: paymasterClient,
  paymasterContext: {
    token: "0xfF0CBFbA43a1Ce2B8d72B2f3121558BcBd4B03a6", // USDC on Soneium Minato
  },
})
The supported tokens per network are listed in Supported networks. The full token paymaster flow, including quotes and approvals, is covered in the ERC-20 paymaster tutorial.

Persisting the client in a React app

A common pattern is to lift the smart account and clients into a context provider so they survive component remounts and are available everywhere in the tree.
StartaleProvider.tsx
import { createContext, useContext, useEffect, useState, type ReactNode } from "react"
import { http, createPublicClient } from "viem"
import { soneiumMinato } from "viem/chains"
import {
  createSCSPaymasterClient,
  createSmartAccountClient,
  toStartaleSmartAccount,
  type StartaleSmartAccount,
  type StartaleAccountClient,
} from "@startale-scs/aa-sdk"

type StartaleContextValue = {
  account?: StartaleSmartAccount
  sponsoredClient?: StartaleAccountClient
  tokenClient?: StartaleAccountClient
  isReady: boolean
  error?: string
}

const StartaleContext = createContext<StartaleContextValue>({ isReady: false })

export function StartaleProvider({ signer, children }: { signer: unknown; children: ReactNode }) {
  const [value, setValue] = useState<StartaleContextValue>({ isReady: false })

  useEffect(() => {
    if (!signer) return

    let cancelled = false
    ;(async () => {
      try {
        const publicClient = createPublicClient({ chain: soneiumMinato, transport: http() })

        const account = await toStartaleSmartAccount({
          signer: signer as never,
          chain: soneiumMinato,
          transport: http(),
          index: 0n,
        })

        const paymasterClient = createSCSPaymasterClient({
          transport: http(process.env.NEXT_PUBLIC_PAYMASTER_URL!),
        })

        const sponsoredClient = createSmartAccountClient({
          account,
          transport: http(process.env.NEXT_PUBLIC_BUNDLER_URL!),
          client: publicClient,
          paymaster: paymasterClient,
          paymasterContext: {
            paymasterId: process.env.NEXT_PUBLIC_PAYMASTER_ID!,
          },
        })

        const tokenClient = createSmartAccountClient({
          account,
          transport: http(process.env.NEXT_PUBLIC_BUNDLER_URL!),
          client: publicClient,
          paymaster: paymasterClient,
          paymasterContext: {
            token: process.env.NEXT_PUBLIC_GAS_TOKEN_ADDRESS as `0x${string}`,
          },
        })

        if (!cancelled) setValue({ account, sponsoredClient, tokenClient, isReady: true })
      } catch (err) {
        if (!cancelled) setValue({ isReady: false, error: (err as Error).message })
      }
    })()

    return () => {
      cancelled = true
    }
  }, [signer])

  return <StartaleContext.Provider value={value}>{children}</StartaleContext.Provider>
}

export const useStartale = () => useContext(StartaleContext)
Hook / typeSourceRole
StartaleSmartAccount@startale-scs/aa-sdkType alias for the account returned by toStartaleSmartAccount.
StartaleAccountClient@startale-scs/aa-sdkType alias for the client returned by createSmartAccountClient.
Environment variables in this snippet use a framework-agnostic placeholder. The SDK does not require any specific prefix; pick whichever your framework expects (process.env.X, import.meta.env.X, runtime config, etc.).

Useful account methods

Once you have the account, you can call these without building a UserOperation first:
account.address                              // counterfactual address
await account.getAddress()                   // resolved onchain address (deploys if needed)
await account.getNonce({ key: 0n })          // ERC-7579 two-dimensional nonce
await account.isDelegated()                  // EIP-7702 delegation check
await account.unDelegate()                   // EIP-7702 reset

Next steps

Contract interactions

Send single and batched calls through sendUserOperation.

Sponsored paymaster

Send sponsored UserOperations end to end with policies.

ERC-20 paymaster

Charge users in ASTR, USDC, or other tokens.

Smart sessions

Reduce signature friction with scoped session keys.