Kryptos Connect Mobile SDK for React Native - Simplifies Web3 finance for mobile apps with support for 5000+ DeFi protocols, 200+ exchanges and wallets, and 100+ blockchains. Works with both Expo and React Native CLI.
npm install @kryptos_connect/mobile-sdkKryptos Connect Mobile SDK for React Native - Works with both Expo and React Native CLI. Simplifies Web3 finance integration for mobile apps.
``bash`
npm install @kryptos_connect/mobile-sdk react-native-svg
`bash`
yarn add @kryptos_connect/mobile-sdk react-native-svg
`bash`
npx expo install @kryptos_connect/mobile-sdk react-native-svg
`bash`
cd ios && pod install
For Expo or bare React Native, install the core WalletConnect/AppKit deps together:
`bash`
npx expo install @reown/appkit-react-native @react-native-async-storage/async-storage react-native-get-random-values react-native-svg @react-native-community/netinfo @walletconnect/react-native-compat react-native-safe-area-context expo-application
- Install the required peer deps (AppKit + WalletConnect) in one go:
`bash`
npx expo install @kryptos_connect/mobile-sdk react-native-svg @react-native-async-storage/async-storage @react-native-community/netinfo react-native-get-random-values @walletconnect/react-native-compat @reown/appkit-react-native @reown/appkit-ethers-react-native expo-application
- Expo SDK 53+: add babel.config.js to enable unstable_transformImportMeta for valtio:`
js`
// babel.config.js
module.exports = function (api) {
api.cache(true);
return {
presets: [["babel-preset-expo", { unstable_transformImportMeta: true }]],
};
};
- Install peer deps:
`bash`
npm install @kryptos_connect/mobile-sdk react-native-svg @react-native-async-storage/async-storage @react-native-community/netinfo react-native-get-random-values @walletconnect/react-native-compat @reown/appkit-react-native @reown/appkit-ethers-react-native viem
cd ios && pod install
- iOS: .
`tsx
import { KryptosConnectProvider } from "@kryptos_connect/mobile-sdk";
const config = {
appName: "Your App Name",
appLogo: "https://your-logo-url.com/logo.png", // or require('./logo.png')
clientId: "your-client-id",
theme: "light", // or 'dark'
walletConnectProjectId: "your-walletconnect-project-id", // optional
};
export default function App() {
return (
);
}
`
`tsx
import { KryptosConnectButton } from "@kryptos_connect/mobile-sdk";
function YourComponent() {
const generateLinkToken = async () => {
// Call your backend API to generate a link token
const response = await fetch("https://your-api.com/generate-link-token", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
// Optional: Include access_token for authenticated users
// access_token: userAccessToken
}),
});
const data = await response.json();
// Return link_token and isAuthorized flag
return {
link_token: data.link_token,
isAuthorized: data.isAuthorized, // pass true if access_token was provided
};
};
const handleSuccess = (userConsent) => {
console.log("Connection successful!", userConsent);
// For new users, exchange the public token for an access token
// For authorized users, userConsent might be null
if (userConsent?.public_token) {
exchangePublicToken(userConsent.public_token);
}
};
const handleError = (error) => {
console.error("Connection failed:", error);
};
return (
onSuccess={handleSuccess}
onError={handleError}
/>
);
}
`
The SDK automatically handles two different user flows based on the isAuthorized flag returned from generateLinkToken:
``
INIT → AUTH (Login) → OTP → INTEGRATION → PERMISSIONS → STATUS
- User enters email and verifies OTP (or continues as guest)
- User selects integrations to connect
- User grants permissions/consent
- Returns public_token in onSuccess callback
``
INIT → INTEGRATION → STATUS
- Skips login/OTP (already authenticated)
- Skips permissions (already consented)
- User directly selects integrations
- Returns null in onSuccess callback (no new token needed)
`tsx
const generateLinkToken = async () => {
const user = getCurrentUser(); // Your auth logic
const response = await fetch("/api/kryptos/create-link-token", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
// Include access_token if user is logged in
access_token: user?.kryptosAccessToken || undefined,
}),
});
const data = await response.json();
return {
link_token: data.link_token,
// isAuthorized will be true if access_token was valid
isAuthorized: !!user?.kryptosAccessToken,
};
};
`
Passing a walletConnectProjectId to KryptosConnectProvider enables the built-in AppKit (WalletConnect v2) flow used by the WalletConnectComponent:
`tsx
import "@walletconnect/react-native-compat";
import "react-native-get-random-values";
import { KryptosConnectProvider } from "@kryptos_connect/mobile-sdk";
const config = {
appName: "Your App",
appLogo: "https://your-logo.png",
clientId: "
walletConnectProjectId: "
};
export default function App() {
return (
);
}
`
Behind the scenes the SDK creates an AppKit instance (see src/wallet-connect/AppKitConfig.ts) with Ethers adapter, common EVM chains, and persistent storage. If you need to customize chains, metadata, or features, copy that file into your app and adjust it following the Reown AppKit guide: https://docs.reown.com/appkit/overview
You can configure different environments (dev/prod) by passing the baseUrl option:
`tsx
import { KryptosConnectProvider } from "@kryptos_connect/mobile-sdk";
// Development Environment
const devConfig = {
appName: "Your App",
clientId: "your-client-id",
theme: "light",
walletConnectProjectId: "your-project-id",
};
// Production Environment
const prodConfig = {
appName: "Your App",
clientId: "your-client-id",
theme: "light",
walletConnectProjectId: "your-project-id",
};
export default function App() {
const config = __DEV__ ? devConfig : prodConfig;
return (
);
}
`
You can also use a custom button:
`tsx
import { KryptosConnectButton } from "@kryptos_connect/mobile-sdk";
import { Text, View, StyleSheet } from "react-native";
function CustomButton() {
return (
onSuccess={handleSuccess}
onError={handleError}
>
);
}
const styles = StyleSheet.create({
customButton: {
backgroundColor: "#00C693",
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 8,
alignItems: "center",
},
customText: {
color: "#FFFFFF",
fontSize: 16,
fontWeight: "600",
},
});
`
The SDK automatically displays a "Sandbox Mode" badge when using the development environment. This helps distinguish between development and production environments visually. The badge appears at the bottom of the authentication modal and only shows when:
- The client's project stage is not "production"
This indicator is automatically managed by the SDK and requires no additional configuration.
The provider component that wraps your app and provides the Kryptos context.
#### Props
| Prop | Type | Required | Description |
| ---------- | --------------- | -------- | -------------------- |
| config | KryptosConfig | Yes | Configuration object |children
| | ReactNode | Yes | Child components |
#### KryptosConfig
`typescript`
type KryptosConfig = {
appName: string; // Your app name
appLogo?: ReactNode | string | ImageSourcePropType; // Logo URL, React Native Image source, or require()
theme?: "light" | "dark"; // Theme mode (default: 'light')
clientId: string; // Your Kryptos client ID
walletConnectProjectId?: string; // Optional WalletConnect project ID
};
The main button component that triggers the connection flow.
#### Props
| Prop | Type | Required | Description |
| ------------------- | --------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------ |
| generateLinkToken | () => Promise<{ link_token: string; isAuthorized?: boolean }> | Yes | Function that returns link token and authorization status. isAuthorized: true skips login flow for authenticated users |onSuccess
| | (consent: UserConsent \| null) => void | No | Callback fired when connection succeeds. Receives public_token for new users, null for authorized users |onError
| | (error?: Error) => void | No | Callback fired when connection fails |children
| | ReactNode | No | Custom button content |style
| | ViewStyle | No | Custom button container style |textStyle
| | TextStyle | No | Custom button text style |
#### TypeScript Types
`typescript
interface KryptosConnectButtonProps {
generateLinkToken: () => Promise<{
link_token: string;
isAuthorized?: boolean;
}>;
onSuccess?: (data: UserConsent | null) => void;
onError?: (error?: Error) => void;
children?: ReactNode;
style?: ViewStyle;
textStyle?: TextStyle;
}
interface UserConsent {
public_token: string;
// Additional consent data
}
// Note: onSuccess receives:
// - UserConsent with public_token for new users (isAuthorized: false)
// - null for authenticated users (isAuthorized: true)
`
You'll need to implement two backend endpoints to complete the integration.
| Environment | URL |
| -------------- | --------------------------------- |
| Production | https://connect-api.kryptos.io/ |
The link token can be created in two ways, depending on whether you want to authenticate an existing user or create a new session.
#### Option A: Without Access Token (New/Anonymous User)
Use this approach for new users or when you want users to go through the login/OTP flow:
`javascript${KRYPTOS_BASE_URL}/link-token
// Example: Node.js/Express
app.post("/api/kryptos/create-link-token", async (req, res) => {
try {
const response = await fetch(, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Client-Id": YOUR_CLIENT_ID,
"X-Client-Secret": YOUR_CLIENT_SECRET,
},
body: JSON.stringify({
scopes:
"openid profile offline_access email portfolios:read transactions:read integrations:read tax:read accounting:read reports:read workspace:read users:read",
}),
});
const data = await response.json();
res.json({
link_token: data.data.link_token,
isAuthorized: false, // No access token provided
});
} catch (error) {
res.status(500).json({ error: "Failed to create link token" });
}
});
`
User Experience Flow:
1. User clicks "Connect to Kryptos"
2. Login screen appears
3. User enters email → Verifies OTP (OR continues as guest)
4. User selects integrations
5. User grants permissions
6. onSuccess receives public_token to exchange
#### Option B: With Access Token (Authenticated User)
Use this approach for existing authenticated users to skip the login flow:
`javascript
// Example: Node.js/Express
app.post("/api/kryptos/create-link-token", async (req, res) => {
try {
// Get the user's stored access token from your database
const userAccessToken = await getUserAccessToken(req.user.id);
const response = await fetch(${KRYPTOS_BASE_URL}/link-token, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Client-Id": YOUR_CLIENT_ID,
"X-Client-Secret": YOUR_CLIENT_SECRET,
},
body: JSON.stringify({
scopes:
"openid profile offline_access integrations:read integrations:write",
access_token: userAccessToken, // Pass the user's access token
}),
});
const data = await response.json();
res.json({
link_token: data.data.link_token,
isAuthorized: true, // Access token provided, user is authenticated
});
} catch (error) {
res.status(500).json({ error: "Failed to create link token" });
}
});
`
User Experience Flow:
1. User clicks "Connect to Kryptos"
2. Directly shows integration selection (no login screen)
3. User selects integrations
4. onSuccess receives null (no new token needed)
#### Choosing the Right Approach
| Scenario | Use Option | isAuthorized | User Flow | Returns public_token |
| ------------------------------------------ | ----------------- | ------------ | --------------------------------------- | -------------------- |
| First-time user connecting to Kryptos | A (Without Token) | false | LOGIN → OTP → INTEGRATION → PERMISSIONS | ✅ Yes |false
| User doesn't have an access token yet | A (Without Token) | | LOGIN → OTP → INTEGRATION → PERMISSIONS | ✅ Yes |true
| Returning user with stored access token | B (With Token) | | INTEGRATION only (skip login) | ❌ No |true
| Adding more integrations for existing user | B (With Token) | | INTEGRATION only (skip login) | ❌ No |
Important Notes:
- After the first successful connection (using Option A), store the access_token you receive from the token exchangeaccess_token
- Use the stored for subsequent connections (Option B) to provide a seamless experienceisAuthorized: true
- For authorized users (), the onSuccess callback receives null instead of a public_token
- Authorized users skip both the login/OTP and permissions steps
After a successful connection, exchange the public_token for an access_token. Important: Store this access_token securely in your database - you'll use it to create link tokens for authenticated users (Option B above).
`javascript
// Example: Node.js/Express
app.post("/api/kryptos/exchange-token", async (req, res) => {
try {
const { public_token } = req.body;
const response = await fetch(${KRYPTOS_BASE_URL}/token/exchange, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
public_token,
client_id: YOUR_CLIENT_ID,
client_secret: YOUR_CLIENT_SECRET,
}),
});
const data = await response.json();
// IMPORTANT: Store the access_token securely in your database
// You'll need this token to:
// 1. Create authenticated link tokens (skip login flow)
// 2. Make API calls on behalf of the user
await saveUserAccessToken(req.user.id, data.data.access_token);
res.json({ success: true });
} catch (error) {
res.status(500).json({ error: "Failed to exchange token" });
}
});
`
Complete Integration Flow:
1. First Connection (New User - isAuthorized: false):
``
App → generateLinkToken() [returns { link_token, isAuthorized: false }]
→ SDK Flow: INIT → AUTH → OTP → INTEGRATION → PERMISSIONS → STATUS
→ User logs in with email/OTP (or as guest)
→ User connects integrations
→ User grants permissions
→ onSuccess receives { public_token: "..." }
→ Backend exchanges public_token for access_token
→ Store access_token in database ← IMPORTANT
2. Subsequent Connections (Returning User - isAuthorized: true):`
`
App → generateLinkToken() [returns { link_token, isAuthorized: true }]
→ SDK Flow: INIT → INTEGRATION → STATUS (skip AUTH, OTP)
→ User directly sees integrations (no login)
→ User connects more integrations
→ onSuccess receives null (no new token needed)
→ Integrations are added to user's existing account
- ✅ iOS 12.0+
- ✅ Android 5.0+ (API 21+)
- ✅ Expo SDK 48+
- ✅ React Native 0.60+
`json``
{
"react": ">=16.8.0",
"react-native": ">=0.60.0",
"react-native-svg": ">=12.0.0"
}
MIT © Kryptos
- 📧 Email: support@kryptos.io
- 📚 Documentation: https://dashboard.kryptos.io
- 🐛 Issues: https://github.com/Kryptoskatt/kryptos-connect-mobile-package/issues