controller-presets

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.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "controller-presets" with this command: npx skills add cartridge-gg/docs/cartridge-gg-docs-controller-presets

Controller Presets

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.

Invocation

The user wants help creating or debugging a Controller preset. Use AskUserQuestion to gather information interactively, one round at a time.

Process

Phase 1: Basics

Ask:

  • Game/project name — used as the directory name in configs/<name>/ . Must be lowercase kebab-case (e.g. dope-wars , loot-survivor ).

  • Which networks? — SN_MAIN , SN_SEPOLIA , or both.

Explain:

  • Sepolia is paymastered by default — no paymaster setup needed for testnet.

  • Mainnet requires a Slot paymaster (see slot-paymaster skill) to sponsor transactions.

Phase 2: Origin Configuration

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.

Phase 3: Session Policies

Ask for the contract addresses and entrypoints the game calls. For each contract, collect:

  • Contract address (hex, checksummed)

  • Human-readable name and description

  • Methods with entrypoints

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:

  • Entrypoints must be snake_case and match the exact Cairo function name.

  • Chain IDs: use SN_MAIN (not SN_MAINNET ) and SN_SEPOLIA (not SN_TESTNET ).

  • Contract addresses differ between networks — confirm separate addresses for mainnet vs sepolia.

  • approve entrypoint triggers a CI warning — the validator flags it. If the user genuinely needs ERC20 approval, acknowledge the warning.

  • VRF: If the game uses Cartridge VRF, include the VRF provider contract (0x051Fea4450Da9D6aeE758BDEbA88B2f665bCbf549D2C61421AA724E9AC0Ced8F ) with request_random entrypoint. The keychain auto-labels VRF contracts with Cartridge branding.

Method options

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

Paymaster predicates

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.

Message signing policies

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" } } ] } }

Phase 4: Theme

Ask if they want a custom theme. Collect:

  • Name: display name for the game

  • Icon: SVG or PNG file (will be optimized to 16–256px)

  • Cover: PNG or JPG file (will be optimized to 768–1440px), optional

  • Primary color: hex color for accent/branding

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.

Phase 5: Apple App Site Association (AASA)

Ask if they have a native iOS app that uses passkeys.

If yes, collect:

  • Team ID: exactly 10 uppercase alphanumeric characters (from Apple Developer account)

  • Bundle ID: reverse DNS format (e.g. com.example.mygame )

The app ID is TEAMID.BUNDLEID . Validation rules:

{ "apple-app-site-association": { "webcredentials": { "apps": ["ABCDE12345.com.example.mygame"] } } }

If no iOS app, skip this section entirely (don't include the key).

Phase 6: Assemble and Validate

Assemble the complete config.json and present it to the user.

Run through validation checklist:

  • Origins have no protocol prefix

  • Chain IDs are SN_MAIN or SN_SEPOLIA (not SN_MAINNET /SN_TESTNET )

  • Contract addresses are different for each network

  • Entrypoints are snake_case matching Cairo function names

  • AASA app IDs match TEAMID.BUNDLEID format (if present)

  • Asset files (icon, cover) are referenced and will exist in the directory

  • No approve entrypoint unless intentional

Phase 7: Connector Integration

Show 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 policies
  • Preset has policies for current chain → uses preset policies (ignores inline)

  • Preset has no policies for current chain → falls back to inline policies

  • No preset → uses inline policies

Phase 8: PR Submission

Guide the user to submit a PR to cartridge-gg/presets:

Mainnet vs Sepolia Reference

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.

Debugging Common Issues

"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/&#x3C;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.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

controller-sessions

No summary provided by upstream source.

Repository SourceNeeds Review
General

controller-setup

No summary provided by upstream source.

Repository SourceNeeds Review
General

controller-react

No summary provided by upstream source.

Repository SourceNeeds Review
General

controller-signers

No summary provided by upstream source.

Repository SourceNeeds Review