# @unifold/connect-react

The complete React SDK for integrating crypto deposits and onramp functionality into your application. Simple, powerful, and fully customizable.

## Installation

```bash
npm install @unifold/connect-react
# or
pnpm add @unifold/connect-react
```

## Quick Start

### 1. Wrap your app with UnifoldProvider

```tsx
import { UnifoldProvider } from '@unifold/connect-react';

function App() {
  return (
    <UnifoldProvider 
      publishableKey="pk_test_your_key"
      config={{
        modalTitle: 'Deposit Crypto',
        hideDepositTracker: false,
      }}
    >
      <YourApp />
    </UnifoldProvider>
  );
}
```

### 2. Call beginDeposit() anywhere in your app

```tsx
import { useUnifold } from '@unifold/connect-react';

function DepositButton() {
  const { beginDeposit } = useUnifold();

  const handleDeposit = async () => {
    try {
      // beginDeposit returns a Promise
      const result = await beginDeposit({
        // Required fields
        userId: 'user_123', // Your user's unique identifier
        destinationChainId: '137', // Polygon
        destinationTokenAddress: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174',
        destinationTokenSymbol: 'USDC',
        recipientAddress: '0x606C49ca2Fa4982F07016265040F777eD3DA3160',
        
        // Optional callbacks (fired immediately)
        onSuccess: ({ message }) => {
          console.log('Immediate callback:', message);
        },
        onError: ({ message }) => {
          console.error('Immediate callback error:', message);
        },
      });
      
      // Handle the result from the promise
      console.log('Deposit completed!', result);
      alert('Success: ' + result.message);
    } catch (error) {
      // Handle the error from the promise
      console.error('Deposit failed:', error);
      alert('Error: ' + error.message);
    }
  };

  return <button onClick={handleDeposit}>Deposit</button>;
}
```

That's it! The modal with full deposit UI will appear automatically.

## API Reference

### UnifoldProvider

Wraps your application and provides the Unifold context.

**Props:**
- `publishableKey` (required): Your Unifold API publishable key
- `config` (optional): Configuration object
  - `modalTitle` (optional): Custom title for the deposit modal
  - `hideDepositTracker` (optional): Hide the deposit tracker option
  - `appearance` (optional): Theme appearance - `'light'` | `'dark'` | `'auto'` (defaults to `'dark'`)
    - `'light'`: Force light mode
    - `'dark'`: Force dark mode (default)
    - `'auto'`: Use system preference and respond to changes
- `children` (required): Your React components

```tsx
<UnifoldProvider 
  publishableKey="pk_test_..."
  config={{
    modalTitle: 'Deposit Crypto',
    hideDepositTracker: false,
    appearance: 'dark', // 'light' | 'dark' | 'auto' (defaults to 'dark')
  }}
>
  {children}
</UnifoldProvider>
```

### useUnifold()

Hook that provides access to the deposit functionality.

**Returns:**
- `publishableKey`: The current publishable key
- `beginDeposit(config)`: Function to launch the deposit modal (returns a Promise)
- `closeDeposit()`: Function to programmatically close the modal

```tsx
const { beginDeposit, closeDeposit, publishableKey } = useUnifold();
```

### beginDeposit(config)

Launches the deposit modal with the specified configuration. **Returns a Promise** that resolves when the deposit completes or rejects on error/cancellation.

**Parameters (DepositConfig):**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `userId` | string | ✅ | Your user's unique identifier for wallet creation/tracking |
| `destinationChainId` | string | ✅ | Target blockchain chain ID |
| `destinationTokenAddress` | string | ✅ | Token contract address |
| `destinationTokenSymbol` | string | ✅ | Token symbol (e.g., "USDC") |
| `recipientAddress` | string | ✅ | Recipient wallet address |
| `defaultSourceChainType` | string | - | Pre-select source chain type in Transfer Crypto (e.g. `"solana"`, `"ethereum"`). Must be paired with `defaultSourceChainId` + symbol or token address. If omitted or no match, defaults to the first token from `/supported_deposit_tokens`. |
| `defaultSourceChainId` | string | - | Source chain ID (e.g. `"mainnet"`, `"137"`). Paired with `defaultSourceChainType`. |
| `defaultSourceTokenAddress` | string | - | Source token contract address. Paired with chain type + chain ID. |
| `defaultSourceSymbol` | string | - | Source token symbol (e.g. `"USDC"`). Paired with chain type + chain ID. |
| `onSuccess` | function | - | Success callback (fired immediately) |
| `onError` | function | - | Error callback (fired immediately) |
| `initialScreen` | `'main' \| 'transfer' \| 'card' \| 'tracker'` | - | `main` (default) = deposit menu. `transfer` / `card` use the **same geo/validation gates** as the menu. `tracker` opens the list **without** those gates. If not `main`, the header **back** is hidden (standalone); from `main`, back returns to the menu. Card quotes/onramp and tracker detail still show back for inner steps. |

**Returns:** `Promise<DepositResult>`

**DepositResult:**
```typescript
interface DepositResult {
  message: string;
  transaction?: unknown;
  executionId?: string;
}
```

**DepositError:**
```typescript
interface DepositError {
  message: string;
  error?: unknown;
  code?: string; // e.g., 'DEPOSIT_CANCELLED', 'POLLING_ERROR'
}
```

**Example:**

```tsx
// Promise-based (async/await)
try {
  const result = await beginDeposit({
    userId: 'user_123',
    destinationChainId: '137',
    destinationTokenAddress: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174',
    destinationTokenSymbol: 'USDC',
    recipientAddress: '0x606C49ca2Fa4982F07016265040F777eD3DA3160',
  });
  console.log('Success:', result);
} catch (error) {
  console.error('Error:', error);
}

// Standalone Transfer Crypto (geo checks + no back to menu by default)
await beginDeposit({
  externalUserId: 'user_123',
  destinationChainId: '137',
  destinationTokenAddress: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174',
  destinationTokenSymbol: 'USDC',
  recipientAddress: '0x606C49ca2Fa4982F07016265040F777eD3DA3160',
  initialScreen: 'transfer',
});

// Hybrid (promise + callbacks)
const depositPromise = beginDeposit({
  userId: 'user_123',
  destinationChainId: '137',
  destinationTokenAddress: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174',
  destinationTokenSymbol: 'USDC',
  recipientAddress: '0x606C49ca2Fa4982F07016265040F777eD3DA3160',
  onSuccess: (data) => {
    // Immediate callback
    showToast('Deposit initiated!');
  },
  onError: (error) => {
    // Immediate callback
    showToast('Error: ' + error.message);
  },
});

// Later handling
depositPromise
  .then(result => console.log('Completed:', result))
  .catch(error => console.error('Failed:', error));
```

## Features

- ✅ **Multi-chain Support** - Ethereum, Polygon, Arbitrum, Optimism, Solana, Bitcoin
- ✅ **Fiat Onramp** - Credit/debit card purchases via Meld
- ✅ **Auto-swap** - Automatic token conversion
- ✅ **QR Codes** - Mobile wallet support
- ✅ **Deposit Tracking** - Real-time status updates
- ✅ **TypeScript** - Full type definitions
- ✅ **SSR-safe** - Works with Next.js and other frameworks
- ✅ **Customizable** - Configure per-transaction

## Advanced Usage

### Pattern 1: Promise-based (Recommended for Modern Apps)

Use async/await for clean, linear code flow:

```tsx
const { beginDeposit } = useUnifold();

const handleDeposit = async () => {
  try {
    const result = await beginDeposit({
      destinationChainId: '137',
      destinationTokenAddress: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174',
      destinationTokenSymbol: 'USDC',
      recipientAddress: '0x606C49ca2Fa4982F07016265040F777eD3DA3160',
    });
    
    // Update your UI state
    setDepositStatus('completed');
    showSuccessMessage(result.message);
  } catch (error) {
    // Handle errors
    setDepositStatus('failed');
    showErrorMessage(error.message);
  }
};
```

### Pattern 2: Callback-based (Fire-and-Forget)

Use callbacks for immediate side effects without awaiting:

```tsx
const { beginDeposit } = useUnifold();

const handleDeposit = () => {
  beginDeposit({
    destinationChainId: '137',
    destinationTokenAddress: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174',
    destinationTokenSymbol: 'USDC',
    recipientAddress: '0x606C49ca2Fa4982F07016265040F777eD3DA3160',
    onSuccess: (data) => {
      console.log('Deposit initiated:', data);
      showToast('Deposit in progress!');
    },
    onError: (error) => {
      console.error('Deposit failed:', error);
      showToast('Error: ' + error.message);
    },
  });
  
  // Code continues immediately without waiting
  console.log('Deposit modal opened');
};
```

### Pattern 3: Hybrid (Promise + Callbacks)

Combine both for maximum flexibility:

```tsx
const { beginDeposit } = useUnifold();

const handleDeposit = async () => {
  try {
    // Get the promise
    const depositPromise = beginDeposit({
      destinationChainId: '137',
      destinationTokenAddress: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174',
      destinationTokenSymbol: 'USDC',
      recipientAddress: '0x606C49ca2Fa4982F07016265040F777eD3DA3160',
      
      // Immediate callbacks for real-time feedback
      onSuccess: (data) => {
        showToast('Deposit detected!');
        trackAnalyticsEvent('deposit_initiated', data);
      },
      onError: (error) => {
        showToast('Error: ' + error.message);
      },
    });
    
    // Continue other work immediately
    setModalOpen(true);
    
    // Await the final result
    const result = await depositPromise;
    
    // Update final state
    setDepositComplete(true);
    navigateToSuccessPage(result);
  } catch (error) {
    setDepositError(error);
  }
};
```

### Programmatic Control

Close the modal programmatically:

```tsx
const { beginDeposit, closeDeposit } = useUnifold();

// Open modal
beginDeposit({
  destinationChainId: '137',
  destinationTokenAddress: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174',
  destinationTokenSymbol: 'USDC',
  recipientAddress: '0x606C49ca2Fa4982F07016265040F777eD3DA3160',
});

// Close modal after 10 seconds
setTimeout(() => {
  closeDeposit();
}, 10000);
```

**Note:** Closing the modal via `closeDeposit()` or clicking outside will reject the promise with code `'DEPOSIT_CANCELLED'`.

## TypeScript

Full TypeScript support with type definitions included:

```tsx
import { 
  UnifoldProvider, 
  useUnifold, 
  DepositConfig,
  UnifoldConnectProviderConfig 
} from '@unifold/connect-react';

// Provider config
const providerConfig: UnifoldConnectProviderConfig = {
  publishableKey: 'pk_test_...',
  config: {
    modalTitle: 'Deposit',
    hideDepositTracker: false,
  },
};

// Deposit config
const depositConfig: DepositConfig = {
  destinationChainId: '137',
  destinationTokenAddress: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174',
  destinationTokenSymbol: 'USDC',
  recipientAddress: '0x606C49ca2Fa4982F07016265040F777eD3DA3160',
  onSuccess: (data) => console.log(data),
  // TypeScript will validate all fields
};

const { beginDeposit } = useUnifold();
beginDeposit(depositConfig);
```

## Architecture

`@unifold/connect-react` is built on a clean, modular architecture:

```
@unifold/connect-react (this package)
  ├─ @unifold/react-provider (base context)
  └─ @unifold/ui-react (UI components)
```

## License

MIT

## Support

- 📧 Email: support@unifold.io
- 🐛 Issues: [GitHub Issues](https://github.com/unifold/unifold-platform/issues)
- 📖 Documentation: [unifold.io/docs](https://unifold.io/docs)
