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

# React.js Integration

> This guide is for integrating the Spice Flow API with a React.js/Next.js frontend application. It covers chain authorization, delegation signing, and transaction submission with actual implementation details.

## Core Concepts

### EIP-7702 Delegation

EIP-7702 allows users to delegate transaction execution to a smart contract. This is essential for enabling cross-chain transactions without requiring users to sign multiple transactions. Read more about EIP-7702 [here](https://eip7702.io/).

### Intent

An Intent is the parent action which contains `ChainBatch`(es) wherein each `ChainBatch` is for a specific chain and has many `Call`(s).

### Chain Batch

Each chain involved in a cross-chain transaction requires its own batch with:

* Chain ID
* Call data (what to execute)
* Recent block number

### Chain Batch

A Chain Batch is the per-chain unit containing `calls` and `chainId`. Each Chain Batch is hashed (via `hashChainBatches`) to create its `hash`, which is included in the intent signature.

## Setup and Dependencies

### Required Dependencies

```json theme={null}
{
  "dependencies": {
    "viem": "^2.0.0",
    "wagmi": "^2.0.0"
  }
}
```

#### viem

Used for interacting with EVM chains. [Documentation](https://viem.sh/docs/getting-started)

#### wagmi

Used for wallet management and signing. [Documentation](https://wagmi.sh/react/getting-started)

### Core Type Definitions

```typescript theme={null}
import { Address, Hash, Hex } from "viem";

export type Call = {
  to: Address;
  value: bigint;
  data: Hex;
};

// Input to hashChainBatches
export interface ChainBatchInput {
  chainId: bigint | number;
  calls: Call[];
}

// Output of hashChainBatches
export type ChainBatch = {
  hash: Hash;
  chainId: bigint;
  calls: Call[];
};

// EIP-7702 authorization, one per chain that carries a 7702 batch
export interface Authorization {
  address: string;   // the delegate contract for that chain
  chainId: number;
  nonce: number;
  r: string;
  s: string;
  yParity: number;
}

// POST /actions request body
export interface CreateActionRequest {
  user: Address;
  chainAuthorizations?: Authorization[];
  intents: Array<{
    mode: "7702";
    signatureType: "ecdsa";
    signature: Hex;
    nbf: number;
    exp: number;
    chainBatches: Array<{
      hash: Hex;
      chainId: number;
      tokenTransfers: unknown[];
      calls: Call[];
    }>;
  }>;
}
```

<img src="https://mintcdn.com/ter-e1ca54a7/bSxPS04-n9iQl8aq/assets/types.png?fit=max&auto=format&n=bSxPS04-n9iQl8aq&q=85&s=988827aea1b451031829d3a8fbf89dc5" alt="Types Guide" width="4439" height="2204" data-path="assets/types.png" />

## Intent Creation Implementation

<Note>
  This page is the low-level, roll-it-yourself path. For most apps, use the prebuilt
  [components](/sdk/components) and [hooks](/sdk/hooks) (`useSpiceExecution`) instead. Even on the manual
  path, do not reimplement the intent hashing: import it from `@spicenet-io/spiceflow-core`.
</Note>

### Intent hashing

Import the hashing functions from the SDK rather than hand-rolling them. Two things that changed from
older examples: a chain batch is `{ chainId, calls }` (there is **no** `recentBlock`), and the intent
hash includes the `signatureType`, `nbf`, and `exp` values.

```typescript theme={null}
import { hashChainBatches, getIntentHash } from "@spicenet-io/spiceflow-core";

// hashChainBatches(batches: { chainId, calls }[]) => { hash, chainId, calls }[]
// getIntentHash(signatureType: string, nbf: bigint, exp: bigint, chainBatches) => Hash
```

### Utility Functions

```typescript theme={null}
export async function getRecentBlock(
  publicClient: PublicClient,
): Promise<bigint> {
  try {
    return await publicClient.getBlockNumber();
  } catch (error) {
    console.error("Error getting recent block:", error);
    return BigInt(0);
  }
}

export async function getAccountNonce(
  address: Address,
  publicClient: PublicClient,
): Promise<number> {
  try {
    return await publicClient.getTransactionCount({ address });
  } catch (error) {
    console.error("Error getting account nonce:", error);
    return 0;
  }
}
```

### Creating Intent with Chain Batches

```typescript theme={null}
import { hashChainBatches } from "@spicenet-io/spiceflow-core";

function createChainBatches(
  sourceChainId: number,
  destinationChainId: number,
  sourceCalls: Call[],
  destinationCalls: Call[],
) {
  // A chain batch is { chainId, calls }. No recentBlock.
  return hashChainBatches([
    { chainId: sourceChainId, calls: sourceCalls },
    { chainId: destinationChainId, calls: destinationCalls },
  ]);
}
```

## Delegation Signing

### EIP-7702 Delegation Process

Each chain requires a separate EIP-7702 delegation signature.

<Note>
  Produce EIP-7702 authorizations with viem's `signAuthorization` (see the
  [Native Swap guide](/guides/native-swap)), not a custom EIP-712 `signTypedData` scheme. The delegate
  contract differs per chain: read it with `getDelegateContract(chainId)` from
  `@spicenet-io/spiceflow-core`.
</Note>

```typescript theme={null}
async function signDelegation(
  walletClient: WalletClient,
  address: Address,
  chainId: number,
  delegateContractAddress: Address,
): Promise<Authorization> {
  const publicClient = getPublicClient({ chainId });
  const nonce = await getAccountNonce(address, publicClient);

  const domain = {
    name: "Authorization",
    version: "1",
    chainId: BigInt(chainId),
    verifyingContract: delegateContractAddress,
  } as const;

  const types = {
    Authorization: [
      { name: "contractAddress", type: "address" },
      { name: "chainId", type: "uint256" },
      { name: "nonce", type: "uint256" },
    ],
  } as const;

  const message = {
    contractAddress: delegateContractAddress,
    chainId: BigInt(chainId),
    nonce: BigInt(nonce),
  } as const;

  const signature = await walletClient.signTypedData({
    account: address,
    domain,
    types,
    primaryType: "Authorization",
    message,
  });

  const authorization = {
    address: address.toString(),
    chainId: Number(chainId),
    nonce: Number(nonce),
    r: signature.slice(0, 66) as `0x${string}`,
    s: `0x${signature.slice(66, 130)}` as `0x${string}`,
    yParity: parseInt(signature.slice(130, 132), 16) as 0 | 1,
  };

  return authorization;
}
```

### Multi-Chain Delegation Example

```typescript theme={null}
import { getDelegateContract } from "@spicenet-io/spiceflow-core";

async function signMultiChainDelegations(
  walletClient: WalletClient,
  address: Address,
  sourceChainId: number,
  destinationChainId: number,
) {
  // The delegate contract differs per chain. Read it from the SDK, never hardcode.
  const sourceAuth = await signDelegation(
    walletClient,
    address,
    sourceChainId,
    getDelegateContract(sourceChainId),
  );

  const destinationAuth = await signDelegation(
    walletClient,
    address,
    destinationChainId,
    getDelegateContract(destinationChainId),
  );

  return [sourceAuth, destinationAuth];
}
```

## Transaction Submission

### Complete Transaction Submission

```typescript theme={null}
import { getIntentHash } from "@spicenet-io/spiceflow-core";

const RELAYER_URL = "https://tx-submission-api.spicenet.io";

async function submitCrossChainTransaction(
  sourceChainId: number,
  destinationChainId: number,
  sourceCalls: Call[],
  destinationCalls: Call[],
  chainAuthorizations: Authorization[], // { r, s, yParity, address, chainId, nonce }[]
) {
  // 1. Hash the chain batches
  const chainBatches = createChainBatches(
    sourceChainId,
    destinationChainId,
    sourceCalls,
    destinationCalls,
  );

  // 2. Sign the intent hash
  const nbf = 0n;
  const exp = BigInt(Math.floor(Date.now() / 1000) + 3600); // 1 hour
  const digest = getIntentHash("ecdsa", nbf, exp, chainBatches);
  const signature = await walletClient.signMessage({
    account: address,
    message: { raw: digest },
  });

  // 3. Submit to POST /actions
  const request = {
    user: address,
    chainAuthorizations,
    intents: [
      {
        mode: "7702",
        signatureType: "ecdsa",
        signature,
        nbf: Number(nbf),
        exp: Number(exp),
        chainBatches: chainBatches.map((b) => ({ ...b, tokenTransfers: [] })),
      },
    ],
  };

  const response = await fetch(`${RELAYER_URL}/actions`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(request, (key, value) =>
      typeof value === "bigint" ? value.toString() : value,
    ),
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`API error: ${response.status} - ${errorText}`);
  }

  return response; // track steps via GET /actions/{actionId}/intents/{i}/steps/{s}
}
```

See the [POST /actions](/api-reference/endpoint/actions-create) reference for the full request and
response schema.

## Best Practices

### 1. Chain Switching

```typescript theme={null}
// Always switch to the correct chain before signing delegations
async function signDelegationForChain(chainId: number, targetNetwork: Network) {
  // Switch to target chain (hook from reown appkit)
  await switchNetwork(targetNetwork);

  // Wait for network switch to complete
  await new Promise((resolve) => setTimeout(resolve, 2000));

  // Sign delegation
  return await signDelegation(chainId);
}
```

### 2. Progress Tracking

```typescript theme={null}
// Track transaction progress
const [progress, setProgress] = useState<string>("");

const executeTransaction = async () => {
  setProgress("Signing source chain delegation...");
  const sourceAuth = await signDelegation(sourceChainId);

  setProgress("Signing destination chain delegation...");
  const destinationAuth = await signDelegation(destinationChainId);

  setProgress("Submitting transaction...");
  const result = await submitTransaction([sourceAuth, destinationAuth]);

  setProgress("Transaction submitted successfully");
  return result;
};
```
