Telegram
Controller Integration Flow
- Generate local Stark key pair and store private key in Telegram cloud storage
- Open session controller page with user's public key
- Controller registers session public key and returns account info
- Create controller session account on client
- Store account info in Telegram cloud storage
Setting up the Session Provider
1. Define your configuration:
// config.ts
export const RPC_URL = "https://api.cartridge.gg/x/starknet/mainnet";
// Define your session policies
export const SESSION_POLICIES = {
contracts: {
"0x70fc96f845e393c732a468b6b6b54d876bd1a29e41a026e8b13579bf98eec8f": {
methods: [
{
name: "attack",
entrypoint: "attack",
description: "Attack the beast",
},
{
name: "claim",
entrypoint: "claim",
description: "Claim your tokens",
},
],
},
},
};
export const REDIRECT_URI = "https://t.me/hitthingbot/hitthing";
2. Create the SessionProvider:
import { PropsWithChildren, createContext, useContext, useEffect, useState } from "react";
import { constants } from "starknet";
import SessionConnector from "@cartridge/connector/session";
import { StarknetConfig, jsonRpcProvider } from "@starknet-react/core";
import { useLaunchParams, cloudStorage } from "@telegram-apps/sdk-react";
import { RPC_URL, SESSION_POLICIES, REDIRECT_URI } from "./config";
const connector = new SessionConnector({
policies: SESSION_POLICIES,
rpc: RPC_URL,
chainId: constants.StarknetChainId.SN_MAINNET,
redirectUrl: REDIRECT_URI,
});
const provider = jsonRpcProvider({
rpc: () => ({ nodeUrl: RPC_URL }),
});
export function SessionProvider({ children }: PropsWithChildren) {
return (
<StarknetConfig
autoConnect
connectors={[connector]}
provider={provider}
>
{children}
</StarknetConfig>
);
}
3. Use the SessionProvider in your app:
// App.tsx
import { SessionProvider } from "./SessionProvider";
function App() {
return (
<SessionProvider>
<YourApp />
</SessionProvider>
);
}
4. Use the session in your components:
function GameComponent() {
const { isConnected, connect, disconnect } = useSession();
const handleConnect = async () => {
try {
await connect();
} catch (error) {
console.error("Failed to connect:", error);
}
};
return (
<div>
{!isConnected ? (
<button onClick={handleConnect}>Connect Wallet</button>
) : (
<button onClick={disconnect}>Disconnect</button>
)}
{isConnected && (
<div>
{/* Your game content */}
</div>
)}
</div>
);
}
5. Error Handling
function GameComponent() {
const { connect } = useSession();
const [error, setError] = useState<string>();
const handleConnect = async () => {
try {
await connect();
} catch (err) {
setError(err instanceof Error ? err.message : "Failed to connect");
}
};
return (
<div>
{error && <div className="error">{error}</div>}
<button onClick={handleConnect}>Connect</button>
</div>
);
}
See the full example here.
Next.js Configuration for WebAssembly
If you're using Next.js, you'll need to configure it to properly handle WebAssembly modules used by the SessionController and SessionConnector. Create or update your next.config.js
:
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
webpack: (config, { isServer, dev }) => {
// Enable WebAssembly
config.experiments = {
...config.experiments,
asyncWebAssembly: true,
topLevelAwait: true,
};
// Fix for WebAssembly in production builds
if (!dev && isServer) {
config.output.webassemblyModuleFilename = "chunks/[id].wasm";
config.plugins.push(new WasmChunksFixPlugin());
}
return config;
},
};
// Plugin to fix WASM chunk loading in production
class WasmChunksFixPlugin {
apply(compiler) {
compiler.hooks.thisCompilation.tap("WasmChunksFixPlugin", (compilation) => {
compilation.hooks.processAssets.tap(
{ name: "WasmChunksFixPlugin" },
(assets) =>
Object.entries(assets).forEach(([pathname, source]) => {
if (!pathname.match(/\.wasm$/)) return;
compilation.deleteAsset(pathname);
const name = pathname.split("/")[1];
const info = compilation.assetsInfo.get(pathname);
compilation.emitAsset(name, source, info);
}),
);
});
}
}
module.exports = nextConfig;