controller-presets

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Controller Presets

Controller 预设

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.
引导团队为Cartridge Controller创建预设。 预设是提交至cartridge-gg/presets仓库的
config.json
文件,用于配置源站验证、会话策略、主题、支付管理器(paymaster)行为以及可选的iOS密钥支持。

Invocation

调用方式

The user wants help creating or debugging a Controller preset. Use
AskUserQuestion
to gather information interactively, one round at a time.
用户需要创建或调试Controller预设的帮助。 使用
AskUserQuestion
交互式收集信息,每次一轮。

Process

流程

Phase 1: Basics

阶段1:基础信息

Ask:
  1. Game/project name — used as the directory name in
    configs/<name>/
    . Must be lowercase kebab-case (e.g.
    dope-wars
    ,
    loot-survivor
    ).
  2. 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.
询问:
  1. 游戏/项目名称 — 用作
    configs/<name>/
    中的目录名。 必须为小写短横线格式(kebab-case),例如
    dope-wars
    loot-survivor
  2. 使用哪些网络?
    SN_MAIN
    SN_SEPOLIA
    ,或两者都用。
说明:
  • Sepolia测试网默认由支付管理器赞助 — 测试网无需配置支付管理器。
  • 主网需要Slot支付管理器(详见
    slot-paymaster
    技能)来赞助交易。

Phase 2: Origin Configuration

阶段2:源站配置

Ask for the production domain(s) where the game will be hosted.
Generate the
origin
field. Apply these rules:
RuleCorrectWrong
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 allowedDon't list itAdding
"localhost"
to origins
If they have a Capacitor mobile app, ask for the custom hostname and include it:
json
{
  "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.
询问游戏将部署的生产环境域名。
生成
origin
字段,需遵循以下规则:
规则正确示例错误示例
不包含协议前缀
"game.example.com"
"https://game.example.com"
通配符支持子域名
"*.example.com"
通配符不匹配基础域名
*.example.com
匹配
app.example.com
但不匹配
example.com
认为
*.example.com
覆盖
example.com
多个源站使用数组
["example.com", "staging.example.com"]
localhost始终被允许无需列出
"localhost"
添加到源站列表
如果用户使用Capacitor移动应用,询问自定义主机名并将其加入配置:
json
{
  "origin": ["yourdomain.com", "my-custom-app"]
}
这将授权
capacitor://my-custom-app
(iOS)和
https://my-custom-app
(Android)。 默认的
capacitor://localhost
会被自动允许。
重要提示:如果用户同时需要
example.com
*.example.com
,必须明确列出两者。

Phase 3: Session Policies

阶段3:会话策略

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:
json
{
  "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.
询问游戏调用的合约地址和入口点。 针对每个合约,收集:
  • 合约地址(十六进制,带校验和)
  • 人类可读的名称和描述
  • 带入口点的方法
构建
chains
部分。示例:
json
{
  "chains": {
    "SN_MAIN": {
      "policies": {
        "contracts": {
          "0x123...abc": {
            "name": "Game World",
            "description": "主游戏合约",
            "methods": [
              {
                "name": "Move Player",
                "description": "移动到新位置",
                "entrypoint": "move_player"
              }
            ]
          }
        }
      }
    }
  }
}
核心规则:
  • 入口点必须为蛇形命名(snake_case),且与Cairo函数名完全一致。
  • 链ID:使用
    SN_MAIN
    (而非
    SN_MAINNET
    )和
    SN_SEPOLIA
    (而非
    SN_TESTNET
    )。
  • 合约地址在不同网络中不同 — 确认主网与Sepolia测试网的独立地址。
  • approve
    入口点会触发CI警告
    — 验证器会标记该入口点。如果用户确实需要ERC20授权,需告知该警告的存在。
  • VRF:如果游戏使用Cartridge VRF,需包含VRF提供商合约(
    0x051Fea4450Da9D6aeE758BDEbA88B2f665bCbf549D2C61421AA724E9AC0Ced8F
    )及
    request_random
    入口点。密钥链会自动为VRF合约添加Cartridge品牌标识。

Method options

方法选项

FieldDefaultNotes
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
字段默认值说明
isPaymastered
true
设置为
false
时,用户需自行支付该方法的Gas费
isEnabled
true
该方法是否在会话授权UI中默认勾选
isRequired
false
如果设为
true
,用户无法取消勾选该方法
predicate
可选:基于合约状态的条件式赞助

Paymaster predicates

支付管理器断言

For conditional sponsorship:
json
{
  "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.
条件式赞助示例:
json
{
  "entrypoint": "move_player",
  "is_paymastered": true,
  "predicate": {
    "address": "0x456...def",
    "entrypoint": "check_move_eligibility"
  }
}
断言合约会先被调用;只有当它返回true时,交易才会被赞助。

Message signing policies

消息签名策略

If the game uses off-chain signed messages (EIP-712 style typed data), add a
messages
array alongside
contracts
:
json
{
  "policies": {
    "contracts": { ... },
    "messages": [
      {
        "types": {
          "StarknetDomain": [...],
          "Message": [{ "name": "content", "type": "felt" }]
        },
        "primaryType": "Message",
        "domain": {
          "name": "MyGame",
          "version": "1",
          "chainId": "SN_MAIN",
          "revision": "1"
        }
      }
    ]
  }
}
如果游戏使用链下签名消息(EIP-712格式的类型化数据),在
contracts
旁添加
messages
数组:
json
{
  "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

阶段4:主题设置

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:
json
{
  "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.
询问用户是否需要自定义主题。收集:
  • 名称:游戏的显示名称
  • 图标:SVG或PNG文件(将被优化为16–256px)
  • 封面:PNG或JPG文件(将被优化为768–1440px),可选
  • 主色调:用于强调/品牌的十六进制颜色
封面支持明暗两种变体:
json
{
  "theme": {
    "name": "MyGame",
    "icon": "icon.svg",
    "cover": { "light": "cover-light.png", "dark": "cover-dark.png" },
    "colors": { "primary": "#F38332" }
  }
}
资源文件需放在与
config.json
相同的目录下。 构建流水线会自动生成优化后的WebP/PNG/JPG版本 — 只需提交源文件即可。

Phase 5: Apple App Site Association (AASA)

阶段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:
  • Pattern:
    /^[A-Z0-9]{10}\.[a-zA-Z0-9.-]+$/
  • Team ID must be exactly 10 characters
  • All AASA entries across all presets are aggregated into a single file served at
    https://x.cartridge.gg/.well-known/apple-app-site-association
  • The aggregated file must stay under 128 KB
json
{
  "apple-app-site-association": {
    "webcredentials": {
      "apps": ["ABCDE12345.com.example.mygame"]
    }
  }
}
If no iOS app, skip this section entirely (don't include the key).
询问用户是否有使用密钥的原生iOS应用。
如果有,收集:
  • Team ID:恰好10位大写字母数字(来自Apple开发者账户)
  • Bundle ID:反向DNS格式(例如
    com.example.mygame
应用ID格式为
TEAMID.BUNDLEID
。验证规则:
  • 格式:
    /^[A-Z0-9]{10}\.[a-zA-Z0-9.-]+$/
  • Team ID必须恰好10位
  • 所有预设中的AASA条目会被聚合到单个文件,托管在
    https://x.cartridge.gg/.well-known/apple-app-site-association
  • 聚合后的文件大小必须小于128 KB
json
{
  "apple-app-site-association": {
    "webcredentials": {
      "apps": ["ABCDE12345.com.example.mygame"]
    }
  }
}
如果没有iOS应用,完全跳过该部分(不要包含该键)。

Phase 6: Assemble and Validate

阶段6:组装与验证

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
组装完整的
config.json
并呈现给用户。
执行验证清单:
  • 源站无协议前缀
  • 链ID为
    SN_MAIN
    SN_SEPOLIA
    (而非
    SN_MAINNET
    /
    SN_TESTNET
  • 每个网络的合约地址不同
  • 入口点为蛇形命名(snake_case)且与Cairo函数名一致
  • AASA应用ID符合
    TEAMID.BUNDLEID
    格式(如果存在)
  • 资源文件(图标、封面)已被引用且将存在于目录中
  • 除非确实需要,否则无
    approve
    入口点

Phase 7: Connector Integration

阶段7:连接器集成

Show how to use the preset in their app:
typescript
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:
  1. shouldOverridePresetPolicies: true
    + policies → uses inline policies
  2. Preset has policies for current chain → uses preset policies (ignores inline)
  3. Preset has no policies for current chain → falls back to inline policies
  4. No preset → uses inline policies
展示如何在应用中使用预设:
typescript
import Controller from "@cartridge/controller";

const controller = new Controller({
  preset: "<preset-name>",  // 与预设仓库中的目录名完全匹配
  // 策略从预设加载 — 除非设置shouldOverridePresetPolicies: true,否则不要在此处传入policies
});
解释策略优先级:
  1. shouldOverridePresetPolicies: true
    + 内联策略 → 使用内联策略
  2. 预设包含当前链的策略 → 使用预设策略(忽略内联)
  3. 预设无当前链的策略 → 回退到内联策略
  4. 无预设 → 使用内联策略

Phase 8: PR Submission

阶段8:提交PR

Guide the user to submit a PR to cartridge-gg/presets:
  1. Create
    configs/<name>/config.json
  2. Add asset files (icon, cover) to the same directory
  3. CI runs
    validate-configs.ts
    — fix any errors before merge
  4. After merge, configs are built and deployed to
    https://static.cartridge.gg/presets/<name>/config.json
引导用户向cartridge-gg/presets提交PR:
  1. 创建
    configs/<name>/config.json
  2. 将资源文件(图标、封面)添加到同一目录
  3. CI会运行
    validate-configs.ts
    — 合并前修复所有错误
  4. 合并后,配置会被构建并部署到
    https://static.cartridge.gg/presets/<name>/config.json

Mainnet vs Sepolia Reference

主网与Sepolia测试网对比参考

AspectSepoliaMainnet
PaymasterFree, automaticRequires Slot paymaster with budget
Chain ID in config
SN_SEPOLIA
SN_MAIN
Contract addressesSepolia deployMainnet deploy
Recommended forDevelopment, testingProduction
Teams often include both chains in a single preset — use separate contract addresses for each.
维度Sepolia测试网主网
支付管理器免费自动赞助需要Slot支付管理器及预算
配置中的链ID
SN_SEPOLIA
SN_MAIN
合约地址Sepolia部署版本主网部署版本
适用场景开发、测试生产环境
团队通常会在单个预设中包含两个网络 — 为每个网络使用独立的合约地址。

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/<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.
"策略显示为未验证" → 源站不匹配。检查
config.origin
是否与应用部署的域名一致(无协议)。如果使用通配符,注意
*.example.com
不匹配
example.com
"预设策略未加载" → 检查Controller构造函数中的
preset
名称是否与预设仓库中的目录名完全一致。配置从CDN地址
https://static.cartridge.gg/presets/<name>/config.json
获取。
"当前链的策略错误" → 策略在运行时根据链ID选择。验证配置中的链ID是否与RPC返回的一致。使用
SN_MAIN
/
SN_SEPOLIA
,而非十六进制链ID。
"主网支付管理器未赞助交易" → Sepolia测试网自动赞助。主网需要创建Slot支付管理器、为其充值信用额度并添加匹配的策略。详见
slot-paymaster
技能。
"AASA验证失败" → Team ID必须为恰好10位大写字母数字。Bundle ID必须为反向DNS格式。示例:
ABCDE12345.com.example.app
"CI警告
approve
入口点"
→ 这是有意为之 —
approve
被标记为安全风险。如果游戏确实需要ERC20授权,该警告是可接受的,但需要审核人员确认。