npm package: @ironxyz/stablekit-js
Description: The StableKit JavaScript SDK enables you to integrate the Iron.xyz widget into your platform.
Author: Iron.xyz
Latest version: 0.1.3

# StableKit JavaScript SDK

A developer-friendly JavaScript SDK for integrating Iron.xyz StableKit widgets into web applications.

## 🚀 Features

- **Multiple Display Variants**: Overlay, embedded, new tab, or new window
- **Full TypeScript Support**: Complete type definitions and IntelliSense
- **Event Handling**: Comprehensive callback system for all widget interactions
- **Cross-frame Communication**: Secure PostMessage-based messaging
- **Environment Support**: Production and Sandbox environments
- **Zero Dependencies**: Lightweight and self-contained
- **Modern Browser Support**: ES2018+ with automatic fallbacks

## 📦 Installation

```bash
npm install @ironxyz/stablekit-js
```

Or with yarn:

```bash
yarn add @ironxyz/stablekit-js
```

## 🎯 Quick Start

### Examples

There're three working example projects to play around with StableKit in

- [examples/minimal](examples/minimal): Very minimal vanilla js example
- [examples/vanilla](examples/vanilla): Full blown vanilla js example
- [examples/react-vite](examples/react-vite): Full blown react example

To, for example, see the `minimal` example in action, you'd do

```
npm install
npm run build
cd examples/minimal
# run a HTTP server here, e.g. `python -m http.server`
# open in the browser
```

### Basic Usage

```javascript
import * as StableKit from "@ironxyz/stablekit-js";

const widget = StableKit.init({
  widgetId: "your-widget-id",
  wallet: "0x1234567890123456789012345678901234567890",
  chain: "Ethereum",
  // Please provide the environment of the widget.
  environment: "sandbox", // Optional: 'production' | 'sandbox'
  handlers: {
    onLogin: (data) => {
      console.log("User logged in:", data);
    },
    onClose: (data) => {
      console.log("Widget closed:", data);
    },
  },
});

// Show the widget
widget.show();
```

### TypeScript Usage

```typescript
import * as StableKit from "@ironxyz/stablekit-js";
import type {
  StableKitConfig,
  StableKitEventHandlers,
} from "@ironxyz/stablekit-js";

const config: StableKitConfig = {
  widgetId: "your-widget-id",
  wallet: "0x1234567890123456789012345678901234567890",
  chain: "Ethereum",
  email: "user@example.com", // Optional
  environment: "production", // Optional: 'production' | 'sandbox'
  variant: "overlay", // Optional: 'overlay' | 'embedded' | 'newTab' | 'newWindow'
  handlers: {
    onLogin: (data) => {
      console.log("Login successful:", data);
    },
    onClose: (data) => {
      console.log("Widget closed:", data.reason);
    },
    onVirtualAccountCreated: (data) => {
      console.log("Virtual account created:", data);
    },
  },
};

const widget = StableKit.init(config);
await widget.show();
```

## 🎨 Display Variants

### Overlay (Default)

Creates a modal overlay with the widget centered on the page.

```javascript
const widget = StableKit.init({
  widgetId: "your-widget-id",
  wallet: "0x...",
  chain: "Ethereum",
  environment: "sandbox",
  variant: "overlay", // This is the default
});
```

### Embedded

Embeds the widget directly into a container element.

```javascript
const widget = StableKit.init({
  widgetId: "your-widget-id",
  wallet: "0x...",
  chain: "Ethereum",
  variant: "embedded",
  environment: "sandbox",
  containerId: "#widget-container", // Required for embedded
});
```

HTML:

```html
<div
  id="widget-container"
  style="width: 100%; height: 600px; min-height: 400px;"
></div>
```

### New Tab

Opens the widget in a new browser tab.

```javascript
const widget = StableKit.init({
  widgetId: "your-widget-id",
  wallet: "0x...",
  chain: "Ethereum",
  environment: "sandbox",
  variant: "newTab",
});
```

### New Window

Opens the widget in a new browser window with specific dimensions.

```javascript
const widget = StableKit.init({
  widgetId: "your-widget-id",
  wallet: "0x...",
  chain: "Ethereum",
  environment: "sandbox",
  variant: "newWindow",
});
```

## 📡 Event Handlers

The SDK provides comprehensive event handling for all widget interactions:

```javascript
const widget = StableKit.init({
  widgetId: "your-widget-id",
  wallet: "0x...",
  chain: "Ethereum",
  environment: "sandbox",
  handlers: {
    // User successfully authenticated
    onLogin: (data) => {
      console.log("Login data:", data);
      // data: { customerId, externalCustomerId?, email, authToken? }
    },

    // Widget was closed by user
    onClose: (data) => {
      console.log("Close reason:", data.reason);
      // data: { reason: string, step?: StepType }
    },

    // User completed onboarding process
    onOnboardingComplete: (data) => {
      console.log("Onboarding complete:", data);
      // data: { customerId, status, accountDetails }
    },

    // Virtual account was created
    onVirtualAccountCreated: (data) => {
      console.log("Virtual account created:", data);
      // data: { virtualAccountId, status }
    },

    // User performed an action
    onUserAction: (data) => {
      console.log("User action:", data);
      // data: { action, step, data?, timestamp }
    },

    // Widget is ready and loaded
    onReady: (data) => {
      console.log("Widget ready:", data);
      // data: { widgetId, version }
    },

    // An error occurred
    onError: (error) => {
      console.error("Widget error:", error);
      // error: { code, message, step?, context? }
    },
  },
});
```

## Here are the types for the payloads above

```ts
// Interface for storage context parameters
export interface WidgetParamsType {
  widgetId?: string | null;
  wallet?: string | null;
  chain?: string | null;
  email?: string | null;
}

// Todo: replace the messaging types below by the published NPM package types
export interface OnLoginPayload {
  email: string;
}

export interface OnOnboardingCompletePayload {
  name: string;
  status: string;
}

export interface OnVirtualAccountCreatedPayload {
  sourceCurrency: string;
  destinationToken: string;
  recipientWallet: string;
  chain: string;
}

export type OnUserActionPayload = Record<string, string | number | boolean>;

export interface OnReadyPayload {
  widgetId: string;
}

export type OnErrorPayload = {
  name: string;
  message: string;
};
```

## ⚙️ Configuration Options

### Required Options

| Option     | Type                                                          | Description                                 |
| ---------- | ------------------------------------------------------------- | ------------------------------------------- |
| `widgetId` | `string`                                                      | Your StableKit widget ID from the dashboard |
| `wallet`   | `string`                                                      | Target wallet address for crypto transfers  |
| `chain`    | `'Solana' \| 'Ethereum' \| 'Polygon' \| 'Base' \| 'Arbitrum'` | Blockchain network to use                   |

### Optional Options

| Option            | Type                                                 | Default        | Description                                                  |
| ----------------- | ---------------------------------------------------- | -------------- | ------------------------------------------------------------ |
| `email`           | `string`                                             | -              | User's email (can be collected in widget)                    |
| `environment`     | `'production' \| 'sandbox'`                          | `'production'` | Environment to use                                           |
| `variant`         | `'overlay' \| 'embedded' \| 'newTab' \| 'newWindow'` | `'overlay'`    | Display variant                                              |
| `containerId`     | `string`                                             | -              | Container selector (required for embedded)                   |
| `handlers`        | `StableKitEventHandlers`                             | `{}`           | Event handler callbacks                                      |
| `theme`           | `'dark' \| 'light' \| 'system'`                      | `'system'`     | Theme configuration                                          |
| `debug`           | `boolean`                                            | `false`        | Enable debug logging                                         |
| `customUrl`       | `string`                                             | -              | Custom widget URL override                                   |
| `hideCloseButton` | `boolean`                                            | variant-based  | Hide close button (auto: false for overlay, true for others) |

## 🎛️ SDK Methods

### Core Methods

```javascript
const widget = StableKit.init(config);

// Show the widget
await widget.show();

// Hide the widget
widget.hide();

// Update configuration
widget.updateConfig({
  email: "newemail@example.com",
  handlers: {
    onLogin: (data) => console.log("Updated handler:", data),
  },
});

// Get current status
const status = widget.getStatus();
console.log(status); // { isVisible, currentStep?, isLoaded }

// Send custom message to widget
widget.sendMessage("customAction", { data: "value" });

// Destroy and clean up
widget.destroy();
```

### Global Instance (Optional)

For convenience, you can use a global instance:

```javascript
import { initGlobal, getGlobalInstance } from "@ironxyz/stablekit-js";

// Initialize global instance
const widget = initGlobal(config);

// Later, get the global instance
const globalWidget = getGlobalInstance();
globalWidget?.show();
```

## 🔧 Advanced Usage

### Custom Error Handling

```javascript
import * as StableKit from "@ironxyz/stablekit-js";
import { StableKitError } from "@ironxyz/stablekit-js";

try {
  const widget = StableKit.init(config);
  await widget.show();
} catch (error) {
  if (error instanceof StableKitError) {
    console.error("StableKit Error:", error.code, error.message);
    console.error("Context:", error.context);
  } else {
    console.error("Unexpected error:", error);
  }
}
```

### Configuration Validation

The SDK automatically validates configuration when you call `init()`. Invalid configurations will throw a `StableKitError`:

```javascript
import * as StableKit from "@ironxyz/stablekit-js";
import { StableKitError } from "@ironxyz/stablekit-js";

try {
  const widget = StableKit.init(config);
} catch (error) {
  if (error instanceof StableKitError) {
    console.error("Configuration error:", error.message);
  }
}
```

### Custom Message Handling

For advanced use cases, you can send custom messages directly to the widget:

```javascript
import * as StableKit from "@ironxyz/stablekit-js";

const widget = StableKit.init(config);

// Send custom message to widget
widget.sendMessage("customAction", {
  data: "payload",
});
```

## 🌍 Environment URLs

The SDK automatically uses the correct URLs for different environments:

- **Production**: `https://app.iron.xyz/stablekit`
- **Sandbox**: `https://app.sandbox.iron.xyz/stablekit`

## 🔒 Security

- **Origin Validation**: All PostMessage communication validates origins
- **HTTPS Only**: Production and staging enforce HTTPS
- **Sandboxed Iframes**: Widgets run in sandboxed environments
- **CSP Compatible**: Supports Content Security Policy headers

## 📱 Browser Support

- **Modern Browsers**: Chrome 70+, Firefox 65+, Safari 12+, Edge 79+
- **Required APIs**: PostMessage, EventTarget, Promises, ES2018+ features
- **Mobile**: iOS Safari 12+, Chrome Mobile 70+

## 🛠️ Development

### Building from Source

```bash
# Install dependencies
npm install

# Build the SDK
npm run build

# Run tests
npm test

# Watch mode for development
npm run dev
```

### Testing

```bash
# Run all tests
npm test

# Run tests with coverage
npm run test -- --coverage

# Run tests in watch mode
npm run test -- --watch
```

## 📝 Examples

### React Integration

```jsx
import React, { useEffect, useRef } from "react";
import * as StableKit from "@ironxyz/stablekit-js";

function MyComponent() {
  const widgetRef = useRef(null);

  useEffect(() => {
    const widget = StableKit.init({
      widgetId: "your-widget-id",
      wallet: "0x...",
      chain: "Ethereum",
      variant: "embedded",
      containerId: "#widget-container",
      handlers: {
        onLogin: (data) => {
          console.log("User logged in:", data);
        },
      },
    });

    widgetRef.current = widget;

    return () => {
      widget.destroy();
    };
  }, []);

  const showWidget = () => {
    widgetRef.current?.show();
  };

  return (
    <div>
      <button onClick={showWidget}>Open StableKit</button>
      <div id="widget-container" style={{ width: "390px", height: "844px" }} />
    </div>
  );
}
```

### Vue Integration

```vue
<template>
  <div>
    <button @click="showWidget">Open StableKit</button>
    <div id="widget-container" style="width: 390px; height: 844px;"></div>
  </div>
</template>

<script>
import * as StableKit from "@ironxyz/stablekit-js";

export default {
  data() {
    return {
      widget: null,
    };
  },
  mounted() {
    this.widget = StableKit.init({
      widgetId: "your-widget-id",
      wallet: "0x...",
      chain: "Ethereum",
      variant: "embedded",
      containerId: "#widget-container",
    });
  },
  beforeDestroy() {
    this.widget?.destroy();
  },
  methods: {
    showWidget() {
      this.widget?.show();
    },
  },
};
</script>
```

## 🤝 Support

- **Documentation**: [https://docs.iron.xyz/docs](https://docs.iron.xyz/docs)
- **Issues**: [GitHub Issues](https://github.com/ironxyz/stablekit-js/issues)
- **Email**: support@iron.xyz