controller-presets
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseController Presets
Controller 预设
Guide teams through creating a preset for the Cartridge Controller.
A preset is a committed to cartridge-gg/presets that configures origin verification, session policies, theming, paymaster behavior, and optional iOS passkey support.
config.json引导团队为Cartridge Controller创建预设。
预设是提交至cartridge-gg/presets仓库的文件,用于配置源站验证、会话策略、主题、支付管理器(paymaster)行为以及可选的iOS密钥支持。
config.jsonInvocation
调用方式
The user wants help creating or debugging a Controller preset.
Use to gather information interactively, one round at a time.
AskUserQuestion用户需要创建或调试Controller预设的帮助。
使用交互式收集信息,每次一轮。
AskUserQuestionProcess
流程
Phase 1: Basics
阶段1:基础信息
Ask:
- Game/project name — used as the directory name in . Must be lowercase kebab-case (e.g.
configs/<name>/,dope-wars).loot-survivor - Which networks? — ,
SN_MAIN, or both.SN_SEPOLIA
Explain:
- Sepolia is paymastered by default — no paymaster setup needed for testnet.
- Mainnet requires a Slot paymaster (see skill) to sponsor transactions.
slot-paymaster
询问:
- 游戏/项目名称 — 用作中的目录名。 必须为小写短横线格式(kebab-case),例如
configs/<name>/、dope-wars。loot-survivor - 使用哪些网络? — 、
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 field. Apply these rules:
origin| Rule | Correct | Wrong |
|---|---|---|
| No protocol prefix | | |
| Wildcard for subdomains | | — |
| Wildcard does NOT match base domain | | Assuming |
| Multiple origins use an array | | — |
| localhost is always allowed | Don't list it | Adding |
If they have a Capacitor mobile app, ask for the custom hostname and include it:
json
{
"origin": ["yourdomain.com", "my-custom-app"]
}This authorizes (iOS) and (Android).
The default is always allowed automatically.
capacitor://my-custom-apphttps://my-custom-appcapacitor://localhostIMPORTANT: If the user needs both and , they must list both explicitly.
example.com*.example.com询问游戏将部署的生产环境域名。
生成字段,需遵循以下规则:
origin| 规则 | 正确示例 | 错误示例 |
|---|---|---|
| 不包含协议前缀 | | |
| 通配符支持子域名 | | — |
| 通配符不匹配基础域名 | | 认为 |
| 多个源站使用数组 | | — |
| localhost始终被允许 | 无需列出 | 将 |
如果用户使用Capacitor移动应用,询问自定义主机名并将其加入配置:
json
{
"origin": ["yourdomain.com", "my-custom-app"]
}这将授权(iOS)和(Android)。
默认的会被自动允许。
capacitor://my-custom-apphttps://my-custom-appcapacitor://localhost重要提示:如果用户同时需要和,必须明确列出两者。
example.com*.example.comPhase 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 section. Example:
chainsjson
{
"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 (not
SN_MAIN) andSN_MAINNET(notSN_SEPOLIA).SN_TESTNET - Contract addresses differ between networks — confirm separate addresses for mainnet vs sepolia.
- entrypoint triggers a CI warning — the validator flags it. If the user genuinely needs ERC20 approval, acknowledge the warning.
approve - VRF: If the game uses Cartridge VRF, include the VRF provider contract () with
0x051Fea4450Da9D6aeE758BDEbA88B2f665bCbf549D2C61421AA724E9AC0Ced8Fentrypoint. The keychain auto-labels VRF contracts with Cartridge branding.request_random
询问游戏调用的合约地址和入口点。
针对每个合约,收集:
- 合约地址(十六进制,带校验和)
- 人类可读的名称和描述
- 带入口点的方法
构建部分。示例:
chainsjson
{
"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测试网的独立地址。
- 入口点会触发CI警告 — 验证器会标记该入口点。如果用户确实需要ERC20授权,需告知该警告的存在。
approve - VRF:如果游戏使用Cartridge VRF,需包含VRF提供商合约()及
0x051Fea4450Da9D6aeE758BDEbA88B2f665bCbf549D2C61421AA724E9AC0Ced8F入口点。密钥链会自动为VRF合约添加Cartridge品牌标识。request_random
Method options
方法选项
| Field | Default | Notes |
|---|---|---|
| | Set to |
| | Whether the method is pre-checked in the session approval UI |
| | If |
| — | Optional: conditional sponsorship based on contract state |
| 字段 | 默认值 | 说明 |
|---|---|---|
| | 设置为 |
| | 该方法是否在会话授权UI中默认勾选 |
| | 如果设为 |
| — | 可选:基于合约状态的条件式赞助 |
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 array alongside :
messagescontractsjson
{
"policies": {
"contracts": { ... },
"messages": [
{
"types": {
"StarknetDomain": [...],
"Message": [{ "name": "content", "type": "felt" }]
},
"primaryType": "Message",
"domain": {
"name": "MyGame",
"version": "1",
"chainId": "SN_MAIN",
"revision": "1"
}
}
]
}
}如果游戏使用链下签名消息(EIP-712格式的类型化数据),在旁添加数组:
contractsmessagesjson
{
"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 .
The build pipeline generates optimized WebP/PNG/JPG versions automatically — commit only the source files.
config.json询问用户是否需要自定义主题。收集:
- 名称:游戏的显示名称
- 图标: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" }
}
}资源文件需放在与相同的目录下。
构建流水线会自动生成优化后的WebP/PNG/JPG版本 — 只需提交源文件即可。
config.jsonPhase 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 . Validation rules:
TEAMID.BUNDLEID- 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 and present it to the user.
config.jsonRun through validation checklist:
- Origins have no protocol prefix
- Chain IDs are or
SN_MAIN(notSN_SEPOLIA/SN_MAINNET)SN_TESTNET - Contract addresses are different for each network
- Entrypoints are snake_case matching Cairo function names
- AASA app IDs match format (if present)
TEAMID.BUNDLEID - Asset files (icon, cover) are referenced and will exist in the directory
- No entrypoint unless intentional
approve
组装完整的并呈现给用户。
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:
- + policies → uses inline policies
shouldOverridePresetPolicies: true - 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
展示如何在应用中使用预设:
typescript
import Controller from "@cartridge/controller";
const controller = new Controller({
preset: "<preset-name>", // 与预设仓库中的目录名完全匹配
// 策略从预设加载 — 除非设置shouldOverridePresetPolicies: true,否则不要在此处传入policies
});解释策略优先级:
- + 内联策略 → 使用内联策略
shouldOverridePresetPolicies: true - 预设包含当前链的策略 → 使用预设策略(忽略内联)
- 预设无当前链的策略 → 回退到内联策略
- 无预设 → 使用内联策略
Phase 8: PR Submission
阶段8:提交PR
Guide the user to submit a PR to cartridge-gg/presets:
- Create
configs/<name>/config.json - Add asset files (icon, cover) to the same directory
- CI runs — fix any errors before merge
validate-configs.ts - After merge, configs are built and deployed to
https://static.cartridge.gg/presets/<name>/config.json
引导用户向cartridge-gg/presets提交PR:
- 创建
configs/<name>/config.json - 将资源文件(图标、封面)添加到同一目录
- CI会运行— 合并前修复所有错误
validate-configs.ts - 合并后,配置会被构建并部署到
https://static.cartridge.gg/presets/<name>/config.json
Mainnet vs Sepolia Reference
主网与Sepolia测试网对比参考
| Aspect | Sepolia | Mainnet |
|---|---|---|
| Paymaster | Free, automatic | Requires Slot paymaster with budget |
| Chain ID in config | | |
| 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.
| 维度 | Sepolia测试网 | 主网 |
|---|---|---|
| 支付管理器 | 免费自动赞助 | 需要Slot支付管理器及预算 |
| 配置中的链ID | | |
| 合约地址 | Sepolia部署版本 | 主网部署版本 |
| 适用场景 | 开发、测试 | 生产环境 |
团队通常会在单个预设中包含两个网络 — 为每个网络使用独立的合约地址。
Debugging Common Issues
常见问题调试
"Policies show as unverified"
→ Origin mismatch. Check that matches the domain your app is served from (without protocol). If using wildcards, remember does NOT match .
config.origin*.example.comexample.com"Preset policies not loading"
→ Check that the name in your Controller constructor matches the directory name in the presets repo exactly. The config is fetched from CDN at .
presethttps://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 /, not hex chain IDs.
SN_MAINSN_SEPOLIA"Paymaster not sponsoring on mainnet"
→ Sepolia is auto-sponsored. Mainnet requires creating a Slot paymaster, funding it with credits, and adding matching policies. See skill.
slot-paymaster"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 — is flagged as a security concern. If your game genuinely needs ERC20 approval, the warning is acceptable but will require reviewer acknowledgment.
approve"策略显示为未验证"
→ 源站不匹配。检查是否与应用部署的域名一致(无协议)。如果使用通配符,注意不匹配。
config.origin*.example.comexample.com"预设策略未加载"
→ 检查Controller构造函数中的名称是否与预设仓库中的目录名完全一致。配置从CDN地址获取。
presethttps://static.cartridge.gg/presets/<name>/config.json"当前链的策略错误"
→ 策略在运行时根据链ID选择。验证配置中的链ID是否与RPC返回的一致。使用/,而非十六进制链ID。
SN_MAINSN_SEPOLIA"主网支付管理器未赞助交易"
→ Sepolia测试网自动赞助。主网需要创建Slot支付管理器、为其充值信用额度并添加匹配的策略。详见技能。
slot-paymaster"AASA验证失败"
→ Team ID必须为恰好10位大写字母数字。Bundle ID必须为反向DNS格式。示例:。
ABCDE12345.com.example.app"CI警告入口点"
→ 这是有意为之 — 被标记为安全风险。如果游戏确实需要ERC20授权,该警告是可接受的,但需要审核人员确认。
approveapprove