npm package: @nexus-cross/crossx-sdk-core
Description: CROSSx Core SDK - Vanilla JavaScript Embedded Wallet SDK
Latest version: 2.0.0

# @nexus-cross/crossx-sdk-core

CROSSx Embedded Wallet SDK — Built on Clean Architecture + Hexagonal Architecture

## Features

- **EIP-1193 Provider**: Direct integration with ethers.js / viem / wagmi
- **Clean Architecture**: Separation of pure business logic and implementation
- **Environment Independent**: Supports Web / Node / React Native
- **Port & Adapters**: Swap implementations without modifying core code
- **CAIP-2**: Standard chain ID format (`eip155:612044`)
- **Transaction Confirmation Modal**: User approval required before signing/sending (cannot be bypassed)

## Installation

```bash
npm install @nexus-cross/crossx-sdk-core
```

## Quick Start

```ts
import { createCROSSxSDK, ChainId } from '@nexus-cross/crossx-sdk-core';

const sdk = createCROSSxSDK({
  receiptPolling: { intervalMs: 2000, timeoutMs: 60000 }, // optional
});

await sdk.init();
const result = await sdk.signIn();
console.log('Wallet address:', sdk.getAddress());
```

> Backend URLs are configured via `VITE_*` environment variables in `.env`.
> Do not add URL fields to `SDKConfig` or pass them directly.

---

## API Reference

### Initialization & Authentication

```ts
// Initialize SDK (includes automatic session restoration)
await sdk.init();

// Sign in (OAuth popup)
const result = await sdk.signIn();
const result = await sdk.signIn({ provider: 'google' });

// Check authentication status
sdk.isAuthenticated(); // boolean
sdk.getAddress();      // string | null

// Sign out
await sdk.signOut();
```

---

### Wallet

```ts
// Create wallet (when no wallet exists after sign-in)
const { address } = await sdk.createWallet();

// Get native balance
const { wei, formatted } = await sdk.getBalance('eip155:612044');
console.log(formatted); // "1.234567 CROSS"

// Get current nonce
const nonce = await sdk.getNonce('eip155:612044');
```

---

### Transaction Confirmation Modal

When calling `signTransaction()` / `sendTransaction()` / `sendTransactionAndWait()`, the SDK automatically displays a user approval modal. There is no public API for DApp developers to bypass this.

```ts
import { CROSSxError, ErrorCode } from '@nexus-cross/crossx-sdk-core';

try {
  const result = await sdk.signTransaction(ChainId.CROSS_TESTNET, tx);
} catch (error) {
  if (error instanceof CROSSxError && error.code === ErrorCode.USER_REJECTED) {
    console.log('User cancelled the transaction');
  }
}
```

> For more details → [Transaction Confirmation Modal Guide](../../doc/transaction-confirmation.md)

---

### Sign & Send Transactions

```ts
const tx = {
  from:                 sdk.getAddress()!,
  to:                   '0x...',
  value:                '0x2386f26fc10000',
  data:                 '0x',
  chainId:              612044,
  gasLimit:             '0x5208',
  maxFeePerGas:         '0x77359400',
  maxPriorityFeePerGas: '0x3b9aca00',
  nonce:                await sdk.getNonce('eip155:612044'),
};

// Sign only
const { signedTx, txHash } = await sdk.signTransaction('eip155:612044', tx);

// Send (returns immediately with status: 'pending')
const { txHash } = await sdk.sendTransaction('eip155:612044', tx);

// Send + Poll receipt (waits until included in block)
const { txHash, receipt } = await sdk.sendTransactionAndWait('eip155:612044', tx);
console.log(receipt.status); // '0x1' = success, '0x0' = reverted
```

---

### Receipt Query & Polling

```ts
// Single query (returns null if not yet mined)
const receipt = await sdk.getTransactionReceipt(txHash, 'eip155:612044');

// Poll until mined
const receipt = await sdk.waitForTransaction(txHash, 'eip155:612044');

// Override polling options
const receipt = await sdk.waitForTransaction(txHash, 'eip155:612044', {
  intervalMs: 3000,
  timeoutMs:  120000,
});
```

---

### EIP-1193 Provider

Standard Provider for integration with ethers.js, viem, wagmi, and more.

```ts
const provider = sdk.getProvider('eip155:612044');

// ethers.js
import { ethers } from 'ethers';
const ethersProvider = new ethers.BrowserProvider(provider);
const signer = await ethersProvider.getSigner();
await signer.sendTransaction({ to: '0x...', value: ethers.parseEther('0.01') });

// viem
import { createWalletClient, custom } from 'viem';
const client = createWalletClient({ transport: custom(provider) });
```

---

### Generic RPC Calls

Sends JSON-RPC requests directly to the node via the `/wallet/rpc` endpoint.

```ts
// Native balance
const balance = await sdk.walletRpc('eth_getBalance', [address, 'latest'], 'eip155:612044');

// eth_call — ERC-20 balanceOf
const selector = '70a08231';
const padded   = address.slice(2).padStart(64, '0');
const raw = await sdk.walletRpc(
  'eth_call',
  [{ to: contractAddress, data: '0x' + selector + padded }, 'latest'],
  'eip155:612044',
);
console.log(BigInt(raw).toString());

// Same call via Provider
const result = await provider.request({
  method: 'eth_call',
  params: [{ to: contractAddress, data: '0x' + selector + padded }, 'latest'],
});
```

---

## Gateway API Access Control (Project Whitelist)

The Gateway API enforces whitelist verification so that only authorized projects can access it.
Only combinations of **project + origin (web) or app ID (native)** registered in the management console are allowed. Unregistered requests are blocked.

### Required Headers

In addition to the existing `Authorization` header, the following headers must be included:

| Header | Description | Required |
|--------|------------|----------|
| `X-Project-Id` | Project ID issued from the management console | **Always required** |
| `Origin` | Request origin domain (automatically set by browser) | Required for web |
| `X-App-Id` | bundle ID or package name | Required for native apps |
| `X-App-Type` | `ios` / `android` / `windows` | Required when using `X-App-Id` |

### Request Examples

**Web request** (browser automatically includes `Origin`):

```
X-Project-Id: {project-id}
Authorization: Bearer {token}
```

**Native app request**:

```
X-Project-Id: {project-id}
X-App-Id: com.example.app
X-App-Type: android
Authorization: Bearer {token}
```

### Error Responses

| Code | Description |
|------|------------|
| `-10022` | Unregistered project or origin/app |
| `-10023` | Missing `X-Project-Id` header |
| `-10024` | Missing `Origin` or `X-App-Id` header |
| `-10025` | `X-App-Type` is not `android` / `ios` / `windows` |

> The SDK automatically includes `SDKConfig.projectId` in the `X-Project-Id` header.
> For web, the browser automatically sets the `Origin` header — no additional configuration needed.
> For native apps, set `SDKConfig.appId` and `SDKConfig.appType` and the SDK will include the appropriate headers automatically.

---

## SDKConfig

```ts
const sdk = createCROSSxSDK({
  /** OAuth popup mode: 'popup' (default) | 'modal' */
  oauthDisplayMode: 'popup',

  /** Enable mock wallet (for development/testing) */
  useMockWallet: false,

  /** Confirmation modal theme: 'light' (default) | 'dark' */
  theme: 'light',

  /**
   * Confirmation modal color customization (independent overrides per light/dark mode)
   */
  themeTokens: {
    light: {
      primary: '#FF6B35',  // Button & accent color (default: #019D92)
      bg:      '#F5F0EB',  // Card background color (default: #FFFFFF)
    },
    dark: {
      primary: '#FF6B35',
      bg:      '#1A0A00',  // Card background color (default: #121212)
    },
  },

  /** Receipt polling defaults */
  receiptPolling: {
    intervalMs: 2000,  // Polling interval (ms)
    timeoutMs: 60000,  // Maximum wait time (ms)
  },

  /**
   * Enable debug log output (default: true)
   * ⚠️ Always disabled in production builds regardless of this value.
   */
  debug: true,
});
```

### Confirmation Modal Theme

| Method | Description |
|---|---|
| `config.theme` | Initial theme setting (`'light'` by default) |
| `sdk.setTheme('dark')` | Runtime theme switching (preserves `themeTokens` overrides) |
| `config.themeTokens.light.*` | Light mode semantic token overrides |
| `config.themeTokens.dark.*` | Dark mode semantic token overrides |

Available override tokens: `primary` · `secondary` · `onPrimary` · `borderDefault` · `borderSubtle` · `textPrimary` · `textSecondary` · `textTertiary` · `surfaceDefault` · `bg`

For details, see [Confirmation Modal & Prepare Flow](../../doc/transaction-confirmation.md#색상-커스터마이징-themetokens).

---

## Chain ID Constants

Use `ChainId` constants to prevent typos.

```ts
import { ChainId } from '@nexus-cross/crossx-sdk-core';

await sdk.getBalance(ChainId.CROSS_TESTNET);  // 'eip155:612044'
await sdk.getBalance(ChainId.CROSS_MAINNET);  // 'eip155:612055'
```

---

## Environment Variables

| Variable | Purpose | Required |
|---|---|---|
| `VITE_WALLET_GATEWAY_URL` | Wallet Gateway API URL | ✅ |
| `VITE_OAUTH_SERVICE_URL` | OAuth Service URL | ✅ |
| `VITE_AUTH_API_URL` | Auth API URL | ✅ |

---

## Public Types

```ts
import type {
  SDKConfig,
  ConfirmationTokenOverride,   // themeTokens type (per light/dark mode overrides)
  ConfirmationThemeTokens,     // Single mode semantic token type
  AuthResult,
  EvmTransactionRequest,
  TransactionResult,
  TransactionWithReceipt,      // sendTransactionAndWait() return type
  TransactionReceipt,          // waitForTransaction() return type
  SignTransactionResult,
  ChainIdValue,
} from '@nexus-cross/crossx-sdk-core';
```

---

## Related Documentation

- [Transaction Confirmation Modal](../../doc/transaction-confirmation.md)
- [Ethereum Provider Guide](../../doc/ethereum-provider.md)
- [Environment Variables](../../doc/environment-variables.md)
- [CAIP-2 Chain ID Guide](../../doc/caip-integration.md)
- [wagmi Integration Guide](../../doc/wagmi-integration.md)
- [Architecture](../../doc/architecture.md)

## License

MIT