# Rainbow Delegation

[![npm version](https://badge.fury.io/js/@rainbow-me/delegation.svg)](https://www.npmjs.com/package/@rainbow-me/delegation)
[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)

EIP-7702 delegation utilities for batched execution, delegation status checks, and delegation lifecycle management.

## Installation

```bash
yarn add @rainbow-me/delegation
# or
npm install @rainbow-me/delegation
# or
pnpm add @rainbow-me/delegation
```

**Peer Dependencies:**

```bash
yarn add viem@^2.38.0 @storesjs/stores@^0.8.6
```

Optional peer dependencies:

- `react` — required only if using hooks (`useDelegations`, `useDelegationDisabled`, `useWillDelegate`)
- `ethers` — required only if using ethers client inputs

## Configuration

Call `configure` once before using actions/hooks.

```ts
import { configure } from '@rainbow-me/delegation';

configure({
  platformClient,
  logger,
  getCurrentAddress,
  // optional
  storeOptions,
});
```

Required services:

- `platformClient`: HTTP client with `get<T>(url, opts)`.
- `logger`: logger with `debug/info/warn/error`.
- `getCurrentAddress`: reactive current-address getter for store prefetch.

Optional services:

- `storeOptions`: storage/sync/shouldEnable options for persisted query stores.

## Delegation Contract Support

New delegation/switch decisions use the contract mapping in
`src/contract/addresses.ts`:

| Chain            | Chain ID | Delegation Contract                          |
| ---------------- | -------- | -------------------------------------------- |
| Ethereum Mainnet | `1`      | `0x612373D7003d694220f7800EeaF8E3924c0951D3` |
| Optimism         | `10`     | `0x612373D7003d694220f7800EeaF8E3924c0951D3` |
| BSC              | `56`     | `0x612373D7003d694220f7800EeaF8E3924c0951D3` |
| Gnosis Chain     | `100`    | `0x612373D7003d694220f7800EeaF8E3924c0951D3` |
| Unichain         | `130`    | `0x612373D7003d694220f7800EeaF8E3924c0951D3` |
| Polygon          | `137`    | `0x612373D7003d694220f7800EeaF8E3924c0951D3` |
| Base             | `8453`   | `0x612373D7003d694220f7800EeaF8E3924c0951D3` |
| Arbitrum         | `42161`  | `0x612373D7003d694220f7800EeaF8E3924c0951D3` |
| Celo             | `42220`  | `0x612373D7003d694220f7800EeaF8E3924c0951D3` |
| Ink              | `57073`  | `0x612373D7003d694220f7800EeaF8E3924c0951D3` |
| Berachain        | `80094`  | `0x612373D7003d694220f7800EeaF8E3924c0951D3` |
| Katana           | `747474` | `0x612373D7003d694220f7800EeaF8E3924c0951D3` |

- If a chain is not mapped, the library will not create/switch delegation on that chain
- `supportsDelegation` may still return supported when the address is already delegated (existing `eip1559` path)

## Client Inputs

Bound transaction actions accept either client shape:

- viem: `{ walletClient, publicClient, ... }`
- ethers: `{ signer, provider, chainId, ... }`

When using ethers inputs, clients are normalized internally to viem clients.

Ethers input requirements:

- `signer` must be wallet-like and expose `privateKey`
- `provider` must expose an RPC URL

## Recommended Flow

Use `supportsDelegation` to check whether delegation requirements for
`executeBatchedTransaction` are satisfied. Execution
actions still revalidate at signing time and may throw if state changes.

```ts
import {
  supportsDelegation,
  willDelegate,
  prepareBatchedTransaction,
  executeBatchedTransaction,
  UnsupportedReason,
} from '@rainbow-me/delegation';

const support = await supportsDelegation({ address, chainId });

if (!support.supported) {
  if (support.reason === UnsupportedReason.USER_DISABLED) {
    // fallback path
  }
  // fallback path when delegation requirements are not satisfied
}

const warning = await willDelegate({ address, chainId });
if (warning.willDelegate) {
  // show "this transaction will include a delegation authorization"
}

const nonce = await publicClient.getTransactionCount({ address });

const simulationTx = await prepareBatchedTransaction({
  from: address,
  calls,
  chainId,
  nonce,
});

const result = await executeBatchedTransaction({
  walletClient,
  publicClient,
  chainId,
  calls,
  nonce,
  transactionOptions: {
    maxFeePerGas,
    maxPriorityFeePerGas,
    gasLimit: null,
  },
});

console.log(result.hash, result.type); // type is 'eip1559' | 'eip7702'
```

## API Reference

### `configure({ platformClient, logger, getCurrentAddress, storeOptions? })`

Initializes services and store wiring for the library.

- Must be called before actions/hooks.

### `supportsDelegation({ address, chainId, requireFreshStatus? })`

Returns:

- `{ supported: true, reason: null }`
- `{ supported: false, reason: UnsupportedReason.USER_DISABLED | UnsupportedReason.NOT_AVAILABLE }`

UI helper only. `executeBatchedTransaction` still performs fresh checks.
`supported: true` means delegation is enabled for this address and either:

- the account is already Rainbow delegated on the chain, or
- the account can authorize delegation on the chain now.

Set `requireFreshStatus: true` to bypass cache and fetch the latest chain status.

### `willDelegate({ address, chainId, requireFreshStatus? })`

Returns:

- `{ willDelegate: true, delegation: { chainId, contractAddress } }`
- `{ willDelegate: false, delegation: null }`

UI helper indicating whether `executeBatchedTransaction` would include an authorization.
Set `requireFreshStatus: true` to bypass cache and fetch the latest chain status.
Already delegated accounts return `willDelegate: false`, including upgrade-available states.

### `prepareBatchedTransaction({ from, calls, chainId, nonce })`

Prepares transaction data for simulation.

Returns `Transaction` with optional `authorization_list` when a new delegation is needed.

### `executeBatchedTransaction({ walletClient|signer, publicClient|provider, chainId, calls, transactionOptions, nonce, value? })`

Executes batched calls:

- uses `eip1559` path when already delegated
- uses `eip7702` path when authorization is needed and allowed

Returns:

- `{ hash, type: 'eip1559' | 'eip7702' }`

Throws when delegation requirements are not satisfied for the address/chain.

### `executeDelegation({ walletClient|signer, publicClient|provider, chainId, calldata, transactionOptions, nonce, value? })`

Low-level delegation execution action.

Performs fresh delegation checks before signing authorization and sending transaction.

Returns:

- `{ hash, type: 'eip7702' }`

### `executeRevokeDelegation({ walletClient|signer, publicClient|provider, chainId, transactionOptions, nonce })`

Signs zero-address authorization and revokes delegation.
Revokes delegation state on the chain regardless of provider classification
(Rainbow or third-party).

Use `shouldRevokeDelegation` to determine if revocation is currently needed.

Returns:

- `{ hash, type: 'eip7702' }`

### `getDelegations({ address })`

Fetches delegation state and returns only active delegations:

- `DelegationStatus.RAINBOW_DELEGATED`
- `DelegationStatus.THIRD_PARTY_DELEGATED`

### `shouldRevokeDelegation({ address })`

Returns:

- `shouldRevoke: boolean`
- `revokes: { address, chainId }[]`

`true` when delegated to Rainbow with revoke reasons (`VULNERABILITY`, `BUG`, `UNSPECIFIED`).

### `resetCache()`

Clears delegation status query cache.

Does not clear per-address delegation preference (`enableDelegation` /
`disableDelegation` state).

## Hooks & Preference Helpers

Hooks require `react` as a peer dependency.

### `useDelegations(address)`

Returns:

- `DelegationWithChainId[]`

Subscribes to active delegations and keeps status fresh for the address
(30s refresh window).

### `useDelegationDisabled(address)`

Returns `true` when delegation is disabled for that address.

### `useWillDelegate(address, chainId)`

Returns `true` when current store state indicates the next batched transaction
would include a delegation authorization.

Subscribes to chain-specific delegation decision state and keeps status fresh
(15s refresh window).

### Preference actions

- `enableDelegation(address)`
- `disableDelegation(address)`
- `isDelegationEnabled(address)`

## Exported Types & Enums

The package exports store/action state types and enums, including:

- `DelegationStatus`
- `UnsupportedReason`
- `RevokeReason`
- `UpdateReason`
- `DelegationState`
- `ChainDelegationState`
- `DelegationWithChainId`
- `ActiveDelegations`

## Notes

- This package is designed for integration with a platform API via `platformClient`.
- Delegation contract addresses are chain-specific and validated against local mapping + backend state.
- `configure()` must be called before any action/hook usage.
