Skip to main content

Mobile SDK

The Kryptos Connect Mobile SDK provides React Native components for iOS and Android applications. It works with both Expo and React Native CLI.

Before you begin

Make sure you have completed the prerequisites in the overview and set up your backend server.

Installation​

npm install @kryptos_connect/mobile-sdk react-native-webview

iOS (React Native CLI only):

cd ios && pod install

Prerequisites​

Quick Start​

import { KryptosConnect, KryptosConnectButton } from "@kryptos_connect/mobile-sdk";

// 1. Initialize once (or on every render to keep config in sync)
KryptosConnect.init({
clientId: "your-client-id",
appName: "My App",
appLogo: "https://yourapp.com/logo.png",
theme: "light", // "light" | "dark" | "auto"
language: "en",
authMethods: ["email", "anonymous"],
});

// 2. Drop in the button
<KryptosConnectButton
generateLinkToken={generateLinkToken}
onConnectSuccess={(consent) => console.log(consent.public_token)}
onConnectError={(err) => console.error(err)}
buttonLabel="Connect Kryptos"
buttonHeight={52}
/>;

Full Example​

Never bundle the client secret into a mobile app

/link-token and /token/exchange authenticate with your client secret. A shipped app binary can be extracted, so a secret compiled into it is a published secret — and it can be used to mint link tokens against your client. Both calls belong on your server; the app calls endpoints you own. The example below follows that shape — see Backend Integration for the server side.

import { KryptosConnect, KryptosConnectButton } from "@kryptos_connect/mobile-sdk";
import { useEffect, useState } from "react";

const API_URL = "https://your-api.com";
const CLIENT_ID = "your-client-id"; // public — safe to ship

export default function App() {
const [accessToken, setAccessToken] = useState(null);

useEffect(() => {
KryptosConnect.init({
clientId: CLIENT_ID,
appName: "My App",
theme: "light",
language: "en",
authMethods: ["email", "anonymous"],
});
}, []);

// Your backend holds the client secret, calls POST /link-token, and returns
// { link_token, isAuthorized }.
async function generateLinkToken(existingAccessToken?: string | null) {
const res = await fetch(`${API_URL}/api/kryptos/link-token`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ access_token: existingAccessToken ?? undefined }),
});
return res.json(); // { link_token, isAuthorized }
}

async function handleSuccess(consent) {
if (!consent) return; // returning user — consent was skipped, nothing to exchange

// Your backend calls POST /token/exchange and stores the access token.
const res = await fetch(`${API_URL}/api/kryptos/exchange`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ public_token: consent.public_token }),
});
const data = await res.json();
setAccessToken(data.access_token);
}

return (
<>
{/* Default button */}
<KryptosConnectButton
generateLinkToken={() => generateLinkToken()}
onConnectSuccess={handleSuccess}
onConnectError={(err) => console.error(err)}
buttonLabel="Link Kryptos Account"
buttonHeight={52}
/>

{/* Pre-select a specific integration with custom style */}
<KryptosConnectButton
generateLinkToken={() => generateLinkToken()}
onConnectSuccess={handleSuccess}
onConnectError={(err) => console.error(err)}
integrationName="coinbase"
buttonLabel="Connect Coinbase"
buttonHeight={48}
style={{ borderRadius: 10, backgroundColor: "#0052FF" }}
/>

{/* Re-authorize with stored access token */}
{accessToken && (
<KryptosConnectButton
generateLinkToken={() => generateLinkToken(accessToken)}
onConnectSuccess={handleSuccess}
onConnectError={(err) => console.error(err)}
buttonLabel="Continue with Access Token"
buttonHeight={52}
/>
)}
</>
);
}

User Flow Variations​

The SDK handles two flows based on the isAuthorized flag returned from generateLinkToken:

Flow 1: New User (isAuthorized: false or undefined)​

press → AUTH → INTEGRATION → onConnectSuccess({ public_token })

Exchange public_token server-side for a long-lived access_token.

Flow 2: Returning User (isAuthorized: true)​

press → INTEGRATION → onConnectSuccess(null)

Pass stored access_token in the link-token request body and return isAuthorized: true. No new token is issued.

KryptosConnect.init Config​

KeyTypeRequiredDescription
clientIdstringYesYour Kryptos client ID.
appNamestringYesDisplayed in the connect UI header.
appLogostringNoURI to your app logo shown in the connect UI.
walletConnectProjectIdstringNoRequired if using WalletConnect.
theme"light" | "dark" | "auto"NoUI theme. Default "light".
languagestringNoUI language. Supported: en fr de pt sv es pl it.
authMethods("email" | "anonymous")[]NoAuth methods shown. Default: both.
detectUsedChainsbooleanNoDetect which chains an EVM address is active on. Default true.
autoSelectAllChainsbooleanNoPre-select every detected chain. Default true.
cssVarsRecord<string, string>NoOverride --kc-* CSS variables in the connect UI. --kc-primary and --kc-primary-text also apply to the native button.

Restricting Auth Methods​

// Email only
KryptosConnect.init({
clientId: "your-client-id",
appName: "My App",
authMethods: ["email"],
});

// Anonymous only
KryptosConnect.init({
clientId: "your-client-id",
appName: "My App",
authMethods: ["anonymous"],
});

Setting the Language​

CodeLanguage
"en"English
"fr"French
"de"German
"pt"Portuguese
"sv"Swedish
"es"Spanish
"pl"Polish
"it"Italian

KryptosConnectButton Props​

PropTypeRequiredDescription
generateLinkToken() => Promise<{ link_token: string; isAuthorized?: boolean }>YesCalled on press. Return isAuthorized: true to skip auth for existing users.
onConnectSuccess(data: UserConsent | null) => voidYesCalled on success. data is null when isAuthorized was true.
onConnectError(error: Error) => voidYesCalled on error or dismissal.
integrationNamestringNoSkip the integration list and open a specific integration directly.
buttonLabelstringNoButton text.
buttonHeightnumberNoButton height in dp. Default 56.
extraConfigRecord<string, unknown>NoPer-button config overrides merged onto the global config. Pass prefill here to pre-populate integration form fields.
styleStyleProp<ViewStyle>NoStyle for the button. backgroundColor overrides --kc-primary for that button.

Pre-filling Integration Forms​

Pass a prefill object inside extraConfig to pre-populate the integration form when the user reaches the connection step. All fields are optional — pass only the ones you have.

FieldTypeDescription
prefill.addressstringWallet or blockchain address. Triggers chain auto-detect for EVM wallets.
prefill.apiKeystringAPI key for exchange or API-based integrations.
prefill.secretKeystringSecret key for integrations that require one.
prefill.passwordstringPassword for integrations that require one.
prefill.accountNamestringAccount name for account-based integrations.
// Pre-fill a wallet address — chains are auto-detected
<KryptosConnectButton
generateLinkToken={generateLinkToken}
onConnectSuccess={handleSuccess}
onConnectError={(err) => console.error(err)}
integrationName="ethereum"
buttonLabel="Connect Wallet"
extraConfig={{ prefill: { address: "0x1234567890123456789012345678901234567890" } }}
/>

// Pre-fill API credentials for an exchange
<KryptosConnectButton
generateLinkToken={generateLinkToken}
onConnectSuccess={handleSuccess}
onConnectError={(err) => console.error(err)}
integrationName="binance"
buttonLabel="Connect Binance"
extraConfig={{ prefill: { apiKey: "user-api-key", secretKey: "user-secret-key" } }}
/>
info

Prefilled values populate the form as editable defaults — the user can still change them before submitting. For EVM wallets, providing an address automatically triggers chain detection and pre-selects all detected chains. Use EVM chain detection to change that behaviour.

EVM chain detection​

When a user enters an EVM address — by typing it or via prefill — the connect UI looks the address up to discover which chains it has activity on, then offers those chains for selection so the user can connect them all at once.

Two booleans control this. Both can be set globally in KryptosConnect.init or per-button inside extraConfig.

KeyTypeDefaultDescription
detectUsedChainsbooleantrueWhether the address is looked up at all. When false, no lookup happens and only the chain the flow was initiated with (from integrationName) is connected, as a single integration.
autoSelectAllChainsbooleantrueWhether every detected chain is pre-selected. When false, only the initiating chain is selected — the others are still listed, but the user opts into each one.

Both default to true, so the default behaviour is: detect every chain the address is active on and pre-select all of them.

Combinations​

detectUsedChainsautoSelectAllChainsResult
truetrueChains are looked up and all are pre-selected. (default)
truefalseChains are looked up and listed, but only the initiating chain is pre-selected.
false(any)No lookup. The initiating chain is connected as a single integration; autoSelectAllChains is ignored.

Examples​

Detect chains, but let the user opt into each one beyond the chain they picked:

<KryptosConnectButton
generateLinkToken={generateLinkToken}
onConnectSuccess={handleSuccess}
onConnectError={(err) => console.error(err)}
integrationName="ethereum"
buttonLabel="Connect Wallet"
extraConfig={{
prefill: { address: "0x1234567890123456789012345678901234567890" },
detectUsedChains: true,
autoSelectAllChains: false,
}}
/>

Skip chain detection entirely and connect only Ethereum:

<KryptosConnectButton
generateLinkToken={generateLinkToken}
onConnectSuccess={handleSuccess}
onConnectError={(err) => console.error(err)}
integrationName="ethereum"
buttonLabel="Connect Wallet"
extraConfig={{
prefill: { address: "0x1234567890123456789012345678901234567890" },
detectUsedChains: false,
}}
/>

Turn detection off globally for every button:

KryptosConnect.init({
clientId: "your-client-id",
appName: "My App",
detectUsedChains: false,
});
note

Both flags only affect EVM wallet integrations — other integrations are unaffected. The lookup is debounced by 500ms as the user types and re-runs whenever the address changes. If it fails or returns no chains, nothing is pre-selected, so a user is never opted into chains they did not choose.

Theming & Customization​

Theme the connect UI by passing cssVars to KryptosConnect.init. The connect UI runs inside a WebView, so global stylesheet overrides have no effect. --kc-primary and --kc-primary-text also apply to the native button's background and label.

KryptosConnect.init({
clientId: "your-client-id",
appName: "My App",
cssVars: {
"--kc-primary": "#6366f1",
"--kc-primary-hover": "#4f46e5",
"--kc-primary-text": "#ffffff",
"--kc-border-focus": "#6366f1",
},
});

For per-button overrides, use the style prop. backgroundColor takes precedence over --kc-primary for that button only:

<KryptosConnectButton
generateLinkToken={generateLinkToken}
onConnectSuccess={handleSuccess}
onConnectError={(err) => console.error(err)}
buttonLabel="Connect Coinbase"
buttonHeight={48}
style={{ backgroundColor: "#0052FF", borderRadius: 10 }}
/>

For the complete variable reference, see Theming & Customization.

Direct Integration Flow​

The integrationName prop directs users to a specific integration, bypassing the integration selection page.

Fetch available integration IDs from the public Kryptos API (see Public Endpoints - Integrations).

<KryptosConnectButton
generateLinkToken={generateLinkToken}
onConnectSuccess={handleSuccess}
onConnectError={(err) => console.error(err)}
integrationName="binance"
buttonLabel="Connect Binance"
/>
info

The integrationName value must match an integration ID from the supported providers list.

Platform Requirements​

PlatformMinimum Version
iOS12.0+
AndroidAPI 21+ (Android 5.0+)
React Native0.60+
Expo SDK48+

Features​

  • Cross-Platform: Single codebase for iOS and Android
  • Expo Support: Works with Expo and React Native CLI
  • One peer dependency: react-native-webview — the connect UI runs in a WebView, so no WalletConnect packages or crypto polyfills are needed
  • Theming: Light, dark, and auto theme support with CSS variable customization
  • TypeScript: Full TypeScript support

Next steps​