npm package: @kryptos_connect/mobile-sdk
Description: 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.
Author: Kryptos
Latest version: 1.0.6

# @kryptos_connect/mobile-sdk

Kryptos Connect Mobile SDK for React Native – works with **Expo** and **React Native CLI**. Seamless Kryptos integration with built-in authentication, theme support, and wallet connectivity. Connect your users to the complete Web3 finance ecosystem with support for 5000+ DeFi protocols, 200+ exchanges and wallets, and 100+ blockchains.

## Installation

### Using npm

```bash
npm install @kryptos_connect/mobile-sdk react-native-svg
```

### Using yarn

```bash
yarn add @kryptos_connect/mobile-sdk react-native-svg
```

### For Expo

```bash
npx expo install @kryptos_connect/mobile-sdk react-native-svg
```

### iOS (React Native CLI only)

```bash
cd ios && pod install
```

## Prerequisites

Before using this package, you'll need:

1. **Client ID**: Obtain from [Kryptos Developer Portal](https://dashboard.kryptos.io)
2. **WalletConnect Project ID**: Get from [WalletConnect Cloud](https://cloud.walletconnect.com) (optional, for wallet connectivity)

## Core library (all-in-one)

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
```

## Platform Setup

### Expo (managed or prebuild)

- 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 }]],
    };
  };
  ```

### React Native CLI

- 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
  ```
- iOS: `cd ios && pod install`.

## Quick Start

### 1. Wrap your app with the Provider

```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",
};

export default function App() {
  return (
    <KryptosConnectProvider config={config}>
      <YourApp />
    </KryptosConnectProvider>
  );
}
```

### Configuration Options

| Property                 | Type                                       | Required | Description                                                                 |
| ------------------------ | ------------------------------------------ | -------- | --------------------------------------------------------------------------- |
| `appName`                | `string`                                   | Yes      | Your application name                                                       |
| `appLogo`                | `React.ReactNode \| string \| ImageSource` | No       | Your app logo (URL, `require()`, or React Native image source)              |
| `clientId`               | `string`                                   | Yes      | Your Kryptos client ID                                                      |
| `theme`                  | `"light" \| "dark"`                        | No       | Default theme mode (defaults to `"light"` if not set)                       |
| `walletConnectProjectId` | `string`                                   | No       | Your WalletConnect project ID (enables built-in AppKit / wallet connection) |

### 2. Add the Connect Button

```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 (
    <KryptosConnectButton
      generateLinkToken={generateLinkToken}
      onSuccess={handleSuccess}
      onError={handleError}
    />
  );
}
```

#### User Flow Variations

The SDK automatically handles two different user flows based on the `isAuthorized` flag returned from `generateLinkToken`:

**Flow 1: New User** (`isAuthorized: false` or undefined)

```
INIT → Connect → INTEGRATION → STATUS
```

- **INIT (Connect)**: Fetches link token and initializes the session & an account is created for them on the backend
- **INTEGRATION**: User selects and connects integrations (wallets, exchanges, blockchains)
- **STATUS**: Success or error result; returns `public_token` in `onSuccess` when consent is given, or `null` when user was already authorized

**Flow 2: Authenticated User** (`isAuthorized: true`)

```
INIT → INTEGRATION → STATUS
```

- Skips Connect (an account already exists for the user on the backend)
- User directly selects integrations
- Returns `null` in `onSuccess` callback (no new token needed)

### Implementation Example

```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,
  };
};
```

## WalletConnect / Reown AppKit configuration

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: "<kryptos-client-id>",
  walletConnectProjectId: "<walletconnect-cloud-project-id>",
};

export default function App() {
  return (
    <KryptosConnectProvider config={config}>
      <YourApp />
    </KryptosConnectProvider>
  );
}
```

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

### Environment Configuration

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 (
    <KryptosConnectProvider config={config}>
      <YourApp />
    </KryptosConnectProvider>
  );
}
```

### 3. Custom Button (Optional)

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 (
    <KryptosConnectButton
      generateLinkToken={generateLinkToken}
      onSuccess={handleSuccess}
      onError={handleError}
    >
      <View style={styles.customButton}>
        <Text style={styles.customText}>🔐 Connect Your Wallet</Text>
      </View>
    </KryptosConnectButton>
  );
}

const styles = StyleSheet.create({
  customButton: {
    backgroundColor: "#00C693",
    paddingVertical: 12,
    paddingHorizontal: 24,
    borderRadius: 8,
    alignItems: "center",
  },
  customText: {
    color: "#FFFFFF",
    fontSize: 16,
    fontWeight: "600",
  },
});
```

#### Direct Integration Flow

You can use the `integrationName` prop to direct users to a specific integration, bypassing the general integration selection. Useful for dedicated buttons for specific exchanges or wallets.

**Getting supported integration IDs:** Fetch from the public Kryptos API:

```bash
GET {BASE_URL}/integrations/public/list
```

**Usage example:**

```tsx
// Direct connection to a specific integration (e.g. Binance)
<KryptosConnectButton
  generateLinkToken={generateLinkToken}
  integrationName="binance"
  onSuccess={(userConsent) => {
    console.log("Binance connected:", userConsent);
  }}
>
  Connect Binance Account
</KryptosConnectButton>

// Default button text when integrationName is set: "Connect {Integration} Account"
<KryptosConnectButton
  generateLinkToken={generateLinkToken}
  integrationName="metamask"
  onSuccess={(userConsent) => console.log("MetaMask connected:", userConsent)}
/>
```

**Use cases:** Dedicated integration buttons, contextual integration (e.g. exchange-focused screens), onboarding steps (e.g. connect wallet then exchange). The `integrationName` value must match an integration ID from the supported providers list.

## API Reference

### KryptosConnectProvider

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
};
```

### KryptosConnectButton

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` indicates the user is already authorized (consent API may be skipped; `onSuccess` receives `null`) |
| `onSuccess`         | `(data: UserConsent \| null) => void`                           | No       | Callback invoked when connection succeeds. Receives `public_token` for new users, `null` for authorized users                                                                      |
| `onError`           | `(error?: Error) => void`                                       | No       | Callback invoked when connection fails                                                                                                                                             |
| `integrationName`   | `string`                                                        | No       | Integration ID from the supported providers list. When provided, skips the integration list and goes directly to that integration                                                  |
| `children`          | `ReactNode`                                                     | No       | Custom button content. Defaults to "Connect With Kryptos", or "Connect {Integration} Account" when `integrationName` is set                                                        |
| `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;
  integrationName?: string; // Integration ID from the supported providers list
  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)
```

### TypeScript

The package includes type definitions. You can import types from the package:

```typescript
import type {
  KryptosConnectButtonProps,
  KryptosConfig,
  UserConsent,
} from "@kryptos_connect/mobile-sdk";
```

## Backend Integration

You need to implement two backend endpoints. The same API is used for web and mobile.

### Base URLs

| Environment    | URL                              |
| -------------- | -------------------------------- |
| **Production** | `https://connect-api.kryptos.io` |

### 1. Create Link Token

A link token can be created in two ways: to authenticate an existing user or to create a new session.

| Approach | When to use                      |
| -------- | -------------------------------- |
| Option A | New user, no access token        |
| Option B | Returning user, has access token |

#### Option A: Without Access Token (New/Anonymous User)

Use this approach for **new users** (no access token):

```javascript
// Example: Node.js/Express
app.post("/api/kryptos/create-link-token", async (req, res) => {
  try {
    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 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.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 taps "Connect to Kryptos"
2. Connect (INIT) screen initializes, then integration selection is shown
3. User selects and connects integrations
4. `onSuccess` receives `public_token` to exchange

#### Option B: With Access Token (Authenticated User)

Use this approach for **existing authenticated users**:

```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.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 taps "Connect to Kryptos"
2. Connect (INIT) then integration selection is shown
3. User selects integrations; `onSuccess` receives `null` (no new token needed)

#### Choosing the Right Approach

| Scenario                                   | Use Option        | isAuthorized | User Flow (mobile)                                                         | Returns public_token |
| ------------------------------------------ | ----------------- | ------------ | -------------------------------------------------------------------------- | -------------------- |
| First-time user connecting to Kryptos      | A (Without Token) | `false`      | INIT → Connect(account created for user on backend) → INTEGRATION → STATUS | ✅ Yes               |
| User doesn't have an access token yet      | A (Without Token) | `false`      | INIT → Connect(account created for user on backend) → INTEGRATION → STATUS | ✅ Yes               |
| Returning user with stored access token    | B (With Token)    | `true`       | INIT → INTEGRATION → STATUS                                                | ❌ No                |
| Adding more integrations for existing user | B (With Token)    | `true`       | INIT → INTEGRATION → STATUS                                                | ❌ No                |

**Important notes:**

- After the first successful connection (Option A), store the `access_token` from the token exchange.
- Use the stored `access_token` for subsequent connections (Option B).
- For authorized users (`isAuthorized: true`), `onSuccess` receives `null`.

### 2. Exchange Public Token

After a successful connection, exchange the `public_token` for an `access_token`. **Important:** Store this `access_token` securely in your database. You will 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",
        "X-Client-Id": YOUR_CLIENT_ID,
        "X-Client-Secret": YOUR_CLIENT_SECRET,
      },
      body: JSON.stringify({
        public_token,
      }),
    });

    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 (Option B)
    // 2. Make API calls on behalf of the user
    await saveUserAccessToken(req.user.id, 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: INIT → Connect → INTEGRATION → STATUS
   → User selects and connects integrations
   → 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: INIT → INTEGRATION → STATUS
   → User sees integrations directly → connects more
   → onSuccess receives null
   → Integrations added to existing account
   ```

## Supported Platforms

- ✅ iOS 12.0+
- ✅ Android 5.0+ (API 21+)
- ✅ Expo SDK 48+
- ✅ React Native 0.60+

## Peer Dependencies

```json
{
  "react": ">=16.8.0",
  "react-native": ">=0.60.0",
  "react-native-svg": ">=12.0.0"
}
```

## Support

- 📧 Email: [support@kryptos.io](mailto:support@kryptos.io)
- 📚 Documentation: [https://docs.kryptos.io](https://docs.kryptos.io)
- 💬 Discord: [Join our community](https://discord.gg/kryptos)
- 🐛 Issues: [GitHub Issues](https://github.com/kryptos/connect-npm-package/issues)

## License

MIT © [Kryptos](https://kryptos.io)

---

Made with ❤️ by the Kryptos team