controller-native
Controller Native Integration
Integrate Controller into native and mobile applications.
Choosing an Approach
| Approach | Use When | Platforms |
|---|---|---|
| Native Bindings (Controller.c) | Performance-critical, native apps | iOS, Android, React Native, C/C++ |
| Web Wrapper (Capacitor) | Existing web app → app store | iOS, Android |
Key Concepts
- SessionConnector: Web/Capacitor apps with browser-based auth
- SessionAccount: Native apps using Controller.c FFI bindings
- ControllerAccount: Headless/server-side with custom signing keys
SessionConnector (Web/Capacitor)
For web apps wrapped with Capacitor:
import { SessionConnector } from "@cartridge/connector";
import { constants } from "starknet";
const policies = {
contracts: {
"0x1234...": {
methods: [{ name: "play", entrypoint: "play" }],
},
},
};
const connector = new SessionConnector({
policies,
rpc: "https://api.cartridge.gg/x/starknet/mainnet",
chainId: constants.StarknetChainId.SN_MAIN,
redirectUrl: "myapp://auth-callback",
disconnectRedirectUrl: "myapp://logout",
});
Authentication Flow
- App generates local keypair
- User authenticates in browser (deep link)
- Browser redirects back with session credentials
- App signs transactions locally
React Native
Prerequisites: Node.js >= 20
Uses TurboModules with Controller.c bindings. The native module is generated locally:
pnpm install
pnpm exec expo prebuild
import { SessionAccount, type SessionPolicy, type Call } from "./modules/controller/src";
// Create session from subscription flow
const session = SessionAccount.createFromSubscribe(
privateKey,
sessionPolicies,
"https://api.cartridge.gg/x/starknet/mainnet",
"https://api.cartridge.gg"
);
// Access session metadata
const address = session.address();
const username = session.username();
// Execute transactions
const calls: Call[] = [
{
contractAddress: "0x...",
entrypoint: "transfer",
calldata: ["0x...", "0x100", "0x0"],
},
];
const txHash = session.executeFromOutside(calls);
iOS (Swift)
Prerequisites: Xcode 15+
Uses UniFFI-generated Swift bindings.
import ControllerAccount
// Create owner from private key
let owner = try Owner.init(privateKey: "0x...")
// Create headless controller
let controller = try ControllerAccount.newHeadless(
appId: "my_app",
username: "player",
classHash: ControllerAccount.getControllerClassHash(.latest),
rpcUrl: "https://api.cartridge.gg/x/starknet/mainnet",
owner: owner,
chainId: "0x534e5f4d41494e"
)
// Execute transaction
let call = Call(
contractAddress: "0x...",
entrypoint: "transfer",
calldata: ["0x...", "0x100", "0x0"]
)
do {
let txHash = try await controller.execute([call])
print("Transaction: \(txHash)")
} catch let error as ControllerError {
print("Error: \(error.message)")
}
Android (Kotlin)
Prerequisites: Rust toolchain for building native libraries
Uses UniFFI-generated Kotlin bindings.
import uniffi.controller.*
val owner = ownerInit("0x...")
val controller = controllerNewHeadless(
appId = "my_app",
username = "player",
classHash = getControllerClassHash(Version.LATEST),
rpcUrl = "https://api.cartridge.gg/x/starknet/mainnet",
owner = owner,
chainId = "0x534e5f4d41494e"
)
val call = Call(
contractAddress = "0x...",
entrypoint = "transfer",
calldata = listOf("0x...", "0x100", "0x0")
)
try {
val txHash = controller.execute(listOf(call))
println("Transaction: $txHash")
} catch (e: ControllerException) {
println("Error: ${e.message}")
}
Capacitor (Web Wrapper)
iOS Configuration
// capacitor.config.ts
export default {
appId: "com.mygame.app",
plugins: {
App: {
launchUrl: "myapp://",
},
},
};
Android Configuration
Add to AndroidManifest.xml:
<activity
android:name=".MainActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" />
</intent-filter>
</activity>
Handle Deep Links
import { App } from "@capacitor/app";
App.addListener("appUrlOpen", (event) => {
if (event.url.includes("auth-callback")) {
sessionConnector.handleCallback(event.url);
}
});
Note: WebAuthn has limited support on Android webviews.
Passkey Configuration (AASA)
To enable passkey sign-in on native apps, configure Apple App Site Association in your preset.
Add to @cartridge/presets:
{
"apple-app-site-association": {
"webcredentials": {
"apps": ["TEAMID.com.mygame.app"]
}
}
}
Replace TEAMID with your Apple Developer Team ID.
Platform Notes
- OAuth limitation: Social login may not work in webviews. Prefer passkeys for native.
- EVM wallets: Desktop only, auto-hidden on mobile browsers.
- Passkeys: Require AASA configuration for native iOS apps.
- Android WebAuthn: Limited support in Capacitor webviews.
Security
Warning: Never commit private keys. Use environment variables, secure storage APIs, or secret managers.
More from cartridge-gg/docs
controller-setup
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.
69controller-sessions
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.
68controller-react
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.
67controller-signers
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.
65controller-backend
Integrate Cartridge Controller into backend services using Node.js, Rust, or headless mode. Use when building server-side applications, game backends, automated bots, or any non-browser environment that needs to execute Starknet transactions. Covers SessionProvider for Node.js, Rust SDK setup, and headless Controller with custom signing keys.
61slot-vrng
Integrate Cartridge's verifiable random number generator (vRNG) into onchain games.
59