Instructions for LLMs to install and use the Cartridge Controller CLI for executing Starknet transactions.
curl -fsSL https://raw.githubusercontent.com/cartridge-gg/controller-cli/main/install.sh | bashIf the installation directory is not in PATH, add it:
export PATH="$PATH:$HOME/.local/bin"Verify:
controller --versionThe skill provides structured tools with automatic JSON parsing and better error handling.
git clone https://github.com/cartridge-gg/controller-cli.git /tmp/controller-cli && \
mkdir -p ~/.claude/skills && \
ln -sf /tmp/controller-cli/.claude/skills/controller-skill ~/.claude/skills/controller-skillOnce installed, tools become available:
controller_session_auth- Generate keypair and authorize a new sessioncontroller_session_status- Check session statuscontroller_session_list- List active sessionscontroller_session_clear- Clear session datacontroller_execute- Execute transactionscontroller_call- Read-only contract callscontroller_transaction- Get transaction statuscontroller_receipt- Get transaction receiptcontroller_balance- Check token balancescontroller_username- Get account usernamecontroller_lookup- Look up usernames/addressescontroller_config- Manage CLI configuration
See: Skill Documentation
controller session status --jsonStatus states:
no_session- No keypair existskeypair_only- Keypair exists but no registered sessionactive- Session registered and not expired
Active session output:
{
"status": "active",
"session": {
"address": "0x...",
"chain_id": "SN_SEPOLIA",
"expires_at": 1735689600,
"expires_in_seconds": 3600,
"expires_at_formatted": "2025-01-01 00:00:00 UTC",
"is_expired": false
},
"keypair": {
"public_key": "0x...",
"has_private_key": true
}
}Requirements: Human user must authorize via browser. Specify either a preset or a local policy file, plus a network.
The session auth command combines keypair generation and session registration in a single step.
For popular games/apps, use a preset from cartridge-gg/presets:
controller session auth \
--preset loot-survivor \
--chain-id SN_MAIN \
--jsonAvailable presets: loot-survivor, influence, realms, pistols, dope-wars, and more.
Create policy.json:
{
"contracts": {
"0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7": {
"name": "STRK Token",
"methods": [
{
"name": "transfer",
"entrypoint": "transfer",
"description": "Transfer STRK tokens"
}
]
}
}
}controller session auth \
--file policy.json \
--rpc-url https://api.cartridge.gg/x/starknet/sepolia \
--jsonJSON output:
{
"authorization_url": "https://x.cartridge.gg/session?public_key=0x...&policies=...",
"short_url": "https://api.cartridge.gg/s/abc123",
"public_key": "0x...",
"message": "Open this URL in your browser to authorize the session. Waiting for authorization..."
}Important:
- Display the
short_url(if present) to the user, otherwise fall back toauthorization_url - Ask them to open it in their browser and authorize
- The command waits automatically and stores the session when authorized (up to 6 minutes)
The session auth command blocks for up to 6 minutes while waiting for the user to authorize in the browser. To avoid blocking your main thread, run it as a background process:
- Start
session authin the background - Capture and display the
short_urlto the user immediately (fall back toauthorization_urlif unavailable) - Poll the process for completion
- Once it exits successfully, verify with
controller session status --json
This keeps the agent responsive to other user requests while waiting for authorization.
Single call (positional args: contract, entrypoint, calldata):
controller execute \
0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 \
transfer \
0xRECIPIENT_ADDRESS,u256:1000000000000000000 \
--jsonMultiple calls from file (calls.json):
{
"calls": [
{
"contractAddress": "0x049d36...",
"entrypoint": "approve",
"calldata": ["0xSPENDER", "0xFFFFFFFF", "0xFFFFFFFF"]
},
{
"contractAddress": "0x123abc...",
"entrypoint": "swap",
"calldata": ["0x100", "0x0", "0x1"]
}
]
}controller execute \
--file calls.json \
--jsonOutput:
{
"transaction_hash": "0x...",
"message": "Transaction submitted successfully"
}Transaction Explorer Links: Always use Voyager:
- Mainnet:
https://voyager.online/tx/0x... - Sepolia:
https://sepolia.voyager.online/tx/0x...
Execute a read-only call to query contract state without submitting a transaction.
Single call (positional args: contract, entrypoint, calldata):
controller call \
0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 \
balance_of \
0xADDRESS \
--chain-id SN_SEPOLIA \
--jsonQuery at a specific block:
controller call \
0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 \
balance_of \
0xADDRESS \
--chain-id SN_SEPOLIA \
--block-id latest \
--jsonMultiple calls from file:
controller call --file calls.json --chain-id SN_SEPOLIA --jsonNote: call does not require an active session. It only needs a network (via --chain-id or --rpc-url).
Check the status and details of a submitted transaction.
controller transaction 0xTRANSACTION_HASH \
--chain-id SN_SEPOLIA \
--jsonWait for confirmation:
controller transaction 0xTRANSACTION_HASH \
--chain-id SN_SEPOLIA \
--wait \
--timeout 300 \
--jsonGet the full transaction receipt including execution status, fee, events, and messages.
controller receipt 0xTRANSACTION_HASH \
--chain-id SN_SEPOLIA \
--jsonWait for receipt to be available:
controller receipt 0xTRANSACTION_HASH \
--chain-id SN_SEPOLIA \
--wait \
--timeout 300 \
--jsonQuery ERC20 token balances for the active session account.
# All non-zero balances
controller balance --json
# Specific token
controller balance eth --json
# Query on mainnet
controller balance --chain-id SN_MAIN --jsonBuilt-in tokens: ETH, STRK, USDC, USD.e, LORDS, SURVIVOR, WBTC. Add custom tokens:
controller config set token.MYTOKEN 0x123...Output:
[
{ "token": "ETH", "balance": "0.500000", "raw": "0x6f05b59d3b20000", "contract": "0x049d36..." },
{ "token": "STRK", "balance": "100.000000", "raw": "0x56bc75e2d63100000", "contract": "0x04718f..." }
]Display the Cartridge username for the active session account.
controller username --jsonResolve Cartridge controller usernames to addresses or vice versa:
# Look up addresses for usernames
controller lookup --usernames shinobi,sensei --json# Look up usernames for addresses
controller lookup --addresses 0x123...,0x456... --jsonOutput:
{
"status": "success",
"data": [
"shinobi:0x123...",
"sensei:0x456..."
]
}Each entry is a username:address pair. You can combine both flags in a single call. See the Cartridge Usernames API for limits and rate-limiting details.
List active sessions:
controller session list --json
controller session list --limit 20 --page 2 --jsonClear all session data:
controller session clear --yesManage CLI settings without editing the config file directly.
# Set a value
controller config set rpc-url https://api.cartridge.gg/x/starknet/mainnet
# Get a value
controller config get rpc-url --json
# List all values
controller config list --jsonValid keys: rpc-url, keychain-url, api-url, storage-path, json-output, colors, callback-timeout, token.<symbol>.
Calldata values support multiple formats:
| Format | Example | Description |
|---|---|---|
| Hex | 0x64 |
Standard hex felt |
| Decimal | 100 |
Decimal felt (auto-converted) |
u256: |
u256:1000000000000000000 |
Auto-splits into low/high 128-bit felts |
str: |
str:hello |
Cairo short string encoding |
The u256: prefix is the recommended way to specify token amounts. It eliminates manual low/high splitting:
# Using u256: prefix (recommended)
controller execute 0x04718f... transfer 0xRECIPIENT,u256:1000000000000000000 --json
# Equivalent manual split
controller execute 0x04718f... transfer 0xRECIPIENT,0xDE0B6B3A7640000,0x0 --jsonAlways be explicit about network. Never rely on defaults.
| Chain ID | RPC URL | Usage |
|---|---|---|
SN_MAIN |
https://api.cartridge.gg/x/starknet/mainnet |
Starknet Mainnet |
SN_SEPOLIA |
https://api.cartridge.gg/x/starknet/sepolia |
Starknet Sepolia |
For SLOT or custom chains, use --rpc-url with your Katana endpoint.
- Session auth: Use
--chain-id SN_MAINor--chain-id SN_SEPOLIA(simplest) - Execute/call/transaction: Use
--chain-idor--rpc-url(explicit)
- Run
controller session status --jsonto check the current session'schain_id - Use the same network, or ask the user
--chain-idor--rpc-urlflag (highest)- Explicit config/env (
config set rpc-urlorCARTRIDGE_RPC_URL) - Stored session RPC URL (from authorization)
- Default (SN_SEPOLIA)
By default, transactions use the paymaster (free execution). If the paymaster is unavailable, the transaction fails rather than falling back to user-funded execution.
Use --no-paymaster to bypass the paymaster and pay with user funds:
controller execute \
0x... \
transfer \
0x... \
--no-paymaster \
--json| Scenario | Flag | Behavior |
|---|---|---|
| Default | None | Free via paymaster, fails if unavailable |
| Urgent / self-pay | --no-paymaster |
User pays fees directly |
All errors return JSON:
{
"status": "error",
"error_code": "ErrorType",
"message": "Human-readable description",
"recovery_hint": "Suggested action"
}| Error Code | Cause | Recovery |
|---|---|---|
NoSession |
No keypair found | Run controller session auth --file policy.json --json |
SessionExpired |
Session past expiry | Run controller session auth again |
ManualExecutionRequired |
No authorized session for this transaction | Authorize session with appropriate policies |
CallbackTimeout |
User didn't authorize within 360s | Retry session auth, ask user to authorize faster |
InvalidInput (UnsupportedChainId) |
Bad chain ID | Use SN_MAIN or SN_SEPOLIA, or --rpc-url for custom chains |
InvalidInput (PresetNotFound) |
Unknown preset name | Check available presets |
InvalidInput (PresetChainNotSupported) |
Preset doesn't support requested chain | Use a supported chain or create a custom policy file |
The lookup + execute commands combine to enable natural-language workflows. An LLM can resolve usernames to addresses transparently, then build the right transaction.
"Send 1 STRK to broody"
controller lookup --usernames broody --json→ resolves tobroody:0xABC...controller execute 0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d transfer 0xABC...,u256:1000000000000000000 --json
"Attack loaf's realm at grid 1,2"
controller lookup --usernames loaf --json→ resolves toloaf:0xDEF...controller execute 0xGAME_CONTRACT attack 0xDEF...,0x1,0x2 --json
"Who is 0x123...?"
controller lookup --addresses 0x123... --json→ resolves toshinobi:0x123...
"How much ETH do I have?"
controller balance eth --json→ returns balance with formatted and raw values
- Always use
--jsonflag for machine-readable output - Always be explicit about network - use
--chain-idor--rpc-url - Check session status before executing to verify session exists and isn't expired
- Prefer presets for known games/apps - they're maintained by project teams
- Display authorization URLs clearly and explain the human authorization step
- Handle errors by checking
error_codeand followingrecovery_hint - Validate addresses (must be hex with 0x prefix)
- Always use Voyager for transaction links, never Starkscan
- Use
u256:prefix for token amounts instead of manual low/high splitting - Use
balancecommand instead of rawcall balance_offor token balance queries
- Private keys stored locally in
~/.config/controller-cli/with restricted permissions - Sessions are scoped: only authorized contracts, methods, and time window
- Human browser authorization required for all sessions (cannot be automated)
- Expired sessions are automatically rejected