> ## Documentation Index
> Fetch the complete documentation index at: https://docs.inco.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrating to @inco/lightning-js (v1)

> Upgrade from the old @inco/js package to the renamed @inco/lightning-js v1 SDK.

The Inco JS SDK has been **renamed from `@inco/js` to `@inco/lightning-js`** for v1. This guide covers the package rename plus the breaking API changes when upgrading from `@inco/js` 0.7.x.

<Note>
  Update every import — `@inco/js` → `@inco/lightning-js` and `@inco/js/lite` → `@inco/lightning-js/lite` — then reinstall. The old `@inco/js` package is no longer updated.
</Note>

## Install

<CodeGroup>
  ```bash npm theme={null}
  npm install @inco/lightning-js@latest
  ```

  ```bash yarn theme={null}
  yarn add @inco/lightning-js@latest
  ```

  ```bash bun theme={null}
  bun add @inco/lightning-js@latest
  ```
</CodeGroup>

***

## Instantiating the SDK

v1 adds explicit per-network factories — `Lightning.baseSepoliaTestnet()` and `Lightning.baseMainnet()` — which take no `pepper` or `chainId`. Prefer them over the v0.7 `Lightning.latest(...)` call:

```diff theme={null}
- // v0.7
- const zap = await Lightning.latest('testnet', supportedChains.baseSepolia);
+ // v1 — Base Sepolia
+ const zap = await Lightning.baseSepoliaTestnet();

+ // v1 — Base mainnet
+ const zap = await Lightning.baseMainnet();
```

Pass your own `hostChainRpcUrls` (recommended — otherwise the SDK falls back to viem's public RPC endpoints):

```ts theme={null}
const zap = await Lightning.baseSepoliaTestnet({
  hostChainRpcUrls: ['https://your-rpc-url'],
});
```

`Lightning.latest(pepper, chainId)` still works for advanced cases, but the network factories are the recommended entry point.

***

## Breaking Changes

### 1. Keypair generation: `generateSecp256k1Keypair` removed

`generateSecp256k1Keypair` was used for reencryption in attested decrypt calls. It has been replaced by `generateXwingKeypair`, a post-quantum X-Wing keypair. The new function is **async**.

```diff theme={null}
- import { generateSecp256k1Keypair } from '@inco/lightning-js/lite';
+ import { generateXwingKeypair } from '@inco/lightning-js/lite';

- const keypair = generateSecp256k1Keypair();
+ const keypair = await generateXwingKeypair();
```

The returned keypair still exposes `encodePublicKey()` for use in attested decrypt calls.

<Note>
  Session keys are unaffected — they use a plain viem `PrivateKeyAccount` (via `privateKeyToAccount(generatePrivateKey())`), not an X-Wing keypair.
</Note>

***

### 2. Attested methods: positional args replaced by an opts object

All attested methods (`attestedDecrypt`, `attestedCompute`, `attestedDecryptWithVoucher`, `attestedComputeWithVoucher`) previously accepted reencryption keys as extra positional arguments. They now use a typed `opts` object as the last parameter.

#### `attestedDecrypt` — reencrypt for a delegate

```diff theme={null}
- const results = await zap.attestedDecrypt(walletClient, handles, delegatePubKey);
+ const results = await zap.attestedDecrypt(walletClient, handles, { reencryptPubKey: delegatePubKey });
```

#### `attestedDecrypt` — reencrypt and decrypt locally

```diff theme={null}
- const results = await zap.attestedDecrypt(walletClient, handles, pubKey, keypair);
+ const results = await zap.attestedDecrypt(walletClient, handles, { reencryptPubKey: pubKey, reencryptKeypair: keypair });
```

#### `attestedCompute` — reencrypt for a delegate

```diff theme={null}
- const result = await zap.attestedCompute(walletClient, handle, op, value, delegatePubKey);
+ const result = await zap.attestedCompute(walletClient, handle, op, value, { reencryptPubKey: delegatePubKey });
```

#### No-opts calls are unchanged

If you were calling `attestedDecrypt` or `attestedCompute` without any reencryption options (plain decryption), no change is needed — the default behaviour is still to return plaintext directly.

***

### 3. Session key methods: reencryption options moved into an `opts` object

The session key is still a plain viem `PrivateKeyAccount`, and `attestedDecryptWithVoucher` / `attestedComputeWithVoucher` keep their positional arguments. What changed is that reencryption keys moved out of positional arguments and into a trailing `opts` object (the same change as section 2).

```diff theme={null}
+ import { generateXwingKeypair } from '@inco/lightning-js/lite';
+ const decryptKeypair = await generateXwingKeypair();

- await zap.attestedDecryptWithVoucher(ephemeralAccount, voucher, handles, pubKey, keypair);
+ await zap.attestedDecryptWithVoucher(ephemeralAccount, voucher, handles, {
+   reencryptPubKey: decryptKeypair.encodePublicKey(),
+   reencryptKeypair: decryptKeypair,
+ });
```

***

### 4. BackoffConfig field renames

The `backoffConfig` object passed to attested methods has two renamed fields, and is now passed inside the opts object.

```diff theme={null}
- await zap.attestedDecrypt(walletClient, handles, {
-   maxRetries: 5,
-   initialDelay: 1000,
-   maxDelay: 10000,
- });
+ await zap.attestedDecrypt(walletClient, handles, {
+   backoffConfig: {
+     maxRetries: 5,
+     baseDelayInMs: 1000,
+     backoffFactor: 2,
+   }
+ });
```

| Old field      | New field       | Notes                         |
| -------------- | --------------- | ----------------------------- |
| `initialDelay` | `baseDelayInMs` | Renamed                       |
| `maxDelay`     | *(removed)*     | Replaced by `backoffFactor`   |
| *(n/a)*        | `backoffFactor` | Multiplier applied each retry |

***

### 5. `AllowanceVoucher`: `warning` field is first and enforced on-chain (EIP-712 digest change)

The `AllowanceVoucher` EIP-712 struct includes a `string warning` field that is now **first** in the struct. This ensures wallets display it before the opaque byte fields. The field order is part of the EIP-712 type hash, so any voucher signed with the old field order produces a different digest and will fail verification.

Additionally, `isAllowedWithProof` now **enforces the exact warning text on-chain** — it reverts with `InvalidVoucherWarning` if the voucher's `warning` does not match `REQUIRED_ALLOWANCE_VOUCHER_WARNING` exactly. Passing an empty string, a custom message, or the old warning text will be rejected.

If you use `grantSessionKeyAllowanceVoucher` or `grantCustomSessionKeyAllowanceVoucher` (the `Lightning` methods), **no change is needed** — the correct `warning` is populated automatically.

Always create vouchers through the SDK, which sets the correct `warning` for you:

```typescript theme={null}
import { Lightning } from '@inco/lightning-js/lite';

const zap = await Lightning.baseSepoliaTestnet();

// warning is set automatically to the required on-chain text
const voucher = await zap.grantSessionKeyAllowanceVoucher(
  walletClient,
  granteeAddress,                          
  new Date(Date.now() + 10 * 60 * 1000),   
  sessionVerifierAddress,                  
);
```

For custom session-verifier logic, use `grantCustomSessionKeyAllowanceVoucher(walletClient, sessionVerifierAddress, sharerArgData)` instead — it also sets the `warning` automatically.

***

## New Features

### Multiple RPC endpoints with automatic fallback

All Lightning factory methods now accept an optional `hostChainRpcUrls` array. When multiple URLs are provided the SDK uses viem's `fallback()` transport internally — it tries providers in order and moves to the next on failure.

```typescript theme={null}
// Recommended: use the explicit network factory for your chain.
// for Base mainnet: Lightning.baseMainnet({ ... })
const zap = await Lightning.baseSepoliaTestnet({
  hostChainRpcUrls: ['https://primary.rpc', 'https://fallback.rpc'],
});
```

`baseSepoliaTestnet()` and `baseMainnet()` are the recommended way to initialize the SDK. Passing your own `hostChainRpcUrls` is recommended — without it the SDK falls back to viem's public RPC endpoints. A single URL also works.
