UserOperation to a counter contract using nothing but Node.js and the Startale AA SDK.
The full SDK source is at
StartaleGroup/scs-aa-sdk. Every symbol referenced below comes from @startale-scs/aa-sdk or viem.Prerequisites
1
Node.js 18 or newer
Verify with
node -v. The SDK targets modern ES2022 output.2
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.
3
An SCS Portal API key and paymaster
Follow Portal setup to issue an API key, create a managed or self-funded paymaster, and copy the resulting
bundlerUrl, paymasterUrl, and paymasterId.4
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.1. Initialize the project
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..env file alongside package.json:
.env
2. Wire up the script
Createindex.ts:
index.ts
What every import does
3. Send a sponsored UserOperation
Append the following toindex.ts. Replace the ABI fragment with the ABI of the function you want to call.
index.ts (continued)
What is happening here
encodeFunctionDataturns thecount()selector into ABI-encoded calldata, exactly as if you were callingpublicClient.writeContract.sendUserOperationpacks the call into a UserOperation, asks the paymaster to co-sign it, signs it with your signer, and submits it to the SCS Bundler.waitForUserOperationReceiptpolls the bundler until the UserOperation is mined and returns the inclusion receipt with the resolved transaction hash.
success: true.
Common adjustments
Sending more than one call atomically
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.Paying gas in an ERC-20 token instead of sponsoring
Paying gas in an ERC-20 token instead of sponsoring
Swap See the ERC-20 paymaster tutorial for the full quote-and-execute flow.
paymasterId for a token address from the supported tokens table.Skipping the paymaster entirely
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.Using a different signer (Privy, Dynamic, ethers, EIP-1193)
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 for the full signer matrix.Next steps
Tutorials path
Move from a script to a real React app: signer setup, provider context, contract interactions, sessions, and recovery.
Smart account setup
Wire
toStartaleSmartAccount into a React provider so the account survives component remounts.Sponsored paymaster
Build a real sponsored flow with a managed or self-funded paymaster and gas policies.
EIP-7702 delegation
Reuse the same script against an existing EOA address by delegating it to the Startale account implementation.