# @zerohash-sdk/fund-js

A JavaScript SDK that enables frontend applications to seamlessly integrate with the Connect Fund product.

Connect Fund provides a secure, customizable flow for funding accounts via crypto deposits directly within your application.

## Installation

### Via NPM (recommended)

```bash
npm install @zerohash-sdk/fund-js
```

```javascript
import { Fund } from '@zerohash-sdk/fund-js';
```

### Via CDN

You can import the script in your HTML file and use the `Fund` class provided by it.

```html
<script
  type="module"
  src="https://sdk-cdn.zerohash.com/fund-web/index.js"
></script>
```

Or import directly in your JavaScript code:

```javascript
import { Fund } from 'https://sdk-cdn.zerohash.com/fund-web/index.js';
```

## Getting Started

Follow these simple steps to integrate Connect Fund into your frontend application:

### 1. Import the Fund module

```javascript
import { Fund } from '@zerohash-sdk/fund-js';
```

### 1.1 Or import via CDN

```javascript
import { Fund } from 'https://sdk-cdn.zerohash.com/fund-web/index.js';
```

### 2. Initialize and render the widget

```javascript
// Create a Fund instance with configuration
const fund = new Fund({
  jwt: 'your-jwt-token', // Obtain this from your backend
  env: 'prod', // or 'cert' for testing
  theme: 'auto', // 'auto' (default), 'light', or 'dark'
  onCompleted: ({ assetSymbol, amount, network, depositAddress }) => {
    console.log('Fund completed:', amount, assetSymbol, network, depositAddress);
  },
  onFailed: ({ transactionId, amount, assetSymbol }) => {
    console.log('Deposit failed:', transactionId, amount, assetSymbol);
  },
  onDeposit: ({ data }) => {
    console.log('External-source deposit:', data.depositId, data.status.value);
  },
  onError: ({ errorCode, reason }) => {
    console.error('Fund error:', errorCode, 'Reason:', reason);
  },
  onClose: () => {
    console.log('Fund widget closed');
  },
  onEvent: ({ type, data }) => {
    console.log('Event received:', type, data);
  },
  onLoaded: () => {
    console.log('Fund widget loaded and ready');
  },
});

// Render the widget to a container element
const container = document.getElementById('fund-container');
await fund.render(container);

// Update configuration dynamically
fund.updateConfig({
  jwt: 'new-jwt-token',
  theme: 'dark',
});

// Check if rendered
if (fund.isRendered()) {
  console.log('Widget is active');
}

// Clean up when done
fund.destroy();
```

### 2.1 Using TypeScript (optional)

```typescript
import { Fund, FundConfig } from '@zerohash-sdk/fund-js';

const config: FundConfig = {
  jwt: 'your-jwt-token',
  env: 'cert',
  theme: 'dark',
  onCompleted: ({ assetSymbol, amount }) => {
    console.log(`Funded ${amount} ${assetSymbol}`);
  },
  onError: ({ errorCode, reason }) => {
    console.error(errorCode, reason);
  },
};

const fund = new Fund(config);
await fund.render(document.getElementById('fund-container')!);
```

## API Reference

### Configuration

| Prop          | Type                                                         | Required | Default  | Description                                                                                                     |
| ------------- | ------------------------------------------------------------ | -------- | -------- | --------------------------------------------------------------------------------------------------------------- |
| `jwt`         | `string`                                                     | Yes      | -        | JWT token for authentication with Connect                                                                       |
| `env`         | `"prod" \| "cert" \| "dev" \| "local"`                       | No       | `"prod"` | Target environment                                                                                              |
| `theme`       | `"auto" \| "light" \| "dark"`                                | No       | `"auto"` | Theme mode for the interface                                                                                    |
| `isPay`       | `boolean`                                                    | No       | `false`  | Run the SDK in Pay mode (see notes below)                                                                       |
| `onCompleted` | `({ assetSymbol, amount, network, depositAddress }) => void` | No       | -        | Callback when the fund flow completes successfully                                                              |
| `onFailed`    | `({ assetSymbol, amount, network, depositAddress }) => void` | No       | -        | Callback when a deposit ends in a failed state                                                                  |
| `onDeposit`   | `(deposit) => void`                                          | No       | -        | Deposit-status updates for the external-source ("connect an account") path; may fire more than once per deposit |
| `onError`     | `({ errorCode, reason }) => void`                            | No       | -        | Callback for error events                                                                                       |
| `onClose`     | `() => void`                                                 | No       | -        | Callback when the widget is closed                                                                              |
| `onEvent`     | `({ type, data }) => void`                                   | No       | -        | Callback for general events                                                                                     |
| `onLoaded`    | `() => void`                                                 | No       | -        | Callback when the widget is loaded and ready                                                                    |

### Pay mode (`isPay`)

When `isPay` is `true`, the Fund SDK runs in Pay mode:

- Uses the `pay-sdk` app id and the `app:internal:pay` scope when calling trade-api.
- Signs the `account_funding_pay` terms agreement.

### Constructor

```javascript
new Fund(config: FundConfig)
```

Creates a new Fund instance with the provided configuration.

### Methods

#### `render(container: HTMLElement): Promise<void>`

Renders the Fund widget to the specified container element.

```javascript
await fund.render(document.getElementById('fund-container'));
```

#### `updateConfig(config: Partial<FundConfig>): void`

Updates the configuration of an already rendered widget.

```javascript
fund.updateConfig({
  jwt: 'new-token',
  theme: 'dark',
});
```

#### `destroy(): void`

Removes the widget from the DOM and cleans up resources.

```javascript
fund.destroy();
```

#### `isRendered(): boolean`

Returns whether the widget is currently rendered.

```javascript
if (fund.isRendered()) {
  // Widget is active
}
```

#### `getConfig(): FundConfig`

Returns a copy of the current configuration.

```javascript
const config = fund.getConfig();
console.log('Current JWT:', config.jwt);
```

## Callback Functions

### onCompleted

Called when the fund flow completes successfully.

```javascript
onCompleted: ({ assetSymbol, amount, network, depositAddress }) => {
  // assetSymbol: Asset symbol (e.g. 'BTC.BITCOIN')
  // amount: Amount to be deposited as a string
  // network: Network used for the deposit
  // depositAddress: Deposit address for the asset
};
```

### onFailed

Called when a deposit transaction reaches a terminal **failed** state. This is a
flow outcome, not an SDK error, so `onError` is not called for it — handle both
if you need to cover every unsuccessful path.

```javascript
onFailed: ({ assetSymbol, amount, network, depositAddress, transactionId }) => {
  // Same shape as onCompleted — the details of the deposit that failed.
};
```

### onDeposit

Called when a deposit funded from an **external source** (the "connect an
account" path) changes status. That flow reports here rather than through
`onCompleted`/`onFailed`, which cover the manual-deposit and Pay paths, so handle
this too if you offer external funding sources.

This is a status notification, **not** a single terminal event: it can fire more
than once for the same deposit, including while account matching is still
verifying. Which outcome it is lives in the payload — there is no separate
failure callback:

```javascript
onDeposit: ({ data }) => {
  data.depositId; // deposit this status belongs to
  data.status.value; // 'PROCESSED' = funded, 'FAILED' = not
  data.status.details; // why, when it failed
  data.accountMatchingValidation?.status; // 'PENDING' = still verifying
  data.assetId;
  data.networkId;
  data.amount;
};
```

Treat it as finished only when `status.value` is `PROCESSED` (funded) or the
deposit has failed — either `status.value === 'FAILED'` or
`accountMatchingValidation.status` is `INVALID` or `ERROR`. Any other
combination, `PENDING` account matching in particular, is still in flight.

### onError

Called when an error occurs in the Fund widget.

```javascript
onError: ({ errorCode, reason }) => {
  // errorCode: Error code ('network_error', 'auth_error', 'server_error', etc.)
  // reason: Human-readable error description
};
```

### onClose

Called when the Fund widget is closed by the user.

```javascript
onClose: () => {
  // Handle close event
};
```

### onEvent

Called for general events from the Fund widget.

```javascript
onEvent: ({ type, data }) => {
  // type: Event type string
  // data: Event-specific data object
};
```

### onLoaded

Called when the Fund widget has fully loaded and is ready for user interaction.

```javascript
onLoaded: () => {
  // Widget is fully loaded and ready
};
```

## Browser Support

- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
- All modern browsers with Web Components support

### The two deposit paths report different fields

`onCompleted` / `onFailed` and `onDeposit` do not carry the same shape, so read
whichever the path you support actually delivers:

|            | `onCompleted` / `onFailed` (manual, Pay) | `onDeposit` (external source)                         |
| ---------- | ---------------------------------------- | ----------------------------------------------------- |
| identifier | `transactionId`, `fundId`                | `depositId`                                           |
| asset      | `assetSymbol` (e.g. `BTC.BITCOIN`)       | `assetId` (asset key, e.g. `usdc`)                    |
| network    | `network`                                | `networkId`                                           |
| amount     | `amount`, `notionalAmount`               | `amount`                                              |
| outcome    | which callback fired                     | `status.value` (+ `accountMatchingValidation.status`) |

There is no `transactionId` on an `onDeposit` payload — an external-source deposit is
identified by `depositId`. If you need one code path for both, key off the identifier
each provides rather than assuming a shared field name.

## More Information & Support

For comprehensive documentation or support about Connect, visit our [Documentation Page](https://docs.zerohash.com/).
