Loading...
Loading...
Integrate Cartridge Controller into native mobile applications (iOS, Android, React Native) and web wrappers (Capacitor). Use when building mobile games or apps that need Controller wallet functionality with local keypair signing. Covers SessionConnector for native auth flows, Controller.c FFI bindings, passkey configuration with Apple App Site Association, and platform-specific setup.
npx skill4agent add cartridge-gg/docs controller-native| 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 |
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",
});pnpm install
pnpm exec expo prebuildimport { 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);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)")
}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.config.ts
export default {
appId: "com.mygame.app",
plugins: {
App: {
launchUrl: "myapp://",
},
},
};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>import { App } from "@capacitor/app";
App.addListener("appUrlOpen", (event) => {
if (event.url.includes("auth-callback")) {
sessionConnector.handleCallback(event.url);
}
});@cartridge/presets{
"apple-app-site-association": {
"webcredentials": {
"apps": ["TEAMID.com.mygame.app"]
}
}
}TEAMIDWarning: Never commit private keys. Use environment variables, secure storage APIs, or secret managers.