.agents/skills/controller-presets/SKILL.md
Interactively walk teams through creating a Cartridge Controller preset, including origin/AASA setup, session policies, themes, and mainnet vs sepolia configuration.
npx skillsauth add cartridge-gg/docs controller-presetsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Guide teams through creating a preset for the Cartridge Controller.
A preset is a config.json committed to cartridge-gg/presets that configures origin verification, session policies, theming, paymaster behavior, and optional iOS passkey support.
The user wants help creating or debugging a Controller preset.
Use AskUserQuestion to gather information interactively, one round at a time.
Ask:
configs/<name>/.
Must be lowercase kebab-case (e.g. dope-wars, loot-survivor).SN_MAIN, SN_SEPOLIA, or both.Explain:
slot-paymaster skill) to sponsor transactions.Ask for the production domain(s) where the game will be hosted.
Generate the origin field. Apply these rules:
| Rule | Correct | Wrong |
|------|---------|-------|
| No protocol prefix | "game.example.com" | "https://game.example.com" |
| Wildcard for subdomains | "*.example.com" | — |
| Wildcard does NOT match base domain | *.example.com matches app.example.com but NOT example.com | Assuming *.example.com covers example.com |
| Multiple origins use an array | ["example.com", "staging.example.com"] | — |
| localhost is always allowed | Don't list it | Adding "localhost" to origins |
If they have a Capacitor mobile app, ask for the custom hostname and include it:
{
"origin": ["yourdomain.com", "my-custom-app"]
}
This authorizes capacitor://my-custom-app (iOS) and https://my-custom-app (Android).
The default capacitor://localhost is always allowed automatically.
IMPORTANT: If the user needs both example.com and *.example.com, they must list both explicitly.
Ask for the contract addresses and entrypoints the game calls. For each contract, collect:
Build the chains section. Example:
{
"chains": {
"SN_MAIN": {
"policies": {
"contracts": {
"0x123...abc": {
"name": "Game World",
"description": "Main game contract",
"methods": [
{
"name": "Move Player",
"description": "Move to a new position",
"entrypoint": "move_player"
}
]
}
}
}
}
}
}
Key rules:
SN_MAIN (not SN_MAINNET) and SN_SEPOLIA (not SN_TESTNET).approve entrypoint triggers a CI warning — the validator flags it. If the user genuinely needs ERC20 approval, acknowledge the warning.0x051Fea4450Da9D6aeE758BDEbA88B2f665bCbf549D2C61421AA724E9AC0Ced8F) with request_random entrypoint. The keychain auto-labels VRF contracts with Cartridge branding.| Field | Default | Notes |
|-------|---------|-------|
| isPaymastered | true | Set to false to require users to pay their own gas for this method |
| isEnabled | true | Whether the method is pre-checked in the session approval UI |
| isRequired | false | If true, user cannot uncheck this method |
| predicate | — | Optional: conditional sponsorship based on contract state |
For conditional sponsorship:
{
"entrypoint": "move_player",
"is_paymastered": true,
"predicate": {
"address": "0x456...def",
"entrypoint": "check_move_eligibility"
}
}
The predicate contract is called first; the transaction is only sponsored if it returns true.
If the game uses off-chain signed messages (EIP-712 style typed data), add a messages array alongside contracts:
{
"policies": {
"contracts": { ... },
"messages": [
{
"types": {
"StarknetDomain": [...],
"Message": [{ "name": "content", "type": "felt" }]
},
"primaryType": "Message",
"domain": {
"name": "MyGame",
"version": "1",
"chainId": "SN_MAIN",
"revision": "1"
}
}
]
}
}
Ask if they want a custom theme. Collect:
Cover supports light/dark variants:
{
"theme": {
"name": "MyGame",
"icon": "icon.svg",
"cover": { "light": "cover-light.png", "dark": "cover-dark.png" },
"colors": { "primary": "#F38332" }
}
}
Asset files go in the same directory as config.json.
The build pipeline generates optimized WebP/PNG/JPG versions automatically — commit only the source files.
Ask if they have a native iOS app that uses passkeys.
If yes, collect:
com.example.mygame)The app ID is TEAMID.BUNDLEID. Validation rules:
/^[A-Z0-9]{10}\.[a-zA-Z0-9.-]+$/https://x.cartridge.gg/.well-known/apple-app-site-association{
"apple-app-site-association": {
"webcredentials": {
"apps": ["ABCDE12345.com.example.mygame"]
}
}
}
If no iOS app, skip this section entirely (don't include the key).
Assemble the complete config.json and present it to the user.
Run through validation checklist:
SN_MAIN or SN_SEPOLIA (not SN_MAINNET/SN_TESTNET)TEAMID.BUNDLEID format (if present)approve entrypoint unless intentionalShow how to use the preset in their app:
import Controller from "@cartridge/controller";
const controller = new Controller({
preset: "<preset-name>", // matches the directory name in configs/
// Policies are loaded from the preset — do NOT also pass policies here
// unless you set shouldOverridePresetPolicies: true
});
Explain policy precedence:
shouldOverridePresetPolicies: true + policies → uses inline policiesGuide the user to submit a PR to cartridge-gg/presets:
configs/<name>/config.jsonvalidate-configs.ts — fix any errors before mergehttps://static.cartridge.gg/presets/<name>/config.json| Aspect | Sepolia | Mainnet |
|--------|---------|---------|
| Paymaster | Free, automatic | Requires Slot paymaster with budget |
| Chain ID in config | SN_SEPOLIA | SN_MAIN |
| Contract addresses | Sepolia deploy | Mainnet deploy |
| Recommended for | Development, testing | Production |
Teams often include both chains in a single preset — use separate contract addresses for each.
"Policies show as unverified"
→ Origin mismatch. Check that config.origin matches the domain your app is served from (without protocol). If using wildcards, remember *.example.com does NOT match example.com.
"Preset policies not loading"
→ Check that the preset name in your Controller constructor matches the directory name in the presets repo exactly. The config is fetched from CDN at https://static.cartridge.gg/presets/<name>/config.json.
"Wrong policies for my chain"
→ Policies are selected by chain ID at runtime. Verify the chain ID in your config matches what your RPC returns. Use SN_MAIN/SN_SEPOLIA, not hex chain IDs.
"Paymaster not sponsoring on mainnet"
→ Sepolia is auto-sponsored. Mainnet requires creating a Slot paymaster, funding it with credits, and adding matching policies. See slot-paymaster skill.
"AASA validation failing"
→ Team ID must be exactly 10 uppercase alphanumeric chars. Bundle ID must be reverse DNS. Pattern: ABCDE12345.com.example.app.
"CI warns about approve entrypoint"
→ This is intentional — approve is flagged as a security concern. If your game genuinely needs ERC20 approval, the warning is acceptable but will require reviewer acknowledgment.
development
Configure authentication methods for Cartridge Controller including passkeys, social login, and external wallets. Use when implementing user authentication, adding multiple signers for account recovery, customizing signup options, or integrating external wallets like MetaMask or Phantom. Covers WebAuthn passkeys, Google/Discord/Twitter OAuth, wallet connections, and dynamic authentication flows.
data-ai
Integrate Cartridge Controller wallet into Starknet applications. Use when setting up Controller for the first time, installing packages, configuring chains/RPC endpoints, or troubleshooting basic integration issues. Covers installation, Controller instantiation, ControllerConnector vs SessionConnector choice, chain configuration, and package compatibility.
testing
Configure session keys and policies for Cartridge Controller to enable gasless, pre-approved transactions. Use when defining contract interaction policies, setting spending limits, configuring signed message policies, or implementing error handling for session-based transactions. Covers SessionPolicies type, policy definitions, verified sessions, and error display modes.
development
Integrate Cartridge Controller into React applications using starknet-react. Use when building React/Next.js web apps with Controller, setting up StarknetConfig provider, using hooks like useConnect/useAccount, or implementing wallet connection components. Covers ControllerConnector setup, provider configuration, and transaction execution patterns.