> ## 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.

# Quickstart

> Send your first sponsored UserOperation with the Startale AA SDK on Soneium Minato.

In about five minutes you will deploy a smart account on Soneium Minato and send a sponsored [`UserOperation`](/aa-sdk/getting-started/introduction) to a counter contract using nothing but Node.js and the Startale AA SDK.

<Note>
  The full SDK source is at [`StartaleGroup/scs-aa-sdk`](https://github.com/StartaleGroup/scs-aa-sdk). Every symbol referenced below comes from `@startale-scs/aa-sdk` or `viem`.
</Note>

## Prerequisites

<Steps>
  <Step title="Node.js 18 or newer">
    Verify with `node -v`. The SDK targets modern ES2022 output.
  </Step>

  <Step title="An EOA private key for development">
    Any local key works. The signer is only used to sign UserOperations; the smart account is a separate address.
  </Step>

  <Step title="An SCS Portal API key and paymaster">
    Follow [Portal setup](/aa-sdk/tutorials/portal) to issue an API key, create a managed or self-funded paymaster, and copy the resulting `bundlerUrl`, `paymasterUrl`, and `paymasterId`.
  </Step>

  <Step title="A target contract on Soneium Minato">
    Any deployed contract with a public method works. The example uses a `count()` method on a `Counter` contract; substitute your own ABI and selector.
  </Step>
</Steps>

## 1. Initialize the project

```bash theme={null}
mkdir aa-quickstart && cd aa-quickstart
npm init -y
npm install @startale-scs/aa-sdk viem dotenv
npm install -D typescript ts-node @types/node
```

<Info>
  These are the only runtime dependencies you need for the script in this guide. `@startale-scs/aa-sdk` carries the smart account and clients; `viem` provides chain definitions, transports, and ABI helpers; `dotenv` loads your bundler and paymaster URLs from `.env`.
</Info>

Create a `.env` file alongside `package.json`:

```bash .env theme={null}
BUNDLER_URL="https://soneium-minato.bundler.scs.startale.com?apikey=YOUR_API_KEY"
PAYMASTER_URL="https://paymaster.scs.startale.com/v1?apikey=YOUR_API_KEY"
PAYMASTER_ID="pm_..."
OWNER_PRIVATE_KEY="0xYourPrivateKey"
COUNTER_CONTRACT_ADDRESS="0xYourCounterContract"
```

## 2. Wire up the script

Create `index.ts`:

```ts index.ts theme={null}
import "dotenv/config"
import {
  type Address,
  createPublicClient,
  encodeFunctionData,
  http,
} from "viem"
import { privateKeyToAccount } from "viem/accounts"
import { soneiumMinato } from "viem/chains"
import {
  createSCSPaymasterClient,
  createSmartAccountClient,
  toStartaleSmartAccount,
} from "@startale-scs/aa-sdk"

const chain = soneiumMinato
const signer = privateKeyToAccount(process.env.OWNER_PRIVATE_KEY as `0x${string}`)

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

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

const smartAccountClient = createSmartAccountClient({
  account: await toStartaleSmartAccount({
    signer,
    chain,
    transport: http(),
    index: 0n,
  }),
  transport: http(process.env.BUNDLER_URL!),
  client: publicClient,
  paymaster: paymasterClient,
  paymasterContext: {
    paymasterId: process.env.PAYMASTER_ID!,
  },
})

console.log("smart account address:", smartAccountClient.account.address)
```

### What every import does

| Symbol                        | Source                 | Role                                                                                                                                                                                                               |
| ----------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `soneiumMinato`               | `viem/chains`          | Pre-baked viem `Chain` for Soneium Minato (chain id `1946`). Replace with `soneium` for mainnet.                                                                                                                   |
| `privateKeyToAccount`         | `viem/accounts`        | Turns a hex private key into a viem `LocalAccount` that the SDK accepts as a signer.                                                                                                                               |
| `createPublicClient` / `http` | `viem`                 | Builds a read-only RPC client and HTTP transport used by the smart account for nonce and code reads.                                                                                                               |
| `encodeFunctionData`          | `viem`                 | Encodes a function call to ABI-encoded `0x` calldata for the UserOperation `calls[].data` field. Used in step 3.                                                                                                   |
| `createSCSPaymasterClient`    | `@startale-scs/aa-sdk` | Connects to the SCS Paymaster RPC and exposes paymaster actions. Pass either a `paymasterUrl`, a viem `transport`, or a `chainId` + `apiKey`.                                                                      |
| `toStartaleSmartAccount`      | `@startale-scs/aa-sdk` | Builds the ERC-7579 smart account from the signer. The `index` parameter is a `bigint` salt that determines the counterfactual address; using a different `index` with the same signer yields a different account. |
| `createSmartAccountClient`    | `@startale-scs/aa-sdk` | Wraps the account with bundler, paymaster, and ERC-7579 module actions. The returned `StartaleAccountClient` is what you call `sendUserOperation` on.                                                              |

<Tip>
  `smartAccountClient.account.address` returns the **counterfactual** address of the smart account. The contract is not deployed until the first UserOperation lands; the bundler will deploy it for you on the first call. With sponsored gas you do not need to fund this address yourself, but if you ever skip the paymaster you must send some ETH there first.
</Tip>

## 3. Send a sponsored UserOperation

Append the following to `index.ts`. Replace the ABI fragment with the ABI of the function you want to call.

```ts index.ts (continued) theme={null}
const counterAbi = [
  { name: "count", type: "function", stateMutability: "nonpayable", inputs: [], outputs: [] },
] as const

const callData = encodeFunctionData({
  abi: counterAbi,
  functionName: "count",
})

const hash = await smartAccountClient.sendUserOperation({
  calls: [
    {
      to: process.env.COUNTER_CONTRACT_ADDRESS as Address,
      value: 0n,
      data: callData,
    },
  ],
})

const receipt = await smartAccountClient.waitForUserOperationReceipt({ hash })
console.log("UserOperation hash:", hash)
console.log("Receipt:", receipt)
```

### What is happening here

1. `encodeFunctionData` turns the `count()` selector into ABI-encoded calldata, exactly as if you were calling `publicClient.writeContract`.
2. `sendUserOperation` packs the call into a UserOperation, asks the paymaster to co-sign it, signs it with your signer, and submits it to the SCS Bundler.
3. `waitForUserOperationReceipt` polls the bundler until the UserOperation is mined and returns the inclusion receipt with the resolved transaction hash.

Run it:

```bash theme={null}
npx ts-node index.ts
```

You should see the smart account address, the UserOperation hash, and the receipt with `success: true`.

## Common adjustments

<AccordionGroup>
  <Accordion title="Sending more than one call atomically">
    Push more entries onto the `calls` array. They run atomically in the order you pass them; if any reverts, the whole UserOperation reverts.

    ```ts theme={null}
    await smartAccountClient.sendUserOperation({
      calls: [
        { to: tokenA, data: approveCalldata, value: 0n },
        { to: dex, data: swapCalldata, value: 0n },
      ],
    })
    ```
  </Accordion>

  <Accordion title="Paying gas in an ERC-20 token instead of sponsoring">
    Swap `paymasterId` for a `token` address from the [supported tokens table](/aa-sdk/resources/supported-networks).

    ```ts theme={null}
    paymasterContext: {
      token: "0xfF0CBFbA43a1Ce2B8d72B2f3121558BcBd4B03a6",
    }
    ```

    See the [ERC-20 paymaster tutorial](/aa-sdk/tutorials/erc20-payment) for the full quote-and-execute flow.
  </Accordion>

  <Accordion title="Skipping the paymaster entirely">
    Drop the `paymaster` and `paymasterContext` keys from `createSmartAccountClient`. You then need to fund the smart account address with ETH on Soneium Minato so that the EntryPoint can debit gas from the account itself.
  </Accordion>

  <Accordion title="Using a different signer (Privy, Dynamic, ethers, EIP-1193)">
    `toStartaleSmartAccount` accepts any of `LocalAccount`, viem `WalletClient`, `EthersWallet`, or an `EthereumProvider` (EIP-1193). See [Installation and setup](/aa-sdk/tutorials/installation) for the full signer matrix.
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Tutorials path" icon="graduation-cap" href="/aa-sdk/tutorials/overview">
    Move from a script to a real React app: signer setup, provider context, contract interactions, sessions, and recovery.
  </Card>

  <Card title="Smart account setup" icon="wallet" href="/aa-sdk/tutorials/smart-account-setup">
    Wire `toStartaleSmartAccount` into a React provider so the account survives component remounts.
  </Card>

  <Card title="Sponsored paymaster" icon="gas-pump" href="/aa-sdk/tutorials/sponsored-tx">
    Build a real sponsored flow with a managed or self-funded paymaster and gas policies.
  </Card>

  <Card title="EIP-7702 delegation" icon="cpu" href="/aa-sdk/eip7702">
    Reuse the same script against an existing EOA address by delegating it to the Startale account implementation.
  </Card>
</CardGroup>
